diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 2f43e05e74..9e16fce4ec 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -13,15 +13,16 @@ export HOMEBREW_NO_AUTO_UPDATE=1 export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 export HOMEBREW_NO_ENV_HINTS=1 export HOMEBREW_NO_INSTALL_CLEANUP=1 +brew update brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER" brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" if [ "$AARCH64" -eq 1 ]; then - brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk - brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf + brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk + brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf || true else arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet python@3.14 opencv@4 "llvm@$LLVM_COMPILER_VER" sdl3 vulkan-headers vulkan-loader molten-vk - arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative + arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative protobuf || true fi export CXX=clang++ diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index 70db614b59..819e0725bc 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -21,7 +21,7 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ "rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \ "rpcs3.app/Contents/Plugins/platforminputcontexts" \ "rpcs3.app/Contents/Plugins/virtualkeyboard" \ -"rpcs3.app/Contents/Resources/git" +"rpcs3.app/Contents/Resources/git" || true ../../.ci/optimize-mac.sh rpcs3.app @@ -49,7 +49,7 @@ QT_TRANS="$WORKDIR/qt-downloader/$QT_VER/clang_64/translations" cp $QT_TRANS/qt_*.qm rpcs3.app/Contents/translations cp $QT_TRANS/qtbase_*.qm rpcs3.app/Contents/translations cp $QT_TRANS/qtmultimedia_*.qm rpcs3.app/Contents/translations -rm -f rpcs3.app/Contents/translations/qt_help_*.qm +rm -f rpcs3.app/Contents/translations/qt_help_*.qm || true # Need to do this rename hack due to case insensitive filesystem mv rpcs3.app RPCS3_.app diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index cee6d624de..d8016d8c13 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -6,7 +6,7 @@ QT_HOST="http://qt.mirror.constant.com/" QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g") QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]') -QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." +QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}_${QT_VER_MSVC}_64/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" QT_SUFFIX="-Windows-Windows_11_24H2-${QT_VER_MSVC_UP}-Windows-Windows_11_24H2-X86_64.7z" QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}" diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 2e6434e24b..325b1e01ee 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -33,23 +33,23 @@ jobs: matrix: include: - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy:1.10" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: clang UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy:1.10" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: gcc - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: clang UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: gcc name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} @@ -137,7 +137,7 @@ jobs: runs-on: macos-14 env: CCACHE_DIR: /tmp/ccache_dir - QT_VER: '6.10.2' + QT_VER: '6.11.0' QT_VER_MAIN: '6' LLVM_COMPILER_VER: '21' RELEASE_MESSAGE: ../GitHubReleaseMessage.txt @@ -216,9 +216,9 @@ jobs: env: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.10.2' + QT_VER: '6.11.0' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202601261212' + QT_DATE: '202603180535' LLVM_VER: '19.1.7' VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' diff --git a/.gitignore b/.gitignore index 4688d5fa52..a3911be3a0 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,9 @@ CMakeSettings.json *PVS-Studio* PVS/* +# Zed Editor files +.zed/* + # Ignore other system generated files x64/* rpcs3/x64/* diff --git a/3rdparty/FAudio b/3rdparty/FAudio index e67d761ead..0372329dbb 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit e67d761ead486de3e69fa11705456bf94df734ca +Subproject commit 0372329dbb56e7814d0dea7b6eafa7a613bd8042 diff --git a/3rdparty/SoundTouch/soundtouch b/3rdparty/SoundTouch/soundtouch index 3982730833..a0fba77b6f 160000 --- a/3rdparty/SoundTouch/soundtouch +++ b/3rdparty/SoundTouch/soundtouch @@ -1 +1 @@ -Subproject commit 3982730833b6daefe77dcfb32b5c282851640c17 +Subproject commit a0fba77b6f9cfbdb71f8bbec58b6ac4e5e3b1097 diff --git a/3rdparty/curl/CMakeLists.txt b/3rdparty/curl/CMakeLists.txt index b20763af65..2b725169f9 100644 --- a/3rdparty/curl/CMakeLists.txt +++ b/3rdparty/curl/CMakeLists.txt @@ -18,9 +18,6 @@ else() set(USE_LIBIDN2 OFF CACHE BOOL "Use libidn2 for IDN support") # Disabled because MacOS CI doesn't work otherwise set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") option(CURL_DISABLE_INSTALL "Disable installation targets" ON) - if(USE_MSVC_STATIC_CRT) - set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl") - endif() if(WIN32) set(ENABLE_UNICODE ON CACHE BOOL "enable Unicode") endif() diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index 400fffa90f..8c908d2d0a 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit 400fffa90f30c7a2dc762fa33009d24851bd2016 +Subproject commit 8c908d2d0a6d32abdedda2c52e90bd56ec76c24d diff --git a/3rdparty/curl/libcurl.vcxproj b/3rdparty/curl/libcurl.vcxproj index dae28be346..4db28782a3 100644 --- a/3rdparty/curl/libcurl.vcxproj +++ b/3rdparty/curl/libcurl.vcxproj @@ -79,12 +79,16 @@ + + + + @@ -106,6 +110,7 @@ + @@ -169,14 +174,13 @@ - + - @@ -184,10 +188,8 @@ - - @@ -204,6 +206,7 @@ + @@ -224,13 +227,11 @@ - - @@ -272,6 +273,7 @@ + @@ -280,6 +282,9 @@ + + + @@ -300,9 +305,7 @@ - - @@ -312,6 +315,7 @@ + @@ -352,7 +356,6 @@ - @@ -367,7 +370,7 @@ - + @@ -376,7 +379,6 @@ - @@ -384,7 +386,6 @@ - @@ -405,6 +406,7 @@ + @@ -418,12 +420,10 @@ - - diff --git a/3rdparty/curl/libcurl.vcxproj.filters b/3rdparty/curl/libcurl.vcxproj.filters index 17f760c54b..d38316e767 100644 --- a/3rdparty/curl/libcurl.vcxproj.filters +++ b/3rdparty/curl/libcurl.vcxproj.filters @@ -204,9 +204,6 @@ Source Files - - Source Files - Source Files @@ -222,9 +219,6 @@ Source Files - - Source Files - Source Files @@ -246,18 +240,12 @@ Source Files - - Source Files - Source Files Source Files - - Source Files - Source Files @@ -318,9 +306,6 @@ Source Files - - Source Files - Source Files @@ -333,9 +318,6 @@ Source Files - - Source Files - Source Files @@ -549,6 +531,27 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -653,9 +656,6 @@ Header Files - - Header Files - Header Files @@ -758,9 +758,6 @@ Header Files - - Header Files - Header Files @@ -791,9 +788,6 @@ Header Files - - Header Files - Header Files @@ -812,9 +806,6 @@ Header Files - - Header Files - Header Files @@ -836,9 +827,6 @@ Header Files - - Header Files - Header Files @@ -887,9 +875,6 @@ Header Files - - Header Files - Header Files @@ -899,9 +884,6 @@ Header Files - - Header Files - Header Files @@ -1103,9 +1085,6 @@ Header Files - - Header Files - Header Files @@ -1121,6 +1100,27 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index c3e304954a..d5515b5b8b 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d +Subproject commit d5515b5b8be3901aac04e5bd8bd5c89f287bcd33 diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index 683181b47c..5848e584a1 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd +Subproject commit 5848e584a1b606de26e3dbd1c7e4ecbc34f807a6 diff --git a/3rdparty/protobuf/CMakeLists.txt b/3rdparty/protobuf/CMakeLists.txt index 6c4b35e99d..9f7ead1347 100644 --- a/3rdparty/protobuf/CMakeLists.txt +++ b/3rdparty/protobuf/CMakeLists.txt @@ -20,10 +20,13 @@ else() option(protobuf_DISABLE_RTTI "Remove runtime type information in the binaries" OFF) option(protobuf_FORCE_FETCH_DEPENDENCIES "Force all dependencies to be downloaded from GitHub. Local installations will be ignored." OFF) option(protobuf_LOCAL_DEPENDENCIES_ONLY "Prevent downloading any dependencies from GitHub. If this option is set, the dependency must be available locally as an installed package." OFF) - if(VCPKG_TOOLCHAIN) + option(protobuf_BUILD_SHARED_LIBS "Build Shared Libraries" OFF) + option(protobuf_MSVC_STATIC_RUNTIME "Link static runtime libraries" OFF) + if(VCPKG_TOOLCHAIN) set(protobuf_BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) message(STATUS "VCPKG detected: Building Protobuf as a shared library") endif() + add_subdirectory(protobuf EXCLUDE_FROM_ALL) target_include_directories(3rdparty_protobuf SYSTEM INTERFACE protobuf/src) target_link_libraries(3rdparty_protobuf INTERFACE libprotobuf) diff --git a/3rdparty/qt6.cmake b/3rdparty/qt6.cmake index e15e0abdcb..969967fa40 100644 --- a/3rdparty/qt6.cmake +++ b/3rdparty/qt6.cmake @@ -6,15 +6,22 @@ find_package(Qt6 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia M if(WIN32) target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) else() - set(QT_NO_PRIVATE_MODULE_WARNING ON) - find_package(Qt6 ${QT_MIN_VER} COMPONENTS DBus Gui GuiPrivate) + find_package(Qt6 ${QT_MIN_VER} COMPONENTS DBus Gui) + if(Qt6_VERSION VERSION_GREATER_EQUAL "6.10.0") + set(QT_NO_PRIVATE_MODULE_WARNING ON) + find_package(Qt6 ${QT_MIN_VER} COMPONENTS GuiPrivate) + endif() if(Qt6DBus_FOUND) target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::DBus Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) target_compile_definitions(3rdparty_qt6 INTERFACE -DHAVE_QTDBUS) else() target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) endif() - target_link_libraries(3rdparty_qt6 INTERFACE Qt6::GuiPrivate) + if(Qt6_VERSION VERSION_GREATER_EQUAL "6.10.0") + target_link_libraries(3rdparty_qt6 INTERFACE Qt6::GuiPrivate) + else() + target_include_directories(3rdparty_qt6 INTERFACE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) + endif() endif() if(Qt6Widgets_FOUND) diff --git a/3rdparty/wolfssl/wolfssl b/3rdparty/wolfssl/wolfssl index b077c81eb6..922d04b356 160000 --- a/3rdparty/wolfssl/wolfssl +++ b/3rdparty/wolfssl/wolfssl @@ -1 +1 @@ -Subproject commit b077c81eb635392e694ccedbab8b644297ec0285 +Subproject commit 922d04b3568c6428a9fb905ddee3ef5a68db3108 diff --git a/3rdparty/yaml-cpp/yaml-cpp b/3rdparty/yaml-cpp/yaml-cpp index 05c44fcd18..51a5d623e3 160000 --- a/3rdparty/yaml-cpp/yaml-cpp +++ b/3rdparty/yaml-cpp/yaml-cpp @@ -1 +1 @@ -Subproject commit 05c44fcd18074836e21e1eda9fc02b3a4a1529b5 +Subproject commit 51a5d623e3fde1f58829a56ba910f1cb33596222 diff --git a/3rdparty/zlib/CMakeLists.txt b/3rdparty/zlib/CMakeLists.txt index 55d7353acf..47645d290c 100644 --- a/3rdparty/zlib/CMakeLists.txt +++ b/3rdparty/zlib/CMakeLists.txt @@ -6,7 +6,8 @@ if (USE_SYSTEM_ZLIB) target_link_libraries(3rdparty_zlib INTERFACE ZLIB::ZLIB) target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1) else() - option(ZLIB_BUILD_EXAMPLES "Enable Zlib Examples" OFF) + option(ZLIB_BUILD_TESTING "Enable Zlib Examples as tests" OFF) + option(ZLIB_BUILD_SHARED "Enable building zlib shared library" OFF) message(STATUS "RPCS3: Using builtin ZLIB") set(SKIP_INSTALL_ALL ON) add_subdirectory(zlib EXCLUDE_FROM_ALL) diff --git a/BUILDING.md b/BUILDING.md index 597621e810..c1774908fd 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -20,26 +20,26 @@ The following tools are required to build RPCS3 on Windows 10 or later: with standalone **CMake** tool. - [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -- [Qt 6.10.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) +- [Qt 6.11.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode. In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs: -- add and set the `QTDIR` environment variable, e.g. `\6.10.2\msvc2022_64\` +- add and set the `QTDIR` environment variable, e.g. `\6.11.0\msvc2022_64\` - or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022) **NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead. In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool): -- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.10.2\msvc2022_64\` +- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.11.0\msvc2022_64\` ### Linux These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager: - Clang 17+ or GCC 13+ - [CMake 3.28.0+](https://www.cmake.org/download/) -- [Qt 6.10.2](https://www.qt.io/download-qt-installer) +- [Qt 6.11.0](https://www.qt.io/download-qt-installer) - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) @@ -123,7 +123,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r ##### Configuring the Qt Plugin (if used) 1) go to `Extensions->Qt VS Tools->Qt Versions` -2) add the path to your Qt installation with compiler e.g. `\6.10.2\msvc2022_64`, version will fill in automatically +2) add the path to your Qt installation with compiler e.g. `\6.11.0\msvc2022_64`, version will fill in automatically 3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**) 4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50cf946fda..217c40f341 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ option(USE_LIBEVDEV "libevdev-based joystick support" ON) option(USE_DISCORD_RPC "Discord rich presence integration" OFF) option(USE_VULKAN "Vulkan render backend" ON) option(USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) -option(USE_SDL "Enables SDL input handler" OFF) +option(USE_SDL "Enables SDL input handler" ON) option(USE_SYSTEM_CUBEB "Prefer system cubeb instead of the builtin one" OFF) option(USE_SYSTEM_CURL "Prefer system Curl instead of the prebuild one" ON) option(USE_SYSTEM_FAUDIO "Prefer system FAudio instead of the builtin one" OFF) @@ -86,52 +86,7 @@ if(CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT MSVC) endif() if(MSVC) - option(USE_MSVC_STATIC_CRT "Use static MSVC C runtime" OFF) - - # TODO(cjj19970505@live.cn) - # DiscordRPC binary in 3rdparty is compiled /MT - # So theoretically we should enable DiscordRPC in Release and static CRT build - # since we might encounter some rumtime issues when more than one CRT version are presented. - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version - # Add other DiscordRPC binaries(compiled with /MTd, /MD, /MDd) or compile it from source may address this issue. - if(NOT IS_MULTI_CONFIG) - if(NOT(CMAKE_BUILD_TYPE MATCHES "Release" AND USE_MSVC_STATIC_CRT)) - set(USE_DISCORD_RPC OFF CACHE BOOL "Discord RPC is only available in Release and static CRT build." FORCE) - endif() - endif() - - if(USE_MSVC_STATIC_CRT) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - else() - # though doc ( https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html ) - # says if that property is not set then CMake uses the default value MultiThreaded$<$:Debug>DLL - # to select a MSVC runtime library. - # But yaml-cpp set /MT(d) if CMAKE_MSVC_RUNTIME_LIBRARY is undefined - # So we have to define it explicitly - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") - endif() - - # TODO(cjj19970505@live.cn) - # offical QT uses dynamic CRT. - # When building our lib with static CRT and debug build type - # and linking with Qt with dynamic CRT and debug build, - # error is encountered in runtime (which is expected). - # But building our lib with static CRT and release build type, - # and linking with Qt with dynamic CRT and release build seems to be working, - # which is the same config with VS solution. - # (though technically it might still have some hidden errors). - # So we allow static CRT in both relase and debug build, but prompt warning in debug build. - # For more info: - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version - # https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F - if(USE_MSVC_STATIC_CRT) - if(IS_MULTI_CONFIG OR CMAKE_BUILD_TYPE MATCHES "Debug") - message(AUTHOR_WARNING "Debug build currently can not work with static CRT.") - endif() - endif() - if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - add_compile_options(/MP) - endif() + add_compile_options("$<$:/MP>") endif() if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) @@ -144,7 +99,7 @@ if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") endif() if(MSVC) - add_compile_options(/wd4530 /utf-8) # C++ exception handler used, but unwind semantics are not enabled + add_compile_options("$<$:/wd4530;/utf-8>") # C++ exception handler used, but unwind semantics are not enabled endif() add_subdirectory(3rdparty) @@ -163,10 +118,6 @@ if (NOT FOUND_LTO EQUAL -1) message(FATAL_ERROR "RPCS3 doesn't support building with LTO, use -DDISABLE_LTO=TRUE to force-disable it") endif() -if(NOT WIN32) - add_compile_options(-pthread) -endif() - ## Look for Gamemode if its installed on Linux if(LINUX) ## User chooses whether to Enable GameMode features or not diff --git a/Utilities/Config.cpp b/Utilities/Config.cpp index cee928def7..bd0fe7a8b8 100644 --- a/Utilities/Config.cpp +++ b/Utilities/Config.cpp @@ -362,6 +362,27 @@ std::vector cfg::try_to_enum_list(decltype(&fmt_class_string:: return result; } +size_t cfg::try_to_enum_size(decltype(&fmt_class_string::format) func) +{ + size_t result = 0; + for (u64 i = 0;; i++) + { + std::string var; + func(var, i); + + std::string hex; + fmt_class_string::format(hex, i); + if (var == hex) + { + break; + } + + result++; + } + + return result; +} + void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs) { switch (rhs.get_type()) @@ -416,7 +437,7 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs) out << YAML::BeginMap; for (const auto& np : static_cast(rhs).get_map()) { - if (np.second == logs::level::notice) continue; + if (np.second == logs::level::_default) continue; out << YAML::Key << np.first; out << YAML::Value << fmt::format("%s", np.second); } diff --git a/Utilities/Config.h b/Utilities/Config.h index 35dcddc1a6..4c79cbf31c 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -30,6 +30,9 @@ namespace cfg // Internal hack std::vector try_to_enum_list(decltype(&fmt_class_string::format) func); + // Internal hack + size_t try_to_enum_size(decltype(&fmt_class_string::format) func); + // Config tree entry type. enum class type : unsigned { @@ -312,6 +315,11 @@ namespace cfg { return try_to_enum_list(&fmt_class_string::format); } + + size_t size() const + { + return try_to_enum_size(&fmt_class_string::format); + } }; // Signed 32/64-bit integer entry with custom Min/Max range. @@ -385,7 +393,7 @@ namespace cfg void set(const s64& value) { - ensure(value >= Min && value <= Max); + if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max); m_value = static_cast(value); } @@ -476,7 +484,7 @@ namespace cfg void set(const f64& value) { - ensure(value >= Min && value <= Max); + if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max); m_value = static_cast(value); } @@ -563,7 +571,7 @@ namespace cfg void set(const u64& value) { - ensure(value >= Min && value <= Max); + if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max); m_value = static_cast(value); } diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 490605c792..aff4537dea 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -166,6 +166,7 @@ static fs::error to_error(int e) case ENOTEMPTY: return fs::error::notempty; case EROFS: return fs::error::readonly; case EISDIR: return fs::error::isdir; + case ENOTDIR: return fs::error::notdir; case ENOSPC: return fs::error::nospace; case EXDEV: return fs::error::xdev; default: return fs::error::unknown; @@ -2833,6 +2834,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case fs::error::notempty: return "Not empty"; case fs::error::readonly: return "Read only"; case fs::error::isdir: return "Is a directory"; + case fs::error::notdir: return "Not a directory"; case fs::error::toolong: return "Path too long"; case fs::error::nospace: return "Not enough space on the device"; case fs::error::xdev: return "Device mismatch"; diff --git a/Utilities/File.h b/Utilities/File.h index dd2db42a46..3d332dd0be 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -683,6 +683,7 @@ namespace fs notempty, readonly, isdir, + notdir, toolong, nospace, xdev, diff --git a/Utilities/JIT.h b/Utilities/JIT.h index 6dfa9e7cd0..86fc72ed55 100644 --- a/Utilities/JIT.h +++ b/Utilities/JIT.h @@ -493,6 +493,10 @@ inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* return reinterpret_cast(uptr(result)); } +#if defined(__INTELLISENSE__) && !defined(LLVM_AVAILABLE) +#define LLVM_AVAILABLE +#endif + #ifdef LLVM_AVAILABLE namespace llvm diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index 26fb1a09da..62f8365339 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -16,12 +16,12 @@ #include #endif -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4996) -#elif defined(__clang__) +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) #else #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 08dce12315..b22c1aeb52 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -14,6 +14,11 @@ #include "Emu/CPU/Backends/AArch64/AArch64Signal.h" #endif +#ifdef __cpp_lib_stacktrace +#include "rpcs3_version.h" +#include +#endif + #ifdef _WIN32 #include #include @@ -102,7 +107,7 @@ thread_local u64 g_tls_fault_rsx = 0; thread_local u64 g_tls_fault_spu = 0; thread_local u64 g_tls_wait_time = 0; thread_local u64 g_tls_wait_fail = 0; -thread_local bool g_tls_access_violation_recovered = false; +thread_local u64 g_tls_access_violation_recovered = umax; extern thread_local std::string(*g_tls_log_prefix)(); namespace stx @@ -1264,7 +1269,7 @@ namespace rsx extern std::function g_access_violation_handler; } -bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noexcept +bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t* context) noexcept { g_tls_fault_all++; @@ -1300,7 +1305,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe } } spu_protection{cpu}; - if (addr < RAW_SPU_BASE_ADDR && vm::check_addr(addr) && rsx::g_access_violation_handler) + if (!is_exec && addr < RAW_SPU_BASE_ADDR && vm::check_addr(addr) && rsx::g_access_violation_handler) { bool state_changed = false; @@ -1366,7 +1371,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe { auto thread = idm::get_unlocked>(spu_thread::find_raw_spu((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET)); - if (!thread) + if (!thread || is_exec) { break; } @@ -1498,7 +1503,9 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe static_cast(context); #endif /* ARCH_ */ - if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable)) + const auto required_page_perms = (is_writing ? vm::page_writable : vm::page_readable) + (is_exec ? vm::page_executable : 0); + + if (vm::check_addr(addr, required_page_perms)) { return true; } @@ -1506,9 +1513,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe // Hack: allocate memory in case the emulator is stopping const auto hack_alloc = [&]() { - g_tls_access_violation_recovered = true; - - if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable)) + if (vm::check_addr(addr, required_page_perms)) { return true; } @@ -1520,17 +1525,45 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe return false; } + extern void ppu_register_range(u32 addr, u32 size); + + bool reprotected = false; + if (vm::writer_lock mlock; area->flags & vm::preallocated || vm::check_addr(addr, 0)) { // For allocated memory with protection lower than required (such as protection::no or read-only while writing to it) utils::memory_protect(vm::base(addr & -0x1000), 0x1000, utils::protection::rw); + reprotected = true; + } + + if (reprotected) + { + if (is_exec && !vm::check_addr(addr, vm::page_executable)) + { + ppu_register_range(addr & -0x10000, 0x10000); + } + + g_tls_access_violation_recovered = addr; return true; } - return area->falloc(addr & -0x10000, 0x10000) || vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable); + const bool allocated = area->falloc(addr & -0x10000, 0x10000); + + if (allocated) + { + if (is_exec && !vm::check_addr(addr, vm::page_executable)) + { + ppu_register_range(addr & -0x10000, 0x10000); + } + + g_tls_access_violation_recovered = addr; + return true; + } + + return false; }; - if (cpu && (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu)) + if (cpu && (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu) && !is_exec) { vm::temporary_unlock(*cpu); u32 pf_port_id = 0; @@ -1673,7 +1706,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe if (cpu->get_class() == thread_class::spu) { - if (!g_tls_access_violation_recovered) + if (g_tls_access_violation_recovered != addr) { vm_log.notice("\n%s", dump_useful_thread_info()); vm_log.always()("[%s] Access violation %s location 0x%x (%s)", cpu->get_name(), is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); @@ -1709,10 +1742,10 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe // Note: a thread may access violate more than once after hack_alloc recovery // Do not log any further access violations in this case. - if (!g_tls_access_violation_recovered) + if (g_tls_access_violation_recovered != addr) { vm_log.notice("\n%s", dump_useful_thread_info()); - vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (cpu && cpu->get_class() == thread_class::ppu && cpu->get_pc() == addr ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); + vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (is_exec ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); } while (Emu.IsPausedOrReady()) @@ -1761,8 +1794,13 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe } } - if (Emu.IsStopped() && !hack_alloc()) + if (Emu.IsStopped()) { + while (!hack_alloc()) + { + thread_ctrl::wait_for(1000); + } + return false; } @@ -1801,6 +1839,7 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp) noexcept if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && !is_executing) { u32 addr = 0; + bool is_exec = false; if (auto [addr0, ok] = vm::try_get_addr(ptr); ok) { @@ -1808,14 +1847,21 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp) noexcept } else if (const usz exec64 = (ptr - vm::g_exec_addr) / 2; exec64 <= u32{umax}) { + is_exec = true; addr = static_cast(exec64); } - else + else if (const usz exec64 = (ptr - vm::g_exec_addr - vm::g_exec_addr_seg_offset); exec64 <= u32{umax}) { + is_exec = true; + addr = static_cast(exec64); + } + else + { + std::this_thread::sleep_for(1ms); return EXCEPTION_CONTINUE_SEARCH; } - if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, pExp->ContextRecord)) + if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, is_exec, pExp->ContextRecord)) { return EXCEPTION_CONTINUE_EXECUTION; } @@ -2022,12 +2068,13 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept #endif const u64 exec64 = (reinterpret_cast(info->si_addr) - reinterpret_cast(vm::g_exec_addr)) / 2; + const u64 exec64_2 = (reinterpret_cast(info->si_addr) - reinterpret_cast(vm::g_exec_addr)) - vm::g_exec_addr_seg_offset; const auto cause = is_executing ? "executing" : is_writing ? "writing" : "reading"; if (auto [addr, ok] = vm::try_get_addr(info->si_addr); ok && !is_executing) { // Try to process access violation - if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, context)) + if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, false, context)) { return; } @@ -2035,7 +2082,14 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept if (exec64 < 0x100000000ull && !is_executing) { - if (thread_ctrl::get_current() && handle_access_violation(static_cast(exec64), is_writing, context)) + if (thread_ctrl::get_current() && handle_access_violation(static_cast(exec64), is_writing, true, context)) + { + return; + } + } + else if (exec64_2 < 0x100000000ull && !is_executing) + { + if (thread_ctrl::get_current() && handle_access_violation(static_cast(exec64_2), is_writing, true, context)) { return; } @@ -2354,7 +2408,7 @@ thread_base::native_entry thread_base::finalize(u64 _self) noexcept g_tls_fault_spu = 0; g_tls_wait_time = 0; g_tls_wait_fail = 0; - g_tls_access_violation_recovered = false; + g_tls_access_violation_recovered = umax; g_tls_log_prefix = []() -> std::string { return {}; }; @@ -2800,6 +2854,16 @@ void thread_base::exec() [[noreturn]] void thread_ctrl::emergency_exit(std::string_view reason) { + // Print stacktrace +#ifdef __cpp_lib_stacktrace + if (rpcs3::is_local_build()) + { + std::ostringstream oss; + oss << std::stacktrace::current(); + sys_log.notice("StackTrace\n\n%s\n", oss.str()); + } +#endif + if (const std::string info = dump_useful_thread_info(); !info.empty()) { sys_log.notice("\n%s", info); @@ -2815,6 +2879,16 @@ void thread_base::exec() } } + if (auto [total, current] = utils::get_memory_usage(); total - current <= 256 * 1024 * 1024) + { + if (reason_buf.empty()) + { + reason_buf = std::string{reason}; + } + + fmt::append(reason_buf, " (Possible RAM deficiency: free RAM: %dMB)", (total - current) / (1024 * 1024)); + } + if (!reason_buf.empty()) { reason = reason_buf; diff --git a/bin/GuiConfigs/Classic (Bright).qss b/bin/GuiConfigs/Classic (Bright).qss index 143e42df94..f961e63e00 100644 --- a/bin/GuiConfigs/Classic (Bright).qss +++ b/bin/GuiConfigs/Classic (Bright).qss @@ -37,10 +37,6 @@ QSlider#sizeSlider::handle:horizontal { QLabel#toolbar_icon_color { color: rgba(64,64,64,255); } -/* thumbnail icon color stylesheet */ -QLabel#thumbnail_icon_color { - color: rgba(0,100,231,255); -} /* gamelist icon color stylesheet */ QLabel#gamelist_icon_background_color { color: rgba(209,209,209,255); diff --git a/bin/GuiConfigs/Darker Style by TheMitoSan.qss b/bin/GuiConfigs/Darker Style by TheMitoSan.qss index 47270a211e..8797ffd3c9 100644 --- a/bin/GuiConfigs/Darker Style by TheMitoSan.qss +++ b/bin/GuiConfigs/Darker Style by TheMitoSan.qss @@ -237,11 +237,6 @@ QLabel#gamelist_icon_background_color { color: transparent; } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #262626; -} - /* Set Log colors */ QPlainTextEdit#log_frame { background-color: #000; /* Black */ diff --git a/bin/GuiConfigs/Envy.qss b/bin/GuiConfigs/Envy.qss index a78ba04f58..3e2e1ddc35 100644 --- a/bin/GuiConfigs/Envy.qss +++ b/bin/GuiConfigs/Envy.qss @@ -573,11 +573,6 @@ QLabel#gamelist_icon_background_color { color: transparent; } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #23262d; -} - /* Log colors */ QPlainTextEdit#log_frame { background-color: #23262d; diff --git a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss index 54c667213b..2cd81267f1 100644 --- a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss +++ b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss @@ -265,11 +265,6 @@ QLabel#gamelist_icon_background_color { color: transparent; } -/* Set Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #444444; -} - /* Memory Viewer */ QLabel#memory_viewer_address_panel { color: #00cbff; /* Font Color: Blue */ diff --git a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss index 410db682f7..96b84a9196 100644 --- a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss +++ b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss @@ -244,11 +244,6 @@ QLabel#gamelist_icon_background_color { color: transparent; } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #262626; -} - /* Set Log colors */ QPlainTextEdit#log_frame { background-color: #181d24; /* Black */ diff --git a/bin/GuiConfigs/Nekotekina by GooseWing.qss b/bin/GuiConfigs/Nekotekina by GooseWing.qss index 435d550755..93fadcdd68 100755 --- a/bin/GuiConfigs/Nekotekina by GooseWing.qss +++ b/bin/GuiConfigs/Nekotekina by GooseWing.qss @@ -397,12 +397,6 @@ QLabel#gamelist_icon_background_color { } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #ffd785; -} - - QLabel#log_level_always { color: #00ffff; /* Cyan */ } diff --git a/bin/GuiConfigs/Skyline (Nightfall).qss b/bin/GuiConfigs/Skyline (Nightfall).qss index 625a6a28b6..4729a59173 100644 --- a/bin/GuiConfigs/Skyline (Nightfall).qss +++ b/bin/GuiConfigs/Skyline (Nightfall).qss @@ -656,11 +656,6 @@ QLabel#color_button { background: transparent; } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #370048; -} - /* Debugger colors */ QLabel#debugger_frame_breakpoint { color: #000; /* Font Color: Black */ diff --git a/bin/GuiConfigs/Skyline.qss b/bin/GuiConfigs/Skyline.qss index ef3c7c6857..1004dc0016 100644 --- a/bin/GuiConfigs/Skyline.qss +++ b/bin/GuiConfigs/Skyline.qss @@ -664,11 +664,6 @@ QLabel#color_button { background: transparent; } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #8500ae; -} - /* Debugger colors */ QLabel#debugger_frame_breakpoint { color: #000; /* Font Color: Black */ diff --git a/bin/GuiConfigs/YoRHa by Ani.qss b/bin/GuiConfigs/YoRHa by Ani.qss index c772f25196..51f09897b1 100644 --- a/bin/GuiConfigs/YoRHa by Ani.qss +++ b/bin/GuiConfigs/YoRHa by Ani.qss @@ -379,11 +379,6 @@ QLabel#gamelist_icon_background_color { color: transparent; } -/* Set Windows Taskbar Thumbnail colors */ -QLabel#thumbnail_icon_color { - color: #4d4940; -} - QLabel#log_level_always { color: #00ffff; /* Cyan */ } diff --git a/bin/Icons/ui/home/256/bug-solid.png b/bin/Icons/ui/home/256/bug-solid.png new file mode 100644 index 0000000000..2c88980603 Binary files /dev/null and b/bin/Icons/ui/home/256/bug-solid.png differ diff --git a/bin/Icons/ui/home/256/circle-left-solid.png b/bin/Icons/ui/home/256/circle-left-solid.png new file mode 100644 index 0000000000..f6b2e58768 Binary files /dev/null and b/bin/Icons/ui/home/256/circle-left-solid.png differ diff --git a/bin/Icons/ui/home/256/display-solid.png b/bin/Icons/ui/home/256/display-solid.png new file mode 100644 index 0000000000..84cc043b07 Binary files /dev/null and b/bin/Icons/ui/home/256/display-solid.png differ diff --git a/bin/Icons/ui/home/256/floppy-disk-solid.png b/bin/Icons/ui/home/256/floppy-disk-solid.png new file mode 100644 index 0000000000..3701361569 Binary files /dev/null and b/bin/Icons/ui/home/256/floppy-disk-solid.png differ diff --git a/bin/Icons/ui/home/256/gamepad-solid.png b/bin/Icons/ui/home/256/gamepad-solid.png new file mode 100644 index 0000000000..fe08f1bc58 Binary files /dev/null and b/bin/Icons/ui/home/256/gamepad-solid.png differ diff --git a/bin/Icons/ui/home/256/gauge-solid.png b/bin/Icons/ui/home/256/gauge-solid.png new file mode 100644 index 0000000000..5b6d731a9d Binary files /dev/null and b/bin/Icons/ui/home/256/gauge-solid.png differ diff --git a/bin/Icons/ui/home/256/headphones-solid.png b/bin/Icons/ui/home/256/headphones-solid.png new file mode 100644 index 0000000000..feb015695d Binary files /dev/null and b/bin/Icons/ui/home/256/headphones-solid.png differ diff --git a/bin/Icons/ui/home/256/home.png b/bin/Icons/ui/home/256/home.png new file mode 100644 index 0000000000..622984c913 Binary files /dev/null and b/bin/Icons/ui/home/256/home.png differ diff --git a/bin/Icons/ui/home/256/maximize-solid.png b/bin/Icons/ui/home/256/maximize-solid.png new file mode 100644 index 0000000000..65eaa43308 Binary files /dev/null and b/bin/Icons/ui/home/256/maximize-solid.png differ diff --git a/bin/Icons/ui/home/256/play-button-arrowhead.png b/bin/Icons/ui/home/256/play-button-arrowhead.png new file mode 100644 index 0000000000..37e9cefe93 Binary files /dev/null and b/bin/Icons/ui/home/256/play-button-arrowhead.png differ diff --git a/bin/Icons/ui/home/256/power-off-solid.png b/bin/Icons/ui/home/256/power-off-solid.png new file mode 100644 index 0000000000..cf0ab44839 Binary files /dev/null and b/bin/Icons/ui/home/256/power-off-solid.png differ diff --git a/bin/Icons/ui/home/256/rotate-left-solid.png b/bin/Icons/ui/home/256/rotate-left-solid.png new file mode 100644 index 0000000000..5eaf29edba Binary files /dev/null and b/bin/Icons/ui/home/256/rotate-left-solid.png differ diff --git a/bin/Icons/ui/home/256/screenshot.png b/bin/Icons/ui/home/256/screenshot.png new file mode 100644 index 0000000000..091e2bbc2e Binary files /dev/null and b/bin/Icons/ui/home/256/screenshot.png differ diff --git a/bin/Icons/ui/home/256/settings.png b/bin/Icons/ui/home/256/settings.png new file mode 100644 index 0000000000..d682b9b27c Binary files /dev/null and b/bin/Icons/ui/home/256/settings.png differ diff --git a/bin/Icons/ui/home/256/sliders-solid.png b/bin/Icons/ui/home/256/sliders-solid.png new file mode 100644 index 0000000000..cfb030721e Binary files /dev/null and b/bin/Icons/ui/home/256/sliders-solid.png differ diff --git a/bin/Icons/ui/home/256/trophy-solid.png b/bin/Icons/ui/home/256/trophy-solid.png new file mode 100644 index 0000000000..1bd98a9ac9 Binary files /dev/null and b/bin/Icons/ui/home/256/trophy-solid.png differ diff --git a/bin/Icons/ui/home/256/user-group-solid.png b/bin/Icons/ui/home/256/user-group-solid.png new file mode 100644 index 0000000000..a154fa5ba9 Binary files /dev/null and b/bin/Icons/ui/home/256/user-group-solid.png differ diff --git a/bin/Icons/ui/home/256/video-camera.png b/bin/Icons/ui/home/256/video-camera.png new file mode 100644 index 0000000000..9f5460fba6 Binary files /dev/null and b/bin/Icons/ui/home/256/video-camera.png differ diff --git a/bin/Icons/ui/home/32/bug-solid.png b/bin/Icons/ui/home/32/bug-solid.png new file mode 100644 index 0000000000..401fa0ea27 Binary files /dev/null and b/bin/Icons/ui/home/32/bug-solid.png differ diff --git a/bin/Icons/ui/home/32/circle-left-solid.png b/bin/Icons/ui/home/32/circle-left-solid.png new file mode 100644 index 0000000000..1acf201d63 Binary files /dev/null and b/bin/Icons/ui/home/32/circle-left-solid.png differ diff --git a/bin/Icons/ui/home/32/display-solid.png b/bin/Icons/ui/home/32/display-solid.png new file mode 100644 index 0000000000..8a48c89494 Binary files /dev/null and b/bin/Icons/ui/home/32/display-solid.png differ diff --git a/bin/Icons/ui/home/32/floppy-disk-solid.png b/bin/Icons/ui/home/32/floppy-disk-solid.png new file mode 100644 index 0000000000..836c6523c3 Binary files /dev/null and b/bin/Icons/ui/home/32/floppy-disk-solid.png differ diff --git a/bin/Icons/ui/home/32/gamepad-solid.png b/bin/Icons/ui/home/32/gamepad-solid.png new file mode 100644 index 0000000000..b783fee792 Binary files /dev/null and b/bin/Icons/ui/home/32/gamepad-solid.png differ diff --git a/bin/Icons/ui/home/32/gauge-solid.png b/bin/Icons/ui/home/32/gauge-solid.png new file mode 100644 index 0000000000..9853811ae2 Binary files /dev/null and b/bin/Icons/ui/home/32/gauge-solid.png differ diff --git a/bin/Icons/ui/home/32/headphones-solid.png b/bin/Icons/ui/home/32/headphones-solid.png new file mode 100644 index 0000000000..e2cfabb07c Binary files /dev/null and b/bin/Icons/ui/home/32/headphones-solid.png differ diff --git a/bin/Icons/ui/home/32/home.png b/bin/Icons/ui/home/32/home.png new file mode 100644 index 0000000000..2ec05eab24 Binary files /dev/null and b/bin/Icons/ui/home/32/home.png differ diff --git a/bin/Icons/ui/home/32/maximize-solid.png b/bin/Icons/ui/home/32/maximize-solid.png new file mode 100644 index 0000000000..7a0a9e4fb6 Binary files /dev/null and b/bin/Icons/ui/home/32/maximize-solid.png differ diff --git a/bin/Icons/ui/home/32/play-button-arrowhead.png b/bin/Icons/ui/home/32/play-button-arrowhead.png new file mode 100644 index 0000000000..6ce5505f4d Binary files /dev/null and b/bin/Icons/ui/home/32/play-button-arrowhead.png differ diff --git a/bin/Icons/ui/home/32/power-off-solid.png b/bin/Icons/ui/home/32/power-off-solid.png new file mode 100644 index 0000000000..21ebad8e69 Binary files /dev/null and b/bin/Icons/ui/home/32/power-off-solid.png differ diff --git a/bin/Icons/ui/home/32/rotate-left-solid.png b/bin/Icons/ui/home/32/rotate-left-solid.png new file mode 100644 index 0000000000..b466d28a8e Binary files /dev/null and b/bin/Icons/ui/home/32/rotate-left-solid.png differ diff --git a/bin/Icons/ui/home/32/screenshot.png b/bin/Icons/ui/home/32/screenshot.png new file mode 100644 index 0000000000..97255279b4 Binary files /dev/null and b/bin/Icons/ui/home/32/screenshot.png differ diff --git a/bin/Icons/ui/home/32/settings.png b/bin/Icons/ui/home/32/settings.png new file mode 100644 index 0000000000..eeaeceef7d Binary files /dev/null and b/bin/Icons/ui/home/32/settings.png differ diff --git a/bin/Icons/ui/home/32/sliders-solid.png b/bin/Icons/ui/home/32/sliders-solid.png new file mode 100644 index 0000000000..ca4c75df23 Binary files /dev/null and b/bin/Icons/ui/home/32/sliders-solid.png differ diff --git a/bin/Icons/ui/home/32/trophy-solid.png b/bin/Icons/ui/home/32/trophy-solid.png new file mode 100644 index 0000000000..9d94e14b97 Binary files /dev/null and b/bin/Icons/ui/home/32/trophy-solid.png differ diff --git a/bin/Icons/ui/home/32/user-group-solid.png b/bin/Icons/ui/home/32/user-group-solid.png new file mode 100644 index 0000000000..114c45599d Binary files /dev/null and b/bin/Icons/ui/home/32/user-group-solid.png differ diff --git a/bin/Icons/ui/home/32/video-camera.png b/bin/Icons/ui/home/32/video-camera.png new file mode 100644 index 0000000000..d8fa8dfaaf Binary files /dev/null and b/bin/Icons/ui/home/32/video-camera.png differ diff --git a/bin/Icons/ui/loading.png b/bin/Icons/ui/loading.png new file mode 100644 index 0000000000..33288035c7 Binary files /dev/null and b/bin/Icons/ui/loading.png differ diff --git a/buildfiles/cmake/ConfigureCompiler.cmake b/buildfiles/cmake/ConfigureCompiler.cmake index 1685e4d13e..a5fe614029 100644 --- a/buildfiles/cmake/ConfigureCompiler.cmake +++ b/buildfiles/cmake/ConfigureCompiler.cmake @@ -107,11 +107,6 @@ else() # This hides our LLVM from mesa's LLVM, otherwise we get some unresolvable conflicts. add_link_options(-Wl,--exclude-libs,ALL) elseif(WIN32) - add_compile_definitions(__STDC_FORMAT_MACROS=1) - - # Workaround for mingw64 (MSYS2) - add_link_options(-Wl,--allow-multiple-definition) - # Increase stack limit to 8 MB add_link_options(-Wl,--stack -Wl,8388608) endif() diff --git a/buildfiles/cmake/FindWolfSSL.cmake b/buildfiles/cmake/FindWolfSSL.cmake index d2e30be60b..35f316837c 100644 --- a/buildfiles/cmake/FindWolfSSL.cmake +++ b/buildfiles/cmake/FindWolfSSL.cmake @@ -1,4 +1,3 @@ -set(WOLFSSL_LIBRARY ON) -set(WOLFSSL_INCLUDE_DIR ON) -set(WOLFSSL_LIBRARIES wolfssl) +set(WOLFSSL_LIBRARY wolfssl) +set(WOLFSSL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/wolfssl) set(WOLFSSL_FOUND TRUE) diff --git a/buildfiles/cmake/FindZLIB.cmake b/buildfiles/cmake/FindZLIB.cmake index 0a29abafa9..ff5869a5f7 100644 --- a/buildfiles/cmake/FindZLIB.cmake +++ b/buildfiles/cmake/FindZLIB.cmake @@ -3,9 +3,9 @@ if(USE_SYSTEM_ZLIB) find_package(ZLIB) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) else() - add_library(ZLIB::ZLIB INTERFACE IMPORTED) + add_library(ZLIB::ZLIB STATIC IMPORTED) set_target_properties(ZLIB::ZLIB PROPERTIES - INTERFACE_LINK_LIBRARIES zlibstatic + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/libzlibstatic.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/zlib/zlib;${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib") set(ZLIB_FOUND TRUE) endif() diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 5ab98ab419..e5df95ffd5 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -87,12 +87,6 @@ if (NOT ANDROID) message(FATAL_ERROR "RPCS3 requires either X11 or Wayland (or both) for Vulkan.") endif() - if(UNIX) - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - find_package(Threads REQUIRED) - target_link_libraries(rpcs3_lib PRIVATE Threads::Threads) - endif() - if(WIN32) target_link_libraries(rpcs3_lib PRIVATE ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi) else() @@ -200,6 +194,7 @@ if(BUILD_RPCS3_TESTS) tests/test_address_range.cpp tests/test_rsx_cfg.cpp tests/test_rsx_fp_asm.cpp + tests/test_dmux_pamf.cpp ) target_link_libraries(rpcs3_test @@ -207,6 +202,7 @@ if(BUILD_RPCS3_TESTS) rpcs3_lib rpcs3_emu GTest::gtest + GTest::gmock ) target_include_directories(rpcs3_test diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 2f9678263f..3e465b1b62 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -488,6 +488,7 @@ target_sources(rpcs3_emu PRIVATE RSX/NV47/HW/nv406e.cpp RSX/NV47/HW/nv4097.cpp RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp + RSX/Overlays/HomeMenu/overlay_home_icons.cpp RSX/Overlays/HomeMenu/overlay_home_menu.cpp RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp @@ -501,6 +502,8 @@ target_sources(rpcs3_emu PRIVATE RSX/Overlays/overlays.cpp RSX/Overlays/overlay_animated_icon.cpp RSX/Overlays/overlay_animation.cpp + RSX/Overlays/overlay_audio.cpp + RSX/Overlays/overlay_checkbox.cpp RSX/Overlays/overlay_compile_notification.cpp RSX/Overlays/overlay_controls.cpp RSX/Overlays/overlay_cursor.cpp @@ -517,6 +520,9 @@ target_sources(rpcs3_emu PRIVATE RSX/Overlays/overlay_perf_metrics.cpp RSX/Overlays/overlay_progress_bar.cpp RSX/Overlays/overlay_save_dialog.cpp + RSX/Overlays/overlay_select.cpp + RSX/Overlays/overlay_slider.cpp + RSX/Overlays/overlay_tabs.cpp RSX/Overlays/overlay_trophy_notification.cpp RSX/Overlays/overlay_user_list_dialog.cpp RSX/Overlays/overlay_utils.cpp @@ -640,6 +646,9 @@ if(TARGET 3rdparty_vulkan) endif() endif() +if(NOT WIN32) + set(THREADS_PREFER_PTHREAD_FLAG ON) +endif() find_package(Threads REQUIRED) target_link_libraries(rpcs3_emu diff --git a/rpcs3/Emu/CPU/Backends/AArch64/AArch64Common.h b/rpcs3/Emu/CPU/Backends/AArch64/AArch64Common.h index dff06dfb81..2ce4fa68b3 100644 --- a/rpcs3/Emu/CPU/Backends/AArch64/AArch64Common.h +++ b/rpcs3/Emu/CPU/Backends/AArch64/AArch64Common.h @@ -20,19 +20,19 @@ namespace aarch64 sp }; - static const char* gpr_names[] = + [[maybe_unused]] static const char* gpr_names[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30" }; - static const char* spr_names[] = + [[maybe_unused]] static const char* spr_names[] = { "xzr", "pc", "sp" }; - static const char* spr_asm_names[] = + [[maybe_unused]] static const char* spr_asm_names[] = { "xzr", ".", "sp" }; diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index e37e6ed0da..4bd5fc9157 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -888,6 +888,14 @@ bool cpu_thread::check_state() noexcept store = true; } + if (flags & cpu_flag::req_exit) + { + // A request for the thread to quit has been made + flags -= cpu_flag::req_exit; + flags += cpu_flag::exit; + store = true; + } + // Can't process dbg_step if we only paused temporarily if (cpu_can_stop && flags & cpu_flag::dbg_step) { @@ -1157,13 +1165,13 @@ void cpu_thread::notify() cpu_thread& cpu_thread::operator=(thread_state) { - if (state & cpu_flag::exit) + if (state & (cpu_flag::exit + cpu_flag::req_exit)) { // Must be notified elsewhere or self-raised return *this; } - const auto old = state.fetch_add(cpu_flag::exit); + const auto old = state.fetch_add(cpu_flag::req_exit); if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit)) { @@ -1322,8 +1330,9 @@ extern std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr< void cpu_thread::dump_all(std::string& ret) const { std::any func_data; + std::any misc_data; - ret += dump_misc(); + dump_misc(ret, misc_data); ret += '\n'; dump_regs(ret, func_data); ret += '\n'; @@ -1371,9 +1380,9 @@ std::vector> cpu_thread::dump_callstack_list() const return {}; } -std::string cpu_thread::dump_misc() const +void cpu_thread::dump_misc(std::string& ret, std::any& /*custom_data*/) const { - return fmt::format("Type: %s; State: %s\n", get_class() == thread_class::ppu ? "PPU" : get_class() == thread_class::spu ? "SPU" : "RSX", state.load()); + fmt::append(ret, "%s[0x%x]; State: %s\n", get_class() == thread_class::ppu ? "PPU" : get_class() == thread_class::spu ? "SPU" : "RSX", id, state.load()); } bool cpu_thread::suspend_work::push(cpu_thread* _this) noexcept diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 5e3484f7f5..e723fd2d4b 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -29,6 +29,7 @@ enum class cpu_flag : u32 yield, // Thread is being requested to yield its execution time if it's running preempt, // Thread is being requested to preempt the execution of all CPU threads + req_exit, // Request the thread to exit dbg_global_pause, // Emulation paused dbg_pause, // Thread paused dbg_step, // Thread forced to pause after one step (one instruction, etc) @@ -39,7 +40,7 @@ enum class cpu_flag : u32 // Test stopped state constexpr bool is_stopped(bs_t state) { - return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again)); + return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again + cpu_flag::req_exit)); } // Test paused state @@ -176,7 +177,7 @@ public: virtual std::vector> dump_callstack_list() const; // Get CPU dump of misc information - virtual std::string dump_misc() const; + virtual void dump_misc(std::string& ret, std::any& /*custom_data*/) const; // Thread entry point function virtual void cpu_task() = 0; diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index f799e4b6be..6bd7924ea5 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -201,9 +201,16 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin m_use_vnni = true; m_use_gfni = true; } + +#ifdef ARCH_ARM64 + if (utils::has_dotprod()) + { + m_use_dotprod = true; + } +#endif } -llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const +llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc) const { uint s1 = type->getScalarSizeInBits(); uint s2 = val->getType()->getScalarSizeInBits(); @@ -215,15 +222,69 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const if (s1 != s2) { - fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)", s1, s2); + fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)\nCalled from: %s", s1, s2, src_loc); } - if (const auto c1 = llvm::dyn_cast(val)) + if (val->getType() == type) + { + return val; + } + + llvm::CastInst* i; + llvm::Value* source_val = val; + + // Try to reuse older bitcasts + while ((i = llvm::dyn_cast_or_null(source_val)) && i->getOpcode() == llvm::Instruction::BitCast) + { + source_val = i->getOperand(0); + + if (source_val->getType() == type) + { + return source_val; + } + } + + for (auto it = source_val->use_begin(); it != source_val->use_end(); ++it) + { + llvm::Value* it_val = *it; + + if (!it_val) + { + continue; + } + + llvm::CastInst* bci = llvm::dyn_cast_or_null(it_val); + + // Walk through bitcasts + while (bci && bci->getOpcode() == llvm::Instruction::BitCast) + { + if (bci->getParent() != m_ir->GetInsertBlock()) + { + break; + } + + if (bci->getType() == type) + { + return bci; + } + + if (bci->use_begin() == bci->use_end()) + { + break; + } + + bci = llvm::dyn_cast_or_null(*bci->use_begin()); + } + } + + // Do bitcast on the source + + if (const auto c1 = llvm::dyn_cast(source_val)) { return ensure(llvm::ConstantFoldCastOperand(llvm::Instruction::BitCast, c1, type, m_module->getDataLayout())); } - return m_ir->CreateBitCast(val, type); + return m_ir->CreateBitCast(source_val, type); } template <> diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 99ddafde0a..ab2aed8156 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -43,6 +43,7 @@ #include #include +#include // Helper function llvm::Value* peek_through_bitcasts(llvm::Value*); @@ -567,6 +568,32 @@ struct llvm_placeholder_t } }; +template >> +struct llvm_place_stealer_t +{ + // TODO: placeholder extracting actual constant values (u64, f64, vector, etc) + + using type = T; + + static constexpr bool is_ok = true; + + llvm::Value* eval(llvm::IRBuilder<>*) const + { + return nullptr; + } + + std::tuple<> match(llvm::Value*& value, llvm::Module*) const + { + if (value && value->getType() == llvm_value_t::get_type(value->getContext())) + { + return {}; + } + + value = nullptr; + return {}; + } +}; + template struct llvm_const_int { @@ -3090,6 +3117,9 @@ protected: // For now, setting this flag will speed up SPU verification // but I will remove this later with explicit parralelism - Whatcookie bool m_use_avx = true; + + // ARMv8 SDOT/UDOT + bool m_use_dotprod = false; #else // Allow FMA bool m_use_fma = false; @@ -3210,7 +3240,7 @@ public: } // Bitcast with immediate constant folding - llvm::Value* bitcast(llvm::Value* val, llvm::Type* type) const; + llvm::Value* bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc = std::source_location::current()) const; template llvm::Value* bitcast(llvm::Value* val) @@ -3224,6 +3254,12 @@ public: return {}; } + template + static llvm_place_stealer_t match_stealer() + { + return {}; + } + template requires requires { typename llvm_common_t; } static auto match_expr(llvm::Value* v, llvm::Module* _m, T&& expr) @@ -3647,10 +3683,59 @@ public: const auto data0 = a.eval(m_ir); const auto data1 = b.eval(m_ir); const auto data2 = c.eval(m_ir); + +#if LLVM_VERSION_MAJOR >= 22 + // LLVM 22+ changed the intrinsic signature from v4i32 to v16i8 for operands 2 and 3 + result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128), + {data0, m_ir->CreateBitCast(data1, get_type()), m_ir->CreateBitCast(data2, get_type())}); +#else result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128), {data0, data1, data2}); +#endif return result; } +#ifdef ARCH_ARM64 +template + value_t udot(T1 a, T2 b, T3 c) + { + value_t result; + + const auto data0 = a.eval(m_ir); + const auto data1 = b.eval(m_ir); + const auto data2 = c.eval(m_ir); + + result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_udot), {data0, data1, data2}); + return result; + } + + template + value_t sdot(T1 a, T2 b, T3 c) + { + value_t result; + + const auto data0 = a.eval(m_ir); + const auto data1 = b.eval(m_ir); + const auto data2 = c.eval(m_ir); + + result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_sdot), {data0, data1, data2}); + return result; + } + +template + auto addp(T1 a, T2 b) + { + using T_vector = typename is_llvm_expr::type; + const auto data1 = a.eval(m_ir); + const auto data2 = b.eval(m_ir); + + const auto func = get_intrinsic(llvm::Intrinsic::aarch64_neon_addp); + + value_t result; + result.value = m_ir->CreateCall(func, {data1, data2}); + return result; + } +#endif + template value_t vpermb(T1 a, T2 b) { @@ -3899,6 +3984,15 @@ public: erase_stores({args.value...}); } + // Debug breakpoint + void debugtrap() + { + const auto _rty = llvm::Type::getVoidTy(m_context); + const auto type = llvm::FunctionType::get(_rty, {}, false); + const auto func = llvm::cast(m_ir->GetInsertBlock()->getParent()->getParent()->getOrInsertFunction("llvm.debugtrap", type).getCallee()); + m_ir->CreateCall(func); + } + template static auto pshufb(T&& a, U&& b) { diff --git a/rpcs3/Emu/Cell/Modules/cellAdec.cpp b/rpcs3/Emu/Cell/Modules/cellAdec.cpp index dfc91c8d2f..0d0c4481e9 100644 --- a/rpcs3/Emu/Cell/Modules/cellAdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAdec.cpp @@ -261,7 +261,7 @@ void LpcmDecContext::exec(ppu_thread& ppu) savestate = lpcm_dec_state::waiting_for_output_mutex_lock; output_mutex_lock: - error_occurred |= static_cast(sys_mutex_lock(ppu, output_mutex, 0) != CELL_OK); + error_occurred |= static_cast(lv2_syscall(ppu, output_mutex, 0) != CELL_OK); if (ppu.state & cpu_flag::again) { @@ -273,7 +273,7 @@ void LpcmDecContext::exec(ppu_thread& ppu) savestate = lpcm_dec_state::waiting_for_output_cond_wait; output_cond_wait: - ensure(sys_cond_wait(ppu, output_consumed, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, output_consumed, 0) == CELL_OK); // Error code isn't checked on LLE if (ppu.state & cpu_flag::again) { @@ -287,7 +287,7 @@ void LpcmDecContext::exec(ppu_thread& ppu) savestate = lpcm_dec_state::queue_mutex_lock; queue_mutex_lock: - ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, queue_mutex, 0) == CELL_OK); // Error code isn't checked on LLE if (ppu.state & cpu_flag::again) { @@ -296,7 +296,7 @@ void LpcmDecContext::exec(ppu_thread& ppu) cmd_queue.pop(cmd); - ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, queue_mutex) == CELL_OK); // Error code isn't checked on LLE cellAdec.trace("Command type: %d", static_cast(cmd.type.get())); @@ -307,7 +307,7 @@ void LpcmDecContext::exec(ppu_thread& ppu) { case LpcmDecCmdType::start_seq: // LLE sends a command to the SPU thread. The SPU thread consumes the command without doing anything, however - error_occurred |= static_cast(sys_mutex_unlock(ppu, output_mutex) != CELL_OK); + error_occurred |= static_cast(lv2_syscall(ppu, output_mutex) != CELL_OK); break; case LpcmDecCmdType::end_seq: @@ -324,11 +324,11 @@ void LpcmDecContext::exec(ppu_thread& ppu) // Doesn't do anything else notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg); - error_occurred |= static_cast(sys_mutex_unlock(ppu, output_mutex) != CELL_OK); + error_occurred |= static_cast(lv2_syscall(ppu, output_mutex) != CELL_OK); break; } case LpcmDecCmdType::close: - ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, output_mutex) == CELL_OK); // Error code isn't checked on LLE return; case LpcmDecCmdType::decode_au: @@ -685,7 +685,7 @@ void LpcmDecContext::exec(ppu_thread& ppu) notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg); output_locked = true; - error_occurred |= static_cast(sys_mutex_unlock(ppu, output_mutex) != CELL_OK); + error_occurred |= static_cast(lv2_syscall(ppu, output_mutex) != CELL_OK); const vm::var bsi_info{{ lpcm_param->channelNumber, lpcm_param->sampleRate, static_cast(output_size) }}; @@ -703,14 +703,14 @@ error_code LpcmDecContext::send_command(ppu_thread& ppu, auto&&... args) { ppu.state += cpu_flag::wait; - if (error_code ret = sys_mutex_lock(ppu, queue_size_mutex, 0); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, queue_size_mutex, 0); ret != CELL_OK) { return ret; } if (cmd_queue.full()) { - ensure(sys_mutex_unlock(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_ADEC_ERROR_BUSY; } @@ -720,39 +720,39 @@ error_code LpcmDecContext::send_command(ppu_thread& ppu, auto&&... args) *lpcm_param = { args... }; } - if (error_code ret = sys_mutex_lock(ppu, queue_mutex, 0); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, queue_mutex, 0); ret != CELL_OK) { - ensure(sys_mutex_unlock(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE return ret; } cmd_queue.emplace(type, std::forward(args)...); - if (error_code ret = sys_mutex_unlock(ppu, queue_mutex); ret != CELL_OK + if (error_code ret = lv2_syscall(ppu, queue_mutex); ret != CELL_OK || (ret = cmd_available.release(ppu)) != CELL_OK) { - ensure(sys_mutex_unlock(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE return ret; } - return sys_mutex_unlock(ppu, queue_size_mutex); + return lv2_syscall(ppu, queue_size_mutex); } inline error_code LpcmDecContext::release_output(ppu_thread& ppu) { - if (error_code ret = sys_mutex_lock(ppu, output_mutex, 0); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, output_mutex, 0); ret != CELL_OK) { return ret; } output_locked = false; - if (error_code ret = sys_cond_signal(ppu, output_consumed); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, output_consumed); ret != CELL_OK) { return ret; // LLE doesn't unlock the mutex } - return sys_mutex_unlock(ppu, output_mutex); + return lv2_syscall(ppu, output_mutex); } void lpcmDecEntry(ppu_thread& ppu, vm::ptr lpcm_dec) @@ -820,13 +820,13 @@ error_code _CellAdecCoreOpOpenExt_lpcm(ppu_thread& ppu, vm::ptr const vm::var queue_mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_adem06"_u64 } }}; const vm::var cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_adec03"_u64 } }}; - error_code ret = sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::queue_size_mutex), mutex_attr); - ret = ret ? ret : sys_cond_create(ppu, handle.ptr(&LpcmDecContext::queue_size_cond), handle->queue_size_mutex, cond_attr); - ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::unk_mutex), mutex_attr); - ret = ret ? ret : sys_cond_create(ppu, handle.ptr(&LpcmDecContext::unk_cond), handle->unk_mutex, cond_attr); - ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::output_mutex), output_mutex_attr); - ret = ret ? ret : sys_cond_create(ppu, handle.ptr(&LpcmDecContext::output_consumed), handle->output_mutex, cond_attr); - ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::queue_mutex), queue_mutex_attr); + error_code ret = lv2_syscall(ppu, handle.ptr(&LpcmDecContext::queue_size_mutex), mutex_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::queue_size_cond), handle->queue_size_mutex, cond_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::unk_mutex), mutex_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::unk_cond), handle->unk_mutex, cond_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::output_mutex), output_mutex_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::output_consumed), handle->output_mutex, cond_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::queue_mutex), queue_mutex_attr); ret = ret ? ret : handle->release_output(ppu); ret = ret ? ret : handle->cmd_available.init(ppu, handle.ptr(&LpcmDecContext::cmd_available), 0); ret = ret ? ret : handle->reserved2.init(ppu, handle.ptr(&LpcmDecContext::reserved2), 0); @@ -844,8 +844,8 @@ error_code _CellAdecCoreOpOpenExt_lpcm(ppu_thread& ppu, vm::ptr const auto entry = g_fxo->get().func_addr(FIND_FUNC(lpcmDecEntry)); ret = ppu_execute<&sys_ppu_thread_create>(ppu, handle.ptr(&LpcmDecContext::thread_id), entry, handle.addr(), +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +_name); - ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::spurs_queue_pop_mutex), mutex_attr); - ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::spurs_queue_push_mutex), mutex_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::spurs_queue_pop_mutex), mutex_attr); + ret = ret ? ret : lv2_syscall(ppu, handle.ptr(&LpcmDecContext::spurs_queue_push_mutex), mutex_attr); return ret; } @@ -865,8 +865,8 @@ error_code _CellAdecCoreOpClose_lpcm(ppu_thread& ppu, vm::ptr ha cellAdec.notice("_CellAdecCoreOpClose_lpcm(handle=*0x%x)", handle); - if (error_code ret = sys_mutex_lock(ppu, handle->queue_size_mutex, 0); ret != CELL_OK - || (ret = sys_mutex_lock(ppu, handle->queue_mutex, 0)) != CELL_OK) + if (error_code ret = lv2_syscall(ppu, handle->queue_size_mutex, 0); ret != CELL_OK + || (ret = lv2_syscall(ppu, handle->queue_mutex, 0)) != CELL_OK) { return ret; } @@ -875,14 +875,14 @@ error_code _CellAdecCoreOpClose_lpcm(ppu_thread& ppu, vm::ptr ha { handle->cmd_queue.emplace(LpcmDecCmdType::close); - if (error_code ret = sys_mutex_unlock(ppu, handle->queue_mutex); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, handle->queue_mutex); ret != CELL_OK) { return ret; // LLE doesn't unlock the queue size mutex } if (error_code ret = handle->cmd_available.release(ppu); ret != CELL_OK) { - ensure(sys_mutex_unlock(ppu, handle->queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, handle->queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE return ret; } } @@ -893,29 +893,29 @@ error_code _CellAdecCoreOpClose_lpcm(ppu_thread& ppu, vm::ptr ha cmd.type = LpcmDecCmdType::close; } - if (error_code ret = sys_mutex_unlock(ppu, handle->queue_mutex); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, handle->queue_mutex); ret != CELL_OK) { return ret; // LLE doesn't unlock the queue size mutex } } - error_code ret = sys_mutex_unlock(ppu, handle->queue_size_mutex); + error_code ret = lv2_syscall(ppu, handle->queue_size_mutex); ret = ret ? ret : handle->release_output(ppu); vm::var thread_ret; - ret = ret ? ret : sys_ppu_thread_join(ppu, static_cast(handle->thread_id), +thread_ret); + ret = ret ? ret : lv2_syscall(ppu, static_cast(handle->thread_id), +thread_ret); - ret = ret ? ret : sys_cond_destroy(ppu, handle->queue_size_cond); - ret = ret ? ret : sys_cond_destroy(ppu, handle->unk_cond); - ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_mutex); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_size_mutex); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->unk_mutex); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->output_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->queue_size_cond); + ret = ret ? ret : lv2_syscall(ppu, handle->unk_cond); + ret = ret ? ret : lv2_syscall(ppu, handle->output_consumed); + ret = ret ? ret : lv2_syscall(ppu, handle->queue_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->queue_size_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->unk_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->output_mutex); ret = ret ? ret : handle->cmd_available.finalize(ppu); ret = ret ? ret : handle->reserved2.finalize(ppu); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->spurs_queue_pop_mutex); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->spurs_queue_push_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->spurs_queue_pop_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->spurs_queue_push_mutex); return ret; } @@ -1091,11 +1091,11 @@ error_code AdecContext::set_pcm_item(s32 pcm_handle, vm::ptr pcm_addr, u32 error_code AdecContext::link_frame(ppu_thread& ppu, s32 pcm_handle) { - ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE if (verify_pcm_handle(pcm_handle) == static_cast(CELL_ADEC_ERROR_FATAL)) { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_ADEC_ERROR_FATAL; } @@ -1115,27 +1115,27 @@ error_code AdecContext::link_frame(ppu_thread& ppu, s32 pcm_handle) } else { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_ADEC_ERROR_FATAL; } - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_OK; } error_code AdecContext::unlink_frame(ppu_thread& ppu, s32 pcm_handle) { - ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE if (verify_pcm_handle(pcm_handle) == static_cast(CELL_ADEC_ERROR_FATAL)) { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_ADEC_ERROR_FATAL; } if (frames_head == -1 || frames_tail == -1) { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_ADEC_ERROR_FATAL; } @@ -1146,7 +1146,7 @@ error_code AdecContext::unlink_frame(ppu_thread& ppu, s32 pcm_handle) { if (pcm_handle != frames_tail) { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_ADEC_ERROR_FATAL; } @@ -1171,7 +1171,7 @@ error_code AdecContext::unlink_frame(ppu_thread& ppu, s32 pcm_handle) frames[prev].next = next; } - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_OK; } @@ -1557,7 +1557,7 @@ error_code adecOpen(ppu_thread& ppu, vm::ptr type, vm::cptr(ppu, _this.ptr(&AdecContext::mutex), _this.ptr(&AdecContext::mutex_attribute)) == CELL_OK); // Error code isn't checked on LLE *handle = _this; @@ -1626,7 +1626,7 @@ error_code cellAdecClose(ppu_thread& ppu, vm::ptr handle) return ret; } - if (error_code ret = sys_mutex_destroy(ppu, handle->mutex); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, handle->mutex); ret != CELL_OK) { return ret; } diff --git a/rpcs3/Emu/Cell/Modules/cellAdec.h b/rpcs3/Emu/Cell/Modules/cellAdec.h index a43c3f0aef..2216561516 100644 --- a/rpcs3/Emu/Cell/Modules/cellAdec.h +++ b/rpcs3/Emu/Cell/Modules/cellAdec.h @@ -485,6 +485,19 @@ struct AdecFrame CHECK_SIZE(AdecFrame, 0x68); +template +static auto lv2_syscall(ppu_thread& ppu, auto&&... args) +{ + const auto ret = Syscall(ppu, std::forward(args)...); + + if (ppu.test_stopped()) + { + ppu.state += cpu_flag::again; + } + + return ret; +} + class AdecOutputQueue { struct entry @@ -511,10 +524,10 @@ public: this->size = 0; const vm::var mutex_attr = {{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_adem07"_u64 } }}; - ensure(sys_mutex_create(ppu, _this.ptr(&AdecOutputQueue::mutex), mutex_attr) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, _this.ptr(&AdecOutputQueue::mutex), mutex_attr) == CELL_OK); // Error code isn't checked on LLE const vm::var cond_attr = {{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_adec05"_u64 } }}; - ensure(sys_cond_create(ppu, _this.ptr(&AdecOutputQueue::cond), mutex, cond_attr) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, _this.ptr(&AdecOutputQueue::cond), mutex, cond_attr) == CELL_OK); // Error code isn't checked on LLE for (s32 i = 0; i < 4; i++) { @@ -524,12 +537,12 @@ public: error_code finalize(ppu_thread& ppu) const { - if (error_code ret = sys_cond_destroy(ppu, cond); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, cond); ret != CELL_OK) { return ret; } - if (error_code ret = sys_mutex_destroy(ppu, mutex); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, mutex); ret != CELL_OK) { return ret; } @@ -539,11 +552,11 @@ public: error_code push(ppu_thread& ppu, vm::ptr pcm_item, s32 pcm_handle) { - ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE if (entries[back].state != 0xff) { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return true; // LLE returns the result of the comparison above } @@ -554,13 +567,13 @@ public: back = (back + 1) & 3; size++; - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return CELL_OK; } const entry* pop(ppu_thread& ppu) { - ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE if (ppu.state & cpu_flag::again) // Savestate was created while waiting on the mutex { @@ -569,7 +582,7 @@ public: if (entries[front].state == 0xff) { - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return nullptr; } @@ -581,15 +594,15 @@ public: front = (front + 1) & 3; size--; - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return ret; } const entry& peek(ppu_thread& ppu) const { - ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE const entry& ret = entries[front]; - ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE + ensure(lv2_syscall(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE return ret; } }; @@ -708,39 +721,39 @@ public: const vm::var mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_adem01"_u64 } }}; const vm::var cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_adec01"_u64 } }}; - if (error_code ret = sys_mutex_create(ppu, _this.ptr(&LpcmDecSemaphore::mutex), mutex_attr); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, _this.ptr(&LpcmDecSemaphore::mutex), mutex_attr); ret != CELL_OK) { return ret; } - return sys_cond_create(ppu, _this.ptr(&LpcmDecSemaphore::cond), mutex, cond_attr); + return lv2_syscall(ppu, _this.ptr(&LpcmDecSemaphore::cond), mutex, cond_attr); } error_code finalize(ppu_thread& ppu) const { - if (error_code ret = sys_cond_destroy(ppu, cond); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, cond); ret != CELL_OK) { return ret; } - return sys_mutex_destroy(ppu, mutex); + return lv2_syscall(ppu, mutex); } error_code release(ppu_thread& ppu) { - if (error_code ret = sys_mutex_lock(ppu, mutex, 0); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, mutex, 0); ret != CELL_OK) { return ret; } value++; - if (error_code ret = sys_cond_signal(ppu, cond); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, cond); ret != CELL_OK) { return ret; // LLE doesn't unlock the mutex } - return sys_mutex_unlock(ppu, mutex); + return lv2_syscall(ppu, mutex); } error_code acquire(ppu_thread& ppu, lpcm_dec_state& savestate) @@ -752,7 +765,7 @@ public: savestate = lpcm_dec_state::waiting_for_cmd_mutex_lock; - if (error_code ret = sys_mutex_lock(ppu, mutex, 0); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, mutex, 0); ret != CELL_OK) { return ret; } @@ -767,7 +780,7 @@ public: savestate = lpcm_dec_state::waiting_for_cmd_cond_wait; cond_wait: - if (error_code ret = sys_cond_wait(ppu, cond, 0); ret != CELL_OK) + if (error_code ret = lv2_syscall(ppu, cond, 0); ret != CELL_OK) { return ret; // LLE doesn't unlock the mutex } @@ -780,7 +793,7 @@ public: value--; - return sys_mutex_unlock(ppu, mutex); + return lv2_syscall(ppu, mutex); } }; diff --git a/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp b/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp index c55cf7b60f..fb3191bcb4 100644 --- a/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp @@ -295,7 +295,7 @@ void AtracXdecContext::exec(ppu_thread& ppu) { savestate = atracxdec_state::initial; - ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -310,24 +310,24 @@ void AtracXdecContext::exec(ppu_thread& ppu) savestate = atracxdec_state::waiting_for_cmd; label1_wait_for_cmd_state: - ensure(sys_cond_wait(ppu, queue_not_empty, 0) == CELL_OK); + ensure(lv2_syscall(ppu, queue_not_empty, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { return; } - ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex) == CELL_OK); } cmd_queue.pop(cmd); - ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex) == CELL_OK); savestate = atracxdec_state::checking_run_thread_1; label2_check_run_thread_1_state: - ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, run_thread_mutex, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -336,11 +336,11 @@ void AtracXdecContext::exec(ppu_thread& ppu) if (!run_thread) { - ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, run_thread_mutex) == CELL_OK); return; } - ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, run_thread_mutex) == CELL_OK); savestate = atracxdec_state::executing_cmd; label3_execute_cmd_state: @@ -392,7 +392,7 @@ void AtracXdecContext::exec(ppu_thread& ppu) cellAtracXdec.trace("Waiting for output to be consumed..."); - ensure(sys_mutex_lock(ppu, output_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, output_mutex, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -404,7 +404,7 @@ void AtracXdecContext::exec(ppu_thread& ppu) savestate = atracxdec_state::waiting_for_output; label4_wait_for_output_state: - ensure(sys_cond_wait(ppu, output_consumed, 0) == CELL_OK); + ensure(lv2_syscall(ppu, output_consumed, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -417,7 +417,7 @@ void AtracXdecContext::exec(ppu_thread& ppu) savestate = atracxdec_state::checking_run_thread_2; label5_check_run_thread_2_state: - ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, run_thread_mutex, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -426,12 +426,12 @@ void AtracXdecContext::exec(ppu_thread& ppu) if (!run_thread) { - ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); - ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, run_thread_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, output_mutex) == CELL_OK); return; } - ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, run_thread_mutex) == CELL_OK); savestate = atracxdec_state::decoding; label6_decode_state: @@ -645,7 +645,7 @@ void AtracXdecContext::exec(ppu_thread& ppu) notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg); output_locked = true; - ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, output_mutex) == CELL_OK); const u32 output_size = decoded_samples_num * (decoder.bw_pcm & 0x7fu) * decoder.nch_out; @@ -680,7 +680,7 @@ error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args) if (!signal) { - ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -692,23 +692,23 @@ error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args) // Close command is only sent if the queue is empty on LLE if (!cmd_queue.empty()) { - ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex) == CELL_OK); return {}; } } if (cmd_queue.full()) { - ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex) == CELL_OK); return CELL_ADEC_ERROR_ATX_BUSY; } cmd_queue.emplace(std::forward(type), std::forward(args)...); - ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, queue_mutex) == CELL_OK); } - ensure(sys_cond_signal(ppu, queue_not_empty) == CELL_OK); + ensure(lv2_syscall(ppu, queue_not_empty) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -779,25 +779,25 @@ error_code _CellAdecCoreOpOpenExt_atracx(ppu_thread& ppu, vm::ptr mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_atd001"_u64 } }}; const vm::var cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_atd002"_u64 } }}; - ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::queue_mutex), mutex_attr) == CELL_OK); - ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::queue_not_empty), handle->queue_mutex, cond_attr) == CELL_OK); + ensure(lv2_syscall(ppu, handle.ptr(&AtracXdecContext::queue_mutex), mutex_attr) == CELL_OK); + ensure(lv2_syscall(ppu, handle.ptr(&AtracXdecContext::queue_not_empty), handle->queue_mutex, cond_attr) == CELL_OK); mutex_attr->name_u64 = "_atd003"_u64; cond_attr->name_u64 = "_atd004"_u64; - ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::run_thread_mutex), mutex_attr) == CELL_OK); - ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::run_thread_cond), handle->run_thread_mutex, cond_attr) == CELL_OK); + ensure(lv2_syscall(ppu, handle.ptr(&AtracXdecContext::run_thread_mutex), mutex_attr) == CELL_OK); + ensure(lv2_syscall(ppu, handle.ptr(&AtracXdecContext::run_thread_cond), handle->run_thread_mutex, cond_attr) == CELL_OK); mutex_attr->name_u64 = "_atd005"_u64; cond_attr->name_u64 = "_atd006"_u64; - ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::output_mutex), mutex_attr) == CELL_OK); - ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::output_consumed), handle->output_mutex, cond_attr) == CELL_OK); + ensure(lv2_syscall(ppu, handle.ptr(&AtracXdecContext::output_mutex), mutex_attr) == CELL_OK); + ensure(lv2_syscall(ppu, handle.ptr(&AtracXdecContext::output_consumed), handle->output_mutex, cond_attr) == CELL_OK); - ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_mutex, 0) == CELL_OK); handle->output_locked = false; - ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK); - ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_consumed) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_mutex) == CELL_OK); const vm::var _name = vm::make_str("HLE ATRAC3plus decoder"); const auto entry = g_fxo->get().func_addr(FIND_FUNC(atracXdecEntry)); @@ -829,26 +829,26 @@ error_code _CellAdecCoreOpClose_atracx(ppu_thread& ppu, vm::ptrrun_thread_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, handle->run_thread_mutex, 0) == CELL_OK); handle->run_thread = false; - ensure(sys_mutex_unlock(ppu, handle->run_thread_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, handle->run_thread_mutex) == CELL_OK); handle->send_command(ppu); - ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_mutex, 0) == CELL_OK); handle->output_locked = false; - ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK); - ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_consumed) == CELL_OK); vm::var thread_ret; - ensure(sys_ppu_thread_join(ppu, static_cast(handle->thread_id), +thread_ret) == CELL_OK); + ensure(lv2_syscall(ppu, static_cast(handle->thread_id), +thread_ret) == CELL_OK); - error_code ret = sys_cond_destroy(ppu, handle->queue_not_empty); - ret = ret ? ret : sys_cond_destroy(ppu, handle->run_thread_cond); - ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_mutex); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->run_thread_mutex); - ret = ret ? ret : sys_mutex_destroy(ppu, handle->output_mutex); + error_code ret = lv2_syscall(ppu, handle->queue_not_empty); + ret = ret ? ret : lv2_syscall(ppu, handle->run_thread_cond); + ret = ret ? ret : lv2_syscall(ppu, handle->output_consumed); + ret = ret ? ret : lv2_syscall(ppu, handle->queue_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->run_thread_mutex); + ret = ret ? ret : lv2_syscall(ppu, handle->output_mutex); return ret != CELL_OK ? static_cast(CELL_ADEC_ERROR_FATAL) : CELL_OK; } @@ -921,7 +921,7 @@ error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptroutput_mutex, 0) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_mutex, 0) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -931,7 +931,7 @@ error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptroutput_locked = false; } - ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_consumed) == CELL_OK); if (ppu.state & cpu_flag::again) { @@ -939,7 +939,7 @@ error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptroutput_mutex) == CELL_OK); + ensure(lv2_syscall(ppu, handle->output_mutex) == CELL_OK); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 096f9330f4..10f9a89cf0 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -919,7 +919,7 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm if (!check_dev_num(dev_num)) { - return CELL_CAMERA_ERROR_PARAM; + return { CELL_CAMERA_ERROR_PARAM, "dev_num=%d", dev_num }; } if (g_cfg.io.camera == camera_handler::null) @@ -935,7 +935,7 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm if (!arg1) { - return CELL_CAMERA_ERROR_PARAM; + return { CELL_CAMERA_ERROR_PARAM, "arg1=null" }; } if (error_code error = check_resolution(dev_num)) @@ -952,7 +952,7 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm if (!attr_name) // invalid attributes don't have a name { - return CELL_CAMERA_ERROR_PARAM; + return { CELL_CAMERA_ERROR_PARAM, "attrib=0x%x", attrib }; } if (arg1) @@ -983,7 +983,7 @@ error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2) if (!check_dev_num(dev_num)) { - return CELL_CAMERA_ERROR_PARAM; + return { CELL_CAMERA_ERROR_PARAM, "dev_num=%d", dev_num }; } if (g_cfg.io.camera == camera_handler::null) @@ -1004,7 +1004,7 @@ error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2) if (!attr_name) // invalid attributes don't have a name { - return CELL_CAMERA_ERROR_PARAM; + return { CELL_CAMERA_ERROR_PARAM, "attrib=0x%x", attrib }; } g_camera.set_attr(attrib, arg1, arg2); diff --git a/rpcs3/Emu/Cell/Modules/cellDmux.cpp b/rpcs3/Emu/Cell/Modules/cellDmux.cpp index d7f6f84f3f..7c2fe6f1a9 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmux.cpp +++ b/rpcs3/Emu/Cell/Modules/cellDmux.cpp @@ -1,16 +1,14 @@ #include "stdafx.h" -#include "Emu/System.h" -#include "Emu/IdManager.h" -#include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_mutex.h" #include "Emu/Cell/lv2/sys_sync.h" +#include "Emu/Cell/lv2/sys_timer.h" +#include "Emu/Cell/PPUModule.h" +#include "Emu/savestate_utils.hpp" +#include "util/asm.hpp" #include "cellPamf.h" #include "cellDmux.h" -#include "util/asm.hpp" - -#include - LOG_CHANNEL(cellDmux); template <> @@ -31,1331 +29,1209 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -/* Demuxer Thread Classes */ - -enum +static error_code get_error(u32 internal_error) { - /* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html */ - - PACKET_START_CODE_MASK = 0xffffff00, - PACKET_START_CODE_PREFIX = 0x00000100, - - PACK_START_CODE = 0x000001ba, - SYSTEM_HEADER_START_CODE = 0x000001bb, - PRIVATE_STREAM_1 = 0x000001bd, - PADDING_STREAM = 0x000001be, - PRIVATE_STREAM_2 = 0x000001bf, -}; - -struct DemuxerStream -{ - u32 addr; - u32 size; - u64 userdata; - bool discontinuity; - - template - bool get(T& out) + switch (internal_error) { - if (sizeof(T) > size) return false; + case 0: return CELL_OK; + case 1: return CELL_DMUX_ERROR_FATAL; + case 2: // Error values two to five are all converted to CELL_DMUX_ERROR_ARG. + case 3: + case 4: + case 5: return CELL_DMUX_ERROR_ARG; + default: return CELL_DMUX_ERROR_FATAL; + } +} - std::memcpy(&out, vm::base(addr), sizeof(T)); - addr += sizeof(T); - size -= sizeof(T); +static inline std::span> get_es_handles(vm::ptr handle) +{ + return { vm::pptr::make(handle.addr() + sizeof(DmuxContext)).get_ptr(), static_cast(handle->enabled_es_num) }; +} - return true; +static inline vm::ptr get_au_queue_elements(vm::ptr es_handle) +{ + return vm::ptr::make(es_handle.addr() + sizeof(DmuxEsContext)); +} + +static inline vm::cptr get_core_ops() +{ + return vm::cptr::make(*ppu_module_manager::cellDmuxPamf.variables.find(0x28b2b7b2)->second.export_addr); +} + +template +static auto lv2_syscall(ppu_thread& ppu, auto&&... args) +{ + const auto ret = Syscall(ppu, std::forward(args)...); + + if (ppu.test_stopped()) + { + ppu.state += cpu_flag::again; } - template - bool peek(T& out, u32 shift = 0) - { - if (sizeof(T) + shift > size) return false; + return ret; +} - std::memcpy(&out, vm::base(addr + shift), sizeof(T)); - return true; - } +// Callbacks for cellDmuxPamf - void skip(u32 count) - { - addr += count; - size = size > count ? size - count : 0; - } - - bool check(u32 count) const - { - return count <= size; - } - - u64 get_ts(u8 c) - { - u8 v[4]; get(v); - return - ((u64{c} & 0x0e) << 29) | - ((u64{v[0]}) << 21) | - ((u64{v[1]} & 0x7e) << 15) | - ((u64{v[2]}) << 7) | (u64{v[3]} >> 1); - } -}; - -struct PesHeader +static error_code notify_demux_done(ppu_thread& ppu, vm::ptr core_handle, u32 error, vm::ptr handle) { - u64 pts; - u64 dts; - u8 size; - bool has_ts; - bool is_ok; + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - PesHeader(DemuxerStream& stream); -}; - -class ElementaryStream; -class Demuxer; - -enum DemuxerJobType -{ - dmuxSetStream, - dmuxResetStream, - dmuxResetStreamAndWaitDone, - dmuxEnableEs, - dmuxDisableEs, - dmuxResetEs, - dmuxFlushEs, - dmuxClose, -}; - -struct DemuxerTask -{ - DemuxerJobType type; - - union + if (!savestate_lock) { - DemuxerStream stream; + ppu.state += cpu_flag::again; + return {}; + } - struct + cellDmux.trace("notify_demux_done(core_handle=*0x%x, error=%d, handle=*0x%x)", core_handle, error, handle); + + ensure(!!handle); // Not checked on LLE + + ensure(lv2_syscall(ppu, handle->_dx_mhd, 0) == CELL_OK); // Failing this check on LLE would result in it dereferencing an invalid pointer. + handle->dmux_state = DMUX_STOPPED; + ensure(lv2_syscall(ppu, handle->_dx_mhd) == CELL_OK); // Failing this check on LLE would result in it dereferencing an invalid pointer. + + if (handle->_this) + { + const vm::var msg{{ .msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE, .supplementalInfo = handle->user_data }}; + handle->dmux_cb.cbFunc(ppu, handle, msg, handle->dmux_cb.cbArg); + } + + return CELL_OK; +} + +static error_code notify_fatal_err(ppu_thread& ppu, vm::ptr core_handle, u32 error, vm::ptr handle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.error("notify_fatal_err(core_handle=*0x%x, error=%d, handle=*0x%x)", core_handle, error, handle); + + ensure(!!handle); // Not checked on LLE + + const vm::var msg{{ .msgType = CELL_DMUX_MSG_TYPE_FATAL_ERR, .supplementalInfo = static_cast(get_error(error)) }}; + return handle->dmux_cb.cbFunc(ppu, handle, msg, handle->dmux_cb.cbArg); +} + +static error_code notify_prog_end_code(ppu_thread& ppu, vm::ptr core_handle, vm::ptr handle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("notify_prog_end_code(core_handle=*0x%x, handle=*0x%x)", core_handle, handle); + + ensure(!!handle); // Not checked on LLE + + if (handle->_this) + { + const vm::var msg{{ .msgType = CELL_DMUX_MSG_TYPE_PROG_END_CODE, .supplementalInfo = handle->user_data }}; + handle->dmux_cb.cbFunc(ppu, handle, msg, handle->dmux_cb.cbArg); + } + + return CELL_OK; +} + +static error_code notify_es_au_found(ppu_thread& ppu, vm::ptr core_es_handle, vm::cptr au_info, vm::ptr es_handle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.trace("notify_es_au_found(core_es_handle=*0x%x, au_info=*0x%x, es_handle=*0x%x)", core_es_handle, au_info, es_handle); + + ensure(!!au_info && !!es_handle); // Not checked on LLE + + const auto fatal_err = [&](be_t es_is_enabled, error_code ret) + { + if (es_is_enabled) { - u32 es; - u32 auInfo_ptr_addr; - u32 auSpec_ptr_addr; - ElementaryStream* es_ptr; - } es; + const vm::var demuxerMsg{{ .msgType = CELL_DMUX_MSG_TYPE_FATAL_ERR, .supplementalInfo = static_cast(ret) }}; + es_handle->dmux_handle->dmux_cb.cbFunc(ppu, es_handle->dmux_handle, demuxerMsg, es_handle->dmux_handle->dmux_cb.cbArg); + } }; - DemuxerTask() - { - } - - DemuxerTask(DemuxerJobType type) - : type(type) - { - } -}; - -class ElementaryStream -{ - std::mutex m_mutex; - - squeue_t entries; // AU starting addresses - u32 put_count = 0; // number of AU written - u32 got_count = 0; // number of AU obtained by GetAu(Ex) - u32 released = 0; // number of AU released - - u32 put; // AU that is being written now - - bool is_full(u32 space); - -public: - static const u32 id_base = 1; - static const u32 id_step = 1; - static const u32 id_count = 1023; - SAVESTATE_INIT_POS(34); - - ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr cbFunc, u32 cbArg, u32 spec); - - Demuxer* dmux; - const u32 id = idm::last_id(); - const u32 memAddr; - const u32 memSize; - const u32 fidMajor; - const u32 fidMinor; - const u32 sup1; - const u32 sup2; - const vm::ptr cbFunc; - const u32 cbArg; - const u32 spec; //addr - - std::vector raw_data; // demultiplexed data stream (managed by demuxer thread) - usz raw_pos = 0; // should be <= raw_data.size() - u64 last_dts = CODEC_TS_INVALID; - u64 last_pts = CODEC_TS_INVALID; - - void push(DemuxerStream& stream, u32 size); // called by demuxer thread (not multithread-safe) - - bool isfull(u32 space); - - void push_au(u32 size, u64 dts, u64 pts, u64 userdata, bool rap, u32 specific); - - bool release(); - - bool peek(u32& out_data, bool no_ex, u32& out_spec, bool update_index); - - void reset(); -}; - -class Demuxer : public ppu_thread -{ -public: - squeue_t job; - const u32 memAddr; - const u32 memSize; - const vm::ptr cbFunc; - const u32 cbArg; - volatile bool is_finished = false; - volatile bool is_closed = false; - atomic_t is_running = false; - atomic_t is_working = false; - - Demuxer(u32 addr, u32 size, vm::ptr func, u32 arg) - : ppu_thread({}, "", 0) - , memAddr(addr) - , memSize(size) - , cbFunc(func) - , cbArg(arg) - { - } - - void non_task() - { - DemuxerTask task; - DemuxerStream stream = {}; - ElementaryStream* esALL[96]{}; - ElementaryStream** esAVC = &esALL[0]; // AVC (max 16 minus M2V count) - //ElementaryStream** esM2V = &esALL[16]; // M2V (max 16 minus AVC count) - //ElementaryStream** esDATA = &esALL[32]; // user data (max 16) - ElementaryStream** esATX = &esALL[48]; // ATRAC3+ (max 16) - //ElementaryStream** esAC3 = &esALL[64]; // AC3 (max 16) - //ElementaryStream** esPCM = &esALL[80]; // LPCM (max 16) - - u32 cb_add = 0; - - while (true) - { - if (Emu.IsStopped() || is_closed) - { - break; - } - - if (!job.try_peek(task) && is_running && stream.addr) - { - // default task (demuxing) (if there is no other work) - be_t code; - be_t len; - - if (!stream.peek(code)) - { - // demuxing finished - is_running = false; - - // callback - auto dmuxMsg = vm::ptr::make(memAddr + (cb_add ^= 16)); - dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE; - dmuxMsg->supplementalInfo = stream.userdata; - cbFunc(*this, id, dmuxMsg, cbArg); - lv2_obj::sleep(*this); - - is_working = false; - - stream = {}; - - continue; - } - - switch (code) - { - case PACK_START_CODE: - { - if (!stream.check(14)) - { - fmt::throw_exception("End of stream (PACK_START_CODE)"); - } - stream.skip(14); - break; - } - - case SYSTEM_HEADER_START_CODE: - { - if (!stream.check(18)) - { - fmt::throw_exception("End of stream (SYSTEM_HEADER_START_CODE)"); - } - stream.skip(18); - break; - } - - case PADDING_STREAM: - { - if (!stream.check(6)) - { - fmt::throw_exception("End of stream (PADDING_STREAM)"); - } - stream.skip(4); - stream.get(len); - - if (!stream.check(len)) - { - fmt::throw_exception("End of stream (PADDING_STREAM, len=%d)", len); - } - stream.skip(len); - break; - } - - case PRIVATE_STREAM_2: - { - if (!stream.check(6)) - { - fmt::throw_exception("End of stream (PRIVATE_STREAM_2)"); - } - stream.skip(4); - stream.get(len); - - cellDmux.notice("PRIVATE_STREAM_2 (%d)", len); - - if (!stream.check(len)) - { - fmt::throw_exception("End of stream (PRIVATE_STREAM_2, len=%d)", len); - } - stream.skip(len); - break; - } - - case PRIVATE_STREAM_1: - { - // audio and user data stream - DemuxerStream backup = stream; - - if (!stream.check(6)) - { - fmt::throw_exception("End of stream (PRIVATE_STREAM_1)"); - } - stream.skip(4); - stream.get(len); - - if (!stream.check(len)) - { - fmt::throw_exception("End of stream (PRIVATE_STREAM_1, len=%d)", len); - } - - const PesHeader pes(stream); - if (!pes.is_ok) - { - fmt::throw_exception("PesHeader error (PRIVATE_STREAM_1, len=%d)", len); - } - - if (len < pes.size + 4) - { - fmt::throw_exception("End of block (PRIVATE_STREAM_1, PesHeader + fid_minor, len=%d)", len); - } - len -= pes.size + 4; - - u8 fid_minor; - if (!stream.get(fid_minor)) - { - fmt::throw_exception("End of stream (PRIVATE_STREAM1, fid_minor)"); - } - - const u32 ch = fid_minor % 16; - if ((fid_minor & -0x10) == 0 && esATX[ch]) - { - ElementaryStream& es = *esATX[ch]; - if (es.raw_data.size() > 1024 * 1024) - { - stream = backup; - std::this_thread::sleep_for(1ms); // hack - continue; - } - - if (len < 3 || !stream.check(3)) - { - fmt::throw_exception("End of block (ATX, unknown header, len=%d)", len); - } - len -= 3; - stream.skip(3); - - if (pes.has_ts) - { - es.last_dts = pes.dts; - es.last_pts = pes.pts; - } - - es.push(stream, len); - - while (true) - { - auto const size = es.raw_data.size() - es.raw_pos; // size of available new data - auto const data = es.raw_data.data() + es.raw_pos; // pointer to available data - - if (size < 8) break; // skip if cannot read ATS header - - if (data[0] != 0x0f || data[1] != 0xd0) - { - fmt::throw_exception("ATX: 0x0fd0 header not found (ats=0x%llx)", *reinterpret_cast*>(data)); - } - - u32 frame_size = (((u32{data[2]} & 0x3) << 8) | u32{data[3]}) * 8 + 8; - - if (size < frame_size + 8) break; // skip non-complete AU - - if (es.isfull(frame_size + 8)) break; // skip if cannot push AU - - es.push_au(frame_size + 8, es.last_dts, es.last_pts, stream.userdata, false /* TODO: set correct value */, 0); - - //cellDmux.notice("ATX AU pushed (ats=0x%llx, frame_size=%d)", *(be_t*)data, frame_size); - - auto esMsg = vm::ptr::make(memAddr + (cb_add ^= 16)); - esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; - esMsg->supplementalInfo = stream.userdata; - es.cbFunc(*this, id, es.id, esMsg, es.cbArg); - lv2_obj::sleep(*this); - } - } - else - { - cellDmux.notice("PRIVATE_STREAM_1 (len=%d, fid_minor=0x%x)", len, fid_minor); - stream.skip(len); - } - break; - } - - case 0x1e0: case 0x1e1: case 0x1e2: case 0x1e3: - case 0x1e4: case 0x1e5: case 0x1e6: case 0x1e7: - case 0x1e8: case 0x1e9: case 0x1ea: case 0x1eb: - case 0x1ec: case 0x1ed: case 0x1ee: case 0x1ef: - { - // video stream (AVC or M2V) - DemuxerStream backup = stream; - - if (!stream.check(6)) - { - fmt::throw_exception("End of stream (video, code=0x%x)", code); - } - stream.skip(4); - stream.get(len); - - if (!stream.check(len)) - { - fmt::throw_exception("End of stream (video, code=0x%x, len=%d)", code, len); - } - - const PesHeader pes(stream); - if (!pes.is_ok) - { - fmt::throw_exception("PesHeader error (video, code=0x%x, len=%d)", code, len); - } - - if (len < pes.size + 3) - { - fmt::throw_exception("End of block (video, code=0x%x, PesHeader)", code); - } - len -= pes.size + 3; - - const u32 ch = code % 16; - if (esAVC[ch]) - { - ElementaryStream& es = *esAVC[ch]; - - const u32 old_size = ::size32(es.raw_data); - if (es.isfull(old_size)) - { - stream = backup; - std::this_thread::sleep_for(1ms); // hack - continue; - } - - if ((pes.has_ts && old_size) || old_size >= 0x69800) - { - // push AU if it becomes too big or the next packet contains PTS/DTS - es.push_au(old_size, es.last_dts, es.last_pts, stream.userdata, false /* TODO: set correct value */, 0); - - // callback - auto esMsg = vm::ptr::make(memAddr + (cb_add ^= 16)); - esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; - esMsg->supplementalInfo = stream.userdata; - es.cbFunc(*this, id, es.id, esMsg, es.cbArg); - lv2_obj::sleep(*this); - } - - if (pes.has_ts) - { - // preserve dts/pts for next AU - es.last_dts = pes.dts; - es.last_pts = pes.pts; - } - - // reconstruction of MPEG2-PS stream for vdec module - const u32 size = len + pes.size + 9; - stream = backup; - es.push(stream, size); - } - else - { - cellDmux.notice("Video stream (code=0x%x, len=%d)", code, len); - stream.skip(len); - } - break; - } - - default: - { - if ((code & PACKET_START_CODE_MASK) == PACKET_START_CODE_PREFIX) - { - fmt::throw_exception("Unknown code found (0x%x)", code); - } - - // search - stream.skip(1); - } - } - - continue; - } - - // wait for task if no work - if (!job.pop(task, &is_closed)) - { - break; // Emu is stopped - } - - switch (task.type) - { - case dmuxSetStream: - { - if (task.stream.discontinuity) - { - cellDmux.warning("dmuxSetStream (beginning)"); - for (u32 i = 0; i < std::size(esALL); i++) - { - if (esALL[i]) - { - esALL[i]->reset(); - } - } - } - - stream = task.stream; - //cellDmux.notice("*** stream updated(addr=0x%x, size=0x%x, discont=%d, userdata=0x%llx)", - //stream.addr, stream.size, stream.discontinuity, stream.userdata); - break; - } - - case dmuxResetStream: - case dmuxResetStreamAndWaitDone: - { - // demuxing stopped - if (is_running.exchange(false)) - { - // callback - auto dmuxMsg = vm::ptr::make(memAddr + (cb_add ^= 16)); - dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE; - dmuxMsg->supplementalInfo = stream.userdata; - cbFunc(*this, id, dmuxMsg, cbArg); - lv2_obj::sleep(*this); - - stream = {}; - - is_working = false; - } - - break; - } - - case dmuxEnableEs: - { - ElementaryStream& es = *task.es.es_ptr; - - // TODO: uncomment when ready to use - //if ((es.fidMajor & -0x10) == 0xe0 && es.fidMinor == 0 && es.sup1 == 1 && !es.sup2) - //{ - // esAVC[es.fidMajor % 16] = task.es.es_ptr; - //} - //else if ((es.fidMajor & -0x10) == 0xe0 && es.fidMinor == 0 && !es.sup1 && !es.sup2) - //{ - // esM2V[es.fidMajor % 16] = task.es.es_ptr; - //} - //else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0 && !es.sup1 && !es.sup2) - //{ - // esATX[es.fidMinor % 16] = task.es.es_ptr; - //} - //else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0x20 && !es.sup1 && !es.sup2) - //{ - // esDATA[es.fidMinor % 16] = task.es.es_ptr; - //} - //else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0x30 && !es.sup1 && !es.sup2) - //{ - // esAC3[es.fidMinor % 16] = task.es.es_ptr; - //} - //else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0x40 && !es.sup1 && !es.sup2) - //{ - // esPCM[es.fidMinor % 16] = task.es.es_ptr; - //} - //else - { - fmt::throw_exception("dmuxEnableEs: unknown filter (0x%x, 0x%x, 0x%x, 0x%x)", es.fidMajor, es.fidMinor, es.sup1, es.sup2); - } - es.dmux = this; - break; - } - - case dmuxDisableEs: - { - ElementaryStream& es = *task.es.es_ptr; - if (es.dmux != this) - { - fmt::throw_exception("dmuxDisableEs: invalid elementary stream"); - } - - for (u32 i = 0; i < std::size(esALL); i++) - { - if (esALL[i] == &es) - { - esALL[i] = nullptr; - } - } - es.dmux = nullptr; - idm::remove(task.es.es); - break; - } - - case dmuxFlushEs: - { - ElementaryStream& es = *task.es.es_ptr; - - const u32 old_size = ::size32(es.raw_data); - if (old_size && (es.fidMajor & -0x10) == 0xe0) - { - // TODO (it's only for AVC, some ATX data may be lost) - while (es.isfull(old_size)) - { - if (Emu.IsStopped() || is_closed) break; - - std::this_thread::sleep_for(1ms); // hack - } - - es.push_au(old_size, es.last_dts, es.last_pts, stream.userdata, false, 0); - - // callback - auto esMsg = vm::ptr::make(memAddr + (cb_add ^= 16)); - esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; - esMsg->supplementalInfo = stream.userdata; - es.cbFunc(*this, id, es.id, esMsg, es.cbArg); - lv2_obj::sleep(*this); - } - - if (!es.raw_data.empty()) - { - cellDmux.error("dmuxFlushEs: 0x%x bytes lost (es_id=%d)", ::size32(es.raw_data), es.id); - } - - // callback - auto esMsg = vm::ptr::make(memAddr + (cb_add ^= 16)); - esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE; - esMsg->supplementalInfo = stream.userdata; - es.cbFunc(*this, id, es.id, esMsg, es.cbArg); - lv2_obj::sleep(*this); - break; - } - - case dmuxResetEs: - { - task.es.es_ptr->reset(); - break; - } - - case dmuxClose: - { - break; - } - - default: - { - fmt::throw_exception("Demuxer thread error: unknown task (0x%x)", +task.type); - } - } - } - - is_finished = true; - } -}; - - -PesHeader::PesHeader(DemuxerStream& stream) - : pts(CODEC_TS_INVALID) - , dts(CODEC_TS_INVALID) - , size(0) - , has_ts(false) - , is_ok(false) -{ - u16 header; - if (!stream.get(header)) - { - fmt::throw_exception("End of stream (header)"); - } - if (!stream.get(size)) - { - fmt::throw_exception("End of stream (size)"); - } - if (!stream.check(size)) - { - fmt::throw_exception("End of stream (size=%d)", size); - } - - u8 pos = 0; - while (pos++ < size) - { - u8 v; - if (!stream.get(v)) - { - return; // should never occur - } - - if (v == 0xff) // skip padding bytes - { - continue; - } - - if ((v & 0xf0) == 0x20 && (size - pos) >= 4) // pts only - { - pos += 4; - pts = stream.get_ts(v); - has_ts = true; - } - else if ((v & 0xf0) == 0x30 && (size - pos) >= 9) // pts and dts - { - pos += 5; - pts = stream.get_ts(v); - stream.get(v); - has_ts = true; - - if ((v & 0xf0) != 0x10) - { - cellDmux.error("PesHeader(): dts not found (v=0x%x, size=%d, pos=%d)", v, size, pos - 1); - stream.skip(size - pos); - return; - } - pos += 4; - dts = stream.get_ts(v); - } - else - { - cellDmux.warning("PesHeader(): unknown code (v=0x%x, size=%d, pos=%d)", v, size, pos - 1); - stream.skip(size - pos); - pos = size; - break; - } - } - - is_ok = true; -} - -ElementaryStream::ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr cbFunc, u32 cbArg, u32 spec) - : put(utils::align(addr, 128)) - , dmux(dmux) - , memAddr(utils::align(addr, 128)) - , memSize(size - (addr - memAddr)) - , fidMajor(fidMajor) - , fidMinor(fidMinor) - , sup1(sup1) - , sup2(sup2) - , cbFunc(cbFunc) - , cbArg(cbArg) - , spec(spec) -{ -} - -bool ElementaryStream::is_full(u32 space) -{ - if (released < put_count) - { - if (entries.is_full()) - { - return true; - } - - u32 first = 0; - if (!entries.peek(first, 0, &dmux->is_closed) || !first) - { - fmt::throw_exception("entries.peek() failed"); - } - else if (first >= put) - { - return first - put < space + 128; - } - else if (put + space + 128 > memAddr + memSize) - { - return first - memAddr < space + 128; - } - else - { - return false; - } - } - else - { - return false; - } -} - -bool ElementaryStream::isfull(u32 space) -{ - std::lock_guard lock(m_mutex); - return is_full(space); -} - -void ElementaryStream::push_au(u32 size, u64 dts, u64 pts, u64 userdata, bool rap, u32 specific) -{ - u32 addr; - { - std::lock_guard lock(m_mutex); - ensure(!is_full(size)); - - if (put + size + 128 > memAddr + memSize) - { - put = memAddr; - } - - std::memcpy(vm::base(put + 128), raw_data.data(), size); - raw_data.erase(raw_data.begin(), raw_data.begin() + size); - - auto info = vm::ptr::make(put); - info->auAddr = put + 128; - info->auSize = size; - info->dts.lower = static_cast(dts); - info->dts.upper = static_cast(dts >> 32); - info->pts.lower = static_cast(pts); - info->pts.upper = static_cast(pts >> 32); - info->isRap = rap; - info->reserved = 0; - info->userData = userdata; - - auto spec = vm::ptr::make(put + u32{sizeof(CellDmuxAuInfoEx)}); - *spec = specific; - - auto inf = vm::ptr::make(put + 64); - inf->auAddr = put + 128; - inf->auSize = size; - inf->dtsLower = static_cast(dts); - inf->dtsUpper = static_cast(dts >> 32); - inf->ptsLower = static_cast(pts); - inf->ptsUpper = static_cast(pts >> 32); - inf->auMaxSize = 0; // ????? - inf->userData = userdata; - - addr = put; - - put = utils::align(put + 128 + size, 128); - - put_count++; - } - - ensure(entries.push(addr, &dmux->is_closed)); -} - -void ElementaryStream::push(DemuxerStream& stream, u32 size) -{ - auto const old_size = raw_data.size(); - - raw_data.resize(old_size + size); - - std::memcpy(raw_data.data() + old_size, vm::base(stream.addr), size); // append bytes - - stream.skip(size); -} - -bool ElementaryStream::release() -{ - std::lock_guard lock(m_mutex); - if (released >= put_count) - { - cellDmux.fatal("es::release() error: buffer is empty"); - return false; - } - if (released >= got_count) - { - cellDmux.fatal("es::release() error: buffer has not been seen yet"); - return false; - } - - u32 addr = 0; - if (!entries.pop(addr, &dmux->is_closed) || !addr) - { - cellDmux.fatal("es::release() error: entries.Pop() failed"); - return false; - } - - released++; - return true; -} - -bool ElementaryStream::peek(u32& out_data, bool no_ex, u32& out_spec, bool update_index) -{ - std::lock_guard lock(m_mutex); - if (got_count < released) - { - cellDmux.fatal("es::peek() error: got_count(%d) < released(%d) (put_count=%d)", got_count, released, put_count); - return false; - } - if (got_count >= put_count) - { - return false; - } - - u32 addr = 0; - if (!entries.peek(addr, got_count - released, &dmux->is_closed) || !addr) - { - cellDmux.fatal("es::peek() error: entries.Peek() failed"); - return false; - } - - out_data = no_ex ? addr + 64 : addr; - out_spec = addr + sizeof(CellDmuxAuInfoEx); - - if (update_index) - { - got_count++; - } - return true; -} - -void ElementaryStream::reset() -{ - std::lock_guard lock(m_mutex); - put = memAddr; - entries.clear(); - put_count = 0; - got_count = 0; - released = 0; - raw_data.clear(); - raw_pos = 0; -} - -void dmuxQueryAttr(u32 /* info_addr, may be 0 */, vm::ptr attr) -{ - attr->demuxerVerLower = 0x280000; // TODO: check values - attr->demuxerVerUpper = 0x260000; - attr->memSize = 0x10000; // 0x3e8e6 from ps3 -} - -void dmuxQueryEsAttr(u32 /* info, may be 0 */, vm::cptr esFilterId, u32 /*esSpecificInfo*/, vm::ptr attr) -{ - if (esFilterId->filterIdMajor >= 0xe0) - { - attr->memSize = 0x500000; // 0x45fa49 from ps3 - } - else - { - attr->memSize = 0x7000; // 0x73d9 from ps3 - } - - cellDmux.warning("*** filter(0x%x, 0x%x, 0x%x, 0x%x)", esFilterId->filterIdMajor, esFilterId->filterIdMinor, esFilterId->supplementalInfo1, esFilterId->supplementalInfo2); -} - -error_code cellDmuxQueryAttr(vm::cptr type, vm::ptr attr) -{ - cellDmux.warning("cellDmuxQueryAttr(type=*0x%x, attr=*0x%x)", type, attr); - - if (type->streamType != CELL_DMUX_STREAM_TYPE_PAMF) - { - return CELL_DMUX_ERROR_ARG; - } - - dmuxQueryAttr(0, attr); - return CELL_OK; -} - -error_code cellDmuxQueryAttr2(vm::cptr type2, vm::ptr attr) -{ - cellDmux.warning("cellDmuxQueryAttr2(demuxerType2=*0x%x, demuxerAttr=*0x%x)", type2, attr); - - if (type2->streamType != CELL_DMUX_STREAM_TYPE_PAMF) - { - return CELL_DMUX_ERROR_ARG; - } - - dmuxQueryAttr(type2->streamSpecificInfo, attr); - return CELL_OK; -} - -error_code cellDmuxOpen(vm::cptr type, vm::cptr res, vm::cptr cb, vm::ptr handle) -{ - cellDmux.warning("cellDmuxOpen(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle); - - if (type->streamType != CELL_DMUX_STREAM_TYPE_PAMF) - { - return CELL_DMUX_ERROR_ARG; - } - - // TODO: check demuxerResource and demuxerCb arguments - fmt::throw_exception("cellDmux disabled, use LLE."); -} - -error_code cellDmuxOpenEx(vm::cptr type, vm::cptr resEx, vm::cptr cb, vm::ptr handle) -{ - cellDmux.warning("cellDmuxOpenEx(type=*0x%x, resEx=*0x%x, cb=*0x%x, handle=*0x%x)", type, resEx, cb, handle); - - if (type->streamType != CELL_DMUX_STREAM_TYPE_PAMF) - { - return CELL_DMUX_ERROR_ARG; - } - - // TODO: check demuxerResourceEx and demuxerCb arguments - fmt::throw_exception("cellDmux disabled, use LLE."); -} - -error_code cellDmuxOpenExt(vm::cptr type, vm::cptr resEx, vm::cptr cb, vm::ptr handle) -{ - cellDmux.warning("cellDmuxOpenExt(type=*0x%x, resEx=*0x%x, cb=*0x%x, handle=*0x%x)", type, resEx, cb, handle); - - return cellDmuxOpenEx(type, resEx, cb, handle); -} - -error_code cellDmuxOpen2(vm::cptr type2, vm::cptr res2, vm::cptr cb, vm::ptr handle) -{ - cellDmux.warning("cellDmuxOpen2(type2=*0x%x, res2=*0x%x, cb=*0x%x, handle=*0x%x)", type2, res2, cb, handle); - - if (type2->streamType != CELL_DMUX_STREAM_TYPE_PAMF) - { - return CELL_DMUX_ERROR_ARG; - } - - // TODO: check demuxerType2, demuxerResource2 and demuxerCb arguments - fmt::throw_exception("cellDmux disabled, use LLE."); -} - -error_code cellDmuxClose(u32 handle) -{ - cellDmux.warning("cellDmuxClose(handle=0x%x)", handle); - - const auto dmux = idm::get_unlocked(handle); - - if (!dmux) - { - return CELL_DMUX_ERROR_ARG; - } - - dmux->is_closed = true; - dmux->job.try_push(DemuxerTask(dmuxClose)); - - while (!dmux->is_finished) - { - if (Emu.IsStopped()) - { - cellDmux.warning("cellDmuxClose(%d) aborted", handle); - return CELL_OK; - } - - std::this_thread::sleep_for(1ms); // hack - } - - idm::remove(handle); - return CELL_OK; -} - -error_code cellDmuxSetStream(u32 handle, u32 streamAddress, u32 streamSize, b8 discontinuity, u64 userData) -{ - cellDmux.trace("cellDmuxSetStream(handle=0x%x, streamAddress=0x%x, streamSize=%d, discontinuity=%d, userData=0x%llx)", handle, streamAddress, streamSize, discontinuity, userData); - - const auto dmux = idm::get_unlocked(handle); - - if (!dmux) - { - return CELL_DMUX_ERROR_ARG; - } - - if (dmux->is_running.exchange(true)) - { - //std::this_thread::sleep_for(1ms); // hack - return CELL_DMUX_ERROR_BUSY; - } - - DemuxerTask task(dmuxSetStream); - auto& info = task.stream; - info.addr = streamAddress; - info.size = streamSize; - info.discontinuity = discontinuity; - info.userdata = userData; - - dmux->job.push(task, &dmux->is_closed); - return CELL_OK; -} - -error_code cellDmuxResetStream(u32 handle) -{ - cellDmux.warning("cellDmuxResetStream(handle=0x%x)", handle); - - const auto dmux = idm::get_unlocked(handle); - - if (!dmux) - { - return CELL_DMUX_ERROR_ARG; - } - - dmux->job.push(DemuxerTask(dmuxResetStream), &dmux->is_closed); - return CELL_OK; -} - -error_code cellDmuxResetStreamAndWaitDone(u32 handle) -{ - cellDmux.warning("cellDmuxResetStreamAndWaitDone(handle=0x%x)", handle); - - const auto dmux = idm::get_unlocked(handle); - - if (!dmux) - { - return CELL_DMUX_ERROR_ARG; - } - - if (!dmux->is_running) + // This is frequently checked in here because the elementary stream could get disabled at any time by a different thread via cellDmuxDisableEs() or cellDmuxClose(). + if (!es_handle->is_enabled) { return CELL_OK; } - dmux->is_working = true; - - dmux->job.push(DemuxerTask(dmuxResetStreamAndWaitDone), &dmux->is_closed); - - while (dmux->is_running && dmux->is_working && !dmux->is_closed) // TODO: ensure that it is safe + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes, 0); ret != CELL_OK) { - if (Emu.IsStopped()) + fatal_err(es_handle->is_enabled, ret); + return 1; + } + + // Check if the access unit queue is full. One slot is reserved for the access unit produced by flushing the stream, so that flushing always succeeds. + if (!es_handle->is_enabled || es_handle->au_queue.allocated_size >= es_handle->au_queue.max_size - !es_handle->flush_started) + { + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes); ret != CELL_OK) { - cellDmux.warning("cellDmuxResetStreamAndWaitDone(%d) aborted", handle); - return CELL_OK; + fatal_err(es_handle->is_enabled, ret); + return 1; } - std::this_thread::sleep_for(1ms); // hack + + return !es_handle->is_enabled ? CELL_OK : not_an_error(1); // Disable error reporting if the queue is full. This is expected to happen frequently. } + DmuxAuInfo& _au_info = get_au_queue_elements(es_handle)[es_handle->au_queue.back].au_info; + + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes); ret != CELL_OK) + { + fatal_err(es_handle->is_enabled, ret); + return 1; + } + + _au_info.info = au_info->info; + std::memcpy(_au_info.specific_info.get_ptr(), au_info->specific_info.get_ptr(), au_info->specific_info_size); + + if (!es_handle->is_enabled) + { + return CELL_OK; + } + + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes, 0); ret != CELL_OK) + { + fatal_err(es_handle->is_enabled, ret); + return CELL_OK; // LLE returns CELL_OK + } + + if (!es_handle->is_enabled) + { + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes); ret != CELL_OK) + { + fatal_err(es_handle->is_enabled, ret); + } + + return CELL_OK; + } + + es_handle->au_queue.back = (es_handle->au_queue.back + 1) % es_handle->au_queue.max_size; + es_handle->au_queue.allocated_size++; + es_handle->au_queue.size++; + + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes); ret != CELL_OK) + { + fatal_err(es_handle->is_enabled, ret); + return CELL_OK; // LLE returns CELL_OK + } + + if (!es_handle->is_enabled) + { + return CELL_OK; + } + + const vm::var es_msg{{ .msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND, .supplementalInfo = es_handle->dmux_handle->user_data }}; + es_handle->es_cb.cbFunc(ppu, es_handle->dmux_handle, es_handle, es_msg, es_handle->es_cb.cbArg); + return CELL_OK; } -error_code cellDmuxQueryEsAttr(vm::cptr type, vm::cptr esFilterId, u32 esSpecificInfo, vm::ptr esAttr) +static error_code notify_es_flush_done(ppu_thread& ppu, vm::ptr core_es_handle, vm::ptr es_handle) { - cellDmux.warning("cellDmuxQueryEsAttr(demuxerType=*0x%x, esFilterId=*0x%x, esSpecificInfo=*0x%x, esAttr=*0x%x)", type, esFilterId, esSpecificInfo, esAttr); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - if (type->streamType != CELL_DMUX_STREAM_TYPE_PAMF) + if (!savestate_lock) { - return CELL_DMUX_ERROR_ARG; + ppu.state += cpu_flag::again; + return {}; } - // TODO: check esFilterId and esSpecificInfo correctly - dmuxQueryEsAttr(0, esFilterId, esSpecificInfo, esAttr); + cellDmux.notice("dmuxEsNotifyFlushDone(unk=*0x%x, es_handle=*0x%x)", core_es_handle, es_handle); + + ensure(!!es_handle); // Not checked on LLE + + if (!es_handle->dmux_handle->_this || !es_handle->is_enabled) + { + return CELL_OK; + } + + es_handle->flush_started = false; + + const vm::var es_msg{{ .msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE, .supplementalInfo = es_handle->dmux_handle->user_data }}; + es_handle->es_cb.cbFunc(ppu, es_handle->dmux_handle, es_handle, es_msg, es_handle->es_cb.cbArg); + return CELL_OK; } -error_code cellDmuxQueryEsAttr2(vm::cptr type2, vm::cptr esFilterId, u32 esSpecificInfo, vm::ptr esAttr) -{ - cellDmux.warning("cellDmuxQueryEsAttr2(type2=*0x%x, esFilterId=*0x%x, esSpecificInfo=*0x%x, esAttr=*0x%x)", type2, esFilterId, esSpecificInfo, esAttr); - if (type2->streamType != CELL_DMUX_STREAM_TYPE_PAMF) +static error_code query_attr(ppu_thread& ppu, vm::ptr demuxerAttr, vm::cptr streamSpecificInfo) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) { - return CELL_DMUX_ERROR_ARG; + ppu.state += cpu_flag::again; + return {}; } - // TODO: check demuxerType2, esFilterId and esSpecificInfo correctly - dmuxQueryEsAttr(type2->streamSpecificInfo, esFilterId, esSpecificInfo, esAttr); + const vm::var pamf_attr; + + if (const error_code ret = get_error(get_core_ops()->queryAttr(ppu, streamSpecificInfo, pamf_attr)); ret != CELL_OK) + { + return ret; + } + + demuxerAttr->memSize = utils::align(sizeof(DmuxContext) + (pamf_attr->maxEnabledEsNum * sizeof(vm::addr_t)) + sizeof(DmuxEsContext), alignof(DmuxContext)) + + pamf_attr->memSize + 0xf; + demuxerAttr->demuxerVerUpper = 0x260000; + demuxerAttr->demuxerVerLower = pamf_attr->version; + return CELL_OK; } -error_code cellDmuxEnableEs(u32 handle, vm::cptr esFilterId, vm::cptr esResourceInfo, vm::cptr esCb, u32 esSpecificInfo, vm::ptr esHandle) +error_code cellDmuxQueryAttr(ppu_thread& ppu, vm::cptr demuxerType, vm::ptr demuxerAttr) { - cellDmux.warning("cellDmuxEnableEs(handle=0x%x, esFilterId=*0x%x, esResourceInfo=*0x%x, esCb=*0x%x, esSpecificInfo=*0x%x, esHandle=*0x%x)", handle, esFilterId, esResourceInfo, esCb, esSpecificInfo, esHandle); + cellDmux.notice("cellDmuxQueryAttr(demuxerType=*0x%x, demuxerAttr=*0x%x)", demuxerType, demuxerAttr); - const auto dmux = idm::get_unlocked(handle); - - if (!dmux) + if (!demuxerType || !demuxerAttr || demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF) { return CELL_DMUX_ERROR_ARG; } - // TODO: check esFilterId, esResourceInfo, esCb and esSpecificInfo correctly + return query_attr(ppu, demuxerAttr, vm::null); +} - const auto es = idm::make_ptr(dmux.get(), esResourceInfo->memAddr, esResourceInfo->memSize, - esFilterId->filterIdMajor, esFilterId->filterIdMinor, esFilterId->supplementalInfo1, esFilterId->supplementalInfo2, - esCb->cbFunc, esCb->cbArg, esSpecificInfo); +error_code cellDmuxQueryAttr2(ppu_thread& ppu, vm::cptr demuxerType2, vm::ptr demuxerAttr) +{ + cellDmux.notice("cellDmuxQueryAttr2(demuxerType2=*0x%x, demuxerAttr=*0x%x)", demuxerType2, demuxerAttr); - *esHandle = es->id; + if (!demuxerType2 || !demuxerAttr || demuxerType2->streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } - cellDmux.warning("*** New ES(dmux=0x%x, addr=0x%x, size=0x%x, filter={0x%x, 0x%x, 0x%x, 0x%x}, cb=0x%x, arg=0x%x, spec=0x%x): id = 0x%x", - handle, es->memAddr, es->memSize, es->fidMajor, es->fidMinor, es->sup1, es->sup2, es->cbFunc, es->cbArg, es->spec, es->id); + return query_attr(ppu, demuxerAttr, demuxerType2->streamSpecificInfo); +} - DemuxerTask task(dmuxEnableEs); - task.es.es = es->id; - task.es.es_ptr = es.get(); +static error_code open(ppu_thread& ppu, vm::cptr demuxerType, vm::cptr demuxerResource, vm::cptr demuxerResourceEx, + vm::cptr demuxerCb, vm::cptr streamSpecificInfo, vm::pptr demuxerHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + const vm::var type{{ .streamType = demuxerType->streamType, .streamSpecificInfo = streamSpecificInfo }}; + const vm::var attr; + + if (const error_code ret = cellDmuxQueryAttr2(ppu, type, attr); ret != CELL_OK) + { + return ret; + } + + if (attr->memSize > demuxerResource->memSize) + { + return CELL_DMUX_ERROR_ARG; + } + + const vm::var core_attr; + + if (const error_code ret = get_error(get_core_ops()->queryAttr(ppu, streamSpecificInfo, core_attr)); ret != CELL_OK) + { + return ret; + } + + const auto handle = vm::ptr::make(utils::align(demuxerResource->memAddr.addr(), alignof(DmuxContext))); + const u32 es_handles_size = core_attr->maxEnabledEsNum * sizeof(vm::addr_t); + const auto core_mem_addr = vm::ptr::make(utils::align(handle.addr() + sizeof(DmuxContext) + es_handles_size, 0x10)); + + const vm::var core_resource = + {{ + .memAddr = core_mem_addr, + .memSize = demuxerResource->memSize - (core_mem_addr.addr() - demuxerResource->memAddr.addr()), + .ppuThreadPriority = demuxerResource->ppuThreadPriority, + .ppuThreadStackSize = demuxerResource->ppuThreadStackSize, + .spuThreadPriority = demuxerResource->spuThreadPriority, + .numOfSpus = demuxerResource->numOfSpus + }}; + + const vm::var res_spurs; + + if (demuxerResourceEx) + { + res_spurs->spurs = demuxerResourceEx->spurs; + res_spurs->priority = demuxerResourceEx->priority; + res_spurs->maxContention = demuxerResourceEx->maxContention; + } + + const auto demux_done_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(notify_demux_done))); + const auto prog_end_code_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(notify_prog_end_code))); + const auto fatal_err_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(notify_fatal_err))); + const vm::var> cb_demux_done{{ .cbFunc = demux_done_func, .cbArg = handle }}; + const vm::var> cb_prog_end_code{{ .cbFunc = prog_end_code_func, .cbArg = handle }}; + const vm::var> cb_fatal_err{{ .cbFunc = fatal_err_func, .cbArg = handle }}; + + const vm::var> core_handle; + + if (const error_code ret = get_error(get_core_ops()->open(ppu, streamSpecificInfo, core_resource, demuxerResourceEx ? +res_spurs : vm::null, + cb_demux_done, cb_prog_end_code, cb_fatal_err, core_handle)); + ret != CELL_OK) + { + return ret; + } + + handle->_this = handle; + handle->_this_size = sizeof(DmuxContext) + es_handles_size; + handle->version = core_attr->version; + handle->dmux_state = DMUX_STOPPED; + handle->dmux_type = *demuxerType; + handle->dmux_cb = *demuxerCb; + handle->stream_is_set = false; + handle->core_handle = *core_handle; + handle->version_ = core_attr->version; + handle->user_data = 0; + handle->max_enabled_es_num = core_attr->maxEnabledEsNum; + handle->enabled_es_num = 0; + + const vm::var mutex_attr = + {{ + .protocol = SYS_SYNC_PRIORITY, + .recursive = SYS_SYNC_NOT_RECURSIVE, + .pshared = SYS_SYNC_NOT_PROCESS_SHARED, + .adaptive = SYS_SYNC_NOT_ADAPTIVE, + .name_u64 = "_dx_mhd"_u64 + }}; + + if (const error_code ret = lv2_syscall(ppu, handle.ptr(&DmuxContext::_dx_mhd), mutex_attr); ret != CELL_OK) + { + return ret; + } + + *demuxerHandle = handle; - dmux->job.push(task, &dmux->is_closed); return CELL_OK; } -error_code cellDmuxDisableEs(u32 esHandle) +error_code cellDmuxOpen(ppu_thread& ppu, vm::cptr demuxerType, vm::cptr demuxerResource, vm::cptr demuxerCb, vm::pptr demuxerHandle) { - cellDmux.warning("cellDmuxDisableEs(esHandle=0x%x)", esHandle); + cellDmux.notice("cellDmuxOpen(demuxerType=*0x%x, demuxerResource=*0x%x, demuxerCb=*0x%x, handle=*0x%x)", demuxerType, demuxerResource, demuxerCb, demuxerHandle); - const auto es = idm::get_unlocked(esHandle); - - if (!es) + if (!demuxerType || demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF + || !demuxerResource || !demuxerResource->memAddr || demuxerResource->memSize == umax || demuxerResource->ppuThreadStackSize == umax + || !demuxerCb || !demuxerCb->cbFunc + || !demuxerHandle) { return CELL_DMUX_ERROR_ARG; } - DemuxerTask task(dmuxDisableEs); - task.es.es = esHandle; - task.es.es_ptr = es.get(); - - es->dmux->job.push(task, &es->dmux->is_closed); - return CELL_OK; + return open(ppu, demuxerType, demuxerResource, vm::null, demuxerCb, vm::null, demuxerHandle); } -error_code cellDmuxResetEs(u32 esHandle) +error_code cellDmuxOpenEx(ppu_thread& ppu, vm::cptr demuxerType, vm::cptr demuxerResourceEx, vm::cptr demuxerCb, vm::pptr demuxerHandle) { - cellDmux.trace("cellDmuxResetEs(esHandle=0x%x)", esHandle); + cellDmux.notice("cellDmuxOpenEx(demuxerType=*0x%x, demuxerResourceEx=*0x%x, demuxerCb=*0x%x, demuxerHandle=*0x%x)", demuxerType, demuxerResourceEx, demuxerCb, demuxerHandle); - const auto es = idm::get_unlocked(esHandle); - - if (!es) + if (!demuxerType || demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF + || !demuxerResourceEx || !demuxerResourceEx->memAddr || demuxerResourceEx->memSize == umax || demuxerResourceEx->ppuThreadStackSize == umax + || !demuxerResourceEx->spurs || demuxerResourceEx->maxContention == 0u + || (demuxerResourceEx->priority & 0xf0f0f0f0f0f0f0f0ull) != 0u // Each byte in priority must be less than 0x10 + || !demuxerCb + || !demuxerHandle) { return CELL_DMUX_ERROR_ARG; } - DemuxerTask task(dmuxResetEs); - task.es.es = esHandle; - task.es.es_ptr = es.get(); + const vm::var resource + {{ + .memAddr = demuxerResourceEx->memAddr, + .memSize = demuxerResourceEx->memSize, + .ppuThreadPriority = demuxerResourceEx->ppuThreadPriority, + .ppuThreadStackSize = demuxerResourceEx->ppuThreadStackSize, + .spuThreadPriority = 0xfa, + .numOfSpus = 1 + }}; - es->dmux->job.push(task, &es->dmux->is_closed); - return CELL_OK; + return open(ppu, demuxerType, resource, demuxerResourceEx, demuxerCb, vm::null, demuxerHandle); } -error_code cellDmuxGetAu(u32 esHandle, vm::ptr auInfo, vm::ptr auSpecificInfo) +error_code cellDmuxOpenExt(ppu_thread& ppu, vm::cptr demuxerType, vm::cptr demuxerResourceEx, vm::cptr demuxerCb, vm::pptr demuxerHandle) { - cellDmux.trace("cellDmuxGetAu(esHandle=0x%x, auInfo=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfo, auSpecificInfo); + cellDmux.notice("cellDmuxOpenExt(demuxerType=*0x%x, demuxerResourceEx=*0x%x, demuxerCb=*0x%x, demuxerHandle=*0x%x)", demuxerType, demuxerResourceEx, demuxerCb, demuxerHandle); - const auto es = idm::get_unlocked(esHandle); - - if (!es) - { - return CELL_DMUX_ERROR_ARG; - } - - u32 info; - u32 spec; - if (!es->peek(info, true, spec, true)) - { - return CELL_DMUX_ERROR_EMPTY; - } - - *auInfo = info; - *auSpecificInfo = spec; - return CELL_OK; + return cellDmuxOpenEx(ppu, demuxerType, demuxerResourceEx, demuxerCb, demuxerHandle); } -error_code cellDmuxPeekAu(u32 esHandle, vm::ptr auInfo, vm::ptr auSpecificInfo) +error_code cellDmuxOpen2(ppu_thread& ppu, vm::cptr demuxerType2, vm::cptr demuxerResource2, vm::cptr demuxerCb, vm::pptr demuxerHandle) { - cellDmux.trace("cellDmuxPeekAu(esHandle=0x%x, auInfo=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfo, auSpecificInfo); + cellDmux.notice("cellDmuxOpen2(demuxerType2=*0x%x, demuxerResource2=*0x%x, demuxerCb=*0x%x, demuxerHandle=*0x%x)", demuxerType2, demuxerResource2, demuxerCb, demuxerHandle); - const auto es = idm::get_unlocked(esHandle); - - if (!es) + if (!demuxerType2 || demuxerType2->streamType != CELL_DMUX_STREAM_TYPE_PAMF + || !demuxerResource2 + || !demuxerCb || !demuxerCb->cbFunc + || !demuxerHandle) { return CELL_DMUX_ERROR_ARG; } - u32 info; - u32 spec; - if (!es->peek(info, true, spec, false)) + const vm::var type{{ .streamType = CELL_DMUX_STREAM_TYPE_PAMF }}; + + if (demuxerResource2->isResourceEx) { - return CELL_DMUX_ERROR_EMPTY; + if (!demuxerResource2->resourceEx.memAddr || demuxerResource2->resourceEx.memSize == umax || demuxerResource2->resourceEx.ppuThreadStackSize == umax + || !demuxerResource2->resourceEx.spurs || demuxerResource2->resourceEx.maxContention == 0u + || (demuxerResource2->resourceEx.priority & 0xf0f0f0f0f0f0f0f0ull) != 0u) // Each byte in priority must be less than 0x10 + { + return CELL_DMUX_ERROR_ARG; + } + + const vm::var resource + {{ + .memAddr = demuxerResource2->resourceEx.memAddr, + .memSize = demuxerResource2->resourceEx.memSize, + .ppuThreadPriority = demuxerResource2->resourceEx.ppuThreadPriority, + .ppuThreadStackSize = demuxerResource2->resourceEx.ppuThreadStackSize, + .spuThreadPriority = 0xfa, + .numOfSpus = 1 + }}; + + return open(ppu, type, resource, demuxerResource2.ptr(&CellDmuxResource2::resourceEx), demuxerCb, demuxerType2->streamSpecificInfo, demuxerHandle); } - *auInfo = info; - *auSpecificInfo = spec; - return CELL_OK; + if (!demuxerResource2->resource.memAddr || demuxerResource2->resource.memSize == umax || demuxerResource2->resource.ppuThreadStackSize == umax) + { + return CELL_DMUX_ERROR_ARG; + } + + return open(ppu, type, demuxerResource2.ptr(&CellDmuxResource2::resource), vm::null, demuxerCb, demuxerType2->streamSpecificInfo, demuxerHandle); } -error_code cellDmuxGetAuEx(u32 esHandle, vm::ptr auInfoEx, vm::ptr auSpecificInfo) +static error_code disable_es(ppu_thread& ppu, DmuxEsContext& esHandle) { - cellDmux.trace("cellDmuxGetAuEx(esHandle=0x%x, auInfoEx=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfoEx, auSpecificInfo); - - const auto es = idm::get_unlocked(esHandle); - - if (!es) + if (const error_code ret = lv2_syscall(ppu, esHandle._dx_mes, 0); ret != CELL_OK) { - return CELL_DMUX_ERROR_ARG; + return ret; } - u32 info; - u32 spec; - if (!es->peek(info, false, spec, true)) + const error_code core_ret = get_core_ops()->disableEs(ppu, esHandle.core_es_handle); + + esHandle.is_enabled = false; + + if (const error_code ret = lv2_syscall(ppu, esHandle._dx_mes); ret != CELL_OK) { - return CELL_DMUX_ERROR_EMPTY; + return ret; } - *auInfoEx = info; - *auSpecificInfo = spec; - return CELL_OK; + error_code ret; + while ((ret = lv2_syscall(ppu, esHandle._dx_mes)) == static_cast(CELL_EBUSY)) + { + lv2_syscall(ppu, 200); + } + + if (ret != CELL_OK) + { + return ret; + } + + esHandle._this = vm::null; + + return get_error(core_ret); } -error_code cellDmuxPeekAuEx(u32 esHandle, vm::ptr auInfoEx, vm::ptr auSpecificInfo) +error_code cellDmuxClose(ppu_thread& ppu, vm::ptr demuxerHandle) { - cellDmux.trace("cellDmuxPeekAuEx(esHandle=0x%x, auInfoEx=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfoEx, auSpecificInfo); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - const auto es = idm::get_unlocked(esHandle); + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } - if (!es) + cellDmux.notice("cellDmuxClose(demuxerHandle=*0x%x)", demuxerHandle); + + if (!demuxerHandle || !demuxerHandle->_this || demuxerHandle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) { return CELL_DMUX_ERROR_ARG; } - u32 info; - u32 spec; - if (!es->peek(info, false, spec, false)) + demuxerHandle->_this = vm::null; + + if (const error_code ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd, 0); ret != CELL_OK) { - return CELL_DMUX_ERROR_EMPTY; + demuxerHandle->_this = demuxerHandle; + return ret; } - *auInfoEx = info; - *auSpecificInfo = spec; - return CELL_OK; + for (const vm::ptr es_handle : get_es_handles(demuxerHandle)) + { + if (const error_code ret = disable_es(ppu, *es_handle); ret != CELL_OK) + { + ensure(lv2_syscall(ppu, demuxerHandle->_dx_mhd) == CELL_OK); // Not checked on LLE + demuxerHandle->_this = demuxerHandle; + return ret; + } + + es_handle->dmux_handle = vm::null; + demuxerHandle->enabled_es_num--; + } + + error_code ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + ret = ret ? ret : get_error(get_core_ops()->close(ppu, demuxerHandle->core_handle)); + ret = ret ? ret : lv2_syscall(ppu, demuxerHandle->_dx_mhd); + + if (ret != CELL_OK) + { + demuxerHandle->_this = demuxerHandle; + } + + return ret; } -error_code cellDmuxReleaseAu(u32 esHandle) +error_code cellDmuxSetStream(ppu_thread& ppu, vm::ptr demuxerHandle, vm::cptr streamAddress, u32 streamSize, b8 discontinuity, u64 userData) { - cellDmux.trace("cellDmuxReleaseAu(esHandle=0x%x)", esHandle); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - const auto es = idm::get_unlocked(esHandle); + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } - if (!es) + cellDmux.trace("cellDmuxSetStream(demuxerHandle=*0x%x, streamAddress=*0x%x, streamSize=0x%x, discontinuity=%d, userData=0x%llx)", + demuxerHandle, streamAddress, streamSize, +discontinuity, userData); + + if (!demuxerHandle || !demuxerHandle->_this || streamSize == 0 || streamSize == umax || demuxerHandle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) { return CELL_DMUX_ERROR_ARG; } - if (!es->release()) + if (!(demuxerHandle->dmux_state & DMUX_STOPPED)) + { + return CELL_DMUX_ERROR_BUSY; + } + + if (const error_code ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd, 0); ret != CELL_OK) + { + return ret; + } + + if (const error_code ret = get_error(get_core_ops()->setStream(ppu, demuxerHandle->core_handle, streamAddress, streamSize, discontinuity, userData)); + ret != CELL_OK) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + return mutex_unlock_ret ? mutex_unlock_ret : ret; + } + + demuxerHandle->stream_is_set = true; + demuxerHandle->dmux_state = DMUX_RUNNING; + demuxerHandle->user_data = userData; + + return lv2_syscall(ppu, demuxerHandle->_dx_mhd); +} + +error_code cellDmuxResetStream(ppu_thread& ppu, vm::ptr demuxerHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("cellDmuxResetStream(demuxerHandle=*0x%x)", demuxerHandle); + + if (!demuxerHandle || !demuxerHandle->_this || demuxerHandle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd, 0); ret != CELL_OK) + { + return ret; + } + + const u32 dmux_status = demuxerHandle->dmux_state; + + if (const error_code ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); ret != CELL_OK) + { + return ret; + } + + if (!(dmux_status & DMUX_RUNNING) || !demuxerHandle->stream_is_set) { return CELL_DMUX_ERROR_SEQ; } + + if (const error_code ret = get_error(get_core_ops()->resetStream(ppu, demuxerHandle->core_handle)); ret != CELL_OK) + { + return ret; + } + + demuxerHandle->stream_is_set = false; + return CELL_OK; } -error_code cellDmuxFlushEs(u32 esHandle) +error_code cellDmuxResetStreamAndWaitDone(ppu_thread& ppu, vm::ptr demuxerHandle) { - cellDmux.warning("cellDmuxFlushEs(esHandle=0x%x)", esHandle); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - const auto es = idm::get_unlocked(esHandle); + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } - if (!es) + cellDmux.notice("cellDmuxResetStreamAndWaitDone(demuxerHandle=*0x%x)", demuxerHandle); + + if (!demuxerHandle || !demuxerHandle->_this || demuxerHandle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) { return CELL_DMUX_ERROR_ARG; } - DemuxerTask task(dmuxFlushEs); - task.es.es = esHandle; - task.es.es_ptr = es.get(); + if (const error_code ret = get_error(get_core_ops()->resetStreamAndWaitDone(ppu, demuxerHandle->core_handle)); ret != CELL_OK) + { + return ret; + } + + // LLE doesn't set DmuxContext::stream_is_set to false + + return CELL_OK; +} + +error_code cellDmuxQueryEsAttr(ppu_thread& ppu, vm::cptr demuxerType, vm::cptr esFilterId, vm::cptr esSpecificInfo, vm::ptr esAttr) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("cellDmuxQueryEsAttr(demuxerType=*0x%x, esFilterId=*0x%x, esSpecificInfo=*0x%x, esAttr=*0x%x)", demuxerType, esFilterId, esSpecificInfo, esAttr); + + if (!demuxerType || demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF || !esFilterId || !esAttr) + { + return CELL_DMUX_ERROR_ARG; + } + + const vm::var core_es_attr; + + if (const error_code ret = get_error(get_core_ops()->queryEsAttr(ppu, vm::make_var(*esFilterId), esSpecificInfo, core_es_attr)); + ret != CELL_OK) + { + return ret; + } + + esAttr->memSize = utils::align(sizeof(DmuxEsContext) + ((core_es_attr->auQueueMaxSize + 1) * (core_es_attr->specificInfoSize + sizeof(DmuxAuQueueElement))), alignof(DmuxEsContext)) + + core_es_attr->memSize + 0xf; + + return CELL_OK; +} + +error_code cellDmuxQueryEsAttr2(ppu_thread& ppu, vm::cptr demuxerType2, vm::cptr esFilterId, vm::cptr esSpecificInfo, vm::ptr esAttr) +{ + cellDmux.notice("cellDmuxQueryEsAttr2(demuxerType2=*0x%x, esFilterId=*0x%x, esSpecificInfo=*0x%x, esAttr=*0x%x)", demuxerType2, esFilterId, esSpecificInfo, esAttr); + + ensure(!!demuxerType2); // Not checked on LLE + + const vm::var demuxerType{{ .streamType = demuxerType2->streamType }}; + + return cellDmuxQueryEsAttr(ppu, demuxerType, esFilterId, esSpecificInfo, esAttr); +} + +error_code cellDmuxEnableEs(ppu_thread& ppu, vm::ptr demuxerHandle, vm::cptr esFilterId, vm::cptr esResourceInfo, + vm::cptr esCb, vm::cptr esSpecificInfo, vm::pptr esHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("cellDmuxEnableEs(demuxerHandle=*0x%x, esFilterId=*0x%x, esResourceInfo=*0x%x, esCb=*0x%x, esSpecificInfo=*0x%x, esHandle=**0x%x)", + demuxerHandle, esFilterId, esResourceInfo, esCb, esSpecificInfo, esHandle); + + if (!demuxerHandle || !demuxerHandle->_this || demuxerHandle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF + || !esFilterId + || !esResourceInfo || !esResourceInfo->memAddr || esResourceInfo->memSize == umax + || !esCb || !esCb->cbFunc + || !esHandle) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd, 0); ret != CELL_OK) + { + return ret; + } + + if (demuxerHandle->enabled_es_num >= demuxerHandle->max_enabled_es_num) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + return mutex_unlock_ret ? mutex_unlock_ret : CELL_DMUX_ERROR_ARG; + } + + const vm::var es_attr; + + if (const error_code ret = cellDmuxQueryEsAttr(ppu, demuxerHandle.ptr(&DmuxContext::dmux_type), esFilterId, esSpecificInfo, es_attr); ret != CELL_OK) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + return mutex_unlock_ret ? mutex_unlock_ret : ret; + } + + if (es_attr->memSize > esResourceInfo->memSize) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + return mutex_unlock_ret ? mutex_unlock_ret : CELL_DMUX_ERROR_ARG; + } + + const vm::var es_filter_id{ *esFilterId }; + const vm::var core_es_attr; + + if (const error_code ret = get_error(get_core_ops()->queryEsAttr(ppu, es_filter_id, esSpecificInfo, core_es_attr)); ret != CELL_OK) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + return mutex_unlock_ret ? mutex_unlock_ret : ret; + } + + core_es_attr->auQueueMaxSize++; // One extra slot for the access unit produced by flushing the stream, so that flushing always succeeds + + const auto es_handle = vm::ptr::make(utils::align(esResourceInfo->memAddr.addr(), alignof(DmuxEsContext))); + const u32 au_queue_elements_size = core_es_attr->auQueueMaxSize * (core_es_attr->specificInfoSize + sizeof(DmuxAuQueueElement)); + const auto core_mem_addr = vm::bptr::make(utils::align(es_handle.addr() + sizeof(DmuxEsContext) + au_queue_elements_size, 0x10)); + + const vm::var core_es_resource + {{ + .memAddr = core_mem_addr, + .memSize = esResourceInfo->memSize - (core_mem_addr.addr() - esResourceInfo->memAddr.addr()) + }}; + + const vm::var mutex_attr = + {{ + .protocol = SYS_SYNC_PRIORITY, + .recursive = SYS_SYNC_NOT_RECURSIVE, + .pshared = SYS_SYNC_NOT_PROCESS_SHARED, + .adaptive = SYS_SYNC_NOT_ADAPTIVE, + .name_u64 = "_dx_mes"_u64 + }}; + + if (const error_code ret = lv2_syscall(ppu, es_handle.ptr(&DmuxEsContext::_dx_mes), mutex_attr); ret != CELL_OK) + { + ensure(lv2_syscall(ppu, demuxerHandle->_dx_mhd) == CELL_OK); // Not checked on LLE + return ret; + } + + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes, 0); ret != CELL_OK) + { + ensure(lv2_syscall(ppu, es_handle->_dx_mes) == CELL_OK); // Not checked on LLE + ensure(lv2_syscall(ppu, demuxerHandle->_dx_mhd) == CELL_OK); // Not checked on LLE + return ret; + } + + const auto au_found_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(notify_es_au_found))); + const auto flush_done_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(notify_es_flush_done))); + const vm::var> cb_au_found{{ .cbFunc = au_found_func, .cbArg = es_handle }}; + const vm::var> cb_flush_done{{ .cbFunc = flush_done_func, .cbArg = es_handle }}; + + const vm::var> core_es_handle; + + if (const error_code ret = get_error(get_core_ops()->enableEs(ppu, demuxerHandle->core_handle, es_filter_id, core_es_resource, cb_au_found, cb_flush_done, + esSpecificInfo, core_es_handle)); + ret != CELL_OK) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, es_handle->_dx_mes); + const error_code mutex_destroy_ret = lv2_syscall(ppu, es_handle->_dx_mes); + + if (mutex_unlock_ret != CELL_OK) + { + ensure(lv2_syscall(ppu, demuxerHandle->_dx_mhd) == CELL_OK); // Not checked on LLE + return mutex_unlock_ret; + } + + if (mutex_destroy_ret != CELL_OK) + { + ensure(lv2_syscall(ppu, demuxerHandle->_dx_mhd) == CELL_OK); // Not checked on LLE + return mutex_destroy_ret; + } + + const error_code mutex_unlock_ret2 = lv2_syscall(ppu, demuxerHandle->_dx_mhd); + return mutex_unlock_ret2 ? mutex_unlock_ret2 : ret; + } + + es_handle->is_enabled = true; + es_handle->error_mem_size = 0; + es_handle->error_count = 0; + // es_handle->error_mem_addr is not initialized on LLE + es_handle->_this = es_handle; + es_handle->_this_size = sizeof(DmuxEsContext) + au_queue_elements_size; + es_handle->_this_index = demuxerHandle->enabled_es_num; + es_handle->dmux_handle = demuxerHandle; + es_handle->es_cb = *esCb; + es_handle->core_es_handle = *core_es_handle; + es_handle->flush_started = bf_t, 0, 1>{}; + es_handle->au_queue.max_size = core_es_attr->auQueueMaxSize; + es_handle->au_queue.allocated_size = 0; + es_handle->au_queue.size = 0; + es_handle->au_queue.front = 0; + es_handle->au_queue.back = 0; + es_handle->au_queue.allocated_back = 0; + + const vm::ptr au_queue_elements = get_au_queue_elements(es_handle); + + for (u32 i = 0; i < core_es_attr->auQueueMaxSize; i++) + { + au_queue_elements[i].index = i; + au_queue_elements[i].unk = 0; + au_queue_elements[i].au_info.info.auAddr = vm::null; + au_queue_elements[i].au_info.info.auMaxSize = 0; + au_queue_elements[i].au_info.specific_info.set(au_queue_elements.addr() + (core_es_attr->auQueueMaxSize * static_cast(sizeof(DmuxAuQueueElement))) + (i * core_es_attr->specificInfoSize)); + au_queue_elements[i].au_info.specific_info_size = core_es_attr->specificInfoSize; + } + + demuxerHandle->enabled_es_num++; + *get_es_handles(demuxerHandle).rbegin() = es_handle; + *esHandle = es_handle; + + if (const error_code ret = lv2_syscall(ppu, es_handle->_dx_mes); ret != CELL_OK) + { + ensure(lv2_syscall(ppu, es_handle->_dx_mes) == CELL_OK); // Not checked on LLE + ensure(lv2_syscall(ppu, demuxerHandle->_dx_mhd) == CELL_OK); // Not checked on LLE + return ret; + } + + return lv2_syscall(ppu, demuxerHandle->_dx_mhd); +} + +error_code cellDmuxDisableEs(ppu_thread& ppu, vm::ptr esHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("cellDmuxDisableEs(esHandle=*0x%x)", esHandle); + + if (!esHandle || !esHandle->_this || !esHandle->dmux_handle || esHandle->dmux_handle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd, 0); ret != CELL_OK) + { + return ret; + } + + if (const error_code ret = disable_es(ppu, *esHandle); ret != CELL_OK) + { + ensure(lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd) == CELL_OK); // Not checked on LLE + return ret; + } + + const std::span> es_handles = get_es_handles(esHandle->dmux_handle); + + std::shift_left(std::ranges::find(es_handles, static_cast>(esHandle)), es_handles.end(), 1); + + esHandle->dmux_handle->enabled_es_num--; + *es_handles.rbegin() = vm::null; + + return lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd); +} + +error_code cellDmuxResetEs(ppu_thread& ppu, vm::ptr esHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("cellDmuxResetEs(esHandle=*0x%x)", esHandle); + + if (!esHandle || !esHandle->_this || !esHandle->dmux_handle || esHandle->dmux_handle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd, 0); ret != CELL_OK) + { + return ret; + } + + const u32 dmux_status = esHandle->dmux_handle->dmux_state; + + if (const error_code ret = lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd); ret != CELL_OK) + { + return ret; + } + + if (dmux_status & DMUX_STOPPED) + { + return CELL_DMUX_ERROR_SEQ; + } + + if (const error_code ret = lv2_syscall(ppu, esHandle->_dx_mes, 0); ret != CELL_OK) + { + return ret; + } + + if (const error_code ret = get_error(get_core_ops()->resetEs(ppu, esHandle->core_es_handle)); ret != CELL_OK) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, esHandle->_dx_mes); + return mutex_unlock_ret ? mutex_unlock_ret : ret; + } + + const auto au_queue_elements = get_au_queue_elements(esHandle); + + for (s32 i = 0; i < esHandle->au_queue.max_size; i++) + { + au_queue_elements[i].index = i; + au_queue_elements[i].unk = 0; + au_queue_elements[i].au_info.info.auAddr = vm::null; + au_queue_elements[i].au_info.info.auMaxSize = 0; + } + + esHandle->error_mem_size = 0; + esHandle->error_count = 0; + esHandle->au_queue.allocated_size = 0; + esHandle->au_queue.size = 0; + esHandle->au_queue.front = 0; + esHandle->au_queue.back = 0; + esHandle->au_queue.allocated_back = 0; + + return lv2_syscall(ppu, esHandle->_dx_mes); +} + +template +static error_code pop_au(ppu_thread& ppu, vm::ptr esHandle, vm::cpptr auInfo, vm::cpptr auSpecificInfo) +{ + if (!esHandle || !esHandle->_this || !esHandle->dmux_handle || esHandle->dmux_handle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, esHandle->_dx_mes, 0); ret != CELL_OK) + { + return ret; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + + if (esHandle->au_queue.size <= 0) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, esHandle->_dx_mes); + return mutex_unlock_ret ? mutex_unlock_ret : CELL_DMUX_ERROR_EMPTY; + } + + const vm::ptr au_info = (get_au_queue_elements(esHandle) + esHandle->au_queue.front).ptr(&DmuxAuQueueElement::au_info); + + if (auInfo) + { + *auInfo = au_info.ptr(&DmuxAuInfo::info); + } + + if (auSpecificInfo) + { + *auSpecificInfo = au_info->specific_info; + } + + if constexpr (!is_peek) + { + esHandle->au_queue.front = (esHandle->au_queue.front + 1) % esHandle->au_queue.max_size; + esHandle->au_queue.size--; + } + + return lv2_syscall(ppu, esHandle->_dx_mes); +} + +error_code cellDmuxGetAu(ppu_thread& ppu, vm::ptr esHandle, vm::cpptr auInfo, vm::cpptr auSpecificInfo) +{ + cellDmux.trace("cellDmuxGetAu(esHandle=*0x%x, auInfo=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfo, auSpecificInfo); + + return pop_au(ppu, esHandle, auInfo, auSpecificInfo); +} + +error_code cellDmuxPeekAu(ppu_thread& ppu, vm::ptr esHandle, vm::cpptr auInfo, vm::cpptr auSpecificInfo) +{ + cellDmux.trace("cellDmuxPeekAu(esHandle=*0x%x, auInfo=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfo, auSpecificInfo); + + return pop_au(ppu, esHandle, auInfo, auSpecificInfo); +} + +error_code cellDmuxGetAuEx(ppu_thread& ppu, vm::ptr esHandle, vm::cpptr auInfoEx, vm::cpptr auSpecificInfo) +{ + cellDmux.trace("cellDmuxGetAuEx(esHandle=*0x%x, auInfoEx=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfoEx, auSpecificInfo); + + return pop_au(ppu, esHandle, auInfoEx, auSpecificInfo); +} + +error_code cellDmuxPeekAuEx(ppu_thread& ppu, vm::ptr esHandle, vm::cpptr auInfoEx, vm::cpptr auSpecificInfo) +{ + cellDmux.trace("cellDmuxPeekAuEx(esHandle=*0x%x, auInfoEx=**0x%x, auSpecificInfo=**0x%x)", esHandle, auInfoEx, auSpecificInfo); + + return pop_au(ppu, esHandle, auInfoEx, auSpecificInfo); +} + +error_code cellDmuxReleaseAu(ppu_thread& ppu, vm::ptr esHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.trace("cellDmuxReleaseAu(esHandle=*0x%x)", esHandle); + + if (!esHandle || !esHandle->_this || !esHandle->dmux_handle || esHandle->dmux_handle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, esHandle->_dx_mes, 0); ret != CELL_OK) + { + return ret; + } + + vm::bptr mem_addr; + u32 mem_size; + + if (esHandle->au_queue.allocated_size < 1) + { + if (esHandle->error_count == 0u) + { + const error_code mutex_unlock_ret = lv2_syscall(ppu, esHandle->_dx_mes); + return mutex_unlock_ret ? mutex_unlock_ret : CELL_DMUX_ERROR_SEQ; + } + + mem_addr = esHandle->error_mem_addr; + mem_size = esHandle->error_mem_size; + } + else + { + const DmuxAuInfo& au_info = get_au_queue_elements(esHandle)[esHandle->au_queue.allocated_back].au_info; + + mem_size = + esHandle->error_mem_size += au_info.info.auSize; + + if (esHandle->error_count == 0u) + { + mem_addr = au_info.info.auAddr; + } + else + { + mem_addr = esHandle->error_mem_addr; + } + + esHandle->au_queue.allocated_back = (esHandle->au_queue.allocated_back + 1) % esHandle->au_queue.max_size; + esHandle->au_queue.allocated_size--; + + if (esHandle->au_queue.allocated_size < esHandle->au_queue.size) + { + esHandle->au_queue.front = (esHandle->au_queue.front + 1) % esHandle->au_queue.max_size; + esHandle->au_queue.size--; + } + } + + if (const error_code ret = get_error(get_core_ops()->releaseAu(ppu, esHandle->core_es_handle, mem_addr, mem_size)); ret != CELL_OK) + { + if (esHandle->error_count == 0u) + { + esHandle->error_mem_addr = mem_addr; + } + + esHandle->error_count++; + + const error_code mutex_unlock_ret = lv2_syscall(ppu, esHandle->_dx_mes); + return mutex_unlock_ret ? mutex_unlock_ret : ret; + } + + esHandle->error_count = 0; + esHandle->error_mem_size = 0; + + return lv2_syscall(ppu, esHandle->_dx_mes); +} + +error_code cellDmuxFlushEs(ppu_thread& ppu, vm::ptr esHandle) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmux.notice("cellDmuxFlushEs(esHandle=*0x%x)", esHandle); + + if (!esHandle || !esHandle->_this || !esHandle->dmux_handle || esHandle->dmux_handle->dmux_type.streamType != CELL_DMUX_STREAM_TYPE_PAMF) + { + return CELL_DMUX_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd, 0); ret != CELL_OK) + { + return ret; + } + + const u32 dmux_state = esHandle->dmux_handle->dmux_state; + + if (const error_code ret = lv2_syscall(ppu, esHandle->dmux_handle->_dx_mhd); ret != CELL_OK) + { + return ret; + } + + if (!(dmux_state & DMUX_STOPPED)) + { + return CELL_DMUX_ERROR_SEQ; + } + + esHandle->flush_started = true; + + if (const error_code ret = get_error(get_core_ops()->flushEs(ppu, esHandle->core_es_handle)); ret != CELL_OK) + { + esHandle->flush_started = false; + return ret; + } - es->dmux->job.push(task, &es->dmux->is_closed); return CELL_OK; } @@ -1382,4 +1258,11 @@ DECLARE(ppu_module_manager::cellDmux)("cellDmux", []() REG_FUNC(cellDmux, cellDmuxPeekAuEx); REG_FUNC(cellDmux, cellDmuxReleaseAu); REG_FUNC(cellDmux, cellDmuxFlushEs); + + REG_HIDDEN_FUNC(notify_demux_done); + REG_HIDDEN_FUNC(notify_fatal_err); + REG_HIDDEN_FUNC(notify_prog_end_code); + + REG_HIDDEN_FUNC(notify_es_au_found); + REG_HIDDEN_FUNC(notify_es_flush_done); }); diff --git a/rpcs3/Emu/Cell/Modules/cellDmux.h b/rpcs3/Emu/Cell/Modules/cellDmux.h index 1767165283..3db8c63bee 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmux.h +++ b/rpcs3/Emu/Cell/Modules/cellDmux.h @@ -1,7 +1,8 @@ #pragma once #include "Emu/Memory/vm_ptr.h" -#include "cellPamf.h" +#include "Emu/Cell/ErrorCodes.h" +#include "Utilities/BitField.h" // Error Codes enum CellDmuxError :u32 @@ -18,6 +19,10 @@ enum CellDmuxStreamType : s32 CELL_DMUX_STREAM_TYPE_UNDEF = 0, CELL_DMUX_STREAM_TYPE_PAMF = 1, CELL_DMUX_STREAM_TYPE_TERMINATOR = 2, + + // Only used in cellSail + CELL_DMUX_STREAM_TYPE_MP4 = 0x81, + CELL_DMUX_STREAM_TYPE_AVI = 0x82 }; enum CellDmuxMsgType : s32 @@ -33,118 +38,6 @@ enum CellDmuxEsMsgType : s32 CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE = 1, }; -enum CellDmuxPamfM2vLevel : s32 -{ - CELL_DMUX_PAMF_M2V_MP_LL = 0, - CELL_DMUX_PAMF_M2V_MP_ML, - CELL_DMUX_PAMF_M2V_MP_H14, - CELL_DMUX_PAMF_M2V_MP_HL, -}; - -enum CellDmuxPamfAvcLevel : s32 -{ - CELL_DMUX_PAMF_AVC_LEVEL_2P1 = 21, - CELL_DMUX_PAMF_AVC_LEVEL_3P0 = 30, - CELL_DMUX_PAMF_AVC_LEVEL_3P1 = 31, - CELL_DMUX_PAMF_AVC_LEVEL_3P2 = 32, - CELL_DMUX_PAMF_AVC_LEVEL_4P1 = 41, - CELL_DMUX_PAMF_AVC_LEVEL_4P2 = 42, -}; - -struct CellDmuxPamfAuSpecificInfoM2v -{ - be_t reserved1; -}; - -struct CellDmuxPamfAuSpecificInfoAvc -{ - be_t reserved1; -}; - -struct CellDmuxPamfAuSpecificInfoLpcm -{ - u8 channelAssignmentInfo; - u8 samplingFreqInfo; - u8 bitsPerSample; -}; - -struct CellDmuxPamfAuSpecificInfoAc3 -{ - be_t reserved1; -}; - -struct CellDmuxPamfAuSpecificInfoAtrac3plus -{ - be_t reserved1; -}; - -struct CellDmuxPamfAuSpecificInfoUserData -{ - be_t reserved1; -}; - -struct CellDmuxPamfEsSpecificInfoM2v -{ - be_t profileLevel; -}; - -struct CellDmuxPamfEsSpecificInfoAvc -{ - be_t level; -}; - -struct CellDmuxPamfEsSpecificInfoLpcm -{ - be_t samplingFreq; - be_t numOfChannels; - be_t bitsPerSample; -}; - -struct CellDmuxPamfEsSpecificInfoAc3 -{ - be_t reserved1; -}; - -struct CellDmuxPamfEsSpecificInfoAtrac3plus -{ - be_t reserved1; -}; - -struct CellDmuxPamfEsSpecificInfoUserData -{ - be_t reserved1; -}; - -enum CellDmuxPamfSamplingFrequency : s32 -{ - CELL_DMUX_PAMF_FS_48K = 48000, -}; - -enum CellDmuxPamfBitsPerSample : s32 -{ - CELL_DMUX_PAMF_BITS_PER_SAMPLE_16 = 16, - CELL_DMUX_PAMF_BITS_PER_SAMPLE_24 = 24, -}; - -enum CellDmuxPamfLpcmChannelAssignmentInfo : s32 -{ - CELL_DMUX_PAMF_LPCM_CH_M1 = 1, - CELL_DMUX_PAMF_LPCM_CH_LR = 3, - CELL_DMUX_PAMF_LPCM_CH_LRCLSRSLFE = 9, - CELL_DMUX_PAMF_LPCM_CH_LRCLSCS1CS2RSLFE = 11, -}; - -enum CellDmuxPamfLpcmFs : s32 -{ - CELL_DMUX_PAMF_LPCM_FS_48K = 1, -}; - -enum CellDmuxPamfLpcmBitsPerSamples : s32 -{ - CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_16 = 1, - CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_24 = 3, -}; - struct CellDmuxMsg { be_t msgType; // CellDmuxMsgType @@ -160,24 +53,19 @@ struct CellDmuxEsMsg struct CellDmuxType { be_t streamType; // CellDmuxStreamType - be_t reserved[2]; -}; - -struct CellDmuxPamfSpecificInfo -{ - be_t thisSize; - b8 programEndCodeCb; + be_t reserved1; + be_t reserved2; }; struct CellDmuxType2 { - be_t streamType; // CellDmuxStreamType - be_t streamSpecificInfo; + be_t streamType; + vm::bcptr streamSpecificInfo; }; struct CellDmuxResource { - be_t memAddr; + vm::bptr memAddr; be_t memSize; be_t ppuThreadPriority; be_t ppuThreadStackSize; @@ -187,12 +75,12 @@ struct CellDmuxResource struct CellDmuxResourceEx { - be_t memAddr; + vm::bptr memAddr; be_t memSize; be_t ppuThreadPriority; be_t ppuThreadStackSize; - be_t spurs_addr; - u8 priority[8]; + vm::bptr spurs; // CellSpurs* + be_t priority; be_t maxContention; }; @@ -203,40 +91,30 @@ struct CellDmuxResourceSpurs be_t maxContention; }; -/* -struct CellDmuxResource2Ex -{ - b8 isResourceEx; //true - CellDmuxResourceEx resourceEx; -}; - -struct CellDmuxResource2NoEx -{ - b8 isResourceEx; //false - CellDmuxResource resource; -}; -*/ - struct CellDmuxResource2 { b8 isResourceEx; - be_t memAddr; - be_t memSize; - be_t ppuThreadPriority; - be_t ppuThreadStackSize; - be_t shit[4]; + + union + { + CellDmuxResource resource; + CellDmuxResourceEx resourceEx; + }; }; -using CellDmuxCbMsg = u32(u32 demuxerHandle, vm::ptr demuxerMsg, u32 cbArg); +struct DmuxContext; +struct DmuxEsContext; -using CellDmuxCbEsMsg = u32(u32 demuxerHandle, u32 esHandle, vm::ptr esMsg, u32 cbArg); +using CellDmuxCbMsg = u32(vm::ptr demuxerHandle, vm::cptr demuxerMsg, vm::ptr cbArg); + +using CellDmuxCbEsMsg = u32(vm::ptr demuxerHandle, vm::ptr esHandle, vm::cptr esMsg, vm::ptr cbArg); // Used for internal callbacks as well template struct DmuxCb { vm::bptr cbFunc; - be_t cbArg; + vm::bptr cbArg; }; using CellDmuxCb = DmuxCb; @@ -250,42 +128,114 @@ struct CellDmuxAttr be_t demuxerVerLower; }; +struct CellDmuxPamfAttr +{ + be_t maxEnabledEsNum; + be_t version; + be_t memSize; +}; + struct CellDmuxEsAttr { be_t memSize; }; +struct CellDmuxPamfEsAttr +{ + be_t auQueueMaxSize; + be_t memSize; + be_t specificInfoSize; +}; + struct CellDmuxEsResource { - be_t memAddr; + vm::bptr memAddr; be_t memSize; }; struct CellDmuxAuInfo { - be_t auAddr; + vm::bptr auAddr; be_t auSize; be_t auMaxSize; - be_t userData; - be_t ptsUpper; - be_t ptsLower; - be_t dtsUpper; - be_t dtsLower; -}; - -struct CellDmuxAuInfoEx -{ - be_t auAddr; - be_t auSize; - be_t reserved; b8 isRap; be_t userData; CellCodecTimeStamp pts; CellCodecTimeStamp dts; }; -struct CellDmuxPamfAttr; -struct CellDmuxPamfEsAttr; +using CellDmuxAuInfoEx = CellDmuxAuInfo; + +struct DmuxAuInfo +{ + CellDmuxAuInfo info; + vm::bptr specific_info; + be_t specific_info_size; +}; + +struct DmuxAuQueueElement +{ + be_t index; + u8 unk; // unused + DmuxAuInfo au_info; +}; + +CHECK_SIZE(DmuxAuQueueElement, 0x38); + +enum DmuxState : u32 +{ + DMUX_STOPPED = 1 << 0, + DMUX_RUNNING = 1 << 1, +}; + +struct alignas(0x10) DmuxContext // CellDmuxHandle = DmuxContext* +{ + vm::bptr _this; + be_t _this_size; + be_t version; + be_t dmux_state; + CellDmuxType dmux_type; + CellDmuxCb dmux_cb; + b8 stream_is_set; + vm::bptr core_handle; + be_t version_; // Same value as 'version' + be_t user_data; + be_t max_enabled_es_num; + be_t enabled_es_num; + be_t _dx_mhd; // sys_mutex_t + u8 reserved[0x7c]; +}; + +CHECK_SIZE_ALIGN(DmuxContext, 0xc0, 0x10); + +struct alignas(0x10) DmuxEsContext // CellDmuxEsHandle = DmuxEsContext* +{ + be_t _dx_mes; // sys_mutex_t + be_t is_enabled; + be_t error_mem_size; + be_t error_count; + vm::bptr error_mem_addr; + vm::bptr _this; + be_t _this_size; + be_t _this_index; + vm::bptr dmux_handle; + CellDmuxEsCb es_cb; + vm::bptr core_es_handle; + bf_t, 0, 1> flush_started; + + struct + { + be_t max_size; + be_t allocated_size; + be_t size; + be_t front; + be_t back; + be_t allocated_back; + } + au_queue; +}; + +CHECK_SIZE_ALIGN(DmuxEsContext, 0x50, 0x10); using DmuxNotifyDemuxDone = error_code(vm::ptr, u32, vm::ptr); using DmuxNotifyFatalErr = error_code(vm::ptr, u32, vm::ptr); @@ -301,13 +251,13 @@ using CellDmuxCoreOpResetStream = error_code(vm::ptr); using CellDmuxCoreOpCreateThread = error_code(vm::ptr); using CellDmuxCoreOpJoinThread = error_code(vm::ptr); using CellDmuxCoreOpSetStream = error_code(vm::ptr, vm::cptr, u32, b8, u64); -using CellDmuxCoreOpFreeMemory = error_code(vm::ptr, vm::ptr, u32); +using CellDmuxCoreOpReleaseAu = error_code(vm::ptr, vm::ptr, u32); using CellDmuxCoreOpQueryEsAttr = error_code(vm::cptr, vm::cptr, vm::ptr); using CellDmuxCoreOpEnableEs = error_code(vm::ptr, vm::cptr, vm::cptr, vm::cptr>, vm::cptr>, vm::cptr, vm::pptr); -using CellDmuxCoreOpDisableEs = u32(vm::ptr); -using CellDmuxCoreOpFlushEs = u32(vm::ptr); -using CellDmuxCoreOpResetEs = u32(vm::ptr); -using CellDmuxCoreOpResetStreamAndWaitDone = u32(vm::ptr); +using CellDmuxCoreOpDisableEs = error_code(vm::ptr); +using CellDmuxCoreOpFlushEs = error_code(vm::ptr); +using CellDmuxCoreOpResetEs = error_code(vm::ptr); +using CellDmuxCoreOpResetStreamAndWaitDone = error_code(vm::ptr); struct CellDmuxCoreOps { @@ -318,7 +268,7 @@ struct CellDmuxCoreOps vm::bptr createThread; vm::bptr joinThread; vm::bptr setStream; - vm::bptr freeMemory; + vm::bptr releaseAu; vm::bptr queryEsAttr; vm::bptr enableEs; vm::bptr disableEs; diff --git a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp index 70162d4031..ecf59db508 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp +++ b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp @@ -1,121 +1,2860 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "Emu/IdManager.h" +#include "Emu/Cell/lv2/sys_cond.h" +#include "Emu/Cell/lv2/sys_memory.h" +#include "Emu/Cell/lv2/sys_mutex.h" +#include "Emu/Cell/lv2/sys_ppu_thread.h" +#include "Emu/Cell/lv2/sys_sync.h" +#include "sysPrxForUser.h" +#include "util/asm.hpp" -#include "cellDmux.h" #include "cellDmuxPamf.h" - +#include +#include vm::gvar g_cell_dmux_core_ops_pamf; vm::gvar g_cell_dmux_core_ops_raw_es; LOG_CHANNEL(cellDmuxPamf) +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](CellDmuxPamfError value) + { + switch (value) + { + STR_CASE(CELL_DMUX_PAMF_ERROR_BUSY); + STR_CASE(CELL_DMUX_PAMF_ERROR_ARG); + STR_CASE(CELL_DMUX_PAMF_ERROR_UNKNOWN_STREAM); + STR_CASE(CELL_DMUX_PAMF_ERROR_NO_MEMORY); + STR_CASE(CELL_DMUX_PAMF_ERROR_FATAL); + } + + return unknown; + }); +} + +inline std::pair dmuxPamfStreamIdToTypeChannel(u16 stream_id, u16 private_stream_id) +{ + if ((stream_id & 0xf0) == 0xe0) + { + return { DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO, stream_id & 0x0f }; + } + + if ((stream_id & 0xff) != 0xbd) + { + return { DMUX_PAMF_STREAM_TYPE_INDEX_INVALID, 0 }; + } + + switch (private_stream_id & 0xf0) + { + case 0x40: return { DMUX_PAMF_STREAM_TYPE_INDEX_LPCM, private_stream_id & 0x0f }; + case 0x30: return { DMUX_PAMF_STREAM_TYPE_INDEX_AC3, private_stream_id & 0x0f }; + case 0x00: return { DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX, private_stream_id & 0x0f }; + case 0x20: return { DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA, private_stream_id & 0x0f }; + default: return { DMUX_PAMF_STREAM_TYPE_INDEX_INVALID, 0 }; + } +} + + +// SPU thread + +void dmux_pamf_base::output_queue::pop_back(u32 au_size) +{ + ensure(back - au_size >= buffer.data(), "Invalid au_size"); + back -= au_size; +} + +void dmux_pamf_base::output_queue::pop_back(u8* au_addr) +{ + ensure(au_addr >= buffer.data() && au_addr < std::to_address(buffer.end()), "Invalid au_addr"); + + // If au_begin is in front of the back pointer, unwrap the back pointer (there are no more access units behind the back pointer) + if (au_addr > back) + { + wrap_pos = buffer.data(); + } + + back = au_addr; +} + +void dmux_pamf_base::output_queue::pop_front(u32 au_size) +{ + ensure(front + au_size <= std::to_address(buffer.end()), "Invalid au_size"); + front += au_size; + + // When front reaches wrap_pos, unwrap the queue + if (wrap_pos != buffer.data() && wrap_pos <= front) + { + ensure(wrap_pos == front, "Invalid au_size"); + front = buffer.data(); + wrap_pos = buffer.data(); + } +} + +void dmux_pamf_base::output_queue::push_unchecked(const access_unit_chunk& au_chunk) +{ + std::ranges::copy(au_chunk.cached_data, back); + std::ranges::copy(au_chunk.data, back + au_chunk.cached_data.size()); + back += au_chunk.data.size() + au_chunk.cached_data.size(); +} + +bool dmux_pamf_base::output_queue::push(const access_unit_chunk& au_chunk, const std::function& on_fatal_error) +{ + // If there are any unconsumed access units behind the back pointer, the distance between the front and back pointers is the remaining capacity, + // otherwise the distance between the end of the buffer and the back pointer is the remaining capacity + if (wrap_pos == buffer.data()) + { + // Since it was already checked if there is enough space for au_max_size, this can only occur if the current access unit is larger than au_max_size + if (au_chunk.data.size() + au_chunk.cached_data.size() > static_cast(std::to_address(buffer.end()) - back)) + { + cellDmuxPamf.error("Access unit larger than specified maximum access unit size"); + on_fatal_error(); + return false; + } + } + else if (au_chunk.data.size() + au_chunk.cached_data.size() + 0x10 > static_cast(front - back)) // + sizeof(v128) because of SPU shenanigans probably + { + return false; + } + + push_unchecked(au_chunk); + return true; +} + +bool dmux_pamf_base::output_queue::prepare_next_au(u32 au_max_size) +{ + // LLE always checks the distance between the end of the buffer and the back pointer, even if the back pointer is wrapped around and there are unconsumed access units behind it + if (std::to_address(buffer.end()) - back < au_max_size) + { + // Can't wrap the back pointer around again as long as there are unconsumed access units behind it + if (wrap_pos != buffer.data()) + { + return false; + } + + wrap_pos = back; + back = buffer.data(); + } + + return true; +} + +void dmux_pamf_base::elementary_stream::flush_es() +{ + if (current_au.accumulated_size != 0) + { + ensure(au_queue.get_free_size() >= cache.size()); + au_queue.push_unchecked({ {}, cache }); + + current_au.accumulated_size += static_cast(cache.size()); + + ctx.on_au_found(get_stream_id().first, get_stream_id().second, user_data, { au_queue.peek_back(current_au.accumulated_size), current_au.accumulated_size }, current_au.pts, current_au.dts, + current_au.rap, au_specific_info_size, current_au.au_specific_info_buf); + } + + reset(); + + while (!ctx.on_flush_done(get_stream_id().first, get_stream_id().second, user_data)) {} // The flush_done event is repeatedly fired until it succeeds +} + +void dmux_pamf_base::elementary_stream::reset_es(u8* au_addr) +{ + if (!au_addr) + { + reset(); + au_queue.clear(); + } + else + { + au_queue.pop_back(au_addr); + } +} + +void dmux_pamf_base::elementary_stream::discard_access_unit() +{ + au_queue.pop_back(current_au.accumulated_size - static_cast(au_chunk.data.size() + au_chunk.cached_data.size())); + reset(); + cache.clear(); +} + +template +u32 dmux_pamf_base::elementary_stream::parse_audio_stream_header(std::span pes_packet_data) +{ + u32 extra_header_size_unk = 0; // No clue what this is, I have not found a single instance in any PAMF stream where it is something other than zero + + if (!au_size_unk) // For some reason, LLE uses the member that stores the size of user data access units here as bool + { + // Not checked on LLE + if (pes_packet_data.size() < sizeof(u32)) + { + return umax; + } + + extra_header_size_unk = read_from_ptr>(pes_packet_data) & extra_header_size_unk_mask; + au_size_unk = true; + } + + return extra_header_size_unk + sizeof(u32); +} + +bool dmux_pamf_base::elementary_stream::process_pes_packet_data() +{ + ensure(pes_packet_data, "set_pes_packet_data() should be used before process_stream()"); + + for (;;) + { + switch (state) + { + case state::initial: + if (stream_chunk.empty()) + { + pes_packet_data.reset(); + return true; + } + + // Parse the current stream section and increment the reading position by the amount that was consumed + stream_chunk = stream_chunk.subspan(parse_stream(stream_chunk)); + + current_au.accumulated_size += static_cast(au_chunk.data.size() + au_chunk.cached_data.size()); + + // If the beginning of a new access unit was found, set the current timestamps and rap indicator + if (!current_au.timestamps_rap_set && (current_au.state == access_unit::state::commenced || current_au.state == access_unit::state::m2v_sequence + || (current_au.state == access_unit::state::complete && au_chunk.cached_data.empty()))) + { + set_au_timestamps_rap(); + } + + state = state::pushing_au_queue; + [[fallthrough]]; + + case state::pushing_au_queue: + if (!au_chunk.data.empty() || !au_chunk.cached_data.empty()) + { + if (!au_queue.push(au_chunk, std::bind_front(&dmux_pamf_base::on_fatal_error, &ctx))) + { + ctx.on_au_queue_full(); + return false; + } + + au_chunk.data = {}; + au_chunk.cached_data.clear(); + } + + // This happens if the distance between two delimiters is greater than the size indicated in the info header of the stream. + if (current_au.state == access_unit::state::size_mismatch) + { + // LLE cuts off one byte from the beginning of the current PES packet data and then starts over. + pes_packet_data = pes_packet_data->subspan<1>(); + stream_chunk = *pes_packet_data; + + // It also removes the entire current access unit from the queue, even if it began in an earlier PES packet + au_queue.pop_back(current_au.accumulated_size); + current_au.accumulated_size = 0; + + state = state::initial; + continue; + } + + state = state::notifying_au_found; + [[fallthrough]]; + + case state::notifying_au_found: + if (current_au.state == access_unit::state::complete && !ctx.on_au_found(get_stream_id().first, get_stream_id().second, user_data, + { au_queue.peek_back(current_au.accumulated_size), current_au.accumulated_size }, current_au.pts, current_au.dts, current_au.rap, au_specific_info_size, current_au.au_specific_info_buf)) + { + return false; + } + + state = state::preparing_for_next_au; + [[fallthrough]]; + + case state::preparing_for_next_au: + if (current_au.state == access_unit::state::complete) + { + if (!au_queue.prepare_next_au(au_max_size)) + { + ctx.on_au_queue_full(); + return false; + } + + current_au = {}; + } + + state = state::initial; + } + } +} + +template +u32 dmux_pamf_base::video_stream::parse_stream(std::span stream) +{ + if (current_au.state != access_unit::state::none && (avc || current_au.state != access_unit::state::m2v_sequence)) + { + current_au.state = access_unit::state::incomplete; + } + + // Concatenate the cache of the previous stream section and the beginning of the current section + std::array buf{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; // Prevent false positives (M2V pic start code ends with 0x00) + ensure(cache.size() <= 3, "The size of the cache should never exceed three bytes"); + std::ranges::copy(cache, buf.begin()); + std::copy_n(stream.begin(), std::min(sizeof(u32) - 1, stream.size()), buf.begin() + cache.size()); // Not entirely accurate: LLE always reads three bytes from the stream, even if it is smaller than that + + auto au_chunk_begin = stream.begin(); + s32 cache_idx = 0; + auto stream_it = stream.begin(); + [&] + { + // Search for delimiter in cache + for (; cache_idx < static_cast(cache.size()); cache_idx++) + { + if (const be_t code = read_from_ptr>(buf.data(), cache_idx); + (avc && code == AVC_AU_DELIMITER) || (!avc && (code == M2V_PIC_START || code == M2V_SEQUENCE_HEADER || code == M2V_SEQUENCE_END))) + { + if (current_au.state != access_unit::state::none && (avc || current_au.state != access_unit::state::m2v_sequence)) + { + // The sequence end code is included in the access unit + // LLE increments the stream pointer instead of the cache index, which will cause the access unit to be corrupted at the end + if (!avc && code == M2V_SEQUENCE_END) + { + cellDmuxPamf.warning("M2V sequence end code in cache"); + stream_it += std::min(sizeof(u32), stream.size()); // Not accurate, LLE always increments by four, regardless of the stream size + } + + current_au.state = access_unit::state::complete; + return; + } + + // If current_au.state is none and there was a delimiter found here, then LLE outputs the entire cache, even if the access unit starts at cache_idx > 0 + + current_au.state = avc || code == M2V_PIC_START ? access_unit::state::commenced : access_unit::state::m2v_sequence; + } + } + + // Search for delimiter in stream + for (; stream_it <= stream.end() - sizeof(u32); stream_it++) + { + if (const be_t code = read_from_ptr>(stream_it); + (avc && code == AVC_AU_DELIMITER) || (!avc && (code == M2V_PIC_START || code == M2V_SEQUENCE_HEADER || code == M2V_SEQUENCE_END))) + { + if (current_au.state != access_unit::state::none && (avc || current_au.state != access_unit::state::m2v_sequence)) + { + stream_it += !avc && code == M2V_SEQUENCE_END ? sizeof(u32) : 0; // The sequence end code is included in the access unit + current_au.state = access_unit::state::complete; + return; + } + + au_chunk_begin = avc || current_au.state == access_unit::state::none ? stream_it : au_chunk_begin; + current_au.state = avc || code == M2V_PIC_START ? access_unit::state::commenced : access_unit::state::m2v_sequence; + } + } + }(); + + if (current_au.state != access_unit::state::none) + { + au_chunk.data = { au_chunk_begin, stream_it }; + std::copy_n(cache.begin(), cache_idx, std::back_inserter(au_chunk.cached_data)); + } + + cache.erase(cache.begin(), cache.begin() + cache_idx); + + // Cache the end of the stream if an access unit wasn't completed. There could be the beginning of a delimiter in the last three bytes + if (current_au.state != access_unit::state::complete) + { + std::copy(stream_it, stream.end(), std::back_inserter(cache)); + } + + return static_cast((current_au.state != access_unit::state::complete || stream_it > stream.end() ? stream.end() : stream_it) - stream.begin()); +} + +u32 dmux_pamf_base::lpcm_stream::parse_stream_header(std::span pes_packet_data, [[maybe_unused]] s8 pts_dts_flag) +{ + // Not checked on LLE + if (pes_packet_data.size() < sizeof(u8) + 0x10) + { + return umax; + } + + std::memcpy(au_specific_info_buf.data(), &pes_packet_data[1], au_specific_info_buf.size()); + return parse_audio_stream_header<0x7ff>(pes_packet_data); +} + +u32 dmux_pamf_base::lpcm_stream::parse_stream(std::span stream) +{ + if (current_au.state == access_unit::state::none) + { + current_au.au_specific_info_buf = au_specific_info_buf; + } + + if (au_max_size - current_au.accumulated_size > stream.size()) + { + au_chunk.data = stream; + current_au.state = current_au.state == access_unit::state::none ? access_unit::state::commenced : access_unit::state::incomplete; + } + else + { + au_chunk.data = stream.first(au_max_size - current_au.accumulated_size); + current_au.state = access_unit::state::complete; + } + + return static_cast(au_chunk.data.size()); +} + +template +u32 dmux_pamf_base::audio_stream::parse_stream(std::span stream) +{ + const auto parse_au_size = [](be_t data) -> u16 + { + if constexpr (ac3) + { + if (const u8 fscod = data >> 14, frmsizecod = data >> 8 & 0x3f; fscod < 3 && frmsizecod < 38) + { + return AC3_FRMSIZE_TABLE[fscod][frmsizecod] * sizeof(s16); + } + } + else if ((data & 0x3ff) < 0x200) + { + return ((data & 0x3ff) + 1) * 8 + ATRACX_ATS_HEADER_SIZE; + } + + return 0; + }; + + if (current_au.state != access_unit::state::none) + { + current_au.state = access_unit::state::incomplete; + } + + // Concatenate the cache of the previous stream section and the beginning of the current section + std::array buf{}; + ensure(cache.size() <= 3, "The size of the cache should never exceed three bytes"); + std::ranges::copy(cache, buf.begin()); + std::copy_n(stream.begin(), std::min(sizeof(u16) - 1, stream.size()), buf.begin() + cache.size()); + + auto au_chunk_begin = stream.begin(); + s32 cache_idx = 0; + auto stream_it = stream.begin(); + [&] + { + // Search for delimiter in cache + for (; cache_idx <= static_cast(cache.size() + std::min(sizeof(u16) - 1, stream.size()) - sizeof(u16)); cache_idx++) + { + if (const be_t tmp = read_from_ptr>(buf.data(), cache_idx); current_au.size_info_offset != 0) + { + if (--current_au.size_info_offset == 0) + { + current_au.parsed_size = parse_au_size(tmp); + } + } + else if (tmp == SYNC_WORD) + { + if (current_au.state == access_unit::state::none) + { + // If current_au.state is none and there was a delimiter found here, then LLE outputs the entire cache, even if the access unit starts at cache_idx > 0 + + current_au.size_info_offset = ac3 ? sizeof(u16) * 2 : sizeof(u16); + current_au.state = access_unit::state::commenced; + } + else if (const u32 au_size = current_au.accumulated_size + cache_idx; au_size >= current_au.parsed_size) + { + current_au.state = au_size == current_au.parsed_size ? access_unit::state::complete : access_unit::state::size_mismatch; + return; + } + } + } + + // As long as the current access unit hasn't reached the size indicated in its header, we don't need to parse the stream + if (current_au.state != access_unit::state::none && current_au.size_info_offset == 0 && current_au.accumulated_size + cache.size() < current_au.parsed_size) + { + stream_it += std::min(current_au.parsed_size - current_au.accumulated_size - cache.size(), stream.size() - sizeof(u32)); + } + + // Search for delimiter in stream + for (; stream_it <= stream.end() - sizeof(u32); stream_it++) // LLE uses sizeof(u32), even though the delimiter is only two bytes large + { + if (const be_t tmp = read_from_ptr>(stream_it); current_au.size_info_offset != 0) + { + if (--current_au.size_info_offset == 0) + { + current_au.parsed_size = parse_au_size(tmp); + } + } + else if (tmp == SYNC_WORD) + { + if (current_au.state == access_unit::state::none) + { + au_chunk_begin = stream_it; + current_au.size_info_offset = ac3 ? sizeof(u16) * 2 : sizeof(u16); + current_au.state = access_unit::state::commenced; + } + else if (const u32 au_size = static_cast(current_au.accumulated_size + stream_it - au_chunk_begin + cache.size()); au_size >= current_au.parsed_size) + { + current_au.state = au_size == current_au.parsed_size ? access_unit::state::complete : access_unit::state::size_mismatch; + return; + } + } + } + }(); + + if (current_au.state != access_unit::state::none) + { + au_chunk.data = { au_chunk_begin, stream_it }; + std::copy_n(cache.begin(), cache_idx, std::back_inserter(au_chunk.cached_data)); + } + + cache.erase(cache.begin(), cache.begin() + cache_idx); + + // Cache the end of the stream if an access unit wasn't completed. There could be the beginning of a delimiter in the last three bytes + if (current_au.state != access_unit::state::complete && current_au.state != access_unit::state::size_mismatch) + { + std::copy(stream_it, stream.end(), std::back_inserter(cache)); + } + + return static_cast((current_au.state != access_unit::state::complete ? stream.end() : stream_it) - stream.begin()); +} + +u32 dmux_pamf_base::user_data_stream::parse_stream_header(std::span pes_packet_data, s8 pts_dts_flag) +{ + if (pts_dts_flag < 0) // PTS field exists + { + // Not checked on LLE + if (pes_packet_data.size() < 2 + sizeof(u32)) + { + return umax; + } + + au_size_unk = read_from_ptr>(pes_packet_data.begin(), 2) - sizeof(u32); + return 10; + } + + return 2; +} + +u32 dmux_pamf_base::user_data_stream::parse_stream(std::span stream) +{ + if (au_size_unk > stream.size()) + { + au_chunk.data = stream; + au_size_unk -= static_cast(stream.size()); + current_au.state = access_unit::state::commenced; // User data streams always use commenced + } + else + { + au_chunk.data = stream.first(au_size_unk); + au_size_unk = 0; + current_au.state = access_unit::state::complete; + } + + return static_cast(stream.size()); // Always consume the entire stream +} + +bool dmux_pamf_base::enable_es(u32 stream_id, u32 private_stream_id, bool is_avc, std::span au_queue_buffer, u32 au_max_size, bool raw_es, u32 user_data) +{ + const auto [type_idx, channel] = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id); + + if (type_idx == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID || elementary_stream::is_enabled(elementary_streams[type_idx][channel])) + { + return false; + } + + this->raw_es = raw_es; + pack_es_type_idx = type_idx; + + switch (type_idx) + { + case DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO: + elementary_streams[0][channel] = is_avc ? static_cast>(std::make_unique>(channel, au_max_size, *this, user_data, au_queue_buffer)) + : std::make_unique>(channel, au_max_size, *this, user_data, au_queue_buffer); + return true; + + case DMUX_PAMF_STREAM_TYPE_INDEX_LPCM: elementary_streams[1][channel] = std::make_unique(channel, au_max_size, *this, user_data, au_queue_buffer); return true; + case DMUX_PAMF_STREAM_TYPE_INDEX_AC3: elementary_streams[2][channel] = std::make_unique>(channel, au_max_size, *this, user_data, au_queue_buffer); return true; + case DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX: elementary_streams[3][channel] = std::make_unique>(channel, au_max_size, *this, user_data, au_queue_buffer); return true; + case DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA: elementary_streams[4][channel] = std::make_unique(channel, au_max_size, *this, user_data, au_queue_buffer); return true; + default: fmt::throw_exception("Unreachable"); + } +} + +bool dmux_pamf_base::disable_es(u32 stream_id, u32 private_stream_id) +{ + const auto [type_idx, channel] = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id); + + if (type_idx == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID || !elementary_stream::is_enabled(elementary_streams[type_idx][channel])) + { + return false; + } + + elementary_streams[type_idx][channel] = nullptr; + return true; +} + +bool dmux_pamf_base::release_au(u32 stream_id, u32 private_stream_id, u32 au_size) const +{ + const auto [type_idx, channel] = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id); + + if (type_idx == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID || !elementary_stream::is_enabled(elementary_streams[type_idx][channel])) + { + return false; + } + + elementary_streams[type_idx][channel]->release_au(au_size); + return true; +} + +bool dmux_pamf_base::flush_es(u32 stream_id, u32 private_stream_id) +{ + const auto [type_idx, channel] = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id); + + if (type_idx == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID || !elementary_stream::is_enabled(elementary_streams[type_idx][channel])) + { + return false; + } + + state = state::initial; + elementary_streams[type_idx][channel]->flush_es(); + return true; +} + +void dmux_pamf_base::set_stream(std::span stream, bool continuity) +{ + if (!continuity) + { + std::ranges::for_each(elementary_streams | std::views::join | std::views::filter(elementary_stream::is_enabled), &elementary_stream::discard_access_unit); + } + + state = state::initial; + + // Not checked on LLE, it would parse old memory contents or uninitialized memory if the size of the input stream set by the user is not a multiple of 0x800. + // Valid PAMF streams are always a multiple of 0x800 bytes large. + if ((stream.size() & 0x7ff) != 0) + { + cellDmuxPamf.warning("Invalid stream size"); + } + + this->stream = stream; + demux_done_notified = false; +} + +void dmux_pamf_base::reset_stream() +{ + std::ranges::for_each(elementary_streams | std::views::join | std::views::filter(elementary_stream::is_enabled), &elementary_stream::discard_access_unit); + state = state::initial; + stream.reset(); +} + +bool dmux_pamf_base::reset_es(u32 stream_id, u32 private_stream_id, u8* au_addr) +{ + const auto [type_idx, channel] = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id); + + if (type_idx == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID || !elementary_stream::is_enabled(elementary_streams[type_idx][channel])) + { + return false; + } + + if (!au_addr) + { + state = state::initial; + } + + elementary_streams[type_idx][channel]->reset_es(au_addr); + return true; +} + +bool dmux_pamf_base::process_next_pack() +{ + if (!stream) + { + demux_done_notified = demux_done_notified || on_demux_done(); + return true; + } + + switch (state) + { + case state::initial: + { + // Search for the next pack start code or prog end code + std::span pack; + + for (;;) + { + if (stream->size() < PACK_STUFFING_LENGTH_OFFSET + sizeof(u8)) + { + stream.reset(); + demux_done_notified = on_demux_done(); + return true; + } + + pack = stream->first(std::min(stream->size(), PACK_SIZE)); + stream = stream->subspan(std::min(stream->size(), PACK_SIZE)); + + // If the input stream is a raw elementary stream, skip everything MPEG-PS related and go straight to elementary stream parsing + if (raw_es) + { + if (elementary_stream::is_enabled(elementary_streams[pack_es_type_idx][0])) + { + elementary_streams[pack_es_type_idx][0]->set_pes_packet_data(pack); + } + + state = state::elementary_stream; + return true; + } + + // While LLE is actually searching the entire section for a pack start code or program end code, + // it doesn't set its current reading position to the address where it found the code, so it would bug out if there isn't one at the start of the section + + if (const be_t code = read_from_ptr>(pack); code == PACK_START) + { + break; + } + else if (code == PROG_END) + { + if (!on_prog_end()) + { + state = state::prog_end; + } + + return true; + } + + cellDmuxPamf.warning("No start code found at the beginning of the current section"); + } + + // Skip over pack header + const u8 pack_stuffing_length = read_from_ptr>(pack, PACK_STUFFING_LENGTH_OFFSET); + + // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store + if (PACK_STUFFING_LENGTH_OFFSET + sizeof(u8) + pack_stuffing_length + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8) > pack.size()) + { + cellDmuxPamf.error("Invalid pack stuffing length"); + return false; + } + + std::span current_pes_packet = pack.subspan(PACK_STUFFING_LENGTH_OFFSET + sizeof(u8) + pack_stuffing_length); + + if (read_from_ptr, 8, 24>>(current_pes_packet) != PACKET_START_CODE_PREFIX) + { + cellDmuxPamf.error("Invalid start code after pack header"); + return false; + } + + // Skip over system header if present + if (read_from_ptr>(current_pes_packet) == SYSTEM_HEADER) + { + const u32 system_header_length = read_from_ptr>(current_pes_packet.begin(), PES_PACKET_LENGTH_OFFSET) + PES_PACKET_LENGTH_OFFSET + sizeof(u16); + + // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store + if (system_header_length + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8) > current_pes_packet.size()) + { + cellDmuxPamf.error("Invalid system header length"); + return false; + } + + current_pes_packet = current_pes_packet.subspan(system_header_length); + + // The SPU thread isn't doing load + rotate here for 4-byte loading (in valid PAMF streams, the next start code after a system header is always 0x10 byte aligned) + const u32 offset_low = (current_pes_packet.data() - pack.data()) & 0xf; + current_pes_packet = { current_pes_packet.begin() - offset_low, current_pes_packet.end() }; + + if (const be_t code = read_from_ptr>(current_pes_packet); code >> 8 != PACKET_START_CODE_PREFIX) + { + cellDmuxPamf.error("Invalid start code after system header"); + return false; + } + else if (code == PRIVATE_STREAM_2) + { + // A system header is optionally followed by a private stream 2 + // The first two bytes of the stream are the stream id of a video stream. The next access unit of that stream is a random access point/keyframe + + const u16 pes_packet_length = read_from_ptr>(current_pes_packet.begin(), PES_PACKET_LENGTH_OFFSET) + PES_PACKET_LENGTH_OFFSET + sizeof(u16); + + // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store + if (pes_packet_length + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8) > current_pes_packet.size()) + { + cellDmuxPamf.error("Invalid private stream 2 length"); + return false; + } + + if (const u8 channel = read_from_ptr>(current_pes_packet.begin(), PES_PACKET_LENGTH_OFFSET + sizeof(u16)) & 0xf; + elementary_stream::is_enabled(elementary_streams[0][channel])) + { + elementary_streams[0][channel]->set_rap(); + } + + current_pes_packet = current_pes_packet.subspan(pes_packet_length); + } + } + + // Parse PES packet + // LLE only parses the first PES packet per pack (valid PAMF streams only have one PES packet per pack, not including the system header + private stream 2) + + const u32 pes_packet_start_code = read_from_ptr>(current_pes_packet); + + if (pes_packet_start_code >> 8 != PACKET_START_CODE_PREFIX) + { + cellDmuxPamf.error("Invalid start code"); + return false; + } + + const u16 pes_packet_length = read_from_ptr>(current_pes_packet.begin(), PES_PACKET_LENGTH_OFFSET) + PES_PACKET_LENGTH_OFFSET + sizeof(u16); + const u8 pes_header_data_length = read_from_ptr(current_pes_packet.begin(), PES_HEADER_DATA_LENGTH_OFFSET) + PES_HEADER_DATA_LENGTH_OFFSET + sizeof(u8); + + // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store + if (pes_packet_length > current_pes_packet.size() || pes_packet_length <= pes_header_data_length) + { + cellDmuxPamf.error("Invalid pes packet length"); + return false; + } + + const std::span pes_packet_data = current_pes_packet.subspan(pes_header_data_length, pes_packet_length - pes_header_data_length); + + const auto [type_idx, channel] = dmuxPamfStreamIdToTypeChannel(pes_packet_start_code, read_from_ptr(pes_packet_data)); + + if (type_idx == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID) + { + cellDmuxPamf.error("Invalid stream type"); + return false; + } + + pack_es_type_idx = type_idx; + pack_es_channel = channel; + + if (elementary_stream::is_enabled(elementary_streams[type_idx][channel])) + { + const s8 pts_dts_flag = read_from_ptr(current_pes_packet.begin(), PTS_DTS_FLAG_OFFSET); + + if (pts_dts_flag < 0) + { + // The timestamps should be unsigned, but are sign-extended from s32 to u64 on LLE. They probably forgot about integer promotion + const s32 PTS_32_30 = read_from_ptr>(current_pes_packet.begin(), 9); + const s32 PTS_29_15 = read_from_ptr, 1, 15>>(current_pes_packet.begin(), 10); + const s32 PTS_14_0 = read_from_ptr, 1, 15>>(current_pes_packet.begin(), 12); + + elementary_streams[type_idx][channel]->set_pts(PTS_32_30 << 30 | PTS_29_15 << 15 | PTS_14_0); // Bit 32 is discarded + } + + if (pts_dts_flag & 0x40) + { + const s32 DTS_32_30 = read_from_ptr>(current_pes_packet.begin(), 14); + const s32 DTS_29_15 = read_from_ptr, 1, 15>>(current_pes_packet.begin(), 15); + const s32 DTS_14_0 = read_from_ptr, 1, 15>>(current_pes_packet.begin(), 17); + + elementary_streams[type_idx][channel]->set_dts(DTS_32_30 << 30 | DTS_29_15 << 15 | DTS_14_0); // Bit 32 is discarded + } + + const usz stream_header_size = elementary_streams[type_idx][channel]->parse_stream_header(pes_packet_data, pts_dts_flag); + + // Not checked on LLE, the SPU task would just increment the reading position and read random data in the SPU local store + if (stream_header_size > pes_packet_data.size()) + { + cellDmuxPamf.error("Invalid stream header size"); + return false; + } + + elementary_streams[type_idx][channel]->set_pes_packet_data(pes_packet_data.subspan(stream_header_size)); + } + + state = state::elementary_stream; + [[fallthrough]]; + } + case state::elementary_stream: + { + if (!elementary_stream::is_enabled(elementary_streams[pack_es_type_idx][pack_es_channel]) || elementary_streams[pack_es_type_idx][pack_es_channel]->process_pes_packet_data()) + { + state = state::initial; + } + + return true; + } + case state::prog_end: + { + if (on_prog_end()) + { + state = state::initial; + } + + return true; + } + default: + fmt::throw_exception("Unreachable"); + } +} + +u32 dmux_pamf_base::get_enabled_es_count() const +{ + return static_cast(std::ranges::count_if(elementary_streams | std::views::join, elementary_stream::is_enabled)); +} + +bool dmux_pamf_spu_context::get_next_cmd(DmuxPamfCommand& lhs, bool new_stream) const +{ + cellDmuxPamf.trace("Getting next command"); + + if (cmd_queue->pop(lhs)) + { + cellDmuxPamf.trace("Command type: %d", static_cast(lhs.type.get())); + return true; + } + + if ((new_stream || has_work()) && !wait_for_au_queue && !wait_for_event_queue) + { + cellDmuxPamf.trace("No new command, continuing demuxing"); + return false; + } + + cellDmuxPamf.trace("No new command and nothing to do, waiting..."); + + cmd_queue->wait(); + + if (thread_ctrl::state() == thread_state::aborting) + { + return false; + } + + ensure(cmd_queue->pop(lhs)); + + cellDmuxPamf.trace("Command type: %d", static_cast(lhs.type.get())); + return true; +} + +bool dmux_pamf_spu_context::send_event(auto&&... args) const +{ + if (event_queue->size() >= max_enqueued_events) + { + return false; + } + + return ensure(event_queue->emplace(std::forward(args)..., event_queue_was_too_full)); +} + +void dmux_pamf_spu_context::operator()() // cellSpursMain() +{ + DmuxPamfCommand cmd; + + while (thread_ctrl::state() != thread_state::aborting) + { + if (get_next_cmd(cmd, new_stream)) + { + event_queue_was_too_full = wait_for_event_queue; + wait_for_event_queue = false; + wait_for_au_queue = false; + + ensure(cmd_result_queue->emplace(static_cast(cmd.type.value()) + 1)); + + switch (cmd.type) + { + case DmuxPamfCommandType::enable_es: + max_enqueued_events += 2; + enable_es(cmd.enable_es.stream_id, cmd.enable_es.private_stream_id, cmd.enable_es.is_avc, { cmd.enable_es.au_queue_buffer.get_ptr(), cmd.enable_es.au_queue_buffer_size }, + cmd.enable_es.au_max_size, cmd.enable_es.is_raw_es, cmd.enable_es.user_data); + break; + + case DmuxPamfCommandType::disable_es: + disable_es(cmd.disable_flush_es.stream_id, cmd.disable_flush_es.private_stream_id); + max_enqueued_events -= 2; + break; + + case DmuxPamfCommandType::set_stream: + new_stream = true; + break; + + case DmuxPamfCommandType::release_au: + release_au(cmd.release_au.stream_id, cmd.release_au.private_stream_id, cmd.release_au.au_size); + break; + + case DmuxPamfCommandType::flush_es: + flush_es(cmd.disable_flush_es.stream_id, cmd.disable_flush_es.private_stream_id); + break; + + case DmuxPamfCommandType::close: + while (!send_event(DmuxPamfEventType::close)) {} + return; + + case DmuxPamfCommandType::reset_stream: + reset_stream(); + break; + + case DmuxPamfCommandType::reset_es: + reset_es(cmd.reset_es.stream_id, cmd.reset_es.private_stream_id, cmd.reset_es.au_addr ? cmd.reset_es.au_addr.get_ptr() : nullptr); + break; + + case DmuxPamfCommandType::resume: + break; + + default: + cellDmuxPamf.error("Invalid command"); + return; + } + } + else if (thread_ctrl::state() == thread_state::aborting) + { + return; + } + + // Only set the new stream once the previous one has been entirely consumed + if (new_stream && !has_work()) + { + new_stream = false; + + DmuxPamfStreamInfo stream_info; + ensure(stream_info_queue->pop(stream_info)); + + set_stream({ stream_info.stream_addr.get_ptr(), stream_info.stream_size }, stream_info.continuity); + + // Delay demuxing a bit + // Prevents White Knight Chronicles II FMVs from freezing, since events are otherwise fired before the game has finished initializing FMV playback + thread_ctrl::wait_for(1'500); + } + + process_next_pack(); + } +} + +void dmux_pamf_base::elementary_stream::save(utils::serial& ar) +{ + // These need to be saved first since they need to be initialized in the constructor's initializer list + if (ar.is_writing()) + { + ar(au_max_size, user_data); + au_queue.save(ar); + } + + ar(state, au_size_unk, au_specific_info_buf, current_au, pts, dts, rap); + + if (state == state::pushing_au_queue) + { + ar(au_chunk.cached_data); + + if (ar.is_writing()) + { + ar(vm::get_addr(au_chunk.data.data()), static_cast(au_chunk.data.size())); + } + else + { + au_chunk.data = { vm::_ptr(ar.pop()), ar.pop() }; + } + } + + if (current_au.state != access_unit::state::complete) + { + ar(cache); + } + + bool save_stream = !!pes_packet_data; + ar(save_stream); + + if (save_stream) + { + if (ar.is_writing()) + { + ensure(stream_chunk.size() <= pes_packet_data->size()); + ar(vm::get_addr(pes_packet_data->data()), static_cast(pes_packet_data->size()), static_cast(stream_chunk.data() - pes_packet_data->data())); + } + else + { + pes_packet_data = { vm::_ptr(ar.pop()), ar.pop() }; + stream_chunk = { pes_packet_data->begin() + ar.pop(), pes_packet_data->end() }; + } + } +} + +void dmux_pamf_base::save_base(utils::serial& ar) +{ + bool stream_not_consumed = !!stream; + + ar(state, stream_not_consumed, demux_done_notified, pack_es_type_idx, raw_es); + + if (stream_not_consumed) + { + if (ar.is_writing()) + { + ar(vm::get_addr(stream->data()), static_cast(stream->size())); + } + else + { + stream = std::span{ vm::_ptr(ar.pop()), ar.pop() }; + } + } + + if (state == state::elementary_stream) + { + ar(pack_es_channel); + } + + std::array enabled_video_streams; + std::array avc_video_streams; + std::array enabled_lpcm_streams; + std::array enabled_ac3_streams; + std::array enabled_atracx_streams; + std::array enabled_user_data_streams; + + if (ar.is_writing()) + { + std::ranges::transform(elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO], enabled_video_streams.begin(), elementary_stream::is_enabled); + std::ranges::transform(elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO], avc_video_streams.begin(), [](auto& es){ return !!dynamic_cast*>(es.get()); }); + std::ranges::transform(elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_LPCM], enabled_lpcm_streams.begin(), elementary_stream::is_enabled); + std::ranges::transform(elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_AC3], enabled_ac3_streams.begin(), elementary_stream::is_enabled); + std::ranges::transform(elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX], enabled_atracx_streams.begin(), elementary_stream::is_enabled); + std::ranges::transform(elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA], enabled_user_data_streams.begin(), elementary_stream::is_enabled); + } + + ar(enabled_video_streams, avc_video_streams, enabled_lpcm_streams, enabled_ac3_streams, enabled_atracx_streams, enabled_user_data_streams); + + if (ar.is_writing()) + { + std::ranges::for_each(elementary_streams | std::views::join | std::views::filter(elementary_stream::is_enabled), [&](const auto& es){ es->save(ar); }); + } + else + { + for (u32 ch = 0; ch < 0x10; ch++) + { + if (enabled_video_streams[ch]) + { + elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO][ch] = avc_video_streams[ch] ? static_cast>(std::make_unique>(ar, ch, *this)) + : std::make_unique>(ar, ch, *this); + } + } + + for (u32 ch = 0; ch < 0x10; ch++) + { + if (enabled_lpcm_streams[ch]) + { + elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_LPCM][ch] = std::make_unique(ar, ch, *this); + } + } + + for (u32 ch = 0; ch < 0x10; ch++) + { + if (enabled_ac3_streams[ch]) + { + elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_AC3][ch] = std::make_unique>(ar, ch, *this); + } + } + + for (u32 ch = 0; ch < 0x10; ch++) + { + if (enabled_atracx_streams[ch]) + { + elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX][ch] = std::make_unique>(ar, ch, *this); + } + } + + for (u32 ch = 0; ch < 0x10; ch++) + { + if (enabled_user_data_streams[ch]) + { + elementary_streams[DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA][ch] = std::make_unique(ar, ch, *this); + } + } + } +} + +void dmux_pamf_spu_context::save(utils::serial& ar) +{ + USING_SERIALIZATION_VERSION(cellDmuxPamf); + ar(cmd_queue, new_stream); // The queues are contiguous in guest memory, so we only need to save the address of the first one + save_base(ar); +} + + +// PPU thread + +template +static auto lv2_syscall(ppu_thread& ppu, auto&&... args) +{ + const auto ret = Syscall(ppu, std::forward(args)...); + + if (ppu.test_stopped()) + { + ppu.state += cpu_flag::again; + } + + return ret; +} + +template +void DmuxPamfContext::send_spu_command_and_wait(ppu_thread& ppu, bool waiting_for_spu_state, auto&&... cmd_params) +{ + if (!waiting_for_spu_state) + { + // The caller is supposed to own the mutex until the SPU thread has consumed the command, so the queue should always be empty here + ensure(cmd_queue.emplace(type, std::forward(cmd_params)...), "The command queue wasn't empty"); + } + + lv2_obj::sleep(ppu); + + // Block until the SPU thread has consumed the command + cmd_result_queue.wait(); + + if (ppu.check_state()) + { + ppu.state += cpu_flag::again; + return; + } + + be_t result{}; + ensure(cmd_result_queue.pop(result), "The result queue was empty"); + ensure(result == static_cast(type) + 1, "The HLE SPU thread sent an invalid result"); +} + +DmuxPamfElementaryStream* DmuxPamfContext::find_es(u16 stream_id, u16 private_stream_id) +{ + const auto it = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first == DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO + ? std::ranges::find_if(elementary_streams | std::views::reverse, [&](const auto& es){ return es && es->stream_id == stream_id; }) + : std::ranges::find_if(elementary_streams | std::views::reverse, [&](const auto& es){ return es && es->stream_id == stream_id && es->private_stream_id == private_stream_id; }); + + return it != std::ranges::rend(elementary_streams) ? it->get_ptr() : nullptr; +} + +error_code DmuxPamfContext::wait_au_released_or_stream_reset(ppu_thread& ppu, u64 au_queue_full_bitset, b8& stream_reset_started, dmux_pamf_state& savestate) +{ + if (savestate == dmux_pamf_state::waiting_for_au_released) + { + goto label1_waiting_for_au_released_state; + } + + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + + if (au_queue_full_bitset) + { + cellDmuxPamf.trace("Access unit queue of elementary stream no. %d is full. Waiting for access unit to be released...", std::countr_zero(au_queue_full_bitset)); + + while (!(au_queue_full_bitset & au_released_bitset) && !stream_reset_requested) + { + savestate = dmux_pamf_state::waiting_for_au_released; + label1_waiting_for_au_released_state: + + if (lv2_syscall(ppu, cond, 0) != CELL_OK) + { + lv2_syscall(ppu, mutex); + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + } + + cellDmuxPamf.trace("Access unit released"); + } + + stream_reset_started = stream_reset_requested; + stream_reset_requested = false; + + au_released_bitset = 0; + + return lv2_syscall(ppu, mutex) != CELL_OK ? static_cast(CELL_DMUX_PAMF_ERROR_FATAL) : CELL_OK; +} + +template +error_code DmuxPamfContext::set_au_reset(ppu_thread& ppu) +{ + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + + std::ranges::for_each(elementary_streams | std::views::filter([](auto es){ return !!es; }), [](auto& reset_next_au) { reset_next_au = reset; }, &DmuxPamfElementaryStream::reset_next_au); + + return lv2_syscall(ppu, mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; +} + +template +error_code DmuxPamfContext::callback(ppu_thread& ppu, DmuxCb cb, auto&&... args) +{ + std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock.owns_lock()) + { + ppu.state += cpu_flag::again; + return {}; + } + + return cb.cbFunc(ppu, std::forward(args)..., cb.cbArg); +} + +void DmuxPamfContext::run_spu_thread() +{ + hle_spu_thread_id = idm::make(cmd_queue_addr, cmd_result_queue_addr, stream_info_queue_addr, event_queue_addr); +} + +void DmuxPamfContext::exec(ppu_thread& ppu) +{ + // This is repeated a lot in this function, in my opinion using a define here makes it more readable +#define RETURN_ON_CPU_FLAG_AGAIN()\ + if (ppu.state & cpu_flag::again)\ + return + + switch (savestate) + { + case dmux_pamf_state::initial: break; + case dmux_pamf_state::waiting_for_au_released: goto label1_waiting_for_au_released_state; + case dmux_pamf_state::waiting_for_au_released_error: goto label2_waiting_for_au_released_error_state; + case dmux_pamf_state::waiting_for_event: goto label3_waiting_for_event_state; + case dmux_pamf_state::starting_demux_done: goto label4_starting_demux_done_state; + case dmux_pamf_state::starting_demux_done_mutex_lock_error: goto label5_starting_demux_done_mutex_lock_error_state; + case dmux_pamf_state::starting_demux_done_mutex_unlock_error: goto label6_starting_demux_done_mutex_unlock_error_state; + case dmux_pamf_state::starting_demux_done_checking_stream_reset: goto label7_starting_demux_done_check_stream_reset_state; + case dmux_pamf_state::starting_demux_done_checking_stream_reset_error: goto label8_start_demux_done_check_stream_reset_error_state; + case dmux_pamf_state::setting_au_reset: goto label9_setting_au_reset_state; + case dmux_pamf_state::setting_au_reset_error: goto label10_setting_au_reset_error_state; + case dmux_pamf_state::processing_event: goto label11_processing_event_state; + case dmux_pamf_state::au_found_waiting_for_spu: goto label12_au_found_waiting_for_spu_state; + case dmux_pamf_state::unsetting_au_reset: goto label13_unsetting_au_reset_state; + case dmux_pamf_state::demux_done_notifying: goto label14_demux_done_notifying_state; + case dmux_pamf_state::demux_done_mutex_lock: goto label15_demux_done_mutex_lock_state; + case dmux_pamf_state::demux_done_cond_signal: goto label16_demux_done_cond_signal_state; + case dmux_pamf_state::resuming_demux_mutex_lock: goto label17_resuming_demux_mutex_lock_state; + case dmux_pamf_state::resuming_demux_waiting_for_spu: goto label18_resuming_demux_waiting_for_spu_state; + case dmux_pamf_state::sending_fatal_err: ; // Handled below + } + + for (;;) + { + if (savestate == dmux_pamf_state::sending_fatal_err) + { + callback(ppu, notify_fatal_err, _this, CELL_OK); + RETURN_ON_CPU_FLAG_AGAIN(); + } + + savestate = dmux_pamf_state::initial; + + stream_reset_started = false; + + // If the access unit queue of an enabled elementary stream is full, wait until the user releases an access unit or requests a stream reset before processing the next event + label1_waiting_for_au_released_state: + + if (wait_au_released_or_stream_reset(ppu, au_queue_full_bitset, stream_reset_started, savestate) != CELL_OK) + { + savestate = dmux_pamf_state::waiting_for_au_released_error; + label2_waiting_for_au_released_error_state: + + callback(ppu, notify_fatal_err, _this, CELL_OK); + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + // Wait for the next event + if (!event_queue.peek(event)) + { + savestate = dmux_pamf_state::waiting_for_event; + label3_waiting_for_event_state: + + cellDmuxPamf.trace("Waiting for the next event..."); + + lv2_obj::sleep(ppu); + event_queue.wait(); + + if (ppu.check_state()) + { + ppu.state += cpu_flag::again; + return; + } + + ensure(event_queue.peek(event)); + } + + cellDmuxPamf.trace("Event type: %d", static_cast(event.type.get())); + + // If the event is a demux done event, set the sequence state to resetting and check for a potential stream reset request again + if (event.type == DmuxPamfEventType::demux_done) + { + savestate = dmux_pamf_state::starting_demux_done; + label4_starting_demux_done_state: + + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + savestate = dmux_pamf_state::starting_demux_done_mutex_lock_error; + label5_starting_demux_done_mutex_lock_error_state: + + callback(ppu, notify_fatal_err, _this, CELL_OK); + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + sequence_state = DmuxPamfSequenceState::resetting; + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::starting_demux_done_mutex_unlock_error; + label6_starting_demux_done_mutex_unlock_error_state: + + callback(ppu, notify_fatal_err, _this, CELL_OK); + + RETURN_ON_CPU_FLAG_AGAIN(); + } + + if (!stream_reset_started) + { + savestate = dmux_pamf_state::starting_demux_done_checking_stream_reset; + label7_starting_demux_done_check_stream_reset_state: + + if (wait_au_released_or_stream_reset(ppu, 0, stream_reset_started, savestate) != CELL_OK) + { + savestate = dmux_pamf_state::starting_demux_done_checking_stream_reset_error; + label8_start_demux_done_check_stream_reset_error_state: + + callback(ppu, notify_fatal_err, _this, CELL_OK); + } + + RETURN_ON_CPU_FLAG_AGAIN(); + } + } + + // If the user requested a stream reset, set the reset flag for every enabled elementary stream + if (stream_reset_started) + { + stream_reset_in_progress = true; + + savestate = dmux_pamf_state::setting_au_reset; + label9_setting_au_reset_state: + + if (set_au_reset(ppu) != CELL_OK) + { + savestate = dmux_pamf_state::setting_au_reset_error; + label10_setting_au_reset_error_state: + + callback(ppu, notify_fatal_err, _this, CELL_OK); + } + + RETURN_ON_CPU_FLAG_AGAIN(); + } + + savestate = dmux_pamf_state::processing_event; + label11_processing_event_state: + + switch (event.type) + { + case DmuxPamfEventType::au_found: + { + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + label12_au_found_waiting_for_spu_state: + + DmuxPamfElementaryStream* const es = find_es(event.au_found.stream_id, event.au_found.private_stream_id); + + // If the elementary stream of the found access unit is not enabled, don't do anything + if (!es || es->_this.get_ptr() != es || es->es_id != event.au_found.user_data) + { + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + break; + } + + // If a stream reset was requested, don't notify the user of any found access units that are still in the event queue + // We need to send the SPU thread the address of the first found access unit for each elementary stream still in the event queue, + // so that it can remove the access units from the queue. + if (stream_reset_in_progress) + { + if (es->reset_next_au) + { + send_spu_command_and_wait(ppu, savestate == dmux_pamf_state::au_found_waiting_for_spu, + event.au_found.stream_id, event.au_found.private_stream_id, event.au_found.au_addr); + + if (ppu.state & cpu_flag::again) + { + savestate = dmux_pamf_state::au_found_waiting_for_spu; + return; + } + + es->reset_next_au = false; + } + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + break; + } + + const vm::var au_info; + au_info->addr = std::bit_cast>(event.au_found.au_addr); + au_info->size = event.au_found.au_size; + au_info->pts = event.au_found.pts; + au_info->dts = event.au_found.dts; + au_info->user_data = user_data; + au_info->specific_info = es->_this.ptr(&DmuxPamfElementaryStream::au_specific_info); + au_info->specific_info_size = es->au_specific_info_size; + au_info->is_rap = static_cast(event.au_found.is_rap); + + if (!is_raw_es && dmuxPamfStreamIdToTypeChannel(event.au_found.stream_id, event.au_found.private_stream_id).first == DMUX_PAMF_STREAM_TYPE_INDEX_LPCM) + { + es->au_specific_info[0] = read_from_ptr>(event.au_found.stream_header_buf); + es->au_specific_info[1] = read_from_ptr>(event.au_found.stream_header_buf); + es->au_specific_info[2] = read_from_ptr>(event.au_found.stream_header_buf, 1); + } + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + if (callback(ppu, es->notify_au_found, es->_this, au_info) != CELL_OK) + { + // If the callback returns an error, the access unit queue for this elementary stream is full + au_queue_full_bitset |= 1ull << es->this_index; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + break; + } + case DmuxPamfEventType::demux_done: + { + if (stream_reset_in_progress) + { + stream_reset_in_progress = false; + + savestate = dmux_pamf_state::unsetting_au_reset; + label13_unsetting_au_reset_state: + + if (set_au_reset(ppu) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + } + + savestate = dmux_pamf_state::demux_done_notifying; + label14_demux_done_notifying_state: + + callback(ppu, notify_demux_done, _this, CELL_OK); + + RETURN_ON_CPU_FLAG_AGAIN(); + + savestate = dmux_pamf_state::demux_done_mutex_lock; + label15_demux_done_mutex_lock_state: + + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + if (sequence_state == DmuxPamfSequenceState::resetting) + { + sequence_state = DmuxPamfSequenceState::dormant; + + savestate = dmux_pamf_state::demux_done_cond_signal; + label16_demux_done_cond_signal_state: + + if (lv2_syscall(ppu, cond) != CELL_OK) + { + lv2_syscall(ppu, mutex); + + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + } + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + break; + } + case DmuxPamfEventType::close: + { + while (event_queue.pop()){} // Clear the event queue + return; + } + case DmuxPamfEventType::flush_done: + { + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + DmuxPamfElementaryStream* const es = find_es(event.flush_done.stream_id, event.flush_done.private_stream_id); + const bool valid = es && es->_this.get_ptr() == es && es->es_id == event.flush_done.user_data; + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + if (valid) + { + callback(ppu, es->notify_flush_done, es->_this); + + RETURN_ON_CPU_FLAG_AGAIN(); + } + + break; + } + case DmuxPamfEventType::prog_end_code: + { + callback(ppu, notify_prog_end_code, _this); + + RETURN_ON_CPU_FLAG_AGAIN(); + + break; + } + case DmuxPamfEventType::fatal_error: + { + ensure(event_queue.pop()); + + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + default: + fmt::throw_exception("Invalid event"); + } + + ensure(event_queue.pop()); + + // If there are too many events enqueued, the SPU thread will stop demuxing until it receives a new command. + // Once the event queue size is reduced to two, send a resume command + if (enabled_es_num >= 0 && event_queue.size() == 2) + { + savestate = dmux_pamf_state::resuming_demux_mutex_lock; + label17_resuming_demux_mutex_lock_state: + + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + + RETURN_ON_CPU_FLAG_AGAIN(); + + if (enabled_es_num >= 0) + { + ensure(cmd_queue.emplace(DmuxPamfCommandType::resume)); + + savestate = dmux_pamf_state::resuming_demux_waiting_for_spu; + label18_resuming_demux_waiting_for_spu_state: + + lv2_obj::sleep(ppu); + cmd_result_queue.wait(); + + if (ppu.check_state()) + { + ppu.state += cpu_flag::again; + return; + } + + ensure(cmd_result_queue.pop()); + } + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + savestate = dmux_pamf_state::sending_fatal_err; + continue; + } + } + + au_queue_full_bitset = 0; + } +} + +void dmuxPamfEntry(ppu_thread& ppu, vm::ptr dmux) +{ + dmux->exec(ppu); + + if (ppu.state & cpu_flag::again) + { + ppu.syscall_args[0] = dmux.addr(); + return; + } + + ppu_execute<&sys_ppu_thread_exit>(ppu, CELL_OK); +} + +error_code dmuxPamfVerifyEsSpecificInfo(u16 stream_id, u16 private_stream_id, bool is_avc, vm::cptr es_specific_info) +{ + // The meaning of error code value 5 in here is inconsistent with how it's used elsewhere for some reason + + if (!es_specific_info) + { + return CELL_OK; + } + + switch (dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first) + { + case DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO: + if (is_avc) + { + if (const u32 level = vm::static_ptr_cast(es_specific_info)->level; + level != CELL_DMUX_PAMF_AVC_LEVEL_2P1 && level != CELL_DMUX_PAMF_AVC_LEVEL_3P0 && level != CELL_DMUX_PAMF_AVC_LEVEL_3P1 + && level != CELL_DMUX_PAMF_AVC_LEVEL_3P2 && level != CELL_DMUX_PAMF_AVC_LEVEL_4P1 && level != CELL_DMUX_PAMF_AVC_LEVEL_4P2) + { + return 5; + } + } + else if (vm::static_ptr_cast(es_specific_info)->profileLevel > CELL_DMUX_PAMF_M2V_MP_HL) + { + return 5; + } + + return CELL_OK; + + case DMUX_PAMF_STREAM_TYPE_INDEX_LPCM: + if (const auto [sampling_freq, nch, bps] = *vm::static_ptr_cast(es_specific_info); + sampling_freq != CELL_DMUX_PAMF_FS_48K || (nch != 1u && nch != 2u && nch != 6u && nch != 8u) || (bps != CELL_DMUX_PAMF_BITS_PER_SAMPLE_16 && bps != CELL_DMUX_PAMF_BITS_PER_SAMPLE_24)) + { + return 5; + } + + return CELL_OK; + + case DMUX_PAMF_STREAM_TYPE_INDEX_AC3: + case DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX: + case DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA: + return CELL_OK; + + default: + return 5; + } +} + +template +u32 dmuxPamfGetAuSpecificInfoSize(u16 stream_id, u16 private_stream_id, bool is_avc) +{ + if constexpr (raw_es) + { + return 0; + } + + switch (dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first) + { + case DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO: + if (is_avc) + { + return 4; // LLE returns four, even though CellDmuxPamfAuSpecificInfoAvc only has a reserved field like the others + } + + return 0; + + case DMUX_PAMF_STREAM_TYPE_INDEX_LPCM: + case DMUX_PAMF_STREAM_TYPE_INDEX_AC3: // LLE returns three, even though CellDmuxPamfAuSpecificInfoAc3 only has a reserved field like the others + return 3; + + case DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX: + case DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA: + default: + return 0; + } +} + +u32 dmuxPamfGetAuQueueMaxSize(u16 stream_id, u16 private_stream_id) +{ + switch (dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first) + { + case DMUX_PAMF_STREAM_TYPE_INDEX_LPCM: + return 0x100; + + case DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO: + case DMUX_PAMF_STREAM_TYPE_INDEX_AC3: + case DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX: + case DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA: + return 0x40; + + default: + return 0; + } +} + +u32 dmuxPamfGetLpcmAuSize(vm::cptr lpcm_info) +{ + return lpcm_info->samplingFreq * lpcm_info->bitsPerSample / CHAR_BIT * (lpcm_info->numOfChannels + (lpcm_info->numOfChannels & 1)) / DMUX_PAMF_LPCM_FRAMES_PER_SEC; // Streams with an odd number of channels contain an empty dummy channel +} + +u32 dmuxPamfGetAuQueueBufferSize(u16 stream_id, u16 private_stream_id, bool is_avc, vm::cptr es_specific_info) +{ + switch (dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first) + { + case DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO: + if (is_avc) + { + if (!es_specific_info) + { + return 0x46a870; + } + + switch (vm::static_ptr_cast(es_specific_info)->level) + { + case CELL_DMUX_PAMF_AVC_LEVEL_2P1: return 0xb00c0; + case CELL_DMUX_PAMF_AVC_LEVEL_3P0: return 0x19f2e0; + case CELL_DMUX_PAMF_AVC_LEVEL_3P1: return 0x260120; + case CELL_DMUX_PAMF_AVC_LEVEL_3P2: return 0x35f6c0; + case CELL_DMUX_PAMF_AVC_LEVEL_4P1: return 0x45e870; + case CELL_DMUX_PAMF_AVC_LEVEL_4P2: // Same as below + default: return 0x46a870; + } + } + + if (es_specific_info && vm::static_ptr_cast(es_specific_info)->profileLevel > CELL_DMUX_PAMF_M2V_MP_ML) + { + return 0x255000; + } + + return 0x70000; + + case DMUX_PAMF_STREAM_TYPE_INDEX_LPCM: + { + if (!es_specific_info) + { + return 0x104380; + } + + const u32 nch = vm::static_ptr_cast(es_specific_info)->numOfChannels; + const u32 lpcm_au_size = dmuxPamfGetLpcmAuSize(vm::static_ptr_cast(es_specific_info)); + + if (vm::static_ptr_cast(es_specific_info)->samplingFreq <= 96000) + { + if (nch > 0 && nch <= 2) + { + return 0x20000 + lpcm_au_size; + } + + if (nch <= 6) + { + return 0x60000 + lpcm_au_size; + } + + if (nch <= 8) + { + return 0x80000 + lpcm_au_size; + } + + return lpcm_au_size; + } + + if (nch > 0 && nch <= 2) + { + return 0x60000 + lpcm_au_size; + } + + if (nch <= 6) + { + return 0x100000 + lpcm_au_size; + } + + return lpcm_au_size; + } + case DMUX_PAMF_STREAM_TYPE_INDEX_AC3: + return 0xa000; + + case DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX: + return 0x6400; + + case DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA: + return 0x160000; + + default: + return 0; + } +} + +template +u32 dmuxPamfGetEsMemSize(u16 stream_id, u16 private_stream_id, bool is_avc, vm::cptr es_specific_info) +{ + return dmuxPamfGetAuSpecificInfoSize(stream_id, private_stream_id, is_avc) * dmuxPamfGetAuQueueMaxSize(stream_id, private_stream_id) + + dmuxPamfGetAuQueueBufferSize(stream_id, private_stream_id, is_avc, es_specific_info) + 0x7f + static_cast(sizeof(DmuxPamfElementaryStream)) + 0xf; +} + +error_code dmuxPamfNotifyDemuxDone(ppu_thread& ppu, [[maybe_unused]] vm::ptr core_handle, error_code error, vm::ptr handle) +{ + handle->notify_demux_done.cbFunc(ppu, handle, error, handle->notify_demux_done.cbArg); + return CELL_OK; +} + +error_code dmuxPamfNotifyProgEndCode(ppu_thread& ppu, [[maybe_unused]] vm::ptr core_handle, vm::ptr handle) +{ + if (handle->notify_prog_end_code.cbFunc) + { + handle->notify_prog_end_code.cbFunc(ppu, handle, handle->notify_prog_end_code.cbArg); + } + + return CELL_OK; +} + +error_code dmuxPamfNotifyFatalErr(ppu_thread& ppu, [[maybe_unused]] vm::ptr core_handle, error_code error, vm::ptr handle) +{ + handle->notify_fatal_err.cbFunc(ppu, handle, error, handle->notify_fatal_err.cbArg); + return CELL_OK; +} + +error_code dmuxPamfEsNotifyAuFound(ppu_thread& ppu, [[maybe_unused]] vm::ptr core_handle, vm::cptr au_info, vm::ptr handle) +{ + const vm::var _au_info; + _au_info->info.auAddr = au_info->addr; + _au_info->info.auSize = au_info->size; + _au_info->info.isRap = au_info->is_rap; + _au_info->info.userData = au_info->user_data; + _au_info->info.pts = au_info->pts; + _au_info->info.dts = au_info->dts; + _au_info->specific_info = au_info->specific_info; + _au_info->specific_info_size = au_info->specific_info_size; + // _au_info->info.auMaxSize is left uninitialized + + return handle->notify_au_found.cbFunc(ppu, handle, _au_info, handle->notify_au_found.cbArg); +} + +error_code dmuxPamfEsNotifyFlushDone(ppu_thread& ppu, [[maybe_unused]] vm::ptr core_handle, vm::ptr handle) +{ + return handle->notify_flush_done.cbFunc(ppu, handle, handle->notify_flush_done.cbArg); +} + error_code _CellDmuxCoreOpQueryAttr(vm::cptr pamfSpecificInfo, vm::ptr pamfAttr) { - cellDmuxPamf.todo("_CellDmuxCoreOpQueryAttr(pamfSpecificInfo=*0x%x, pamfAttr=*0x%x)", pamfSpecificInfo, pamfAttr); + cellDmuxPamf.notice("_CellDmuxCoreOpQueryAttr(pamfSpecificInfo=*0x%x, pamfAttr=*0x%x)", pamfSpecificInfo, pamfAttr); + + if (!pamfAttr || (pamfSpecificInfo && pamfSpecificInfo->thisSize != sizeof(CellDmuxPamfSpecificInfo))) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + pamfAttr->maxEnabledEsNum = DMUX_PAMF_MAX_ENABLED_ES_NUM; + pamfAttr->version = DMUX_PAMF_VERSION; + pamfAttr->memSize = sizeof(CellDmuxPamfHandle) + sizeof(DmuxPamfContext) + 0xe7b; return CELL_OK; } -error_code _CellDmuxCoreOpOpen(vm::cptr pamfSpecificInfo, vm::cptr demuxerResource, vm::cptr demuxerResourceSpurs, vm::cptr> notifyDemuxDone, - vm::cptr> notifyProgEndCode, vm::cptr> notifyFatalErr, vm::pptr handle) +error_code DmuxPamfContext::open(ppu_thread& ppu, const CellDmuxPamfResource& res, vm::cptr res_spurs, const DmuxCb& notify_dmux_done, + const DmuxCb& notify_prog_end_code, const DmuxCb& notify_fatal_err, vm::bptr& handle) { - cellDmuxPamf.todo("_CellDmuxCoreOpOpen(pamfSpecificInfo=*0x%x, demuxerResource=*0x%x, demuxerResourceSpurs=*0x%x, notifyDemuxDone=*0x%x, notifyProgEndCode=*0x%x, notifyFatalErr=*0x%x, handle=**0x%x)", + if (res.ppuThreadPriority >= 0xc00u || res.ppuThreadStackSize < 0x1000u || res.spuThreadPriority >= 0x100u || res.numOfSpus != 1u || !res.memAddr || res.memSize < sizeof(DmuxPamfContext) + 0xe7b) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + const auto _this = vm::ptr::make(utils::align(+res.memAddr.addr(), 0x80)); + + _this->_this = _this; + _this->this_size = res.memSize; + _this->version = DMUX_PAMF_VERSION; + _this->notify_demux_done = notify_dmux_done; + _this->notify_prog_end_code = notify_prog_end_code; + _this->notify_fatal_err = notify_fatal_err; + _this->resource = res; + _this->unk = 0; + _this->ppu_thread_stack_size = res.ppuThreadStackSize; + _this->au_released_bitset = 0; + _this->stream_reset_requested = false; + _this->sequence_state = DmuxPamfSequenceState::dormant; + _this->max_enabled_es_num = DMUX_PAMF_MAX_ENABLED_ES_NUM; + _this->enabled_es_num = 0; + std::ranges::fill(_this->elementary_streams, vm::null); + _this->next_es_id = 0; + + const vm::var mutex_attr = {{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_dxpmtx"_u64 } }}; + const vm::var cond_attr = {{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_dxpcnd"_u64 } }}; + + if (lv2_syscall(ppu, _this.ptr(&DmuxPamfContext::mutex), mutex_attr) != CELL_OK + || lv2_syscall(ppu, _this.ptr(&DmuxPamfContext::cond), _this->mutex, cond_attr) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + _this->spurs_context_addr = _this.ptr(&DmuxPamfContext::spurs_context); + _this->cmd_queue_addr_ = _this.ptr(&DmuxPamfContext::cmd_queue); + _this->cmd_queue_buffer_addr_ = _this.ptr(&DmuxPamfContext::cmd_queue_buffer); + _this->cmd_queue_addr = _this.ptr(&DmuxPamfContext::cmd_queue); + _this->cmd_result_queue_addr = _this.ptr(&DmuxPamfContext::cmd_result_queue); + _this->stream_info_queue_addr = _this.ptr(&DmuxPamfContext::stream_info_queue); + _this->event_queue_addr = _this.ptr(&DmuxPamfContext::event_queue); + _this->cmd_queue_buffer_addr = _this.ptr(&DmuxPamfContext::cmd_queue_buffer); + _this->cmd_result_queue_buffer_addr = _this.ptr(&DmuxPamfContext::cmd_result_queue_buffer); + _this->event_queue_buffer_addr = _this.ptr(&DmuxPamfContext::event_queue_buffer); + _this->stream_info_queue_buffer_addr = _this.ptr(&DmuxPamfContext::stream_info_queue_buffer); + _this->cmd_queue_addr__ = _this.ptr(&DmuxPamfContext::cmd_queue); + + ensure(std::snprintf(_this->spurs_taskset_name, sizeof(_this->spurs_taskset_name), "_libdmux_pamf_%08x", _this.addr()) == 22); + + _this->use_existing_spurs = !!res_spurs; + + if (!res_spurs && g_fxo->get().take(0x40000) != 0x40000) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + _this->cmd_queue.init(_this->cmd_queue_buffer); + _this->cmd_result_queue.init(_this->cmd_result_queue_buffer); + _this->stream_info_queue.init(_this->stream_info_queue_buffer); + _this->event_queue.init(_this->event_queue_buffer); + + // HLE exclusive + _this->savestate = {}; + _this->au_queue_full_bitset = 0; + _this->stream_reset_started = false; + _this->stream_reset_in_progress = false; + + _this->run_spu_thread(); + + handle = _this; + return _this->create_thread(ppu); +} + +error_code _CellDmuxCoreOpOpen(ppu_thread& ppu, vm::cptr pamfSpecificInfo, vm::cptr demuxerResource, vm::cptr demuxerResourceSpurs, vm::cptr> notifyDemuxDone, + vm::cptr> notifyProgEndCode, vm::cptr> notifyFatalErr, vm::pptr handle) +{ + // Block savestates during ppu_execute + std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock.owns_lock()) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmuxPamf.notice("_CellDmuxCoreOpOpen(pamfSpecificInfo=*0x%x, demuxerResource=*0x%x, demuxerResourceSpurs=*0x%x, notifyDemuxDone=*0x%x, notifyProgEndCode=*0x%x, notifyFatalErr=*0x%x, handle=**0x%x)", pamfSpecificInfo, demuxerResource, demuxerResourceSpurs, notifyDemuxDone, notifyProgEndCode, notifyFatalErr, handle); - return CELL_OK; + if ((pamfSpecificInfo && pamfSpecificInfo->thisSize != sizeof(CellDmuxPamfSpecificInfo)) + || !demuxerResource + || (demuxerResourceSpurs && !demuxerResourceSpurs->spurs) + || !notifyDemuxDone || !notifyDemuxDone->cbFunc || !notifyDemuxDone->cbArg + || !notifyProgEndCode + || !notifyFatalErr || !notifyFatalErr->cbFunc || !notifyFatalErr->cbArg + || !handle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + ensure(demuxerResource->memAddr.aligned(0x10)); // Not checked on LLE + ensure(demuxerResource->memSize >= sizeof(CellDmuxPamfHandle)); // Not checked on LLE + ensure(vm::check_addr(demuxerResource->memAddr.addr(), vm::page_readable | vm::page_writable, demuxerResource->memSize)); + + const auto _handle = vm::static_ptr_cast(demuxerResource->memAddr); + + _handle->notify_demux_done = *notifyDemuxDone; + _handle->notify_fatal_err = *notifyFatalErr; + _handle->notify_prog_end_code = *notifyProgEndCode; + + if (!pamfSpecificInfo || !pamfSpecificInfo->programEndCodeCb) + { + _handle->notify_prog_end_code.cbFunc = vm::null; + } + + const CellDmuxPamfResource res{ demuxerResource->ppuThreadPriority, demuxerResource->ppuThreadStackSize, demuxerResource->numOfSpus, demuxerResource->spuThreadPriority, + vm::bptr::make(demuxerResource->memAddr.addr() + sizeof(CellDmuxPamfHandle)), demuxerResource->memSize - sizeof(CellDmuxPamfHandle) }; + + const auto demux_done_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(dmuxPamfNotifyDemuxDone))); + const auto prog_end_code_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(dmuxPamfNotifyProgEndCode))); + const auto fatal_err_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(dmuxPamfNotifyFatalErr))); + + const error_code ret = DmuxPamfContext::open(ppu, res, demuxerResourceSpurs, { demux_done_func, _handle }, { prog_end_code_func, _handle }, { fatal_err_func, _handle }, _handle->demuxer); + + *handle = _handle; + + return ret; } -error_code _CellDmuxCoreOpClose(vm::ptr handle) +error_code DmuxPamfContext::close(ppu_thread& ppu) { - cellDmuxPamf.todo("_CellDmuxCoreOpClose(handle=*0x%x)", handle); + if (join_thread(ppu) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + ensure(idm::remove(hle_spu_thread_id)); + + if (!use_existing_spurs) + { + g_fxo->get().free(0x40000); + } + + if (lv2_syscall(ppu, cond) != CELL_OK + || lv2_syscall(ppu, mutex) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } return CELL_OK; } -error_code _CellDmuxCoreOpResetStream(vm::ptr handle) +error_code _CellDmuxCoreOpClose(ppu_thread& ppu, vm::ptr handle) { - cellDmuxPamf.todo("_CellDmuxCoreOpResetStream(handle=*0x%x)", handle); + // The PPU thread is going to use ppu_execute + std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock.owns_lock()) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmuxPamf.notice("_CellDmuxCoreOpClose(handle=*0x%x)", handle); + + if (!handle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return handle->demuxer->close(ppu); +} + +error_code DmuxPamfContext::reset_stream(ppu_thread& ppu) +{ + auto& ar = *ppu.optional_savestate_state; + const u8 savestate = ar.try_read().second; + ar.clear(); + + switch (savestate) + { + case 0: + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(0); + return {}; + } + + if (sequence_state != DmuxPamfSequenceState::running) + { + return lv2_syscall(ppu, mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; + } + + [[fallthrough]]; + + case 1: + send_spu_command_and_wait(ppu, savestate); + + if (ppu.state & cpu_flag::again) + { + ar(1); + return {}; + } + + stream_reset_requested = true; + [[fallthrough]]; + + case 2: + if (const error_code ret = lv2_syscall(ppu, cond, static_cast(thread_id)); ret != CELL_OK && ret != static_cast(CELL_EPERM)) + { + lv2_syscall(ppu, mutex); + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(2); + return {}; + } + + return lv2_syscall(ppu, mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; + + default: + fmt::throw_exception("Unexpected savestate value: 0x%x", savestate); + } +} + +error_code _CellDmuxCoreOpResetStream(ppu_thread& ppu, vm::ptr handle) +{ + cellDmuxPamf.notice("_CellDmuxCoreOpResetStream(handle=*0x%x)", handle); + + if (!handle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return handle->demuxer->reset_stream(ppu); +} + +error_code DmuxPamfContext::create_thread(ppu_thread& ppu) +{ + const vm::var name = vm::make_str("HLE PAMF demuxer"); + const auto entry = g_fxo->get().func_addr(FIND_FUNC(dmuxPamfEntry)); + + if (ppu_execute<&sys_ppu_thread_create>(ppu, _this.ptr(&DmuxPamfContext::thread_id), entry, +_this.addr(), +resource.ppuThreadPriority, +resource.ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +name) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } return CELL_OK; } -error_code _CellDmuxCoreOpCreateThread(vm::ptr handle) +error_code _CellDmuxCoreOpCreateThread(ppu_thread& ppu, vm::ptr handle) { - cellDmuxPamf.todo("_CellDmuxCoreOpCreateThread(handle=*0x%x)", handle); + // Block savestates during ppu_execute + std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - return CELL_OK; + if (!savestate_lock.owns_lock()) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmuxPamf.notice("_CellDmuxCoreOpCreateThread(handle=*0x%x)", handle); + + if (!handle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return handle->demuxer->create_thread(ppu); } -error_code _CellDmuxCoreOpJoinThread(vm::ptr handle) +error_code DmuxPamfContext::join_thread(ppu_thread& ppu) { - cellDmuxPamf.todo("_CellDmuxCoreOpJoinThread(handle=*0x%x)", handle); + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + std::ranges::fill_n(elementary_streams, enabled_es_num, vm::null); + + enabled_es_num = -1; + + send_spu_command_and_wait(ppu, false); + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + return lv2_syscall(ppu, static_cast(thread_id), +vm::var{}) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; +} + +error_code _CellDmuxCoreOpJoinThread(ppu_thread& ppu, vm::ptr handle) +{ + // The PPU thread is going to use ppu_execute + std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock.owns_lock()) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellDmuxPamf.notice("_CellDmuxCoreOpJoinThread(handle=*0x%x)", handle); + + if (!handle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return handle->demuxer->join_thread(ppu); +} + +template +error_code DmuxPamfContext::set_stream(ppu_thread& ppu, vm::cptr stream_address, u32 stream_size, b8 discontinuity, u32 user_data) +{ + auto& ar = *ppu.optional_savestate_state; + const bool waiting_for_spu_state = ar.try_read().second; + ar.clear(); + + if (!waiting_for_spu_state) + { + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(false); + return {}; + } + + this->user_data = user_data; + + if (!stream_info_queue.emplace(stream_address, stream_size, user_data, !discontinuity, raw_es)) + { + return lv2_syscall(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_BUSY : CELL_DMUX_PAMF_ERROR_FATAL; + } + } + + send_spu_command_and_wait(ppu, waiting_for_spu_state); + + if (ppu.state & cpu_flag::again) + { + ar(true); + return {}; + } + + sequence_state = DmuxPamfSequenceState::running; + + return lv2_syscall(ppu, mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; +} + +template +error_code _CellDmuxCoreOpSetStream(ppu_thread& ppu, vm::ptr handle, vm::cptr streamAddress, u32 streamSize, b8 discontinuity, u64 userData) +{ + cellDmuxPamf.trace("_CellDmuxCoreOpSetStream(handle=*0x%x, streamAddress=*0x%x, streamSize=0x%x, discontinuity=%d, userData=0x%llx)", raw_es, handle, streamAddress, streamSize, +discontinuity, userData); + + if (!streamAddress || streamSize == 0) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + ensure(!!handle); // Not checked on LLE + + return handle->demuxer->set_stream(ppu, streamAddress, streamSize, discontinuity, static_cast(userData)); +} + +error_code DmuxPamfElementaryStream::release_au(ppu_thread& ppu, vm::ptr au_addr, u32 au_size) const +{ + auto& ar = *ppu.optional_savestate_state; + const u8 savestate = ar.try_read().second; + ar.clear(); + + switch (savestate) + { + case 0: + if (lv2_syscall(ppu, demuxer->mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(0); + return {}; + } + + [[fallthrough]]; + + case 1: + demuxer->send_spu_command_and_wait(ppu, savestate, au_addr, au_size, static_cast>(stream_id), static_cast>(private_stream_id)); + + if (ppu.state & cpu_flag::again) + { + ar(1); + return {}; + } + + demuxer->au_released_bitset |= 1ull << this_index; + [[fallthrough]]; + + case 2: + if (const error_code ret = lv2_syscall(ppu, demuxer->cond, static_cast(demuxer->thread_id)); ret != CELL_OK && ret != static_cast(CELL_EPERM)) + { + lv2_syscall(ppu, demuxer->mutex); + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(2); + return {}; + } + + return lv2_syscall(ppu, demuxer->mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; + + default: + fmt::throw_exception("Unexpected savestate value: 0x%x", savestate); + } +} + +error_code _CellDmuxCoreOpReleaseAu(ppu_thread& ppu, vm::ptr esHandle, vm::ptr auAddr, u32 auSize) +{ + cellDmuxPamf.trace("_CellDmuxCoreOpReleaseAu(esHandle=*0x%x, auAddr=*0x%x, auSize=0x%x)", esHandle, auAddr, auSize); + + if (!auAddr || auSize == 0) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + ensure(!!esHandle); // Not checked on LLE + + return esHandle->es->release_au(ppu, auAddr, auSize); +} + +template +error_code dmuxPamfGetEsAttr(u16 stream_id, u16 private_stream_id, bool is_avc, vm::cptr es_specific_info, CellDmuxPamfEsAttr& attr) +{ + if (dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID) + { + return CELL_DMUX_PAMF_ERROR_UNKNOWN_STREAM; + } + + if (dmuxPamfVerifyEsSpecificInfo(stream_id, private_stream_id, is_avc, es_specific_info) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + attr.auQueueMaxSize = dmuxPamfGetAuQueueMaxSize(stream_id, private_stream_id); + attr.memSize = dmuxPamfGetEsMemSize(stream_id, private_stream_id, is_avc, es_specific_info); + attr.specificInfoSize = dmuxPamfGetAuSpecificInfoSize(stream_id, private_stream_id, is_avc); return CELL_OK; } template -error_code _CellDmuxCoreOpSetStream(vm::ptr handle, vm::cptr streamAddress, u32 streamSize, b8 discontinuity, u64 userData) +static inline std::tuple get_stream_ids(vm::cptr esFilterId) { - cellDmuxPamf.todo("_CellDmuxCoreOpSetStream(handle=*0x%x, streamAddress=*0x%x, streamSize=0x%x, discontinuity=%d, userData=0x%llx)", raw_es, handle, streamAddress, streamSize, +discontinuity, userData); + if constexpr (raw_es) + { + const auto filter_id = vm::static_ptr_cast(esFilterId); + return { filter_id[2], filter_id[3], filter_id[8] >> 7 }; + } - return CELL_OK; -} - -error_code _CellDmuxCoreOpFreeMemory(vm::ptr esHandle, vm::ptr memAddr, u32 memSize) -{ - cellDmuxPamf.todo("_CellDmuxCoreOpFreeMemory(esHandle=*0x%x, memAddr=*0x%x, memSize=0x%x)", esHandle, memAddr, memSize); - - return CELL_OK; + const auto filter_id = vm::static_ptr_cast(esFilterId); + return { filter_id->filterIdMajor, filter_id->filterIdMinor, filter_id->supplementalInfo1 }; } template error_code _CellDmuxCoreOpQueryEsAttr(vm::cptr esFilterId, vm::cptr esSpecificInfo, vm::ptr attr) { - cellDmuxPamf.todo("_CellDmuxCoreOpQueryEsAttr(esFilterId=*0x%x, esSpecificInfo=*0x%x, attr=*0x%x)", raw_es, esFilterId, esSpecificInfo, attr); + cellDmuxPamf.notice("_CellDmuxCoreOpQueryEsAttr(esFilterId=*0x%x, esSpecificInfo=*0x%x, attr=*0x%x)", raw_es, esFilterId, esSpecificInfo, attr); + if (!esFilterId || !attr) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + const auto [stream_id, private_stream_id, is_avc] = get_stream_ids(esFilterId); + + CellDmuxPamfEsAttr es_attr; + + const error_code ret = dmuxPamfGetEsAttr(stream_id, private_stream_id, is_avc, esSpecificInfo, es_attr); + + *attr = es_attr; + attr->memSize += static_cast(sizeof(CellDmuxPamfEsHandle)); + + return ret; +} + +template +error_code DmuxPamfContext::enable_es(ppu_thread& ppu, u16 stream_id, u16 private_stream_id, bool is_avc, vm::cptr es_specific_info, vm::ptr mem_addr, u32 mem_size, const DmuxCb& notify_au_found, + const DmuxCb& notify_flush_done, vm::bptr& es) +{ + auto& ar = *ppu.optional_savestate_state; + const bool waiting_for_spu_state = ar.try_read().second; + ar.clear(); + + if (mem_size < dmuxPamfGetEsMemSize(stream_id, private_stream_id, is_avc, es_specific_info)) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + const auto stream_type = dmuxPamfStreamIdToTypeChannel(stream_id, private_stream_id).first; + + if (!waiting_for_spu_state) + { + if (stream_type == DMUX_PAMF_STREAM_TYPE_INDEX_INVALID) + { + return CELL_DMUX_PAMF_ERROR_UNKNOWN_STREAM; + } + + if (dmuxPamfVerifyEsSpecificInfo(stream_id, private_stream_id, is_avc, es_specific_info) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + if (const error_code ret = lv2_syscall(ppu, mutex, 0); ret != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(false); + return {}; + } + + this->is_raw_es = raw_es; + + if (enabled_es_num == max_enabled_es_num) + { + return lv2_syscall(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_NO_MEMORY : CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (find_es(stream_id, private_stream_id)) + { + // Elementary stream is already enabled + return lv2_syscall(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_ARG : CELL_DMUX_PAMF_ERROR_FATAL; + } + } + + const be_t au_max_size = [&]() -> be_t + { + switch (stream_type) + { + case DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO: + if (is_avc) + { + if (!es_specific_info || vm::static_ptr_cast(es_specific_info)->level == CELL_DMUX_PAMF_AVC_LEVEL_4P2) + { + return 0xcc000u; + } + + switch (vm::static_ptr_cast(es_specific_info)->level) + { + case CELL_DMUX_PAMF_AVC_LEVEL_2P1: return 0x12900u; + case CELL_DMUX_PAMF_AVC_LEVEL_3P0: return 0x25f80u; + case CELL_DMUX_PAMF_AVC_LEVEL_3P1: return 0x54600u; + case CELL_DMUX_PAMF_AVC_LEVEL_3P2: return 0x78000u; + case CELL_DMUX_PAMF_AVC_LEVEL_4P1: return 0xc0000u; + default: fmt::throw_exception("Unreachable"); // es_specific_info was already checked for invalid values in dmuxPamfVerifyEsSpecificInfo() + } + } + + if (!es_specific_info || vm::static_ptr_cast(es_specific_info)->profileLevel > CELL_DMUX_PAMF_M2V_MP_ML) + { + return 0x12a800u; + } + + return 0x38000u; + + case DMUX_PAMF_STREAM_TYPE_INDEX_LPCM: return dmuxPamfGetLpcmAuSize(vm::static_ptr_cast(es_specific_info)); + case DMUX_PAMF_STREAM_TYPE_INDEX_AC3: return 0xf00u; + case DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX: return 0x1008u; + case DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA: return 0xa0000u; + default: fmt::throw_exception("Unreachable"); // stream_type was already checked + } + }(); + + const auto _es = vm::bptr::make(utils::align(mem_addr.addr(), 0x10)); + + const auto au_queue_buffer = vm::bptr::make(utils::align(_es.addr() + static_cast(sizeof(DmuxPamfElementaryStream)), 0x80)); + const be_t au_specific_info_size = dmuxPamfGetAuSpecificInfoSize(stream_id, private_stream_id, is_avc); + + send_spu_command_and_wait(ppu, waiting_for_spu_state, stream_id, private_stream_id, is_avc, au_queue_buffer, + dmuxPamfGetAuQueueBufferSize(stream_id, private_stream_id, is_avc, es_specific_info), au_max_size, au_specific_info_size, raw_es, next_es_id); + + if (ppu.state & cpu_flag::again) + { + ar(true); + return {}; + } + + u32 es_idx = umax; + while (elementary_streams[++es_idx]){} // There is guaranteed to be an empty slot, this was already checked above + + _es->_this = _es; + _es->this_size = mem_size; + _es->this_index = es_idx; + _es->demuxer = _this; + _es->notify_au_found = notify_au_found; + _es->notify_flush_done = notify_flush_done; + _es->stream_id = stream_id; + _es->private_stream_id = private_stream_id; + _es->is_avc = is_avc; + _es->au_queue_buffer = au_queue_buffer; + _es->au_max_size = au_max_size; + _es->au_specific_info_size = au_specific_info_size; + _es->reset_next_au = false; + _es->es_id = next_es_id++; + + elementary_streams[es_idx] = _es; + + enabled_es_num++; + + if (lv2_syscall(ppu, mutex) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + es = _es; return CELL_OK; } template -error_code _CellDmuxCoreOpEnableEs(vm::ptr handle, vm::cptr esFilterId, vm::cptr esResource, vm::cptr> notifyAuFound, - vm::cptr> notifyFlushDone, vm::cptr esSpecificInfo, vm::pptr esHandle) +error_code _CellDmuxCoreOpEnableEs(ppu_thread& ppu, vm::ptr handle, vm::cptr esFilterId, vm::cptr esResource, vm::cptr> notifyAuFound, + vm::cptr> notifyFlushDone, vm::cptr esSpecificInfo, vm::pptr esHandle) { - cellDmuxPamf.todo("_CellDmuxCoreOpEnableEs(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle)", + cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle=**0x%x)", raw_es, handle, esFilterId, esResource, notifyAuFound, notifyFlushDone, esSpecificInfo, esHandle); - return CELL_OK; + if (!handle || !esFilterId || !esResource || !esResource->memAddr || esResource->memSize == 0u || !notifyAuFound || !notifyAuFound->cbFunc || !notifyAuFound->cbArg || !notifyFlushDone || !notifyFlushDone->cbFunc || !notifyFlushDone->cbArg) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + ensure(!!esHandle && esResource->memAddr.aligned(0x10)); // Not checked on LLE + ensure(esResource->memSize >= sizeof(CellDmuxPamfEsHandle)); // Not checked on LLE + ensure(vm::check_addr(esResource->memAddr.addr(), vm::page_readable | vm::page_writable, esResource->memSize)); + + const auto es_handle = vm::static_ptr_cast(esResource->memAddr); + + es_handle->notify_au_found = *notifyAuFound; + es_handle->notify_flush_done = *notifyFlushDone; + + const auto au_found_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(dmuxPamfEsNotifyAuFound))); + const auto flush_done_func = vm::bptr::make(g_fxo->get().func_addr(FIND_FUNC(dmuxPamfEsNotifyFlushDone))); + + const auto [stream_id, private_stream_id, is_avc] = get_stream_ids(esFilterId); + + const error_code ret = handle->demuxer->enable_es(ppu, stream_id, private_stream_id, is_avc, esSpecificInfo, vm::ptr::make(esResource->memAddr.addr() + sizeof(CellDmuxPamfEsHandle)), + esResource->memSize - sizeof(CellDmuxPamfEsHandle), { au_found_func, es_handle }, { flush_done_func, es_handle }, es_handle->es); + + *esHandle = es_handle; + + return ret; } -error_code _CellDmuxCoreOpDisableEs(vm::ptr esHandle) +error_code DmuxPamfElementaryStream::disable_es(ppu_thread& ppu) { - cellDmuxPamf.todo("_CellDmuxCoreOpDisableEs(esHandle=*0x%x)", esHandle); + const auto dmux = demuxer.get_ptr(); - return CELL_OK; + auto& ar = *ppu.optional_savestate_state; + const u8 savestate = ar.try_read().second; + ar.clear(); + + switch (savestate) + { + case 0: + if (lv2_syscall(ppu, dmux->mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(0); + return {}; + } + + if (!dmux->find_es(stream_id, private_stream_id)) + { + // Elementary stream is already disabled + return lv2_syscall(ppu, dmux->mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_ARG : CELL_DMUX_PAMF_ERROR_FATAL; + } + + [[fallthrough]]; + + case 1: + dmux->send_spu_command_and_wait(ppu, savestate, static_cast>(stream_id), static_cast>(private_stream_id)); + + if (ppu.state & cpu_flag::again) + { + ar(1); + return {}; + } + + _this = vm::null; + this_size = 0; + demuxer = vm::null; + notify_au_found = {}; + au_queue_buffer = vm::null; + unk = 0; + au_max_size = 0; + + dmux->elementary_streams[this_index] = vm::null; + dmux->enabled_es_num--; + + dmux->au_released_bitset |= 1ull << this_index; + + this_index = 0; + [[fallthrough]]; + + case 2: + if (const error_code ret = lv2_syscall(ppu, dmux->cond, static_cast(dmux->thread_id)); ret != CELL_OK && ret != static_cast(CELL_EPERM)) + { + lv2_syscall(ppu, dmux->mutex); + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(2); + return {}; + } + + return lv2_syscall(ppu, dmux->mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; + + default: + fmt::throw_exception("Unexpected savestate value: 0x%x", savestate); + } } -error_code _CellDmuxCoreOpFlushEs(vm::ptr esHandle) +error_code _CellDmuxCoreOpDisableEs(ppu_thread& ppu, vm::ptr esHandle) { - cellDmuxPamf.todo("_CellDmuxCoreOpFlushEs(esHandle=*0x%x)", esHandle); + cellDmuxPamf.notice("_CellDmuxCoreOpDisableEs(esHandle=*0x%x)", esHandle); - return CELL_OK; + if (!esHandle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return esHandle->es->disable_es(ppu); } -error_code _CellDmuxCoreOpResetEs(vm::ptr esHandle) +error_code DmuxPamfElementaryStream::flush_es(ppu_thread& ppu) const { - cellDmuxPamf.todo("_CellDmuxCoreOpResetEs(esHandle=*0x%x)", esHandle); + auto& ar = *ppu.optional_savestate_state; + const bool waiting_for_spu_state = ar.try_read().second; + ar.clear(); - return CELL_OK; + if (!waiting_for_spu_state) + { + if (lv2_syscall(ppu, demuxer->mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(false); + return {}; + } + } + + demuxer->send_spu_command_and_wait(ppu, waiting_for_spu_state, static_cast>(stream_id), static_cast>(private_stream_id)); + + if (ppu.state & cpu_flag::again) + { + ar(true); + return {}; + } + + return lv2_syscall(ppu, demuxer->mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; } -error_code _CellDmuxCoreOpResetStreamAndWaitDone(vm::ptr handle) +error_code _CellDmuxCoreOpFlushEs(ppu_thread& ppu, vm::ptr esHandle) { - cellDmuxPamf.todo("_CellDmuxCoreOpResetStreamAndWaitDone(handle=*0x%x)", handle); + cellDmuxPamf.notice("_CellDmuxCoreOpFlushEs(esHandle=*0x%x)", esHandle); - return CELL_OK; + if (!esHandle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return esHandle->es->flush_es(ppu); } +error_code DmuxPamfElementaryStream::reset_es(ppu_thread& ppu) const +{ + auto& ar = *ppu.optional_savestate_state; + const bool waiting_for_spu_state = ar.try_read().second; + ar.clear(); + + if (!waiting_for_spu_state) + { + if (lv2_syscall(ppu, demuxer->mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + ar(false); + return {}; + } + } + + demuxer->send_spu_command_and_wait(ppu, waiting_for_spu_state, static_cast>(stream_id), static_cast>(private_stream_id), vm::null); + + if (ppu.state & cpu_flag::again) + { + ar(true); + return {}; + } + + return lv2_syscall(ppu, demuxer->mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; +} + +error_code _CellDmuxCoreOpResetEs(ppu_thread& ppu, vm::ptr esHandle) +{ + cellDmuxPamf.notice("_CellDmuxCoreOpResetEs(esHandle=*0x%x)", esHandle); + + if (!esHandle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return esHandle->es->reset_es(ppu); +} + +error_code DmuxPamfContext::reset_stream_and_wait_done(ppu_thread& ppu) +{ + // Both sys_cond_wait() and DmuxPamfContext::reset_stream() are already using ppu_thread::optional_savestate_state, so we can't save this function currently + std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock.owns_lock()) + { + ppu.state += cpu_flag::again; + return {}; + } + + if (reset_stream(ppu) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + + if (lv2_syscall(ppu, mutex, 0) != CELL_OK) + { + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + + while (sequence_state != DmuxPamfSequenceState::dormant) + { + if (lv2_syscall(ppu, cond, 0) != CELL_OK) + { + lv2_syscall(ppu, mutex); + return CELL_DMUX_PAMF_ERROR_FATAL; + } + + if (ppu.state & cpu_flag::again) + { + return {}; + } + } + + return lv2_syscall(ppu, mutex) == CELL_OK ? static_cast(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL; +} + +error_code _CellDmuxCoreOpResetStreamAndWaitDone(ppu_thread& ppu, vm::ptr handle) +{ + cellDmuxPamf.notice("_CellDmuxCoreOpResetStreamAndWaitDone(handle=*0x%x)", handle); + + if (!handle) + { + return CELL_DMUX_PAMF_ERROR_ARG; + } + + return handle->demuxer->reset_stream_and_wait_done(ppu); +} + +template static void init_gvar(const vm::gvar& var) { var->queryAttr.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpQueryAttr))); @@ -124,7 +2863,10 @@ static void init_gvar(const vm::gvar& var) var->resetStream.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpResetStream))); var->createThread.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpCreateThread))); var->joinThread.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpJoinThread))); - var->freeMemory.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpFreeMemory))); + var->setStream.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpSetStream))); + var->releaseAu.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpReleaseAu))); + var->queryEsAttr.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpQueryEsAttr))); + var->enableEs.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpEnableEs))); var->disableEs.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpDisableEs))); var->flushEs.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpFlushEs))); var->resetEs.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpResetEs))); @@ -133,21 +2875,8 @@ static void init_gvar(const vm::gvar& var) DECLARE(ppu_module_manager::cellDmuxPamf)("cellDmuxPamf", [] { - REG_VNID(cellDmuxPamf, 0x28b2b7b2, g_cell_dmux_core_ops_pamf).init = [] - { - g_cell_dmux_core_ops_pamf->setStream.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpSetStream))); - g_cell_dmux_core_ops_pamf->queryEsAttr.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpQueryEsAttr))); - g_cell_dmux_core_ops_pamf->enableEs.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpEnableEs))); - init_gvar(g_cell_dmux_core_ops_pamf); - }; - - REG_VNID(cellDmuxPamf, 0x9728a0e9, g_cell_dmux_core_ops_raw_es).init = [] - { - g_cell_dmux_core_ops_raw_es->setStream.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpSetStream))); - g_cell_dmux_core_ops_raw_es->queryEsAttr.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpQueryEsAttr))); - g_cell_dmux_core_ops_raw_es->enableEs.set(g_fxo->get().func_addr(FIND_FUNC(_CellDmuxCoreOpEnableEs))); - init_gvar(g_cell_dmux_core_ops_raw_es); - }; + REG_VNID(cellDmuxPamf, 0x28b2b7b2, g_cell_dmux_core_ops_pamf).init = []{ init_gvar(g_cell_dmux_core_ops_pamf); }; + REG_VNID(cellDmuxPamf, 0x9728a0e9, g_cell_dmux_core_ops_raw_es).init = []{ init_gvar(g_cell_dmux_core_ops_raw_es); }; REG_HIDDEN_FUNC(_CellDmuxCoreOpQueryAttr); REG_HIDDEN_FUNC(_CellDmuxCoreOpOpen); @@ -157,7 +2886,7 @@ DECLARE(ppu_module_manager::cellDmuxPamf)("cellDmuxPamf", [] REG_HIDDEN_FUNC(_CellDmuxCoreOpJoinThread); REG_HIDDEN_FUNC(_CellDmuxCoreOpSetStream); REG_HIDDEN_FUNC(_CellDmuxCoreOpSetStream); - REG_HIDDEN_FUNC(_CellDmuxCoreOpFreeMemory); + REG_HIDDEN_FUNC(_CellDmuxCoreOpReleaseAu); REG_HIDDEN_FUNC(_CellDmuxCoreOpQueryEsAttr); REG_HIDDEN_FUNC(_CellDmuxCoreOpQueryEsAttr); REG_HIDDEN_FUNC(_CellDmuxCoreOpEnableEs); @@ -166,4 +2895,12 @@ DECLARE(ppu_module_manager::cellDmuxPamf)("cellDmuxPamf", [] REG_HIDDEN_FUNC(_CellDmuxCoreOpFlushEs); REG_HIDDEN_FUNC(_CellDmuxCoreOpResetEs); REG_HIDDEN_FUNC(_CellDmuxCoreOpResetStreamAndWaitDone); + + REG_HIDDEN_FUNC(dmuxPamfNotifyDemuxDone); + REG_HIDDEN_FUNC(dmuxPamfNotifyProgEndCode); + REG_HIDDEN_FUNC(dmuxPamfNotifyFatalErr); + REG_HIDDEN_FUNC(dmuxPamfEsNotifyAuFound); + REG_HIDDEN_FUNC(dmuxPamfEsNotifyFlushDone); + + REG_HIDDEN_FUNC(dmuxPamfEntry); }); diff --git a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.h b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.h index 01983b724a..5b42103ca7 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.h +++ b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.h @@ -1,15 +1,1039 @@ #pragma once -struct CellDmuxPamfAttr +#include "Emu/savestate_utils.hpp" +#include "Utilities/Thread.h" +#include "cellPamf.h" +#include "cellDmux.h" +#include + +// Replacement for CellSpursQueue +template requires(std::is_trivial_v && max_num_of_entries > 0) +class alignas(0x80) dmux_pamf_hle_spurs_queue { - be_t maxEnabledEsNum; - be_t version; + T* buffer; + + alignas(atomic_t) std::array)> _size; // Stored in a byte array since the PPU context needs to be trivial + u32 front; + u32 back; + + template + bool _pop(T* lhs) + { + atomic_t& _size = *std::launder(reinterpret_cast*>(this->_size.data())); + + if (_size == 0) + { + return false; + } + + if (lhs) + { + *lhs = buffer[front]; + } + + if constexpr (!is_peek) + { + front = (front + 1) % max_num_of_entries; + _size--; + _size.notify_one(); + } + + return true; + } + +public: + void init(T (&buffer)[max_num_of_entries]) + { + this->buffer = buffer; + new (_size.data()) atomic_t(0); + front = 0; + back = 0; + } + + bool pop(T& lhs) { return _pop(&lhs); } + bool pop() { return _pop(nullptr); } + bool peek(T& lhs) const { return const_cast(this)->_pop(&lhs); } + bool emplace(auto&&... args) + { + atomic_t& _size = *std::launder(reinterpret_cast*>(this->_size.data())); + + if (_size >= max_num_of_entries) + { + return false; + } + + new (&buffer[back]) T(std::forward(args)...); + + back = (back + 1) % max_num_of_entries; + _size++; + _size.notify_one(); + + return true; + } + + [[nodiscard]] u32 size() const { return std::launder(reinterpret_cast*>(this->_size.data()))->observe(); } + + void wait() const + { + const atomic_t& _size = *std::launder(reinterpret_cast*>(this->_size.data())); + + while (_size == 0 && thread_ctrl::state() != thread_state::aborting) + { + thread_ctrl::wait_on(_size, 0); + } + } +}; + +enum class DmuxPamfCommandType : u32 +{ + enable_es = 0, + disable_es = 2, + set_stream = 4, + release_au = 6, + flush_es = 8, + close = 10, + reset_stream = 12, + reset_es = 14, + resume = 16, +}; + +struct alignas(0x80) DmuxPamfCommand +{ + be_t type; + + union + { + struct + { + be_t stream_id; + be_t private_stream_id; + be_t is_avc; + vm::bptr au_queue_buffer; + be_t au_queue_buffer_size; + be_t au_max_size; + be_t au_specific_info_size; + be_t is_raw_es; + be_t user_data; + } + enable_es; + + struct + { + be_t stream_id; + be_t private_stream_id; + } + disable_flush_es; + + struct + { + vm::ptr> au_addr; + be_t au_size; + be_t stream_id; + be_t private_stream_id; + } + release_au; + + struct + { + be_t stream_id; + be_t private_stream_id; + vm::ptr> au_addr; + } + reset_es; + }; + + DmuxPamfCommand() = default; + + DmuxPamfCommand(be_t&& type) + : type(type) + { + } + + DmuxPamfCommand(be_t&& type, const be_t& stream_id, const be_t& private_stream_id) + : type(type), disable_flush_es{ stream_id, private_stream_id } + { + } + + DmuxPamfCommand(be_t&& type, const be_t& stream_id, const be_t& private_stream_id, const vm::ptr>& au_addr) + : type(type), reset_es{ stream_id, private_stream_id, au_addr } + { + } + + DmuxPamfCommand(be_t&& type, const vm::ptr>& au_addr, const be_t& au_size, const be_t& stream_id, const be_t& private_stream_id) + : type(type), release_au{ au_addr, au_size, stream_id, private_stream_id } + { + } + + DmuxPamfCommand(be_t&& type, const be_t& stream_id, const be_t& private_stream_id, const be_t& is_avc, const vm::bptr& au_queue_buffer, + const be_t& au_queue_buffer_size, const be_t& au_max_size, const be_t& au_specific_info_size, const be_t& is_raw_es, const be_t& user_data) + : type(type), enable_es{ stream_id, private_stream_id, is_avc, au_queue_buffer, au_queue_buffer_size, au_max_size, au_specific_info_size, is_raw_es, user_data } + { + } +}; + +CHECK_SIZE_ALIGN(DmuxPamfCommand, 0x80, 0x80); + +enum class DmuxPamfEventType : u32 +{ + au_found, + demux_done, + fatal_error, + close, + flush_done, + prog_end_code, +}; + +struct alignas(0x80) DmuxPamfEvent +{ + be_t type; + + union + { + u8 pad[0x78]; + + struct + { + be_t stream_id; + be_t private_stream_id; + vm::ptr> au_addr; + CellCodecTimeStamp pts; + CellCodecTimeStamp dts; + be_t unk; + u8 reserved[4]; + be_t au_size; + be_t stream_header_size; + std::array stream_header_buf; + be_t user_data; + be_t is_rap; + } + au_found; + + struct + { + be_t stream_id; + be_t private_stream_id; + be_t user_data; + } + flush_done; + }; + + be_t event_queue_was_too_full; + + DmuxPamfEvent() = default; + + DmuxPamfEvent(be_t&& type, const be_t& event_queue_was_too_full) + : type(type), event_queue_was_too_full(event_queue_was_too_full) + { + } + + DmuxPamfEvent(be_t&& type, const be_t& stream_id, const be_t& private_stream_id, const be_t& user_data, const be_t& event_queue_was_too_full) + : type(type), flush_done{ stream_id, private_stream_id, user_data }, event_queue_was_too_full(event_queue_was_too_full) + { + } + + DmuxPamfEvent(be_t&& type, const be_t& stream_id, const be_t& private_stream_id, const vm::ptr>& au_addr, const CellCodecTimeStamp& pts, const CellCodecTimeStamp& dts, const be_t& unk, + const be_t& au_size, const be_t& au_specific_info_size, const std::array& au_specific_info, const be_t& user_data, const be_t& is_rap, const be_t& event_queue_was_too_full) + : type(type) + , au_found{ stream_id, private_stream_id, au_addr, pts, dts, static_cast>(unk), {}, au_size, au_specific_info_size, au_specific_info, user_data, is_rap } + , event_queue_was_too_full(event_queue_was_too_full) + { + } +}; + +CHECK_SIZE_ALIGN(DmuxPamfEvent, 0x80, 0x80); + +struct alignas(0x80) DmuxPamfStreamInfo +{ + vm::bcptr stream_addr; + be_t stream_size; + be_t user_data; + be_t continuity; + be_t is_raw_es; +}; + +CHECK_SIZE_ALIGN(DmuxPamfStreamInfo, 0x80, 0x80); + +enum DmuxPamfStreamTypeIndex +{ + DMUX_PAMF_STREAM_TYPE_INDEX_INVALID = -1, + DMUX_PAMF_STREAM_TYPE_INDEX_VIDEO, + DMUX_PAMF_STREAM_TYPE_INDEX_LPCM, + DMUX_PAMF_STREAM_TYPE_INDEX_AC3, + DMUX_PAMF_STREAM_TYPE_INDEX_ATRACX, + DMUX_PAMF_STREAM_TYPE_INDEX_USER_DATA, +}; + + +// SPU thread + +class dmux_pamf_base +{ + // Event handlers for the demuxer. These correspond to the events that the SPU thread sends to the PPU thread on LLE (except for au_queue_full(): the SPU thread just sets a global bool, + // but it is never notified to the PPU thread or the user). + + virtual bool on_au_found(u8 stream_id, u8 private_stream_id, u32 user_data, std::span au, u64 pts, u64 dts, bool rap, u8 au_specific_info_size, std::array au_specific_info_buf) = 0; + virtual bool on_demux_done() = 0; + virtual void on_fatal_error() = 0; + virtual bool on_flush_done(u8 stream_id, u8 private_stream_id, u32 user_data) = 0; + virtual bool on_prog_end() = 0; + virtual void on_au_queue_full() = 0; + +public: + virtual ~dmux_pamf_base() = default; + + bool enable_es(u32 stream_id, u32 private_stream_id, bool is_avc, std::span au_queue_buffer, u32 au_max_size, bool raw_es, u32 user_data); + bool disable_es(u32 stream_id, u32 private_stream_id); + bool release_au(u32 stream_id, u32 private_stream_id, u32 au_size) const; + bool flush_es(u32 stream_id, u32 private_stream_id); + void set_stream(std::span stream, bool continuity); + void reset_stream(); + bool reset_es(u32 stream_id, u32 private_stream_id, u8* au_addr); + bool process_next_pack(); + +protected: + void save_base(utils::serial& ar); + [[nodiscard]] bool has_work() const { return !!stream || !demux_done_notified; } + [[nodiscard]] u32 get_enabled_es_count() const; + +private: + static constexpr u16 PACK_SIZE = 0x800; + static constexpr s8 PACK_STUFFING_LENGTH_OFFSET = 0xd; + static constexpr s8 PES_PACKET_LENGTH_OFFSET = 0x4; + static constexpr s8 PES_HEADER_DATA_LENGTH_OFFSET = 0x8; + static constexpr s8 PTS_DTS_FLAG_OFFSET = 0x7; + static constexpr u8 PACKET_START_CODE_PREFIX = 1; + + static constexpr be_t M2V_PIC_START = 0x100; + static constexpr be_t AVC_AU_DELIMITER = 0x109; + static constexpr be_t M2V_SEQUENCE_HEADER = 0x1b3; + static constexpr be_t M2V_SEQUENCE_END = 0x1b7; + static constexpr be_t PACK_START = 0x1ba; + static constexpr be_t SYSTEM_HEADER = 0x1bb; + static constexpr be_t PRIVATE_STREAM_1 = 0x1bd; + static constexpr be_t PRIVATE_STREAM_2 = 0x1bf; + static constexpr be_t PROG_END = 0x1b9; + static constexpr be_t VIDEO_STREAM_BASE = 0x1e0; // The lower 4 bits indicate the channel + + // Partial access unit that will be written to the output queue + struct access_unit_chunk + { + std::vector cached_data; // Up to three bytes of data from the previous PES packet (copied into this vector, since it might not be in memory anymore) + std::span data; // Data of the current PES packet + }; + + // Output queue for access units + // The queue doesn't keep track of where access units are in the buffer (only which parts are used and which are free), this has to be done extenally + class output_queue + { + public: + explicit output_queue(std::span buffer) : buffer(buffer) {} + + explicit output_queue(utils::serial& ar) + : buffer{vm::_ptr(ar.pop()), ar.pop()} + , back(vm::_ptr(ar.pop())) + , front(vm::_ptr(ar.pop())) + , wrap_pos(vm::_ptr(ar.pop())) + { + } + + void save(utils::serial& ar) const { ar(vm::get_addr(buffer.data()), static_cast(buffer.size()), vm::get_addr(back), vm::get_addr(front), vm::get_addr(wrap_pos)); } + + // The queue itself doesn't keep track of the location of each access unit, so the pop and access operations need the size or address of the access unit to remove/return + void pop_back(u32 au_size); + void pop_back(u8* au_addr); + void pop_front(u32 au_size); + [[nodiscard]] const u8* peek_back(u32 au_size) const { return back - au_size; } + + void clear() { wrap_pos = front = back = buffer.data(); } + + void push_unchecked(const access_unit_chunk& au_chunk); + bool push(const access_unit_chunk& au_chunk, const std::function& on_fatal_error); + + [[nodiscard]] bool prepare_next_au(u32 au_max_size); + + [[nodiscard]] usz get_free_size() const { return wrap_pos != buffer.data() ? front - back : std::to_address(buffer.end()) - back; } + + private: + const std::span buffer; + + // Since access units have a variable size, uses pointers instead of indices + u8* back = buffer.data(); + const u8* front = buffer.data(); + const u8* wrap_pos = buffer.data(); // The address where the back pointer wrapped around to the beginning of the queue + }; + + // Base class for elementary streams and subclasses for each stream type + // Responsible for processing the data section of PES packets and splitting it into access units with the stream parsers of each subclass + class elementary_stream + { + public: + elementary_stream(u8 channel, u32 au_max_size, dmux_pamf_base& ctx, u32 user_data, u8 au_specific_info_size, std::span au_queue_buffer) + : channel(channel) + , au_max_size(au_max_size == umax || au_max_size > au_queue_buffer.size() ? 0x800 : au_max_size) + , ctx(ctx) + , au_specific_info_size(au_specific_info_size) + , user_data(user_data) + , au_queue(au_queue_buffer) + { + // The cache sizes will never exceed three bytes + cache.reserve(3); + au_chunk.cached_data.reserve(3); + } + + elementary_stream(utils::serial& ar, u8 channel, dmux_pamf_base& ctx, u8 au_specific_info_size) + : channel(channel) + , au_max_size(ar.pop()) + , ctx(ctx) + , au_specific_info_size(au_specific_info_size) + , user_data(ar.pop()) + , au_queue(ar) + { + save(ar); + } + + virtual ~elementary_stream() = default; + void save(utils::serial& ar); + + static bool is_enabled(const std::unique_ptr& es) { return !!es; } + + [[nodiscard]] virtual std::pair get_stream_id() const = 0; + + void set_pes_packet_data(std::span pes_packet_data) { ensure(!this->pes_packet_data); this->pes_packet_data = this->stream_chunk = pes_packet_data; } + void set_pts(u64 pts) { this->pts = pts; } + void set_dts(u64 dts) { this->dts = dts; } + void set_rap() { rap = true; } + + // Parses the proprietary header of private streams. Returns the size of the header or umax if the stream is invalid + virtual u32 parse_stream_header(std::span elementary_stream, s8 pts_dts_flag) = 0; + + // Processes the current PES packet. Returns true if it has been entirely consumed + bool process_pes_packet_data(); + + void release_au(u32 au_size) { au_queue.pop_front(au_size); } + void flush_es(); + void reset_es(u8* au_addr); + void discard_access_unit(); + + protected: + const u8 channel : 4; + const u32 au_max_size; // Maximum possible size of an access unit + u32 au_size_unk = 0; // For user data streams, used to store the size of the current access unit. For other private streams, used as a bool for some reason + alignas(0x10) std::array au_specific_info_buf{}; // For LPCM streams, stores the first 0x10 bytes of the current PES packet data, contains info like the number of channels + + // The access unit that is currently being cut out + struct access_unit + { + ENABLE_BITWISE_SERIALIZATION + + enum class state : u8 + { + none, // An access unit is not currently being cut out + incomplete, // An access unit is currently being cut out + commenced, // The current PES packet contains the beginning of an access unit + complete, // The current PES packet contains the end of an access unit + size_mismatch, // The distance between sync words and size indicated in the access unit's info header does not match + m2v_sequence, // Special case for M2V, access unit commenced, but the next start code does not complete the access unit + } + state = state::none; + + bool rap = false; + bool timestamps_rap_set = false; + + // Since the delimiters of compressed audio streams are allowed to appear anywhere in the stream (instead of just the beginning of an access unit), we need to parse the size of the access unit from the stream + u8 size_info_offset = 0; + u16 parsed_size = 0; + + u32 accumulated_size = 0; // Incremented after every access unit chunk cut out from the stream + + u64 pts = umax; + u64 dts = umax; + + alignas(0x10) std::array au_specific_info_buf{}; + } + current_au; + + access_unit_chunk au_chunk; // A partial access unit that will be written to the access unit queue. Set by the stream parsers + std::vector cache; // The last three bytes of the current PES packet need to be saved, since they could contain part of an access unit delimiter + + // Returns the stream header size of audio streams. The only difference between LPCM and compressed streams is the extra_header_size_unk_mask + template + u32 parse_audio_stream_header(std::span pes_packet_data); + + private: + dmux_pamf_base& ctx; // For access to event handlers + + enum class state : u8 + { + initial, + pushing_au_queue, + notifying_au_found, + preparing_for_next_au + } + state = state::initial; + + // Size of the "CellDmuxPamfAuSpecificInfo..." struct for the type of this stream ("reserved" fields are not counted, so for all stream types other than LPCM this will be 0) + // This does NOT correspond to the amount of data in au_specific_info_buf, the info in the buffer gets unpacked by the PPU thread + const u8 au_specific_info_size; + + const u32 user_data; + + // Data section of the current PES packet. Needs to be remembered separately from the span we're working with below + std::optional> pes_packet_data; + + std::span stream_chunk; // The current section of the PES packet data to be processed + u64 pts = umax; // Presentation time stamp of the current PES packet + u64 dts = umax; // Decoding time stamp of the current PES packet + bool rap = false; // Random access point indicator + + output_queue au_queue; + + // Extracts access units from the stream by searching for the access unit delimiter and setting au_chunk accordingly. Returns the number of bytes that were parsed + virtual u32 parse_stream(std::span stream) = 0; + + void reset() + { + state = state::initial; + pes_packet_data.reset(); + au_size_unk = 0; + pts = + dts = umax; + rap = false; + au_chunk.data = {}; + au_chunk.cached_data.clear(); + current_au = {}; + } + + void set_au_timestamps_rap() + { + current_au.pts = pts; + current_au.dts = dts; + current_au.rap = rap; + pts = + dts = umax; + rap = false; + current_au.timestamps_rap_set = true; + } + }; + + template + class video_stream final : public elementary_stream + { + public: + video_stream(u8 channel, u32 au_max_size, dmux_pamf_base& ctx, u32 user_data, std::span au_queue_buffer) : elementary_stream(channel, au_max_size, ctx, user_data, 0, au_queue_buffer) {} + video_stream(utils::serial& ar, u8 channel, dmux_pamf_base& ctx) : elementary_stream(ar, channel, ctx, 0) {} + + private: + u32 parse_stream(std::span stream) override; + u32 parse_stream_header([[maybe_unused]] std::span pes_packet_data, [[maybe_unused]] s8 pts_dts_flag) override { return 0; } + [[nodiscard]] std::pair get_stream_id() const override { return { 0xe0 | channel, 0 }; } + }; + + class lpcm_stream final : public elementary_stream + { + public: + lpcm_stream(u8 channel, u32 au_max_size, dmux_pamf_base& ctx, u32 user_data, std::span au_queue_buffer) : elementary_stream(channel, au_max_size, ctx, user_data, 3, au_queue_buffer) {} + lpcm_stream(utils::serial& ar, u8 channel, dmux_pamf_base& ctx) : elementary_stream(ar, channel, ctx, 3) {} + + private: + u32 parse_stream(std::span stream) override; + u32 parse_stream_header(std::span pes_packet_data, [[maybe_unused]] s8 pts_dts_flag) override; + [[nodiscard]] std::pair get_stream_id() const override { return { 0xbd, 0x40 | channel }; } + }; + + template + class audio_stream final : public elementary_stream + { + public: + audio_stream(u8 channel, u32 au_max_size, dmux_pamf_base& ctx, u32 user_data, std::span au_queue_buffer) : elementary_stream(channel, au_max_size, ctx, user_data, 0, au_queue_buffer) {} + audio_stream(utils::serial& ar, u8 channel, dmux_pamf_base& ctx) : elementary_stream(ar, channel, ctx, 0) {} + + private: + static constexpr be_t SYNC_WORD = ac3 ? 0x0b77 : 0x0fd0; + static constexpr u8 ATRACX_ATS_HEADER_SIZE = 8; + static constexpr u16 AC3_FRMSIZE_TABLE[3][38] = + { + { 0x40, 0x40, 0x50, 0x50, 0x60, 0x60, 0x70, 0x70, 0x80, 0x80, 0xa0, 0xa0, 0xc0, 0xc0, 0xe0, 0xe0, 0x100, 0x100, 0x140, 0x140, 0x180, 0x180, 0x1c0, 0x1c0, 0x200, 0x200, 0x280, 0x280, 0x300, 0x300, 0x380, 0x380, 0x400, 0x400, 0x480, 0x480, 0x500, 0x500 }, + { 0x45, 0x46, 0x57, 0x58, 0x68, 0x69, 0x79, 0x7a, 0x8b, 0x8c, 0xae, 0xaf, 0xd0, 0xd1, 0xf3, 0xf4, 0x116, 0x117, 0x15c, 0x15d, 0x1a1, 0x1a2, 0x1e7, 0x1e8, 0x22d, 0x22e, 0x2b8, 0x2b9, 0x343, 0x344, 0x3cf, 0x3d0, 0x45a, 0x45b, 0x4e5, 0x4e6, 0x571, 0x572 }, + { 0x60, 0x60, 0x78, 0x78, 0x90, 0x90, 0xa8, 0xa8, 0xc0, 0xc0, 0xf0, 0xf0, 0x120, 0x120, 0x150, 0x150, 0x180, 0x180, 0x1e0, 0x1e0, 0x240, 0x240, 0x2a0, 0x2a0, 0x300, 0x300, 0x3c0, 0x3c0, 0x480, 0x480, 0x540, 0x540, 0x600, 0x600, 0x6c0, 0x6c0, 0x780, 0x780 } + }; + + u32 parse_stream(std::span stream) override; + u32 parse_stream_header(std::span pes_packet_data, [[maybe_unused]] s8 pts_dts_flag) override { return parse_audio_stream_header<0xffff>(pes_packet_data); } + [[nodiscard]] std::pair get_stream_id() const override { return { 0xbd, (ac3 ? 0x30 : 0x00) | channel }; } + }; + + class user_data_stream final : public elementary_stream + { + public: + user_data_stream(u8 channel, u32 au_max_size, dmux_pamf_base& ctx, u32 user_data, std::span au_queue_buffer) : elementary_stream(channel, au_max_size, ctx, user_data, 0, au_queue_buffer) {} + user_data_stream(utils::serial& ar, u8 channel, dmux_pamf_base& ctx) : elementary_stream(ar, channel, ctx, 0) {} + + private: + u32 parse_stream(std::span stream) override; + u32 parse_stream_header(std::span pes_packet_data, s8 pts_dts_flag) override; + [[nodiscard]] std::pair get_stream_id() const override { return { 0xbd, 0x20 | channel }; } + }; + + + enum class state : u8 + { + initial, + elementary_stream, + prog_end + } + state = state::initial; + + bool demux_done_notified = true; // User was successfully notified that the stream has been consumed + + u8 pack_es_type_idx = umax; // Elementary stream type in the current pack + u8 pack_es_channel = 0; // Elementary stream channel in the current pack + + bool raw_es = false; // Indicates that the input stream is a raw elementary stream instead of a multiplexed MPEG program stream. If set to true, MPEG-PS related parsing will be skipped + + std::optional> stream; // The stream to be demultiplexed, provided by the user + + std::unique_ptr elementary_streams[5][0x10]; // One for each possible type and channel +}; + +// Implementation of the SPU thread +class dmux_pamf_spu_context : dmux_pamf_base +{ +public: + static constexpr u32 id_base = 0; + static constexpr u32 id_step = 1; + static constexpr u32 id_count = 0x400; + SAVESTATE_INIT_POS(std::numeric_limits::max()); // Doesn't depend on or is a dependency of anything + + dmux_pamf_spu_context(vm::ptr> cmd_queue, vm::ptr, 1>> cmd_result_queue, + vm::ptr> stream_info_queue, vm::ptr> event_queue) + : cmd_queue(cmd_queue), cmd_result_queue(cmd_result_queue), stream_info_queue(stream_info_queue), event_queue(event_queue) + { + } + + explicit dmux_pamf_spu_context(utils::serial& ar) + : cmd_queue(ar.pop>>()) + , cmd_result_queue(vm::ptr, 1>>::make(cmd_queue.addr() + sizeof(dmux_pamf_hle_spurs_queue))) + , stream_info_queue(vm::ptr>::make(cmd_result_queue.addr() + sizeof(dmux_pamf_hle_spurs_queue, 1>))) + , event_queue(vm::ptr>::make(stream_info_queue.addr() + sizeof(dmux_pamf_hle_spurs_queue))) + , new_stream(ar.pop()) + { + save_base(ar); + max_enqueued_events += 2 * get_enabled_es_count(); + } + + void save(utils::serial& ar); + + void operator()(); // cellSpursMain() + static constexpr auto thread_name = "HLE PAMF demuxer SPU thread"sv; + +private: + // These are globals in the SPU thread + const vm::ptr> cmd_queue; + const vm::ptr, 1>> cmd_result_queue; + const vm::ptr> stream_info_queue; + const vm::ptr> event_queue; + bool wait_for_au_queue = false; + bool wait_for_event_queue = false; + bool event_queue_was_too_full = false; // Sent to the PPU thread + u8 max_enqueued_events = 4; // 4 + 2 * number of enabled elementary streams + + // This is a local variable in cellSpursMain(), needs to be saved for savestates + bool new_stream = false; + + bool get_next_cmd(DmuxPamfCommand& lhs, bool new_stream) const; + bool send_event(auto&&... args) const; + + // The events are sent to the PPU thread via the event_queue + bool on_au_found(u8 stream_id, u8 private_stream_id, u32 user_data, std::span au, u64 pts, u64 dts, bool rap, u8 au_specific_info_size, std::array au_specific_info_buf) override + { + return !((wait_for_event_queue = !send_event(DmuxPamfEventType::au_found, stream_id, private_stream_id, vm::get_addr(au.data()), std::bit_cast(static_cast>(pts)), + std::bit_cast(static_cast>(dts)), 0, static_cast(au.size()), au_specific_info_size, au_specific_info_buf, user_data, rap))); + } + bool on_demux_done() override { return !((wait_for_event_queue = !send_event(DmuxPamfEventType::demux_done))); } + void on_fatal_error() override { send_event(DmuxPamfEventType::fatal_error); } + bool on_flush_done(u8 stream_id, u8 private_stream_id, u32 user_data) override { return send_event(DmuxPamfEventType::flush_done, stream_id, private_stream_id, user_data); } // The "flush done" event does not set wait_for_event_queue if the queue is full + bool on_prog_end() override { return !((wait_for_event_queue = !send_event(DmuxPamfEventType::prog_end_code))); } + void on_au_queue_full() override { wait_for_au_queue = true; } +}; + +using dmux_pamf_spu_thread = named_thread; + + +// PPU thread + +// For some reason, cellDmuxPamf doesn't use regular error code values and also has a second set of error codes that's only used internally +enum CellDmuxPamfError +{ + CELL_DMUX_PAMF_ERROR_BUSY = 1, + CELL_DMUX_PAMF_ERROR_ARG = 2, + CELL_DMUX_PAMF_ERROR_UNKNOWN_STREAM = 3, + CELL_DMUX_PAMF_ERROR_NO_MEMORY = 5, + CELL_DMUX_PAMF_ERROR_FATAL = 6, +}; + +enum CellDmuxPamfM2vLevel +{ + CELL_DMUX_PAMF_M2V_MP_LL = 0, + CELL_DMUX_PAMF_M2V_MP_ML, + CELL_DMUX_PAMF_M2V_MP_H14, + CELL_DMUX_PAMF_M2V_MP_HL, +}; + +enum CellDmuxPamfAvcLevel +{ + CELL_DMUX_PAMF_AVC_LEVEL_2P1 = 21, + CELL_DMUX_PAMF_AVC_LEVEL_3P0 = 30, + CELL_DMUX_PAMF_AVC_LEVEL_3P1 = 31, + CELL_DMUX_PAMF_AVC_LEVEL_3P2 = 32, + CELL_DMUX_PAMF_AVC_LEVEL_4P1 = 41, + CELL_DMUX_PAMF_AVC_LEVEL_4P2 = 42, +}; + +struct CellDmuxPamfAuSpecificInfoM2v +{ + be_t reserved1; +}; + +struct CellDmuxPamfAuSpecificInfoAvc +{ + be_t reserved1; +}; + +struct CellDmuxPamfAuSpecificInfoLpcm +{ + u8 channelAssignmentInfo; + u8 samplingFreqInfo; + u8 bitsPerSample; +}; + +struct CellDmuxPamfAuSpecificInfoAc3 +{ + be_t reserved1; +}; + +struct CellDmuxPamfAuSpecificInfoAtrac3plus +{ + be_t reserved1; +}; + +struct CellDmuxPamfAuSpecificInfoUserData +{ + be_t reserved1; +}; + +struct CellDmuxPamfEsSpecificInfoM2v +{ + be_t profileLevel; +}; + +struct CellDmuxPamfEsSpecificInfoAvc +{ + be_t level; +}; + +struct CellDmuxPamfEsSpecificInfoLpcm +{ + be_t samplingFreq; + be_t numOfChannels; + be_t bitsPerSample; +}; + +struct CellDmuxPamfEsSpecificInfoAc3 +{ + be_t reserved1; +}; + +struct CellDmuxPamfEsSpecificInfoAtrac3plus +{ + be_t reserved1; +}; + +struct CellDmuxPamfEsSpecificInfoUserData +{ + be_t reserved1; +}; + +enum CellDmuxPamfSamplingFrequency +{ + CELL_DMUX_PAMF_FS_48K = 48000, +}; + +enum CellDmuxPamfBitsPerSample +{ + CELL_DMUX_PAMF_BITS_PER_SAMPLE_16 = 16, + CELL_DMUX_PAMF_BITS_PER_SAMPLE_24 = 24, +}; + +enum CellDmuxPamfLpcmChannelAssignmentInfo +{ + CELL_DMUX_PAMF_LPCM_CH_M1 = 1, + CELL_DMUX_PAMF_LPCM_CH_LR = 3, + CELL_DMUX_PAMF_LPCM_CH_LRCLSRSLFE = 9, + CELL_DMUX_PAMF_LPCM_CH_LRCLSCS1CS2RSLFE = 11, +}; + +enum CellDmuxPamfLpcmFs +{ + CELL_DMUX_PAMF_LPCM_FS_48K = 1, +}; + +enum CellDmuxPamfLpcmBitsPerSamples +{ + CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_16 = 1, + CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_24 = 3, +}; + +constexpr u8 DMUX_PAMF_LPCM_FRAMES_PER_SEC = 200; + +struct CellDmuxPamfSpecificInfo +{ + be_t thisSize; + b8 programEndCodeCb; +}; + +struct CellDmuxPamfResource +{ + be_t ppuThreadPriority; + be_t ppuThreadStackSize; + be_t numOfSpus; + be_t spuThreadPriority; + vm::bptr memAddr; be_t memSize; }; -struct CellDmuxPamfEsAttr +struct DmuxPamfAuInfo { - be_t auQueueMaxSize; - be_t memSize; - be_t specificInfoSize; + vm::bptr addr; + be_t size; + CellCodecTimeStamp pts; + CellCodecTimeStamp dts; + be_t user_data; + vm::bptr specific_info; + be_t specific_info_size; + b8 is_rap; }; + +CHECK_SIZE(DmuxPamfAuInfo, 0x30); + +constexpr u32 DMUX_PAMF_VERSION = 0x280000; +constexpr s32 DMUX_PAMF_MAX_ENABLED_ES_NUM = 0x40; + +// HLE exclusive, for savestates +enum class dmux_pamf_state : u8 +{ + initial, + waiting_for_au_released, + waiting_for_au_released_error, + waiting_for_event, + starting_demux_done, + starting_demux_done_mutex_lock_error, + starting_demux_done_mutex_unlock_error, + starting_demux_done_checking_stream_reset, + starting_demux_done_checking_stream_reset_error, + setting_au_reset, + setting_au_reset_error, + processing_event, + au_found_waiting_for_spu, + unsetting_au_reset, + demux_done_notifying, + demux_done_mutex_lock, + demux_done_cond_signal, + resuming_demux_mutex_lock, + resuming_demux_waiting_for_spu, + sending_fatal_err +}; + +enum class DmuxPamfSequenceState : u32 +{ + dormant, + resetting, + running +}; + +struct DmuxPamfElementaryStream; + +class DmuxPamfContext +{ + // HLE exclusive + // These are local variables in the PPU thread function, they're here for savestates + DmuxPamfEvent event; + u64 au_queue_full_bitset; + b8 stream_reset_started; + b8 stream_reset_in_progress; + + u32 hle_spu_thread_id; + dmux_pamf_state savestate; + + [[maybe_unused]] u8 spurs[0xf6b]; // CellSpurs, 0x1000 bytes on LLE + [[maybe_unused]] vm::bptr spurs_addr; // CellSpurs* + [[maybe_unused]] b8 use_existing_spurs; + + [[maybe_unused]] alignas(0x80) u8 spurs_taskset[0x1900]; // CellSpursTaskset + [[maybe_unused]] be_t spurs_task_id; // CellSpursTaskId + vm::bptr spurs_context_addr; + + [[maybe_unused]] u8 reserved1[0x10]; + + vm::bptr _this; + be_t this_size; + be_t version; + + DmuxCb notify_demux_done; + DmuxCb notify_prog_end_code; + DmuxCb notify_fatal_err; + + CellDmuxPamfResource resource; + + be_t thread_id; // sys_ppu_thread_t + + be_t unk; // Unused + + be_t ppu_thread_stack_size; + + be_t au_released_bitset; // Each bit corresponds to an elementary stream, if a bit is set then cellDmuxReleaseAu() was called for that elementary stream + + b8 stream_reset_requested; + + be_t sequence_state; + + be_t max_enabled_es_num; + be_t enabled_es_num; + vm::bptr elementary_streams[DMUX_PAMF_MAX_ENABLED_ES_NUM]; + + be_t mutex; // sys_mutex_t + be_t cond; // sys_cond_t + + vm::bptr> cmd_queue_addr_; // Same as cmd_queue_addr, unused + vm::bptr cmd_queue_buffer_addr_; // Same as cmd_queue_buffer_addr, unused + + vm::bptr> cmd_queue_addr; // CellSpursQueue* + vm::bptr, 1>> cmd_result_queue_addr; // CellSpursQueue* + vm::bptr> stream_info_queue_addr; // CellSpursQueue* + vm::bptr> event_queue_addr; // CellSpursQueue* + + vm::bptr cmd_queue_buffer_addr; + vm::bptr[1]> cmd_result_queue_buffer_addr; + vm::bptr event_queue_buffer_addr; + vm::bptr stream_info_queue_buffer_addr; + + vm::bptr> cmd_queue_addr__; // Same as cmd_queue_addr, unused + + be_t user_data; + + b8 is_raw_es; + + be_t next_es_id; + + char spurs_taskset_name[24]; + + [[maybe_unused]] u8 reserved2[928]; // Unused + + dmux_pamf_hle_spurs_queue cmd_queue; // CellSpursQueue + dmux_pamf_hle_spurs_queue, 1> cmd_result_queue; // CellSpursQueue + dmux_pamf_hle_spurs_queue stream_info_queue; // CellSpursQueue + dmux_pamf_hle_spurs_queue event_queue; // CellSpursQueue + + DmuxPamfCommand cmd_queue_buffer[1]; + alignas(0x80) be_t cmd_result_queue_buffer[1]; + DmuxPamfStreamInfo stream_info_queue_buffer[1]; + DmuxPamfEvent event_queue_buffer[4 + 2 * DMUX_PAMF_MAX_ENABLED_ES_NUM]; + + alignas(0x80) u8 spurs_context[0x36400]; + + + template + void send_spu_command_and_wait(ppu_thread& ppu, bool waiting_for_spu_state, auto&&... cmd_params); + + error_code wait_au_released_or_stream_reset(ppu_thread& ppu, u64 au_queue_full_bitset, b8& stream_reset_started, dmux_pamf_state& savestate); + + template + error_code set_au_reset(ppu_thread& ppu); + + template + static error_code callback(ppu_thread& ppu, DmuxCb cb, auto&&... args); + + friend struct DmuxPamfElementaryStream; + +public: + void run_spu_thread(); + + DmuxPamfElementaryStream* find_es(u16 stream_id, u16 private_stream_id); + + void exec(ppu_thread& ppu); + + static error_code open(ppu_thread& ppu, const CellDmuxPamfResource& res, vm::cptr res_spurs, const DmuxCb& notify_dmux_done, + const DmuxCb& notify_prog_end_code, const DmuxCb& notify_fatal_err, vm::bptr& handle); + error_code create_thread(ppu_thread& ppu); + error_code close(ppu_thread& ppu); + error_code reset_stream(ppu_thread& ppu); + error_code join_thread(ppu_thread& ppu); + + template + error_code set_stream(ppu_thread& ppu, vm::cptr stream_address, u32 stream_size, b8 discontinuity, u32 user_data); + + template + error_code enable_es(ppu_thread& ppu, u16 stream_id, u16 private_stream_id, bool is_avc, vm::cptr es_specific_info, vm::ptr mem_addr, u32 mem_size, const DmuxCb& notify_au_found, + const DmuxCb& notify_flush_done, vm::bptr& es); + + error_code reset_stream_and_wait_done(ppu_thread& ppu); +}; + +static_assert(std::is_standard_layout_v && std::is_trivial_v); +CHECK_SIZE_ALIGN(DmuxPamfContext, 0x3d880, 0x80); + +struct CellDmuxPamfHandle +{ + vm::bptr demuxer; + + DmuxCb notify_demux_done; + DmuxCb notify_prog_end_code; + DmuxCb notify_fatal_err; +}; + +CHECK_SIZE(CellDmuxPamfHandle, 0x1c); + +struct DmuxPamfElementaryStream +{ + vm::bptr _this; + be_t this_size; + u8 this_index; + + vm::bptr demuxer; + + DmuxCb notify_au_found; + DmuxCb notify_flush_done; + + be_t stream_id; + be_t private_stream_id; + b8 is_avc; + + vm::bptr au_queue_buffer; + be_t unk; // Likely au_queue_buffer_size, unused + be_t au_max_size; + u8 au_specific_info[0x10]; + be_t au_specific_info_size; + + b8 reset_next_au; + + be_t es_id; + + u8 reserved[72]; + + error_code release_au(ppu_thread& ppu, vm::ptr au_addr, u32 au_size) const; + error_code disable_es(ppu_thread& ppu); + error_code flush_es(ppu_thread& ppu) const; + error_code reset_es(ppu_thread& ppu) const; +}; + +static_assert(std::is_standard_layout_v && std::is_trivial_v); +CHECK_SIZE_ALIGN(DmuxPamfElementaryStream, 0x98, 4); + +struct CellDmuxPamfEsHandle +{ + vm::bptr es; + + DmuxCb notify_au_found; + DmuxCb notify_flush_done; +}; + +CHECK_SIZE(CellDmuxPamfEsHandle, 0x14); diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 3c7b299af7..f9f5ea4100 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -770,8 +770,8 @@ namespace gem if constexpr (use_gain) { dst0[0] = static_cast(std::clamp(r * gain_r, 0.0f, 255.0f)); - dst0[1] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); - dst0[2] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + dst0[1] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + dst0[2] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); } else { @@ -822,8 +822,8 @@ namespace gem if constexpr (use_gain) { dst0[0] = static_cast(std::clamp(r * gain_r, 0.0f, 255.0f)); - dst0[1] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); - dst0[2] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + dst0[1] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + dst0[2] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); } else { @@ -845,6 +845,53 @@ namespace gem debayer_raw8_impl(src, dst, alpha, gain_r, gain_g, gain_b); } + template + static inline void debayer_raw8_downscale_impl(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b) + { + constexpr u32 in_pitch = 640; + constexpr u32 out_pitch = 320 * 4; + + // Simple debayer + for (s32 y = 0; y < 240; y++) + { + const u8* src0 = src + y * 2 * in_pitch; + const u8* src1 = src0 + in_pitch; + + u8* dst0 = dst + y * out_pitch; + + for (s32 x = 0; x < 320; x++, dst0 += 4, src0 += 2, src1 += 2) + { + const u8 b = src0[0]; + const u8 g0 = src0[1]; + const u8 g1 = src1[0]; + const u8 r = src1[1]; + const u8 g = (g0 + g1) >> 1; + + if constexpr (use_gain) + { + dst0[0] = static_cast(std::clamp(r * gain_r, 0.0f, 255.0f)); + dst0[1] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + dst0[2] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); + } + else + { + dst0[0] = r; + dst0[1] = g; + dst0[2] = b; + } + dst0[3] = alpha; + } + } + } + + static void debayer_raw8_downscale(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b) + { + if (gain_r != 1.0f || gain_g != 1.0f || gain_b != 1.0f) + debayer_raw8_downscale_impl(src, dst, alpha, gain_r, gain_g, gain_b); + else + debayer_raw8_downscale_impl(src, dst, alpha, gain_r, gain_g, gain_b); + } + bool convert_image_format(CellCameraFormat input_format, const CellGemVideoConvertAttribute& vc, const std::vector& video_data_in, u32 width, u32 height, u8* video_data_out, u32 video_data_out_size, u8* buffer_memory, @@ -881,9 +928,9 @@ namespace gem const u8* src_data = video_data_in.data(); const u8 alpha = vc.alpha; - const f32 gain_r = vc.gain * vc.blue_gain; + const f32 gain_r = vc.gain * vc.red_gain; const f32 gain_g = vc.gain * vc.green_gain; - const f32 gain_b = vc.gain * vc.red_gain; + const f32 gain_b = vc.gain * vc.blue_gain; // Only RAW8 should be relevant for cellGem unless I'm mistaken if (input_format == CELL_CAMERA_RAW8) @@ -1183,34 +1230,7 @@ namespace gem { case CELL_CAMERA_RAW8: { - const u32 in_pitch = width; - const u32 out_pitch = width * 4 / 2; - - for (u32 y = 0; y < height - 1; y += 2) - { - const u8* src0 = src_data + y * in_pitch; - const u8* src1 = src0 + in_pitch; - - u8* dst0 = video_data_out + (y / 2) * out_pitch; - u8* dst1 = dst0 + out_pitch; - - for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst0 += 4, dst1 += 4) - { - const u8 b = src0[0]; - const u8 g0 = src0[1]; - const u8 g1 = src1[0]; - const u8 r = src1[1]; - - const u8 top[4] = { r, g0, b, alpha }; - const u8 bottom[4] = { r, g1, b, alpha }; - - // Top-Left - std::memcpy(dst0, top, 4); - - // Bottom-Left Pixel - std::memcpy(dst1, bottom, 4); - } - } + debayer_raw8_downscale(src_data, video_data_out, alpha, gain_r, gain_g, gain_b); break; } case CELL_CAMERA_RGBA: @@ -1609,13 +1629,8 @@ public: return false; } - if (!m_camera_info.bytesize) - { - cellGem.error("gem_tracker: unexpected image size: %d", m_camera_info.bytesize); - return false; - } - m_tracker.set_image_data(m_camera_info.buffer.get_ptr(), m_camera_info.bytesize, m_camera_info.width, m_camera_info.height, m_camera_info.format); + m_framenumber++; // using framenumber instead of timestamp since the timestamp could be identical return true; } @@ -1648,6 +1663,7 @@ public: } auto& gem = g_fxo->get(); + u64 last_framenumber = 0; while (thread_ctrl::state() != thread_state::aborting) { @@ -1663,6 +1679,13 @@ public: } } + if (std::exchange(last_framenumber, m_framenumber.load()) == last_framenumber) + { + cellGem.warning("Tracker woke up without new frame. Skipping processing (framenumber=%d)", last_framenumber); + tracker_done(); + continue; + } + m_busy.release(true); // Update PS Move LED colors @@ -1751,9 +1774,16 @@ public: shared_mutex mutex; + gem_tracker& operator=(thread_state) noexcept + { + wake_up_tracker(); + return *this; + } + private: atomic_t m_wake_up_tracker = 0; atomic_t m_tracker_done = 0; + atomic_t m_framenumber = 0; atomic_t m_busy = false; ps_move_tracker m_tracker{}; CellCameraInfoEx m_camera_info{}; @@ -1873,21 +1903,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con gem_state->pos[2] = controller.distance_mm; gem_state->pos[3] = 0.f; - // TODO: calculate handle position based on our world coordinate and the angles - gem_state->handle_pos[0] = camera_x; - gem_state->handle_pos[1] = camera_y; - gem_state->handle_pos[2] = controller.distance_mm + 10.0f; - gem_state->handle_pos[3] = 0.f; - // Calculate orientation - if (g_cfg.io.move == move_handler::real || (g_cfg.io.move == move_handler::fake && move_data.orientation_enabled)) - { - gem_state->quat[0] = move_data.quaternion.x(); - gem_state->quat[1] = move_data.quaternion.y(); - gem_state->quat[2] = move_data.quaternion.z(); - gem_state->quat[3] = move_data.quaternion.w(); - } - else + ps_move_data::vect<4> quat = move_data.quaternion; + + if (g_cfg.io.move != move_handler::real && !(g_cfg.io.move == move_handler::fake && move_data.orientation_enabled)) { const f32 max_angle_per_side_h = g_cfg.io.fake_move_rotation_cone_h / 2.0f; const f32 max_angle_per_side_v = g_cfg.io.fake_move_rotation_cone_v / 2.0f; @@ -1901,17 +1920,27 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con const f32 cy = std::cos(yaw * 0.5f); const f32 sy = std::sin(yaw * 0.5f); - const f32 q_x = sr * cp * cy - cr * sp * sy; - const f32 q_y = cr * sp * cy + sr * cp * sy; - const f32 q_z = cr * cp * sy - sr * sp * cy; - const f32 q_w = cr * cp * cy + sr * sp * sy; - - gem_state->quat[0] = q_x; - gem_state->quat[1] = q_y; - gem_state->quat[2] = q_z; - gem_state->quat[3] = q_w; + quat.x() = sr * cp * cy - cr * sp * sy; + quat.y() = cr * sp * cy + sr * cp * sy; + quat.z() = cr * cp * sy - sr * sp * cy; + quat.w() = cr * cp * cy + sr * sp * sy; } + gem_state->quat[0] = quat.x(); + gem_state->quat[1] = quat.y(); + gem_state->quat[2] = quat.z(); + gem_state->quat[3] = quat.w(); + + // Calculate handle position based on our world coordinate and the current orientation + constexpr ps_move_data::vect<3> offset_local_mm({0.f, 0.f, -45.f}); // handle is ~45 mm below sphere + const ps_move_data::vect<3> offset_world = ps_move_data::rotate_vector(quat, offset_local_mm); + + gem_state->handle_pos[0] = gem_state->pos[0] - offset_world.x(); // Flip x offset + gem_state->handle_pos[1] = gem_state->pos[1] - offset_world.y(); // Flip y offset + gem_state->handle_pos[2] = gem_state->pos[2] + offset_world.z(); + gem_state->handle_pos[3] = 0.f; + + // Calculate velocity if constexpr (!ps_move_data::use_imu_for_velocity) { move_data.update_velocity(shared_data.frame_timestamp_us, gem_state->pos); @@ -1920,6 +1949,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con { gem_state->vel[i] = move_data.vel_world[i]; gem_state->accel[i] = move_data.accel_world[i]; + + // TODO: maybe this also needs to be adjusted depending on the orientation + gem_state->handle_vel[i] = gem_state->vel[i]; + gem_state->handle_accel[i] = gem_state->accel[i]; } } @@ -3612,7 +3645,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr ext_id, v if (!pad->move_data.external_device_read_requested) { *ext_id = controller.ext_id = pad->move_data.external_device_id; - std::memcpy(pad->move_data.external_device_read.data(), ext_info.get_ptr(), CELL_GEM_EXTERNAL_PORT_OUTPUT_SIZE); + std::memcpy(ext_info.get_ptr(), pad->move_data.external_device_read.data(), CELL_GEM_EXTERNAL_PORT_DEVICE_INFO_SIZE); break; } } @@ -3876,13 +3909,15 @@ error_code cellGemUpdateStart(vm::cptr camera_frame, u64 timestamp) gem.camera_frame = camera_frame.addr(); - if (!tracker.set_image(gem.camera_frame)) + const bool image_set = tracker.set_image(gem.camera_frame); + + tracker.wake_up_tracker(); + + if (!image_set) { return not_an_error(CELL_GEM_NO_VIDEO); } - tracker.wake_up_tracker(); - return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp index 97375c4e6d..83b001cc52 100644 --- a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp +++ b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp @@ -192,7 +192,46 @@ error_code cellNetCtlDelHandler(s32 hid) error_code cellNetCtlGetInfo(s32 code, vm::ptr info) { - cellNetCtl.warning("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info); + bool log_it_once = false; + + switch (code) + { + case CELL_NET_CTL_INFO_ETHER_ADDR: + case CELL_NET_CTL_INFO_DEVICE: + case CELL_NET_CTL_INFO_MTU: + case CELL_NET_CTL_INFO_LINK_TYPE: + case CELL_NET_CTL_INFO_IP_CONFIG: + case CELL_NET_CTL_INFO_IP_ADDRESS: + case CELL_NET_CTL_INFO_NETMASK: + case CELL_NET_CTL_INFO_DEFAULT_ROUTE: + case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG: + case CELL_NET_CTL_INFO_UPNP_CONFIG: + { + log_it_once = true; + break; + } + default: + { + break; + } + } + + bool log_it = true; + + if (log_it_once && vm::check_addr(info.addr())) + { + struct logged_t + { + std::array, 256> logged_code{}; + }; + + if (g_fxo->get().logged_code[::narrow(code)].exchange(true)) + { + log_it = false; + } + } + + (log_it ? cellNetCtl.warning : cellNetCtl.trace)("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info); auto& nph = g_fxo->get>(); diff --git a/rpcs3/Emu/Cell/Modules/cellPamf.cpp b/rpcs3/Emu/Cell/Modules/cellPamf.cpp index d63e7bb3e2..4bb383c2d2 100644 --- a/rpcs3/Emu/Cell/Modules/cellPamf.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPamf.cpp @@ -5,14 +5,6 @@ #include #include "cellPamf.h" -const std::function SQUEUE_ALWAYS_EXIT = []() { return true; }; -const std::function SQUEUE_NEVER_EXIT = []() { return false; }; - -bool squeue_test_exit() -{ - return Emu.IsStopped(); -} - LOG_CHANNEL(cellPamf); template<> diff --git a/rpcs3/Emu/Cell/Modules/cellPamf.h b/rpcs3/Emu/Cell/Modules/cellPamf.h index e42acf60f4..14608f9100 100644 --- a/rpcs3/Emu/Cell/Modules/cellPamf.h +++ b/rpcs3/Emu/Cell/Modules/cellPamf.h @@ -1,5 +1,6 @@ #pragma once +#include "Emu/Cell/ErrorCodes.h" #include "Emu/Memory/vm_ptr.h" // Error Codes @@ -594,345 +595,3 @@ struct CellPamfReader CHECK_SIZE(CellPamfReader, 128); error_code cellPamfReaderInitialize(vm::ptr pSelf, vm::cptr pAddr, u64 fileSize, u32 attribute); - -#include -#include - -extern const std::function SQUEUE_ALWAYS_EXIT; -extern const std::function SQUEUE_NEVER_EXIT; - -bool squeue_test_exit(); - -// TODO: eliminate this boolshit -template -class squeue_t -{ - struct squeue_sync_var_t - { - struct - { - u32 position : 31; - u32 pop_lock : 1; - }; - struct - { - u32 count : 31; - u32 push_lock : 1; - }; - }; - - atomic_t m_sync; - - mutable std::mutex m_rcv_mutex; - mutable std::mutex m_wcv_mutex; - mutable std::condition_variable m_rcv; - mutable std::condition_variable m_wcv; - - T m_data[sq_size]; - - enum squeue_sync_var_result : u32 - { - SQSVR_OK = 0, - SQSVR_LOCKED = 1, - SQSVR_FAILED = 2, - }; - -public: - squeue_t() - : m_sync(squeue_sync_var_t{}) - { - } - - static u32 get_max_size() - { - return sq_size; - } - - bool is_full() const - { - return m_sync.load().count == sq_size; - } - - bool push(const T& data, const std::function& test_exit) - { - u32 pos = 0; - - while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32 - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - - if (sync.push_lock) - { - return SQSVR_LOCKED; - } - if (sync.count == sq_size) - { - return SQSVR_FAILED; - } - - sync.push_lock = 1; - pos = sync.position + sync.count; - return SQSVR_OK; - })) - { - if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit())) - { - return false; - } - - std::unique_lock wcv_lock(m_wcv_mutex); - m_wcv.wait_for(wcv_lock, std::chrono::milliseconds(1)); - } - - m_data[pos >= sq_size ? pos - sq_size : pos] = data; - - m_sync.atomic_op([](squeue_sync_var_t& sync) - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - ensure(!!sync.push_lock); - sync.push_lock = 0; - sync.count++; - }); - - m_rcv.notify_one(); - m_wcv.notify_one(); - return true; - } - - bool push(const T& data, const volatile bool* do_exit) - { - return push(data, [do_exit]() { return do_exit && *do_exit; }); - } - - bool push(const T& data) - { - return push(data, SQUEUE_NEVER_EXIT); - } - - bool try_push(const T& data) - { - return push(data, SQUEUE_ALWAYS_EXIT); - } - - bool pop(T& data, const std::function& test_exit) - { - u32 pos = 0; - - while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32 - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - - if (!sync.count) - { - return SQSVR_FAILED; - } - if (sync.pop_lock) - { - return SQSVR_LOCKED; - } - - sync.pop_lock = 1; - pos = sync.position; - return SQSVR_OK; - })) - { - if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit())) - { - return false; - } - - std::unique_lock rcv_lock(m_rcv_mutex); - m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1)); - } - - data = m_data[pos]; - - m_sync.atomic_op([](squeue_sync_var_t& sync) - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - ensure(!!sync.pop_lock); - sync.pop_lock = 0; - sync.position++; - sync.count--; - if (sync.position == sq_size) - { - sync.position = 0; - } - }); - - m_rcv.notify_one(); - m_wcv.notify_one(); - return true; - } - - bool pop(T& data, const volatile bool* do_exit) - { - return pop(data, [do_exit]() { return do_exit && *do_exit; }); - } - - bool pop(T& data) - { - return pop(data, SQUEUE_NEVER_EXIT); - } - - bool try_pop(T& data) - { - return pop(data, SQUEUE_ALWAYS_EXIT); - } - - bool peek(T& data, u32 start_pos, const std::function& test_exit) - { - ensure(start_pos < sq_size); - u32 pos = 0; - - while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32 - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - - if (sync.count <= start_pos) - { - return SQSVR_FAILED; - } - if (sync.pop_lock) - { - return SQSVR_LOCKED; - } - - sync.pop_lock = 1; - pos = sync.position + start_pos; - return SQSVR_OK; - })) - { - if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit())) - { - return false; - } - - std::unique_lock rcv_lock(m_rcv_mutex); - m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1)); - } - - data = m_data[pos >= sq_size ? pos - sq_size : pos]; - - m_sync.atomic_op([](squeue_sync_var_t& sync) - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - ensure(!!sync.pop_lock); - sync.pop_lock = 0; - }); - - m_rcv.notify_one(); - return true; - } - - bool peek(T& data, u32 start_pos, const volatile bool* do_exit) - { - return peek(data, start_pos, [do_exit]() { return do_exit && *do_exit; }); - } - - bool peek(T& data, u32 start_pos = 0) - { - return peek(data, start_pos, SQUEUE_NEVER_EXIT); - } - - bool try_peek(T& data, u32 start_pos = 0) - { - return peek(data, start_pos, SQUEUE_ALWAYS_EXIT); - } - - class squeue_data_t - { - T* const m_data; - const u32 m_pos; - const u32 m_count; - - squeue_data_t(T* data, u32 pos, u32 count) - : m_data(data) - , m_pos(pos) - , m_count(count) - { - } - - public: - T& operator [] (u32 index) - { - ensure(index < m_count); - index += m_pos; - index = index < sq_size ? index : index - sq_size; - return m_data[index]; - } - }; - - void process(void(*proc)(squeue_data_t data)) - { - u32 pos, count; - - while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32 - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - - if (sync.pop_lock || sync.push_lock) - { - return SQSVR_LOCKED; - } - - pos = sync.position; - count = sync.count; - sync.pop_lock = 1; - sync.push_lock = 1; - return SQSVR_OK; - })) - { - std::unique_lock rcv_lock(m_rcv_mutex); - m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1)); - } - - proc(squeue_data_t(m_data, pos, count)); - - m_sync.atomic_op([](squeue_sync_var_t& sync) - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - ensure(!!sync.pop_lock); - ensure(!!sync.push_lock); - sync.pop_lock = 0; - sync.push_lock = 0; - }); - - m_wcv.notify_one(); - m_rcv.notify_one(); - } - - void clear() - { - while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32 - { - ensure(sync.count <= sq_size); - ensure(sync.position < sq_size); - - if (sync.pop_lock || sync.push_lock) - { - return SQSVR_LOCKED; - } - - sync.pop_lock = 1; - sync.push_lock = 1; - return SQSVR_OK; - })) - { - std::unique_lock rcv_lock(m_rcv_mutex); - m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1)); - } - - m_sync.exchange({}); - m_wcv.notify_one(); - m_rcv.notify_one(); - } -}; diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index 1bc0a345db..e6b8bff945 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -1267,6 +1267,11 @@ struct SceNpOnlineId { char data[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1]; // char term; char dummy[3]; + + bool operator<(const SceNpOnlineId& other) const + { + return memcmp(data, other.data, sizeof(data)) < 0; + } }; // NP ID structure @@ -1283,6 +1288,11 @@ struct SceNpId }; u8 reserved[8]; + + bool operator<(const SceNpId& other) const + { + return handle < other.handle; + } }; CHECK_SIZE_ALIGN(SceNpId, 0x24, 1); @@ -1689,12 +1699,22 @@ struct SceNpLobbyId { u8 opt[28]; u8 reserved[8]; + + bool operator<(const SceNpLobbyId& other) const + { + return memcmp(opt, other.opt, sizeof(opt)) < 0; + } }; struct SceNpRoomId { u8 opt[28]; u8 reserved[8]; + + bool operator<(const SceNpRoomId& other) const + { + return memcmp(opt, other.opt, sizeof(opt)) < 0; + } }; struct SceNpMatchingAttr diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 56d4398d09..783ed9e477 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -2535,7 +2535,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con // SLDI mnemonic reg_state_t rs = get_reg(op.rs); - if (!rs.shift_left(op.sh32, reg_tag_allocator)) + if (!rs.shift_left(sh, reg_tag_allocator)) { unmap_reg(op.ra); } diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index b298539519..e95a1542d8 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1004,7 +1004,7 @@ static import_result_t ppu_load_imports(const ppu_module& _module, std: // Check address // TODO: The address of use should be extracted from analyser instead - if (fstub && fstub >= _module.segs[0].addr && fstub <= _module.segs[0].addr + _module.segs[0].size) + if (fstub && fstub >= _module.segs[0].addr && fstub < _module.segs[0].addr + _module.segs[0].size) { nid_to_use_addr.emplace(fnid, fstub); } @@ -1895,7 +1895,7 @@ shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c } else { - ppu_loader.error("Library %s: PRX library info not found"); + ppu_loader.error("Library: PRX library info not found"); } prx->start.set(prx->specials[0xbc9a0086]); @@ -3192,7 +3192,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf) for (const auto& s : elf.shdrs) { - if (s.sh_type != sec_type::sht_progbits) + if (s.sh_type == sec_type::sht_progbits) { memsize = utils::align(memsize + vm::cast(s.sh_size), 128); } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a9bef5e640..f5d91cc519 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1286,7 +1286,7 @@ extern bool ppu_patch(u32 addr, u32 value) { if (addr % 4) { - ppu_log.fatal("Patch failed at 0x%x: unanligned memory address.", addr); + ppu_log.fatal("Patch failed at 0x%x: unaligned memory address.", addr); return false; } @@ -1364,9 +1364,7 @@ void ppu_thread::dump_regs(std::string& ret, std::any& custom_data) const u32 preferred_cr_field_index = 7; }; - dump_registers_data_t* func_data = nullptr; - - func_data = std::any_cast(&custom_data); + dump_registers_data_t* func_data = std::any_cast(&custom_data); if (!func_data) { @@ -2039,9 +2037,9 @@ std::vector> ppu_thread::dump_callstack_list() const return call_stack_list; } -std::string ppu_thread::dump_misc() const +void ppu_thread::dump_misc(std::string& ret, std::any& custom_data) const { - std::string ret = cpu_thread::dump_misc(); + cpu_thread::dump_misc(ret, custom_data); if (ack_suspend) { @@ -2096,7 +2094,6 @@ std::string ppu_thread::dump_misc() const { ret += '\n'; } - return ret; } void ppu_thread::dump_all(std::string& ret) const @@ -3867,12 +3864,12 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector offs; + for (u32 j = 0; j < hdr.count; j++) { mself_record rec{}; - std::set offs; - if (mself.read(rec) && rec.get_pos(mself.size())) { if (rec.size <= 0x20) diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 97c705aed5..cf5b91c487 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -145,7 +145,7 @@ public: virtual void dump_regs(std::string&, std::any& custom_data) const override; virtual std::string dump_callstack() const override; virtual std::vector> dump_callstack_list() const override; - virtual std::string dump_misc() const override; + virtual void dump_misc(std::string& ret, std::any& custom_data) const override; virtual void dump_all(std::string&) const override; virtual void cpu_task() override final; virtual void cpu_sleep() override; diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 70d34aa775..0205715328 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -340,7 +340,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto ftype = FunctionType::get(get_type(), { get_type(), // Exec base - m_ir->getPtrTy(), // PPU context + get_type(), // PPU context get_type(), // Segment address (for PRX) get_type(), // Memory base get_type(), // r0 @@ -386,7 +386,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs)); // Create an array of function pointers - const auto func_table_type = ArrayType::get(m_ir->getPtrTy(), functions.size()); + const auto func_table_type = ArrayType::get(get_type(), functions.size()); const auto init_func_table = ConstantArray::get(func_table_type, functions); const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table); @@ -413,7 +413,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto func_pc = ZExt(m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst), get_type()); ptr_inst = dyn_cast(m_ir->CreateGEP(func_table->getValueType(), func_table, {m_ir->getInt64(0), index_value})); - assert(ptr_inst->getResultElementType() == m_ir->getPtrTy()); + assert(ptr_inst->getResultElementType() == get_type()); const auto faddr = m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst); const auto pos_32 = m_reloc ? m_ir->CreateAdd(func_pc, m_seg0) : func_pc; @@ -550,11 +550,12 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect) else if (_target >= caddr && _target <= cend) { u32 target_last = static_cast(_target); - std::unordered_set passed_targets{target_last}; // Try to follow unconditional branches as long as there is no infinite loop - while (target_last != _target) + // !! Triggers compilation issues in Asura's Wrath in other parts of the code + // !! See https://github.com/RPCS3/rpcs3/issues/18287 + while (false) { const ppu_opcode_t op{*ensure(m_info.get_ptr(target_last))}; const ppu_itype::type itype = g_ppu_itype.decode(op.opcode); @@ -621,7 +622,7 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect) const auto pos = m_ir->CreateShl(indirect, 1); const auto ptr = m_ir->CreatePtrAdd(m_exec, pos); const auto val = m_ir->CreateLoad(get_type(), ptr); - callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, m_ir->getPtrTy())); + callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, get_type())); // Load new segment address const auto seg_base_ptr = m_ir->CreatePtrAdd(m_exec, m_ir->getInt64(vm::g_exec_addr_seg_offset)); @@ -1304,7 +1305,7 @@ void PPUTranslator::VMADDFP(ppu_opcode_t op) if (!m_use_fma && data == v128{}) { set_vr(op.vd, vec_handle_result(a * c + fsplat(0.f))); - ppu_log.notice("LLVM: VMADDFP with -0 addend at [0x%08x]", m_addr + (m_reloc ? m_reloc->addr : 0)); + ppu_log.notice("LLVM: VMADDFP with +0 addend at [0x%08x]", m_addr + (m_reloc ? m_reloc->addr : 0)); return; } } @@ -3680,9 +3681,7 @@ void PPUTranslator::STVLX(ppu_opcode_t op) const auto addr = op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb); const auto data = pshufb(get_vr(op.vs), build(127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112) + vsplat(trunc(value(addr) & 0xf))); const auto mask = bitcast(splat(0xffff) << trunc(value(addr) & 0xf)); - const auto ptr = value(GetMemory(m_ir->CreateAnd(addr, ~0xfull))); - const auto align = splat(16); - eval(llvm_calli{"llvm.masked.store.v16i8.p0", {data, ptr, align, mask}}); + m_ir->CreateMaskedStore(data.eval(m_ir), GetMemory(m_ir->CreateAnd(addr, ~0xfull)), llvm::Align(16), mask.eval(m_ir)); } void PPUTranslator::STDBRX(ppu_opcode_t op) @@ -3710,9 +3709,7 @@ void PPUTranslator::STVRX(ppu_opcode_t op) const auto addr = op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb); const auto data = pshufb(get_vr(op.vs), build(255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240) + vsplat(trunc(value(addr) & 0xf))); const auto mask = bitcast(trunc(splat(0xffff) << (value(addr) & 0xf) >> 16)); - const auto ptr = value(GetMemory(m_ir->CreateAnd(addr, ~0xfull))); - const auto align = splat(16); - eval(llvm_calli{"llvm.masked.store.v16i8.p0", {data, ptr, align, mask}}); + m_ir->CreateMaskedStore(data.eval(m_ir), GetMemory(m_ir->CreateAnd(addr, ~0xfull)), llvm::Align(16), mask.eval(m_ir)); } void PPUTranslator::STFSUX(ppu_opcode_t op) @@ -5417,7 +5414,7 @@ MDNode* PPUTranslator::CheckBranchProbability(u32 bo) void PPUTranslator::build_interpreter() { #define BUILD_VEC_INST(i) { \ - m_function = llvm::cast(m_module->getOrInsertFunction("op_" #i, get_type(), m_ir->getPtrTy()).getCallee()); \ + m_function = llvm::cast(m_module->getOrInsertFunction("op_" #i, get_type(), get_type()).getCallee()); \ std::fill(std::begin(m_globals), std::end(m_globals), nullptr); \ std::fill(std::begin(m_locals), std::end(m_locals), nullptr); \ IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); \ diff --git a/rpcs3/Emu/Cell/SPUAnalyser.h b/rpcs3/Emu/Cell/SPUAnalyser.h index 123a629bed..1598551c7d 100644 --- a/rpcs3/Emu/Cell/SPUAnalyser.h +++ b/rpcs3/Emu/Cell/SPUAnalyser.h @@ -13,6 +13,7 @@ struct spu_itype static constexpr struct quadrop_tag{} _quadrop{}; // 4-op Instructions static constexpr struct xfloat_tag{} xfloat{}; // Instructions producing xfloat values static constexpr struct zregmod_tag{} zregmod{}; // Instructions not modifying any GPR + static constexpr struct pure_tag{} pure{}; // Instructions that always produce the same values as long as arguments are equal enum class type : unsigned char { @@ -158,6 +159,15 @@ struct spu_itype CUFLT, FRDS, // xfloat_tag last + CFLTS, + CFLTU, + FCEQ, + FCMEQ, + FCGT, + FCMGT, // floating_tag last + FSCRWR, + FSCRRD, + DFA, DFS, DFM, @@ -167,20 +177,11 @@ struct spu_itype DFNMA, FESD, - CFLTS, - CFLTU, - FCEQ, - FCMEQ, - FCGT, - FCMGT, - FSCRWR, - FSCRRD, - DFCEQ, DFCMEQ, DFCGT, DFCMGT, - DFTSV, // floating_tag last + DFTSV, SHLH, // shiftrot_tag first SHLHI, @@ -248,10 +249,10 @@ struct spu_itype return value >= BR && value <= BISL; } - // Test for floating point instruction + // Test for floating point instruction (32-bit float) friend constexpr bool operator &(type value, floating_tag) { - return value >= FMA && value <= DFTSV; + return value >= FMA && value <= FCMGT; } // Test for 4-op instruction @@ -301,8 +302,16 @@ struct spu_itype { return (value >= HEQ && value <= STQR) || (value >= BR && value <= BIHNZ); } + + // Test for instructions which always produce the same values as long as arguments and immediate values are equal + friend constexpr bool operator &(type value, pure_tag) + { + return (value >= ILH && value <= CLGTI); + } }; +using spu_itype_t = spu_itype::type; + struct spu_iflag { enum @@ -528,6 +537,8 @@ struct spu_iflag } }; +using spu_iflag_t = spu_iflag::flag; + #define NAME(x) static constexpr const char& x = *#x struct spu_iname diff --git a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp index 1b6003036b..53dc0df200 100644 --- a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp @@ -52,6 +52,36 @@ struct span_less template inline constexpr span_less s_span_less{}; +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](spu_recompiler_base::compare_direction arg) + { + switch (arg) + { + case spu_recompiler_base::CMP_SLESS: return "SLT"; + case spu_recompiler_base::CMP_SGREATER: return "SGT"; + case spu_recompiler_base::CMP_EQUAL: return "IEQ"; + case spu_recompiler_base::CMP_LLESS: return "ULT"; + case spu_recompiler_base::CMP_LGREATER: return "UGT"; + case spu_recompiler_base::CMP_SGREATER_EQUAL: return "SGE"; + case spu_recompiler_base::CMP_SLOWER_EQUAL: return "SLE"; + case spu_recompiler_base::CMP_NOT_EQUAL: return "INE"; + case spu_recompiler_base::CMP_LGREATER_EQUAL: return "UGE"; + case spu_recompiler_base::CMP_LLOWER_EQUAL: return "ULE"; + case spu_recompiler_base::CMP_UNKNOWN: + case spu_recompiler_base::CMP_NOT_EQUAL2: + case spu_recompiler_base::CMP_EQUAL2: + default: + { + break; + } + } + + return unknown; + }); +} + // Move 4 args for calling native function from a GHC calling convention function #if defined(ARCH_X64) static u8* move_args_ghc_to_native(u8* raw) @@ -2927,7 +2957,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s if (g_cfg.core.spu_block_size == spu_block_size_type::safe) { // Stop on special instructions (TODO) - m_targets[pos]; + m_targets[pos].push_back(SPU_LS_SIZE); next_block(); break; } @@ -2948,7 +2978,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s spu_log.error("[0x%x] Invalid interrupt flags (DE)", pos); } - m_targets[pos]; + m_targets[pos].push_back(SPU_LS_SIZE); next_block(); break; } @@ -2981,7 +3011,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s bool is_no_return = false; - if (pos_next >= lsa && pos_next < limit) + if (sl && pos_next >= lsa && pos_next < limit) { const u32 data_next = ls[pos_next / 4]; const auto type_next = g_spu_itype.decode(data_next); @@ -3002,7 +3032,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s is_no_return = is_no_return || (op_next.rb >= 4 && op_next.rb < 10); } - if (type_next & spu_itype::_quadrop && +iflags & +spu_iflag::use_rc) + if (+iflags & +spu_iflag::use_rc) { is_no_return = is_no_return || (op_next.ra >= 4 && op_next.rb < 10); } @@ -3244,6 +3274,12 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s spu_log.notice("[0x%x] At 0x%x: ignoring indirect branch (SYNC)", entry_point, pos); } + if (!(af & vf::is_const)) + { + // Possible unknown target + m_targets[pos].emplace_back(SPU_LS_SIZE); + } + if (type == spu_itype::BI || sl || is_no_return) { if (type == spu_itype::BI || g_cfg.core.spu_block_size == spu_block_size_type::safe || is_no_return) @@ -3308,7 +3344,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s is_no_return = is_no_return || (op_next.rb >= 4 && op_next.rb < 10); } - if (type_next & spu_itype::_quadrop && +iflags & +spu_iflag::use_rc) + if (+iflags & +spu_iflag::use_rc) { is_no_return = is_no_return || (op_next.rc >= 4 && op_next.rc < 10); } @@ -3834,17 +3870,26 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s continue; } + bool removed = false; + for (auto it2 = it->second.begin(); it2 != it->second.end();) { if (*it2 < lsa || *it2 >= limit) { it2 = it->second.erase(it2); + removed = true; continue; } it2++; } + if (removed) + { + it->second.emplace_back(SPU_LS_SIZE); + } + + std::sort(it->second.begin(), it->second.end()); it++; } @@ -3895,7 +3940,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s const auto type = g_spu_itype.decode(op.opcode); - u8 reg_save = 255; + u8 reg_save = s_reg_max; if (type == spu_itype::STQD && op.ra == s_reg_sp && !block.reg_mod[op.rt] && !block.reg_use[op.rt]) { @@ -3915,7 +3960,17 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s // Register reg use only if it happens before reg mod if (!block.reg_mod[reg]) { - block.reg_use.set(reg); + if (type & spu_itype::floating) + { + block.reg_maybe_float.set(reg); + } + + if (type == spu_itype::SHUFB && reg == op.rc) + { + block.reg_maybe_shuffle_mask.set(reg); + } + + block.reg_use[reg]++; if (reg_save != reg && block.reg_save_dom[reg]) { @@ -3932,7 +3987,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s for (u8 reg : {s_reg_mfc_lsa, s_reg_mfc_tag, s_reg_mfc_size}) { if (!block.reg_mod[reg]) - block.reg_use.set(reg); + block.reg_use[reg]++; } } @@ -3986,7 +4041,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s if (i == s_reg_lr || (i >= 2 && i < s_reg_80) || i > s_reg_127) { if (!block.reg_mod[i]) - block.reg_use.set(i); + block.reg_use[i]++; if (!is_tail) { @@ -4863,19 +4918,24 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s return map; }; - struct putllc16_statistics_t + struct stats_t { atomic_t all = 0; atomic_t single = 0; - atomic_t nowrite = 0; std::array, 128> breaking_reason{}; }; - struct rchcnt_statistics_t + struct putllc16_statistics_t : stats_t + { + atomic_t nowrite = 0; + }; + + struct rchcnt_statistics_t : stats_t + { + }; + + struct reduced_statistics_t : stats_t { - atomic_t all = 0; - atomic_t single = 0; - std::array, 128> breaking_reason{}; }; // Pattern structures @@ -4987,6 +5047,8 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s // RDCH/RCHCNT Loop analysis tracker rchcnt_loop_t rchcnt_loop{}; + reduced_loop_t reduced_loop{}; + block_reg_state_iterator(u32 _pc, usz _parent_iterator_index = umax, usz _parent_target_index = 0) noexcept : pc(_pc) , parent_iterator_index(_parent_iterator_index) @@ -4999,6 +5061,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s std::map atomic16_all; // RdAtomicStat location -> atomic loop optimization state std::map rchcnt_loop_all; // RDCH/RCHCNT location -> channel read loop optimization state + std::map reduced_loop_all; std::map getllar_starts; // True for failed loops std::map run_on_block; std::map logged_block; @@ -5007,6 +5070,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s atomic16_t dummy16{}; rchcnt_loop_t dummy_loop{}; + reduced_loop_t dummy_rloop{}; bool likely_putllc_loop = false; bool had_putllc_evaluation = false; @@ -5053,6 +5117,194 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s u32 iterator_id_alloc = 0; + auto get_block_targets = [&](u32 pc) -> std::span + { + if (m_block_info[pc / 4] && m_bbs.count(pc)) + { + return ::at32(m_bbs, pc).targets; + } + + return {}; + }; + + auto get_block_preds = [&](u32 pc) -> std::span + { + if (m_block_info[pc / 4] && m_bbs.count(pc)) + { + return ::at32(m_bbs, pc).preds; + } + + return {}; + }; + + const auto initiate_patterns = [&](block_reg_state_iterator& block_state_it, u32 bpc, bool is_multi_block) + { + // Initiate patterns (that are initiated on block start) + const auto& bb_body = ::at32(m_bbs, bpc); + + bool invalid = bb_body.size <= 2; + bool valid = true; + + u32 expected_sup_conds = 0; + u32 first_pred_of_loop = SPU_LS_SIZE; + + for (u32 pred : get_block_preds(bpc)) + { + if (is_multi_block ? pred >= bpc : pred == bpc) + { + first_pred_of_loop = std::min(pred, first_pred_of_loop); + } + } + + valid = first_pred_of_loop != SPU_LS_SIZE; + + const auto& bb_connect = ::at32(m_bbs, valid ? first_pred_of_loop : bpc); + + invalid = invalid || !valid; + valid = false; + + // Check loop connector block (must jump to block-next or to loop-start) + u32 targets_count = 0; + + for (u32 target : get_block_targets(first_pred_of_loop)) + { + valid = true; + targets_count++; + + if (first_pred_of_loop == bpc) + { + continue; + } + + if (target != bpc) + { + if (target != first_pred_of_loop + bb_connect.size * 4) + { + invalid = true; + } + } + } + + if (targets_count > 2) + { + invalid = true; + } + + const bool is_two_block_loop = targets_count == 1; + + invalid = invalid || !valid; + valid = false; + + // Check loop body block (must jump to last-block or another location) + + for (u32 block_pc = bpc; !invalid;) + { + targets_count = 0; + + const u32 cond_next = block_pc + ::at32(m_bbs, block_pc).size * 4; + valid = false; + + bool is_end = false; + + for (u32 target : get_block_targets(block_pc)) + { + targets_count++; + + if (target == cond_next) + { + // Conditional branch + valid = true; + } + + if (target <= block_pc && target > bpc) + { + // Branch backwards + invalid = true; + } + + if (target == bpc) + { + is_end = true; + } + } + + // if (bpc != block_pc) + // { + // for (u32 pred : get_block_preds(block_pc)) + // { + // if (pred < bpc || pred > first_pred_of_loop + ::at32(m_bbs, first_pred_of_loop).size * 4) + // { + // invalid = true; + // break; + // } + // } + // } + + if (targets_count > 2) + { + invalid = true; + break; + } + + if (cond_next == first_pred_of_loop && is_two_block_loop) + { + valid = true; + break; + } + + if (!valid) + { + break; + } + + if (bpc == first_pred_of_loop || is_end) + { + break; + } + + if (targets_count == 2) + { + expected_sup_conds++; + } + + block_pc = cond_next; + } + + invalid = invalid || !valid; + + if (bb_body.size > 2 && !invalid) + { + // Early filtering of false positives + const spu_opcode_t op{std::bit_cast>(::at32(result.data, (bpc - entry_point) / 4 + bb_body.size - 2))}; + const spu_opcode_t op2{std::bit_cast>(::at32(result.data, (bpc - entry_point) / 4))}; + + switch (g_spu_itype.decode(op.opcode)) + { + case spu_itype::RDCH: invalid = op.ra != SPU_RdDec; break; + case spu_itype::RCHCNT: invalid = true; break; + default: break; + } + + switch (g_spu_itype.decode(op2.opcode)) + { + case spu_itype::RDCH: invalid = invalid || op2.ra != SPU_RdDec; break; + case spu_itype::RCHCNT: invalid = true; break; + default: break; + } + } + + if (valid && !invalid && !reduced_loop_all.count(bpc) && expected_sup_conds == 0) + { + const auto reduced_loop = &block_state_it.reduced_loop; + reduced_loop->discard(); + reduced_loop->active = true; + reduced_loop->loop_pc = bpc; + reduced_loop->loop_end = first_pred_of_loop; + reduced_loop->expected_sup_conds = expected_sup_conds; + reduced_loop->is_two_block_loop = is_two_block_loop; + } + }; + for (u32 wf = 0, wi = 0, wa = entry_point, bpc = wa; wf <= 1;) { const bool is_form_block = wf == 0; @@ -5121,6 +5373,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s auto& vregs = is_form_block ? infos[bpc]->local_state : *true_state_walkby; const auto atomic16 = is_pattern_match ? &::at32(reg_state_it, wi).atomic16 : &dummy16; const auto rchcnt_loop = is_pattern_match ? &::at32(reg_state_it, wi).rchcnt_loop : &dummy_loop; + const auto reduced_loop = &::at32(reg_state_it, wi).reduced_loop; const u32 pos = wa; @@ -5244,10 +5497,71 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s } }; + const auto break_reduced_loop_pattern = [&](u32 cause, reduced_loop_t previous) + { + if (previous.active && previous.loop_pc != SPU_LS_SIZE && reduced_loop_all.count(previous.loop_pc) == 0) + { + g_fxo->get().breaking_reason[cause]++; + + if (!spu_log.notice) + { + return; + } + + previous.active = false; + previous.failed = true; + + reduced_loop_all[previous.loop_pc] = previous; + + std::string break_error = fmt::format("Reduced loop pattern breakage [%x cause=%u] (read_pc=0x%x)", pos, cause, previous.loop_pc); + + const auto values = sort_breakig_reasons(g_fxo->get().breaking_reason); + + std::string tracing = "Top Breaking Reasons:"; + + usz i = 0; + usz fail_count = 0; + bool switched_to_minimal = false; + + for (auto it = values.begin(); it != values.end(); i++, it++) + { + fail_count += it->second; + + if (i >= 12) + { + continue; + } + + if (i < 8 && it->second > 1) + { + fmt::append(tracing, " [cause=%u, n=%d]", it->first, it->second); + } + else + { + if (!std::exchange(switched_to_minimal, true)) + { + fmt::append(tracing, "; More:"); + } + + fmt::append(tracing, " %u", it->first); + } + } + + fmt::append(tracing, " of %d failures", fail_count); + spu_log.notice("%s\n%s", break_error, tracing); + + std::string block_dump; + this->dump(result, block_dump, previous.loop_pc, previous.loop_end + 1); + + spu_log.notice("SPU Block Dump:\n%s", block_dump); + } + }; + const auto break_all_patterns = [&](u32 cause) { break_putllc16(cause, atomic16->discard()); break_channel_pattern(cause, rchcnt_loop->discard()); + break_reduced_loop_pattern(cause, reduced_loop->discard()); }; const auto calculate_absolute_ls_difference = [](u32 addr1, u32 addr2) @@ -5309,16 +5623,6 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s u32 stackframe_pc = SPU_LS_SIZE; usz entry_index = umax; - auto get_block_targets = [&](u32 pc) -> std::span - { - if (m_block_info[pc / 4] && m_bbs.count(pc)) - { - return m_bbs.at(pc).targets; - } - - return {}; - }; - u32 target_pc = SPU_LS_SIZE; bool insert_entry = false; bool is_code_backdoor = false; @@ -5508,7 +5812,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s } } - const u32 previous_pc = m_bbs.at(reg_state_it[stackframe_it].pc).size * 4 + reg_state_it[stackframe_it].pc - 4; + const u32 previous_pc = ::at32(m_bbs, reg_state_it[stackframe_it].pc).size * 4 + reg_state_it[stackframe_it].pc - 4; bool may_return = previous_pc + 4 != entry_point + result.data.size() * 4 && (m_ret_info[(previous_pc / 4) + 1] || m_entry_info[previous_pc / 4]); @@ -5537,6 +5841,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s // Backup analyser information const auto atomic16_info = reg_state_it[stackframe_it].atomic16; const auto rchcnt_loop_info = reg_state_it[stackframe_it].rchcnt_loop; + const auto reduced_loop_info = reg_state_it[stackframe_it].reduced_loop; // Clean from the back possible because it does not affect old indices // Technically should always do a full cleanup at the moment @@ -5562,6 +5867,8 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s spu_log.trace("Emplacing: block_id=%d, pc=0x%x, target_it=%d/%d, new_pc=0x%x (has_it=%d)", reg_state_it[stackframe_it].iterator_id, stackframe_pc, entry_index + 1, target_size, target_pc, atomic16_info.active); auto& next = reg_state_it.emplace_back(target_pc, stackframe_it, 0); + initiate_patterns(next, target_pc, true); + if (!is_code_backdoor) { // Restore analyser information (if not an entry) @@ -5569,6 +5876,9 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s if (previous_pc != rchcnt_loop_info.branch_pc || target_pc == rchcnt_loop_info.branch_target) next.rchcnt_loop = rchcnt_loop_info; + + if (previous_pc + 4 == target_pc && reduced_loop_info.loop_pc != reduced_loop_info.loop_end && reduced_loop_info.active && target_pc <= reduced_loop_info.loop_end) + next.reduced_loop = reduced_loop_info; } else { @@ -5604,15 +5914,30 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s if (!infos.empty()) { - reg_state_it.emplace_back(::at32(infos, entry_point)->pc).iterator_id = iterator_id_alloc++;; + reg_state_it.emplace_back(::at32(infos, entry_point)->pc).iterator_id = iterator_id_alloc++; + + initiate_patterns(reg_state_it.back(), ::at32(infos, entry_point)->pc, true); } } } + const auto prev_wi = wi - 1; + if (prev_wi != umax && ::at32(reg_state_it, prev_wi).reduced_loop.active) + { + const auto reduced_loop = &::at32(reg_state_it, prev_wi).reduced_loop; + + for (const auto& [reg_num, reg] : reduced_loop->regs) + { + + } + } + if (wi < reg_state_it.size()) { wa = ::at32(reg_state_it, wi).pc; bpc = wa; + + initiate_patterns(::at32(reg_state_it, wi), bpc, false); } }; @@ -5737,7 +6062,8 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s if (!is_form_block) { // Call for external code - break_all_patterns(25); + break_putllc16(25, atomic16->discard()); + break_channel_pattern(25, rchcnt_loop->discard()); } } @@ -5762,6 +6088,149 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s const auto op = spu_opcode_t{data}; const auto type = g_spu_itype.decode(data); + if (reduced_loop->active && !(type & spu_itype::zregmod)) + { + const u32 op_rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt; + + u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max; + + if (::at32(m_use_ra, pos / 4)) + { + ra = op.ra; + } + + if (::at32(m_use_rb, pos / 4)) + { + rb = op.rb; + } + + if (::at32(m_use_rc, pos / 4)) + { + rc = op.rc; + } + + bool is_move_register_op = false; + + switch (type) + { + case spu_itype::SHLQBYI: + { + is_move_register_op = op.i7 == 0; + break; + } + // Technically only ORI is needed but I am taking into account possible third-party SPU compilers or hand-written assembly + case spu_itype::ORI: + case spu_itype::ORHI: + case spu_itype::ORBI: + case spu_itype::AI: + case spu_itype::AHI: + case spu_itype::XORI: + case spu_itype::XORHI: + case spu_itype::XORBI: + { + is_move_register_op = op.si10 == 0; + break; + } + case spu_itype::ANDI: + case spu_itype::ANDHI: + case spu_itype::ANDBI: + { + is_move_register_op = op.si10 == -1; + break; + } + default: + { + break; + } + } + + u32 reg_pos = SPU_LS_SIZE; + + auto org = reduced_loop->get_reg(op_rt); + + u32 reg_first = s_reg_max; + + for (u32 reg : {ra, rb, rc}) + { + if (reg != s_reg_max && reg != reg_first) + { + const auto arg = reduced_loop->find_reg(reg); + + if (arg && arg->modified >= 1) + { + reg_first = reg; + + if (reg_first != s_reg_max && !is_move_register_op) + { + // Multiple origins + org.add_instruction_modifier(spu_itype::UNK, op.opcode); + break; + } + } + } + } + + if (reg_first == s_reg_max) + { + org = {}; + + if (!is_move_register_op) + { + org.add_instruction_modifier(type, op.opcode); + } + } + else if (reg_first == rb) + { + std::swap(ra, rb); + } + else if (reg_first == rc) + { + std::swap(ra, rc); + } + + for (u32 reg : {ra, rb, rc}) + { + if (reg != s_reg_max) + { + const auto arg = reduced_loop->find_reg(reg); + + if (arg && arg->regs.count() != 0) + { + if (reg_first == reg) + { + org = *arg; + + if (!is_move_register_op) + { + org.add_instruction_modifier(type, op.opcode); + } + + continue; + } + + org.join_with_this(*arg); + } + else + { + org.add_register_origin(reg); + } + } + } + + if (type & spu_itype::memory || type == spu_itype::RDCH || type == spu_itype::RCHCNT) + { + // Register external origin + org.add_register_origin(s_reg_max); + } + + *ensure(reduced_loop->find_reg(op_rt)) = org; + } + + if (reduced_loop->active && ((type & spu_itype::memory) || type == spu_itype::STOP || type == spu_itype::STOPD)) + { + reduced_loop->is_constant_expression = false; + } + // For debugging if (false && likely_putllc_loop && is_pattern_match) { @@ -5848,12 +6317,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s break; } - if (type == spu_itype::SYNC) - { - // Remember - sync = true; - } - + break_reduced_loop_pattern(19, reduced_loop->discard()); break; } @@ -5861,10 +6325,6 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s case spu_itype::BI: case spu_itype::BISL: case spu_itype::BISLED: - case spu_itype::BIZ: - case spu_itype::BINZ: - case spu_itype::BIHZ: - case spu_itype::BIHNZ: { if (op.e || op.d) { @@ -5880,8 +6340,86 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s break; } + case spu_itype::BR: case spu_itype::BRA: { + if (reduced_loop->active) + { + if (!reduced_loop->is_two_block_loop || !reduced_loop->has_cond_state) + { + break_reduced_loop_pattern(20, reduced_loop->discard()); + break; + } + + for (const auto& [reg_num, reg] : reduced_loop->regs) + { + if (reg.is_loop_dictator(reg_num)) + { + if (reg.is_non_predictable_loop_dictator(reg_num)) + { + //break_reduced_loop_pattern(13, reduced_loop->discard()); + reduced_loop->is_constant_expression = false; + } + + reduced_loop->loop_dicts.set(reg_num); + } + } + + std::array reg_use{}; + std::bitset reg_maybe_float{}; + std::bitset reg_mod{}; + + for (auto it = m_bbs.find(reduced_loop->loop_pc); it != m_bbs.end() && it->first <= bpc; it++) + { + for (u32 i = 0; i < s_reg_max; i++) + { + if (!reg_mod[i]) + { + reg_use[i] += it->second.reg_use[i]; + } + } + + reg_maybe_float |= it->second.reg_maybe_float; + reg_mod |= it->second.reg_mod; + + // Note: update when sup_conds are implemented + if (it->first == bpc && it->first != reduced_loop->loop_pc) + { + reduced_loop->loop_may_update |= it->second.reg_mod; + } + } + + for (u32 i = 0; i < s_reg_max; i++) + { + if (!::at32(reduced_loop->loop_dicts, i)) + { + if (reg_use[i] && reg_mod[i]) + { + reduced_loop->is_constant_expression = false; + reduced_loop->loop_writes.set(i); + reduced_loop->loop_may_update.reset(i); + } + else if (reg_use[i]) + { + reduced_loop->loop_args.set(i); + + if (reg_use[i] >= 3 && reg_maybe_float[i]) + { + reduced_loop->gpr_not_nans.set(i); + } + } + } + else + { + // Cleanup + reduced_loop->loop_may_update.reset(i); + } + } + + reduced_loop_all.emplace(reduced_loop->loop_pc, *reduced_loop); + reduced_loop->discard(); + } + break; } @@ -5891,7 +6429,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s const u32 next_pc = spu_branch_target(pos, 1); const u32 target = spu_branch_target(pos, op.i16); - if (rchcnt_loop->active) + while (rchcnt_loop->active) { const reg_state_t& rt = vregs[op.rt]; @@ -5907,16 +6445,710 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s rchcnt_loop->conditioned = true; rchcnt_loop->branch_pc = pos; rchcnt_loop->branch_target = rchcnt_loop->product_test_negate != (type == spu_itype::BRZ) ? target : next_pc; + } + + break; + } + + [[fallthrough]]; + } + case spu_itype::BRHZ: + case spu_itype::BRHNZ: + + case spu_itype::BIZ: + case spu_itype::BINZ: + case spu_itype::BIHZ: + case spu_itype::BIHNZ: + { + if (type == spu_itype::spu_itype::BIZ || type == spu_itype::BINZ || type == spu_itype::BIHZ || type == spu_itype::BIHNZ) + { + if (op.e || op.d) + { + break_all_patterns(27); break; } } - break; - } - case spu_itype::BR: - case spu_itype::BRHZ: - case spu_itype::BRHNZ: - { + const bool is_u16_jump = type == spu_itype::BRHZ || type == spu_itype::BRHNZ || type == spu_itype::BIHZ || type == spu_itype::BIHNZ; + const bool is_jump_zero = (type == spu_itype::BRZ || type == spu_itype::BRHZ || type == spu_itype::BIZ || type == spu_itype::BIHZ) ^ reduced_loop->is_two_block_loop; + + while (reduced_loop->active) + { + if (reduced_loop->expected_sup_conds) + { + break_reduced_loop_pattern(50, reduced_loop->discard()); + break; + } + + const u32 op_rt = op.rt; + + const auto reg = reduced_loop->find_reg(op_rt); + + if (!reg/* || reg->modified == 0*/) // See special case regarding branch with direct comparison with 0 + { + break_reduced_loop_pattern(1, reduced_loop->discard()); + break; + } + + bool should_have_argument_dictator = false; + bool should_have_argument_increment = false; + bool cond_val_incr_before_cond = false; + bool ends_with_comparison = false; + + bool pattern_ok1 = true; + + switch (reg->mod1_type) + { + case spu_itype::A: + { + should_have_argument_increment = true; + [[fallthrough]]; + } + case spu_itype::AI: + case spu_itype::AHI: + { + cond_val_incr_before_cond = true; + pattern_ok1 = true; + break; + } + case spu_itype::CEQ: + case spu_itype::CEQH: + case spu_itype::CEQB: + case spu_itype::CGT: + case spu_itype::CGTH: + case spu_itype::CGTB: + case spu_itype::CLGT: + case spu_itype::CLGTH: + case spu_itype::CLGTB: + { + ends_with_comparison = true; + should_have_argument_dictator = true; + break; + } + case spu_itype::CEQI: + case spu_itype::CEQHI: + case spu_itype::CEQBI: + case spu_itype::CGTI: + case spu_itype::CGTHI: + case spu_itype::CGTBI: + case spu_itype::CLGTI: + case spu_itype::CLGTHI: + case spu_itype::CLGTBI: + { + ends_with_comparison = true; + pattern_ok1 = true; + break; + } + default: + { + if (reg->modified == 0) + { + // Special case: target may be sourced from another register which would be the loop dictator + break; + } + + pattern_ok1 = false; + break; + } + } + + if (!pattern_ok1) + { + break_reduced_loop_pattern(9, reduced_loop->discard()); + break; + } + + if (reg->modified >= 2) + { + switch (reg->mod2_type) + { + case spu_itype::A: + { + should_have_argument_increment = true; + [[fallthrough]]; + } + case spu_itype::AI: + case spu_itype::AHI: + { + if (cond_val_incr_before_cond) + { + // AI twice + break_reduced_loop_pattern(8, reduced_loop->discard()); + pattern_ok1 = false; + break; + } + + cond_val_incr_before_cond = false; + pattern_ok1 = true; + break; + } + case spu_itype::CEQ: + case spu_itype::CEQH: + case spu_itype::CEQB: + case spu_itype::CGT: + case spu_itype::CGTH: + case spu_itype::CGTB: + case spu_itype::CLGT: + case spu_itype::CLGTH: + case spu_itype::CLGTB: + { + if (!cond_val_incr_before_cond) + { + // Double comparison + break_reduced_loop_pattern(19, reduced_loop->discard()); + pattern_ok1 = false; + break; + } + + pattern_ok1 = true; + ends_with_comparison = true; + should_have_argument_dictator = true; + break; + } + case spu_itype::CEQI: + case spu_itype::CEQHI: + case spu_itype::CEQBI: + case spu_itype::CGTI: + case spu_itype::CGTHI: + case spu_itype::CGTBI: + case spu_itype::CLGTI: + case spu_itype::CLGTHI: + case spu_itype::CLGTBI: + { + if (!cond_val_incr_before_cond) + { + // Double comparison + break_reduced_loop_pattern(19, reduced_loop->discard()); + pattern_ok1 = false; + break; + } + + ends_with_comparison = true; + pattern_ok1 = true; + break; + } + default: + { + pattern_ok1 = false; + break; + } + } + } + + if (!pattern_ok1) + { + break_reduced_loop_pattern(10, reduced_loop->discard()); + break; + } + + bool found_loop_dictator = false; + bool found_loop_argument_for_dictator = false; + u32 null_regs_found = 0; + + for (u32 i = 0; i < reg->regs.size() && reduced_loop->active; i++) + { + if (::at32(reg->regs, i)) + { + if (0) if (i == op_rt || reg->modified == 0) + { + // Special case: direct comparison with zero for 32-bits (the only supported form by SPU) + + if (is_jump_zero) + { + // Infinite or single-time "loop" + break_reduced_loop_pattern(3, reduced_loop->discard()); + break; + } + + if (reg->modified >= 2) + { + break_reduced_loop_pattern(22, reduced_loop->discard()); + break; + } + + reduced_loop->cond_val_mask = u32{umax}; + reduced_loop->cond_val_min = 0; + reduced_loop->cond_val_size = u32{umax}; + + auto comp_reg = i == op_rt ? reg : reduced_loop->find_reg(i); + + if (!comp_reg || !comp_reg->is_predictable_loop_dictator(i)) + { + break_reduced_loop_pattern(4, reduced_loop->discard()); + break; + } + + ensure(reg->modified == 1 || i != op_rt); + + reduced_loop->cond_val_incr = static_cast(comp_reg->IMM); + reduced_loop->cond_val_incr_before_cond = reg->modified == 1; + reduced_loop->cond_val_register_idx = i; + reduced_loop->cond_val_compare = CMP_NOT_EQUAL; + reduced_loop->cond_val_is_immediate = true; + + found_loop_dictator = true; + break; + } + + auto reg_org = reduced_loop->find_reg(i); + u32 reg_index = i; + + if (reg_org && !cond_val_incr_before_cond && reg_org->modified == 0 && reg_org->regs.count() - 1u <= 1u && !::at32(reg_org->regs, i)) + { + for (u32 j = 0; j <= s_reg_127; j++) + { + if (::at32(reg_org->regs, j)) + { + if (const auto reg_found = reduced_loop->find_reg(j)) + { + if (reg_found->modified) + { + reg_org = reg_found; + reg_index = j; + break; + } + } + } + } + } + + if (!reg_org || reg_org->is_null(reg_index)) + { + // if (found_loop_dictator && !reduced_loop->cond_val_incr_is_immediate) + // { + // ensure(reduced_loop->cond_val_incr < s_reg_max); + + // } + // if (!should_have_argument_dictator) + // { + // break_reduced_loop_pattern(11, reduced_loop->discard()); + // break; + // } + + // if (found_loop_argument_for_dictator) + // { + // break_reduced_loop_pattern(6, reduced_loop->discard()); + // break; + // } + + // found_loop_argument_for_dictator = true; + // reduced_loop->cond_val_is_immediate = false; + + // if (found_loop_dictator) + // { + // ensure(i == reduced_loop->cond_val_register_argument_idx); + // } + // else + // { + // reduced_loop->cond_val_register_argument_idx = i; + // } + + // if (found_loop_dictator && reg->regs.count() == 2) + // { + // break; + // } + + null_regs_found++; + continue; + } + + if (found_loop_dictator) + { + break_reduced_loop_pattern(13, reduced_loop->discard()); + break; + } + + found_loop_dictator = true; + + if (!reg_org->is_predictable_loop_dictator(i)) + { + break_reduced_loop_pattern(7, reduced_loop->discard()); + break; + } + + if (reg_index != i && ::at32(reg->regs, reg_index)) + { + // Unimplemented + break_reduced_loop_pattern(30, reduced_loop->discard()); + break; + } + + u32 cond_val_incr = static_cast(reg_org->IMM); + + if (reg_org->mod1_type == spu_itype::AI || reg_org->mod1_type == spu_itype::AHI) + { + reduced_loop->cond_val_incr_is_immediate = true; + reduced_loop->cond_val_incr = static_cast(reg_org->IMM); + } + else if (reg_org->mod1_type == spu_itype::A) + { + reduced_loop->cond_val_incr_is_immediate = false; + + const u32 op_ra = spu_opcode_t{reg_org->IMM}.ra; + const u32 op_rb = spu_opcode_t{reg_org->IMM}.rb; + + if (!(op_ra == reg_index || op_rb == reg_index)) + { + break_reduced_loop_pattern(25, reduced_loop->discard()); + break; + } + + const u32 incr_arg_reg = reg_index == op_ra ? op_rb : op_ra; + + if (!reduced_loop->is_reg_null(incr_arg_reg)) + { + break_reduced_loop_pattern(26, reduced_loop->discard()); + break; + } + + reduced_loop->cond_val_incr = incr_arg_reg; + } + else + { + break_reduced_loop_pattern(28, reduced_loop->discard()); + break; + } + + reduced_loop->cond_val_incr_before_cond = cond_val_incr_before_cond; + + u64 cmp_mask = 0; + compare_direction cmp_direction{}; + + if (!ends_with_comparison) + { + if (is_jump_zero) + { + // Infinite or single-time "loop" + break_reduced_loop_pattern(3, reduced_loop->discard()); + break; + } + + cmp_mask = is_u16_jump ? u16{umax} : u32{umax}; + reduced_loop->cond_val_min = 0; + reduced_loop->cond_val_is_immediate = true; + cmp_direction = CMP_NOT_EQUAL; + } + else if (!should_have_argument_dictator) + { + reduced_loop->cond_val_min = reg->IMM; + reduced_loop->cond_val_is_immediate = true; + + const auto cmp_optype = reg->reverse1_type() == spu_itype::XSBH ? reg->reverse2_type() : reg->reverse1_type(); + + switch (cmp_optype) + { + case spu_itype::CEQI: + case spu_itype::CEQHI: + case spu_itype::CEQBI: + { + cmp_direction = CMP_EQUAL; + break; + } + case spu_itype::CGTI: + case spu_itype::CGTHI: + case spu_itype::CGTBI: + { + cmp_direction = CMP_SGREATER; + break; + } + case spu_itype::CLGTI: + case spu_itype::CLGTHI: + case spu_itype::CLGTBI: + { + cmp_direction = CMP_LGREATER; + break; + } + default: + { + break_reduced_loop_pattern(21, reduced_loop->discard()); + } + } + + switch (cmp_optype) + { + case spu_itype::CEQI: + case spu_itype::CGTI: + case spu_itype::CLGTI: + { + cmp_mask = u32{umax}; + break; + } + case spu_itype::CLGTHI: + case spu_itype::CEQHI: + case spu_itype::CGTHI: + { + cmp_mask = u16{umax}; + break; + } + case spu_itype::CEQBI: + case spu_itype::CGTBI: + case spu_itype::CLGTBI: + { + cmp_mask = u8{umax}; + break; + } + default: break_reduced_loop_pattern(21, reduced_loop->discard()); + } + + if (is_jump_zero) + { + cmp_direction = compare_direction{cmp_direction ^ CMP_NEGATE_FLAG}; + } + + if (cmp_direction == CMP_EQUAL2 || cmp_direction == CMP_NOT_EQUAL2) + { + // Fixup (no sense in remembering the turnaround for euqality comparison) + cmp_direction = compare_direction{cmp_direction & ~CMP_TURNAROUND_FLAG}; + } + } + else + { + const u32 op_ra = spu_opcode_t{reg->IMM}.ra; + const u32 op_rb = spu_opcode_t{reg->IMM}.rb; + + if (!(op_ra == reg_index || op_rb == reg_index)) + { + break_reduced_loop_pattern(20, reduced_loop->discard()); + break; + } + + const auto cmp_optype = reg->reverse1_type() == spu_itype::XSBH ? reg->reverse2_type() : reg->reverse1_type(); + + switch (cmp_optype) + { + case spu_itype::CEQ: + case spu_itype::CEQH: + case spu_itype::CEQB: + { + cmp_direction = CMP_EQUAL; + break; + } + case spu_itype::CGT: + case spu_itype::CGTH: + case spu_itype::CGTB: + { + cmp_direction = CMP_SGREATER; + break; + } + case spu_itype::CLGT: + case spu_itype::CLGTH: + case spu_itype::CLGTB: + { + cmp_direction = CMP_LGREATER; + break; + } + default: ensure(false); + } + + switch (cmp_optype) + { + case spu_itype::CEQ: + case spu_itype::CGT: + case spu_itype::CLGT: + { + cmp_mask = u32{umax}; + break; + } + case spu_itype::CLGTH: + case spu_itype::CEQH: + case spu_itype::CGTH: + { + cmp_mask = u16{umax}; + break; + } + case spu_itype::CEQB: + case spu_itype::CGTB: + case spu_itype::CLGTB: + { + cmp_mask = u8{umax}; + break; + } + default: ensure(false); + } + + if (op_ra != i) + { + // Compare is on the oppsoite direction + // This variation exists only via register mode (due to lack of SPU opcodes) + cmp_direction = compare_direction{cmp_direction ^ CMP_TURNAROUND_FLAG}; + } + + if (is_jump_zero) + { + cmp_direction = compare_direction{cmp_direction ^ CMP_NEGATE_FLAG}; + } + + if (cmp_direction == CMP_EQUAL2 || cmp_direction == CMP_NOT_EQUAL2) + { + // Fixup (no sense in remembering the turnaround for euqality comparison) + cmp_direction = compare_direction{cmp_direction & ~CMP_TURNAROUND_FLAG}; + } + + // The loop dictator is the register that is not the argument + const u32 loop_arg_reg = reg_index == op_ra ? op_rb : op_ra; + const u32 loop_dict_reg = reg_index == op_ra ? op_ra : op_rb; + reduced_loop->cond_val_is_immediate = false; + + if (found_loop_argument_for_dictator) + { + ensure(loop_arg_reg == reduced_loop->cond_val_register_argument_idx); + } + else + { + reduced_loop->cond_val_register_argument_idx = loop_arg_reg; + } + + if (!reduced_loop->is_reg_null(loop_arg_reg)) + { + break_reduced_loop_pattern(27, reduced_loop->discard()); + break; + } + + found_loop_argument_for_dictator = true; + } + + if (cmp_direction == CMP_EQUAL) + { + // Infinite or single-time "loop" + break_reduced_loop_pattern(18, reduced_loop->discard()); + break; + } + + if (cmp_mask == u16{umax} && !is_u16_jump) + { + break_reduced_loop_pattern(14, reduced_loop->discard()); + break; + } + + if (cmp_mask == u8{umax}) + { + bool instructions_ok = false; + + if (is_u16_jump) + { + // If ANDI(0xff) is used, although unlikely, it fine as well for 16-bits + instructions_ok = FN(x == spu_itype::XSBH || x == spu_itype::ANDI)(!cond_val_incr_before_cond ? reg->mod2_type : reg->mod3_type); + } + else + { + instructions_ok = FN(x == spu_itype::ANDI)(!cond_val_incr_before_cond ? reg->mod2_type : reg->mod3_type); + } + + if (!instructions_ok) + { + break_reduced_loop_pattern(15, reduced_loop->discard()); + break; + } + } + + reduced_loop->cond_val_compare = cmp_direction; + reduced_loop->cond_val_mask = cmp_mask; + reduced_loop->cond_val_register_idx = reg_index; + + // if (!should_have_argument_dictator && reg->regs.count() == 1) + // { + // break; + // } + + // if (found_loop_argument_for_dictator && reg->regs.count() == 2) + // { + // break; + // } + } + } + + if (!found_loop_dictator) + { + break_reduced_loop_pattern(16, reduced_loop->discard()); + } + + if (should_have_argument_dictator && !found_loop_argument_for_dictator) + { + break_reduced_loop_pattern(17, reduced_loop->discard()); + } + + if (reduced_loop->active) + { + ensure(reduced_loop->cond_val_register_idx != umax); + + if (reduced_loop->is_two_block_loop) + { + reduced_loop->has_cond_state = true; + break; + } + + for (const auto& [reg_num, reg] : reduced_loop->regs) + { + if (reg.is_loop_dictator(reg_num)) + { + if (reg.is_non_predictable_loop_dictator(reg_num)) + { + //break_reduced_loop_pattern(13, reduced_loop->discard()); + reduced_loop->is_constant_expression = false; + } + + reduced_loop->loop_dicts.set(reg_num); + } + } + + std::array reg_use{}; + std::bitset reg_maybe_float{}; + std::bitset reg_mod{}; + + for (auto it = m_bbs.find(reduced_loop->loop_pc); it != m_bbs.end() && it->first <= bpc; it++) + { + for (u32 i = 0; i < s_reg_max; i++) + { + if (!reg_mod[i]) + { + reg_use[i] += it->second.reg_use[i]; + } + } + + reg_maybe_float |= it->second.reg_maybe_float; + reg_mod |= it->second.reg_mod; + + // Note: update when sup_conds are implemented + if (it->first == bpc && it->first != reduced_loop->loop_pc) + { + reduced_loop->loop_may_update |= it->second.reg_mod; + } + } + + for (u32 i = 0; i < s_reg_max; i++) + { + if (!::at32(reduced_loop->loop_dicts, i)) + { + if (reg_use[i] && reg_mod[i]) + { + reduced_loop->is_constant_expression = false; + reduced_loop->loop_writes.set(i); + reduced_loop->loop_may_update.reset(i); + } + else if (reg_use[i]) + { + reduced_loop->loop_args.set(i); + + if (reg_use[i] >= 3 && reg_maybe_float[i]) + { + reduced_loop->gpr_not_nans.set(i); + } + } + } + else + { + // Cleanup + reduced_loop->loop_may_update.reset(i); + } + } + + reduced_loop_all.emplace(reduced_loop->loop_pc, *reduced_loop); + reduced_loop->discard(); + } + + break; + } + break; } @@ -5929,17 +7161,49 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s case spu_itype::HLGTI: case spu_itype::LNOP: case spu_itype::NOP: - case spu_itype::MTSPR: case spu_itype::FSCRWR: { // Do nothing break; } - + + case spu_itype::MTSPR: + { + break_all_patterns(99); + break; + } + case spu_itype::WRCH: { break_channel_pattern(56, rchcnt_loop->discard()); + if (reduced_loop->active) + { + switch (op.ra) + { + case MFC_EAL: + case MFC_LSA: + case MFC_TagID: + case MFC_Size: + case MFC_EAH: + case SPU_WrDec: + case SPU_WrSRR0: + case SPU_WrEventAck: + case SPU_Set_Bkmk_Tag: + case SPU_PM_Start_Ev: + case SPU_PM_Stop_Ev: + case MFC_WrTagMask: + { + break; + } + default: + { + break_reduced_loop_pattern(18, reduced_loop->discard()); + break; + } + } + } + switch (op.ra) { case MFC_EAL: @@ -6202,6 +7466,14 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s const bool is_read = type == spu_itype::RDCH; bool invalidate = true; + if (!is_read || op.ra != SPU_RdDec) + { + if (reduced_loop->active) + { + break_reduced_loop_pattern(17, reduced_loop->discard()); + } + } + const auto it = rchcnt_loop_all.find(pos); if (it != rchcnt_loop_all.end()) @@ -7111,17 +8383,17 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max; - if (m_use_ra.test(pos / 4)) + if (::at32(m_use_ra, pos / 4)) { ra = op.ra; } - if (m_use_rb.test(pos / 4)) + if (::at32(m_use_rb, pos / 4)) { rb = op.rb; } - if (type & spu_itype::_quadrop && m_use_rc.test(pos / 4)) + if (::at32(m_use_rc, pos / 4)) { rc = op.rc; } @@ -7169,6 +8441,11 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s { for (u32 next_target : ::at32(m_targets, pos)) { + if (next_target == SPU_LS_SIZE) + { + continue; + } + add_block(next_target); } @@ -7353,6 +8630,76 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s } } + for (const auto& [loop_pc, pattern] : reduced_loop_all) + { + if (!pattern.active || pattern.loop_pc == SPU_LS_SIZE) + { + continue; + } + + if (inst_attr attr = m_inst_attrs[(loop_pc - entry_point) / 4]; attr == inst_attr::none) + { + const u64 hash = loop_pc / 4 + read_from_ptr>(func_hash.data()); + + add_pattern(inst_attr::reduced_loop, loop_pc - result.entry_point, 0, std::make_shared(pattern)); + + std::string regs = "{"; + + for (const auto& [reg_num, reg] : pattern.regs) + { + if (reg.is_loop_dictator(reg_num)) + { + if (regs.size() != 1) + { + regs += ","; + } + + fmt::append(regs, " r%u", reg_num); + } + } + + for (u32 i = 0; i < s_reg_max; i++) + { + if (::at32(pattern.loop_writes, i)) + { + if (regs.size() != 1) + { + regs += ","; + } + + fmt::append(regs, " r%u-w", i); + } + + if (::at32(pattern.loop_args, i)) + { + if (regs.size() != 1) + { + regs += ","; + } + + fmt::append(regs, " r%u-r", i); + } + + if (::at32(pattern.loop_may_update, i)) + { + if (regs.size() != 1) + { + regs += ","; + } + + fmt::append(regs, " r%u-m", i); + } + } + + regs += " }"; + + spu_log.success("Reduced Loop Pattern Detected! (REGS: %s, DICT: r%d, ARG: %s, Incr: %s (%s), CMP/Size: %s/%u, loop_pc=0x%x, 0x%x-%s)", regs, pattern.cond_val_register_idx + , pattern.cond_val_is_immediate ? fmt::format("0x%x", pattern.cond_val_min) : fmt::format("r%d", pattern.cond_val_register_argument_idx) + , pattern.cond_val_incr_is_immediate ? fmt::format("%d", static_cast(pattern.cond_val_incr)) : fmt::format("r%d", pattern.cond_val_incr), pattern.cond_val_incr_before_cond ? "BEFORE" : "AFTER" + , pattern.cond_val_compare, std::popcount(pattern.cond_val_mask), loop_pc, entry_point, func_hash); + } + } + if (likely_putllc_loop && !had_putllc_evaluation) { spu_log.notice("Likely missed PUTLLC16 patterns. (entry=0x%x)", entry_point); @@ -7363,7 +8710,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s // Blocks starting from 0x0 or invalid instruction won't be compiled, may need special interpreter fallback } - if (!m_patterns.empty()) + if (!m_patterns.empty() && g_cfg.core.spu_debug) { std::string out_dump; dump(result, out_dump); @@ -7386,7 +8733,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s return result; } -void spu_recompiler_base::dump(const spu_program& result, std::string& out) +void spu_recompiler_base::dump(const spu_program& result, std::string& out, u32 block_min, u32 block_max) { SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast(result.data.data()), result.lower_bound); @@ -7409,10 +8756,18 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out) hash = "N/A"; } - fmt::append(out, "========== SPU BLOCK 0x%05x (size %u, %s) ==========\n\n", result.entry_point, result.data.size(), hash); + if (block_min == 0) + { + fmt::append(out, "========== SPU BLOCK 0x%05x (size %u, %s) ==========\n\n", result.entry_point, result.data.size(), hash); + } for (auto& bb : m_bbs) { + if (bb.first < block_min || bb.first >= block_max) + { + continue; + } + if (m_block_info[bb.first / 4]) { fmt::append(out, "A: [0x%05x] %s [%s]\n", bb.first, m_entry_info[bb.first / 4] ? (m_ret_info[bb.first / 4] ? "Chunk" : "Entry") : "Block", spu_block_hash{(hash_start & -65536) + bb.first / 4}); @@ -8435,9 +9790,9 @@ std::array& block_reg_info::evaluate_start_state(const s return walkby_state; } -void spu_recompiler_base::add_pattern(inst_attr attr, u32 start, u64 info) +void spu_recompiler_base::add_pattern(inst_attr attr, u32 start, u64 info, std::shared_ptr info_ptr) { - m_patterns[start] = pattern_info{info}; + m_patterns[start] = pattern_info{info, info_ptr}; m_inst_attrs[start / 4] = attr; } diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index d42acd3560..6837baaa97 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -60,6 +60,7 @@ const extern spu_decoder g_spu_iflag; #pragma GCC diagnostic pop #endif +#pragma optimize("", off) #ifdef ARCH_ARM64 #include "Emu/CPU/Backends/AArch64/AArch64JIT.h" #endif @@ -132,6 +133,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator llvm::MDNode* m_md_unlikely; llvm::MDNode* m_md_likely; + llvm::MDNode* m_md_spu_memory_domain; + llvm::MDNode* m_md_spu_context_domain; struct block_info { @@ -139,7 +142,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator spu_recompiler_base::block_info* bb{}; // Current block's entry block - llvm::BasicBlock* block; + llvm::BasicBlock* block{}; // Final block (for PHI nodes, set after completion) llvm::BasicBlock* block_end{}; @@ -150,11 +153,15 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Current register values std::array reg{}; + // Opimization: restoring register state for registers that would be rewritten in other blocks + std::array reg_save_and_restore{}; + // PHI nodes created for this block (if any) std::array phi{}; // Store instructions std::array store{}; + bool block_wide_reg_store_elimination = false; // Store reordering/elimination protection std::array store_context_last_id = fill_array(0); // Protects against illegal forward ordering @@ -189,10 +196,13 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator }; // Current block - block_info* m_block; + block_info* m_block = nullptr; // Current function or chunk - function_info* m_finfo; + function_info* m_finfo = nullptr; + + // Reduced Loop Pattern information (if available) + reduced_loop_t* m_reduced_loop_info = nullptr; // All blocks in the current function chunk std::unordered_map> m_blocks; @@ -364,7 +374,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator { if (i != s_reg_lr && i != s_reg_sp && (i < s_reg_80 || i > s_reg_127)) { - m_block->reg[i] = m_ir->CreateLoad(get_reg_type(i), init_reg_fixed(i)); + m_block->reg[i] = get_reg_fixed(i, get_reg_type(i)); } } @@ -549,6 +559,40 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator return _ptr(m_thread, ::offset32(offset_args...)); } + template + T* spu_mem_attr(T* inst) + { + if (auto load_inst = llvm::dyn_cast(inst)) + { + load_inst->setMetadata(llvm::LLVMContext::MD_noalias, m_md_spu_context_domain); + load_inst->setMetadata(llvm::LLVMContext::MD_alias_scope, m_md_spu_memory_domain); + } + else if (auto store_inst = llvm::dyn_cast(inst)) + { + store_inst->setMetadata(llvm::LLVMContext::MD_noalias, m_md_spu_context_domain); + store_inst->setMetadata(llvm::LLVMContext::MD_alias_scope, m_md_spu_memory_domain); + } + + return inst; + } + + template + T* spu_context_attr(T* inst) + { + if (auto load_inst = llvm::dyn_cast(inst)) + { + load_inst->setMetadata(llvm::LLVMContext::MD_alias_scope, m_md_spu_context_domain); + load_inst->setMetadata(llvm::LLVMContext::MD_noalias, m_md_spu_memory_domain); + } + else if (auto store_inst = llvm::dyn_cast(inst)) + { + store_inst->setMetadata(llvm::LLVMContext::MD_alias_scope, m_md_spu_context_domain); + store_inst->setMetadata(llvm::LLVMContext::MD_noalias, m_md_spu_memory_domain); + } + + return inst; + } + // Return default register type llvm::Type* get_reg_type(u32 index) { @@ -709,8 +753,14 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator if (!reg) { + if (m_block && m_block->block_wide_reg_store_elimination) + { + fmt::throw_exception("Unexpected load: [%s] at 0x%x (gpr=r%d)", m_hash, m_pos, index); + } + // Load register value if necessary reg = m_finfo && m_finfo->load[index] ? m_finfo->load[index] : m_ir->CreateLoad(get_reg_type(index), init_reg_fixed(index)); + spu_context_attr(reg); } if (reg->getType() == get_type()) @@ -920,6 +970,14 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator if (m_block) { + if (m_block->block_wide_reg_store_elimination) + { + // Don't save registers for the current block iteration + // Affected optimizations: + // 1. Single-block reduced loop + return; + } + // Keep the store's location in history of gpr preservaions m_block->store_context_last_id[index] = m_block->store_context_ctr[index]; m_block->store_context_first_id[index] = std::min(m_block->store_context_first_id[index], m_block->store_context_ctr[index]); @@ -935,7 +993,9 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator } // Write register to the context - _store = m_ir->CreateStore(is_xfloat ? double_to_xfloat(saved_value) : m_ir->CreateBitCast(value, get_reg_type(index)), addr); + _store = m_ir->CreateStore(is_xfloat ? double_to_xfloat(saved_value) : bitcast(value, get_reg_type(index)), addr); + + spu_context_attr(_store); } template @@ -1046,7 +1106,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Update PC for current or explicitly specified instruction address void update_pc(u32 target = -1) { - m_ir->CreateStore(m_ir->CreateAnd(get_pc(target + 1 ? target : m_pos), 0x3fffc), spu_ptr(&spu_thread::pc))->setVolatile(true); + spu_context_attr(m_ir->CreateStore(m_ir->CreateAnd(get_pc(target + 1 ? target : m_pos), 0x3fffc), spu_ptr(&spu_thread::pc)))->setVolatile(true); } // Call cpu_thread::check_state if necessary and return or continue (full check) @@ -1055,7 +1115,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto pstate = spu_ptr(&spu_thread::state); const auto _body = llvm::BasicBlock::Create(m_context, "", m_function); const auto check = llvm::BasicBlock::Create(m_context, "", m_function); - m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(get_type(), pstate, true), m_ir->getInt32(0)), _body, check, m_md_likely); + m_ir->CreateCondBr(m_ir->CreateICmpEQ(spu_context_attr(m_ir->CreateLoad(get_type(), pstate, true)), m_ir->getInt32(0)), _body, check, m_md_likely); m_ir->SetInsertPoint(check); update_pc(addr); @@ -1066,14 +1126,14 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator if (may_be_unsafe_for_savestate) { - m_ir->CreateStore(m_ir->getInt8(1), spu_ptr(&spu_thread::unsavable))->setVolatile(true); + spu_context_attr(m_ir->CreateStore(m_ir->getInt8(1), spu_ptr(&spu_thread::unsavable)))->setVolatile(true); } m_ir->CreateCall(m_test_state, {m_thread}); if (may_be_unsafe_for_savestate) { - m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::unsavable))->setVolatile(true); + spu_context_attr(m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::unsavable)))->setVolatile(true); } m_ir->CreateBr(_body); @@ -1509,6 +1569,16 @@ public: m_md_likely = llvm::MDTuple::get(m_context, {md_name, md_high, md_low}); m_md_unlikely = llvm::MDTuple::get(m_context, {md_name, md_low, md_high}); + const auto domain = llvm::MDNode::getDistinct(m_context, {llvm::MDString::get(m_context, "SPU_mem")}); + const auto scope = llvm::MDNode::get(m_context, {llvm::MDString::get(m_context, "SPU_mem_scope"), domain}); + + m_md_spu_memory_domain = llvm::MDNode::get(m_context, scope); + + const auto domain2 = llvm::MDNode::getDistinct(m_context, {llvm::MDString::get(m_context, "SPU_ctx")}); + const auto scope2 = llvm::MDNode::get(m_context, {llvm::MDString::get(m_context, "SPU_ctx_scope"), domain2}); + + m_md_spu_context_domain = llvm::MDNode::get(m_context, scope2); + // Initialize transform passes clear_transforms(); #ifdef ARCH_ARM64 @@ -1678,7 +1748,7 @@ public: // Emit state check const auto pstate = spu_ptr(&spu_thread::state); - m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), pstate), m_ir->getInt32(0)), label_stop, label_test, m_md_unlikely); + m_ir->CreateCondBr(m_ir->CreateICmpNE(spu_context_attr(m_ir->CreateLoad(get_type(), pstate)), m_ir->getInt32(0)), label_stop, label_test, m_md_unlikely); // Emit code check u32 check_iterations = 0; @@ -2059,6 +2129,43 @@ public: bool need_check = false; m_block->bb = &bb; + // [1gJ45f2-0x00a40]: 16.4982% (113258) + // [ZsQTud1-0x0924c]: 6.1202% (42014) + // [ZsQTud1-0x08e54]: 5.6610% (38862) + // [0000000-0x3fffc]: 4.3764% (30043) + // [Zh4tpJM-0x00bcc]: 3.7908% (26023) + // [CFt8hXu-0x063b8]: 3.6177% (24835) + // [8YJCUjv-0x0ad18]: 3.2417% (22254) + // [Try3XHn-0x0f018]: 2.3721% (16284) + // [s6ti9iu-0x07678]: 1.8464% (12675) + // [oyxkAPv-0x0c22c]: 1.7776% (12203) + // [Q0jLqH4-0x00324]: 1.6015% (10994) + static const std::array, 4> to_nop + { + { } + }; + + bool found_block = false; + + for (auto& [hash, pos] : to_nop) + { + if (m_hash.find(hash) <= 2 && baddr == pos) + { + found_block = true; + break; + } + } + + if (found_block) + { + for (u32 i = 0; i < 100; i++) + { + auto value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::last_getllar_lsa)); + auto mod_val = m_ir->CreateFDiv(value, llvm::ConstantFP::get(value->getType(), 1.1 + i)); + m_ir->CreateStore(value, spu_ptr(&spu_thread::last_getllar_lsa)); + } + } + if (!bb.preds.empty()) { // Initialize registers and build PHI nodes if necessary @@ -2174,6 +2281,528 @@ public: check_state(baddr); } + const bool is_reduced_loop = m_inst_attrs[(baddr - start) / 4] == inst_attr::reduced_loop; + m_reduced_loop_info = is_reduced_loop ? std::static_pointer_cast(ensure(m_patterns.at(baddr - start).info_ptr)).get() : nullptr; + + BasicBlock* block_optimization_phi_parent = nullptr; + const auto block_optimization_inner = is_reduced_loop ? BasicBlock::Create(m_context, fmt::format("b-loop-it-0x%x", m_pos), m_function) : nullptr; + const auto block_optimization_exit_early = is_reduced_loop ? BasicBlock::Create(m_context, fmt::format("b-loop-exit-0x%x", m_pos), m_function) : nullptr; + const auto block_optimization_next = is_reduced_loop ? BasicBlock::Create(m_context, fmt::format("b2-0x%x", m_pos), m_function) : nullptr; + + std::array reduced_loop_phi_nodes{}; + std::array reduced_loop_init_regs{}; + + // Reserve additional iteration for rare case where GPR may not be rewritten after the iteration + // So that it would have to be rewritten by future code + // This avoids using additional PHI connectors + const u32 reserve_iterations = m_reduced_loop_info && m_reduced_loop_info->loop_may_update.count() != 0 ? 3 : 2; + + for (u32 i = 0; i < s_reg_max; i++) + { + if (m_reduced_loop_info && m_reduced_loop_info->loop_may_update.test(i)) + { + m_block->reg_save_and_restore[i] = m_block->reg[i]; + } + } + + auto make_reduced_loop_condition = [&](llvm::BasicBlock* optimization_block, bool is_second_time) + { + llvm::ICmpInst::Predicate compare{}; + + switch (m_reduced_loop_info->cond_val_compare) + { + case CMP_SLESS: compare = ICmpInst::ICMP_SLT; break; + case CMP_SGREATER: compare = ICmpInst::ICMP_SGT; break; + case CMP_EQUAL: compare = ICmpInst::ICMP_EQ; break; + case CMP_LLESS: compare = ICmpInst::ICMP_ULT; break; + case CMP_LGREATER: compare = ICmpInst::ICMP_UGT; break; + case CMP_SGREATER_EQUAL: compare = ICmpInst::ICMP_SGE; break; + case CMP_SLOWER_EQUAL: compare = ICmpInst::ICMP_SLE; break; + case CMP_NOT_EQUAL: compare = ICmpInst::ICMP_NE; break; + case CMP_LGREATER_EQUAL: compare = ICmpInst::ICMP_UGE; break; + case CMP_LLOWER_EQUAL: compare = ICmpInst::ICMP_ULE; break; + { + break; + } + case CMP_UNKNOWN: + case CMP_NOT_EQUAL2: + case CMP_EQUAL2: + default: + { + ensure(false); + break; + } + } + + llvm::Value* loop_dictator_before_adjustment{}; + llvm::Value* loop_dictator_after_adjustment{}; + + spu_opcode_t reg_target{}; + reg_target.rt = static_cast(m_reduced_loop_info->cond_val_register_idx); + + if (reg_target.rt != m_reduced_loop_info->cond_val_register_idx) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Illegal condition register index: 0x%llx", m_reduced_loop_info->cond_val_register_idx); + } + + if (!m_block->reg[reg_target.rt]) + { + m_block->reg[reg_target.rt] = reduced_loop_init_regs[reg_target.rt]; + } + + switch (m_reduced_loop_info->cond_val_mask) + { + case u8{umax}: + { + loop_dictator_before_adjustment = get_scalar(get_vr(reg_target.rt)).eval(m_ir); + break; + } + case u16{umax}: + { + loop_dictator_before_adjustment = get_scalar(get_vr(reg_target.rt)).eval(m_ir); + break; + } + case u32{umax}: + { + loop_dictator_before_adjustment = get_scalar(get_vr(reg_target.rt)).eval(m_ir); + break; + } + case u64{umax}: + { + ensure(false); // TODO + loop_dictator_before_adjustment = get_scalar(get_vr(reg_target.rt)).eval(m_ir); + break; + } + default: + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Illegal condition bit mask: 0x%llx", m_reduced_loop_info->cond_val_mask); + } + } + + const u32 type_bits = std::popcount(m_reduced_loop_info->cond_val_mask); + + llvm::Value* cond_val_incr = nullptr; + + if (m_reduced_loop_info->cond_val_incr_is_immediate) + { + cond_val_incr = m_ir->getIntN(type_bits, m_reduced_loop_info->cond_val_incr & m_reduced_loop_info->cond_val_mask); + } + else + { + spu_opcode_t reg_incr{}; + reg_incr.rt = static_cast(m_reduced_loop_info->cond_val_incr); + + if (reg_incr.rt != m_reduced_loop_info->cond_val_incr) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Illegal increment arguemnt register index: 0x%llx", m_reduced_loop_info->cond_val_incr); + } + switch (m_reduced_loop_info->cond_val_mask) + { + case u8{umax}: + { + cond_val_incr = get_scalar(get_vr(reg_incr.rt)).eval(m_ir); + break; + } + case u16{umax}: + { + cond_val_incr = get_scalar(get_vr(reg_incr.rt)).eval(m_ir); + break; + } + case u32{umax}: + { + cond_val_incr = get_scalar(get_vr(reg_incr.rt)).eval(m_ir); + break; + } + case u64{umax}: + { + ensure(false); // TODO + cond_val_incr = get_scalar(get_vr(reg_incr.rt)).eval(m_ir); + break; + } + } + } + + if (m_reduced_loop_info->cond_val_incr_before_cond && !m_reduced_loop_info->cond_val_incr_before_cond_taken_in_account) + { + loop_dictator_after_adjustment = m_ir->CreateAdd(loop_dictator_before_adjustment, cond_val_incr); + } + else + { + loop_dictator_after_adjustment = loop_dictator_before_adjustment; + } + + llvm::Value* loop_argument = nullptr; + + if (m_reduced_loop_info->cond_val_is_immediate) + { + loop_argument = m_ir->CreateTrunc(m_ir->getInt64(m_reduced_loop_info->cond_val_min & m_reduced_loop_info->cond_val_mask), loop_dictator_before_adjustment->getType()); + } + else + { + spu_opcode_t reg_target2{}; + reg_target2.rt = static_cast(m_reduced_loop_info->cond_val_register_argument_idx); + + if (reg_target2.rt != m_reduced_loop_info->cond_val_register_argument_idx) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Illegal condition arguemnt register index: 0x%llx", m_reduced_loop_info->cond_val_register_argument_idx); + } + + switch (m_reduced_loop_info->cond_val_mask) + { + case u8{umax}: + { + loop_argument = get_scalar(get_vr(reg_target2.rt)).eval(m_ir); + break; + } + case u16{umax}: + { + loop_argument = get_scalar(get_vr(reg_target2.rt)).eval(m_ir); + break; + } + case u32{umax}: + { + loop_argument = get_scalar(get_vr(reg_target2.rt)).eval(m_ir); + break; + } + case u64{umax}: + { + ensure(false); // TODO + loop_argument = get_scalar(get_vr(reg_target2.rt)).eval(m_ir); + break; + } + } + } + + llvm::Value* condition = nullptr; + + if (reserve_iterations == 1) + { + condition = m_ir->CreateICmp(compare, loop_dictator_after_adjustment, loop_argument); + } + // else if ((m_reduced_loop_info->cond_val_compare == CMP_LGREATER || (m_reduced_loop_info->cond_val_compare == CMP_LGREATER_EQUAL && m_reduced_loop_info->cond_val_is_immediate && m_reduced_loop_info->cond_val_incr)) && cond_val_incr->getSExtValue() < 0) + // { + // const auto cond_val_incr_multiplied = m_ir->CreateMul(cond_val_incr, reserve_iterations - 1); + // condition = m_ir->CreateICmp(compare, select(m_ir->CreateICmpUGE(cond_val_incr_multiplied, loop_dictator_after_adjustment), m_ir->CreateAdd(loop_dictator_after_adjustment, cond_val_incr_multiplied), m_ir->getIntN(type_bits, 0)), loop_argument); + // } + else + { + //debugtrap(); + + llvm::Value* prev_it = loop_dictator_after_adjustment; + + for (u32 i = 0; i < reserve_iterations; i++) + { + if (i) + { + prev_it = m_ir->CreateAdd(prev_it, cond_val_incr); + } + + const auto also_cond = m_ir->CreateICmp(compare, prev_it, loop_argument); + condition = condition ? m_ir->CreateAnd(condition, also_cond) : also_cond; + } + } + + if (!is_second_time) + { + for (u32 i = 0, count = 0, prev_i = umax;; i++) + { + const bool is_last = !(count <= 20 && i < s_reg_max); + + if (is_last || m_reduced_loop_info->is_gpr_not_NaN_hint(i)) + { + count++; + + if (prev_i == umax) + { + if (!is_last) + { + prev_i = i; + continue; + } + + break; + } + + auto access_gpr = [&](u32 index) + { + spu_opcode_t op_arg{}; + op_arg.ra = index; + return get_vr(op_arg.ra); + }; + + // OR LSB to convert infinity to NaN + llvm::Value* arg1 = bitcast(access_gpr(prev_i) | splat(1)).eval(m_ir); + llvm::Value* arg2 = is_last ? arg1 : bitcast(access_gpr(i) | splat(1)).eval(m_ir); + + llvm::Value* acc = m_ir->CreateSExt(m_ir->CreateFCmpUNO(arg1, arg2), get_type()); + + // Pattern for PTEST + acc = m_ir->CreateBitCast(acc, get_type()); + + llvm::Value* elem = m_ir->CreateExtractElement(acc, u64{0}); + + for (u64 i = 1; i < 2; i++) + { + elem = m_ir->CreateOr(elem, m_ir->CreateExtractElement(acc, i)); + } + + // Compare result with zero + const auto cond_nans = m_ir->CreateICmpEQ(elem, m_ir->getInt64(0)); + condition = m_ir->CreateAnd(cond_nans, condition); + prev_i = umax; + } + + if (is_last) + { + break; + } + } + + // TODO: Optimze so constant evalatuated cases will not be checked + const bool is_cond_need_runtime_verify = compare == ICmpInst::ICMP_NE && (!m_reduced_loop_info->cond_val_is_immediate || m_reduced_loop_info->cond_val_incr % 2 == 0); + + if (is_cond_need_runtime_verify) + { + // Verify that it is actually possible to finish the loop and it is not an infinite loop + + // First: create a mask of the bits that definitely do not change between iterations (0 results in umax which is accurate here) + const auto no_change_bits = m_ir->CreateAnd(m_ir->CreateNot(cond_val_incr), m_ir->CreateSub(cond_val_incr, m_ir->getIntN(type_bits, 1))); + + // Compare that when the mask applied to both the result and the original value is the same + const auto cond_verify = m_ir->CreateICmpEQ(m_ir->CreateAnd(loop_dictator_after_adjustment, no_change_bits), m_ir->CreateAnd(loop_argument, no_change_bits)); + + // Amend condition + condition = m_ir->CreateAnd(cond_verify, condition); + } + } + + m_ir->CreateCondBr(condition, optimization_block, block_optimization_next); + }; + + if (is_reduced_loop) + { + for (u32 i = 0; i < s_reg_max; i++) + { + llvm::Type* type = g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate && bb.reg_maybe_xf[i] ? get_type() : get_reg_type(i); + + if (i < m_reduced_loop_info->loop_dicts.size() && (m_reduced_loop_info->loop_dicts.test(i) || m_reduced_loop_info->loop_writes.test(i))) + { + // Connect registers which are used and then modified by the block + auto value = m_block->reg[i]; + + if (!value || value->getType() != type) + { + value = get_reg_fixed(i, type); + } + + reduced_loop_init_regs[i] = value; + } + else if (i < m_reduced_loop_info->loop_dicts.size() && m_reduced_loop_info->loop_args.test(i)) + { + // Load registers used as arguments of the loop + if (!m_block->reg[i]) + { + m_block->reg[i] = get_reg_fixed(i, type); + } + } + } + + const auto prev_insert_block = m_ir->GetInsertBlock(); + + block_optimization_phi_parent = prev_insert_block; + + make_reduced_loop_condition(block_optimization_inner, false); + m_ir->SetInsertPoint(block_optimization_inner); + + for (u32 i = 0; i < s_reg_max; i++) + { + if (auto init_val = reduced_loop_init_regs[i]) + { + llvm::Type* type = g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate && bb.reg_maybe_xf[i] ? get_type() : get_reg_type(i); + + const auto _phi = m_ir->CreatePHI(init_val->getType(), 2, fmt::format("reduced_0x%05x_r%u", baddr, i)); + _phi->addIncoming(init_val, prev_insert_block); + + reduced_loop_phi_nodes[i] = _phi; + m_block->reg[i] = _phi; + } + } + + m_block->block_wide_reg_store_elimination = true; + } + + // Instructions emitting optimizations: Loop iteration is not the last + m_pos = baddr; + + // Masked opcodde -> register modification times + std::map>> masked_times; + std::array reg_states{}; + u32 s_reg_state{1}; + + for (u32 iteration_emit = 0; is_reduced_loop; m_pos += 4) + { + if (m_pos != baddr && m_block_info[m_pos / 4] && m_reduced_loop_info->loop_end < m_pos) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Exit(1) too early at 0x%x", m_pos); + } + + if (!(m_pos >= start && m_pos < end)) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Exit(2) too early at 0x%x", m_pos); + } + + if (m_ir->GetInsertBlock()->getTerminator()) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: Exit(3) too early at 0x%x", m_pos); + } + + const u32 op = std::bit_cast>(func.data[(m_pos - start) / 4]); + const auto itype = g_spu_itype.decode(op); + + if (itype & spu_itype::branch) + { + bool branches_back = false; + + for (u32 dest : op_branch_targets(m_pos, spu_opcode_t{op})) + { + branches_back = branches_back || dest == baddr; + } + + if (!branches_back) + { + continue; + } + + iteration_emit++; + + if (iteration_emit < 2) + { + // Reset mpos (with fixup) + m_pos = baddr - 4; + continue; + } + + // Optimization block body + const auto block_inner = m_ir->GetInsertBlock(); + + std::array block_reg_results{}; + + for (u32 i = 0; i < s_reg_max; i++) + { + if (auto phi = reduced_loop_phi_nodes[i]) + { + const auto type = phi->getType() == get_type() ? get_type() : get_reg_type(i); + block_reg_results[i] = ensure(get_reg_fixed(i, type)); + phi->addIncoming(block_reg_results[i], block_inner); + } + } + + ensure(!!m_block->reg[m_reduced_loop_info->cond_val_register_idx]); + make_reduced_loop_condition(block_optimization_inner, true); + m_ir->SetInsertPoint(block_optimization_next); + m_block->block_wide_reg_store_elimination = false; + + for (u32 i = 0; i < s_reg_max; i++) + { + if (const auto loop_value = block_reg_results[i]) + { + const auto phi = m_ir->CreatePHI(loop_value->getType(), 2, fmt::format("redres_0x%05x_r%u", baddr, i)); + + phi->addIncoming(loop_value, block_inner); + phi->addIncoming(reduced_loop_init_regs[i], block_optimization_phi_parent); + m_block->reg[i] = phi; + } + } + + + break; + } + + if (!op) + { + fmt::throw_exception("LLVM: Reduced Loop Pattern: [%s] Unexpected fallthrough to 0x%x (chunk=0x%x, entry=0x%x)", m_hash, m_pos, m_entry, m_function_queue[0]); + } + + const auto [reg_rt, reg_access, masked_op] = op_register_targets(m_pos, spu_opcode_t{op}); + + bool erased = false; + + const auto inst_times = std::array{reg_states[reg_access[0]], reg_states[reg_access[1]], reg_states[reg_access[2]]}; + + // Try to reuse the reult of the previous iteration (if argumnent registers have not been modified) + if (reg_rt < 128 && masked_times.count(masked_op) && masked_times[masked_op].first && m_inst_attrs[(m_pos - start) / 4] == inst_attr::none) + { + auto times = masked_times[masked_op].second; + + bool is_ok = true; + for (u32 regi = 0; regi < 3; regi++) + { + if (reg_access[regi] < 128 && times[regi] != inst_times[regi]) + { + is_ok = false; + } + } + + if (is_ok) + { + m_block->reg[reg_rt] = masked_times[masked_op].first; + erased = true; + } + } + + if (reg_rt < 128) + { + reg_states[reg_rt] = s_reg_state++; + } + + if (erased) + { + continue; + } + + m_next_op = 0; + + masked_times[masked_op] = {}; + + switch (m_inst_attrs[(m_pos - start) / 4]) + { + case inst_attr::putllc0: + { + putllc0_pattern(func, m_patterns.at(m_pos - start).info); + continue; + } + case inst_attr::putllc16: + { + putllc16_pattern(func, m_patterns.at(m_pos - start).info); + continue; + } + case inst_attr::omit: + { + // TODO + continue; + } + default: break; + } + + // Execute recompiler function (TODO) + (this->*decode(op))({op}); + + if (reg_rt < 128 && itype & spu_itype::pure && reg_rt != reg_access[0] && reg_rt != reg_access[1] && reg_rt != reg_access[2]) + { + masked_times[masked_op] = {ensure(m_block->reg[reg_rt]), inst_times}; + } + } + + for (u32 i = 0; i < s_reg_max; i++) + { + if (m_reduced_loop_info && m_reduced_loop_info->loop_may_update.test(i)) + { + m_block->reg[i] = m_block->reg_save_and_restore[i]; + } + } + + m_reduced_loop_info = nullptr; + // Emit instructions for (m_pos = baddr; m_pos >= start && m_pos < end && !m_ir->GetInsertBlock()->getTerminator(); m_pos += 4) { @@ -2640,6 +3269,8 @@ public: m_ir->SetInsertPoint(ins); auto si = llvm::cast(m_ir->Insert(bs->clone())); + spu_context_attr(si); + if (b2->store[i] == nullptr) { // Protect against backwards ordering now @@ -2705,7 +3336,7 @@ public: continue; m_ir->SetInsertPoint(ins); - m_ir->Insert(bs->clone()); + m_ir->Insert(spu_context_attr(bs->clone())); } bs->eraseFromParent(); @@ -2955,7 +3586,7 @@ public: // Create interpreter table const auto if_type = get_ftype(); - m_function_table = new GlobalVariable(*m_module, ArrayType::get(m_ir->getPtrTy(), 1ull << m_interp_magn), true, GlobalValue::InternalLinkage, nullptr); + m_function_table = new GlobalVariable(*m_module, ArrayType::get(get_type(), 1ull << m_interp_magn), true, GlobalValue::InternalLinkage, nullptr); init_luts(); @@ -2999,7 +3630,7 @@ public: m_ir->CreateStore(m_ir->CreateCall(get_intrinsic(Intrinsic::read_register), {rsp_name}), native_sp); // Decode (shift) and load function pointer - const auto first = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(m_interp_op, 32u - m_interp_magn))); + const auto first = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_interp_table, m_ir->CreateLShr(m_interp_op, 32u - m_interp_magn))); const auto call0 = m_ir->CreateCall(if_type, first, {m_lsptr, m_thread, m_interp_pc, m_interp_op, m_interp_table, m_interp_7f0, m_interp_regs}); call0->setCallingConv(CallingConv::GHC); m_ir->CreateRetVoid(); @@ -3143,7 +3774,7 @@ public: const auto next_pc = itype & spu_itype::branch ? m_interp_pc : m_interp_pc_next; const auto be32_op = m_ir->CreateLoad(get_type(), _ptr(m_lsptr, m_ir->CreateZExt(next_pc, get_type()))); const auto next_op = m_ir->CreateCall(get_intrinsic(Intrinsic::bswap), {be32_op}); - const auto next_if = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn))); + const auto next_if = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn))); llvm::cast(next_if)->setVolatile(true); if (!(itype & spu_itype::branch)) @@ -3268,7 +3899,7 @@ public: } } - m_function_table->setInitializer(ConstantArray::get(ArrayType::get(m_ir->getPtrTy(), 1ull << m_interp_magn), iptrs)); + m_function_table->setInitializer(ConstantArray::get(ArrayType::get(get_type(), 1ull << m_interp_magn), iptrs)); m_function_table = nullptr; for (auto& f : *_module) @@ -4759,6 +5390,50 @@ public: } const auto a = get_vr(op.ra); + +#ifdef ARCH_ARM64 + // Use dot product instructions with special values to shift then sum results into the preferred slot + if (m_use_dotprod) + { + if (match_vr(op.ra, [&](auto c, auto MP) + { + using VT = typename decltype(MP)::type; + + if (auto [ok, x] = match_expr(c, sext(match]>())); ok) + { + const auto zeroes = splat(0); + + const auto es = zshuffle(bitcast(a), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 4, 8, 12); + + set_vr(op.rt, sdot(zeroes, es, build( + -0x01, -0x02, -0x04, -0x08, + -0x01, -0x02, -0x04, -0x08, + -0x01, -0x02, -0x04, -0x08, + -0x01, -0x02, -0x04, -0x08 + ))); + return true; + } + return false; + })) + { + return; + } + + const auto zeroes = splat(0); + const auto masked = a & 0x01; + + const auto es = zshuffle(bitcast(masked), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 4, 8, 12); + + set_vr(op.rt, udot(zeroes, es, build( + 0x01, 0x02, 0x04, 0x08, + 0x01, 0x02, 0x04, 0x08, + 0x01, 0x02, 0x04, 0x08, + 0x01, 0x02, 0x04, 0x08 + ))); + return; + } +#endif + const auto m = zext(bitcast(trunc(a))); set_vr(op.rt, insert(splat(0), 3, eval(m))); } @@ -4774,6 +5449,54 @@ public: } const auto a = get_vr(op.ra); + +#ifdef ARCH_ARM64 + // Use dot product instructions with special values to shift then sum results into the preferred slot + if (m_use_dotprod) + { + if (match_vr(op.ra, [&](auto c, auto MP) + { + using VT = typename decltype(MP)::type; + + if (auto [ok, x] = match_expr(c, sext(match]>())); ok) + { + const auto zeroes = splat(0); + + const auto es = zshuffle(bitcast(a), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14); + + const auto extracted = sdot(zeroes, es, build( + -0x01, -0x02, -0x04, -0x08, + -0x10, -0x20, -0x40, -0x80, + -0x01, -0x02, -0x04, -0x08, + -0x10, -0x20, -0x40, -0x80 + )); + + set_vr(op.rt, addp(zeroes, bitcast(extracted))); + return true; + } + return false; + })) + { + return; + } + + const auto zeroes = splat(0); + const auto masked = a & 0x01; + + const auto es = zshuffle(bitcast(masked), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14); + + const auto extracted = udot(zeroes, es, build( + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 + )); + + set_vr(op.rt, addp(zeroes, bitcast(extracted))); + return; + } +#endif + const auto m = zext(bitcast(trunc(a))); set_vr(op.rt, insert(splat(0), 3, eval(m))); } @@ -4782,6 +5505,53 @@ public: { const auto a = get_vr(op.ra); +#ifdef ARCH_ARM64 + // Use dot product instructions with special values to shift then sum results into the preferred slot + if (m_use_dotprod) + { + if (match_vr(op.ra, [&](auto c, auto MP) + { + using VT = typename decltype(MP)::type; + + if (auto [ok, x] = match_expr(c, sext(match]>())); ok) + { + const auto zeroes = splat(0); + + const auto extracted = sdot(zeroes, a, build( + -0x01, -0x02, -0x04, -0x08, + -0x10, -0x20, -0x40, -0x80, + -0x01, -0x02, -0x04, -0x08, + -0x10, -0x20, -0x40, -0x80 + )); + + const auto es = zshuffle(bitcast(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 0, 8, 4, 12, 16, 16, 16, 16); + const auto zeroes16 = splat(0); + set_vr(op.rt, addp(zeroes16, bitcast(es))); + return true; + } + return false; + })) + { + return; + } + + const auto zeroes = splat(0); + const auto masked = a & 0x01; + + const auto extracted = udot(zeroes, masked, build( + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 + )); + + const auto es = zshuffle(bitcast(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 0, 8, 4, 12, 16, 16, 16, 16); + const auto zeroes16 = splat(0); + set_vr(op.rt, addp(zeroes16, bitcast(es))); + return; + } +#endif + if (m_use_gfni) { const auto as = zshuffle(a, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8); @@ -5313,13 +6083,24 @@ public: return; } +#ifdef ARCH_ARM64 + if (m_use_dotprod) +#else if (m_use_vnni) +#endif { - const auto [a, b] = get_vrs(op.ra, op.rb); const auto zeroes = splat(0); +#ifdef ARCH_ARM64 + const auto [a, b] = get_vrs(op.ra, op.rb); + const auto ones = splat(0x01); + const auto ax = bitcast(udot(zeroes, a, ones)); + const auto bx = bitcast(udot(zeroes, b, ones)); +#else + const auto [a, b] = get_vrs(op.ra, op.rb); const auto ones = splat(0x01010101); const auto ax = bitcast(vpdpbusd(zeroes, a, ones)); const auto bx = bitcast(vpdpbusd(zeroes, b, ones)); +#endif set_vr(op.rt, shuffle2(ax, bx, 0, 8, 2, 10, 4, 12, 6, 14)); return; } @@ -5606,11 +6387,59 @@ public: void CEQI(spu_opcode_t op) { + // CEQHI following a comparison instruction (compare-equal negation) + if (!m_interp_magn && !op.si10 && match_vr(op.ra, [&](auto c, auto MT) + { + using VT = typename decltype(MT)::type; + using VT_HALF = s16[8]; + + if (auto [ok, a, b] = match_expr(c, bitcast(sext(match() == match())) << 16 >> 16); ok && m_block->block_wide_reg_store_elimination) + { + set_vr(op.rt, bitcast(sext(a != b)) << 16 >> 16); + return true; + } + + if (auto [ok, a, b] = match_expr(c, sext(MT == MT)); ok) + { + set_vr(op.rt, sext(a != b)); + return true; + } + + return false; + })) + { + return; + } + set_vr(op.rt, sext(get_vr(op.ra) == get_imm(op.si10))); } void CEQHI(spu_opcode_t op) { + // CEQHI following a comparison instruction (compare-equal negation) + if (!m_interp_magn && !op.si10 && match_vr(op.ra, [&](auto c, auto MT) + { + using VT = typename decltype(MT)::type; + using VT_HALF = s8[16]; + + if (auto [ok, a, b] = match_expr(c, bitcast(sext(match() == match())) << 8 >> 8); ok && m_block->block_wide_reg_store_elimination) + { + set_vr(op.rt, bitcast(sext(a != b)) << 8 >> 8); + return true; + } + + if (auto [ok, a, b] = match_expr(c, sext(match() == match())); ok) + { + set_vr(op.rt, sext(a != b)); + return true; + } + + return false; + })) + { + return; + } + set_vr(op.rt, sext(get_vr(op.ra) == get_imm(op.si10))); } @@ -6187,8 +7016,13 @@ public: return eval(bitcast(min(bitcast(v),splat(0xff7fffff)))); } - value_t clamp_smax(value_t v) + value_t clamp_smax(value_t v, u32 gpr = s_reg_max) { + if (m_reduced_loop_info && gpr < s_reg_max && m_reduced_loop_info->is_gpr_not_NaN_hint(gpr)) + { + return v; + } + if (m_use_avx512) { if (is_input_positive(v)) @@ -6208,16 +7042,6 @@ public: return eval(clamp_positive_smax(clamp_negative_smax(v))); } - // FMA favouring zeros - value_t xmuladd(value_t a, value_t b, value_t c) - { - const auto ma = eval(sext(fcmp_uno(a != fsplat(0.)))); - const auto mb = eval(sext(fcmp_uno(b != fsplat(0.)))); - const auto ca = eval(bitcast(bitcast(a) & mb)); - const auto cb = eval(bitcast(bitcast(b) & ma)); - return eval(fmuladd(ca, cb, c)); - } - // Checks for postive and negative zero, or Denormal (treated as zero) // If sign is +-1 check equality againts all sign bits bool is_spu_float_zero(v128 a, int sign = 0) @@ -6304,12 +7128,6 @@ public: set_vr(op.rt, frsqest(get_vr(op.ra))); } - template - static llvm_calli fcgt(T&& a, U&& b) - { - return {"spu_fcgt", {std::forward(a), std::forward(b)}}; - } - void FCGT(spu_opcode_t op) { if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) @@ -6318,11 +7136,8 @@ public: return; } - register_intrinsic("spu_fcgt", [&](llvm::CallInst* ci) + const auto fcgt = [&](value_t a, value_t b) { - const auto a = value(ci->getOperand(0)); - const auto b = value(ci->getOperand(1)); - const value_t ab[2]{a, b}; std::bitset<2> safe_int_compare(0); @@ -6354,6 +7169,16 @@ public: } } + if (m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(op.ra)) + { + safe_finite_compare.set(0); + } + + if (m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(op.rb)) + { + safe_finite_compare.set(1); + } + if (safe_int_compare.any()) { return eval(sext(bitcast(a) > bitcast(b))); @@ -6373,7 +7198,7 @@ public: const auto bi = eval(bitcast(b)); return eval(sext(fcmp_uno(a != b) & select((ai & bi) >= 0, ai > bi, ai < bi))); - }); + }; set_vr(op.rt, fcgt(get_vr(op.ra), get_vr(op.rb))); } @@ -6470,12 +7295,6 @@ public: set_vr(op.rt, fa(get_vr(op.ra), get_vr(op.rb))); } - template - static llvm_calli fs(T&& a, U&& b) - { - return {"spu_fs", {std::forward(a), std::forward(b)}}; - } - void FS(spu_opcode_t op) { if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) @@ -6484,29 +7303,26 @@ public: return; } - register_intrinsic("spu_fs", [&](llvm::CallInst* ci) + const auto fs = [&](value_t a, value_t b) { - const auto a = value(ci->getOperand(0)); - const auto b = value(ci->getOperand(1)); - if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { - const auto bc = clamp_smax(b); // for #4478 + const auto bc = clamp_smax(b, op.rb); // for #4478 return eval(a - bc); } else { return eval(a - b); } - }); + }; set_vr(op.rt, fs(get_vr(op.ra), get_vr(op.rb))); } - template - static llvm_calli fm(T&& a, U&& b) + template , typename W = llvm_place_stealer_t> + static auto fm(T&& a, U&& b, V&& a_not_nan = match_stealer(), W&& b_not_nan = match_stealer()) { - return llvm_calli{"spu_fm", {std::forward(a), std::forward(b)}}.set_order_equality_hint(1, 1); + return llvm_calli{"spu_fm", {std::forward(a), std::forward(b), a_not_nan, b_not_nan}}.set_order_equality_hint(1, 1, 2, 3); } void FM(spu_opcode_t op) @@ -6521,14 +7337,27 @@ public: { const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); + const bool a_notnan = llvm::cast(ci->getOperand(2))->getZExtValue() != 0; + const bool b_notnan = llvm::cast(ci->getOperand(3))->getZExtValue() != 0; if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { - if (a.value == b.value) + if (a.value == b.value || (a_notnan && b_notnan)) { return eval(a * b); } + if (a_notnan) + { + const auto ma = sext(fcmp_uno(a != fsplat(0.))); + return eval(bitcast(bitcast(a * b) & ma)); + } + else if (b_notnan) + { + const auto mb = sext(fcmp_uno(b != fsplat(0.))); + return eval(bitcast(bitcast(a * b) & mb)); + } + const auto ma = sext(fcmp_uno(a != fsplat(0.))); const auto mb = sext(fcmp_uno(b != fsplat(0.))); return eval(bitcast(bitcast(a * b) & ma & mb)); @@ -6539,10 +7368,13 @@ public: } }); + const u32 a_notnan = m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(op.ra) ? 1 : 0; + const u32 b_notnan = m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(op.rb) ? 1 : 0; + if (op.ra == op.rb && !m_interp_magn) { const auto a = get_vr(op.ra); - set_vr(op.rt, fm(a, a)); + set_vr(op.rt, fm(a, a, splat(a_notnan), splat(a_notnan))); return; } @@ -6581,7 +7413,7 @@ public: } } - set_vr(op.rt, fm(a, b)); + set_vr(op.rt, fm(a, b, splat(a_notnan), splat(b_notnan))); } template @@ -6874,10 +7706,10 @@ public: set_vr(op.rt4, fnms(get_vr(op.ra), get_vr(op.rb), get_vr(op.rc))); } - template - static llvm_calli fma(T&& a, U&& b, V&& c) + template , typename X = llvm_place_stealer_t> + static llvm_calli fma(T&& a, U&& b, V&& c, W&& d = match_stealer(), X&& e = match_stealer()) { - return llvm_calli{"spu_fma", {std::forward(a), std::forward(b), std::forward(c)}}.set_order_equality_hint(1, 1, 0); + return llvm_calli{"spu_fma", {std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e)}}.set_order_equality_hint(1, 1, 2, 3, 4); } template @@ -6896,14 +7728,35 @@ public: return; } + register_intrinsic("spu_fma", [&](llvm::CallInst* ci) { const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - + const bool a_notnan = llvm::cast(ci->getOperand(3))->getZExtValue() != 0; + const bool b_notnan = llvm::cast(ci->getOperand(4))->getZExtValue() != 0; + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { + if (a.value == b.value || (a_notnan && b_notnan)) + { + return fma32x4(a, b, c); + } + + if (a_notnan) + { + const auto ma = sext(fcmp_uno(a != fsplat(0.))); + const auto cb = bitcast(bitcast(b) & ma); + return fma32x4(a, eval(cb), c); + } + else if (b_notnan) + { + const auto mb = sext(fcmp_uno(b != fsplat(0.))); + const auto ca = bitcast(bitcast(a) & mb); + return fma32x4(eval(ca), b, c); + } + const auto ma = sext(fcmp_uno(a != fsplat(0.))); const auto mb = sext(fcmp_uno(b != fsplat(0.))); const auto ca = bitcast(bitcast(a) & mb); @@ -6952,6 +7805,9 @@ public: const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); static const auto MT = match(); + const u32 a_notnan = m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(op.ra) ? 1 : 0; + const u32 b_notnan = m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(op.rb) ? 1 : 0; + auto check_sqrt_pattern_for_float = [&](f32 float_value) -> bool { auto match_fnms = [&](f32 float_value) @@ -7147,7 +8003,13 @@ public: spu_log.todo("[%s:0x%05x] Unmatched spu_rsqrte(c) found in FMA", m_hash, m_pos); } - set_vr(op.rt4, fma(a, b, c)); + if (!m_interp_magn && op.ra == op.rb) + { + set_vr(op.rt4, fma(a, a, c, splat(a_notnan), splat(a_notnan))); + return; + } + + set_vr(op.rt4, fma(a, b, c, splat(a_notnan), splat(b_notnan))); } template @@ -7570,13 +8432,13 @@ public: void make_store_ls(value_t addr, value_t data) { const auto bswapped = byteswap(data); - m_ir->CreateStore(bswapped.eval(m_ir), _ptr(m_lsptr, addr.value)); + spu_mem_attr(m_ir->CreateStore(bswapped.eval(m_ir), _ptr(m_lsptr, addr.value))); } auto make_load_ls(value_t addr) { value_t data; - data.value = m_ir->CreateLoad(get_type(), _ptr(m_lsptr, addr.value)); + data.value = spu_mem_attr(m_ir->CreateLoad(get_type(), _ptr(m_lsptr, addr.value))); return byteswap(data); } @@ -7591,12 +8453,18 @@ public: { data._u32[3] %= SPU_LS_SIZE; - if (data._u32[3] % 0x10 == 0) + if (const u32 remainder = data._u32[3] % 0x10; remainder == 0) { value_t addr = eval(splat(data._u32[3]) + zext(extract(pair.second, 3) & 0x3fff0)); make_store_ls(addr, get_vr(op.rt)); return; } + else + { + value_t addr = eval(splat(data._u32[3] - remainder) + zext((extract(pair.second, 3) + remainder) & 0x3fff0)); + make_store_ls(addr, get_vr(op.rt)); + return; + } } } @@ -7615,12 +8483,18 @@ public: { data._u32[3] %= SPU_LS_SIZE; - if (data._u32[3] % 0x10 == 0) + if (const u32 remainder = data._u32[3] % 0x10; remainder == 0) { value_t addr = eval(splat(data._u32[3]) + zext(extract(pair.second, 3) & 0x3fff0)); set_vr(op.rt, make_load_ls(addr)); return; } + else + { + value_t addr = eval(splat(data._u32[3] - remainder) + zext((extract(pair.second, 3) + remainder) & 0x3fff0)); + set_vr(op.rt, make_load_ls(addr)); + return; + } } } @@ -7674,13 +8548,51 @@ public: } } - value_t addr = eval(zext(extract(get_vr(op.ra), 3) & 0x3fff0) + (get_imm(op.si10) << 4)); + const auto a = get_vr(op.ra); + + if (auto [ok, x, y] = match_expr(a, match() + match()); ok) + { + if (auto [ok1, data] = get_const_vector(x.value, m_pos + 1); ok1 && data._u32[3] % 16 == 0) + { + value_t addr = eval(zext(extract(y, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); + make_store_ls(addr, get_vr(op.rt)); + return; + } + + if (auto [ok2, data] = get_const_vector(y.value, m_pos + 2); ok2 && data._u32[3] % 16 == 0) + { + value_t addr = eval(zext(extract(x, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); + make_store_ls(addr, get_vr(op.rt)); + return; + } + } + + value_t addr = eval(zext(extract(a, 3) & 0x3fff0) + (get_imm(op.si10) << 4)); make_store_ls(addr, get_vr(op.rt)); } void LQD(spu_opcode_t op) { - value_t addr = eval(zext(extract(get_vr(op.ra), 3) & 0x3fff0) + (get_imm(op.si10) << 4)); + const auto a = get_vr(op.ra); + + if (auto [ok, x1, y1] = match_expr(a, match() + match()); ok) + { + if (auto [ok1, data] = get_const_vector(x1.value, m_pos + 1); ok1 && data._u32[3] % 16 == 0) + { + value_t addr = eval(zext(extract(y1, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); + set_vr(op.rt, make_load_ls(addr)); + return; + } + + if (auto [ok2, data] = get_const_vector(y1.value, m_pos + 2); ok2 && data._u32[3] % 16 == 0) + { + value_t addr = eval(zext(extract(x1, 3) & 0x3fff0) + ((get_imm(op.si10) << 4) + splat(data._u32[3] & 0x3fff0))); + set_vr(op.rt, make_load_ls(addr)); + return; + } + } + + value_t addr = eval(zext(extract(a, 3) & 0x3fff0) + (get_imm(op.si10) << 4)); set_vr(op.rt, make_load_ls(addr)); } diff --git a/rpcs3/Emu/Cell/SPUOpcodes.h b/rpcs3/Emu/Cell/SPUOpcodes.h index cea4513e3f..42d76792a2 100644 --- a/rpcs3/Emu/Cell/SPUOpcodes.h +++ b/rpcs3/Emu/Cell/SPUOpcodes.h @@ -24,6 +24,20 @@ union spu_opcode_t bf_t i16; // 9..24 bf_t si16; // 9..24, signed bf_t i18; // 7..24 + + // For 16-bit instructions in the context of 32-bits + u32 duplicate_si10() const + { + const u32 _16 = static_cast(static_cast(si10)); + return (_16 << 16) | _16; + } + + // For 8-bit instructions in the context of 32-bits + u32 duplicate_duplicate_si10() const + { + const u32 _8 = static_cast(si10 & 0xff); + return (_8 << 24) | (_8 << 16) | (_8 << 8) | _8; + } }; constexpr u32 spu_branch_target(u32 pc, u32 imm = 0) @@ -42,6 +56,7 @@ constexpr u32 spu_decode(u32 inst) } std::array op_branch_targets(u32 pc, spu_opcode_t op); +std::tuple, u32> op_register_targets(u32 /*pc*/, spu_opcode_t op); // SPU decoder object. D provides functions. T is function pointer type returned. template diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index 57d842e69d..6c629571d9 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -4,12 +4,24 @@ #include "Utilities/lockless.h" #include "Utilities/address_range.h" #include "SPUThread.h" +#include "SPUAnalyser.h" #include #include #include #include #include +// std::bitset +template + requires requires(std::remove_cvref_t& x, T&& y) { x.count(); x.test(y); x.flip(y); } +[[nodiscard]] constexpr bool at32(CT&& container, T&& index, std::source_location src_loc = std::source_location::current()) +{ + const usz csv = container.size(); + if (csv <= std::forward(index)) [[unlikely]] + fmt::raw_range_error(src_loc, format_object_simplified(index), csv); + return container[std::forward(index)]; +} + // Helper class class spu_cache { @@ -201,6 +213,25 @@ public: __bitset_enum_max }; + enum compare_direction : u32 + { + CMP_TURNAROUND_FLAG = 0x1, + CMP_NEGATE_FLAG = 0x100, + CMP_SLESS = 0, + CMP_SGREATER = CMP_SLESS | CMP_TURNAROUND_FLAG, + CMP_EQUAL, + CMP_EQUAL2 = CMP_EQUAL | CMP_TURNAROUND_FLAG, + CMP_LLESS, + CMP_LGREATER = CMP_LLESS | CMP_TURNAROUND_FLAG, + CMP_SGREATER_EQUAL = CMP_SLESS | CMP_NEGATE_FLAG, + CMP_SLOWER_EQUAL = CMP_SGREATER | CMP_NEGATE_FLAG, + CMP_NOT_EQUAL = CMP_EQUAL | CMP_NEGATE_FLAG, + CMP_NOT_EQUAL2 = CMP_NOT_EQUAL | CMP_TURNAROUND_FLAG, + CMP_LGREATER_EQUAL = CMP_LLESS | CMP_NEGATE_FLAG, + CMP_LLOWER_EQUAL = CMP_LGREATER | CMP_NEGATE_FLAG, + CMP_UNKNOWN, + }; + struct reg_state_t { bs_t flag{+vf::is_null}; @@ -273,6 +304,406 @@ public: static u32 alloc_tag(bool reset = false) noexcept; }; + struct reduced_loop_t + { + bool active = false; // Single block loop detected + bool failed = false; + u32 loop_pc = SPU_LS_SIZE; + u32 loop_end = SPU_LS_SIZE; + + // False: single-block loop + // True: loop with a trailing block of aftermath (iteration update) stuff (like for (u32 i = 0; i < 10; /*update*/ i++)) + bool is_two_block_loop = false; + bool has_cond_state = false; + + // Loop stay-in state requirement + u64 cond_val_mask = umax; + u64 cond_val_min = 0; + u64 cond_val_size = 0; + compare_direction cond_val_compare{}; + u64 cond_val_incr = 0; + bool cond_val_incr_is_immediate = false; + u64 cond_val_register_argument_idx = umax; + u64 cond_val_register_idx = umax; + bool cond_val_incr_before_cond = false; + bool cond_val_incr_before_cond_taken_in_account = false; + bool cond_val_is_immediate = false; + + // Loop attributes + bool is_constant_expression = false; + bool is_secret = false; + + struct supplemental_condition_t + { + u64 immediate_value = umax; + u64 type_size = 0; + compare_direction val_compare{}; + }; + + // Supplemental loop condition: + // Inner conditions that depend on extrnal values (not produced inside the loop) + // all should evaluate to false in order for the optimization to work (at the moment) + // So succeeding can be treated linearly + u64 expected_sup_conds = 0; + u64 current_sup_conds_index = 0; + std::vector sup_conds; + + void take_cond_val_incr_before_cond_into_account() + { + if (cond_val_is_immediate && cond_val_incr_before_cond_taken_in_account && !cond_val_incr_before_cond_taken_in_account) + { + cond_val_min -= cond_val_incr; + cond_val_min &= cond_val_mask; + cond_val_incr_before_cond_taken_in_account = true; + } + } + + std::bitset loop_args; + std::bitset loop_dicts; + std::bitset loop_writes; + std::bitset loop_may_update; + std::bitset gpr_not_nans; + + struct origin_t + { + std::bitset regs{}; + u32 modified = 0; + spu_itype_t mod1_type = spu_itype::UNK; + spu_itype_t mod2_type = spu_itype::UNK; + spu_itype_t mod3_type = spu_itype::UNK; + u32 IMM = 0; + +private: + // Internal, please access using fixed order + spu_itype_t access_type(u32 i) const + { + if (i > modified) + { + return spu_itype::UNK; + } + + switch (i) + { + case 1: return mod1_type; + case 2: return mod2_type; + case 3: return mod3_type; + default: return spu_itype::UNK; + } + + return spu_itype::UNK; + } +public: + + spu_itype_t reverse1_type() + { + return access_type(modified); + } + + spu_itype_t reverse2_type() + { + return access_type(modified - 1); + } + + spu_itype_t reverse3_type() + { + return access_type(modified - 2); + } + + origin_t& join_with_this(const origin_t& rhs) + { + regs |= rhs.regs; + return *this; + } + + origin_t& join_with_this(u32 rhs) + { + regs.set(rhs); + return *this; + } + + origin_t& add_register_origin(u32 reg_val) + { + regs.set(reg_val); + return *this; + } + + bool is_single_reg_access(u32 reg_val) const + { + if (!modified) + { + return true; + } + + return regs.count() == 1 && ::at32(regs, reg_val); + } + + bool is_loop_dictator(u32 reg_val, bool test_predictable = false, bool should_predictable = true) const + { + if (!modified) + { + return false; + } + + if (regs.count() >= 1 && ::at32(regs, reg_val)) + { + if (!test_predictable) + { + return true; + } + + if (modified > 1) + { + return should_predictable ^ true; + } + + switch (mod1_type) + { + case spu_itype::A: + { + if (regs.count() == 2) + { + return should_predictable; + } + + return should_predictable ^ true; + } + case spu_itype::AI: + case spu_itype::AHI: + { + if (IMM && regs.count() == 1) + { + return should_predictable; + } + + return should_predictable ^ true; + } + default: break; + } + + return should_predictable ^ true; + } + + return false; + } + + bool is_predictable_loop_dictator(u32 reg_val) const + { + return is_loop_dictator(reg_val, true, true); + } + + bool is_non_predictable_loop_dictator(u32 reg_val) const + { + return is_loop_dictator(reg_val, true, false); + } + + bool is_null(u32 reg_val) const noexcept + { + if (modified) + { + return false; + } + + if (regs.count() - (::at32(regs, reg_val) ? 1 : 0)) + { + return false; + } + + return true; + } + + origin_t& add_instruction_modifier(spu_itype_t inst_type, u32 imm = 0) + { + if (inst_type == spu_itype::UNK) + { + mod1_type = spu_itype::UNK; + mod2_type = spu_itype::UNK; + mod3_type = spu_itype::UNK; + IMM = umax; + modified = 1; + return *this; + } + + if (modified == 1) + { + if (modified == 3) + { + mod1_type = spu_itype::UNK; + mod2_type = spu_itype::UNK; + mod3_type = spu_itype::UNK; + IMM = umax; + modified = 1; + return *this; + } + + bool is_ok = false; + switch (inst_type) + { + case spu_itype::XSBH: + { + const auto prev_type = modified == 1 ? mod1_type : mod2_type; + is_ok &= mod1_type == spu_itype::CEQB || mod1_type == spu_itype::CEQBI || mod1_type == spu_itype::CGTB || mod1_type == spu_itype::CGTBI || mod1_type == spu_itype::CLGTB || mod1_type == spu_itype::CLGTBI; + break; + } + case spu_itype::ANDI: + { + const auto prev_type = modified == 1 ? mod1_type : mod2_type; + is_ok &= mod1_type == spu_itype::CEQB || mod1_type == spu_itype::CEQBI || mod1_type == spu_itype::CGTB || mod1_type == spu_itype::CGTBI || mod1_type == spu_itype::CLGTB || mod1_type == spu_itype::CLGTBI; + is_ok &= (spu_opcode_t{imm}.si10 & 0xff) == 0xff; + break; + } + case spu_itype::CEQ: + case spu_itype::CEQH: + case spu_itype::CEQB: + case spu_itype::CGT: + case spu_itype::CGTH: + case spu_itype::CGTB: + case spu_itype::CLGT: + case spu_itype::CLGTH: + case spu_itype::CLGTB: + { + is_ok = modified == 1 && (mod1_type == spu_itype::AI || mod1_type == spu_itype::AHI); + IMM = imm; + break; + } + case spu_itype::CEQI: + case spu_itype::CEQHI: + case spu_itype::CEQBI: + case spu_itype::CGTI: + case spu_itype::CGTHI: + case spu_itype::CGTBI: + case spu_itype::CLGTI: + case spu_itype::CLGTHI: + case spu_itype::CLGTBI: + { + is_ok = modified == 1 && (mod1_type == spu_itype::AI || mod1_type == spu_itype::AHI); + IMM = spu_opcode_t{imm}.si10; + break; + } + } + + if (!is_ok) + { + mod1_type = spu_itype::UNK; + mod2_type = spu_itype::UNK; + mod3_type = spu_itype::UNK; + IMM = umax; + modified = 1; + return *this; + } + + (modified == 1 ? mod2_type : mod3_type) = inst_type; + modified++; + return *this; + } + + mod1_type = inst_type; + modified = 1; + + switch (inst_type) + { + case spu_itype::AHI: + { + IMM = spu_opcode_t{imm}.duplicate_si10(); + return *this; + } + case spu_itype::AI: + case spu_itype::ORI: + case spu_itype::XORI: + case spu_itype::ANDI: + + case spu_itype::CEQI: + case spu_itype::CEQHI: + case spu_itype::CEQBI: + case spu_itype::CGTI: + case spu_itype::CGTHI: + case spu_itype::CGTBI: + case spu_itype::CLGTI: + case spu_itype::CLGTHI: + case spu_itype::CLGTBI: + { + IMM = spu_opcode_t{imm}.si10; + return *this; + } + case spu_itype::ILA: + { + IMM = spu_opcode_t{imm}.i18; + return *this; + } + case spu_itype::IOHL: + case spu_itype::ILH: + case spu_itype::ILHU: + { + IMM = spu_opcode_t{imm}.i16; + return *this; + } + default: + { + IMM = imm; + break; + } + } + + return *this; + } + }; + + static origin_t make_reg(u32 reg_val) noexcept + { + origin_t org{}; + org.add_register_origin(reg_val); + return org; + } + + const origin_t* find_reg(u32 reg_val) const noexcept + { + for (auto& pair : regs) + { + if (pair.first == reg_val) + { + return &pair.second; + } + } + + return nullptr; + } + + origin_t* find_reg(u32 reg_val) noexcept + { + return const_cast(std::as_const(*this).find_reg(reg_val)); + } + + bool is_reg_null(u32 reg_val) const noexcept + { + if (const auto reg_found = find_reg(reg_val)) + { + return reg_found->is_null(reg_val); + } + + return true; + } + + bool is_gpr_not_NaN_hint(u32 i) const noexcept + { + return ::at32(gpr_not_nans, i); + } + + origin_t get_reg(u32 reg_val) noexcept + { + const auto org = find_reg(reg_val); + return org ? *org : regs.emplace_back(reg_val, std::remove_reference_t{}).second; + } + + std::vector> regs; + + // Return old state for error reporting + reduced_loop_t discard() + { + const reduced_loop_t old = *this; + *this = reduced_loop_t{}; + return old; + } + }; + protected: spu_runtime* m_spurt{}; @@ -326,8 +757,14 @@ protected: // Set if the initial register value in this block may be xfloat std::bitset reg_maybe_xf{}; - // Bit mask of the registers used (before modified) - std::bitset reg_use{}; + // Set if register is used in floating pont instruction + std::bitset reg_maybe_float{}; + + // Set if register is used as shuffle mask + std::bitset reg_maybe_shuffle_mask{}; + + // Number of times registers are used (before modified) + std::array reg_use{}; // Bit mask of the trivial (u32 x 4) constant value resulting in this block std::bitset reg_const{}; @@ -391,18 +828,23 @@ protected: putllc16, putllc0, rchcnt_loop, + reduced_loop, }; std::vector m_inst_attrs; struct pattern_info { - u64 info; + // Info via integral + u64 info{}; + + // Info via additional erased-typed pointer + std::shared_ptr info_ptr; }; - std::unordered_map m_patterns; + std::map m_patterns; - void add_pattern(inst_attr attr, u32 start, u64 info); + void add_pattern(inst_attr attr, u32 start, u64 info, std::shared_ptr info_ptr = nullptr); private: // For private use @@ -435,7 +877,7 @@ public: spu_program analyse(const be_t* ls, u32 entry_point, std::map>* out_target_list = nullptr); // Print analyser internal state - void dump(const spu_program& result, std::string& out); + void dump(const spu_program& result, std::string& out, u32 block_min = 0, u32 block_max = SPU_LS_SIZE); // Get SPU Runtime spu_runtime& get_runtime() diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index c113492820..79b45acdc2 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -495,7 +495,8 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write); extern thread_local u64 g_tls_fault_spu; -const spu_decoder s_spu_itype; +const extern spu_decoder g_spu_itype; +const extern spu_decoder g_spu_iflag; namespace vm { @@ -598,7 +599,7 @@ std::array op_branch_targets(u32 pc, spu_opcode_t op) { std::array res{spu_branch_target(pc + 4), umax}; - switch (const auto type = s_spu_itype.decode(op.opcode)) + switch (const auto type = g_spu_itype.decode(op.opcode)) { case spu_itype::BR: case spu_itype::BRA: @@ -639,6 +640,54 @@ std::array op_branch_targets(u32 pc, spu_opcode_t op) return res; } +std::tuple, u32> op_register_targets(u32 /*pc*/, spu_opcode_t op) +{ + std::tuple, u32> result{u32{umax}, std::array{128, 128, 128}, op.opcode}; + + const auto type = g_spu_itype.decode(op.opcode); + + if (type & spu_itype::zregmod) + { + std::get<2>(result) = 0; + return result; + } + + std::get<0>(result) = type & spu_itype::_quadrop ? op.rt4 : op.rt; + + spu_opcode_t op_masked = op; + + if (type & spu_itype::_quadrop) + { + op_masked.rt4 = 0; + } + else + { + op_masked.rt = 0; + } + + std::get<2>(result) = op_masked.opcode; + + if (auto iflags = g_spu_iflag.decode(op.opcode)) + { + if (+iflags & +spu_iflag::use_ra) + { + std::get<1>(result)[0] = op.ra; + } + + if (+iflags & +spu_iflag::use_rb) + { + std::get<1>(result)[1] = op.rb; + } + + if (+iflags & +spu_iflag::use_rc) + { + std::get<1>(result)[2] = op.rc; + } + } + + return result; +} + void spu_int_ctrl_t::set(u64 ints) { // leave only enabled interrupts @@ -988,7 +1037,7 @@ std::vector> spu_thread::dump_callstack_list() const passed[i / 4] = true; const spu_opcode_t op{_ref(i)}; - const auto type = s_spu_itype.decode(op.opcode); + const auto type = g_spu_itype.decode(op.opcode); if (start == 0 && type == spu_itype::STQD && op.ra == 1u && op.rt == 0u) { @@ -1090,11 +1139,62 @@ std::vector> spu_thread::dump_callstack_list() const return call_stack_list; } -std::string spu_thread::dump_misc() const +void spu_thread::dump_misc(std::string& ret, std::any& custom_data) const { - std::string ret = cpu_thread::dump_misc(); + cpu_thread::dump_misc(ret, custom_data); - fmt::append(ret, "Block Weight: %u (Retreats: %u)", block_counter, block_failure); + struct dump_misc_data_t + { + u32 cpu_id = umax; + u64 last_read_time = umax; + u64 last_block_counter = umax; + u64 update_count = 0; + + std::pair update(u64 current_block_counter, u64 current_timestamp = get_system_time()) + { + const u64 diff_time = current_timestamp <= last_read_time ? 0 : current_timestamp - last_read_time; + const u64 diff_block = current_block_counter <= last_block_counter ? 0 : current_block_counter - last_block_counter; + + if (last_read_time == umax || update_count >= 1000) + { + last_read_time = current_timestamp; + last_block_counter = current_block_counter; + update_count = 0; + } + else if (diff_time >= 100000 && diff_block >= 100) + { + // Update values to measure rate (but not fully so rate can be measured later) + last_read_time += diff_time / 10 * 9; + last_block_counter += diff_block / 10 * 9; + update_count++; + } + + return {diff_time, diff_block}; + } + }; + + dump_misc_data_t* func_data = std::any_cast(&custom_data); + + if (!func_data) + { + custom_data.reset(); + custom_data = std::make_any(); + func_data = ensure(std::any_cast(&custom_data)); + } + + if (func_data->cpu_id != this->id) + { + *func_data = {}; + func_data->cpu_id = this->id; + } + + const u64 current_block_counter = atomic_storage::load(block_counter); + + const auto [diff_time, diff_block] = func_data->update(current_block_counter); + + const u64 rate_of_diff = diff_block ? std::max(1, utils::rational_mul(diff_block, 1'000'000, std::max(diff_time, 1))) : 0; + + fmt::append(ret, "Block Weight: log10(%u/second): %.1f (Retreats: %u)", rate_of_diff, std::log10(std::max(rate_of_diff, 10)), block_failure); if (u64 hash = atomic_storage::load(block_hash)) { @@ -1145,8 +1245,6 @@ std::string spu_thread::dump_misc() const break; } } - - return ret; } void spu_thread::cpu_on_stop() @@ -3761,7 +3859,7 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add const u32 addr0 = spu_branch_target(addr); const spu_opcode_t op{read_from_ptr>(ls_ptr, addr0 - base_addr)}; - const auto type = s_spu_itype.decode(op.opcode); + const auto type = g_spu_itype.decode(op.opcode); if (type == spu_itype::UNK || !op.opcode) { @@ -3907,7 +4005,7 @@ bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_add // Test the validity of a single instruction of the optional target // This function can't be too slow and is unlikely to improve results by a great deal const u32 op0 = read_from_ptr>(ls_ptr, route_pc - base_addr); - const spu_itype::type type0 = s_spu_itype.decode(op0); + const spu_itype::type type0 = g_spu_itype.decode(op0); if (type0 == spu_itype::UNK || !op0) { @@ -6878,7 +6976,7 @@ spu_exec_object spu_thread::capture_memory_as_elf(std::span>(all_data, pc0 - 4); // Try to find function entry (if they are placed sequentially search for BI $LR of previous function) - if (!op || op == 0x35000000u || s_spu_itype.decode(op) == spu_itype::UNK) + if (!op || op == 0x35000000u || g_spu_itype.decode(op) == spu_itype::UNK) { if (is_exec_code(pc0, { all_data.data(), SPU_LS_SIZE })) break; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 9596f7b006..889d6f291c 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -630,7 +630,7 @@ public: virtual void dump_regs(std::string&, std::any& custom_data) const override; virtual std::string dump_callstack() const override; virtual std::vector> dump_callstack_list() const override; - virtual std::string dump_misc() const override; + virtual void dump_misc(std::string& ret, std::any& custom_data) const override; virtual void cpu_task() override final; virtual void cpu_on_stop() override; virtual void cpu_return() override; diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index b476571ab7..7b9041a06c 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -2219,6 +2219,28 @@ void lv2_obj::prepare_for_sleep(cpu_thread& cpu) cpu_counter::remove(&cpu); } +ppu_thread* lv2_obj::get_running_ppu(u32 index) +{ + usz thread_count = g_cfg.core.ppu_threads; + + if (index >= thread_count) + { + return nullptr; + } + + auto target = atomic_storage::load(g_ppu); + + for (usz cur = 0; target; target = atomic_storage::load(target->next_ppu), cur++) + { + if (cur == index) + { + return target; + } + } + + return nullptr; +} + void lv2_obj::notify_all() noexcept { for (auto cpu : g_to_notify) diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index ffd31227a8..2534f6a8c1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -140,6 +140,32 @@ bool verify_mself(const fs::file& mself_file) return true; } +// TODO: May not be thread-safe (or even, process-safe) +bool has_non_directory_components(std::string_view path) +{ + std::string path0{path}; + + while (true) + { + const std::string sub_path = fs::get_parent_dir(path0); + + if (sub_path.size() >= path0.size()) + { + break; + } + + fs::stat_t stat{}; + if (fs::get_stat(sub_path, stat)) + { + return !stat.is_directory; + } + + path0 = std::move(sub_path); + } + + return false; +} + lv2_fs_mount_info_map::lv2_fs_mount_info_map() { for (auto mp = &g_mp_sys_dev_root; mp; mp = mp->next) // Scan and keep track of pre-mounted devices @@ -899,11 +925,18 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3 switch (auto error = fs::g_tls_error) { + case fs::error::notdir: return {CELL_ENOTDIR}; case fs::error::noent: return {CELL_ENOENT}; - default: sys_fs.error("lv2_file::open(): unknown error %s", error); break; - } + default: + { + if (has_non_directory_components(local_path)) + { + return {CELL_ENOTDIR}; + } - return {CELL_EIO}; + fmt::throw_exception("unknown error %s", error); + } + } } if (flags & CELL_FS_O_MSELF && !verify_mself(file)) @@ -1374,8 +1407,12 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr path, vm::ptr fd) } default: { - sys_fs.error("sys_fs_opendir(): unknown error %s", error); - return {CELL_EIO, path}; + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } + + fmt::throw_exception("unknown error %s", error); } } } @@ -1558,6 +1595,10 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr path, vm::ptr { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: + { + return { CELL_ENOTDIR, path}; + } case fs::error::noent: { // Try to analyse split file (TODO) @@ -1597,8 +1638,12 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr path, vm::ptr } default: { - sys_fs.error("sys_fs_stat(): unknown error %s", error); - return {CELL_EIO, path}; + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } + + fmt::throw_exception("unknown error %s", error); } } } @@ -1724,6 +1769,10 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr path, s32 mode) { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: + { + return { CELL_ENOTDIR, path}; + } case fs::error::noent: { return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path}; @@ -1732,10 +1781,16 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr path, s32 mode) { return {sys_fs.warning, CELL_EEXIST, path}; } - default: sys_fs.error("sys_fs_mkdir(): unknown error %s", error); - } + default: + { + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } - return {CELL_EIO, path}; // ??? + fmt::throw_exception("unknown error %s", error); + } + } } sys_fs.notice("sys_fs_mkdir(): directory %s created", path); @@ -1795,12 +1850,19 @@ error_code sys_fs_rename(ppu_thread& ppu, vm::cptr from, vm::cptr to { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: return {CELL_ENOTDIR, from}; case fs::error::noent: return {CELL_ENOENT, from}; case fs::error::exist: return {CELL_EEXIST, to}; - default: sys_fs.error("sys_fs_rename(): unknown error %s", error); - } + default: + { + if (has_non_directory_components(local_from)) + { + return {CELL_ENOTDIR, from}; + } - return {CELL_EIO, from}; // ??? + fmt::throw_exception("unknown error %s", error); + } + } } sys_fs.notice("sys_fs_rename(): %s renamed to %s", from, to); @@ -1850,12 +1912,19 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr path) { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: return {CELL_ENOTDIR, path}; case fs::error::noent: return {CELL_ENOENT, path}; case fs::error::notempty: return {CELL_ENOTEMPTY, path}; - default: sys_fs.error("sys_fs_rmdir(): unknown error %s", error); - } + default: + { + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } - return {CELL_EIO, path}; // ??? + fmt::throw_exception("unknown error %s", error); + } + } } sys_fs.notice("sys_fs_rmdir(): directory %s removed", path); @@ -1906,14 +1975,24 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr path) { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: + { + return { CELL_ENOTDIR, path }; + } case fs::error::noent: { return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path}; } - default: sys_fs.error("sys_fs_unlink(): unknown error %s", error); - } + default: + { + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } - return {CELL_EIO, path}; // ??? + fmt::throw_exception("unknown error %s", error); + } + } } sys_fs.notice("sys_fs_unlink(): file %s deleted", path); @@ -2632,10 +2711,8 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr switch (auto error = fs::g_tls_error) { case fs::error::inval: return {CELL_EINVAL, "fd=%u, offset=0x%x, whence=%d", fd, offset, whence}; - default: sys_fs.error("sys_fs_lseek(): unknown error %s", error); + default: fmt::throw_exception("unknown error %s", error); } - - return CELL_EIO; // ??? } lock.unlock(); @@ -2751,10 +2828,16 @@ error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr path, vm::ptr(ppu.test_stopped()); @@ -2805,14 +2888,24 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr path, u64 size) { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: + { + return { CELL_ENOTDIR, path}; + } case fs::error::noent: { return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path}; } - default: sys_fs.error("sys_fs_truncate(): unknown error %s", error); - } + default: + { + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } - return {CELL_EIO, path}; // ??? + fmt::throw_exception("unknown error %s", error); + } + } } return CELL_OK; @@ -2858,10 +2951,11 @@ error_code sys_fs_ftruncate(ppu_thread& ppu, u32 fd, u64 size) switch (auto error = fs::g_tls_error) { case fs::error::ok: - default: sys_fs.error("sys_fs_ftruncate(): unknown error %s", error); + default: + { + fmt::throw_exception("unknown error %s", error); + } } - - return CELL_EIO; // ??? } return CELL_OK; @@ -2907,6 +3001,10 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr path, s32 mode) { switch (auto error = fs::g_tls_error) { + case fs::error::notdir: + { + return { CELL_ENOTDIR, path}; + } case fs::error::noent: { // Try to locate split files @@ -2920,8 +3018,12 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr path, s32 mode) } default: { - sys_fs.error("sys_fs_chmod(): unknown error %s", error); - return {CELL_EIO, path}; + if (has_non_directory_components(local_path)) + { + return { CELL_ENOTDIR, path }; + } + + fmt::throw_exception("unknown error %s", error); } } } @@ -3053,14 +3155,24 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr path, vm::cptr in, vm::ptr out) // syscall(621,packet_id,u8 *in,u8 *out) Talk:LV2_Functions_and_Syscalls#Syscall_621_.280x26D.29 gamepad_if usage u32 sys_gamepad_ycon_if(u8 packet_id, vm::ptr in, vm::ptr out) { - switch (packet_id) { case 0: diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 6110d7d4d5..0f6fb09865 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -15,6 +15,18 @@ LOG_CHANNEL(sys_memory); // static shared_mutex s_memstats_mtx; +// This struct is for reduced logging repetition +struct last_reported_memory_stats +{ + struct inner_body + { + u32 prev_total = umax; + u32 prev_avail = umax; + }; + + atomic_t body{}; +}; + lv2_memory_container::lv2_memory_container(u32 size, bool from_idm) noexcept : size(size) , id{from_idm ? idm::last_id() : SYS_MEMORY_CONTAINER_ID_INVALID} @@ -313,8 +325,6 @@ error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptrget(); @@ -332,6 +342,22 @@ error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptrget().body.exchange(now); + + if (now.prev_total != out.total_user_memory || now.prev_avail != out.available_user_memory) + { + // Log on change + sys_memory.warning("sys_memory_get_user_memory_size(mem_info=*0x%x): Avail=0x%x, Total=0x%x", mem_info, out.available_user_memory, out.total_user_memory); + } + else + { + sys_memory.trace("sys_memory_get_user_memory_size(mem_info=*0x%x): Avail=0x%x, Total=0x%x", mem_info, out.available_user_memory, out.total_user_memory); + } + cpu.check_state(); *mem_info = out; return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index f20b6be6cb..6f930e79dd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -63,8 +63,8 @@ extern const std::map g_prx_list { "libcelpenc.sprx", 0 }, { "libddpdec.sprx", 0 }, { "libdivxdec.sprx", 0 }, - { "libdmux.sprx", 0 }, - { "libdmuxpamf.sprx", 0 }, + { "libdmux.sprx", 1 }, + { "libdmuxpamf.sprx", 1 }, { "libdtslbrdec.sprx", 0 }, { "libfiber.sprx", 0 }, { "libfont.sprx", 0 }, diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 0aff5e1e7a..6d55f4b1f0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -453,6 +453,7 @@ public: // Can be called before the actual sleep call in order to move it out of mutex scope static void prepare_for_sleep(cpu_thread& cpu); + static ppu_thread* get_running_ppu(u32 index); struct notify_all_t { diff --git a/rpcs3/Emu/GameInfo.h b/rpcs3/Emu/GameInfo.h index 3efca1410a..da8b2638ba 100644 --- a/rpcs3/Emu/GameInfo.h +++ b/rpcs3/Emu/GameInfo.h @@ -8,6 +8,7 @@ struct GameInfo std::string path; std::string icon_path; std::string movie_path; + std::string audio_path; std::string name; std::string serial; diff --git a/rpcs3/Emu/Io/GameTablet.cpp b/rpcs3/Emu/Io/GameTablet.cpp index 4c2330d908..1d0fcf24fe 100644 --- a/rpcs3/Emu/Io/GameTablet.cpp +++ b/rpcs3/Emu/Io/GameTablet.cpp @@ -229,6 +229,9 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp case CELL_PAD_CTRL_LEFT: left = true; break; + case CELL_PAD_CTRL_PS: + gt.btn_ps |= 1; + break; default: break; } @@ -249,9 +252,6 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp case CELL_PAD_CTRL_TRIANGLE: gt.btn_triangle |= 1; break; - case CELL_PAD_CTRL_PS: - gt.btn_ps |= 1; - break; default: break; } diff --git a/rpcs3/Emu/Io/LogitechG27.cpp b/rpcs3/Emu/Io/LogitechG27.cpp index 1f02c92d59..3f07e92560 100644 --- a/rpcs3/Emu/Io/LogitechG27.cpp +++ b/rpcs3/Emu/Io/LogitechG27.cpp @@ -884,7 +884,7 @@ static s16 fetch_sdl_as_axis(SDL_Joystick* joystick, const sdl_mapping& mapping) return 0; } -static s16 fetch_sdl_axis_avg(std::map>& joysticks, const sdl_mapping& mapping) +static s16 fetch_sdl_axis_avg(const std::map>& joysticks, const sdl_mapping& mapping) { constexpr s16 MAX = 0x7FFF; constexpr s16 MIN = -0x8000; @@ -910,7 +910,7 @@ static s16 fetch_sdl_axis_avg(std::map>& joystic return std::clamp(sdl_joysticks_total_value / static_cast(joysticks_of_type->second.size()), MIN, MAX); } -static bool sdl_to_logitech_g27_button(std::map>& joysticks, const sdl_mapping& mapping) +static bool sdl_to_logitech_g27_button(const std::map>& joysticks, const sdl_mapping& mapping) { auto joysticks_of_type = joysticks.find(mapping.device_type_id); if (joysticks_of_type == joysticks.end()) @@ -931,32 +931,21 @@ static bool sdl_to_logitech_g27_button(std::map> return pressed; } -static u16 sdl_to_logitech_g27_steering(std::map>& joysticks, const sdl_mapping& mapping) +static u16 sdl_to_logitech_g27_steering(const std::map>& joysticks, const sdl_mapping& mapping) { const s16 avg = fetch_sdl_axis_avg(joysticks, mapping); const u16 unsigned_avg = avg + 0x8000; return unsigned_avg * (0xFFFF >> 2) / 0xFFFF; } -static u8 sdl_to_logitech_g27_pedal(std::map>& joysticks, const sdl_mapping& mapping) +static u8 sdl_to_logitech_g27_pedal(const std::map>& joysticks, const sdl_mapping& mapping) { const s16 avg = fetch_sdl_axis_avg(joysticks, mapping); const u16 unsigned_avg = avg + 0x8000; return unsigned_avg * 0xFF / 0xFFFF; } -static inline void set_bit(u8* buf, int bit_num, bool set) -{ - const int byte_num = bit_num / 8; - bit_num %= 8; - const u8 mask = 1 << bit_num; - if (set) - buf[byte_num] = buf[byte_num] | mask; - else - buf[byte_num] = buf[byte_num] & (~mask); -} - -void usb_device_logitech_g27::transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* transfer) +void usb_device_logitech_g27::transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* transfer) const { DFEX_data data{}; ensure(buf_size >= sizeof(data)); @@ -990,7 +979,7 @@ void usb_device_logitech_g27::transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* std::memcpy(buf, &data, sizeof(data)); } -void usb_device_logitech_g27::transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer) +void usb_device_logitech_g27::transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer) const { DFP_data data{}; ensure(buf_size >= sizeof(data)); @@ -1026,7 +1015,7 @@ void usb_device_logitech_g27::transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* t std::memcpy(buf, &data, sizeof(data)); } -void usb_device_logitech_g27::transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer) +void usb_device_logitech_g27::transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer) const { DFGT_data data{}; ensure(buf_size >= sizeof(data)); @@ -1068,7 +1057,7 @@ void usb_device_logitech_g27::transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* std::memcpy(buf, &data, sizeof(data)); } -void usb_device_logitech_g27::transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer) +void usb_device_logitech_g27::transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer) const { G25_data data{}; ensure(buf_size >= sizeof(data)); @@ -1116,7 +1105,7 @@ void usb_device_logitech_g27::transfer_g25(u32 buf_size, u8* buf, UsbTransfer* t std::memcpy(buf, &data, sizeof(data)); } -void usb_device_logitech_g27::transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer) +void usb_device_logitech_g27::transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer) const { G27_data data{}; ensure(buf_size >= sizeof(data)); diff --git a/rpcs3/Emu/Io/LogitechG27.h b/rpcs3/Emu/Io/LogitechG27.h index 4ec0d35c0b..ccb2f58908 100644 --- a/rpcs3/Emu/Io/LogitechG27.h +++ b/rpcs3/Emu/Io/LogitechG27.h @@ -38,10 +38,7 @@ struct logitech_g27_ffb_slot logitech_g27_ffb_state state = logitech_g27_ffb_state::inactive; u64 last_update = 0; SDL_HapticEffect last_effect {}; - - // TODO switch to SDL_HapticEffectID when it becomes available in a future SDL release - // Match the return of SDL_CreateHapticEffect for now - int effect_id = -1; + SDL_HapticEffectID effect_id = -1; }; struct sdl_mapping @@ -124,11 +121,11 @@ public: private: void sdl_refresh(); void set_personality(logitech_personality personality, bool reconnect = false); - void transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* transfer); - void transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer); - void transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer); - void transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer); - void transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer); + void transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* transfer) const; + void transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer) const; + void transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer) const; + void transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer) const; + void transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer) const; u32 m_controller_index = 0; @@ -137,7 +134,7 @@ private: logitech_g27_sdl_mapping m_mapping {}; bool m_reverse_effects = false; - std::mutex m_sdl_handles_mutex; + mutable std::mutex m_sdl_handles_mutex; SDL_Joystick* m_led_joystick_handle = nullptr; SDL_Haptic* m_haptic_handle = nullptr; std::map> m_joysticks; diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 9110687a9c..ccd81d2805 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -11,26 +11,49 @@ PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { } -std::set PadHandlerBase::narrow_set(const std::set& src) +std::vector> PadHandlerBase::find_key_combos(const std::unordered_map& map, const std::string& cfg_string) { - if (src.empty()) - return {}; + std::vector> key_codes; - std::set dst; - for (const u64& s : src) + const std::vector combos = cfg_pad::get_combos(cfg_string); + + for (const pad::combo& combo : combos) { - dst.insert(::narrow(s)); + std::set keys = find_key_codes(map, combo); + + if (!keys.empty()) + { + key_codes.push_back(std::move(keys)); + } } - return dst; + + return key_codes; +} + +std::set PadHandlerBase::find_key_codes(const std::unordered_map& map, const pad::combo& combo) +{ + std::set key_codes; + + for (const std::string& button_name : combo.buttons()) + { + for (const auto& [code, name] : map) + { + if (button_name == name) + { + key_codes.insert(code); + break; + } + } + } + + return key_codes; } -// Get new multiplied value based on the multiplier s32 PadHandlerBase::MultipliedInput(s32 raw_value, s32 multiplier) { return (multiplier * raw_value) / 100; } -// Get new scaled value between 0 and range based on its minimum and maximum f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range) { if (deadzone > 0 && deadzone > minimum) @@ -46,7 +69,6 @@ f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 dea return range * val; } -// Get new scaled value between -range and range based on its minimum and maximum f32 PadHandlerBase::ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range) { // convert [min, max] to [0, 1] @@ -79,7 +101,6 @@ f32 PadHandlerBase::ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 return (2.0f * range * val) - range; } -// Get normalized trigger value based on the range defined by a threshold u16 PadHandlerBase::NormalizeTriggerInput(u16 value, u32 threshold) const { if (value <= threshold || threshold >= trigger_max) @@ -90,8 +111,6 @@ u16 PadHandlerBase::NormalizeTriggerInput(u16 value, u32 threshold) const return static_cast(ScaledInput(static_cast(value), static_cast(trigger_min), static_cast(trigger_max), static_cast(threshold))); } -// normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions -// the input values must lie in 0+ u16 PadHandlerBase::NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 maximum) const { if (threshold >= maximum || maximum <= 0 || raw_value < 0) @@ -114,9 +133,6 @@ u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, s32 threshold, s32 multip return static_cast(ScaledInput(static_cast(scaled_value), 0.0f, static_cast(thumb_max), static_cast(threshold))); } -// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% (default of anti deadzone) -// X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range -// return is new x and y values in 0-255 range std::tuple PadHandlerBase::NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone, u32 anti_deadzone) const { f32 X = inX / 255.0f; @@ -150,28 +166,21 @@ std::tuple PadHandlerBase::NormalizeStickDeadzone(s32 inX, s32 inY, u3 return std::tuple(ConvertAxis(X), ConvertAxis(Y)); } -// get clamped value between 0 and 255 u16 PadHandlerBase::Clamp0To255(f32 input) { return static_cast(std::clamp(input, 0.0f, 255.0f)); } -// get clamped value between 0 and 1023 u16 PadHandlerBase::Clamp0To1023(f32 input) { return static_cast(std::clamp(input, 0.0f, 1023.0f)); } -// input has to be [-1,1]. result will be [0,255] u16 PadHandlerBase::ConvertAxis(f32 value) { return static_cast((value + 1.0) * (255.0 / 2.0)); } -// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle -// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange -// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of ~4000 -// This function assumes inX and inY is already in 0-255 void PadHandlerBase::ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor) { if (!squircle_factor) @@ -243,7 +252,7 @@ cfg_pad* PadHandlerBase::get_config(const std::string& pad_id) return nullptr; } -PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::string& pad_id, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector& /*buttons*/) +PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::string& pad_id, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector& buttons) { if (call_type == gui_call_type::blacklist) blacklist.clear(); @@ -284,12 +293,9 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri // Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed. // Return the new value if the button was pressed (aka. its value was bigger than 0 or the defined threshold) - // Get all the legally pressed buttons and use the one with highest value (prioritize first) - struct - { - u16 value = 0; - std::string name; - } pressed_button{}; + // Get all the legally pressed buttons. We only accept one value per stick though, otherwise it will get messy. + std::map pressed_buttons; + std::array, 2> pressed_sticks{}; for (const auto& [keycode, name] : button_list) { @@ -306,7 +312,9 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri } const bool is_trigger = get_is_left_trigger(device, keycode) || get_is_right_trigger(device, keycode); - const bool is_stick = !is_trigger && (get_is_left_stick(device, keycode) || get_is_right_stick(device, keycode)); + const bool is_left_stick = !is_trigger && get_is_left_stick(device, keycode); + const bool is_right_stick = !is_trigger && !is_left_stick && get_is_right_stick(device, keycode); + const bool is_stick = is_left_stick || is_right_stick; const bool is_touch_motion = !is_trigger && !is_stick && get_is_touch_pad_motion(device, keycode); const bool is_button = !is_trigger && !is_stick && !is_touch_motion; @@ -324,9 +332,27 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri const u16 diff = value > min_value ? value - min_value : 0; - if (diff > button_press_threshold && value > pressed_button.value) + if (diff > button_press_threshold) { - pressed_button = { .value = value, .name = name }; + if (is_left_stick) + { + if (pressed_sticks[0].second < value) + { + pressed_sticks[0] = { name, value }; + } + } + else if (is_right_stick) + { + if (pressed_sticks[1].second < value) + { + pressed_sticks[1] = { name, value }; + } + } + else + { + u16& pressed_value = pressed_buttons[name]; + pressed_value = std::max(pressed_value, value); + } } } } @@ -345,14 +371,11 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri if (callback) { - pad_preview_values preview_values = get_preview_values(data); + pad_preview_values preview_values = get_preview_values(data, buttons); pad_capabilities capabilities = get_capabilities(pad_id); const u32 battery_level = get_battery_level(pad_id); - if (pressed_button.value > 0) - callback(pressed_button.value, pressed_button.name, pad_id, battery_level, std::move(preview_values), std::move(capabilities)); - else - callback(0, "", pad_id, battery_level, std::move(preview_values), std::move(capabilities)); + callback(std::move(pressed_buttons), std::move(pressed_sticks), pad_id, battery_level, std::move(preview_values), std::move(capabilities)); } return status; @@ -412,7 +435,7 @@ void PadHandlerBase::convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 } // Update the pad button values based on their type and thresholds. With this you can use axis or triggers as buttons or vice versa -void PadHandlerBase::TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold, bool ignore_trigger_threshold) +void PadHandlerBase::TranslateButtonPress(const std::shared_ptr& device, u32 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold, bool ignore_trigger_threshold) { if (!device || !device->config) { @@ -476,7 +499,7 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) return false; } - std::array, button::button_count> mapping = get_mapped_key_codes(pad_device, config); + std::array>, button::button_count> mapping = get_mapped_key_codes(pad_device, config); u32 pclass_profile = 0x0; u32 capabilities = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE; @@ -566,69 +589,58 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) return true; } -std::array, PadHandlerBase::button::button_count> PadHandlerBase::get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) +std::array>, PadHandlerBase::button::button_count> PadHandlerBase::get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) { - std::array, button::button_count> mapping{}; + std::array>, button::button_count> mapping{}; if (!device || !cfg) return mapping; - device->trigger_code_left = FindKeyCodes(button_list, cfg->l2); - device->trigger_code_right = FindKeyCodes(button_list, cfg->r2); - device->axis_code_left[0] = FindKeyCodes(button_list, cfg->ls_left); - device->axis_code_left[1] = FindKeyCodes(button_list, cfg->ls_right); - device->axis_code_left[2] = FindKeyCodes(button_list, cfg->ls_down); - device->axis_code_left[3] = FindKeyCodes(button_list, cfg->ls_up); - device->axis_code_right[0] = FindKeyCodes(button_list, cfg->rs_left); - device->axis_code_right[1] = FindKeyCodes(button_list, cfg->rs_right); - device->axis_code_right[2] = FindKeyCodes(button_list, cfg->rs_down); - device->axis_code_right[3] = FindKeyCodes(button_list, cfg->rs_up); + mapping[button::up] = find_key_combos(button_list, cfg->up); + mapping[button::down] = find_key_combos(button_list, cfg->down); + mapping[button::left] = find_key_combos(button_list, cfg->left); + mapping[button::right] = find_key_combos(button_list, cfg->right); + mapping[button::cross] = find_key_combos(button_list, cfg->cross); + mapping[button::square] = find_key_combos(button_list, cfg->square); + mapping[button::circle] = find_key_combos(button_list, cfg->circle); + mapping[button::triangle] = find_key_combos(button_list, cfg->triangle); + mapping[button::start] = find_key_combos(button_list, cfg->start); + mapping[button::select] = find_key_combos(button_list, cfg->select); + mapping[button::l1] = find_key_combos(button_list, cfg->l1); + mapping[button::l2] = find_key_combos(button_list, cfg->l2); + mapping[button::l3] = find_key_combos(button_list, cfg->l3); + mapping[button::r1] = find_key_combos(button_list, cfg->r1); + mapping[button::r2] = find_key_combos(button_list, cfg->r2); + mapping[button::r3] = find_key_combos(button_list, cfg->r3); + mapping[button::ls_left] = find_key_combos(button_list, cfg->ls_left); + mapping[button::ls_right] = find_key_combos(button_list, cfg->ls_right); + mapping[button::ls_down] = find_key_combos(button_list, cfg->ls_down); + mapping[button::ls_up] = find_key_combos(button_list, cfg->ls_up); + mapping[button::rs_left] = find_key_combos(button_list, cfg->rs_left); + mapping[button::rs_right] = find_key_combos(button_list, cfg->rs_right); + mapping[button::rs_down] = find_key_combos(button_list, cfg->rs_down); + mapping[button::rs_up] = find_key_combos(button_list, cfg->rs_up); + mapping[button::ps] = find_key_combos(button_list, cfg->ps); - mapping[button::up] = FindKeyCodes(button_list, cfg->up); - mapping[button::down] = FindKeyCodes(button_list, cfg->down); - mapping[button::left] = FindKeyCodes(button_list, cfg->left); - mapping[button::right] = FindKeyCodes(button_list, cfg->right); - mapping[button::cross] = FindKeyCodes(button_list, cfg->cross); - mapping[button::square] = FindKeyCodes(button_list, cfg->square); - mapping[button::circle] = FindKeyCodes(button_list, cfg->circle); - mapping[button::triangle] = FindKeyCodes(button_list, cfg->triangle); - mapping[button::start] = FindKeyCodes(button_list, cfg->start); - mapping[button::select] = FindKeyCodes(button_list, cfg->select); - mapping[button::l1] = FindKeyCodes(button_list, cfg->l1); - mapping[button::l2] = narrow_set(device->trigger_code_left); - mapping[button::l3] = FindKeyCodes(button_list, cfg->l3); - mapping[button::r1] = FindKeyCodes(button_list, cfg->r1); - mapping[button::r2] = narrow_set(device->trigger_code_right); - mapping[button::r3] = FindKeyCodes(button_list, cfg->r3); - mapping[button::ls_left] = narrow_set(device->axis_code_left[0]); - mapping[button::ls_right] = narrow_set(device->axis_code_left[1]); - mapping[button::ls_down] = narrow_set(device->axis_code_left[2]); - mapping[button::ls_up] = narrow_set(device->axis_code_left[3]); - mapping[button::rs_left] = narrow_set(device->axis_code_right[0]); - mapping[button::rs_right] = narrow_set(device->axis_code_right[1]); - mapping[button::rs_down] = narrow_set(device->axis_code_right[2]); - mapping[button::rs_up] = narrow_set(device->axis_code_right[3]); - mapping[button::ps] = FindKeyCodes(button_list, cfg->ps); - - mapping[button::skateboard_ir_nose] = FindKeyCodes(button_list, cfg->ir_nose); - mapping[button::skateboard_ir_tail] = FindKeyCodes(button_list, cfg->ir_tail); - mapping[button::skateboard_ir_left] = FindKeyCodes(button_list, cfg->ir_left); - mapping[button::skateboard_ir_right] = FindKeyCodes(button_list, cfg->ir_right); - mapping[button::skateboard_tilt_left] = FindKeyCodes(button_list, cfg->tilt_left); - mapping[button::skateboard_tilt_right] = FindKeyCodes(button_list, cfg->tilt_right); + mapping[button::skateboard_ir_nose] = find_key_combos(button_list, cfg->ir_nose); + mapping[button::skateboard_ir_tail] = find_key_combos(button_list, cfg->ir_tail); + mapping[button::skateboard_ir_left] = find_key_combos(button_list, cfg->ir_left); + mapping[button::skateboard_ir_right] = find_key_combos(button_list, cfg->ir_right); + mapping[button::skateboard_tilt_left] = find_key_combos(button_list, cfg->tilt_left); + mapping[button::skateboard_tilt_right] = find_key_combos(button_list, cfg->tilt_right); if (b_has_pressure_intensity_button) { - mapping[button::pressure_intensity_button] = FindKeyCodes(button_list, cfg->pressure_intensity_button); + mapping[button::pressure_intensity_button] = find_key_combos(button_list, cfg->pressure_intensity_button); } if (b_has_analog_limiter_button) { - mapping[button::analog_limiter_button] = FindKeyCodes(button_list, cfg->analog_limiter_button); + mapping[button::analog_limiter_button] = find_key_combos(button_list, cfg->analog_limiter_button); } if (b_has_orientation) { - mapping[button::orientation_reset_button] = FindKeyCodes(button_list, cfg->orientation_reset_button); + mapping[button::orientation_reset_button] = find_key_combos(button_list, cfg->orientation_reset_button); } return mapping; @@ -660,30 +672,46 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) bool pressed{}; u16 value{}; - for (u32 code : button.m_key_codes) + // The DS3 Button is considered pressed if any configured button combination is pressed + for (const std::set& combo : button.m_key_combos) { - bool press{}; - u16 val = button_values[code]; + bool combo_pressed = !combo.empty(); + u16 combo_val = 0; - TranslateButtonPress(device, code, press, val, analog_limiter_enabled); - - if (press) + // The button combination is only considered pressed if all the buttons are pressed + for (u32 code : combo) { + bool btn_pressed{}; + u16 btn_val = button_values[code]; + TranslateButtonPress(device, code, btn_pressed, btn_val, analog_limiter_enabled); + + if (btn_pressed == false) + { + combo_pressed = false; + break; + } + // Modify pressure if necessary if the button was pressed if (adjust_pressure) { - val = pad->m_pressure_intensity; + btn_val = pad->m_pressure_intensity; } else if (pressure_intensity_deadzone > 0) { // Ignore triggers, since they have their own deadzones if (!get_is_left_trigger(device, code) && !get_is_right_trigger(device, code)) { - val = NormalizeDirectedInput(val, pressure_intensity_deadzone, 255); + btn_val = NormalizeDirectedInput(btn_val, pressure_intensity_deadzone, 255); } } - value = std::max(value, val); + // Take minimum combo value. Otherwise we will always end up with the max value in case an actual button is part of the combo. + combo_val = (combo_val == 0) ? btn_val : std::min(combo_val, btn_val); + } + + if (combo_pressed) + { + value = std::max(value, combo_val); pressed = value > 0; } } @@ -702,31 +730,44 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) u16 val_min{}; u16 val_max{}; - // m_key_codes_min are the mapped keys for left or down - for (u32 key_min : pad->m_sticks[i].m_key_codes_min) + // The DS3 Stick direction is considered pressed if any configured button combination is pressed + const auto get_stick_val = [this, &device, &button_values, &pressed, analog_limiter_enabled](const std::vector>& combos, u16& value) { - u16 val = button_values[key_min]; - - TranslateButtonPress(device, key_min, pressed, val, analog_limiter_enabled, true); - - if (pressed) + for (const std::set& combo : combos) { - val_min = std::max(val_min, val); + bool combo_pressed = !combo.empty(); + u16 combo_val = 0; + + for (u32 key_min : combo) + { + bool btn_pressed{}; + u16 btn_val = button_values[key_min]; + + TranslateButtonPress(device, key_min, btn_pressed, btn_val, analog_limiter_enabled, true); + + if (btn_pressed == false) + { + combo_pressed = false; + break; + } + + // Take minimum combo value. Otherwise we will always end up with the max value in case an actual button is part of the combo. + combo_val = (combo_val == 0) ? btn_val : std::min(combo_val, btn_val); + } + + if (combo_pressed) + { + value = std::max(value, combo_val); + pressed = value > 0; + } } - } + }; - // m_key_codes_max are the mapped keys for right or up - for (u32 key_max : pad->m_sticks[i].m_key_codes_max) - { - u16 val = button_values[key_max]; + // m_key_combos_min are the mapped keys for left or down + get_stick_val(pad->m_sticks[i].m_key_combos_min, val_min); - TranslateButtonPress(device, key_max, pressed, val, analog_limiter_enabled, true); - - if (pressed) - { - val_max = std::max(val_max, val); - } - } + // m_key_combos_max are the mapped keys for right or up + get_stick_val(pad->m_sticks[i].m_key_combos_max, val_max); // cancel out opposing values and get the resulting difference stick_val[i] = val_max - val_min; diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 98cca72a7d..0f38f0cb3b 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -36,10 +36,6 @@ public: u8 small_motor{0}; bool new_output_data{true}; steady_clock::time_point last_output; - std::set trigger_code_left{}; - std::set trigger_code_right{}; - std::array, 4> axis_code_left{}; - std::array, 4> axis_code_right{}; struct color { @@ -94,12 +90,12 @@ struct pad_capabilities }; using pad_preview_values = std::array; -using pad_callback = std::function; +using pad_callback = std::function&& /*pressed_buttons*/, std::array, 2>&& /*pressed_sticks*/, std::string /*pad_name*/, u32 /*battery_level*/, pad_preview_values&&, pad_capabilities&&)>; using pad_fail_callback = std::function; using motion_preview_values = std::array; -using motion_callback = std::function; -using motion_fail_callback = std::function; +using motion_callback = std::function; +using motion_fail_callback = std::function; class PadHandlerBase { @@ -196,76 +192,11 @@ protected: std::shared_ptr m_pad_for_pad_settings; - static std::set narrow_set(const std::set& src); + // Search an unordered map for a string value and return the found combos + static std::vector> find_key_combos(const std::unordered_map& map, const std::string& cfg_string); - // Search an unordered map for a string value and return found keycode - template - static std::set FindKeyCodes(const std::unordered_map& map, const cfg::string& cfg_string, bool fallback = true) - { - std::set key_codes; - - const std::string& def = cfg_string.def; - const std::vector names = cfg_pad::get_buttons(cfg_string.to_string()); - T def_code = umax; - - for (const std::string& nam : names) - { - for (const auto& [code, name] : map) - { - if (name == nam) - { - key_codes.insert(static_cast(code)); - } - - if (fallback && name == def) - def_code = static_cast(code); - } - } - - if (!key_codes.empty()) - { - return key_codes; - } - - if (fallback) - { - if (!names.empty()) - input_log.error("FindKeyCode for [name = %s] returned with [def_code = %d] for [def = %s]", cfg_string.to_string(), def_code, def); - - if (def_code != umax) - { - return { def_code }; - } - } - - return {}; - } - - // Search an unordered map for a string value and return found keycode - template - static std::set FindKeyCodes(const std::unordered_map& map, const std::vector& names) - { - std::set key_codes; - - for (const std::string& name : names) - { - for (const auto& [code, nam] : map) - { - if (nam == name) - { - key_codes.insert(static_cast(code)); - break; - } - } - } - - if (!key_codes.empty()) - { - return key_codes; - } - - return {}; - } + // Search an unordered map for a combo and return the found key codes + static std::set find_key_codes(const std::unordered_map& map, const pad::combo& combo); // Get normalized trigger value based on the range defined by a threshold u16 NormalizeTriggerInput(u16 value, u32 threshold) const; @@ -274,7 +205,7 @@ protected: // the input values must lie in 0+ u16 NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 maximum) const; - // This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% + // This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% (default of anti deadzone) // X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range // return is new x and y values in 0-255 range std::tuple NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone, u32 anti_deadzone) const; @@ -284,10 +215,10 @@ public: // Get new multiplied value based on the multiplier static s32 MultipliedInput(s32 raw_value, s32 multiplier); - // Get new scaled value between 0 and 255 based on its minimum and maximum + // Get new scaled value between 0 and range based on its minimum and maximum static f32 ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f); - // Get new scaled value between -255 and 255 based on its minimum and maximum + // Get new scaled value between -range and range based on its minimum and maximum static f32 ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f); // get clamped value between 0 and 255 @@ -301,7 +232,7 @@ public: // The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle // using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange - // this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000 + // this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of ~4000 // This function assumes inX and inY is already in 0-255 static void ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor); @@ -375,23 +306,23 @@ public: private: virtual std::shared_ptr get_device(const std::string& /*device*/) { return nullptr; } - virtual bool get_is_left_trigger(const std::shared_ptr& /*device*/, u64 /*keyCode*/) { return false; } - virtual bool get_is_right_trigger(const std::shared_ptr& /*device*/, u64 /*keyCode*/) { return false; } - virtual bool get_is_left_stick(const std::shared_ptr& /*device*/, u64 /*keyCode*/) { return false; } - virtual bool get_is_right_stick(const std::shared_ptr& /*device*/, u64 /*keyCode*/) { return false; } - virtual bool get_is_touch_pad_motion(const std::shared_ptr& /*device*/, u64 /*keyCode*/) { return false; } + virtual bool get_is_left_trigger(const std::shared_ptr& /*device*/, u32 /*keyCode*/) { return false; } + virtual bool get_is_right_trigger(const std::shared_ptr& /*device*/, u32 /*keyCode*/) { return false; } + virtual bool get_is_left_stick(const std::shared_ptr& /*device*/, u32 /*keyCode*/) { return false; } + virtual bool get_is_right_stick(const std::shared_ptr& /*device*/, u32 /*keyCode*/) { return false; } + virtual bool get_is_touch_pad_motion(const std::shared_ptr& /*device*/, u32 /*keyCode*/) { return false; } virtual PadHandlerBase::connection update_connection(const std::shared_ptr& /*device*/) { return connection::disconnected; } virtual void get_extended_info(const pad_ensemble& /*binding*/) {} virtual void apply_pad_data(const pad_ensemble& /*binding*/) {} - virtual std::unordered_map get_button_values(const std::shared_ptr& /*device*/) { return {}; } - virtual pad_preview_values get_preview_values(const std::unordered_map& /*data*/) { return {}; } + virtual std::unordered_map get_button_values(const std::shared_ptr& /*device*/) { return {}; } + virtual pad_preview_values get_preview_values(const std::unordered_map& /*data*/, const std::vector& /*buttons*/){ return {}; } void get_orientation(const pad_ensemble& binding) const; protected: - virtual std::array, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg); + virtual std::array>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg); virtual void get_mapping(const pad_ensemble& binding); - void TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); + void TranslateButtonPress(const std::shared_ptr& device, u32 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); void init_configs(); cfg_pad* get_config(const std::string& pad_id); diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index 937626076e..7d042bddbc 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -5,30 +5,103 @@ extern std::string g_input_config_override; -std::vector cfg_pad::get_buttons(std::string_view str) +std::vector cfg_pad::get_combos(std::string_view button_string) { - std::vector vec = fmt::split(str, {","}); + if (button_string.empty()) + return {}; - // Handle special case: string contains separator itself as configured value - if (str == "," || str.find(",,") != umax) + // Handle special case: string contains separator itself as configured value (it's why I don't use fmt::split here) + const auto split = [](std::string_view str, char sep) { - vec.push_back(","); + std::set buttons; + bool was_sep = true; + usz btn_start = 0ULL; + usz i = 0ULL; + + for (; i < str.size(); i++) + { + const char c = str[i]; + + if (c == sep) + { + if (!was_sep) + { + was_sep = true; + buttons.insert(std::string(str.substr(btn_start, i - btn_start))); + continue; + } + } + + if (was_sep) + { + was_sep = false; + btn_start = i; + } + + if (i == (str.size() - 1)) + { + buttons.insert(std::string(str.substr(btn_start, i - btn_start + 1))); + } + } + + return buttons; + }; + + std::vector combos; + + // Get all combos (seperated by ',') + const std::set combo_strings = split(button_string, ','); + + for (const std::string& combo_string : combo_strings) + { + // Get all keys for this combo (seperated by '&') + std::set combo = split(combo_string, '&'); + if (!combo.empty()) + { + combos.push_back(pad::combo{std::move(combo)}); + } } - // Remove duplicates - std::sort(vec.begin(), vec.end()); - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); - - return vec; + return combos; } -std::string cfg_pad::get_buttons(std::vector vec) +std::string cfg_pad::get_button_string(std::vector& combos) { - // Remove duplicates - std::sort(vec.begin(), vec.end()); - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + std::vector combo_strings; - return fmt::merge(vec, ","); + // Remove duplicates + std::sort(combos.begin(), combos.end()); + combos.erase(std::unique(combos.begin(), combos.end()), combos.end()); + + for (const pad::combo& combo : combos) + { + // Merge all keys for this combo (seperated by '&') + combo_strings.push_back(combo.to_string()); + } + + // Merge combos (seperated by ',') + return fmt::merge(combo_strings, ","); +} + +std::string cfg_pad::make_button_string(const std::unordered_map& button_list, const std::vector>& button_combos) +{ + std::vector combos; + + for (const std::set& button_combo : button_combos) + { + if (button_combo.empty()) continue; + + pad::combo combo {}; + + for (u32 button : button_combo) + { + combo.add_button(::at32(button_list, button)); + } + + combos.push_back(std::move(combo)); + } + + return get_button_string(combos); } u8 cfg_pad::get_motor_speed(VibrateMotor& motor, f32 multiplier) const diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 7dd4bd6323..86779c0859 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -5,10 +5,47 @@ #include "Utilities/Config.h" #include +#include namespace pad { constexpr static std::string_view keyboard_device_name = "Keyboard"; + + struct combo + { + public: + combo() = default; + combo(std::set buttons) : m_buttons(std::move(buttons)) {} + + const std::set& buttons() const + { + return m_buttons; + } + + void add_button(const std::string& button) + { + if (button.empty()) return; + m_buttons.insert(button); + } + + std::string to_string() const + { + return fmt::merge(m_buttons, "&"); + } + + bool operator==(const combo& other) const + { + return m_buttons == other.m_buttons; + } + + bool operator<(const combo& other) const + { + return m_buttons < other.m_buttons; + } + + private: + std::set m_buttons; + }; } struct cfg_sensor final : cfg::node @@ -25,8 +62,9 @@ struct cfg_pad final : cfg::node cfg_pad() {}; cfg_pad(node* owner, const std::string& name) : cfg::node(owner, name) {} - static std::vector get_buttons(std::string_view str); - static std::string get_buttons(std::vector vec); + static std::vector get_combos(std::string_view button_string); + static std::string get_button_string(std::vector& combos); + static std::string make_button_string(const std::unordered_map& button_list, const std::vector>& button_combos); u8 get_motor_speed(VibrateMotor& motor, f32 multiplier) const; u8 get_large_motor_speed(std::array& motors) const; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index ad3369a7a7..d5ef26f0d7 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -203,7 +203,7 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) const Button& analog_limiter_button = m_buttons[m_analog_limiter_button_index]; - if (analog_limiter_button.m_key_codes.empty()) + if (analog_limiter_button.m_key_combos.empty()) { // Active by default if no button was assigned return true; diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 5fd9c8973a..d2b1535a1a 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -386,18 +386,18 @@ struct Button u16 m_value = 0; bool m_pressed = false; - std::set m_key_codes{}; + std::vector> m_key_combos; - u16 m_actual_value = 0; // only used in keyboard_pad_handler - bool m_analog = false; // only used in keyboard_pad_handler - bool m_trigger = false; // only used in keyboard_pad_handler - std::map m_pressed_keys{}; // only used in keyboard_pad_handler + u16 m_actual_value = 0; // only used in keyboard_pad_handler + bool m_analog = false; // only used in keyboard_pad_handler + bool m_trigger = false; // only used in keyboard_pad_handler + std::map m_pressed_keys; // only used in keyboard_pad_handler Button(){} - Button(u32 offset, std::set key_codes, u32 outKeyCode) + Button(u32 offset, std::vector> key_combos, u32 outKeyCode) : m_offset(offset) , m_outKeyCode(outKeyCode) - , m_key_codes(std::move(key_codes)) + , m_key_combos(std::move(key_combos)) { if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1) { @@ -426,17 +426,19 @@ struct AnalogStick u32 m_offset = 0; u16 m_value = 128; - std::set m_key_codes_min{}; - std::set m_key_codes_max{}; + std::vector> m_key_combos_min; + std::vector> m_key_combos_max; - std::map m_pressed_keys_min{}; // only used in keyboard_pad_handler - std::map m_pressed_keys_max{}; // only used in keyboard_pad_handler + std::map m_pressed_keys_min; // only used in keyboard_pad_handler + std::map m_pressed_keys_max; // only used in keyboard_pad_handler + std::map m_pressed_combos_min; // only used in keyboard_pad_handler + std::map m_pressed_combos_max; // only used in keyboard_pad_handler AnalogStick() {} - AnalogStick(u32 offset, std::set key_codes_min, std::set key_codes_max) + AnalogStick(u32 offset, std::vector> key_combos_min, std::vector> key_combos_max) : m_offset(offset) - , m_key_codes_min(std::move(key_codes_min)) - , m_key_codes_max(std::move(key_codes_max)) + , m_key_combos_min(std::move(key_combos_min)) + , m_key_combos_max(std::move(key_combos_max)) {} }; diff --git a/rpcs3/Emu/Io/ps_move_data.cpp b/rpcs3/Emu/Io/ps_move_data.cpp index 0a167eed39..bf35f26a19 100644 --- a/rpcs3/Emu/Io/ps_move_data.cpp +++ b/rpcs3/Emu/Io/ps_move_data.cpp @@ -24,34 +24,34 @@ void ps_move_data::reset_sensors() angaccel_world = {}; } +ps_move_data::vect<3> ps_move_data::rotate_vector(const vect<4>& q, const vect<3>& v) +{ + const auto cross = [](const vect<3>& a, const vect<3>& b) + { + return vect<3>({ + a.y() * b.z() - a.z() * b.y(), + a.z() * b.x() - a.x() * b.z(), + a.x() * b.y() - a.y() * b.x() + }); + }; + + // q = (x, y, z, w) + const vect<3> q_vec({q.x(), q.y(), q.z()}); + + // t = 2 * cross(q_vec, v) + const vect<3> t = cross(q_vec, v) * 2.0f; + + // v' = v + w * t + cross(q_vec, t) + const vect<3> v_prime = v + t * q.w() + cross(q_vec, t); + + return v_prime; +} + void ps_move_data::update_orientation(f32 delta_time) { if (!delta_time) return; - // Rotate vector v by quaternion q - const auto rotate_vector = [](const vect<4>& q, const vect<3>& v) - { - const vect<4> qv({0.0f, v.x(), v.y(), v.z()}); - const vect<4> q_inv({q.w(), -q.x(), -q.y(), -q.z()}); - - // t = q * v - vect<4> t; - t.w() = -q.x() * qv.x() - q.y() * qv.y() - q.z() * qv.z(); - t.x() = q.w() * qv.x() + q.y() * qv.z() - q.z() * qv.y(); - t.y() = q.w() * qv.y() - q.x() * qv.z() + q.z() * qv.x(); - t.z() = q.w() * qv.z() + q.x() * qv.y() - q.y() * qv.x(); - - // r = t * q_inv - vect<4> r; - r.w() = -t.x() * q_inv.x() - t.y() * q_inv.y() - t.z() * q_inv.z(); - r.x() = t.w() * q_inv.x() + t.y() * q_inv.z() - t.z() * q_inv.y(); - r.y() = t.w() * q_inv.y() - t.x() * q_inv.z() + t.z() * q_inv.x(); - r.z() = t.w() * q_inv.z() + t.x() * q_inv.y() - t.y() * q_inv.x(); - - return vect<3>({r.x(), r.y(), r.z()}); - }; - if constexpr (use_imu_for_velocity) { // Gravity in world frame diff --git a/rpcs3/Emu/Io/ps_move_data.h b/rpcs3/Emu/Io/ps_move_data.h index 1ae30f5c66..2470e47e8c 100644 --- a/rpcs3/Emu/Io/ps_move_data.h +++ b/rpcs3/Emu/Io/ps_move_data.h @@ -15,6 +15,26 @@ struct ps_move_data template const T& operator[](I i) const { return data[i]; } + vect operator*(f32 s) const + { + vect result = *this; + for (int i = 0; i < Size; ++i) + { + result[i] *= s; + } + return result; + } + + vect operator+(const vect& other) const + { + vect result = *this; + for (int i = 0; i < Size; ++i) + { + result[i] += other[i]; + } + return result; + } + T x() const requires (Size >= 1) { return data[0]; } T y() const requires (Size >= 2) { return data[1]; } T z() const requires (Size >= 3) { return data[2]; } @@ -72,4 +92,7 @@ struct ps_move_data void reset_sensors(); void update_orientation(f32 delta_time); void update_velocity(u64 timestamp, be_t pos_world[4]); + + // Rotate vector v by quaternion q + static vect<3> rotate_vector(const vect<4>& q, const vect<3>& v); }; diff --git a/rpcs3/Emu/Io/recording_config.h b/rpcs3/Emu/Io/recording_config.h index 127d24015f..ef73149f5e 100644 --- a/rpcs3/Emu/Io/recording_config.h +++ b/rpcs3/Emu/Io/recording_config.h @@ -13,13 +13,13 @@ struct cfg_recording final : cfg::node node_video(cfg::node* _this) : cfg::node(_this, "Video") {} cfg::uint<0, 60> framerate{this, "Framerate", 30}; - cfg::uint<0, 7680> width{this, "Width", 1280}; - cfg::uint<0, 4320> height{this, "Height", 720}; + cfg::uint<640, 7680> width{this, "Width", 1280}; + cfg::uint<360, 4320> height{this, "Height", 720}; cfg::uint<0, 192> pixel_format{this, "AVPixelFormat", 0}; // AVPixelFormat::AV_PIX_FMT_YUV420P cfg::uint<0, 0xFFFF> video_codec{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4 - cfg::uint<0, 25000000> video_bps{this, "Video Bitrate", 4000000}; - cfg::uint<0, 5> max_b_frames{this, "Max B-Frames", 2}; - cfg::uint<0, 20> gop_size{this, "Group of Pictures Size", 12}; + cfg::uint<1'000'000, 60'000'000> video_bps{this, "Video Bitrate", 4'000'000}; + cfg::uint<0, 3> max_b_frames{this, "Max B-Frames", 2}; + cfg::uint<1, 120> gop_size{this, "Group of Pictures Size", 30}; } video{ this }; @@ -28,7 +28,7 @@ struct cfg_recording final : cfg::node node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {} cfg::uint<0x10000, 0x17000> audio_codec{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC - cfg::uint<0, 25000000> audio_bps{this, "Audio Bitrate", 320000}; + cfg::uint<64'000, 320'000> audio_bps{this, "Audio Bitrate", 192'000}; } audio{ this }; diff --git a/rpcs3/Emu/NP/np_cache.cpp b/rpcs3/Emu/NP/np_cache.cpp index 4731e0cede..4d70cacaa0 100644 --- a/rpcs3/Emu/NP/np_cache.cpp +++ b/rpcs3/Emu/NP/np_cache.cpp @@ -125,7 +125,7 @@ namespace np rooms[room_id].opt_param = *sce_opt_param; } - std::pair> cache_manager::get_slots(SceNpMatching2RoomId room_id) + std::pair> cache_manager::get_slots(SceNpMatching2RoomId room_id) const { std::lock_guard lock(mutex); @@ -134,7 +134,7 @@ namespace np return {SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND, {}}; } - const auto& room = rooms[room_id]; + const auto& room = ::at32(rooms, room_id); SceNpMatching2RoomSlotInfo slots{}; @@ -166,7 +166,7 @@ namespace np return {CELL_OK, slots}; } - std::pair> cache_manager::get_memberids(u64 room_id, s32 sort_method) + std::pair> cache_manager::get_memberids(u64 room_id, s32 sort_method) const { std::lock_guard lock(mutex); @@ -175,7 +175,7 @@ namespace np return {SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND, {}}; } - const auto& room = rooms[room_id]; + const auto& room = ::at32(rooms, room_id); std::vector vec_memberids; @@ -211,7 +211,7 @@ namespace np return {CELL_OK, vec_memberids}; } - std::pair> cache_manager::get_password(SceNpMatching2RoomId room_id) + std::pair> cache_manager::get_password(SceNpMatching2RoomId room_id) const { std::lock_guard lock(mutex); @@ -220,15 +220,17 @@ namespace np return {SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND, {}}; } - if (!rooms[room_id].owner) + const auto& room = ::at32(rooms, room_id); + + if (!room.owner) { return {SCE_NP_MATCHING2_ERROR_NOT_ALLOWED, {}}; } - return {CELL_OK, rooms[room_id].password}; + return {CELL_OK, room.password}; } - std::pair> cache_manager::get_opt_param(SceNpMatching2RoomId room_id) + std::pair> cache_manager::get_opt_param(SceNpMatching2RoomId room_id) const { std::lock_guard lock(mutex); @@ -237,10 +239,10 @@ namespace np return {SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND, {}}; } - return {CELL_OK, rooms[room_id].opt_param}; + return {CELL_OK, ::at32(rooms, room_id).opt_param}; } - error_code cache_manager::get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, bool include_onlinename, bool include_avatarurl) + error_code cache_manager::get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, bool include_onlinename, bool include_avatarurl) const { std::lock_guard lock(mutex); @@ -249,7 +251,7 @@ namespace np return SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND; } - if (!rooms[room_id].members.contains(member_id)) + if (!::at32(rooms, room_id).members.contains(member_id)) { return SCE_NP_MATCHING2_ERROR_ROOM_MEMBER_NOT_FOUND; } @@ -352,7 +354,7 @@ namespace np return not_an_error(needed_data_size); } - std::pair> cache_manager::get_npid(u64 room_id, u16 member_id) + std::pair> cache_manager::get_npid(u64 room_id, u16 member_id) const { std::lock_guard lock(mutex); @@ -371,7 +373,7 @@ namespace np return {CELL_OK, ::at32(::at32(rooms, room_id).members, member_id).userInfo.npId}; } - std::optional cache_manager::get_memberid(u64 room_id, const SceNpId& npid) + std::optional cache_manager::get_memberid(u64 room_id, const SceNpId& npid) const { std::lock_guard lock(mutex); diff --git a/rpcs3/Emu/NP/np_cache.h b/rpcs3/Emu/NP/np_cache.h index 8870f169a8..bd0bd8a736 100644 --- a/rpcs3/Emu/NP/np_cache.h +++ b/rpcs3/Emu/NP/np_cache.h @@ -74,16 +74,16 @@ namespace np void update_password(SceNpMatching2RoomId room_id, const std::optional& password); void update_opt_param(SceNpMatching2RoomId room_id, const SceNpMatching2SignalingOptParam* sce_opt_param); - std::pair> get_slots(SceNpMatching2RoomId room_id); - std::pair> get_memberids(u64 room_id, s32 sort_method); - std::pair> get_password(SceNpMatching2RoomId room_id); - std::pair> get_opt_param(SceNpMatching2RoomId room_id); - error_code get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, bool include_onlinename, bool include_avatarurl); - std::pair> get_npid(u64 room_id, u16 member_id); - std::optional get_memberid(u64 room_id, const SceNpId& npid); + std::pair> get_slots(SceNpMatching2RoomId room_id) const; + std::pair> get_memberids(u64 room_id, s32 sort_method) const; + std::pair> get_password(SceNpMatching2RoomId room_id) const; + std::pair> get_opt_param(SceNpMatching2RoomId room_id) const; + error_code get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, bool include_onlinename, bool include_avatarurl) const; + std::pair> get_npid(u64 room_id, u16 member_id) const; + std::optional get_memberid(u64 room_id, const SceNpId& npid) const; private: - shared_mutex mutex; + mutable shared_mutex mutex; std::map rooms; }; } // namespace np diff --git a/rpcs3/Emu/NP/np_contexts.cpp b/rpcs3/Emu/NP/np_contexts.cpp index f43d02c362..739a46368b 100644 --- a/rpcs3/Emu/NP/np_contexts.cpp +++ b/rpcs3/Emu/NP/np_contexts.cpp @@ -25,7 +25,7 @@ generic_async_transaction_context::~generic_async_transaction_context() } } -std::optional generic_async_transaction_context::get_transaction_status() +std::optional generic_async_transaction_context::get_transaction_status() const { std::lock_guard lock(mutex); return result; diff --git a/rpcs3/Emu/NP/np_contexts.h b/rpcs3/Emu/NP/np_contexts.h index 20e123730b..98ed36fa0a 100644 --- a/rpcs3/Emu/NP/np_contexts.h +++ b/rpcs3/Emu/NP/np_contexts.h @@ -20,12 +20,12 @@ struct generic_async_transaction_context generic_async_transaction_context(const SceNpCommunicationId& communicationId, const SceNpCommunicationPassphrase& passphrase, u64 timeout); - std::optional get_transaction_status(); + std::optional get_transaction_status() const; void abort_transaction(); error_code wait_for_completion(); void set_result_and_wake(error_code err); - shared_mutex mutex; + mutable shared_mutex mutex; std::condition_variable_any wake_cond, completion_cond; std::optional result; SceNpCommunicationId communicationId; diff --git a/rpcs3/Emu/NP/np_gui_cache.cpp b/rpcs3/Emu/NP/np_gui_cache.cpp index fee08356d5..1b85275d24 100644 --- a/rpcs3/Emu/NP/np_gui_cache.cpp +++ b/rpcs3/Emu/NP/np_gui_cache.cpp @@ -63,7 +63,7 @@ namespace np np_gui_cache.error("Cache mismatch: tried to remove a member but it wasn't in the room"); } - error_code gui_cache_manager::get_room_member_list(const SceNpRoomId& room_id, u32 buf_len, vm::ptr data) + error_code gui_cache_manager::get_room_member_list(const SceNpRoomId& room_id, u32 buf_len, vm::ptr data) const { std::lock_guard lock(mutex); diff --git a/rpcs3/Emu/NP/np_gui_cache.h b/rpcs3/Emu/NP/np_gui_cache.h index d742cc5749..431259df6b 100644 --- a/rpcs3/Emu/NP/np_gui_cache.h +++ b/rpcs3/Emu/NP/np_gui_cache.h @@ -45,10 +45,10 @@ namespace np void add_member(const SceNpRoomId& room_id, const SceNpMatchingRoomMember* user_info, bool new_member); void del_member(const SceNpRoomId& room_id, const SceNpMatchingRoomMember* user_info); - error_code get_room_member_list(const SceNpRoomId& room_id, u32 buf_len, vm::ptr data); + error_code get_room_member_list(const SceNpRoomId& room_id, u32 buf_len, vm::ptr data) const; private: - shared_mutex mutex; + mutable shared_mutex mutex; std::map rooms; }; } // namespace np diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index f1fda0752c..3eb2bef0ed 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -1228,16 +1228,22 @@ namespace np } auto messages = rpcn->get_new_messages(); - if (basic_handler_registered) + + for (const auto msg_id : messages) { - for (const auto msg_id : messages) + const auto opt_msg = rpcn->get_message(msg_id); + + if (!opt_msg) + { + continue; + } + + const auto& msg = opt_msg.value(); + const localized_string_id loc_id = (msg->second.mainType == SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE) ? localized_string_id::CELL_NP_MESSAGE_INVITE_RECEIVED : localized_string_id::CELL_NP_MESSAGE_OTHER_RECEIVED; + rsx::overlays::queue_message(get_localized_string(loc_id, msg->first.c_str()), 6'000'000); + + if (basic_handler_registered) { - const auto opt_msg = rpcn->get_message(msg_id); - if (!opt_msg) - { - continue; - } - const auto& msg = opt_msg.value(); if (strncmp(msg->second.commId.data, basic_handler.context.data, sizeof(basic_handler.context.data) - 1) == 0) { u32 event; @@ -1441,7 +1447,7 @@ namespace np return req_id; } - u32 np_handler::get_players_history_count(u32 options) + u32 np_handler::get_players_history_count(u32 options) const { const bool all_history = (options == SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL); @@ -1459,7 +1465,7 @@ namespace np })); } - bool np_handler::get_player_history_entry(u32 options, u32 index, SceNpId* npid) + bool np_handler::get_player_history_entry(u32 options, u32 index, SceNpId* npid) const { const bool all_history = (options == SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL); diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index 62b69a73ac..7e43bbf3cd 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -261,8 +261,8 @@ namespace np ticket get_clan_ticket() const; void add_player_to_history(const SceNpId* npid, const char* description); u32 add_players_to_history(const SceNpId* npids, const char* description, u32 count); - u32 get_players_history_count(u32 options); - bool get_player_history_entry(u32 options, u32 index, SceNpId* npid); + u32 get_players_history_count(u32 options) const; + bool get_player_history_entry(u32 options, u32 index, SceNpId* npid) const; SceNpMatching2MemoryInfo get_memory_info() const; error_code abort_request(u32 req_id); @@ -518,7 +518,7 @@ namespace np player_history& get_player_and_set_timestamp(const SceNpId& npid, u64 timestamp); void save_players_history(); - shared_mutex mutex_history; + mutable shared_mutex mutex_history; std::map players_history; // npid / history struct diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 56cfabd855..96ab505abf 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -3180,7 +3180,7 @@ namespace rpcn } } - std::optional>> rpcn_client::get_message(u64 id) + std::optional>> rpcn_client::get_message(u64 id) const { { std::lock_guard lock(mutex_messages); @@ -3238,21 +3238,21 @@ namespace rpcn active_messages.erase(id); } - u32 rpcn_client::get_num_friends() + u32 rpcn_client::get_num_friends() const { std::lock_guard lock(mutex_friends); return ::size32(friend_infos.friends); } - u32 rpcn_client::get_num_blocks() + u32 rpcn_client::get_num_blocks() const { std::lock_guard lock(mutex_friends); return ::size32(friend_infos.blocked); } - std::optional rpcn_client::get_friend_by_index(u32 index) + std::optional rpcn_client::get_friend_by_index(u32 index) const { std::lock_guard lock(mutex_friends); @@ -3270,7 +3270,7 @@ namespace rpcn return it->first; } - std::optional> rpcn_client::get_friend_presence_by_index(u32 index) + std::optional> rpcn_client::get_friend_presence_by_index(u32 index) const { std::lock_guard lock(mutex_friends); @@ -3284,7 +3284,7 @@ namespace rpcn return std::optional(*it); } - std::optional> rpcn_client::get_friend_presence_by_npid(const std::string& npid) + std::optional> rpcn_client::get_friend_presence_by_npid(const std::string& npid) const { std::lock_guard lock(mutex_friends); const auto it = friend_infos.friends.find(npid); diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index 377cd9c898..daa60d90e7 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -242,7 +242,7 @@ namespace rpcn std::mutex mutex_packets_to_send; // Friends related - shared_mutex mutex_friends; + mutable shared_mutex mutex_friends; std::set> friend_cbs; friend_data friend_infos; @@ -304,11 +304,11 @@ namespace rpcn std::optional add_friend(const std::string& friend_username); bool remove_friend(const std::string& friend_username); - u32 get_num_friends(); - u32 get_num_blocks(); - std::optional get_friend_by_index(u32 index); - std::optional> get_friend_presence_by_index(u32 index); - std::optional> get_friend_presence_by_npid(const std::string& npid); + u32 get_num_friends() const; + u32 get_num_blocks() const; + std::optional get_friend_by_index(u32 index) const; + std::optional> get_friend_presence_by_index(u32 index) const; + std::optional> get_friend_presence_by_npid(const std::string& npid) const; std::vector>> get_notifications(); std::map>> get_replies(); @@ -316,7 +316,7 @@ namespace rpcn std::map get_presence_states(); std::vector get_new_messages(); - std::optional>> get_message(u64 id); + std::optional>> get_message(u64 id) const; std::vector>>> get_messages_and_register_cb(SceNpBasicMessageMainType type, bool include_bootable, message_cb_func cb_func, void* cb_param); void remove_message_cb(message_cb_func cb_func, void* cb_param); void mark_message_used(u64 id); @@ -445,7 +445,7 @@ namespace rpcn return (void_cb_func < void_other_cb_func) || ((!(void_other_cb_func < void_cb_func)) && (cb_param < other.cb_param)); } }; - shared_mutex mutex_messages; + mutable shared_mutex mutex_messages; std::set message_cbs; std::unordered_map>> messages; // msg id / (sender / message) std::set active_messages; // msg id of messages that have not been discarded diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp index 0b5cabe768..6665adbf7d 100644 --- a/rpcs3/Emu/NP/rpcn_config.cpp +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -10,8 +10,7 @@ void cfg_rpcn::load() { const std::string path = cfg_rpcn::get_path(); - fs::file cfg_file(path, fs::read); - if (cfg_file) + if (fs::file cfg_file(path, fs::read); cfg_file) { rpcn_log.notice("Loading RPCN config. Path: %s", path); from_string(cfg_file.to_string()); diff --git a/rpcs3/Emu/NP/rpcn_config.h b/rpcs3/Emu/NP/rpcn_config.h index 3ea6b707b1..113d83e408 100644 --- a/rpcs3/Emu/NP/rpcn_config.h +++ b/rpcs3/Emu/NP/rpcn_config.h @@ -4,7 +4,7 @@ struct cfg_rpcn : cfg::node { - cfg::uint32 version{this, "Version", 1}; + cfg::uint32 version{this, "Version", 2}; cfg::string host{this, "Host", "np.rpcs3.net"}; cfg::string npid{this, "NPID", ""}; cfg::string password{this, "Password", ""}; diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp index 85c9321e28..a4c59763cb 100644 --- a/rpcs3/Emu/NP/signaling_handler.cpp +++ b/rpcs3/Emu/NP/signaling_handler.cpp @@ -832,7 +832,7 @@ u32 signaling_handler::init_sig2(const SceNpId& npid, u64 room_id, u16 member_id return conn_id; } -std::optional signaling_handler::get_conn_id_from_npid(const SceNpId& npid) +std::optional signaling_handler::get_conn_id_from_npid(const SceNpId& npid) const { std::lock_guard lock(data_mutex); @@ -843,7 +843,7 @@ std::optional signaling_handler::get_conn_id_from_npid(const SceNpId& npid) return std::nullopt; } -std::optional signaling_handler::get_sig_infos(u32 conn_id) +std::optional signaling_handler::get_sig_infos(u32 conn_id) const { std::lock_guard lock(data_mutex); if (sig_peers.contains(conn_id)) @@ -852,7 +852,7 @@ std::optional signaling_handler::get_sig_infos(u32 conn_id) return std::nullopt; } -std::optional signaling_handler::get_conn_id_from_addr(u32 addr, u16 port) +std::optional signaling_handler::get_conn_id_from_addr(u32 addr, u16 port) const { std::lock_guard lock(data_mutex); diff --git a/rpcs3/Emu/NP/signaling_handler.h b/rpcs3/Emu/NP/signaling_handler.h index a3eb027fe1..c97c049d36 100644 --- a/rpcs3/Emu/NP/signaling_handler.h +++ b/rpcs3/Emu/NP/signaling_handler.h @@ -63,9 +63,9 @@ public: u32 init_sig1(const SceNpId& npid); u32 init_sig2(const SceNpId& npid, u64 room_id, u16 member_id); - std::optional get_sig_infos(u32 conn_id); - std::optional get_conn_id_from_npid(const SceNpId& npid); - std::optional get_conn_id_from_addr(u32 addr, u16 port); + std::optional get_sig_infos(u32 conn_id) const; + std::optional get_conn_id_from_npid(const SceNpId& npid) const; + std::optional get_conn_id_from_addr(u32 addr, u16 port) const; void add_sig_ctx(u32 ctx_id); void remove_sig_ctx(u32 ctx_id); @@ -128,7 +128,7 @@ private: void retire_all_packets(std::shared_ptr& si); void stop_sig_nl(u32 conn_id, bool forceful); - shared_mutex data_mutex; + mutable shared_mutex data_mutex; atomic_t wakey = 0; signaling_packet sig_packet{}; diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index 937508035b..8cf3466bb9 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -244,7 +244,7 @@ struct copy_unmodified_block_swizzled } const u32 size_in_block = padded_width * padded_height * depth * 2; - rsx::simple_array tmp(size_in_block * words_per_block); + rsx::simple_array tmp(size_in_block * words_per_block); if (words_per_block == 1) [[likely]] { diff --git a/rpcs3/Emu/RSX/Common/io_buffer.h b/rpcs3/Emu/RSX/Common/io_buffer.h index 64f95a5e61..59e8e6a32e 100644 --- a/rpcs3/Emu/RSX/Common/io_buffer.h +++ b/rpcs3/Emu/RSX/Common/io_buffer.h @@ -9,7 +9,7 @@ namespace rsx template concept SpanLike = requires(T t) { - { t.data() } -> std::convertible_to; + { t.data() } -> std::convertible_to; { t.size_bytes() } -> std::convertible_to; }; @@ -71,9 +71,10 @@ namespace rsx return static_cast(m_ptr); } - usz size() const + template + T size() const { - return m_size; + return static_cast(m_size); } template diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index 4476930607..4b3aaa0605 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -132,7 +132,7 @@ namespace rsx free_rsx_memory(Traits::get(sink)); } - Traits::clone_surface(cmd, sink, region.source, new_address, region); + Traits::clone_surface(cmd, sink, region.source, new_address, region, region.source->resolution_scaling_config); allocate_rsx_memory(Traits::get(sink)); if (invalidated) [[unlikely]] @@ -398,6 +398,7 @@ namespace rsx surface_antialiasing antialias, usz width, usz height, usz pitch, u8 bpp, + const rsx::surface_scaling_config_t& scaling_config, Args&&... extra_params) { surface_storage_type old_surface_storage; @@ -448,7 +449,7 @@ namespace rsx } } - if (Traits::surface_matches_properties(surface, format, width, height, antialias)) + if (Traits::surface_matches_properties(surface, format, width, height, antialias, scaling_config)) { if (!pitch_compatible) { @@ -495,7 +496,7 @@ namespace rsx for (auto It = invalidated_resources.begin(); It != invalidated_resources.end(); It++) { auto &surface = *It; - if (Traits::surface_matches_properties(surface, format, width, height, antialias, true)) + if (Traits::surface_matches_properties(surface, format, width, height, antialias, scaling_config, true)) { new_surface_storage = std::move(surface); Traits::notify_surface_reused(new_surface_storage); @@ -531,7 +532,7 @@ namespace rsx if (!new_surface) { ensure(store); - new_surface_storage = Traits::create_new_surface(address, format, width, height, pitch, antialias, std::forward(extra_params)...); + new_surface_storage = Traits::create_new_surface(address, format, width, height, pitch, antialias, scaling_config, std::forward(extra_params)...); new_surface = Traits::get(new_surface_storage); Traits::prepare_surface_for_drawing(command_list, new_surface); allocate_rsx_memory(new_surface); @@ -842,11 +843,13 @@ namespace rsx surface_color_format color_format, surface_antialiasing antialias, usz width, usz height, usz pitch, + const rsx::surface_scaling_config_t& scaling_config, Args&&... extra_params) { return bind_surface_address( command_list, address, color_format, antialias, width, height, pitch, get_format_block_size_in_bytes(color_format), + scaling_config, std::forward(extra_params)...); } @@ -857,12 +860,14 @@ namespace rsx surface_depth_format2 depth_format, surface_antialiasing antialias, usz width, usz height, usz pitch, + const rsx::surface_scaling_config_t& scaling_config, Args&&... extra_params) { return bind_surface_address( command_list, address, depth_format, antialias, width, height, pitch, get_format_block_size_in_bytes(depth_format), + scaling_config, std::forward(extra_params)...); } @@ -969,6 +974,7 @@ namespace rsx surface_raster_type raster_type, const std::array &surface_addresses, u32 address_z, const std::array &surface_pitch, u32 zeta_pitch, + const rsx::surface_scaling_config_t& scaling_config, Args&&... extra_params) { u32 clip_width = clip_horizontal_reg; @@ -998,7 +1004,7 @@ namespace rsx m_bound_render_targets[surface_index] = std::make_pair(surface_addresses[surface_index], bind_address_as_render_targets(command_list, surface_addresses[surface_index], color_format, antialias, - clip_width, clip_height, surface_pitch[surface_index], std::forward(extra_params)...)); + clip_width, clip_height, surface_pitch[surface_index], scaling_config, std::forward(extra_params)...)); m_bound_render_target_ids.push_back(surface_index); } @@ -1014,7 +1020,7 @@ namespace rsx { m_bound_depth_stencil = std::make_pair(address_z, bind_address_as_depth_stencil(command_list, address_z, depth_format, antialias, - clip_width, clip_height, zeta_pitch, std::forward(extra_params)...)); + clip_width, clip_height, zeta_pitch, scaling_config, std::forward(extra_params)...)); } else { @@ -1463,5 +1469,113 @@ namespace rsx } } } + + void sync_scaling_config(command_list_type cmd, const rsx::surface_scaling_config_t& active_config) + { + auto process_list_function = [&](surface_ranged_map& data, const utils::address_range32& range) + { + std::vector surfaces_to_clone; + + for (auto It = data.begin_range(range); It != data.end();) + { + auto surface = Traits::get(It->second); + if (surface->get_resolution_scaling_config() == active_config) + { + ++It; + continue; + } + + // Perform a test scaling and check if anything is different after scaling + // There are many cases where this will avoid creating new surfaces + const auto [new_w, new_h] = rsx::apply_resolution_scale( + active_config, + surface->template get_surface_width<>(), + surface->template get_surface_height<>()); + + if (new_w == surface->width() && new_h == surface->height()) + { + // Not affected by resolution scale. Just update the details and move on. + surface->resolution_scaling_config = active_config; + ++It; + continue; + } + + surfaces_to_clone.push_back(surface); + + // Invalidate the previous surface + invalidate(It->second); + It = data.erase(It); + } + + for (auto& surface : surfaces_to_clone) + { + // Enqueue the memory transfer + surface_storage_type sink{}; + deferred_clipped_region copy{}; + copy.width = surface->template get_surface_width<>(); + copy.height = surface->template get_surface_height<>(); + copy.transfer_scale_x = 1.f; + copy.transfer_scale_y = 1.f; + copy.target = nullptr; + copy.source = surface; + + Traits::clone_surface(cmd, sink, surface, surface->base_addr, copy, active_config); + allocate_rsx_memory(Traits::get(sink)); + + // Replace with the new one + auto new_surface = Traits::get(sink); + ensure(copy.target == new_surface); + data.emplace(surface->get_memory_range(), std::move(sink)); + + // Force barrier to reduce VRAM pressure + new_surface->memory_barrier(cmd, rsx::surface_access::memory_read); + } + }; + + const auto rtt_bind_backup = m_bound_render_targets; + const auto dsv_bind_backup = m_bound_depth_stencil; + + // Unbind everything. We'll restore it later + for (auto& rtt_bind : m_bound_render_targets) + { + rtt_bind = {}; + } + + m_bound_depth_stencil = {}; + + process_list_function(m_render_targets_storage, m_render_targets_memory_range); + process_list_function(m_depth_stencil_storage, m_depth_stencil_memory_range); + + // Restore bindings. + for (int i = 0; i < 4; ++i) + { + const auto address = rtt_bind_backup[i].first; + if (!address) + { + continue; + } + + auto rtt = m_render_targets_storage.find(address); + ensure(rtt != m_render_targets_storage.end()); + + m_bound_render_targets[i] = + { + address, + Traits::get(rtt->second) + }; + } + + if (const auto ds_address = dsv_bind_backup.first) + { + auto ds = m_depth_stencil_storage.find(ds_address); + ensure(ds != m_depth_stencil_storage.end()); + + m_bound_depth_stencil = + { + ds_address, + Traits::get(ds->second) + }; + } + } }; } diff --git a/rpcs3/Emu/RSX/Common/surface_utils.h b/rpcs3/Emu/RSX/Common/surface_utils.h index bf7dee2db3..8ec97600bc 100644 --- a/rpcs3/Emu/RSX/Common/surface_utils.h +++ b/rpcs3/Emu/RSX/Common/surface_utils.h @@ -88,18 +88,18 @@ namespace rsx auto dst_h = std::get<3>(region); // Apply resolution scale if needed - if (g_cfg.video.resolution_scale_percent != 100) - { - auto src = static_cast(source); + auto src = static_cast(source); + std::tie(src_w, src_h) = rsx::apply_resolution_scale( + src->resolution_scaling_config, + src_w, src_h, + src->template get_surface_width(), + src->template get_surface_height()); - std::tie(src_w, src_h) = rsx::apply_resolution_scale(src_w, src_h, - src->template get_surface_width(), - src->template get_surface_height()); - - std::tie(dst_w, dst_h) = rsx::apply_resolution_scale(dst_w, dst_h, - target_surface->template get_surface_width(), - target_surface->template get_surface_height()); - } + std::tie(dst_w, dst_h) = rsx::apply_resolution_scale( + target_surface->resolution_scaling_config, + dst_w, dst_h, + target_surface->template get_surface_width(), + target_surface->template get_surface_height()); width = src_w; height = src_h; @@ -146,6 +146,9 @@ namespace rsx u8 samples_x = 1; u8 samples_y = 1; + // Scaling configuration + surface_scaling_config_t resolution_scaling_config; + rsx::address_range32 memory_range; std::unique_ptr> resolve_surface; @@ -303,6 +306,11 @@ namespace rsx format_info.gcm_depth_format = format; } + void set_resolution_scaling_config(const surface_scaling_config_t& config) + { + resolution_scaling_config = config; + } + inline rsx::surface_color_format get_surface_color_format() const { return format_info.gcm_color_format; @@ -323,6 +331,11 @@ namespace rsx ); } + inline const rsx::surface_scaling_config_t& get_resolution_scaling_config() const + { + return resolution_scaling_config; + } + inline bool dirty() const { return (state_flags != rsx::surface_state_flags::ready) || !old_contents.empty(); @@ -541,10 +554,16 @@ namespace rsx } // Apply resolution scale if needed - if (g_cfg.video.resolution_scale_percent != 100) + if (resolution_scaling_config.scale_percent != 100 || + region.source->resolution_scaling_config.scale_percent != 100) { - auto [src_width, src_height] = rsx::apply_resolution_scale(slice.width, slice.height, slice.source->width(), slice.source->height()); - auto [dst_width, dst_height] = rsx::apply_resolution_scale(slice.width, slice.height, slice.target->width(), slice.target->height()); + const auto& src_res_scale = region.source->resolution_scaling_config; + const auto& dst_res_scale = resolution_scaling_config; + const auto src_surface = ensure(dynamic_cast(slice.source)); + const auto dst_surface = ensure(dynamic_cast(slice.target)); + + auto [src_width, src_height] = rsx::apply_resolution_scale(src_res_scale, slice.width, slice.height, src_surface->get_surface_width(), src_surface->get_surface_height()); + auto [dst_width, dst_height] = rsx::apply_resolution_scale(dst_res_scale, slice.width, slice.height, dst_surface->get_surface_width(), dst_surface->get_surface_height()); slice.transfer_scale_x *= f32(dst_width) / src_width; slice.transfer_scale_y *= f32(dst_height) / src_height; @@ -552,8 +571,8 @@ namespace rsx slice.width = src_width; slice.height = src_height; - std::tie(slice.src_x, slice.src_y) = rsx::apply_resolution_scale(slice.src_x, slice.src_y, slice.source->width(), slice.source->height()); - std::tie(slice.dst_x, slice.dst_y) = rsx::apply_resolution_scale(slice.dst_x, slice.dst_y, slice.target->width(), slice.target->height()); + std::tie(slice.src_x, slice.src_y) = rsx::apply_resolution_scale(src_res_scale, slice.src_x, slice.src_y, src_surface->get_surface_width(), src_surface->get_surface_height()); + std::tie(slice.dst_x, slice.dst_y) = rsx::apply_resolution_scale(dst_res_scale, slice.dst_x, slice.dst_y, dst_surface->get_surface_width(), dst_surface->get_surface_height()); } } diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index dfe8043bd3..8aed0ccc34 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -2413,9 +2413,13 @@ namespace rsx // 2. The image has to have been generated on the GPU (fbo or blit target only) rsx::simple_array sections; - const bool use_upscaling = (result.upload_context == rsx::texture_upload_context::framebuffer_storage && g_cfg.video.resolution_scale_percent != 100); + const bool use_upscaling = (result.upload_context == rsx::texture_upload_context::framebuffer_storage); + auto to_surface_type = [](const copy_region_descriptor& rgn) -> typename surface_store_type::surface_type + { + return static_cast(rgn.src); + }; - if (!helpers::append_mipmap_level(sections, result, attributes, 0, use_upscaling, attributes)) [[unlikely]] + if (!helpers::append_mipmap_level(to_surface_type, sections, result, attributes, 0, use_upscaling, attributes)) [[unlikely]] { // Abort if mip0 is not compatible return result; @@ -2445,7 +2449,7 @@ namespace rsx options, range, extended_dimension, m_rtts, std::forward(extras)...); if (!ret.validate() || - !helpers::append_mipmap_level(sections, ret, attr2, subsurface, use_upscaling, attributes)) + !helpers::append_mipmap_level(to_surface_type, sections, ret, attr2, subsurface, use_upscaling, attributes)) { // Abort break; @@ -2778,7 +2782,7 @@ namespace rsx surf->template get_surface_height() != surf->height()) { // Must go through a scaling operation due to resolution scaling being present - ensure(g_cfg.video.resolution_scale_percent != 100); + ensure(src_subres.surface->resolution_scaling_config.scale_percent != 100); use_null_region = false; } } @@ -3389,8 +3393,8 @@ namespace rsx { const auto surface_width = src_subres.surface->template get_surface_width(); const auto surface_height = src_subres.surface->template get_surface_height(); - std::tie(src_area.x1, src_area.y1) = rsx::apply_resolution_scale(src_area.x1, src_area.y1, surface_width, surface_height); - std::tie(src_area.x2, src_area.y2) = rsx::apply_resolution_scale(src_area.x2, src_area.y2, surface_width, surface_height); + std::tie(src_area.x1, src_area.y1) = rsx::apply_resolution_scale(src_subres.surface->resolution_scaling_config, src_area.x1, src_area.y1, surface_width, surface_height); + std::tie(src_area.x2, src_area.y2) = rsx::apply_resolution_scale(src_subres.surface->resolution_scaling_config, src_area.x2, src_area.y2, surface_width, surface_height); // The resource is of surface type; possibly disabled AA emulation src_subres.surface->transform_blit_coordinates(rsx::surface_access::transfer_read, src_area); @@ -3400,8 +3404,8 @@ namespace rsx { const auto surface_width = dst_subres.surface->template get_surface_width(); const auto surface_height = dst_subres.surface->template get_surface_height(); - std::tie(dst_area.x1, dst_area.y1) = rsx::apply_resolution_scale(dst_area.x1, dst_area.y1, surface_width, surface_height); - std::tie(dst_area.x2, dst_area.y2) = rsx::apply_resolution_scale(dst_area.x2, dst_area.y2, surface_width, surface_height); + std::tie(dst_area.x1, dst_area.y1) = rsx::apply_resolution_scale(dst_subres.surface->resolution_scaling_config, dst_area.x1, dst_area.y1, surface_width, surface_height); + std::tie(dst_area.x2, dst_area.y2) = rsx::apply_resolution_scale(dst_subres.surface->resolution_scaling_config, dst_area.x2, dst_area.y2, surface_width, surface_height); // The resource is of surface type; possibly disabled AA emulation dst_subres.surface->transform_blit_coordinates(rsx::surface_access::transfer_write, dst_area); diff --git a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h index 1560f40ad4..8898830034 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h @@ -357,11 +357,11 @@ namespace rsx const auto surface_width = section.surface->template get_surface_width(); const auto surface_height = section.surface->template get_surface_height(); - const auto [src_width, src_height] = rsx::apply_resolution_scale(section.src_area.width, h, surface_width, surface_height); - const auto [dst_width, dst_height] = rsx::apply_resolution_scale(section.dst_area.width, h, attr.width, attr.height); + const auto [src_width, src_height] = rsx::apply_resolution_scale(section.surface->resolution_scaling_config, section.src_area.width, h, surface_width, surface_height); + const auto [dst_width, dst_height] = rsx::apply_resolution_scale(section.surface->resolution_scaling_config, section.dst_area.width, h, attr.width, attr.height); - std::tie(src_x, src_y) = rsx::apply_resolution_scale(src_x, src_y, surface_width, surface_height); - std::tie(dst_x, dst_y) = rsx::apply_resolution_scale(dst_x, dst_y, attr.width, attr.height); + std::tie(src_x, src_y) = rsx::apply_resolution_scale(section.surface->resolution_scaling_config, src_x, src_y, surface_width, surface_height); + std::tie(dst_x, dst_y) = rsx::apply_resolution_scale(section.surface->resolution_scaling_config, dst_x, dst_y, attr.width, attr.height); section.surface->memory_barrier(cmd, rsx::surface_access::transfer_read); @@ -430,8 +430,10 @@ namespace rsx if (scaling) { // Since output is upscaled, also upscale on dst - const auto [_dst_x, _dst_y] = rsx::apply_resolution_scale(static_cast(dst_offset.x), static_cast(dst_y - dst_slice_begin), attr.width, attr.height); - const auto [_dst_w, _dst_h] = rsx::apply_resolution_scale(dst_w, height, attr.width, attr.height); + + const auto& scaling_config = rsx::get_current_renderer()->resolution_scaling_config; + const auto [_dst_x, _dst_y] = rsx::apply_resolution_scale(scaling_config, static_cast(dst_offset.x), static_cast(dst_y - dst_slice_begin), attr.width, attr.height); + const auto [_dst_w, _dst_h] = rsx::apply_resolution_scale(scaling_config, dst_w, height, attr.width, attr.height); out.push_back ({ @@ -660,10 +662,10 @@ namespace rsx bool is_depth = texptr->is_depth_surface(); auto attr2 = attr; - if (rsx::get_resolution_scale_percent() != 100) + if (texptr->resolution_scaling_config.scale_percent != 100) { - const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(attr.width, attr.height, surface_width, surface_height); - const auto [unused, scaled_slice_h] = rsx::apply_resolution_scale(RSX_SURFACE_DIMENSION_IGNORED, attr.slice_h, surface_width, surface_height); + const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(texptr->resolution_scaling_config, attr.width, attr.height, surface_width, surface_height); + const auto [unused, scaled_slice_h] = rsx::apply_resolution_scale(texptr->resolution_scaling_config, RSX_SURFACE_DIMENSION_IGNORED, attr.slice_h, surface_width, surface_height); attr2.width = scaled_w; attr2.height = scaled_h; attr2.slice_h = scaled_slice_h; @@ -841,7 +843,8 @@ namespace rsx } // If this method was called, there is no easy solution, likely means atlas gather is needed - const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(attr2.width, attr2.height); + const auto& scaling_config = rsx::get_current_renderer()->resolution_scaling_config; + const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(scaling_config, attr2.width, attr2.height); const auto format_class = classify_format(attr2.gcm_format); const auto upload_context = (fbos.empty()) ? texture_upload_context::shader_read : texture_upload_context::framebuffer_storage; @@ -892,14 +895,15 @@ namespace rsx return result; } - template + template bool append_mipmap_level( + to_surface_type_converter&& as_surface_type, // Cast function to surface type rsx::simple_array& sections, // Destination list - const sampled_image_descriptor& level, // Descriptor for the image level being checked - const image_section_attributes_t& attr, // Attributes of image level - u8 mipmap_level, // Level index - bool apply_upscaling, // Whether to upscale the results or not - const image_section_attributes_t& level0_attr) // Attributes of the first mipmap level + const sampled_image_descriptor& level, // Descriptor for the image level being checked + const image_section_attributes_t& attr, // Attributes of image level + u8 mipmap_level, // Level index + bool apply_upscaling, // Whether to upscale the results or not + const image_section_attributes_t& level0_attr) // Attributes of the first mipmap level { if (level.image_handle) { @@ -916,7 +920,8 @@ namespace rsx // Calculate transfer dimensions from attr if (level.upload_context == rsx::texture_upload_context::framebuffer_storage) [[likely]] { - std::tie(mip.src_w, mip.src_h) = rsx::apply_resolution_scale(attr.width, attr.height); + auto rtv = as_surface_type(mip); + std::tie(mip.src_w, mip.src_h) = rsx::apply_resolution_scale(rtv->resolution_scaling_config, attr.width, attr.height); } else { @@ -964,7 +969,9 @@ namespace rsx if (apply_upscaling) { auto& mip = sections.back(); - std::tie(mip.dst_w, mip.dst_h) = rsx::apply_resolution_scale(mip.dst_w, mip.dst_h, level0_attr.width, level0_attr.height); + std::tie(mip.dst_w, mip.dst_h) = rsx::apply_resolution_scale( + as_surface_type(mip)->resolution_scaling_config, + mip.dst_w, mip.dst_h, level0_attr.width, level0_attr.height); } return true; diff --git a/rpcs3/Emu/RSX/Core/RSXDisplay.cpp b/rpcs3/Emu/RSX/Core/RSXDisplay.cpp index e263a945ef..f86c6dea04 100644 --- a/rpcs3/Emu/RSX/Core/RSXDisplay.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDisplay.cpp @@ -49,7 +49,7 @@ namespace rsx } } - std::string framebuffer_statistics_t::to_string(bool squash) const + std::string framebuffer_statistics_t::to_string(const surface_scaling_config_t& scaling_config, bool squash) const { // Format is sorted by sample count struct sorted_message_t @@ -70,7 +70,7 @@ namespace rsx for (const auto& [aa_mode, stat] : data) { auto real_stat = stat; - std::tie(real_stat.width, real_stat.height) = apply_resolution_scale(stat.width, stat.height); + std::tie(real_stat.width, real_stat.height) = apply_resolution_scale(scaling_config, stat.width, stat.height); real_stats.push_back(real_stat); sorted_message_t msg; diff --git a/rpcs3/Emu/RSX/Core/RSXDisplay.h b/rpcs3/Emu/RSX/Core/RSXDisplay.h index 77407f2f19..6a04374e1b 100644 --- a/rpcs3/Emu/RSX/Core/RSXDisplay.h +++ b/rpcs3/Emu/RSX/Core/RSXDisplay.h @@ -12,6 +12,8 @@ namespace rsx { enum class surface_antialiasing : u8; + struct surface_scaling_config_t; + struct framebuffer_dimensions_t { u16 width; @@ -42,7 +44,7 @@ namespace rsx void add(u16 width, u16 height, rsx::surface_antialiasing aa); // Returns a formatted string representing the statistics collected over the frame. - std::string to_string(bool squash) const; + std::string to_string(const surface_scaling_config_t& scaling_config, bool squash) const; }; struct frame_statistics_t diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 53f6ce31e7..8ef45e1f06 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -699,7 +699,9 @@ namespace rsx const auto window_origin = REGS(m_ctx)->shader_window_origin(); const u32 window_height = REGS(m_ctx)->shader_window_height(); const auto pixel_center = REGS(m_ctx)->pixel_center(); - const f32 resolution_scale = (window_height <= static_cast(g_cfg.video.min_scalable_dimension)) ? 1.f : rsx::get_resolution_scale(); + const f32 resolution_scale = (window_height <= RSX(m_ctx)->resolution_scaling_config.min_scalable_dimension) + ? 1.f + : RSX(m_ctx)->resolution_scaling_config.scale_factor(); payload.wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale); payload.wpos_bias[0] = 0.f; diff --git a/rpcs3/Emu/RSX/GL/GLCompute.cpp b/rpcs3/Emu/RSX/GL/GLCompute.cpp index 5607c149ed..12d10d2368 100644 --- a/rpcs3/Emu/RSX/GL/GLCompute.cpp +++ b/rpcs3/Emu/RSX/GL/GLCompute.cpp @@ -340,7 +340,8 @@ namespace gl void cs_d24x8_to_ssbo::run(gl::command_context& cmd, gl::viewable_image* src, const gl::buffer* dst, u32 out_offset, const coordu& region, const gl::pixel_buffer_layout& layout) { - const auto row_pitch = region.width; + const auto row_pitch = layout.row_length ? layout.row_length : region.width; + ensure(row_pitch >= region.width); m_program.uniforms["swap_bytes"] = layout.swap_bytes; m_program.uniforms["output_pitch"] = row_pitch; @@ -390,14 +391,15 @@ namespace gl void cs_rgba8_to_ssbo::run(gl::command_context& cmd, gl::viewable_image* src, const gl::buffer* dst, u32 out_offset, const coordu& region, const gl::pixel_buffer_layout& layout) { - const auto row_pitch = region.width; + const auto row_pitch = layout.row_length ? layout.row_length : region.width; + ensure(row_pitch >= region.width); m_program.uniforms["swap_bytes"] = layout.swap_bytes; m_program.uniforms["output_pitch"] = row_pitch; m_program.uniforms["region_offset"] = color2i(region.x, region.y); m_program.uniforms["region_size"] = color2i(region.width, region.height); m_program.uniforms["is_bgra"] = (layout.format == static_cast(gl::texture::format::bgra)); - m_program.uniforms["block_width"] = static_cast(layout.size); + m_program.uniforms["block_width"] = static_cast(layout.block_size); auto data_view = src->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_IDENTITY), gl::image_aspect::color); @@ -441,6 +443,7 @@ namespace gl { const u32 bpp = dst->image()->pitch() / dst->image()->width(); const u32 row_length = utils::align(dst_region.width * bpp, std::max(layout.alignment, 1)) / bpp; + ensure(row_length >= dst_region.width); m_program.uniforms["swap_bytes"] = layout.swap_bytes; m_program.uniforms["src_pitch"] = row_length; diff --git a/rpcs3/Emu/RSX/GL/GLDMA.cpp b/rpcs3/Emu/RSX/GL/GLDMA.cpp index 3c12009f67..5e60525a5c 100644 --- a/rpcs3/Emu/RSX/GL/GLDMA.cpp +++ b/rpcs3/Emu/RSX/GL/GLDMA.cpp @@ -22,7 +22,7 @@ namespace gl void* userptr = vm::get_super_ptr(base_address); m_data = std::make_unique(); - m_data->create(buffer::target::array, block_size, userptr, buffer::memory_type::userptr, 0); + m_data->create(buffer::target::copy_dst, block_size, userptr, buffer::memory_type::userptr, 0); m_base_address = base_address; // Some drivers may reject userptr input for whatever reason. Check that the state is still valid. @@ -77,7 +77,7 @@ namespace gl { const auto start_block_address = start & s_dma_block_mask; const auto end_block_address = (start + length + s_dma_block_size - 1) & s_dma_block_mask; - return utils::address_range32::start_end(start_block_address, end_block_address); + return utils::address_range32::start_length(start_block_address, end_block_address - start_block_address); } const dma_block& get_block(u32 start, u32 length) diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 3de2585523..d0c2e233e9 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -227,7 +227,7 @@ void GLGSRender::update_draw_state() case rsx::primitive_type::lines: case rsx::primitive_type::line_loop: case rsx::primitive_type::line_strip: - gl_state.line_width(rsx::method_registers.line_width() * rsx::get_resolution_scale()); + gl_state.line_width(rsx::method_registers.line_width() * resolution_scaling_config.scale_factor()); gl_state.enable(rsx::method_registers.line_smooth_enabled(), GL_LINE_SMOOTH); break; default: diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index ebff202303..c1acabd601 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -73,6 +73,7 @@ void GLGSRender::set_viewport() { // NOTE: scale offset matrix already contains the viewport transformation const auto [clip_width, clip_height] = rsx::apply_resolution_scale( + resolution_scaling_config, rsx::method_registers.surface_clip_width(), rsx::method_registers.surface_clip_height()); glViewport(0, 0, clip_width, clip_height); @@ -138,8 +139,7 @@ void GLGSRender::on_init_thread() gl::init(); gl::set_command_context(gl_state); - // Enable adaptive vsync if vsync is requested - gl::set_swapinterval(g_cfg.video.vsync ? -1 : 0); + update_swap_interval(); if (g_cfg.video.debug_output) gl::enable_debugging(); @@ -249,22 +249,23 @@ void GLGSRender::on_init_thread() // Fallback null texture instead of relying on texture0 { std::array pixeldata = { 0, 0, 0, 0, 0, 0, 0, 0 }; + const rsx::io_buffer src_buf = std::span(pixeldata); // 1D auto tex1D = std::make_unique(GL_TEXTURE_1D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); - tex1D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + tex1D->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); // 2D auto tex2D = std::make_unique(GL_TEXTURE_2D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); - tex2D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + tex2D->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); // 3D auto tex3D = std::make_unique(GL_TEXTURE_3D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); - tex3D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + tex3D->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); // CUBE auto texCUBE = std::make_unique(GL_TEXTURE_CUBE_MAP, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); - texCUBE->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + texCUBE->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); m_null_textures[GL_TEXTURE_1D] = std::move(tex1D); m_null_textures[GL_TEXTURE_2D] = std::move(tex2D); @@ -397,6 +398,7 @@ void GLGSRender::on_init_thread() m_ui_renderer.create(); m_video_output_pass.create(); + gl::init_global_texture_resources(); m_gl_texture_cache.initialize(); m_prog_buffer.initialize @@ -578,6 +580,33 @@ void GLGSRender::on_exit() gl::set_primary_context_thread(false); } +void GLGSRender::update_swap_interval() +{ + const vsync_mode current_mode = g_cfg.video.vsync; + if (current_mode == m_vsync_mode) + { + return; + } + + // Enable adaptive vsync if vsync is requested + int swap_interval = 0; + switch (current_mode) + { + default: + case vsync_mode::off: + break; + case vsync_mode::adaptive: + swap_interval = -1; + break; + case vsync_mode::full: + swap_interval = 1; + break; + } + + gl::set_swapinterval(swap_interval); + m_vsync_mode = current_mode; +} + void GLGSRender::clear_surface(u32 arg) { if (skip_current_frame) return; @@ -908,7 +937,7 @@ void GLGSRender::load_program_env() m_draw_processor.fill_scale_offset_data(buf, false); m_draw_processor.fill_user_clip_data(buf + 64); *(reinterpret_cast(buf + 68)) = rsx::method_registers.transform_branch_bits(); - *(reinterpret_cast(buf + 72)) = rsx::method_registers.point_size() * rsx::get_resolution_scale(); + *(reinterpret_cast(buf + 72)) = rsx::method_registers.point_size() * resolution_scaling_config.scale_factor(); *(reinterpret_cast(buf + 76)) = rsx::method_registers.clip_min(); *(reinterpret_cast(buf + 80)) = rsx::method_registers.clip_max(); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index a05eb0bf3d..779519fee7 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -186,6 +186,8 @@ private: gl::texture* get_present_source(gl::present_surface_info* info, const rsx::avconf& avconfig); + void update_swap_interval(); + public: void set_viewport(); void set_scissor(bool clip_viewport); diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.cpp b/rpcs3/Emu/RSX/GL/GLOverlays.cpp index 7d36e5598b..a758804e4f 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.cpp +++ b/rpcs3/Emu/RSX/GL/GLOverlays.cpp @@ -220,10 +220,10 @@ namespace gl m_input_filter = gl::filter::linear; } - gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid) + gl::texture_view* ui_overlay_renderer::load_simple_image(const rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid) { auto tex = std::make_unique(GL_TEXTURE_2D, desc->w, desc->h, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); - tex->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + tex->copy_from(desc->as_span(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); const GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN }; auto view = std::make_unique(tex.get(), remap); @@ -236,7 +236,7 @@ namespace gl } else { - const u64 key = reinterpret_cast(desc); + const u64 key = reinterpret_cast(desc); temp_image_cache[key] = std::make_pair(owner_uid, std::move(tex)); temp_view_cache[key] = std::move(view); } @@ -287,7 +287,7 @@ namespace gl } } - gl::texture_view* ui_overlay_renderer::find_font(rsx::overlays::font* font) + gl::texture_view* ui_overlay_renderer::find_font(const rsx::overlays::font* font) { const auto font_size = font->get_glyph_data_dimensions(); @@ -308,7 +308,7 @@ namespace gl const std::vector& glyph_data = font->get_glyph_data(); auto tex = std::make_unique(GL_TEXTURE_2D_ARRAY, font_size.width, font_size.height, font_size.depth, 1, 1, GL_R8, RSX_FORMAT_CLASS_COLOR); - tex->copy_from(glyph_data.data(), gl::texture::format::r, gl::texture::type::ubyte, {}); + tex->copy_from(std::span(glyph_data), gl::texture::format::r, gl::texture::type::ubyte, {}); GLenum remap[] = { GL_RED, GL_RED, GL_RED, GL_RED }; auto view = std::make_unique(tex.get(), remap); @@ -320,7 +320,7 @@ namespace gl return result; } - gl::texture_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info_base* desc, u32 owner_uid) + gl::texture_view* ui_overlay_renderer::find_temp_image(const rsx::overlays::image_info_base* desc, u32 owner_uid) { const bool dirty = std::exchange(desc->dirty, false); const u64 key = reinterpret_cast(desc); @@ -332,7 +332,7 @@ namespace gl if (dirty) { - view->image()->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + view->image()->copy_from(desc->as_span(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); } return view; @@ -399,8 +399,15 @@ namespace gl void ui_overlay_renderer::run(gl::command_context& cmd_, const areau& viewport, GLuint target, rsx::overlays::overlay& ui, bool flip_vertically) { - program_handle.uniforms["viewport"] = color4f(static_cast(viewport.width()), static_cast(viewport.height()), static_cast(viewport.x1), static_cast(viewport.y1)); - program_handle.uniforms["ui_scale"] = color4f(static_cast(ui.virtual_width), static_cast(ui.virtual_height), 1.f, 1.f); + ui.set_render_viewport( + static_cast(std::min(viewport.width(), std::numeric_limits::max())), + static_cast(std::min(viewport.height(), std::numeric_limits::max())) + ); + const auto ui_scale = color4f(static_cast(ui.virtual_width), static_cast(ui.virtual_height), 1.f, 1.f); + const auto ui_viewport = color4f(static_cast(viewport.width()), static_cast(viewport.height()), static_cast(viewport.x1), static_cast(viewport.y1)); + + program_handle.uniforms["viewport"] = ui_viewport; + program_handle.uniforms["ui_scale"] = ui_scale; saved_sampler_state save_30(30, m_sampler); saved_sampler_state save_31(31, m_sampler); @@ -431,7 +438,7 @@ namespace gl } case rsx::overlays::image_resource_id::raw_image: { - cmd_->bind_texture(31, GL_TEXTURE_2D, find_temp_image(static_cast(cmd.config.external_data_ref), ui.uid)->id()); + cmd_->bind_texture(31, GL_TEXTURE_2D, find_temp_image(static_cast(cmd.config.external_data_ref), ui.uid)->id()); break; } case rsx::overlays::image_resource_id::font_file: @@ -458,12 +465,24 @@ namespace gl .texture_mode(texture_mode) .clip_fragments(cmd.config.clip_region) .pulse_glow(cmd.config.pulse_glow) + .set_sdf(cmd.config.sdf_config.func) .get(); program_handle.uniforms["timestamp"] = cmd.config.get_sinus_value(); program_handle.uniforms["albedo"] = cmd.config.color; program_handle.uniforms["clip_bounds"] = cmd.config.clip_rect; program_handle.uniforms["blur_intensity"] = static_cast(cmd.config.blur_strength); + + if (cmd.config.sdf_config.func != rsx::overlays::sdf_function::none) + { + auto sdf_config = cmd.config.sdf_config; + sdf_config.transform(static_cast(viewport).flipped_vertical(), {ui_scale.x, ui_scale.y}); + + program_handle.uniforms["sdf_params"] = color4f(sdf_config.hx, sdf_config.hy, sdf_config.br, sdf_config.bw); + program_handle.uniforms["sdf_origin"] = color2f(sdf_config.cx, sdf_config.cy); + program_handle.uniforms["sdf_border_color"] = sdf_config.border_color; + } + overlay_pass::run(cmd_, viewport, target, gl::image_aspect::color, true); } @@ -551,7 +570,8 @@ namespace gl const pixel_buffer_layout& layout) { const u32 bpp = dst->image()->pitch() / dst->image()->width(); - const u32 row_length = utils::align(dst_region.width * bpp, std::max(layout.alignment, 1)) / bpp; + const u32 aligned_width = utils::align(dst_region.width * bpp, std::max(layout.alignment, 1)) / bpp; + const u32 row_length = layout.row_length ? layout.row_length : aligned_width; program_handle.uniforms["src_pitch"] = row_length; program_handle.uniforms["swap_bytes"] = layout.swap_bytes; diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.h b/rpcs3/Emu/RSX/GL/GLOverlays.h index 96478a161e..8ccfd67305 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.h +++ b/rpcs3/Emu/RSX/GL/GLOverlays.h @@ -75,16 +75,16 @@ namespace gl ui_overlay_renderer(); - gl::texture_view* load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid); + gl::texture_view* load_simple_image(const rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid); void create(); void destroy(); void remove_temp_resources(u64 key); - gl::texture_view* find_font(rsx::overlays::font* font); + gl::texture_view* find_font(const rsx::overlays::font* font); - gl::texture_view* find_temp_image(rsx::overlays::image_info_base* desc, u32 owner_uid); + gl::texture_view* find_temp_image(const rsx::overlays::image_info_base* desc, u32 owner_uid); void set_primitive_type(rsx::overlays::primitive_type type); diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index e7c03dfcf5..2aa11868ee 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -95,6 +95,7 @@ gl::texture* GLGSRender::get_present_source(gl::present_surface_info* info, cons image = section.surface->get_surface(rsx::surface_access::transfer_read); std::tie(info->width, info->height) = rsx::apply_resolution_scale( + resolution_scaling_config, std::min(surface_width, info->width), std::min(surface_height, info->height)); } @@ -132,7 +133,8 @@ gl::texture* GLGSRender::get_present_source(gl::present_surface_info* info, cons const auto range = utils::address_range32::start_length(info->address, info->pitch * info->height); m_gl_texture_cache.invalidate_range(cmd, range, rsx::invalidation_cause::read); - flip_image->copy_from(vm::base(info->address), static_cast(expected_format), gl::texture::type::uint_8_8_8_8, unpack_settings); + const rsx::io_buffer read_buf = { vm::base(info->address), range.length() }; + flip_image->copy_from(read_buf, static_cast(expected_format), gl::texture::type::uint_8_8_8_8, unpack_settings); image = flip_image.get(); } else if (image->get_internal_format() != static_cast(expected_format)) @@ -224,7 +226,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) if (avconfig.stereo_enabled) [[unlikely]] { - const auto [unused, min_expected_height] = rsx::apply_resolution_scale(RSX_SURFACE_DIMENSION_IGNORED, buffer_height + 30); + const auto [unused, min_expected_height] = rsx::apply_resolution_scale(resolution_scaling_config, RSX_SURFACE_DIMENSION_IGNORED, buffer_height + 30); if (image_to_flip->height() < min_expected_height) { // Get image for second eye @@ -239,7 +241,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) else { // Account for possible insets - const auto [unused2, scaled_buffer_height] = rsx::apply_resolution_scale(RSX_SURFACE_DIMENSION_IGNORED, buffer_height); + const auto [unused2, scaled_buffer_height] = rsx::apply_resolution_scale(resolution_scaling_config, RSX_SURFACE_DIMENSION_IGNORED, buffer_height); buffer_height = std::min(image_to_flip->height() - min_expected_height, scaled_buffer_height); } } @@ -251,6 +253,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) if (info.emu_flip) { evaluate_cpu_usage_reduction_limits(); + update_swap_interval(); } // Get window state @@ -314,9 +317,11 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) // Lock to avoid modification during run-update chain std::lock_guard lock(*m_overlay_manager); + const areau display_area = {0, 0, static_cast(m_frame->client_width()), static_cast(m_frame->client_height())}; for (const auto& view : m_overlay_manager->get_views()) { - m_ui_renderer.run(cmd, aspect_ratio, target, *view.get(), flip_vertically); + const areau render_area = view->use_window_space ? display_area : aspect_ratio; + m_ui_renderer.run(cmd, render_area, target, *view.get(), flip_vertically); } } }; @@ -368,7 +373,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) std::vector sshot_frame(buffer_height * buffer_width * 4); glGetError(); - tex->copy_to(sshot_frame.data(), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings); + tex->copy_to(std::span(sshot_frame), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings); m_sshot_tex.reset(); @@ -475,7 +480,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) "Texture uploads: %11u (%u from CPU - %02u%%, %u copies avoided)\n" "Vertex cache hits: %9u/%u (%u%%)\n" "Program cache lookup ellision: %u/%u (%u%%)", - info.stats.framebuffer_stats.to_string(!backend_config.supports_hw_msaa), + info.stats.framebuffer_stats.to_string(resolution_scaling_config, !backend_config.supports_hw_msaa), get_load(), info.stats.draw_calls, info.stats.setup_time, info.stats.vertex_upload_time, info.stats.textures_upload_time, info.stats.draw_exec_time, num_dirty_textures, texture_memory_size, num_flushes, num_misses, cache_miss_ratio, num_unavoidable, num_mispredict, num_speculate, @@ -512,6 +517,19 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) m_frame->flip(m_context); rsx::thread::flip(info); + // Data sync + const rsx::surface_scaling_config_t active_res_scaling_config = + { + .scale_percent = static_cast(g_cfg.video.resolution_scale_percent), + .min_scalable_dimension = static_cast(g_cfg.video.min_scalable_dimension), + }; + + if (active_res_scaling_config != this->resolution_scaling_config) + { + m_rtts.sync_scaling_config(cmd, active_res_scaling_config); + this->resolution_scaling_config = active_res_scaling_config; + } + // Cleanup m_gl_texture_cache.on_frame_end(); m_vertex_cache->purge(); diff --git a/rpcs3/Emu/RSX/GL/GLProcTable.h b/rpcs3/Emu/RSX/GL/GLProcTable.h index 8f42995a9d..ad8943bd4f 100644 --- a/rpcs3/Emu/RSX/GL/GLProcTable.h +++ b/rpcs3/Emu/RSX/GL/GLProcTable.h @@ -218,6 +218,9 @@ OPENGL_PROC(PFNGLNAMEDBUFFERDATAEXTPROC, NamedBufferDataEXT); OPENGL_PROC(PFNGLNAMEDBUFFERSUBDATAPROC, NamedBufferSubData); OPENGL_PROC(PFNGLNAMEDBUFFERSUBDATAEXTPROC, NamedBufferSubDataEXT); +OPENGL_PROC(PFNGLCLEARNAMEDBUFFERSUBDATAPROC, ClearNamedBufferSubData); +OPENGL_PROC(PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC, ClearNamedBufferSubDataEXT); + // ARB_shader_image_load_store OPENGL_PROC(PFNGLBINDIMAGETEXTUREPROC, BindImageTexture); @@ -256,6 +259,7 @@ OPENGL_PROC(PFNGLDELETESYNCPROC, DeleteSync); // KHR_debug OPENGL_PROC(PFNGLDEBUGMESSAGECALLBACKPROC, DebugMessageCallback); +OPENGL_PROC(PFNGLOBJECTLABELPROC, ObjectLabel); // Immutable textures OPENGL_PROC(PFNGLTEXSTORAGE1DPROC, TexStorage1D); diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp index 18058842aa..34c2ca72d3 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp @@ -141,7 +141,8 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool /* m_framebuffer_layout.width, m_framebuffer_layout.height, m_framebuffer_layout.target, m_framebuffer_layout.aa_mode, m_framebuffer_layout.raster_type, m_framebuffer_layout.color_addresses, m_framebuffer_layout.zeta_address, - m_framebuffer_layout.actual_color_pitch, m_framebuffer_layout.actual_zeta_pitch); + m_framebuffer_layout.actual_color_pitch, m_framebuffer_layout.actual_zeta_pitch, + resolution_scaling_config); std::array color_targets; GLuint depth_stencil_target; @@ -448,7 +449,7 @@ void gl::render_target::load_memory(gl::command_context& cmd) subres.data = { vm::get_super_ptr(base_addr), static_cast::size_type>(rsx_pitch * surface_height * samples_y) }; // TODO: MSAA support - if (g_cfg.video.resolution_scale_percent == 100 && spp == 1) [[likely]] + if (resolution_scaling_config.scale_percent == 100 && spp == 1) [[likely]] { gl::upload_texture(cmd, this, get_gcm_format(), is_swizzled, { subres }); } @@ -689,6 +690,8 @@ gl::viewable_image* gl::render_target::get_resolve_target_safe(gl::command_conte static_cast(get_internal_format()), format_class() )); + + resolve_surface->set_name(fmt::format("MSAA_Resolve_%u@0x%x", resolve_surface->id(), base_addr)); } return static_cast(resolve_surface.get()); diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.h b/rpcs3/Emu/RSX/GL/GLRenderTargets.h index 9b01746a87..f5e2252b92 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.h +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.h @@ -98,7 +98,7 @@ namespace gl bool matches_dimensions(u16 _width, u16 _height) const { //Use forward scaling to account for rounding and clamping errors - const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(_width, _height); + const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(resolution_scaling_config, _width, _height); return (scaled_w == width()) && (scaled_h == height()); } @@ -138,11 +138,12 @@ struct gl_render_target_traits u32 address, rsx::surface_color_format surface_color_format, usz width, usz height, usz pitch, - rsx::surface_antialiasing antialias + rsx::surface_antialiasing antialias, + const rsx::surface_scaling_config_t& resolution_scaling_config ) { auto format = rsx::internals::surface_color_format_to_gl(surface_color_format); - const auto [width_, height_] = rsx::apply_resolution_scale(static_cast(width), static_cast(height)); + const auto [width_, height_] = rsx::apply_resolution_scale(resolution_scaling_config, static_cast(width), static_cast(height)); u8 samples; rsx::surface_sample_layout sample_layout; @@ -160,7 +161,9 @@ struct gl_render_target_traits std::unique_ptr result(new gl::render_target(width_, height_, samples, static_cast(format.internal_format), RSX_FORMAT_CLASS_COLOR)); + result->set_name(fmt::format("RTV_%u@0x%x", result->id(), address)); result->set_aa_mode(antialias); + result->set_resolution_scaling_config(resolution_scaling_config); result->set_native_pitch(static_cast(width) * get_format_block_size_in_bytes(surface_color_format) * result->samples_x); result->set_surface_dimensions(static_cast(width), static_cast(height), static_cast(pitch)); result->set_format(surface_color_format); @@ -181,11 +184,12 @@ struct gl_render_target_traits u32 address, rsx::surface_depth_format2 surface_depth_format, usz width, usz height, usz pitch, - rsx::surface_antialiasing antialias + rsx::surface_antialiasing antialias, + const rsx::surface_scaling_config_t& resolution_scaling_config ) { auto format = rsx::internals::surface_depth_format_to_gl(surface_depth_format); - const auto [width_, height_] = rsx::apply_resolution_scale(static_cast(width), static_cast(height)); + const auto [width_, height_] = rsx::apply_resolution_scale(resolution_scaling_config, static_cast(width), static_cast(height)); u8 samples; rsx::surface_sample_layout sample_layout; @@ -203,7 +207,9 @@ struct gl_render_target_traits std::unique_ptr result(new gl::render_target(width_, height_, samples, static_cast(format.internal_format), rsx::classify_format(surface_depth_format))); + result->set_name(fmt::format("DSV_%u@0x%x", result->id(), address)); result->set_aa_mode(antialias); + result->set_resolution_scaling_config(resolution_scaling_config); result->set_surface_dimensions(static_cast(width), static_cast(height), static_cast(pitch)); result->set_format(surface_depth_format); result->set_native_pitch(static_cast(width) * get_format_block_size_in_bytes(surface_depth_format) * result->samples_x); @@ -223,13 +229,17 @@ struct gl_render_target_traits void clone_surface( gl::command_context& cmd, std::unique_ptr& sink, gl::render_target* ref, - u32 address, barrier_descriptor_t& prev) + u32 address, barrier_descriptor_t& prev, + const rsx::surface_scaling_config_t& scaling_config) { if (!sink) { auto internal_format = static_cast(ref->get_internal_format()); - const auto [new_w, new_h] = rsx::apply_resolution_scale(prev.width, prev.height, - ref->get_surface_width(), ref->get_surface_height()); + const auto [new_w, new_h] = rsx::apply_resolution_scale( + scaling_config, + prev.width, prev.height, + ref->get_surface_width(), + ref->get_surface_height()); sink = std::make_unique(new_w, new_h, ref->samples(), internal_format, ref->format_class()); sink->add_ref(); @@ -238,6 +248,10 @@ struct gl_render_target_traits sink->state_flags = rsx::surface_state_flags::erase_bkgnd; sink->format_info = ref->format_info; + sink->sample_layout = ref->sample_layout; + sink->resolution_scaling_config = scaling_config; + + sink->set_name(fmt::format("SINK_%u@0x%x", sink->id(), address)); sink->set_spp(ref->get_spp()); sink->set_native_pitch(static_cast(prev.width) * ref->get_bpp() * ref->samples_x); sink->set_rsx_pitch(ref->get_rsx_pitch()); @@ -325,6 +339,7 @@ struct gl_render_target_traits std::array native_layout = { static_cast(fmt.swizzle.a), static_cast(fmt.swizzle.r), static_cast(fmt.swizzle.g), static_cast(fmt.swizzle.b) }; surface->set_native_component_layout(native_layout); surface->set_format(format); + surface->set_name(fmt::format("RTV_%u@0x%x", surface->id(), address)); int_invalidate_surface_contents(cmd, surface, address, pitch); } @@ -338,6 +353,7 @@ struct gl_render_target_traits usz pitch) { surface->set_format(format); + surface->set_name(fmt::format("DSV_%u@0x%x", surface->id(), address)); int_invalidate_surface_contents(cmd, surface, address, pitch); } @@ -370,6 +386,7 @@ struct gl_render_target_traits gl::texture::internal_format format, usz width, usz height, rsx::surface_antialiasing antialias, + const rsx::surface_scaling_config_t& scaling_config, bool check_refs = false) { if (check_refs && surface->has_refs()) @@ -377,7 +394,8 @@ struct gl_render_target_traits return surface->get_internal_format() == format && surface->get_spp() == get_format_sample_count(antialias) && - surface->matches_dimensions(static_cast(width), static_cast(height)); + surface->matches_dimensions(static_cast(width), static_cast(height)) && + surface->resolution_scaling_config == scaling_config; } static @@ -386,10 +404,11 @@ struct gl_render_target_traits rsx::surface_color_format format, usz width, usz height, rsx::surface_antialiasing antialias, + const rsx::surface_scaling_config_t& scaling_config, bool check_refs=false) { const auto internal_fmt = rsx::internals::surface_color_format_to_gl(format).internal_format; - return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, check_refs); + return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, scaling_config, check_refs); } static @@ -398,10 +417,11 @@ struct gl_render_target_traits rsx::surface_depth_format2 format, usz width, usz height, rsx::surface_antialiasing antialias, + const rsx::surface_scaling_config_t& scaling_config, bool check_refs = false) { const auto internal_fmt = rsx::internals::surface_depth_format_to_gl(format).internal_format; - return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, check_refs); + return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, scaling_config, check_refs); } static diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index c7f9ec2622..fa5b3627c4 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -300,6 +300,7 @@ namespace gl } builder << "\n" + "#undef TEX_PARAM\n" "#define TEX_PARAM(index) texture_parameters[index + texture_base_index]\n" "#define IS_TEXTURE_RESIDENT(index) (texture_handles[index] < 0xFF)\n" "#define SAMPLER1D(index) sampler1D_array[texture_handles[index]]\n" diff --git a/rpcs3/Emu/RSX/GL/GLTexture.cpp b/rpcs3/Emu/RSX/GL/GLTexture.cpp index 181e5058c8..6f566b9a82 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.cpp +++ b/rpcs3/Emu/RSX/GL/GLTexture.cpp @@ -22,6 +22,13 @@ namespace gl legacy_ring_buffer g_upload_transfer_buffer; scratch_ring_buffer g_compute_decode_buffer; scratch_ring_buffer g_deswizzle_scratch_buffer; + blitter g_blitter; + + void init_global_texture_resources() + { + g_blitter.init(); + g_hw_blitter = &g_blitter; + } void destroy_global_texture_resources() { @@ -29,6 +36,8 @@ namespace gl g_upload_transfer_buffer.remove(); g_compute_decode_buffer.remove(); g_deswizzle_scratch_buffer.remove(); + g_blitter.destroy(); + g_hw_blitter = nullptr; } template @@ -157,42 +166,42 @@ namespace gl case texture::internal_format::compressed_rgba_s3tc_dxt1: case texture::internal_format::compressed_rgba_s3tc_dxt3: case texture::internal_format::compressed_rgba_s3tc_dxt5: - return { GL_RGBA, GL_UNSIGNED_BYTE, 1, false }; + return { .format = GL_RGBA, .type = GL_UNSIGNED_BYTE, .block_size = 1, .swap_bytes = false }; case texture::internal_format::r8: - return { GL_RED, GL_UNSIGNED_BYTE, 1, false }; + return { .format = GL_RED, .type = GL_UNSIGNED_BYTE, .block_size = 1, .swap_bytes = false }; case texture::internal_format::r16: - return { GL_RED, GL_UNSIGNED_SHORT, 2, true }; + return { .format = GL_RED, .type = GL_UNSIGNED_SHORT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::r32f: - return { GL_RED, GL_FLOAT, 4, true }; + return { .format = GL_RED, .type = GL_FLOAT, .block_size = 4, .swap_bytes = true }; case texture::internal_format::rg8: - return { GL_RG, GL_UNSIGNED_SHORT, 2, true }; + return { .format = GL_RG, .type = GL_UNSIGNED_SHORT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::rg16: - return { GL_RG, GL_UNSIGNED_SHORT, 2, true }; + return { .format = GL_RG, .type = GL_UNSIGNED_SHORT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::rg16f: - return { GL_RG, GL_HALF_FLOAT, 2, true }; + return { .format = GL_RG, .type = GL_HALF_FLOAT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::rgb565: - return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2, true }; + return { .format = GL_RGB, .type = GL_UNSIGNED_SHORT_5_6_5, .block_size = 2, .swap_bytes = true }; case texture::internal_format::rgb5a1: - return { GL_RGB, GL_UNSIGNED_SHORT_5_5_5_1, 2, true }; + return { .format = GL_RGB, .type = GL_UNSIGNED_SHORT_5_5_5_1, .block_size = 2, .swap_bytes = true }; case texture::internal_format::bgr5a1: - return { GL_RGB, GL_UNSIGNED_SHORT_1_5_5_5_REV, 2, true }; + return { .format = GL_RGB, .type = GL_UNSIGNED_SHORT_1_5_5_5_REV, .block_size = 2, .swap_bytes = true }; case texture::internal_format::rgba4: - return { GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4, 2, false }; + return { .format = GL_BGRA, .type = GL_UNSIGNED_SHORT_4_4_4_4, .block_size = 2, .swap_bytes = false }; case texture::internal_format::rgba8: - return { GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, true }; + return { .format = GL_RGBA, .type = GL_UNSIGNED_INT_8_8_8_8_REV, .block_size = 4, .swap_bytes = true }; case texture::internal_format::bgra8: - return { GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, true }; + return { .format = GL_BGRA, .type = GL_UNSIGNED_INT_8_8_8_8_REV, .block_size = 4, .swap_bytes = true }; case texture::internal_format::rgba16f: - return { GL_RGBA, GL_HALF_FLOAT, 2, true }; + return { .format = GL_RGBA, .type = GL_HALF_FLOAT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::rgba32f: - return { GL_RGBA, GL_FLOAT, 4, true }; + return { .format = GL_RGBA, .type = GL_FLOAT, .block_size = 4, .swap_bytes = true }; case texture::internal_format::depth16: - return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 2, true }; + return { .format = GL_DEPTH_COMPONENT, .type = GL_UNSIGNED_SHORT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::depth32f: - return { GL_DEPTH_COMPONENT, GL_FLOAT, 2, true }; + return { .format = GL_DEPTH_COMPONENT, .type = GL_FLOAT, .block_size = 2, .swap_bytes = true }; case texture::internal_format::depth24_stencil8: case texture::internal_format::depth32f_stencil8: - return { GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 4, true }; + return { .format = GL_DEPTH_STENCIL, .type = GL_UNSIGNED_INT_24_8, .block_size = 4, .swap_bytes = true }; default: fmt::throw_exception("Unexpected internal format 0x%X", static_cast(format)); } @@ -311,7 +320,7 @@ namespace gl return nullptr; } - switch (pack_info.size) + switch (pack_info.block_size) { case 1: return nullptr; @@ -363,8 +372,10 @@ namespace gl } } - dst->bind(buffer::target::pixel_pack); - src->copy_to(reinterpret_cast(static_cast(dst_offset)), static_cast(pack_info.format), static_cast(pack_info.type), src_level, src_region, {}); + pixel_pack_settings pack_settings{}; + if (pack_info.alignment) pack_settings.alignment(pack_info.alignment); + if (pack_info.row_length) pack_settings.row_length(pack_info.row_length); + src->copy_to(*dst, dst_offset, static_cast(pack_info.format), static_cast(pack_info.type), src_level, src_region, pack_settings); return false; }; @@ -611,10 +622,12 @@ namespace gl } glBindBuffer(GL_SHADER_STORAGE_BUFFER, GL_NONE); - transfer_buf->bind(buffer::target::pixel_unpack); - dst->copy_from(reinterpret_cast(u64(out_offset)), static_cast(unpack_info.format), - static_cast(unpack_info.type), dst_level, dst_region, {}); + pixel_unpack_settings unpack_settings{}; + if (unpack_info.alignment) unpack_settings.alignment(unpack_info.alignment); + if (unpack_info.format) unpack_settings.row_length(unpack_info.row_length); + dst->copy_from(*transfer_buf, out_offset, static_cast(unpack_info.format), + static_cast(unpack_info.type), dst_level, dst_region, unpack_settings); } } @@ -704,172 +717,168 @@ namespace gl } } } + + return; } - else + + std::pair upload_scratch_mem = {}, compute_scratch_mem = {}; + image_memory_requirements mem_info; + pixel_buffer_layout mem_layout; + + std::span dst_buffer = staging_buffer; + u8 block_size_in_bytes = rsx::get_format_block_size_in_bytes(format); + u64 image_linear_size = staging_buffer.size(); + + const auto min_required_buffer_size = std::max(utils::align(image_linear_size * 4, 0x100000), 16 * 0x100000); + + if (driver_caps.ARB_compute_shader_supported) { - std::pair upload_scratch_mem = {}, compute_scratch_mem = {}; - image_memory_requirements mem_info; - pixel_buffer_layout mem_layout; - - std::span dst_buffer = staging_buffer; - void* out_pointer = staging_buffer.data(); - u8 block_size_in_bytes = rsx::get_format_block_size_in_bytes(format); - u64 image_linear_size = staging_buffer.size(); - - const auto min_required_buffer_size = std::max(utils::align(image_linear_size * 4, 0x100000), 16 * 0x100000); - - if (driver_caps.ARB_compute_shader_supported) + if (g_upload_transfer_buffer.size() < static_cast(min_required_buffer_size)) { - if (g_upload_transfer_buffer.size() < static_cast(min_required_buffer_size)) - { - g_upload_transfer_buffer.remove(); - g_upload_transfer_buffer.create(gl::buffer::target::pixel_unpack, min_required_buffer_size); - } - - if (g_compute_decode_buffer.size() < min_required_buffer_size) - { - g_compute_decode_buffer.remove(); - g_compute_decode_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size); - } - - out_pointer = nullptr; + g_upload_transfer_buffer.remove(); + g_upload_transfer_buffer.create(gl::buffer::target::pixel_unpack, min_required_buffer_size); } - for (const rsx::subresource_layout& layout : input_layouts) + if (g_compute_decode_buffer.size() < min_required_buffer_size) { - if (driver_caps.ARB_compute_shader_supported) + g_compute_decode_buffer.remove(); + g_compute_decode_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size); + } + } + + for (const rsx::subresource_layout& layout : input_layouts) + { + if (driver_caps.ARB_compute_shader_supported) + { + u64 row_pitch = rsx::align2(layout.width_in_block * block_size_in_bytes, caps.alignment); + + // We're in the "else" branch, so "is_compressed_host_format()" is always false. + // Handle emulated compressed formats with host unpack (R8G8 compressed) + row_pitch = std::max(row_pitch, dst->pitch()); + + // FIXME: Double-check this logic; it seems like we should always use texels both here and for row_pitch. + image_linear_size = row_pitch * layout.height_in_texel * layout.depth; + + compute_scratch_mem = { nullptr, g_compute_decode_buffer.alloc(static_cast(image_linear_size), 256) }; + compute_scratch_mem.first = reinterpret_cast(static_cast(compute_scratch_mem.second)); + + g_upload_transfer_buffer.reserve_storage_on_heap(static_cast(image_linear_size)); + upload_scratch_mem = g_upload_transfer_buffer.alloc_from_heap(static_cast(image_linear_size), 256); + dst_buffer = { reinterpret_cast(upload_scratch_mem.first), image_linear_size }; + } + + rsx::io_buffer io_buf = dst_buffer; + caps.supports_hw_deswizzle = (is_swizzled && driver_caps.ARB_compute_shader_supported && image_linear_size > 1024); + auto op = upload_texture_subresource(io_buf, layout, format, is_swizzled, caps); + + // Define upload region + coord3u region; + region.x = 0; + region.y = 0; + region.z = layout.layer; + region.width = layout.width_in_texel; + region.height = layout.height_in_texel; + region.depth = layout.depth; + + if (!driver_caps.ARB_compute_shader_supported) + { + unpack_settings.swap_bytes(op.require_swap); + dst->copy_from(staging_buffer, static_cast(gl_format), static_cast(gl_type), layout.level, region, unpack_settings); + continue; + } + + // 0. Preconf + mem_layout.alignment = static_cast(caps.alignment); + mem_layout.swap_bytes = op.require_swap; + mem_layout.format = gl_format; + mem_layout.type = gl_type; + mem_layout.block_size = block_size_in_bytes; + + // 2. Upload memory to GPU + if (!op.require_deswizzle) + { + g_upload_transfer_buffer.unmap(); + g_upload_transfer_buffer.copy_to(&g_compute_decode_buffer.get(), upload_scratch_mem.second, compute_scratch_mem.second, image_linear_size); + } + else + { + // 2.1 Copy data to deswizzle buf + if (g_deswizzle_scratch_buffer.size() < min_required_buffer_size) { - u64 row_pitch = rsx::align2(layout.width_in_block * block_size_in_bytes, caps.alignment); - - // We're in the "else" branch, so "is_compressed_host_format()" is always false. - // Handle emulated compressed formats with host unpack (R8G8 compressed) - row_pitch = std::max(row_pitch, dst->pitch()); - - // FIXME: Double-check this logic; it seems like we should always use texels both here and for row_pitch. - image_linear_size = row_pitch * layout.height_in_texel * layout.depth; - - compute_scratch_mem = { nullptr, g_compute_decode_buffer.alloc(static_cast(image_linear_size), 256) }; - compute_scratch_mem.first = reinterpret_cast(static_cast(compute_scratch_mem.second)); - - g_upload_transfer_buffer.reserve_storage_on_heap(static_cast(image_linear_size)); - upload_scratch_mem = g_upload_transfer_buffer.alloc_from_heap(static_cast(image_linear_size), 256); - dst_buffer = { reinterpret_cast(upload_scratch_mem.first), image_linear_size }; + g_deswizzle_scratch_buffer.remove(); + g_deswizzle_scratch_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size); } - rsx::io_buffer io_buf = dst_buffer; - caps.supports_hw_deswizzle = (is_swizzled && driver_caps.ARB_compute_shader_supported && image_linear_size > 1024); - auto op = upload_texture_subresource(io_buf, layout, format, is_swizzled, caps); + u32 deswizzle_data_offset = g_deswizzle_scratch_buffer.alloc(static_cast(image_linear_size), 256); + g_upload_transfer_buffer.unmap(); + g_upload_transfer_buffer.copy_to(&g_deswizzle_scratch_buffer.get(), upload_scratch_mem.second, deswizzle_data_offset, static_cast(image_linear_size)); - // Define upload region - coord3u region; - region.x = 0; - region.y = 0; - region.z = layout.layer; - region.width = layout.width_in_texel; - region.height = layout.height_in_texel; - region.depth = layout.depth; + // 2.2 Apply compute transform to deswizzle input and dump it in compute_scratch_mem + const auto block_size = op.element_size * op.block_length; - if (driver_caps.ARB_compute_shader_supported) + if (op.require_swap) { - // 0. Preconf - mem_layout.alignment = static_cast(caps.alignment); - mem_layout.swap_bytes = op.require_swap; - mem_layout.format = gl_format; - mem_layout.type = gl_type; - mem_layout.size = block_size_in_bytes; + mem_layout.swap_bytes = false; - // 2. Upload memory to GPU - if (!op.require_deswizzle) + switch (op.element_size) { - g_upload_transfer_buffer.unmap(); - g_upload_transfer_buffer.copy_to(&g_compute_decode_buffer.get(), upload_scratch_mem.second, compute_scratch_mem.second, image_linear_size); + case 1: + do_deswizzle_transformation(cmd, block_size, + &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, + static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); + break; + case 2: + do_deswizzle_transformation(cmd, block_size, + &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, + static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); + break; + case 4: + do_deswizzle_transformation(cmd, block_size, + &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, + static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); + break; + default: + fmt::throw_exception("Unimplemented element size deswizzle"); } - else - { - // 2.1 Copy data to deswizzle buf - if (g_deswizzle_scratch_buffer.size() < min_required_buffer_size) - { - g_deswizzle_scratch_buffer.remove(); - g_deswizzle_scratch_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size); - } - - u32 deswizzle_data_offset = g_deswizzle_scratch_buffer.alloc(static_cast(image_linear_size), 256); - g_upload_transfer_buffer.unmap(); - g_upload_transfer_buffer.copy_to(&g_deswizzle_scratch_buffer.get(), upload_scratch_mem.second, deswizzle_data_offset, static_cast(image_linear_size)); - - // 2.2 Apply compute transform to deswizzle input and dump it in compute_scratch_mem - const auto block_size = op.element_size * op.block_length; - - if (op.require_swap) - { - mem_layout.swap_bytes = false; - - switch (op.element_size) - { - case 1: - do_deswizzle_transformation(cmd, block_size, - &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, - static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); - break; - case 2: - do_deswizzle_transformation(cmd, block_size, - &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, - static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); - break; - case 4: - do_deswizzle_transformation(cmd, block_size, - &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, - static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); - break; - default: - fmt::throw_exception("Unimplemented element size deswizzle"); - } - } - else - { - switch (op.element_size) - { - case 1: - do_deswizzle_transformation(cmd, block_size, - &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, - static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); - break; - case 2: - do_deswizzle_transformation(cmd, block_size, - &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, - static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); - break; - case 4: - do_deswizzle_transformation(cmd, block_size, - &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, - static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); - break; - default: - fmt::throw_exception("Unimplemented element size deswizzle"); - } - } - - // Barrier - g_deswizzle_scratch_buffer.push_barrier(deswizzle_data_offset, static_cast(image_linear_size)); - } - - // 3. Update configuration - mem_info.image_size_in_texels = image_linear_size / block_size_in_bytes; - mem_info.image_size_in_bytes = image_linear_size; - mem_info.memory_required = 0; - - // 4. Dispatch compute routines - copy_buffer_to_image(cmd, mem_layout, &g_compute_decode_buffer.get(), dst, compute_scratch_mem.first, layout.level, region, &mem_info); - - // Barrier - g_compute_decode_buffer.push_barrier(compute_scratch_mem.second, static_cast(image_linear_size)); } else { - unpack_settings.swap_bytes(op.require_swap); - dst->copy_from(out_pointer, static_cast(gl_format), static_cast(gl_type), layout.level, region, unpack_settings); + switch (op.element_size) + { + case 1: + do_deswizzle_transformation(cmd, block_size, + &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, + static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); + break; + case 2: + do_deswizzle_transformation(cmd, block_size, + &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, + static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); + break; + case 4: + do_deswizzle_transformation(cmd, block_size, + &g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset, + static_cast(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth); + break; + default: + fmt::throw_exception("Unimplemented element size deswizzle"); + } } + + // Barrier + g_deswizzle_scratch_buffer.push_barrier(deswizzle_data_offset, static_cast(image_linear_size)); } + + // 3. Update configuration + mem_info.image_size_in_texels = image_linear_size / block_size_in_bytes; + mem_info.image_size_in_bytes = image_linear_size; + mem_info.memory_required = 0; + + // 4. Dispatch compute routines + copy_buffer_to_image(cmd, mem_layout, &g_compute_decode_buffer.get(), dst, compute_scratch_mem.first, layout.level, region, &mem_info); + + // Barrier + g_compute_decode_buffer.push_barrier(compute_scratch_mem.second, static_cast(image_linear_size)); } } @@ -1057,7 +1066,7 @@ namespace gl skip_transform = (pack_info.format == unpack_info.format && pack_info.type == unpack_info.type && pack_info.swap_bytes == unpack_info.swap_bytes && - pack_info.size == unpack_info.size); + pack_info.block_size == unpack_info.block_size); } if (skip_transform) [[likely]] @@ -1138,7 +1147,7 @@ namespace gl if (src->aspect() & image_aspect::depth) { // Source is depth, modify unpack rule - if (pack_info.size == 4 && unpack_info.size == 4) + if (pack_info.block_size == 4 && unpack_info.block_size == 4) { unpack_info.swap_bytes = !unpack_info.swap_bytes; } @@ -1146,7 +1155,7 @@ namespace gl else { // Dest is depth, modify pack rule - if (pack_info.size == 4 && unpack_info.size == 4) + if (pack_info.block_size == 4 && unpack_info.block_size == 4) { pack_info.swap_bytes = !pack_info.swap_bytes; } @@ -1156,9 +1165,7 @@ namespace gl // Start pack operation pixel_pack_settings pack_settings{}; pack_settings.swap_bytes(pack_info.swap_bytes); - - g_typeless_transfer_buffer.get().bind(buffer::target::pixel_pack); - src->copy_to(nullptr, static_cast(pack_info.format), static_cast(pack_info.type), 0, src_region, pack_settings); + src->copy_to(g_typeless_transfer_buffer.get(), 0, static_cast(pack_info.format), static_cast(pack_info.type), 0, src_region, pack_settings); glBindBuffer(GL_PIXEL_PACK_BUFFER, GL_NONE); @@ -1166,8 +1173,7 @@ namespace gl pixel_unpack_settings unpack_settings{}; unpack_settings.swap_bytes(unpack_info.swap_bytes); - g_typeless_transfer_buffer.get().bind(buffer::target::pixel_unpack); - dst->copy_from(nullptr, static_cast(unpack_info.format), static_cast(unpack_info.type), 0, dst_region, unpack_settings); + dst->copy_from(g_typeless_transfer_buffer.get(), 0, static_cast(unpack_info.format), static_cast(unpack_info.type), 0, dst_region, unpack_settings); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, GL_NONE); } } diff --git a/rpcs3/Emu/RSX/GL/GLTexture.h b/rpcs3/Emu/RSX/GL/GLTexture.h index 10c26dc536..6d0bbb18a3 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.h +++ b/rpcs3/Emu/RSX/GL/GLTexture.h @@ -14,18 +14,20 @@ namespace gl { struct pixel_buffer_layout { - GLenum format; - GLenum type; - u8 size; - bool swap_bytes; - u8 alignment; + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; + u32 row_length = 0; + u8 block_size = 0; + bool swap_bytes = false; + u8 alignment = 0; + u8 reserved; }; struct image_memory_requirements { - u64 image_size_in_texels; - u64 image_size_in_bytes; - u64 memory_required; + u64 image_size_in_texels = 0; + u64 image_size_in_bytes = 0; + u64 memory_required = 0; }; struct clear_cmd_info @@ -86,5 +88,6 @@ namespace gl extern std::unique_ptr g_vis_texture; } + void init_global_texture_resources(); void destroy_global_texture_resources(); } diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp index c2b0e3c252..f8aa9d95df 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp @@ -152,6 +152,8 @@ namespace gl dst = data.get(); dst->properties_encoding = match_key; m_temporary_surfaces.emplace_back(std::move(data)); + + dst->set_name(fmt::format("[Temp View] id=%u, fmt=0x%x", dst->id(), gcm_format)); } dst->add_ref(); @@ -179,6 +181,10 @@ namespace gl auto components = get_component_mapping(gcm_format, rsx::component_order::default_); dst->set_native_component_layout(components); } + else + { + dst->set_native_component_layout(src->get_native_component_layout()); + } return dst->get_view(remap); } diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index ae3b8e37e9..27b455374e 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -48,7 +48,7 @@ namespace gl void init_buffer(const gl::texture* src) { - const u32 vram_size = src->pitch() * src->height(); + const u32 vram_size = std::max(src->pitch() * src->height(), get_section_size()); const u32 buffer_size = utils::align(vram_size, 4096); if (pbo) @@ -148,7 +148,7 @@ namespace gl } } - void dma_transfer(gl::command_context& cmd, gl::texture* src, const areai& /*src_area*/, const utils::address_range32& /*valid_range*/, u32 pitch) + void dma_transfer(gl::command_context& cmd, gl::texture* src, const areai& src_area, const utils::address_range32& valid_range, u32 pitch) { init_buffer(src); glGetError(); @@ -165,6 +165,20 @@ namespace gl real_pitch = src->pitch(); rsx_pitch = pitch; + const coord3u src_rgn = + { + { static_cast(src_area.x1), static_cast(src_area.y1), 0 }, + { static_cast(src_area.width()), static_cast(src_area.height()), 1 } + }; + + u32 pbo_offset = 0; + if (valid_range.valid()) + { + const u32 section_base = get_section_base(); + pbo_offset = valid_range.start - section_base; + ensure(valid_range.start >= section_base && pbo_offset <= pbo.size()); + } + bool use_driver_pixel_transform = true; if (get_driver_caps().ARB_compute_shader_supported) [[likely]] { @@ -180,11 +194,12 @@ namespace gl pack_info.format = static_cast(format); pack_info.type = static_cast(type); - pack_info.size = (src->aspect() & image_aspect::stencil) ? 4 : 2; + pack_info.block_size = (src->aspect() & image_aspect::stencil) ? 4 : 2; pack_info.swap_bytes = true; + pack_info.row_length = rsx_pitch / pack_info.block_size; - mem_info.image_size_in_texels = src->width() * src->height(); - mem_info.image_size_in_bytes = src->pitch() * src->height(); + mem_info.image_size_in_texels = pack_info.row_length * src_area.height(); + mem_info.image_size_in_bytes = rsx_pitch * src_area.height(); mem_info.memory_required = 0; if (pack_info.type == GL_FLOAT_32_UNSIGNED_INT_24_8_REV) @@ -193,14 +208,16 @@ namespace gl mem_info.image_size_in_bytes *= 2; } - void* out_offset = copy_image_to_buffer(cmd, pack_info, src, &scratch_mem, 0, 0, { {}, src->size3D() }, &mem_info); + void* out_offset = copy_image_to_buffer(cmd, pack_info, src, &scratch_mem, 0, 0, src_rgn, &mem_info); + real_pitch = rsx_pitch; glBindBuffer(GL_SHADER_STORAGE_BUFFER, GL_NONE); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); - real_pitch = pack_info.size * src->width(); - const u64 data_length = pack_info.size * mem_info.image_size_in_texels; - scratch_mem.copy_to(&pbo, reinterpret_cast(out_offset), 0, data_length); + const u64 data_length = mem_info.image_size_in_bytes - rsx_pitch + (src_area.width() * pack_info.block_size); + ensure(data_length + pbo_offset <= static_cast(pbo.size()), "Memory allocation cannot fit image contents. Report to developers."); + + scratch_mem.copy_to(&pbo, reinterpret_cast(out_offset), pbo_offset, data_length); } else { @@ -219,13 +236,16 @@ namespace gl pack_unpack_swap_bytes = false; } - pbo.bind(buffer::target::pixel_pack); + const auto bpp = src->pitch() / src->width(); + real_pitch = rsx_pitch; + ensure((real_pitch % bpp) == 0); pixel_pack_settings pack_settings; pack_settings.alignment(1); pack_settings.swap_bytes(pack_unpack_swap_bytes); + pack_settings.row_length(rsx_pitch / bpp); - src->copy_to(nullptr, format, type, pack_settings); + src->copy_to(pbo, pbo_offset, format, type, 0, src_rgn, pack_settings); } if (auto error = glGetError()) @@ -266,6 +286,8 @@ namespace gl gl::texture* target_texture = vram_texture; u32 transfer_width = width; u32 transfer_height = height; + u32 transfer_x = 0, transfer_y = 0; + u16 resolution_scale_percent = 100; if (context == rsx::texture_upload_context::framebuffer_storage) { @@ -274,9 +296,10 @@ namespace gl target_texture = surface->get_surface(rsx::surface_access::transfer_read); transfer_width *= surface->samples_x; transfer_height *= surface->samples_y; + resolution_scale_percent = surface->resolution_scaling_config.scale_percent; } - if ((rsx::get_resolution_scale_percent() != 100 && context == rsx::texture_upload_context::framebuffer_storage) || + if ((resolution_scale_percent != 100 && context == rsx::texture_upload_context::framebuffer_storage) || (vram_texture->pitch() != rsx_pitch)) { areai src_area = { 0, 0, 0, 0 }; @@ -311,7 +334,35 @@ namespace gl } } - dma_transfer(cmd, target_texture, {}, {}, rsx_pitch); + const auto valid_range = get_confirmed_range(); + if (const auto section_range = get_section_range(); section_range != valid_range) + { + if (const auto offset = (valid_range.start - get_section_base())) + { + transfer_y = offset / rsx_pitch; + transfer_x = (offset % rsx_pitch) / rsx::get_format_block_size_in_bytes(gcm_format); + + ensure(transfer_width >= transfer_x); + ensure(transfer_height >= transfer_y); + transfer_width -= transfer_x; + transfer_height -= transfer_y; + } + + if (const auto tail = (section_range.end - valid_range.end)) + { + const auto row_count = tail / rsx_pitch; + + ensure(transfer_height >= row_count); + transfer_height -= row_count; + } + } + + areai src_area; + src_area.x1 = static_cast(transfer_x); + src_area.y1 = static_cast(transfer_y); + src_area.x2 = s32(transfer_x + transfer_width); + src_area.y2 = s32(transfer_y + transfer_height); + dma_transfer(cmd, target_texture, src_area, valid_range, rsx_pitch); } /** @@ -427,9 +478,7 @@ namespace gl using gl::viewable_image::viewable_image; }; - blitter m_hw_blitter; std::vector> m_temporary_surfaces; - const u32 max_cached_image_pool_size = 256; private: @@ -733,6 +782,7 @@ namespace gl gl::upload_texture(cmd, section->get_raw_texture(), gcm_format, input_swizzled, subresource_layout); + section->get_raw_texture()->set_name(fmt::format("Raw Texture @0x%x", rsx_range.start)); section->last_write_tag = rsx::get_shared_tag(); return section; } @@ -810,16 +860,11 @@ namespace gl using baseclass::texture_cache; void initialize() - { - m_hw_blitter.init(); - g_hw_blitter = &m_hw_blitter; - } + {} void destroy() override { clear(); - g_hw_blitter = nullptr; - m_hw_blitter.destroy(); } bool is_depth_texture(u32 rsx_address, u32 rsx_size) override @@ -865,7 +910,7 @@ namespace gl bool blit(gl::command_context& cmd, const rsx::blit_src_info& src, const rsx::blit_dst_info& dst, bool linear_interpolate, gl_render_targets& m_rtts) { - auto result = upload_scaled_image(src, dst, linear_interpolate, cmd, m_rtts, m_hw_blitter); + auto result = upload_scaled_image(src, dst, linear_interpolate, cmd, m_rtts, *g_hw_blitter); if (result.succeeded) { diff --git a/rpcs3/Emu/RSX/GL/glutils/blitter.cpp b/rpcs3/Emu/RSX/GL/glutils/blitter.cpp index 57998b761d..47c7d7b1ca 100644 --- a/rpcs3/Emu/RSX/GL/glutils/blitter.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/blitter.cpp @@ -8,6 +8,18 @@ namespace gl { blitter* g_hw_blitter = nullptr; + void blitter::init() + { + blit_src.create(); + blit_dst.create(); + } + + void blitter::destroy() + { + blit_dst.remove(); + blit_src.remove(); + } + void blitter::copy_image(gl::command_context&, const texture* src, const texture* dst, int src_level, int dst_level, const position3i& src_offset, const position3i& dst_offset, const size3i& size) const { ensure(src_level == 0); @@ -147,6 +159,9 @@ namespace gl gl::fbo::attachment dst_att{ blit_dst, static_cast(attachment) }; dst_att = *real_dst; + blit_src.check(); + blit_dst.check(); + blit_src.blit(blit_dst, src_rect, dst_rect, target, interp); // Release the attachments explicitly (not doing so causes glitches, e.g Journey Menu) diff --git a/rpcs3/Emu/RSX/GL/glutils/blitter.h b/rpcs3/Emu/RSX/GL/glutils/blitter.h index d7adc1dd14..d56754fcae 100644 --- a/rpcs3/Emu/RSX/GL/glutils/blitter.h +++ b/rpcs3/Emu/RSX/GL/glutils/blitter.h @@ -30,17 +30,9 @@ namespace gl public: - void init() - { - blit_src.create(); - blit_dst.create(); - } + void init(); - void destroy() - { - blit_dst.remove(); - blit_src.remove(); - } + void destroy(); void scale_image(gl::command_context& cmd, const texture* src, texture* dst, areai src_rect, areai dst_rect, bool linear_interpolation, const rsx::typeless_xfer& xfer_info); diff --git a/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp b/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp index 5c1f0d0447..f3849718d0 100644 --- a/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp @@ -131,6 +131,11 @@ namespace gl DSA_CALL2(NamedBufferSubData, m_id, offset, length, data); } + void buffer::fill(GLsizeiptr offset, GLsizeiptr length, GLuint pattern) + { + DSA_CALL2(ClearNamedBufferSubData, m_id, GL_R32UI, offset, length, GL_RED, GL_UNSIGNED_INT, &pattern); + } + GLubyte* buffer::map(GLsizeiptr offset, GLsizeiptr length, access access_) { ensure(m_memory_type == memory_type::host_visible); diff --git a/rpcs3/Emu/RSX/GL/glutils/buffer_object.h b/rpcs3/Emu/RSX/GL/glutils/buffer_object.h index dccb2a314e..28730bdd0d 100644 --- a/rpcs3/Emu/RSX/GL/glutils/buffer_object.h +++ b/rpcs3/Emu/RSX/GL/glutils/buffer_object.h @@ -15,7 +15,9 @@ namespace gl element_array = GL_ELEMENT_ARRAY_BUFFER, uniform = GL_UNIFORM_BUFFER, texture = GL_TEXTURE_BUFFER, - ssbo = GL_SHADER_STORAGE_BUFFER + ssbo = GL_SHADER_STORAGE_BUFFER, + copy_src = GL_COPY_READ_BUFFER, + copy_dst = GL_COPY_WRITE_BUFFER }; enum class access @@ -65,6 +67,8 @@ namespace gl case target::uniform: pname = GL_UNIFORM_BUFFER_BINDING; break; case target::texture: pname = GL_TEXTURE_BUFFER_BINDING; break; case target::ssbo: pname = GL_SHADER_STORAGE_BUFFER_BINDING; break; + case target::copy_src: pname = GL_COPY_READ_BUFFER_BINDING; break; + case target::copy_dst: pname = GL_COPY_WRITE_BUFFER_BINDING; break; default: fmt::throw_exception("Invalid binding state target (0x%x)", static_cast(target_)); } @@ -113,6 +117,7 @@ namespace gl void data(GLsizeiptr size, const void* data_ = nullptr, GLenum usage = GL_STREAM_DRAW); void sub_data(GLsizeiptr offset, GLsizeiptr length, const GLvoid* data); + void fill(GLsizeiptr offset, GLsizeiptr length, GLuint pattern); GLubyte* map(GLsizeiptr offset, GLsizeiptr length, access access_); void unmap(); diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index d6a51beb09..a6999d6f9c 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -2,6 +2,7 @@ #include "capabilities.h" #include "Utilities/StrUtil.h" +#include "Emu/system_config.h" #include @@ -43,6 +44,8 @@ namespace gl all_extensions.emplace(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); } + RENDERDOC_debug = !!g_cfg.video.renderdoc_compatiblity; + #define CHECK_EXTENSION_SUPPORT(extension_short_name)\ do {\ if (all_extensions.contains("GL_"#extension_short_name)) {\ diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.h b/rpcs3/Emu/RSX/GL/glutils/capabilities.h index 756250430b..cfe104ffbe 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.h +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.h @@ -23,6 +23,7 @@ namespace gl bool initialized = false; version_info glsl_version; + bool RENDERDOC_debug = false; bool EXT_direct_state_access_supported = false; bool EXT_depth_bounds_test_supported = false; bool AMD_pinned_memory_supported = false; diff --git a/rpcs3/Emu/RSX/GL/glutils/common.h b/rpcs3/Emu/RSX/GL/glutils/common.h index f99c3590a6..24e62a9189 100644 --- a/rpcs3/Emu/RSX/GL/glutils/common.h +++ b/rpcs3/Emu/RSX/GL/glutils/common.h @@ -76,10 +76,30 @@ namespace gl } }; - // Very useful util when capturing traces with RenderDoc - static inline void push_debug_label(const char* label) + template + struct named_object { - glInsertEventMarkerEXT(static_cast(strlen(label)), label); + protected: + GLuint m_id = GL_NONE; + std::string m_name = "Unnamed"; + + public: + void set_name(std::string_view name) + { + m_name = name.data(); + glObjectLabel(Ns, m_id, static_cast(name.length()), name.data()); + } + + std::string_view name() const + { + return m_name; + } + }; + + // Very useful util when capturing traces with RenderDoc + static inline void push_debug_label(std::string_view label) + { + glInsertEventMarkerEXT(static_cast(label.size()), label.data()); } // Checks if GL state is still valid diff --git a/rpcs3/Emu/RSX/GL/glutils/image.cpp b/rpcs3/Emu/RSX/GL/glutils/image.cpp index 7dbd7fc254..e2f02afdfa 100644 --- a/rpcs3/Emu/RSX/GL/glutils/image.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/image.cpp @@ -19,6 +19,54 @@ namespace gl } } + static const char* gl_type_to_str(texture::type type) + { + switch (type) + { + case texture::type::ubyte: return "GL_UNSIGNED_BYTE"; + case texture::type::ushort: return "GL_UNSIGNED_SHORT"; + case texture::type::uint: return "GL_UNSIGNED_INT"; + case texture::type::ubyte_3_3_2: return "GL_UNSIGNED_BYTE_3_3_2"; + case texture::type::ubyte_2_3_3_rev: return "GL_UNSIGNED_BYTE_2_3_3_REV"; + case texture::type::ushort_5_6_5: return "GL_UNSIGNED_SHORT_5_6_5"; + case texture::type::ushort_5_6_5_rev: return "GL_UNSIGNED_SHORT_5_6_5_REV"; + case texture::type::ushort_4_4_4_4: return "GL_UNSIGNED_SHORT_4_4_4_4"; + case texture::type::ushort_4_4_4_4_rev: return "GL_UNSIGNED_SHORT_4_4_4_4_REV"; + case texture::type::ushort_5_5_5_1: return "GL_UNSIGNED_SHORT_5_5_5_1"; + case texture::type::ushort_1_5_5_5_rev: return "GL_UNSIGNED_SHORT_1_5_5_5_REV"; + case texture::type::uint_8_8_8_8: return "GL_UNSIGNED_INT_8_8_8_8"; + case texture::type::uint_8_8_8_8_rev: return "GL_UNSIGNED_INT_8_8_8_8_REV"; + case texture::type::uint_10_10_10_2: return "GL_UNSIGNED_INT_10_10_10_2"; + case texture::type::uint_2_10_10_10_rev: return "GL_UNSIGNED_INT_2_10_10_10_REV"; + case texture::type::uint_24_8: return "GL_UNSIGNED_INT_24_8"; + case texture::type::float32_uint8: return "GL_FLOAT_32_UNSIGNED_INT_24_8_REV"; + case texture::type::sbyte: return "GL_BYTE"; + case texture::type::sshort: return "GL_SHORT"; + case texture::type::sint: return "GL_INT"; + case texture::type::f16: return "GL_HALF_FLOAT"; + case texture::type::f32: return "GL_FLOAT"; + case texture::type::f64: return "GL_DOUBLE"; + default: return "UNKNOWN"; + } + } + + static const char* gl_format_to_str(texture::format format) + { + switch (format) + { + case texture::format::r: return "GL_RED"; + case texture::format::rg: return "GL_RG"; + case texture::format::rgb: return "GL_RGB"; + case texture::format::rgba: return "GL_RGBA"; + case texture::format::bgr: return "GL_BGR"; + case texture::format::bgra: return "GL_BGRA"; + case texture::format::stencil: return "GL_STENCIL_INDEX"; + case texture::format::depth: return "GL_DEPTH_COMPONENT"; + case texture::format::depth_stencil: return "GL_DEPTH_STENCIL"; + default: return "UNKNOWN"; + } + } + texture::texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLubyte samples, GLenum sized_format, rsx::format_class format_class) { // Upgrade targets for MSAA @@ -175,7 +223,7 @@ namespace gl m_id = GL_NONE; } - void texture::copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings) + void texture::copy_from(const rsx::io_buffer& src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings) { ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures."); @@ -185,30 +233,30 @@ namespace gl { case GL_TEXTURE_1D: { - DSA_CALL(TextureSubImage1D, m_id, GL_TEXTURE_1D, level, region.x, region.width, static_cast(format), static_cast(type), src); + DSA_CALL(TextureSubImage1D, m_id, GL_TEXTURE_1D, level, region.x, region.width, static_cast(format), static_cast(type), src.data()); break; } case GL_TEXTURE_2D: { - DSA_CALL(TextureSubImage2D, m_id, GL_TEXTURE_2D, level, region.x, region.y, region.width, region.height, static_cast(format), static_cast(type), src); + DSA_CALL(TextureSubImage2D, m_id, GL_TEXTURE_2D, level, region.x, region.y, region.width, region.height, static_cast(format), static_cast(type), src.data()); break; } case GL_TEXTURE_3D: case GL_TEXTURE_2D_ARRAY: { - DSA_CALL(TextureSubImage3D, m_id, target_, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast(format), static_cast(type), src); + DSA_CALL(TextureSubImage3D, m_id, target_, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast(format), static_cast(type), src.data()); break; } case GL_TEXTURE_CUBE_MAP: { if (get_driver_caps().ARB_direct_state_access_supported) { - glTextureSubImage3D(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast(format), static_cast(type), src); + glTextureSubImage3D(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast(format), static_cast(type), src.data()); } else { rsx_log.warning("Cubemap upload via texture::copy_from is halfplemented!"); - auto ptr = static_cast(src); + auto ptr = static_cast(src.data()); const auto end = std::min(6u, region.z + region.depth); for (unsigned face = region.z; face < end; ++face) { @@ -221,40 +269,51 @@ namespace gl } } - void texture::copy_from(buffer& buf, u32 gl_format_type, u32 offset, u32 length) + void texture::copy_from(buffer& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings) { ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures."); - if (get_target() != target::textureBuffer) - fmt::throw_exception("OpenGL error: texture cannot copy from buffer"); + buf.bind(buffer::target::pixel_unpack); - DSA_CALL(TextureBufferRange, m_id, GL_TEXTURE_BUFFER, gl_format_type, buf.id(), offset, length); + const rsx::io_buffer src{ reinterpret_cast(static_cast(offset)), buf.size() - offset }; + copy_from(src, format, type, level, region, pixel_settings); } void texture::copy_from(buffer_view& view) { - copy_from(*view.value(), view.format(), view.offset(), view.range()); + if (get_target() != target::textureBuffer) + fmt::throw_exception("OpenGL error: texture cannot copy from buffer"); + + DSA_CALL(TextureBufferRange, m_id, GL_TEXTURE_BUFFER, view.format(), view.value()->id(), view.offset(), view.range()); } - void texture::copy_to(void* dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const + void texture::copy_to(const rsx::io_buffer& dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const { ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures."); pixel_settings.apply(); const auto& caps = get_driver_caps(); + if (caps.RENDERDOC_debug) + { + const auto msg = fmt::format("glGetTextureSubImage('[%u] %s', %u, %u, %u, %u, %u, %u, %u, %s, %s, %d, %p)", + m_id, m_name.c_str(), level, region.x, region.y, region.z, region.width, region.height, region.depth, + gl_format_to_str(format), gl_type_to_str(type), s32{ smax }, dst.data()); + push_debug_label(msg); + } + if (!region.x && !region.y && !region.z && region.width == m_width && region.height == m_height && region.depth == m_depth) { if (caps.ARB_direct_state_access_supported) - glGetTextureImage(m_id, level, static_cast(format), static_cast(type), s32{ smax }, dst); + glGetTextureImage(m_id, level, static_cast(format), static_cast(type), dst.size(), dst.data()); else - glGetTextureImageEXT(m_id, static_cast(m_target), level, static_cast(format), static_cast(type), dst); + glGetTextureImageEXT(m_id, static_cast(m_target), level, static_cast(format), static_cast(type), dst.data()); } else if (caps.ARB_direct_state_access_supported) { glGetTextureSubImage(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth, - static_cast(format), static_cast(type), s32{ smax }, dst); + static_cast(format), static_cast(type), s32{ smax }, dst.data()); } else { @@ -269,6 +328,16 @@ namespace gl } } + void texture::copy_to(buffer& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const + { + ensure(offset < buf.size(), "PBO write is out of range"); + + buf.bind(buffer::target::pixel_pack); + + const rsx::io_buffer dst{ reinterpret_cast(static_cast(offset)), buf.size() - offset }; + copy_to(dst, format, type, level, region, pixel_settings); + } + void texture_view::create(texture* data, GLenum target, GLenum sized_format, const subresource_range& range, const GLenum* argb_swizzle) { m_target = target; @@ -397,6 +466,8 @@ namespace gl auto view = std::make_unique(this, swizzle, aspect_flags); auto result = view.get(); views.emplace(key, std::move(view)); + + result->set_name(fmt::format("%s, remap=%x", name(), remap.encoded)); return result; } diff --git a/rpcs3/Emu/RSX/GL/glutils/image.h b/rpcs3/Emu/RSX/GL/glutils/image.h index 4112d833c7..bd974c226a 100644 --- a/rpcs3/Emu/RSX/GL/glutils/image.h +++ b/rpcs3/Emu/RSX/GL/glutils/image.h @@ -4,6 +4,7 @@ #include "Utilities/geometry.h" #include "Emu/RSX/Common/TextureUtils.h" +#include "Emu/RSX/Common/io_buffer.h" //using enum rsx::format_class; using namespace ::rsx::format_class_; @@ -58,7 +59,7 @@ namespace gl GLuint num_layers; }; - class texture + class texture : public named_object { friend class texture_view; @@ -180,7 +181,6 @@ namespace gl }; protected: - GLuint m_id = GL_NONE; GLuint m_width = 0; GLuint m_height = 0; GLuint m_depth = 0; @@ -321,32 +321,33 @@ namespace gl } // Data management - void copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings); + void copy_from(const rsx::io_buffer& src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings); - void copy_from(buffer& buf, u32 gl_format_type, u32 offset, u32 length); + void copy_from(buffer& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings); void copy_from(buffer_view& view); - void copy_to(void* dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const; + void copy_to(const rsx::io_buffer& dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const; + + void copy_to(buffer& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const; // Convenience wrappers - void copy_from(const void* src, texture::format format, texture::type type, const pixel_unpack_settings& pixel_settings) + void copy_from(const rsx::io_buffer& src, texture::format format, texture::type type, const pixel_unpack_settings& pixel_settings) { const coord3u region = { {}, size3D() }; copy_from(src, format, type, 0, region, pixel_settings); } - void copy_to(void* dst, texture::format format, texture::type type, const pixel_pack_settings& pixel_settings) const + void copy_to(const rsx::io_buffer& dst, texture::format format, texture::type type, const pixel_pack_settings& pixel_settings) const { const coord3u region = { {}, size3D() }; copy_to(dst, format, type, 0, region, pixel_settings); } }; - class texture_view + class texture_view : public named_object { protected: - GLuint m_id = GL_NONE; GLenum m_target = 0; GLenum m_format = 0; GLenum m_view_format = 0; @@ -462,6 +463,7 @@ namespace gl class viewable_image : public texture { + protected: std::unordered_map> views; public: diff --git a/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp b/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp index cfd7b1cc08..75e2d3f3db 100644 --- a/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp +++ b/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp @@ -152,7 +152,7 @@ namespace gl void rcas_pass::configure() { // 0 is actually the sharpest with 2 being the chosen limit. Each progressive unit 'halves' the sharpening intensity. - auto cas_attenuation = 2.f - (g_cfg.video.vk.rcas_sharpening_intensity / 50.f); + auto cas_attenuation = 2.f - (g_cfg.video.rcas_sharpening_intensity / 50.f); FsrRcasCon(&m_constants_buf[0], cas_attenuation); } } diff --git a/rpcs3/Emu/RSX/GSRender.cpp b/rpcs3/Emu/RSX/GSRender.cpp index e9e859952e..f09f4704da 100644 --- a/rpcs3/Emu/RSX/GSRender.cpp +++ b/rpcs3/Emu/RSX/GSRender.cpp @@ -13,6 +13,8 @@ GSRender::GSRender(utils::serial* ar) noexcept : rsx::thread(ar) { m_frame = nullptr; } + + m_vsync_mode = g_cfg.video.vsync; } GSRender::~GSRender() diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index d2a6fd9c5f..f597b5562b 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -23,6 +23,8 @@ protected: draw_context_t m_context = nullptr; bool m_continuous_mode = false; + vsync_mode m_vsync_mode{}; + public: ~GSRender() override; diff --git a/rpcs3/Emu/RSX/NV47/HW/nv0039.cpp b/rpcs3/Emu/RSX/NV47/HW/nv0039.cpp index fcd30c45f8..d929caa488 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv0039.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv0039.cpp @@ -14,7 +14,7 @@ namespace rsx namespace nv0039 { // Transfer with stride - inline void block2d_copy_with_stride(u8* dst, const u8* src, u32 width, u32 height, u32 src_pitch, u32 dst_pitch, u8 src_stride, u8 dst_stride) + inline void block2d_copy_with_stride(u8* dst, const u8* src, u32 width, u32 height, s32 src_pitch, s32 dst_pitch, u8 src_stride, u8 dst_stride) { for (u32 row = 0; row < height; ++row) { @@ -33,7 +33,7 @@ namespace rsx } } - inline void block2d_copy(u8* dst, const u8* src, u32 width, u32 height, u32 src_pitch, u32 dst_pitch) + inline void block2d_copy(u8* dst, const u8* src, u32 width, u32 height, s32 src_pitch, s32 dst_pitch) { for (u32 i = 0; i < height; ++i) { diff --git a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp index 17ee040f8c..3cc40efed1 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp @@ -306,7 +306,7 @@ namespace rsx REGS(ctx)->decode(reg, REGS(ctx)->latch); } - void set_aa_control(context* ctx, u32 reg, u32 arg) + void set_aa_control(context* ctx, u32 /*reg*/, u32 arg) { const auto latch = REGS(ctx)->latch; if (arg == latch) @@ -633,9 +633,17 @@ namespace rsx case 2: break; default: - rsx_log.error("Unknown render mode %d", mode); + { + struct logged_t + { + atomic_t logged_cause[256]{}; + }; + + const auto& is_error = ::at32(g_fxo->get().logged_cause, mode).try_inc(10); + (is_error ? rsx_log.error : rsx_log.trace)("Unknown render mode %d", mode); return; } + } const u32 offset = arg & 0xffffff; auto address_ptr = util::get_report_data_impl(ctx, offset); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_icons.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_icons.cpp new file mode 100644 index 0000000000..4ef9dbea9a --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_icons.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "overlay_home_icons.h" + +#include "Emu/RSX/Overlays/overlay_controls.h" + +#include + +namespace rsx::overlays::home_menu +{ + std::unordered_map> g_icons_cache; + std::mutex g_icons_cache_lock; + + static const char* fa_icon_to_filename(fa_icon icon) + { + switch (icon) + { + default: + case fa_icon::none: + return ""; + case fa_icon::home: + return "home.png"; + case fa_icon::settings: + return "settings.png"; + case fa_icon::back: + return "circle-left-solid.png"; + case fa_icon::floppy: + return "floppy-disk-solid.png"; + case fa_icon::maximize: + return "maximize-solid.png"; + case fa_icon::play: + return "play-button-arrowhead.png"; + case fa_icon::poweroff: + return "power-off-solid.png"; + case fa_icon::restart: + return "rotate-left-solid.png"; + case fa_icon::screenshot: + return "screenshot.png"; + case fa_icon::video_camera: + return "video-camera.png"; + case fa_icon::friends: + return "user-group-solid.png"; + case fa_icon::trophy: + return "trophy-solid.png"; + case fa_icon::audio: + return "headphones-solid.png"; + case fa_icon::video: + return "display-solid.png"; + case fa_icon::gamepad: + return "gamepad-solid.png"; + case fa_icon::settings_sliders: + return "sliders-solid.png"; + case fa_icon::settings_gauge: + return "gauge-solid.png"; + case fa_icon::bug: + return "bug-solid.png"; + } + } + + void load_icon(fa_icon icon) + { + const std::string image_path = fmt::format("home/32/%s", fa_icon_to_filename(icon)); + g_icons_cache[icon] = rsx::overlays::resource_config::load_icon(image_path); + } + + const image_info* get_icon(fa_icon icon) + { + if (icon == fa_icon::none) + { + return nullptr; + } + + std::lock_guard lock(g_icons_cache_lock); + + auto found = g_icons_cache.find(icon); + if (found != g_icons_cache.end()) + { + return found->second.get(); + } + + load_icon(icon); + return g_icons_cache.at(icon).get(); + } +} diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_icons.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_icons.h new file mode 100644 index 0000000000..4b05b852e1 --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_icons.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Emu/RSX/Overlays/overlays.h" +#include "Emu/RSX/Overlays/overlay_controls.h" + +namespace rsx::overlays::home_menu +{ + enum class fa_icon + { + none = 0, + home, + settings, + back, + floppy, + maximize, + play, + poweroff, + restart, + screenshot, + video_camera, + trophy, + friends, + audio, + video, + gamepad, + settings_sliders, + settings_gauge, + bug, + }; + + const image_info* get_icon(fa_icon icon); +} diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp index 497ce12031..1dadaa5e7d 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp @@ -21,7 +21,7 @@ namespace rsx m_allow_input_on_pause = true; m_dim_background.set_size(virtual_width, virtual_height); - m_dim_background.back_color.a = 0.5f; + m_dim_background.back_color.a = 0.85f; m_description.set_font("Arial", 20); m_description.set_pos(20, 37); @@ -56,6 +56,8 @@ namespace rsx m_time_display.auto_resize(); last_time = std::move(new_time); } + + m_main_menu.update(timestamp_us); } void home_menu_dialog::on_button_pressed(pad_button button_press, bool is_auto_repeat) @@ -88,6 +90,11 @@ namespace rsx std::string path = page->title; for (home_menu_page* parent = page->parent; parent; parent = parent->parent) { + if (parent->title.empty()) + { + break; + } + path = parent->title + " > " + path; } m_description.set_text(path); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp index d0ed9dc460..94e70f4c63 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp @@ -5,61 +5,106 @@ namespace rsx { namespace overlays { - home_menu_entry::home_menu_entry(const std::string& text) + home_menu_entry::home_menu_entry(home_menu::fa_icon icon, const std::string& text, u16 width, text_align alignment) { - std::unique_ptr text_stack = std::make_unique(); - std::unique_ptr padding = std::make_unique(); - std::unique_ptr title = std::make_unique @@ -2833,6 +2851,27 @@ Loader + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays\HomeMenu + + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays + diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index 135a61c491..5d9240254d 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -49,7 +49,7 @@ void headless_application::InitializeCallbacks() { EmuCallbacks callbacks = CreateCallbacks(); - callbacks.try_to_quit = [this](bool force_quit, std::function on_exit) -> bool + callbacks.try_to_quit = [](bool force_quit, std::function on_exit) -> bool { if (force_quit) { diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index ff3b70f952..b5d4401b7e 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -70,10 +70,15 @@ void main_application::InitializeEmulator(const std::string& user, bool show_gui const std::string firmware_version = utils::get_firmware_version(); const std::string firmware_string = firmware_version.empty() ? "Missing Firmware" : ("Firmware version: " + firmware_version); sys_log.always()("%s", firmware_string); + + rpcs3::utils::configure_logs(Emu.IsStopped()); } void main_application::OnEmuSettingsChange() { + // Change logging + rpcs3::utils::configure_logs(Emu.IsStopped()); + if (Emu.IsRunning()) { enable_display_sleep(!g_cfg.misc.prevent_display_sleep); @@ -81,9 +86,6 @@ void main_application::OnEmuSettingsChange() if (!Emu.IsStopped()) { - // Change logging (only allowed during gameplay) - rpcs3::utils::configure_logs(); - // Force audio provider g_cfg.audio.provider.set(Emu.IsVsh() ? audio_provider::rsxaudio : audio_provider::cell_audio); } diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 6c2130eca6..011dfbe39f 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -68,7 +68,9 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti #include "util/media_utils.h" #include "rpcs3_version.h" #include "Emu/System.h" +#include "Emu/system_config.h" #include "Emu/system_utils.hpp" +#include "Emu/RSX/Overlays/overlay_message.h" #include #include @@ -131,7 +133,7 @@ std::set get_one_drive_paths() do { path_buffer.resize(path_buffer.size() + MAX_PATH); - DWORD buffer_size = static_cast(path_buffer.size() - 1); + DWORD buffer_size = static_cast((path_buffer.size() - 1) * sizeof(wchar_t)); status = RegQueryValueExW(hkey, L"UserFolder", NULL, &type, reinterpret_cast(path_buffer.data()), &buffer_size); } while (status == ERROR_MORE_DATA); @@ -197,6 +199,10 @@ std::set get_one_drive_paths() fmt::append(buf, "\nBuild: \"%s\"", rpcs3::get_verbose_version()); fmt::append(buf, "\nDate: \"%s\"", std::chrono::system_clock::now()); + + const auto [total, current] = utils::get_memory_usage(); + + fmt::append(buf, "\nRAM Usage: %dMB/%dMB (%dMB free)", current / (1024 * 1024), total / (1024 * 1024), (total - current) / (1024 * 1024)); } std::string_view text = s_is_error_launch ? _text : buf; @@ -308,7 +314,8 @@ public: { if (msg == logs::level::fatal || (msg == logs::level::always && m_log_always)) { - std::string _msg = "RPCS3: "; + static const std::string rpcs3_prefix = "RPCS3: "; + std::string _msg = rpcs3_prefix; if (!prefix.empty()) { @@ -347,6 +354,13 @@ public: #endif if (msg == logs::level::fatal) { + if (g_cfg.misc.show_fatal_error_hints) + { + std::string overlay_msg = "Fatal error: " + _msg.substr(rpcs3_prefix.size()); + fmt::trim_back(overlay_msg, " \t\n"); + rsx::overlays::queue_message(overlay_msg, umax); + } + // Pause emulation if fatal error encountered Emu.Pause(true); } diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 9749f60fcd..d25ca3905c 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -386,6 +386,9 @@ true + + true + true @@ -689,6 +692,9 @@ true + + true + true @@ -826,6 +832,7 @@ + @@ -867,6 +874,7 @@ + @@ -890,6 +898,7 @@ + @@ -1155,6 +1164,7 @@ + @@ -1578,6 +1588,7 @@ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + @@ -1621,6 +1632,16 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + @@ -1717,6 +1738,7 @@ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL3 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) @@ -2192,6 +2214,16 @@ .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) Uic%27ing %(Identity)... diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index a011ddf62e..28af783415 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -291,6 +291,12 @@ Generated Files\Release + + Generated Files\Debug + + + Generated Files\Release + Generated Files\Debug @@ -441,6 +447,9 @@ Gui\settings + + Gui\settings + Gui\log @@ -1275,6 +1284,12 @@ Io + + Gui\settings + + + Gui\utils + @@ -1298,6 +1313,9 @@ Generated Files + + Generated Files + Generated Files @@ -1517,6 +1535,12 @@ Io + + Gui\settings + + + Gui\utils + @@ -1543,6 +1567,9 @@ Form Files + + Form Files + Form Files @@ -1585,6 +1612,9 @@ Gui\settings + + Gui\settings + Gui\log diff --git a/rpcs3/rpcs3_version.cpp b/rpcs3/rpcs3_version.cpp index 19d107e582..6eb904222f 100644 --- a/rpcs3/rpcs3_version.cpp +++ b/rpcs3/rpcs3_version.cpp @@ -26,7 +26,7 @@ namespace rpcs3 const utils::version& get_version() { - static constexpr utils::version version{ 0, 0, 39, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; + static constexpr utils::version version{ 0, 0, 40, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; return version; } diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index b59d6f7a11..b0a75d9bfa 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -53,6 +53,7 @@ add_library(rpcs3_ui STATIC localized.cpp localized_emu.cpp log_frame.cpp + log_level_dialog.cpp log_viewer.cpp main_window.cpp memory_string_searcher.cpp @@ -82,6 +83,7 @@ add_library(rpcs3_ui STATIC qt_video_source.cpp raw_mouse_settings_dialog.cpp register_editor_dialog.cpp + recording_settings_dialog.cpp recvmessage_dialog_frame.cpp render_creator.cpp rpcn_settings_dialog.cpp @@ -103,6 +105,7 @@ add_library(rpcs3_ui STATIC shortcut_settings.cpp skylander_dialog.cpp sound_effect_manager_dialog.cpp + steam_utils.cpp syntax_highlighter.cpp system_cmd_dialog.cpp table_item_delegate.cpp @@ -133,6 +136,7 @@ add_library(rpcs3_ui STATIC patch_creator_dialog.ui patch_manager_dialog.ui ps_move_tracker_dialog.ui + recording_settings_dialog.ui settings_dialog.ui shortcut_dialog.ui welcome_dialog.ui diff --git a/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp b/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp index 26cbfc96b7..4eaadb9e7e 100644 --- a/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp @@ -181,7 +181,7 @@ void auto_pause_settings_dialog::ShowContextMenu(const QPoint &pos) OnEntryConfig(idx, true); }); connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove); - connect(config, &QAction::triggered, this, [=, this]() {OnEntryConfig(row, false); }); + connect(config, &QAction::triggered, this, [=]() {OnEntryConfig(row, false); }); myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos)); } diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index 9745d11294..0b26b791ee 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -66,7 +66,7 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) g_cfg_clans.save(); }); - connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [this](int index) + connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [](int index) { if (index < 0) return; diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 7ffe1b32a2..2184974d85 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -16,6 +16,7 @@ #include "Emu/IdManager.h" #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/RSXDisAsm.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Cell/PPUAnalyser.h" #include "Emu/Cell/PPUDisAsm.h" #include "Emu/Cell/PPUThread.h" @@ -57,6 +58,7 @@ debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidg : custom_dock_widget(tr("Debugger [Press F1 for Help]"), parent) , m_gui_settings(std::move(gui_settings)) { + setObjectName("debugger"); setContentsMargins(0, 0, 0, 0); m_update = new QTimer(this); @@ -804,12 +806,47 @@ cpu_thread* debugger_frame::get_cpu() return nullptr; } + if (u32 cur = m_choice_units->currentIndex(); cur >= m_hw_ppu_idx && cur < g_cfg.core.ppu_threads + m_hw_ppu_idx) + { + reader_lock lock(lv2_obj::g_mutex); + + const auto ppu = lv2_obj::get_running_ppu(cur - m_hw_ppu_idx); + + if (ppu == m_cpu.get()) + { + // Nothing to do + } + else if (ppu) + { + m_cpu = idm::get_unlocked>(ppu->id); + } + else + { + m_cpu.reset(); + } + } + + if (!!m_disasm != !!m_cpu) + { + // Fixup for HW PPU viewer + if (m_cpu) + { + m_disasm = make_disasm(m_cpu.get(), m_cpu); + } + else + { + m_disasm.reset(); + } + + m_debugger_list->UpdateCPUData(m_disasm); + m_breakpoint_list->UpdateCPUData(m_disasm); + } + // Wait flag is raised by the thread itself, acknowledging exit if (m_cpu) { if (m_cpu->state.all_of(cpu_flag::wait + cpu_flag::exit)) { - m_cpu.reset(); return nullptr; } @@ -823,19 +860,20 @@ cpu_thread* debugger_frame::get_cpu() { if (g_fxo->try_get() != m_rsx || !m_rsx->ctrl || m_rsx->state.all_of(cpu_flag::wait + cpu_flag::exit)) { - m_rsx = nullptr; return nullptr; } + + return m_rsx; } - return m_rsx; + return nullptr; } std::function debugger_frame::make_check_cpu(cpu_thread* cpu, bool unlocked) { constexpr cpu_thread* null_cpu = nullptr; - if (Emu.IsStopped()) + if (Emu.IsStopped(true)) { return []() { return null_cpu; }; } @@ -884,7 +922,7 @@ std::function debugger_frame::make_check_cpu(cpu_thread* cpu, boo return [cpu, type, shared = std::move(shared), emulation_id = Emu.GetEmulationIdentifier()]() mutable -> cpu_thread* { - if (emulation_id != Emu.GetEmulationIdentifier() || Emu.IsStopped()) + if (emulation_id != Emu.GetEmulationIdentifier() || Emu.IsStopped(true)) { // Invalidate all data after Emu.Kill() shared.reset(); @@ -922,19 +960,20 @@ std::function debugger_frame::make_check_cpu(cpu_thread* cpu, boo void debugger_frame::UpdateUI() { - const auto cpu = get_cpu(); + auto cpu = get_cpu(); // Refresh at a high rate during initialization (looks weird otherwise) if (m_ui_update_ctr % (cpu || m_ui_update_ctr < 200 || m_debugger_list->m_dirty_flag ? 5 : 50) == 0) { // If no change to instruction position happened, update instruction list at 20hz - ShowPC(); + ShowPC(false, cpu); } if (m_ui_update_ctr % 20 == 0 && !m_thread_list_pending_update) { // Update threads list at 5hz (low priority) UpdateUnitList(); + cpu = get_cpu(); } if (!cpu) @@ -945,12 +984,13 @@ void debugger_frame::UpdateUI() { // Update threads list (thread exited) UpdateUnitList(); + cpu = get_cpu(); } - ShowPC(); + ShowPC(false, cpu); m_last_query_state.clear(); m_last_pc = -1; - DoUpdate(); + DoUpdate(cpu); } } else if (m_ui_update_ctr % 5 == 0 || m_ui_update_ctr < m_ui_fast_update_permission_deadline) @@ -966,7 +1006,7 @@ void debugger_frame::UpdateUI() std::memcpy(m_last_query_state.data(), static_cast(cpu), size_context); m_last_pc = cia; - DoUpdate(); + DoUpdate(cpu); const bool paused = !!(cpu->state & s_pause_flags); @@ -982,7 +1022,7 @@ void debugger_frame::UpdateUI() if (m_ui_update_ctr % 5) { // Call if it hasn't been called before - ShowPC(); + ShowPC(false, cpu); } if (is_using_interpreter(cpu->get_class())) @@ -1001,7 +1041,7 @@ void debugger_frame::UpdateUnitList() const u64 emulation_id = static_cast>(Emu.GetEmulationIdentifier()); const u64 threads_created = cpu_thread::g_threads_created; const u64 threads_deleted = cpu_thread::g_threads_deleted; - const system_state emu_state = Emu.GetStatus(); + const system_state emu_state = Emu.GetStatus(false); std::unique_lock lock{id_manager::g_mutex, std::defer_lock}; @@ -1022,18 +1062,31 @@ void debugger_frame::UpdateUnitList() } std::vector>> cpu_list; - cpu_list.reserve(threads_created >= threads_deleted ? 0 : threads_created - threads_deleted); + cpu_list.reserve(threads_created >= threads_deleted ? threads_created - threads_deleted : 0); usz reselected_index = umax; + usz hw_ppu_idx = umax; + + if (u32 cur = m_choice_units->currentIndex(); cur >= m_hw_ppu_idx && cur < g_cfg.core.ppu_threads + m_hw_ppu_idx) + { + hw_ppu_idx = cur - m_hw_ppu_idx; + } + + m_hw_ppu_idx = umax; const auto on_select = [&](u32 id, cpu_thread& cpu) { std::function func_cpu = make_check_cpu(std::addressof(cpu), true); + if (cpu.state & cpu_flag::exit) + { + return; + } + // Space at the end is to pad a gap on the right cpu_list.emplace_back(QString::fromStdString((id >> 24 == 0x55 ? "RSX[0x55555555]" : cpu.get_name()) + ' '), std::move(func_cpu)); - if (old_cpu_ptr == std::addressof(cpu)) + if (old_cpu_ptr == std::addressof(cpu) && hw_ppu_idx == umax) { reselected_index = cpu_list.size() - 1; } @@ -1046,6 +1099,40 @@ void debugger_frame::UpdateUnitList() idm::select>(on_select, idm::unlocked); } + m_hw_ppu_idx = ::size32(cpu_list) + 1; // Account for NoThreadString thread + + for (u32 i = 0; i < g_cfg.core.ppu_threads + 0u; i++) + { + std::function get_ppu_at = [index = i, cpu_storage = shared_ptr>()]() mutable + { + reader_lock lock(lv2_obj::g_mutex); + + const auto ppu = lv2_obj::get_running_ppu(index); + + if (ppu == cpu_storage.get()) + { + // Nothing to do + } + else if (ppu) + { + cpu_storage = idm::get_unlocked>(ppu->id); + } + else + { + cpu_storage.reset(); + } + + return cpu_storage.get(); + }; + + if (hw_ppu_idx == i) + { + reselected_index = cpu_list.size(); + } + + cpu_list.emplace_back(tr("HwPPU[%0]: Hardware PPU Thread #%1").arg(i + 1).arg(i + 1), std::move(get_ppu_at)); + } + if (g_fxo->is_init>>()) { idm::select>(on_select, idm::unlocked); @@ -1114,6 +1201,7 @@ void debugger_frame::OnSelectUnit() } cpu_thread* selected = nullptr; + usz hw_ppu_idx = umax; if (m_emu_state != system_state::stopped) { @@ -1135,7 +1223,14 @@ void debugger_frame::OnSelectUnit() if (!selected && !m_rsx && !m_cpu) { - return; + if (u32 cur = m_choice_units->currentIndex(); cur >= m_hw_ppu_idx && cur < g_cfg.core.ppu_threads + m_hw_ppu_idx) + { + hw_ppu_idx = cur - m_hw_ppu_idx; + } + else + { + return; + } } } @@ -1144,6 +1239,15 @@ void debugger_frame::OnSelectUnit() m_rsx = nullptr; m_spu_disasm_memory.reset(); + if (hw_ppu_idx != umax) + { + if (hw_ppu_idx > 1) + { + // Sample PPU + selected = ::at32(m_threads_info, 1)(); + } + } + if (selected) { const u32 cpu_id = selected->id; @@ -1198,8 +1302,8 @@ void debugger_frame::OnSelectUnit() m_debugger_list->UpdateCPUData(m_disasm); m_breakpoint_list->UpdateCPUData(m_disasm); - ShowPC(true); - DoUpdate(); + ShowPC(true, selected); + DoUpdate(selected); UpdateUI(); } @@ -1339,30 +1443,28 @@ void debugger_frame::OnSelectSPUDisassembler() m_debugger_list->UpdateCPUData(m_disasm); m_breakpoint_list->UpdateCPUData(m_disasm); - ShowPC(true); - DoUpdate(); + ShowPC(true, nullptr); + DoUpdate(nullptr); UpdateUI(); }); m_spu_disasm_dialog->show(); } -void debugger_frame::DoUpdate() +void debugger_frame::DoUpdate(cpu_thread* cpu0) { // Check if we need to disable a step over bp - if (const auto cpu0 = get_cpu(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint) + if (cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint) { m_ppu_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint); m_last_step_over_breakpoint = -1; } - WritePanels(); + WritePanels(cpu0); } -void debugger_frame::WritePanels() +void debugger_frame::WritePanels(cpu_thread* cpu) { - const auto cpu = get_cpu(); - if (!cpu) { m_misc_state->clear(); @@ -1373,8 +1475,11 @@ void debugger_frame::WritePanels() int loc = m_misc_state->verticalScrollBar()->value(); int hloc = m_misc_state->horizontalScrollBar()->value(); + + m_last_misc_state.clear(); + cpu->dump_misc(m_last_misc_state, m_dump_misc_func_data); m_misc_state->clear(); - m_misc_state->setPlainText(QString::fromStdString(cpu->dump_misc())); + m_misc_state->setPlainText(QString::fromStdString(m_last_misc_state)); m_misc_state->verticalScrollBar()->setValue(loc); m_misc_state->horizontalScrollBar()->setValue(hloc); @@ -1412,7 +1517,9 @@ void debugger_frame::ShowGotoAddressDialog() QLineEdit* expression_input(new QLineEdit(m_goto_dialog)); expression_input->setFont(m_mono); - if (const auto thread = get_cpu(); !thread || thread->get_class() != thread_class::spu) + const auto thread = get_cpu(); + + if (!thread || thread->get_class() != thread_class::spu) { expression_input->setValidator(new HexValidator(expression_input)); } @@ -1436,8 +1543,8 @@ void debugger_frame::ShowGotoAddressDialog() m_goto_dialog->setLayout(vbox_panel); - const auto cpu_check = make_check_cpu(get_cpu()); - const auto cpu = cpu_check(); + const auto cpu_check = make_check_cpu(thread); + const auto cpu = thread; const QFont font = expression_input->font(); // -1 from get_pc() turns into 0 @@ -1544,9 +1651,12 @@ void debugger_frame::ClearCallStack() Q_EMIT CallStackUpdateRequested({}); } -void debugger_frame::ShowPC(bool user_requested) +void debugger_frame::ShowPC(bool user_requested, cpu_thread* cpu0) { - const auto cpu0 = get_cpu(); + if (!cpu0) + { + cpu0 = get_cpu(); + } const u32 pc = (cpu0 ? cpu0->get_pc() : (m_is_spu_disasm_mode ? m_spu_disasm_pc : 0)); diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index 6837347c3f..abe7ec11cc 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -69,7 +69,9 @@ class debugger_frame : public custom_dock_widget u32 m_last_pc = -1; std::vector m_last_query_state; std::string m_last_reg_state; + std::string m_last_misc_state; std::any m_dump_reg_func_data; + std::any m_dump_misc_func_data; std::vector> m_threads_info; u32 m_last_step_over_breakpoint = -1; u64 m_ui_update_ctr = 0; @@ -79,6 +81,7 @@ class debugger_frame : public custom_dock_widget std::shared_ptr m_disasm; // Only shared to allow base/derived functionality shared_ptr m_cpu; rsx::thread* m_rsx = nullptr; + u32 m_hw_ppu_idx = umax; std::shared_ptr m_spu_disasm_memory; u32 m_spu_disasm_origin_eal = 0; u32 m_spu_disasm_pc = 0; @@ -107,8 +110,8 @@ public: void UpdateUI(); void UpdateUnitList(); - void DoUpdate(); - void WritePanels(); + void DoUpdate(cpu_thread* cpu0); + void WritePanels(cpu_thread* cpu); void EnableButtons(bool enable); void ShowGotoAddressDialog(); void PerformGoToRequest(const QString& text_argument); @@ -138,7 +141,7 @@ private Q_SLOTS: void OnSelectUnit(); void OnSelectSPUDisassembler(); void OnRegsContextMenu(const QPoint& pos); - void ShowPC(bool user_requested = false); + void ShowPC(bool user_requested = false, cpu_thread* cpu = nullptr); void EnableUpdateTimer(bool enable) const; void RunBtnPress(); void RegsShowMemoryViewerAction(); diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index 39078c880d..9a5038fcc1 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -1,12 +1,14 @@ #include #include #include +#include #include "downloader.h" #include "curl_handle.h" #include "progress_dialog.h" #include "util/logs.hpp" +#include "Utilities/Thread.h" #include @@ -34,7 +36,7 @@ downloader::~downloader() } } -void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title, bool keep_progress_dialog_open, int expected_size, bool check_return_code, bool again) +void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, bool check_return_code, const QString& progress_dialog_title, bool keep_progress_dialog_open, int expected_size, bool again) { network_log.notice("Starting download from URL: %s", url); @@ -81,6 +83,8 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p m_thread = QThread::create([this] { + thread_base::set_name("Downloader"); + // Reset error buffer before we call curl_easy_perform m_curl->reset_error_buffer(); @@ -121,7 +125,7 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p { network_log.error("Error during download. Trying to download again (attempts=%d, return_code=%d)", m_download_attempts, return_code); std::this_thread::sleep_for(500ms); // Wait for a little while - start(url, follow_location, show_progress_dialog, progress_dialog_title, keep_progress_dialog_open, expected_size, check_return_code, true); + start(url, follow_location, show_progress_dialog, check_return_code, progress_dialog_title, keep_progress_dialog_open, expected_size, true); return; } } diff --git a/rpcs3/rpcs3qt/downloader.h b/rpcs3/rpcs3qt/downloader.h index be0cd0e568..35c2d5533e 100644 --- a/rpcs3/rpcs3qt/downloader.h +++ b/rpcs3/rpcs3qt/downloader.h @@ -21,7 +21,7 @@ public: explicit downloader(QWidget* parent = nullptr); ~downloader(); - void start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1, bool check_return_code = true, bool again = false); + void start(const std::string& url, bool follow_location, bool show_progress_dialog, bool check_return_code, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1, bool again = false); usz update_buffer(char* data, usz size); void update_progress_dialog(const QString& title) const; diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index ccb2308509..0ec017a02c 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -31,18 +31,20 @@ namespace out << YAML::Null; return; } + if (node.IsMap()) { std::vector keys; keys.reserve(node.size()); + // generate vector of strings to be sorted using the as function from YAML documentation for (const auto& pair : node) { - keys.push_back(pair.first.as()); + keys.push_back(pair.first.Scalar()); } std::sort(keys.begin(), keys.end()); + // recursively generate sorted maps - // alternative implementations could have stops at specified recursion levels or maybe just the first two levels would be sorted out << YAML::BeginMap; for (const std::string& key : keys) { @@ -51,15 +53,10 @@ namespace emit_data(out, node[key]); } out << YAML::EndMap; + return; } - // alternatively: an else statement could be used however I wanted to follow a similar format to the += operator so the YAML Undefined class can be ignored - else if (node.IsScalar() || node.IsSequence()) - { - out << node; - } - // this exists to preserve the same functionality as before where Undefined nodes would still be output, can be removed or consolidated with the else if branch - else - out << node; + + out << node; } // Incrementally load YAML @@ -210,7 +207,7 @@ bool emu_settings::ValidateSettings(bool cleanup) bool is_clean = true; std::function&, cfg::_base*)> search_level; - search_level = [&search_level, &is_clean, &cleanup, this](int level, YAML::Node& yml_node, std::vector& keys, cfg::_base* cfg_base) + search_level = [&search_level, &is_clean, &cleanup](int level, YAML::Node& yml_node, std::vector& keys, cfg::_base* cfg_base) { if (!yml_node || !yml_node.IsMap()) { @@ -910,11 +907,38 @@ std::string emu_settings::GetSetting(emu_settings_type type) const return ""; } +std::map emu_settings::GetMapSettingDefault(emu_settings_type type) const +{ + if (const auto node = cfg_adapter::get_node(m_default_settings, ::at32(settings_location, type)); node && node.IsMap()) + { + return node.as>(); + } + + cfg_log.fatal("GetMapSettingDefault(type=%d) could not retrieve the requested node", static_cast(type)); + return {}; +} + +std::map emu_settings::GetMapSetting(emu_settings_type type) const +{ + if (const auto node = cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)); node && node.IsMap()) + { + return node.as>(); + } + + cfg_log.fatal("GetMapSetting(type=%d) could not retrieve the requested node", static_cast(type)); + return {}; +} + void emu_settings::SetSetting(emu_settings_type type, const std::string& val) const { cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)) = val; } +void emu_settings::SetMapSetting(emu_settings_type type, const std::map& val) const +{ + cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)) = val; +} + emu_settings_type emu_settings::FindSettingsType(const cfg::_base* node) const { // Add key and value to static map on first use @@ -1030,9 +1054,9 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case emu_settings_type::ShaderMode: switch (static_cast(index)) { - case shader_mode::recompiler: return tr("Legacy (single threaded)", "Shader Mode"); - case shader_mode::async_recompiler: return tr("Async (multi threaded)", "Shader Mode"); - case shader_mode::async_with_interpreter: return tr("Async with Shader Interpreter", "Shader Mode"); + case shader_mode::recompiler: return tr("Legacy Recompiler (single-threaded)", "Shader Mode"); + case shader_mode::async_recompiler: return tr("Async Recompiler (multi-threaded)", "Shader Mode"); + case shader_mode::async_with_interpreter: return tr("Async Recompiler with Shader Interpreter", "Shader Mode"); case shader_mode::interpreter_only: return tr("Shader Interpreter only", "Shader Mode"); } break; @@ -1475,6 +1499,13 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case xfloat_accuracy::inaccurate: return tr("Inaccurate XFloat"); } break; + case emu_settings_type::VSync: + switch (static_cast(index)) + { + case vsync_mode::off: return tr("Disabled", "VSync Mode"); + case vsync_mode::adaptive: return tr("Adaptive", "VSync Mode"); + case vsync_mode::full: return tr("Full", "VSync Mode"); + } default: break; } diff --git a/rpcs3/rpcs3qt/emu_settings.h b/rpcs3/rpcs3qt/emu_settings.h index 5da946dabe..c5a1e89252 100644 --- a/rpcs3/rpcs3qt/emu_settings.h +++ b/rpcs3/rpcs3qt/emu_settings.h @@ -78,9 +78,18 @@ public: /** Returns the value of the setting type.*/ std::string GetSetting(emu_settings_type type) const; + /** Returns the default map value of the setting type.*/ + std::map GetMapSettingDefault(emu_settings_type type) const; + + /** Returns the value of the setting type as map.*/ + std::map GetMapSetting(emu_settings_type type) const; + /** Sets the setting type to a given value.*/ void SetSetting(emu_settings_type type, const std::string& val) const; + /** Sets the setting type to a given map value.*/ + void SetMapSetting(emu_settings_type type, const std::map& val) const; + /** Try to find the settings type for a given string.*/ emu_settings_type FindSettingsType(const cfg::_base* node) const; diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 6a5b6841b1..db3e3556da 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -127,6 +127,7 @@ enum class emu_settings_type PerfOverlayMarginY, PerfOverlayCenterX, PerfOverlayCenterY, + PerfOverlayUseWindowSpace, // Shader Loading Dialog ShaderLoadBgEnabled, @@ -188,9 +189,11 @@ enum class emu_settings_type ShowPressureIntensityToggleHint, ShowAnalogLimiterToggleHint, ShowMouseAndKeyboardToggleHint, + ShowFatalErrorHints, ShowCaptureHints, WindowTitleFormat, PauseDuringHomeMenu, + PlayMusicDuringBoot, EnableGamemode, // Network @@ -217,6 +220,9 @@ enum class emu_settings_type EmptyHdd0Tmp, LimitCacheSize, MaximumCacheSize, + + // Log + Log, }; /** A helper map that keeps track of where a given setting type is located*/ @@ -238,7 +244,7 @@ inline static const std::map settings_location { emu_settings_type::AccurateClineStores, { "Core", "Accurate Cache Line Stores"}}, { emu_settings_type::AccurateRSXAccess, { "Core", "Accurate RSX reservation access"}}, { emu_settings_type::FIFOAccuracy, { "Core", "RSX FIFO Fetch Accuracy"}}, - { emu_settings_type::XFloatAccuracy, { "Core", "XFloat Accuracy"}}, + { emu_settings_type::XFloatAccuracy, { "Core", "SPU XFloat Accuracy"}}, { emu_settings_type::MFCCommandsShuffling, { "Core", "MFC Commands Shuffling Limit"}}, { emu_settings_type::SetDAZandFTZ, { "Core", "Set DAZ and FTZ"}}, { emu_settings_type::SPUBlockSize, { "Core", "SPU Block Size"}}, @@ -275,7 +281,7 @@ inline static const std::map settings_location { emu_settings_type::ReadColorBuffers, { "Video", "Read Color Buffers"}}, { emu_settings_type::ReadDepthBuffer, { "Video", "Read Depth Buffer"}}, { emu_settings_type::HandleRSXTiledMemory, { "Video", "Handle RSX Memory Tiling"}}, - { emu_settings_type::VSync, { "Video", "VSync"}}, + { emu_settings_type::VSync, { "Video", "VSync Mode"}}, { emu_settings_type::DebugOutput, { "Video", "Debug output"}}, { emu_settings_type::DebugOverlay, { "Video", "Debug overlay"}}, { emu_settings_type::RenderdocCompatibility, { "Video", "Renderdoc Compatibility Mode"}}, @@ -314,11 +320,11 @@ inline static const std::map settings_location { emu_settings_type::DisableAsyncHostMM, { "Video", "Disable Asynchronous Memory Manager"}}, { emu_settings_type::RecordWithOverlays, { "Video", "Record With Overlays"}}, { emu_settings_type::DisableHWTexelRemapping, { "Video", "Disable Hardware ColorSpace Remapping"}}, + { emu_settings_type::FsrSharpeningStrength, { "Video", "FidelityFX CAS Sharpening Intensity"}}, // Vulkan - { emu_settings_type::VulkanAsyncTextureUploads, { "Video", "Vulkan", "Asynchronous Texture Streaming 2"}}, + { emu_settings_type::VulkanAsyncTextureUploads, { "Video", "Vulkan", "Asynchronous Texture Streaming"}}, { emu_settings_type::VulkanAsyncSchedulerDriver, { "Video", "Vulkan", "Asynchronous Queue Scheduler"}}, - { emu_settings_type::FsrSharpeningStrength, { "Video", "Vulkan", "FidelityFX CAS Sharpening Intensity"}}, { emu_settings_type::ExclusiveFullscreenMode, { "Video", "Vulkan", "Exclusive Fullscreen Mode"}}, { emu_settings_type::UseReBAR, { "Video", "Vulkan", "Use Re-BAR for GPU uploads"}}, @@ -335,10 +341,11 @@ inline static const std::map settings_location { emu_settings_type::PerfOverlayUpdateInterval, { "Video", "Performance Overlay", "Metrics update interval (ms)" } }, { emu_settings_type::PerfOverlayFontSize, { "Video", "Performance Overlay", "Font size (px)" } }, { emu_settings_type::PerfOverlayOpacity, { "Video", "Performance Overlay", "Opacity (%)" } }, - { emu_settings_type::PerfOverlayMarginX, { "Video", "Performance Overlay", "Horizontal Margin (px)" } }, - { emu_settings_type::PerfOverlayMarginY, { "Video", "Performance Overlay", "Vertical Margin (px)" } }, + { emu_settings_type::PerfOverlayMarginX, { "Video", "Performance Overlay", "Horizontal Margin (%)" } }, + { emu_settings_type::PerfOverlayMarginY, { "Video", "Performance Overlay", "Vertical Margin (%)" } }, { emu_settings_type::PerfOverlayCenterX, { "Video", "Performance Overlay", "Center Horizontally" } }, { emu_settings_type::PerfOverlayCenterY, { "Video", "Performance Overlay", "Center Vertically" } }, + { emu_settings_type::PerfOverlayUseWindowSpace, { "Video", "Performance Overlay", "Use Window Space"}}, // Shader Loading Dialog { emu_settings_type::ShaderLoadBgEnabled, { "Video", "Shader Loading Dialog", "Allow custom background" } }, @@ -400,10 +407,12 @@ inline static const std::map settings_location { emu_settings_type::ShowPressureIntensityToggleHint, { "Miscellaneous", "Show pressure intensity toggle hint"}}, { emu_settings_type::ShowAnalogLimiterToggleHint, { "Miscellaneous", "Show analog limiter toggle hint"}}, { emu_settings_type::ShowMouseAndKeyboardToggleHint, { "Miscellaneous", "Show mouse and keyboard toggle hint"}}, + { emu_settings_type::ShowFatalErrorHints, { "Miscellaneous", "Show fatal error hints"}}, { emu_settings_type::ShowCaptureHints, { "Miscellaneous", "Show capture hints" }}, { emu_settings_type::SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }}, { emu_settings_type::WindowTitleFormat, { "Miscellaneous", "Window Title Format" }}, { emu_settings_type::PauseDuringHomeMenu, { "Miscellaneous", "Pause Emulation During Home Menu" }}, + { emu_settings_type::PlayMusicDuringBoot, { "Miscellaneous", "Play music during boot sequence" }}, { emu_settings_type::EnableGamemode, { "Miscellaneous", "Enable GameMode" }}, // Networking @@ -434,4 +443,7 @@ inline static const std::map settings_location { emu_settings_type::SuspendEmulationSavestateMode, { "Savestate", "Suspend Emulation Savestate Mode" }}, { emu_settings_type::CompatibleEmulationSavestateMode, { "Savestate", "Compatible Savestate Mode" }}, { emu_settings_type::StartSavestatePaused, { "Savestate", "Start Paused" }}, + + // Logs + { emu_settings_type::Log, { "Log" }}, }; diff --git a/rpcs3/rpcs3qt/find_dialog.cpp b/rpcs3/rpcs3qt/find_dialog.cpp index 9c5e805050..756e355e4f 100644 --- a/rpcs3/rpcs3qt/find_dialog.cpp +++ b/rpcs3/rpcs3qt/find_dialog.cpp @@ -1,6 +1,10 @@ +#include "stdafx.h" #include "find_dialog.h" +#include #include +#include +#include find_dialog::find_dialog(QPlainTextEdit* edit, QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) { @@ -9,109 +13,159 @@ find_dialog::find_dialog(QPlainTextEdit* edit, QWidget *parent, Qt::WindowFlags m_find_bar = new QLineEdit(); m_find_bar->setPlaceholderText(tr("Search...")); - m_label_count_lines = new QLabel(tr("Counted in lines: -")); - m_label_count_total = new QLabel(tr("Counted in total: -")); + QCheckBox* cb_case_sensitive = new QCheckBox(tr("Case sensitive")); + cb_case_sensitive->setChecked(m_case_sensitive); + connect(cb_case_sensitive, &QCheckBox::toggled, this, [this](bool checked) + { + m_case_sensitive = checked; + }); - m_find_first = new QPushButton(tr("First")); - m_find_last = new QPushButton(tr("Last")); - m_find_next = new QPushButton(tr("Next")); - m_find_previous = new QPushButton(tr("Previous")); + m_label_count_lines = new QLabel(tr("Line 0/0")); + m_label_count_total = new QLabel(tr("Word 0/0")); + + QPushButton* find_first = new QPushButton(tr("First")); + QPushButton* find_last = new QPushButton(tr("Last")); + QPushButton* find_next = new QPushButton(tr("Next")); + QPushButton* find_previous = new QPushButton(tr("Previous")); + + QHBoxLayout* find_layout = new QHBoxLayout(); + find_layout->addWidget(m_find_bar); + find_layout->addWidget(cb_case_sensitive); QHBoxLayout* count_layout = new QHBoxLayout(); count_layout->addWidget(m_label_count_lines); count_layout->addWidget(m_label_count_total); QHBoxLayout* button_layout = new QHBoxLayout(); - button_layout->addWidget(m_find_first); - button_layout->addWidget(m_find_last); - button_layout->addWidget(m_find_previous); - button_layout->addWidget(m_find_next); + button_layout->addWidget(find_first); + button_layout->addWidget(find_last); + button_layout->addWidget(find_previous); + button_layout->addWidget(find_next); QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget(m_find_bar); + layout->addLayout(find_layout); layout->addLayout(count_layout); layout->addLayout(button_layout); setLayout(layout); - connect(m_find_first, &QPushButton::clicked, this, &find_dialog::find_first); - connect(m_find_last, &QPushButton::clicked, this, &find_dialog::find_last); - connect(m_find_next, &QPushButton::clicked, this, &find_dialog::find_next); - connect(m_find_previous, &QPushButton::clicked, this, &find_dialog::find_previous); + connect(find_first, &QPushButton::clicked, this, [this](){ find(find_type::first); }); + connect(find_last, &QPushButton::clicked, this, [this](){ find(find_type::last); }); + connect(find_next, &QPushButton::clicked, this, [this](){ find(find_type::next); }); + connect(find_previous, &QPushButton::clicked, this, [this](){ find(find_type::prev); }); - m_find_next->setDefault(true); + find_next->setDefault(true); show(); } -int find_dialog::count_all() +void find_dialog::find(find_type type) { m_count_lines = 0; m_count_total = 0; + m_current_line = 0; + m_current_index = 0; if (!m_text_edit || m_find_bar->text().isEmpty()) { show_count(); - return 0; + return; } + const QTextDocument::FindFlags flags = m_case_sensitive ? QTextDocument::FindCaseSensitively : QTextDocument::FindFlag{}; + + std::map /*pos*/>> block_indices; + const QTextCursor old_cursor = m_text_edit->textCursor(); m_text_edit->moveCursor(QTextCursor::Start); - int old_line_index = -1; + const QString text = m_find_bar->text(); - while (m_text_edit->find(m_find_bar->text())) + while (m_text_edit->find(text, flags)) { m_count_total++; - const int new_line_index = m_text_edit->textCursor().blockNumber(); - if (new_line_index != old_line_index) + const QTextCursor cursor = m_text_edit->textCursor(); + const QTextBlock block = cursor.block(); + const QTextLayout* layout = block.layout(); + + const int relative_pos = cursor.position() - block.position(); + const QTextLine line = layout->lineForTextPosition(relative_pos); + const int pos_in_line = relative_pos - line.textStart(); + + block_indices[cursor.blockNumber()][line.lineNumber()].push_back(pos_in_line); + } + + switch (type) + { + case find_type::first: + { + m_text_edit->moveCursor(QTextCursor::Start); + m_text_edit->find(text, flags); + break; + } + case find_type::last: + { + m_text_edit->moveCursor(QTextCursor::End); + m_text_edit->find(text, flags | QTextDocument::FindBackward); + break; + } + case find_type::next: + { + m_text_edit->setTextCursor(old_cursor); + m_text_edit->find(text, flags); + break; + } + case find_type::prev: + { + m_text_edit->setTextCursor(old_cursor); + m_text_edit->find(text, flags | QTextDocument::FindBackward); + break; + } + } + + const QTextCursor cursor = m_text_edit->textCursor(); + const QTextBlock block = cursor.block(); + const QTextLayout* layout = block.layout(); + + const int relative_pos = cursor.position() - block.position(); + const QTextLine line = layout->lineForTextPosition(relative_pos); + const int pos_in_line = relative_pos - line.textStart(); + + const int current_line = line.lineNumber(); + const int current_pos = pos_in_line; + + int word_count = 0; + for (const auto& [block, lines] : block_indices) + { + const bool is_current_block = block == cursor.blockNumber(); + + for (const auto& [line, positions] : lines) { + const bool is_current_line = line == current_line; + m_count_lines++; - old_line_index = new_line_index; + + int pos_count = 0; + + for (int pos : positions) + { + pos_count++; + word_count++; + + if (is_current_block && is_current_line && pos == current_pos) + { + m_current_line = m_count_lines; + m_current_index = word_count; + } + } } } - m_text_edit->setTextCursor(old_cursor); show_count(); - return m_count_total; -} - -void find_dialog::find_first() -{ - if (count_all() <= 0) - return; - - m_text_edit->moveCursor(QTextCursor::Start); - m_text_edit->find(m_find_bar->text()); -} - -void find_dialog::find_last() -{ - if (count_all() <= 0) - return; - - m_text_edit->moveCursor(QTextCursor::End); - m_text_edit->find(m_find_bar->text(), QTextDocument::FindBackward); -} - -void find_dialog::find_next() -{ - if (count_all() <= 0) - return; - - m_text_edit->find(m_find_bar->text()); -} - -void find_dialog::find_previous() -{ - if (count_all() <= 0) - return; - - m_text_edit->find(m_find_bar->text(), QTextDocument::FindBackward); } void find_dialog::show_count() const { - m_label_count_lines->setText(tr("Counted in lines: %0").arg(m_count_lines)); - m_label_count_total->setText(tr("Counted in total: %0").arg(m_count_total)); + m_label_count_lines->setText(tr("Line %0/%1").arg(m_current_line).arg(m_count_lines)); + m_label_count_total->setText(tr("Word %0/%1").arg(m_current_index).arg(m_count_total)); } diff --git a/rpcs3/rpcs3qt/find_dialog.h b/rpcs3/rpcs3qt/find_dialog.h index 27baf03c75..a4e9293fc6 100644 --- a/rpcs3/rpcs3qt/find_dialog.h +++ b/rpcs3/rpcs3qt/find_dialog.h @@ -3,7 +3,6 @@ #include #include #include -#include #include class find_dialog : public QDialog @@ -14,22 +13,26 @@ public: find_dialog(QPlainTextEdit* edit, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); private: + enum class find_type + { + first, + last, + next, + prev + }; + + void find(find_type type); + void show_count() const; + int m_count_lines = 0; int m_count_total = 0; + int m_current_line = 0; + int m_current_index = 0; + + bool m_case_sensitive = false; + QLabel* m_label_count_lines; QLabel* m_label_count_total; QPlainTextEdit* m_text_edit; QLineEdit* m_find_bar; - QPushButton* m_find_first; - QPushButton* m_find_last; - QPushButton* m_find_next; - QPushButton* m_find_previous; - -private Q_SLOTS: - int count_all(); - void find_first(); - void find_last(); - void find_next(); - void find_previous(); - void show_count() const; }; diff --git a/rpcs3/rpcs3qt/game_compatibility.cpp b/rpcs3/rpcs3qt/game_compatibility.cpp index b9cb32a7c1..a7a4a4bc34 100644 --- a/rpcs3/rpcs3qt/game_compatibility.cpp +++ b/rpcs3/rpcs3qt/game_compatibility.cpp @@ -36,7 +36,7 @@ void game_compatibility::handle_download_finished(const QByteArray& content) compat_log.notice("Database download finished"); // Create new map from database and write database to file if database was valid - if (ReadJSON(QJsonDocument::fromJson(content).object(), true)) + if (handle_json(content, true)) { // Write database to file QFile file(m_filepath); @@ -67,8 +67,9 @@ void game_compatibility::handle_download_canceled() Q_EMIT DownloadCanceled(); } -bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_download) +bool game_compatibility::handle_json(const QByteArray& data, bool after_download) { + const QJsonObject json_data = QJsonDocument::fromJson(data).object(); const int return_code = json_data["return_code"].toInt(-255); if (return_code < 0) @@ -101,7 +102,7 @@ bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_downl m_compat_database.clear(); - QJsonObject json_results = json_data["results"].toObject(); + const QJsonObject json_results = json_data["results"].toObject(); // Retrieve status data for every valid entry for (const auto& key : json_results.keys()) @@ -112,7 +113,7 @@ bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_downl continue; } - QJsonObject json_result = json_results[key].toObject(); + const QJsonObject json_result = json_results[key].toObject(); // Retrieve compatibility information from json compat::status status = ::at32(Status_Data, json_result.value("status").toString("NoResult")); @@ -210,15 +211,14 @@ void game_compatibility::RequestCompatibility(bool online) compat_log.notice("Finished reading database from file: %s", m_filepath); // Create new map from database - ReadJSON(QJsonDocument::fromJson(content).object(), online); - + handle_json(content, online); return; } const std::string url = "https://rpcs3.net/compatibility?api=v1&export"; compat_log.notice("Beginning compatibility database download from: %s", url); - m_downloader->start(url, true, true, tr("Downloading Database")); + m_downloader->start(url, true, true, true, tr("Downloading Database")); // We want to retrieve a new database, therefore refresh gamelist and indicate that Q_EMIT DownloadStarted(); diff --git a/rpcs3/rpcs3qt/game_compatibility.h b/rpcs3/rpcs3qt/game_compatibility.h index 664bafea34..8dc1ce5aa5 100644 --- a/rpcs3/rpcs3qt/game_compatibility.h +++ b/rpcs3/rpcs3qt/game_compatibility.h @@ -138,7 +138,7 @@ private: std::map m_compat_database; /** Creates new map from the database */ - bool ReadJSON(const QJsonObject& json_data, bool after_download); + bool handle_json(const QByteArray& data, bool after_download); public: /** Handles reads, writes and downloads for the compatibility database */ diff --git a/rpcs3/rpcs3qt/game_list_actions.cpp b/rpcs3/rpcs3qt/game_list_actions.cpp index b37348f49d..25b9f06b96 100644 --- a/rpcs3/rpcs3qt/game_list_actions.cpp +++ b/rpcs3/rpcs3qt/game_list_actions.cpp @@ -7,11 +7,11 @@ #include "progress_dialog.h" #include "Utilities/File.h" +#include "Loader/ISO.h" #include "Emu/System.h" #include "Emu/system_utils.hpp" #include "Emu/VFS.h" -#include "Emu/vfs_config.h" #include "Input/pad_thread.h" @@ -846,16 +846,19 @@ bool game_list_actions::RemoveContentList(const std::string& serial, bool is_int RemoveContentBySerial(rpcs3::utils::get_icons_dir(), serial, "icons"); } - // Remove shortcuts in "games/shortcuts" folder and from desktop / start menu (if any) + // Remove shortcuts in "games/shortcuts" folder and from desktop / start menu / Steam (if any) if (content_types & SHORTCUTS) { if (const auto it = m_content_info.name_list.find(serial); it != m_content_info.name_list.cend()) { + std::vector> games; for (const std::string& name : it->second) { - // Remove all shortcuts - gui::utils::remove_shortcuts(name, serial); + games.push_back(std::pair(name, serial)); } + + // Batch remove all shortcuts + gui::utils::batch_remove_shortcuts(games); } } @@ -905,7 +908,7 @@ void game_list_actions::BatchActionBySerials(progress_dialog* pdlg, const std::s const int serials_size = ::narrow(serials.size()); - *iterate_over_serial = [=, this, index_ptr = index](int index) + *iterate_over_serial = [=, index_ptr = index](int index) { if (index == serials_size) { @@ -1452,91 +1455,7 @@ void game_list_actions::CreateShortcuts(const std::vector& games, con return; } - bool success = true; - - for (const game_info& gameinfo : games) - { - std::string gameid_token_value; - - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - - if (gameinfo->info.category == "DG" && !fs::is_file(rpcs3::utils::get_hdd0_dir() + "/game/" + gameinfo->info.serial + "/USRDIR/EBOOT.BIN")) - { - const usz ps3_game_dir_pos = fs::get_parent_dir(gameinfo->info.path).size(); - std::string relative_boot_dir = gameinfo->info.path.substr(ps3_game_dir_pos); - - if (usz char_pos = relative_boot_dir.find_first_not_of(fs::delim); char_pos != umax) - { - relative_boot_dir = relative_boot_dir.substr(char_pos); - } - else - { - relative_boot_dir.clear(); - } - - if (!relative_boot_dir.empty()) - { - if (relative_boot_dir != "PS3_GAME") - { - gameid_token_value = gameinfo->info.serial + "/" + relative_boot_dir; - } - else - { - gameid_token_value = gameinfo->info.serial; - } - } - } - else - { - gameid_token_value = gameinfo->info.serial; - } - -#ifdef __linux__ - const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%%%RPCS3_VFS%%%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) - : fmt::format("--no-gui \"%%%%RPCS3_GAMEID%%%%:%s\"", gameid_token_value); -#else - const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%RPCS3_VFS%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) - : fmt::format("--no-gui \"%%RPCS3_GAMEID%%:%s\"", gameid_token_value); -#endif - const std::string target_icon_dir = fmt::format("%sIcons/game_icons/%s/", fs::get_config_dir(), gameinfo->info.serial); - - if (!fs::create_path(target_icon_dir)) - { - game_list_log.error("Failed to create shortcut path %s (%s)", QString::fromStdString(gameinfo->info.name).simplified(), target_icon_dir, fs::g_tls_error); - success = false; - continue; - } - - for (const gui::utils::shortcut_location& location : locations) - { - std::string destination; - - switch (location) - { - case gui::utils::shortcut_location::desktop: - destination = "desktop"; - break; - case gui::utils::shortcut_location::applications: - destination = "application menu"; - break; -#ifdef _WIN32 - case gui::utils::shortcut_location::rpcs3_shortcuts: - destination = "/games/shortcuts/"; - break; -#endif - } - - if (!gameid_token_value.empty() && gui::utils::create_shortcut(gameinfo->info.name, gameinfo->icon_in_archive ? gameinfo->info.path : "", gameinfo->info.serial, target_cli_args, gameinfo->info.name, gameinfo->info.icon_path, target_icon_dir, location)) - { - game_list_log.success("Created %s shortcut for %s", destination, QString::fromStdString(gameinfo->info.name).simplified()); - } - else - { - game_list_log.error("Failed to create %s shortcut for %s", destination, QString::fromStdString(gameinfo->info.name).simplified()); - success = false; - } - } - } + const bool success = gui::utils::batch_create_shortcuts(games, locations); #ifdef _WIN32 if (locations.size() == 1 && locations.contains(gui::utils::shortcut_location::rpcs3_shortcuts)) diff --git a/rpcs3/rpcs3qt/game_list_base.h b/rpcs3/rpcs3qt/game_list_base.h index 2af79b0b01..6997594070 100644 --- a/rpcs3/rpcs3qt/game_list_base.h +++ b/rpcs3/rpcs3qt/game_list_base.h @@ -16,7 +16,8 @@ public: [[maybe_unused]] const std::map& notes_map, [[maybe_unused]] const std::map& title_map, [[maybe_unused]] const std::set& selected_item_ids, - [[maybe_unused]] bool play_hover_movies){}; + [[maybe_unused]] bool play_hover_movies, + [[maybe_unused]] bool play_hover_music){}; void set_icon_size(QSize size) { m_icon_size = std::move(size); } void set_icon_color(QColor color) { m_icon_color = std::move(color); } diff --git a/rpcs3/rpcs3qt/game_list_context_menu.cpp b/rpcs3/rpcs3qt/game_list_context_menu.cpp index 720aac8596..81f6f6e973 100644 --- a/rpcs3/rpcs3qt/game_list_context_menu.cpp +++ b/rpcs3/rpcs3qt/game_list_context_menu.cpp @@ -6,6 +6,7 @@ #include "input_dialog.h" #include "qt_utils.h" #include "shortcut_utils.h" +#include "steam_utils.h" #include "settings_dialog.h" #include "pad_settings_dialog.h" #include "patch_manager_dialog.h" @@ -273,6 +274,7 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info& { m_game_list_actions->CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::desktop}); }); + #ifdef _WIN32 QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Start Menu Shortcut")); #elif defined(__APPLE__) @@ -285,6 +287,17 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info& m_game_list_actions->CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::applications}); }); + if (gui::utils::steam_shortcut::steam_installed()) + { + const bool steam_running = gui::utils::steam_shortcut::is_steam_running(); + QAction* create_steam_shortcut = manage_game_menu->addAction(steam_running ? tr("&Create Steam Shortcut (Steam must be closed)") : tr("&Create Steam Shortcut")); + connect(create_steam_shortcut, &QAction::triggered, this, [this, gameinfo]() + { + m_game_list_actions->CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::steam}); + }); + create_steam_shortcut->setEnabled(!steam_running); + } + manage_game_menu->addSeparator(); // Hide/rename game in game list @@ -843,6 +856,20 @@ void game_list_context_menu::show_multi_selection_context_menu(const std::vector m_game_list_actions->CreateShortcuts(games, {gui::utils::shortcut_location::applications}); }); + if (gui::utils::steam_shortcut::steam_installed()) + { + const bool steam_running = gui::utils::steam_shortcut::is_steam_running(); + QAction* create_steam_shortcut = manage_game_menu->addAction(steam_running ? tr("&Create Steam Shortcut (Steam must be closed)") : tr("&Create Steam Shortcut")); + connect(create_steam_shortcut, &QAction::triggered, this, [this, games]() + { + if (QMessageBox::question(m_game_list_frame, tr("Confirm Creation"), tr("Create Steam shortcut?")) != QMessageBox::Yes) + return; + + m_game_list_actions->CreateShortcuts(games, {gui::utils::shortcut_location::steam}); + }); + create_steam_shortcut->setEnabled(!steam_running); + } + manage_game_menu->addSeparator(); // Hide game in game list diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index ea7b87ff6d..36e26cf43d 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -40,6 +40,8 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std , m_emu_settings(std::move(emu_settings)) , m_persistent_settings(std::move(persistent_settings)) { + setObjectName("gamelist"); + m_game_list_actions = std::make_shared(this, m_gui_settings); m_icon_size = gui::gl_icon_size_min; // ensure a valid size @@ -215,6 +217,7 @@ void game_list_frame::LoadSettings() m_prefer_game_data_icons = m_gui_settings->GetValue(gui::gl_pref_gd_icon).toBool(); m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool(); m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool(); + m_play_hover_music = m_gui_settings->GetValue(gui::gl_hover_music).toBool(); m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast(col)); }); } @@ -482,7 +485,7 @@ void game_list_frame::Refresh(const bool from_drive, const std::vectorclear_list(); const int scroll_position = m_game_list->verticalScrollBar()->value(); - m_game_list->populate(matching_apps, m_notes, m_titles, selected_items, m_play_hover_movies); + m_game_list->populate(matching_apps, m_notes, m_titles, selected_items, m_play_hover_movies, m_play_hover_music); m_game_list->sort(m_game_data.size(), m_sort_column, m_col_sort_order); RepaintIcons(); @@ -498,7 +501,7 @@ void game_list_frame::Refresh(const bool from_drive, const std::vectorclear_list(); - m_game_grid->populate(matching_apps, m_notes, m_titles, selected_items, m_play_hover_movies); + m_game_grid->populate(matching_apps, m_notes, m_titles, selected_items, m_play_hover_movies, m_play_hover_music); RepaintIcons(); } } @@ -521,7 +524,10 @@ void game_list_frame::OnParsingFinished() const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); const std::string localized_movie = fmt::format("ICON1_%02d.PAM", language_index); - const auto add_game = [this, localized_title, localized_icon, localized_movie, dev_flash, cat_unknown_localized = localized.category.unknown.toStdString(), cat_unknown = cat::cat_unknown.toStdString(), game_icon_path, _hdd, play_hover_movies = m_play_hover_movies, show_custom_icons = m_show_custom_icons](const std::string& dir_or_elf) + const auto add_game = [this, localized_title, localized_icon, localized_movie, dev_flash, game_icon_path, _hdd, + cat_unknown_localized = localized.category.unknown.toStdString(), cat_unknown = cat::cat_unknown.toStdString(), + play_hover_movies = m_play_hover_movies, play_hover_music = m_play_hover_music, show_custom_icons = m_show_custom_icons] + (const std::string& dir_or_elf) { std::unique_ptr archive; if (is_file_iso(dir_or_elf)) @@ -621,20 +627,32 @@ void game_list_frame::OnParsingFinished() game.icon_in_archive = archive && archive->exists(game.info.icon_path); } - if (std::string movie_path = game_icon_path + game.info.serial + "/hover.gif"; file_exists(movie_path)) + if (play_hover_movies) { - game.info.movie_path = std::move(movie_path); - game.has_hover_gif = true; + if (std::string movie_path = game_icon_path + game.info.serial + "/hover.gif"; file_exists(movie_path)) + { + game.info.movie_path = std::move(movie_path); + game.has_hover_gif = true; + } + else if (std::string movie_path = sfo_dir + "/" + localized_movie; file_exists(movie_path)) + { + game.info.movie_path = std::move(movie_path); + game.has_hover_pam = true; + } + else if (std::string movie_path = sfo_dir + "/ICON1.PAM"; file_exists(movie_path)) + { + game.info.movie_path = std::move(movie_path); + game.has_hover_pam = true; + } } - else if (std::string movie_path = sfo_dir + "/" + localized_movie; file_exists(movie_path)) + + if (play_hover_music) { - game.info.movie_path = std::move(movie_path); - game.has_hover_pam = true; - } - else if (std::string movie_path = sfo_dir + "/ICON1.PAM"; file_exists(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_pam = true; + if (std::string audio_path = sfo_dir + "/SND0.AT3"; file_exists(audio_path)) + { + game.info.audio_path = std::move(audio_path); + game.has_audio_file = true; + } } const QString serial = QString::fromStdString(game.info.serial); @@ -1385,6 +1403,16 @@ void game_list_frame::SetPlayHoverGifs(bool play) } } +void game_list_frame::SetPlayHoverMusic(bool play) +{ + if (m_play_hover_music != play) + { + m_play_hover_music = play; + m_gui_settings->SetValue(gui::gl_hover_music, play); + Refresh(true); + } +} + void game_list_frame::WaitAndAbortRepaintThreads() { for (const game_info& game : m_game_data) diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index b8148630f9..637229bf60 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -88,6 +88,7 @@ public Q_SLOTS: void SetPreferGameDataIcons(bool enabled); void SetShowCustomIcons(bool show); void SetPlayHoverGifs(bool play); + void SetPlayHoverMusic(bool play); void FocusAndSelectFirstEntryIfNoneIs(); private Q_SLOTS: @@ -204,5 +205,6 @@ private: bool m_prefer_game_data_icons = false; bool m_show_custom_icons = true; bool m_play_hover_movies = true; + bool m_play_hover_music = true; std::optional> m_refresh_funcs_manage_type{std::in_place}; }; diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp index a33755ff54..903fdf016a 100644 --- a/rpcs3/rpcs3qt/game_list_grid.cpp +++ b/rpcs3/rpcs3qt/game_list_grid.cpp @@ -21,7 +21,7 @@ game_list_grid::game_list_grid() Q_EMIT IconReady(game, item); }; - connect(this, &game_list_grid::IconReady, this, [this](const game_info& game, const movie_item_base* item) + connect(this, &game_list_grid::IconReady, this, [](const game_info& game, const movie_item_base* item) { if (game && item && game->item == item) item->image_change_callback(); }, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case... @@ -45,7 +45,8 @@ void game_list_grid::populate( const std::map& notes_map, const std::map& title_map, const std::set& selected_item_ids, - bool play_hover_movies) + bool play_hover_movies, + bool play_hover_music) { clear_list(); @@ -92,7 +93,7 @@ void game_list_grid::populate( if (const QPixmap pixmap = item->get_movie_image(frame); item->get_active() && !pixmap.isNull()) { - item->set_icon(gui::utils::get_centered_pixmap(pixmap, m_icon_size, 0, 0, 1.0, Qt::FastTransformation)); + item->set_icon(gui::utils::get_aligned_pixmap(pixmap, m_icon_size, 1.0, Qt::FastTransformation, gui::utils::align_h::center, gui::utils::align_v::center)); return; } @@ -109,14 +110,23 @@ void game_list_grid::populate( } }); + bool check_iso = false; + if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam)) { item->set_video_path(game->info.movie_path); + check_iso |= !fs::exists(game->info.movie_path); + } - if (!fs::exists(game->info.movie_path) && is_file_iso(game->info.path)) - { - item->set_iso_path(game->info.path); - } + if (play_hover_music && game->has_audio_file) + { + item->set_audio_path(game->info.audio_path); + check_iso |= !fs::exists(game->info.audio_path); + } + + if (check_iso && is_file_iso(game->info.path)) + { + item->set_iso_path(game->info.path); } if (selected_item_ids.contains(game->info.path + game->info.icon_path)) diff --git a/rpcs3/rpcs3qt/game_list_grid.h b/rpcs3/rpcs3qt/game_list_grid.h index e9e5890e81..0aebbe0547 100644 --- a/rpcs3/rpcs3qt/game_list_grid.h +++ b/rpcs3/rpcs3qt/game_list_grid.h @@ -19,7 +19,8 @@ public: const std::map& notes_map, const std::map& title_map, const std::set& selected_item_ids, - bool play_hover_movies) override; + bool play_hover_movies, + bool play_hover_music) override; void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override; diff --git a/rpcs3/rpcs3qt/game_list_grid_item.cpp b/rpcs3/rpcs3qt/game_list_grid_item.cpp index 928a5fa264..a3952a264a 100644 --- a/rpcs3/rpcs3qt/game_list_grid_item.cpp +++ b/rpcs3/rpcs3qt/game_list_grid_item.cpp @@ -77,6 +77,7 @@ bool game_list_grid_item::event(QEvent* event) set_active(true); break; case QEvent::HoverLeave: + case QEvent::FocusOut: set_active(false); break; default: diff --git a/rpcs3/rpcs3qt/game_list_table.cpp b/rpcs3/rpcs3qt/game_list_table.cpp index 98b9ef344d..ec88561580 100644 --- a/rpcs3/rpcs3qt/game_list_table.cpp +++ b/rpcs3/rpcs3qt/game_list_table.cpp @@ -38,6 +38,7 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptrsetStretchLastSection(true); horizontalHeader()->setDefaultSectionSize(150); horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + horizontalHeader()->setSectionsMovable(true); setContextMenuPolicy(Qt::CustomContextMenu); setAlternatingRowColors(true); setColumnCount(static_cast(gui::game_list_columns::count)); @@ -54,7 +55,7 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptritem == item) item->image_change_callback(); }); @@ -72,6 +73,8 @@ void game_list_table::restore_layout(const QByteArray& state) // Nothing to do } + // Re-apply after restoreState() since it resets setSectionsMovable to false + horizontalHeader()->setSectionsMovable(true); // Make sure no columns are squished fix_narrow_columns(); @@ -207,7 +210,8 @@ void game_list_table::populate( const std::map& notes_map, const std::map& title_map, const std::set& selected_item_ids, - bool play_hover_movies) + bool play_hover_movies, + bool play_hover_music) { clear_list(); @@ -299,14 +303,23 @@ void game_list_table::populate( } }); + bool check_iso = false; + if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam)) { icon_item->set_video_path(game->info.movie_path); + check_iso |= !fs::exists(game->info.movie_path); + } - if (!fs::exists(game->info.movie_path) && is_file_iso(game->info.path)) - { - icon_item->set_iso_path(game->info.path); - } + if (play_hover_music && game->has_audio_file) + { + icon_item->set_audio_path(game->info.audio_path); + check_iso |= !fs::exists(game->info.audio_path); + } + + if (check_iso && is_file_iso(game->info.path)) + { + icon_item->set_iso_path(game->info.path); } icon_item->setData(Qt::UserRole, index, true); @@ -400,10 +413,21 @@ void game_list_table::populate( row++; } + bool first_index = true; + for (int selected_row : selected_rows) { - selectionModel()->select(model()->index(selected_row, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + const QModelIndex index = model()->index(selected_row, 0); + + selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + + if (first_index) + { + selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + first_index = false; + } } + Q_EMIT itemSelectionChanged(); } void game_list_table::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) diff --git a/rpcs3/rpcs3qt/game_list_table.h b/rpcs3/rpcs3qt/game_list_table.h index ac9bff64e5..24949e6baa 100644 --- a/rpcs3/rpcs3qt/game_list_table.h +++ b/rpcs3/rpcs3qt/game_list_table.h @@ -29,7 +29,8 @@ public: const std::map& notes_map, const std::map& title_map, const std::set& selected_item_ids, - bool play_hover_movies) override; + bool play_hover_movies, + bool play_hover_music) override; void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 67a750316e..bcc148d9a6 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -15,6 +15,7 @@ #include "Emu/Cell/Modules/cellScreenshot.h" #include "Emu/Cell/Modules/cellAudio.h" #include "Emu/Cell/lv2/sys_rsxaudio.h" +#include "Emu/RSX/RSXThread.h" #include "Emu/RSX/rsx_utils.h" #include "Emu/RSX/Overlays/overlay_message.h" #include "Emu/Io/interception.h" @@ -361,7 +362,7 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey { Emu.after_kill_callback = []() { - Emu.Restart(); + Emu.Restart(true, false); }; // Make sure we keep the game window opened @@ -792,7 +793,7 @@ f64 gs_frame::client_display_rate() { f64 rate = 20.; // Minimum is 20 - Emu.BlockingCallFromMainThread([this, &rate]() + Emu.BlockingCallFromMainThread([&rate]() { const QList screens = QGuiApplication::screens(); @@ -1040,7 +1041,7 @@ void gs_frame::take_screenshot(std::vector&& data, u32 sshot_width, u32 ssho if (new_size.width != static_cast(img.width()) || new_size.height != static_cast(img.height())) { - const int scale = rsx::get_resolution_scale_percent(); + const int scale = rsx::get_current_renderer()->resolution_scaling_config.scale_percent; const int x = (scale * manager.overlay_offset_x) / 100; const int y = (scale * manager.overlay_offset_y) / 100; const int width = (scale * overlay_img.width()) / 100; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 113f5a6ebb..8379e2f5e0 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -154,6 +154,12 @@ bool gui_application::Init() // Force init the emulator InitializeEmulator(m_active_user, m_show_gui); + // Create callbacks from the emulator, which reference the handlers. + InitializeCallbacks(); + + // Create connects to propagate events throughout Gui. + InitializeConnects(); + // Create the main window if (m_show_gui) { @@ -164,14 +170,23 @@ bool gui_application::Init() const auto index = codes.indexOf(language); LoadLanguage(index < 0 ? QLocale(QLocale::English).bcp47Name() : ::at32(codes, index)); + + connect(m_main_window, &main_window::RequestLanguageChange, this, &gui_application::LoadLanguage); + connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); + connect(m_main_window, &main_window::NotifyEmuSettingsChange, this, [this](){ OnEmuSettingsChange(); }); + connect(m_main_window, &main_window::NotifyShortcutHandlers, this, &gui_application::OnShortcutChange); + + connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); + connect(this, &gui_application::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); + connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause); + connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume); + connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); + connect(this, &gui_application::OnEnableDiscEject, m_main_window, &main_window::OnEnableDiscEject); + connect(this, &gui_application::OnEnableDiscInsert, m_main_window, &main_window::OnEnableDiscInsert); + + connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [this](){ OnChangeStyleSheetRequest(); }); } - // Create callbacks from the emulator, which reference the handlers. - InitializeCallbacks(); - - // Create connects to propagate events throughout Gui. - InitializeConnects(); - if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool()) { welcome_dialog* welcome = new welcome_dialog(m_gui_settings, false); @@ -438,24 +453,6 @@ void gui_application::InitializeConnects() connect(this, &gui_application::OnEmulatorResume, this, &gui_application::StartPlaytime); connect(this, &QGuiApplication::applicationStateChanged, this, &gui_application::OnAppStateChanged); - if (m_main_window) - { - connect(m_main_window, &main_window::RequestLanguageChange, this, &gui_application::LoadLanguage); - connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); - connect(m_main_window, &main_window::NotifyEmuSettingsChange, this, [this](){ OnEmuSettingsChange(); }); - connect(m_main_window, &main_window::NotifyShortcutHandlers, this, &gui_application::OnShortcutChange); - - connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); - connect(this, &gui_application::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); - connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause); - connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume); - connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); - connect(this, &gui_application::OnEnableDiscEject, m_main_window, &main_window::OnEnableDiscEject); - connect(this, &gui_application::OnEnableDiscInsert, m_main_window, &main_window::OnEnableDiscInsert); - - connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [this](){ OnChangeStyleSheetRequest(); }); - } - #ifdef WITH_DISCORD_RPC connect(this, &gui_application::OnEmulatorRun, [this](bool /*start_playtime*/) { @@ -740,8 +737,8 @@ void gui_application::InitializeCallbacks() callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; callbacks.get_osk_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; callbacks.get_save_dialog = []() -> std::unique_ptr { return std::make_unique(); }; - callbacks.get_sendmessage_dialog = [this]() -> std::shared_ptr { return std::make_shared(); }; - callbacks.get_recvmessage_dialog = [this]() -> std::shared_ptr { return std::make_shared(); }; + callbacks.get_sendmessage_dialog = []() -> std::shared_ptr { return std::make_shared(); }; + callbacks.get_recvmessage_dialog = []() -> std::shared_ptr { return std::make_shared(); }; callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr { return std::make_unique(m_game_window); }; callbacks.on_run = [this](bool start_playtime) { OnEmulatorRun(start_playtime); }; @@ -842,7 +839,7 @@ void gui_application::InitializeCallbacks() }; } - callbacks.on_emulation_stop_no_response = [this](std::shared_ptr> closed_successfully, int seconds_waiting_already) + callbacks.on_emulation_stop_no_response = [](std::shared_ptr> closed_successfully, int seconds_waiting_already) { const std::string terminate_message = tr("Stopping emulator took too long." "\nSome thread has probably deadlocked. Aborting.").toStdString(); @@ -852,7 +849,7 @@ void gui_application::InitializeCallbacks() report_fatal_error(terminate_message); } - Emu.CallFromMainThread([this, closed_successfully, seconds_waiting_already, terminate_message] + Emu.CallFromMainThread([closed_successfully, seconds_waiting_already, terminate_message] { const auto seconds = std::make_shared(seconds_waiting_already); diff --git a/rpcs3/rpcs3qt/gui_game_info.h b/rpcs3/rpcs3qt/gui_game_info.h index 08483fa7fb..693483dd6a 100644 --- a/rpcs3/rpcs3qt/gui_game_info.h +++ b/rpcs3/rpcs3qt/gui_game_info.h @@ -21,6 +21,7 @@ struct gui_game_info bool has_custom_icon = false; bool has_hover_gif = false; bool has_hover_pam = false; + bool has_audio_file = false; bool icon_in_archive = false; movie_item_base* item = nullptr; diff --git a/rpcs3/rpcs3qt/gui_settings.cpp b/rpcs3/rpcs3qt/gui_settings.cpp index 8f42a14dd7..fdbab7345e 100644 --- a/rpcs3/rpcs3qt/gui_settings.cpp +++ b/rpcs3/rpcs3qt/gui_settings.cpp @@ -17,6 +17,7 @@ namespace gui { QString stylesheet; bool custom_stylesheet_active = false; + f32 volume = 1.0f; QString get_savestate_list_column_name(savestate_list_columns col) { diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 21a46259c2..41be2732ab 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -13,6 +13,7 @@ namespace gui { extern QString stylesheet; extern bool custom_stylesheet_active; + extern f32 volume; enum custom_roles { @@ -139,6 +140,8 @@ namespace gui const QColor gl_icon_color = QColor(240, 240, 240, 255); + const gui_save gui_volume = gui_save(main_window, "guiVolume", 1.0f); + const gui_save rg_freeze = gui_save(main_window, "recentGamesFrozen", false); const gui_save rg_entries = gui_save(main_window, "recentGamesNames", QVariant::fromValue(q_pair_list())); @@ -170,7 +173,7 @@ namespace gui const gui_save fd_save_log = gui_save(main_window, "lastExplorePathSaveLog", ""); const gui_save mw_debugger = gui_save(main_window, "debuggerVisible", false); - const gui_save mw_logger = gui_save(main_window, "loggerVisible", true); + const gui_save mw_logger = gui_save(main_window, "loggerVisible", false); const gui_save mw_gamelist = gui_save(main_window, "gamelistVisible", true); const gui_save mw_toolBarVisible = gui_save(main_window, "toolBarVisible", true); const gui_save mw_titleBarsVisible = gui_save(main_window, "titleBarsVisible", true); @@ -217,6 +220,7 @@ namespace gui const gui_save gl_pref_gd_icon = gui_save(game_list, "pref_gd_icon", false); const gui_save gl_custom_icon = gui_save(game_list, "custom_icon", true); const gui_save gl_hover_gifs = gui_save(game_list, "hover_gifs", true); + const gui_save gl_hover_music = gui_save(game_list, "hover_music", true); const gui_save fs_emulator_dir_list = gui_save(fs, "emulator_dir_list", QStringList()); const gui_save fs_dev_hdd0_list = gui_save(fs, "dev_hdd0_list", QStringList()); @@ -315,7 +319,7 @@ namespace gui const gui_save sc_shortcuts = gui_save(sc, "shortcuts", QVariantMap()); - const gui_save nav_enabled = gui_save(navigation, "pad_input_enabled", false); + const gui_save nav_enabled = gui_save(navigation, "pad_input_enabled", true); const gui_save nav_global = gui_save(navigation, "allow_global_pad_input", false); } diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 68c6f45e2b..926949fb58 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -316,7 +316,7 @@ void kernel_explorer::update() add_solid_node(find_node(root, additional_nodes::process_info), QString::fromStdString(fmt::format("Process Info, Sdk Version: 0x%08x, PPC SEG: %#x, SFO Category: %s (Fake: %s)", g_ps3_process_info.sdk_ver, g_ps3_process_info.ppc_seg, Emu.GetCat(), Emu.GetFakeCat()))); - auto display_program_segments = [this](QTreeWidgetItem* tree, const ppu_module& m) + auto display_program_segments = [](QTreeWidgetItem* tree, const ppu_module& m) { for (usz i = 0; i < m.segs.size(); i++) { @@ -661,7 +661,7 @@ void kernel_explorer::update() const s32 prio = ppu.prio.load().prio; std::string prio_text = fmt::format("%4d", prio); prio_text = fmt::replace_all(prio_text, " ", " "); - + ppu_threads.emplace_back(prio, fmt::format(u8"PPU 0x%07x: PRIO: %s, “%s”Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, prio_text, *ppu.ppu_tname.load(), ppu.joiner.load(), status, ppu.state.load() , ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time))); }, idm::unlocked); diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index d05d3e52ee..ba4960aa73 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -38,6 +38,7 @@ private: { switch (id) { + case localized_string_id::RSX_OVERLAYS_SPINNER_NO_TEXT: return ""; case localized_string_id::RSX_OVERLAYS_TROPHY_BRONZE: return tr("You have earned a bronze trophy.\n%0", "Trophy text").arg(std::forward(args)...); case localized_string_id::RSX_OVERLAYS_TROPHY_SILVER: return tr("You have earned a silver trophy.\n%0", "Trophy text").arg(std::forward(args)...); case localized_string_id::RSX_OVERLAYS_TROPHY_GOLD: return tr("You have earned a gold trophy.\n%0", "Trophy text").arg(std::forward(args)...); @@ -180,6 +181,8 @@ private: case localized_string_id::CELL_NP_SENDMESSAGE_DIALOG_CONFIRMATION: return tr("Send message to %0 ?\n\nSubject:", "SENDMESSAGE_DIALOG").arg(std::forward(args)...); case localized_string_id::CELL_NP_SENDMESSAGE_DIALOG_CONFIRMATION_INVITE: return tr("Send invite to %0 ?\n\nSubject:", "SENDMESSAGE_DIALOG").arg(std::forward(args)...); case localized_string_id::CELL_NP_SENDMESSAGE_DIALOG_CONFIRMATION_ADD_FRIEND: return tr("Send friend request to %0 ?\n\nSubject:", "SENDMESSAGE_DIALOG").arg(std::forward(args)...); + case localized_string_id::CELL_NP_MESSAGE_INVITE_RECEIVED: return tr("Received an invite from %0").arg(std::forward(args)...); + case localized_string_id::CELL_NP_MESSAGE_OTHER_RECEIVED: return tr("Received a message from %0").arg(std::forward(args)...); case localized_string_id::RECORDING_ABORTED: return tr("Recording aborted!"); case localized_string_id::RPCN_NO_ERROR: return tr("RPCN: No Error"); case localized_string_id::RPCN_ERROR_INVALID_INPUT: return tr("RPCN: Invalid Input (Wrong Host/Port)"); @@ -225,6 +228,7 @@ private: case localized_string_id::HOME_MENU_SETTINGS_SAVE_BUTTON: return tr("Save"); case localized_string_id::HOME_MENU_SETTINGS_DISCARD: return tr("Discard the current settings' changes?"); case localized_string_id::HOME_MENU_SETTINGS_DISCARD_BUTTON: return tr("Discard"); + case localized_string_id::HOME_MENU_SETTINGS_RESET_BUTTON: return tr("To default"); case localized_string_id::HOME_MENU_SETTINGS_AUDIO: return tr("Audio"); case localized_string_id::HOME_MENU_SETTINGS_AUDIO_MASTER_VOLUME: return tr("Master Volume", "Audio"); case localized_string_id::HOME_MENU_SETTINGS_AUDIO_BACKEND: return tr("Audio Backend", "Audio"); @@ -233,10 +237,13 @@ private: case localized_string_id::HOME_MENU_SETTINGS_AUDIO_TIME_STRETCHING: return tr("Enable Time Stretching", "Audio"); case localized_string_id::HOME_MENU_SETTINGS_AUDIO_TIME_STRETCHING_THRESHOLD: return tr("Time Stretching Threshold", "Audio"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO: return tr("Video"); + case localized_string_id::HOME_MENU_SETTINGS_VIDEO_VSYNC: return tr("VSync", "Video"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO_FRAME_LIMIT: return tr("Frame Limit", "Video"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO_ANISOTROPIC_OVERRIDE: return tr("Anisotropic Filter Override", "Video"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO_OUTPUT_SCALING: return tr("Output Scaling", "Video"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO_RCAS_SHARPENING: return tr("FidelityFX CAS Sharpening Intensity", "Video"); + case localized_string_id::HOME_MENU_SETTINGS_VIDEO_RESOLUTION_SCALE_PERCENT: return tr("Resolution Scale", "Video"); + case localized_string_id::HOME_MENU_SETTINGS_VIDEO_RESOLUTION_SCALE_THRESHOLD: return tr("Resolution Scale Threshold", "Video"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO_STRETCH_TO_DISPLAY: return tr("Stretch To Display Area", "Video"); case localized_string_id::HOME_MENU_SETTINGS_VIDEO_STEREO_MODE: return tr("Stereo Mode", "Video"); case localized_string_id::HOME_MENU_SETTINGS_INPUT: return tr("Input"); @@ -253,6 +260,7 @@ private: case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_MAX_CPU_PREEMPTIONS: return tr("Max Power Saving CPU-Preemptions", "Advanced"); case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_ACCURATE_RSX_RESERVATION_ACCESS: return tr("Accurate RSX reservation access", "Advanced"); case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_SLEEP_TIMERS_ACCURACY: return tr("Sleep Timers Accuracy", "Advanced"); + case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_RSX_MEMORY_TILING: return tr("Handle RSX Memory Tiling", "Advanced"); case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_MAX_SPURS_THREADS: return tr("Max SPURS Threads", "Advanced"); case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_DRIVER_WAKE_UP_DELAY: return tr("Driver Wake-Up Delay", "Advanced"); case localized_string_id::HOME_MENU_SETTINGS_ADVANCED_VBLANK_FREQUENCY: return tr("VBlank Frequency", "Advanced"); @@ -266,7 +274,9 @@ private: case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_PRESSURE_INTENSITY_TOGGLE_HINT: return tr("Show Pressure Intensity Toggle Hint", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_ANALOG_LIMITER_TOGGLE_HINT: return tr( "Show Analog Limiter Toggle Hint", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_MOUSE_AND_KB_TOGGLE_HINT: return tr("Show Mouse And Keyboard Toggle Hint", "Overlays"); + case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_FATAL_ERROR_HINTS: return tr("Show Fatal Error Hints", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS: return tr("Record With Overlays", "Overlays"); + case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_PLAY_MUSIC_DURING_BOOT: return tr("Play music during boot sequence.", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY: return tr("Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE: return tr("Enable Performance Overlay", "Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE_FRAMERATE_GRAPH: return tr("Enable Framerate Graph", "Performance Overlay"); @@ -284,6 +294,7 @@ private: case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_MARGIN_Y: return tr("Vertical Margin", "Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_FONT_SIZE: return tr("Font Size", "Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_OPACITY: return tr("Opacity", "Performance Overlay"); + case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_USE_WINDOW_SPACE: return tr("Use Window Space", "Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_DEBUG: return tr("Debug"); case localized_string_id::HOME_MENU_SETTINGS_DEBUG_OVERLAY: return tr("Debug Overlay", "Debug"); case localized_string_id::HOME_MENU_SETTINGS_DEBUG_INPUT_OVERLAY: return tr("Input Debug Overlay", "Debug"); diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index cf570eea7a..61ca66a909 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -113,6 +113,8 @@ static gui_listener s_gui_listener; log_frame::log_frame(std::shared_ptr _gui_settings, QWidget* parent) : custom_dock_widget(tr("Log"), parent), m_gui_settings(std::move(_gui_settings)) { + setObjectName("logger"); + const int max_block_count_log = m_gui_settings->GetValue(gui::l_limit).toInt(); const int max_block_count_tty = m_gui_settings->GetValue(gui::l_limit_tty).toInt(); diff --git a/rpcs3/rpcs3qt/log_level_dialog.cpp b/rpcs3/rpcs3qt/log_level_dialog.cpp new file mode 100644 index 0000000000..3f5fd3c58d --- /dev/null +++ b/rpcs3/rpcs3qt/log_level_dialog.cpp @@ -0,0 +1,155 @@ +#include "stdafx.h" +#include "log_level_dialog.h" +#include "emu_settings.h" + +#include +#include +#include +#include +#include +#include + +LOG_CHANNEL(cfg_log, "CFG"); + +log_level_dialog::log_level_dialog(QWidget* parent, std::shared_ptr emu_settings) + : QDialog(parent), m_emu_settings(emu_settings) +{ + setWindowTitle(tr("Configure minimum log levels")); + setObjectName("log_level_dialog"); + setAttribute(Qt::WA_DeleteOnClose); + setMinimumSize(QSize(700, 400)); + + const std::set channels = logs::get_channels(); + std::vector> levels; + + const auto add_level = [&levels](logs::level level, const QString& localized) + { + levels.push_back(std::pair(fmt::format("%s", level), localized)); + }; + + add_level(logs::level::always, tr("Always")); + add_level(logs::level::fatal, tr("Fatal")); + add_level(logs::level::error, tr("Error")); + add_level(logs::level::todo, tr("Todo")); + add_level(logs::level::success, tr("Success")); + add_level(logs::level::warning, tr("Warning")); + add_level(logs::level::notice, tr("Notice")); + add_level(logs::level::trace, tr("Trace")); + + const std::map current_settings = m_emu_settings->GetMapSetting(emu_settings_type::Log); + for (const auto& [channel, level] : current_settings) + { + if (!channels.contains(channel)) + { + cfg_log.warning("log_level_dialog: Ignoring unknown channel '%s' found in config file.", channel); + } + } + + m_table = new QTableWidget(static_cast(channels.size()), 2, this); + m_table->setHorizontalHeaderLabels({ tr("Channel"), tr("Level") }); + + int i = 0; + for (const std::string& channel : channels) + { + QComboBox* combo = new QComboBox(); + + for (const auto& [level, localized] : levels) + { + combo->addItem(localized, QString::fromStdString(level)); + } + + connect(combo, &QComboBox::currentIndexChanged, combo, [this, combo, ch = channel](int index) + { + if (index < 0) return; + + const QVariant var = combo->itemData(index); + if (!var.canConvert()) return; + + std::map settings = m_emu_settings->GetMapSetting(emu_settings_type::Log); + + settings[ch] = var.toString().toStdString(); + + m_emu_settings->SetMapSetting(emu_settings_type::Log, settings); + }); + + m_table->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(channel))); + m_table->setCellWidget(i, 1, combo); + + i++; + } + + QLineEdit* filter_edit = new QLineEdit(this); + filter_edit->setPlaceholderText(tr("Filter channels")); + connect(filter_edit, &QLineEdit::textChanged, this, [this](const QString& text) + { + for (int i = 0; i < m_table->rowCount(); i++) + { + if (QTableWidgetItem* item = m_table->item(i, 0)) + { + m_table->setRowHidden(i, !text.isEmpty() && !item->text().contains(text, Qt::CaseInsensitive)); + } + } + }); + + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); + connect(button_box, &QDialogButtonBox::clicked, this, [this, button_box, old_settings = m_emu_settings->GetMapSetting(emu_settings_type::Log)](QAbstractButton* button) + { + if (button == button_box->button(QDialogButtonBox::Ok)) + { + accept(); + } + else if (button == button_box->button(QDialogButtonBox::Cancel)) + { + m_emu_settings->SetMapSetting(emu_settings_type::Log, old_settings); + reject(); + } + else if (button == button_box->button(QDialogButtonBox::RestoreDefaults)) + { + m_emu_settings->SetMapSetting(emu_settings_type::Log, m_emu_settings->GetMapSettingDefault(emu_settings_type::Log)); + reload_page(); + } + }); + + reload_page(); + + m_table->resizeColumnsToContents(); + m_table->horizontalHeader()->stretchLastSection(); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget(filter_edit); + layout->addWidget(m_table); + layout->addWidget(button_box); + setLayout(layout); +} + +log_level_dialog::~log_level_dialog() +{ +} + +void log_level_dialog::reload_page() +{ + const std::map settings = m_emu_settings->GetMapSetting(emu_settings_type::Log); + const QString def_str = QString::fromStdString(fmt::format("%s", logs::level::_default)); + + for (int i = 0; i < m_table->rowCount(); i++) + { + QTableWidgetItem* item = m_table->item(i, 0); + if (!item) continue; + + const std::string channel = item->text().toStdString(); + + if (QComboBox* combo = static_cast(m_table->cellWidget(i, 1))) + { + combo->blockSignals(true); + combo->setCurrentIndex(combo->findData(def_str)); + if (settings.contains(channel)) + { + if (const int index = combo->findData(QString::fromStdString(settings.at(channel))); index >= 0) + { + combo->setCurrentIndex(index); + } + } + combo->blockSignals(false); + } + } +} diff --git a/rpcs3/rpcs3qt/log_level_dialog.h b/rpcs3/rpcs3qt/log_level_dialog.h new file mode 100644 index 0000000000..87a0ccf8a4 --- /dev/null +++ b/rpcs3/rpcs3qt/log_level_dialog.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +class emu_settings; + +class log_level_dialog : public QDialog +{ +public: + log_level_dialog(QWidget* parent, std::shared_ptr emu_settings); + virtual ~log_level_dialog(); + +private: + void reload_page(); + + std::shared_ptr m_emu_settings; + QTableWidget* m_table = nullptr; +}; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 56e125ab11..2a366705ab 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -35,9 +35,9 @@ #include "camera_settings_dialog.h" #include "ps_move_tracker_dialog.h" #include "ipc_settings_dialog.h" -#include "shortcut_utils.h" #include "config_checker.h" #include "shortcut_dialog.h" +#include "steam_utils.h" #include "system_cmd_dialog.h" #include "emulated_pad_settings_dialog.h" #include "emulated_logitech_g27_settings_dialog.h" @@ -46,6 +46,7 @@ #include "welcome_dialog.h" #include "music_player_dialog.h" #include "sound_effect_manager_dialog.h" +#include "recording_settings_dialog.h" #include #include @@ -128,7 +129,7 @@ extern void qt_events_aware_op(int repeat_duration_ms, std::function wra } else { - QTimer::singleShot(repeat_duration_ms, *check_iteration); + QTimer::singleShot(repeat_duration_ms, event_loop, *check_iteration); } }); @@ -138,7 +139,7 @@ extern void qt_events_aware_op(int repeat_duration_ms, std::function wra event_loop = new QEventLoop(); // Queue event initially - QTimer::singleShot(0, *check_iteration); + QTimer::singleShot(0, event_loop, *check_iteration); // Event loop event_loop->exec(); @@ -213,8 +214,6 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::main_window, this, m_gui_settings); connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &main_window::handle_shortcut); - show(); // needs to be done before creating the thumbnail toolbar - // enable play options if a recent game exists const bool enable_play_last = !m_recent_game.actions.isEmpty() && m_recent_game.actions.first(); @@ -279,6 +278,8 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) update_gui_pad_thread(); + show(); + return true; } @@ -935,23 +936,41 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo } std::vector packages; + std::set shortcut_locations; bool precompile_caches = false; - bool create_desktop_shortcuts = false; - bool create_app_shortcut = false; + bool canceled = false; game_compatibility* compat = m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr; // Let the user choose the packages to install and select the order in which they shall be installed. pkg_install_dialog dlg(file_paths, compat, this); - connect(&dlg, &QDialog::accepted, this, [&]() + connect(&dlg, &QDialog::finished, this, [&](int result) { + if (result != QDialog::Accepted) + { + canceled = true; + return; + } + packages = dlg.get_paths_to_install(); precompile_caches = dlg.precompile_caches(); - create_desktop_shortcuts = dlg.create_desktop_shortcuts(); - create_app_shortcut = dlg.create_app_shortcut(); + + if (dlg.create_desktop_shortcuts()) + shortcut_locations.insert(gui::utils::shortcut_location::desktop); + + if (dlg.create_app_shortcut()) + shortcut_locations.insert(gui::utils::shortcut_location::applications); + + if (dlg.create_steam_shortcut()) + shortcut_locations.insert(gui::utils::shortcut_location::steam); }); dlg.exec(); + if (canceled) + { + return false; + } + if (!from_boot) { if (!m_gui_settings->GetBootConfirmation(this)) @@ -1124,7 +1143,7 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo if (!bootable_paths_installed.empty()) { - m_game_list_frame->AddRefreshedSlot([this, create_desktop_shortcuts, precompile_caches, create_app_shortcut, paths = std::move(bootable_paths_installed)](std::set& claimed_paths) mutable + m_game_list_frame->AddRefreshedSlot([this, shortcut_locations, precompile_caches, paths = std::move(bootable_paths_installed)](std::set& claimed_paths) mutable { // Try to claim operations on ID for (auto it = paths.begin(); it != paths.end();) @@ -1142,7 +1161,7 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo } } - CreateShortCuts(paths, create_desktop_shortcuts, create_app_shortcut); + CreateShortCuts(paths, shortcut_locations); if (precompile_caches) { @@ -1507,7 +1526,7 @@ void main_window::HandlePupInstallation(const QString& file_path, const QString& return; } - static constexpr std::string_view cur_version = "4.92"; + static constexpr std::string_view cur_version = "4.93"; std::string version_string; @@ -1716,7 +1735,7 @@ void main_window::DecryptSPRXLibraries() dlg->set_button_enabled(QDialogButtonBox::StandardButton::Ok, text.size() - (text.indexOf('x') + 1) == 32); }); - connect(dlg, &QDialog::accepted, this, [this, iterate, dlg, mod_index, decrypter, repeat_count]() + connect(dlg, &QDialog::accepted, this, [iterate, dlg, mod_index, decrypter, repeat_count]() { std::string text = dlg->get_input_text().toStdString(); @@ -1777,17 +1796,6 @@ void main_window::SaveWindowState() const } } -void main_window::RepaintThumbnailIcons() -{ - const QColor color = gui::utils::get_foreground_color(); - [[maybe_unused]] const QColor new_color = gui::utils::get_label_color("thumbnail_icon_color", color, color); - - [[maybe_unused]] const auto icon = [&new_color](const QString& path) - { - return gui::utils::get_colorized_icon(QPixmap::fromImage(gui::utils::get_opaque_image_area(path)), Qt::black, new_color); - }; -} - void main_window::RepaintToolBarIcons() { const QColor color = gui::utils::get_foreground_color(); @@ -2293,7 +2301,6 @@ void main_window::RepaintGui() } RepaintToolBarIcons(); - RepaintThumbnailIcons(); Q_EMIT RequestDialogRepaint(); } @@ -2358,6 +2365,7 @@ void main_window::ShowOptionalGamePreparations(const QString& title, const QStri #else QCheckBox* quick_check = new QCheckBox(tr("Add launcher shortcut(s)")); #endif + QLabel* label = new QLabel(tr("%1\nWould you like to precompile caches and install shortcuts to the installed software? (%2 new software detected)\n\n").arg(message).arg(bootable_paths.size()), dlg); vlayout->addWidget(label); @@ -2369,6 +2377,17 @@ void main_window::ShowOptionalGamePreparations(const QString& title, const QStri vlayout->addWidget(quick_check); vlayout->addStretch(3); + QCheckBox* steam_check = nullptr; + if (gui::utils::steam_shortcut::steam_installed()) + { + const bool steam_running = gui::utils::steam_shortcut::is_steam_running(); + steam_check = new QCheckBox(steam_running ? tr("Add Steam Shortcut(s) (Steam must be closed)") : tr("Add Steam shortcut(s)")); + steam_check->setEnabled(!steam_running); + + vlayout->addWidget(steam_check); + vlayout->addStretch(3); + } + QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok); vlayout->addWidget(btn_box); @@ -2377,13 +2396,22 @@ void main_window::ShowOptionalGamePreparations(const QString& title, const QStri connect(btn_box, &QDialogButtonBox::accepted, this, [=, this, paths = std::move(bootable_paths)]() { const bool precompile_caches = precompile_check->isChecked(); - const bool create_desktop_shortcuts = desk_check->isChecked(); - const bool create_app_shortcut = quick_check->isChecked(); + + std::set shortcut_locations; + + if (desk_check->isChecked()) + shortcut_locations.insert(gui::utils::shortcut_location::desktop); + + if (quick_check->isChecked()) + shortcut_locations.insert(gui::utils::shortcut_location::applications); + + if (steam_check && steam_check->isChecked()) + shortcut_locations.insert(gui::utils::shortcut_location::steam); dlg->hide(); dlg->accept(); - CreateShortCuts(paths, create_desktop_shortcuts, create_app_shortcut); + CreateShortCuts(paths, shortcut_locations); if (precompile_caches) { @@ -2395,52 +2423,40 @@ void main_window::ShowOptionalGamePreparations(const QString& title, const QStri dlg->open(); } -void main_window::CreateShortCuts(const std::map& paths, bool create_desktop_shortcuts, bool create_app_shortcut) +void main_window::CreateShortCuts(const std::map& paths, std::set locations) { if (paths.empty()) return; - std::set locations; - #ifdef _WIN32 locations.insert(gui::utils::shortcut_location::rpcs3_shortcuts); +#else + if (locations.empty()) + { + return; + } #endif - if (create_desktop_shortcuts) - { - locations.insert(gui::utils::shortcut_location::desktop); - } - if (create_app_shortcut) - { - locations.insert(gui::utils::shortcut_location::applications); - } + std::vector game_data_shortcuts; - if (!locations.empty()) + for (const auto& [boot_path, title_id] : paths) { - std::vector game_data_shortcuts; - - for (const auto& [boot_path, title_id] : paths) + for (const game_info& gameinfo : m_game_list_frame->GetGameInfo()) { - for (const game_info& gameinfo : m_game_list_frame->GetGameInfo()) + if (gameinfo && gameinfo->info.serial == title_id.toStdString()) { - if (gameinfo && gameinfo->info.serial == title_id.toStdString()) + if (Emu.IsPathInsideDir(boot_path, gameinfo->info.path)) { - if (Emu.IsPathInsideDir(boot_path, gameinfo->info.path)) - { - if (!locations.empty()) - { - game_data_shortcuts.push_back(gameinfo); - } - } - - break; + game_data_shortcuts.push_back(gameinfo); } + + break; } } + } - if (!game_data_shortcuts.empty() && !locations.empty()) - { - m_game_list_frame->actions()->CreateShortcuts(game_data_shortcuts, locations); - } + if (!game_data_shortcuts.empty()) + { + m_game_list_frame->actions()->CreateShortcuts(game_data_shortcuts, locations); } } @@ -2523,7 +2539,7 @@ void main_window::CreateConnects() { Emu.after_kill_callback = []() { - Emu.Restart(); + Emu.Restart(true, false); }; // Make sure we keep the game window opened @@ -3105,16 +3121,22 @@ void main_window::CreateConnects() connect(ui->actionManage_Screenshots, &QAction::triggered, this, [this] { - screenshot_manager_dialog* screenshot_manager = new screenshot_manager_dialog(); + screenshot_manager_dialog* screenshot_manager = new screenshot_manager_dialog(m_game_list_frame ? m_game_list_frame->GetGameInfo() : std::vector{}); screenshot_manager->show(); }); - connect(ui->actionManage_SoundEffects, &QAction::triggered, this, [this] + connect(ui->actionManage_SoundEffects, &QAction::triggered, this, [] { sound_effect_manager_dialog* dlg = new sound_effect_manager_dialog(); dlg->show(); }); + connect(ui->actionRecording, &QAction::triggered, this, [this] + { + recording_settings_dialog* dlg = new recording_settings_dialog(this); + dlg->open(); + }); + connect(ui->toolsCgDisasmAct, &QAction::triggered, this, [this] { cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings); @@ -3361,7 +3383,7 @@ void main_window::CreateConnects() welcome->open(); }); - connect(ui->supportAct, &QAction::triggered, this, [this] + connect(ui->supportAct, &QAction::triggered, this, [] { QDesktopServices::openUrl(QUrl("https://rpcs3.net/patreon")); }); @@ -3397,6 +3419,7 @@ void main_window::CreateConnects() connect(ui->actionPreferGameDataIcons, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPreferGameDataIcons); connect(ui->showCustomIconsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetShowCustomIcons); connect(ui->playHoverGifsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPlayHoverGifs); + connect(ui->playHoverMusicAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPlayHoverMusic); connect(m_game_list_frame, &game_list_frame::RequestIconSizeChange, this, [this](int val) { @@ -3489,11 +3512,8 @@ void main_window::CreateDockWindows() m_mw->setContextMenuPolicy(Qt::PreventContextMenu); m_game_list_frame = new game_list_frame(m_gui_settings, m_emu_settings, m_persistent_settings, m_mw); - m_game_list_frame->setObjectName("gamelist"); m_debugger_frame = new debugger_frame(m_gui_settings, m_mw); - m_debugger_frame->setObjectName("debugger"); m_log_frame = new log_frame(m_gui_settings, m_mw); - m_log_frame->setObjectName("logger"); m_mw->addDockWidget(Qt::LeftDockWidgetArea, m_game_list_frame); m_mw->addDockWidget(Qt::LeftDockWidgetArea, m_log_frame); @@ -3647,6 +3667,8 @@ void main_window::ConfigureGuiFromSettings() m_recent_game.entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rg_entries)); m_recent_save.entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rs_entries)); + gui::volume = std::clamp(m_gui_settings->GetValue(gui::gui_volume).toFloat() * 100.0f, 0.0f, 100.0f); + const auto update_recent_games_menu = [this](bool is_savestate) { recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game; @@ -3703,6 +3725,7 @@ void main_window::ConfigureGuiFromSettings() ui->actionPreferGameDataIcons->setChecked(m_gui_settings->GetValue(gui::gl_pref_gd_icon).toBool()); ui->showCustomIconsAct->setChecked(m_gui_settings->GetValue(gui::gl_custom_icon).toBool()); ui->playHoverGifsAct->setChecked(m_gui_settings->GetValue(gui::gl_hover_gifs).toBool()); + ui->playHoverMusicAct->setChecked(m_gui_settings->GetValue(gui::gl_hover_music).toBool()); m_is_list_mode = m_gui_settings->GetValue(gui::gl_listMode).toBool(); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 588c6e6918..e9206070cf 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -10,6 +10,7 @@ #include "update_manager.h" #include "settings.h" #include "shortcut_handler.h" +#include "shortcut_utils.h" #include "Emu/config_mode.h" #include "Emu/System.h" @@ -133,7 +134,6 @@ protected: private: void ConfigureGuiFromSettings(); void RepaintToolBarIcons(); - void RepaintThumbnailIcons(); void CreateActions(); void CreateConnects(); void CreateDockWindows(); @@ -145,7 +145,7 @@ private: static bool InstallFileInExData(const std::string& extension, const QString& path, const std::string& filename); bool HandlePackageInstallation(QStringList file_paths, bool from_boot); - void CreateShortCuts(const std::map& paths, bool create_desktop_shortcuts, bool create_app_shortcut); + void CreateShortCuts(const std::map& paths, std::set locations); void HandlePupInstallation(const QString& file_path, const QString& dir_path = ""); void ExtractPup(); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 1f6a75c6f7..7a7c965ce8 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -290,6 +290,7 @@ + @@ -372,6 +373,7 @@ + @@ -1379,7 +1381,7 @@ - Shortcuts + Keyboard Shortcuts @@ -1524,6 +1526,22 @@ Boot ISO + + + true + + + true + + + Play Hover Music + + + + + Recording + + diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index bcaa6537ef..0c779a9558 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -504,6 +504,8 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr buttons; - if (append_key) + std::vector combos; + if (mode != binding_mode::single) { - buttons = cfg_pad::get_buttons(keys); + combos = cfg_pad::get_combos(m_button_string); } - buttons.push_back(key); - keys = cfg_pad::get_buttons(std::move(buttons)); - text = QString::fromStdString(keys).replace(",", ", "); + if (combos.empty() || mode != binding_mode::combo) + { + combos.push_back(pad::combo({button})); + } + else if (mode == binding_mode::combo) + { + combos.back().add_button(button); + } + + update(cfg_pad::get_button_string(combos)); +} + +void pad_settings_dialog::pad_button::update(const std::string& button_string) +{ + m_button_string = button_string; + QString new_text = QString::fromStdString(button_string); + m_text = new_text.replace(",", ", ").replace("&", " + "); } pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_settings, QWidget* parent, const GameInfo* game) @@ -537,16 +552,95 @@ void pad_settings_dialog::InitButtons() // Enable Button Remapping update_preview(data.pad_name, true, data.battery_level, data.preview_values[0], data.preview_values[1], data.preview_values[2], data.preview_values[3], data.preview_values[4], data.preview_values[5], data.capabilities); + static Timer s_first_input_timer = {}; + static std::map s_pressed_buttons; + static std::array, 2> s_pressed_sticks = {}; + static u32 s_button_id = button_ids::id_pad_begin; + + const u32 button_id = m_button_id; + + if (s_button_id != button_id) + { + s_button_id = button_id; + s_pressed_buttons.clear(); + s_pressed_sticks = {}; + s_first_input_timer.Stop(); + } + // Handle Button Presses for (const input_callback_data::input_values& values : data.values) { - if (values.val <= 0) continue; - - cfg_log.notice("get_next_button_press: %s device %s button %s pressed with value %d", m_handler->m_type, data.pad_name, values.button_name, values.val); - - if (m_button_id > button_ids::id_pad_begin && m_button_id < button_ids::id_pad_end && m_button_id == values.button_id) + for (const auto& [key, value] : values.buttons) { - m_cfg_entries[m_button_id].insert_key(values.button_name, m_enable_multi_binding); + if (value == 0) continue; + + cfg_log.notice("get_next_button_press: %s device %s button %s pressed with value %d", m_handler->m_type, data.pad_name, key, value); + + if (button_id > button_ids::id_pad_begin && button_id < button_ids::id_pad_end && button_id == values.button_id) + { + if (s_pressed_buttons.empty()) + { + s_first_input_timer.Start(); + } + + u16& val = s_pressed_buttons[key]; + val = std::max(val, value); + } + } + + for (usz i = 0; i < values.sticks.size(); i++) + { + const auto& [key, value] = values.sticks[i]; + + if (value == 0) continue; + + cfg_log.notice("get_next_button_press: %s device %s button %s pressed with value %d", m_handler->m_type, data.pad_name, key, value); + + if (button_id > button_ids::id_pad_begin && button_id < button_ids::id_pad_end && button_id == values.button_id) + { + if (s_pressed_sticks[i].second == 0) + { + s_first_input_timer.Start(); + } + + if (value > s_pressed_sticks[i].second) + { + s_pressed_sticks[i] = {key, value}; + } + } + } + } + + if (button_id > button_ids::id_pad_begin && button_id < button_ids::id_pad_end && (!s_pressed_buttons.empty() || s_pressed_sticks[0].second || s_pressed_sticks[1].second)) + { + const double elapsed_ms = s_first_input_timer.GetElapsedTimeInMilliSec(); + if (elapsed_ms > 100.0) + { + binding_mode mode = m_binding_mode; + + for (const auto& [key, value] : s_pressed_buttons) + { + if (value == 0) continue; + + m_cfg_entries[m_button_id].insert_button(key, mode); + + // Switch to combo mode for all further keys + mode = binding_mode::combo; + } + + for (const auto& [key, value] : s_pressed_sticks) + { + if (value == 0) continue; + + m_cfg_entries[m_button_id].insert_button(key, mode); + + // Switch to combo mode for all further keys + mode = binding_mode::combo; + } + + s_pressed_buttons.clear(); + s_pressed_sticks = {}; + s_first_input_timer.Stop(); ReactivateButtons(); } } @@ -580,16 +674,16 @@ void pad_settings_dialog::InitButtons() const std::vector buttons = { - m_cfg_entries[button_ids::id_pad_l2].keys, - m_cfg_entries[button_ids::id_pad_r2].keys, - m_cfg_entries[button_ids::id_pad_lstick_left].keys, - m_cfg_entries[button_ids::id_pad_lstick_right].keys, - m_cfg_entries[button_ids::id_pad_lstick_down].keys, - m_cfg_entries[button_ids::id_pad_lstick_up].keys, - m_cfg_entries[button_ids::id_pad_rstick_left].keys, - m_cfg_entries[button_ids::id_pad_rstick_right].keys, - m_cfg_entries[button_ids::id_pad_rstick_down].keys, - m_cfg_entries[button_ids::id_pad_rstick_up].keys + m_cfg_entries[button_ids::id_pad_l2].button_string(), + m_cfg_entries[button_ids::id_pad_r2].button_string(), + m_cfg_entries[button_ids::id_pad_lstick_left].button_string(), + m_cfg_entries[button_ids::id_pad_lstick_right].button_string(), + m_cfg_entries[button_ids::id_pad_lstick_down].button_string(), + m_cfg_entries[button_ids::id_pad_lstick_up].button_string(), + m_cfg_entries[button_ids::id_pad_rstick_left].button_string(), + m_cfg_entries[button_ids::id_pad_rstick_right].button_string(), + m_cfg_entries[button_ids::id_pad_rstick_down].button_string(), + m_cfg_entries[button_ids::id_pad_rstick_up].button_string() }; // Check if this is the first call during a remap @@ -599,7 +693,7 @@ void pad_settings_dialog::InitButtons() const PadHandlerBase::gui_call_type call_type = first_call ? PadHandlerBase::gui_call_type::reset_input : PadHandlerBase::gui_call_type::normal; const PadHandlerBase::connection status = m_handler->get_next_button_press(m_device_name, - [this, button_id](u16 val, std::string button_name, std::string pad_name, u32 battery_level, pad_preview_values preview_values, pad_capabilities capabilities) + [this, button_id](std::map&& pressed_buttons, std::array, 2>&& pressed_sticks, std::string pad_name, u32 battery_level, pad_preview_values&& preview_values, pad_capabilities&& capabilities) { std::lock_guard lock(m_input_mutex); if (m_input_callback_data.pad_name != pad_name) @@ -612,13 +706,13 @@ void pad_settings_dialog::InitButtons() m_input_callback_data.capabilities = std::move(capabilities); m_input_callback_data.has_new_data = true; m_input_callback_data.status = PadHandlerBase::connection::connected; - if (val > 0) + if (!pressed_buttons.empty() || !pressed_sticks.empty()) { m_input_callback_data.values.push_back(input_callback_data::input_values { - .button_name = std::move(button_name), .button_id = button_id, - .val = val, + .buttons = std::move(pressed_buttons), + .sticks = std::move(pressed_sticks) }); } }, @@ -723,10 +817,10 @@ void pad_settings_dialog::ReloadButtons() { m_cfg_entries.clear(); - auto updateButton = [this](int id, QPushButton* button, cfg::string* cfg_text) + const auto updateButton = [this](int id, QPushButton* button, cfg::string* cfg_text) { const QString text = QString::fromStdString(*cfg_text); - m_cfg_entries.insert(std::make_pair(id, pad_button{cfg_text, *cfg_text, text})); + m_cfg_entries.insert(std::make_pair(id, pad_button(cfg_text))); button->setText(text); }; @@ -775,7 +869,7 @@ void pad_settings_dialog::ReactivateButtons() { m_remap_timer.stop(); m_seconds = MAX_SECONDS; - m_enable_multi_binding = false; + m_binding_mode = binding_mode::single; if (m_button_id == button_ids::id_pad_begin) { @@ -903,7 +997,7 @@ void pad_settings_dialog::RepaintPreviewLabel(QLabel* label, int deadzone, int a label->setPixmap(pixmap); } -void pad_settings_dialog::keyPressEvent(QKeyEvent *keyEvent) +void pad_settings_dialog::keyPressEvent(QKeyEvent* keyEvent) { if (m_button_id == button_ids::id_pad_begin) { @@ -929,7 +1023,7 @@ void pad_settings_dialog::keyPressEvent(QKeyEvent *keyEvent) } else { - m_cfg_entries[m_button_id].insert_key(keyboard_pad_handler::GetKeyName(keyEvent, false), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_button(keyboard_pad_handler::GetKeyName(keyEvent, false), m_binding_mode); } ReactivateButtons(); @@ -956,13 +1050,13 @@ void pad_settings_dialog::mouseReleaseEvent(QMouseEvent* event) } else { - m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(event), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_button((static_cast(m_handler.get()))->GetMouseName(event), m_binding_mode); } ReactivateButtons(); } -void pad_settings_dialog::wheelEvent(QWheelEvent *event) +void pad_settings_dialog::wheelEvent(QWheelEvent* event) { if (m_button_id == button_ids::id_pad_begin) { @@ -1018,7 +1112,7 @@ void pad_settings_dialog::wheelEvent(QWheelEvent *event) } } - m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(key), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_button((static_cast(m_handler.get()))->GetMouseName(key), m_binding_mode); ReactivateButtons(); } @@ -1069,7 +1163,7 @@ void pad_settings_dialog::mouseMoveEvent(QMouseEvent* event) if (key != 0) { - m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(key), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_button((static_cast(m_handler.get()))->GetMouseName(key), m_binding_mode); ReactivateButtons(); } } @@ -1090,8 +1184,7 @@ bool pad_settings_dialog::eventFilter(QObject* object, QEvent* event) if (const int button_id = m_pad_buttons->id(button); m_cfg_entries.contains(button_id)) { pad_button& button = m_cfg_entries[button_id]; - button.keys.clear(); - button.text.clear(); + button.update(""); UpdateLabels(); return true; @@ -1279,14 +1372,13 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) { if (is_reset) { - button.keys = *button.cfg_text; - button.text = QString::fromStdString(button.keys); + button.update(*button.cfg_text()); } // The button has to contain at least one character, because it would be square'ish otherwise if (auto btn = m_pad_buttons->button(id)) { - btn->setText(button.text.isEmpty() ? QStringLiteral("-") : button.text); + btn->setText(button.text().isEmpty() ? QStringLiteral("-") : button.text()); } } } @@ -1358,7 +1450,11 @@ void pad_settings_dialog::OnPadButtonClicked(int id) // On shift+click or shift+space enable multi key binding if (QApplication::keyboardModifiers() & Qt::KeyboardModifier::ShiftModifier) { - m_enable_multi_binding = true; + m_binding_mode = binding_mode::multi; + } + else if (QApplication::keyboardModifiers() & Qt::KeyboardModifier::ControlModifier) + { + m_binding_mode = binding_mode::combo; } // On alt+click or alt+space allow to handle triggers as the entire stick axis @@ -2006,7 +2102,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) return; } - m_duplicate_buttons[m_last_player_id].clear(); + m_duplicate_combos[m_last_player_id].clear(); auto& player = g_cfg_input.player[m_last_player_id]; m_last_player_id = new_player_id; @@ -2014,7 +2110,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) // Check for duplicate button choices if (m_handler->m_type != pad_handler::null) { - std::set unique_keys; + std::set unique_combo_strings; for (const auto& [id, button] : m_cfg_entries) { // Let's ignore special keys, unless we're using a keyboard @@ -2024,11 +2120,13 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) continue; } - for (const std::string& key : cfg_pad::get_buttons(button.keys)) + for (const pad::combo& combo : cfg_pad::get_combos(button.button_string())) { - if (const auto& [it, ok] = unique_keys.insert(key); !ok) + std::string combo_string = combo.to_string(); + + if (const auto& [it, ok] = unique_combo_strings.insert(combo_string); !ok) { - m_duplicate_buttons[m_last_player_id] = key; + m_duplicate_combos[m_last_player_id] = std::move(combo_string); break; } } @@ -2038,7 +2136,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) // Apply buttons for (const auto& entry : m_cfg_entries) { - entry.second.cfg_text->from_string(entry.second.keys); + entry.second.cfg_text()->from_string(entry.second.button_string()); } // Apply rest of config @@ -2107,27 +2205,29 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.product_id.set(info.product_id); } -void pad_settings_dialog::save(bool check_duplicates) +bool pad_settings_dialog::save(bool check_duplicates) { ApplyCurrentPlayerConfig(m_last_player_id); if (check_duplicates) { - for (const auto& [player_id, key] : m_duplicate_buttons) + for (const auto& [player_id, combo] : m_duplicate_combos) { - if (!key.empty()) + if (!combo.empty()) { int result = QMessageBox::Yes; m_gui_settings->ShowConfirmationBox( tr("Warning!"), - tr("The %0 button %1 of Player %2 was assigned at least twice.
Please consider adjusting the configuration.

Continue anyway?
") + tr("The %0 button or combo %1 of Player %2 was assigned at least twice.
Please consider adjusting the configuration.

Continue anyway?
") .arg(QString::fromStdString(g_cfg_input.player[player_id]->handler.to_string())) - .arg(QString::fromStdString(key)) + .arg(QString::fromStdString(combo)) .arg(player_id + 1), gui::ib_same_buttons, &result, this); if (result == QMessageBox::No) - return; + { + return false; + } break; } @@ -2140,13 +2240,16 @@ void pad_settings_dialog::save(bool check_duplicates) g_cfg_input_configs.save(); g_cfg_input.save(m_title_id, m_config_file); + + return true; } void pad_settings_dialog::SaveExit() { - save(true); - - QDialog::accept(); + if (save(true)) + { + QDialog::accept(); + } } void pad_settings_dialog::CancelExit() diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 0e31b0a297..4f20d2d0db 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -78,13 +78,32 @@ class pad_settings_dialog : public QDialog id_remove_config_file }; + enum class binding_mode + { + single, + multi, + combo + }; + struct pad_button { - cfg::string* cfg_text = nullptr; - std::string keys; - QString text; + pad_button() {} + pad_button(cfg::string* cfg_text) : m_cfg_text(ensure(cfg_text)) + { + update(*cfg_text); + } - void insert_key(const std::string& key, bool append_key); + void insert_button(const std::string& button, binding_mode mode); + void update(const std::string& button_string); + + cfg::string* cfg_text() const { return m_cfg_text; } + const std::string& button_string() const { return m_button_string; } + const QString& text() const { return m_text; } + + private: + cfg::string* m_cfg_text = nullptr; + std::string m_button_string; + QString m_text; }; const QString Disconnected_suffix = tr(" (disconnected)"); @@ -133,7 +152,7 @@ private: QButtonGroup* m_pad_buttons = nullptr; atomic_t m_button_id = button_ids::id_pad_begin; std::map m_cfg_entries; - std::map m_duplicate_buttons; + std::map m_duplicate_combos; // Real time stick values int m_lx = 0; @@ -161,7 +180,7 @@ private: static constexpr int MAX_SECONDS = 5; int m_seconds = MAX_SECONDS; QTimer m_remap_timer; - bool m_enable_multi_binding = false; + binding_mode m_binding_mode = binding_mode::single; // Mouse Move QPoint m_last_pos; @@ -180,9 +199,9 @@ private: struct input_values { - std::string button_name; u32 button_id = button_ids::id_pad_begin; - u16 val = 0; + std::map buttons; + std::array, 2> sticks{}; }; std::vector values; } m_input_callback_data; @@ -197,7 +216,7 @@ private: std::pair get_config_files(); - void save(bool check_duplicates); + bool save(bool check_duplicates); void SaveExit(); void CancelExit(); @@ -254,10 +273,10 @@ protected: void showEvent(QShowEvent* event) override; /** Handle keyboard handler input */ - void keyPressEvent(QKeyEvent *keyEvent) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void wheelEvent(QWheelEvent *event) override; + void keyPressEvent(QKeyEvent* keyEvent) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void wheelEvent(QWheelEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; void closeEvent(QCloseEvent* event) override; }; diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index a41d8ed7b5..2e9d980d20 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -1163,7 +1163,7 @@ void patch_manager_dialog::download_update(bool automatic, bool auto_accept) } } - m_downloader->start(url, true, !m_download_automatic, tr("Downloading latest patches")); + m_downloader->start(url, true, !m_download_automatic, true, tr("Downloading latest patches")); } bool patch_manager_dialog::handle_json(const QByteArray& data) diff --git a/rpcs3/rpcs3qt/pkg_install_dialog.cpp b/rpcs3/rpcs3qt/pkg_install_dialog.cpp index 9b0f926484..3d4193d4e5 100644 --- a/rpcs3/rpcs3qt/pkg_install_dialog.cpp +++ b/rpcs3/rpcs3qt/pkg_install_dialog.cpp @@ -3,6 +3,7 @@ #include "numbered_widget_item.h" #include "richtext_item_delegate.h" #include "qt_utils.h" +#include "steam_utils.h" #include "Emu/system_utils.hpp" #include "Utilities/File.h" @@ -194,6 +195,17 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil vbox->addWidget(precompile_check); vbox->addWidget(desk_check); vbox->addWidget(quick_check); + + if (gui::utils::steam_shortcut::steam_installed()) + { + const bool steam_running = gui::utils::steam_shortcut::is_steam_running(); + QCheckBox* steam_check = new QCheckBox(steam_running ? tr("Add Steam Shortcut(s) (Steam must be closed)") : tr("Add Steam shortcut(s)")); + connect(steam_check, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state){ m_create_steam_shortcut = state != Qt::CheckState::Unchecked; }); + steam_check->setEnabled(!steam_running); + + vbox->addWidget(steam_check); + } + vbox->addWidget(installation_info); vbox->addWidget(buttons); diff --git a/rpcs3/rpcs3qt/pkg_install_dialog.h b/rpcs3/rpcs3qt/pkg_install_dialog.h index f1a623009d..b1f3dbeb9d 100644 --- a/rpcs3/rpcs3qt/pkg_install_dialog.h +++ b/rpcs3/rpcs3qt/pkg_install_dialog.h @@ -22,6 +22,7 @@ public: bool precompile_caches() const { return m_precompile_caches; } bool create_desktop_shortcuts() const { return m_create_desktop_shortcuts; } bool create_app_shortcut() const { return m_create_app_shortcut; } + bool create_steam_shortcut() const { return m_create_steam_shortcut; } private: void update_info(QLabel* installation_info, QDialogButtonBox* buttons) const; @@ -31,4 +32,5 @@ private: bool m_precompile_caches = false; bool m_create_desktop_shortcuts = false; bool m_create_app_shortcut = false; + bool m_create_steam_shortcut = false; }; diff --git a/rpcs3/rpcs3qt/ps_move_tracker_dialog.cpp b/rpcs3/rpcs3qt/ps_move_tracker_dialog.cpp index f00d66245a..218ccfc809 100644 --- a/rpcs3/rpcs3qt/ps_move_tracker_dialog.cpp +++ b/rpcs3/rpcs3qt/ps_move_tracker_dialog.cpp @@ -405,6 +405,8 @@ void ps_move_tracker_dialog::reset_camera() m_tracker_thread.reset(QThread::create([this]() { + thread_base::set_name("PS Move Tracker"); + while (!m_stop_threads) { process_camera_frame(); diff --git a/rpcs3/rpcs3qt/qt_camera_video_sink.cpp b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp index 3dee6fa6e3..2c830b5ef3 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_sink.cpp +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp @@ -3,8 +3,6 @@ #include "Emu/system_config.h" -#include - LOG_CHANNEL(camera_log, "Camera"); qt_camera_video_sink::qt_camera_video_sink(bool front_facing, QObject *parent) diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index 73dbaa87b6..ede9f6be9a 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -272,71 +272,49 @@ namespace gui .arg(text.replace("\n", "
")); } - QPixmap get_centered_pixmap(QPixmap pixmap, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode) + QPixmap get_aligned_pixmap(QPixmap pixmap, const QSize& icon_size, qreal device_pixel_ratio, Qt::TransformationMode mode, align_h h_alignment, align_v v_alignment) { // Create empty canvas for expanded image QPixmap exp_img(icon_size); exp_img.setDevicePixelRatio(device_pixel_ratio); exp_img.fill(Qt::transparent); + if (pixmap.isNull()) + { + return exp_img; + } + // Load scaled pixmap pixmap = pixmap.scaled(icon_size, Qt::KeepAspectRatio, mode); - // Define offset for raw image placement - QPoint offset(offset_x + icon_size.width() / 2 - pixmap.width() / 2, - offset_y + icon_size.height() / 2 - pixmap.height() / 2); + QRect target(QPoint(0, 0), pixmap.size()); + + switch (h_alignment) + { + case align_h::left: target.moveLeft(0); break; + case align_h::center: target.moveCenter(QPoint(icon_size.width() / 2, target.center().y())); break; + case align_h::right: target.moveRight(icon_size.width()); break; + } + + switch (v_alignment) + { + case align_v::top: target.moveTop(0); break; + case align_v::center: target.moveCenter(QPoint(target.center().x(), icon_size.height() / 2)); break; + case align_v::bottom: target.moveBottom(icon_size.height()); break; + } // Place raw image inside expanded image QPainter painter(&exp_img); painter.setRenderHint(QPainter::SmoothPixmapTransform); - painter.drawPixmap(offset, pixmap); + painter.drawPixmap(target, pixmap); painter.end(); return exp_img; } - QPixmap get_centered_pixmap(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode) + QPixmap get_aligned_pixmap(const QString& path, const QSize& icon_size, qreal device_pixel_ratio, Qt::TransformationMode mode, align_h h_alignment, align_v v_alignment) { - return get_centered_pixmap(QPixmap(path), icon_size, offset_x, offset_y, device_pixel_ratio, mode); - } - - QImage get_opaque_image_area(const QString& path) - { - QImage image = QImage(path); - - int w_min = 0; - int w_max = image.width(); - int h_min = 0; - int h_max = image.height(); - - for (int y = 0; y < image.height(); ++y) - { - const QRgb* row = reinterpret_cast(image.constScanLine(y)); - bool row_filled = false; - - for (int x = 0; x < image.width(); ++x) - { - if (qAlpha(row[x])) - { - row_filled = true; - w_min = std::max(w_min, x); - - if (w_max > x) - { - w_max = x; - x = w_min; - } - } - } - - if (row_filled) - { - h_max = std::min(h_max, y); - h_min = y; - } - } - - return image.copy(QRect(QPoint(w_max, h_max), QPoint(w_min, h_min))); + return get_aligned_pixmap(QPixmap(path), icon_size, device_pixel_ratio, mode, h_alignment, v_alignment); } // taken from https://stackoverflow.com/a/30818424/8353754 diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index 98e0eb8cd6..22e94109ef 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -23,6 +23,20 @@ namespace gui { namespace utils { + enum class align_h + { + left, + center, + right + }; + + enum class align_v + { + top, + center, + bottom + }; + class circle_pixmap : public QPixmap { public: @@ -112,14 +126,11 @@ namespace gui qobj.setFont(font); } - // Returns a scaled, centered QPixmap - QPixmap get_centered_pixmap(QPixmap pixmap, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode); + // Returns a scaled, aligned QPixmap + QPixmap get_aligned_pixmap(QPixmap pixmap, const QSize& icon_size, qreal device_pixel_ratio, Qt::TransformationMode mode, align_h h_alignment, align_v v_alignment); - // Returns a scaled, centered QPixmap - QPixmap get_centered_pixmap(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode); - - // Returns the part of the image loaded from path that is inside the bounding box of its opaque areas - QImage get_opaque_image_area(const QString& path); + // Returns a scaled, aligned QPixmap + QPixmap get_aligned_pixmap(const QString& path, const QSize& icon_size, qreal device_pixel_ratio, Qt::TransformationMode mode, align_h h_alignment, align_v v_alignment); // Workaround: resize the dropdown combobox items void resize_combo_box_view(QComboBox* combo); diff --git a/rpcs3/rpcs3qt/qt_video_source.cpp b/rpcs3/rpcs3qt/qt_video_source.cpp index d74d395c58..760369ad74 100644 --- a/rpcs3/rpcs3qt/qt_video_source.cpp +++ b/rpcs3/rpcs3qt/qt_video_source.cpp @@ -1,13 +1,41 @@ #include "stdafx.h" #include "Emu/System.h" +#include "Emu/system_config.h" +#include "Emu/Audio/audio_utils.h" #include "qt_video_source.h" +#include "gui_settings.h" #include "Loader/ISO.h" +#include +#include #include -qt_video_source::qt_video_source() +struct qt_audio_instance +{ + static constexpr u32 gui_index = 0; + static constexpr u32 emu_index = 1; + + video_source* source = nullptr; + std::unique_ptr player; + std::unique_ptr output; + std::unique_ptr buffer; + std::unique_ptr data; +}; + +static std::array s_audio_instance = {}; + +static constexpr int emu_timeout_start_ms = 0; +static constexpr int gui_timeout_start_ms = 1000; +static constexpr int gui_fade_in_ms = 2000; +static constexpr int gui_fade_out_ms = 1000; + +static_assert(gui_fade_out_ms <= gui_timeout_start_ms); + +qt_video_source::qt_video_source(bool is_emulation) : video_source() + , m_audio_instance_index(is_emulation ? qt_audio_instance::emu_index : qt_audio_instance::gui_index) + , m_video_timer_timeout_ms(is_emulation ? emu_timeout_start_ms : gui_timeout_start_ms) { } @@ -21,6 +49,11 @@ void qt_video_source::set_video_path(const std::string& video_path) m_video_path = QString::fromStdString(video_path); } +void qt_video_source::set_audio_path(const std::string& audio_path) +{ + m_audio_path = QString::fromStdString(audio_path); +} + void qt_video_source::set_iso_path(const std::string& iso_path) { m_iso_path = iso_path; @@ -32,7 +65,7 @@ void qt_video_source::set_active(bool active) if (active) { - start_movie(); + start_movie_timer(); } else { @@ -89,7 +122,6 @@ void qt_video_source::init_movie() m_video_buffer = std::make_unique(&m_video_data); m_video_buffer->open(QIODevice::ReadOnly); m_movie = std::make_unique(m_video_buffer.get()); - } if (!m_movie->isValid()) @@ -164,6 +196,28 @@ void qt_video_source::init_movie() } } +void qt_video_source::start_movie_timer() +{ + if (m_video_timer_timeout_ms == 0) + { + start_movie(); + return; + } + + if (!m_video_timer) + { + m_video_timer = std::make_unique(); + m_video_timer->setSingleShot(true); + QObject::connect(m_video_timer.get(), &QTimer::timeout, m_video_timer.get(), [this]() + { + if (!m_active) return; + start_movie(); + }); + } + + m_video_timer->start(m_video_timer_timeout_ms); +} + void qt_video_source::start_movie() { init_movie(); @@ -179,12 +233,15 @@ void qt_video_source::start_movie() m_media_player->play(); } + start_audio(); + m_active = true; } void qt_video_source::stop_movie() { m_active = false; + m_video_timer.reset(); if (m_movie) { @@ -196,6 +253,116 @@ void qt_video_source::stop_movie() m_media_player.reset(); m_video_buffer.reset(); m_video_data.clear(); + + stop_audio(); +} + +void qt_video_source::start_audio() +{ + if (m_audio_path.isEmpty()) return; + + qt_audio_instance& audio = ::at32(s_audio_instance, m_audio_instance_index); + if (audio.source == this) return; + + if (!audio.player) + { + audio.output = std::make_unique(); + audio.player = std::make_unique(); + audio.player->setAudioOutput(audio.output.get()); + audio.player->setLoops(QMediaPlayer::Infinite); + } + + if (m_iso_path.empty()) + { + audio.player->setSource(QUrl::fromLocalFile(m_audio_path)); + } + else + { + iso_archive archive(m_iso_path); + auto audio_file = archive.open(m_audio_path.toStdString()); + const auto audio_size = audio_file.size(); + if (audio_size == 0) return; + + std::unique_ptr old_audio_data = std::move(audio.data); + audio.data = std::make_unique(audio_size, 0); + audio_file.read(audio.data->data(), audio_size); + + if (!audio.buffer) + { + audio.buffer = std::make_unique(); + } + + audio.buffer->setBuffer(audio.data.get()); + audio.buffer->open(QIODevice::ReadOnly); + audio.player->setSourceDevice(audio.buffer.get()); + + if (old_audio_data) + { + old_audio_data.reset(); + } + } + + f32 volume = gui::volume; + + if (m_audio_instance_index == qt_audio_instance::emu_index) + { + volume = audio::get_volume(); + } + + QPropertyAnimation* fade_in = new QPropertyAnimation(audio.output.get(), "volume", audio.output.get()); + fade_in->setDuration(gui_fade_in_ms); + fade_in->setStartValue(0.0); + fade_in->setEndValue(std::clamp(volume, 0.0f, 1.0f)); + fade_in->setEasingCurve(QEasingCurve::InSine); + fade_in->start(QAbstractAnimation::DeleteWhenStopped); + + audio.player->play(); + audio.source = this; +} + +void qt_video_source::stop_audio() +{ + qt_audio_instance& audio = ::at32(s_audio_instance, m_audio_instance_index); + if (audio.source != this) return; + + audio.source = nullptr; + + QMediaPlayer* player = audio.player.release(); + QAudioOutput* output = audio.output.release(); + QBuffer* buffer = audio.buffer.release(); + QByteArray* data = audio.data.release(); + + const auto reset_player = [=]() + { + if (player) + { + player->stop(); + delete player; + } + + if (output) delete output; + if (buffer) delete buffer; + if (data) delete data; + }; + + if (output) + { + QPropertyAnimation* fade_out = new QPropertyAnimation(output, "volume", output); + fade_out->setDuration(gui_fade_out_ms); + fade_out->setEasingCurve(QEasingCurve::OutSine); + fade_out->setStartValue(output->volume()); + fade_out->setEndValue(0.0); + + QObject::connect(fade_out, &QPropertyAnimation::finished, [reset_player]() + { + reset_player(); + }); + + fade_out->start(QAbstractAnimation::DeleteWhenStopped); + return; + } + + reset_player(); } QPixmap qt_video_source::get_movie_image(const QVideoFrame& frame) const @@ -252,11 +419,20 @@ qt_video_source_wrapper::~qt_video_source_wrapper() }); } +void qt_video_source_wrapper::init_video_source() +{ + if (!m_qt_video_source) + { + m_qt_video_source = std::make_unique(true); + } +} + void qt_video_source_wrapper::set_video_path(const std::string& video_path) { Emu.CallFromMainThread([this, path = video_path]() { - m_qt_video_source = std::make_unique(); + init_video_source(); + m_qt_video_source->m_image_change_callback = [this](const QVideoFrame& frame) { std::unique_lock lock(m_qt_video_source->m_image_mutex); @@ -288,10 +464,21 @@ void qt_video_source_wrapper::set_video_path(const std::string& video_path) }); } +void qt_video_source_wrapper::set_audio_path(const std::string& audio_path) +{ + Emu.CallFromMainThread([this, path = audio_path]() + { + init_video_source(); + + m_qt_video_source->set_audio_path(path); + }); +} + void qt_video_source_wrapper::set_active(bool active) { Emu.CallFromMainThread([this, active]() { + ensure(m_qt_video_source); m_qt_video_source->set_active(true); }); } diff --git a/rpcs3/rpcs3qt/qt_video_source.h b/rpcs3/rpcs3qt/qt_video_source.h index ce43d593d7..22f1acb5bd 100644 --- a/rpcs3/rpcs3qt/qt_video_source.h +++ b/rpcs3/rpcs3qt/qt_video_source.h @@ -10,16 +10,19 @@ #include #include #include +#include class qt_video_source : public video_source { public: - qt_video_source(); + qt_video_source(bool is_emulation = false); virtual ~qt_video_source(); void set_iso_path(const std::string& iso_path); void set_video_path(const std::string& video_path) override; + void set_audio_path(const std::string& audio_path) override; const QString& video_path() const { return m_video_path; } + const QString& audio_path() const { return m_audio_path; } void get_image(std::vector& data, int& w, int& h, int& ch, int& bpp) override; bool has_new() const override { return m_has_new; } @@ -27,9 +30,13 @@ public: void set_active(bool active) override; bool get_active() const override { return m_active; } + void start_movie_timer(); void start_movie(); void stop_movie(); + void start_audio(); + void stop_audio(); + QPixmap get_movie_image(const QVideoFrame& frame) const; void image_change_callback(const QVideoFrame& frame = {}) const; @@ -44,11 +51,15 @@ protected: atomic_t m_has_new = false; QString m_video_path; + QString m_audio_path; + u32 m_audio_instance_index = 0; + u32 m_video_timer_timeout_ms = 0; std::string m_iso_path; // path of the source archive QByteArray m_video_data{}; QImage m_image{}; std::vector m_image_path; + std::unique_ptr m_video_timer; std::unique_ptr m_video_buffer; std::unique_ptr m_media_player; std::unique_ptr m_video_sink; @@ -67,11 +78,14 @@ public: virtual ~qt_video_source_wrapper(); void set_video_path(const std::string& video_path) override; + void set_audio_path(const std::string& audio_path) override; void set_active(bool active) override; bool get_active() const override; bool has_new() const override { return m_qt_video_source && m_qt_video_source->has_new(); } void get_image(std::vector& data, int& w, int& h, int& ch, int& bpp) override; private: + void init_video_source(); + std::unique_ptr m_qt_video_source; }; diff --git a/rpcs3/rpcs3qt/recording_settings_dialog.cpp b/rpcs3/rpcs3qt/recording_settings_dialog.cpp new file mode 100644 index 0000000000..befd14713a --- /dev/null +++ b/rpcs3/rpcs3qt/recording_settings_dialog.cpp @@ -0,0 +1,455 @@ +#include "stdafx.h" +#include "recording_settings_dialog.h" +#include "ui_recording_settings_dialog.h" + +#include + +#ifdef _MSC_VER +#pragma warning(push, 0) +#else +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wextra" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +} +#ifdef _MSC_VER +#pragma warning(pop) +#else +#pragma GCC diagnostic pop +#endif + +LOG_CHANNEL(cfg_log, "CFG"); + +static std::vector get_video_codecs(const AVOutputFormat* fmt) +{ + std::vector codecs; + + void* opaque = nullptr; + while (const AVCodec* codec = av_codec_iterate(&opaque)) + { + if (!codec->pix_fmts) + continue; + + if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) + continue; + + if (codec->type != AVMediaType::AVMEDIA_TYPE_VIDEO) + continue; + + switch (codec->id) + { + case AV_CODEC_ID_H264: + case AV_CODEC_ID_HEVC: + case AV_CODEC_ID_MPEG4: + case AV_CODEC_ID_AV1: + break; + default: + continue; + } + + if (!av_codec_is_encoder(codec)) + continue; + + if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1) + continue; + + codecs.push_back(codec); + } + + return codecs; +} + +static std::vector get_audio_codecs(const AVOutputFormat* fmt) +{ + std::vector codecs; + + void* opaque = nullptr; + while (const AVCodec* codec = av_codec_iterate(&opaque)) + { + if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) + continue; + + if (codec->type != AVMediaType::AVMEDIA_TYPE_AUDIO) + continue; + + if (!av_codec_is_encoder(codec)) + continue; + + if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1) + continue; + + codecs.push_back(codec); + } + + return codecs; +} + +recording_settings_dialog::recording_settings_dialog(QWidget* parent) + : QDialog(parent), ui(new Ui::recording_settings_dialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + if (!g_cfg_recording.load()) + { + cfg_log.notice("Could not load recording config. Using defaults."); + } + + ui->combo_presets->addItem(tr("720p 30fps"), static_cast(quality_preset::_720p_30)); + ui->combo_presets->addItem(tr("720p 60fps"), static_cast(quality_preset::_720p_60)); + ui->combo_presets->addItem(tr("1080p 30fps"), static_cast(quality_preset::_1080p_30)); + ui->combo_presets->addItem(tr("1080p 60fps"), static_cast(quality_preset::_1080p_60)); + ui->combo_presets->addItem(tr("1440p 30fps"), static_cast(quality_preset::_1440p_30)); + ui->combo_presets->addItem(tr("1440p 60fps"), static_cast(quality_preset::_1440p_60)); + ui->combo_presets->addItem(tr("2160p 30fps"), static_cast(quality_preset::_2160p_30)); + ui->combo_presets->addItem(tr("2160p 60fps"), static_cast(quality_preset::_2160p_60)); + ui->combo_presets->addItem(tr("Custom"), static_cast(quality_preset::custom)); + connect(ui->combo_presets, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_presets->itemData(index); + if (var.canConvert()) + { + const quality_preset preset = static_cast(var.toInt()); + select_preset(preset, g_cfg_recording); + update_ui(); + } + }); + + ui->combo_resolution->addItem("360p", QVariant::fromValue(QPair(640, 360))); + ui->combo_resolution->addItem("480p", QVariant::fromValue(QPair(854, 480))); + ui->combo_resolution->addItem("720p", QVariant::fromValue(QPair(1280, 720))); + ui->combo_resolution->addItem("1080p", QVariant::fromValue(QPair(1920, 1080))); + ui->combo_resolution->addItem("1440p", QVariant::fromValue(QPair(2560, 1440))); + ui->combo_resolution->addItem("2160p", QVariant::fromValue(QPair(3840, 2160))); + connect(ui->combo_resolution, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_resolution->itemData(index); + if (var.canConvert>()) + { + const QPair size = var.value>(); + g_cfg_recording.video.width.set(size.first); + g_cfg_recording.video.height.set(size.second); + update_preset(); + } + }); + + const AVOutputFormat* fmt = av_guess_format("mp4", nullptr, nullptr); + m_video_codecs = get_video_codecs(fmt); + m_audio_codecs = get_audio_codecs(fmt); + + for (const AVCodec* codec : m_video_codecs) + { + if (!codec) continue; + + const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id); + ui->combo_video_codec->addItem(QString::fromStdString(name), static_cast(codec->id)); + } + + for (const AVCodec* codec : m_audio_codecs) + { + if (!codec) continue; + + const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id); + ui->combo_audio_codec->addItem(QString::fromStdString(name), static_cast(codec->id)); + } + + connect(ui->combo_video_codec, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_video_codec->itemData(index); + if (var.canConvert()) + { + const int codec_id = var.toInt(); + g_cfg_recording.video.video_codec.set(codec_id); + update_preset(); + } + }); + + connect(ui->combo_audio_codec, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_audio_codec->itemData(index); + if (var.canConvert()) + { + const int codec_id = var.toInt(); + g_cfg_recording.audio.audio_codec.set(codec_id); + update_preset(); + } + }); + + ui->combo_framerate->addItem("30", 30); + ui->combo_framerate->addItem("60", 60); + connect(ui->combo_framerate, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_framerate->itemData(index); + if (var.canConvert()) + { + const int fps = var.toInt(); + g_cfg_recording.video.framerate.set(fps); + update_preset(); + } + }); + + ui->spinbox_video_bitrate->setSingleStep(1); + ui->spinbox_video_bitrate->setMinimum(g_cfg_recording.video.video_bps.min); + ui->spinbox_video_bitrate->setMaximum(g_cfg_recording.video.video_bps.max); + connect(ui->spinbox_video_bitrate, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.video.video_bps.set(value); + update_preset(); + }); + + ui->spinbox_audio_bitrate->setSingleStep(1); + ui->spinbox_audio_bitrate->setMinimum(g_cfg_recording.audio.audio_bps.min); + ui->spinbox_audio_bitrate->setMaximum(g_cfg_recording.audio.audio_bps.max); + connect(ui->spinbox_audio_bitrate, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.audio.audio_bps.set(value); + update_preset(); + }); + + ui->spinbox_gop_size->setSingleStep(1); + ui->spinbox_gop_size->setMinimum(g_cfg_recording.video.gop_size.min); + ui->spinbox_gop_size->setMaximum(g_cfg_recording.video.gop_size.max); + connect(ui->spinbox_gop_size, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.video.gop_size.set(value); + update_preset(); + }); + + ui->spinbox_max_b_frames->setSingleStep(1); + ui->spinbox_max_b_frames->setMinimum(g_cfg_recording.video.max_b_frames.min); + ui->spinbox_max_b_frames->setMaximum(g_cfg_recording.video.max_b_frames.max); + connect(ui->spinbox_max_b_frames, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.video.max_b_frames.set(value); + update_preset(); + }); + + connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) + { + if (button == ui->buttonBox->button(QDialogButtonBox::Save)) + { + g_cfg_recording.save(); + accept(); + } + else if (button == ui->buttonBox->button(QDialogButtonBox::Cancel)) + { + reject(); + } + else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) + { + g_cfg_recording.from_default(); + update_ui(); + update_preset(); + } + }); + + connect(this, &QDialog::rejected, this, []() + { + if (!g_cfg_recording.load()) + { + cfg_log.notice("Could not load recording config. Using defaults."); + } + }); + + update_ui(); + update_preset(); +} + +recording_settings_dialog::~recording_settings_dialog() +{ +} + +void recording_settings_dialog::update_preset() +{ + const quality_preset preset = current_preset(); + ui->combo_presets->setCurrentIndex(ui->combo_presets->findData(static_cast(preset))); +} + +void recording_settings_dialog::update_ui() +{ + ui->combo_resolution->blockSignals(true); + ui->combo_framerate->blockSignals(true); + ui->combo_video_codec->blockSignals(true); + ui->combo_audio_codec->blockSignals(true); + ui->spinbox_video_bitrate->blockSignals(true); + ui->spinbox_audio_bitrate->blockSignals(true); + ui->spinbox_gop_size->blockSignals(true); + ui->spinbox_max_b_frames->blockSignals(true); + + ui->combo_resolution->setCurrentIndex(ui->combo_resolution->findData(QVariant::fromValue(QPair(g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get())))); + ui->combo_framerate->setCurrentIndex(ui->combo_framerate->findData(static_cast(g_cfg_recording.video.framerate.get()))); + ui->combo_video_codec->setCurrentIndex(ui->combo_video_codec->findData(static_cast(g_cfg_recording.video.video_codec.get()))); + ui->combo_audio_codec->setCurrentIndex(ui->combo_audio_codec->findData(static_cast(g_cfg_recording.audio.audio_codec.get()))); + ui->spinbox_video_bitrate->setValue(g_cfg_recording.video.video_bps); + ui->spinbox_audio_bitrate->setValue(g_cfg_recording.audio.audio_bps); + ui->spinbox_gop_size->setValue(g_cfg_recording.video.gop_size); + ui->spinbox_max_b_frames->setValue(g_cfg_recording.video.max_b_frames); + + ui->combo_resolution->blockSignals(false); + ui->combo_framerate->blockSignals(false); + ui->combo_video_codec->blockSignals(false); + ui->combo_audio_codec->blockSignals(false); + ui->spinbox_video_bitrate->blockSignals(false); + ui->spinbox_audio_bitrate->blockSignals(false); + ui->spinbox_gop_size->blockSignals(false); + ui->spinbox_max_b_frames->blockSignals(false); + + const auto get_codec_name = [](const std::vector& codecs, u32 id) + { + for (const AVCodec* codec : codecs) + { + if (codec && codec->id == static_cast(id)) + { + const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id); + return name; + } + } + return std::string(); + }; + + ui->label_info_keys->setText( + tr("Resolution:") + "\n" + + tr("Framerate:") + "\n" + + tr("Video Codec:") + "\n" + + tr("Video Bitrate:") + "\n" + + tr("Audio Codec:") + "\n" + + tr("Audio Bitrate:") + "\n" + + tr("Gop-Size:") + "\n" + + tr("Max B-Frames:") + ); + + ui->label_info_values->setText(QString::fromStdString( + fmt::format("%d x %d\n%d fps\n%s\n%d\n%s\n%d\n%d\n%d", + g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get(), + g_cfg_recording.video.framerate.get(), + get_codec_name(m_video_codecs, g_cfg_recording.video.video_codec.get()), + g_cfg_recording.video.video_bps.get(), + get_codec_name(m_audio_codecs, g_cfg_recording.audio.audio_codec.get()), + g_cfg_recording.audio.audio_bps.get(), + g_cfg_recording.video.gop_size.get(), + g_cfg_recording.video.max_b_frames.get() + ) + )); +} + +void recording_settings_dialog::select_preset(quality_preset preset, cfg_recording& cfg) +{ + if (preset == quality_preset::custom) + { + return; + } + + cfg.audio.audio_codec.set(static_cast(AVCodecID::AV_CODEC_ID_AAC)); + cfg.audio.audio_bps.set(192'000); // 192 kbps + + cfg.video.video_codec.set(static_cast(AVCodecID::AV_CODEC_ID_MPEG4)); + cfg.video.pixel_format.set(static_cast(::AV_PIX_FMT_YUV420P)); + + switch (preset) + { + case quality_preset::_720p_30: + case quality_preset::_720p_60: + cfg.video.width.set(1280); + cfg.video.height.set(720); + break; + case quality_preset::_1080p_30: + case quality_preset::_1080p_60: + cfg.video.width.set(1920); + cfg.video.height.set(1080); + break; + case quality_preset::_1440p_30: + case quality_preset::_1440p_60: + cfg.video.width.set(2560); + cfg.video.height.set(1440); + break; + case quality_preset::_2160p_30: + case quality_preset::_2160p_60: + cfg.video.width.set(3840); + cfg.video.height.set(2160); + break; + case quality_preset::custom: + break; + } + + switch (preset) + { + case quality_preset::_720p_30: + case quality_preset::_1080p_30: + case quality_preset::_1440p_30: + case quality_preset::_2160p_30: + cfg.video.framerate.set(30); + break; + case quality_preset::_720p_60: + case quality_preset::_1080p_60: + case quality_preset::_1440p_60: + case quality_preset::_2160p_60: + cfg.video.framerate.set(60); + break; + case quality_preset::custom: + break; + } + + switch (preset) + { + case quality_preset::_720p_30: + cfg.video.video_bps.set(4'000'000); + break; + case quality_preset::_720p_60: + cfg.video.video_bps.set(6'000'000); + break; + case quality_preset::_1080p_30: + cfg.video.video_bps.set(8'000'000); + break; + case quality_preset::_1080p_60: + cfg.video.video_bps.set(12'000'000); + break; + case quality_preset::_1440p_30: + cfg.video.video_bps.set(16'000'000); + break; + case quality_preset::_1440p_60: + cfg.video.video_bps.set(24'000'000); + break; + case quality_preset::_2160p_30: + cfg.video.video_bps.set(40'000'000); + break; + case quality_preset::_2160p_60: + cfg.video.video_bps.set(60'000'000); + break; + case quality_preset::custom: + break; + } + + cfg.video.gop_size.set(cfg.video.framerate.get()); + cfg.video.max_b_frames.set(2); +} + +recording_settings_dialog::quality_preset recording_settings_dialog::current_preset() +{ + for (u32 i = 0; i < static_cast(quality_preset::custom); i++) + { + const quality_preset preset = static_cast(i); + + cfg_recording cfg; + select_preset(preset, cfg); + + if (g_cfg_recording.video.framerate.get() == cfg.video.framerate.get() && + g_cfg_recording.video.width.get() == cfg.video.width.get() && + g_cfg_recording.video.height.get() == cfg.video.height.get() && + g_cfg_recording.video.pixel_format.get() == cfg.video.pixel_format.get() && + g_cfg_recording.video.video_codec.get() == cfg.video.video_codec.get() && + g_cfg_recording.video.video_bps.get() == cfg.video.video_bps.get() && + g_cfg_recording.video.max_b_frames.get() == cfg.video.max_b_frames.get() && + g_cfg_recording.video.gop_size.get() == cfg.video.gop_size.get() && + g_cfg_recording.audio.audio_codec.get() == cfg.audio.audio_codec.get() && + g_cfg_recording.audio.audio_bps.get() == cfg.audio.audio_bps.get()) + { + return preset; + } + } + + return quality_preset::custom; +} diff --git a/rpcs3/rpcs3qt/recording_settings_dialog.h b/rpcs3/rpcs3qt/recording_settings_dialog.h new file mode 100644 index 0000000000..3a17d7d8a8 --- /dev/null +++ b/rpcs3/rpcs3qt/recording_settings_dialog.h @@ -0,0 +1,47 @@ +#pragma once + +#include "util/types.hpp" +#include "Emu/Io/recording_config.h" + +#include + +namespace Ui +{ + class recording_settings_dialog; +} + +struct AVCodec; + +class recording_settings_dialog : public QDialog +{ + Q_OBJECT + +public: + recording_settings_dialog(QWidget* parent = nullptr); + virtual ~recording_settings_dialog(); + +private: + enum class quality_preset + { + _720p_30, + _720p_60, + _1080p_30, + _1080p_60, + _1440p_30, + _1440p_60, + _2160p_30, + _2160p_60, + custom + }; + + void update_preset(); + void update_ui(); + + static void select_preset(quality_preset preset, cfg_recording& cfg); + static quality_preset current_preset(); + + Ui::recording_settings_dialog* ui; + + std::vector m_video_codecs; + std::vector m_audio_codecs; +}; diff --git a/rpcs3/rpcs3qt/recording_settings_dialog.ui b/rpcs3/rpcs3qt/recording_settings_dialog.ui new file mode 100644 index 0000000000..9d11e1299a --- /dev/null +++ b/rpcs3/rpcs3qt/recording_settings_dialog.ui @@ -0,0 +1,273 @@ + + + recording_settings_dialog + + + + 0 + 0 + 692 + 734 + + + + Recording Settings + + + + + + 0 + + + + Presets + + + + + + Preset + + + + + + + + + + + + Info + + + + + + Keys + + + + + + + Values + + + + + + + Qt::Orientation::Horizontal + + + + 0 + 0 + + + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 0 + + + + + + + + + Advanced + + + + + + Video + + + + + + Codec + + + + + + + + + + + + Resolution + + + + + + + + + + + + Framerate + + + + + + + + + + + + Bitrate + + + + + + + + + + + + Group of Pictures Size + + + + + + + + + + + + Max. B-Frames + + + + + + + + + + + + + + + Audio + + + + + + Codec + + + + + + + + + + + + Bitrate + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 0 + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save + + + + + + + + + buttonBox + accepted() + recording_settings_dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + recording_settings_dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 6de6007b31..cab71be3bc 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -1347,7 +1347,10 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) rpcn_friends_dialog::~rpcn_friends_dialog() { - m_rpcn->remove_friend_cb(friend_callback, this); + if (m_rpcn) + { + m_rpcn->remove_friend_cb(friend_callback, this); + } } bool rpcn_friends_dialog::add_friend_with_error_dialog(const std::string& friend_username) diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp index 2dd2a14e86..002df8d527 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp @@ -360,6 +360,11 @@ void save_manager_dialog::UpdateList() icon_item->set_video_path(movie_path); } + if (const std::string audio_path = dir_path + "SND0.AT3"; fs::is_file(audio_path)) + { + icon_item->set_audio_path(audio_path); + } + icon_item->set_image_change_callback([this, icon_item](const QVideoFrame& frame) { if (!icon_item) @@ -686,6 +691,7 @@ void save_manager_dialog::UpdateDetails() const SaveDataEntry& save = ::at32(m_save_entries, idx); m_details_icon->set_video_path(icon_item->video_path().toStdString()); + m_details_icon->set_audio_path(icon_item->audio_path().toStdString()); m_details_icon->set_thumbnail(icon_item->data(SaveUserRole::Pixmap).value()); m_details_icon->set_active(false); diff --git a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp index 2898c9b170..dbeca1698f 100644 --- a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp @@ -575,7 +575,7 @@ void savestate_manager_dialog::ShowGameTableContextMenu(const QPoint& pos) if (!name.isEmpty()) { QAction* copy_name = new QAction(tr("&Copy Name"), menu); - connect(copy_name, &QAction::triggered, this, [this, name]() + connect(copy_name, &QAction::triggered, this, [name]() { QApplication::clipboard()->setText(name); }); diff --git a/rpcs3/rpcs3qt/screenshot_item.cpp b/rpcs3/rpcs3qt/screenshot_item.cpp index d0916f7114..2897cf968c 100644 --- a/rpcs3/rpcs3qt/screenshot_item.cpp +++ b/rpcs3/rpcs3qt/screenshot_item.cpp @@ -1,26 +1,40 @@ #include "screenshot_item.h" #include "qt_utils.h" +#include "Utilities/Thread.h" #include -screenshot_item::screenshot_item(QWidget* parent) +screenshot_item::screenshot_item(QWidget* parent, QSize icon_size, const QString& icon_path, const QPixmap& placeholder) : flow_widget_item(parent) + , m_icon_path(icon_path) + , m_icon_size(icon_size) { + setToolTip(icon_path); + cb_on_first_visibility = [this]() { m_thread.reset(QThread::create([this]() { - const QPixmap pixmap = gui::utils::get_centered_pixmap(icon_path, icon_size, 0, 0, 1.0, Qt::SmoothTransformation); + thread_base::set_name("Screenshot item"); + + const QPixmap src_icon = QPixmap(m_icon_path); + if (src_icon.isNull()) return; + + const QPixmap pixmap = gui::utils::get_aligned_pixmap(src_icon, m_icon_size, 1.0, Qt::SmoothTransformation, gui::utils::align_h::center, gui::utils::align_v::center); Q_EMIT signal_icon_update(pixmap); })); m_thread->start(); }; - label = new QLabel(this); + m_label = new QLabel(this); + m_label->setPixmap(placeholder); + QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(label); + layout->addWidget(m_label); setLayout(layout); + + connect(this, &screenshot_item::signal_icon_update, this, &screenshot_item::update_icon, Qt::ConnectionType::QueuedConnection); } screenshot_item::~screenshot_item() @@ -30,3 +44,23 @@ screenshot_item::~screenshot_item() m_thread->wait(); } } + +void screenshot_item::update_icon(const QPixmap& pixmap) +{ + if (m_label) + { + m_label->setPixmap(pixmap); + } +} + +void screenshot_item::mouseDoubleClickEvent(QMouseEvent* ev) +{ + flow_widget_item::mouseDoubleClickEvent(ev); + + if (!ev) return; + + if (ev->button() == Qt::LeftButton) + { + Q_EMIT signal_icon_preview(m_icon_path); + } +} diff --git a/rpcs3/rpcs3qt/screenshot_item.h b/rpcs3/rpcs3qt/screenshot_item.h index 36f0232b1f..df223783f3 100644 --- a/rpcs3/rpcs3qt/screenshot_item.h +++ b/rpcs3/rpcs3qt/screenshot_item.h @@ -3,22 +3,29 @@ #include "flow_widget_item.h" #include #include +#include class screenshot_item : public flow_widget_item { Q_OBJECT public: - screenshot_item(QWidget* parent); + screenshot_item(QWidget* parent, QSize icon_size, const QString& icon_path, const QPixmap& placeholder); virtual ~screenshot_item(); - QString icon_path; - QSize icon_size; - QLabel* label{}; - private: + QLabel* m_label{}; + QString m_icon_path; + QSize m_icon_size; std::unique_ptr m_thread; +protected: + void mouseDoubleClickEvent(QMouseEvent* ev) override; + Q_SIGNALS: void signal_icon_update(const QPixmap& pixmap); + void signal_icon_preview(const QString& path); + +public Q_SLOTS: + void update_icon(const QPixmap& pixmap); }; diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index 3825dfd747..d99ce35605 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -10,13 +10,15 @@ #include #include #include +#include #include #include #include LOG_CHANNEL(gui_log, "GUI"); -screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(parent) +screenshot_manager_dialog::screenshot_manager_dialog(const std::vector& games, QWidget* parent) + : QDialog(parent), m_games(games) { setWindowTitle(tr("Screenshots")); setAttribute(Qt::WA_DeleteOnClose); @@ -28,11 +30,50 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog( m_placeholder = QPixmap(m_icon_size); m_placeholder.fill(Qt::gray); - connect(this, &screenshot_manager_dialog::signal_icon_preview, this, &screenshot_manager_dialog::show_preview); - connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry); + connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry, Qt::ConnectionType::QueuedConnection); - QVBoxLayout* layout = new QVBoxLayout; + m_combo_sort_filter = new QComboBox(); + m_combo_sort_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_combo_sort_filter->addItem(tr("Sort by Game"), static_cast(sort_filter::game)); + m_combo_sort_filter->addItem(tr("Sort by Date"), static_cast(sort_filter::date)); + connect(m_combo_sort_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); }); + + m_combo_type_filter = new QComboBox(); + m_combo_type_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_combo_type_filter->addItem(tr("All Screenshots"), static_cast(type_filter::all)); + m_combo_type_filter->addItem(tr("RPCS3 Screenshots"), static_cast(type_filter::rpcs3)); + m_combo_type_filter->addItem(tr("Cell Screenshots"), static_cast(type_filter::cell)); + connect(m_combo_type_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); }); + + m_combo_game_filter = new QComboBox(); + m_combo_game_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_combo_game_filter->addItem(tr("All Games"), QString()); + connect(m_combo_game_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); }); + + QHBoxLayout* sort_layout = new QHBoxLayout(); + sort_layout->addWidget(m_combo_sort_filter); + QGroupBox* gb_sort = new QGroupBox(tr("Sort")); + gb_sort->setLayout(sort_layout); + + QHBoxLayout* type_layout = new QHBoxLayout(); + type_layout->addWidget(m_combo_type_filter); + QGroupBox* gb_type = new QGroupBox(tr("Filter Type")); + gb_type->setLayout(type_layout); + + QHBoxLayout* game_layout = new QHBoxLayout(); + game_layout->addWidget(m_combo_game_filter); + QGroupBox* gb_game = new QGroupBox(tr("Filter Game")); + gb_game->setLayout(game_layout); + + QHBoxLayout* top_layout = new QHBoxLayout(); + top_layout->addWidget(gb_sort); + top_layout->addWidget(gb_type); + top_layout->addWidget(gb_game); + top_layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); + + QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); + layout->addLayout(top_layout); layout->addWidget(m_flow_widget); setLayout(layout); @@ -47,14 +88,8 @@ screenshot_manager_dialog::~screenshot_manager_dialog() void screenshot_manager_dialog::add_entry(const QString& path) { - screenshot_item* item = new screenshot_item(m_flow_widget); - ensure(item->label); - item->setToolTip(path); - item->installEventFilter(this); - item->label->setPixmap(m_placeholder); - item->icon_path = path; - item->icon_size = m_icon_size; - connect(item, &screenshot_item::signal_icon_update, this, &screenshot_manager_dialog::update_icon); + screenshot_item* item = new screenshot_item(m_flow_widget, m_icon_size, path, m_placeholder); + connect(item, &screenshot_item::signal_icon_preview, this, &screenshot_manager_dialog::show_preview); m_flow_widget->add_widget(item); } @@ -65,28 +100,74 @@ void screenshot_manager_dialog::show_preview(const QString& path) preview->show(); } -void screenshot_manager_dialog::update_icon(const QPixmap& pixmap) -{ - if (screenshot_item* item = static_cast(QObject::sender())) - { - if (item->label) - { - item->label->setPixmap(pixmap); - } - } -} - void screenshot_manager_dialog::reload() { m_abort_parsing = true; + m_parsing_watcher.disconnect(); gui::utils::stop_future_watcher(m_parsing_watcher, true); - const std::string screenshot_path_qt = fs::get_config_dir() + "screenshots/"; - const std::string screenshot_path_cell = rpcs3::utils::get_hdd0_dir() + "/photo/"; + const type_filter t_filter = static_cast(m_combo_type_filter->currentData().toInt()); + const sort_filter s_filter = static_cast(m_combo_sort_filter->currentData().toInt()); + const QString game_filter = m_combo_game_filter->currentData().toString(); + + const std::string screenshot_path_rpcs3 = fs::get_config_dir() + "screenshots/"; + const std::string screenshot_path_cell = rpcs3::utils::get_hdd0_dir() + "/photo/"; + + std::vector folders; + switch (t_filter) + { + case type_filter::all: + folders.push_back(screenshot_path_rpcs3); + folders.push_back(screenshot_path_cell); + break; + case type_filter::rpcs3: + folders.push_back(screenshot_path_rpcs3); + break; + case type_filter::cell: + folders.push_back(screenshot_path_cell); + break; + } m_flow_widget->clear(); + m_game_folders.clear(); m_abort_parsing = false; - m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, screenshot_path_qt, screenshot_path_cell](int index) + + connect(&m_parsing_watcher, &QFutureWatcher::finished, this, [this]() + { + std::vector> games; + for (const auto& [dirname, paths] : m_game_folders) + { + const std::string serial = dirname.toStdString(); + std::string text = serial; + for (const auto& game : m_games) + { + if (game && game->info.serial == serial) + { + text = fmt::format("%s (%s)", game->info.name, serial); + break; + } + } + games.push_back(std::pair(dirname, QString::fromStdString(text))); + } + + std::sort(games.begin(), games.end(), [](const std::pair& l, const std::pair& r) + { + return l.second < r.second; + }); + + const QString old_filter = m_combo_game_filter->currentData().toString(); + m_combo_game_filter->blockSignals(true); + m_combo_game_filter->clear(); + m_combo_game_filter->addItem(tr("All Games"), QString()); + for (const auto& [dirname, text] : games) + { + m_combo_game_filter->addItem(text, dirname); + } + m_combo_game_filter->setCurrentIndex(m_combo_game_filter->findData(old_filter)); + m_combo_game_filter->blockSignals(false); + }); + + m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, folders, game_filter, s_filter](int index) { if (index != 0) { @@ -95,26 +176,68 @@ void screenshot_manager_dialog::reload() const QStringList filter{ QStringLiteral("*.png") }; - for (const std::string& path : { screenshot_path_qt, screenshot_path_cell }) + for (const std::string& folder : folders) { if (m_abort_parsing) { return; } - if (path.empty()) + if (folder.empty()) { gui_log.error("Screenshot manager: Trying to load screenshots from empty path!"); continue; } - QDirIterator dir_iter(QString::fromStdString(path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + QDirIterator dir_iter(QString::fromStdString(folder), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (dir_iter.hasNext() && !m_abort_parsing) { - Q_EMIT signal_entry_parsed(dir_iter.next()); + QFileInfo info(dir_iter.next()); + const QString dirname = info.dir().dirName(); + m_game_folders[dirname].push_back(std::move(info)); } } + + switch (s_filter) + { + case sort_filter::game: + { + for (const auto& [dirname, infos] : m_game_folders) + { + if (game_filter.isEmpty() || game_filter == dirname) + { + for (const QFileInfo& info : infos) + { + Q_EMIT signal_entry_parsed(info.filePath()); + } + } + } + break; + } + case sort_filter::date: + { + std::vector sorted_infos; + for (const auto& [dirname, infos] : m_game_folders) + { + if (game_filter.isEmpty() || game_filter == dirname) + { + sorted_infos.insert(sorted_infos.end(), infos.begin(), infos.end()); + } + } + + std::sort(sorted_infos.begin(), sorted_infos.end(), [](const QFileInfo& a, const QFileInfo& b) + { + return a.lastModified() < b.lastModified(); + }); + + for (const QFileInfo& info : sorted_infos) + { + Q_EMIT signal_entry_parsed(info.filePath()); + } + break; + } + } })); } @@ -123,17 +246,3 @@ void screenshot_manager_dialog::showEvent(QShowEvent* event) QDialog::showEvent(event); reload(); } - -bool screenshot_manager_dialog::eventFilter(QObject* watched, QEvent* event) -{ - if (event && event->type() == QEvent::MouseButtonDblClick && static_cast(event)->button() == Qt::LeftButton) - { - if (screenshot_item* item = static_cast(watched)) - { - Q_EMIT signal_icon_preview(item->icon_path); - return true; - } - } - - return false; -} diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.h b/rpcs3/rpcs3qt/screenshot_manager_dialog.h index ab4d7c13a8..a236eb5e0d 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.h +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.h @@ -1,30 +1,29 @@ #pragma once #include "flow_widget.h" +#include "gui_game_info.h" +#include #include +#include #include #include #include -#include +#include #include +#include +#include class screenshot_manager_dialog : public QDialog { Q_OBJECT public: - screenshot_manager_dialog(QWidget* parent = nullptr); + screenshot_manager_dialog(const std::vector& games, QWidget* parent = nullptr); ~screenshot_manager_dialog(); - bool eventFilter(QObject* watched, QEvent* event) override; - Q_SIGNALS: void signal_entry_parsed(const QString& path); - void signal_icon_preview(const QString& path); - -public Q_SLOTS: - void update_icon(const QPixmap& pixmap); private Q_SLOTS: void add_entry(const QString& path); @@ -36,11 +35,28 @@ protected: private: void reload(); + enum class type_filter + { + all, + rpcs3, + cell + }; + + enum class sort_filter + { + game, + date + }; + + std::vector m_games; bool m_abort_parsing = false; const std::array m_parsing_threads{0}; QFutureWatcher m_parsing_watcher; flow_widget* m_flow_widget = nullptr; - + QComboBox* m_combo_sort_filter = nullptr; + QComboBox* m_combo_game_filter = nullptr; + QComboBox* m_combo_type_filter = nullptr; QSize m_icon_size; QPixmap m_placeholder; + std::map> m_game_folders; }; diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 0c0fb63efd..7e7f115d22 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -23,13 +23,13 @@ #include "emu_settings_type.h" #include "render_creator.h" #include "microphone_creator.h" -#include "Emu/NP/rpcn_countries.h" +#include "log_level_dialog.h" +#include "Emu/NP/rpcn_countries.h" #include "Emu/GameInfo.h" #include "Emu/System.h" #include "Emu/system_config.h" #include "Emu/title.h" - #include "Emu/Audio/audio_device_enumerator.h" #include "Loader/PSF.h" @@ -502,6 +502,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std } } + m_emu_settings->EnhanceComboBox(ui->vsyncMode, emu_settings_type::VSync); + SubscribeTooltip(ui->vsyncMode, tooltips.settings.vsync); + m_emu_settings->EnhanceComboBox(ui->antiAliasing, emu_settings_type::MSAA); SubscribeTooltip(ui->gb_antiAliasing, tooltips.settings.anti_aliasing); @@ -604,9 +607,6 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->dumpColor, emu_settings_type::WriteColorBuffers); SubscribeTooltip(ui->dumpColor, tooltips.settings.dump_color); - m_emu_settings->EnhanceCheckBox(ui->vsync, emu_settings_type::VSync); - SubscribeTooltip(ui->vsync, tooltips.settings.vsync); - m_emu_settings->EnhanceCheckBox(ui->stretchToDisplayArea, emu_settings_type::StretchToDisplayArea); SubscribeTooltip(ui->stretchToDisplayArea, tooltips.settings.stretch_to_display_area); @@ -858,7 +858,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->vulkansched->setEnabled(is_vulkan); }; - const auto apply_fsr_specific_options = [r_creator, this]() + const auto apply_fsr_specific_options = [this]() { const auto [text, value] = get_data(ui->outputScalingMode, ui->outputScalingMode->currentIndex()); const bool fsr_selected = static_cast(value) == output_scaling_mode::fsr; @@ -1829,6 +1829,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->enableGamemode->setVisible(false); #endif + m_emu_settings->EnhanceCheckBox(ui->playMusicDuringBoot, emu_settings_type::PlayMusicDuringBoot); + SubscribeTooltip(ui->playMusicDuringBoot, tooltips.settings.play_music_during_boot); + m_emu_settings->EnhanceCheckBox(ui->showShaderCompilationHint, emu_settings_type::ShowShaderCompilationHint); SubscribeTooltip(ui->showShaderCompilationHint, tooltips.settings.show_shader_compilation_hint); @@ -1847,6 +1850,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->showMouseAndKeyboardToggleHint, emu_settings_type::ShowMouseAndKeyboardToggleHint); SubscribeTooltip(ui->showMouseAndKeyboardToggleHint, tooltips.settings.show_mouse_and_keyboard_toggle_hint); + m_emu_settings->EnhanceCheckBox(ui->showFatalErrorHints, emu_settings_type::ShowFatalErrorHints); + SubscribeTooltip(ui->showFatalErrorHints, tooltips.settings.show_fatal_error_hints); + m_emu_settings->EnhanceCheckBox(ui->showCaptureHints, emu_settings_type::ShowCaptureHints); SubscribeTooltip(ui->showCaptureHints, tooltips.settings.show_capture_hints); @@ -1875,6 +1881,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std }); ui->perfOverlayMarginY->setEnabled(!ui->perfOverlayCenterY->isChecked()); + m_emu_settings->EnhanceCheckBox(ui->perfOverlayUseWindowSpace, emu_settings_type::PerfOverlayUseWindowSpace); + SubscribeTooltip(ui->perfOverlayUseWindowSpace, tooltips.settings.perf_overlay_use_window_space); + m_emu_settings->EnhanceCheckBox(ui->perfOverlayFramerateGraphEnabled, emu_settings_type::PerfOverlayFramerateGraphEnabled); SubscribeTooltip(ui->perfOverlayFramerateGraphEnabled, tooltips.settings.perf_overlay_framerate_graph_enabled); @@ -1901,6 +1910,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->perfOverlayMarginY->setEnabled(enabled && !ui->perfOverlayCenterY->isChecked()); ui->perfOverlayCenterX->setEnabled(enabled); ui->perfOverlayCenterY->setEnabled(enabled); + ui->perfOverlayUseWindowSpace->setEnabled(enabled); ui->perfOverlayFramerateGraphEnabled->setEnabled(enabled); ui->perfOverlayFrametimeGraphEnabled->setEnabled(enabled); ui->perf_overlay_framerate_datapoints->setEnabled(enabled); @@ -1946,10 +1956,10 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std // SpinBoxes - m_emu_settings->EnhanceSpinBox(ui->perfOverlayMarginX, emu_settings_type::PerfOverlayMarginX, "", tr("px", "Performance overlay margin x")); + m_emu_settings->EnhanceDoubleSpinBox(ui->perfOverlayMarginX, emu_settings_type::PerfOverlayMarginX, "", tr("%", "Performance overlay margin x")); SubscribeTooltip(ui->perfOverlayMarginX, tooltips.settings.perf_overlay_margin_x); - m_emu_settings->EnhanceSpinBox(ui->perfOverlayMarginY, emu_settings_type::PerfOverlayMarginY, "", tr("px", "Performance overlay margin y")); + m_emu_settings->EnhanceDoubleSpinBox(ui->perfOverlayMarginY, emu_settings_type::PerfOverlayMarginY, "", tr("%", "Performance overlay margin y")); SubscribeTooltip(ui->perfOverlayMarginY, tooltips.settings.perf_overlay_margin_y); // Global settings (gui_settings) @@ -2091,7 +2101,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std connect(ui->edit_button_game_window_title_format, &QAbstractButton::clicked, [get_game_window_title, set_game_window_title, this]() { - auto get_game_window_title_label = [get_game_window_title, set_game_window_title, this](const QString& format) + auto get_game_window_title_label = [get_game_window_title](const QString& format) { const QString game_window_title = get_game_window_title(format); @@ -2165,9 +2175,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std SubscribeTooltip(ui->cb_custom_colors, tooltips.settings.custom_colors); SubscribeTooltip(ui->cb_show_welcome, tooltips.settings.show_welcome); SubscribeTooltip(ui->cb_show_exit_game, tooltips.settings.show_exit_game); - SubscribeTooltip(ui->cb_show_boot_game, tooltips.settings.show_boot_game); SubscribeTooltip(ui->cb_show_pkg_install, tooltips.settings.show_pkg_install); - SubscribeTooltip(ui->cb_show_pup_install, tooltips.settings.show_pup_install); SubscribeTooltip(ui->cb_show_obsolete_cfg_dialog, tooltips.settings.show_obsolete_cfg); SubscribeTooltip(ui->cb_show_same_buttons_dialog, tooltips.settings.show_same_buttons); SubscribeTooltip(ui->cb_show_restart_hint, tooltips.settings.show_restart_hint); @@ -2179,7 +2187,6 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std SubscribeTooltip(ui->cb_global_pad_navigation, tooltips.settings.global_navigation); ui->cb_pad_navigation->setChecked(m_gui_settings->GetValue(gui::nav_enabled).toBool()); ui->cb_global_pad_navigation->setChecked(m_gui_settings->GetValue(gui::nav_global).toBool()); -#if defined(_WIN32) || defined(__linux__) || defined(__APPLE__) connect(ui->cb_pad_navigation, &QCheckBox::toggled, [this](bool checked) { m_gui_settings->SetValue(gui::nav_enabled, checked); @@ -2188,9 +2195,16 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std { m_gui_settings->SetValue(gui::nav_global, checked); }); -#else - ui->gb_gui_pad_input->setEnabled(false); -#endif + + // Audio + SubscribeTooltip(ui->gb_gui_volume, tooltips.settings.gui_volume); + connect(ui->guiVolume, &QSlider::valueChanged, [this](int value) + { + ui->guiVolumeLabel->setText(tr("User Interface: %0 %", "GUI volume").arg(value)); + gui::volume = std::clamp(value / 100.0f, 0.0f, 1.0f); + m_gui_settings->SetValue(gui::gui_volume, gui::volume); + }); + ui->guiVolume->setValue(std::clamp(m_gui_settings->GetValue(gui::gui_volume).toFloat() * 100.0f, 0.0f, 100.0f)); // Discord: SubscribeTooltip(ui->useRichPresence, tooltips.settings.use_rich_presence); @@ -2273,9 +2287,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->cb_show_welcome->setChecked(m_gui_settings->GetValue(gui::ib_show_welcome).toBool()); ui->cb_show_exit_game->setChecked(m_gui_settings->GetValue(gui::ib_confirm_exit).toBool()); - ui->cb_show_boot_game->setChecked(m_gui_settings->GetValue(gui::ib_confirm_boot).toBool()); ui->cb_show_pkg_install->setChecked(m_gui_settings->GetValue(gui::ib_pkg_success).toBool()); - ui->cb_show_pup_install->setChecked(m_gui_settings->GetValue(gui::ib_pup_success).toBool()); ui->cb_show_obsolete_cfg_dialog->setChecked(m_gui_settings->GetValue(gui::ib_obsolete_cfg).toBool()); ui->cb_show_same_buttons_dialog->setChecked(m_gui_settings->GetValue(gui::ib_same_buttons).toBool()); ui->cb_show_restart_hint->setChecked(m_gui_settings->GetValue(gui::ib_restart_hint).toBool()); @@ -2305,17 +2317,11 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std connect(ui->cb_show_exit_game, &QCheckBox::toggled, [this](bool checked) { m_gui_settings->SetValue(gui::ib_confirm_exit, checked); - }); - connect(ui->cb_show_boot_game, &QCheckBox::toggled, [this](bool checked) - { m_gui_settings->SetValue(gui::ib_confirm_boot, checked); }); connect(ui->cb_show_pkg_install, &QCheckBox::toggled, [this](bool checked) { m_gui_settings->SetValue(gui::ib_pkg_success, checked); - }); - connect(ui->cb_show_pup_install, &QCheckBox::toggled, [this](bool checked) - { m_gui_settings->SetValue(gui::ib_pup_success, checked); }); connect(ui->cb_show_obsolete_cfg_dialog, &QCheckBox::toggled, [this](bool checked) @@ -2496,6 +2502,14 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceComboBox(ui->vulkansched, emu_settings_type::VulkanAsyncSchedulerDriver); SubscribeTooltip(ui->gb_vulkansched, tooltips.settings.vulkan_async_scheduler); + // Log levels + SubscribeTooltip(ui->gb_log_levels, tooltips.settings.log_levels); + connect(ui->pb_log_levels, &QAbstractButton::clicked, this, [this]() + { + log_level_dialog* dlg = new log_level_dialog(this, m_emu_settings); + dlg->open(); + }); + if (!restoreGeometry(m_gui_settings->GetValue(gui::cfg_geometry).toByteArray())) { // Ignore. This will most likely only fail if the setting doesn't contain any values diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index e394407e11..d3b98a2608 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -181,22 +181,6 @@ - - - - Additional Settings - - - - - - Enable SPU loop detection - - - - - - @@ -265,7 +249,7 @@ - + @@ -473,7 +457,7 @@ - Anti-Aliasing + Anti-Aliasing (MSAA) @@ -527,6 +511,36 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Vsync + + + + + + + + + + + @@ -924,13 +938,6 @@ - - - - VSync - - - @@ -2321,9 +2328,9 @@ - - Enable Clans - + + Enable Clans + @@ -2414,24 +2421,10 @@ Core - - - - Debug Console Mode - - - - - - - Accurate DFMA - - - - Accurate RSX reservation access + Accurate RSX Reservation Access @@ -2442,6 +2435,27 @@ + + + + Anti-Cheat Savestates Mode + + + + + + + Debug Console Mode + + + + + + + Delay each odd MFC Command + + + @@ -2457,9 +2471,9 @@ - + - PPU Non-Java Mode Fixup + Enable SPU loop detection @@ -2471,30 +2485,16 @@ - + - Delay each odd MFC Command - - - - - - - Anti-Cheat Savestates Mode + Silence All Logs - SPU-Compatible Savestates Mode - - - - - - - Silence All Logs + SPU Compatible Savestates Mode @@ -2672,30 +2672,16 @@ - + - Read Depth Buffers + Allow Host GPU Labels (Experimental) - + - Write Depth Buffers - - - - - - - Read Color Buffers - - - - - - - Handle RSX memory tiling + Disable MSL Fast Math @@ -2707,16 +2693,30 @@ - + - Allow Host GPU Labels (Experimental) + Force Hardware MSAA Resolve - + - Force Hardware MSAA Resolve + Handle RSX Memory Tiling + + + + + + + Read Depth Buffer + + + + + + + Read Color Buffers @@ -2728,9 +2728,9 @@ - + - Disable MSL Fast Math + Write Depth Buffer @@ -2945,16 +2945,16 @@ - + - Exit RPCS3 when process finishes + Enable GameMode - + - Pause emulation on RPCS3 focus loss + Exit RPCS3 when process finishes @@ -2972,6 +2972,13 @@ + + + + Pause emulation on RPCS3 focus loss + + + @@ -2979,69 +2986,6 @@ - - - - Show trophy popups - - - - - - - Show RPCN popups - - - - - - - Show shader compilation hint - - - - - - - Show PPU compilation hint - - - - - - - Show autosave/autoload hint - - - - - - - Show analog limiter toggle hint - - - - - - - Show pressure intensity toggle hint - - - - - - - Show mouse and keyboard toggle hint - - - - - - - Show capture hints - - - @@ -3056,17 +3000,96 @@ + + + + + + + Overlay Settings + + - + - Record with overlays + Play music during boot sequence - + - Enable GameMode + Record and screenshot with overlays + + + + + + + Show analog limiter toggle hint + + + + + + + Show autosave/autoload hint + + + + + + + Show capture hints + + + + + + + Show fatal error hints + + + + + + + Show mouse and keyboard toggle hint + + + + + + + Show netplay popups + + + + + + + Show pressure intensity toggle hint + + + + + + + Show PPU compilation hint + + + + + + + Show shader compilation hint + + + + + + + Show trophy popups @@ -3515,7 +3538,14 @@ - + + + 1 + + + 0.5 + + @@ -3536,10 +3566,24 @@ - + + + 1 + + + 0.5 + + + + + + Use Window Space + + + @@ -3971,6 +4015,56 @@ + + + + Volume + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + GUI: 0% + + + + + + + 100 + + + Qt::Orientation::Horizontal + + + QSlider::TickPosition::TicksBelow + + + 50 + + + + + + + + + @@ -4007,28 +4101,14 @@ - Show Exit Game Dialog - - - - - - - Show Boot Game Dialog + Show Exit Game Confirmation - Show PKG Installation Dialog - - - - - - - Show PUP Installation Dialog + Show PKG/PUP Installation Result @@ -4348,6 +4428,13 @@ Core + + + + Accurate DFMA + + + @@ -4411,6 +4498,13 @@ + + + + PPU Non-Java Mode Fixup + + + @@ -4467,7 +4561,7 @@ - + 0 @@ -4545,6 +4639,22 @@ + + + + Log Levels + + + + + + Configure + + + + + + diff --git a/rpcs3/rpcs3qt/shortcut_dialog.ui b/rpcs3/rpcs3qt/shortcut_dialog.ui index efafa998a2..04dbdb2157 100644 --- a/rpcs3/rpcs3qt/shortcut_dialog.ui +++ b/rpcs3/rpcs3qt/shortcut_dialog.ui @@ -11,7 +11,7 @@ - Shortcuts + Keyboard Shortcuts @@ -25,10 +25,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::MinimumExpanding + QSizePolicy::Policy::MinimumExpanding @@ -50,10 +50,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::MinimumExpanding + QSizePolicy::Policy::MinimumExpanding @@ -71,7 +71,7 @@ - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Save + QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save diff --git a/rpcs3/rpcs3qt/shortcut_utils.cpp b/rpcs3/rpcs3qt/shortcut_utils.cpp index 2bc2bfc578..d7b5586e75 100644 --- a/rpcs3/rpcs3qt/shortcut_utils.cpp +++ b/rpcs3/rpcs3qt/shortcut_utils.cpp @@ -1,7 +1,12 @@ #include "stdafx.h" #include "shortcut_utils.h" +#include "steam_utils.h" #include "qt_utils.h" +#include "gui_game_info.h" + #include "Emu/VFS.h" +#include "Emu/vfs_config.h" +#include "Emu/system_utils.hpp" #include "Utilities/File.h" #include "Utilities/StrUtil.h" #include "Loader/ISO.h" @@ -30,6 +35,25 @@ LOG_CHANNEL(sys_log, "SYS"); +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](gui::utils::shortcut_location value) + { + switch (value) + { + case gui::utils::shortcut_location::desktop: return "desktop"; + case gui::utils::shortcut_location::applications: return "applications"; + case gui::utils::shortcut_location::steam: return "steam"; +#ifdef _WIN32 + case gui::utils::shortcut_location::rpcs3_shortcuts: return "rpcs3"; +#endif + } + + return unknown; + }); +} + namespace gui::utils { #ifdef _WIN32 @@ -57,7 +81,7 @@ namespace gui::utils return false; } - if (!gui::utils::create_square_pixmap(icon, size)) + if (!create_square_pixmap(icon, size)) { sys_log.error("Failed to create shortcut. Icon empty."); return false; @@ -85,14 +109,23 @@ namespace gui::utils return true; } + static std::string make_simple_name(const std::string& name) + { + const std::string simple_name = QString::fromStdString(vfs::escape(name, true)).simplified().toStdString(); + return simple_name; + } + bool create_shortcut(const std::string& name, const std::string& path, [[maybe_unused]] const std::string& serial, - [[maybe_unused]] const std::string& target_cli_args, + const std::string& target_cli_args, [[maybe_unused]] const std::string& description, - [[maybe_unused]] const std::string& src_icon_path, + const std::string& src_icon_path, [[maybe_unused]] const std::string& target_icon_dir, - shortcut_location location) + const std::string& src_banner_path, + shortcut_location location, + std::shared_ptr steam_sc, + std::shared_ptr archive) { if (name.empty()) { @@ -101,7 +134,7 @@ namespace gui::utils } // Remove illegal characters from filename - const std::string simple_name = QString::fromStdString(vfs::escape(name, true)).simplified().toStdString(); + const std::string simple_name = make_simple_name(name); if (simple_name.empty() || simple_name == "." || simple_name == "..") { sys_log.error("Failed to create shortcut: Cleaned file name empty or not allowed"); @@ -109,6 +142,7 @@ namespace gui::utils } std::string link_path; + bool append_rpcs3 = false; switch (location) { @@ -117,6 +151,13 @@ namespace gui::utils break; case shortcut_location::applications: link_path = QStandardPaths::writableLocation(QStandardPaths::StandardLocation::ApplicationsLocation).toStdString(); + append_rpcs3 = true; + break; + case shortcut_location::steam: +#ifdef __APPLE__ + link_path = QStandardPaths::writableLocation(QStandardPaths::StandardLocation::ApplicationsLocation).toStdString(); + append_rpcs3 = true; +#endif break; #ifdef _WIN32 case shortcut_location::rpcs3_shortcuts: @@ -126,13 +167,13 @@ namespace gui::utils #endif } - if (!fs::is_dir(link_path) && !fs::create_dir(link_path)) + if (!link_path.empty() && !fs::is_dir(link_path) && !fs::create_dir(link_path)) { sys_log.error("Failed to create shortcut. Folder does not exist: %s", link_path); return false; } - if (location == shortcut_location::applications) + if (append_rpcs3) { link_path += "/RPCS3"; @@ -144,6 +185,28 @@ namespace gui::utils } #ifdef _WIN32 + const std::string working_dir{fs::get_executable_dir()}; + const std::string rpcs3_path{fs::get_executable_path()}; + std::string target_icon_path; + + if (!src_icon_path.empty() && !target_icon_dir.empty()) + { + if (!create_square_shortcut_icon_file(path, src_icon_path, target_icon_dir, target_icon_path, 512)) + { + sys_log.error("Failed to create shortcut: .ico creation failed"); + return false; + } + } + + if (location == shortcut_location::steam) + { + sys_log.notice("Creating %s shortcut with arguments '%s'", location, target_cli_args); + ensure(steam_sc)->add_shortcut(simple_name, rpcs3_path, working_dir, target_cli_args, target_icon_path, src_icon_path, src_banner_path, archive); + return true; + } + + sys_log.notice("Creating %s shortcut '%s' with arguments '%s' and .ico dir '%s'", location, link_path, target_cli_args, target_icon_dir); + const auto str_error = [](HRESULT hr) -> std::string { _com_error err(hr); @@ -153,8 +216,6 @@ namespace gui::utils fmt::append(link_path, "/%s.lnk", simple_name); - sys_log.notice("Creating shortcut '%s' with arguments '%s' and .ico dir '%s'", link_path, target_cli_args, target_icon_dir); - // https://stackoverflow.com/questions/3906974/how-to-programmatically-create-a-shortcut-using-win32 HRESULT res = CoInitialize(NULL); if (FAILED(res)) @@ -177,9 +238,6 @@ namespace gui::utils if (FAILED(res)) return cleanup(false, "CoCreateInstance failed"); - const std::string working_dir{ fs::get_executable_dir() }; - const std::string rpcs3_path{ working_dir + "rpcs3.exe" }; - const std::wstring w_target_file = utf8_to_wchar(rpcs3_path); res = pShellLink->SetPath(w_target_file.c_str()); if (FAILED(res)) @@ -206,12 +264,8 @@ namespace gui::utils return cleanup(false, fmt::format("SetDescription failed (%s)", str_error(res))); } - if (!src_icon_path.empty() && !target_icon_dir.empty()) + if (!target_icon_path.empty()) { - std::string target_icon_path; - if (!create_square_shortcut_icon_file(path, src_icon_path, target_icon_dir, target_icon_path, 512)) - return cleanup(false, ".ico creation failed"); - const std::wstring w_icon_path = utf8_to_wchar(target_icon_path); res = pShellLink->SetIconLocation(w_icon_path.c_str(), 0); if (FAILED(res)) @@ -241,9 +295,10 @@ namespace gui::utils return cleanup(true, {}); #elif defined(__APPLE__) + fmt::append(link_path, "/%s.app", simple_name); - sys_log.notice("Creating shortcut '%s' with arguments '%s'", link_path, target_cli_args); + sys_log.notice("Creating %s shortcut '%s' with arguments '%s'", location, link_path, target_cli_args); const std::string contents_dir = link_path + "/Contents/"; const std::string macos_dir = contents_dir + "MacOS/"; @@ -330,10 +385,15 @@ namespace gui::utils } } + if (location == shortcut_location::steam) + { + ensure(steam_sc)->add_shortcut(simple_name, link_path, macos_dir, ""/*target_cli_args are already in the launcher*/, "", src_icon_path, src_banner_path, archive); + return true; + } + return true; #else - const std::string exe_path = fs::get_executable_path(); if (exe_path.empty()) { @@ -341,9 +401,27 @@ namespace gui::utils return false; } + std::string target_icon_path; + if (!src_icon_path.empty() && !target_icon_dir.empty()) + { + if (!create_square_shortcut_icon_file(path, src_icon_path, target_icon_dir, target_icon_path, 512)) + { + // Error is logged in create_square_shortcut_icon_file + return false; + } + } + + if (location == shortcut_location::steam) + { + sys_log.notice("Creating %s shortcut with arguments '%s'", location, target_cli_args); + const std::string working_dir{fs::get_executable_dir()}; + ensure(steam_sc)->add_shortcut(simple_name, exe_path, working_dir, target_cli_args, target_icon_path, src_icon_path, src_banner_path, archive); + return true; + } + fmt::append(link_path, "/%s.desktop", simple_name); - sys_log.notice("Creating shortcut '%s' for '%s' with arguments '%s'", link_path, exe_path, target_cli_args); + sys_log.notice("Creating %s shortcut '%s' for '%s' with arguments '%s'", location, link_path, exe_path, target_cli_args); std::string file_content; fmt::append(file_content, "[Desktop Entry]\n"); @@ -360,15 +438,8 @@ namespace gui::utils fmt::append(file_content, "Comment=%s\n", QString::fromStdString(description).simplified()); } - if (!src_icon_path.empty() && !target_icon_dir.empty()) + if (!target_icon_path.empty()) { - std::string target_icon_path; - if (!create_square_shortcut_icon_file(path, src_icon_path, target_icon_dir, target_icon_path, 512)) - { - // Error is logged in create_square_shortcut_icon_file - return false; - } - fmt::append(file_content, "Icon=%s\n", target_icon_path); } @@ -398,9 +469,150 @@ namespace gui::utils #endif } - void remove_shortcuts(const std::string& name, [[maybe_unused]] const std::string& serial) + bool create_shortcuts(const std::shared_ptr& game, + const std::set& locations, + std::shared_ptr steam_sc) + { + if (!game || locations.empty()) return false; + + std::string gameid_token_value; + + const std::string dev_flash = g_cfg_vfs.get_dev_flash(); + const bool is_iso = is_file_iso(game->info.path); + std::shared_ptr archive; + + const auto file_exists = [&archive](const std::string& path) + { + return archive ? archive->is_file(path) : fs::is_file(path); + }; + + if (is_iso) + { + gameid_token_value = game->info.serial; + archive = std::make_shared(game->info.path); + } + else if (game->info.category == "DG" && !fs::is_file(rpcs3::utils::get_hdd0_dir() + "/game/" + game->info.serial + "/USRDIR/EBOOT.BIN")) + { + const usz ps3_game_dir_pos = fs::get_parent_dir(game->info.path).size(); + std::string relative_boot_dir = game->info.path.substr(ps3_game_dir_pos); + + if (usz char_pos = relative_boot_dir.find_first_not_of(fs::delim); char_pos != umax) + { + relative_boot_dir = relative_boot_dir.substr(char_pos); + } + else + { + relative_boot_dir.clear(); + } + + if (!relative_boot_dir.empty()) + { + if (relative_boot_dir != "PS3_GAME") + { + gameid_token_value = game->info.serial + "/" + relative_boot_dir; + } + else + { + gameid_token_value = game->info.serial; + } + } + } + else + { + gameid_token_value = game->info.serial; + } + + const std::string target_icon_dir = fmt::format("%sIcons/game_icons/%s/", fs::get_config_dir(), game->info.serial); + + if (!fs::create_path(target_icon_dir)) + { + sys_log.error("Failed to create shortcut path %s (%s)", QString::fromStdString(game->info.name).simplified(), target_icon_dir, fs::g_tls_error); + return false; + } + + bool success = true; + const bool is_vsh = game->info.path.starts_with(dev_flash); + const std::string cli_arg_token = is_vsh ? "RPCS3_VFS" : "RPCS3_GAMEID"; + const std::string cli_arg_value = is_vsh ? ("dev_flash/" + game->info.path.substr(dev_flash.size())) : gameid_token_value; + + for (shortcut_location location : locations) + { + std::string banner_path; + + if (location == shortcut_location::steam) + { + // Try to find a nice banner for steam + const std::string sfo_dir = is_iso ? "PS3_GAME" : rpcs3::utils::get_sfo_dir_from_game_path(game->info.path); + + for (const std::string& filename : {"PIC1.PNG"s, "PIC3.PNG"s, "PIC0.PNG"s, "PIC2.PNG"s, "ICON0.PNG"s}) + { + if (const std::string filepath = fmt::format("%s/%s", sfo_dir, filename); file_exists(filepath)) + { + banner_path = filepath; + break; + } + } + } + +#ifdef __linux__ + const std::string percent = location == shortcut_location::steam ? "%" : "%%"; +#else + const std::string percent = "%"; +#endif + const std::string target_cli_args = fmt::format("--no-gui \"%s%s%s:%s\"", percent, cli_arg_token, percent, cli_arg_value); + + if (!gameid_token_value.empty() && create_shortcut(game->info.name, game->icon_in_archive ? game->info.path : "", game->info.serial, target_cli_args, game->info.name, game->info.icon_path, target_icon_dir, banner_path, location, steam_sc, archive)) + { + if (location == shortcut_location::steam) + { + // Creation is done in caller + sys_log.notice("Prepared %s shortcut for '%s'", location, QString::fromStdString(game->info.name).simplified()); + } + else + { + sys_log.success("Created %s shortcut for '%s'", location, QString::fromStdString(game->info.name).simplified()); + } + } + else + { + sys_log.error("Failed to create %s shortcut for '%s'", location, QString::fromStdString(game->info.name).simplified()); + success = false; + } + } + + return success; + } + + bool batch_create_shortcuts(const std::vector>& games, + const std::set& locations) + { + if (games.empty() || locations.empty()) return false; + + std::shared_ptr steam_sc; + + if (locations.contains(shortcut_location::steam)) + { + // Batch steam shortcut creation + steam_sc = std::make_shared(); + } + + bool result = true; + + for (const auto& game : games) + { + result &= create_shortcuts(game, locations, steam_sc); + } + + if (steam_sc) + { + result &= steam_sc->write_file(); + } + + return result; + } + + static void remove_shortcuts(const std::string& simple_name, [[maybe_unused]] const std::string& serial) { - const std::string simple_name = QString::fromStdString(vfs::escape(name, true)).simplified().toStdString(); if (simple_name.empty() || simple_name == "." || simple_name == "..") { sys_log.error("Failed to remove shortcuts: Cleaned file name empty or not allowed"); @@ -438,7 +650,8 @@ namespace gui::utils std::vector locations = { shortcut_location::desktop, - shortcut_location::applications + shortcut_location::applications, + //shortcut_location::steam, // Handled separately }; #ifdef _WIN32 locations.push_back(shortcut_location::rpcs3_shortcuts); @@ -457,6 +670,9 @@ namespace gui::utils link_path = QStandardPaths::writableLocation(QStandardPaths::StandardLocation::ApplicationsLocation).toStdString(); link_path += "/RPCS3"; break; + case shortcut_location::steam: + fmt::throw_exception("Steam shortcuts should be removed in batch"); + break; #ifdef _WIN32 case shortcut_location::rpcs3_shortcuts: link_path = rpcs3::utils::get_games_shortcuts_dir(); @@ -479,4 +695,32 @@ namespace gui::utils const std::string icon_path = fmt::format("%sIcons/game_icons/%s/shortcut.%s", fs::get_config_dir(), serial, icon_extension); remove_path(icon_path, true); } + + void batch_remove_shortcuts(const std::vector>& games) + { + if (games.empty()) return; + + // Batch steam shortcut removal + const std::string exe_path = fs::get_executable_path(); + const std::string working_dir = fs::get_executable_dir(); + const bool is_steam_installed = steam_shortcut::steam_installed(); + steam_shortcut steam_sc{}; + + for (const auto& [name, serial] : games) + { + const std::string simple_name = make_simple_name(name); + + remove_shortcuts(simple_name, serial); + + if (is_steam_installed) + { + steam_sc.remove_shortcut(simple_name, exe_path, working_dir); + } + } + + if (is_steam_installed && !steam_sc.write_file()) + { + sys_log.error("Failed to remove steam shortcuts"); + } + } } diff --git a/rpcs3/rpcs3qt/shortcut_utils.h b/rpcs3/rpcs3qt/shortcut_utils.h index 4e7fcdf0ce..f41741ecdf 100644 --- a/rpcs3/rpcs3qt/shortcut_utils.h +++ b/rpcs3/rpcs3qt/shortcut_utils.h @@ -1,11 +1,18 @@ #pragma once +#include + +struct gui_game_info; +class iso_archive; namespace gui::utils { + class steam_shortcut; + enum class shortcut_location { desktop, applications, + steam, #ifdef _WIN32 rpcs3_shortcuts, #endif @@ -18,7 +25,13 @@ namespace gui::utils const std::string& description, const std::string& src_icon_path, const std::string& target_icon_dir, - shortcut_location shortcut_location); + const std::string& src_banner_path, + shortcut_location shortcut_location, + std::shared_ptr steam_sc = nullptr, + std::shared_ptr archive = nullptr); - void remove_shortcuts(const std::string& name, const std::string& serial); + bool batch_create_shortcuts(const std::vector>& games, + const std::set& locations); + + void batch_remove_shortcuts(const std::vector>& games); } diff --git a/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp b/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp index cfd93246eb..3f7aeccce0 100644 --- a/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp @@ -41,7 +41,7 @@ sound_effect_manager_dialog::sound_effect_manager_dialog(QWidget* parent) } QPushButton* button = new QPushButton("", this); - connect(button, &QAbstractButton::clicked, this, [this, button, sound, name]() + connect(button, &QAbstractButton::clicked, this, [this, sound, name]() { const std::string path = rsx::overlays::get_sound_filepath(sound); if (fs::is_file(path)) diff --git a/rpcs3/rpcs3qt/steam_utils.cpp b/rpcs3/rpcs3qt/steam_utils.cpp new file mode 100644 index 0000000000..e5aedf9e20 --- /dev/null +++ b/rpcs3/rpcs3qt/steam_utils.cpp @@ -0,0 +1,1135 @@ +#include "stdafx.h" +#include "steam_utils.h" +#include "qt_utils.h" + +#include "Loader/ISO.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +LOG_CHANNEL(sys_log, "SYS"); + +namespace gui::utils +{ + void steam_shortcut::add_shortcut( + const std::string& app_name, + const std::string& exe, + const std::string& start_dir, + const std::string& launch_options, + const std::string& icon_path, + const std::string& banner_small_path, + const std::string& banner_large_path, + std::shared_ptr archive) + { + shortcut_entry entry{}; + entry.app_name = app_name; + entry.exe = quote(fix_slashes(exe), true); + entry.start_dir = quote(fix_slashes(start_dir), false); + entry.launch_options = launch_options; + entry.icon = quote(fix_slashes(icon_path), false); + entry.banner_small = banner_small_path; + entry.banner_large = banner_large_path; + entry.appid = steam_appid(exe, app_name); + entry.iso = archive; + + m_entries_to_add.push_back(std::move(entry)); + } + + void steam_shortcut::remove_shortcut( + const std::string& app_name, + const std::string& exe, + const std::string& start_dir) + { + shortcut_entry entry{}; + entry.app_name = app_name; + entry.exe = quote(fix_slashes(exe), true); + entry.start_dir = quote(fix_slashes(start_dir), false); + entry.appid = steam_appid(exe, app_name); + + m_entries_to_remove.push_back(std::move(entry)); + } + + bool steam_shortcut::parse_file(const std::string& path) + { + m_vdf_entries.clear(); + + fs::file vdf(path); + if (!vdf) + { + sys_log.error("Failed to parse steam shortcut file '%s': %s", path, fs::g_tls_error); + return false; + } + + const std::vector data = vdf.to_vector(); + vdf.close(); + + usz last_pos = 0; + usz pos = 0; + + const auto read_type_id = [&]() -> u8 + { + if (pos >= data.size()) + { + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: read_type_id: end of file", path, pos); + return umax; + } + + last_pos = pos; + return data[pos++]; + }; + + const auto read_u32 = [&]() -> std::optional + { + if ((pos + sizeof(u32)) > data.size()) + { + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: read_u32: end of file", path, pos); + return {}; + } + + last_pos = pos; + + u32 v {}; + std::memcpy(&v, &data[pos], sizeof(u32)); + pos += sizeof(u32); + return v; + }; + + const auto read_string = [&]() -> std::optional + { + if (pos >= data.size()) + { + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: read_string: end of file", path, pos); + return {}; + } + + last_pos = pos; + + std::string str; + + while (pos < data.size()) + { + const u8 c = data[pos++]; + if (!c) break; + str += c; + } + + if (pos >= data.size()) + { + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: read_string: not null terminated", path, last_pos); + return {}; + } + + return str; + }; + + #define CHECK_VDF(cond, msg) \ + if (!(cond)) \ + { \ + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: %s", path, last_pos, msg); \ + return false; \ + } + + #define READ_VDF_STRING(name) \ + const std::optional name##_opt = read_string(); \ + if (!name##_opt.has_value()) return false; \ + std::string name = name##_opt.value(); + + #define READ_VDF_U32(name) \ + const std::optional name##_opt = read_u32(); \ + if (!name##_opt.has_value()) return false; \ + const u32 name = name##_opt.value(); + + CHECK_VDF(read_type_id() == type_id::Start, "expected type_id::Start for shortcuts"); + READ_VDF_STRING(shortcuts); + CHECK_VDF(shortcuts == "shortcuts", "expected 'shortcuts' key"); + + for (usz index = 0; true; index++) + { + vdf_shortcut_entry entry {}; + + u8 type = read_type_id(); + if (type == type_id::End) + { + // End of shortcuts + break; + } + + CHECK_VDF(type == type_id::Start, "expected type_id::Start for entry"); + + READ_VDF_STRING(entry_index_str); + u64 entry_index = 0; + CHECK_VDF(try_to_uint64(&entry_index, entry_index_str, 0, umax), "failed to convert entry index"); + CHECK_VDF(entry_index == index, "unexpected entry index"); + + type = umax; + while (type != type_id::Start) + { + type = read_type_id(); + + switch (type) + { + case type_id::String: + { + READ_VDF_STRING(key); + READ_VDF_STRING(value); + CHECK_VDF(!key.empty(), "key is empty"); + entry.values.push_back({std::move(key), std::move(value)}); + break; + } + case type_id::Integer: + { + READ_VDF_STRING(key); + READ_VDF_U32(value); + CHECK_VDF(!key.empty(), "key is empty"); + entry.values.push_back({std::move(key), value}); + break; + } + case type_id::Start: + // Expect tags next + break; + default: + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: unexpected type id 0x%x", path, last_pos, type); + return false; + } + } + + CHECK_VDF(type == type_id::Start, "expected type_id::Start for tags"); + READ_VDF_STRING(tags); + CHECK_VDF(tags == "tags", "key is empty"); + type = umax; + while (type != type_id::End) + { + type = read_type_id(); + + switch (type) + { + case type_id::String: + { + READ_VDF_STRING(key); + READ_VDF_STRING(value); + CHECK_VDF(!key.empty(), "key is empty"); + entry.tags.push_back({std::move(key), std::move(value)}); + break; + } + case type_id::End: + break; + default: + sys_log.error("Failed to parse steam shortcut file '%s' at pos 0x%x: unexpected type id 0x%x", path, last_pos, type); + return false; + } + } + CHECK_VDF(type == type_id::End, "expected type_id::End for tags"); + + CHECK_VDF(read_type_id() == type_id::End, "expected type_id::End for entry"); + + m_vdf_entries.push_back(std::move(entry)); + } + + CHECK_VDF(read_type_id() == type_id::End, "expected type_id::End for end of file"); + CHECK_VDF(pos == data.size(), fmt::format("bytes found at end of file (pos=%d, size=%d)", pos, data.size())); + + #undef CHECK_VDF_OPT + #undef CHECK_VDF + return true; + } + + bool steam_shortcut::write_file() + { + if (m_entries_to_add.empty() && m_entries_to_remove.empty()) + { + sys_log.error("Failed to create steam shortcut: No entries."); + return false; + } + + const std::string steam_path = get_steam_path(); + if (steam_path.empty()) + { + sys_log.error("Failed to create steam shortcut: Steam directory not found."); + return false; + } + + if (!fs::is_dir(steam_path)) + { + sys_log.error("Failed to create steam shortcut: '%s' not a directory.", steam_path); + return false; + } + + const std::string user_id = get_last_active_steam_user(steam_path); + if (user_id.empty()) + { + sys_log.error("Failed to create steam shortcut: last active user not found."); + return false; + } + + const std::string user_dir = steam_path + "/userdata/" + user_id + "/config/"; + if (!fs::is_dir(user_dir)) + { + sys_log.error("Failed to create steam shortcut: '%s' not a directory.", user_dir); + return false; + } + + if (is_steam_running()) + { + sys_log.error("Failed to create steam shortcut: steam is running."); + return false; + } + + const std::string file_path = user_dir + "shortcuts.vdf"; + const std::string backup_path = fs::get_config_dir() + "/shortcuts.vdf.backup"; + + if (fs::is_file(file_path)) + { + if (!fs::copy_file(file_path, backup_path, true)) + { + sys_log.error("Failed to backup steam shortcut file '%s'", file_path); + return false; + } + + if (!parse_file(file_path)) + { + sys_log.error("Failed to parse steam shortcut file '%s'", file_path); + return false; + } + } + + std::vector removed_entries; + + for (const shortcut_entry& entry : m_entries_to_remove) + { + bool removed_entry = false; + for (auto it = m_vdf_entries.begin(); it != m_vdf_entries.end();) + { + const auto appid = it->value("appid"); + const auto exe = it->value("Exe"); + const auto start_dir = it->value("StartDir"); + + if (appid.has_value() && appid.value() == entry.appid && + exe.has_value() && exe.value() == entry.exe && + start_dir.has_value() && start_dir.value() == entry.start_dir) + { + sys_log.notice("Removing steam shortcut for '%s'", entry.app_name); + it = m_vdf_entries.erase(it); + removed_entry = true; + } + else + { + it++; + } + } + + if (removed_entry) + { + removed_entries.push_back(entry); + } + + if (m_vdf_entries.empty()) + { + break; + } + } + + for (const vdf_shortcut_entry& entry : m_vdf_entries) + { + for (auto it = m_entries_to_add.begin(); it != m_entries_to_add.end();) + { + const auto appid = entry.value("appid"); + const auto exe = entry.value("Exe"); + const auto start_dir = entry.value("StartDir"); + const auto launch_options = entry.value("LaunchOptions"); + const auto icon = entry.value("icon"); + + const bool appid_matches = appid.has_value() && appid.value() == it->appid; + + if (appid_matches && + exe.has_value() && exe.value() == it->exe && + start_dir.has_value() && start_dir.value() == it->start_dir && + launch_options.has_value() && launch_options.value() == it->launch_options && + icon.has_value() && icon.value() == it->icon) + { + sys_log.notice("Entry '%s' already exists in steam shortcut file '%s'.", it->app_name, file_path); + it = m_entries_to_add.erase(it); + } + else + { + if (appid_matches) + { + sys_log.notice("Entry '%s' already exists in steam shortcut file '%s' but with other parameters. Creating an additional one...", it->app_name, file_path); + } + + it++; + } + } + + if (m_entries_to_add.empty()) + { + break; + } + } + + if (m_entries_to_add.empty() && removed_entries.empty()) + { + sys_log.notice("Nothing to add or remove in steam shortcut file '%s'.", file_path); + return true; + } + + usz index = 0; + std::string content; + + content += type_id::Start; + append(content, "shortcuts"); + for (const vdf_shortcut_entry& entry : m_vdf_entries) + { + const auto val = entry.build_binary_blob(index++); + if (!val.has_value()) + { + sys_log.error("Failed to create steam shortcut '%s': '%s'", file_path, val.error()); + return false; + } + content += val.value(); + } + for (const shortcut_entry& entry : m_entries_to_add) + { + content += entry.build_binary_blob(index++); + } + content += type_id::End; + content += type_id::End; // End of file + + if (!fs::write_file(file_path, fs::rewrite, content)) + { + sys_log.error("Failed to create steam shortcut '%s': '%s'", file_path, fs::g_tls_error); + + if (!fs::copy_file(backup_path, file_path, true)) + { + sys_log.error("Failed to restore steam shortcuts backup: '%s'", fs::g_tls_error); + } + + return false; + } + + const std::string grid_dir = user_dir + "grid/"; + if (!fs::create_path(grid_dir)) + { + sys_log.error("Failed to create steam shortcut grid dir '%s': '%s'", grid_dir, fs::g_tls_error); + } + + QFutureWatcher* future_watcher = new QFutureWatcher(); + future_watcher->setFuture(QtConcurrent::map(m_entries_to_add, [&grid_dir](const shortcut_entry& entry) + { + std::string banner_small_path; + std::string banner_large_path; + + const auto file_exists = [&entry](const std::string& path) + { + return entry.iso ? entry.iso->is_file(path) : fs::is_file(path); + }; + + for (const std::string& path : { entry.banner_small, entry.banner_large }) + { + if (file_exists(path)) + { + banner_small_path = path; + break; + } + } + + for (const std::string& path : { entry.banner_large, entry.banner_small }) + { + if (file_exists(path)) + { + banner_large_path = path; + break; + } + } + + if (QPixmap banner; load_icon(banner, banner_large_path, entry.iso ? entry.iso->path() : "")) + { + create_steam_banner(steam_banner::wide_cover, banner_large_path, banner, grid_dir, entry); + create_steam_banner(steam_banner::background, banner_large_path, banner, grid_dir, entry); + } + else + { + sys_log.warning("Failed to load large steam banner file '%s'", banner_large_path); + } + + if (QPixmap banner; load_icon(banner, banner_small_path, entry.iso ? entry.iso->path() : "")) + { + create_steam_banner(steam_banner::cover, banner_small_path, banner, grid_dir, entry); + create_steam_banner(steam_banner::logo, banner_small_path, banner, grid_dir, entry); + create_steam_banner(steam_banner::icon, banner_small_path, banner, grid_dir, entry); + } + else + { + sys_log.warning("Failed to load small steam banner file '%s'", banner_small_path); + } + })); + + future_watcher->waitForFinished(); + future_watcher->deleteLater(); + + for (const shortcut_entry& entry : m_entries_to_add) + { + sys_log.success("Created steam shortcut for '%s'", entry.app_name); + } + + for (const shortcut_entry& entry : removed_entries) + { + const auto remove_banner = [&entry, &grid_dir](steam_banner banner) + { + const std::string banner_path = get_steam_banner_path(banner, grid_dir, entry.appid); + + if (fs::is_file(banner_path)) + { + if (fs::remove_file(banner_path)) + { + sys_log.notice("Removed steam shortcut banner '%s'", banner_path); + } + else + { + sys_log.error("Failed to remove steam shortcut banner '%s': error='%s'", banner_path, fs::g_tls_error); + } + } + }; + + remove_banner(steam_banner::cover); + remove_banner(steam_banner::wide_cover); + remove_banner(steam_banner::background); + remove_banner(steam_banner::logo); + remove_banner(steam_banner::icon); + + sys_log.success("Removed steam shortcut(s) for '%s'", entry.app_name); + } + + update_steam_input_config(user_dir); + + return true; + } + + bool steam_shortcut::steam_installed() + { + const std::string path = get_steam_path(); + return !path.empty() && fs::is_dir(path); + } + + u32 steam_shortcut::crc32(const std::string& data) + { + u32 crc = 0xFFFFFFFF; + + for (u8 c : data) + { + crc ^= c; + + for (int i = 0; i < 8; i++) + { + crc = (crc >> 1) ^ (0xEDB88320 & -static_cast(crc & 1)); + } + } + + return ~crc; + } + + u32 steam_shortcut::steam_appid(const std::string& exe, const std::string& name) + { + return crc32(exe + name) | 0x80000000; + } + + void steam_shortcut::append(std::string& s, const std::string& val) + { + s += val; + s += '\0'; // append null terminator + } + + std::string steam_shortcut::quote(const std::string& s, bool force) + { + if (force || s.contains(" ")) + { + return "\"" + s + "\""; + } + + return s; + } + + std::string steam_shortcut::fix_slashes(const std::string& s) + { +#ifdef _WIN32 + return fmt::replace_all(s, "/", "\\"); +#else + return s; +#endif + } + + std::string steam_shortcut::kv_string(const std::string& key, const std::string& value) + { + std::string ret; + ret += type_id::String; + append(ret, key); + append(ret, value); + return ret; + } + + std::string steam_shortcut::kv_int(const std::string& key, u32 value) + { + std::string str; + str.reserve(64); + str += type_id::Integer; + append(str, key); + str += static_cast(value & 0xFF); + str += static_cast((value >> 8) & 0xFF); + str += static_cast((value >> 16) & 0xFF); + str += static_cast((value >> 24) & 0xFF); + return str; + } + + std::string steam_shortcut::shortcut_entry::build_binary_blob(usz index) const + { + std::string str; + str.reserve(1024); + str += type_id::Start; + + append(str, std::to_string(index)); + + str += kv_int("appid", appid); + str += kv_string("AppName", app_name); + str += kv_string("Exe", exe); + str += kv_string("StartDir", start_dir); + str += kv_string("icon", icon); + str += kv_string("ShortcutPath", ""); + str += kv_string("LaunchOptions", launch_options); + str += kv_int("IsHidden", 0); + str += kv_int("AllowDesktopConfig", 1); + str += kv_int("AllowOverlay", 1); + str += kv_int("OpenVR", 0); + str += kv_int("Devkit", 0); + str += kv_string("DevkitGameID", ""); + str += kv_int("DevkitOverrideAppID", 0); + str += kv_int("LastPlayTime", 0); + str += kv_string("FlatpakAppID", ""); + str += kv_string("sortas", ""); + + str += type_id::Start; + append(str, "tags"); + str += type_id::End; + + str += type_id::End; + return str; + } + + std::expected steam_shortcut::vdf_shortcut_entry::build_binary_blob(usz index) const + { + std::string str; + str.reserve(1024); + str += type_id::Start; + + append(str, std::to_string(index)); + + std::optional error = std::nullopt; + for (const auto& [key, value] : values) + { + std::visit([&key, &str, &error](const auto& value) + { + using T = std::decay_t; + + if constexpr (std::is_same_v) + { + str += kv_string(key, value); + } + else if constexpr (std::is_same_v) + { + str += kv_int(key, value); + } + else + { + error = fmt::format("vdf entry for key '%s' has unexpected type '%s'", key, typeid(value).name()); + } + }, + value); + + if (error.has_value()) + { + return std::unexpected(error.value()); + } + } + + str += type_id::Start; + append(str, "tags"); + for (const auto& [key, value] : tags) + { + str += kv_string(key, value); + } + str += type_id::End; + + str += type_id::End; + return str; + } + + void steam_shortcut::update_steam_input_config(const std::string& user_dir) + { + if (m_entries_to_add.empty() && m_entries_to_remove.empty()) + { + return; + } + + const std::string vdf_path = user_dir + "localconfig.vdf"; + const std::string backup_path = fs::get_config_dir() + "/localconfig.vdf.backup"; + + if (fs::is_file(vdf_path) && !fs::copy_file(vdf_path, backup_path, true)) + { + sys_log.error("Failed to backup steam localconfig file '%s'", vdf_path); + return; + } + + fs::file vdf(vdf_path); + if (!vdf) + { + sys_log.error("update_steam_input_config: Failed to open steam localconfig file '%s': %s", vdf_path, fs::g_tls_error); + return; + } + + std::string content = vdf.to_string(); + vdf.close(); + + static const std::string app_section_start = "\n\t\"apps\"\n\t{"; + static const std::string app_section_end = "\n\t}\n"; + static const std::string entry_section_end = "\n\t\t}"; + + bool nothing_to_remove = m_entries_to_remove.empty(); + + usz app_pos = content.rfind(app_section_start); + if (app_pos == umax) + { + if (!nothing_to_remove) + { + // We don't have to remove anything because this section did not exist + sys_log.notice("update_steam_input_config: Could not find \"apps\" section. No need to remove anything."); + nothing_to_remove = true; + } + + if (m_entries_to_add.empty()) + { + return; // Nothing to do anyway + } + + const usz insert_pos = content.rfind("\n}"); + if (insert_pos == umax) + { + sys_log.error("update_steam_input_config: Could not find main section end"); + return; + } + + sys_log.notice("update_steam_input_config: Inserting missing \"apps\" section"); + content.insert(insert_pos, fmt::format("%s\n\t}", app_section_start)); + + app_pos = content.rfind(app_section_start); + ensure(app_pos != umax); + } + + const usz search_start = app_pos + app_section_start.size(); + + const usz insert_pos = content.find(app_section_end, search_start); + if (insert_pos == umax) + { + sys_log.error("update_steam_input_config: Could not find apps section end"); + return; + } + + const auto appid_string = [](s32 signed_appid) + { + return fmt::format("\n\t\t\"%d\"\n", signed_appid); + }; + + const auto find_entry = [&content, &appid_string, search_start, insert_pos](s32 signed_appid) -> usz + { + const usz pos = content.find(appid_string(signed_appid), search_start); + if (pos >= insert_pos) return umax; + return pos; + }; + + bool dirty = false; + + for (const shortcut_entry& entry : m_entries_to_remove) + { + constexpr bool removal_diabled = true; // Disabled for now. Steam doesn't seem to remove the entries either + if constexpr (removal_diabled) + { + break; + } + + if (nothing_to_remove) + { + break; + } + + const s32 signed_appid = static_cast(entry.appid); + const usz pos = find_entry(signed_appid); + + if (pos == umax) + { + // does not exist, do nothing + sys_log.notice("update_steam_input_config: Entry for '%s' with appid '%d' does not exist. Skipping removal", entry.app_name, signed_appid); + continue; + } + + // Find the opening brace of this entry + const usz pos_brace_open = content.find('{', pos); + if (pos_brace_open == umax || pos_brace_open >= insert_pos) + { + sys_log.error("update_steam_input_config: Can't find opening brace for entry for '%s' with appid '%d'.", entry.app_name, signed_appid); + continue; + } + + // Find the closing brace + const usz pos_brace_close = content.find(entry_section_end, pos_brace_open); + if (pos_brace_close == umax || pos_brace_close >= insert_pos) + { + sys_log.error("update_steam_input_config: Can't find closing brace for entry for '%s' with appid '%d'.", entry.app_name, signed_appid); + continue; + } + + // Include the closing brace line + const usz erase_end = pos_brace_close + entry_section_end.size(); + + // Erase the whole block + content.erase(pos, erase_end - pos); + + sys_log.notice("update_steam_input_config: Removed '%s' with appid '%d'", entry.app_name, signed_appid); + + dirty = true; + } + + for (const shortcut_entry& entry : m_entries_to_add) + { + const s32 signed_appid = static_cast(entry.appid); + const usz pos = find_entry(signed_appid); + + if (pos != umax) + { + // already exists, do nothing + sys_log.notice("update_steam_input_config: Entry for '%s' with appid '%d' already exists", entry.app_name, signed_appid); + continue; + } + + sys_log.notice("update_steam_input_config: Inserting '%s' with appid '%d'", entry.app_name, signed_appid); + content.insert(insert_pos, fmt::format( + "%s" + "\t\t{\n" + "\t\t\t\"UseSteamControllerConfig\"\t\t\"0\"\n" + //"\t\t\t\"SteamControllerRumble\"\t\t\"-1\"\n" + //"\t\t\t\"SteamControllerRumbleIntensity\"\t\t\"320\"\n" + "\t\t}", appid_string(signed_appid))); + + dirty = true; + } + + if (dirty && !fs::write_file(vdf_path, fs::rewrite, content)) + { + sys_log.error("Failed to update steam localconfig '%s': '%s'", vdf_path, fs::g_tls_error); + + if (!fs::copy_file(backup_path, vdf_path, true)) + { + sys_log.error("Failed to restore steam localconfig backup: '%s'", fs::g_tls_error); + } + } + } + +#ifdef _WIN32 + static std::string get_registry_string(const wchar_t* key, const wchar_t* name) + { + HKEY hkey = NULL; + LSTATUS status = RegOpenKeyW(HKEY_CURRENT_USER, key, &hkey); + if (status != ERROR_SUCCESS) + { + sys_log.trace("get_registry_string: RegOpenKeyW failed: %s (key='%s', name='%s')", fmt::win_error{static_cast(status), nullptr}, wchar_to_utf8(key), wchar_to_utf8(name)); + return ""; + } + + std::wstring path_buffer; + DWORD type = 0U; + do + { + path_buffer.resize(path_buffer.size() + MAX_PATH); + DWORD buffer_size = static_cast((path_buffer.size() - 1) * sizeof(wchar_t)); + status = RegQueryValueExW(hkey, name, NULL, &type, reinterpret_cast(path_buffer.data()), &buffer_size); + } + while (status == ERROR_MORE_DATA); + + const LSTATUS close_status = RegCloseKey(hkey); + if (close_status != ERROR_SUCCESS) + { + sys_log.error("get_registry_string: RegCloseKey failed: %s (key='%s', name='%s')", fmt::win_error{static_cast(close_status), nullptr}, wchar_to_utf8(key), wchar_to_utf8(name)); + } + + if (status != ERROR_SUCCESS) + { + sys_log.trace("get_registry_string: RegQueryValueExW failed: %s (key='%s', name='%s')", fmt::win_error{static_cast(status), nullptr}, wchar_to_utf8(key), wchar_to_utf8(name)); + return ""; + } + + if ((type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ)) + { + return wchar_to_utf8(path_buffer.data()); + } + + return ""; + } +#endif + + std::string steam_shortcut::steamid64_to_32(const std::string& steam_id) + { + u64 id = 0; + if (!try_to_uint64(&id, steam_id, 0, umax)) + { + sys_log.error("Failed to convert steam id '%s' to u64", steam_id); + return ""; + } + constexpr u64 base = 76561197960265728ULL; + const u32 id32 = static_cast(id - base); + return std::to_string(id32); + } + + std::string steam_shortcut::get_steam_path() + { +#ifdef _WIN32 + const std::string path = get_registry_string(L"Software\\Valve\\Steam", L"SteamPath"); + if (path.empty()) + { + sys_log.notice("get_steam_path: SteamPath not found in registry"); + return ""; + } + + // The path might be lowercase... sigh + std::error_code ec; + const std::filesystem::path canonical_path = std::filesystem::canonical(std::filesystem::path(path), ec); + if (ec) + { + sys_log.error("get_steam_path: Failed to canonicalize path '%s': %s", path, ec.message()); + return ""; + } + + const std::string path_fixed = canonical_path.string(); + sys_log.notice("get_steam_path: Found steam registry path: '%s'", path_fixed); + return path_fixed; +#else + if (const char* home = ::getenv("HOME")) + { +#if __APPLE__ + const std::string path = std::string(home) + "/Library/Application Support/Steam/"; +#else + const std::string path = std::string(home) + "/.local/share/Steam/"; +#endif + return path; + } + + return ""; +#endif + } + + std::string steam_shortcut::get_steam_banner_path(steam_banner banner, const std::string& grid_dir, u32 appid) + { + switch (banner) + { + case steam_banner::cover: return fmt::format("%s%dp.png", grid_dir, appid); + case steam_banner::wide_cover: return fmt::format("%s%d.png", grid_dir, appid); + case steam_banner::background: return fmt::format("%s%d_hero.png", grid_dir, appid); + case steam_banner::logo: return fmt::format("%s%d_logo.png", grid_dir, appid); + case steam_banner::icon: return fmt::format("%s%d_icon.png", grid_dir, appid); + } + + return {}; + } + + void steam_shortcut::create_steam_banner(steam_banner banner, const std::string& src_path, const QPixmap& src_icon, const std::string& grid_dir, const shortcut_entry& entry) + { + const std::string dst_path = get_steam_banner_path(banner, grid_dir, entry.appid); + QSize size = src_icon.size(); + + if (banner == steam_banner::cover) + { + size = QSize(600, 900); // We want to center the icon vertically in the portrait + } + + if (size == src_icon.size()) + { + if (entry.iso) + { + // TODO: fs::copy_file does not support copy from an unmounted iso archive + if (!src_icon.save(QString::fromStdString(dst_path))) + { + sys_log.error("Failed to save steam shortcut banner to '%s'", dst_path); + } + return; + } + else if (!fs::copy_file(src_path, dst_path, true)) + { + sys_log.error("Failed to copy steam shortcut banner from '%s' to '%s': '%s'", src_path, dst_path, fs::g_tls_error); + } + return; + } + + const QPixmap scaled = gui::utils::get_aligned_pixmap(src_icon, size, 1.0, Qt::SmoothTransformation, gui::utils::align_h::center, gui::utils::align_v::center); + + if (!scaled.save(QString::fromStdString(dst_path))) + { + sys_log.error("Failed to save steam shortcut banner to '%s'", dst_path); + } + } + + std::string steam_shortcut::get_last_active_steam_user(const std::string& steam_path) + { + const std::string vdf_path = steam_path + "/config/loginusers.vdf"; + fs::file vdf(vdf_path); + if (!vdf) + { + sys_log.error("get_last_active_steam_user: Failed to parse steam loginusers file '%s': %s", vdf_path, fs::g_tls_error); + return ""; + } + + // The file looks roughly like this. We need the numerical ID. + // "users" + // { + // "12345678901234567" + // { + // "AccountName" "myusername" + // "MostRecent" "1" + // ... + // } + // ... + // } + + const std::string content = vdf.to_string(); + vdf.close(); + + usz user_count = 0; + + const auto find_user_id = [&content, &user_count](const std::string& key, const std::string& comp) -> std::string + { + user_count = 0; + usz pos = 0; + while (true) + { + pos = content.find(key, pos); + if (pos == umax) break; + + user_count++; + + const usz val_start = content.find('"', pos + key.size()); + if (val_start == umax) break; + + const usz val_end = content.find('"', val_start + 1); + if (val_end == umax) break; + + const std::string value = content.substr(val_start + 1, val_end - val_start - 1); + + if (value != comp) + { + pos = val_end + 1; + continue; + } + + const usz pos_brace = content.rfind('{', pos - 2); + if (pos_brace == umax) return ""; + + const usz pos_end = content.rfind('"', pos_brace - 2); + if (pos_end == umax) return ""; + + const usz pos_start = content.rfind('"', pos_end - 1); + if (pos_start == umax) return ""; + + const std::string user_id_64 = content.substr(pos_start + 1, pos_end - pos_start - 1); + return steamid64_to_32(user_id_64); + } + + return ""; + }; + + if (const std::string id = find_user_id("\"MostRecent\"", "1"); !id.empty()) + { + return id; + } + +#ifdef _WIN32 + // Fallback to AutoLoginUser + const std::string username = get_registry_string(L"Software\\Valve\\Steam", L"AutoLoginUser"); + if (username.empty()) + { + sys_log.notice("get_last_active_steam_user: AutoLoginUser not found in registry"); + return ""; + } + + sys_log.notice("get_last_active_steam_user: Found steam user: '%s'", username); + + if (const std::string id = find_user_id("\"AccountName\"", username); !id.empty()) + { + return id; + } +#endif + + sys_log.error("get_last_active_steam_user: Failed to parse steam loginusers file '%s' (user_count=%d)", vdf_path, user_count); + return ""; + } + + bool steam_shortcut::is_steam_running() + { +#ifdef _WIN32 + if (HANDLE mutex = OpenMutexA(SYNCHRONIZE, FALSE, "Global\\SteamClientRunning")) + { + CloseHandle(mutex); + return true; + } + + // Fallback to check process + PROCESSENTRY32 entry{}; + entry.dwSize = sizeof(entry); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (Process32First(snapshot, &entry)) + { + do + { + if (lstrcmpiW(entry.szExeFile, L"steam.exe") == 0) + { + CloseHandle(snapshot); + return true; + } + } while (Process32Next(snapshot, &entry)); + } + + CloseHandle(snapshot); +#else + std::vector pid_paths = { get_steam_path() }; +#ifdef __linux__ + if (const char* home = ::getenv("HOME")) + { + pid_paths.push_back(std::string(home) + "/.steam"); + pid_paths.push_back(std::string(home) + "/.steam/steam"); + } +#endif + for (const std::string& pid_path : pid_paths) + { + if (fs::file pid_file(pid_path + "/steam.pid"); pid_file) + { + const std::string pid = pid_file.to_string(); + pid_file.close(); + + if (pid.empty()) + { + continue; + } + + const pid_t pid_val = std::stoi(pid); + return kill(pid_val, 0) == 0 || errno != ESRCH; + } + } +#endif + return false; + } +} diff --git a/rpcs3/rpcs3qt/steam_utils.h b/rpcs3/rpcs3qt/steam_utils.h new file mode 100644 index 0000000000..deb7c0dad7 --- /dev/null +++ b/rpcs3/rpcs3qt/steam_utils.h @@ -0,0 +1,128 @@ +#pragma once + +#include "util/types.hpp" +#include "Utilities/StrFmt.h" +#include +#include +#include + +class iso_archive; + +namespace gui::utils +{ + class steam_shortcut + { + public: + steam_shortcut() {} + ~steam_shortcut() {} + + void add_shortcut( + const std::string& app_name, + const std::string& exe, + const std::string& start_dir, + const std::string& launch_options, + const std::string& icon_path, + const std::string& banner_small_path, + const std::string& banner_large_path, + std::shared_ptr archive); + + void remove_shortcut( + const std::string& app_name, + const std::string& exe, + const std::string& start_dir); + + bool write_file(); + + static bool steam_installed(); + static bool is_steam_running(); + + private: + enum type_id + { + Null = 0x00, + Start = Null, + String = 0x01, + Integer = 0x02, + Float = 0x03, + Pointer = 0x04, + Nested = 0x05, + Array = 0x06, + Bool = 0x07, + End = 0x08, + }; + + enum class steam_banner + { + cover, + wide_cover, + background, + logo, + icon + }; + + struct shortcut_entry + { + std::string app_name; + std::string exe; + std::string start_dir; + std::string launch_options; + std::string icon; + std::string banner_small; + std::string banner_large; + u32 appid = 0; + + std::shared_ptr iso; + + std::string build_binary_blob(usz index) const; + }; + + struct vdf_shortcut_entry + { + std::vector>> values; + std::vector> tags; + + template + std::expected value(const std::string& key) const + { + const auto it = std::find_if(values.cbegin(), values.cend(), [&key](const auto& v){ return v.first == key; }); + if (it == values.cend()) + { + return std::unexpected(fmt::format("key '%s' not found", key)); + } + + if (const auto* p = std::get_if(&it->second)) + { + return *p; + } + + return std::unexpected(fmt::format("value for key '%s' has wrong type", key)); + } + + std::expected build_binary_blob(usz index) const; + }; + + bool parse_file(const std::string& path); + + void update_steam_input_config(const std::string& user_dir); + + static u32 crc32(const std::string& data); + static u32 steam_appid(const std::string& exe, const std::string& name); + + static void append(std::string& s, const std::string& val); + + static std::string quote(const std::string& s, bool force); + static std::string fix_slashes(const std::string& s); + static std::string kv_string(const std::string& key, const std::string& value); + static std::string kv_int(const std::string& key, u32 value); + static std::string steamid64_to_32(const std::string& steam_id); + static std::string get_steam_path(); + static std::string get_last_active_steam_user(const std::string& steam_path); + + static std::string get_steam_banner_path(steam_banner banner, const std::string& grid_dir, u32 appid); + static void create_steam_banner(steam_banner banner, const std::string& src_path, const QPixmap& src_icon, const std::string& grid_dir, const shortcut_entry& entry); + + std::vector m_entries_to_add; + std::vector m_entries_to_remove; + std::vector m_vdf_entries; + }; +} diff --git a/rpcs3/rpcs3qt/stylesheets.h b/rpcs3/rpcs3qt/stylesheets.h index 52367de7db..7396cec605 100644 --- a/rpcs3/rpcs3qt/stylesheets.h +++ b/rpcs3/rpcs3qt/stylesheets.h @@ -24,9 +24,6 @@ namespace gui // main window toolbar icon color "QLabel#toolbar_icon_color { color: #5b5b5b; }" - // thumbnail icon color - "QLabel#thumbnail_icon_color { color: rgba(0, 100, 231, 255); }" - // game list icon color "QLabel#gamelist_icon_background_color { color: rgba(240, 240, 240, 255); }" diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index e9c8ad436b..caccc1c47b 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -59,6 +59,7 @@ public: const QString paused_savestates = tr("When this mode is on, savestates are loaded and paused on the first frame.\nThis allows players to prepare for gameplay without being thrown into the action immediately."); const QString spu_profiler = tr("When enabled, SPU performance is measured at runtime.\nEnable only at a developer's request because when enabled it reduces performance a bit by itself."); const QString use_ReBAR = tr("When enabled, Vulkan will try to use PCI-e resizable bar address space for GPU uploads of timing-sensitive data.\nThis yields a massive performance win on NVIDIA cards when the base framerate is low.\nFor games with very high framerates, this option can result in worse performance for all GPU vendors.\n"); + const QString log_levels = tr("Set the minimum log levels for any log channels."); // audio @@ -153,10 +154,12 @@ public: const QString show_pressure_intensity_toggle_hint = tr("Shows pressure intensity toggle hint using the native overlay."); const QString show_analog_limiter_toggle_hint = tr("Shows analog limiter toggle hint using the native overlay."); const QString show_mouse_and_keyboard_toggle_hint = tr("Shows mouse and keyboard toggle hint using the native overlay."); + const QString show_fatal_error_hints = tr("Shows fatal error hints using the native overlay."); const QString show_capture_hints = tr("Shows screenshot and recording hints using the native overlay."); const QString use_native_interface = tr("Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, the on-screen keyboard only supports the English key layout."); const QString record_with_overlays = tr("Enables recording with overlays.\nThis also affects screenshots."); const QString pause_during_home_menu = tr("When enabled, opening the home menu will also pause emulation.\nWhile most games pause themselves while the home menu is shown, some do not.\nIn that case it can be helpful to pause the emulation whenever the home menu is open."); + const QString play_music_during_boot = tr("Play music during boot sequence if available."); const QString perf_overlay_enabled = tr("Enables or disables the performance overlay."); const QString perf_overlay_framerate_graph_enabled = tr("Enables or disables the framerate graph."); @@ -168,10 +171,11 @@ public: const QString perf_overlay_update_interval = tr("Sets the time interval in which the performance overlay is being updated (measured in milliseconds).\nSetting this to 16 milliseconds will refresh the performance overlay at roughly 60Hz.\nThe performance overlay refresh rate does not affect the frame graph statistics and can only be as fast as the current game allows."); const QString perf_overlay_font_size = tr("Sets the font size of the performance overlay (measured in pixels)."); const QString perf_overlay_opacity = tr("Sets the opacity of the performance overlay (measured in %)."); - const QString perf_overlay_margin_x = tr("Sets the horizontal distance to the screen border relative to the screen quadrant (measured in pixels)."); - const QString perf_overlay_margin_y = tr("Sets the vertical distance to the screen border relative to the screen quadrant (measured in pixels)."); + const QString perf_overlay_margin_x = tr("Sets the horizontal distance to the screen border relative to the screen quadrant (measured in %)."); + const QString perf_overlay_margin_y = tr("Sets the vertical distance to the screen border relative to the screen quadrant (measured in %)."); const QString perf_overlay_center_x = tr("Centers the performance overlay horizontally and overrides the horizontal margin."); const QString perf_overlay_center_y = tr("Centers the performance overlay vertically and overrides the vertical margin."); + const QString perf_overlay_use_window_space = tr("Position overlay relative to the full window surface, enabling placement outside game's render area."); const QString shader_load_bg_enabled = tr("Shows a background image during the native shader loading dialog/loading screen.\nBy default the used image will be /PS3_GAME/PIC1.PNG."); const QString shader_load_bg_darkening = tr("Changes the background image darkening effect strength of the native shader loading dialog.\nThis may be used to improve readability and/or aesthetics."); @@ -189,7 +193,7 @@ public: const QString resolution_scale = tr("Scales the game's resolution by the given percentage.\nThe base resolution is always 1280x720.\nSet this value to 100% if you want to use the normal Resolution options.\nValues below 100% will usually not improve performance."); const QString minimum_scalable_dimension = tr("Only framebuffers greater than this size will be upscaled.\nIncreasing this value might fix problems with missing graphics when upscaling, especially when Write Color Buffers is enabled.\nIf unsure, don't change this option."); const QString dump_color = tr("Enable this option if you get missing graphics or broken lighting ingame.\nMight degrade performance and introduce stuttering in some cases.\nRequired for Demon's Souls."); - const QString vsync = tr("By having this off you might obtain a higher framerate at the cost of tearing artifacts in the game."); + const QString vsync = tr("Enables vertical synchronization to eliminate tearing.\nAdaptive Mode - Prefers keeping up performance. It may skip frames or even tear to avoid reducing the game's framerate.\nFull Mode - No tearing allowed even if performance is reduced. This mode will by default limit your framerate to the display's refresh rate unless overriden in the driver control panel."); const QString strict_rendering_mode = tr("Enforces strict compliance to the API specification.\nMight result in degraded performance in some games.\nCan resolve rare cases of missing graphics and flickering.\nIf unsure, don't use this option."); const QString stretch_to_display_area = tr("Overrides the aspect ratio and stretches the image to the full display area."); const QString multithreaded_rsx = tr("Offloads some RSX operations to a secondary thread.\nImproves performance for high-core processors.\nMay cause slowdown in weaker CPUs due to the extra worker thread load."); @@ -215,10 +219,8 @@ public: const QString tty_limit = tr("Sets the maximum amount of blocks that the TTY can display.\nThis usually equals the number of lines.\nSet 0 in order to remove the limit."); const QString stylesheets = tr("Changes the overall look of RPCS3.\nChoose a stylesheet and click Apply to change between styles."); const QString show_welcome = tr("Shows the initial welcome screen upon starting RPCS3."); - const QString show_exit_game = tr("Shows a confirmation dialog when the game window is being closed."); - const QString show_boot_game = tr("Shows a confirmation dialog when a game was booted while another game is running."); - const QString show_pkg_install = tr("Shows a dialog when packages were installed successfully."); - const QString show_pup_install = tr("Shows a dialog when firmware was installed successfully."); + const QString show_exit_game = tr("Shows a confirmation dialog when the game window is being closed and when a game was booted while another game is running."); + const QString show_pkg_install = tr("Shows a dialog when packages and firmware were installed successfully."); const QString show_obsolete_cfg = tr("Shows a dialog when obsolete settings were found."); const QString show_same_buttons = tr("Shows a dialog in the game pad configuration when the same button was assigned twice."); const QString show_restart_hint = tr("Shows a dialog when RPCS3 is ready to restart after an update."); @@ -229,6 +231,7 @@ public: const QString uuid = tr("This is the ID used for hardware statistics.\nIt should only be reset if you change your hardware configuration or if you copied RPCS3 to another PC."); const QString pad_navigation = tr("Use the game pad that is configured for player 1 to navigate in the GUI."); const QString global_navigation = tr("Keep control over pad navigation if RPCS3 is not the active window."); + const QString gui_volume = tr("Set the audio volume of the user interface. This does not affect the ingame audio."); // input @@ -302,7 +305,7 @@ public: const QString analog_limiter = tr("Applies the stick multipliers while this special button is pressed.
Enable \"Toggle\" if you want to toggle the analog limiter on button press instead.
If no button has been assigned, the stick multipliers are always applied."); const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.
Enable \"Toggle\" if you want to toggle the intensity on button press instead.
Use the percentage to change how hard you want to press a button."); const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range."); - const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 8000 is usually recommended."); + const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 4000 is usually recommended."); const QString stick_multiplier = tr("The stick multipliers can be used to change the sensitivity of your stick movements.
The default setting is 1 and represents normal input."); const QString stick_deadzones = tr("A stick's deadzone determines how far the stick has to be moved until it is fully recognized by the game. The resulting range will be projected onto the full input range in order to give you a smooth experience. Movement inside the deadzone is simulated using the anti-deadzone slider (default is 13%), so don't worry if there is still movement shown in the emulated stick preview."); const QString vibration = tr("The PS3 activates two motors (large and small) to handle controller vibrations.
You can enable, disable or even switch these signals for the currently selected pad here.
The game sends values from 0-255 to activate the motors.
Any value smaller or equal the threshold will be set to 0. This is 63 by default for pad handlers other than DualShock3 in order to emulate the DualShock3's behavior."); @@ -313,7 +316,7 @@ public: const QString mouse_deadzones = tr("The mouse deadzones represent the games' own deadzones on the x and y axes. Games usually enforce their own deadzones to filter out small unwanted stick movements. In consequence, mouse input feels unintuitive since it relies on immediate responsiveness. You can change these values temporarily during gameplay in order to find out the optimal values for your game (Alt+T and Alt+Y for x, Alt+U and Alt+I for y)."); const QString mouse_acceleration = tr("The mouse acceleration can be used to amplify your mouse movements on the x and y axes. Increase these values if your mouse movements feel too slow while playing a game. You can change these values temporarily during gameplay in order to find out the optimal values (Alt+G and Alt+H for x, Alt+J and Alt+K for y). Keep in mind that modern mice usually provide different modes and settings that can be used to change mouse movement speeds as well."); const QString mouse_movement = tr("The mouse movement mode determines how the mouse movement is translated to pad input.
Use the relative mode for traditional mouse movement.
Use the absolute mode to use the mouse's distance to the center of the screen as input value."); - const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an additional button mapping.
Alt + Left-click: differentiate between trigger press and release (only XInput for now).
Right-click: clear this button mapping."); + const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an additional button mapping.
Ctrl + Left-click: Create a combo by adding a button to the last mapping.
Alt + Left-click: differentiate between trigger press and release (only XInput for now).
Right-click: clear this button mapping."); } gamepad_settings; }; diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index 51ef2d2e9f..11046f2a70 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -392,7 +392,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr gui_s m_trophy_table->create_header_actions(m_trophy_column_acts, [this](int col) { return m_gui_settings->GetTrophylistColVisibility(static_cast(col)); }, [this](int col, bool visible) { m_gui_settings->SetTrophylistColVisibility(static_cast(col), visible); }); - + m_game_table->create_header_actions(m_game_column_acts, [this](int col) { return m_gui_settings->GetTrophyGamelistColVisibility(static_cast(col)); }, [this](int col, bool visible) { m_gui_settings->SetTrophyGamelistColVisibility(static_cast(col), visible); }); @@ -651,7 +651,7 @@ void trophy_manager_dialog::ResizeGameIcons() const int trophy_index = item->data(GameUserRole::GameIndex).toInt(); QString trophy_icon_path = QString::fromStdString(m_trophies_db[trophy_index]->path); - item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, trophy_index, cancel = item->icon_loading_aborted(), dpr](int index) + item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, cancel = item->icon_loading_aborted(), dpr](int index) { if (cancel && cancel->load()) { @@ -897,7 +897,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos) if (!name.isEmpty() && !desc.isEmpty()) { QAction* copy_both = new QAction(tr("&Copy Name + Description"), copy_menu); - connect(copy_both, &QAction::triggered, this, [this, name, desc]() + connect(copy_both, &QAction::triggered, this, [name, desc]() { QApplication::clipboard()->setText(name % QStringLiteral("\n\n") % desc); }); @@ -907,7 +907,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos) if (!name.isEmpty()) { QAction* copy_name = new QAction(tr("&Copy Name"), copy_menu); - connect(copy_name, &QAction::triggered, this, [this, name]() + connect(copy_name, &QAction::triggered, this, [name]() { QApplication::clipboard()->setText(name); }); @@ -917,7 +917,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos) if (!desc.isEmpty()) { QAction* copy_desc = new QAction(tr("&Copy Description"), copy_menu); - connect(copy_desc, &QAction::triggered, this, [this, desc]() + connect(copy_desc, &QAction::triggered, this, [desc]() { QApplication::clipboard()->setText(desc); }); @@ -1038,7 +1038,7 @@ void trophy_manager_dialog::ShowGameTableContextMenu(const QPoint& pos) if (!name.isEmpty()) { QAction* copy_name = new QAction(tr("&Copy Name"), menu); - connect(copy_name, &QAction::triggered, this, [this, name]() + connect(copy_name, &QAction::triggered, this, [name]() { QApplication::clipboard()->setText(name); }); diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index ec4d7ce44f..a34292ce0e 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #if defined(_WIN32) || defined(__APPLE__) #include <7z.h> @@ -110,7 +109,7 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut const std::string url = fmt::format("https://update.rpcs3.net/?api=v3&c=%s&os_type=%s&os_arch=%s&os_version=%i.%i.%i", rpcs3::get_commit_and_hash().second, os.type, os.arch, os.version_major, os.version_minor, os.version_patch); - m_downloader->start(url, true, !automatic, tr("Checking For Updates"), true); + m_downloader->start(url, true, !automatic, true, tr("Checking For Updates"), true); } bool update_manager::handle_json(bool automatic, bool check_only, bool auto_accept, const QByteArray& data) @@ -440,7 +439,7 @@ void update_manager::update(bool auto_accept) }); update_log.notice("Downloading update..."); - m_downloader->start(m_request_url, true, true, tr("Downloading Update"), true, m_expected_size); + m_downloader->start(m_request_url, true, true, false, tr("Downloading Update"), true, m_expected_size); } bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept) diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp index f5ceac8aee..44aceff70a 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp @@ -81,7 +81,7 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin item->setCheckState(Qt::CheckState::Checked); }); - connect(m_dir_list, &QListWidget::currentRowChanged, this, [this, button_remove_dir](int row) + connect(m_dir_list, &QListWidget::currentRowChanged, this, [button_remove_dir](int row) { button_remove_dir->setEnabled(row > 0); }); diff --git a/rpcs3/rpcs3qt/welcome_dialog.cpp b/rpcs3/rpcs3qt/welcome_dialog.cpp index e20415594d..e8b3f39e1e 100644 --- a/rpcs3/rpcs3qt/welcome_dialog.cpp +++ b/rpcs3/rpcs3qt/welcome_dialog.cpp @@ -83,12 +83,12 @@ welcome_dialog::welcome_dialog(std::shared_ptr gui_settings, bool { if (ui->create_desktop_shortcut->isChecked()) { - gui::utils::create_shortcut("RPCS3", "", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), gui::utils::shortcut_location::desktop); + gui::utils::create_shortcut("RPCS3", "", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), "", gui::utils::shortcut_location::desktop); } if (ui->create_applications_menu_shortcut->isChecked()) { - gui::utils::create_shortcut("RPCS3", "", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), gui::utils::shortcut_location::applications); + gui::utils::create_shortcut("RPCS3", "", "", "", "RPCS3", ":/rpcs3.svg", fs::get_temp_dir(), "", gui::utils::shortcut_location::applications); } if (ui->use_dark_theme->isChecked() && ui->use_dark_theme->isEnabled()) // if checked and also on initial welcome dialog diff --git a/rpcs3/tests/rpcs3_test.vcxproj b/rpcs3/tests/rpcs3_test.vcxproj index 263334d19e..fb9d0d21d4 100644 --- a/rpcs3/tests/rpcs3_test.vcxproj +++ b/rpcs3/tests/rpcs3_test.vcxproj @@ -85,8 +85,16 @@ + + + true + + + + true + diff --git a/rpcs3/tests/test_dmux_pamf.cpp b/rpcs3/tests/test_dmux_pamf.cpp new file mode 100644 index 0000000000..b6fdbf73b4 --- /dev/null +++ b/rpcs3/tests/test_dmux_pamf.cpp @@ -0,0 +1,638 @@ +#include "stdafx.h" +#include +#include "Emu/Cell/Modules/cellDmuxPamf.h" +#include "Crypto/sha1.h" +#include "test_dmux_pamf.h" + +using namespace testing; + +struct DmuxPamfMock : dmux_pamf_base +{ + MOCK_METHOD(bool, on_au_found, (u8 stream_id, u8 private_stream_id, u32 user_data, std::span au, u64 pts, u64 dts, bool rap, u8 au_specific_info_size, (std::array) au_specific_info_buf), (override)); + MOCK_METHOD(bool, on_demux_done, (), (override)); + MOCK_METHOD(void, on_fatal_error, (), (override)); + MOCK_METHOD(bool, on_flush_done, (u8 stream_id, u8 private_stream_id, u32 user_data), (override)); + MOCK_METHOD(bool, on_prog_end, (), (override)); + MOCK_METHOD(void, on_au_queue_full, (), (override)); +}; + +struct DmuxPamfTest : Test +{ + StrictMock demuxer; + MockFunction check_point; + std::array au_queue_buffer{}; + InSequence seq; +}; + +MATCHER_P(Sha1Is, expected_sha1, "") { std::array sha1_buf{}; sha1(arg.data(), arg.size(), sha1_buf.data()); return sha1_buf == expected_sha1; } + +struct DmuxPamfSinglePack : DmuxPamfTest, WithParamInterface, AccessUnit, std::optional>> {}; + +TEST_P(DmuxPamfSinglePack, Test) +{ + const auto& [stream_id, private_stream_id, is_avc, au_max_size, stream, au_1, au_2] = GetParam(); + + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au_1.data_sha1), au_1.pts, au_1.dts, au_1.rap, au_1.specific_info_size, au_1.specific_info_buf)).WillOnce(Return(true)); + + if (au_2) + { + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au_2->data_sha1), au_2->pts, au_2->dts, au_2->rap, au_2->specific_info_size, au_2->specific_info_buf)); + } + + demuxer.enable_es(stream_id, private_stream_id, is_avc, au_queue_buffer, au_max_size, false, 0x12345678); + demuxer.set_stream(stream, false); + check_point.Call(0); + demuxer.process_next_pack(); +} + +// These streams are made of a single pack that contains two access units (one for the user data stream) +INSTANTIATE_TEST_SUITE_P(Basic, DmuxPamfSinglePack, Values +( + std::tuple{ 0xe0, 0x00, true, 0x800, AVC_SINGLE_PACK_STREAM, AVC_SINGLE_PACK_EXPECTED_AU_1, AVC_SINGLE_PACK_EXPECTED_AU_2 }, + std::tuple{ 0xe0, 0x00, false, 0x800, M2V_SINGLE_PACK_STREAM, M2V_SINGLE_PACK_EXPECTED_AU_1, M2V_SINGLE_PACK_EXPECTED_AU_2 }, + std::tuple{ 0xbd, 0x00, false, 0x800, ATRACX_SINGLE_PACK_STREAM, ATRACX_SINGLE_PACK_EXPECTED_AU_1, ATRACX_SINGLE_PACK_EXPECTED_AU_2 }, + std::tuple{ 0xbd, 0x30, false, 0x800, AC3_SINGLE_PACK_STREAM, AC3_SINGLE_PACK_EXPECTED_AU_1, AC3_SINGLE_PACK_EXPECTED_AU_2 }, + std::tuple{ 0xbd, 0x40, false, 0x369, LPCM_SINGLE_PACK_STREAM, LPCM_SINGLE_PACK_EXPECTED_AU_1, LPCM_SINGLE_PACK_EXPECTED_AU_2 }, + std::tuple{ 0xbd, 0x20, false, 0x800, USER_DATA_SINGLE_PACK_STREAM, USER_DATA_SINGLE_PACK_EXPECTED_AU, std::nullopt } // There can be at most one user data access unit in a single pack +)); + +// Tests buggy behavior: timestamps should be unsigned, but get sign-extended from s32 to u64 on LLE. They probably forgot about integer promotion +INSTANTIATE_TEST_SUITE_P(TimeStampSignExtended, DmuxPamfSinglePack, Values +( + std::tuple{ 0xbd, 0x20, false, 0x800, TIME_STAMP_SIGN_EXTENDED_STREAM, TIME_STAMP_SIGN_EXTENDED_EXPECTED_AU, std::nullopt } +)); + +// Tests buggy behavior: if there is no Pack Start Code at the beginning of the current stream section, LLE will search for the code. +// However, if it finds one, it will not set the reading position to where the code was found. It continues reading from the beginning of the stream section instead. +// Test disabled since this is not currently implemented +INSTANTIATE_TEST_SUITE_P(DISABLED_PackStartCodeNotAtBeginning, DmuxPamfSinglePack, Values +( + std::tuple{ 0xe0, 0x00, true, 0x800, []() { auto pack = AVC_SINGLE_PACK_STREAM; std::copy_n(pack.begin(), 4, pack.begin() + 0x93); std::fill_n(pack.begin(), 4, 0); return pack; }(), AVC_SINGLE_PACK_EXPECTED_AU_1, AVC_SINGLE_PACK_EXPECTED_AU_2 } +)); + +struct DmuxPamfMultiplePackSingleAu : DmuxPamfTest, WithParamInterface, AccessUnit>> {}; + +TEST_P(DmuxPamfMultiplePackSingleAu, Test) +{ + const auto& [stream_id, private_stream_id, is_avc, au_max_size, stream, au] = GetParam(); + + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au.data_sha1), au.pts, au.dts, au.rap, au.specific_info_size, au.specific_info_buf)).WillOnce(Return(true)); + + demuxer.enable_es(stream_id, private_stream_id, is_avc, au_queue_buffer, au_max_size, false, 0x12345678); + demuxer.set_stream(stream, false); + + // Demultiplex all packs until the last one + for (u32 i = 0; i < stream.size() - 0x800; i += 0x800) + { + demuxer.process_next_pack(); + } + + // The next call to process_next_pack() should produce an access unit + check_point.Call(0); + demuxer.process_next_pack(); +} + +// These streams contain a single access unit that is split across four packs +INSTANTIATE_TEST_SUITE_P(Basic, DmuxPamfMultiplePackSingleAu, Values +( + std::tuple{ 0xe0, 0x00, true, 0x1800, AVC_LARGE_AU_STREAM, AVC_LARGE_AU_EXPECTED_AU }, + std::tuple{ 0xe0, 0x00, false, 0x1800, M2V_LARGE_AU_STREAM, M2V_LARGE_AU_EXPECTED_AU }, + std::tuple{ 0xbd, 0x00, false, 0x1800, ATRACX_LARGE_AU_STREAM, ATRACX_LARGE_AU_EXPECTED_AU }, + std::tuple{ 0xbd, 0x30, false, 0x1800, AC3_LARGE_AU_STREAM, AC3_LARGE_AU_EXPECTED_AU }, + std::tuple{ 0xbd, 0x40, false, 0x1400, LPCM_LARGE_AU_STREAM, LPCM_LARGE_AU_EXPECTED_AU }, + std::tuple{ 0xbd, 0x20, false, 0x1800, USER_DATA_LARGE_AU_STREAM, USER_DATA_LARGE_AU_EXPECTED_AU } +)); + +// PES packets with audio streams contain a proprietary header before the actual stream. +// The last two bytes of the header indicate how much of the stream should be skipped +// (note that I have not found a single PAMF stream in any game where this is not 0). +// These streams test if this is implemented properly, the first access unit should not be output +INSTANTIATE_TEST_SUITE_P(SkipByes, DmuxPamfMultiplePackSingleAu, Values +( + std::tuple{ 0xbd, 0x00, false, 0x1800, ATRACX_SKIP_BYTES_STREAM, ATRACX_SKIP_BYTES_EXPECTED_AU }, + std::tuple{ 0xbd, 0x30, false, 0x1800, AC3_SKIP_BYTES_STREAM, AC3_SKIP_BYTES_EXPECTED_AU }, + std::tuple{ 0xbd, 0x40, false, 0xb40, LPCM_SKIP_BYTES_STREAM, LPCM_SKIP_BYTES_EXPECTED_AU } +)); + +// Tests buggy behavior: if the start of an access unit in the last three bytes of the previous PES packet and the demuxer was not cutting out an access unit before that, +// LLE will always output all three bytes, even if the access unit starts at the second last or last byte +INSTANTIATE_TEST_SUITE_P(FirstDilimiterSplit, DmuxPamfMultiplePackSingleAu, Values +( + std::tuple{ 0xe0, 0x00, true, 0x1800, AVC_BEGIN_OF_AU_SPLIT_STREAM, AVC_BEGIN_OF_AU_SPLIT_EXPECTED_AU }, + std::tuple{ 0xe0, 0x00, false, 0x1800, M2V_BEGIN_OF_AU_SPLIT_STREAM, M2V_BEGIN_OF_AU_SPLIT_EXPECTED_AU }, + std::tuple{ 0xbd, 0x00, false, 0x1800, ATRACX_BEGIN_OF_AU_SPLIT_STREAM, ATRACX_BEGIN_OF_AU_SPLIT_EXPECTED_AU }, + std::tuple{ 0xbd, 0x30, false, 0x1800, AC3_BEGIN_OF_AU_SPLIT_STREAM, AC3_BEGIN_OF_AU_SPLIT_EXPECTED_AU } +)); + +// The distance of the access units in these streams do not match the size indicated in the ATRAC3plus ATS header/AC3 frmsizecod + fscod fields +// LLE repeatedly cuts off one byte at the beginning of the PES packet until the sizes match and clears the current access unit +INSTANTIATE_TEST_SUITE_P(SizeMismatch, DmuxPamfMultiplePackSingleAu, Values +( + std::tuple{ 0xbd, 0x00, false, 0x608, ATRACX_SIZE_MISMATCH_STREAM, ATRACX_SIZE_MISMATCH_EXPECTED_AU }, + std::tuple{ 0xbd, 0x30, false, 0x600, AC3_SIZE_MISMATCH_STREAM, AC3_SIZE_MISMATCH_EXPECTED_AU } +)); + +// Tests buggy behavior: if an M2V sequence end code is found, it should be included in the current access unit, unlike all other delimiters. +// If the sequence end code is split across two packs, LLE isn't properly including it in the access unit. +// Disabled because this behavior isn't properly implemented currently +INSTANTIATE_TEST_SUITE_P(DISABLED_M2vSequenceEndSplit, DmuxPamfMultiplePackSingleAu, Values +( + std::tuple{ 0xe0, 0x00, false, 0x1000, M2V_SEQUENCE_END_SPLIT_STREAM, M2V_SEQUENCE_END_SPLIT_EXPECTED_AU } +)); + +struct DmuxPamfSplitDelimiter : DmuxPamfTest, WithParamInterface, AccessUnit, AccessUnit, AccessUnit, AccessUnit>> {}; + +TEST_P(DmuxPamfSplitDelimiter, Test) +{ + const auto& [stream_id, private_stream_id, is_avc, au_max_size, stream, au_1, au_2, au_3, au_4] = GetParam(); + + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au_1.data_sha1), au_1.pts, au_1.dts, au_1.rap, au_1.specific_info_size, au_1.specific_info_buf)).WillOnce(Return(true)); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au_2.data_sha1), au_2.pts, au_2.dts, au_2.rap, au_2.specific_info_size, au_2.specific_info_buf)).WillOnce(Return(true)); + EXPECT_CALL(check_point, Call(2)); + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au_3.data_sha1), au_3.pts, au_3.dts, au_3.rap, au_3.specific_info_size, au_3.specific_info_buf)).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_found(stream_id, private_stream_id, 0x12345678, Sha1Is(au_4.data_sha1), au_4.pts, au_4.dts, au_4.rap, au_4.specific_info_size, au_4.specific_info_buf)).WillOnce(Return(true)); + + demuxer.enable_es(stream_id, private_stream_id, is_avc, au_queue_buffer, au_max_size, false, 0x12345678); + demuxer.set_stream(stream, false); + demuxer.process_next_pack(); + check_point.Call(0); + demuxer.process_next_pack(); + check_point.Call(1); + demuxer.process_next_pack(); + check_point.Call(2); + demuxer.process_next_pack(); +} + +// The delimiters of each stream is split across two packs. Tests, if it is still found and the access units are extracted properly +INSTANTIATE_TEST_SUITE_P(Basic, DmuxPamfSplitDelimiter, Values +( + std::tuple{ 0xe0, 0x00, true, 0x800, AVC_MULTIPLE_PACK_STREAM, AVC_MULTIPLE_PACK_EXPECTED_AU_1, AVC_MULTIPLE_PACK_EXPECTED_AU_2, AVC_MULTIPLE_PACK_EXPECTED_AU_3, AVC_MULTIPLE_PACK_EXPECTED_AU_4 }, + std::tuple{ 0xe0, 0x00, false, 0x800, M2V_MULTIPLE_PACK_STREAM, M2V_MULTIPLE_PACK_EXPECTED_AU_1, M2V_MULTIPLE_PACK_EXPECTED_AU_2, M2V_MULTIPLE_PACK_EXPECTED_AU_3, M2V_MULTIPLE_PACK_EXPECTED_AU_4 }, + std::tuple{ 0xbd, 0x00, false, 0x800, ATRACX_MULTIPLE_PACK_STREAM, ATRACX_MULTIPLE_PACK_EXPECTED_AU_1, ATRACX_MULTIPLE_PACK_EXPECTED_AU_2, ATRACX_MULTIPLE_PACK_EXPECTED_AU_3, ATRACX_MULTIPLE_PACK_EXPECTED_AU_4 }, + std::tuple{ 0xbd, 0x30, false, 0x800, AC3_MULTIPLE_PACK_STREAM, AC3_MULTIPLE_PACK_EXPECTED_AU_1, AC3_MULTIPLE_PACK_EXPECTED_AU_2, AC3_MULTIPLE_PACK_EXPECTED_AU_3, AC3_MULTIPLE_PACK_EXPECTED_AU_4 } +)); + +struct DmuxPamfNoAccessUnits : DmuxPamfTest, WithParamInterface>> {}; + +TEST_P(DmuxPamfNoAccessUnits, Test) +{ + const auto& [stream_id, private_stream_id, is_avc, stream] = GetParam(); + + demuxer.enable_es(stream_id, private_stream_id, is_avc, au_queue_buffer, 0x1000, false, 0x12345678); + demuxer.set_stream(stream, false); + demuxer.process_next_pack(); + demuxer.process_next_pack(); + demuxer.process_next_pack(); + demuxer.process_next_pack(); +} + +// Tests buggy behavior: LLE doesn't handle multiple consecutive PES packets with less than three bytes correctly. +// These streams contain access units, but they are not found on LLE +INSTANTIATE_TEST_SUITE_P(TinyPackets, DmuxPamfNoAccessUnits, Values +( + std::tuple{ 0xe0, 0x00, true, AVC_TINY_PACKETS_STREAM }, + std::tuple{ 0xe0, 0x00, false, M2V_TINY_PACKETS_STREAM } +)); + +struct DmuxPamfInvalidStream : DmuxPamfTest, WithParamInterface> {}; + +TEST_P(DmuxPamfInvalidStream, Test) +{ + demuxer.set_stream(GetParam(), false); + demuxer.enable_es(0xbd, 0x00, false, au_queue_buffer, 0x800, false, 0); + demuxer.enable_es(0xbd, 0x20, false, au_queue_buffer, 0x800, false, 0); + demuxer.enable_es(0xbd, 0x40, false, au_queue_buffer, 0x800, false, 0); + EXPECT_FALSE(demuxer.process_next_pack()); +} + +// Tests if invalid streams are handled correctly. +// Each of these should fail a different check in process_next_pack() +// (LLE doesn't actually check anything, it would just read random things in the SPU local store) +INSTANTIATE_TEST_SUITE_P(Instance, DmuxPamfInvalidStream, Values +( + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0xe] = 0x01; return pack; }(), // Invalid code after pack header + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x12] = 0x07; pack[0x13] = 0xe7; return pack; }(), // System header size too large + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x20] = 0x01; return pack; }(), // Invalid code after system header + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x24] = 0x07; pack[0x25] = 0xd2; return pack; }(), // Private stream 2 size too large + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x114] = 0x01; return pack; }(), // Invalid code after private stream 2 + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x117] = 0xbe; return pack; }(), // Invalid stream type + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x119] = 0xe7; return pack; }(), // PES packet size too large + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x118] = 0x00; pack[0x119] = 0x03; pack[0x11c] = 0x00; return pack; }(), // PES packet header size too large + []() consteval { auto pack = USER_DATA_SINGLE_PACK_STREAM; pack[0x118] = 0x00; pack[0x119] = 0x15; return pack; }(), // User data stream too small + []() consteval { auto pack = LPCM_SINGLE_PACK_STREAM; pack[0x118] = 0x00; pack[0x119] = 0x20; pack[0x12c] = 0x00; pack[0x12d] = 0x00; return pack; }(), // LPCM stream too small + []() consteval { auto pack = ATRACX_SINGLE_PACK_STREAM; pack[0x118] = 0x00; pack[0x119] = 0x13; pack[0x12c] = 0x00; pack[0x12d] = 0x00; return pack; }(), // Stream header too small + []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; pack[0x118] = 0x00; pack[0x119] = 0x03; pack[0x11c] = 0x00; return pack; }() // PES packet header size too large +)); + +// Since the "pack stuffing length" field only has a size of three bits, this can only occur if the stream is not a multiple of 0x800 bytes large. +// Like the other invalid streams above, LLE does not check for this +TEST_F(DmuxPamfTest, InvalidPackStuffingLength) +{ + demuxer.set_stream({ AVC_SINGLE_PACK_STREAM.cbegin(), AVC_SINGLE_PACK_STREAM.cbegin() + 0x16 }, false); + EXPECT_FALSE(demuxer.process_next_pack()); +} + +// Tests if the program end code is properly detected and the corresponding event is fired +TEST_F(DmuxPamfTest, ProgEnd) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_prog_end).WillOnce(Return(false)); // Since this returns false, the event should be fired again immediately after the next process_next_pack() call + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_prog_end).WillOnce(Return(true)); + + constexpr std::array stream = { 0x00, 0x00, 0x01, 0xb9 }; + + demuxer.set_stream(stream, false); + check_point.Call(0); + demuxer.process_next_pack(); + check_point.Call(1); + demuxer.process_next_pack(); +} + +// Tests if the output queue properly checks whether there is enough space to write an au_chunk to the queue +TEST_F(DmuxPamfTest, QueueNoSpaceForAuChunk) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_queue_full); + + // Set a large au_max_size, this causes the back pointer of the queue to wrap around to the beginning after the first access units + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x2c14, false, 0); + + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + + // Since the first access unit is behind the back pointer and there is no space to write the next au_chunk to the queue, this should cause the au_queue_full event to be fired + check_point.Call(0); + demuxer.process_next_pack(); + + // Since we didn't release the first access unit to remove it from the queue, this should result in au_queue_full again + check_point.Call(1); + demuxer.process_next_pack(); +} + +// Tests if access units are properly removed from the queue +TEST_F(DmuxPamfTest, QueueReleaseAu) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x2c14, false, 0); + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + check_point.Call(0); + demuxer.process_next_pack(); + demuxer.release_au(0xe0, 0x00, 0x3ed); // Should remove the first access unit from the queue + check_point.Call(1); + demuxer.process_next_pack(); // Since there is space again in the queue, the second access unit should now be found +} + +// LLE adds 0x10 bytes to the size of the au_chunk when checking whether there is enough space. +TEST_F(DmuxPamfTest, QueueNoSpaceSPUShenanigans) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_queue_full); + + std::array small_au_queue_buffer{}; + + demuxer.enable_es(0xe0, 0x00, true, small_au_queue_buffer, 0x400, false, 0); + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + check_point.Call(0); + demuxer.process_next_pack(); + demuxer.release_au(0xe0, 0x00, 0x3fc); // 0xf bytes more than needed, but this should still result in au_queue_full + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + check_point.Call(1); + demuxer.process_next_pack(); +} + +// After completing an access unit, it should be checked whether au_max_size fits into the queue. +TEST_F(DmuxPamfTest, QueueNoSpaceForMaxAu) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + + std::array small_au_queue_buffer{}; + + demuxer.enable_es(0xe0, 0x00, true, small_au_queue_buffer, 0x400, false, 0); + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + check_point.Call(0); + demuxer.process_next_pack(); + demuxer.release_au(0xe0, 0x00, 0x300); // This does not remove the entire access unit from the queue + + // Since there is still data behind the back pointer of the queue, it can't wrap around again. + // Because au_max_size won't fit between the back pointer and the end of the queue buffer after finding the next access unit, + // this should cause au_queue_full before starting to extract the next access unit + check_point.Call(1); + demuxer.process_next_pack(); +} + +// Tests if a fatal error is produced if an access unit in the stream is larger than au_max_size +TEST_F(DmuxPamfTest, QueueAuLargerThanMaxSize) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_fatal_error); + EXPECT_CALL(demuxer, on_au_queue_full); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_fatal_error); + EXPECT_CALL(demuxer, on_au_queue_full); + + std::array small_au_queue_buffer{}; + + demuxer.enable_es(0xe0, 0x00, true, small_au_queue_buffer, 0x800, false, 0); // au_max_size is smaller than the access unit in the stream + demuxer.set_stream(AVC_LARGE_AU_STREAM, false); + demuxer.process_next_pack(); + check_point.Call(0); + demuxer.process_next_pack(); + check_point.Call(1); + demuxer.process_next_pack(); +} + +// LLE sets au_max_size to 0x800 if it is too large (no fatal error or anything else) +TEST_F(DmuxPamfTest, EnableEsAuMaxSizeTooLarge) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).Times(2).WillRepeatedly(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, std::numeric_limits::max(), false, 0); // The access unit max size will be set to 0x800 if it is larger than the queue buffer size or UINT32_MAX + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + + // Normally, this would cause the event au_queue_full because au_max_size wouldn't fit in the queue. + // However, since au_max_size gets set to 0x800, this doesn't occur + check_point.Call(0); + demuxer.process_next_pack(); +} + +// Tests if the demux_done event is fired when process_next_pack() is called after the end of the stream has been reached +TEST_F(DmuxPamfTest, DemuxDone) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_demux_done).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x1800, false, 0); + demuxer.set_stream({ AVC_MULTIPLE_PACK_STREAM.begin(), 0x800 }, false); + demuxer.process_next_pack(); + check_point.Call(0); + demuxer.process_next_pack(); + demuxer.process_next_pack(); // Since on_demux_done was already called and returned true, it should not produce another demux_done event +} + +// process_next_pack() should skip through the stream until it finds a start code or the end of the stream is reached +TEST_F(DmuxPamfTest, DemuxDoneEmptyStream) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_demux_done).WillOnce(Return(true)); + + constexpr std::array stream{}; + + demuxer.set_stream(stream, false); + + // Should immediately cause demux_done since there is no start code in the stream + check_point.Call(0); + demuxer.process_next_pack(); +} + +// Tests if the demuxer handles an input stream that is a continuation of the previous one correctly +TEST_F(DmuxPamfTest, StreamContinuity) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x1800, false, 0); + demuxer.set_stream({ AVC_MULTIPLE_PACK_STREAM.begin(), 0x800 }, false); + demuxer.process_next_pack(); + demuxer.set_stream({ AVC_MULTIPLE_PACK_STREAM.begin() + 0x800, AVC_MULTIPLE_PACK_STREAM.end() }, true); + check_point.Call(0); + demuxer.process_next_pack(); +} + +// Flushing an elementary stream manually produces an au_found event +TEST_F(DmuxPamfTest, Flush) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_FLUSH_EXPECTED_AU_SHA1), 0x15f90, 0x159b2, true, 0, Each(0))).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_flush_done).Times(10).WillRepeatedly(Return(false)); // The flush_done event is fired repeatedly in a loop until it returns true + EXPECT_CALL(demuxer, on_flush_done).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x800, false, 0); + demuxer.set_stream(AVC_MULTIPLE_PACK_STREAM, false); + demuxer.process_next_pack(); + check_point.Call(0); + demuxer.flush_es(0xe0, 0x00); + demuxer.process_next_pack(); // The elementary stream should be reset after a flush, so this should not find the next access unit +} + +// Tests resetting the stream +// On LLE, it is necessary to reset the stream before setting a new one, if the current stream has not been entirely consumed yet +TEST_F(DmuxPamfTest, ResetStream) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_demux_done).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x1800, false, 0); + demuxer.set_stream(AVC_MULTIPLE_PACK_STREAM, false); + demuxer.reset_stream(); + check_point.Call(0); + demuxer.process_next_pack(); // Should produce a demux_done event and shouldn't find an access unit since the stream was reset + demuxer.process_next_pack(); +} + +// Disabling an elementary stream should clear the access unit queue and internal state, and the disabled stream should not be demultiplexed anymore +TEST_F(DmuxPamfTest, DisableEs) +{ + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x1800, false, 0); + demuxer.set_stream(AVC_MULTIPLE_PACK_STREAM, false); + demuxer.process_next_pack(); + demuxer.disable_es(0xe0, 0x00); + + // Should not find an access unit since the elementary stream was disabled + demuxer.process_next_pack(); + demuxer.process_next_pack(); + demuxer.process_next_pack(); +} + +// Resetting an elementary stream should clear the queue and internal state, like disabling, except the elementary stream remains enabled +TEST_F(DmuxPamfTest, ResetEs) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(0)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x1800, false, 0); + demuxer.set_stream(AVC_MULTIPLE_PACK_STREAM, false); + demuxer.process_next_pack(); + demuxer.reset_es(0xe0, 0x00, nullptr); + demuxer.process_next_pack(); // Should not find an access unit since the output queue and the current access unit state was cleared + check_point.Call(0); + demuxer.process_next_pack(); +} + +// If reset_es is called with an address, only the most recent access unit should be removed from the queue. Nothing else should be reset +TEST_F(DmuxPamfTest, ResetEsSingleAu) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found).Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_found).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_queue_full); + + std::array small_au_queue_buffer{}; + + demuxer.enable_es(0xe0, 0x00, true, small_au_queue_buffer, 0x400, false, 0); + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + check_point.Call(0); + demuxer.process_next_pack(); + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + demuxer.reset_es(0xe0, 0x00, small_au_queue_buffer.data() + 0x400); + check_point.Call(1); + demuxer.process_next_pack(); +} + +// Tests the raw elementary stream mode, which skips all MPEG program stream related parsing +// (this isn't actually available through the abstraction layer cellDmux/libdmux.sprx, but the related PPU functions are exported in libdmuxpamf.sprx) +TEST_F(DmuxPamfTest, RawEs) +{ + // Zero out MPEG-PS related stuff from the stream, so that only the elementary stream remains + constexpr std::array stream = []() consteval { auto pack = AVC_SINGLE_PACK_STREAM; std::fill_n(pack.begin(), 0x12a, 0); return pack; }(); + + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_SINGLE_PACK_EXPECTED_AU_1.data_sha1), std::numeric_limits::max(), std::numeric_limits::max(), false, _, _)).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_SINGLE_PACK_EXPECTED_AU_2.data_sha1), std::numeric_limits::max(), std::numeric_limits::max(), false, _, _)).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x400, true, 0); + demuxer.set_stream(stream, false); + + // In raw elementary stream mode, the demuxer still processes the stream in chunks of 0x800 bytes. + // The first call to process_next_pack() each chunk does nothing, it only sets the internal state to elementary stream parsing + demuxer.process_next_pack(); + check_point.Call(0); + demuxer.process_next_pack(); +} + +// If any of the event handlers return false (on LLE this happens if the event queue is full), the state of process_next_pack() should be saved and return immediately. +// The next time process_next_pack() is called, it should resume where it left off +TEST_F(DmuxPamfTest, EventHandlersReturnFalse) +{ + EXPECT_CALL(check_point, Call(0)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_SINGLE_PACK_EXPECTED_AU_1.data_sha1), AVC_SINGLE_PACK_EXPECTED_AU_1.pts, AVC_SINGLE_PACK_EXPECTED_AU_1.dts, AVC_SINGLE_PACK_EXPECTED_AU_1.rap, _, _)).WillOnce(Return(false)); + EXPECT_CALL(check_point, Call(1)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_SINGLE_PACK_EXPECTED_AU_1.data_sha1), AVC_SINGLE_PACK_EXPECTED_AU_1.pts, AVC_SINGLE_PACK_EXPECTED_AU_1.dts, AVC_SINGLE_PACK_EXPECTED_AU_1.rap, _, _)).WillOnce(Return(true)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_SINGLE_PACK_EXPECTED_AU_2.data_sha1), AVC_SINGLE_PACK_EXPECTED_AU_2.pts, AVC_SINGLE_PACK_EXPECTED_AU_2.dts, AVC_SINGLE_PACK_EXPECTED_AU_2.rap, _, _)).WillOnce(Return(false)); + EXPECT_CALL(check_point, Call(2)); + EXPECT_CALL(demuxer, on_au_found(0xe0, 0x00, 0, Sha1Is(AVC_SINGLE_PACK_EXPECTED_AU_2.data_sha1), AVC_SINGLE_PACK_EXPECTED_AU_2.pts, AVC_SINGLE_PACK_EXPECTED_AU_2.dts, AVC_SINGLE_PACK_EXPECTED_AU_2.rap, _, _)).WillOnce(Return(true)); + + EXPECT_CALL(check_point, Call(3)); + EXPECT_CALL(demuxer, on_demux_done).WillOnce(Return(false)); + EXPECT_CALL(check_point, Call(4)); + EXPECT_CALL(demuxer, on_demux_done).WillOnce(Return(true)); + + demuxer.enable_es(0xe0, 0x00, true, au_queue_buffer, 0x800, false, 0); + demuxer.set_stream(AVC_SINGLE_PACK_STREAM, false); + check_point.Call(0); + demuxer.process_next_pack(); + check_point.Call(1); + demuxer.process_next_pack(); + check_point.Call(2); + demuxer.process_next_pack(); + + check_point.Call(3); + demuxer.process_next_pack(); + check_point.Call(4); + demuxer.process_next_pack(); + demuxer.process_next_pack(); + demuxer.process_next_pack(); +} + +// Tests if invalid stream ids are handled properly +TEST_F(DmuxPamfTest, InvalidStreamIds) +{ + const auto test = [&](u8 stream_id, u8 private_stream_id) + { + EXPECT_FALSE(demuxer.release_au(stream_id, private_stream_id, 0x800)); + EXPECT_FALSE(demuxer.disable_es(stream_id, private_stream_id)); + EXPECT_FALSE(demuxer.reset_es(stream_id, private_stream_id, nullptr)); + EXPECT_FALSE(demuxer.flush_es(stream_id, private_stream_id)); + }; + + // Trying to use or disable already disabled elementary streams should result in failures + for (u32 stream_id = 0xe0; stream_id < 0xf0; stream_id++) + { + test(stream_id, 0x00); + } + for (u32 private_stream_id = 0x00; private_stream_id < 0x10; private_stream_id++) + { + test(0xbd, private_stream_id); + } + for (u32 private_stream_id = 0x20; private_stream_id < 0x50; private_stream_id++) + { + test(0xbd, private_stream_id); + } + + // Enabling all possible elementary streams + for (u32 stream_id = 0xe0; stream_id < 0xf0; stream_id++) + { + EXPECT_TRUE(demuxer.enable_es(stream_id, 0x00, true, au_queue_buffer, 0x800, false, 0)); + } + for (u32 private_stream_id = 0x00; private_stream_id < 0x10; private_stream_id++) + { + EXPECT_TRUE(demuxer.enable_es(0xbd, private_stream_id, true, au_queue_buffer, 0x800, false, 0)); + } + for (u32 private_stream_id = 0x20; private_stream_id < 0x50; private_stream_id++) + { + EXPECT_TRUE(demuxer.enable_es(0xbd, private_stream_id, true, au_queue_buffer, 0x800, false, 0)); + } + + // Attempting to enable them again should result in failures + for (u32 stream_id = 0xe0; stream_id < 0xf0; stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(stream_id, 0x00, true, au_queue_buffer, 0x800, false, 0)); + } + for (u32 private_stream_id = 0x00; private_stream_id < 0x10; private_stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(0xbd, private_stream_id, true, au_queue_buffer, 0x800, false, 0)); + } + for (u32 private_stream_id = 0x20; private_stream_id < 0x50; private_stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(0xbd, private_stream_id, true, au_queue_buffer, 0x800, false, 0)); + } + + // Testing all possible invalid IDs + for (u32 stream_id = 0; stream_id < 0xbd; stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(stream_id, 0x00, true, au_queue_buffer, 0x800, false, 0)); + test(stream_id, 0x00); + } + for (u32 stream_id = 0xbe; stream_id < 0xe0; stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(stream_id, 0x00, true, au_queue_buffer, 0x800, false, 0)); + test(stream_id, 0x00); + } + for (u32 stream_id = 0xf0; stream_id <= 0xff; stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(stream_id, 0x00, true, au_queue_buffer, 0x800, false, 0)); + test(stream_id, 0x00); + } + for (u32 private_stream_id = 0x10; private_stream_id < 0x20; private_stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(0xbd, private_stream_id, true, au_queue_buffer, 0x800, false, 0)); + test(0xbd, private_stream_id); + } + for (u32 private_stream_id = 0x50; private_stream_id <= 0xff; private_stream_id++) + { + EXPECT_FALSE(demuxer.enable_es(0xbd, private_stream_id, true, au_queue_buffer, 0x800, false, 0)); + test(0xbd, private_stream_id); + } +} diff --git a/rpcs3/tests/test_dmux_pamf.h b/rpcs3/tests/test_dmux_pamf.h new file mode 100644 index 0000000000..841051a4e0 --- /dev/null +++ b/rpcs3/tests/test_dmux_pamf.h @@ -0,0 +1,123 @@ +#pragma once + +struct AccessUnit +{ + u64 pts; + u64 dts; + bool rap; + u8 specific_info_size; + std::array specific_info_buf; + std::array data_sha1; +}; + +constexpr std::array AVC_SINGLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x01, 0x09, 0x36, 0x2c, 0xe0, 0x95, 0x35, 0x29, 0xce, 0xf6, 0xc8, 0xc9, 0xc1, 0x2b, 0xa1, 0xc5, 0xae, 0xfd, 0xa6, 0x0d, 0x98, 0xa7, 0x39, 0x66, 0xe2, 0xd9, 0xbc, 0x1a, 0xbe, 0xba, 0x03, 0xcc, 0x80, 0x90, 0x3a, 0xe9, 0x5b, 0xa7, 0xa0, 0x31, 0x21, 0xbf, 0xee, 0xd2, 0x90, 0x1e, 0x9c, 0x79, 0xbf, 0x15, 0xce, 0xac, 0x02, 0xcf, 0x85, 0xe1, 0xf9, 0x9a, 0xcb, 0x97, 0x2f, 0xd9, 0x97, 0x67, 0x63, 0xed, 0xa3, 0x6d, 0x1b, 0xa3, 0x8b, 0xef, 0x0d, 0x54, 0xd6, 0xb6, 0x4b, 0x42, 0x9b, 0x5e, 0xc4, 0x47, 0xbc, 0x21, 0x02, 0x83, 0xa0, 0x00, 0x08, 0x27, 0x83, 0x2f, 0x5a, 0x02, 0x54, 0xda, 0xe6, 0x16, 0x57, 0x93, 0x09, 0xd6, 0xe4, 0x4a, 0xd9, 0x90, 0xc0, 0x8f, 0x3e, 0xe2, 0xcc, 0xc6, 0xec, 0x8b, 0x4e, 0xf3, 0x96, 0xa7, 0x86, 0x06, 0xca, 0xae, 0xf7, 0xb5, 0xde, 0xe6, 0xcd, 0x41, 0x6e, 0x3e, 0x8e, 0x33, 0xb5, 0x2b, 0xf4, 0xbb, 0xd4, 0x5b, 0xad, 0x1d, 0xfe, 0x5e, 0xc8, 0x03, 0xdd, 0x9d, 0x7e, 0xb9, 0x55, 0xd3, 0x41, 0x76, 0xa0, 0xf1, 0xca, 0x00, 0xe5, 0x80, 0x93, 0x4f, 0x30, 0x15, 0x8c, 0x4b, 0xa6, 0xda, 0xc1, 0xac, 0x1d, 0xe5, 0x96, 0xc4, 0x88, 0x6a, 0x64, 0x4a, 0x10, 0x18, 0x94, 0xe3, 0x18, 0xc2, 0x4d, 0xe7, 0xa0, 0x5e, 0xa3, 0x7c, 0x4a, 0x47, 0x7f, 0xd6, 0x22, 0xc9, 0x44, 0xc2, 0xd1, 0x99, 0xba, 0xdf, 0xa7, 0xfd, 0x24, 0xc6, 0x4c, 0x3c, 0x32, 0xf6, 0xb1, 0x98, 0x30, 0xd1, 0x51, 0x00, 0x18, 0x76, 0xe6, 0xe5, 0x86, 0xdc, 0x4a, 0x6d, 0x28, 0x18, 0x3e, 0x0f, 0x5c, 0xb9, 0x65, 0x2b, 0x48, 0x3c, 0x90, 0x20, 0xe7, 0x88, 0x3f, 0x65, 0x6d, 0x64, 0x83, 0x7d, 0xf6, 0xd3, 0xb0, 0xc7, 0x21, 0xc0, 0xf0, 0x9a, 0xad, 0xc8, 0x53, 0x6d, 0x05, 0xa4, 0x66, 0x2d, 0x87, 0x3f, 0x03, 0x3c, 0x76, 0xd9, 0xa7, 0x6c, 0x25, 0x34, 0xe7, 0x36, 0x46, 0xf9, 0x56, 0xf4, 0x37, 0xe7, 0xa3, 0x8c, 0x32, 0xfc, 0x3f, 0x1b, 0xb6, 0x93, 0xc9, 0x4c, 0x10, 0xaa, 0xba, 0xde, 0xed, 0x3e, 0x98, 0x5f, 0x38, 0x1b, 0xe0, 0xee, 0x48, 0xab, 0xeb, 0x18, 0x1f, 0xa0, 0xc5, 0x08, 0x97, 0x00, 0x85, 0xdf, 0xd4, 0x0a, 0x96, 0xb1, 0x59, 0xeb, 0xce, 0x35, 0xc9, 0x97, 0x83, 0x15, 0xc7, 0xef, 0x01, 0xce, 0x4f, 0xb2, 0x48, 0x36, 0xef, 0xd6, 0x46, 0x54, 0x4f, 0x47, 0xa3, 0x62, 0xf0, 0xd3, 0xbd, 0xd6, 0xe5, 0xec, 0x12, 0x63, 0x60, 0xe5, 0xe7, 0x2c, 0xc1, 0xeb, 0x72, 0xb5, 0x96, 0x50, 0x34, 0x8a, 0x48, 0xc0, 0x6c, 0x48, 0xfe, 0xb0, 0x04, 0xa7, 0x1a, 0xac, 0x3a, 0x08, 0x96, 0xae, 0x9b, 0x93, 0x66, 0x56, 0x9c, 0x22, 0x6a, 0x5a, 0x57, 0x2e, 0xc7, 0xef, 0x6e, 0x54, 0xdb, 0x7f, 0x7d, 0xf1, 0x88, 0x9c, 0x01, 0x95, 0x5d, 0xa1, 0x5a, 0xbb, 0x00, 0xc1, 0xe0, 0x88, 0x9a, 0x17, 0x3f, 0x6b, 0xab, 0x96, 0x95, 0x01, 0x6e, 0x79, 0xa4, 0x54, 0x34, 0xa2, 0x79, 0xdc, 0x53, 0xf5, 0xfa, 0x4a, 0xee, 0x3f, 0x3f, 0xeb, 0xe0, 0x0b, 0x06, 0x64, 0xa5, 0x1a, 0xae, 0x92, 0xd6, 0xdc, 0x1c, 0x3e, 0x8b, 0x1c, 0x72, 0x8f, 0x1d, 0x0b, 0xac, 0xe5, 0x0a, 0x57, 0x65, 0xa9, 0x1e, 0xae, 0x6c, 0x42, 0x63, 0x4a, 0x54, 0x55, 0xa3, 0x34, 0xcd, 0x29, 0x9a, 0xcb, 0xb0, 0x26, 0xf9, 0xc7, 0x55, 0x9c, 0xe1, 0x38, 0x81, 0x3e, 0x8a, 0x72, 0x2c, 0x7f, 0xff, 0xcb, 0x66, 0x75, 0x11, 0xda, 0xe6, 0xb0, 0x12, 0xa8, 0x4c, 0x22, 0x50, 0x6a, 0x69, 0x9e, 0x3d, 0xdc, 0x46, 0x68, 0x2f, 0x9d, 0x27, 0x03, 0x70, 0xe8, 0x46, 0xa2, 0x12, 0x2d, 0x3f, 0x57, 0x45, 0x2a, 0x8f, 0x5a, 0xb9, 0x0d, 0xf0, 0x61, 0x41, 0x7b, 0x51, 0x6c, 0x2d, 0xc5, 0x93, 0x5e, 0x50, 0xcd, 0x98, 0x20, 0xdb, 0x95, 0x3e, 0xf9, 0x80, 0x7d, 0xfd, 0xae, 0xd1, 0xec, 0xa2, 0x96, 0x2b, 0xd5, 0xd5, 0x29, 0x71, 0xd1, 0xb2, 0xbd, 0x4e, 0x03, 0xb2, 0xa3, 0x30, 0x54, 0xbe, 0xbc, 0x08, 0x7b, 0x05, 0xa3, 0x86, 0x8d, 0x7f, 0xa3, 0x6e, 0x9b, 0xad, 0xbd, 0xc8, 0x59, 0xe4, 0xcf, 0x9e, 0x3e, 0x19, 0x6f, 0xcf, 0xda, 0x2b, 0x9b, 0x6b, 0x9d, 0xb7, 0xc5, 0xbc, 0x9a, 0x67, 0x4c, 0x08, 0x0e, 0xa1, 0xa2, 0x19, 0xc6, 0x3c, 0x0d, 0x87, 0xcb, 0x11, 0x0f, 0x53, 0x85, 0x9b, 0xa9, 0x97, 0xa0, 0xba, 0x04, 0x98, 0x66, 0x57, 0xa5, 0xb9, 0x09, 0x31, 0xfe, 0xa9, 0xd2, 0xb4, 0x30, 0xa9, 0x9c, 0x0e, 0x6c, 0x62, 0x3a, 0x04, 0xcd, 0x2a, 0x64, 0x91, 0xfe, 0xee, 0x19, 0xa6, 0xa8, 0x6d, 0x87, 0xa2, 0x1a, 0xda, 0xb6, 0xf1, 0x27, 0x35, 0x95, 0x47, 0x64, 0xfa, 0x1f, 0xed, 0xcf, 0x7b, 0xdc, 0x9a, 0x18, 0x2e, 0xfe, 0xf3, 0x56, 0x6d, 0x28, 0xce, 0xcd, 0x16, 0x03, 0x59, 0x14, 0x4a, 0x90, 0xc2, 0xe3, 0x20, 0x6b, 0x96, 0xd5, 0x30, 0x20, 0x3f, 0xc4, 0xeb, 0x48, 0xe5, 0x9c, 0x34, 0xdf, 0x8d, 0x46, 0x73, 0x1d, 0x54, 0x11, 0x65, 0x5b, 0xe4, 0xab, 0xb3, 0xa6, 0x1e, 0x40, 0x43, 0xb5, 0x84, 0x61, 0x94, 0x01, 0xea, 0x92, 0xd4, 0x9f, 0xb9, 0xc6, 0x26, 0x2d, 0x56, 0xda, 0xa4, 0x11, 0x5f, 0xf6, 0x3f, 0xa2, 0xd8, 0xcb, 0xda, 0xb7, 0x0e, 0xc6, 0x0e, 0x6d, 0xbe, 0x0d, 0x9a, 0x46, 0xc2, 0x77, 0x7d, 0x3a, 0x0d, 0x67, 0xf9, 0x58, 0xf4, 0xba, 0x65, 0xb2, 0xe0, 0x39, 0x60, 0xcf, 0x76, 0xd4, 0x15, 0x73, 0x46, 0xdf, 0x14, 0x22, 0xa7, 0x25, 0x6f, 0xd3, 0x9f, 0x8f, 0xa9, 0x60, 0x1f, 0xbb, 0x13, 0x71, 0x1b, 0x43, 0x4f, 0x9f, 0x66, 0x17, 0x4a, 0xd7, 0x56, 0xd6, 0x84, 0xec, 0x65, 0xd1, 0xb1, 0x64, 0x48, 0x65, 0x18, 0x61, 0x8f, 0x7e, 0xef, 0xdf, 0x5d, 0xea, 0x7d, 0x67, 0xb2, 0x66, 0x95, 0xfe, 0xf3, 0x6c, 0xf4, 0xaa, 0x1d, 0x14, 0x0e, 0x73, 0x0e, 0xca, 0x32, 0xc1, 0x52, 0x23, 0x77, 0xba, 0x94, 0x31, 0x4b, 0x26, 0x44, 0xce, 0x34, 0xe6, 0xd0, 0x6a, 0xa9, 0x5c, 0x58, 0xbd, 0xf8, 0xc6, 0xf4, 0x0b, 0xac, 0xab, 0x1e, 0x5a, 0x81, 0xee, 0x4a, 0xe4, 0x7a, 0x4b, 0xf0, 0x92, 0xad, 0xad, 0x75, 0xd3, 0x0a, 0xa1, 0x71, 0xab, 0x1f, 0x90, 0xbd, 0x8c, 0x80, 0xcd, 0x09, 0x7f, 0x9a, 0x46, 0xbd, 0xd5, 0x92, 0x66, 0x8b, 0xc9, 0xc0, 0x3b, 0xbc, 0xdb, 0xff, 0xa6, 0x99, 0x08, 0xb6, 0x73, 0xf9, 0x79, 0x6e, 0x8d, 0xee, 0x51, 0xdd, 0xd7, 0xe5, 0xec, 0xb4, 0xa1, 0x92, 0x58, 0xab, 0xdc, 0x79, 0xfe, 0xc9, 0xad, 0x55, 0xf3, 0x07, 0x86, 0x56, 0x81, 0x4d, 0x90, 0x52, 0x48, 0x15, 0xbf, 0x56, 0xa1, 0xd4, 0x96, 0xae, 0x44, 0x3c, 0x35, 0x25, 0x06, 0x39, 0xde, 0x60, 0x86, 0x88, 0xb0, 0xc6, 0xcc, 0x98, 0x8f, 0x06, 0x14, 0xb7, 0xed, 0x5b, 0x92, 0x7f, 0x64, 0xc4, 0xd0, 0xf1, 0x2a, 0x89, 0x08, 0x32, 0xab, 0x26, 0x28, 0x55, 0xff, 0xd7, 0xca, 0xf0, 0x41, 0xfe, 0x7d, 0x90, 0xd3, 0x52, 0xcb, 0x6b, 0x1b, 0x2d, 0xfa, 0x05, 0x01, 0x2f, 0xf1, 0xda, 0x84, 0xbe, 0x5c, 0x86, 0xcd, 0x41, 0x09, 0xfe, 0x92, 0xbf, 0x74, 0xc9, 0x75, 0x21, 0x89, 0x70, 0xac, 0x1c, 0x70, 0xdd, 0x56, 0xcb, 0xb0, 0xe1, 0xb9, 0xb4, 0xf8, 0x1c, 0x00, 0x00, 0x01, 0x09, 0x22, 0x6d, 0x07, 0x49, 0x80, 0xdb, 0x25, 0x5c, 0xeb, 0xa5, 0x99, 0x4a, 0xb0, 0x12, 0x9c, 0xc1, 0xce, 0x89, 0xca, 0xc8, 0x57, 0x99, 0x56, 0x40, 0x18, 0xe7, 0xb1, 0xe7, 0x47, 0x97, 0x47, 0xc2, 0xa4, 0x7a, 0xae, 0x3e, 0xe2, 0xb3, 0xec, 0xc2, 0x69, 0x43, 0xd9, 0xe3, 0x9c, 0x95, 0xde, 0x01, 0x95, 0xb7, 0xb4, 0x08, 0x8d, 0x7f, 0x1d, 0x2e, 0x95, 0x0f, 0x3c, 0xdd, 0x9e, 0x64, 0xd6, 0xf3, 0xe6, 0xa0, 0x24, 0xe5, 0x19, 0xf0, 0x96, 0x92, 0x0a, 0xde, 0xc5, 0x12, 0xf7, 0x3a, 0xa3, 0x26, 0x12, 0x84, 0x7b, 0x73, 0x1d, 0xd5, 0xbc, 0xf4, 0x47, 0xfd, 0x81, 0xf7, 0xd9, 0x03, 0xc4, 0x34, 0x06, 0x2b, 0x05, 0x40, 0xa7, 0x2e, 0x1d, 0xbd, 0x5c, 0x85, 0x27, 0x77, 0xf7, 0x75, 0xca, 0xd5, 0x34, 0x5b, 0xf5, 0x40, 0x7e, 0x06, 0x92, 0x5c, 0x94, 0x89, 0x0a, 0xa3, 0x0e, 0x63, 0x95, 0x5d, 0x54, 0x28, 0x86, 0x68, 0x17, 0x2d, 0xd3, 0xc3, 0x46, 0x93, 0xbb, 0x9c, 0xae, 0xea, 0x35, 0xdd, 0xda, 0xc4, 0x1b, 0xa3, 0xba, 0xee, 0x1e, 0x7e, 0xab, 0xb9, 0xba, 0xe4, 0x8d, 0x5e, 0x98, 0x9f, 0x84, 0xba, 0x0c, 0x7c, 0x29, 0x63, 0xed, 0xae, 0x90, 0x54, 0x9c, 0x74, 0x2a, 0x80, 0xe4, 0x4d, 0xbc, 0x5b, 0xaa, 0xd5, 0xbe, 0x64, 0xae, 0xab, 0xc1, 0x45, 0xb7, 0xfd, 0xc1, 0x48, 0xd3, 0x48, 0x32, 0x82, 0xea, 0x3f, 0x93, 0x3c, 0xa5, 0xef, 0xf1, 0x45, 0x73, 0xae, 0x60, 0x98, 0x36, 0x5e, 0xb1, 0xaa, 0x7c, 0x1b, 0x23, 0x68, 0xaa, 0x7f, 0x5c, 0x6a, 0x2d, 0xad, 0x23, 0xa5, 0x82, 0x9a, 0x0e, 0x65, 0x62, 0xca, 0x91, 0x61, 0x39, 0xfd, 0xd1, 0x3f, 0x6f, 0xc0, 0xf0, 0xa2, 0x2d, 0xcc, 0x99, 0x96, 0xd3, 0x6b, 0xe3, 0x51, 0x5c, 0x12, 0x3c, 0x23, 0x88, 0xc3, 0x8d, 0x42, 0x7c, 0xf3, 0xec, 0x9e, 0x5c, 0x69, 0xb7, 0xb6, 0xf7, 0xa7, 0x07, 0xd5, 0x56, 0x8b, 0x8b, 0x2c, 0x3f, 0x29, 0x02, 0x03, 0xb1, 0xde, 0xe6, 0xeb, 0xbd, 0x52, 0x4c, 0xf3, 0x4e, 0x03, 0xf9, 0x9f, 0x4a, 0x2d, 0x23, 0x10, 0x57, 0xf5, 0x96, 0xd2, 0x0e, 0x8c, 0x72, 0xd2, 0x56, 0xdf, 0x44, 0x1a, 0xcb, 0x36, 0xe9, 0x03, 0x67, 0xca, 0xa9, 0xbf, 0x38, 0x43, 0x73, 0x49, 0xb8, 0x9a, 0x13, 0x4c, 0x78, 0x89, 0xac, 0xc7, 0xb9, 0x8c, 0x2c, 0xee, 0x80, 0x04, 0xf7, 0x75, 0x4f, 0xd2, 0x7c, 0xb5, 0x20, 0xe2, 0x3b, 0xb8, 0xc5, 0x57, 0x2e, 0xc8, 0x9f, 0xcb, 0xd1, 0xee, 0xcf, 0x59, 0x6c, 0xc1, 0x85, 0xe6, 0x34, 0x42, 0xe2, 0x65, 0x7b, 0xbb, 0xae, 0x2e, 0x97, 0x0b, 0x3f, 0x1b, 0xfb, 0xd3, 0xcc, 0x5d, 0x01, 0xba, 0xf9, 0x30, 0x62, 0xa5, 0xd6, 0xe0, 0xca, 0xce, 0xde, 0x52, 0x68, 0x90, 0x76, 0xa3, 0x82, 0x88, 0x52, 0xa2, 0xb0, 0x9c, 0xa1, 0x3d, 0xf8, 0x95, 0x35, 0x31, 0x8d, 0x0a, 0x9c, 0xaa, 0xac, 0xb7, 0x2f, 0x30, 0x4e, 0xda, 0x0b, 0x56, 0x32, 0x8d, 0xda, 0xd5, 0xc2, 0xd3, 0x1b, 0x7f, 0xc1, 0xf6, 0xd1, 0xde, 0x07, 0xe4, 0x94, 0x34, 0xb9, 0x00, 0x40, 0x05, 0xa3, 0x6a, 0x0d, 0x8f, 0xd8, 0x44, 0x0b, 0xc3, 0x64, 0xd0, 0x53, 0x70, 0x5c, 0xc9, 0x99, 0x13, 0xac, 0xc8, 0x09, 0x85, 0x44, 0x3c, 0x1d, 0x99, 0x26, 0x52, 0x3c, 0xce, 0x50, 0xe4, 0x44, 0xd0, 0xd0, 0xa5, 0xa8, 0xad, 0x6b, 0xae, 0xf0, 0xca, 0x58, 0x22, 0x9f, 0x61, 0xf6, 0xb3, 0x6b, 0xd0, 0x34, 0x9b, 0x3b, 0x45, 0x25, 0x5e, 0x0e, 0xac, 0xf6, 0x82, 0x2b, 0x01, 0xa4, 0x49, 0x72, 0x23, 0xd7, 0xd3, 0x31, 0xb9, 0x36, 0xb2, 0x3e, 0x06, 0xcc, 0xfc, 0x82, 0xee, 0x12, 0x97, 0x26, 0xae, 0xe5, 0x03, 0xb5, 0x54, 0x3f, 0x45, 0xde, 0x94, 0xb1, 0x39, 0xbd, 0xd7, 0xe3, 0x20, 0xca, 0x85, 0xa1, 0x83, 0x04, 0xfc, 0x93, 0x8d, 0xbf, 0x2d, 0x49, 0x36, 0x8e, 0xf2, 0x82, 0x3f, 0xcf, 0xcf, 0xfb, 0xae, 0x31, 0x35, 0x45, 0xa4, 0xde, 0xd7, 0xc7, 0x07, 0xdd, 0xdc, 0x12, 0x68, 0x6f, 0x93, 0x44, 0xef, 0xd5, 0x96, 0x00, 0x04, 0x1a, 0xc7, 0x46, 0xe7, 0x51, 0x76, 0xd4, 0x3a, 0x50, 0x40, 0xce, 0x9a, 0x55, 0x1e, 0xd6, 0x9a, 0x8d, 0xd9, 0xc8, 0x34, 0xea, 0xda, 0xdc, 0x5f, 0x51, 0x88, 0x80, 0xcf, 0xf9, 0xb9, 0x20, 0x8b, 0x60, 0xf9, 0xbf, 0x2a, 0x24, 0x18, 0x8f, 0x86, 0xc8, 0xaa, 0x00, 0xf9, 0x05, 0xef, 0x38, 0x59, 0xa2, 0xe9, 0x36, 0xac, 0x01, 0xd5, 0xa6, 0x09, 0x22, 0x5c, 0xd7, 0xca, 0xc3, 0x15, 0xb4, 0x8f, 0x6f, 0x1e, 0xe9, 0xca, 0xe4, 0xad, 0x77, 0x28, 0x99, 0x8f, 0xdd, 0x8a, 0x5d, 0x82, 0x17, 0xa7, 0x58, 0x6f, 0x71, 0x66, 0x8f, 0x74, 0x69, 0xc9, 0x0e, 0x58, 0xf3, 0xc6, 0x37, 0x93, 0xf9, 0xff, 0xf6, 0x6b, 0x51, 0xa7, 0x5c, 0x61, 0x12, 0x92, 0x0d, 0xc9, 0x3b, 0x59, 0xe9, 0xea, 0x14, 0x25, 0x29, 0x28, 0xd1, 0x51, 0xc6, 0x1c, 0x39, 0x80, 0x2f, 0x99, 0xbd, 0xd0, 0xcd, 0x03, 0x71, 0xba, 0xb4, 0x1c, 0xd3, 0x2f, 0xa9, 0xaf, 0xc6, 0x24, 0x24, 0xb9, 0xd5, 0xc7, 0xe3, 0xda, 0xf1, 0xd2, 0x4d, 0xc2, 0x69, 0x31, 0x64, 0xc6, 0xfd, 0xb5, 0xce, 0x00, 0x3a, 0xc7, 0x9c, 0x95, 0xfd, 0x88, 0xb5, 0x58, 0xc5, 0x7d, 0x52, 0xf4, 0x58, 0x83, 0x4a, 0xc8, 0x07, 0xd3, 0x45, 0x53, 0x66, 0x00, 0x00, 0x01, 0x09 }; +constexpr AccessUnit AVC_SINGLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, true, 0, {}, { 0xdb, 0x46, 0x52, 0xf7, 0x3e, 0x38, 0x99, 0x16, 0xe6, 0x60, 0x20, 0x01, 0x4d, 0xce, 0x65, 0x8f, 0xfa, 0xb5, 0x8f, 0x3a } }; +constexpr AccessUnit AVC_SINGLE_PACK_EXPECTED_AU_2 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0xd2, 0x40, 0xa9, 0x31, 0x5a, 0x2f, 0x92, 0xb0, 0x3c, 0x26, 0xa9, 0xd0, 0xdf, 0xfb, 0x97, 0x2d, 0xb3, 0xf1, 0x4d, 0x78 } }; + +constexpr std::array M2V_SINGLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x01, 0xb3, 0x5f, 0x55, 0xe4, 0xf8, 0x98, 0x9f, 0x07, 0x15, 0x3e, 0x0f, 0x8d, 0x16, 0xed, 0xcd, 0x0a, 0xa8, 0x39, 0xbf, 0xb5, 0xba, 0x30, 0xf0, 0x64, 0x37, 0x31, 0xba, 0xec, 0x8e, 0xa0, 0x68, 0xcc, 0xa1, 0xfd, 0x1f, 0x93, 0xff, 0xc3, 0xd1, 0xbf, 0x05, 0x6f, 0x8f, 0x71, 0x78, 0x90, 0xeb, 0xc1, 0x9e, 0xe6, 0x63, 0xad, 0x00, 0x07, 0xcb, 0x8f, 0x60, 0x57, 0xc6, 0x3c, 0xbb, 0x4b, 0x47, 0xe8, 0xc9, 0x8c, 0x58, 0x5c, 0x78, 0x12, 0x21, 0x20, 0xc8, 0x54, 0xa7, 0x85, 0x65, 0x2d, 0x4a, 0x86, 0xb3, 0x66, 0x5e, 0x89, 0x8e, 0xeb, 0xd6, 0x37, 0xaa, 0xcb, 0xab, 0xb0, 0x42, 0x94, 0xe6, 0x77, 0x86, 0xfd, 0x21, 0x8e, 0xb8, 0xe5, 0x4d, 0x5f, 0x94, 0x38, 0xc1, 0x0c, 0xaa, 0x92, 0xb4, 0xf4, 0x20, 0x75, 0x2d, 0x60, 0xa8, 0x83, 0x7b, 0x1d, 0xa1, 0x91, 0x7c, 0xf9, 0x82, 0xfb, 0x89, 0x19, 0x8e, 0x3b, 0xb2, 0x8b, 0xb9, 0x61, 0x3e, 0x62, 0x29, 0x96, 0xe6, 0x64, 0x91, 0xce, 0x61, 0x11, 0xb9, 0x69, 0x4d, 0xcb, 0xcb, 0x9c, 0xe2, 0xca, 0xa4, 0x97, 0x3a, 0x3a, 0xc5, 0xea, 0xeb, 0x2b, 0xf1, 0x08, 0xad, 0x68, 0xe3, 0xd2, 0xd0, 0xff, 0x49, 0x34, 0xb6, 0x09, 0x91, 0xa3, 0xfe, 0xc2, 0x4f, 0x69, 0x7e, 0xaf, 0xf5, 0x15, 0x42, 0x47, 0xc2, 0x8e, 0x14, 0xc6, 0x99, 0x80, 0x2f, 0x57, 0xbe, 0x39, 0x6e, 0x5e, 0xb2, 0x7f, 0x96, 0x21, 0xe5, 0x96, 0x6b, 0x4c, 0x99, 0xb5, 0x18, 0xcf, 0x87, 0x28, 0x5a, 0x66, 0x03, 0xaa, 0xcc, 0xd5, 0xce, 0x1d, 0x42, 0x99, 0xb4, 0x58, 0xf8, 0xf8, 0x82, 0x58, 0x8a, 0x94, 0x4a, 0xce, 0x92, 0x53, 0xec, 0xeb, 0x00, 0x00, 0x01, 0x00, 0xdd, 0xdc, 0xcc, 0xa9, 0x9d, 0x30, 0xfa, 0x65, 0xff, 0xd5, 0xc6, 0xb5, 0x2f, 0x5a, 0x14, 0xab, 0x0a, 0x86, 0x18, 0x98, 0x8c, 0x02, 0x49, 0x11, 0x05, 0x78, 0xf7, 0xae, 0x21, 0x7a, 0x5e, 0xa8, 0x1d, 0x5e, 0x1c, 0x1b, 0x7f, 0x8d, 0xe9, 0x05, 0x0b, 0x93, 0x4f, 0x98, 0xaa, 0x11, 0x71, 0x91, 0x0f, 0x8b, 0x47, 0x43, 0x69, 0x12, 0x1b, 0x30, 0x31, 0x3b, 0x8d, 0x5a, 0x09, 0x91, 0x19, 0x83, 0xd8, 0x68, 0x0d, 0xc9, 0x25, 0xf6, 0xdf, 0x42, 0xb2, 0x49, 0x76, 0xcf, 0x98, 0x6a, 0x54, 0xfa, 0x10, 0xd8, 0x5b, 0x51, 0x75, 0xb8, 0x80, 0x7e, 0xfd, 0x92, 0x7f, 0x57, 0xcf, 0x9c, 0xad, 0xb4, 0x90, 0xb1, 0x42, 0x3e, 0xa0, 0xea, 0xde, 0xea, 0x66, 0x7b, 0xd6, 0xa0, 0xf3, 0xc3, 0xa6, 0xe0, 0x4b, 0x3c, 0x97, 0xf0, 0x11, 0x95, 0xb7, 0xb7, 0xd1, 0x94, 0x0a, 0x32, 0x11, 0x4d, 0x34, 0x8d, 0x72, 0xc5, 0xfd, 0x96, 0xfc, 0x44, 0xd7, 0x59, 0xcf, 0xa5, 0xd7, 0x2d, 0x3a, 0xd0, 0x7d, 0x1c, 0x88, 0xac, 0x7d, 0x63, 0xad, 0xcd, 0x35, 0xd1, 0xba, 0x66, 0xc5, 0x20, 0xb7, 0x65, 0x41, 0xe3, 0x41, 0x39, 0xfe, 0xdd, 0x90, 0x40, 0x52, 0xe8, 0x51, 0x86, 0x2c, 0xb0, 0x1f, 0x36, 0x57, 0x77, 0x8c, 0xe7, 0xf6, 0x8f, 0xc6, 0x5d, 0xfe, 0xa4, 0x4c, 0x65, 0x01, 0x53, 0x0b, 0xe6, 0x8a, 0xc5, 0x6b, 0x17, 0xa0, 0xe7, 0x31, 0xe1, 0xce, 0x1e, 0xc8, 0xc8, 0xb3, 0x8d, 0x04, 0x0e, 0x7f, 0x6a, 0xaa, 0x32, 0x4e, 0x24, 0xe8, 0xc8, 0xbf, 0x8c, 0xec, 0xea, 0xcd, 0xb2, 0xd4, 0xdc, 0x0c, 0xb6, 0xc9, 0xac, 0x6e, 0x66, 0xa7, 0x22, 0xa5, 0x26, 0xe0, 0x90, 0xad, 0x6f, 0xef, 0x96, 0x85, 0xee, 0x53, 0xfb, 0xdd, 0xd6, 0x6a, 0xa7, 0x45, 0xb9, 0x64, 0x3b, 0x4d, 0xfc, 0xce, 0x21, 0x7d, 0x33, 0x70, 0xb4, 0xb6, 0x43, 0x97, 0x77, 0xcc, 0x8e, 0x38, 0xee, 0x82, 0x54, 0x26, 0x1e, 0xc1, 0x6e, 0x86, 0x1c, 0x39, 0x62, 0x46, 0xf5, 0x11, 0x95, 0xbf, 0x64, 0xa0, 0xf9, 0x1d, 0x1a, 0x23, 0x34, 0x42, 0x0b, 0x11, 0x6b, 0x55, 0x19, 0x37, 0xb8, 0x43, 0xa2, 0x24, 0x22, 0x08, 0xca, 0xa0, 0xb9, 0x90, 0xae, 0x74, 0x57, 0xb3, 0x7b, 0x31, 0x1a, 0x50, 0xc3, 0xe6, 0xb9, 0xaa, 0xb1, 0xc9, 0x41, 0xcd, 0x7f, 0x11, 0xac, 0xf2, 0x4f, 0x92, 0xeb, 0xe7, 0xd1, 0xd5, 0x58, 0x6a, 0xdd, 0x15, 0x1c, 0xd6, 0x9f, 0x4e, 0xb3, 0xa5, 0x7a, 0xdc, 0xde, 0x33, 0x25, 0x14, 0x59, 0x58, 0x21, 0x96, 0x8f, 0x5c, 0x78, 0x08, 0x5b, 0xe6, 0xa0, 0xca, 0x75, 0x83, 0x2a, 0x76, 0x16, 0x10, 0x34, 0x1f, 0x6f, 0x4b, 0x60, 0x8d, 0x8e, 0xf8, 0xb1, 0xe2, 0xac, 0xe5, 0xe3, 0x48, 0xcb, 0x99, 0xdb, 0x5e, 0xc8, 0xad, 0x15, 0x35, 0x44, 0xac, 0x92, 0x83, 0x95, 0x19, 0x91, 0x0c, 0x7f, 0x0a, 0xf2, 0x17, 0xc7, 0xff, 0x95, 0xd2, 0xee, 0x10, 0xad, 0xde, 0x03, 0xe2, 0x20, 0x1e, 0xd9, 0x4f, 0xda, 0x80, 0xf8, 0xba, 0x7e, 0x6d, 0x0c, 0x9e, 0xce, 0xbe, 0x61, 0x89, 0x16, 0x2a, 0x2f, 0xf6, 0x58, 0xce, 0x50, 0x4d, 0x23, 0x02, 0x5a, 0xf4, 0x11, 0xf3, 0xd7, 0x5a, 0x15, 0xaa, 0xd2, 0x74, 0xa1, 0x9b, 0x87, 0x40, 0xec, 0x37, 0x9f, 0x36, 0x05, 0x36, 0xd6, 0xf1, 0x7d, 0x2d, 0xd4, 0x16, 0x38, 0xf1, 0xe3, 0x64, 0xeb, 0x8d, 0x69, 0xa5, 0x6e, 0xac, 0xd6, 0x1c, 0x8b, 0x3d, 0x7a, 0x52, 0xb5, 0x50, 0xe4, 0x57, 0x61, 0xb8, 0xf2, 0xde, 0x77, 0x5c, 0x4b, 0x4a, 0x5a, 0x76, 0x97, 0x0d, 0x82, 0x50, 0xb6, 0x8a, 0x72, 0x64, 0x9f, 0xc2, 0x14, 0xbf, 0xbb, 0x4f, 0xe9, 0x5b, 0x34, 0x41, 0x27, 0xf3, 0x04, 0x8e, 0x9e, 0xd2, 0xc4, 0xc0, 0x56, 0xd5, 0x91, 0x99, 0x18, 0xbb, 0xd4, 0x41, 0xb3, 0xe4, 0x0a, 0xaf, 0x3f, 0xc2, 0xde, 0x4c, 0x97, 0x00, 0x4d, 0xc2, 0x83, 0x1c, 0x1c, 0xd9, 0x49, 0xd5, 0x24, 0x25, 0xab, 0x90, 0x8b, 0xac, 0x39, 0x58, 0x55, 0x1b, 0x17, 0xc3, 0x41, 0x66, 0x91, 0xa1, 0x78, 0x6e, 0x01, 0x20, 0x82, 0x54, 0x7e, 0xad, 0x0b, 0x6d, 0x05, 0x51, 0x00, 0x00, 0x01, 0x00, 0x12, 0xb7, 0x73, 0x59, 0x4c, 0x73, 0x73, 0x28, 0xdb, 0x82, 0x34, 0xcf, 0x9a, 0xa1, 0x13, 0xe9, 0xdf, 0x22, 0x5c, 0x02, 0xf7, 0x43, 0x86, 0x56, 0x99, 0x20, 0x4c, 0x74, 0x5b, 0x39, 0xe7, 0x55, 0x28, 0x06, 0x7e, 0x18, 0x1e, 0xbf, 0xaf, 0xbd, 0x73, 0xa5, 0x15, 0xb5, 0xc7, 0x8c, 0x1a, 0x07, 0x78, 0x78, 0xed, 0x02, 0x10, 0xe7, 0x34, 0xbd, 0x09, 0x14, 0xb9, 0xb3, 0xf8, 0x15, 0xe9, 0xd4, 0xa0, 0x41, 0x93, 0x69, 0x97, 0x46, 0xd1, 0x41, 0x0c, 0x3f, 0xa3, 0x2d, 0x24, 0xda, 0x2f, 0x2b, 0x04, 0xad, 0x07, 0x61, 0x91, 0xf0, 0x8f, 0x55, 0xa6, 0x70, 0x51, 0x5c, 0x2d, 0xfc, 0x30, 0xad, 0xc6, 0x47, 0xc4, 0x35, 0x6e, 0x47, 0xe0, 0x25, 0x02, 0x51, 0x2f, 0xed, 0xf5, 0x34, 0xe0, 0xe8, 0x4e, 0x9f, 0xc0, 0x43, 0x68, 0x05, 0x5c, 0x66, 0x54, 0x7d, 0x19, 0x19, 0xba, 0xf3, 0x12, 0xf1, 0xa6, 0xb1, 0xe3, 0x22, 0x2d, 0x71, 0x55, 0xe6, 0xce, 0x91, 0x0d, 0x92, 0x0d, 0x94, 0x1d, 0x7a, 0xc4, 0xa5, 0x53, 0x1c, 0x9b, 0xa7, 0x7f, 0xaa, 0xd2, 0x3b, 0xdd, 0x0f, 0x92, 0xc5, 0xef, 0x5f, 0xbd, 0x1a, 0xc4, 0xa1, 0x7b, 0x9a, 0xc6, 0xc2, 0x51, 0x8a, 0x8f, 0x02, 0x26, 0x0d, 0x49, 0x4b, 0x36, 0xfa, 0x1c, 0xfe, 0x62, 0xcd, 0x81, 0x95, 0x7c, 0x98, 0x7a, 0x45, 0x0d, 0xc5, 0x84, 0x75, 0x7e, 0x14, 0xaf, 0x11, 0x06, 0x23, 0x90, 0x8b, 0x50, 0x27, 0x8c, 0x57, 0xcc, 0x55, 0xa0, 0xa9, 0x52, 0x1a, 0x94, 0x9e, 0x96, 0x52, 0xdf, 0x7f, 0x6d, 0xcf, 0x37, 0x87, 0xae, 0x01, 0x28, 0x41, 0xde, 0x29, 0x70, 0x0c, 0x90, 0xf8, 0xcc, 0x7c, 0x2e, 0x6a, 0x37, 0x12, 0x9d, 0x49, 0xfe, 0x3f, 0xec, 0x6d, 0x21, 0xce, 0x67, 0x8b, 0xc9, 0xab, 0x20, 0x38, 0x80, 0x8b, 0x93, 0x24, 0x3f, 0x62, 0x03, 0x57, 0x59, 0x83, 0xb4, 0x6c, 0xdd, 0x68, 0xdd, 0x4c, 0x89, 0xfc, 0x79, 0x64, 0x47, 0x0e, 0x44, 0x96, 0xb7, 0x0b, 0x0a, 0xab, 0x9a, 0x1e, 0xc5, 0x24, 0x92, 0x3c, 0xf5, 0x91, 0x77, 0x39, 0x67, 0x41, 0x5a, 0xdd, 0x62, 0x91, 0x95, 0xe5, 0x64, 0x56, 0x0a, 0x2d, 0xee, 0xf4, 0x65, 0x98, 0xa6, 0x73, 0x28, 0x71, 0xb8, 0xa0, 0x9b, 0xa8, 0x95, 0x6e, 0xa6, 0xec, 0xb3, 0xf7, 0x19, 0xf5, 0xcd, 0x60, 0xf2, 0x5d, 0x1e, 0x23, 0x4c, 0xeb, 0x53, 0xad, 0x28, 0xd1, 0x7e, 0xa2, 0xbb, 0x83, 0xf8, 0x55, 0x9e, 0x08, 0x85, 0xfb, 0x47, 0x3c, 0x9e, 0x3b, 0xbb, 0x81, 0xa2, 0x13, 0xc0, 0x8e, 0x18, 0x37, 0x2e, 0x98, 0x61, 0x88, 0x3e, 0x13, 0x06, 0x67, 0x1e, 0x03, 0xea, 0x7a, 0xb1, 0x3e, 0x6b, 0xef, 0x68, 0x09, 0x11, 0xfb, 0x80, 0x16, 0x12, 0x34, 0xc3, 0xb1, 0x14, 0x13, 0x64, 0x64, 0xc6, 0x6d, 0xf9, 0x43, 0x2c, 0xc2, 0x08, 0xac, 0xb5, 0x72, 0xe2, 0xf6, 0x44, 0x65, 0x90, 0x0a, 0xdc, 0x80, 0xab, 0x69, 0xdf, 0x63, 0x22, 0x57, 0x9d, 0x8e, 0xd7, 0x7d, 0x8d, 0xd5, 0x10, 0xd1, 0xc8, 0xd1, 0xbd, 0x39, 0xd9, 0x71, 0x7b, 0x0f, 0x10, 0x7f, 0x9a, 0x35, 0xdb, 0x1f, 0x3e, 0x69, 0x7c, 0x79, 0x6c, 0x5e, 0x2f, 0x5e, 0x97, 0xd4, 0xfc, 0xff, 0x7f, 0xf3, 0x5c, 0x68, 0xa7, 0xd6, 0x3d, 0xce, 0x5c, 0x90, 0x98, 0x61, 0x0e, 0x13, 0xe6, 0x3d, 0xa0, 0x24, 0xae, 0x75, 0x0d, 0x2d, 0xfc, 0xd7, 0x93, 0xae, 0x17, 0xf6, 0x9a, 0x27, 0xb8, 0x78, 0xca, 0xfa, 0x5d, 0x54, 0x10, 0x12, 0xf2, 0x54, 0x71, 0x46, 0xf3, 0xf7, 0x32, 0xe4, 0xb6, 0xb7, 0xab, 0x9a, 0xe5, 0xd4, 0x12, 0xd8, 0x09, 0xdc, 0x35, 0xa1, 0xe9, 0x5e, 0x39, 0x47, 0xe2, 0x9d, 0x79, 0xf7, 0x56, 0x34, 0x73, 0x51, 0x48, 0x3d, 0xce, 0xef, 0x3c, 0xbd, 0xe8, 0xd5, 0xd5, 0x5b, 0x95, 0x64, 0xf0, 0x12, 0x57, 0xac, 0x66, 0xa0, 0x73, 0x43, 0xd9, 0x35, 0x40, 0x0e, 0x98, 0x1d, 0x3e, 0x7b, 0x35, 0xa6, 0xaa, 0xfe, 0xad, 0x7d, 0x5a, 0xe6, 0x10, 0x0b, 0xc8, 0x27, 0x7a, 0x1c, 0xb2, 0x72, 0x78, 0x16, 0xbf, 0x87, 0x3e, 0xc9, 0xe3, 0xf2, 0xea, 0x44, 0x4a, 0x4c, 0x65, 0x5c, 0x2e, 0xe1, 0xfc, 0xb8, 0x69, 0xfe, 0xf2, 0xdd, 0xe6, 0x96, 0x45, 0x84, 0xf8, 0xd2, 0xc2, 0x8b, 0xe3, 0x05, 0x9b, 0x32, 0x59, 0xf8, 0xba, 0xcf, 0x2d, 0xa8, 0x05, 0x26, 0x03, 0x5a, 0x1d, 0x9e, 0xc5, 0x1a, 0xdb, 0xb8, 0x9a, 0x9d, 0xab, 0x05, 0x3a, 0x15, 0x45, 0x20, 0x88, 0xe4, 0xd4, 0x89, 0x3b, 0x2d, 0xf8, 0x91, 0x21, 0x4f, 0xed, 0x30, 0xfd, 0xc7, 0x0c, 0x6b, 0x94, 0xb3, 0xa8, 0x56, 0x9b, 0x05, 0x94, 0x96, 0x4c, 0x06, 0xdf, 0x1b, 0xf6, 0xc8, 0xc8, 0x5c, 0xe6, 0x51, 0x4c, 0x93, 0xf0, 0x92, 0x1b, 0x9f, 0x89, 0xf9, 0xe7, 0x0d, 0xbd, 0x17, 0x8f, 0xc1, 0x27, 0x77, 0x8a, 0xde, 0xb4, 0xb3, 0x2b, 0xe3, 0x6a, 0xcb, 0xe7, 0xf2, 0x42, 0x3f, 0x7c, 0x3d, 0xa6, 0xa5, 0xcf, 0x82, 0x52, 0x99, 0x69, 0xbd, 0xee, 0xf1, 0xbf, 0xcd, 0x64, 0x03, 0x37, 0x64, 0xf3, 0x24, 0x8a, 0x57, 0x49, 0x6c, 0x20, 0xfd, 0x09, 0x69, 0xd8, 0x65, 0x04, 0x7d, 0xbe, 0x35, 0x5a, 0xd3, 0x4b, 0xe2, 0x4e, 0xa7, 0x0c, 0xee, 0x7b, 0xac, 0x7f, 0xb5, 0x02, 0x4f, 0x84, 0x87, 0xf7, 0x17, 0x22, 0xa2, 0x6f, 0x33, 0x94, 0x79, 0xc1, 0xb9, 0xc1, 0x14, 0xd6, 0x7a, 0x11, 0x70, 0xa8, 0xb7, 0x08, 0x3e, 0x7a, 0x36, 0x00, 0x26, 0x9d, 0x82, 0x08, 0x7f, 0x65, 0x81, 0x11, 0x2d, 0xf7, 0x63, 0xa1, 0xf0, 0x2c, 0x66, 0x7e, 0xf3, 0x35, 0xf8, 0x49, 0x5c, 0x17, 0xd0, 0x56, 0xc5, 0xbb, 0x78, 0x1c, 0xe8, 0x77, 0xde, 0xd9, 0x20, 0x82, 0xad, 0xc3, 0x0d, 0x2e, 0x7c, 0xc6, 0x9c, 0x7f, 0x6f, 0xb8, 0xc6, 0x20, 0x6c, 0x93, 0x62, 0x32, 0x1d, 0x1d, 0x79, 0xba, 0xaa, 0x59, 0x44, 0xfb, 0xcd, 0x66, 0xa5, 0x49, 0xae, 0xab, 0x0d, 0xa3, 0xcf, 0x29, 0x8b, 0xb4, 0x3a, 0x90, 0xc0, 0x2e, 0x64, 0x91, 0x78, 0x86, 0xfc, 0x75, 0x81, 0xed, 0xa8, 0x7d, 0x58, 0x65, 0xc4, 0x72, 0xf8, 0x3b, 0x6b, 0xbd, 0xe6, 0x09, 0x3d, 0x63, 0x87, 0x39, 0xbc, 0xf2, 0xbf, 0xe6, 0x75, 0x66, 0x2e, 0x50, 0x06, 0xe5, 0xf7, 0xab, 0x5a, 0xb5, 0x88, 0x91, 0x04, 0x81, 0x7f, 0xb7, 0x7b, 0xfa, 0x37, 0xa6, 0x6b, 0x7d, 0x8e, 0x96, 0x17, 0xcc, 0xf1, 0x9e, 0xb3, 0x5a, 0xd6, 0xf9, 0xa1, 0x70, 0x15, 0x63, 0x22, 0xaf, 0xe2, 0x0f, 0x6d, 0xb3, 0x34, 0x0c, 0x86, 0xb5, 0x48, 0x91, 0x1e, 0x70, 0x63, 0xb3, 0xa6, 0x14, 0x8f, 0xfe, 0x54, 0xaa, 0x5d, 0x26, 0xdb, 0x78, 0x24, 0xc1, 0xfe, 0x2b, 0x83, 0x7e, 0x1f, 0xd1, 0xf5, 0x50, 0x30, 0x1a, 0x3a, 0xf0, 0x3c, 0x58, 0xa5, 0x00, 0x00, 0x01, 0xb7 }; +constexpr AccessUnit M2V_SINGLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, true, 0, {}, { 0xcc, 0xde, 0x79, 0xd3, 0x4e, 0x18, 0x1d, 0xe1, 0xd9, 0xb6, 0x96, 0x08, 0x89, 0x4d, 0x21, 0xc9, 0x3c, 0x69, 0xf0, 0x97 } }; +constexpr AccessUnit M2V_SINGLE_PACK_EXPECTED_AU_2 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0x62, 0xee, 0x39, 0x77, 0xee, 0xb9, 0x4b, 0xa8, 0x02, 0x81, 0xb1, 0x65, 0x11, 0x6b, 0xa8, 0x40, 0xd8, 0x80, 0xff, 0xf3 } }; + +constexpr std::array ATRACX_SINGLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xd0, 0x00, 0x00, 0xf9, 0x59, 0xfb, 0xc5, 0x0f, 0xd0, 0x00, 0xd6, 0xa6, 0x43, 0x65, 0x4e, 0x0f, 0xd0, 0x00, 0xd5, 0x0b, 0xf0, 0x8b, 0x9a, 0x6b, 0x02, 0xf9, 0xe8, 0xc3, 0x80, 0x7f, 0x21, 0xad, 0xe3, 0x55, 0xe4, 0x2b, 0xb3, 0x50, 0xad, 0x7d, 0xb8, 0x7b, 0x69, 0xaa, 0x09, 0xf6, 0x6f, 0xb1, 0x31, 0x57, 0x04, 0x1b, 0x22, 0x91, 0x9a, 0xc0, 0x05, 0xe6, 0x27, 0x6a, 0x65, 0xee, 0xac, 0xc9, 0x56, 0xa3, 0x4e, 0xcf, 0xef, 0xcb, 0xff, 0x5a, 0x4d, 0x31, 0xed, 0x72, 0xa4, 0xb0, 0x26, 0x8a, 0x7e, 0xd6, 0x28, 0x8a, 0x51, 0x77, 0x4a, 0x51, 0x9f, 0x38, 0x7a, 0xa5, 0xac, 0x69, 0xcf, 0x7b, 0x22, 0xbb, 0xc1, 0x0d, 0x1b, 0x7b, 0x50, 0x1e, 0x66, 0xce, 0xc0, 0x98, 0x33, 0x4f, 0x04, 0xcb, 0xd3, 0x44, 0x89, 0x54, 0xe4, 0xb9, 0x0d, 0x05, 0x86, 0xfd, 0x21, 0xf1, 0x5f, 0xda, 0xcb, 0x19, 0x74, 0xb9, 0x90, 0xa8, 0x10, 0xe4, 0xf8, 0xbc, 0x9b, 0x04, 0xc1, 0x67, 0x11, 0x6b, 0x13, 0x82, 0xec, 0x62, 0x48, 0x3b, 0x44, 0xa7, 0xf1, 0x45, 0x54, 0x2f, 0x51, 0x77, 0xfc, 0xa5, 0xed, 0xd3, 0x24, 0x2e, 0xf6, 0xfa, 0x8d, 0xff, 0x06, 0x13, 0x66, 0xda, 0x5f, 0x70, 0x4e, 0x3e, 0xcf, 0xe2, 0xd1, 0x03, 0x8e, 0xa2, 0xec, 0x22, 0x50, 0x81, 0x0f, 0xdf, 0x3f, 0x8e, 0x99, 0x0f, 0xb9, 0x5c, 0x05, 0x32, 0xa9, 0x0a, 0x0d, 0xd1, 0xfb, 0xcb, 0xdf, 0x21, 0x13, 0xf1, 0x35, 0xcc, 0x39, 0xb8, 0xb2, 0x09, 0xc7, 0xa8, 0x01, 0x33, 0xb4, 0x0d, 0x15, 0x59, 0x0f, 0xca, 0x36, 0x3c, 0x64, 0xb7, 0x91, 0x35, 0xe9, 0x0d, 0x40, 0x1a, 0xd3, 0xe0, 0xe9, 0x00, 0xd0, 0x9d, 0x98, 0x0b, 0x1a, 0x54, 0xf6, 0x9a, 0x1b, 0x46, 0x66, 0x87, 0xea, 0x21, 0xc6, 0x30, 0xb3, 0x32, 0xaa, 0xd0, 0xb3, 0x4e, 0x70, 0x36, 0xc5, 0xfd, 0xae, 0x35, 0xb1, 0x65, 0x6c, 0xfd, 0xa8, 0xe4, 0xe6, 0x00, 0x7a, 0x51, 0x0d, 0xcf, 0x99, 0x62, 0x16, 0xc6, 0xde, 0x7f, 0x79, 0xc4, 0xe7, 0x0f, 0xed, 0xc5, 0x6e, 0xaa, 0xd2, 0x98, 0xef, 0xb4, 0x34, 0x76, 0x58, 0x1b, 0x74, 0x62, 0xa4, 0x87, 0x3a, 0x37, 0x45, 0x1f, 0x48, 0xc0, 0xe5, 0xb7, 0xd5, 0x15, 0xe7, 0x4b, 0xc6, 0xef, 0x39, 0x19, 0x49, 0x89, 0x85, 0x41, 0xa3, 0xd3, 0x6f, 0x2f, 0xb1, 0x00, 0x8b, 0xa4, 0xf0, 0xcd, 0x40, 0x73, 0xfa, 0xf8, 0x46, 0x95, 0x75, 0xe0, 0xb1, 0x63, 0x74, 0xf1, 0x5c, 0xd7, 0xf0, 0x38, 0x21, 0xc7, 0xad, 0xa4, 0xa6, 0xbd, 0x47, 0x13, 0xc2, 0x6f, 0x2f, 0xbe, 0x87, 0x05, 0x36, 0x3e, 0x00, 0xe7, 0x8d, 0xb7, 0x00, 0xcd, 0xc1, 0xb7, 0xbc, 0x94, 0x5b, 0x45, 0x8f, 0xf3, 0x05, 0x35, 0xa1, 0xc9, 0x84, 0xde, 0x52, 0x5b, 0xc9, 0xe8, 0xc0, 0x60, 0x14, 0x7f, 0x81, 0xe6, 0xb2, 0xdb, 0x5a, 0xd1, 0xfd, 0x38, 0xe8, 0x3d, 0x63, 0x55, 0x00, 0x0c, 0xae, 0xf0, 0x7f, 0xb2, 0x39, 0x9b, 0xff, 0x25, 0x87, 0xc9, 0x2a, 0x94, 0xf3, 0xfc, 0x11, 0xcd, 0x3f, 0x8b, 0x1a, 0xca, 0x14, 0xe5, 0x26, 0x5f, 0xed, 0xc5, 0xe4, 0x4d, 0xb9, 0x20, 0x04, 0x24, 0x0e, 0xeb, 0x2e, 0x8e, 0x0e, 0x9e, 0xa4, 0xea, 0x1a, 0x92, 0xb3, 0x37, 0xe9, 0x08, 0x39, 0x8c, 0xe0, 0x96, 0x3d, 0x4c, 0xa3, 0xb5, 0xd9, 0xb1, 0x24, 0xea, 0x40, 0xeb, 0x99, 0x21, 0x7d, 0x91, 0xc7, 0xae, 0x36, 0x7a, 0x95, 0xd0, 0x79, 0x65, 0xa8, 0x6f, 0xf7, 0x87, 0xf3, 0x99, 0x95, 0xfd, 0xe6, 0x35, 0x1e, 0xf9, 0x8d, 0xd1, 0xdb, 0x3d, 0x46, 0xe0, 0xd4, 0x5c, 0x94, 0x59, 0x86, 0x34, 0x47, 0xae, 0x63, 0xb0, 0x4c, 0xea, 0xa0, 0x99, 0x03, 0x71, 0x2d, 0xd3, 0x92, 0xea, 0x5a, 0x8e, 0x82, 0x06, 0x27, 0xd7, 0xce, 0xc1, 0x3c, 0xbc, 0xa7, 0xb6, 0x5a, 0xfc, 0x5c, 0x58, 0x1e, 0x5a, 0x66, 0x77, 0xae, 0x20, 0xa7, 0x8e, 0xb9, 0x3a, 0x96, 0xef, 0x1f, 0xfc, 0x6c, 0x89, 0x36, 0xae, 0x75, 0xac, 0xff, 0x40, 0xca, 0x95, 0x5d, 0xa0, 0x61, 0x65, 0x33, 0x3a, 0xae, 0x67, 0x0f, 0x2e, 0x12, 0x69, 0x9f, 0x8c, 0x21, 0x2b, 0x6a, 0x74, 0xe2, 0xbc, 0xf3, 0xfc, 0xe9, 0xa5, 0x19, 0xf7, 0xb5, 0x0f, 0x69, 0x0f, 0x5f, 0x06, 0xc2, 0xac, 0x06, 0x2d, 0xea, 0x1a, 0x49, 0x02, 0x01, 0x50, 0xd1, 0xa7, 0xbf, 0x6a, 0xbf, 0x15, 0x21, 0xcf, 0xf1, 0x21, 0xae, 0x1e, 0x25, 0x48, 0x34, 0x30, 0x77, 0x65, 0xca, 0x91, 0x9d, 0xa1, 0xff, 0xb0, 0xd7, 0xdd, 0x9e, 0xd2, 0x8a, 0x7e, 0x8f, 0x95, 0x16, 0xae, 0x1e, 0x37, 0xc4, 0x43, 0x29, 0x35, 0x4d, 0x8f, 0xe0, 0xbe, 0x0a, 0x42, 0x9c, 0x16, 0x32, 0x65, 0x1a, 0x8e, 0xea, 0xb6, 0xda, 0x90, 0xb3, 0x18, 0x5b, 0x26, 0xcb, 0x95, 0xfd, 0xaf, 0x59, 0x4b, 0x74, 0xc7, 0xbe, 0x26, 0xa2, 0x27, 0x85, 0x64, 0xd7, 0x45, 0xe7, 0x90, 0x4f, 0x25, 0xa3, 0x47, 0x5c, 0xbe, 0x24, 0xa8, 0x20, 0xb9, 0xda, 0x42, 0x4e, 0xea, 0x71, 0x02, 0x9e, 0xb0, 0x64, 0x18, 0x38, 0xb0, 0x53, 0x2f, 0x5c, 0x4f, 0x10, 0x91, 0x31, 0x66, 0xa3, 0x51, 0xa0, 0xf4, 0x75, 0x9f, 0x3f, 0xd6, 0xaf, 0x5e, 0x42, 0xde, 0xd1, 0x7d, 0xa8, 0x46, 0x1d, 0xf8, 0xe6, 0xb8, 0x34, 0x50, 0xc7, 0x8d, 0xa5, 0x56, 0x8a, 0xce, 0xec, 0x03, 0xc8, 0xb1, 0xa1, 0x7e, 0xb6, 0x40, 0xf4, 0x0b, 0x6b, 0x17, 0x4d, 0xbe, 0x87, 0x2a, 0xe4, 0xe5, 0xc0, 0x1a, 0x05, 0x40, 0xe8, 0x3b, 0xbe, 0x97, 0x1d, 0xca, 0xee, 0xfb, 0xc7, 0x8f, 0xf5, 0x96, 0xa4, 0xbb, 0xc7, 0x1e, 0x79, 0x5a, 0x95, 0x6f, 0x12, 0x6d, 0x46, 0x37, 0xa5, 0x9a, 0xbb, 0xe4, 0xb6, 0xbe, 0x82, 0xb0, 0x7f, 0xe5, 0x63, 0xa4, 0xbd, 0x73, 0x41, 0x5c, 0xbf, 0xd5, 0x59, 0x44, 0x83, 0x1a, 0x26, 0xf9, 0xaa, 0x03, 0xa0, 0xaf, 0x72, 0xb1, 0x3a, 0x17, 0x01, 0x80, 0xf1, 0xca, 0x4e, 0x18, 0x00, 0x0c, 0x1b, 0xd9, 0xaa, 0xce, 0x2e, 0xe9, 0x89, 0xf0, 0x42, 0x32, 0x19, 0x4f, 0x74, 0x5f, 0x2f, 0x04, 0x6f, 0xe7, 0x8a, 0x03, 0x81, 0x6f, 0x76, 0x74, 0xc0, 0xfc, 0xba, 0x92, 0x73, 0xbe, 0x95, 0xc5, 0x4b, 0x78, 0xcd, 0x30, 0x7a, 0x0f, 0xca, 0x1c, 0x36, 0x85, 0xfb, 0xc7, 0xd9, 0xc4, 0xb0, 0xd5, 0xb9, 0xf4, 0x23, 0x70, 0x6a, 0x9a, 0x27, 0x86, 0x24, 0xa6, 0xb0, 0x96, 0xfb, 0xaa, 0xe7, 0xf0, 0x5d, 0x80, 0xb6, 0x19, 0x80, 0x01, 0xf5, 0x9c, 0x0f, 0x08, 0x49, 0x82, 0x58, 0x59, 0x16, 0x99, 0xbb, 0x78, 0x65, 0xe0, 0x2c, 0x6c, 0x1c, 0xf3, 0xda, 0x67, 0xb9, 0xf3, 0xb1, 0xa2, 0x19, 0x4f, 0x9b, 0x30, 0x2e, 0xc3, 0xc0, 0x01, 0x90, 0x7d, 0x4e, 0x05, 0xda, 0xde, 0x49, 0x40, 0xe5, 0x53, 0xf1, 0x0b, 0x66, 0x3b, 0x7f, 0x5d, 0xda, 0xcd, 0x28, 0x38, 0xfa, 0x44, 0x3f, 0xe6, 0x27, 0x06, 0x58, 0x27, 0x96, 0xaa, 0xe1, 0x1a, 0xe5, 0x1c, 0xc4, 0xa6, 0xc7, 0xbe, 0x27, 0xd3, 0xb8, 0x53, 0xc6, 0x71, 0xd8, 0x44, 0x2f, 0xe9, 0x6d, 0xae, 0xe8, 0x06, 0x62, 0xad, 0xfc, 0x7d, 0x1e, 0xc0, 0x67, 0x8a, 0xb3, 0x18, 0xd6, 0xb5, 0x14, 0xe7, 0xc9, 0x99, 0xeb, 0x3d, 0xab, 0x1c, 0x4f, 0xda, 0x56, 0xde, 0xba, 0x5b, 0xe6, 0xed, 0x58, 0x22, 0x3a, 0xc9, 0x89, 0xef, 0x4e, 0xf1, 0xaa, 0xdf, 0xae, 0x81, 0xbe, 0x34, 0x29, 0x5e, 0x18, 0x90, 0xe9, 0x7c, 0x89, 0xa4, 0xeb, 0x11, 0x33, 0x39, 0x3b, 0x36, 0x18, 0x00, 0x43, 0xac, 0xa3, 0x02, 0xe6, 0xd9, 0x6b, 0x0d, 0xa9, 0xea, 0x31, 0x6f, 0x66, 0xc8, 0xab, 0x3d, 0x9f, 0x11, 0x8a, 0x17, 0x13, 0x43, 0x13, 0xd2, 0xc1, 0x37, 0x90, 0x9f, 0x57, 0xc7, 0x03, 0xcf, 0x05, 0x9c, 0x74, 0x8d, 0xd5, 0xe1, 0xd9, 0x75, 0xe2, 0x12, 0x04, 0x41, 0x30, 0x24, 0x6e, 0x52, 0xf3, 0xf8, 0xbc, 0xa4, 0xba, 0x39, 0x39, 0xff, 0xea, 0x71, 0x78, 0x9f, 0x38, 0xbe, 0xbd, 0xcd, 0x74, 0xc5, 0x56, 0xb8, 0x35, 0x5c, 0xf5, 0x01, 0x9e, 0xbf, 0x8a, 0xc4, 0x2c, 0x3c, 0x7c, 0xb5, 0x78, 0x36, 0x27, 0xc3, 0x19, 0xb8, 0x37, 0x6e, 0xa2, 0x02, 0xc0, 0x1f, 0x09, 0x6c, 0x2e, 0xf6, 0xcc, 0xcf, 0x05, 0xbf, 0xdb, 0xa3, 0x23, 0xe4, 0x6f, 0xb2, 0x33, 0xd8, 0xc5, 0xa1, 0x83, 0x04, 0x15, 0x45, 0xcc, 0xf9, 0xc5, 0xba, 0x98, 0xc5, 0x82, 0xf5, 0xda, 0x08, 0x64, 0xf0, 0xb9, 0x54, 0x5f, 0x34, 0x2c, 0x23, 0x57, 0x22, 0x81, 0xf5, 0xb7, 0x01, 0xdb, 0x41, 0x29, 0x78, 0xbb, 0x93, 0xe7, 0x08, 0x85, 0x86, 0x84, 0xf1, 0x6d, 0x9e, 0xc4, 0x91, 0x18, 0x74, 0xd8, 0xd3, 0x5f, 0xb5, 0x33, 0x56, 0x76, 0xc1, 0xd2, 0x39, 0x83, 0x29, 0x44, 0xf8, 0x1d, 0xf6, 0xd0, 0xd2, 0x4b, 0x50, 0x01, 0xc0, 0xf4, 0xa2, 0xac, 0x1a, 0x04, 0x77, 0xce, 0xf1, 0x3e, 0x53, 0xea, 0xe0, 0xfb, 0xc8, 0xe6, 0xe0, 0xda, 0x23, 0xa9, 0xcc, 0x88, 0x50, 0x95, 0x5c, 0x0f, 0x48, 0x31, 0x4d, 0x6a, 0x46, 0xc8, 0xad, 0xa3, 0xbe, 0xb0, 0x8a, 0x70, 0x3e, 0x4a, 0x79, 0x77, 0x07, 0xb1, 0xfc, 0x8f, 0xdd, 0x2d, 0x2e, 0x1d, 0x10, 0x33, 0xcf, 0x62, 0x35, 0x55, 0x12, 0x40, 0x50, 0x24, 0xc2, 0x0f, 0xb6, 0x59, 0x88, 0x1e, 0x87, 0xbf, 0x77, 0x1c, 0x78, 0x77, 0x7e, 0xad, 0x8d, 0x43, 0xf4, 0xe4, 0xe4, 0x00, 0x7a, 0x71, 0xff, 0x3b, 0x3f, 0x3e, 0x17, 0x4f, 0x19, 0xa1, 0x4d, 0x6f, 0xa8, 0xec, 0x30, 0x87, 0xd7, 0xff, 0x46, 0x9d, 0x4d, 0x2e, 0xa8, 0x61, 0x45, 0x06, 0x51, 0xd0, 0x7b, 0x2e, 0xbe, 0x5e, 0x7d, 0x45, 0x3e, 0x95, 0x52, 0xff, 0x64, 0xb6, 0x28, 0x7e, 0x2a, 0x7b, 0x33, 0xb4, 0x80, 0x17, 0x57, 0xc6, 0x80, 0xde, 0xfc, 0x3f, 0x25, 0xe4, 0x35, 0x85, 0xd1, 0xce, 0xa1, 0x2a, 0xd7, 0x6e, 0x83, 0x6c, 0x61, 0x5d, 0xa8, 0xaf, 0xa0, 0x1e, 0x15, 0x51, 0x57, 0x93, 0xd0, 0x51, 0x21, 0x68, 0x79, 0x99, 0x25, 0xbe, 0x51, 0xc2, 0xf4, 0x72, 0x5c, 0x05, 0x60, 0x05, 0x81, 0xff, 0x20, 0x81, 0x28, 0xb7, 0x38, 0xb4, 0xb2, 0x1a, 0xdd, 0xe9, 0xab, 0xc7, 0x29, 0x4f, 0x24, 0xef, 0x99, 0xae, 0x0c, 0xcc, 0xe7, 0x24, 0xca, 0xa8, 0x71, 0x15, 0xef, 0x58, 0xb2, 0x58, 0x4b, 0xbe, 0x64, 0xbe, 0x68, 0xcc, 0x9f, 0xbf, 0x31, 0xa5, 0x0c, 0x44, 0x71, 0xec, 0x47, 0xa0, 0xda, 0xa5, 0xde, 0x54, 0x19, 0x30, 0xa9, 0x1c, 0x19, 0xbc, 0x18, 0x65, 0x71, 0x70, 0xbc, 0x53, 0x95, 0xe3, 0x44, 0x3f, 0x02, 0x24, 0x15, 0xc9, 0xe0, 0x7e, 0xb2, 0x29, 0xc9, 0x8b, 0x73, 0x28, 0xab, 0xc5, 0xde, 0x37, 0x0f, 0x44, 0x0d, 0x3e, 0x80, 0x52, 0x44, 0xe2, 0xe2, 0x0d, 0xe2, 0x6e, 0xd5, 0xc6, 0xa0, 0xb5, 0xe9, 0x70, 0x0d, 0xcc, 0x52, 0x1f, 0xc3, 0xaa, 0x64, 0x0e, 0xbe, 0x83, 0x0e, 0xfe, 0xb6, 0xee, 0xe0, 0xee, 0xb9, 0x50, 0x47, 0xe3, 0xa1, 0x8d, 0xff, 0x33, 0x8c, 0x6b, 0x45, 0xfb, 0xa0, 0x2c, 0xb6, 0xae, 0xf3, 0x93, 0xb3, 0xca, 0x27, 0xf2, 0x43, 0x83, 0x15, 0x26, 0xa5, 0xe8, 0x48, 0xc4, 0x91, 0x43, 0x42, 0xa2, 0x59, 0x38, 0xff, 0x55, 0x08, 0x0c, 0xf2, 0x22, 0x56, 0x25, 0xda, 0xf9, 0x33, 0xa9, 0xff, 0x0a, 0xbb, 0x83, 0x68, 0xee, 0xc1, 0xa0, 0xde, 0x2d, 0x75, 0x67, 0x3d, 0xa2, 0x4a, 0xef, 0xf5, 0x05, 0x62, 0x6a, 0x5e, 0x3c, 0x26, 0x86, 0xb6, 0xed, 0x22, 0xea, 0x96, 0xbb, 0x91, 0xa7, 0xee, 0x20, 0x77, 0x3a, 0x59, 0x97, 0x31, 0x47, 0x07, 0xad, 0xcd, 0xe3, 0x96, 0x27, 0x43, 0xab, 0xc2, 0xa4, 0xb7, 0x69, 0xef, 0x25, 0xb1, 0x97, 0xa4, 0x79, 0x17, 0x81, 0xbd, 0x68, 0xb5, 0x97, 0x60, 0x66, 0xb1, 0x97, 0x2b, 0x7a, 0x50, 0xf3, 0xc2, 0x10, 0xec, 0xd4, 0xf1, 0x38, 0x1c, 0xdf, 0xa0, 0x08, 0xb8, 0xb8, 0x9f, 0x46, 0x0e, 0x57, 0x53, 0x34, 0xf8, 0x14, 0xa5, 0xdc, 0x69, 0xef, 0x96, 0xe3, 0x54, 0xcf, 0x13, 0xcb, 0x02, 0xb7, 0x24, 0x77, 0xb5, 0xd6, 0xc0, 0x74, 0x65, 0x65, 0xb4, 0x97, 0x0d, 0x15, 0x2e, 0xa9, 0xc7, 0x24, 0x22, 0x13, 0xf7, 0x50, 0x5f, 0xf5, 0xb1, 0x79, 0xce, 0xf6, 0x15, 0xcf, 0x47, 0x1c, 0x4c, 0x40, 0x61, 0x84, 0x74, 0x23, 0x6e, 0xae, 0xaa, 0x00, 0x3a, 0x34, 0x26, 0x8e, 0x92, 0xa2, 0x55, 0xae, 0x89, 0xc1, 0xbc, 0xb9, 0x13, 0xe1, 0x22, 0x8b, 0x52, 0x62, 0x45, 0xa1, 0x01, 0x4f, 0x97, 0x4e, 0xc7, 0xc3, 0xfd, 0xc9, 0x9e, 0x92, 0x67, 0xbb, 0x21, 0x31, 0x5d, 0x10, 0xbc, 0x3a, 0x27, 0xe8, 0x55, 0x48, 0x46, 0x21, 0x56, 0x8a, 0x0f, 0xd0, 0x62, 0x9a, 0xe1, 0xd0, 0xe1, 0x77, 0x53, 0x0b }; +constexpr AccessUnit ATRACX_SINGLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, false, 0, {}, { 0xca, 0x9d, 0x63, 0xb7, 0xda, 0xcd, 0xd6, 0xb1, 0xa0, 0x27, 0xf2, 0x61, 0xc1, 0x56, 0x3e, 0x3e, 0xb1, 0x1c, 0x5e, 0x76 } }; +constexpr AccessUnit ATRACX_SINGLE_PACK_EXPECTED_AU_2 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0xeb, 0x30, 0x7d, 0xad, 0x5f, 0xcb, 0xc1, 0x17, 0xc1, 0x10, 0x19, 0xef, 0xd5, 0xd3, 0xaa, 0x72, 0x6f, 0xd2, 0x5d, 0x5b } }; + +constexpr std::array AC3_SINGLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x59, 0x66, 0x00, 0x87, 0x05, 0xf4, 0xa8, 0x09, 0x93, 0x66, 0x9e, 0xcf, 0x4e, 0x3e, 0x1e, 0xc6, 0x10, 0x6b, 0xaa, 0x2b, 0x3d, 0x5c, 0xec, 0x4b, 0x2e, 0xd1, 0x47, 0x6f, 0x9c, 0x77, 0x35, 0x7a, 0x6a, 0xcf, 0x95, 0x42, 0x66, 0xca, 0x6c, 0x31, 0xae, 0x53, 0xc3, 0x84, 0xf1, 0x00, 0x1a, 0x5a, 0x11, 0xb4, 0xd0, 0x32, 0x15, 0xaa, 0x97, 0x7e, 0xb8, 0x2f, 0xc6, 0x5e, 0x79, 0xd8, 0xf5, 0x85, 0x8b, 0x2a, 0x4b, 0x8c, 0xfd, 0x0b, 0xb0, 0x5a, 0x05, 0x0b, 0x77, 0x76, 0xd7, 0x69, 0x3c, 0xf2, 0xad, 0x10, 0x4f, 0xc7, 0x20, 0x30, 0x68, 0xb3, 0x72, 0x47, 0x58, 0x13, 0xcb, 0x17, 0x94, 0xe0, 0x13, 0xd8, 0xa2, 0xf7, 0xf2, 0x71, 0xbe, 0xdc, 0x9f, 0x15, 0x23, 0xfd, 0x98, 0x1c, 0x5b, 0x79, 0xca, 0xe0, 0x6f, 0x8e, 0xa2, 0x7a, 0xce, 0xa6, 0xc5, 0x41, 0xd5, 0x30, 0x83, 0x22, 0x0b, 0x77, 0x99, 0x0a, 0x99, 0x5c, 0xaa, 0xc6, 0x49, 0x3c, 0x95, 0x33, 0x0a, 0x9f, 0x5c, 0x73, 0xfb, 0x0e, 0xa9, 0xaf, 0x4e, 0x0d, 0xec, 0xfc, 0xbb, 0x0f, 0x61, 0x9a, 0x9f, 0xa0, 0x5b, 0x14, 0x88, 0x6b, 0x76, 0x43, 0x7a, 0x0d, 0x2d, 0x1b, 0x6d, 0xea, 0x84, 0x13, 0x0f, 0xe2, 0x89, 0xb3, 0xef, 0xe2, 0xe7, 0x2b, 0xbd, 0xde, 0x20, 0x07, 0x61, 0x99, 0xbd, 0x24, 0x94, 0xd5, 0x0c, 0xa8, 0x69, 0x99, 0xb9, 0xf3, 0x7d, 0x9c, 0x5b, 0xbd, 0x76, 0x6f, 0x1f, 0x8b, 0x61, 0x20, 0x83, 0x53, 0xb0, 0xd5, 0x86, 0xd9, 0xd9, 0xcf, 0x49, 0x4a, 0x3a, 0xd6, 0x51, 0xf6, 0x09, 0x4a, 0x09, 0x1b, 0xc5, 0xce, 0xbf, 0x35, 0xfa, 0x93, 0x75, 0x5f, 0x7c, 0x04, 0xc3, 0xd2, 0x17, 0xd8, 0x76, 0x0d, 0xc0, 0x86, 0xc3, 0x7e, 0xfc, 0x7c, 0x2a, 0xe3, 0xc9, 0x13, 0x74, 0xec, 0x5b, 0x8d, 0x9b, 0x45, 0x6b, 0x12, 0xa5, 0x1f, 0xeb, 0xf5, 0x75, 0x24, 0x22, 0xa8, 0xbf, 0xc6, 0xb8, 0x35, 0xe5, 0x95, 0x69, 0xaf, 0xfc, 0xc1, 0xe6, 0x49, 0x01, 0x0d, 0x4b, 0xc8, 0xf0, 0x02, 0x9e, 0x4a, 0xb2, 0x43, 0xb4, 0x4f, 0xb2, 0xd6, 0x6a, 0x38, 0xfe, 0xe8, 0x80, 0x6e, 0x73, 0x51, 0x2f, 0xd4, 0x9e, 0x56, 0xd8, 0xa0, 0x40, 0x9b, 0x65, 0xb5, 0xc2, 0x04, 0x86, 0x15, 0x92, 0xe0, 0x7a, 0x26, 0x0c, 0xc9, 0x6a, 0x27, 0xd9, 0xfc, 0x4e, 0x50, 0xc5, 0xac, 0xe5, 0x45, 0xec, 0x2a, 0x13, 0x95, 0xaf, 0xf3, 0x81, 0xe1, 0x5b, 0xc7, 0x87, 0x50, 0x67, 0xdb, 0xc2, 0x79, 0xd5, 0x2e, 0xdd, 0x42, 0x51, 0x3b, 0x14, 0x1d, 0x83, 0x3c, 0x25, 0x4b, 0xe7, 0x77, 0x48, 0xa8, 0xed, 0x1d, 0x55, 0x75, 0xf1, 0x65, 0xca, 0x6d, 0x5a, 0x13, 0x7c, 0x9b, 0xdb, 0x95, 0x7d, 0xf3, 0x42, 0xcf, 0x49, 0x17, 0xe5, 0x07, 0xbb, 0x7d, 0xf0, 0x6f, 0x9d, 0xcb, 0x9e, 0x07, 0x11, 0x5a, 0x51, 0x9b, 0x80, 0x5e, 0x77, 0xc6, 0x3c, 0xa8, 0xa5, 0x22, 0xa8, 0xb2, 0x83, 0x97, 0x8a, 0xdf, 0x75, 0x49, 0x43, 0x59, 0x5d, 0x5c, 0x32, 0x14, 0x6c, 0xc3, 0xb4, 0xf5, 0x25, 0xe7, 0xdf, 0xb0, 0xb3, 0xc6, 0xb7, 0x1f, 0x3f, 0x7c, 0xfd, 0xe4, 0xf5, 0x0c, 0xbe, 0x92, 0xcb, 0xb6, 0x72, 0xa0, 0x8a, 0x9f, 0x3b, 0xfd, 0x76, 0x5d, 0xb2, 0xdf, 0xee, 0x21, 0x19, 0xd4, 0x5a, 0x05, 0x6a, 0xb9, 0x9f, 0xdb, 0x9c, 0xe1, 0xc0, 0xd4, 0x14, 0xb4, 0x5e, 0x4d, 0x2e, 0x3d, 0xca, 0x73, 0xc6, 0x01, 0xd6, 0x73, 0x01, 0x24, 0xef, 0x2b, 0x6c, 0xfc, 0xa0, 0xea, 0xb4, 0x54, 0x57, 0x70, 0xfb, 0x31, 0x0d, 0xc9, 0xcb, 0xef, 0x49, 0x64, 0x2e, 0x9b, 0xea, 0x6d, 0x68, 0x09, 0x5c, 0x86, 0xb8, 0xc5, 0x03, 0xd5, 0x34, 0x62, 0x9f, 0xf7, 0x38, 0x0d, 0x2b, 0x9f, 0x54, 0x90, 0x14, 0x48, 0xd9, 0xaf, 0xa7, 0x9e, 0x20, 0x4e, 0x4a, 0x68, 0x60, 0x95, 0x7b, 0x53, 0x96, 0x9e, 0x05, 0x70, 0x7c, 0xbc, 0xe8, 0x95, 0xfd, 0xa7, 0x16, 0x1e, 0x98, 0x5d, 0x45, 0xda, 0x3b, 0x93, 0x21, 0xc3, 0xc3, 0x22, 0xe6, 0x55, 0x99, 0xcb, 0x9a, 0x57, 0xcf, 0x34, 0x5d, 0x52, 0xf8, 0xce, 0x28, 0xe6, 0xcf, 0xac, 0x4e, 0x34, 0x82, 0x81, 0x1f, 0x99, 0x63, 0xcc, 0xcc, 0x07, 0x5d, 0xcc, 0x7e, 0x7b, 0x33, 0x5e, 0xba, 0x7d, 0xc3, 0xa7, 0xc7, 0x4b, 0x23, 0xb4, 0x64, 0x80, 0x6e, 0x5b, 0x79, 0x0d, 0xc0, 0x54, 0x03, 0x96, 0x5d, 0x63, 0xe7, 0x4b, 0xd9, 0x86, 0x65, 0xcf, 0xfb, 0x3e, 0x29, 0xdf, 0x3c, 0xd4, 0xcf, 0xdd, 0x96, 0x4a, 0x27, 0x73, 0x69, 0xd2, 0x40, 0xb6, 0xb7, 0xa5, 0xe0, 0x56, 0x37, 0x1f, 0xc2, 0x95, 0xe2, 0xb8, 0x4d, 0x42, 0x22, 0xc1, 0xbd, 0xd2, 0x57, 0x11, 0x27, 0x49, 0x6a, 0x88, 0x3d, 0x8e, 0x09, 0x87, 0x87, 0xd0, 0xf2, 0xf6, 0x05, 0xac, 0x47, 0xab, 0xf7, 0x5c, 0xa0, 0xa1, 0xb0, 0x0a, 0x74, 0x01, 0xd5, 0xeb, 0x04, 0xa6, 0x53, 0x2e, 0x8f, 0xc3, 0x68, 0xe1, 0x0a, 0x59, 0xcb, 0xee, 0xf3, 0xde, 0x94, 0x35, 0xe8, 0x78, 0x44, 0x73, 0x87, 0x0b, 0xe2, 0x87, 0xa3, 0xd1, 0xa2, 0x87, 0x8d, 0x64, 0x3a, 0x53, 0xd8, 0x09, 0xba, 0xae, 0x04, 0x7e, 0x07, 0xc2, 0x07, 0x98, 0xc0, 0x87, 0x56, 0x12, 0x86, 0x14, 0xdb, 0x4c, 0xc4, 0xed, 0x84, 0xad, 0xca, 0xf3, 0x2e, 0x1a, 0x28, 0x64, 0x76, 0x0c, 0x4c, 0x27, 0x9d, 0xab, 0x12, 0x7d, 0xb3, 0xfa, 0xed, 0x5c, 0xf0, 0x77, 0xdc, 0xf3, 0x45, 0x5d, 0x31, 0x27, 0x5f, 0x4c, 0xe2, 0x96, 0x98, 0x25, 0xab, 0xcd, 0x3d, 0x22, 0x64, 0xc6, 0x00, 0x47, 0xbd, 0xb8, 0xde, 0x65, 0xdb, 0x54, 0xd5, 0xc6, 0x62, 0x1b, 0x45, 0x6b, 0xd8, 0x9d, 0x00, 0xb7, 0xe3, 0xfc, 0x84, 0x03, 0xf0, 0x6c, 0xf8, 0x23, 0x14, 0xf6, 0x92, 0x73, 0xc6, 0x42, 0xcc, 0x67, 0xd3, 0x3e, 0xaa, 0xc4, 0x3d, 0x81, 0x13, 0xfb, 0x13, 0x22, 0xd3, 0x9b, 0xc8, 0xf5, 0x0b, 0x78, 0x47, 0x9b, 0x83, 0xb5, 0xd7, 0x98, 0xe9, 0x3f, 0x7c, 0x8f, 0x42, 0xa3, 0xef, 0x1f, 0x94, 0x99, 0x75, 0x21, 0x95, 0xd1, 0xcf, 0x74, 0xfd, 0x5f, 0x80, 0xec, 0x40, 0x22, 0x28, 0x92, 0x9e, 0x65, 0x73, 0x8b, 0xc5, 0x30, 0xe5, 0xb0, 0xb6, 0xf2, 0x03, 0x24, 0x00, 0x2e, 0x6a, 0xa3, 0x74, 0x62, 0xad, 0xbe, 0x11, 0x80, 0x61, 0xcf, 0x23, 0x3f, 0xd8, 0x0a, 0xf0, 0x2d, 0x4a, 0x58, 0x49, 0xf4, 0x63, 0xf3, 0x05, 0x9b, 0x78, 0xc7, 0xf7, 0xb7, 0x31, 0x02, 0x16, 0x63, 0x87, 0x08, 0x3b, 0x7b, 0x7b, 0xd1, 0xb1, 0xb8, 0x99, 0x39, 0xfe, 0x71, 0x49, 0xac, 0xb7, 0x79, 0x84, 0x47, 0x15, 0x74, 0x5e, 0x29, 0x01, 0xb7, 0x93, 0x9a, 0x87, 0xbd, 0x1a, 0x59, 0xff, 0xec, 0xe5, 0xb8, 0xb9, 0x30, 0x8c, 0x29, 0xd6, 0x0d, 0x32, 0x22, 0x28, 0xd4, 0x4d, 0x25, 0x06, 0x9f, 0x6a, 0xf7, 0x92, 0x16, 0x4b, 0xb6, 0x17, 0xbc, 0xa7, 0xca, 0x17, 0xa6, 0x76, 0x48, 0x46, 0x51, 0xd9, 0xed, 0xc1, 0x69, 0x60, 0xf4, 0x00, 0x64, 0x17, 0x5c, 0x76, 0x18, 0xc4, 0xd1, 0x74, 0x06, 0x67, 0x34, 0xa5, 0xd4, 0x77, 0x1c, 0x62, 0x6d, 0xbb, 0x76, 0x5d, 0x49, 0x5b, 0x08, 0x60, 0x5a, 0x29, 0x4c, 0xec, 0x92, 0x99, 0x55, 0xc0, 0xcc, 0x64, 0xdc, 0x4c, 0x22, 0x37, 0x3d, 0xdb, 0x3b, 0xb0, 0x3f, 0x19, 0x95, 0x6a, 0x04, 0x47, 0x09, 0xf4, 0xd7, 0xe4, 0xf7, 0x28, 0x4f, 0xef, 0xf7, 0x30, 0x37, 0x28, 0xa6, 0xdc, 0x82, 0xfe, 0x2c, 0x7c, 0x67, 0xf8, 0x73, 0x0a, 0x0d, 0x5c, 0xaa, 0x4e, 0x09, 0x0b, 0x47, 0x45, 0x07, 0x44, 0x49, 0x21, 0xe8, 0x12, 0xd1, 0x3e, 0x29, 0x6d, 0xe5, 0x2f, 0xe6, 0x52, 0xd6, 0xa2, 0x76, 0x0f, 0x8c, 0xbb, 0x87, 0xd4, 0x72, 0x49, 0xba, 0xa4, 0x1b, 0x9b, 0x52, 0xe2, 0xa0, 0x82, 0x72, 0x8d, 0x4a, 0x25, 0xf2, 0xe5, 0xb7, 0x59, 0xff, 0x47, 0x69, 0xb5, 0x26, 0x8b, 0x35, 0x76, 0xd7, 0xd3, 0xf4, 0xab, 0x8d, 0x4b, 0x5b, 0x3d, 0xda, 0xec, 0xb6, 0xb8, 0x9c, 0x4a, 0xd1, 0x2d, 0x9d, 0x0c, 0x39, 0x89, 0x58, 0xa6, 0x37, 0x96, 0x2e, 0x7e, 0x21, 0xe4, 0x05, 0xad, 0xdd, 0xf7, 0x5c, 0xf0, 0x7d, 0x79, 0x0a, 0x66, 0x11, 0xcf, 0x46, 0x8a, 0x28, 0xbc, 0x62, 0xbe, 0x4b, 0xf4, 0xb3, 0x4f, 0xba, 0x9c, 0xf7, 0xc2, 0xc4, 0xa0, 0xb3, 0x0e, 0x5c, 0x30, 0x25, 0x3e, 0x94, 0x03, 0x73, 0x81, 0x98, 0xfa, 0x2e, 0xa6, 0xad, 0x6b, 0xf5, 0x6a, 0x08, 0xf9, 0x75, 0xde, 0xb4, 0xdc, 0xf9, 0xad, 0xb2, 0x31, 0xe5, 0x9f, 0xba, 0x86, 0xb6, 0x59, 0x4e, 0xae, 0xcf, 0xf9, 0xef, 0xf9, 0x16, 0xc3, 0x5f, 0xce, 0x90, 0xbe, 0xce, 0xd0, 0xf3, 0x49, 0x1f, 0x44, 0xae, 0xce, 0x5d, 0x8e, 0xcd, 0x12, 0x07, 0x19, 0xf9, 0x0a, 0x3e, 0x29, 0xbb, 0x97, 0x8d, 0x56, 0x48, 0x69, 0x62, 0xf7, 0x68, 0xe8, 0xbc, 0x41, 0x90, 0xd6, 0x26, 0x26, 0xa5, 0x0d, 0xd9, 0x4c, 0x52, 0x0c, 0x4f, 0x20, 0xc7, 0x6e, 0x62, 0xfd, 0x91, 0xc2, 0x57, 0xef, 0x8e, 0x43, 0xc1, 0x48, 0x82, 0x73, 0x16, 0xed, 0x1e, 0x10, 0x66, 0x49, 0xda, 0xaf, 0x83, 0xc1, 0x48, 0xb4, 0x41, 0xd5, 0xa5, 0x9e, 0xa1, 0x44, 0x41, 0x75, 0x5c, 0x50, 0xd0, 0x7a, 0x5a, 0x0d, 0xd2, 0x32, 0xe8, 0x0c, 0x88, 0x4c, 0xb8, 0x72, 0x07, 0xbf, 0xe2, 0x85, 0xef, 0xb4, 0x20, 0xb5, 0x24, 0x00, 0x9d, 0x23, 0xca, 0x6e, 0x53, 0xf8, 0x62, 0xe4, 0x21, 0xbe, 0x79, 0x21, 0x8e, 0x5d, 0xde, 0x91, 0x0b, 0x8a, 0xcd, 0x0c, 0x89, 0xa0, 0x5b, 0xd5, 0xbe, 0x91, 0xdb, 0xa1, 0x71, 0x79, 0xfb, 0xbf, 0xc8, 0x22, 0x19, 0x7e, 0x66, 0x13, 0x9c, 0x94, 0xf2, 0xbc, 0xc1, 0x1a, 0x8a, 0xf6, 0xc2, 0x62, 0xcd, 0x11, 0xda, 0xc0, 0xa5, 0x34, 0x6c, 0x0c, 0x24, 0x60, 0xda, 0x5c, 0x71, 0x0d, 0x77, 0xbc, 0x0e, 0x59, 0xb7, 0xea, 0xa7, 0x81, 0x32, 0x27, 0xf8, 0x9c, 0xdf, 0x75, 0x41, 0x59, 0xdf, 0xb2, 0x56, 0x13, 0x93, 0x22, 0x2c, 0x30, 0x27, 0xc6, 0xa3, 0x29, 0x34, 0x17, 0xf1, 0xe3, 0xc1, 0xa0, 0x38, 0x53, 0x0c, 0xe5, 0x2e, 0xaf, 0x4e, 0x8f, 0xca, 0x9b, 0x9c, 0xe1, 0xc6, 0x6f, 0x9e, 0x8c, 0x91, 0xa2, 0xc9, 0x39, 0xa9, 0x20, 0xd3, 0x61, 0x4e, 0x7c, 0x33, 0xc8, 0x6f, 0x13, 0xbd, 0x74, 0xdd, 0xcd, 0xca, 0xa7, 0x0b, 0x00, 0x74, 0xa2, 0xbe, 0x55, 0x6e, 0xe8, 0x72, 0x57, 0x9c, 0x30, 0x19, 0xe0, 0x4a, 0xb2, 0xa3, 0xdd, 0xb9, 0xb9, 0x6a, 0x30, 0xea, 0xc3, 0xd4, 0xb8, 0x8b, 0x32, 0xdf, 0x5c, 0x04, 0x8c, 0x82, 0x0a, 0x19, 0x9a, 0x78, 0xa9, 0xd2, 0xc2, 0x3c, 0xaf, 0xcd, 0x5d, 0xc7, 0x9a, 0x9f, 0x51, 0x8b, 0x72, 0xee, 0x26, 0xe7, 0xba, 0x24, 0xfb, 0xef, 0xf2, 0x9f, 0x73, 0x51, 0xb3, 0x6a, 0xe6, 0x1f, 0x26, 0xa6, 0x93, 0xde, 0xe3, 0xaf, 0x06, 0x96, 0x25, 0x61, 0x98, 0x9d, 0xc8, 0x0e, 0x5b, 0xb3, 0x3f, 0x60, 0xb3, 0xdf, 0xe2, 0xfb, 0xd7, 0x11, 0x52, 0x40, 0xdd, 0x90, 0x93, 0xbd, 0xbf, 0xd5, 0x76, 0x40, 0x27, 0xb2, 0xe7, 0xe8, 0x16, 0xfa, 0x89, 0xca, 0xdf, 0xfa, 0xbd, 0x5a, 0x38, 0xba, 0x58, 0x5a, 0xd8, 0xc4, 0xd5, 0x53, 0x2c, 0x5a, 0x2d, 0x65, 0xe7, 0xce, 0x8d, 0xe1, 0x9b, 0x29, 0x2f, 0xa4, 0x70, 0xa2, 0x3c, 0xa3, 0x69, 0x89, 0x11, 0x01, 0x90, 0x6f, 0xa2, 0x3d, 0x0e, 0xac, 0xeb, 0x6e, 0x2a, 0x5e, 0x19, 0x28, 0x21, 0x95, 0xa1, 0xf1, 0x63, 0x16, 0x68, 0x5a, 0x2f, 0xf0, 0xc4, 0xe2, 0xca, 0xc8, 0xe3, 0xee, 0xcd, 0x23, 0x19, 0xf9, 0x38, 0x90, 0x6f, 0x8c, 0x23, 0x8f, 0x71, 0x8b, 0x3e, 0xa9, 0xee, 0x0e, 0x37, 0xca, 0xbb, 0x44, 0x06, 0xbe, 0x07, 0x81, 0xd0, 0x22, 0x1f, 0x3c, 0x20, 0x02, 0x34, 0x16, 0xd8, 0x28, 0x99, 0x46, 0x3f, 0x0a, 0xbe, 0x24, 0x30, 0xb3, 0xba, 0x8b, 0xfa, 0x11, 0xed, 0x47, 0xe2, 0xf5, 0x36, 0x27, 0x7d, 0x9b, 0x36, 0xb3, 0x33, 0x62, 0x1c, 0x4d, 0xd4, 0x38, 0x30, 0x0b, 0xb4, 0x92, 0xe4, 0x1e, 0xf4, 0xd2, 0x17, 0x3d, 0x36, 0xba, 0x28, 0x39, 0x0b, 0x77, 0x93, 0xb8, 0x91, 0x4f, 0x44, 0xb3, 0x3d, 0xef, 0x72, 0xdc, 0x39, 0xd3, 0x90, 0x9a, 0x27, 0xcd, 0xdb, 0x47, 0xde, 0xe0, 0x5e, 0x14, 0x76, 0x81, 0x09, 0xd6, 0x00, 0x37, 0xb8, 0x97, 0x1b, 0xc1, 0x0f, 0xbe, 0x0d, 0xcd, 0xe6, 0x33, 0x3a, 0x52, 0xe9, 0x40, 0x8e, 0x18, 0xfa, 0xbe, 0xd6, 0xd7, 0x6a, 0xa9, 0x49, 0xbd, 0x49, 0xe9, 0x0d, 0x57, 0xee, 0xb0, 0x31, 0xa1, 0x18, 0xbb, 0xb8, 0x4f, 0x31, 0xc5, 0x63, 0xa5, 0x74, 0xb4, 0x26, 0x67, 0x67, 0x70, 0x66, 0x39, 0x2b, 0x96, 0xde, 0x26 }; +constexpr AccessUnit AC3_SINGLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, false, 0, {}, { 0xe4, 0x34, 0x28, 0xe9, 0x7e, 0x1b, 0xa0, 0xea, 0x42, 0x2c, 0xd7, 0x21, 0x26, 0x44, 0x70, 0x9f, 0x0e, 0x3d, 0x4f, 0x23 } }; +constexpr AccessUnit AC3_SINGLE_PACK_EXPECTED_AU_2 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0xf9, 0x27, 0xd1, 0x79, 0x4b, 0x94, 0xd7, 0x63, 0x4c, 0x57, 0x74, 0x46, 0xa3, 0xc2, 0x9d, 0xf0, 0x99, 0x18, 0x78, 0x3c } }; + +constexpr std::array LPCM_SINGLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x40, 0xb1, 0x40, 0x00, 0xda, 0x2c, 0xae, 0xd8, 0xf8, 0x31, 0x4d, 0x58, 0x96, 0x3f, 0x03, 0x7e, 0x2e, 0x5d, 0xb9, 0x16, 0xf9, 0x14, 0x26, 0x07, 0x3f, 0x85, 0xc1, 0x78, 0xcb, 0xf1, 0x73, 0xc1, 0xd6, 0xc2, 0x80, 0x8d, 0xaf, 0xa9, 0x76, 0xb1, 0xb2, 0x83, 0xcf, 0xce, 0x89, 0x25, 0xe4, 0x78, 0xe7, 0x06, 0x10, 0x18, 0xdd, 0x52, 0xf6, 0x17, 0x36, 0xe0, 0xba, 0x91, 0xc5, 0x0b, 0x07, 0xca, 0x35, 0x6b, 0xe0, 0x1c, 0xbf, 0xdf, 0x36, 0xfb, 0xca, 0x06, 0x73, 0x67, 0x8d, 0xcf, 0xb0, 0xf8, 0x8d, 0x34, 0xef, 0x73, 0x07, 0x1b, 0x74, 0x31, 0x13, 0xf4, 0x5a, 0x45, 0x77, 0xf9, 0xc2, 0x7e, 0x0c, 0x9b, 0xb1, 0x3a, 0xf9, 0x48, 0xbb, 0x8e, 0xda, 0xc6, 0x9f, 0x68, 0xfd, 0x4a, 0x39, 0xcd, 0x81, 0xa1, 0x4e, 0xcc, 0x6e, 0x2a, 0x49, 0x32, 0xc4, 0xb2, 0xee, 0xb9, 0xcd, 0x5d, 0x92, 0x07, 0x53, 0x9e, 0xca, 0x7f, 0xfc, 0x6c, 0xe0, 0xa5, 0xc8, 0xed, 0xba, 0x5d, 0x35, 0xc9, 0x4d, 0x1a, 0x86, 0x1b, 0xaf, 0xfc, 0x44, 0xb4, 0xec, 0xf8, 0xeb, 0xdc, 0x22, 0x08, 0x5f, 0x29, 0x85, 0x81, 0xdb, 0xe9, 0x8f, 0x4e, 0xf2, 0xb9, 0xd9, 0x09, 0x58, 0x11, 0xd4, 0xfa, 0x2a, 0x01, 0x5d, 0xf1, 0x07, 0x6f, 0xe8, 0x75, 0x66, 0x39, 0xac, 0x1b, 0x28, 0x57, 0x6b, 0xd3, 0xd4, 0x06, 0xee, 0xd0, 0x86, 0xde, 0xc4, 0x88, 0x99, 0x62, 0x52, 0x37, 0xd9, 0xed, 0x44, 0x3d, 0xc7, 0xa4, 0x2c, 0x2f, 0x96, 0xed, 0xfd, 0xba, 0xd8, 0xd1, 0x72, 0xf6, 0xbb, 0xf3, 0x5f, 0x3b, 0x3e, 0xc3, 0x2c, 0x23, 0xca, 0xb2, 0x72, 0x69, 0xb7, 0x14, 0xcc, 0x81, 0x1a, 0x24, 0x9b, 0x95, 0xd4, 0xc7, 0x97, 0x9f, 0xd3, 0x5b, 0x22, 0xa6, 0x57, 0x63, 0xec, 0x27, 0x20, 0xc4, 0x76, 0x6e, 0xd9, 0xf0, 0x56, 0xaa, 0x0d, 0x54, 0xb7, 0x49, 0x39, 0xf2, 0x19, 0x7d, 0x56, 0xa2, 0x70, 0x7e, 0xce, 0x89, 0x46, 0x08, 0xb2, 0x7f, 0xba, 0x73, 0x9a, 0x2d, 0x11, 0xdd, 0x74, 0x90, 0x70, 0x36, 0xd6, 0x97, 0x62, 0xf0, 0xf7, 0x39, 0x99, 0xf6, 0x50, 0xb2, 0xd4, 0xff, 0x42, 0x8e, 0xbe, 0xf1, 0x57, 0x0e, 0xc8, 0x29, 0x6f, 0x16, 0xa7, 0x86, 0xdb, 0xd9, 0x81, 0x43, 0x9b, 0x95, 0x24, 0xbd, 0x9a, 0x0c, 0x54, 0x2e, 0x71, 0x72, 0x42, 0xe6, 0x25, 0x41, 0xa9, 0x6e, 0x1c, 0x2e, 0x74, 0xb1, 0xe0, 0x4e, 0x66, 0xc3, 0xda, 0xb6, 0x16, 0xb2, 0x84, 0xc5, 0x1f, 0x1f, 0x65, 0xf2, 0xc8, 0x3d, 0x9c, 0xb4, 0x28, 0xed, 0xd3, 0xe2, 0xa2, 0xf8, 0x24, 0xd8, 0x84, 0x16, 0x9f, 0x72, 0x57, 0xa0, 0xfd, 0x28, 0x81, 0x97, 0xb3, 0x7b, 0x5d, 0x29, 0x37, 0xa3, 0x81, 0x2a, 0x5f, 0x45, 0x6b, 0xd3, 0xbd, 0x13, 0x78, 0x4a, 0x63, 0x9f, 0x7a, 0x34, 0x3c, 0xf4, 0x45, 0x7f, 0x51, 0xe9, 0x58, 0xdc, 0xcb, 0x39, 0x38, 0x87, 0xab, 0xb4, 0x0d, 0xaf, 0x4d, 0x63, 0x4b, 0x0f, 0x2e, 0xac, 0xc6, 0xd6, 0x04, 0x1b, 0x3a, 0xda, 0x9d, 0x3b, 0x20, 0x90, 0x3b, 0x19, 0x4f, 0x6e, 0x82, 0x93, 0x85, 0x53, 0x18, 0xf7, 0xa6, 0x1a, 0x2f, 0x98, 0xc8, 0x1f, 0xe3, 0x59, 0x5b, 0x43, 0x76, 0xc4, 0xd4, 0x37, 0xef, 0x0e, 0xce, 0xa3, 0x35, 0xc9, 0x4d, 0x43, 0x13, 0x24, 0xe3, 0x6c, 0x5a, 0xc8, 0x8d, 0xb2, 0x68, 0x5d, 0xc0, 0xd2, 0xd0, 0x06, 0x1d, 0x77, 0x80, 0x65, 0xcd, 0x0a, 0xc0, 0x93, 0xb1, 0x8c, 0xf8, 0x1b, 0xf6, 0x8c, 0x59, 0xf5, 0x99, 0x7f, 0xec, 0x34, 0x0e, 0x16, 0x4e, 0x6a, 0x0c, 0x86, 0xba, 0x1b, 0xd8, 0x53, 0x66, 0x1e, 0xdc, 0x66, 0x8d, 0x91, 0x1b, 0x2b, 0xb4, 0xdd, 0xe2, 0x54, 0x1e, 0xea, 0xc2, 0x5b, 0x6b, 0x56, 0x96, 0x8c, 0xfb, 0xf8, 0x21, 0x22, 0x8f, 0x1e, 0xbb, 0xd7, 0xe7, 0x7b, 0xd7, 0xbf, 0x0c, 0xbb, 0x9e, 0xe2, 0x6c, 0x31, 0x3a, 0xaa, 0xcd, 0xd3, 0xfb, 0xcb, 0x6e, 0xc6, 0x82, 0x7f, 0x01, 0xda, 0x0e, 0x0b, 0xc8, 0x21, 0xc4, 0x6c, 0x2b, 0xe3, 0x88, 0xcd, 0x0c, 0xb3, 0x3d, 0x89, 0xf6, 0xd9, 0x75, 0x16, 0xf8, 0x0e, 0x6d, 0xee, 0x5a, 0xd6, 0xaf, 0xe1, 0x63, 0xe0, 0x77, 0x55, 0x78, 0x39, 0xf9, 0xc4, 0xa2, 0xa7, 0x96, 0x34, 0x17, 0xe0, 0x4f, 0x79, 0xcc, 0x0c, 0x3f, 0x67, 0x31, 0x55, 0xc9, 0xe0, 0x3b, 0x2c, 0x45, 0xaf, 0xbb, 0xbc, 0x73, 0x7c, 0x13, 0xe6, 0x0a, 0x5b, 0x87, 0xd5, 0x91, 0x35, 0xbc, 0x63, 0xd2, 0x31, 0x41, 0x4f, 0x17, 0xb2, 0xf3, 0x44, 0x33, 0xbf, 0x41, 0x7f, 0x49, 0x2f, 0xab, 0x38, 0xd4, 0x9e, 0xf8, 0x9d, 0xb7, 0xe4, 0x97, 0x0f, 0x0e, 0xec, 0xa3, 0xe8, 0xda, 0x9a, 0x4f, 0x23, 0xab, 0x2f, 0xa8, 0x0f, 0x7e, 0x17, 0xed, 0x56, 0xc8, 0x69, 0x5b, 0xe6, 0xc1, 0xbd, 0x01, 0x19, 0xe5, 0xc3, 0xa6, 0x96, 0x91, 0x84, 0x36, 0x9e, 0xb1, 0x25, 0x72, 0xc5, 0x60, 0x3a, 0xcb, 0x77, 0xf9, 0x9b, 0x70, 0x78, 0x6a, 0x67, 0xd4, 0x9e, 0x7b, 0x48, 0x59, 0xa9, 0x6a, 0xed, 0x72, 0x70, 0x22, 0x5a, 0xfe, 0x07, 0x66, 0x1a, 0xb2, 0x3a, 0x7a, 0x1e, 0xcf, 0x3c, 0xc8, 0x94, 0xe0, 0x97, 0xcb, 0xba, 0x4b, 0x1c, 0xb9, 0xab, 0x5f, 0xa5, 0xeb, 0xf7, 0x25, 0x94, 0xce, 0x66, 0x5a, 0xf3, 0xdb, 0xf7, 0x3e, 0xca, 0xb2, 0x64, 0xf2, 0x04, 0xb3, 0xc5, 0x44, 0xf1, 0x6d, 0x2e, 0xfe, 0x16, 0xfa, 0xcc, 0x49, 0x6a, 0x61, 0x6f, 0x27, 0x4f, 0x0b, 0x85, 0x92, 0x17, 0xcc, 0x07, 0xe1, 0x93, 0x9e, 0x5f, 0x34, 0x6f, 0x9d, 0x65, 0xae, 0x03, 0x6a, 0x55, 0xa5, 0x45, 0x9f, 0x41, 0x0f, 0x29, 0xee, 0x87, 0xf5, 0x2d, 0x1e, 0xf3, 0x78, 0xa7, 0x4b, 0xb7, 0x80, 0x62, 0x5f, 0x90, 0x22, 0x80, 0x22, 0x44, 0xaa, 0xb2, 0x41, 0x33, 0xcd, 0x7c, 0xa7, 0x2b, 0x44, 0x5c, 0x3d, 0x54, 0x25, 0xfd, 0x6e, 0x47, 0xcb, 0x81, 0x46, 0x77, 0x8b, 0x75, 0x84, 0xe6, 0x94, 0x58, 0xa3, 0xd9, 0xbe, 0x54, 0x11, 0x25, 0xd7, 0x43, 0x53, 0xba, 0x43, 0x85, 0xfd, 0xec, 0x5b, 0x59, 0xe0, 0x03, 0x0e, 0x57, 0x80, 0x97, 0x7a, 0x0f, 0xf3, 0x17, 0x27, 0x50, 0x88, 0x72, 0x85, 0xb0, 0x14, 0x9d, 0x28, 0xec, 0x09, 0xb1, 0xb4, 0x56, 0x9b, 0x36, 0x4f, 0xa9, 0x8f, 0xb9, 0x66, 0x1f, 0xac, 0xea, 0xc3, 0x9f, 0xfe, 0x5a, 0xb8, 0xe8, 0xde, 0x63, 0xeb, 0x6f, 0xb8, 0x26, 0xdf, 0x42, 0x4b, 0x46, 0x94, 0xe0, 0xdb, 0x61, 0xae, 0x0a, 0x6f, 0xd6, 0xbf, 0xb3, 0x3b, 0x13, 0x82, 0xd6, 0x3e, 0xaf, 0x18, 0x2e, 0x4b, 0x03, 0xca, 0xf9, 0xa8, 0x83, 0x61, 0x2f, 0x04, 0x0b, 0x6e, 0xf0, 0x0e, 0x8c, 0xc2, 0x19, 0x9b, 0x10, 0x78, 0x3b, 0x36, 0xf6, 0x54, 0x12, 0x9b, 0xa1, 0xad, 0xf8, 0x02, 0x3b, 0x7b, 0x51, 0x53, 0x56, 0x8b, 0xc7, 0xeb, 0xaa, 0x5c, 0xfe, 0x38, 0x80, 0x59, 0x6c, 0x37, 0x79, 0x5e, 0x33, 0x77, 0xc7, 0x8b, 0x6b, 0x78, 0x1b, 0x9b, 0xd9, 0x91, 0x2f, 0xfb, 0x9b, 0x42, 0xca, 0x9b, 0xfe, 0xc8, 0x8f, 0x6a, 0xba, 0x47, 0x04, 0x94, 0xfd, 0xa2, 0x1a, 0x07, 0xb3, 0xc2, 0xb8, 0xaf, 0x0d, 0x9f, 0x7e, 0x2a, 0x6b, 0x1e, 0x82, 0x91, 0xe1, 0x17, 0xc0, 0x51, 0xf3, 0xc9, 0x14, 0xef, 0xf4, 0xbe, 0xe3, 0xa8, 0x57, 0xec, 0x2d, 0xb3, 0x1d, 0x26, 0x62, 0xd1, 0xb4, 0xad, 0x89, 0x58, 0x72, 0xc3, 0x87, 0x6a, 0xfb, 0x0f, 0x89, 0xa7, 0x1e, 0x34, 0x89, 0x3a, 0x3b, 0x96, 0x85, 0xa3, 0x91, 0x64, 0xf1, 0x59, 0x66, 0x66, 0x91, 0xa1, 0x76, 0x3a, 0x73, 0xc2, 0x93, 0x43, 0x07, 0xc3, 0xed, 0xb7, 0xf0, 0x9d, 0x6a, 0x48, 0x6c, 0xc8, 0xb4, 0x56, 0x12, 0xa6, 0xc2, 0x52, 0x50, 0x25, 0xf9, 0xd4, 0x1d, 0xfc, 0x4d, 0xbe, 0x0e, 0x48, 0x02, 0x31, 0xa9, 0xea, 0xce, 0xcb, 0x9a, 0xe9, 0x31, 0x9b, 0xd2, 0x8d, 0xdc, 0x37, 0x52, 0x95, 0x85, 0xdb, 0x36, 0x05, 0x1f, 0x1a, 0x6a, 0xbd, 0xc5, 0x73, 0x61, 0xdb, 0x27, 0xbf, 0x2a, 0x13, 0x47, 0x16, 0xde, 0x29, 0xa7, 0xc1, 0x24, 0xe4, 0x85, 0x23, 0x78, 0x13, 0x3a, 0x31, 0xc4, 0x32, 0x37, 0xbc, 0x30, 0x67, 0xd3, 0xdc, 0x47, 0x5c, 0xa6, 0x34, 0x8a, 0xd4, 0xb3, 0x7c, 0x5f, 0x5f, 0xb2, 0x14, 0x53, 0x50, 0xd8, 0xc4, 0x9d, 0xf0, 0xe5, 0x0d, 0x37, 0xa0, 0xd4, 0x43, 0x18, 0x05, 0xbc, 0x91, 0x33, 0x3a, 0x39, 0x06, 0x21, 0x53, 0x35, 0xd5, 0x15, 0xc0, 0x19, 0xac, 0x4d, 0xf0, 0x89, 0x8d, 0x0a, 0x07, 0x44, 0x43, 0xb7, 0xe0, 0x66, 0x1f, 0xc3, 0xcd, 0x30, 0x8b, 0x01, 0xd0, 0x8a, 0xa4, 0x21, 0x06, 0x25, 0xef, 0x3d, 0x19, 0xd0, 0xa3, 0x6c, 0x15, 0x7a, 0x32, 0x12, 0xad, 0xce, 0xfa, 0xba, 0xa2, 0xc9, 0x0d, 0xe0, 0x84, 0x56, 0x7e, 0xc8, 0x6b, 0x59, 0xbe, 0x86, 0xc8, 0xa4, 0xdc, 0x65, 0xac, 0xf1, 0x80, 0x44, 0x5d, 0x6b, 0x3c, 0x13, 0x11, 0x47, 0xf6, 0x16, 0x35, 0x4e, 0xa3, 0xbf, 0xf2, 0x2a, 0x31, 0xee, 0x38, 0x47, 0xf4, 0x78, 0x42, 0x61, 0x6d, 0x4a, 0xab, 0xc0, 0x58, 0x04, 0xb2, 0x96, 0xb6, 0x30, 0xed, 0xec, 0x9a, 0x85, 0x09, 0x21, 0xfc, 0xbe, 0x5c, 0xbd, 0xec, 0xa4, 0x1e, 0xa4, 0x3d, 0xb1, 0x4e, 0xf8, 0x5e, 0xfb, 0x3a, 0x0f, 0xf8, 0xdc, 0x45, 0xda, 0xd3, 0x78, 0x82, 0x4f, 0x99, 0x8a, 0x42, 0x1a, 0xd7, 0x73, 0x55, 0x79, 0x42, 0xb9, 0x05, 0x19, 0xba, 0x57, 0x7f, 0xb1, 0x58, 0xc5, 0x55, 0x61, 0x4c, 0xed, 0x0d, 0x3e, 0x24, 0xf1, 0x04, 0x5e, 0xf3, 0xe7, 0x3e, 0x02, 0x36, 0x38, 0xdc, 0xca, 0x3b, 0x9f, 0xe1, 0x25, 0xf5, 0xa8, 0xc6, 0xc4, 0xdc, 0x39, 0xb0, 0xc0, 0xd8, 0x61, 0x84, 0xcd, 0xcf, 0xbe, 0xb7, 0x07, 0x08, 0x12, 0xef, 0x53, 0x37, 0xd7, 0xf8, 0x3e, 0x41, 0xde, 0x12, 0x1f, 0xe3, 0xa7, 0x90, 0x01, 0xce, 0x91, 0x87, 0x62, 0xdd, 0x7a, 0x9f, 0xde, 0xdf, 0xb0, 0x62, 0x96, 0x66, 0x6e, 0x9c, 0xd4, 0x82, 0x43, 0xa1, 0xa7, 0xde, 0x03, 0x7a, 0x49, 0xc8, 0x8a, 0x72, 0x2d, 0xee, 0x42, 0x7f, 0x6b, 0x1e, 0x81, 0x31, 0xee, 0x61, 0xe5, 0x97, 0x38, 0xe4, 0x6f, 0xf3, 0x42, 0x5e, 0x38, 0xb3, 0x37, 0xe5, 0x53, 0x5c, 0xeb, 0x7d, 0x86, 0xcf, 0xfa, 0xe2, 0x8d, 0xb5, 0x57, 0x44, 0x59, 0x48, 0x30, 0x51, 0xe6, 0x7b, 0x9a, 0xc4, 0x25, 0x61, 0x51, 0x68, 0xf9, 0x42, 0x28, 0x8c, 0x6c, 0xba, 0x70, 0x3d, 0x0d, 0xa2, 0x8d, 0xcd, 0xcf, 0xf2, 0x4b, 0x85, 0xd6, 0xd6, 0x14, 0xe6, 0x1a, 0x9b, 0x18, 0x22, 0x67, 0x22, 0xa5, 0xb8, 0x7c, 0xec, 0x6d, 0x66, 0xf5, 0xa4, 0x29, 0x13, 0x5c, 0x1c, 0x4f, 0xcd, 0x8b, 0xbc, 0x0f, 0x27, 0x75, 0xda, 0xc6, 0x77, 0xd2, 0x5f, 0x37, 0xee, 0x46, 0xd4, 0xb1, 0x18, 0x72, 0xfb, 0xb7, 0x5f, 0x62, 0x08, 0xff, 0x7f, 0x2e, 0x50, 0x36, 0x29, 0x53, 0x0b, 0x62, 0x38, 0xc6, 0x5a, 0x3e, 0xe9, 0xba, 0x36, 0xe2, 0xcb, 0x7d, 0xf4, 0x67, 0xd8, 0x98, 0x88, 0xf6, 0x39, 0xf9, 0x85, 0x64, 0xeb, 0xc4, 0x7f, 0xb6, 0xc9, 0xf8, 0x03, 0x1a, 0xca, 0x0d, 0x35, 0x94, 0xe2, 0xc3, 0xcc, 0xef, 0x5d, 0x5a, 0x83, 0x28, 0x50, 0x8e, 0x88, 0x67, 0x31, 0xd3, 0xa0, 0x5c, 0x69, 0x83, 0x6d, 0x21, 0x0e, 0x2a, 0x67, 0x35, 0x68, 0x9a, 0xb2, 0x41, 0x67, 0x87, 0x8b, 0x1a, 0x84, 0x53, 0x78, 0xa3, 0x26, 0x1b, 0xc3, 0x0f, 0x24, 0x5f, 0x76, 0xbf, 0x0c, 0x50, 0xff, 0x40, 0x38, 0x98, 0x55, 0x6d, 0x24, 0x44, 0x84, 0x05, 0x79, 0x20, 0x36, 0xe0, 0xb8, 0x04, 0xcb, 0x69, 0x63, 0x91, 0x81, 0xb9, 0x07, 0xa9, 0xa6, 0x28, 0x91, 0xf3, 0xe6, 0xf9, 0xa0, 0xbb, 0x77, 0xaa, 0xe1, 0xe9, 0x90, 0xe8, 0x36, 0x60, 0x15, 0x5b, 0xb3, 0x9b, 0x72, 0x7b, 0x1c, 0x17, 0xcb, 0xd5, 0xf0, 0x1b, 0x2f, 0xce, 0x76, 0xf9, 0xae, 0x50, 0x7b, 0x70, 0xc5, 0xe8, 0x3a, 0x08, 0x96, 0x08, 0xa2, 0x9f, 0x22, 0xbb, 0x39, 0x4f, 0x37, 0x3c, 0x72, 0x36, 0x71, 0x1b, 0x4b, 0x09, 0x25, 0x71, 0x6e, 0xcf, 0xc1, 0x09, 0x42, 0x7f, 0x5c, 0xa4, 0xc8, 0xf2, 0x8e, 0xa7, 0x21, 0xc7, 0x89, 0xa7, 0x51, 0xbc, 0xf3, 0x87, 0x3d, 0x57, 0xd0, 0x38, 0x0e, 0xde, 0x3c, 0x28, 0x02, 0x1b, 0x75, 0x5c, 0x01, 0x90, 0xa2, 0xd7, 0x5a, 0x39, 0x0a, 0xe6, 0x01, 0x16, 0x80, 0xad, 0x98, 0x21, 0x82, 0xf4, 0x31, 0xca, 0x50, 0x76, 0x98, 0xd1, 0xd7, 0x62, 0xd2, 0xfb, 0xf4, 0x94, 0x61, 0x34, 0x30, 0x5b, 0x90, 0xe0, 0x98, 0x66, 0xf3, 0xba, 0x10, 0xa4, 0x3f, 0x30, 0x06, 0x0e, 0xcc, 0x2b, 0x61, 0xa3, 0xaa, 0x3b, 0xef, 0x8a, 0x26, 0x59, 0xdf, 0x6b, 0xd2, 0xc5, 0x8e, 0x8d }; +constexpr AccessUnit LPCM_SINGLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, false, 3, { 0xb1, 0x40, 0x00, 0xda, 0x2c, 0xae, 0xd8, 0xf8, 0x31, 0x4d, 0x58, 0x96, 0x3f, 0x03, 0x7e, 0x2e }, { 0xec, 0x7a, 0x1d, 0x5c, 0xa0, 0xca, 0x0e, 0xdc, 0x52, 0x1a, 0x9c, 0xc1, 0x52, 0xdc, 0xcd, 0xa6, 0x9e, 0x28, 0x97, 0xde } }; +constexpr AccessUnit LPCM_SINGLE_PACK_EXPECTED_AU_2 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 3, { 0xb1, 0x40, 0x00, 0xda, 0x2c, 0xae, 0xd8, 0xf8, 0x31, 0x4d, 0x58, 0x96, 0x3f, 0x03, 0x7e, 0x2e }, { 0x09, 0xa0, 0xb5, 0x46, 0xd8, 0x95, 0xf7, 0x34, 0x86, 0xc9, 0x2c, 0x8f, 0xe0, 0xb6, 0x69, 0x72, 0x75, 0x0b, 0xa9, 0x59 } }; + +constexpr std::array USER_DATA_SINGLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x20, 0xb1, 0x00, 0x00, 0x04, 0x29, 0xae, 0xd8, 0xf8, 0x31, 0x4d, 0x58, 0x96, 0x3f, 0x03, 0x7e, 0x2e, 0x5d, 0xb9, 0x16, 0xf9, 0x14, 0x26, 0x07, 0x3f, 0x85, 0xc1, 0x78, 0xcb, 0xf1, 0x73, 0xc1, 0xd6, 0xc2, 0x80, 0x8d, 0xaf, 0xa9, 0x76, 0xb1, 0xb2, 0x83, 0xcf, 0xce, 0x89, 0x25, 0xe4, 0x78, 0xe7, 0x06, 0x10, 0x18, 0xdd, 0x52, 0xf6, 0x17, 0x36, 0xe0, 0xba, 0x91, 0xc5, 0x0b, 0x07, 0xca, 0x35, 0x6b, 0xe0, 0x1c, 0xbf, 0xdf, 0x36, 0xfb, 0xca, 0x06, 0x73, 0x67, 0x8d, 0xcf, 0xb0, 0xf8, 0x8d, 0x34, 0xef, 0x73, 0x07, 0x1b, 0x74, 0x31, 0x13, 0xf4, 0x5a, 0x45, 0x77, 0xf9, 0xc2, 0x7e, 0x0c, 0x9b, 0xb1, 0x3a, 0xf9, 0x48, 0xbb, 0x8e, 0xda, 0xc6, 0x9f, 0x68, 0xfd, 0x4a, 0x39, 0xcd, 0x81, 0xa1, 0x4e, 0xcc, 0x6e, 0x2a, 0x49, 0x32, 0xc4, 0xb2, 0xee, 0xb9, 0xcd, 0x5d, 0x92, 0x07, 0x53, 0x9e, 0xca, 0x7f, 0xfc, 0x6c, 0xe0, 0xa5, 0xc8, 0xed, 0xba, 0x5d, 0x35, 0xc9, 0x4d, 0x1a, 0x86, 0x1b, 0xaf, 0xfc, 0x44, 0xb4, 0xec, 0xf8, 0xeb, 0xdc, 0x22, 0x08, 0x5f, 0x29, 0x85, 0x81, 0xdb, 0xe9, 0x8f, 0x4e, 0xf2, 0xb9, 0xd9, 0x09, 0x58, 0x11, 0xd4, 0xfa, 0x2a, 0x01, 0x5d, 0xf1, 0x07, 0x6f, 0xe8, 0x75, 0x66, 0x39, 0xac, 0x1b, 0x28, 0x57, 0x6b, 0xd3, 0xd4, 0x06, 0xee, 0xd0, 0x86, 0xde, 0xc4, 0x88, 0x99, 0x62, 0x52, 0x37, 0xd9, 0xed, 0x44, 0x3d, 0xc7, 0xa4, 0x2c, 0x2f, 0x96, 0xed, 0xfd, 0xba, 0xd8, 0xd1, 0x72, 0xf6, 0xbb, 0xf3, 0x5f, 0x3b, 0x3e, 0xc3, 0x2c, 0x23, 0xca, 0xb2, 0x72, 0x69, 0xb7, 0x14, 0xcc, 0x81, 0x1a, 0x24, 0x9b, 0x95, 0xd4, 0xc7, 0x97, 0x9f, 0xd3, 0x5b, 0x22, 0xa6, 0x57, 0x63, 0xec, 0x27, 0x20, 0xc4, 0x76, 0x6e, 0xd9, 0xf0, 0x56, 0xaa, 0x0d, 0x54, 0xb7, 0x49, 0x39, 0xf2, 0x19, 0x7d, 0x56, 0xa2, 0x70, 0x7e, 0xce, 0x89, 0x46, 0x08, 0xb2, 0x7f, 0xba, 0x73, 0x9a, 0x2d, 0x11, 0xdd, 0x74, 0x90, 0x70, 0x36, 0xd6, 0x97, 0x62, 0xf0, 0xf7, 0x39, 0x99, 0xf6, 0x50, 0xb2, 0xd4, 0xff, 0x42, 0x8e, 0xbe, 0xf1, 0x57, 0x0e, 0xc8, 0x29, 0x6f, 0x16, 0xa7, 0x86, 0xdb, 0xd9, 0x81, 0x43, 0x9b, 0x95, 0x24, 0xbd, 0x9a, 0x0c, 0x54, 0x2e, 0x71, 0x72, 0x42, 0xe6, 0x25, 0x41, 0xa9, 0x6e, 0x1c, 0x2e, 0x74, 0xb1, 0xe0, 0x4e, 0x66, 0xc3, 0xda, 0xb6, 0x16, 0xb2, 0x84, 0xc5, 0x1f, 0x1f, 0x65, 0xf2, 0xc8, 0x3d, 0x9c, 0xb4, 0x28, 0xed, 0xd3, 0xe2, 0xa2, 0xf8, 0x24, 0xd8, 0x84, 0x16, 0x9f, 0x72, 0x57, 0xa0, 0xfd, 0x28, 0x81, 0x97, 0xb3, 0x7b, 0x5d, 0x29, 0x37, 0xa3, 0x81, 0x2a, 0x5f, 0x45, 0x6b, 0xd3, 0xbd, 0x13, 0x78, 0x4a, 0x63, 0x9f, 0x7a, 0x34, 0x3c, 0xf4, 0x45, 0x7f, 0x51, 0xe9, 0x58, 0xdc, 0xcb, 0x39, 0x38, 0x87, 0xab, 0xb4, 0x0d, 0xaf, 0x4d, 0x63, 0x4b, 0x0f, 0x2e, 0xac, 0xc6, 0xd6, 0x04, 0x1b, 0x3a, 0xda, 0x9d, 0x3b, 0x20, 0x90, 0x3b, 0x19, 0x4f, 0x6e, 0x82, 0x93, 0x85, 0x53, 0x18, 0xf7, 0xa6, 0x1a, 0x2f, 0x98, 0xc8, 0x1f, 0xe3, 0x59, 0x5b, 0x43, 0x76, 0xc4, 0xd4, 0x37, 0xef, 0x0e, 0xce, 0xa3, 0x35, 0xc9, 0x4d, 0x43, 0x13, 0x24, 0xe3, 0x6c, 0x5a, 0xc8, 0x8d, 0xb2, 0x68, 0x5d, 0xc0, 0xd2, 0xd0, 0x06, 0x1d, 0x77, 0x80, 0x65, 0xcd, 0x0a, 0xc0, 0x93, 0xb1, 0x8c, 0xf8, 0x1b, 0xf6, 0x8c, 0x59, 0xf5, 0x99, 0x7f, 0xec, 0x34, 0x0e, 0x16, 0x4e, 0x6a, 0x0c, 0x86, 0xba, 0x1b, 0xd8, 0x53, 0x66, 0x1e, 0xdc, 0x66, 0x8d, 0x91, 0x1b, 0x2b, 0xb4, 0xdd, 0xe2, 0x54, 0x1e, 0xea, 0xc2, 0x5b, 0x6b, 0x56, 0x96, 0x8c, 0xfb, 0xf8, 0x21, 0x22, 0x8f, 0x1e, 0xbb, 0xd7, 0xe7, 0x7b, 0xd7, 0xbf, 0x0c, 0xbb, 0x9e, 0xe2, 0x6c, 0x31, 0x3a, 0xaa, 0xcd, 0xd3, 0xfb, 0xcb, 0x6e, 0xc6, 0x82, 0x7f, 0x01, 0xda, 0x0e, 0x0b, 0xc8, 0x21, 0xc4, 0x6c, 0x2b, 0xe3, 0x88, 0xcd, 0x0c, 0xb3, 0x3d, 0x89, 0xf6, 0xd9, 0x75, 0x16, 0xf8, 0x0e, 0x6d, 0xee, 0x5a, 0xd6, 0xaf, 0xe1, 0x63, 0xe0, 0x77, 0x55, 0x78, 0x39, 0xf9, 0xc4, 0xa2, 0xa7, 0x96, 0x34, 0x17, 0xe0, 0x4f, 0x79, 0xcc, 0x0c, 0x3f, 0x67, 0x31, 0x55, 0xc9, 0xe0, 0x3b, 0x2c, 0x45, 0xaf, 0xbb, 0xbc, 0x73, 0x7c, 0x13, 0xe6, 0x0a, 0x5b, 0x87, 0xd5, 0x91, 0x35, 0xbc, 0x63, 0xd2, 0x31, 0x41, 0x4f, 0x17, 0xb2, 0xf3, 0x44, 0x33, 0xbf, 0x41, 0x7f, 0x49, 0x2f, 0xab, 0x38, 0xd4, 0x9e, 0xf8, 0x9d, 0xb7, 0xe4, 0x97, 0x0f, 0x0e, 0xec, 0xa3, 0xe8, 0xda, 0x9a, 0x4f, 0x23, 0xab, 0x2f, 0xa8, 0x0f, 0x7e, 0x17, 0xed, 0x56, 0xc8, 0x69, 0x5b, 0xe6, 0xc1, 0xbd, 0x01, 0x19, 0xe5, 0xc3, 0xa6, 0x96, 0x91, 0x84, 0x36, 0x9e, 0xb1, 0x25, 0x72, 0xc5, 0x60, 0x3a, 0xcb, 0x77, 0xf9, 0x9b, 0x70, 0x78, 0x6a, 0x67, 0xd4, 0x9e, 0x7b, 0x48, 0x59, 0xa9, 0x6a, 0xed, 0x72, 0x70, 0x22, 0x5a, 0xfe, 0x07, 0x66, 0x1a, 0xb2, 0x3a, 0x7a, 0x1e, 0xcf, 0x3c, 0xc8, 0x94, 0xe0, 0x97, 0xcb, 0xba, 0x4b, 0x1c, 0xb9, 0xab, 0x5f, 0xa5, 0xeb, 0xf7, 0x25, 0x94, 0xce, 0x66, 0x5a, 0xf3, 0xdb, 0xf7, 0x3e, 0xca, 0xb2, 0x64, 0xf2, 0x04, 0xb3, 0xc5, 0x44, 0xf1, 0x6d, 0x2e, 0xfe, 0x16, 0xfa, 0xcc, 0x49, 0x6a, 0x61, 0x6f, 0x27, 0x4f, 0x0b, 0x85, 0x92, 0x17, 0xcc, 0x07, 0xe1, 0x93, 0x9e, 0x5f, 0x34, 0x6f, 0x9d, 0x65, 0xae, 0x03, 0x6a, 0x55, 0xa5, 0x45, 0x9f, 0x41, 0x0f, 0x29, 0xee, 0x87, 0xf5, 0x2d, 0x1e, 0xf3, 0x78, 0xa7, 0x4b, 0xb7, 0x80, 0x62, 0x5f, 0x90, 0x22, 0x80, 0x22, 0x44, 0xaa, 0xb2, 0x41, 0x33, 0xcd, 0x7c, 0xa7, 0x2b, 0x44, 0x5c, 0x3d, 0x54, 0x25, 0xfd, 0x6e, 0x47, 0xcb, 0x81, 0x46, 0x77, 0x8b, 0x75, 0x84, 0xe6, 0x94, 0x58, 0xa3, 0xd9, 0xbe, 0x54, 0x11, 0x25, 0xd7, 0x43, 0x53, 0xba, 0x43, 0x85, 0xfd, 0xec, 0x5b, 0x59, 0xe0, 0x03, 0x0e, 0x57, 0x80, 0x97, 0x7a, 0x0f, 0xf3, 0x17, 0x27, 0x50, 0x88, 0x72, 0x85, 0xb0, 0x14, 0x9d, 0x28, 0xec, 0x09, 0xb1, 0xb4, 0x56, 0x9b, 0x36, 0x4f, 0xa9, 0x8f, 0xb9, 0x66, 0x1f, 0xac, 0xea, 0xc3, 0x9f, 0xfe, 0x5a, 0xb8, 0xe8, 0xde, 0x63, 0xeb, 0x6f, 0xb8, 0x26, 0xdf, 0x42, 0x4b, 0x46, 0x94, 0xe0, 0xdb, 0x61, 0xae, 0x0a, 0x6f, 0xd6, 0xbf, 0xb3, 0x3b, 0x13, 0x82, 0xd6, 0x3e, 0xaf, 0x18, 0x2e, 0x4b, 0x03, 0xca, 0xf9, 0xa8, 0x83, 0x61, 0x2f, 0x04, 0x0b, 0x6e, 0xf0, 0x0e, 0x8c, 0xc2, 0x19, 0x9b, 0x10, 0x78, 0x3b, 0x36, 0xf6, 0x54, 0x12, 0x9b, 0xa1, 0xad, 0xf8, 0x02, 0x3b, 0x7b, 0x51, 0x53, 0x56, 0x8b, 0xc7, 0xeb, 0xaa, 0x5c, 0xfe, 0x38, 0x80, 0x59, 0x6c, 0x37, 0x79, 0x5e, 0x33, 0x77, 0xc7, 0x8b, 0x6b, 0x78, 0x1b, 0x9b, 0xd9, 0x91, 0x2f, 0xfb, 0x9b, 0x42, 0xca, 0x9b, 0xfe, 0xc8, 0x8f, 0x6a, 0xba, 0x47, 0x04, 0x94, 0xfd, 0xa2, 0x1a, 0x07, 0xb3, 0xc2, 0xb8, 0xaf, 0x0d, 0x9f, 0x7e, 0x2a, 0x6b, 0x1e, 0x82, 0x91, 0xe1, 0x17, 0xc0, 0x51, 0xf3, 0xc9, 0x14, 0xef, 0xf4, 0xbe, 0xe3, 0xa8, 0x57, 0xec, 0x2d, 0xb3, 0x1d, 0x26, 0x62, 0xd1, 0xb4, 0xad, 0x89, 0x58, 0x72, 0xc3, 0x87, 0x6a, 0xfb, 0x0f, 0x89, 0xa7, 0x1e, 0x34, 0x89, 0x3a, 0x3b, 0x96, 0x85, 0xa3, 0x91, 0x64, 0xf1, 0x59, 0x66, 0x66, 0x91, 0xa1, 0x76, 0x3a, 0x73, 0xc2, 0x93, 0x43, 0x07, 0xc3, 0xed, 0xb7, 0xf0, 0x9d, 0x6a, 0x48, 0x6c, 0xc8, 0xb4, 0x56, 0x12, 0xa6, 0xc2, 0x52, 0x50, 0x25, 0xf9, 0xd4, 0x1d, 0xfc, 0x4d, 0xbe, 0x0e, 0x48, 0x02, 0x31, 0xa9, 0xea, 0xce, 0xcb, 0x9a, 0xe9, 0x31, 0x9b, 0xd2, 0x8d, 0xdc, 0x37, 0x52, 0x95, 0x85, 0xdb, 0x36, 0x05, 0x1f, 0x1a, 0x6a, 0xbd, 0xc5, 0x73, 0x61, 0xdb, 0x27, 0xbf, 0x2a, 0x13, 0x47, 0x16, 0xde, 0x29, 0xa7, 0xc1, 0x24, 0xe4, 0x85, 0x23, 0x78, 0x13, 0x3a, 0x31, 0xc4, 0x32, 0x37, 0xbc, 0x30, 0x67, 0xd3, 0xdc, 0x47, 0x5c, 0xa6, 0x34, 0x8a, 0xd4, 0xb3, 0x7c, 0x5f, 0x5f, 0xb2, 0x14, 0x53, 0x50, 0xd8, 0xc4, 0x9d, 0xf0, 0xe5, 0x0d, 0x37, 0xa0, 0xd4, 0x43, 0x18, 0x05, 0xbc, 0x91, 0x33, 0x3a, 0x39, 0x06, 0x21, 0x53, 0x35, 0xd5, 0x15, 0xc0, 0x19, 0xac, 0x4d, 0xf0, 0x89, 0x8d, 0x0a, 0x07, 0x44, 0x43, 0xb7, 0xe0, 0x66, 0x1f, 0xc3, 0xcd, 0x30, 0x8b, 0x01, 0xd0, 0x8a, 0xa4, 0x21, 0x06, 0x25, 0xef, 0x3d, 0x19, 0xd0, 0xa3, 0x6c, 0x15, 0x7a, 0x32, 0x12, 0xad, 0xce, 0xfa, 0xba, 0xa2, 0xc9, 0x0d, 0xe0, 0x84, 0x56, 0x7e, 0xc8, 0x6b, 0x59, 0xbe, 0x86, 0xc8, 0xa4, 0xdc, 0x65, 0xac, 0xf1, 0x80, 0x44, 0x5d, 0x6b, 0x3c, 0x13, 0x11, 0x47, 0xf6, 0x16, 0x35, 0x4e, 0xa3, 0xbf, 0xf2, 0x2a, 0x31, 0xee, 0x38, 0x47, 0xf4, 0x78, 0x42, 0x61, 0x6d, 0x4a, 0xab, 0xc0, 0x58, 0x04, 0xb2, 0x96, 0xb6, 0x30, 0xed, 0xec, 0x9a, 0x85, 0x09, 0x21, 0xfc, 0xbe, 0x5c, 0xbd, 0xec, 0xa4, 0x1e, 0xa4, 0x3d, 0xb1, 0x4e, 0xf8, 0x5e, 0xfb, 0x3a, 0x0f, 0xf8, 0xdc, 0x45, 0xda, 0xd3, 0x78, 0x82, 0x4f, 0x99, 0x8a, 0x42, 0x1a, 0xd7, 0x73, 0x55, 0x79, 0x42, 0xb9, 0x05, 0x19, 0xba, 0x57, 0x7f, 0xb1, 0x58, 0xc5, 0x55, 0x61, 0x4c, 0xed, 0x0d, 0x3e, 0x24, 0xf1, 0x04, 0x5e, 0xf3, 0xe7, 0x3e, 0x02, 0x36, 0x38, 0xdc, 0xca, 0x3b, 0x9f, 0xe1, 0x25, 0xf5, 0xa8, 0xc6, 0xc4, 0xdc, 0x39, 0xb0, 0xc0, 0xd8, 0x61, 0x84, 0xcd, 0xcf, 0xbe, 0xb7, 0x07, 0x08, 0x12, 0xef, 0x53, 0x37, 0xd7, 0xf8, 0x3e, 0x41, 0xde, 0x12, 0x1f, 0xe3, 0xa7, 0x90, 0x01, 0xce, 0x91, 0x87, 0x62, 0xdd, 0x7a, 0x9f, 0xde, 0xdf, 0xb0, 0x62, 0x96, 0x66, 0x6e, 0x9c, 0xd4, 0x82, 0x43, 0xa1, 0xa7, 0xde, 0x03, 0x7a, 0x49, 0xc8, 0x8a, 0x72, 0x2d, 0xee, 0x42, 0x7f, 0x6b, 0x1e, 0x81, 0x31, 0xee, 0x61, 0xe5, 0x97, 0x38, 0xe4, 0x6f, 0xf3, 0x42, 0x5e, 0x38, 0xb3, 0x37, 0xe5, 0x53, 0x5c, 0xeb, 0x7d, 0x86, 0xcf, 0xfa, 0xe2, 0x8d, 0xb5, 0x57, 0x44, 0x59, 0x48, 0x30, 0x51, 0xe6, 0x7b, 0x9a, 0xc4, 0x25, 0x61, 0x51, 0x68, 0xf9, 0x42, 0x28, 0x8c, 0x6c, 0xba, 0x70, 0x3d, 0x0d, 0xa2, 0x8d, 0xcd, 0xcf, 0xf2, 0x4b, 0x85, 0xd6, 0xd6, 0x14, 0xe6, 0x1a, 0x9b, 0x18, 0x22, 0x67, 0x22, 0xa5, 0xb8, 0x7c, 0xec, 0x6d, 0x66, 0xf5, 0xa4, 0x29, 0x13, 0x5c, 0x1c, 0x4f, 0xcd, 0x8b, 0xbc, 0x0f, 0x27, 0x75, 0xda, 0xc6, 0x77, 0xd2, 0x5f, 0x37, 0xee, 0x46, 0xd4, 0xb1, 0x18, 0x72, 0xfb, 0xb7, 0x5f, 0x62, 0x08, 0xff, 0x7f, 0x2e, 0x50, 0x36, 0x29, 0x53, 0x0b, 0x62, 0x38, 0xc6, 0x5a, 0x3e, 0xe9, 0xba, 0x36, 0xe2, 0xcb, 0x7d, 0xf4, 0x67, 0xd8, 0x98, 0x88, 0xf6, 0x39, 0xf9, 0x85, 0x64, 0xeb, 0xc4, 0x7f, 0xb6, 0xc9, 0xf8, 0x03, 0x1a, 0xca, 0x0d, 0x35, 0x94, 0xe2, 0xc3, 0xcc, 0xef, 0x5d, 0x5a, 0x83, 0x28, 0x50, 0x8e, 0x88, 0x67, 0x31, 0xd3, 0xa0, 0x5c, 0x69, 0x83, 0x6d, 0x21, 0x0e, 0x2a, 0x67, 0x35, 0x68, 0x9a, 0xb2, 0x41, 0x67, 0x87, 0x8b, 0x1a, 0x84, 0x53, 0x78, 0xa3, 0x26, 0x1b, 0xc3, 0x0f, 0x24, 0x5f, 0x76, 0xbf, 0x0c, 0x50, 0xff, 0x40, 0x38, 0x98, 0x55, 0x6d, 0x24, 0x44, 0x84, 0x05, 0x79, 0x20, 0x36, 0xe0, 0xb8, 0x04, 0xcb, 0x69, 0x63, 0x91, 0x81, 0xb9, 0x07, 0xa9, 0xa6, 0x28, 0x91, 0xf3, 0xe6, 0xf9, 0xa0, 0xbb, 0x77, 0xaa, 0xe1, 0xe9, 0x90, 0xe8, 0x36, 0x60, 0x15, 0x5b, 0xb3, 0x9b, 0x72, 0x7b, 0x1c, 0x17, 0xcb, 0xd5, 0xf0, 0x1b, 0x2f, 0xce, 0x76, 0xf9, 0xae, 0x50, 0x7b, 0x70, 0xc5, 0xe8, 0x3a, 0x08, 0x96, 0x08, 0xa2, 0x9f, 0x22, 0xbb, 0x39, 0x4f, 0x37, 0x3c, 0x72, 0x36, 0x71, 0x1b, 0x4b, 0x09, 0x25, 0x71, 0x6e, 0xcf, 0xc1, 0x09, 0x42, 0x7f, 0x5c, 0xa4, 0xc8, 0xf2, 0x8e, 0xa7, 0x21, 0xc7, 0x89, 0xa7, 0x51, 0xbc, 0xf3, 0x87, 0x3d, 0x57, 0xd0, 0x38, 0x0e, 0xde, 0x3c, 0x28, 0x02, 0x1b, 0x75, 0x5c, 0x01, 0x90, 0xa2, 0xd7, 0x5a, 0x39, 0x0a, 0xe6, 0x01, 0x16, 0x80, 0xad, 0x98, 0x21, 0x82, 0xf4, 0x31, 0xca, 0x50, 0x76, 0x98, 0xd1, 0xd7, 0x62, 0xd2, 0xfb, 0xf4, 0x94, 0x61, 0x34, 0x30, 0x5b, 0x90, 0xe0, 0x98, 0x66, 0xf3, 0xba, 0x10, 0xa4, 0x3f, 0x30, 0x06, 0x0e, 0xcc, 0x2b, 0x61, 0xa3, 0xaa, 0x3b, 0xef, 0x8a, 0x26, 0x59, 0xdf, 0x6b, 0xd2, 0xc5, 0x8e, 0x8d }; +constexpr AccessUnit USER_DATA_SINGLE_PACK_EXPECTED_AU = { 0x15f90, 0x159b2, false, 0, {}, { 0x65, 0x94, 0x4b, 0xa5, 0x88, 0x59, 0x24, 0xa3, 0x5a, 0x30, 0x33, 0x6d, 0xaf, 0xb2, 0x95, 0x4b, 0x84, 0x08, 0x72, 0xd6 } }; + + +constexpr std::array TIME_STAMP_SIGN_EXTENDED_STREAM = []() constexpr { auto pack = USER_DATA_SINGLE_PACK_STREAM; pack[0x11d] |= 0x4; pack[0x122] |= 0x4; return pack; }(); // Set 31st bits of the timestamps +constexpr AccessUnit TIME_STAMP_SIGN_EXTENDED_EXPECTED_AU = { 0xffffffff'80015f90ull, 0xffffffff'800159b2ull, false, 0, {}, { 0x65, 0x94, 0x4b, 0xa5, 0x88, 0x59, 0x24, 0xa3, 0x5a, 0x30, 0x33, 0x6d, 0xaf, 0xb2, 0x95, 0x4b, 0x84, 0x08, 0x72, 0xd6 } }; + + +const std::vector AVC_LARGE_AU_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x01, 0x09, 0xf6, 0xc0, 0x00, 0x4f, 0x57, 0x7f, 0xb7, 0xec, 0x51, 0xfa, 0xd4, 0x23, 0xdf, 0x05, 0x0a, 0x7a, 0x74, 0x3b, 0xbc, 0x91, 0xb6, 0xc8, 0x73, 0x6b, 0xe4, 0x1d, 0x28, 0xe9, 0xf2, 0x5f, 0xf8, 0x91, 0x13, 0xe6, 0xdb, 0x83, 0x92, 0x7f, 0x24, 0x04, 0x68, 0x57, 0x5c, 0x20, 0x6a, 0x4d, 0xfe, 0x93, 0x47, 0xe9, 0xf0, 0xf0, 0x18, 0xe2, 0xef, 0x2a, 0x94, 0xa9, 0xe4, 0xac, 0xc7, 0x1b, 0x62, 0x71, 0x96, 0xc8, 0x9e, 0xf6, 0x91, 0x44, 0x83, 0x34, 0x23, 0xf8, 0xb7, 0x35, 0x15, 0x1d, 0xe9, 0xc5, 0x1a, 0x61, 0xbd, 0x48, 0x21, 0x6b, 0xff, 0xd6, 0x2d, 0x85, 0xc8, 0x37, 0xf5, 0x6b, 0xaa, 0x59, 0x24, 0xf8, 0x33, 0xb5, 0xc8, 0xbc, 0x0a, 0x43, 0x3f, 0x82, 0xea, 0xd8, 0x8c, 0xdc, 0xd0, 0x6e, 0x92, 0x20, 0x3e, 0xce, 0x6c, 0x36, 0x3c, 0x43, 0xbf, 0x6e, 0x47, 0x92, 0xaa, 0xa3, 0x2a, 0x21, 0x95, 0x4c, 0x2e, 0x06, 0xbd, 0x64, 0x07, 0xf7, 0xdd, 0x15, 0x58, 0xf4, 0x15, 0xde, 0x47, 0x02, 0xe5, 0x2b, 0x9a, 0xce, 0x25, 0x2a, 0x85, 0x48, 0x3e, 0x05, 0xc2, 0xd2, 0xc6, 0x5a, 0x6d, 0xfe, 0xe8, 0x8b, 0xda, 0xe4, 0x4a, 0x6e, 0xc7, 0xc4, 0xff, 0x94, 0xe4, 0x46, 0xac, 0xf1, 0xa1, 0x8b, 0xdd, 0x54, 0xe0, 0xac, 0xd7, 0x1f, 0x76, 0x3c, 0xa6, 0x8f, 0x24, 0x37, 0xc1, 0xd2, 0xa6, 0xa4, 0x95, 0xdd, 0x53, 0x28, 0x5d, 0xb4, 0x89, 0xfd, 0x8f, 0x16, 0xa1, 0x9d, 0x5a, 0x1a, 0x7c, 0xda, 0x50, 0xa0, 0x33, 0x3e, 0x9b, 0x1a, 0x33, 0xa6, 0x4a, 0xc0, 0xfa, 0x14, 0xbf, 0xb9, 0x24, 0xa0, 0x99, 0x64, 0xb8, 0x9d, 0xb1, 0xfb, 0x98, 0xc9, 0xcf, 0xac, 0x7b, 0xcb, 0x9e, 0x85, 0x6f, 0x1e, 0xb3, 0x56, 0x33, 0xaf, 0x68, 0x42, 0xd9, 0xf7, 0x0c, 0x79, 0x2b, 0xfe, 0x61, 0x14, 0xa6, 0x59, 0x8c, 0x72, 0x97, 0xb3, 0x5e, 0x4e, 0xce, 0x25, 0xea, 0x1c, 0x09, 0x40, 0x1e, 0xfc, 0x6b, 0x31, 0xf1, 0xe3, 0x43, 0x10, 0xe4, 0x3b, 0x66, 0xa3, 0xde, 0x3b, 0xcc, 0x94, 0x5f, 0x97, 0xfd, 0xb6, 0x5a, 0x69, 0x9d, 0x28, 0xc9, 0xf0, 0xc9, 0x19, 0xd0, 0xe5, 0x52, 0xb9, 0x2b, 0x2d, 0xac, 0x15, 0xe3, 0x14, 0x3d, 0xea, 0x32, 0xc5, 0x49, 0x8e, 0x7f, 0xfc, 0x26, 0xe8, 0xc9, 0xbc, 0x60, 0x2f, 0x7c, 0xc9, 0x42, 0xca, 0xfe, 0x81, 0xe1, 0x02, 0x08, 0x91, 0x03, 0xe4, 0xe6, 0xca, 0x00, 0xef, 0x06, 0x5b, 0x3e, 0x8a, 0x91, 0xc8, 0x0e, 0x3c, 0xa7, 0x94, 0x08, 0x4f, 0x09, 0x92, 0x98, 0xc8, 0x48, 0xb0, 0x2d, 0xf3, 0x95, 0xe7, 0x01, 0x83, 0xee, 0x90, 0xb0, 0xc7, 0xbf, 0x27, 0x77, 0xfc, 0x19, 0xaf, 0xb4, 0xc2, 0x36, 0xad, 0x95, 0xca, 0xab, 0xa9, 0xf1, 0x1a, 0xaa, 0xfc, 0x2e, 0x56, 0x8a, 0xd8, 0x97, 0x8e, 0x91, 0xd7, 0xee, 0x80, 0x85, 0x9d, 0x33, 0x6d, 0xa6, 0x3d, 0xcd, 0x78, 0xb1, 0xcb, 0x58, 0xdd, 0x43, 0x59, 0xd3, 0x6b, 0xaa, 0x34, 0xcc, 0x56, 0xd5, 0x09, 0x5f, 0x0c, 0x62, 0x53, 0xb2, 0x69, 0x3d, 0xcd, 0xe2, 0x5e, 0x21, 0xfa, 0x34, 0x1e, 0xa7, 0x35, 0x04, 0x8b, 0xfc, 0x45, 0x3b, 0x9a, 0x56, 0x61, 0x2d, 0x09, 0xe3, 0xdc, 0xa4, 0xf1, 0xb2, 0xb0, 0xdc, 0x48, 0xdb, 0x41, 0xb0, 0x9b, 0xef, 0x0e, 0x55, 0x02, 0x83, 0x5c, 0x97, 0xe3, 0x90, 0x4b, 0xdb, 0x81, 0x03, 0xc5, 0x60, 0x91, 0x9e, 0xf6, 0x80, 0x7c, 0x7e, 0xe4, 0xdf, 0x91, 0x1b, 0xcd, 0x71, 0x2d, 0xe5, 0xa6, 0x19, 0x95, 0x09, 0xfc, 0x9b, 0x82, 0x8e, 0x99, 0xde, 0x4c, 0x4d, 0x11, 0x87, 0x2b, 0x9a, 0x3d, 0xdc, 0xc8, 0xd3, 0x23, 0x35, 0x7d, 0x87, 0xbb, 0xcb, 0xbe, 0x89, 0x7a, 0x8e, 0xa8, 0x84, 0x6e, 0xcf, 0x4d, 0x2f, 0x1f, 0xd1, 0xfb, 0x65, 0x4f, 0x58, 0xc1, 0xa3, 0xa7, 0x5c, 0x71, 0x8a, 0x01, 0x98, 0xee, 0x99, 0xd1, 0xde, 0xac, 0xd9, 0x1c, 0x23, 0x70, 0x88, 0x28, 0x83, 0x3b, 0x13, 0xd1, 0xc8, 0x87, 0x31, 0xe5, 0xdf, 0x56, 0x71, 0xa7, 0x81, 0xe7, 0x82, 0x25, 0x9e, 0x95, 0xb8, 0xc9, 0x8c, 0x82, 0xc6, 0xc7, 0x29, 0x80, 0xa1, 0xa6, 0x90, 0x5e, 0xbc, 0x2e, 0x13, 0x49, 0x51, 0x94, 0x67, 0x48, 0xd8, 0x54, 0x05, 0xe9, 0xe5, 0x0b, 0x48, 0xcf, 0x7e, 0x7e, 0x17, 0x7e, 0x05, 0xa0, 0x2f, 0x92, 0xbe, 0xd0, 0x28, 0xae, 0xac, 0x65, 0xae, 0x4b, 0x61, 0x6f, 0x37, 0x2e, 0xd8, 0xb2, 0x54, 0xe6, 0xfd, 0x9d, 0x03, 0x70, 0xcf, 0xb9, 0x29, 0xe7, 0x8c, 0xef, 0x4a, 0xf2, 0xcf, 0xd9, 0x85, 0xfe, 0xf1, 0x69, 0x60, 0x98, 0xe0, 0x0d, 0x87, 0xe0, 0x04, 0x70, 0x53, 0xeb, 0x90, 0x26, 0x7a, 0xa5, 0x57, 0x3a, 0x96, 0x84, 0x0e, 0x60, 0x73, 0x9f, 0x37, 0x0c, 0xb1, 0x4e, 0xc5, 0x29, 0xbe, 0x32, 0x1b, 0x6e, 0xed, 0xdc, 0x28, 0xa2, 0x48, 0x57, 0x3b, 0x78, 0x10, 0xbf, 0x98, 0x39, 0x91, 0x58, 0x35, 0xc4, 0x21, 0x48, 0x13, 0xf1, 0xc3, 0x63, 0xa4, 0x63, 0x97, 0x66, 0x49, 0x6b, 0x32, 0x4c, 0xe5, 0x1b, 0xd8, 0x50, 0xd2, 0x44, 0x91, 0x26, 0xe2, 0x2d, 0x04, 0xab, 0x52, 0x67, 0xe3, 0x8f, 0x29, 0x1c, 0x04, 0x4f, 0xc7, 0xc0, 0xa2, 0x19, 0xb0, 0x87, 0xdd, 0x50, 0xe7, 0xa8, 0xdb, 0x86, 0x78, 0xa4, 0x78, 0x6d, 0xc9, 0xd0, 0x88, 0x69, 0x87, 0xaf, 0xa4, 0x95, 0x61, 0x61, 0xdb, 0x83, 0x94, 0x53, 0xa6, 0xb4, 0xb2, 0x52, 0x24, 0xca, 0x83, 0x5f, 0x14, 0x16, 0xc7, 0x98, 0x51, 0xb5, 0x20, 0xb0, 0xc8, 0xb7, 0xdb, 0xd3, 0xd0, 0x95, 0xce, 0x0d, 0xef, 0xd4, 0x1e, 0x10, 0x8d, 0x8d, 0x8f, 0xb1, 0x12, 0x55, 0x03, 0xd8, 0x64, 0x0b, 0x14, 0xa8, 0xc8, 0x6f, 0x48, 0x47, 0x69, 0x46, 0x96, 0x81, 0xbe, 0xed, 0xf2, 0x39, 0x87, 0x71, 0xc7, 0x95, 0xf8, 0xb1, 0x4c, 0xd9, 0x07, 0xfa, 0x35, 0x93, 0xba, 0x09, 0x70, 0x79, 0xdf, 0x1f, 0x78, 0x8b, 0x12, 0x26, 0x06, 0x1d, 0x8e, 0xf0, 0x16, 0x8a, 0x18, 0xd9, 0xd8, 0xd3, 0x9a, 0x50, 0x76, 0x61, 0x22, 0xdd, 0x87, 0x3d, 0x4c, 0x5a, 0xdf, 0xe3, 0xe9, 0x47, 0x98, 0x5c, 0xac, 0x7a, 0x65, 0x55, 0xb5, 0xd7, 0xd5, 0xe3, 0x2b, 0x7b, 0x26, 0xa9, 0xce, 0xcc, 0x7c, 0x21, 0x8f, 0x7c, 0xcf, 0x28, 0x7e, 0x59, 0xfa, 0x8e, 0xb7, 0xee, 0x60, 0xda, 0x7b, 0xae, 0x73, 0xbe, 0x03, 0xb8, 0x8d, 0xab, 0xa2, 0x0a, 0x55, 0x27, 0xaf, 0x59, 0x96, 0xa0, 0xa5, 0xb4, 0xf0, 0xbc, 0xf8, 0xbe, 0xa6, 0x9d, 0x9b, 0x12, 0xd5, 0x03, 0xfa, 0x38, 0x4b, 0x00, 0x14, 0x4f, 0xe0, 0xe7, 0x49, 0x06, 0x83, 0x96, 0x60, 0x53, 0xe9, 0x08, 0x66, 0xb2, 0x3d, 0x4d, 0xf1, 0x65, 0x51, 0x09, 0x1c, 0x0c, 0x2a, 0x6f, 0xc8, 0x76, 0x3a, 0x4c, 0x98, 0x08, 0x3b, 0x1e, 0xd3, 0x8d, 0x6c, 0x2e, 0xd4, 0xf6, 0x4b, 0x1c, 0xba, 0xfc, 0x75, 0x94, 0xee, 0x1b, 0xa1, 0xa3, 0x9a, 0xa5, 0x57, 0xbc, 0x7e, 0xdb, 0xef, 0x81, 0x65, 0x39, 0x2f, 0x5f, 0x1e, 0x42, 0x5b, 0xb4, 0xe4, 0xf1, 0x46, 0x92, 0x3f, 0x86, 0x89, 0x24, 0xdd, 0x4b, 0x18, 0x49, 0x76, 0xfc, 0x65, 0xbf, 0xe7, 0x11, 0x8a, 0xb8, 0x78, 0xd6, 0x40, 0xe4, 0xd4, 0xc1, 0x8e, 0xa9, 0xe2, 0xb4, 0x9c, 0x8e, 0x7a, 0xcb, 0xca, 0x39, 0x2a, 0xf6, 0x17, 0x0e, 0x69, 0x91, 0xc1, 0x83, 0x1a, 0xe3, 0xfd, 0x49, 0xc1, 0x1f, 0x17, 0xc6, 0xf6, 0x6b, 0x20, 0x86, 0xea, 0xf2, 0x35, 0xaf, 0xf5, 0x3e, 0xd9, 0x11, 0x14, 0xf9, 0x3f, 0x46, 0x71, 0x2a, 0x47, 0x7b, 0xeb, 0xf3, 0xd5, 0xe6, 0x3e, 0xd0, 0xdc, 0xd3, 0x3e, 0x32, 0x82, 0x3d, 0xf2, 0xb8, 0x91, 0xfc, 0xa9, 0x54, 0x43, 0x6a, 0x6f, 0x68, 0xb4, 0x75, 0x49, 0x7f, 0x3e, 0xb9, 0x4e, 0x88, 0x4e, 0xb3, 0xf5, 0xd3, 0x91, 0x21, 0xde, 0xf8, 0x75, 0xd6, 0xed, 0x6c, 0xa0, 0x2c, 0x3a, 0x26, 0x5c, 0xea, 0x82, 0x9d, 0x6c, 0x25, 0x1b, 0x93, 0x64, 0x74, 0x8e, 0xca, 0x8e, 0x1f, 0xba, 0x6d, 0x6c, 0x08, 0xae, 0x42, 0xba, 0x1f, 0x0a, 0x8c, 0xa0, 0x66, 0xd4, 0xda, 0x44, 0x48, 0x29, 0x78, 0x6b, 0xdb, 0x2a, 0x2b, 0xbf, 0x32, 0xd9, 0xb5, 0xf1, 0xf9, 0xc0, 0x78, 0x7b, 0x4a, 0x1f, 0xd8, 0xff, 0xa7, 0xdc, 0x20, 0x1a, 0x69, 0x1c, 0x97, 0x77, 0x49, 0x21, 0x83, 0xf1, 0xb2, 0x5b, 0xfb, 0x4b, 0xb0, 0x7e, 0x32, 0xae, 0x47, 0xb5, 0x2b, 0x3e, 0x34, 0x62, 0xce, 0x5c, 0x37, 0x85, 0x3f, 0x51, 0x4b, 0xa9, 0x98, 0x1f, 0x18, 0x8d, 0xfa, 0x7f, 0x81, 0x70, 0x50, 0x97, 0x48, 0xa7, 0xec, 0x6b, 0xd4, 0xf0, 0x1c, 0x16, 0xfd, 0x54, 0xdc, 0x5d, 0x39, 0x2a, 0x6f, 0x6b, 0x02, 0x75, 0xbb, 0x7b, 0x24, 0xe9, 0xa7, 0xc4, 0xd8, 0xd4, 0x00, 0xbb, 0x8f, 0x17, 0x82, 0x87, 0x06, 0x55, 0x11, 0xd4, 0xd9, 0x95, 0x03, 0x84, 0x13, 0xec, 0xf7, 0x60, 0x30, 0xb5, 0xf1, 0xcb, 0x4b, 0xa7, 0x60, 0xd4, 0x4f, 0x4c, 0xfd, 0x37, 0x3b, 0x13, 0x89, 0xc2, 0x95, 0xbe, 0x90, 0xf8, 0x00, 0x7e, 0xcb, 0x24, 0x1e, 0xd9, 0x54, 0x73, 0x7f, 0x1e, 0xdc, 0x6c, 0x27, 0x57, 0x39, 0x2b, 0x94, 0x03, 0x41, 0x4b, 0xeb, 0xa7, 0x2b, 0x03, 0xd6, 0xf4, 0xb2, 0xc4, 0x3f, 0xa1, 0x60, 0x2d, 0x76, 0xa3, 0x43, 0x1f, 0x7c, 0xfe, 0x0e, 0x2f, 0x07, 0xb0, 0x4f, 0x7e, 0xd8, 0xca, 0x9f, 0xcb, 0xec, 0x32, 0x77, 0xce, 0x67, 0xcf, 0x06, 0xbd, 0xd1, 0xe4, 0x3c, 0x24, 0x6e, 0x9e, 0x82, 0x7a, 0x4c, 0xa9, 0xa2, 0xd5, 0xe5, 0xe7, 0x8c, 0x94, 0x1f, 0x6b, 0x1a, 0xf8, 0x2b, 0x31, 0xb1, 0x87, 0xbd, 0xd3, 0x76, 0x0d, 0x41, 0xeb, 0x72, 0x0a, 0xd0, 0x15, 0xad, 0x74, 0x16, 0xb8, 0x53, 0x79, 0xd6, 0xf9, 0xa7, 0x67, 0x6d, 0x0d, 0x22, 0xc2, 0xe2, 0x7c, 0xb6, 0xbc, 0x06, 0x77, 0xe9, 0x1e, 0xcb, 0xd2, 0xfa, 0xfd, 0x68, 0x57, 0x3e, 0x22, 0x5c, 0xe9, 0xe4, 0x61, 0x12, 0xd0, 0x9f, 0xfa, 0x38, 0x31, 0x5f, 0x40, 0xe2, 0x3d, 0xbe, 0x39, 0xff, 0x02, 0x78, 0x31, 0xa4, 0x39, 0xc4, 0xe2, 0xcc, 0x3c, 0xde, 0x8b, 0x24, 0x89, 0x8c, 0xba, 0x3f, 0x2f, 0x1a, 0x23, 0x1c, 0x27, 0x38, 0x34, 0xeb, 0x7a, 0x99, 0x0b, 0x61, 0x2c, 0x6a, 0xd8, 0xe8, 0xcd, 0x64, 0xce, 0x9d, 0x63, 0x31, 0xb0, 0xad, 0xdf, 0x0b, 0x81, 0xcc, 0x78, 0x5b, 0x3f, 0xa7, 0x52, 0x92, 0x5d, 0xf0, 0xd4, 0xed, 0x6f, 0xb1, 0xc3, 0x37, 0xde, 0x57, 0x05, 0xd6, 0xdd, 0x10, 0x14, 0x7c, 0x00, 0xde, 0x08, 0x54, 0x8f, 0x07, 0xb5, 0xd7, 0x8d, 0x1f, 0x5f, 0x6f, 0x5f, 0x27, 0x3c, 0x80, 0xd5, 0xfa, 0xdb, 0x26, 0x0f, 0x63, 0xda, 0x1c, 0xf4, 0xb7, 0x36, 0xc1, 0x1c, 0x79, 0x9d, 0xe3, 0xbd, 0xae, 0x3f, 0xfb, 0x41, 0xe2, 0x41, 0xe8, 0xa9, 0x6e, 0x01, 0x16, 0x59, 0xf0, 0x1c, 0x2b, 0x25, 0xc7, 0x05, 0xc6, 0x72, 0x51, 0x40, 0x4d, 0xa1, 0x2e, 0x31, 0xbf, 0xd9, 0x13, 0x4f, 0x9d, 0x6b, 0xce, 0xfb, 0x4a, 0xb1, 0xb5, 0xfa, 0x1f, 0x94, 0x88, 0x07, 0x43, 0x48, 0x12, 0x63, 0x40, 0x24, 0xaf, 0xfd, 0x67, 0x50, 0x21, 0xc9, 0xf0, 0x3f, 0xaa, 0xeb, 0xb1, 0xcf, 0x48, 0x2d, 0x93, 0x92, 0xfd, 0xdf, 0x21, 0xb1, 0xda, 0xa1, 0x26, 0xca, 0x9d, 0x76, 0xc1, 0xab, 0x01, 0x76, 0x7b, 0x5e, 0x08, 0xa7, 0xd1, 0x1f, 0x1c, 0x89, 0x50, 0x25, 0x39, 0x0e, 0xa5, 0x04, 0x1c, 0x13, 0xf6, 0x21, 0x6e, 0xbe, 0x6f, 0xc9, 0x6e, 0x6c, 0x84, 0xa1, 0x41, 0xdd, 0xa3, 0x95, 0x65, 0x50, 0xcf, 0xca, 0xac, 0x1a, 0xa3, 0x4d, 0x86, 0x12, 0x23, 0x5b, 0x3a, 0xaa, 0x43, 0xac, 0x04, 0x43, 0x81, 0x77, 0xa8, 0x59, 0xf8, 0x0f, 0xb9, 0x33, 0xd9, 0x24, 0x18, 0x3b, 0x53, 0xea, 0x97, 0x88, 0x30, 0x84, 0xb0, 0x72, 0x5b, 0x49, 0x6d, 0x27, 0xbe, 0x14, 0x71, 0x5a, 0xf2, 0x9d, 0x21, 0xae, 0xc2, 0xf6, 0x37, 0x31, 0xd1, 0xba, 0x66, 0x58, 0xfe, 0xf9, 0x31, 0x3e, 0x51, 0x95, 0xd4, 0x8f, 0x27, 0x7f, 0x91, 0xeb, 0xe6, 0x94, 0x16, 0xb6, 0x0e, 0x2e, 0x28, 0x9f, 0x8d, 0x2e, 0x47, 0x37, 0x01, 0x9c, 0x60, 0x16, 0xc1, 0xd6, 0x06, 0xfc, 0x59, 0x55, 0x1d, 0x2e, 0xbf, 0x62, 0x72, 0xc3, 0xaa, 0x18, 0x5e, 0x97, 0x33, 0x61, 0x8a, 0x8a, 0xa0, 0xdd, 0x4a, 0x41, 0x4e, 0x72, 0xf9, 0x98, 0xbf, 0xc7, 0x06, 0xbb, 0x35, 0x61, 0x48, 0x60, 0xdf, 0xa1, 0xba, 0xcf, 0x25, 0x4a, 0x09, 0x3b, 0xff, 0xd6, 0xf1, 0xc0, 0xc7, 0xa3, 0x2e, 0x50, 0x3e, 0x07, 0xb7, 0x02, 0x29, 0xbd, 0x69, 0x70, 0xd7, 0xa5, 0xc4, 0xda, 0x7a, 0xc5, 0xdb, 0x0c, 0x2e, 0xde, 0xc7, 0x4d, 0x57, 0x93, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x07, 0xec, 0x81, 0x00, 0x00, 0x48, 0x7d, 0x43, 0xe1, 0xdb, 0x54, 0x90, 0x90, 0x65, 0x1f, 0xe2, 0xac, 0x42, 0x11, 0xbc, 0x94, 0x4c, 0xa0, 0x08, 0x07, 0x9e, 0x23, 0x7d, 0x4f, 0x8b, 0xe8, 0x12, 0x1c, 0x18, 0xbb, 0x03, 0x67, 0x80, 0x32, 0x88, 0x98, 0x45, 0x91, 0xb1, 0x4a, 0x0f, 0xca, 0x75, 0xf8, 0x03, 0x59, 0x88, 0x96, 0x5a, 0x37, 0xf3, 0xd1, 0xe0, 0x0d, 0x27, 0x07, 0x66, 0x0c, 0xe5, 0x75, 0x2d, 0x29, 0x88, 0xfc, 0xc4, 0x71, 0x0a, 0xd0, 0x78, 0xab, 0xb1, 0x96, 0xca, 0x3f, 0xa5, 0x71, 0x15, 0xc7, 0x16, 0x2e, 0xd6, 0xf4, 0x10, 0x42, 0xf0, 0x28, 0x8d, 0xba, 0xae, 0x36, 0xcc, 0x72, 0xe1, 0x78, 0x40, 0xb1, 0x6d, 0xc9, 0x4b, 0x21, 0x8d, 0xc1, 0xa6, 0x66, 0x4d, 0xa3, 0x4e, 0x4e, 0xc1, 0x0c, 0x6f, 0x54, 0xe3, 0xb8, 0xab, 0x1b, 0xc5, 0xe3, 0x58, 0x9c, 0xbd, 0x89, 0xfb, 0xca, 0xaa, 0x65, 0x2b, 0xf9, 0x7e, 0x59, 0x08, 0xde, 0xb9, 0xc9, 0xde, 0x56, 0x72, 0x61, 0xba, 0xe2, 0x9f, 0x4a, 0x02, 0x03, 0x86, 0xa2, 0x96, 0x33, 0xf8, 0x52, 0xc6, 0xf3, 0x05, 0xb7, 0x80, 0x74, 0xeb, 0x05, 0x6d, 0x07, 0x07, 0x44, 0x2f, 0xf7, 0xc2, 0xf5, 0xd7, 0x20, 0x3a, 0xbb, 0x07, 0x13, 0x70, 0xcc, 0xb1, 0x76, 0x51, 0x4d, 0xfc, 0x33, 0xf4, 0x60, 0x09, 0xf8, 0x47, 0xc8, 0xde, 0x4e, 0x7c, 0x2b, 0x0a, 0x59, 0x9a, 0x89, 0x9c, 0x56, 0x6c, 0xd8, 0x3d, 0x85, 0x8e, 0xa3, 0xe4, 0x20, 0x13, 0xf9, 0xf0, 0x84, 0x4b, 0x51, 0x4e, 0x75, 0xbd, 0xac, 0xd8, 0x5b, 0x04, 0x4b, 0x2e, 0xbe, 0xf3, 0xb7, 0xa3, 0x0a, 0xe2, 0x14, 0x4e, 0xc5, 0xc7, 0x30, 0xc0, 0xe1, 0xab, 0x2c, 0x9d, 0xec, 0x74, 0x64, 0x83, 0x19, 0x6c, 0x05, 0x81, 0x5c, 0x89, 0xf5, 0x99, 0xa2, 0x8b, 0x8b, 0x86, 0x9f, 0xcf, 0xb3, 0x3f, 0xcf, 0x1a, 0x12, 0x49, 0xc6, 0xa6, 0xaa, 0x5c, 0x60, 0x06, 0x99, 0x97, 0x1e, 0x6f, 0x65, 0xe6, 0x91, 0x51, 0x0b, 0x76, 0xca, 0x14, 0x05, 0x37, 0x77, 0x7f, 0x9c, 0x0e, 0xc9, 0x15, 0x05, 0xd7, 0xd9, 0x52, 0x16, 0x68, 0x41, 0x27, 0xb4, 0x95, 0xe1, 0xf1, 0xe6, 0x35, 0x66, 0x0f, 0x26, 0xc5, 0x42, 0xde, 0xab, 0xa8, 0x34, 0x16, 0xb1, 0x4e, 0x7c, 0x5a, 0xc1, 0xe7, 0x29, 0x5b, 0x32, 0x44, 0x3d, 0xea, 0xa0, 0x59, 0xf6, 0xa9, 0x7a, 0x94, 0x9f, 0x14, 0x0c, 0xc5, 0x75, 0xc7, 0x94, 0xfc, 0xcf, 0xd7, 0x0c, 0xe6, 0x1b, 0x79, 0x9b, 0x9e, 0x25, 0xfd, 0x0e, 0xa1, 0xe3, 0xb7, 0xf4, 0x35, 0x71, 0x7e, 0xd0, 0x18, 0x50, 0x07, 0xf6, 0xe8, 0xf5, 0x82, 0x29, 0xd6, 0x47, 0xa8, 0x4f, 0xa7, 0xd0, 0xcb, 0x87, 0x60, 0xdb, 0x7a, 0xe8, 0x7b, 0x30, 0xbc, 0xeb, 0x43, 0x81, 0x9f, 0x1e, 0x1b, 0xec, 0xef, 0x0e, 0x52, 0x9b, 0xd8, 0x5d, 0xb5, 0x45, 0x3e, 0x35, 0x33, 0xc7, 0x99, 0x7b, 0xdb, 0x12, 0xb9, 0x92, 0xa7, 0xd6, 0xb3, 0xf1, 0x7e, 0x53, 0x61, 0x40, 0x1f, 0xf2, 0x48, 0x45, 0xc3, 0x9f, 0xbe, 0x1f, 0xb8, 0x24, 0x2b, 0xa3, 0xcb, 0x0d, 0xbf, 0xe5, 0xb0, 0x1a, 0x0a, 0xc0, 0xd8, 0x68, 0x51, 0x1e, 0x06, 0xfe, 0xad, 0x88, 0x0f, 0xd3, 0xe5, 0x49, 0x9e, 0xf6, 0x79, 0x21, 0x68, 0x98, 0xda, 0x23, 0x5b, 0x55, 0xd9, 0xba, 0xd9, 0xa4, 0x6a, 0x47, 0x46, 0x3d, 0xbc, 0xd7, 0xdb, 0xa6, 0x95, 0x8a, 0x24, 0xc8, 0x52, 0x95, 0xfd, 0xcd, 0xf2, 0x71, 0x8c, 0x60, 0x7e, 0x60, 0xa4, 0xe4, 0xe0, 0x42, 0x25, 0x2c, 0x33, 0x5a, 0x94, 0x87, 0xd0, 0x95, 0x00, 0xd8, 0xd1, 0xa2, 0x85, 0xa5, 0x04, 0xd6, 0x61, 0xb3, 0x3b, 0xa1, 0x01, 0xb1, 0xe8, 0x1d, 0xd0, 0x1b, 0xf1, 0xbc, 0xda, 0xe7, 0xa9, 0x07, 0x58, 0xd5, 0x82, 0x68, 0x09, 0x1a, 0xe3, 0xb4, 0x1e, 0x98, 0x8b, 0xce, 0xe1, 0x73, 0xf2, 0x5e, 0xa8, 0xe7, 0xa3, 0x66, 0xb1, 0x47, 0x61, 0x56, 0x70, 0x90, 0xb2, 0xe0, 0x39, 0x24, 0x7f, 0x02, 0x73, 0x65, 0x0d, 0x0f, 0x51, 0x93, 0x22, 0x82, 0x20, 0xd1, 0xdd, 0x4c, 0x3e, 0xe8, 0xf6, 0xe1, 0x16, 0x58, 0xaf, 0x1f, 0x03, 0xd5, 0x8b, 0x28, 0x2d, 0x19, 0x74, 0x28, 0xe5, 0xbc, 0x76, 0xb2, 0x0e, 0xb5, 0xb4, 0x69, 0x5b, 0xf4, 0x87, 0x5a, 0xd9, 0xcd, 0x9f, 0xf4, 0xe0, 0xfc, 0x92, 0xf2, 0x16, 0x34, 0x9a, 0xc7, 0x36, 0x62, 0xff, 0x47, 0x19, 0x11, 0x02, 0x45, 0x67, 0xfa, 0xec, 0x45, 0xf0, 0xaf, 0x5b, 0x85, 0x21, 0x10, 0x49, 0xcc, 0x1b, 0xeb, 0x60, 0x02, 0x89, 0x52, 0x06, 0xf7, 0x72, 0xeb, 0xd2, 0xf6, 0x69, 0x44, 0xe4, 0xbe, 0xc0, 0x46, 0x16, 0xf7, 0x61, 0x55, 0x0b, 0x15, 0x61, 0x37, 0x4a, 0x7e, 0x1e, 0xa5, 0xd3, 0xe7, 0xab, 0x13, 0xca, 0xab, 0x33, 0x13, 0x77, 0x25, 0x9e, 0x2e, 0xf1, 0xe2, 0x00, 0x9c, 0x17, 0x38, 0x65, 0xb1, 0x49, 0x34, 0xea, 0xd8, 0x28, 0x3d, 0x6f, 0xc3, 0x6d, 0xc6, 0x9e, 0xdc, 0xe3, 0x28, 0xae, 0xe4, 0xf2, 0x2b, 0x6e, 0x78, 0x0b, 0x78, 0x09, 0xdb, 0x76, 0x56, 0x98, 0x8e, 0x71, 0xb4, 0x09, 0x76, 0x72, 0xd0, 0xfe, 0x3a, 0x5c, 0x6f, 0x0f, 0x30, 0x68, 0xb5, 0x14, 0x84, 0x83, 0xfc, 0xd4, 0x65, 0x92, 0x35, 0xec, 0x32, 0xbd, 0xb7, 0x76, 0x5f, 0xa9, 0x51, 0x61, 0x54, 0x00, 0x69, 0x67, 0x2f, 0x74, 0x2c, 0x42, 0xf6, 0xed, 0x17, 0x0d, 0x40, 0x43, 0x68, 0xcf, 0x66, 0xb2, 0x98, 0x36, 0x99, 0x2a, 0x72, 0x69, 0xff, 0x4c, 0xa4, 0x71, 0x69, 0xf3, 0x62, 0xb5, 0x21, 0xac, 0x9f, 0x7c, 0x5e, 0x15, 0x95, 0xd4, 0xbb, 0xa1, 0x5b, 0xc8, 0x73, 0x9a, 0x1f, 0xdb, 0x57, 0x7e, 0x3e, 0x9e, 0x80, 0x99, 0xab, 0xb6, 0xd4, 0x9f, 0xf0, 0xaf, 0x95, 0x2b, 0xf6, 0x39, 0x41, 0xb1, 0x77, 0x53, 0x45, 0xc7, 0x66, 0x17, 0xa9, 0xce, 0xac, 0xd5, 0xa9, 0xcf, 0xc3, 0x9e, 0x51, 0xe0, 0x5b, 0x1a, 0x84, 0xbe, 0xcd, 0xc7, 0xa6, 0x56, 0x29, 0x05, 0x57, 0x3a, 0xfe, 0xf1, 0x6f, 0x3e, 0xc6, 0x02, 0x2b, 0xbe, 0xd9, 0xa8, 0xa3, 0x6e, 0x0e, 0xe1, 0x86, 0x3a, 0xdc, 0x9a, 0x81, 0x71, 0xd8, 0x1d, 0xb4, 0xd7, 0xf0, 0xd7, 0x20, 0x45, 0x41, 0x79, 0xe3, 0x1c, 0xe1, 0x80, 0xb4, 0xb1, 0x11, 0xab, 0x25, 0xdd, 0xb5, 0x4e, 0x75, 0x29, 0x40, 0xcf, 0x83, 0x81, 0xd8, 0x28, 0x07, 0x36, 0x9c, 0xa8, 0x9c, 0x77, 0x6d, 0x77, 0x5f, 0xc6, 0x70, 0xf3, 0x5a, 0x95, 0x04, 0x1f, 0x13, 0x1d, 0x14, 0xad, 0xa5, 0xb9, 0x9c, 0xcb, 0xdf, 0xda, 0xe1, 0x71, 0xb4, 0x08, 0x26, 0x2b, 0x4e, 0x44, 0xd7, 0x0d, 0x7c, 0xfb, 0xb9, 0x1a, 0xab, 0xb3, 0xab, 0x03, 0x01, 0xda, 0xb1, 0x99, 0x15, 0x23, 0x8e, 0xaa, 0x46, 0x3f, 0x93, 0x36, 0xb6, 0x94, 0x96, 0x0e, 0x6e, 0x92, 0x70, 0x3c, 0x27, 0xf8, 0x7e, 0x89, 0xfa, 0xcd, 0x2d, 0xa8, 0xf0, 0xb7, 0x81, 0x38, 0x54, 0xac, 0xca, 0xec, 0xc0, 0x3b, 0x51, 0x7f, 0x86, 0xb7, 0xa3, 0x03, 0x0c, 0x71, 0xf4, 0xa3, 0x8d, 0x4a, 0x73, 0x03, 0x81, 0xe8, 0x5c, 0x20, 0x39, 0x97, 0xbd, 0x1b, 0x05, 0xcc, 0xe0, 0x9d, 0x7e, 0x7b, 0x48, 0x7e, 0xdb, 0x39, 0xdf, 0xe6, 0xf2, 0xc4, 0x45, 0xf5, 0xe2, 0xc7, 0xb7, 0xe8, 0xb8, 0x1b, 0x50, 0xa3, 0xf5, 0xf4, 0xb1, 0x81, 0x60, 0xbd, 0x69, 0x20, 0x6e, 0x63, 0xa3, 0x80, 0xe6, 0x54, 0x4b, 0x57, 0x93, 0x9d, 0x9e, 0xa2, 0x6a, 0xb4, 0x43, 0x6b, 0x02, 0xdd, 0x1d, 0xa0, 0xec, 0xe3, 0xa1, 0x3c, 0x64, 0x4d, 0xec, 0xf4, 0x33, 0x37, 0x5e, 0x24, 0x1e, 0x96, 0x42, 0x89, 0x86, 0x0b, 0x47, 0x88, 0xf1, 0x8a, 0xa9, 0x30, 0xb3, 0x54, 0xc1, 0xa7, 0x1f, 0x88, 0xb5, 0xac, 0xf7, 0x70, 0xfc, 0x00, 0x14, 0xd7, 0xdc, 0x43, 0x4e, 0xbd, 0x20, 0x1f, 0x55, 0x63, 0xb3, 0x04, 0xff, 0x01, 0x83, 0xcb, 0x37, 0x6b, 0xf7, 0x18, 0x81, 0x0c, 0xf3, 0x95, 0x0e, 0xe2, 0x1e, 0x2b, 0x55, 0x69, 0xbe, 0x05, 0x66, 0xe0, 0x8e, 0x67, 0x90, 0xe6, 0xcf, 0xb9, 0x1b, 0xa3, 0x83, 0x17, 0x43, 0xdb, 0x06, 0xe7, 0x38, 0x38, 0x69, 0x08, 0x8f, 0x82, 0xc0, 0xa7, 0x33, 0xdf, 0xf4, 0x84, 0xe0, 0x43, 0xd2, 0x20, 0x0d, 0xcf, 0x15, 0x63, 0x67, 0xa9, 0xe1, 0xb3, 0x9d, 0xe5, 0x5c, 0x90, 0xc3, 0x11, 0xfc, 0x4d, 0x88, 0x3c, 0x5e, 0x26, 0x57, 0x15, 0x3e, 0xcd, 0xce, 0x7b, 0xd3, 0xfd, 0x2d, 0x5e, 0x0c, 0x9e, 0x48, 0x9b, 0xb9, 0x20, 0x0f, 0x7a, 0xce, 0x92, 0x86, 0xc8, 0x50, 0xef, 0xa5, 0xe8, 0x84, 0x9a, 0xb5, 0x2b, 0xa5, 0x0e, 0xdc, 0x2c, 0x2c, 0x47, 0x68, 0x2a, 0x1c, 0xb9, 0x3c, 0x91, 0x24, 0x60, 0x24, 0xe7, 0xfd, 0x22, 0x78, 0xcd, 0x64, 0x3e, 0x05, 0xb9, 0x4f, 0x28, 0x98, 0x4b, 0x83, 0x73, 0x81, 0x21, 0x00, 0xea, 0x33, 0x03, 0x7e, 0xe2, 0x6c, 0x22, 0xac, 0x71, 0xcc, 0x38, 0x6f, 0xdd, 0x93, 0xd0, 0xef, 0x27, 0x6e, 0xb8, 0xf9, 0x15, 0x60, 0xfe, 0x59, 0x76, 0xb7, 0x30, 0x65, 0xaf, 0xdd, 0xc7, 0x89, 0x06, 0x15, 0x89, 0x82, 0x49, 0x2a, 0xcb, 0x9a, 0xda, 0xbe, 0x98, 0x74, 0xa4, 0x38, 0x41, 0xd0, 0x8e, 0x37, 0x59, 0x00, 0x82, 0x12, 0xec, 0xe1, 0x98, 0x6a, 0x7e, 0xc8, 0x19, 0xee, 0xba, 0xd2, 0xb3, 0xf3, 0x66, 0x75, 0xc4, 0xf6, 0x35, 0x80, 0xc2, 0x39, 0x04, 0xc2, 0x78, 0xde, 0xd9, 0xa7, 0x19, 0x4e, 0xb8, 0xc0, 0xf1, 0xda, 0x29, 0x01, 0xee, 0xd1, 0xda, 0xa4, 0xb0, 0xdd, 0x1e, 0x15, 0x90, 0x0d, 0x0d, 0x86, 0xd3, 0xb4, 0xf2, 0xf4, 0x3b, 0x05, 0x67, 0x8c, 0x39, 0x6f, 0xfe, 0x4b, 0xa5, 0xa1, 0x21, 0x24, 0x0f, 0x81, 0xf4, 0x3c, 0x38, 0xff, 0xd6, 0x0d, 0x26, 0xb5, 0x3f, 0xd8, 0x2b, 0xcd, 0x77, 0xe2, 0xab, 0xd0, 0xa6, 0xa4, 0x70, 0xfb, 0x2a, 0x8a, 0xbe, 0x07, 0x4e, 0x91, 0x6f, 0x3b, 0x1e, 0xe8, 0x13, 0xfa, 0xd3, 0xd6, 0xf6, 0x74, 0x67, 0xc9, 0xb1, 0xf1, 0x7f, 0x9a, 0xe8, 0xae, 0x0c, 0x1f, 0xa8, 0xf9, 0xcc, 0x34, 0x39, 0x04, 0x96, 0x28, 0x6b, 0x5e, 0xa9, 0x11, 0x37, 0xe1, 0xf5, 0x53, 0x40, 0x17, 0x40, 0xea, 0x99, 0x81, 0x93, 0x99, 0x4c, 0x6a, 0x78, 0x4d, 0x54, 0x4c, 0x6a, 0x66, 0x93, 0xf3, 0xe8, 0x8f, 0x8e, 0xfa, 0xd7, 0x13, 0x0f, 0xe8, 0xcc, 0x46, 0x3c, 0x92, 0x1a, 0xb3, 0x66, 0x15, 0x3b, 0x9d, 0xab, 0x81, 0x34, 0x6f, 0xac, 0x15, 0x62, 0x44, 0xa3, 0x67, 0x50, 0xa9, 0x77, 0x1d, 0x1e, 0x0f, 0xac, 0xb4, 0xcb, 0xaf, 0x29, 0xbc, 0x36, 0xfb, 0xdb, 0x5c, 0x83, 0x4b, 0x35, 0xd5, 0x77, 0x59, 0x0e, 0x21, 0x46, 0x57, 0x21, 0xb6, 0x1a, 0x10, 0xcb, 0xc1, 0x7a, 0x7f, 0x58, 0xfd, 0xe7, 0xaa, 0x5b, 0xcb, 0xa4, 0xdd, 0x2c, 0x12, 0x2d, 0xd2, 0xf3, 0x62, 0xda, 0xe7, 0x4e, 0x57, 0x34, 0xcf, 0x76, 0xf7, 0x01, 0x2f, 0x7b, 0xa1, 0x65, 0x02, 0x98, 0xad, 0xe2, 0xa2, 0x60, 0xf0, 0xf6, 0xed, 0x98, 0xcc, 0xc3, 0x33, 0x4f, 0x19, 0xf7, 0xee, 0x85, 0xd4, 0x26, 0x2c, 0x22, 0xb8, 0x34, 0xe6, 0xfe, 0xce, 0xec, 0x4d, 0x96, 0x87, 0x58, 0x83, 0xd5, 0x36, 0x04, 0x6d, 0x52, 0x60, 0x65, 0x0e, 0x31, 0x59, 0x53, 0xb7, 0x12, 0x06, 0x6b, 0x0a, 0xf6, 0x45, 0x56, 0x5e, 0xaf, 0x86, 0x17, 0xbc, 0x1b, 0x10, 0xc8, 0x04, 0x63, 0xe6, 0xf2, 0xa9, 0x55, 0xe5, 0x00, 0x16, 0xc8, 0x7c, 0x0e, 0xa2, 0x1a, 0x60, 0x94, 0xf8, 0x75, 0x5e, 0x08, 0x67, 0xcd, 0x3a, 0x84, 0x45, 0x88, 0x71, 0x45, 0x82, 0x93, 0x01, 0xd0, 0x80, 0x12, 0x02, 0x2c, 0x55, 0xe2, 0x70, 0x0c, 0x05, 0xdf, 0xff, 0xa3, 0x8e, 0x57, 0x1f, 0xbe, 0x01, 0x6b, 0x98, 0x40, 0xde, 0x60, 0xdb, 0x18, 0xa3, 0xe6, 0xfc, 0x79, 0x4e, 0xac, 0x04, 0x99, 0x8c, 0xca, 0xb2, 0x32, 0xc9, 0x51, 0x7c, 0x6f, 0x97, 0xca, 0x42, 0x4c, 0xd4, 0x07, 0x2b, 0x0c, 0x19, 0x69, 0x32, 0x1c, 0x86, 0xa2, 0x57, 0x6d, 0xbd, 0x0c, 0x6c, 0xd7, 0xb4, 0x24, 0xff, 0x63, 0x88, 0xe8, 0xef, 0x2b, 0x66, 0x63, 0xa8, 0xea, 0x4b, 0x27, 0x02, 0xac, 0x5b, 0x01, 0xa8, 0xdf, 0x9f, 0x5b, 0x8a, 0xf0, 0xaf, 0x60, 0x9b, 0xe4, 0x90, 0xd0, 0x90, 0x92, 0x6c, 0x5b, 0x9a, 0x0c, 0x03, 0x9f, 0xd7, 0x5d, 0x28, 0xfa, 0x27, 0xcb, 0x1d, 0x6b, 0x2b, 0x43, 0x50, 0x6b, 0xca, 0xd5, 0x1b, 0x41, 0x35, 0x59, 0x28, 0x97, 0x56, 0x2f, 0xa8, 0x03, 0x9a, 0xd9, 0x91, 0xff, 0x17, 0x32, 0x0b, 0x28, 0x49, 0x11, 0x37, 0x07, 0x05, 0xb3, 0x21, 0x51, 0xf3, 0x08, 0x81, 0x7a, 0xc1, 0x5d, 0x37, 0x40, 0xa5, 0xe8, 0x31, 0xcf, 0x85, 0x01, 0x3f, 0xa6, 0xff, 0xa4, 0x06, 0xcb, 0x8a, 0x8b, 0xd8, 0x18, 0xa7, 0x90, 0xaf, 0x81, 0x36, 0x4d, 0xdf, 0x81, 0xd9, 0xd0, 0x63, 0x61, 0x9e, 0xc8, 0x33, 0x5a, 0xd5, 0xc9, 0xaa, 0x66, 0xcb, 0x56, 0x57, 0xc5, 0x06, 0x71, 0x68, 0xbf, 0xe0, 0x1a, 0x8e, 0xf7, 0x7a, 0x02, 0xa9, 0x62, 0x43, 0xc0, 0xe7, 0x31, 0x7d, 0x27, 0xfe, 0xc6, 0xd9, 0xd5, 0xb4, 0xee, 0x0d, 0x50, 0x8a, 0x2a, 0xf4, 0xac, 0x9b, 0x78, 0x10, 0x0d, 0xc9, 0xef, 0x01, 0x84, 0x34, 0xd4, 0x0e, 0x7a, 0xfb, 0xc9, 0x1c, 0x13, 0x4e, 0xd6, 0x44, 0xd1, 0xed, 0x3c, 0xf4, 0x04, 0x15, 0xd0, 0x7a, 0xbc, 0x0f, 0x5c, 0x2d, 0xa6, 0x42, 0xaa, 0xdf, 0xfc, 0xde, 0x35, 0xf4, 0x7b, 0x38, 0x7f, 0xe3, 0x99, 0x72, 0x5f, 0x07, 0x33, 0x9f, 0x71, 0x73, 0x02, 0x2f, 0x5a, 0xba, 0x5a, 0xd3, 0x21, 0x42, 0x56, 0xf4, 0x52, 0x7d, 0x2a, 0xe7, 0x00, 0x23, 0xea, 0x76, 0xea, 0x2e, 0xb4, 0x89, 0xc5, 0x5a, 0x0d, 0x69, 0xa0, 0x0b, 0xad, 0x8e, 0xd5, 0x20, 0x23, 0x2d, 0xb0, 0xf8, 0x8e, 0xa2, 0x78, 0xc3, 0xea, 0xd4, 0x48, 0x4d, 0x66, 0x88, 0x83, 0xc1, 0xa6, 0x9f, 0xf7, 0x6d, 0x3d, 0x44, 0xce, 0x22, 0x4d, 0x4a, 0x43, 0xcd, 0x81, 0xa1, 0x06, 0xa9, 0x32, 0xfc, 0x47, 0x0c, 0x41, 0x5c, 0xa1, 0x74, 0x0e, 0x36, 0xd5, 0x9c, 0x93, 0x23, 0x0a, 0xdb, 0x15, 0x33, 0x6c, 0xa3, 0xa1, 0xa1, 0xf6, 0x7a, 0xbc, 0xe9, 0xa3, 0x5e, 0x83, 0x0a, 0xa1, 0x9a, 0xd7, 0xb5, 0x70, 0x78, 0xc5, 0xb1, 0xdf, 0x94, 0xf6, 0x16, 0xa4, 0x5e, 0xe6, 0x4b, 0xa6, 0x2e, 0x89, 0x7f, 0xb2, 0xde, 0x40, 0x50, 0x39, 0x8e, 0xf5, 0x52, 0x80, 0xbe, 0x56, 0x8f, 0x5e, 0x3e, 0xd0, 0x91, 0x82, 0x73, 0xf7, 0x9a, 0xe9, 0xef, 0x2a, 0x70, 0xe1, 0x5d, 0xbf, 0xf8, 0xca, 0x05, 0x0c, 0x20, 0xd8, 0xf9, 0xb7, 0x1f, 0xf9, 0xdf, 0xc8, 0x38, 0xd2, 0x21, 0x77, 0xd8, 0x2e, 0x6b, 0xda, 0x11, 0x7d, 0x01, 0xfa, 0x5d, 0x95, 0x44, 0x86, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x07, 0xec, 0x81, 0x00, 0x00, 0xc4, 0x7a, 0x33, 0xf0, 0xf1, 0x4a, 0x5d, 0x7b, 0xe7, 0x1a, 0x12, 0xf2, 0x08, 0x12, 0xe6, 0x62, 0x45, 0x6b, 0x2e, 0x93, 0x7b, 0x50, 0x92, 0x1f, 0x9a, 0x78, 0x3c, 0x07, 0x79, 0xb9, 0x82, 0x19, 0xd8, 0x11, 0xfe, 0x50, 0x92, 0x8d, 0x73, 0x56, 0xea, 0xc3, 0x5c, 0xa3, 0x91, 0x81, 0x82, 0x45, 0xc7, 0x40, 0x35, 0xcf, 0xe3, 0x65, 0xa3, 0xbc, 0xf5, 0xe2, 0xc6, 0xc2, 0x05, 0xc0, 0xe3, 0x4d, 0x44, 0xc5, 0xa3, 0xd7, 0xea, 0x96, 0x0f, 0x34, 0xb2, 0x87, 0x35, 0xaa, 0x76, 0xb8, 0xe3, 0x3e, 0x97, 0x71, 0x5f, 0xbc, 0xc8, 0x27, 0x9e, 0x78, 0xbd, 0x1c, 0x0f, 0x04, 0x19, 0x0f, 0x1e, 0x42, 0x25, 0x98, 0xea, 0x68, 0x09, 0x1c, 0x6a, 0xb9, 0xb6, 0xe2, 0xec, 0x1d, 0x7d, 0xf7, 0xca, 0x9f, 0x24, 0xe1, 0x97, 0x2b, 0xb5, 0x1e, 0xc8, 0x2e, 0x8f, 0x98, 0x5f, 0xc2, 0x32, 0xe0, 0x39, 0xc7, 0xd9, 0xf9, 0x8d, 0x58, 0xae, 0xb5, 0x2e, 0xee, 0xda, 0xf7, 0x85, 0x28, 0x9b, 0x49, 0xb9, 0xd4, 0x62, 0x72, 0x54, 0x9a, 0xac, 0x78, 0x42, 0x58, 0xdd, 0x00, 0x42, 0x77, 0xd1, 0xc0, 0xdc, 0xc2, 0xdd, 0xe8, 0x43, 0x35, 0x5a, 0x69, 0xa8, 0x58, 0xe0, 0xe1, 0x11, 0xe7, 0x8a, 0xa8, 0x37, 0x4a, 0xbd, 0x79, 0x03, 0x17, 0x9e, 0xd8, 0xa5, 0x3f, 0x0d, 0x7e, 0x78, 0xf8, 0xa2, 0x50, 0x40, 0x82, 0x34, 0xf2, 0x95, 0x89, 0x72, 0x61, 0xd3, 0x2b, 0x33, 0x25, 0x90, 0x7f, 0xba, 0x74, 0xde, 0xed, 0x69, 0x71, 0x01, 0xad, 0x0a, 0x9b, 0x89, 0x03, 0xcc, 0x2a, 0x87, 0xfb, 0x55, 0x5c, 0xc3, 0xce, 0xd9, 0x3b, 0x59, 0x26, 0xa3, 0x57, 0x00, 0xb2, 0x81, 0x90, 0x26, 0x2f, 0x24, 0x8b, 0x83, 0xa7, 0x85, 0x76, 0xb8, 0xcd, 0xe1, 0x5c, 0xd4, 0xac, 0x38, 0x78, 0x19, 0x9a, 0xff, 0x3b, 0xa5, 0x68, 0x2c, 0x04, 0xed, 0xfd, 0x11, 0x28, 0xda, 0xdc, 0xf3, 0xf9, 0x43, 0x06, 0x8b, 0x76, 0xb4, 0xd2, 0xaf, 0xb2, 0x4f, 0x52, 0xa9, 0x40, 0xaf, 0x53, 0x1c, 0xac, 0xdb, 0x51, 0x82, 0xda, 0x7b, 0xba, 0x11, 0xef, 0x4f, 0xa3, 0x9c, 0x71, 0x7c, 0xd8, 0x9c, 0x22, 0x5c, 0x1f, 0x21, 0xbf, 0x42, 0xce, 0xce, 0x4b, 0x2a, 0xdb, 0x37, 0xaa, 0x6d, 0x71, 0x75, 0xf2, 0x84, 0x39, 0x3f, 0xac, 0x2e, 0xcd, 0xa2, 0x8e, 0xde, 0x90, 0x0d, 0xcb, 0x3c, 0x7b, 0x40, 0xa9, 0xc8, 0x2c, 0x1f, 0x45, 0x77, 0x04, 0xef, 0x7d, 0xfd, 0x42, 0x37, 0x06, 0x22, 0xc7, 0x13, 0x55, 0x17, 0x0b, 0xd7, 0xeb, 0xea, 0x29, 0x6e, 0x83, 0xa8, 0x09, 0xc8, 0x97, 0x27, 0x11, 0x4a, 0xd0, 0x92, 0xcf, 0x96, 0xf7, 0x38, 0x0f, 0x05, 0xe4, 0xe2, 0x8f, 0xc5, 0x4e, 0x26, 0xf6, 0x97, 0xf0, 0x49, 0x47, 0xe6, 0xf6, 0x38, 0x47, 0xc4, 0xf8, 0x33, 0x6b, 0x78, 0xca, 0x83, 0x4d, 0xbc, 0xaf, 0xf2, 0x08, 0xde, 0x9d, 0xcf, 0xb4, 0xe3, 0xc1, 0x8b, 0x17, 0x37, 0x3e, 0x08, 0x9f, 0x9b, 0x8b, 0xc7, 0x07, 0x84, 0x26, 0x04, 0xb6, 0x9d, 0x3d, 0x43, 0x7a, 0xf1, 0x4a, 0x92, 0xec, 0x21, 0xcc, 0x0c, 0xe5, 0x77, 0x29, 0xf1, 0x68, 0xd6, 0x5d, 0x19, 0x3f, 0x4f, 0xa3, 0x4c, 0x66, 0x60, 0x8f, 0xf4, 0x6e, 0x66, 0x87, 0xdd, 0x75, 0x80, 0x95, 0x77, 0xf1, 0xb7, 0x95, 0x82, 0xc1, 0xab, 0x3d, 0xa0, 0x65, 0xbb, 0xc3, 0xeb, 0xa2, 0x84, 0xa6, 0x6e, 0x4a, 0xf5, 0x52, 0x92, 0x35, 0x7b, 0xb1, 0x7d, 0xb0, 0x28, 0x34, 0xa1, 0x4e, 0x91, 0xcf, 0x9d, 0xea, 0x5c, 0x4e, 0xa5, 0xa8, 0xe1, 0x80, 0x35, 0x33, 0x0c, 0x15, 0x9a, 0x6a, 0xf3, 0x28, 0x14, 0x4d, 0x31, 0x52, 0x62, 0x70, 0x89, 0x8a, 0x78, 0x7a, 0x99, 0xa8, 0x64, 0x33, 0x24, 0xb5, 0xe1, 0x51, 0x78, 0x05, 0xb4, 0x27, 0xb4, 0xec, 0xe6, 0x53, 0x4d, 0x1f, 0x3b, 0x41, 0xb5, 0x04, 0x49, 0xe6, 0x1e, 0x1b, 0x2a, 0x7b, 0x88, 0x46, 0xf7, 0xc2, 0x57, 0xa2, 0x6a, 0x8b, 0xca, 0x95, 0x56, 0xf9, 0xf1, 0x27, 0x96, 0x57, 0xf1, 0x30, 0x41, 0x5e, 0x8e, 0x95, 0x5b, 0xc8, 0xd2, 0xbd, 0xe7, 0x2b, 0x39, 0x0a, 0xf3, 0xd9, 0xd0, 0xb0, 0xd7, 0x98, 0x24, 0xf5, 0xeb, 0x1d, 0xde, 0x1c, 0x89, 0xb9, 0x39, 0x0c, 0xd0, 0x54, 0xc8, 0xc2, 0x9d, 0x6c, 0x58, 0xb5, 0xcc, 0xc9, 0xfb, 0x43, 0x82, 0x70, 0x87, 0xe5, 0xae, 0xb3, 0x84, 0x21, 0x1e, 0x16, 0xce, 0xb2, 0xed, 0x08, 0xee, 0xee, 0xd1, 0x97, 0x8d, 0xd7, 0x27, 0x20, 0xe9, 0x61, 0x8d, 0x7a, 0x7e, 0xc8, 0x4c, 0xa8, 0x91, 0xfc, 0x7e, 0x8b, 0x44, 0xa1, 0xdd, 0x87, 0x61, 0x39, 0xc6, 0x85, 0x5d, 0xff, 0x32, 0x65, 0x23, 0x46, 0x8b, 0xe6, 0xaa, 0x62, 0x7c, 0x71, 0xca, 0x6e, 0xf5, 0xeb, 0xde, 0xb8, 0x98, 0x1f, 0x21, 0x6b, 0xe2, 0xe4, 0x1b, 0x2c, 0x25, 0x4b, 0x07, 0xae, 0x2d, 0x32, 0xf2, 0x53, 0x24, 0x11, 0xd5, 0xfd, 0x52, 0x49, 0x56, 0x6e, 0x60, 0xd0, 0x17, 0x3a, 0xa4, 0x5c, 0xda, 0x12, 0x49, 0x7c, 0x0d, 0xed, 0x0f, 0xce, 0xc0, 0x42, 0x01, 0xe9, 0x12, 0x13, 0x06, 0x62, 0x36, 0x69, 0x8a, 0xca, 0xdf, 0xda, 0x1a, 0xac, 0xc1, 0x4b, 0x12, 0xb2, 0xbd, 0xbd, 0x75, 0x60, 0x64, 0x83, 0x7d, 0xfb, 0x4c, 0xad, 0xad, 0x03, 0x7b, 0x01, 0x3e, 0x27, 0x26, 0x30, 0xb3, 0x22, 0x27, 0x44, 0x95, 0xe6, 0x82, 0xd0, 0xcd, 0x49, 0xfd, 0x6e, 0x0b, 0x8b, 0x24, 0xd7, 0x14, 0xf4, 0xc2, 0xf3, 0xb1, 0x2d, 0x66, 0xa6, 0x51, 0x4e, 0x12, 0xd6, 0x4f, 0x7e, 0xa8, 0x4d, 0x73, 0x5d, 0x9d, 0xe9, 0x29, 0xfe, 0xc2, 0xcb, 0x83, 0x1a, 0x03, 0x19, 0x91, 0xd8, 0x3d, 0x88, 0x5e, 0x35, 0xb9, 0x9a, 0x96, 0x0b, 0x3d, 0x3a, 0x1a, 0xc9, 0xa9, 0x03, 0x61, 0x18, 0xfd, 0xcf, 0x5c, 0x1c, 0x6d, 0xa5, 0x59, 0xe0, 0xcf, 0x1a, 0x08, 0x5a, 0xbf, 0xd7, 0x7d, 0x55, 0xc9, 0xd9, 0x50, 0x72, 0x2b, 0xad, 0xc6, 0xc5, 0x05, 0xb4, 0xad, 0x06, 0xf7, 0xda, 0xb0, 0xc0, 0xa2, 0x9b, 0xc2, 0xfd, 0xf9, 0x4a, 0xdf, 0x3f, 0x67, 0xde, 0x18, 0x2f, 0xe5, 0xc9, 0x62, 0x26, 0x5e, 0x69, 0xf4, 0x1b, 0x1d, 0x74, 0xa2, 0xcb, 0x9f, 0xde, 0xa8, 0xbb, 0xa9, 0xaa, 0xe5, 0xa8, 0x80, 0x5b, 0x16, 0x60, 0xaf, 0xe8, 0xee, 0x21, 0x5c, 0x17, 0x61, 0xa2, 0x1b, 0x1e, 0x63, 0xc7, 0x01, 0x71, 0x4c, 0x73, 0xd8, 0xed, 0x74, 0xf8, 0xa5, 0x3e, 0x8c, 0xec, 0x6e, 0xd5, 0x54, 0xb6, 0x59, 0x91, 0xf9, 0x28, 0xb8, 0xb4, 0x50, 0x28, 0x1c, 0xf9, 0x5e, 0x0e, 0x60, 0x1b, 0xb4, 0x9d, 0xad, 0x1e, 0x6e, 0xa3, 0x8e, 0xfd, 0xfb, 0x4e, 0x28, 0xd2, 0x3f, 0x51, 0x5f, 0xc0, 0xb4, 0x52, 0xa1, 0x29, 0xf1, 0x25, 0xa0, 0x84, 0x14, 0xc9, 0x87, 0xc7, 0xda, 0xc1, 0xad, 0x5c, 0xf8, 0x9c, 0x53, 0x11, 0xa8, 0x76, 0xba, 0xeb, 0x50, 0x33, 0x66, 0xf5, 0xab, 0xdc, 0xb8, 0xab, 0x57, 0x57, 0x5d, 0xc9, 0x54, 0x8a, 0x9f, 0x97, 0x3b, 0x51, 0x07, 0xdb, 0xc6, 0xa8, 0xaf, 0x6c, 0x86, 0x16, 0x04, 0x0f, 0x48, 0x97, 0xa9, 0xab, 0x17, 0x8b, 0x08, 0x35, 0xc7, 0xba, 0x6a, 0xa9, 0xe0, 0xed, 0x58, 0x80, 0x24, 0xe8, 0x37, 0xb3, 0x04, 0x8f, 0x96, 0xa0, 0x57, 0x63, 0xfb, 0xe3, 0x47, 0xd5, 0x42, 0x98, 0x6a, 0xda, 0x20, 0xef, 0xa1, 0xfb, 0x0c, 0xcb, 0x97, 0x86, 0xe6, 0x17, 0x0f, 0xda, 0x30, 0x0c, 0x79, 0xd2, 0xec, 0x51, 0x7d, 0x3f, 0x6b, 0x52, 0x62, 0x23, 0xbf, 0x73, 0x00, 0xa4, 0x00, 0xb4, 0x8a, 0xd5, 0x42, 0x88, 0x36, 0x86, 0x86, 0xb6, 0x6f, 0xbe, 0x69, 0x16, 0x65, 0x7d, 0x3d, 0x33, 0x34, 0xa0, 0x06, 0x4f, 0xa8, 0x6d, 0xd7, 0x96, 0xb5, 0x98, 0xd3, 0xe6, 0xda, 0xb8, 0xd1, 0x7e, 0x4f, 0x2c, 0x97, 0xa7, 0x2e, 0xf2, 0x08, 0xc7, 0xac, 0x32, 0x67, 0x45, 0xbf, 0x47, 0xb7, 0xe1, 0x55, 0x3a, 0x07, 0x0c, 0xb8, 0xb4, 0x51, 0x00, 0xfd, 0x81, 0x33, 0x4a, 0xb8, 0xde, 0x16, 0x38, 0xfd, 0x74, 0x4f, 0x3d, 0xad, 0xf5, 0x0f, 0x5f, 0x0c, 0x53, 0xd2, 0x0b, 0xa5, 0x49, 0xb6, 0xd0, 0x6e, 0x88, 0x0f, 0x0e, 0x9b, 0x4d, 0x26, 0xf3, 0xff, 0x59, 0x95, 0x93, 0x69, 0x85, 0xc2, 0xf5, 0x41, 0xc8, 0x35, 0x5c, 0x31, 0x02, 0xd8, 0x6c, 0x0c, 0x8e, 0xae, 0xb7, 0x54, 0x3c, 0x29, 0xa3, 0xae, 0x58, 0x70, 0xe6, 0x4c, 0xf7, 0x61, 0xdd, 0xc6, 0x25, 0xd4, 0xb0, 0x37, 0x37, 0xb5, 0xff, 0x27, 0x58, 0x08, 0xae, 0x79, 0x1b, 0xc5, 0xf5, 0x28, 0x8a, 0x37, 0x15, 0x40, 0x95, 0x6f, 0x5a, 0x6e, 0x3f, 0x94, 0xf3, 0xfc, 0x09, 0x3d, 0xfd, 0x40, 0xc8, 0x21, 0xd4, 0x37, 0x4b, 0x89, 0xf3, 0x81, 0x6d, 0xf5, 0xa1, 0x96, 0x68, 0xa3, 0xbf, 0xb6, 0x8e, 0xd2, 0xba, 0x4f, 0x1c, 0x91, 0x8d, 0x07, 0xa5, 0x05, 0xb7, 0x20, 0x9f, 0xe3, 0x88, 0x92, 0x62, 0xc9, 0x73, 0xed, 0x43, 0x3f, 0x9d, 0x59, 0x5b, 0xd8, 0xd5, 0x3d, 0x62, 0xde, 0x56, 0x3d, 0x63, 0x85, 0x7d, 0x58, 0x90, 0xd2, 0xb9, 0x70, 0xad, 0x13, 0xf3, 0xde, 0xc9, 0x5c, 0xd1, 0x89, 0x04, 0x50, 0x05, 0xf7, 0xf2, 0xbe, 0x1c, 0x6d, 0xa9, 0x04, 0x7d, 0x0e, 0xcf, 0x17, 0x51, 0x57, 0xbc, 0x0a, 0xc6, 0xa7, 0x0f, 0x9e, 0xbb, 0x88, 0xa8, 0x7a, 0xa9, 0x8c, 0x42, 0xb9, 0x19, 0x89, 0xee, 0x62, 0xda, 0x10, 0xd9, 0x90, 0xc9, 0x66, 0x23, 0x45, 0x36, 0xc9, 0x25, 0xb4, 0x45, 0x30, 0x9b, 0x34, 0x81, 0xa5, 0x70, 0xfc, 0xc3, 0xed, 0xb5, 0xfa, 0xf3, 0xeb, 0xfe, 0xc8, 0x49, 0xc7, 0x9c, 0x05, 0x23, 0x7f, 0x4c, 0x9f, 0x5a, 0xf8, 0x71, 0xce, 0xc0, 0xe1, 0xb6, 0x6f, 0x8e, 0xfd, 0xd5, 0x20, 0xbf, 0x18, 0x1e, 0x5a, 0x47, 0x29, 0xc5, 0x61, 0x89, 0x9b, 0x97, 0x4c, 0x2e, 0x88, 0x33, 0xf5, 0xcb, 0x08, 0xe4, 0x6a, 0x10, 0x3b, 0x22, 0x32, 0x2b, 0x8f, 0x52, 0x7b, 0xa2, 0xe7, 0x50, 0xb9, 0x39, 0x77, 0x63, 0x2f, 0x78, 0x91, 0x56, 0x03, 0xb3, 0x69, 0x2e, 0xbb, 0x71, 0x00, 0xc2, 0x53, 0x11, 0xdb, 0x3f, 0xc6, 0x49, 0x4c, 0xdc, 0xb0, 0x02, 0x36, 0x12, 0x05, 0x6b, 0x50, 0x5c, 0xa6, 0xad, 0x7f, 0xd6, 0x5b, 0xd3, 0x39, 0xe6, 0x9a, 0xd1, 0xbc, 0x75, 0xd3, 0x0a, 0xbc, 0x9a, 0x2e, 0x95, 0x2a, 0xa0, 0xc0, 0x33, 0x83, 0x4a, 0x5e, 0x10, 0x9a, 0x03, 0x99, 0xbc, 0xec, 0x71, 0xcb, 0xb7, 0xed, 0xbd, 0xd7, 0xa3, 0xa7, 0xfa, 0xa5, 0xb5, 0xf5, 0x1a, 0xd8, 0xed, 0x9f, 0x2b, 0xcc, 0x75, 0x7c, 0x7d, 0x4e, 0x1a, 0xcc, 0x6e, 0x21, 0xa6, 0x00, 0x51, 0xee, 0xf4, 0xf2, 0x01, 0xcd, 0x6a, 0x35, 0x3f, 0x43, 0xa5, 0xf7, 0x43, 0x86, 0xbb, 0x0f, 0xbd, 0x96, 0x6c, 0x26, 0x1f, 0x6f, 0xc2, 0xaa, 0x20, 0x9e, 0x81, 0x9c, 0x53, 0x57, 0xce, 0xf7, 0x33, 0x4e, 0xe7, 0x12, 0xe9, 0xeb, 0x56, 0x14, 0x22, 0x0a, 0x29, 0xe8, 0xf6, 0x45, 0x01, 0x4b, 0x40, 0xab, 0x83, 0x31, 0x33, 0x87, 0xb6, 0xa2, 0x57, 0xa8, 0xa7, 0x3a, 0x2a, 0xd2, 0x35, 0xe1, 0x69, 0x94, 0xde, 0x75, 0xdb, 0x8d, 0x1e, 0x34, 0x78, 0x79, 0x07, 0x93, 0xea, 0x7b, 0x20, 0x14, 0xf8, 0xba, 0x5f, 0xb1, 0xbc, 0x15, 0x7f, 0xa6, 0x42, 0x04, 0x9d, 0xfe, 0x8c, 0xd2, 0x22, 0xe8, 0x39, 0x1f, 0x8a, 0x44, 0xcf, 0x8e, 0x58, 0x19, 0x33, 0x4b, 0xc8, 0x44, 0x20, 0x40, 0xba, 0x57, 0x3c, 0xd0, 0x55, 0xb2, 0x73, 0xea, 0x95, 0x32, 0x3d, 0xb1, 0xae, 0xe0, 0xb0, 0x04, 0xe9, 0x74, 0xff, 0xcb, 0xba, 0xe1, 0xed, 0x6d, 0x35, 0x10, 0xaf, 0x3e, 0xc3, 0xca, 0xbb, 0x6b, 0xdc, 0xfe, 0x65, 0x81, 0xa1, 0x2e, 0x1a, 0x5e, 0x15, 0x3e, 0xe8, 0x56, 0x8f, 0x90, 0x6b, 0xd8, 0x1a, 0xae, 0x2a, 0xc2, 0xbd, 0xff, 0xb5, 0xf2, 0x36, 0xd0, 0x2d, 0x0d, 0x1e, 0x4f, 0x0c, 0xa6, 0xcc, 0x5d, 0x57, 0xac, 0x1e, 0x8f, 0x92, 0x0f, 0x7c, 0x04, 0xeb, 0x79, 0x54, 0x41, 0x6f, 0x2c, 0x9c, 0xeb, 0x56, 0x8b, 0x19, 0x5f, 0x7b, 0x79, 0xa1, 0xff, 0x68, 0xe2, 0xe3, 0x3e, 0x0d, 0x75, 0xe3, 0x92, 0x54, 0x06, 0x57, 0x25, 0x52, 0x9e, 0x0a, 0xb9, 0xdf, 0x88, 0x19, 0x85, 0x1a, 0x81, 0xb9, 0x3f, 0x3d, 0x42, 0x59, 0x7c, 0xb5, 0x74, 0xd8, 0xfa, 0x29, 0x96, 0x66, 0x7e, 0xf6, 0xad, 0x49, 0x93, 0x38, 0x17, 0x3c, 0x3f, 0x73, 0xb0, 0x33, 0x70, 0x5c, 0xee, 0x8d, 0xbc, 0xf1, 0x74, 0x45, 0x7f, 0x46, 0xa2, 0x91, 0x1a, 0xab, 0x00, 0x34, 0x3c, 0xb9, 0x25, 0xc3, 0x5c, 0x76, 0x07, 0xd5, 0xb4, 0x8b, 0x2d, 0xf0, 0x6b, 0xa8, 0x11, 0x77, 0xa9, 0xc6, 0xf8, 0x6c, 0x75, 0x57, 0xaa, 0xd4, 0x94, 0x9d, 0x7e, 0xb8, 0xfa, 0x5b, 0xee, 0xb7, 0xaa, 0x47, 0x6e, 0x28, 0x30, 0xb4, 0x84, 0x52, 0x49, 0x8c, 0x39, 0x41, 0x07, 0x5c, 0x24, 0x4f, 0xe4, 0x5e, 0xab, 0xfa, 0x69, 0xcd, 0x90, 0x6c, 0x81, 0x55, 0x62, 0x23, 0x3b, 0x81, 0x76, 0x14, 0x3c, 0x3e, 0x56, 0x89, 0x19, 0x30, 0xa2, 0x2f, 0x34, 0x23, 0x3a, 0x07, 0xf2, 0x6b, 0x9f, 0x03, 0x62, 0x47, 0x97, 0x5a, 0xc4, 0xda, 0x3d, 0xaa, 0x79, 0x35, 0x03, 0x41, 0xf9, 0x7d, 0x61, 0xd5, 0x2b, 0x23, 0xc0, 0xb1, 0x72, 0xfe, 0x2a, 0x38, 0x93, 0x32, 0x8a, 0xe1, 0x87, 0xaf, 0xb2, 0x0c, 0xaf, 0xf3, 0x98, 0xf3, 0x01, 0x00, 0x7b, 0x12, 0x48, 0x48, 0x72, 0xfc, 0xc8, 0xcf, 0xc1, 0xf9, 0x88, 0x87, 0x92, 0x22, 0x3b, 0x4b, 0xf8, 0x67, 0x1d, 0xb5, 0x5f, 0x87, 0x0a, 0x57, 0x9c, 0x7e, 0x91, 0x4f, 0xac, 0xc8, 0x1b, 0xc5, 0x1e, 0xba, 0xb8, 0xb4, 0x4c, 0x4b, 0x46, 0x7f, 0x18, 0xb6, 0x09, 0x4f, 0x43, 0xb8, 0x87, 0xd3, 0x4f, 0x51, 0xef, 0x50, 0xcc, 0xa0, 0x7f, 0x30, 0xbd, 0xcd, 0x80, 0x80, 0x96, 0x13, 0xf8, 0x95, 0xc4, 0xeb, 0xb3, 0xca, 0x60, 0x99, 0xc0, 0x76, 0x44, 0x51, 0x9a, 0x20, 0xef, 0x54, 0x8d, 0xeb, 0xea, 0xd1, 0xb6, 0x55, 0xc4, 0x4e, 0xfd, 0x9f, 0xf9, 0x6e, 0xe1, 0xbd, 0xbc, 0x6e, 0x6f, 0x9b, 0x24, 0x36, 0xaf, 0x23, 0x27, 0x39, 0xf6, 0x4a, 0xf3, 0xde, 0x58, 0x9d, 0xcb, 0xea, 0xcf, 0x63, 0xdf, 0xbe, 0xe2, 0x02, 0x4d, 0x51, 0xed, 0x06, 0x83, 0xb3, 0xee, 0x06, 0x2f, 0xb1, 0xb1, 0x46, 0x55, 0x61, 0xdf, 0xa8, 0xcb, 0x77, 0x3a, 0x81, 0xac, 0x27, 0x53, 0xd8, 0xaa, 0xbe, 0xae, 0xab, 0x16, 0xe6, 0x85, 0xda, 0xec, 0xc0, 0xa3, 0xdd, 0x60, 0xcf, 0x96, 0x2f, 0x10, 0xb8, 0x6b, 0x8d, 0x81, 0x34, 0x74, 0x9b, 0xc7, 0x37, 0xde, 0x1c, 0x04, 0x41, 0xd5, 0x26, 0x8e, 0xfb, 0x38, 0x7e, 0x72, 0xe3, 0x7a, 0xf8, 0x68, 0x8b, 0x81, 0x1e, 0xd3, 0x00, 0x00, 0x01, 0x09 }; +constexpr AccessUnit AVC_LARGE_AU_EXPECTED_AU = { 0x15f90, 0x159b2, true, 0, {}, { 0x59, 0x01, 0x7a, 0x65, 0x04, 0xcc, 0xfb, 0xf8, 0x14, 0x0a, 0x82, 0xb6, 0xa0, 0xb8, 0xe9, 0x55, 0x47, 0x35, 0x9c, 0x90 } }; + +const std::vector M2V_LARGE_AU_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x01, 0xb3, 0xad, 0x05, 0x26, 0xaa, 0xe6, 0x35, 0x3c, 0xd3, 0x39, 0xd0, 0x8e, 0x3e, 0x56, 0x34, 0x97, 0x50, 0xf0, 0x75, 0x62, 0x8f, 0x4f, 0x2e, 0x75, 0x9c, 0x56, 0xc5, 0xcb, 0x1f, 0xa9, 0x84, 0xd5, 0xe2, 0x85, 0xec, 0xcf, 0x29, 0xcc, 0xbd, 0xc6, 0x77, 0xff, 0x7d, 0x1b, 0xec, 0x2c, 0x85, 0x65, 0x1f, 0xeb, 0x71, 0x26, 0x0d, 0x71, 0x01, 0x09, 0x7a, 0xb2, 0xed, 0x0b, 0x50, 0x39, 0x71, 0x96, 0xc9, 0x38, 0x98, 0x7b, 0x43, 0x4c, 0xa1, 0x7f, 0xac, 0xe2, 0x26, 0x30, 0xfc, 0xc0, 0x89, 0xb2, 0x6b, 0x09, 0xbf, 0x39, 0x02, 0x2f, 0x9f, 0x40, 0x3d, 0x17, 0xd3, 0x29, 0x03, 0xa4, 0x28, 0x30, 0x55, 0xa9, 0x36, 0x87, 0x10, 0xf3, 0x31, 0x88, 0x89, 0xda, 0x33, 0x88, 0x04, 0x49, 0x00, 0x81, 0x49, 0x5c, 0xe3, 0x8c, 0x83, 0x11, 0xc0, 0x84, 0xcd, 0xb5, 0x95, 0x29, 0x91, 0x83, 0x4b, 0xc8, 0x50, 0xf4, 0x5d, 0xb5, 0xeb, 0x28, 0xdc, 0x3a, 0x79, 0x68, 0x6e, 0x2b, 0x78, 0x06, 0x67, 0x83, 0x3b, 0x10, 0x7e, 0xba, 0x58, 0x72, 0x06, 0xce, 0x41, 0xed, 0xe1, 0xb7, 0x4d, 0x0e, 0x3d, 0x4a, 0xc2, 0x0b, 0xb7, 0xb1, 0x3b, 0x9a, 0x23, 0x6e, 0x1c, 0xaf, 0x02, 0xfa, 0x73, 0x0d, 0x58, 0x15, 0x33, 0x4f, 0x2f, 0x6b, 0x91, 0x82, 0x1a, 0x8d, 0xb6, 0xc2, 0x44, 0x96, 0x08, 0x9e, 0x69, 0x53, 0xb1, 0x5b, 0x2d, 0x3d, 0xa4, 0xac, 0xac, 0xf1, 0x54, 0xb3, 0xfe, 0x6a, 0x21, 0x67, 0x19, 0xfa, 0x37, 0x99, 0x41, 0x08, 0x81, 0xf6, 0xac, 0x12, 0x1d, 0x98, 0xef, 0x7a, 0x4b, 0xcc, 0xb2, 0xea, 0xdb, 0x08, 0x5e, 0xad, 0x02, 0xe6, 0x6e, 0x83, 0x10, 0x12, 0x3b, 0x7e, 0xc7, 0xdc, 0xc0, 0xd7, 0x26, 0x48, 0x6a, 0x66, 0x01, 0x11, 0x35, 0xc3, 0x4a, 0xce, 0xd6, 0x03, 0xe7, 0x29, 0x6a, 0x22, 0x3a, 0x8e, 0x55, 0x8c, 0x8a, 0xc8, 0xad, 0x3b, 0x22, 0xb9, 0xfa, 0xc1, 0x05, 0x4e, 0xd2, 0x9e, 0x84, 0xdf, 0x6f, 0xec, 0x2a, 0x75, 0x9b, 0x7f, 0xcc, 0x3b, 0xeb, 0x59, 0xb9, 0xf3, 0x02, 0x99, 0x50, 0x79, 0x41, 0x1e, 0x58, 0x8b, 0x6c, 0x39, 0x08, 0xce, 0x90, 0xb6, 0x5c, 0x59, 0x2c, 0x0b, 0xd2, 0xa0, 0xae, 0x47, 0xa9, 0x01, 0x81, 0xe4, 0xda, 0x5c, 0xdb, 0x68, 0xac, 0xda, 0xa5, 0xcf, 0xfe, 0x71, 0xed, 0xab, 0x00, 0x4a, 0xcf, 0x62, 0x95, 0xce, 0x74, 0xf4, 0x48, 0x18, 0xab, 0x00, 0xe2, 0x43, 0x5a, 0x9f, 0xff, 0x03, 0xdd, 0xd5, 0xd3, 0xd4, 0x22, 0xcb, 0x83, 0x9d, 0x25, 0xc0, 0x43, 0x1f, 0x3d, 0xfa, 0x0c, 0x39, 0x89, 0x33, 0x4f, 0x6c, 0xdd, 0x40, 0x8e, 0xb8, 0x4d, 0x75, 0x38, 0x3d, 0x1e, 0x6c, 0x0a, 0x8f, 0xd4, 0x07, 0x71, 0xcf, 0x8b, 0xa8, 0x65, 0x4a, 0xb4, 0xc9, 0x9f, 0x4a, 0x6f, 0xee, 0xab, 0x2a, 0xee, 0xc1, 0x5b, 0x88, 0xe6, 0x93, 0x7c, 0x7b, 0x23, 0xee, 0x1a, 0xab, 0xb6, 0x82, 0x03, 0x63, 0x24, 0x8e, 0xea, 0xd3, 0xdc, 0x1f, 0x8e, 0x3d, 0x6a, 0xb9, 0x96, 0x79, 0xd2, 0x8f, 0x37, 0x23, 0x9d, 0x1b, 0x58, 0x38, 0xb4, 0x9e, 0x50, 0x8e, 0x13, 0x00, 0xcc, 0x22, 0x17, 0x25, 0x04, 0xde, 0x29, 0xea, 0x36, 0xac, 0x1a, 0x70, 0x7a, 0x15, 0x22, 0xbd, 0x14, 0x70, 0x08, 0x62, 0xfa, 0x1d, 0xeb, 0x37, 0xe1, 0xcd, 0x16, 0x8b, 0x3b, 0x24, 0x39, 0xec, 0xe8, 0x76, 0x4e, 0x50, 0x4a, 0x9f, 0x83, 0xc2, 0xb5, 0x9d, 0x1d, 0x71, 0xb3, 0x7f, 0xcd, 0x8d, 0x5d, 0x3e, 0x75, 0x6d, 0x3e, 0xd9, 0x2d, 0xfb, 0x3e, 0x0d, 0x95, 0x63, 0x3b, 0xe0, 0x34, 0x85, 0x91, 0xb3, 0x88, 0x6c, 0x87, 0x58, 0x60, 0x1c, 0x10, 0x4e, 0xd0, 0x18, 0x10, 0x77, 0x9f, 0xe1, 0x73, 0x05, 0x69, 0xd0, 0xc3, 0xe0, 0xa9, 0x7d, 0x11, 0x5f, 0xe8, 0xee, 0x05, 0xc3, 0x97, 0x19, 0x5b, 0x6f, 0xa9, 0x9f, 0xb8, 0x1c, 0x6c, 0x43, 0x40, 0x88, 0x7b, 0xc1, 0xf0, 0x5a, 0x2e, 0x5e, 0x2c, 0xf9, 0xd4, 0xbb, 0xef, 0x4a, 0x84, 0xe3, 0x3e, 0x9b, 0xbf, 0x62, 0x99, 0x8d, 0x5f, 0x26, 0x76, 0x39, 0x13, 0xc7, 0x8d, 0x36, 0xff, 0xa1, 0xf1, 0xd7, 0x47, 0xf7, 0x73, 0x10, 0x6e, 0x24, 0x1a, 0x44, 0xad, 0xca, 0xbd, 0xbc, 0xa6, 0xa8, 0xb4, 0x56, 0x26, 0xfc, 0x1f, 0x57, 0xe0, 0xca, 0x6e, 0x67, 0x33, 0x3e, 0x5f, 0x39, 0x47, 0x82, 0xb6, 0x54, 0xde, 0xc5, 0x92, 0xa8, 0xb7, 0xbc, 0x82, 0x03, 0xa7, 0x3d, 0x7b, 0x13, 0x70, 0x64, 0x57, 0x29, 0xa5, 0x33, 0x27, 0x63, 0xc1, 0xaa, 0x60, 0x3b, 0xe1, 0x91, 0xfd, 0x6c, 0x01, 0x88, 0x34, 0xc0, 0x07, 0xc3, 0x85, 0xfb, 0x1f, 0x12, 0xd7, 0x31, 0xfe, 0x78, 0xb8, 0x15, 0xe5, 0xc7, 0xf2, 0x97, 0x60, 0x05, 0xd1, 0x35, 0x34, 0x1b, 0x25, 0x4f, 0x35, 0xc6, 0x98, 0x12, 0x17, 0xc1, 0xe6, 0x42, 0xd0, 0xa3, 0xd7, 0xc8, 0xef, 0xed, 0x01, 0x7c, 0x3e, 0xc5, 0xc1, 0xf5, 0x02, 0xd7, 0x92, 0xea, 0x53, 0xfc, 0x65, 0xc4, 0x84, 0x85, 0x08, 0x1a, 0xbc, 0x07, 0xd2, 0xbc, 0xc4, 0x28, 0xf6, 0xab, 0xb0, 0x22, 0x9e, 0x05, 0x9b, 0xc6, 0xb4, 0x5e, 0x5f, 0xb5, 0xf5, 0x1f, 0x6c, 0xe0, 0xc8, 0xdb, 0x17, 0xd8, 0xe3, 0xdb, 0x50, 0x65, 0xef, 0x2a, 0xda, 0x40, 0x34, 0xc3, 0x21, 0x21, 0x13, 0x79, 0x12, 0x04, 0x84, 0x35, 0x34, 0x7f, 0x82, 0x37, 0xe8, 0x14, 0x29, 0x61, 0x2f, 0x19, 0xd2, 0xa2, 0x42, 0x93, 0x6f, 0x6d, 0x85, 0x38, 0x30, 0x6e, 0x25, 0x45, 0x78, 0xd5, 0x2a, 0xc4, 0x0e, 0xfd, 0xa0, 0xab, 0xa6, 0xae, 0xdc, 0x93, 0xa7, 0x72, 0x2a, 0x09, 0x04, 0xb4, 0x4e, 0xf2, 0x56, 0x00, 0x15, 0x98, 0x7f, 0x3b, 0x4f, 0x43, 0x90, 0x08, 0x42, 0xd0, 0x11, 0x93, 0x58, 0xd1, 0x23, 0xd4, 0x9a, 0x9a, 0x80, 0x0b, 0xf6, 0xa8, 0xe4, 0x17, 0x1a, 0xa8, 0xe3, 0x3d, 0x5a, 0x05, 0xf4, 0x73, 0xa7, 0x2d, 0xe6, 0xee, 0xa9, 0x78, 0xa2, 0x6e, 0x1b, 0xc2, 0x07, 0x04, 0xb8, 0x31, 0xb2, 0x01, 0x76, 0xba, 0x19, 0x0c, 0x0f, 0x4f, 0xce, 0xbe, 0x63, 0xa2, 0xb8, 0x80, 0xf2, 0x22, 0x8d, 0x0c, 0xdb, 0x25, 0x66, 0x7a, 0x40, 0xa3, 0x0b, 0xe2, 0x2f, 0x47, 0x4a, 0xfe, 0xd4, 0xd6, 0x12, 0x0f, 0x26, 0x5b, 0x04, 0xf4, 0x8c, 0xdf, 0x6b, 0x67, 0x45, 0x67, 0xe9, 0x62, 0xf3, 0x1d, 0x2c, 0x2e, 0x1a, 0xd6, 0x58, 0x43, 0x62, 0x03, 0x0a, 0xf1, 0xe6, 0x2d, 0xf8, 0xe0, 0x1a, 0x77, 0xbf, 0xe6, 0x39, 0x4c, 0x45, 0xaa, 0xce, 0xbf, 0x6c, 0x6c, 0xbb, 0x55, 0x91, 0x51, 0xd5, 0x49, 0x05, 0x1d, 0x39, 0xe4, 0xf6, 0x22, 0x20, 0x24, 0xc4, 0xb2, 0x83, 0xda, 0xb7, 0xb3, 0x86, 0xdf, 0x37, 0xe0, 0x01, 0xe1, 0x5b, 0x55, 0xd9, 0x60, 0x8b, 0x55, 0xcb, 0x28, 0x7f, 0xf9, 0xfa, 0x50, 0x0b, 0x4c, 0xc0, 0x18, 0x1d, 0x2e, 0x92, 0x19, 0x05, 0xa4, 0xd7, 0xde, 0xc7, 0x3e, 0xc4, 0x3f, 0x27, 0x56, 0x35, 0x00, 0xf5, 0xe3, 0x32, 0x71, 0xcb, 0xa0, 0x34, 0xa2, 0xbd, 0x3c, 0x6b, 0x17, 0x1e, 0x94, 0xb9, 0x7b, 0x62, 0xb8, 0xcb, 0xa4, 0xa2, 0x8a, 0x90, 0xb0, 0xc0, 0xb6, 0xc3, 0xfa, 0x9e, 0x1f, 0xa3, 0x75, 0x09, 0x71, 0x5d, 0x9c, 0x23, 0xdd, 0xf6, 0x6d, 0x4f, 0x42, 0xd2, 0xaf, 0x8d, 0x89, 0x54, 0xc1, 0xfd, 0x36, 0x25, 0x2c, 0x06, 0x2e, 0x4a, 0xea, 0x41, 0x63, 0x0e, 0x3d, 0xfc, 0x40, 0xa7, 0x71, 0x1c, 0x3c, 0x9c, 0x75, 0x02, 0xcb, 0xb3, 0x32, 0x6e, 0x33, 0xf5, 0x30, 0xb9, 0x63, 0x40, 0xd2, 0xb4, 0xff, 0xd5, 0x9e, 0xcd, 0x44, 0x9b, 0xeb, 0x4a, 0x43, 0x1d, 0x20, 0xf1, 0x60, 0xa6, 0xad, 0x0a, 0xb7, 0x6d, 0xef, 0x82, 0x15, 0x02, 0x35, 0xd8, 0x4d, 0xdc, 0x1b, 0xdf, 0x1f, 0x69, 0x2c, 0x86, 0xdd, 0x11, 0x91, 0x22, 0x70, 0x74, 0x3e, 0x64, 0xef, 0x3f, 0xb2, 0xb1, 0xc3, 0xbe, 0x6e, 0x9d, 0x13, 0x8c, 0x8f, 0xff, 0xd6, 0x74, 0x0b, 0xc0, 0x89, 0x6a, 0x87, 0xd0, 0xe5, 0x4b, 0x3c, 0x30, 0xd7, 0x27, 0xad, 0xf1, 0xc8, 0x0c, 0x07, 0x1d, 0xea, 0x31, 0xfe, 0x37, 0x5b, 0x0b, 0xeb, 0xfe, 0xc2, 0x43, 0x55, 0xa4, 0xf6, 0x92, 0x11, 0x38, 0xa4, 0x71, 0xbc, 0x77, 0x59, 0x8e, 0x38, 0x24, 0xe3, 0xd3, 0xb0, 0x61, 0x5f, 0xe5, 0x76, 0x20, 0x93, 0x81, 0xba, 0x0f, 0x8d, 0xc4, 0x86, 0x73, 0x78, 0x74, 0xdf, 0x7a, 0xb4, 0x93, 0x58, 0xf2, 0x02, 0x40, 0x4e, 0x10, 0x93, 0xe4, 0xdd, 0xeb, 0x1e, 0x8d, 0x96, 0xa2, 0x32, 0x0a, 0x4b, 0xfe, 0xa7, 0x48, 0x2d, 0x72, 0x1e, 0x89, 0xa7, 0x50, 0x5c, 0x10, 0xe8, 0x52, 0x49, 0x4a, 0x13, 0xcc, 0x51, 0xba, 0x22, 0x7c, 0xa3, 0xca, 0xc8, 0xcf, 0x31, 0x79, 0x40, 0x2a, 0x6f, 0xf6, 0x6d, 0x82, 0x5c, 0x6c, 0xb5, 0x3a, 0x6c, 0xa7, 0x4f, 0xdf, 0x52, 0x94, 0xe0, 0x1a, 0xaa, 0xe6, 0x43, 0x20, 0x2f, 0xa6, 0x27, 0x67, 0x08, 0xbb, 0x31, 0x5c, 0x3d, 0xfd, 0x9a, 0xf9, 0x2e, 0x42, 0x95, 0x56, 0x8f, 0xad, 0x86, 0x20, 0xae, 0xd4, 0x04, 0x74, 0x1f, 0xfe, 0xe2, 0x0d, 0x0f, 0x69, 0xb3, 0x73, 0x2a, 0x57, 0x26, 0xca, 0x2f, 0xdd, 0xa5, 0xdd, 0x4d, 0x77, 0x44, 0x43, 0x54, 0x37, 0xf5, 0xda, 0x70, 0x36, 0x59, 0x89, 0x3d, 0x91, 0x3f, 0x6b, 0xff, 0x8f, 0x29, 0x3d, 0x8f, 0x4b, 0x07, 0xb8, 0x70, 0x7f, 0x13, 0x89, 0x9d, 0x03, 0xe9, 0x81, 0xcb, 0x19, 0xc5, 0x98, 0xde, 0xcd, 0x32, 0xdf, 0x33, 0x14, 0x69, 0xbf, 0x9b, 0xb1, 0x39, 0xdc, 0xec, 0x29, 0xf2, 0x3a, 0x88, 0x1d, 0x23, 0x1b, 0xec, 0xd1, 0x0f, 0xc4, 0x82, 0x94, 0xf7, 0xc1, 0x55, 0x3f, 0x98, 0xc9, 0xd8, 0x34, 0xd9, 0x80, 0x05, 0x59, 0xb7, 0xca, 0x24, 0x85, 0xbe, 0xf1, 0xfc, 0xb7, 0x42, 0xa0, 0x1b, 0xd5, 0x1c, 0x10, 0x6b, 0x1c, 0x99, 0x20, 0x4c, 0x7e, 0xa8, 0x8d, 0xf6, 0x27, 0x07, 0x48, 0xf3, 0x18, 0x99, 0x73, 0x42, 0x37, 0x96, 0x4e, 0x34, 0x1d, 0x38, 0x91, 0x5a, 0x0e, 0x61, 0x6b, 0x9f, 0xfa, 0xe0, 0x39, 0x3a, 0x54, 0x87, 0xfc, 0x5d, 0xcf, 0x13, 0xba, 0x74, 0x8c, 0xc3, 0xa2, 0xa3, 0x89, 0x91, 0x7c, 0x97, 0xbe, 0xf8, 0x26, 0x0f, 0x54, 0x5c, 0x40, 0xb4, 0x61, 0x3f, 0xbb, 0x87, 0xa1, 0xdd, 0x62, 0x5d, 0x5c, 0x49, 0x93, 0x85, 0x50, 0x30, 0x32, 0x8a, 0xd7, 0x2c, 0x83, 0xf0, 0x4b, 0x76, 0x4a, 0xac, 0x87, 0x0e, 0x6e, 0xff, 0x7a, 0x11, 0xbd, 0x49, 0x71, 0xaf, 0x2d, 0xa7, 0x44, 0x7b, 0xcc, 0x71, 0xc3, 0xb8, 0x13, 0x28, 0xe9, 0x62, 0x1a, 0xa5, 0x67, 0x78, 0xb6, 0x19, 0x83, 0xc5, 0xa7, 0x15, 0xd2, 0x3d, 0xf9, 0x85, 0xb1, 0x99, 0xd8, 0x18, 0x4d, 0x16, 0x1d, 0x17, 0x3e, 0x29, 0x63, 0x8f, 0x07, 0x86, 0x03, 0x9f, 0x0f, 0xc5, 0xcf, 0x76, 0x9d, 0xef, 0xe3, 0xaa, 0x0a, 0xac, 0x37, 0xa4, 0xfc, 0x49, 0x25, 0xa5, 0x8f, 0x1c, 0x4e, 0xab, 0x2b, 0x68, 0x3f, 0x59, 0xbb, 0x9f, 0x80, 0x47, 0x82, 0x31, 0x92, 0x64, 0xf3, 0xab, 0x6b, 0xd3, 0xd1, 0x42, 0xe7, 0x26, 0xb4, 0x73, 0x5e, 0xac, 0x08, 0xdd, 0x48, 0x89, 0xbd, 0xd3, 0x68, 0xa1, 0xfa, 0x20, 0x05, 0xe3, 0x39, 0x37, 0x84, 0x78, 0x52, 0x3d, 0x0f, 0xe8, 0x2b, 0xa2, 0x8c, 0x3c, 0xd3, 0x5b, 0x60, 0x31, 0xf1, 0x60, 0x20, 0xd6, 0x64, 0xb2, 0x9c, 0xa4, 0x99, 0xf5, 0x90, 0xd9, 0xca, 0x5f, 0xf2, 0x39, 0x1c, 0x6e, 0xd2, 0x0d, 0x65, 0xda, 0x0c, 0x2a, 0x67, 0x16, 0x6f, 0x63, 0x88, 0x6f, 0x66, 0xc7, 0x39, 0x4b, 0xf6, 0x5b, 0xaa, 0x30, 0x2b, 0xc2, 0xb9, 0x40, 0x88, 0xda, 0x10, 0xb4, 0x2d, 0x5f, 0x9b, 0x16, 0xc1, 0xb5, 0x19, 0x2f, 0x70, 0xa7, 0x22, 0xdf, 0x2d, 0xe8, 0x7b, 0x1c, 0x6d, 0xa8, 0xce, 0x75, 0xb9, 0x5b, 0x68, 0x01, 0xb3, 0x36, 0x95, 0xcd, 0x0c, 0x98, 0x5b, 0xf1, 0x1d, 0xfe, 0xaa, 0x20, 0xf2, 0xd8, 0x9b, 0xdd, 0xeb, 0x5b, 0x98, 0x6d, 0x59, 0xd1, 0xd4, 0xe8, 0x83, 0x05, 0xf1, 0x15, 0x9d, 0x56, 0xb2, 0x5a, 0xd6, 0xfd, 0x05, 0x7e, 0x7b, 0x89, 0xf6, 0x53, 0xcf, 0x5a, 0x43, 0xef, 0x49, 0x99, 0xf5, 0xee, 0xa0, 0x36, 0x5f, 0x07, 0x02, 0x3d, 0x08, 0xd9, 0xb1, 0xfc, 0xd2, 0xb5, 0x72, 0x60, 0xee, 0xa9, 0xaf, 0x27, 0x48, 0x96, 0x86, 0x97, 0xf5, 0x37, 0x74, 0x56, 0x2e, 0x80, 0x02, 0xc5, 0xf1, 0x80, 0x2e, 0xa8, 0xc1, 0x20, 0xcf, 0x60, 0x78, 0xcc, 0xa6, 0x48, 0xea, 0xf1, 0x72, 0xce, 0xb7, 0x23, 0xeb, 0x58, 0x51, 0x9f, 0xfa, 0xf6, 0x3d, 0x13, 0x5b, 0x82, 0xa6, 0x1c, 0x25, 0xac, 0x71, 0x94, 0xe9, 0x86, 0x60, 0xf7, 0x86, 0xe3, 0xab, 0x11, 0x0a, 0xbf, 0x23, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x07, 0xec, 0x81, 0x00, 0x00, 0x3d, 0x4f, 0xe8, 0x8a, 0x84, 0x70, 0xd4, 0x47, 0x10, 0x56, 0xdf, 0x21, 0xf4, 0xb6, 0x51, 0xed, 0x6a, 0xba, 0xf6, 0x41, 0x64, 0xac, 0x13, 0xd8, 0x9e, 0xee, 0xeb, 0xc1, 0xc1, 0xc5, 0xd5, 0x88, 0x4e, 0xa9, 0x5a, 0x51, 0x4d, 0xd6, 0xbc, 0x4e, 0xa1, 0x0d, 0x56, 0x46, 0xed, 0x11, 0xba, 0xe2, 0xa8, 0x7a, 0xd8, 0xd6, 0x42, 0x13, 0x27, 0x53, 0xc6, 0x42, 0x41, 0x1f, 0xed, 0x44, 0x76, 0x11, 0x1d, 0x89, 0xcb, 0x39, 0x99, 0x03, 0xa8, 0x6e, 0x6a, 0x89, 0x13, 0xfc, 0xa3, 0x40, 0x1c, 0x08, 0x40, 0x19, 0x68, 0xc2, 0x4e, 0xc2, 0x7d, 0x03, 0x33, 0xa6, 0x04, 0xf1, 0x78, 0xac, 0xc1, 0xdb, 0x78, 0x38, 0xfc, 0x01, 0x3a, 0x81, 0xed, 0xc6, 0xa1, 0x42, 0xd3, 0x22, 0x0c, 0x41, 0x7a, 0x8a, 0x30, 0x99, 0x12, 0x4d, 0xab, 0x94, 0xe7, 0xb4, 0xba, 0x1f, 0x9c, 0xbd, 0xfd, 0x49, 0xef, 0x93, 0x74, 0xf4, 0x88, 0x72, 0xc4, 0x75, 0xc2, 0x95, 0x76, 0x21, 0x86, 0xf8, 0x24, 0xc3, 0x4c, 0x22, 0x8d, 0x60, 0xd2, 0x8f, 0xec, 0xca, 0x20, 0x99, 0x87, 0x4b, 0x69, 0xc2, 0x45, 0xff, 0x89, 0xab, 0xe8, 0x9b, 0x7e, 0x51, 0x00, 0xf2, 0x0b, 0xc7, 0x7e, 0xe2, 0xdd, 0x16, 0x94, 0xb3, 0x76, 0x24, 0xf9, 0xe5, 0x0a, 0x3e, 0xc1, 0x7b, 0xd7, 0x1a, 0x40, 0x23, 0x99, 0x3d, 0x16, 0x8e, 0xe0, 0x7b, 0xcc, 0x67, 0xeb, 0xeb, 0xab, 0xee, 0xab, 0x17, 0xca, 0xd6, 0xca, 0xb3, 0x37, 0xe4, 0x4b, 0x93, 0x85, 0x0c, 0x65, 0xe0, 0x7e, 0x16, 0xd4, 0x5f, 0x5b, 0x3c, 0x57, 0x3d, 0x1f, 0x6a, 0x7a, 0x81, 0xf3, 0x94, 0x93, 0x52, 0xf4, 0x7f, 0xac, 0x56, 0xdf, 0xf1, 0xa9, 0xed, 0xca, 0xb3, 0x2b, 0x6b, 0x23, 0x2d, 0x12, 0x40, 0x6f, 0xfe, 0x07, 0x8a, 0x53, 0x19, 0x1d, 0x42, 0x65, 0x2d, 0x3d, 0x99, 0xf6, 0x39, 0xb5, 0x01, 0x7f, 0xca, 0x30, 0x88, 0x0a, 0x4a, 0xe6, 0x3e, 0xc2, 0x50, 0xd0, 0x2a, 0x8e, 0xe7, 0xdd, 0xcb, 0xfc, 0x4e, 0xdd, 0xb2, 0x7a, 0x21, 0x79, 0xbc, 0x7c, 0xfc, 0x96, 0xe1, 0x71, 0xfa, 0xf0, 0x28, 0x05, 0x18, 0x8c, 0x8f, 0x41, 0x00, 0x9f, 0x6d, 0xe4, 0xd2, 0x82, 0xb9, 0xe5, 0x21, 0xdf, 0xc0, 0xfc, 0x73, 0x38, 0xd4, 0x11, 0x38, 0x88, 0x06, 0x16, 0x59, 0x14, 0x92, 0xbe, 0x13, 0xa2, 0xc7, 0x53, 0xf2, 0xe2, 0xe6, 0x01, 0x51, 0x30, 0x12, 0x3f, 0xf2, 0x1e, 0xe1, 0x78, 0xe9, 0x9d, 0xac, 0xfe, 0x47, 0x6a, 0x96, 0x85, 0x08, 0xe3, 0x11, 0x34, 0x7c, 0xfe, 0xe8, 0x9d, 0x2d, 0x36, 0x58, 0x22, 0x03, 0xaf, 0x9f, 0x10, 0x5e, 0xb7, 0x04, 0xed, 0x47, 0xbb, 0xcb, 0x96, 0xed, 0xca, 0xde, 0x1f, 0xf5, 0xe6, 0x84, 0x12, 0xa3, 0x31, 0x06, 0xd9, 0xb2, 0x32, 0xe2, 0xc5, 0x32, 0xa4, 0x8a, 0x7e, 0x56, 0xcd, 0x17, 0x4b, 0xc8, 0x20, 0xd9, 0xa4, 0xc3, 0x23, 0xda, 0x0c, 0x62, 0x56, 0xa2, 0xaf, 0xf5, 0xcd, 0x1a, 0xdf, 0x9b, 0xcd, 0x63, 0xf9, 0xdc, 0xae, 0xf5, 0x43, 0xb1, 0x83, 0x21, 0x33, 0xb2, 0x75, 0x2a, 0xd7, 0xa8, 0x4f, 0x70, 0x37, 0x61, 0xd5, 0xa6, 0x6c, 0xec, 0x98, 0x89, 0x0c, 0xfc, 0x2b, 0x0e, 0x35, 0x0f, 0xca, 0xa0, 0x1d, 0x03, 0xd8, 0x4f, 0xc2, 0x11, 0x4d, 0x7e, 0x72, 0xd3, 0xa9, 0xb9, 0x04, 0xee, 0x4e, 0xd0, 0xa2, 0xb6, 0xbb, 0x70, 0x9d, 0xd5, 0x25, 0x3e, 0x92, 0xab, 0xe1, 0x10, 0x7d, 0xe4, 0x75, 0xe9, 0xd7, 0x44, 0x6e, 0xa5, 0x60, 0x88, 0xc3, 0x14, 0x67, 0x89, 0x65, 0xb5, 0x52, 0xf5, 0x97, 0x9c, 0xf7, 0x2d, 0xa7, 0xa4, 0xda, 0xb6, 0x63, 0x93, 0x28, 0x6c, 0x81, 0xb4, 0x8a, 0x36, 0x8c, 0x2d, 0xa1, 0xa4, 0xde, 0xac, 0x7e, 0xee, 0x91, 0xe5, 0xb9, 0x68, 0x4f, 0x6d, 0x56, 0xbb, 0x81, 0xf9, 0xa1, 0xdb, 0x39, 0xb6, 0x6b, 0xf9, 0xd6, 0x3d, 0x5e, 0x7d, 0x4f, 0x14, 0x97, 0xae, 0x83, 0xf7, 0x56, 0xe1, 0xa6, 0x78, 0x49, 0x69, 0x81, 0x44, 0x29, 0x99, 0xf4, 0xbb, 0x49, 0xaa, 0x9b, 0x7c, 0x73, 0xdf, 0xe8, 0x6c, 0xd2, 0x3c, 0x8c, 0xb4, 0xcc, 0xde, 0xea, 0x55, 0x70, 0x58, 0xca, 0x72, 0x47, 0x49, 0xe2, 0x36, 0xb0, 0x90, 0x6e, 0x40, 0xd8, 0xb6, 0x8d, 0x0c, 0xcd, 0xab, 0xa8, 0x3c, 0x25, 0x6f, 0x37, 0xc5, 0x01, 0xf0, 0xa5, 0x11, 0x0a, 0xa0, 0x77, 0x9b, 0x02, 0xd5, 0x20, 0xfa, 0x06, 0x37, 0x4e, 0x6c, 0xa7, 0x44, 0xdd, 0x84, 0x77, 0x3e, 0xe6, 0x59, 0x65, 0x33, 0xb3, 0x66, 0x79, 0x4f, 0x1a, 0xd3, 0x2f, 0x7d, 0xd2, 0xde, 0x29, 0x10, 0x41, 0xa5, 0x1b, 0x06, 0xe7, 0x99, 0xfd, 0x07, 0x55, 0x98, 0xfe, 0x61, 0xdd, 0x90, 0xc4, 0x85, 0x46, 0xcb, 0x09, 0x39, 0xf3, 0xd2, 0x29, 0xf2, 0x52, 0x15, 0xc3, 0xb3, 0xad, 0x65, 0x3d, 0xa4, 0xb3, 0x73, 0x9f, 0x2f, 0xd7, 0xc7, 0x3f, 0xc4, 0x06, 0xa9, 0x40, 0xcd, 0x1a, 0xb0, 0xb1, 0x22, 0x32, 0x80, 0xe8, 0x58, 0xc2, 0x79, 0x9e, 0x5a, 0xf0, 0x2d, 0xad, 0xa0, 0x8a, 0xcc, 0x65, 0x30, 0x91, 0xa9, 0xe9, 0xae, 0xce, 0xe9, 0xf6, 0x30, 0x44, 0x6f, 0x7f, 0xc2, 0x60, 0xa6, 0xa6, 0x16, 0xd3, 0x4b, 0x92, 0x4f, 0xa1, 0xd9, 0x7a, 0xbe, 0x14, 0x24, 0xac, 0xa8, 0xc4, 0xed, 0xd9, 0x01, 0xde, 0xea, 0x45, 0x83, 0x92, 0x52, 0xfa, 0xc1, 0xdd, 0xc7, 0xf3, 0x62, 0x8e, 0x2b, 0x0e, 0x1d, 0x6e, 0xcc, 0x00, 0xe6, 0x0d, 0xba, 0xb2, 0x4c, 0x98, 0x81, 0xab, 0x12, 0x42, 0x8b, 0x05, 0x66, 0x9a, 0xfe, 0xa5, 0xc4, 0xc5, 0x16, 0xc4, 0xa9, 0xb7, 0x97, 0x6c, 0x5d, 0x92, 0x45, 0xf6, 0xf9, 0xe9, 0x38, 0x8f, 0xec, 0x0e, 0x14, 0xb3, 0x97, 0x26, 0x66, 0x4c, 0xeb, 0x14, 0x01, 0x18, 0xb1, 0xc8, 0x1e, 0x83, 0x49, 0x54, 0x98, 0x30, 0xf3, 0xa7, 0x03, 0x08, 0x98, 0x2b, 0x5e, 0x63, 0xbf, 0x79, 0xe1, 0x8e, 0xf9, 0xbd, 0x85, 0xc8, 0xdb, 0x79, 0xd8, 0x14, 0x93, 0xc5, 0x78, 0xf7, 0x67, 0xe1, 0xa9, 0xeb, 0xab, 0xf2, 0xa9, 0x16, 0x2b, 0xfa, 0x65, 0x45, 0x0b, 0x6e, 0xa8, 0xcd, 0x9c, 0x43, 0x3c, 0xb7, 0x24, 0xe5, 0x68, 0x92, 0x96, 0x50, 0xd5, 0x8b, 0xf2, 0x32, 0xf7, 0x49, 0xe3, 0x4c, 0xbb, 0xb1, 0xaa, 0x39, 0xfe, 0xc4, 0xa1, 0xf3, 0xf2, 0x1e, 0x77, 0xd4, 0x4f, 0x7f, 0x7c, 0x61, 0x28, 0x05, 0x55, 0xb3, 0xd2, 0xd6, 0x05, 0xbb, 0x38, 0x4c, 0x82, 0xc5, 0x19, 0x58, 0x53, 0xd8, 0xdb, 0xf3, 0x85, 0xb3, 0x12, 0x5e, 0xe6, 0x03, 0xa8, 0x2a, 0x9e, 0xc0, 0xfd, 0xcf, 0x87, 0x87, 0xe5, 0x64, 0x98, 0xf2, 0x44, 0x2e, 0x27, 0x3d, 0x7a, 0x91, 0x02, 0x2e, 0x4f, 0xac, 0xa1, 0x86, 0xc5, 0x64, 0x8e, 0xb9, 0x75, 0xf5, 0x94, 0xfe, 0x01, 0xfd, 0x73, 0xc2, 0xd6, 0xfb, 0x39, 0xda, 0xae, 0x91, 0x3b, 0xae, 0xb7, 0x70, 0x6c, 0x87, 0xfe, 0x89, 0x31, 0x8d, 0x1a, 0xdb, 0x95, 0x1c, 0x8d, 0xdf, 0x86, 0x27, 0x0b, 0x00, 0x7d, 0x9a, 0xfc, 0xb5, 0xaa, 0xf0, 0x79, 0x30, 0xf8, 0xab, 0xc1, 0xac, 0x2f, 0xdf, 0x8a, 0x36, 0x63, 0x59, 0xca, 0x77, 0x98, 0x00, 0x00, 0x01, 0x00, 0xca, 0xc7, 0x4a, 0x81, 0xaf, 0xcb, 0x45, 0x6a, 0xe9, 0x9d, 0xa3, 0x03, 0x70, 0xb3, 0x07, 0xd0, 0xca, 0x3d, 0xac, 0xad, 0xca, 0x8c, 0x63, 0xbd, 0x65, 0x83, 0x99, 0x84, 0x62, 0xb4, 0xe0, 0x9d, 0xdd, 0x1f, 0x41, 0x1e, 0x53, 0x28, 0x0c, 0xd3, 0x15, 0x7d, 0xd3, 0x69, 0x5c, 0xbe, 0xcc, 0xeb, 0xf0, 0xe7, 0xb7, 0xf2, 0x54, 0xd6, 0x8b, 0xee, 0x2c, 0xcb, 0x5c, 0x02, 0x17, 0x34, 0x07, 0xb3, 0x42, 0x41, 0xaf, 0x2a, 0x1d, 0xdc, 0x40, 0x50, 0x10, 0x6d, 0x2e, 0x00, 0xff, 0xe9, 0x8b, 0x9b, 0x8f, 0xc2, 0x9c, 0x74, 0x33, 0xd3, 0x69, 0x3f, 0x22, 0x34, 0xc5, 0x2f, 0xcd, 0x3a, 0x66, 0xc7, 0x7d, 0x23, 0x15, 0x8d, 0x1e, 0x26, 0x4b, 0x09, 0xf0, 0x10, 0x5d, 0x4a, 0x87, 0x8b, 0xf2, 0x94, 0xda, 0x9e, 0x98, 0x69, 0x28, 0x7c, 0x01, 0x04, 0x26, 0xb1, 0xea, 0x16, 0x4e, 0x6a, 0x88, 0x58, 0x1f, 0xb2, 0x44, 0xf2, 0x65, 0xe3, 0x75, 0x53, 0x1c, 0x34, 0x67, 0x47, 0x34, 0x2f, 0x61, 0x28, 0x5a, 0x70, 0x00, 0x4f, 0xb6, 0x47, 0x25, 0x8a, 0x34, 0x1f, 0xa8, 0x68, 0xcb, 0x05, 0x92, 0x5e, 0xf0, 0x6f, 0xa0, 0x41, 0x3c, 0x68, 0xf1, 0xb3, 0x9e, 0x11, 0x2a, 0xe5, 0xc9, 0xff, 0xa0, 0x79, 0x8e, 0x1c, 0xf2, 0x86, 0xbe, 0xd2, 0x7a, 0xf4, 0xaa, 0xd9, 0x2b, 0x7b, 0x6f, 0xfc, 0x97, 0x1b, 0x02, 0x98, 0x6b, 0x28, 0x43, 0x79, 0x2c, 0x49, 0x0c, 0x7c, 0x12, 0x02, 0x91, 0xf6, 0x7e, 0xf9, 0xfe, 0x13, 0x3d, 0x4a, 0x47, 0xed, 0x24, 0xae, 0xae, 0x7f, 0xfc, 0x3b, 0x93, 0xc4, 0x64, 0x8c, 0xcb, 0x79, 0xfc, 0xff, 0x8b, 0xdd, 0xcc, 0x16, 0x02, 0x0b, 0xd6, 0x95, 0xcc, 0x02, 0x74, 0xd5, 0x9b, 0xe5, 0x05, 0x1d, 0x0a, 0x8e, 0xb9, 0x5d, 0xe5, 0xf5, 0xda, 0xf3, 0xbd, 0x93, 0xb3, 0x1f, 0x7f, 0xfe, 0x8c, 0x5a, 0x9e, 0x9e, 0x3c, 0x44, 0x29, 0x2b, 0x2c, 0x2b, 0x02, 0x7d, 0xe7, 0x76, 0xac, 0x20, 0xe5, 0x51, 0x7b, 0xa6, 0x6d, 0xba, 0x00, 0x73, 0xcd, 0xe0, 0xa1, 0x9d, 0x0b, 0x52, 0xe0, 0x11, 0x46, 0xff, 0x18, 0x46, 0x0a, 0x80, 0x1c, 0xe7, 0xc7, 0x11, 0x6a, 0x06, 0x77, 0xec, 0x40, 0xfc, 0x45, 0xe1, 0xe2, 0x8e, 0x3d, 0xc2, 0x6e, 0x01, 0xa4, 0x07, 0x56, 0xd7, 0xa8, 0x39, 0x8c, 0x54, 0x88, 0xa7, 0x96, 0x8f, 0x0b, 0x55, 0x41, 0x39, 0x2e, 0x62, 0x83, 0x8e, 0x96, 0xe4, 0x42, 0xc8, 0xd9, 0x36, 0x5b, 0x4c, 0x2a, 0xdc, 0x3a, 0x4c, 0x73, 0x7a, 0xde, 0x75, 0x9f, 0x98, 0x4d, 0xf1, 0x23, 0xd5, 0x53, 0xf3, 0x51, 0x79, 0x26, 0x3a, 0x48, 0xc9, 0xd0, 0xa3, 0x0a, 0xb9, 0x38, 0x43, 0x84, 0x33, 0x5d, 0x66, 0x76, 0x1d, 0xcb, 0xef, 0x98, 0x9b, 0xab, 0xb4, 0xef, 0x11, 0x5d, 0x61, 0xbe, 0x84, 0xcf, 0x6a, 0x05, 0x0e, 0x46, 0x7c, 0x9b, 0x7c, 0xd9, 0x1e, 0x14, 0xea, 0x03, 0xa3, 0xf4, 0x19, 0x59, 0xf0, 0xa1, 0xb8, 0x5c, 0xd0, 0x53, 0x85, 0x09, 0x82, 0x4a, 0x67, 0x6c, 0xe7, 0x2f, 0x69, 0x98, 0x4f, 0x7f, 0xbe, 0x83, 0x93, 0x82, 0x40, 0xde, 0x28, 0xfd, 0xec, 0xd7, 0x59, 0x49, 0xc8, 0x0e, 0x2e, 0x98, 0x69, 0x51, 0x34, 0x71, 0x75, 0x19, 0xac, 0x7c, 0xac, 0x6f, 0xdb, 0x24, 0x48, 0xbb, 0xa5, 0x13, 0x23, 0x02, 0xbc, 0xe2, 0x62, 0x32, 0x12, 0xa9, 0x15, 0x15, 0x16, 0x92, 0x54, 0x4f, 0xa3, 0xa2, 0x27, 0x8b, 0x14, 0x61, 0x57, 0x8d, 0x6d, 0x24, 0x87, 0x0d, 0x36, 0x5c, 0x5d, 0x10, 0xf1, 0x27, 0xf2, 0x36, 0xf1, 0xc4, 0x32, 0x28, 0x20, 0x23, 0xc2, 0x83, 0x51, 0x21, 0xe5, 0xe1, 0x60, 0x10, 0x2e, 0x38, 0xc8, 0xc2, 0x05, 0x73, 0xd2, 0x6e, 0xb3, 0x0f, 0x20, 0xfe, 0xd9, 0x1b, 0x99, 0xbc, 0x09, 0x3a, 0x69, 0x20, 0xd6, 0xb6, 0x08, 0xc2, 0x62, 0xa5, 0x2d, 0xf5, 0x09, 0x1b, 0x12, 0xf2, 0x2b, 0x36, 0x29, 0xff, 0xfa, 0xec, 0x20, 0x0c, 0xdf, 0x15, 0x52, 0x56, 0x91, 0x34, 0x40, 0xbc, 0xdd, 0x86, 0x93, 0x01, 0x7d, 0xb9, 0x70, 0xdb, 0xff, 0xfa, 0x12, 0xe5, 0x4a, 0x1b, 0xec, 0x61, 0x8f, 0x28, 0x9e, 0xaa, 0x60, 0x31, 0x00, 0xc7, 0x7f, 0xcb, 0x1f, 0x94, 0x79, 0xd6, 0x79, 0x03, 0xce, 0x6b, 0x4c, 0x62, 0x26, 0xc8, 0x4f, 0xae, 0x9c, 0x14, 0x89, 0x54, 0x3c, 0x29, 0x74, 0xdd, 0x69, 0x9d, 0x84, 0x79, 0x40, 0x90, 0xc9, 0xe2, 0xbc, 0xfa, 0x94, 0x87, 0x86, 0x09, 0x19, 0x71, 0x29, 0x53, 0x77, 0xd2, 0x24, 0xf8, 0x69, 0xcb, 0x14, 0x34, 0x51, 0x58, 0x89, 0xa2, 0xd6, 0x17, 0xf2, 0xee, 0x51, 0x4f, 0x03, 0x9a, 0x9e, 0x6f, 0x58, 0x5f, 0xb8, 0xf9, 0x0f, 0x4f, 0x9c, 0x11, 0x64, 0x84, 0xa2, 0x89, 0x94, 0xca, 0x9a, 0x7a, 0x9e, 0xd1, 0xf8, 0x92, 0x4e, 0x45, 0xae, 0xd1, 0xab, 0x09, 0x4f, 0x0b, 0x32, 0x76, 0xf0, 0x39, 0x55, 0x77, 0x1e, 0xfe, 0xa5, 0xc2, 0xce, 0x3d, 0x3b, 0xd5, 0xc6, 0x6d, 0xb6, 0x70, 0x7a, 0x2b, 0x64, 0x20, 0x47, 0x71, 0x23, 0xf9, 0x40, 0x58, 0xd2, 0x68, 0x66, 0xf2, 0x8b, 0x48, 0xfa, 0x66, 0xa4, 0x80, 0x13, 0x4a, 0xc3, 0xe5, 0x2e, 0x65, 0xae, 0xd8, 0x61, 0xfb, 0xba, 0x67, 0x8d, 0x20, 0x54, 0x07, 0xa4, 0x91, 0x2f, 0x94, 0x08, 0x7c, 0xae, 0xc3, 0x6e, 0x79, 0x3a, 0xf4, 0xa3, 0xe9, 0xaf, 0xf7, 0x7b, 0x13, 0xa7, 0x29, 0xdb, 0xf5, 0x93, 0x3e, 0x9a, 0x14, 0xd6, 0x61, 0x45, 0x52, 0x9b, 0x19, 0xfe, 0xa3, 0x85, 0xc4, 0x00, 0x13, 0x31, 0xc8, 0x0d, 0x72, 0x72, 0x8e, 0x68, 0xd0, 0x82, 0x4f, 0x4b, 0xe5, 0x13, 0xe1, 0x96, 0x18, 0xb3, 0x8d, 0xcc, 0x50, 0x59, 0x2a, 0x46, 0xb2, 0x21, 0x98, 0x78, 0xf6, 0xf6, 0xc6, 0x3d, 0xce, 0x38, 0x92, 0x21, 0x33, 0x2a, 0x76, 0x70, 0xec, 0x58, 0x72, 0xfa, 0xde, 0x64, 0xda, 0x08, 0xc9, 0x93, 0x4c, 0xde, 0x1c, 0x86, 0x35, 0x52, 0x58, 0x5b, 0xb0, 0x8e, 0x03, 0x6e, 0x7e, 0xab, 0x11, 0x5e, 0x67, 0x18, 0x74, 0xc5, 0x83, 0xab, 0x6c, 0x10, 0x38, 0x73, 0x0f, 0x2e, 0x92, 0x28, 0x61, 0x7a, 0xd6, 0x45, 0xcb, 0xc0, 0x35, 0x57, 0x72, 0x03, 0x11, 0x12, 0x1f, 0x2b, 0x5d, 0xee, 0x9b, 0xdb, 0xa9, 0x10, 0xd6, 0x21, 0xb0, 0x1e, 0xfd, 0xbe, 0x6e, 0x4b, 0x27, 0xec, 0xc7, 0xc5, 0x46, 0xde, 0x0a, 0x53, 0x53, 0x64, 0xcf, 0x35, 0xc7, 0xf5, 0x02, 0x3a, 0x39, 0xb9, 0xf9, 0xfb, 0x2a, 0x8d, 0xc7, 0x67, 0x63, 0x09, 0x5e, 0x09, 0xd9, 0x16, 0x27, 0x8c, 0x9a, 0xfb, 0xb6, 0xf9, 0x5d, 0xa8, 0x37, 0x92, 0xde, 0x9f, 0x7b, 0x0b, 0x2d, 0xb1, 0x48, 0x09, 0x2e, 0x2e, 0x6a, 0x1c, 0x05, 0x2f, 0x27, 0x83, 0xf5, 0x37, 0xd2, 0x3f, 0xf3, 0x2c, 0x45, 0x80, 0xdc, 0x31, 0x68, 0x05, 0x81, 0x65, 0xa3, 0x1b, 0x6e, 0x5c, 0xaa, 0xf8, 0xa6, 0x60, 0x6a, 0x2e, 0x4d, 0x9d, 0x9c, 0x2f, 0xbf, 0x4c, 0x2e, 0xf3, 0x01, 0xc0, 0xa5, 0x59, 0x00, 0x7d, 0x61, 0x42, 0x21, 0xec, 0x2b, 0x13, 0xfd, 0x3c, 0x45, 0xb4, 0xcd, 0x01, 0x7c, 0x40, 0x47, 0x50, 0xdc, 0x7a, 0x33, 0x26, 0xc0, 0x9a, 0xb9, 0xf4, 0x8c, 0xa8, 0x00, 0xfb, 0x51, 0xbf, 0x04, 0x9b, 0x92, 0x4b, 0xd8, 0x06, 0xb2, 0x71, 0x8b, 0x3a, 0xee, 0x69, 0xc0, 0x2c, 0xc9, 0x5f, 0x57, 0x45, 0x62, 0xa2, 0xdc, 0x89, 0x14, 0xcb, 0x40, 0x74, 0xbb, 0x99, 0x56, 0x0c, 0xb5, 0xdd, 0x11, 0x12, 0xc3, 0x9b, 0x5e, 0x17, 0xdd, 0x4d, 0xcf, 0x1c, 0x4b, 0x5f, 0x3e, 0xd2, 0x45, 0x20, 0x12, 0xc2, 0xeb, 0x70, 0x09, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x07, 0xec, 0x81, 0x00, 0x00, 0xb3, 0x9c, 0x26, 0x3a, 0x16, 0x44, 0x6e, 0xe1, 0xfb, 0x28, 0x93, 0x4d, 0x84, 0x3a, 0x9f, 0xe0, 0xb6, 0x9c, 0x08, 0xe9, 0xb2, 0x19, 0xe5, 0xb2, 0xb4, 0x79, 0x1c, 0x3c, 0x44, 0xa3, 0xe5, 0x33, 0x62, 0xf2, 0x26, 0xdd, 0xef, 0x55, 0x90, 0x47, 0x4c, 0xb6, 0xc2, 0x33, 0x7f, 0xc4, 0xd0, 0x9d, 0x79, 0xcd, 0x9b, 0xf0, 0x47, 0xe4, 0x57, 0xa5, 0x81, 0x51, 0xd8, 0x0c, 0x8d, 0xa4, 0x73, 0xd9, 0xae, 0x6f, 0xaa, 0xfc, 0x73, 0x96, 0xe1, 0x01, 0x40, 0xe8, 0x30, 0xb7, 0x8d, 0x11, 0x95, 0x0d, 0xc8, 0x31, 0x8e, 0x24, 0x93, 0x6e, 0xb4, 0xfc, 0xe8, 0xe0, 0x94, 0x29, 0x6a, 0x6e, 0x98, 0x01, 0x6d, 0x04, 0xde, 0x04, 0xac, 0xf0, 0xcf, 0x89, 0xaf, 0x79, 0xd6, 0xc1, 0xf4, 0x82, 0x90, 0x78, 0xd0, 0x91, 0x8c, 0x6f, 0x0d, 0xc8, 0x80, 0xce, 0x67, 0x3f, 0x2a, 0x86, 0x43, 0x4a, 0x6e, 0x33, 0x87, 0x9c, 0xc5, 0x72, 0xae, 0xc1, 0x90, 0xda, 0x96, 0x46, 0xf1, 0x86, 0x0c, 0x3b, 0xe1, 0x52, 0xad, 0x7b, 0x90, 0xbe, 0x81, 0x91, 0x22, 0xa5, 0xee, 0x65, 0xef, 0xb4, 0x3c, 0xd6, 0x21, 0xd5, 0x30, 0xc7, 0x24, 0x4f, 0x2a, 0x70, 0x81, 0xf4, 0x27, 0xab, 0x2f, 0x2d, 0x75, 0xc6, 0x34, 0xda, 0x36, 0x3c, 0xfa, 0x55, 0x79, 0xd1, 0xe3, 0xee, 0xcd, 0x88, 0x84, 0xd7, 0x1e, 0x95, 0x9c, 0x68, 0x73, 0x07, 0xbb, 0xff, 0x9b, 0x50, 0xbc, 0xdf, 0x12, 0xde, 0xfb, 0xb4, 0x14, 0xe1, 0x41, 0x9f, 0x1e, 0xe3, 0x96, 0xb3, 0xc4, 0x12, 0x2b, 0x7f, 0xb6, 0x73, 0x7c, 0x25, 0x66, 0xa9, 0x6d, 0x4b, 0x81, 0x7f, 0xda, 0x66, 0x88, 0xca, 0x9a, 0x40, 0xa8, 0x57, 0xed, 0x56, 0x7b, 0x39, 0xe9, 0x53, 0x00, 0xeb, 0x7c, 0x7b, 0x7e, 0x96, 0x21, 0x78, 0x13, 0xee, 0x24, 0x52, 0xdd, 0x14, 0x54, 0xc7, 0x98, 0xff, 0x01, 0x9f, 0x63, 0x3b, 0xc1, 0x11, 0x31, 0x96, 0x63, 0x2b, 0xca, 0x38, 0x9d, 0xb3, 0xda, 0xcb, 0x48, 0x72, 0xa2, 0x60, 0xaa, 0x4e, 0xb4, 0xb3, 0xad, 0x66, 0xed, 0x2c, 0x27, 0x43, 0xa2, 0x88, 0x05, 0x39, 0x62, 0x47, 0x79, 0x9d, 0x3f, 0xe3, 0x7d, 0xc5, 0xfb, 0x9f, 0x6a, 0x72, 0xce, 0x24, 0xbf, 0x61, 0x02, 0x23, 0xde, 0x00, 0x84, 0xcc, 0xd8, 0xd5, 0x8e, 0xe3, 0xd3, 0xc9, 0x47, 0x29, 0x83, 0x7a, 0x81, 0xc0, 0x52, 0xcb, 0x4e, 0x1f, 0x61, 0x16, 0x3f, 0x95, 0xc8, 0x2a, 0xf9, 0x0d, 0x3b, 0xb2, 0x6f, 0x2b, 0x89, 0x22, 0xd4, 0xf3, 0x62, 0xc8, 0x49, 0x45, 0xce, 0x7d, 0xa9, 0x4b, 0xc5, 0x02, 0x17, 0x96, 0x9c, 0xcd, 0x87, 0x99, 0x1c, 0xa8, 0xb3, 0xb3, 0xf9, 0xab, 0x27, 0x04, 0xf9, 0x3a, 0xf9, 0xd8, 0x92, 0xb8, 0x3f, 0x3c, 0x66, 0x0e, 0x62, 0xf9, 0x86, 0xfe, 0x4e, 0xe6, 0x57, 0xf7, 0xa2, 0x7f, 0x0b, 0x7f, 0xf5, 0x42, 0xbb, 0xb0, 0x5e, 0xc2, 0xc3, 0x4c, 0x8f, 0xc7, 0x97, 0xa8, 0x63, 0xbb, 0x20, 0x41, 0xc8, 0x67, 0x61, 0x08, 0x27, 0x4a, 0x4b, 0x72, 0x81, 0x4c, 0xca, 0x85, 0xed, 0x93, 0x81, 0x41, 0x60, 0x16, 0x85, 0xaa, 0x7e, 0x56, 0x07, 0x65, 0xd3, 0xe9, 0x35, 0x0b, 0xf7, 0x96, 0xbc, 0x00, 0x31, 0x5a, 0xae, 0x68, 0xe7, 0x94, 0x97, 0x29, 0x2a, 0xec, 0x0b, 0xc7, 0x89, 0xa0, 0x03, 0xd2, 0xc0, 0x35, 0x11, 0xa9, 0xe7, 0x19, 0x6e, 0x5b, 0x83, 0x40, 0xe6, 0xf5, 0xf4, 0x90, 0x6c, 0xdc, 0x8c, 0xe4, 0xac, 0xde, 0xb7, 0x9f, 0x15, 0x54, 0xaa, 0x46, 0x56, 0xf1, 0x60, 0xe6, 0x77, 0x1b, 0x80, 0xa5, 0x21, 0xb9, 0x5b, 0x93, 0x6d, 0x7e, 0xf0, 0x06, 0x5f, 0x65, 0xc7, 0xdc, 0x5f, 0x12, 0x88, 0x31, 0x2f, 0x78, 0x8b, 0x56, 0xea, 0x23, 0x9a, 0xf7, 0x7c, 0xcc, 0x96, 0x56, 0xc8, 0x49, 0x86, 0xb0, 0x90, 0x71, 0xc0, 0x83, 0xc5, 0xdd, 0xa3, 0x40, 0xa2, 0x11, 0x7d, 0xc3, 0xc5, 0x07, 0x05, 0xe1, 0x19, 0x6c, 0xed, 0x23, 0x01, 0xf8, 0xbe, 0x96, 0xa9, 0x6f, 0x66, 0xb3, 0xcd, 0x88, 0xc2, 0xf0, 0x19, 0xaf, 0x4b, 0x79, 0x16, 0xdf, 0x84, 0x28, 0x28, 0x3e, 0x05, 0xe4, 0xcb, 0xf7, 0x69, 0x45, 0xf8, 0x31, 0xb9, 0x35, 0xad, 0xc0, 0x71, 0xf5, 0xd9, 0x38, 0xc7, 0xa2, 0xd3, 0xf6, 0x5b, 0xf6, 0xb4, 0xc0, 0x3d, 0x3f, 0x1d, 0xdf, 0xad, 0x05, 0xf6, 0x15, 0x01, 0x1f, 0xdd, 0x03, 0x3d, 0x9e, 0xb5, 0x9a, 0x16, 0xdc, 0xf5, 0x6c, 0x8e, 0x59, 0x03, 0xa8, 0x84, 0x6b, 0x16, 0x43, 0xf0, 0x57, 0xcb, 0x5e, 0xa4, 0x74, 0x26, 0x88, 0xac, 0x41, 0x8d, 0x97, 0xd0, 0x69, 0x6c, 0xeb, 0x32, 0xc4, 0xda, 0x5e, 0x02, 0xa2, 0x36, 0x5e, 0x07, 0x81, 0x26, 0x38, 0x0c, 0x73, 0x26, 0x12, 0x8f, 0x90, 0xff, 0xdc, 0x5b, 0x69, 0xd9, 0x2f, 0xc4, 0x67, 0xbb, 0xc6, 0x14, 0x36, 0xba, 0xee, 0x3b, 0x2c, 0xb4, 0x22, 0x13, 0x89, 0x2e, 0xf4, 0x65, 0x1d, 0x22, 0x19, 0xbe, 0x9c, 0xf4, 0x00, 0x6c, 0xb2, 0x42, 0x5b, 0x65, 0x1a, 0x8a, 0x11, 0xae, 0xf6, 0x29, 0x8a, 0xbc, 0xef, 0xce, 0x8d, 0xeb, 0x7a, 0x9e, 0xb7, 0xb3, 0x5d, 0xfa, 0x07, 0xa4, 0x98, 0xa2, 0xbc, 0xbc, 0xb6, 0xca, 0x72, 0x00, 0x7c, 0xbf, 0xde, 0xe9, 0x5c, 0x12, 0x1f, 0x1e, 0x03, 0xaa, 0x15, 0xde, 0x7e, 0xfc, 0xfa, 0x4c, 0x97, 0x21, 0x77, 0xb1, 0xfa, 0x16, 0xa8, 0x8d, 0x88, 0x00, 0xc5, 0x9b, 0xb2, 0x2e, 0xd3, 0x65, 0xfc, 0x68, 0x44, 0x86, 0xf1, 0x08, 0xec, 0xa7, 0x88, 0xcd, 0x56, 0xf1, 0x09, 0x55, 0x1e, 0x6a, 0x6b, 0x71, 0x4b, 0x7b, 0x7f, 0x58, 0xf0, 0xd7, 0xd7, 0xe6, 0xb9, 0xe6, 0x5e, 0xef, 0xa2, 0xc0, 0x24, 0x8a, 0xee, 0xd5, 0xeb, 0x38, 0x0e, 0x9b, 0x18, 0x8b, 0x9c, 0x5f, 0xb2, 0x1e, 0x21, 0xb5, 0x1f, 0xb7, 0xef, 0xf5, 0xa4, 0xdf, 0xae, 0x07, 0xcd, 0xf8, 0x7f, 0xe4, 0x13, 0x0a, 0x90, 0x2e, 0x2a, 0x6d, 0xd3, 0x2b, 0x30, 0x06, 0x2f, 0xea, 0xd7, 0x26, 0x06, 0x94, 0xc3, 0x97, 0x6e, 0x00, 0x29, 0x80, 0x85, 0xa9, 0xb9, 0xfc, 0x86, 0x52, 0x78, 0x19, 0xdc, 0x82, 0xc7, 0xae, 0xda, 0x54, 0xc3, 0x27, 0x6d, 0xf9, 0x2e, 0xf8, 0x62, 0xcf, 0xe7, 0xd1, 0x27, 0x44, 0x2b, 0x7f, 0x97, 0xf1, 0xe7, 0x1a, 0x4f, 0x0d, 0xae, 0x93, 0x77, 0x96, 0x41, 0xfb, 0x3c, 0x40, 0xb0, 0xc2, 0xef, 0x5e, 0x7b, 0x50, 0x4e, 0xb3, 0x73, 0xbb, 0xf9, 0x29, 0x22, 0x82, 0x3a, 0x0f, 0xf9, 0x99, 0x42, 0x1a, 0xf5, 0x4e, 0x13, 0xa1, 0x16, 0xc5, 0x00, 0xd9, 0xa6, 0x25, 0xdd, 0x14, 0xf8, 0xd7, 0x87, 0xc7, 0x67, 0x13, 0xc4, 0x09, 0xaa, 0xa9, 0xab, 0x51, 0x2b, 0xf2, 0xbf, 0x81, 0xfb, 0xcd, 0x8d, 0x53, 0xf7, 0xc7, 0x9b, 0xe2, 0xdc, 0xe9, 0x7c, 0xa3, 0x27, 0x79, 0xe4, 0xc5, 0x00, 0x8f, 0x40, 0xf8, 0x71, 0xcf, 0x80, 0xf6, 0xa2, 0xc2, 0x9e, 0x62, 0xcc, 0x71, 0x2c, 0xc8, 0x88, 0xde, 0x76, 0xdd, 0x5e, 0xfa, 0xbc, 0xdd, 0x9e, 0x30, 0xbd, 0x2f, 0x90, 0x0d, 0x0b, 0x3f, 0x66, 0x5a, 0xb3, 0x76, 0x0f, 0x3a, 0xd8, 0x14, 0xec, 0xc2, 0x24, 0x56, 0x5b, 0xe2, 0xf8, 0xe2, 0xac, 0x05, 0x70, 0x92, 0xf6, 0x5b, 0xf5, 0x77, 0x60, 0xa8, 0x55, 0xbe, 0x6a, 0xf1, 0x54, 0x87, 0x62, 0xfe, 0x0c, 0xb5, 0x2c, 0x25, 0x8f, 0xc7, 0xff, 0xdf, 0x0e, 0x66, 0x2a, 0xd5, 0xb2, 0x4d, 0x75, 0x8e, 0xa7, 0xcc, 0x17, 0x10, 0x32, 0xfb, 0xeb, 0x67, 0xc0, 0x19, 0x80, 0x0d, 0xd0, 0xfc, 0x69, 0xaa, 0xd0, 0xe1, 0x9e, 0xff, 0xd6, 0x41, 0xbb, 0xe9, 0x1a, 0x04, 0x13, 0x4f, 0x82, 0x1e, 0x30, 0x20, 0xea, 0x77, 0x85, 0x05, 0x75, 0x6c, 0x0c, 0xc8, 0x66, 0x62, 0x77, 0xbd, 0xaf, 0xf3, 0xfa, 0x06, 0x4b, 0xb9, 0xc3, 0xb2, 0xc3, 0x63, 0x64, 0x02, 0x81, 0x2a, 0x8d, 0x02, 0x5a, 0x87, 0x94, 0x0f, 0x4f, 0x22, 0x1e, 0x30, 0x4c, 0x44, 0xac, 0xf0, 0x2d, 0x87, 0xe1, 0x56, 0x61, 0xe9, 0xa6, 0xcc, 0xa6, 0x39, 0xba, 0xac, 0xeb, 0x49, 0x7c, 0x04, 0xbb, 0xeb, 0x77, 0x5c, 0x9b, 0xc8, 0x69, 0xf4, 0x9a, 0xfb, 0xac, 0xa6, 0x54, 0xd7, 0x7b, 0x4e, 0x53, 0x74, 0x95, 0x6a, 0xa2, 0x7a, 0x9b, 0x10, 0x0a, 0x10, 0x1d, 0xae, 0x4e, 0xf7, 0xf4, 0x38, 0xd7, 0x4f, 0x94, 0xb4, 0xc1, 0x16, 0xee, 0xce, 0xab, 0x0f, 0xaf, 0x89, 0x85, 0x2a, 0x66, 0xb9, 0x5c, 0xfb, 0xf8, 0xd1, 0xa8, 0xc5, 0x01, 0xf6, 0x98, 0xac, 0x77, 0xce, 0x4b, 0xaa, 0xc3, 0xe3, 0x1d, 0x23, 0x23, 0xec, 0xb4, 0x92, 0xcf, 0xa5, 0xbf, 0x9f, 0xac, 0x92, 0x6f, 0xa7, 0xdf, 0xf4, 0x46, 0x81, 0x3d, 0x93, 0x85, 0x03, 0x7a, 0x7a, 0x65, 0xed, 0xe1, 0x22, 0xd5, 0x5a, 0xcb, 0x9b, 0x6b, 0x3c, 0xa0, 0x81, 0xc3, 0xc2, 0x3b, 0x8c, 0x2e, 0x7e, 0xfb, 0xf2, 0x97, 0x9d, 0x2b, 0x5f, 0x81, 0x31, 0xdc, 0xce, 0xec, 0x88, 0x9c, 0x70, 0x84, 0xed, 0x5f, 0xac, 0xbd, 0x76, 0x0e, 0xab, 0x1f, 0x15, 0x97, 0xbe, 0xd1, 0x74, 0x5d, 0x3a, 0x0a, 0xe0, 0xd7, 0x4a, 0x79, 0xb0, 0xd2, 0xf5, 0x70, 0x72, 0x6a, 0xa0, 0x50, 0xd2, 0x15, 0x97, 0xc9, 0xb6, 0x6f, 0x3c, 0xd7, 0x71, 0x94, 0x64, 0x7e, 0x15, 0xa1, 0x90, 0xe2, 0xa4, 0xed, 0xef, 0x00, 0x4d, 0xb8, 0xea, 0x48, 0x00, 0x18, 0x73, 0x24, 0x54, 0xb7, 0x96, 0x37, 0x5f, 0x04, 0xb3, 0x50, 0x06, 0xfe, 0x1c, 0x2e, 0x23, 0x19, 0xce, 0x8a, 0x64, 0x02, 0xfa, 0x6e, 0x77, 0xd2, 0xd4, 0x0b, 0x96, 0xc5, 0xfa, 0x04, 0xc5, 0x35, 0x24, 0x9d, 0x55, 0x6a, 0xac, 0x4e, 0x2a, 0x85, 0x44, 0xa8, 0xba, 0xde, 0x2d, 0x54, 0x26, 0xf4, 0x51, 0x78, 0x47, 0x93, 0x0b, 0x00, 0xa8, 0x88, 0x24, 0xac, 0x05, 0x56, 0x6c, 0xb7, 0xe0, 0x4c, 0xc6, 0xc4, 0x74, 0xe7, 0xc5, 0x77, 0xfd, 0x8f, 0x3a, 0x77, 0x1b, 0xb8, 0xc7, 0xca, 0x12, 0x9c, 0xcc, 0x86, 0x0a, 0x77, 0xba, 0x1d, 0x79, 0x42, 0xba, 0x01, 0x52, 0x97, 0x9d, 0x84, 0xdb, 0xdc, 0xbf, 0x27, 0x1e, 0x82, 0x22, 0x2e, 0x10, 0xb3, 0x1a, 0x2a, 0x95, 0x34, 0x84, 0x1d, 0x68, 0xda, 0xec, 0xc3, 0xc7, 0x19, 0xcb, 0xe1, 0x12, 0x98, 0xac, 0x78, 0xad, 0x18, 0xe7, 0xa0, 0xfe, 0x26, 0xae, 0xd7, 0xda, 0x38, 0xce, 0x83, 0x0d, 0xdb, 0x0d, 0xea, 0x02, 0x21, 0x3e, 0x03, 0x71, 0x9a, 0x4d, 0xc6, 0x2f, 0x8a, 0xe6, 0x5f, 0x12, 0x60, 0xfe, 0x88, 0x4b, 0x1d, 0x1f, 0x9c, 0x6f, 0x72, 0x0e, 0xdb, 0x5d, 0xcf, 0x19, 0x95, 0xeb, 0x9e, 0x18, 0xa6, 0x14, 0xe2, 0x81, 0x50, 0x45, 0x7d, 0x0b, 0xb4, 0x27, 0x56, 0xda, 0x22, 0x9a, 0x4b, 0x98, 0xed, 0x3e, 0x0e, 0xd8, 0xe7, 0x96, 0xe9, 0xa0, 0x5b, 0x41, 0xbe, 0x3a, 0x5c, 0x11, 0x99, 0x98, 0x86, 0x5a, 0xe6, 0xba, 0xe5, 0xc7, 0x3f, 0xa8, 0x13, 0x26, 0x26, 0x53, 0x4c, 0x8b, 0x76, 0xa6, 0xee, 0x0f, 0x04, 0x02, 0x5d, 0x34, 0xf1, 0x94, 0x79, 0x4d, 0x44, 0x8d, 0x8b, 0xf6, 0x9e, 0xbf, 0x3c, 0x88, 0xee, 0xcf, 0x75, 0x28, 0xfe, 0xfb, 0x89, 0x77, 0xb7, 0x33, 0xd9, 0xe0, 0x3c, 0x92, 0x14, 0x1c, 0x8e, 0xcb, 0x9b, 0x49, 0x4b, 0xd7, 0x02, 0x4d, 0xdd, 0xea, 0x57, 0x5c, 0x03, 0xd2, 0xbe, 0x06, 0x67, 0x75, 0x55, 0x48, 0x06, 0x78, 0xc6, 0x6f, 0xe9, 0x80, 0x61, 0xc3, 0xc5, 0xeb, 0xdd, 0x44, 0xd7, 0xd6, 0x22, 0x13, 0xeb, 0x4b, 0x22, 0xfc, 0xfa, 0xa0, 0xa9, 0xd7, 0x6f, 0x20, 0x29, 0x74, 0xe9, 0x27, 0x19, 0xbc, 0x57, 0x30, 0x91, 0x1c, 0x16, 0x86, 0x7a, 0xf4, 0x71, 0xbe, 0x67, 0x33, 0xde, 0xfc, 0x96, 0x61, 0xa9, 0x8e, 0x9b, 0x3f, 0x7e, 0xc9, 0xb2, 0xe2, 0xea, 0x07, 0xe2, 0x0c, 0xf6, 0x58, 0x12, 0x2d, 0x18, 0x73, 0x9e, 0x8b, 0xfe, 0xe1, 0x7c, 0x55, 0x7c, 0x81, 0x35, 0x85, 0x0d, 0xf1, 0x0d, 0xa2, 0x0a, 0x5f, 0x32, 0x52, 0x90, 0xd5, 0xad, 0xc4, 0xcc, 0xdf, 0x2f, 0xd4, 0xf1, 0x65, 0xeb, 0xdd, 0xf0, 0x77, 0x9e, 0x70, 0x25, 0x8d, 0x44, 0xae, 0x6b, 0x25, 0x1b, 0x1a, 0xce, 0x39, 0x2e, 0xe0, 0xc7, 0xa4, 0xc5, 0x8a, 0x96, 0xd7, 0x25, 0x28, 0xb2, 0x38, 0x35, 0x62, 0xbd, 0x8b, 0x12, 0x84, 0x47, 0xa4, 0xf3, 0x72, 0x06, 0x33, 0x7f, 0xc7, 0x35, 0xa5, 0x5c, 0x8b, 0x57, 0x9d, 0xd5, 0x3f, 0xc6, 0x64, 0xa8, 0xaf, 0xbf, 0x51, 0x8f, 0xcc, 0x88, 0x15, 0x34, 0xea, 0xc3, 0x13, 0x71, 0x02, 0x12, 0xa6, 0xb9, 0x01, 0x58, 0xe7, 0x3a, 0x24, 0x8f, 0x31, 0xa7, 0xfb, 0x4d, 0x1e, 0x76, 0x44, 0xb2, 0xd8, 0x15, 0xe5, 0xf6, 0x79, 0x37, 0x00, 0x0d, 0x3f, 0x12, 0xcf, 0x2b, 0x10, 0xa2, 0x51, 0x28, 0x99, 0x88, 0xc6, 0x33, 0xf7, 0xf3, 0x3f, 0xf9, 0xd2, 0xa9, 0x00, 0x3a, 0x1f, 0xfa, 0x28, 0xb2, 0xe5, 0x2c, 0xd0, 0x7d, 0x9a, 0x14, 0xcd, 0x2e, 0x20, 0x27, 0x71, 0x57, 0x73, 0x78, 0x61, 0x99, 0x9b, 0x2b, 0x98, 0xa0, 0x0b, 0xf3, 0x4a, 0x36, 0x1c, 0xd1, 0x38, 0x91, 0xda, 0x0c, 0xf2, 0x38, 0x27, 0x41, 0x69, 0xa7, 0xe9, 0x88, 0x9d, 0xe8, 0xcf, 0xe7, 0x0c, 0xe2, 0x61, 0x25, 0x5a, 0xb4, 0xce, 0x4e, 0x1d, 0xbd, 0x6f, 0x8f, 0x06, 0x20, 0xb1, 0x4a, 0x24, 0x46, 0x84, 0xfe, 0xaa, 0x82, 0x07, 0x59, 0x08, 0x79, 0xd9, 0xc3, 0xb5, 0x14, 0xce, 0x1a, 0xd8, 0x76, 0x06, 0x39, 0x09, 0x2f, 0x18, 0x4e, 0x6b, 0x1c, 0xe3, 0xb6, 0xff, 0x96, 0xbe, 0x39, 0x8d, 0xa1, 0x46, 0xe4, 0x3b, 0xeb, 0x85, 0xf5, 0xe5, 0x7c, 0x05, 0xf2, 0x39, 0xcd, 0x0d, 0x7b, 0xa0, 0x95, 0x5c, 0x03, 0x99, 0xaa, 0x53, 0xca, 0x12, 0xb3, 0x25, 0x9a, 0x0a, 0x2f, 0x4b, 0x3f, 0x89, 0x28, 0x12, 0x27, 0x31, 0x2c, 0xce, 0x55, 0x2f, 0x4d, 0xb1, 0xfb, 0x2a, 0x5e, 0x3b, 0x8f, 0x73, 0x1d, 0x4d, 0xfa, 0x20, 0x8f, 0x3b, 0x8d, 0x83, 0x08, 0xb3, 0x53, 0xb6, 0x0f, 0xcb, 0xe9, 0xa3, 0xde, 0x25, 0xa1, 0xbc, 0x0e, 0x1c, 0x15, 0x9c, 0xc4, 0x2f, 0xee, 0x39, 0x41, 0xf9, 0xe4, 0x57, 0x14, 0xf3, 0x4a, 0x92, 0xeb, 0xae, 0xbf, 0x1c, 0x18, 0x01, 0xbd, 0x78, 0xa1, 0xf6, 0xbe, 0x40, 0x0e, 0xcb, 0x35, 0x1c, 0x7e, 0x17, 0x8c, 0x2e, 0xc3, 0xb3, 0x8d, 0xa1, 0xbe, 0xec, 0x04, 0x35, 0x6a, 0x2f, 0xe0, 0xd0, 0x94, 0x63, 0xca, 0x86, 0x19, 0x1a, 0xdc, 0x70, 0xf9, 0x5d, 0x06, 0xf1, 0xeb, 0x4c, 0x04, 0x05, 0x25, 0x7e, 0xe1, 0xfa, 0x14, 0x01, 0x06, 0x21, 0x95, 0x50, 0x8b, 0x3f, 0xd8, 0xee, 0x11, 0xc2, 0x79, 0xb7, 0x54, 0x50, 0x66, 0x0d, 0xed, 0x83, 0x5c, 0x38, 0xed, 0x54, 0x11, 0x0a, 0xb3, 0x52, 0x50, 0x05, 0x7a, 0x58, 0xb9, 0xe2, 0xea, 0xde, 0x00, 0x00, 0x01, 0xb7 }; +constexpr AccessUnit M2V_LARGE_AU_EXPECTED_AU = { 0x15f90, 0x159b2, true, 0, {}, { 0x60, 0x03, 0x38, 0xf1, 0x3d, 0xc4, 0xab, 0x4f, 0x09, 0xbd, 0xc3, 0xae, 0x12, 0xf0, 0xc2, 0xb8, 0x16, 0x3e, 0xa1, 0x92 } }; + +const std::vector ATRACX_LARGE_AU_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xd0, 0xfd, 0xff, 0xe6, 0x35, 0x3c, 0xd3, 0x39, 0xd0, 0x8e, 0x3e, 0x56, 0x34, 0x97, 0x50, 0xf0, 0x75, 0x62, 0x8f, 0x4f, 0x2e, 0x75, 0x9c, 0x56, 0xc5, 0xcb, 0x1f, 0xa9, 0x84, 0xd5, 0xe2, 0x85, 0xec, 0xcf, 0x29, 0xcc, 0xbd, 0xc6, 0x77, 0xff, 0x7d, 0x1b, 0xec, 0x2c, 0x85, 0x65, 0x1f, 0xeb, 0x71, 0x26, 0x0d, 0x71, 0x01, 0x09, 0x7a, 0xb2, 0xed, 0x0b, 0x50, 0x39, 0x71, 0x96, 0xc9, 0x38, 0x98, 0x7b, 0x43, 0x4c, 0xa1, 0x7f, 0xac, 0xe2, 0x26, 0x30, 0xfc, 0xc0, 0x89, 0xb2, 0x6b, 0x09, 0xbf, 0x39, 0x02, 0x2f, 0x9f, 0x40, 0x3d, 0x17, 0xd3, 0x29, 0x03, 0xa4, 0x28, 0x30, 0x55, 0xa9, 0x36, 0x87, 0x10, 0xf3, 0x31, 0x88, 0x89, 0xda, 0x33, 0x88, 0x04, 0x49, 0x00, 0x81, 0x49, 0x5c, 0xe3, 0x8c, 0x83, 0x11, 0xc0, 0x84, 0xcd, 0xb5, 0x95, 0x29, 0x91, 0x83, 0x4b, 0xc8, 0x50, 0xf4, 0x5d, 0xb5, 0xeb, 0x28, 0xdc, 0x3a, 0x79, 0x68, 0x6e, 0x2b, 0x78, 0x06, 0x67, 0x83, 0x3b, 0x10, 0x7e, 0xba, 0x58, 0x72, 0x06, 0xce, 0x41, 0xed, 0xe1, 0xb7, 0x4d, 0x0e, 0x3d, 0x4a, 0xc2, 0x0b, 0xb7, 0xb1, 0x3b, 0x9a, 0x23, 0x6e, 0x1c, 0xaf, 0x02, 0xfa, 0x73, 0x0d, 0x58, 0x15, 0x33, 0x4f, 0x2f, 0x6b, 0x91, 0x82, 0x1a, 0x8d, 0xb6, 0xc2, 0x44, 0x96, 0x08, 0x9e, 0x69, 0x53, 0xb1, 0x5b, 0x2d, 0x3d, 0xa4, 0xac, 0xac, 0xf1, 0x54, 0xb3, 0xfe, 0x6a, 0x21, 0x67, 0x19, 0xfa, 0x37, 0x99, 0x41, 0x08, 0x81, 0xf6, 0xac, 0x12, 0x1d, 0x98, 0xef, 0x7a, 0x4b, 0xcc, 0xb2, 0xea, 0xdb, 0x08, 0x5e, 0xad, 0x02, 0xe6, 0x6e, 0x83, 0x10, 0x12, 0x3b, 0x7e, 0xc7, 0xdc, 0xc0, 0xd7, 0x26, 0x48, 0x6a, 0x66, 0x01, 0x11, 0x35, 0xc3, 0x4a, 0xce, 0xd6, 0x03, 0xe7, 0x29, 0x6a, 0x22, 0x3a, 0x8e, 0x55, 0x8c, 0x8a, 0xc8, 0xad, 0x3b, 0x22, 0xb9, 0xfa, 0xc1, 0x05, 0x4e, 0xd2, 0x9e, 0x84, 0xdf, 0x6f, 0xec, 0x2a, 0x75, 0x9b, 0x7f, 0xcc, 0x3b, 0xeb, 0x59, 0xb9, 0xf3, 0x02, 0x99, 0x50, 0x79, 0x41, 0x1e, 0x58, 0x8b, 0x6c, 0x39, 0x08, 0xce, 0x90, 0xb6, 0x5c, 0x59, 0x2c, 0x0b, 0xd2, 0xa0, 0xae, 0x47, 0xa9, 0x01, 0x81, 0xe4, 0xda, 0x5c, 0xdb, 0x68, 0xac, 0xda, 0xa5, 0xcf, 0xfe, 0x71, 0xed, 0xab, 0x00, 0x4a, 0xcf, 0x62, 0x95, 0xce, 0x74, 0xf4, 0x48, 0x18, 0xab, 0x00, 0xe2, 0x43, 0x5a, 0x9f, 0xff, 0x03, 0xdd, 0xd5, 0xd3, 0xd4, 0x22, 0xcb, 0x83, 0x9d, 0x0f, 0xd0, 0x43, 0x1f, 0x3d, 0xfa, 0x0c, 0x39, 0x89, 0x33, 0x4f, 0x6c, 0xdd, 0x40, 0x8e, 0xb8, 0x4d, 0x75, 0x38, 0x3d, 0x1e, 0x6c, 0x0a, 0x8f, 0xd4, 0x07, 0x71, 0xcf, 0x8b, 0xa8, 0x65, 0x4a, 0xb4, 0xc9, 0x9f, 0x4a, 0x6f, 0xee, 0xab, 0x2a, 0xee, 0xc1, 0x5b, 0x88, 0xe6, 0x93, 0x7c, 0x7b, 0x23, 0xee, 0x1a, 0xab, 0xb6, 0x82, 0x03, 0x63, 0x24, 0x8e, 0xea, 0xd3, 0xdc, 0x1f, 0x8e, 0x3d, 0x6a, 0xb9, 0x96, 0x79, 0xd2, 0x8f, 0x37, 0x23, 0x9d, 0x1b, 0x58, 0x38, 0xb4, 0x9e, 0x50, 0x8e, 0x13, 0x00, 0xcc, 0x22, 0x17, 0x25, 0x04, 0xde, 0x29, 0xea, 0x36, 0xac, 0x1a, 0x70, 0x7a, 0x15, 0x22, 0xbd, 0x14, 0x70, 0x08, 0x62, 0xfa, 0x1d, 0xeb, 0x37, 0xe1, 0xcd, 0x16, 0x8b, 0x3b, 0x24, 0x39, 0xec, 0xe8, 0x76, 0x4e, 0x50, 0x4a, 0x9f, 0x83, 0xc2, 0xb5, 0x9d, 0x1d, 0x71, 0xb3, 0x7f, 0xcd, 0x8d, 0x5d, 0x3e, 0x75, 0x6d, 0x3e, 0xd9, 0x2d, 0xfb, 0x3e, 0x0d, 0x95, 0x63, 0x3b, 0xe0, 0x34, 0x85, 0x91, 0xb3, 0x88, 0x6c, 0x87, 0x58, 0x60, 0x1c, 0x10, 0x4e, 0xd0, 0x18, 0x10, 0x77, 0x9f, 0xe1, 0x73, 0x05, 0x69, 0xd0, 0xc3, 0xe0, 0xa9, 0x7d, 0x11, 0x5f, 0xe8, 0xee, 0x05, 0xc3, 0x97, 0x19, 0x5b, 0x6f, 0xa9, 0x9f, 0xb8, 0x1c, 0x6c, 0x43, 0x40, 0x88, 0x7b, 0xc1, 0xf0, 0x5a, 0x2e, 0x5e, 0x2c, 0xf9, 0xd4, 0xbb, 0xef, 0x4a, 0x84, 0xe3, 0x3e, 0x9b, 0xbf, 0x62, 0x99, 0x8d, 0x5f, 0x26, 0x76, 0x39, 0x13, 0xc7, 0x8d, 0x36, 0xff, 0xa1, 0xf1, 0xd7, 0x47, 0xf7, 0x73, 0x10, 0x6e, 0x24, 0x1a, 0x44, 0xad, 0xca, 0xbd, 0xbc, 0xa6, 0xa8, 0xb4, 0x56, 0x26, 0xfc, 0x1f, 0x57, 0xe0, 0xca, 0x6e, 0x67, 0x33, 0x3e, 0x5f, 0x39, 0x47, 0x82, 0xb6, 0x54, 0xde, 0xc5, 0x92, 0xa8, 0xb7, 0xbc, 0x82, 0x03, 0xa7, 0x3d, 0x7b, 0x13, 0x70, 0x64, 0x57, 0x29, 0xa5, 0x33, 0x27, 0x63, 0xc1, 0xaa, 0x60, 0x3b, 0xe1, 0x91, 0xfd, 0x6c, 0x01, 0x88, 0x34, 0xc0, 0x07, 0xc3, 0x85, 0xfb, 0x1f, 0x12, 0xd7, 0x31, 0xfe, 0x78, 0xb8, 0x15, 0xe5, 0xc7, 0xf2, 0x97, 0x60, 0x05, 0xd1, 0x35, 0x34, 0x1b, 0x25, 0x4f, 0x35, 0xc6, 0x98, 0x12, 0x17, 0xc1, 0xe6, 0x42, 0xd0, 0xa3, 0xd7, 0xc8, 0xef, 0xed, 0x01, 0x7c, 0x3e, 0xc5, 0xc1, 0xf5, 0x02, 0xd7, 0x92, 0xea, 0x53, 0xfc, 0x65, 0xc4, 0x84, 0x85, 0x08, 0x1a, 0xbc, 0x07, 0xd2, 0xbc, 0xc4, 0x28, 0xf6, 0xab, 0xb0, 0x22, 0x9e, 0x05, 0x9b, 0xc6, 0xb4, 0x5e, 0x5f, 0xb5, 0xf5, 0x1f, 0x6c, 0xe0, 0xc8, 0xdb, 0x17, 0xd8, 0xe3, 0xdb, 0x50, 0x65, 0xef, 0x2a, 0xda, 0x40, 0x34, 0xc3, 0x21, 0x21, 0x13, 0x79, 0x12, 0x04, 0x84, 0x35, 0x34, 0x7f, 0x82, 0x37, 0xe8, 0x14, 0x29, 0x61, 0x2f, 0x19, 0xd2, 0xa2, 0x42, 0x93, 0x6f, 0x6d, 0x85, 0x38, 0x30, 0x6e, 0x25, 0x45, 0x78, 0xd5, 0x2a, 0xc4, 0x0e, 0xfd, 0xa0, 0xab, 0xa6, 0xae, 0xdc, 0x93, 0xa7, 0x72, 0x2a, 0x09, 0x04, 0xb4, 0x4e, 0xf2, 0x56, 0x00, 0x15, 0x98, 0x7f, 0x3b, 0x4f, 0x43, 0x90, 0x08, 0x42, 0xd0, 0x11, 0x93, 0x58, 0xd1, 0x23, 0xd4, 0x9a, 0x9a, 0x80, 0x0b, 0xf6, 0xa8, 0xe4, 0x17, 0x1a, 0xa8, 0xe3, 0x3d, 0x5a, 0x05, 0xf4, 0x73, 0xa7, 0x2d, 0xe6, 0xee, 0xa9, 0x78, 0xa2, 0x6e, 0x1b, 0xc2, 0x07, 0x04, 0xb8, 0x31, 0xb2, 0x01, 0x76, 0xba, 0x19, 0x0c, 0x0f, 0x4f, 0xce, 0xbe, 0x63, 0xa2, 0xb8, 0x80, 0xf2, 0x22, 0x8d, 0x0c, 0xdb, 0x25, 0x66, 0x7a, 0x40, 0xa3, 0x0b, 0xe2, 0x2f, 0x47, 0x4a, 0xfe, 0xd4, 0xd6, 0x12, 0x0f, 0x26, 0x5b, 0x04, 0xf4, 0x8c, 0xdf, 0x6b, 0x67, 0x45, 0x67, 0xe9, 0x62, 0xf3, 0x1d, 0x2c, 0x2e, 0x1a, 0xd6, 0x58, 0x43, 0x62, 0x03, 0x0a, 0xf1, 0xe6, 0x2d, 0xf8, 0xe0, 0x1a, 0x77, 0xbf, 0xe6, 0x39, 0x4c, 0x45, 0xaa, 0xce, 0xbf, 0x6c, 0x6c, 0xbb, 0x55, 0x91, 0x51, 0xd5, 0x49, 0x05, 0x1d, 0x39, 0xe4, 0xf6, 0x22, 0x20, 0x24, 0xc4, 0xb2, 0x83, 0xda, 0xb7, 0xb3, 0x86, 0xdf, 0x37, 0xe0, 0x01, 0xe1, 0x5b, 0x55, 0xd9, 0x60, 0x8b, 0x55, 0xcb, 0x28, 0x7f, 0xf9, 0xfa, 0x50, 0x0b, 0x4c, 0xc0, 0x18, 0x1d, 0x2e, 0x92, 0x19, 0x05, 0xa4, 0xd7, 0xde, 0xc7, 0x3e, 0xc4, 0x3f, 0x27, 0x56, 0x35, 0x00, 0xf5, 0xe3, 0x32, 0x71, 0xcb, 0xa0, 0x34, 0xa2, 0xbd, 0x3c, 0x6b, 0x17, 0x1e, 0x94, 0xb9, 0x7b, 0x62, 0xb8, 0xcb, 0xa4, 0xa2, 0x8a, 0x90, 0xb0, 0xc0, 0xb6, 0xc3, 0xfa, 0x9e, 0x1f, 0xa3, 0x75, 0x09, 0x71, 0x5d, 0x9c, 0x23, 0xdd, 0xf6, 0x6d, 0x4f, 0x42, 0xd2, 0xaf, 0x8d, 0x89, 0x54, 0xc1, 0xfd, 0x36, 0x25, 0x2c, 0x06, 0x2e, 0x4a, 0xea, 0x41, 0x63, 0x0e, 0x3d, 0xfc, 0x40, 0xa7, 0x71, 0x1c, 0x3c, 0x9c, 0x75, 0x02, 0xcb, 0xb3, 0x32, 0x6e, 0x33, 0xf5, 0x30, 0xb9, 0x63, 0x40, 0xd2, 0xb4, 0xff, 0xd5, 0x9e, 0xcd, 0x44, 0x9b, 0xeb, 0x4a, 0x43, 0x1d, 0x0f, 0xd0, 0x60, 0xa6, 0xad, 0x0a, 0xb7, 0x6d, 0xef, 0x82, 0x15, 0x02, 0x35, 0xd8, 0x4d, 0xdc, 0x1b, 0xdf, 0x1f, 0x69, 0x2c, 0x86, 0xdd, 0x11, 0x91, 0x22, 0x70, 0x74, 0x3e, 0x64, 0xef, 0x3f, 0xb2, 0xb1, 0xc3, 0xbe, 0x6e, 0x9d, 0x13, 0x8c, 0x8f, 0xff, 0xd6, 0x74, 0x0b, 0xc0, 0x89, 0x6a, 0x87, 0xd0, 0xe5, 0x4b, 0x3c, 0x30, 0xd7, 0x27, 0xad, 0xf1, 0xc8, 0x0c, 0x07, 0x1d, 0xea, 0x31, 0xfe, 0x37, 0x5b, 0x0b, 0xeb, 0xfe, 0xc2, 0x43, 0x55, 0xa4, 0xf6, 0x92, 0x11, 0x38, 0xa4, 0x71, 0xbc, 0x77, 0x59, 0x8e, 0x38, 0x24, 0xe3, 0xd3, 0xb0, 0x61, 0x5f, 0xe5, 0x76, 0x20, 0x93, 0x81, 0xba, 0x0f, 0x8d, 0xc4, 0x86, 0x73, 0x78, 0x74, 0xdf, 0x7a, 0xb4, 0x93, 0x58, 0xf2, 0x02, 0x40, 0x4e, 0x10, 0x93, 0xe4, 0xdd, 0xeb, 0x1e, 0x8d, 0x96, 0xa2, 0x32, 0x0a, 0x4b, 0xfe, 0xa7, 0x48, 0x2d, 0x72, 0x1e, 0x89, 0xa7, 0x50, 0x5c, 0x10, 0xe8, 0x52, 0x49, 0x4a, 0x13, 0xcc, 0x51, 0xba, 0x22, 0x7c, 0xa3, 0xca, 0xc8, 0xcf, 0x31, 0x79, 0x40, 0x2a, 0x6f, 0xf6, 0x6d, 0x82, 0x5c, 0x6c, 0xb5, 0x3a, 0x6c, 0xa7, 0x4f, 0xdf, 0x52, 0x94, 0xe0, 0x1a, 0xaa, 0xe6, 0x43, 0x20, 0x2f, 0xa6, 0x27, 0x67, 0x08, 0xbb, 0x31, 0x5c, 0x3d, 0xfd, 0x9a, 0xf9, 0x2e, 0x42, 0x95, 0x56, 0x8f, 0xad, 0x86, 0x20, 0xae, 0xd4, 0x04, 0x74, 0x1f, 0xfe, 0xe2, 0x0d, 0x0f, 0x69, 0xb3, 0x73, 0x2a, 0x57, 0x26, 0xca, 0x2f, 0xdd, 0xa5, 0xdd, 0x4d, 0x77, 0x44, 0x43, 0x54, 0x37, 0xf5, 0xda, 0x70, 0x36, 0x59, 0x89, 0x3d, 0x91, 0x3f, 0x6b, 0xff, 0x8f, 0x29, 0x3d, 0x8f, 0x4b, 0x07, 0xb8, 0x70, 0x7f, 0x13, 0x89, 0x9d, 0x03, 0xe9, 0x81, 0xcb, 0x19, 0xc5, 0x98, 0xde, 0xcd, 0x32, 0xdf, 0x33, 0x14, 0x69, 0xbf, 0x9b, 0xb1, 0x39, 0xdc, 0xec, 0x29, 0xf2, 0x3a, 0x88, 0x1d, 0x23, 0x1b, 0xec, 0xd1, 0x0f, 0xc4, 0x82, 0x94, 0xf7, 0xc1, 0x55, 0x3f, 0x98, 0xc9, 0xd8, 0x34, 0xd9, 0x80, 0x05, 0x59, 0xb7, 0xca, 0x24, 0x85, 0xbe, 0xf1, 0xfc, 0xb7, 0x42, 0xa0, 0x1b, 0xd5, 0x1c, 0x10, 0x6b, 0x1c, 0x99, 0x20, 0x4c, 0x7e, 0xa8, 0x8d, 0xf6, 0x27, 0x07, 0x48, 0xf3, 0x18, 0x99, 0x73, 0x42, 0x37, 0x96, 0x4e, 0x34, 0x1d, 0x38, 0x91, 0x5a, 0x0e, 0x61, 0x6b, 0x9f, 0xfa, 0xe0, 0x39, 0x3a, 0x54, 0x87, 0xfc, 0x5d, 0xcf, 0x13, 0xba, 0x74, 0x8c, 0xc3, 0xa2, 0xa3, 0x89, 0x91, 0x7c, 0x97, 0xbe, 0xf8, 0x26, 0x0f, 0x54, 0x5c, 0x40, 0xb4, 0x61, 0x3f, 0xbb, 0x87, 0xa1, 0xdd, 0x62, 0x5d, 0x5c, 0x49, 0x93, 0x85, 0x50, 0x30, 0x32, 0x8a, 0xd7, 0x2c, 0x83, 0xf0, 0x4b, 0x76, 0x4a, 0xac, 0x87, 0x0e, 0x6e, 0xff, 0x7a, 0x11, 0xbd, 0x49, 0x71, 0xaf, 0x2d, 0xa7, 0x44, 0x7b, 0xcc, 0x71, 0xc3, 0xb8, 0x13, 0x28, 0xe9, 0x62, 0x1a, 0xa5, 0x67, 0x78, 0xb6, 0x19, 0x83, 0xc5, 0xa7, 0x15, 0xd2, 0x3d, 0xf9, 0x85, 0xb1, 0x99, 0xd8, 0x18, 0x4d, 0x16, 0x1d, 0x17, 0x3e, 0x29, 0x63, 0x8f, 0x07, 0x86, 0x03, 0x9f, 0x0f, 0xc5, 0xcf, 0x76, 0x9d, 0xef, 0xe3, 0xaa, 0x0a, 0xac, 0x37, 0xa4, 0xfc, 0x49, 0x25, 0xa5, 0x8f, 0x1c, 0x4e, 0xab, 0x2b, 0x68, 0x3f, 0x59, 0xbb, 0x9f, 0x80, 0x47, 0x82, 0x31, 0x92, 0x64, 0xf3, 0xab, 0x6b, 0xd3, 0xd1, 0x42, 0xe7, 0x26, 0xb4, 0x73, 0x5e, 0xac, 0x08, 0xdd, 0x48, 0x89, 0xbd, 0xd3, 0x68, 0xa1, 0xfa, 0x20, 0x05, 0xe3, 0x39, 0x37, 0x84, 0x78, 0x52, 0x3d, 0x0f, 0xe8, 0x2b, 0xa2, 0x8c, 0x3c, 0xd3, 0x5b, 0x60, 0x31, 0xf1, 0x60, 0x20, 0xd6, 0x64, 0xb2, 0x9c, 0xa4, 0x99, 0xf5, 0x90, 0xd9, 0xca, 0x5f, 0xf2, 0x39, 0x1c, 0x6e, 0xd2, 0x0d, 0x65, 0xda, 0x0c, 0x2a, 0x67, 0x16, 0x6f, 0x63, 0x88, 0x6f, 0x66, 0xc7, 0x39, 0x4b, 0xf6, 0x5b, 0xaa, 0x30, 0x2b, 0xc2, 0xb9, 0x40, 0x88, 0xda, 0x10, 0xb4, 0x2d, 0x5f, 0x9b, 0x16, 0xc1, 0xb5, 0x19, 0x2f, 0x70, 0xa7, 0x22, 0xdf, 0x2d, 0xe8, 0x7b, 0x1c, 0x6d, 0xa8, 0xce, 0x75, 0xb9, 0x5b, 0x68, 0x01, 0xb3, 0x36, 0x95, 0xcd, 0x0c, 0x98, 0x5b, 0xf1, 0x1d, 0xfe, 0xaa, 0x20, 0xf2, 0xd8, 0x9b, 0xdd, 0xeb, 0x5b, 0x98, 0x6d, 0x59, 0xd1, 0xd4, 0xe8, 0x83, 0x05, 0xf1, 0x15, 0x9d, 0x56, 0xb2, 0x5a, 0xd6, 0xfd, 0x05, 0x7e, 0x7b, 0x89, 0xf6, 0x53, 0xcf, 0x5a, 0x43, 0xef, 0x49, 0x99, 0xf5, 0xee, 0xa0, 0x36, 0x5f, 0x07, 0x02, 0x3d, 0x08, 0xd9, 0xb1, 0xfc, 0xd2, 0xb5, 0x72, 0x60, 0xee, 0xa9, 0xaf, 0x27, 0x48, 0x96, 0x86, 0x97, 0xf5, 0x37, 0x74, 0x56, 0x2e, 0x80, 0x02, 0xc5, 0xf1, 0x80, 0x2e, 0xa8, 0xc1, 0x20, 0xcf, 0x60, 0x78, 0xcc, 0xa6, 0x48, 0xea, 0xf1, 0x72, 0xce, 0xb7, 0x23, 0xeb, 0x58, 0x51, 0x9f, 0xfa, 0xf6, 0x3d, 0x13, 0x5b, 0x82, 0xa6, 0x1c, 0x25, 0xac, 0x71, 0x94, 0xe9, 0x86, 0x60, 0xf7, 0x86, 0xe3, 0xab, 0x11, 0x0a, 0xbf, 0x23, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x75, 0x77, 0xd9, 0xe4, 0xa6, 0x14, 0x6e, 0x44, 0x44, 0xa3, 0xd5, 0x48, 0xc2, 0x54, 0x30, 0xb1, 0xbe, 0x24, 0x0f, 0x09, 0xf3, 0xb4, 0xb1, 0xb4, 0x97, 0x47, 0xff, 0x0a, 0xe2, 0x1e, 0xa2, 0xff, 0xcf, 0x78, 0x49, 0x56, 0xe6, 0x64, 0xfc, 0x68, 0xcd, 0xf8, 0x61, 0xef, 0x67, 0xf1, 0x44, 0x5e, 0x70, 0x90, 0xf3, 0xf2, 0x4e, 0x4e, 0x8c, 0xae, 0x31, 0xca, 0x70, 0xc6, 0x31, 0xed, 0x4b, 0x1c, 0xac, 0x2d, 0x6a, 0x0b, 0xaa, 0xb4, 0x08, 0xc8, 0xb6, 0xd3, 0xef, 0xf6, 0x2b, 0x5f, 0x8b, 0xe4, 0xd4, 0x3b, 0x7a, 0x73, 0x76, 0x75, 0x4f, 0xe7, 0x61, 0xd5, 0x66, 0xca, 0x4f, 0xd1, 0x16, 0xfb, 0x53, 0x9f, 0x1b, 0x1e, 0xd5, 0x2c, 0xdb, 0x0b, 0x11, 0x95, 0xb8, 0xa5, 0x29, 0xf6, 0xa3, 0x00, 0x61, 0x73, 0x0f, 0x5f, 0xbc, 0x6a, 0x87, 0xe0, 0x84, 0x4e, 0xcc, 0x91, 0x9d, 0x8a, 0x5c, 0x4a, 0x63, 0x67, 0xd5, 0xa7, 0x31, 0xe2, 0xc8, 0x79, 0xc3, 0x42, 0xce, 0xa2, 0x0f, 0x68, 0x3c, 0x8a, 0x84, 0xad, 0x53, 0xd7, 0x4f, 0x27, 0xfa, 0x65, 0xcf, 0x48, 0xd5, 0x50, 0xdc, 0x83, 0x29, 0xfc, 0x2c, 0xcf, 0x41, 0x07, 0xe8, 0xf8, 0x90, 0x03, 0xf1, 0x12, 0x8c, 0x9c, 0xee, 0x42, 0x19, 0x2a, 0xff, 0xf8, 0xf8, 0x0f, 0x64, 0x32, 0x28, 0x79, 0xf6, 0xe4, 0xad, 0x07, 0x2e, 0x1f, 0xef, 0x3b, 0xc6, 0xc9, 0x01, 0x08, 0x94, 0xfe, 0x22, 0x10, 0x05, 0x07, 0x7f, 0x01, 0x19, 0x86, 0xf1, 0xf6, 0x86, 0x79, 0x62, 0xb4, 0xe3, 0x0c, 0x92, 0x3c, 0x0b, 0x82, 0xb7, 0x7f, 0x8b, 0x4e, 0x3e, 0x85, 0x7e, 0xe1, 0x84, 0x5e, 0xc1, 0xa6, 0xf9, 0xdc, 0xc9, 0x8e, 0xec, 0x09, 0x7d, 0x9e, 0x52, 0xeb, 0xe6, 0x70, 0x9b, 0xa6, 0x8a, 0xbe, 0x09, 0xd6, 0xea, 0x34, 0x30, 0x26, 0x5c, 0x70, 0xc6, 0x55, 0x7c, 0x4d, 0xfc, 0xb6, 0xb5, 0x76, 0x8b, 0x5a, 0xa1, 0x96, 0x72, 0x81, 0xba, 0x47, 0xd4, 0x3e, 0xde, 0x29, 0x94, 0xa6, 0xcb, 0x59, 0xe4, 0xff, 0x44, 0xc9, 0xc4, 0xf5, 0x6c, 0x9a, 0xef, 0x52, 0xe4, 0x82, 0x5d, 0x51, 0xdd, 0x8b, 0x17, 0xfd, 0x56, 0x01, 0x8f, 0x9a, 0x36, 0xc8, 0xe5, 0x92, 0x40, 0xdb, 0x8c, 0x21, 0x30, 0x02, 0xdf, 0x4f, 0xd3, 0x42, 0x0f, 0xd1, 0x72, 0x01, 0x42, 0x5b, 0xe6, 0xb8, 0x27, 0x0c, 0xb6, 0x57, 0xf4, 0xf4, 0x81, 0xdb, 0x88, 0x24, 0x87, 0xa3, 0xb7, 0x06, 0x09, 0x4a, 0x9c, 0x1d, 0x42, 0xa6, 0x3d, 0x20, 0x5c, 0x14, 0x24, 0x53, 0xb3, 0x02, 0x32, 0xcd, 0xea, 0x10, 0xab, 0xa7, 0x79, 0x00, 0xd9, 0xa4, 0x44, 0xa3, 0x2d, 0x8a, 0xa2, 0xdd, 0x44, 0xf3, 0x32, 0x35, 0x10, 0x31, 0xf7, 0x59, 0x5a, 0x0b, 0x5a, 0x87, 0x51, 0xa7, 0x6b, 0x3b, 0xea, 0xcb, 0xd5, 0xf8, 0x07, 0x56, 0x1a, 0x53, 0xd9, 0xd8, 0x83, 0x77, 0xf3, 0xf4, 0xec, 0x3d, 0x0b, 0xc7, 0xea, 0x91, 0x4f, 0xe5, 0xaf, 0x4d, 0x8a, 0xb9, 0xcf, 0xd5, 0x86, 0x61, 0x5a, 0x94, 0xaa, 0x84, 0xb9, 0xad, 0x62, 0xeb, 0x1f, 0x26, 0x1b, 0x64, 0xb8, 0xdc, 0xe9, 0xe7, 0x8f, 0x8c, 0xfd, 0x19, 0xf5, 0x9d, 0xff, 0x6a, 0xb9, 0x01, 0x7c, 0x93, 0x70, 0x3f, 0x45, 0x4d, 0x29, 0xcc, 0x45, 0xcb, 0xba, 0x43, 0xc6, 0x2e, 0x03, 0xda, 0x4c, 0xd6, 0x21, 0x57, 0x8f, 0x74, 0x99, 0x31, 0x0f, 0xd0, 0x9b, 0x54, 0x8c, 0x57, 0xb0, 0x14, 0xd0, 0xdd, 0x26, 0xec, 0xbb, 0xb4, 0x2f, 0xb1, 0xfe, 0xb1, 0xba, 0x50, 0xd9, 0x24, 0xd1, 0x49, 0xbb, 0x68, 0x7a, 0x88, 0xeb, 0x38, 0xc8, 0x1e, 0x8e, 0x11, 0xb0, 0x58, 0x88, 0xaf, 0xa1, 0xf3, 0xb7, 0x7f, 0xc9, 0xab, 0xc2, 0x29, 0xec, 0x5c, 0x85, 0xc8, 0x18, 0xb8, 0x8d, 0xd6, 0x7f, 0xbb, 0xc2, 0x48, 0xfc, 0x73, 0xfa, 0xb4, 0x6a, 0xaa, 0x3c, 0x0f, 0x7c, 0x90, 0x75, 0x52, 0xed, 0x0e, 0x24, 0xdd, 0x5f, 0xbe, 0x1d, 0x88, 0xce, 0xbb, 0xeb, 0xf3, 0xcc, 0x24, 0x02, 0xed, 0xad, 0x97, 0x7e, 0x73, 0x1f, 0x82, 0x52, 0x35, 0x9b, 0xd9, 0x95, 0x9a, 0x2f, 0x2e, 0x91, 0x07, 0xa9, 0xb1, 0x5c, 0x39, 0xe8, 0xe4, 0x60, 0x7e, 0x1a, 0xa1, 0x45, 0xa2, 0xc8, 0xdb, 0x6f, 0xbb, 0x82, 0x33, 0x54, 0x46, 0xca, 0x97, 0xc6, 0xa5, 0xf6, 0x16, 0x7d, 0x98, 0x10, 0x6a, 0x66, 0x82, 0x22, 0x4a, 0x35, 0x35, 0x6b, 0x8c, 0xd3, 0xe7, 0x36, 0x39, 0x0f, 0x1a, 0x9c, 0xa5, 0xf2, 0x5d, 0x8b, 0x57, 0x16, 0xc8, 0xc3, 0xa6, 0xa9, 0x34, 0xa7, 0x30, 0x82, 0x12, 0x79, 0x6a, 0x75, 0x5e, 0x38, 0xef, 0x37, 0xaf, 0xad, 0x8b, 0xf1, 0x60, 0xdc, 0x6c, 0xb3, 0x97, 0xf5, 0x06, 0x56, 0x11, 0xdc, 0x96, 0xf2, 0x37, 0x12, 0x75, 0xac, 0xa7, 0x40, 0xb4, 0xd7, 0x43, 0x97, 0x4e, 0x70, 0x56, 0xef, 0xf0, 0x5e, 0xfb, 0xbe, 0xc2, 0xc2, 0x9a, 0xc3, 0xf5, 0xfb, 0x75, 0xde, 0x2a, 0xc6, 0x6e, 0x44, 0x3b, 0x65, 0x2d, 0xa4, 0xd2, 0x3b, 0x39, 0x21, 0x64, 0x4a, 0xc8, 0x14, 0xa5, 0xb9, 0x4a, 0x38, 0x6d, 0x8c, 0x46, 0x4c, 0x22, 0x76, 0x20, 0x9b, 0x00, 0x71, 0x32, 0x98, 0xd4, 0xea, 0x9e, 0x38, 0x89, 0xdb, 0x83, 0xd2, 0x9d, 0xbc, 0x04, 0x55, 0x55, 0x38, 0x0e, 0x03, 0x51, 0x57, 0x33, 0x75, 0x82, 0x07, 0x8c, 0x2a, 0xe7, 0x64, 0xe1, 0x18, 0x2c, 0x1f, 0x9e, 0xca, 0x3e, 0x35, 0xda, 0xaa, 0x08, 0x8c, 0xa7, 0x59, 0x5c, 0xaa, 0x92, 0x53, 0xc3, 0x87, 0x66, 0x14, 0x2a, 0x94, 0x35, 0xda, 0x03, 0x3b, 0xea, 0xea, 0x54, 0xea, 0x27, 0x25, 0x65, 0x64, 0x78, 0xbc, 0x76, 0x50, 0xd7, 0xd4, 0xc3, 0x3f, 0x62, 0x71, 0xbb, 0xe5, 0x85, 0x5d, 0xf9, 0x39, 0xc4, 0xdf, 0x6a, 0xa7, 0x46, 0x18, 0x4f, 0x71, 0x34, 0x6e, 0x1a, 0x3b, 0x5d, 0x9a, 0xa3, 0xde, 0xe0, 0xc1, 0xd7, 0xa1, 0xc5, 0x49, 0x68, 0x4e, 0xd1, 0xc8, 0xb6, 0x2b, 0xbe, 0xda, 0xdf, 0xc1, 0x68, 0x63, 0xdd, 0xef, 0xed, 0x86, 0x5e, 0xc1, 0x15, 0x73, 0x2d, 0x40, 0x2f, 0xe9, 0xeb, 0x16, 0x10, 0x34, 0x5e, 0xdf, 0x5d, 0xee, 0x75, 0x43, 0xe0, 0x29, 0x8e, 0x5e, 0xe3, 0x3f, 0xbb, 0xd1, 0x04, 0x9c, 0x91, 0xf1, 0x4b, 0xd7, 0x44, 0xeb, 0xba, 0xbe, 0x0c, 0x06, 0x0f, 0x5d, 0x05, 0x2c, 0x2f, 0x46, 0x46, 0x01, 0x7d, 0xae, 0x50, 0x2b, 0xe5, 0x38, 0xec, 0x44, 0x6f, 0x32, 0x27, 0xcc, 0x54, 0xfa, 0xf9, 0x97, 0x9a, 0xa6, 0xaf, 0xa5, 0x11, 0x6c, 0x59, 0xf5, 0x51, 0xd1, 0xd2, 0x5a, 0xfa, 0x6e, 0x58, 0xf0, 0xd0, 0xd0, 0xca, 0x84, 0x8d, 0x07, 0x3f, 0xb1, 0xe9, 0x27, 0x4d, 0x6d, 0x1e, 0x3e, 0x09, 0xaf, 0xd3, 0x5a, 0x6b, 0x36, 0x06, 0x7d, 0x85, 0xbe, 0x72, 0x76, 0x8d, 0x8f, 0xf3, 0x14, 0x82, 0xad, 0xbe, 0x89, 0xf3, 0x46, 0x78, 0xd9, 0x32, 0x49, 0xdb, 0x96, 0x19, 0xe2, 0x86, 0x29, 0xcf, 0x2f, 0x41, 0x4a, 0xdd, 0xab, 0xb1, 0xad, 0x36, 0xf0, 0x4a, 0xc3, 0x31, 0xa2, 0x95, 0xc2, 0xdb, 0xfc, 0x6b, 0xda, 0xf6, 0x07, 0x39, 0x55, 0x53, 0x21, 0x28, 0x06, 0x3d, 0xaf, 0xf2, 0x6c, 0x56, 0x94, 0xc4, 0x9d, 0xd2, 0x80, 0xac, 0x53, 0xc2, 0xc7, 0xaf, 0x55, 0x60, 0x6c, 0x18, 0x34, 0x37, 0x9f, 0x57, 0xdc, 0xb8, 0xdb, 0x96, 0x0d, 0x03, 0x23, 0xaf, 0xff, 0xfd, 0x03, 0x3d, 0x4f, 0x7e, 0x33, 0xa3, 0x24, 0xd8, 0x61, 0x23, 0xfc, 0x18, 0x76, 0xed, 0xa0, 0xaf, 0xb6, 0x09, 0x87, 0x3c, 0x7f, 0x1d, 0xae, 0x23, 0x2b, 0x50, 0x17, 0x39, 0x02, 0x72, 0x55, 0xc7, 0x67, 0xc7, 0x04, 0x0e, 0x31, 0x40, 0x7f, 0xb3, 0x84, 0xe7, 0x10, 0x89, 0x2f, 0xef, 0xe6, 0xfd, 0x1e, 0xfb, 0xcf, 0x3b, 0x10, 0xe8, 0x24, 0x50, 0x81, 0x72, 0xc3, 0x31, 0xd2, 0xa7, 0x65, 0xf2, 0xed, 0x95, 0xc4, 0x2a, 0xd9, 0x7e, 0x98, 0x4f, 0x50, 0xe3, 0xb1, 0x9d, 0x60, 0xbf, 0x3c, 0x50, 0x4f, 0xb4, 0x33, 0xd6, 0x19, 0xb6, 0xeb, 0x66, 0x36, 0x16, 0xd9, 0x39, 0x50, 0xcd, 0x16, 0xce, 0xdf, 0x52, 0x48, 0x8f, 0x01, 0x64, 0xff, 0x3d, 0x6c, 0x7e, 0x8a, 0xb4, 0x14, 0x5f, 0xc7, 0x8c, 0x29, 0xb9, 0xc5, 0xc7, 0x18, 0x47, 0xfe, 0xe3, 0x9e, 0xe8, 0xea, 0x65, 0x14, 0xe5, 0x40, 0x43, 0x71, 0xb9, 0x93, 0x3f, 0x7b, 0xf2, 0x81, 0x5d, 0x9b, 0x66, 0x1b, 0x08, 0x51, 0x0a, 0x4d, 0x27, 0x35, 0x54, 0x62, 0xb5, 0x71, 0x00, 0x99, 0x61, 0x79, 0xfe, 0x45, 0xa3, 0xaa, 0x72, 0x5c, 0xec, 0x5d, 0x0a, 0x7b, 0xee, 0xa2, 0x9b, 0x1f, 0xff, 0xf2, 0x53, 0x40, 0x9b, 0xce, 0xae, 0x9f, 0xd5, 0x07, 0x60, 0x18, 0x86, 0xb9, 0xdb, 0xc1, 0x2e, 0x61, 0xc8, 0xba, 0x73, 0x60, 0x2c, 0x7c, 0x14, 0x55, 0x6e, 0xce, 0x9d, 0x23, 0xf6, 0xcb, 0xbf, 0x3f, 0x3b, 0x9a, 0xd6, 0x21, 0x3b, 0x73, 0x27, 0x5f, 0xf3, 0xda, 0x99, 0x19, 0xb6, 0x81, 0xd9, 0x7e, 0xc0, 0x2e, 0x29, 0x4e, 0xcb, 0xa2, 0x3f, 0x67, 0xf3, 0x69, 0xde, 0xa7, 0xa5, 0xed, 0x9c, 0xb4, 0x4e, 0xcd, 0xd2, 0x74, 0xa3, 0xce, 0x3d, 0x59, 0x7d, 0x71, 0x72, 0x83, 0xb7, 0x10, 0x07, 0xd4, 0x2c, 0x03, 0xa3, 0x26, 0xe2, 0xed, 0x3c, 0x8b, 0xd7, 0xab, 0xf5, 0xb2, 0xe7, 0xf7, 0xe1, 0x0a, 0xeb, 0xe2, 0x3d, 0xcf, 0x1c, 0x96, 0x92, 0xa7, 0xe1, 0x35, 0x45, 0x3d, 0x7f, 0xe9, 0x58, 0x5b, 0xae, 0xfe, 0x6b, 0xfd, 0xaf, 0xd9, 0x68, 0x33, 0x26, 0xee, 0x6d, 0x9e, 0xc3, 0x0d, 0x17, 0x22, 0x0a, 0xf6, 0x55, 0x24, 0x2b, 0x26, 0x05, 0xaf, 0x66, 0x1d, 0xfb, 0x19, 0xec, 0x7c, 0x01, 0x1f, 0x71, 0xa3, 0x86, 0x50, 0x82, 0xb1, 0x61, 0x5a, 0x30, 0xac, 0x58, 0xe7, 0x62, 0xe1, 0x9f, 0xf9, 0x0c, 0x99, 0x06, 0x6d, 0xf9, 0x0f, 0xd0, 0x78, 0x79, 0x46, 0x65, 0x27, 0xe4, 0x03, 0xf2, 0x25, 0xb4, 0xa5, 0xe2, 0x95, 0x14, 0x5d, 0x49, 0x71, 0x9b, 0x5e, 0xf7, 0x0f, 0xe7, 0x6b, 0xe3, 0xe9, 0xce, 0x92, 0xb4, 0xef, 0x15, 0x3f, 0x1a, 0x5b, 0x73, 0xe9, 0x7e, 0x45, 0x4d, 0x8b, 0xdb, 0x58, 0x56, 0x67, 0x10, 0xbd, 0xd8, 0xe4, 0x83, 0x6f, 0x42, 0x67, 0x55, 0x78, 0xa7, 0x2a, 0xe6, 0xf7, 0x0b, 0x21, 0x04, 0x19, 0x2b, 0xbe, 0x5c, 0x40, 0x3c, 0xa0, 0x7e, 0x94, 0x56, 0x7c, 0xcd, 0x85, 0x49, 0x28, 0xf9, 0xec, 0x85, 0x94, 0xfc, 0x8c, 0x4f, 0x60, 0xd6, 0x9a, 0x58, 0xaa, 0x83, 0xbe, 0x9a, 0xf1, 0x99, 0xeb, 0x02, 0xce, 0xb5, 0x74, 0x27, 0x49, 0x35, 0xd1, 0xcd, 0xdc, 0x05, 0xab, 0x07, 0x81, 0xac, 0x36, 0xf4, 0x1a, 0x4d, 0x6b, 0xd9, 0xc4, 0xd5, 0x1a, 0x41, 0xab, 0x90, 0x34, 0xe8, 0xbb, 0xe1, 0xb7, 0xad, 0x46, 0x5d, 0x11, 0x17, 0x2f, 0x60, 0xa1, 0x2e, 0x4a, 0xbf, 0xea, 0x75, 0xba, 0x65, 0xd7, 0x30, 0xf5, 0x75, 0x92, 0xba, 0xbb, 0x69, 0x2e, 0x2b, 0x5d, 0x72, 0xcc, 0xec, 0xf3, 0x19, 0x74, 0x2d, 0x1c, 0x1b, 0xb3, 0xf5, 0xe8, 0xd8, 0xc8, 0x9f, 0xe7, 0x6e, 0x83, 0x44, 0x61, 0xbb, 0x5e, 0x8c, 0xaf, 0x3f, 0xc1, 0x8b, 0x7f, 0xaf, 0x2c, 0xc9, 0x4d, 0xfe, 0xde, 0x37, 0xa1, 0x96, 0x32, 0x76, 0x38, 0xcb, 0xc3, 0x38, 0xce, 0x96, 0x12, 0x9b, 0xb9, 0x8c, 0x91, 0xac, 0x9f, 0x95, 0x0d, 0x27, 0x3c, 0xb2, 0x1d, 0xfb, 0xc3, 0x94, 0x29, 0xc8, 0xc2, 0xa9, 0xd8, 0x3f, 0x29, 0x85, 0xa7, 0x16, 0x37, 0xb9, 0xc3, 0x96, 0x52, 0x76, 0xe2, 0x01, 0xa3, 0x4e, 0x54, 0xb5, 0x3b, 0x0a, 0x29, 0x6d, 0x90, 0xc1, 0xd1, 0x97, 0x56, 0x04, 0xd7, 0x78, 0x0a, 0x1d, 0x5c, 0x5e, 0x27, 0xa0, 0x46, 0x95, 0xb8, 0x7d, 0x0e, 0xad, 0x2c, 0xe7, 0x1f, 0xea, 0xa1, 0x80, 0xc9, 0x20, 0x21, 0xf7, 0x98, 0xca, 0x9c, 0x06, 0x31, 0x34, 0xec, 0x75, 0xd2, 0x10, 0x50, 0xe8, 0xae, 0x95, 0x54, 0x96, 0xc2, 0x45, 0x70, 0xef, 0x32, 0xae, 0x38, 0xd1, 0x69, 0x95, 0xba, 0xa8, 0x8a, 0x9f, 0xae, 0x8f, 0xea, 0x06, 0x57, 0x98, 0xe7, 0xdd, 0x3f, 0xd8, 0x7d, 0x9a, 0xff, 0xf5, 0xb4, 0x4b, 0xe1, 0x72, 0x2d, 0x5f, 0x53, 0xdb, 0x8a, 0x3a, 0xff, 0xe5, 0x58, 0x7d, 0xa1, 0xe9, 0xdb, 0x9b, 0x71, 0xf6, 0xb9, 0x0b, 0x43, 0x04, 0x70, 0xad, 0x29, 0xd1, 0x98, 0x0f, 0x1b, 0xbe, 0x27, 0x94, 0x7d, 0xf6, 0x1c, 0x7d, 0x39, 0x1d, 0x8d, 0x3d, 0x25, 0xbb, 0x1c, 0x8e, 0x67, 0x3b, 0x22, 0xa9, 0x5e, 0x5c, 0x95, 0x6e, 0x2c, 0x61, 0x1e, 0x5d, 0x18, 0x4b, 0xd0, 0x41, 0x6f, 0x9b, 0x1c, 0x6d, 0x5d, 0x8b, 0x8a, 0x10, 0x4b, 0x14, 0xc0, 0x91, 0xcf, 0xc9, 0x69, 0x77, 0x08, 0x3b, 0x1d, 0x4f, 0x2b, 0x8e, 0x7b, 0xc7, 0x66, 0xe1, 0x0b, 0xda, 0x69, 0xbb, 0xcc, 0x21, 0x09, 0x07, 0xaa, 0x47, 0xd3, 0x6c, 0x91, 0xc3, 0x43, 0xdb, 0x07, 0x5a, 0xd4, 0xbf, 0x88, 0xca, 0x23, 0xcb, 0xcc, 0xf9, 0x0c, 0x09, 0xc6, 0xdf, 0xe2, 0xe1, 0xa4, 0x7c, 0x42, 0x4a, 0x0d, 0xf9, 0xa7, 0xf1, 0x1a, 0x6c, 0xb9, 0x5c, 0x09, 0xa9, 0x92, 0x90, 0xe9, 0xce, 0xa5, 0x62, 0xd4, 0x51, 0x6b, 0x32, 0xc1, 0x2f, 0x70, 0x68, 0x40, 0x11, 0x3d, 0x6a, 0x35, 0xa0, 0xde, 0x38, 0x3a, 0x10, 0x0f, 0x6e, 0x9b, 0x23, 0x6a, 0x3d, 0x2e, 0xab, 0x7b, 0x77, 0x5e, 0xa2, 0x11, 0xbc, 0xa1, 0x8e, 0x05, 0xcd, 0x27, 0x97, 0x98, 0x4b, 0x9c, 0xec, 0x30, 0x8b, 0x2b, 0xdf, 0x84, 0x30, 0xa2, 0x72, 0xc7, 0x59, 0xdb, 0xbf, 0x4f, 0xc8, 0x9e, 0x90, 0x3f, 0x33, 0x4a, 0x86, 0xd5, 0x43, 0x8f, 0xa0, 0x7f, 0x75, 0x90, 0xb1, 0x51, 0xbd, 0xc8, 0x58, 0xe6, 0xbb, 0x55, 0x24, 0x62, 0x28, 0x54, 0xb9, 0x85, 0xe7, 0x09, 0x8d, 0xec, 0xfb, 0xe5, 0x55, 0x4a, 0x09, 0x08, 0xc0, 0xbc, 0x2a, 0x29, 0xb7, 0xbe, 0x69, 0x73, 0x68, 0xd4, 0xc2, 0xaa, 0x24, 0xd0, 0x69, 0x9d, 0xc2, 0xcd, 0xf5, 0x78, 0x80, 0xbe, 0xe0, 0xcc, 0xe1, 0x1d, 0x7d, 0xcc, 0xe1, 0x1e, 0x3b, 0x40, 0xfb, 0x17, 0xa2, 0x11, 0xa5, 0xa5, 0x7a, 0x22, 0xf3, 0x98, 0x9c, 0x85, 0xa8, 0x2f, 0x01, 0x6f, 0xb5, 0x7b, 0xb4, 0x2f, 0xde, 0x0b, 0x85, 0x02, 0x7c, 0x1b, 0xba, 0x0e, 0x40, 0xcb, 0x1f, 0xac, 0x88, 0x35, 0x84, 0xe3, 0x30, 0x90, 0x77, 0xe7, 0xc9, 0xf5, 0x1c, 0xf7, 0x3a, 0x21, 0x8a, 0x26, 0x2a, 0xc8, 0x98, 0x1a, 0xaf, 0xbd, 0x4e, 0x12, 0xe3, 0x76, 0x01, 0x20, 0x17, 0x2e, 0x57, 0x15, 0xe0, 0xab, 0x1c, 0xf6, 0x89, 0x1b, 0x45, 0xfa, 0x20, 0x1f, 0x3d, 0xc0, 0x40, 0xf4, 0x83, 0x03, 0x76, 0x38, 0x81, 0xe1, 0x35, 0xdc, 0x7a, 0x88, 0x13, 0x50, 0x47, 0x72, 0x9c, 0x8d, 0x8f, 0x69, 0x70, 0xcd, 0xc0, 0x6f, 0x7a, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x7d, 0x70, 0x64, 0xa8, 0x3f, 0x8a, 0x3a, 0x5c, 0xdc, 0xc3, 0xb6, 0x0e, 0x2e, 0x85, 0x44, 0x01, 0x62, 0xb2, 0x2b, 0x63, 0xcb, 0x0a, 0xab, 0xa1, 0xf7, 0x60, 0xae, 0x98, 0xa1, 0x3a, 0xf7, 0xd6, 0xc8, 0x39, 0x4d, 0x37, 0xe5, 0x42, 0x0e, 0x62, 0xfc, 0x7c, 0x33, 0x38, 0xdc, 0xaa, 0x14, 0x67, 0xa7, 0xed, 0xaa, 0xc6, 0xc7, 0x96, 0xb7, 0xf5, 0x5a, 0x9d, 0x23, 0xbd, 0x0f, 0x50, 0xa8, 0x0d, 0x4d, 0x27, 0x48, 0xfe, 0x79, 0xc6, 0x13, 0x6f, 0x4e, 0xd3, 0xd7, 0xae, 0xd7, 0xcc, 0xf5, 0x43, 0x07, 0x5b, 0x81, 0x4e, 0x23, 0x4e, 0x29, 0x16, 0x22, 0xb7, 0xa1, 0x90, 0x0a, 0x99, 0x23, 0xdd, 0xac, 0x5f, 0xb3, 0x36, 0x23, 0x84, 0x64, 0xf9, 0xf3, 0x8d, 0x4a, 0x3c, 0x70, 0x43, 0x39, 0x9d, 0x5d, 0xf4, 0x23, 0x2e, 0x02, 0xd2, 0x7f, 0x78, 0x5e, 0x73, 0x3b, 0xfd, 0x8f, 0x68, 0x59, 0x82, 0x2c, 0x64, 0xfe, 0x7e, 0x36, 0x68, 0x89, 0x59, 0x75, 0xa4, 0x07, 0x8b, 0xfc, 0x87, 0x32, 0xcf, 0x98, 0xee, 0xa4, 0xda, 0x37, 0x1d, 0x3d, 0x09, 0x17, 0x70, 0x25, 0x11, 0x90, 0xc9, 0xda, 0x0e, 0xca, 0xa7, 0x6f, 0xc8, 0x7f, 0x12, 0xe9, 0x18, 0xf0, 0xb0, 0xa9, 0xbf, 0x94, 0x00, 0x5e, 0x3b, 0x76, 0xa1, 0x41, 0x14, 0xec, 0x57, 0x3b, 0x67, 0x6c, 0x0d, 0xaa, 0x20, 0x52, 0xc5, 0x82, 0x19, 0xd3, 0x37, 0x34, 0x5e, 0xd2, 0x62, 0xe2, 0x66, 0x95, 0xf9, 0x58, 0x25, 0x16, 0x77, 0xff, 0xff, 0x8e, 0xd1, 0xfc, 0xb2, 0xa5, 0x60, 0x15, 0x21, 0x34, 0xf0, 0x2e, 0x74, 0xf2, 0x85, 0x23, 0x60, 0x57, 0x01, 0xaa, 0x9f, 0x18, 0x70, 0x3a, 0x4c, 0x87, 0xa2, 0x7f, 0xaf, 0xb4, 0xfa, 0x6d, 0xba, 0xaa, 0xfe, 0x12, 0x9c, 0x04, 0x2a, 0x28, 0xcd, 0x14, 0x9a, 0x18, 0xf9, 0xc9, 0x2e, 0x08, 0x5f, 0x3a, 0x8c, 0x11, 0xe4, 0x52, 0x0a, 0xdf, 0x2e, 0x26, 0xd4, 0x99, 0xf8, 0x4c, 0xa7, 0xbf, 0x2a, 0xb3, 0x90, 0x07, 0x9c, 0xc0, 0x43, 0x0b, 0x07, 0x5a, 0xa5, 0x9b, 0x1d, 0x25, 0x69, 0x1a, 0x5c, 0x35, 0x5a, 0x59, 0x06, 0x26, 0xa2, 0xe3, 0xf7, 0x03, 0x58, 0x01, 0x90, 0x37, 0x07, 0x8b, 0x44, 0x64, 0xc6, 0xf3, 0x0b, 0x64, 0x79, 0xff, 0x59, 0x3e, 0xe0, 0x82, 0xaf, 0xac, 0x06, 0x31, 0x7a, 0x93, 0x69, 0x67, 0xe3, 0xca, 0x22, 0xcf, 0xc2, 0x0f, 0xd0, 0xec, 0xc5, 0x75, 0xbf, 0x9b, 0x02, 0x0f, 0xd0, 0x09, 0x1d, 0x02, 0x80, 0x31, 0x6c, 0x7b, 0xeb, 0x11, 0xe5, 0x06, 0xe8, 0x84, 0x27, 0x9f, 0x9a, 0xbf, 0x39, 0x73, 0xbe, 0xef, 0x5b, 0xce, 0x05, 0x60, 0xe7, 0x02, 0x47, 0xf2, 0x59, 0x4f, 0xd9, 0xfd, 0x1a, 0x4f, 0x13, 0x69, 0xc0, 0xdc, 0xd9, 0xfa, 0xfa, 0x18, 0xaa, 0x8d, 0x8f, 0xd8, 0x42, 0xee, 0x3a, 0x12, 0xcf, 0xe6, 0xaa, 0x64, 0x3d, 0x98, 0xc9, 0x28, 0x1f, 0x01, 0x63, 0xa4, 0xe6, 0x4a, 0xb0, 0x69, 0x30, 0x4c, 0x52, 0x8a, 0xbc, 0xa9, 0x18, 0x31, 0xe9, 0x14, 0x55, 0xf7, 0xdf, 0x79, 0x12, 0x0b, 0x8c, 0x69, 0xf3, 0xb3, 0x8a, 0xb8, 0x86, 0x90, 0x21, 0x96, 0x1c, 0x5b, 0xd8, 0x0a, 0x10, 0xbe, 0xc3, 0xd7, 0x15, 0xae, 0xcb, 0x19, 0x2b, 0xf9, 0xa8, 0x24, 0x5f, 0x04, 0x8c, 0xfe, 0xc3, 0x93, 0x8e, 0x8c, 0x01, 0x44, 0x6b, 0x92, 0x27, 0x06, 0xc4, 0x4b, 0x41, 0xf1, 0x6c, 0xd4, 0xfa, 0xf2, 0x51, 0xd5, 0xaa, 0xac, 0x5b, 0xcd, 0xa3, 0xb0, 0x14, 0xb7, 0x48, 0x5c, 0x77, 0xf1, 0x12, 0xeb, 0xcd, 0x17, 0x04, 0xb0, 0xea, 0x37, 0x3b, 0xc4, 0xa2, 0x47, 0x35, 0x87, 0x4d, 0x8c, 0x9d, 0x61, 0xdb, 0xad, 0x95, 0x76, 0xad, 0x11, 0x72, 0x8c, 0xff, 0xed, 0x8e, 0x94, 0xbf, 0xd3, 0xb5, 0xe8, 0xfd, 0xa9, 0x60, 0x17, 0x8b, 0xec, 0xaa, 0x4e, 0x8d, 0x1b, 0xf3, 0xa0, 0xca, 0x56, 0x1a, 0xe8, 0x95, 0x48, 0x35, 0xa2, 0xa3, 0xd1, 0xd7, 0xd2, 0x99, 0x14, 0x4c, 0x96, 0x53, 0x9a, 0x8b, 0x4e, 0xa8, 0x0b, 0xb1, 0xaa, 0x6d, 0x9b, 0x43, 0x7e, 0x6c, 0xe9, 0x77, 0x68, 0x75, 0x09, 0x43, 0xd5, 0xa7, 0x36, 0xc6, 0x3b, 0xf4, 0x2c, 0xd4, 0x65, 0x6f, 0xa0, 0xe2, 0x80, 0x81, 0xc7, 0xbf, 0x19, 0x92, 0xb1, 0x1a, 0x00, 0x4f, 0xa0, 0x4a, 0x5b, 0xad, 0x62, 0x9a, 0xad, 0x50, 0x86, 0xf2, 0x88, 0x04, 0x80, 0x35, 0x08, 0x3a, 0x34, 0x4b, 0xde, 0xc3, 0x3d, 0xd8, 0x2a, 0x7a, 0x53, 0x9d, 0x88, 0xca, 0xca, 0x47, 0xa4, 0xcb, 0xff, 0x2e, 0xbf, 0x8d, 0x34, 0xba, 0x79, 0xdf, 0x7b, 0x2a, 0xa2, 0xa3, 0x5f, 0x05, 0x85, 0x7e, 0x90, 0x81, 0xb2, 0xf6, 0xa3, 0xd4, 0x82, 0x0f, 0xc9, 0x64, 0x06, 0x92, 0x0c, 0x09, 0x47, 0x72, 0x37, 0x34, 0x84, 0x8a, 0x02, 0x34, 0x49, 0x9a, 0xae, 0xa2, 0x47, 0xa2, 0xb6, 0x17, 0x3d, 0x32, 0xb3, 0x0a, 0x6b, 0x0a, 0xb4, 0x2c, 0xbc, 0x80, 0x06, 0x84, 0xcd, 0x86, 0xec, 0x9e, 0x21, 0x6d, 0xb6, 0xbb, 0x1c, 0x60, 0x96, 0x0b, 0xe8, 0xe3, 0x28, 0x03, 0xc6, 0x2e, 0x89, 0xd3, 0x87, 0x1b, 0xf5, 0x14, 0xdd, 0x37, 0x50, 0x6a, 0xd1, 0x93, 0x37, 0x0b, 0x82, 0x78, 0x93, 0x18, 0x56, 0x9c, 0x95, 0x0b, 0x37, 0x37, 0xfe, 0xfd, 0x88, 0xc3, 0xad, 0xb8, 0x56, 0x16, 0xa4, 0xa2, 0xda, 0x0c, 0x92, 0xa5, 0xa2, 0xe9, 0x37, 0x55, 0x9c, 0x24, 0x90, 0x3a, 0x53, 0x81, 0xee, 0x6d, 0x98, 0x9e, 0x7a, 0x46, 0xb9, 0x42, 0x22, 0xfd, 0x2a, 0xb9, 0xc2, 0xa2, 0xde, 0x01, 0x0d, 0x5c, 0x5d, 0x91, 0x0f, 0x31, 0x52, 0xec, 0x23, 0x72, 0x5c, 0xee, 0x63, 0x81, 0xfa, 0x96, 0x9f, 0x1e, 0x4f, 0x62, 0x00, 0xb8, 0xb5, 0xf1, 0x03, 0x2a, 0x7f, 0xe0, 0x7b, 0x2c, 0x38, 0xf0, 0xb6, 0x50, 0xfc, 0xf3, 0xbc, 0xe2, 0x7c, 0xf8, 0xf3, 0x4f, 0x6b, 0x98, 0xf8, 0x9b, 0x02, 0x6d, 0xcc, 0x34, 0x2b, 0xfb, 0xfc, 0x59, 0x13, 0xb2, 0x2f, 0x7e, 0x50, 0x90, 0xe5, 0x99, 0x4d, 0x6b, 0xa0, 0xe9, 0x0f, 0x71, 0x73, 0xff, 0xaf, 0xcd, 0x14, 0xf1, 0x70, 0x31, 0x6c, 0x2d, 0x51, 0x3c, 0xf4, 0xe6, 0x4a, 0x25, 0x90, 0x51, 0x86, 0x86, 0x66, 0xce, 0x20, 0xdd, 0x5c, 0xe0, 0x35, 0x0a, 0xf9, 0x9b, 0xe0, 0xd5, 0xa5, 0x76, 0xf6, 0x7f, 0x9a, 0x96, 0x3b, 0x92, 0x37, 0xe5, 0xb1, 0xb7, 0xba, 0x0c, 0xe0, 0x73, 0x07, 0x99, 0xac, 0xa4, 0x3d, 0x2f, 0x14, 0x69, 0x6f, 0x70, 0xf5, 0x2b, 0xe5, 0x11, 0x31, 0x71, 0x02, 0xca, 0x77, 0xb2, 0x81, 0x1c, 0xc5, 0xe9, 0xf4, 0xcd, 0x85, 0xb0, 0xd0, 0x97, 0x10, 0xa9, 0xc5, 0xa9, 0xc5, 0xcf, 0x6c, 0xd3, 0x5d, 0xe5, 0x55, 0xe2, 0x2a, 0xb3, 0x23, 0xda, 0x36, 0x63, 0xa7, 0xd7, 0x1c, 0xb8, 0xba, 0x5a, 0x2b, 0xd2, 0x88, 0xdb, 0xe1, 0x77, 0xf9, 0x2b, 0xf6, 0x9f, 0xa5, 0xa5, 0xcd, 0x08, 0x8e, 0x2e, 0x72, 0x35, 0xac, 0x50, 0xe5, 0x5f, 0xfa, 0xf9, 0xa4, 0x9d, 0x7c, 0xfd, 0xf8, 0x15, 0x35, 0x38, 0xa1, 0x7e, 0x26, 0x0c, 0x9a, 0x16, 0x3a, 0x14, 0x22, 0x14, 0x9d, 0x26, 0x33, 0x55, 0x76, 0x53, 0x2e, 0xaa, 0x84, 0xb0, 0xb0, 0x2d, 0x4d, 0xff, 0x4d, 0x08, 0x01, 0x69, 0x2a, 0x97, 0xdc, 0xff, 0x41, 0x35, 0x27, 0xc1, 0x93, 0xed, 0xd7, 0xe7, 0x70, 0xcb, 0x9c, 0xa1, 0x8a, 0xa0, 0xce, 0x42, 0xd7, 0x41, 0x5d, 0x9a, 0xef, 0x08, 0x58, 0xd2, 0x8b, 0x72, 0x3f, 0x21, 0xeb, 0xcf, 0x20, 0xaf, 0xed, 0x0b, 0xc4, 0x25, 0x14, 0xe9, 0x57, 0xd5, 0x0d, 0xc7, 0xcb, 0x56, 0x10, 0x89, 0x11, 0xa5, 0x92, 0xb3, 0xbb, 0xb1, 0xc7, 0xc7, 0x94, 0x4f, 0x0a, 0x9d, 0xcd, 0x10, 0x62, 0x06, 0xa7, 0x48, 0x68, 0x2e, 0x67, 0xf0, 0xb0, 0x1e, 0x12, 0xd6, 0x27, 0xaf, 0xef, 0xe9, 0xa5, 0x6e, 0x58, 0x79, 0x6a, 0x4f, 0xc6, 0xad, 0xee, 0xb3, 0x5d, 0xd4, 0xf8, 0x51, 0x47, 0x71, 0x55, 0x5f, 0x25, 0xc6, 0x57, 0x0d, 0xbe, 0xba, 0x8a, 0xa9, 0xf6, 0x25, 0x24, 0x32, 0x76, 0x9f, 0x75, 0x3b, 0xfa, 0x30, 0xf2, 0xa1, 0x5e, 0xea, 0x69, 0xa4, 0x4b, 0xa3, 0x27, 0x25, 0x3c, 0x3f, 0xe1, 0xb1, 0x1f, 0xd3, 0x03, 0x52, 0xf4, 0x8e, 0xf4, 0xf3, 0x10, 0x83, 0x69, 0x81, 0x58, 0x44, 0x3f, 0x93, 0xb6, 0x41, 0x1b, 0x86, 0x5d, 0x9d, 0xe4, 0x16, 0x51, 0xac, 0x02, 0xb1, 0xdb, 0x77, 0xdc, 0x51, 0x61, 0x8d, 0x5b, 0x04, 0x08, 0xa6, 0x8d, 0xa0, 0x89, 0x11, 0xf4, 0x95, 0xd8, 0xc1, 0x87, 0xb2, 0xa9, 0xe1, 0xb1, 0x68, 0xbb, 0x0d, 0xdf, 0x2d, 0x14, 0xa5, 0x0f, 0x1b, 0x5a, 0xbd, 0x32, 0x2f, 0x88, 0x9b, 0x5d, 0x78, 0xdf, 0x89, 0x13, 0x62, 0xc3, 0x4f, 0x6b, 0x29, 0xa0, 0x0c, 0x65, 0x21, 0x9a, 0x4f, 0x18, 0xbc, 0x4c, 0x94, 0x21, 0x37, 0x76, 0x22, 0xf8, 0x39, 0x21, 0xe2, 0xb3, 0x7c, 0x83, 0xe3, 0x13, 0x1b, 0x92, 0x30, 0x9e, 0x07, 0xc9, 0x78, 0xe5, 0xe9, 0x4a, 0x0b, 0x09, 0xe3, 0x9c, 0x8e, 0x00, 0x98, 0x1b, 0x57, 0x92, 0x1e, 0x3b, 0x78, 0x24, 0xc2, 0xf7, 0x24, 0x31, 0x15, 0x99, 0x5b, 0xc8, 0x58, 0xc0, 0x32, 0xde, 0x31, 0x3a, 0xb8, 0xb4, 0xac, 0xde, 0xfe, 0x2f, 0x1b, 0x82, 0x42, 0xe9, 0xb0, 0x7e, 0xd0, 0x68, 0x0d, 0x69, 0xce, 0xa4, 0x2a, 0x2a, 0x63, 0xc2, 0xfd, 0xc3, 0xd4, 0x73, 0x0e, 0x2c, 0xd5, 0x8e, 0x5c, 0xeb, 0x2b, 0x7e, 0x31, 0xd4, 0x1a, 0x49, 0x64, 0xd0, 0xb4, 0x5b, 0x06, 0x4c, 0xb4, 0x55, 0x25, 0xf1, 0x8e, 0xc6, 0xd7, 0xe9, 0x37, 0x11, 0x46, 0x40, 0x43, 0xb2, 0x9d, 0x51, 0xfe, 0x83, 0x51, 0xf6, 0xd4, 0xca, 0xd3, 0xed, 0x0c, 0x0f, 0x74, 0xa5, 0x4c, 0xc7, 0x29, 0x4f, 0x55, 0xf1, 0x71, 0x6d, 0x90, 0x9a, 0x36, 0x51, 0x3a, 0x7f, 0xbb, 0xd1, 0x37, 0x8a, 0xde, 0xd6, 0xfe, 0xb7, 0x1c, 0xbe, 0xe4, 0xc6, 0x4e, 0xb0, 0x91, 0x54, 0xd2, 0xf3, 0x1b, 0xbe, 0xaa, 0x1a, 0xf6, 0x70, 0xbd, 0x07, 0x5d, 0x65, 0xa9, 0xd4, 0xbd, 0xda, 0xa3, 0x35, 0xad, 0x2f, 0x88, 0x7e, 0xb4, 0x0c, 0x8c, 0x62, 0x5a, 0x9a, 0x03, 0x3b, 0x9d, 0x67, 0x1b, 0x94, 0xa2, 0xce, 0x7d, 0x9d, 0x05, 0x0d, 0x19, 0xe2, 0x84, 0x3e, 0xb4, 0x5d, 0xc2, 0xf1, 0x72, 0x9f, 0xbd, 0xad, 0x52, 0xa4, 0x7a, 0xa2, 0x4a, 0x99, 0x28, 0x5b, 0x9a, 0x47, 0xb0, 0xac, 0xc5, 0x02, 0x4b, 0x2d, 0x3f, 0xc5, 0x20, 0xed, 0xcd, 0x8c, 0xda, 0x44, 0x73, 0x0f, 0x68, 0x86, 0x19, 0xce, 0xf0, 0xf5, 0x58, 0x44, 0x0d, 0xb6, 0x4e, 0x4d, 0x1e, 0xf6, 0x72, 0xcb, 0x3a, 0xff, 0xfb, 0xd1, 0x2a, 0xf6, 0x7e, 0x1f, 0xe0, 0xe0, 0x29, 0xdf, 0xb8, 0x0c, 0x75, 0x2d, 0x28, 0x48, 0x29, 0xa2, 0xd5, 0xcc, 0xe7, 0x9c, 0xf2, 0x0b, 0x12, 0x27, 0x19, 0xd9, 0xb7, 0x10, 0x31, 0x36, 0xc5, 0x51, 0xaa, 0xe5, 0x94, 0xeb, 0x0f, 0x36, 0xaf, 0xb5, 0xdf, 0x22, 0xd5, 0x32, 0xbc, 0xeb, 0xf7, 0x38, 0x68, 0x86, 0x3a, 0x19, 0x43, 0xed, 0x78, 0x1a, 0x15, 0x9e, 0xb9, 0xc2, 0x9c, 0x98, 0x04, 0x10, 0x28, 0x1d, 0x22, 0xd9, 0x3d, 0x20, 0x99, 0x62, 0xfb, 0xd8, 0xec, 0x13, 0x7f, 0xc0, 0x79, 0x99, 0xe2, 0x73, 0x74, 0xf7, 0xa9, 0xc9, 0x05, 0x18, 0xdb, 0x15, 0x11, 0x12, 0x5c, 0xd7, 0xeb, 0x53, 0x6f, 0xba, 0x3b, 0x7c, 0x8c, 0x7d, 0xd8, 0x74, 0xa1, 0x32, 0x8e, 0x3c, 0x3f, 0x59, 0x5d, 0x3c, 0xcf, 0xf5, 0xee, 0x62, 0x2d, 0x50, 0x23, 0xbd, 0x3d, 0x13, 0xce, 0x0d, 0x90, 0xc9, 0xf5, 0xbd, 0x80, 0x2e, 0xf8, 0x7d, 0x9c, 0x9d, 0xbd, 0xd2, 0x2b, 0xad, 0x2b, 0x44, 0x0f, 0x47, 0x07, 0x7b, 0x64, 0x57, 0x2e, 0x9e, 0xbd, 0xff, 0x63, 0xa2, 0xf6, 0x6c, 0xc0, 0x2a, 0x93, 0x77, 0xfb, 0xb8, 0x9c, 0x7a, 0x77, 0x4b, 0x24, 0x6b, 0x0e, 0xb0, 0x2b, 0xa3, 0xcf, 0xa9, 0xec, 0x5c, 0x7d, 0x4c, 0xcd, 0xbf, 0x65, 0xbb, 0x5d, 0x8b, 0x43, 0x69, 0x45, 0xdb, 0x8b, 0x81, 0xdf, 0x3b, 0x34, 0xd6, 0x8c, 0x74, 0xc8, 0x63, 0xd5, 0x88, 0x5c, 0xfe, 0xd7, 0x9b, 0xd8, 0x32, 0x42, 0x88, 0x79, 0x81, 0x33, 0xaa, 0x9c, 0x0e, 0x0e, 0x90, 0x52, 0xf8, 0x44, 0x10, 0xb3, 0x01, 0x07, 0xf9, 0x3b, 0x67, 0x26, 0x71, 0xf2, 0x7b, 0x39, 0xf0, 0x1e, 0xe8, 0xc8, 0xd2, 0xbd, 0xbe, 0x36, 0xae, 0x28, 0x8f, 0xe7, 0xf3, 0x1a, 0xb5, 0x92, 0xf2, 0x46, 0x48, 0xf6, 0xc1, 0xc3, 0x1d, 0x05, 0xe5, 0x26, 0xd3, 0xba, 0x32, 0xcd, 0x17, 0x64, 0xbd, 0xc1, 0xfc, 0xd1, 0x53, 0xbf, 0x8d, 0xd4, 0xf4, 0x03, 0x48, 0x24, 0xb8, 0x16, 0x40, 0x63, 0xce, 0xd3, 0xd1, 0xf2, 0x21, 0x38, 0x9e, 0x01, 0xc4, 0x6a, 0xfb, 0x0b, 0x27, 0x35, 0x6d, 0xf5, 0x65, 0x67, 0xe2, 0x2a, 0x20, 0x32, 0xbc, 0xa8, 0xca, 0x95, 0x24, 0xb2, 0xe9, 0xab, 0xaa, 0xa8, 0xcf, 0xa3, 0xb6, 0xc4, 0x29, 0xe2, 0x5b, 0x72, 0x00, 0x64, 0x64, 0x96, 0x25, 0x04, 0xb0, 0x98, 0x74, 0xfb, 0xe8, 0x90, 0x83, 0x4e, 0x9f, 0x08, 0x0e, 0xbe, 0x71, 0xfa, 0xf5, 0x5f, 0x0d, 0xd9, 0xb3, 0x1e, 0x11, 0xb8, 0x50, 0x28, 0x5d, 0x21, 0xd9, 0x77, 0x28, 0xa7, 0xa8, 0x63, 0x84, 0xeb, 0xab, 0xa1, 0x29, 0x35, 0xf9, 0xc6, 0x6a, 0xfa, 0x7b, 0x31, 0xbb, 0xdc, 0x4a, 0xce, 0x86, 0xd4, 0xcd, 0x64, 0x53, 0x45, 0x4b, 0x0b, 0x8c, 0x1d, 0x76, 0x8d, 0xd2, 0x27, 0x74, 0xf2, 0x37, 0x95, 0x84, 0xe3, 0xfd, 0x43, 0xf6, 0x0c, 0x44, 0xea, 0xbc, 0x4c, 0x52, 0x6f, 0xca, 0x23, 0x48, 0x92, 0xa8, 0x43, 0x3e, 0x64, 0xd6, 0x0e, 0xd1, 0x59, 0xf2, 0xe2, 0xfe, 0xe5, 0xdc, 0x12, 0x9a, 0x65, 0x26, 0x1d, 0x18, 0x1f, 0x8d, 0xf8, 0xeb, 0xc5, 0x92, 0xcc, 0x47, 0x1d, 0x32, 0x60, 0xe7, 0x7b, 0xa6, 0xd2, 0x09, 0xaa, 0x70, 0xfc, 0x1e, 0x02, 0x70, 0x54, 0xff, 0x71, 0x93, 0xa5, 0x28, 0xc9, 0xf4, 0x08, 0x5a, 0xa6, 0x29, 0x85, 0x77, 0xaa, 0xdc, 0xdb, 0x8e, 0xb6, 0x52, 0x48, 0x97, 0x02, 0x16, 0x4c, 0x87, 0x19, 0x06, 0x2f, 0x3f, 0x31, 0x1a, 0x42, 0xc0, 0xd3, 0xed, 0xcf, 0xd5, 0x43, 0x82, 0xcc, 0xc8, 0xbf, 0x4f, 0x45, 0x0f, 0x09, 0x3e, 0xc3, 0x80, 0x39, 0xd2, 0x08, 0xe5, 0x02, 0x63, 0xfe, 0x35, 0xe8, 0x27, 0x9a, 0xb7, 0x54, 0xb2, 0x9d, 0x06, 0x24, 0x2a, 0x22, 0x90, 0xbf, 0x99, 0x48, 0x7a, 0x1a, 0x9e, 0xc7, 0xb5, 0x1f, 0xea, 0x63, 0x2c, 0x45, 0xff, 0x41, 0x59, 0xa7, 0x5e, 0xd1, 0xb4, 0x54, 0x93, 0x30, 0x9f, 0xa7, 0x7e, 0xbb, 0x1f, 0x90, 0x27, 0x32, 0xc1, 0x9d, 0x55, 0x45, 0xf7, 0x25, 0x40, 0x4d, 0xbc, 0xdf, 0x23, 0xd4, 0xcf, 0x5d, 0x2f, 0x03, 0xdf, 0xda }; +constexpr AccessUnit ATRACX_LARGE_AU_EXPECTED_AU = { 0x15f90, 0x159b2, false, 0, {}, { 0x11, 0x92, 0xc6, 0xab, 0x16, 0x53, 0x3d, 0x96, 0x54, 0xa0, 0x81, 0xef, 0x3b, 0x10, 0x94, 0x47, 0x4a, 0x4d, 0x0a, 0x36 } }; + +const std::vector AC3_LARGE_AU_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x77, 0xe2, 0x05, 0xa5, 0xda, 0x2b, 0xef, 0x62, 0xd4, 0xa5, 0xaf, 0xd9, 0x1b, 0xd0, 0x7c, 0xec, 0x75, 0x5e, 0x09, 0x04, 0x80, 0xb4, 0x45, 0x05, 0x0e, 0xb6, 0xa6, 0x5d, 0x0d, 0x01, 0x68, 0x40, 0xce, 0x9f, 0x93, 0xf9, 0x3e, 0xda, 0x08, 0x41, 0x76, 0xeb, 0x39, 0xfa, 0xbb, 0xe6, 0xd6, 0x8e, 0x40, 0x05, 0x75, 0xbc, 0x84, 0x47, 0x2b, 0x79, 0x20, 0x5e, 0x94, 0x66, 0x36, 0x39, 0x4c, 0xd4, 0xa9, 0xa9, 0xf7, 0xf7, 0x9e, 0x0e, 0x80, 0xe8, 0x5a, 0x63, 0xb4, 0x53, 0x04, 0x59, 0xe5, 0xf6, 0x31, 0xc9, 0x6d, 0xe3, 0x29, 0x13, 0x81, 0xd5, 0x17, 0x70, 0xf3, 0x9c, 0x2c, 0x7a, 0xec, 0xcf, 0x9b, 0xb2, 0xe9, 0x62, 0x25, 0xf3, 0x1f, 0x4b, 0x08, 0xcc, 0x67, 0xaf, 0x7c, 0x21, 0x7a, 0xac, 0xda, 0xf6, 0x06, 0x6d, 0x3f, 0x48, 0xac, 0x8e, 0xc6, 0x0a, 0x49, 0xe3, 0xf6, 0x60, 0x07, 0x62, 0x7b, 0xa6, 0x86, 0x85, 0xf2, 0xac, 0xa2, 0x11, 0x7b, 0xda, 0x9d, 0xa3, 0xad, 0x10, 0xe4, 0xe1, 0x7d, 0x13, 0x8a, 0xe6, 0xcb, 0xe0, 0xf7, 0xec, 0xc0, 0x51, 0x1e, 0xf5, 0x51, 0x4f, 0x08, 0xbc, 0x30, 0xcf, 0xaa, 0x9c, 0x6b, 0xf7, 0xc4, 0x85, 0xe6, 0x72, 0x06, 0xbf, 0x1f, 0x0a, 0x59, 0xf2, 0x27, 0x8e, 0xb2, 0xde, 0x77, 0x1c, 0x4a, 0x6d, 0x3a, 0x10, 0x31, 0x70, 0x19, 0x30, 0x4e, 0x73, 0xbb, 0xf6, 0xf2, 0xf4, 0x7f, 0x5f, 0xb5, 0x08, 0x34, 0x72, 0x40, 0x66, 0x9a, 0xa0, 0xe7, 0xb2, 0x60, 0x12, 0x0b, 0x0a, 0x01, 0x6b, 0x18, 0x0b, 0x63, 0xc9, 0xf2, 0x9c, 0x63, 0xde, 0xf7, 0x41, 0xca, 0xfb, 0x4f, 0x62, 0x69, 0x31, 0x44, 0xd4, 0x78, 0xf6, 0xce, 0xc9, 0xc5, 0x3d, 0x89, 0x7b, 0xee, 0xf9, 0x7b, 0xab, 0x0a, 0x1b, 0xb3, 0x32, 0xc8, 0xad, 0x1a, 0xdc, 0x4c, 0xf4, 0x6d, 0x5d, 0xc0, 0x7a, 0x9b, 0x17, 0x2f, 0xf4, 0x49, 0x15, 0xe3, 0x17, 0x1f, 0x4e, 0x40, 0x2e, 0x51, 0x41, 0xa8, 0x71, 0x0d, 0x9c, 0x7b, 0x4f, 0xad, 0x57, 0xc3, 0xb3, 0xa6, 0x38, 0x50, 0xa0, 0x43, 0x68, 0xa2, 0x38, 0xc1, 0xf3, 0x42, 0xfc, 0x3f, 0xa3, 0x29, 0x06, 0x2b, 0x8a, 0xff, 0x58, 0xa4, 0x61, 0xef, 0xc0, 0xbf, 0x34, 0x99, 0x6b, 0x86, 0xaf, 0x92, 0x4b, 0x00, 0xcd, 0xfe, 0xe8, 0xae, 0x26, 0xfc, 0x9f, 0xb4, 0x55, 0x52, 0x84, 0x5c, 0x06, 0x99, 0xf8, 0x08, 0x98, 0x09, 0x4d, 0x67, 0x7e, 0xc9, 0xa2, 0x98, 0xe0, 0x55, 0xc1, 0x30, 0x87, 0x85, 0x4d, 0xdf, 0xaf, 0x57, 0x09, 0x37, 0x43, 0x3b, 0x49, 0xee, 0x13, 0x69, 0xc8, 0x53, 0xa7, 0x9c, 0xb2, 0x87, 0x87, 0xd5, 0xcf, 0x4b, 0xa8, 0xb0, 0x4a, 0x2e, 0xd2, 0x47, 0x31, 0xad, 0x77, 0x29, 0x43, 0xf7, 0x69, 0x90, 0x8f, 0x84, 0xd5, 0x8a, 0xea, 0xb2, 0x1f, 0x27, 0x7a, 0x2a, 0xf7, 0x6b, 0x90, 0x0f, 0xed, 0x72, 0x97, 0xdf, 0xf2, 0x03, 0xb3, 0xd3, 0xee, 0x99, 0x6e, 0xce, 0x6c, 0x3f, 0x9d, 0xc5, 0x0a, 0x57, 0xcd, 0xbd, 0x5f, 0x25, 0x7b, 0x64, 0x27, 0xb3, 0xef, 0x34, 0xaa, 0x06, 0x1c, 0x6c, 0x75, 0xd2, 0x9d, 0xc8, 0xd5, 0x96, 0x0c, 0x31, 0x9f, 0xdd, 0x75, 0x99, 0x0e, 0x77, 0x10, 0xe5, 0x77, 0xda, 0xc8, 0xde, 0xc5, 0xf5, 0x3b, 0xae, 0x2f, 0xe2, 0x08, 0xc1, 0x43, 0x02, 0xf0, 0x13, 0x15, 0xfc, 0x86, 0xbf, 0xaa, 0x31, 0xac, 0x6b, 0xdd, 0xac, 0x60, 0x4e, 0xe2, 0x69, 0x26, 0xd9, 0x8b, 0xdb, 0x69, 0x41, 0xc8, 0x04, 0x76, 0x29, 0xa1, 0xb3, 0xba, 0xee, 0xf8, 0xa5, 0x1e, 0x02, 0x86, 0x2c, 0x62, 0x0a, 0x00, 0x7d, 0xba, 0x57, 0x39, 0x85, 0xd3, 0x03, 0x7e, 0x42, 0x0c, 0x06, 0x74, 0x15, 0x5b, 0xba, 0x82, 0x96, 0x61, 0x6a, 0x3d, 0x93, 0x8a, 0xae, 0x0c, 0xf1, 0x45, 0x61, 0x22, 0x58, 0xcc, 0xd7, 0x64, 0xb4, 0x23, 0x0a, 0xa7, 0xf1, 0xd8, 0x82, 0xce, 0xb8, 0x3b, 0xf1, 0xc8, 0x9d, 0xb4, 0xb2, 0x2b, 0x9a, 0xc4, 0xf3, 0x9d, 0x6c, 0xe9, 0x5a, 0xca, 0x37, 0x8f, 0x03, 0xb2, 0xad, 0x52, 0x67, 0xd0, 0x53, 0x72, 0xd2, 0xeb, 0xc6, 0x93, 0x41, 0x31, 0x83, 0x60, 0x18, 0x48, 0xb7, 0xe8, 0xe4, 0x93, 0xbc, 0x0b, 0xab, 0xd6, 0x78, 0x8a, 0x7d, 0x24, 0x83, 0x49, 0xf4, 0x80, 0x77, 0xfa, 0xb1, 0xa1, 0xa6, 0x7a, 0x7f, 0xd7, 0xba, 0x9d, 0x62, 0x46, 0x30, 0xc4, 0x38, 0xf5, 0x97, 0x11, 0x00, 0xdc, 0xfe, 0x93, 0xe9, 0xcd, 0xbf, 0xd0, 0x55, 0xea, 0xce, 0xb0, 0x70, 0x76, 0x2d, 0x69, 0x2b, 0xdc, 0xe9, 0x6a, 0xfe, 0xda, 0x36, 0x3b, 0xca, 0x3d, 0x0a, 0x7d, 0x76, 0x7e, 0x0d, 0xad, 0x93, 0xe8, 0xa6, 0xfc, 0x17, 0x31, 0xb0, 0x0b, 0x77, 0xc7, 0x9f, 0x75, 0x41, 0xe3, 0xe3, 0xea, 0x48, 0xc6, 0xe9, 0x62, 0x69, 0xbd, 0x87, 0x86, 0x5f, 0xe4, 0x13, 0xbf, 0xb9, 0x63, 0xc8, 0xb4, 0x24, 0x49, 0x17, 0x5c, 0xe5, 0x5f, 0xc1, 0x4d, 0x77, 0x78, 0x26, 0xbd, 0x66, 0x21, 0xcf, 0xd0, 0x3b, 0x7e, 0x01, 0x06, 0x49, 0x9a, 0x99, 0x46, 0x89, 0x18, 0xa3, 0x3e, 0x1b, 0xae, 0x6a, 0x33, 0xdd, 0xca, 0x36, 0x33, 0x34, 0xa8, 0xa8, 0xd3, 0x18, 0x6c, 0x6b, 0x7e, 0x1e, 0x0f, 0xca, 0x12, 0x35, 0x78, 0xc2, 0x30, 0x84, 0xcf, 0xc4, 0xe5, 0x81, 0x65, 0xc7, 0xf3, 0xb5, 0xed, 0x89, 0xaa, 0x65, 0xcf, 0x9d, 0x71, 0xd8, 0x06, 0x5a, 0xac, 0xe5, 0x66, 0xf2, 0x18, 0xc4, 0x28, 0x54, 0xdc, 0x45, 0x1b, 0x16, 0x7b, 0xd9, 0x56, 0x70, 0x9f, 0x66, 0x92, 0x65, 0x50, 0x95, 0x27, 0x75, 0x2c, 0x85, 0x67, 0x8d, 0x6e, 0x6e, 0x12, 0x09, 0xa5, 0x34, 0x62, 0x55, 0x50, 0xc8, 0xb0, 0xe6, 0xd8, 0x63, 0x81, 0x63, 0xc7, 0x44, 0xf1, 0x35, 0x5e, 0xc6, 0x41, 0x24, 0x6c, 0x00, 0xb3, 0xba, 0x5f, 0x7b, 0x4a, 0x01, 0x49, 0x6a, 0x64, 0x45, 0x7b, 0xf1, 0xce, 0x3b, 0xe3, 0x59, 0xeb, 0x83, 0xd1, 0xda, 0xa7, 0x5e, 0x60, 0x8f, 0x16, 0x32, 0x06, 0xa9, 0xd8, 0x88, 0x2d, 0x5a, 0xf9, 0x21, 0x51, 0xb2, 0x1b, 0x62, 0x2a, 0x6a, 0xce, 0xd3, 0x02, 0x06, 0xc1, 0xf5, 0x2f, 0xe7, 0xb4, 0x6e, 0x22, 0x2b, 0x52, 0x80, 0x94, 0x7c, 0x86, 0xc6, 0x0f, 0xfd, 0xc1, 0xd0, 0x7e, 0xb1, 0x8d, 0xd1, 0x5f, 0xa6, 0xd9, 0x1b, 0x42, 0x62, 0x88, 0x11, 0x68, 0x21, 0x9e, 0x66, 0x6e, 0xca, 0xf6, 0xa0, 0x9a, 0x05, 0xef, 0x4c, 0xa4, 0xf4, 0x59, 0x7a, 0x33, 0x25, 0x22, 0xdc, 0xf8, 0x32, 0x34, 0xbb, 0x2f, 0x0a, 0xf0, 0xb7, 0x2f, 0x47, 0x1a, 0x08, 0x62, 0xcb, 0x46, 0xaf, 0x67, 0xf0, 0x9d, 0x02, 0xb9, 0x24, 0x26, 0x2a, 0x84, 0x61, 0x62, 0x3e, 0x44, 0x36, 0xd0, 0x41, 0x37, 0xf6, 0x48, 0xd7, 0x6d, 0x02, 0x4e, 0xef, 0xe1, 0x57, 0xc3, 0x0a, 0xc4, 0xe3, 0x9f, 0x63, 0x49, 0xed, 0x3a, 0xda, 0x68, 0xd5, 0x51, 0x96, 0x58, 0x05, 0xa6, 0xdc, 0xf9, 0xcb, 0xeb, 0x7d, 0x1a, 0xfe, 0x99, 0x0f, 0xca, 0x35, 0x65, 0xdc, 0x82, 0xae, 0x3b, 0x05, 0x02, 0x0e, 0xad, 0xf7, 0x55, 0xfb, 0xc6, 0x1d, 0xc1, 0x00, 0x50, 0x29, 0x1b, 0x10, 0xa2, 0x4d, 0x7f, 0x58, 0xea, 0x85, 0x56, 0x13, 0x74, 0x76, 0x4e, 0xd4, 0x30, 0xc4, 0xec, 0x81, 0xc2, 0x2d, 0x8d, 0x54, 0x61, 0x50, 0xb2, 0x98, 0xab, 0x89, 0x16, 0x7d, 0xaf, 0x78, 0xa7, 0x99, 0xdd, 0x91, 0xa8, 0x20, 0xc0, 0x0f, 0xf9, 0x76, 0xde, 0xaa, 0x16, 0x2d, 0xcb, 0xc3, 0x12, 0xeb, 0x6a, 0x9c, 0x57, 0xfa, 0x76, 0xb0, 0x30, 0x3a, 0x0e, 0x82, 0xc1, 0x6f, 0x9a, 0x43, 0xfa, 0x8d, 0xf6, 0xf9, 0x97, 0x1a, 0xf9, 0x70, 0x44, 0xa5, 0x04, 0x13, 0xc5, 0x5e, 0x89, 0x68, 0xf5, 0x3c, 0x53, 0xa7, 0xd2, 0x2d, 0x15, 0x77, 0x7a, 0xfc, 0x3e, 0x94, 0xe4, 0xa7, 0x66, 0xbe, 0xe6, 0xcd, 0x72, 0x34, 0xa8, 0x92, 0x64, 0x18, 0x81, 0x16, 0x8a, 0x7c, 0x12, 0xdd, 0x9a, 0xc0, 0x3e, 0x2f, 0x0b, 0xb7, 0x55, 0xfe, 0xc3, 0x2a, 0x04, 0x60, 0x0d, 0x98, 0x4c, 0x63, 0xfb, 0x13, 0x2b, 0xb9, 0x07, 0xc3, 0xb0, 0x69, 0xec, 0x56, 0x55, 0xe4, 0x50, 0xc5, 0x60, 0xb7, 0xd6, 0x2d, 0xf2, 0xff, 0xbb, 0x26, 0xeb, 0xb3, 0xc5, 0x41, 0xdd, 0x11, 0x20, 0x97, 0xb3, 0x4a, 0xa8, 0x14, 0xb0, 0xa0, 0xd4, 0x35, 0xaa, 0x5a, 0xa7, 0x68, 0xb5, 0xcc, 0x8d, 0xab, 0xa2, 0x68, 0x38, 0x9a, 0x9e, 0xbb, 0xe6, 0x3d, 0x2f, 0xc0, 0xd3, 0xe9, 0x9c, 0x74, 0x51, 0xe8, 0xce, 0x5d, 0x0d, 0xcf, 0xea, 0x44, 0x01, 0x32, 0x39, 0x8e, 0x00, 0x0e, 0xa4, 0x0e, 0x28, 0x04, 0xf1, 0x1b, 0xa2, 0xd1, 0x34, 0xa6, 0x52, 0xc5, 0x2c, 0xd4, 0xf2, 0xd5, 0x53, 0x99, 0x67, 0x07, 0x92, 0x6e, 0x6c, 0x66, 0xfe, 0x29, 0xbd, 0xdf, 0x63, 0x72, 0x82, 0xde, 0xb1, 0x2a, 0xb7, 0x06, 0xc1, 0xa6, 0x71, 0x61, 0x61, 0x79, 0xc0, 0x0c, 0x1f, 0x56, 0x5e, 0x97, 0xe6, 0xb8, 0xfb, 0xe6, 0x00, 0xcd, 0x1a, 0xb5, 0x18, 0xf9, 0xad, 0x35, 0x6b, 0x9d, 0xf8, 0x9a, 0xfc, 0x87, 0x7a, 0xb6, 0x3b, 0x10, 0xa0, 0xb7, 0x67, 0xc5, 0xa0, 0xdb, 0xd8, 0x59, 0xde, 0xb2, 0x18, 0x81, 0xce, 0x84, 0xbc, 0xf0, 0xfe, 0xa2, 0x53, 0x96, 0x15, 0x17, 0x0f, 0x94, 0x4c, 0xac, 0x3b, 0x4c, 0x11, 0x1f, 0xd7, 0x07, 0x79, 0x12, 0xe0, 0x1a, 0xbc, 0x16, 0x16, 0x30, 0x54, 0x71, 0x05, 0x0f, 0x66, 0x7f, 0xb9, 0x2e, 0xd7, 0x38, 0xbe, 0xaf, 0x78, 0x7d, 0x91, 0x6a, 0xee, 0x33, 0xa0, 0xea, 0xe0, 0xed, 0xbf, 0x0f, 0x59, 0x2d, 0xe3, 0x07, 0xd4, 0xa0, 0xe3, 0xb8, 0xdf, 0x83, 0x1a, 0xd8, 0x22, 0x9b, 0x42, 0x5a, 0x08, 0x2e, 0x7a, 0x4b, 0x17, 0xf4, 0x92, 0xb9, 0xb2, 0x99, 0x5e, 0x28, 0x0f, 0x4e, 0x51, 0x4f, 0x97, 0x7a, 0x1a, 0xa0, 0x7b, 0x5a, 0x0d, 0x4e, 0xab, 0x46, 0xed, 0x93, 0xa0, 0xc2, 0x04, 0x9b, 0x6f, 0xde, 0xc9, 0xf5, 0xf4, 0xb3, 0x7c, 0x37, 0xef, 0x4a, 0x93, 0xf7, 0x9b, 0x1f, 0x7a, 0x23, 0xe8, 0x08, 0x87, 0x19, 0x2f, 0xe2, 0xb8, 0xe6, 0x34, 0xac, 0x96, 0x66, 0x61, 0xf0, 0xdc, 0x16, 0x15, 0x79, 0x43, 0x3c, 0xc0, 0x43, 0x6c, 0x11, 0x51, 0x91, 0x8a, 0xd8, 0x48, 0x8d, 0xae, 0x1c, 0x69, 0xec, 0x76, 0x60, 0xdd, 0xb8, 0xc6, 0x1c, 0x40, 0x2b, 0xf0, 0x29, 0x11, 0x27, 0xc1, 0x36, 0x2c, 0x49, 0x74, 0xbd, 0xc1, 0xfe, 0x0d, 0x12, 0x3f, 0x1f, 0xd4, 0x27, 0x30, 0xc3, 0x4e, 0xfd, 0x87, 0x72, 0x25, 0xd7, 0x45, 0x12, 0xce, 0x9a, 0x93, 0x50, 0x24, 0xd2, 0x70, 0xf9, 0x46, 0x1b, 0x53, 0x59, 0x6b, 0x9e, 0x65, 0x39, 0x60, 0x01, 0xe1, 0xbd, 0x0c, 0x61, 0xfb, 0x3c, 0x0a, 0x7f, 0x3c, 0xd1, 0x5f, 0x4b, 0x82, 0xfb, 0x55, 0xde, 0x86, 0x66, 0xd1, 0xb6, 0x7c, 0x6e, 0xd9, 0xe1, 0x9f, 0x41, 0x45, 0x56, 0x2f, 0x91, 0xaf, 0xc5, 0x65, 0x45, 0xd8, 0x34, 0x31, 0x7d, 0xbe, 0xb1, 0xf5, 0xe6, 0x13, 0x8e, 0x72, 0x2e, 0x3b, 0x76, 0xbc, 0x3f, 0x34, 0x82, 0x25, 0x35, 0x6f, 0xaf, 0xe2, 0x90, 0xef, 0x08, 0xc6, 0xb4, 0xdc, 0xa5, 0x11, 0xea, 0xbb, 0x23, 0x8b, 0x71, 0x4e, 0xba, 0x6e, 0x6f, 0xb2, 0x70, 0xca, 0xf0, 0xe1, 0xfa, 0xc2, 0x93, 0x37, 0xc9, 0x24, 0xa3, 0xb9, 0x38, 0x7e, 0x38, 0xc6, 0xde, 0x2c, 0x73, 0x70, 0x1d, 0x21, 0x4b, 0xae, 0x3e, 0x46, 0x2c, 0x6f, 0x9f, 0x10, 0xd0, 0xb0, 0xee, 0x1b, 0x14, 0x19, 0x55, 0x59, 0x72, 0x7c, 0xcb, 0x53, 0x61, 0xdc, 0x52, 0x1d, 0xea, 0x47, 0x7b, 0xed, 0xe6, 0x06, 0x6e, 0xbf, 0xe8, 0x56, 0xa8, 0xa7, 0xd7, 0x5d, 0xb8, 0xd0, 0x8c, 0x9f, 0xcf, 0xc3, 0x33, 0xae, 0x5a, 0x73, 0x02, 0xc2, 0x76, 0x7f, 0x65, 0x82, 0x23, 0x58, 0xed, 0x6c, 0x5e, 0xde, 0x98, 0x6c, 0x3e, 0x36, 0xec, 0x8a, 0xc1, 0x0b, 0xa4, 0x28, 0x20, 0x2c, 0xf8, 0xcd, 0xe7, 0x49, 0x50, 0xec, 0x6f, 0xe0, 0xcc, 0x76, 0x03, 0x10, 0x6b, 0xdf, 0xf8, 0x94, 0x2d, 0x71, 0x2b, 0x11, 0xd5, 0x64, 0x3f, 0xeb, 0x62, 0x50, 0x40, 0xf8, 0x94, 0x9f, 0xa4, 0x10, 0x61, 0x6c, 0x18, 0x50, 0xa1, 0x0c, 0xaf, 0x13, 0x5b, 0x96, 0x2b, 0x17, 0x05, 0x19, 0xd3, 0xba, 0x83, 0xba, 0xc2, 0xed, 0xb7, 0x58, 0xe5, 0x57, 0xd5, 0x26, 0xf4, 0xaf, 0xac, 0x0b, 0x06, 0x28, 0x79, 0xee, 0xbf, 0x76, 0xf8, 0xee, 0x93, 0xcd, 0x0a, 0x88, 0x00, 0xc2, 0xfe, 0x5a, 0x44, 0xce, 0x0b, 0xc7, 0xb5, 0x5d, 0xb8, 0x8c, 0x5c, 0x91, 0xbf, 0xd6, 0x4d, 0x16, 0xfd, 0x76, 0x5f, 0xd6, 0x2c, 0x71, 0xf7, 0x45, 0xe7, 0xf0, 0x61, 0x24, 0x55, 0x86, 0xb1, 0x88, 0x0b, 0x77, 0xc7, 0xe0, 0x54, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x8f, 0x7b, 0xf7, 0x90, 0x80, 0xe5, 0x04, 0x40, 0xe5, 0x72, 0xe8, 0x0f, 0x33, 0x29, 0xa8, 0x11, 0x1b, 0xf6, 0xd9, 0x92, 0x2a, 0xf4, 0x5f, 0xe7, 0x96, 0x33, 0xc4, 0xd5, 0x56, 0x52, 0x07, 0x63, 0xc8, 0xa5, 0xc7, 0x4b, 0x1a, 0xf6, 0x7a, 0x9f, 0x9e, 0x15, 0x3f, 0x8d, 0x9c, 0xd2, 0xb9, 0x72, 0x78, 0x4b, 0xbe, 0x51, 0xbd, 0xca, 0xa2, 0xc7, 0xb0, 0x15, 0x55, 0x23, 0x79, 0x2b, 0x7e, 0x48, 0x2a, 0x9d, 0x5a, 0xb9, 0xc6, 0x9c, 0xbd, 0x91, 0x2f, 0x94, 0x4b, 0xff, 0x05, 0xd9, 0xb8, 0x6c, 0xb7, 0xd5, 0x13, 0x16, 0xd7, 0x7e, 0xd3, 0x73, 0xd8, 0x18, 0x4b, 0x8c, 0x1b, 0x89, 0xdd, 0xdb, 0x43, 0x39, 0x73, 0x7b, 0x9f, 0x65, 0x20, 0x03, 0x74, 0xc8, 0xf4, 0x31, 0x23, 0xe8, 0xae, 0xcd, 0x7b, 0x08, 0x9d, 0x38, 0xa0, 0x48, 0x8b, 0xa2, 0xe5, 0xe6, 0xa6, 0x72, 0x1f, 0x2a, 0x71, 0x84, 0x15, 0x4d, 0xde, 0x68, 0x3e, 0xb3, 0x74, 0x0d, 0xe1, 0x5a, 0xa5, 0xc0, 0x5b, 0x95, 0x57, 0x39, 0x24, 0xda, 0xde, 0x36, 0x36, 0x59, 0x85, 0x5a, 0x8b, 0x5d, 0x6f, 0x7a, 0xfe, 0x20, 0x12, 0x9e, 0x01, 0x6e, 0xe1, 0x2d, 0x3a, 0xb5, 0xa9, 0x53, 0xcc, 0x20, 0x55, 0xcc, 0x9f, 0xe7, 0xda, 0xb0, 0x91, 0x71, 0xb3, 0xa1, 0x71, 0x49, 0xab, 0xa4, 0x41, 0xeb, 0x44, 0xb0, 0x14, 0x70, 0x69, 0xd9, 0x61, 0x3d, 0x08, 0x39, 0xee, 0x44, 0xb0, 0x14, 0x0b, 0xdb, 0xb5, 0xa6, 0x75, 0x6d, 0x11, 0x06, 0x61, 0x5f, 0x99, 0xdf, 0xd8, 0x20, 0x17, 0x96, 0x07, 0xee, 0xa8, 0x11, 0xc3, 0xbc, 0x5b, 0x1d, 0x23, 0x76, 0x87, 0x50, 0x99, 0xc8, 0x59, 0x31, 0xa1, 0x7d, 0x89, 0x46, 0x65, 0x74, 0x9c, 0x07, 0xc6, 0x9d, 0x9a, 0x53, 0xd3, 0x81, 0x68, 0xf0, 0xf5, 0x9b, 0xb9, 0x10, 0x1d, 0x5d, 0x5b, 0x34, 0x2a, 0xc6, 0x29, 0xbb, 0xd2, 0x00, 0xef, 0xee, 0x86, 0xfc, 0xcd, 0x6b, 0x5d, 0xf8, 0xa0, 0x46, 0x8a, 0xd3, 0x6d, 0x8f, 0xc1, 0x69, 0x29, 0xad, 0xa2, 0xcc, 0x33, 0x10, 0x53, 0x1f, 0xbd, 0xc6, 0x08, 0x0e, 0xd7, 0x3f, 0xcc, 0xd0, 0xaf, 0xae, 0x54, 0x16, 0x40, 0x0e, 0x64, 0xc4, 0x91, 0x50, 0xd5, 0xc3, 0x2d, 0xe9, 0xe7, 0x2e, 0x9b, 0x97, 0xd7, 0xd5, 0x63, 0xdd, 0xb5, 0x29, 0x46, 0x2c, 0xec, 0xc5, 0x02, 0x1b, 0x3c, 0xb4, 0xe4, 0xb4, 0xae, 0xa7, 0x25, 0x84, 0x0b, 0x77, 0xd3, 0xd3, 0x02, 0xdd, 0x1f, 0xab, 0x25, 0x18, 0x63, 0xe3, 0xa5, 0xb6, 0x67, 0x97, 0x42, 0xae, 0x8d, 0x77, 0x0f, 0xaa, 0x8b, 0xe2, 0x67, 0x49, 0xa2, 0x87, 0xd8, 0x66, 0x5d, 0xc5, 0xb3, 0x12, 0x52, 0x88, 0xb3, 0x34, 0x8c, 0x03, 0xda, 0x92, 0x1e, 0xbc, 0x73, 0xba, 0x4f, 0x54, 0xb4, 0x70, 0x17, 0x0e, 0x2b, 0xa9, 0x3b, 0x61, 0x32, 0x2b, 0x22, 0xb2, 0xb8, 0x61, 0xfc, 0xbb, 0xe6, 0x84, 0xa3, 0x1c, 0x65, 0xe8, 0xe4, 0x08, 0x55, 0xf0, 0x7a, 0xfc, 0x66, 0x2b, 0x7f, 0xaf, 0xe2, 0x74, 0xdd, 0x06, 0xb4, 0xa2, 0x2f, 0x44, 0xb3, 0x40, 0x84, 0xba, 0x95, 0xf5, 0x6d, 0xf9, 0x4b, 0xa2, 0x33, 0x92, 0xfe, 0x89, 0x2d, 0xd6, 0x32, 0xd4, 0xe0, 0x2b, 0x10, 0x9f, 0x65, 0xe9, 0x18, 0x76, 0x23, 0xa6, 0x78, 0x4d, 0xdf, 0x7f, 0xce, 0x87, 0xf7, 0x5a, 0xfe, 0x8f, 0x6b, 0xd7, 0x68, 0xcd, 0x00, 0x75, 0xaf, 0x14, 0xdd, 0xea, 0x85, 0x39, 0x83, 0x73, 0x2b, 0xe5, 0x64, 0x78, 0x03, 0x51, 0x4b, 0x06, 0x67, 0x32, 0x73, 0x58, 0x0c, 0x86, 0xcc, 0x09, 0xfb, 0x41, 0x7f, 0x2d, 0x67, 0xe9, 0xcf, 0x0e, 0x5d, 0x73, 0x0b, 0x76, 0x12, 0xb1, 0x66, 0x5f, 0xd0, 0x46, 0x57, 0x23, 0xad, 0xb9, 0xf8, 0x8e, 0x99, 0xc4, 0xd2, 0xb6, 0x88, 0x40, 0x7a, 0x48, 0xe1, 0x38, 0x08, 0xc3, 0x90, 0xec, 0x32, 0xd5, 0x68, 0x38, 0xfb, 0x58, 0xc4, 0x9e, 0xb2, 0x0d, 0x9e, 0xb2, 0x22, 0x22, 0xb4, 0x62, 0x49, 0x56, 0x26, 0x5f, 0xbc, 0x1f, 0xb9, 0x1b, 0x02, 0x6e, 0x0b, 0x22, 0xcc, 0x31, 0x7e, 0x64, 0x82, 0xc5, 0x75, 0x78, 0xf5, 0xa7, 0x56, 0x2e, 0x19, 0xf8, 0x73, 0x66, 0xea, 0x16, 0x52, 0x0c, 0xc4, 0xcb, 0x55, 0xf0, 0x34, 0x2d, 0x42, 0x2f, 0x0c, 0x50, 0x41, 0xc5, 0x68, 0x09, 0xd1, 0x14, 0xab, 0x6b, 0x46, 0x45, 0x35, 0x92, 0x93, 0xd0, 0x49, 0x72, 0xdb, 0x99, 0x10, 0xcd, 0x31, 0x5b, 0x97, 0x6c, 0xb7, 0x1a, 0xea, 0x04, 0x77, 0xd5, 0xb5, 0x05, 0x52, 0xe6, 0x43, 0xc9, 0x07, 0x67, 0xf7, 0xb4, 0xf1, 0x05, 0x2b, 0xb2, 0x88, 0x9a, 0x20, 0xe6, 0x4a, 0x8b, 0x8f, 0xb9, 0x32, 0x0a, 0x85, 0x2e, 0x1c, 0xcb, 0x5d, 0xdc, 0x56, 0x7f, 0x2f, 0x7a, 0x59, 0x4b, 0xf2, 0x6b, 0xb7, 0x37, 0xf8, 0x46, 0xf2, 0x85, 0xfb, 0x19, 0x37, 0x69, 0x46, 0x48, 0xbb, 0xe6, 0x3d, 0x70, 0xba, 0xbf, 0x18, 0xf7, 0xbf, 0x9d, 0xc4, 0xf6, 0xb8, 0xda, 0xdb, 0xc5, 0x14, 0x35, 0xe4, 0x2c, 0x35, 0x33, 0x13, 0x6d, 0xe6, 0xb2, 0x04, 0x9b, 0xed, 0x4c, 0x9c, 0xd3, 0xf6, 0x6d, 0x34, 0xca, 0xb7, 0xbd, 0xf0, 0x4e, 0x98, 0xbe, 0xbd, 0x70, 0xb8, 0x82, 0xef, 0xd9, 0x2c, 0x9e, 0x75, 0x14, 0x2d, 0xd3, 0x74, 0x7f, 0xf2, 0xb0, 0x6b, 0x7f, 0xb6, 0x38, 0x02, 0x1f, 0x56, 0xe0, 0xf1, 0x91, 0x55, 0x53, 0xf6, 0xad, 0x26, 0xde, 0xcc, 0x64, 0x86, 0xc7, 0xb2, 0x11, 0x74, 0x0a, 0x09, 0x91, 0x53, 0x98, 0x94, 0x44, 0xd0, 0xe5, 0x9a, 0x5f, 0x68, 0x3e, 0x21, 0x14, 0xc6, 0x3f, 0x17, 0x9a, 0x3e, 0x93, 0x6b, 0x8a, 0xbd, 0x34, 0xfe, 0x4e, 0xa8, 0x72, 0xca, 0x20, 0x01, 0x09, 0x52, 0x51, 0x67, 0x36, 0x29, 0xd3, 0x07, 0x5e, 0xbf, 0xed, 0xce, 0x56, 0xfa, 0x98, 0x5b, 0x1c, 0x68, 0xad, 0x92, 0x42, 0xb8, 0x19, 0xfe, 0x3a, 0xbf, 0x4e, 0x1f, 0x06, 0xea, 0xd2, 0x78, 0x02, 0x91, 0x21, 0xfd, 0x88, 0x83, 0x49, 0xc2, 0x31, 0x4e, 0x40, 0x6e, 0x8e, 0x41, 0xbc, 0xd0, 0x74, 0xc4, 0x07, 0xa1, 0x39, 0x8b, 0x95, 0x4c, 0x33, 0x3d, 0x4a, 0x1a, 0xdd, 0x26, 0x1b, 0xcc, 0x03, 0xf5, 0xe6, 0xc3, 0xae, 0xb4, 0x90, 0x33, 0x75, 0x8b, 0xbd, 0xfc, 0xa6, 0x22, 0xea, 0x6b, 0x5d, 0x39, 0x15, 0x1f, 0x15, 0x60, 0xea, 0x56, 0x00, 0xcd, 0x65, 0x28, 0x40, 0x59, 0x87, 0xf6, 0x40, 0x08, 0x61, 0x65, 0x8e, 0xc8, 0xde, 0x2a, 0x5e, 0x4a, 0x2b, 0xfb, 0x1c, 0x61, 0xea, 0xd0, 0xe8, 0x2f, 0xd4, 0xcd, 0x5c, 0xb4, 0xfa, 0x93, 0x87, 0xfd, 0x87, 0x56, 0x83, 0x87, 0x25, 0x25, 0xfb, 0x80, 0x87, 0x59, 0xca, 0x85, 0x55, 0x86, 0x8e, 0xd6, 0x63, 0xfd, 0x0f, 0x15, 0xb5, 0x7c, 0xf7, 0x87, 0xb9, 0x09, 0xa7, 0xbf, 0x9b, 0x36, 0x2b, 0x8c, 0x5b, 0xd1, 0xa6, 0x8a, 0xc2, 0x27, 0x6a, 0xbc, 0xb1, 0xea, 0xa2, 0x04, 0xe0, 0x6e, 0x34, 0xbf, 0x4b, 0x1d, 0x2a, 0xf2, 0xe2, 0x22, 0xfc, 0xb0, 0x58, 0x33, 0x35, 0x01, 0x51, 0x27, 0x03, 0x7f, 0x6a, 0x24, 0xb8, 0xe9, 0x8b, 0x42, 0xbd, 0x7e, 0x60, 0x39, 0xaf, 0x43, 0xfd, 0xfb, 0xb3, 0x4a, 0xc9, 0xb8, 0x21, 0xbd, 0x06, 0xad, 0x39, 0x67, 0x55, 0xc3, 0x8c, 0x01, 0x23, 0x70, 0x48, 0x7c, 0x5b, 0x5b, 0x94, 0xd3, 0x37, 0xd2, 0x27, 0xac, 0x11, 0x78, 0xd9, 0x8e, 0xbb, 0x52, 0xdf, 0x75, 0x96, 0x4d, 0x2f, 0xe0, 0x7a, 0x52, 0x02, 0xb5, 0xed, 0xc5, 0xd1, 0xe4, 0x41, 0xa5, 0x1f, 0xba, 0x3e, 0xf3, 0x1b, 0x7c, 0x61, 0x08, 0xce, 0x2e, 0xba, 0xf3, 0xa4, 0x5b, 0x46, 0x9a, 0x59, 0x64, 0xe2, 0xd6, 0x0c, 0x3e, 0x8d, 0x2d, 0xd4, 0x92, 0x15, 0xec, 0xf8, 0xfb, 0xeb, 0x35, 0x05, 0x13, 0x7c, 0x70, 0x37, 0x88, 0x3e, 0x59, 0xe4, 0x51, 0x53, 0x31, 0xc8, 0x09, 0x51, 0xf7, 0x30, 0xbe, 0xad, 0x8d, 0xe2, 0x63, 0xc8, 0x48, 0xc1, 0xee, 0x3e, 0x71, 0xc4, 0x56, 0xb7, 0x5f, 0xf9, 0x32, 0x58, 0x33, 0x14, 0x7d, 0x51, 0xbf, 0xeb, 0x23, 0x19, 0x4a, 0x07, 0x3e, 0x73, 0xd4, 0xd2, 0xc8, 0xc8, 0x2a, 0x72, 0x24, 0x0e, 0xed, 0xf0, 0xb6, 0x95, 0x0e, 0xea, 0x24, 0xcb, 0x92, 0xdb, 0xa6, 0xf3, 0xc2, 0xef, 0x73, 0x9a, 0xef, 0x3e, 0xb9, 0x00, 0xf3, 0xda, 0xe2, 0x28, 0x3b, 0xb4, 0xfa, 0x5f, 0x9e, 0xc2, 0x4b, 0x8b, 0x4a, 0xbb, 0x7c, 0x25, 0xf6, 0x32, 0xf8, 0x60, 0x07, 0xeb, 0x11, 0xb6, 0x75, 0xf2, 0xc9, 0x66, 0x87, 0xb3, 0x31, 0x96, 0x1b, 0x13, 0x99, 0x13, 0x00, 0x9e, 0xd6, 0xd7, 0x59, 0xba, 0x90, 0xf8, 0xda, 0x00, 0x96, 0x94, 0x0f, 0xa1, 0x74, 0x84, 0x9f, 0xd7, 0xc1, 0xbb, 0xd3, 0xae, 0xe6, 0x7f, 0x0f, 0x82, 0x70, 0x13, 0x4d, 0x55, 0xdc, 0x0a, 0x5f, 0xa6, 0x48, 0xae, 0x16, 0x24, 0x83, 0x38, 0x42, 0x39, 0x7b, 0x23, 0x29, 0x3f, 0x98, 0x76, 0x04, 0x3d, 0x9b, 0x60, 0x51, 0xf7, 0xc7, 0xdb, 0x50, 0x39, 0x4d, 0x87, 0x7c, 0xef, 0x1b, 0xbc, 0xf0, 0x0c, 0xa6, 0x1b, 0x27, 0x4a, 0x0c, 0x6b, 0x09, 0x3b, 0x9f, 0xc3, 0x67, 0x86, 0x0d, 0xdb, 0x11, 0xd2, 0x50, 0xbb, 0x19, 0xe8, 0xb8, 0x18, 0xf2, 0x51, 0xce, 0xa8, 0x9b, 0x12, 0x69, 0x49, 0x3f, 0x22, 0x81, 0xfe, 0x23, 0x50, 0x02, 0xc0, 0xbd, 0x4b, 0xe4, 0x36, 0xd8, 0xe5, 0x5d, 0x11, 0x29, 0x10, 0x79, 0xd9, 0x6e, 0xd0, 0xdb, 0x81, 0x57, 0x9e, 0xa7, 0x54, 0x1f, 0x85, 0xb8, 0x1a, 0xdf, 0xa6, 0xbf, 0x0a, 0x43, 0x74, 0xc2, 0xb8, 0x6c, 0x09, 0x20, 0xbe, 0x59, 0xae, 0x05, 0xc7, 0x52, 0xe2, 0x67, 0xa8, 0x00, 0x21, 0x23, 0xbd, 0x3e, 0x6a, 0x2f, 0x10, 0x44, 0x09, 0xfb, 0x62, 0x35, 0xe0, 0x9a, 0x41, 0x1f, 0x06, 0x72, 0x12, 0x49, 0xed, 0x26, 0x78, 0x67, 0xdd, 0xfb, 0x9e, 0x28, 0xe4, 0x8d, 0xe1, 0x7a, 0xa2, 0x2f, 0x64, 0xca, 0xda, 0xa0, 0xee, 0x51, 0xc5, 0x2c, 0x8d, 0x65, 0x8c, 0xb1, 0x01, 0xd0, 0x91, 0x5f, 0x60, 0x8f, 0xba, 0xbd, 0x6a, 0x7f, 0x35, 0x40, 0x74, 0xaa, 0x24, 0xad, 0x42, 0xcc, 0xfa, 0xba, 0x02, 0xc4, 0xdc, 0xda, 0x24, 0x87, 0xb7, 0xd6, 0xc9, 0x1e, 0x3e, 0xd5, 0xb2, 0x31, 0x83, 0xc8, 0x32, 0x99, 0x9e, 0xef, 0xec, 0x94, 0xe6, 0x0c, 0xba, 0x6d, 0x0d, 0x0e, 0x18, 0x3e, 0x5c, 0xfb, 0xa1, 0xbc, 0xda, 0x16, 0xec, 0x71, 0xd7, 0x67, 0xe6, 0xbb, 0x2e, 0x88, 0x4c, 0x37, 0x14, 0x71, 0x23, 0x38, 0x6d, 0xae, 0x4b, 0xb0, 0x7a, 0xd1, 0xdb, 0x2a, 0x6d, 0xbb, 0xd5, 0x9f, 0x38, 0xdf, 0x4b, 0x43, 0xe5, 0xfa, 0x27, 0x5c, 0x95, 0x45, 0x28, 0x6a, 0xfe, 0x29, 0x82, 0x49, 0x24, 0x11, 0x59, 0xc1, 0x81, 0x37, 0x22, 0x18, 0xf6, 0x35, 0xf3, 0x74, 0x54, 0xea, 0x4c, 0xed, 0x04, 0x71, 0x14, 0x20, 0x09, 0xbf, 0x79, 0x34, 0xa5, 0x15, 0xdb, 0x9c, 0xa0, 0x18, 0x67, 0xbc, 0x74, 0x83, 0x5f, 0x7e, 0x7e, 0xbf, 0x1a, 0x8a, 0x8b, 0xf3, 0xb8, 0xe8, 0x9e, 0xb6, 0x98, 0x4a, 0x99, 0x0b, 0x77, 0x9a, 0xf6, 0x47, 0x24, 0x34, 0xf3, 0xcb, 0xc3, 0x48, 0x73, 0x6b, 0x17, 0x52, 0x3a, 0xf5, 0x50, 0xcd, 0x9e, 0xc2, 0xec, 0xf6, 0x15, 0x4b, 0x67, 0xf5, 0x8c, 0x90, 0xdc, 0x4b, 0x0a, 0xb4, 0x53, 0xf8, 0x63, 0x10, 0x9d, 0x01, 0x5d, 0x97, 0xc7, 0x9e, 0xc0, 0xcf, 0x99, 0xcd, 0x71, 0x4b, 0x71, 0xd4, 0xf2, 0x3d, 0xb8, 0x79, 0xc1, 0x15, 0xfd, 0xe2, 0xc0, 0xe9, 0xb5, 0x5b, 0xef, 0xb7, 0xc4, 0x72, 0xf9, 0x65, 0x14, 0xad, 0x96, 0x2b, 0x26, 0x3e, 0x75, 0xac, 0xb1, 0xea, 0x9b, 0xd2, 0xe1, 0xd1, 0xb6, 0xe1, 0x5e, 0x46, 0xa7, 0x4f, 0x50, 0xde, 0x00, 0x3b, 0xb2, 0x7d, 0xb0, 0xdf, 0x59, 0x69, 0xd6, 0xac, 0xa1, 0x64, 0xe8, 0xdf, 0xb9, 0x71, 0xf2, 0x31, 0x45, 0x1f, 0xd2, 0x4d, 0x8b, 0x67, 0x18, 0xd9, 0x67, 0x9c, 0xd6, 0x90, 0x64, 0x04, 0x5b, 0x46, 0x12, 0x45, 0xd3, 0xb7, 0xa0, 0xc9, 0xb8, 0x25, 0x79, 0xb7, 0x7c, 0x0e, 0x08, 0xc4, 0x4b, 0x01, 0xef, 0x36, 0x6b, 0xda, 0x9a, 0xb9, 0x34, 0x1b, 0x2c, 0xba, 0xf7, 0xc9, 0xfa, 0x5a, 0x16, 0xec, 0xbb, 0x4d, 0x94, 0x40, 0x1b, 0x37, 0xaf, 0x57, 0x9c, 0x7d, 0xd1, 0x69, 0x12, 0xeb, 0xb7, 0x20, 0x0b, 0x4b, 0x67, 0x9d, 0xfb, 0x6a, 0x58, 0x81, 0xaa, 0x9a, 0xe9, 0xd4, 0xf8, 0xbb, 0x0d, 0x68, 0x8c, 0xe0, 0x88, 0x91, 0x4d, 0xd7, 0x57, 0x73, 0x62, 0x88, 0xd5, 0xb7, 0xc6, 0x2e, 0x53, 0xa1, 0xfa, 0x7b, 0x1f, 0xdf, 0xc4, 0x78, 0x48, 0xcf, 0x06, 0x52, 0xbf, 0xff, 0xa3, 0xf6, 0x97, 0xa4, 0x04, 0x4e, 0xeb, 0x56, 0xa7, 0xf9, 0x39, 0x78, 0x8e, 0x23, 0x0a, 0xbf, 0xd9, 0x4a, 0xde, 0xda, 0x9f, 0x0a, 0x94, 0x2d, 0x8b, 0xb5, 0x7e, 0x28, 0x88, 0xa0, 0xfb, 0x72, 0x63, 0x3c, 0x26, 0xa3, 0x2f, 0xd4, 0x4b, 0xd0, 0x25, 0x6d, 0xc7, 0x9b, 0x34, 0x3d, 0x54, 0xb7, 0xd2, 0x4b, 0x89, 0x81, 0x07, 0x01, 0x89, 0x0e, 0x0f, 0x22, 0x64, 0xce, 0x21, 0xff, 0x4d, 0xfd, 0x9b, 0x04, 0x0f, 0xb8, 0x44, 0xd2, 0xe1, 0x2f, 0x0c, 0x41, 0xdd, 0xf3, 0x72, 0xdd, 0x02, 0x89, 0xca, 0x3e, 0xb6, 0xc8, 0xce, 0xff, 0xaf, 0x20, 0xd1, 0x8e, 0x9b, 0xf5, 0x87, 0x94, 0x49, 0x5b, 0x9f, 0x82, 0x28, 0xd4, 0x3c, 0xe3, 0xb9, 0x46, 0x4e, 0x0a, 0x48, 0xfb, 0x8c, 0x77, 0x7b, 0xf1, 0x10, 0x6f, 0x07, 0x8b, 0x75, 0x4f, 0xaa, 0x66, 0x45, 0x6a, 0xef, 0xee, 0x6f, 0x2c, 0x3c, 0x58, 0x30, 0x3c, 0x0d, 0xa2, 0x52, 0x32, 0x6a, 0x37, 0xda, 0x25, 0xd5, 0x02, 0xf9, 0x64, 0x36, 0x0e, 0x42, 0xb9, 0xb6, 0x88, 0xa9, 0xd5, 0xd1, 0x58, 0x3d, 0x33, 0x4d, 0x92, 0xa0, 0x10, 0xe0, 0x05, 0x63, 0xc8, 0x0a, 0x72, 0x6a, 0x95, 0xa7, 0x71, 0x18, 0x6c, 0x23, 0x65, 0xab, 0x01, 0x77, 0xa7, 0x23, 0x38, 0xd9, 0x1a, 0xec, 0x4e, 0x58, 0x1f, 0x5e, 0xfc, 0xf0, 0x7c, 0xf6, 0x3e, 0x00, 0x06, 0xfc, 0x10, 0x92, 0xf9, 0x07, 0x2c, 0x60, 0xcf, 0xbd, 0x74, 0xb6, 0x5b, 0xe4, 0x81, 0x07, 0x7b, 0x92, 0x8c, 0x0b, 0xf1, 0xc1, 0xf9, 0x60, 0x96, 0x1c, 0x1f, 0xf6, 0x6e, 0x21, 0x99, 0xea, 0xce, 0x12, 0x8f, 0x9b, 0xe4, 0x69, 0xaf, 0x3e, 0x61, 0x85, 0xca, 0x96, 0xbf, 0xcb, 0xbd, 0xb3, 0x91, 0x65, 0x9a, 0xcf, 0xe8, 0xcc, 0x98, 0xb1, 0xa5, 0x5a, 0xd4, 0xaa, 0xff, 0x07, 0x54, 0x4e, 0xb9, 0x1c, 0x0b, 0x77, 0xef, 0x10, 0x11, 0x0f, 0xa7, 0x75, 0xf8, 0x24, 0xe8, 0xe2, 0xf5, 0x9f, 0xe0, 0x91, 0xd4, 0x39, 0xad, 0xd9, 0x97, 0x44, 0xc0, 0xd1, 0x00, 0x66, 0xfa, 0x69, 0xad, 0xd1, 0x2c, 0xbc, 0x46, 0x5b, 0xe7, 0x4f, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x97, 0xe6, 0x8b, 0xbe, 0x78, 0x2d, 0xeb, 0xc6, 0x0e, 0x8e, 0x5a, 0xa9, 0xee, 0x19, 0xd4, 0x5a, 0x17, 0x32, 0xb0, 0xf9, 0x3c, 0x7e, 0x4a, 0xb1, 0x7f, 0x9b, 0xc0, 0x9e, 0xf5, 0x0d, 0x22, 0x73, 0x1e, 0x93, 0xac, 0x8f, 0x06, 0xea, 0x26, 0x10, 0x2f, 0xca, 0x13, 0xc0, 0x8e, 0xcd, 0x52, 0x13, 0x59, 0xdf, 0x30, 0xda, 0x86, 0x9b, 0x82, 0x5a, 0xbd, 0x13, 0x34, 0xcb, 0x58, 0x31, 0xad, 0x08, 0x49, 0xc3, 0x08, 0x5b, 0xf2, 0xf8, 0x13, 0xec, 0x7a, 0x0b, 0x77, 0x1a, 0x06, 0x1f, 0xe7, 0x4b, 0xf8, 0xf8, 0x1e, 0x75, 0xab, 0xa3, 0x3e, 0xb5, 0x5f, 0x85, 0xc3, 0x77, 0xc9, 0x81, 0xeb, 0x46, 0xd9, 0x3b, 0x91, 0xe6, 0xc2, 0x13, 0x16, 0x39, 0xf0, 0x50, 0x89, 0xa6, 0x79, 0x83, 0xba, 0xe4, 0x0e, 0x6c, 0x1b, 0x8b, 0xf2, 0x46, 0xc7, 0x63, 0x01, 0xb8, 0x36, 0x45, 0x6d, 0x89, 0xdb, 0xff, 0x30, 0xa8, 0xb7, 0x67, 0x48, 0xa7, 0x74, 0xe3, 0x08, 0xbf, 0x1a, 0xa0, 0xeb, 0x24, 0xa9, 0xfb, 0x11, 0x33, 0xb0, 0x11, 0xc6, 0x22, 0x6f, 0x23, 0x99, 0x3a, 0xf2, 0x8d, 0xc7, 0x9d, 0x3c, 0xab, 0xf1, 0xaa, 0x7f, 0x19, 0x9b, 0x2f, 0xcf, 0x3f, 0xf4, 0xe7, 0x2d, 0xda, 0x49, 0x70, 0x09, 0x12, 0xb3, 0xe8, 0xc5, 0xec, 0x29, 0xd9, 0xf0, 0x36, 0x40, 0xdf, 0xdc, 0x10, 0x54, 0xba, 0x4e, 0xad, 0xbd, 0x6b, 0xe8, 0xc0, 0xed, 0x81, 0x8e, 0x57, 0xb0, 0x1c, 0xd3, 0x6c, 0x7f, 0x10, 0x1c, 0x5e, 0x51, 0x34, 0x50, 0x48, 0x9b, 0x90, 0x01, 0x50, 0x7e, 0x36, 0x28, 0x3a, 0x75, 0x90, 0x13, 0x17, 0xe6, 0x43, 0x33, 0xc8, 0xac, 0xba, 0xa8, 0x00, 0x2c, 0xa2, 0xd0, 0x98, 0xfc, 0x97, 0x8b, 0x14, 0x70, 0xa7, 0x6e, 0x80, 0xc3, 0x0f, 0xe9, 0x1d, 0x00, 0xe5, 0xac, 0x53, 0x6d, 0x0f, 0xd4, 0xc5, 0x92, 0x0d, 0xf8, 0x2f, 0x75, 0x32, 0x72, 0x8b, 0x87, 0x05, 0xb3, 0xf8, 0xce, 0x1c, 0x1a, 0x41, 0x44, 0xe8, 0x1a, 0x8b, 0x8b, 0x6a, 0x82, 0x29, 0xa7, 0x35, 0x31, 0xe8, 0xee, 0x7f, 0x9f, 0x10, 0xe4, 0xe8, 0xd0, 0xaf, 0x04, 0x18, 0x71, 0xbc, 0x45, 0xdf, 0x59, 0x84, 0xd6, 0xca, 0x1f, 0x8f, 0xd7, 0x29, 0xff, 0xc1, 0x3d, 0x18, 0xc7, 0x91, 0x16, 0x53, 0x90, 0x36, 0x70, 0x7c, 0xad, 0x7f, 0xf2, 0x67, 0x30, 0x22, 0x31, 0x5b, 0x19, 0x21, 0x86, 0x37, 0xd7, 0x82, 0x82, 0x14, 0x35, 0x06, 0xbc, 0x73, 0x55, 0x39, 0x54, 0xbb, 0xf5, 0x6e, 0xd9, 0x47, 0xc3, 0xdc, 0xd0, 0xf8, 0xd0, 0x8b, 0x1c, 0x25, 0x89, 0xdf, 0x78, 0x09, 0xb8, 0xcd, 0x98, 0x8f, 0xe8, 0xf3, 0xbe, 0x88, 0xf1, 0x26, 0x3b, 0x97, 0x02, 0x6e, 0xaa, 0xe9, 0x95, 0x5d, 0x9e, 0xe4, 0xd0, 0x53, 0xa6, 0x91, 0xa0, 0x88, 0x84, 0xc6, 0x2f, 0x0d, 0x41, 0x0c, 0x9d, 0x0f, 0x51, 0x3f, 0xea, 0xf8, 0x85, 0x14, 0x17, 0x70, 0x00, 0xf2, 0x57, 0xa8, 0x6e, 0x79, 0xce, 0x2f, 0x28, 0x3f, 0x6c, 0xb3, 0x28, 0x8b, 0x29, 0xec, 0xe5, 0x42, 0x4e, 0xf1, 0x6f, 0x1b, 0x09, 0x4e, 0x57, 0x49, 0xf8, 0xac, 0x0b, 0x32, 0x9f, 0x84, 0x01, 0x6c, 0xcf, 0x11, 0xc7, 0xb9, 0x01, 0xe1, 0xe2, 0x8d, 0x74, 0xe0, 0xff, 0xcc, 0xb6, 0x26, 0xfb, 0xf2, 0xce, 0xa3, 0x78, 0x59, 0xc5, 0x64, 0x84, 0x72, 0xed, 0xaf, 0xa8, 0x5f, 0x7d, 0xe2, 0x17, 0xa1, 0xb2, 0xc9, 0x21, 0xb0, 0x83, 0x5b, 0x2a, 0xea, 0xc6, 0xa8, 0xe0, 0x63, 0x61, 0x73, 0x1e, 0x43, 0xc8, 0xc4, 0x29, 0x19, 0xa8, 0x21, 0x1b, 0xb5, 0x80, 0x15, 0x23, 0xf4, 0xeb, 0x3e, 0x3a, 0xcc, 0x88, 0x88, 0xae, 0x5c, 0x52, 0xd5, 0xb3, 0x2f, 0x9e, 0x74, 0x97, 0x61, 0x14, 0x1b, 0x82, 0xaa, 0x75, 0xdf, 0x08, 0x58, 0x94, 0x27, 0xe8, 0x59, 0x6b, 0xc8, 0xd6, 0x2e, 0x2a, 0xc5, 0x83, 0x80, 0x64, 0x32, 0x77, 0xd9, 0xf1, 0x7c, 0xca, 0x27, 0xe2, 0x68, 0xbc, 0xf2, 0x41, 0x49, 0x5f, 0x52, 0xbf, 0xef, 0xf6, 0xcc, 0x70, 0x94, 0x37, 0xa8, 0x2d, 0xb2, 0x15, 0x9a, 0xab, 0x45, 0x26, 0xee, 0x5c, 0x5c, 0x06, 0x03, 0x24, 0x21, 0xff, 0xc5, 0x86, 0x08, 0xbe, 0x96, 0xda, 0x36, 0x45, 0x6a, 0x4d, 0xa7, 0xb6, 0xce, 0xd4, 0xde, 0x66, 0x87, 0x4e, 0xc3, 0xbb, 0x3c, 0xe7, 0xf6, 0xe5, 0x56, 0x38, 0xd0, 0x3a, 0x94, 0x9a, 0xe4, 0xea, 0x97, 0x5c, 0x1e, 0xe6, 0x1a, 0x99, 0xc8, 0xf6, 0x50, 0x3d, 0x76, 0xf9, 0xfc, 0x37, 0xbd, 0xd0, 0x7a, 0xc3, 0x51, 0x02, 0x2e, 0xea, 0xb4, 0x43, 0x88, 0x1f, 0xcb, 0x95, 0x6a, 0xda, 0x05, 0xfc, 0x8a, 0xa4, 0x2e, 0xa0, 0x60, 0x90, 0x7c, 0x66, 0xdb, 0xa7, 0x70, 0xb8, 0x39, 0xe2, 0xca, 0x74, 0x0c, 0xf8, 0x60, 0x59, 0x15, 0xa2, 0x62, 0x76, 0x73, 0xfa, 0xf3, 0x74, 0x4f, 0x57, 0xc4, 0xd4, 0xba, 0xca, 0x2e, 0x40, 0x9c, 0x45, 0x59, 0xbb, 0x6c, 0x88, 0xbb, 0x30, 0x8e, 0xdf, 0x01, 0xbb, 0x9d, 0xbc, 0xae, 0x64, 0x2f, 0xe2, 0x48, 0xbd, 0x10, 0xd6, 0xad, 0x31, 0x93, 0xb8, 0xff, 0x06, 0xf8, 0xc6, 0x01, 0x8c, 0x0e, 0x87, 0x63, 0xbb, 0x9d, 0x80, 0xa5, 0x5e, 0x0e, 0x8b, 0xc1, 0xb6, 0xec, 0xfe, 0x46, 0x23, 0x64, 0x54, 0x52, 0x46, 0x46, 0x79, 0x07, 0x4e, 0xd0, 0xd7, 0x6d, 0x8d, 0xe1, 0x9a, 0xd1, 0xfe, 0xa2, 0x38, 0xfb, 0xdf, 0x03, 0x47, 0x78, 0xd5, 0x01, 0x5c, 0xf9, 0x48, 0xa6, 0x63, 0xa0, 0xe4, 0xfb, 0x50, 0x35, 0x9f, 0xb8, 0xce, 0x80, 0x9f, 0xb5, 0x2b, 0x01, 0xcf, 0x78, 0x31, 0x72, 0x2c, 0x44, 0x66, 0xb7, 0x7d, 0x6f, 0xfd, 0x00, 0x25, 0x0e, 0xf9, 0x04, 0x98, 0x49, 0xf5, 0xc1, 0xeb, 0xae, 0x27, 0x95, 0xe4, 0x47, 0xc3, 0xb5, 0x27, 0x64, 0x2d, 0x1a, 0xa5, 0x0b, 0x57, 0xde, 0xa3, 0x23, 0xda, 0x11, 0xd5, 0xeb, 0xa9, 0xec, 0xdc, 0x6f, 0xeb, 0xc2, 0x95, 0x56, 0xf4, 0x26, 0x6d, 0xb4, 0x64, 0x8e, 0x8c, 0x88, 0x1d, 0x59, 0x25, 0xb6, 0x56, 0x78, 0x85, 0x3d, 0xf4, 0xd9, 0x12, 0xa0, 0x7a, 0xc8, 0xd2, 0x49, 0x0c, 0xfc, 0x68, 0x1c, 0xbd, 0xf5, 0xe6, 0x9a, 0x0f, 0x54, 0x1e, 0x37, 0x60, 0x9e, 0x93, 0xe5, 0xe7, 0x46, 0x9d, 0x0d, 0x2f, 0xd7, 0x61, 0xfc, 0x4c, 0xdf, 0x6c, 0x39, 0x07, 0x4f, 0x4b, 0x04, 0xfb, 0xec, 0x5c, 0x83, 0x54, 0x06, 0x14, 0x80, 0x05, 0x5f, 0xba, 0xf9, 0xec, 0x2f, 0xe3, 0x43, 0x74, 0xc6, 0x4d, 0xb9, 0x48, 0x93, 0xd8, 0xc8, 0x37, 0xdd, 0xf5, 0x39, 0x0a, 0x64, 0xb7, 0x91, 0x79, 0xf8, 0x85, 0x03, 0xbb, 0x41, 0xa1, 0xb1, 0x62, 0x2c, 0xd7, 0xec, 0x62, 0xc4, 0xc6, 0xc5, 0xc4, 0xdb, 0xb8, 0xf7, 0x5f, 0x41, 0xb7, 0x92, 0xe7, 0x17, 0xfc, 0xcf, 0x4f, 0x35, 0xb3, 0x49, 0xaf, 0xf1, 0x32, 0x3c, 0x16, 0x46, 0xc7, 0xb2, 0xef, 0xf4, 0x5d, 0x42, 0x11, 0x36, 0x29, 0x68, 0x92, 0x44, 0xb8, 0x1a, 0x14, 0xa0, 0x25, 0x94, 0x71, 0xdf, 0xa5, 0xbd, 0xa2, 0x30, 0xa8, 0xe5, 0xaf, 0x6b, 0x46, 0x62, 0xff, 0xcd, 0x34, 0xe6, 0x09, 0x4c, 0x96, 0x06, 0x5d, 0xb2, 0xb4, 0xcb, 0xc0, 0x54, 0x61, 0x56, 0x8f, 0x8e, 0xef, 0x59, 0x0f, 0x8e, 0xc2, 0x62, 0x83, 0x01, 0x50, 0x2f, 0x16, 0x6d, 0xe8, 0x6e, 0xda, 0xd6, 0x7b, 0x9d, 0xf6, 0xca, 0x30, 0x10, 0x59, 0xc7, 0x06, 0x92, 0x9d, 0xdd, 0x98, 0xd3, 0x07, 0x06, 0x3d, 0x35, 0xba, 0xe6, 0x4e, 0xf1, 0x49, 0x4f, 0x11, 0x4a, 0x75, 0x23, 0x5b, 0xc2, 0xec, 0x0c, 0x43, 0xd4, 0x9d, 0x3b, 0xfc, 0xc8, 0xc9, 0x75, 0x77, 0xee, 0x96, 0x0a, 0x52, 0xbe, 0xa5, 0x76, 0xde, 0x8e, 0x40, 0x85, 0x1d, 0x32, 0x3a, 0xe8, 0x75, 0xd1, 0x1d, 0x2b, 0x65, 0xfd, 0x40, 0xbe, 0x7d, 0x5b, 0x25, 0x95, 0xdf, 0x60, 0xde, 0xa9, 0x70, 0x5d, 0x53, 0x2d, 0xe0, 0xa1, 0x6d, 0xdb, 0xe5, 0xd5, 0xd8, 0x77, 0x19, 0xeb, 0x4e, 0xa3, 0x32, 0x82, 0x25, 0x5b, 0xe4, 0xfa, 0x90, 0x16, 0x6e, 0xfa, 0x0d, 0x77, 0xa7, 0xee, 0xf4, 0xf6, 0x50, 0xe7, 0xdb, 0x46, 0x6e, 0xe0, 0x7b, 0x37, 0xe5, 0xf6, 0xee, 0x81, 0x0b, 0x90, 0x39, 0x4f, 0x0e, 0x1a, 0x1f, 0xb5, 0x4d, 0x80, 0x7c, 0x31, 0xbd, 0x86, 0x5c, 0x30, 0x50, 0x38, 0x93, 0x18, 0x78, 0x5d, 0xbb, 0x23, 0x4b, 0x60, 0x8b, 0x42, 0x69, 0x7e, 0xfd, 0xf4, 0xaa, 0xe2, 0x9a, 0xb6, 0xe1, 0x28, 0x83, 0x37, 0x1b, 0x62, 0x17, 0x2f, 0x9b, 0xc5, 0xa1, 0x9d, 0x0b, 0xea, 0x0e, 0x9e, 0x68, 0x8b, 0xa9, 0x04, 0xf8, 0x5c, 0x21, 0x94, 0xad, 0x0b, 0x94, 0xc0, 0x43, 0x92, 0x7d, 0xd0, 0xc2, 0x09, 0x36, 0x47, 0x59, 0x4f, 0x85, 0x70, 0xba, 0x03, 0x91, 0xd0, 0x25, 0xb6, 0xf5, 0xc7, 0xd2, 0xf5, 0x38, 0x67, 0x5a, 0x26, 0xdd, 0xcb, 0xff, 0x7b, 0xac, 0xe0, 0x26, 0x13, 0xfa, 0x2d, 0xb9, 0x05, 0x86, 0xa9, 0xf6, 0xc4, 0xb8, 0x8f, 0x5d, 0x94, 0xe0, 0x51, 0xfe, 0x38, 0x6b, 0x71, 0x26, 0x3f, 0xa4, 0x93, 0x6f, 0x6d, 0xf4, 0xd8, 0x53, 0xdd, 0x08, 0xb2, 0x51, 0x7f, 0x30, 0xab, 0x46, 0x4d, 0xaa, 0x15, 0xa0, 0xba, 0xed, 0x38, 0x90, 0xb9, 0x28, 0x62, 0x2f, 0xfd, 0xae, 0xf6, 0xa1, 0x1d, 0x82, 0xfa, 0xc2, 0x50, 0x8a, 0xfe, 0xf1, 0x82, 0xaf, 0x58, 0x3e, 0xec, 0x78, 0x94, 0x54, 0x2d, 0xaa, 0x8e, 0x5f, 0xe0, 0x11, 0x41, 0xd5, 0xcf, 0x25, 0x0b, 0x37, 0x4d, 0xc0, 0x1f, 0xf0, 0x98, 0x55, 0x3e, 0x6c, 0x75, 0xd7, 0xd1, 0x60, 0xe7, 0x03, 0x03, 0x4d, 0x48, 0x5b, 0x30, 0x75, 0xb3, 0x95, 0x0b, 0xa2, 0x2f, 0x34, 0xe6, 0x23, 0xdc, 0x04, 0x36, 0x5e, 0x32, 0x5b, 0x25, 0x49, 0xe1, 0x26, 0x11, 0xbf, 0x5c, 0x27, 0xba, 0xa7, 0x1c, 0x11, 0xbb, 0xcc, 0x0b, 0x83, 0xb3, 0x46, 0x1d, 0x7b, 0xbc, 0x51, 0x11, 0x45, 0xc4, 0xbd, 0x85, 0x2a, 0xda, 0x0a, 0x86, 0xb2, 0x7b, 0x61, 0x6b, 0x3f, 0x5e, 0x6b, 0xcf, 0x77, 0x48, 0x4f, 0x59, 0xf0, 0x2f, 0x10, 0x53, 0xeb, 0x52, 0x98, 0x4d, 0xf5, 0x8f, 0xee, 0xb5, 0x97, 0xaa, 0xe7, 0xdc, 0xa5, 0x92, 0xfb, 0xd8, 0xec, 0x3b, 0x23, 0xd1, 0xbf, 0x9a, 0x99, 0xc3, 0x07, 0x56, 0xc1, 0x68, 0x99, 0x8f, 0xc6, 0xbc, 0x39, 0xf2, 0x3e, 0x20, 0xd1, 0xa3, 0xa5, 0xc5, 0x1c, 0xe6, 0x4d, 0x73, 0x83, 0x8a, 0x86, 0xbd, 0xde, 0x63, 0xdd, 0xe4, 0x78, 0x99, 0xcc, 0x8a, 0x24, 0x4b, 0x08, 0x74, 0x22, 0x11, 0x14, 0xd3, 0xfa, 0x3a, 0x90, 0xd4, 0xd6, 0xe1, 0x43, 0x2c, 0xc4, 0xfa, 0x43, 0x2e, 0x42, 0x79, 0x48, 0xb2, 0x52, 0x48, 0x76, 0x06, 0x93, 0xc1, 0x92, 0x76, 0x7a, 0xb8, 0xa8, 0x35, 0x5b, 0xb6, 0x63, 0x5d, 0x74, 0x3e, 0xb6, 0x10, 0xa1, 0x4a, 0xf7, 0x3c, 0xb1, 0x67, 0x35, 0x2a, 0x62, 0xbd, 0xd6, 0x44, 0x11, 0x0d, 0x25, 0xf5, 0xe1, 0xd2, 0xd6, 0x98, 0xfb, 0xc0, 0xc7, 0x5b, 0x0a, 0xae, 0x2d, 0x1d, 0x4e, 0x5c, 0xcf, 0x82, 0x8e, 0xd7, 0x54, 0x85, 0x93, 0xb9, 0xbe, 0x09, 0x61, 0xa9, 0x51, 0x74, 0xc8, 0xb5, 0x0f, 0xa6, 0xb2, 0x23, 0x3f, 0x5e, 0x06, 0xa4, 0x36, 0x95, 0xaa, 0xd7, 0xfa, 0x05, 0x8d, 0x32, 0x30, 0x6b, 0xae, 0x97, 0x0a, 0xf5, 0x51, 0x4b, 0x88, 0x61, 0x29, 0x81, 0xee, 0xc9, 0xde, 0xe0, 0x46, 0x19, 0x2d, 0xe8, 0xc6, 0x88, 0x0c, 0x01, 0x5c, 0xf8, 0x8c, 0xf6, 0x66, 0xa0, 0x21, 0x5b, 0x65, 0x79, 0x46, 0x7f, 0xb3, 0x85, 0x8d, 0x51, 0xd8, 0xfb, 0x94, 0xcc, 0x0b, 0x94, 0x79, 0x29, 0xcc, 0x07, 0x6b, 0xb0, 0x2e, 0x9f, 0x3b, 0x81, 0xab, 0x62, 0x0b, 0x3c, 0x2e, 0x09, 0x46, 0x6a, 0x22, 0x95, 0x04, 0x1c, 0xd1, 0x9e, 0x6c, 0x59, 0xe1, 0x0d, 0xf4, 0xc0, 0x9c, 0x58, 0x52, 0xa2, 0xf1, 0x68, 0x30, 0xe1, 0x8e, 0x4b, 0x4c, 0x3c, 0xbc, 0x0e, 0xb1, 0x5a, 0x54, 0x97, 0xef, 0x0c, 0x69, 0x6a, 0x7b, 0xbc, 0x2b, 0x02, 0xc6, 0x84, 0xfa, 0x0a, 0x92, 0xe7, 0xea, 0x11, 0x89, 0x2f, 0x16, 0xf4, 0x0b, 0xfc, 0x7d, 0xc0, 0x21, 0x7f, 0x10, 0xb7, 0x98, 0x3d, 0x50, 0x51, 0x4e, 0xcb, 0x4f, 0x06, 0x85, 0x1b, 0xda, 0xdb, 0x39, 0x15, 0x8a, 0x86, 0xe5, 0xe9, 0xa7, 0xc9, 0x52, 0xde, 0x56, 0xca, 0x87, 0x1d, 0xb1, 0x92, 0x14, 0x2a, 0x64, 0x3e, 0x3a, 0x44, 0x4e, 0x48, 0x5d, 0x1f, 0x8a, 0x26, 0x92, 0xd3, 0xf5, 0xbc, 0x0d, 0x33, 0x0c, 0x94, 0xe8, 0x47, 0x62, 0x8c, 0x86, 0xaa, 0x50, 0x5d, 0x5b, 0xb8, 0x37, 0xc0, 0x21, 0x8f, 0x10, 0xba, 0x0a, 0xd1, 0xb4, 0x33, 0xa3, 0x6b, 0xe8, 0xe3, 0x29, 0x2e, 0xfd, 0x4a, 0xd4, 0xfb, 0xa5, 0x06, 0x55, 0x3a, 0xe4, 0xba, 0x69, 0x61, 0x80, 0x5d, 0x43, 0x92, 0xb5, 0xd9, 0xeb, 0x40, 0x50, 0x4c, 0xd5, 0x74, 0x2e, 0xfa, 0x88, 0xe4, 0xf1, 0xf3, 0x36, 0xb1, 0x19, 0xf7, 0xd7, 0xba, 0x6a, 0x11, 0xc3, 0x84, 0x28, 0xc2, 0xa7, 0xda, 0x6e, 0x0b, 0xad, 0x0e, 0x8b, 0xe5, 0x32, 0xe8, 0xee, 0x77, 0x3d, 0xfe, 0xc5, 0x25, 0x76, 0xb3, 0x1a, 0x08, 0xa9, 0xf6, 0x01, 0x5d, 0x3f, 0xdb, 0xd1, 0x25, 0x59, 0xb9, 0x3f, 0x54, 0x88, 0x29, 0x42, 0xd9, 0xfd, 0xa6, 0x89, 0x01, 0x16, 0x1b, 0xbe, 0x72, 0xb5, 0x41, 0x00, 0x44, 0x15, 0xd9, 0xd0, 0xca, 0x5c, 0x50, 0xbb, 0x45, 0x76, 0x4d, 0xa3, 0x28, 0x2e, 0x76, 0x7c, 0x4d, 0xef, 0xfc, 0x95, 0x21, 0x19, 0x45, 0x77, 0xfe, 0xe6, 0x51, 0xc3, 0xad, 0xbf, 0x37, 0x41, 0xf0, 0xb6, 0x65, 0x96, 0x8b, 0xa9, 0x72, 0x64, 0x57, 0xc1, 0xcc, 0x43, 0xd5, 0x1a, 0xff, 0xcf, 0x8c, 0x03, 0x30, 0xd8, 0x92, 0x18, 0xa2, 0xf0, 0x74, 0xab, 0xb0, 0xa4, 0xf2, 0xab, 0x73, 0x66, 0x85, 0x4a, 0x07, 0x4b, 0xcb, 0x7f, 0xb2, 0x19, 0x3a, 0xa0, 0xc8, 0x89, 0x4c, 0x1f, 0x57, 0x15, 0x3a, 0x1d, 0x9d, 0xca, 0x39, 0x19, 0xe7, 0x2a, 0xf6, 0x03, 0x33, 0x41, 0x5d, 0x94, 0xf5, 0xc2, 0x40, 0xdf, 0xf0, 0x20, 0xd1, 0x68, 0x25, 0xfa, 0x4d, 0x50, 0xdc, 0x00, 0x30, 0x93, 0xa1, 0xc6, 0xc9, 0x17, 0xd5, 0x8a, 0xb7, 0xb5, 0x34, 0xb7, 0xaf, 0xad, 0x92, 0x7f, 0xb6, 0x30, 0x37, 0x7d, 0xc8, 0x22, 0x26, 0x56, 0x63, 0xe0, 0xbe, 0x23, 0x23, 0x81, 0xeb, 0xe7, 0xe4, 0x1a, 0x06, 0x1a, 0x12, 0x7e, 0x6a, 0xb6, 0xab, 0x27, 0xd6, 0xff, 0x81, 0xa7, 0x91, 0x68, 0x3d, 0x7d, 0x82, 0xce, 0xf5, 0xc0, 0x58, 0x2c, 0x3d, 0xd0, 0xc8, 0x9e, 0x19, 0x91, 0x13, 0x8a, 0xf7, 0x0e, 0xab, 0xc3, 0x0f, 0x92, 0xe8, 0x56, 0xed, 0x64, 0xbf, 0x84, 0x99, 0xd7, 0xdb, 0xbc, 0xc8, 0x99, 0xad, 0x6e, 0xcc, 0xd5, 0xc3, 0xe4, 0x44, 0xb5, 0x03, 0x1f }; +constexpr AccessUnit AC3_LARGE_AU_EXPECTED_AU = { 0x15f90, 0x159b2, false, 0, {}, { 0x15, 0x52, 0x08, 0x0e, 0x4a, 0x8b, 0xd1, 0xa1, 0xfe, 0x30, 0xc1, 0x4b, 0xc9, 0x96, 0x59, 0xc4, 0x6c, 0x6b, 0x42, 0xed } }; + +const std::vector LPCM_LARGE_AU_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x40, 0x00, 0x00, 0x00, 0xfb, 0xba, 0x2b, 0xca, 0x88, 0x65, 0x2b, 0x01, 0xf4, 0xe6, 0xfc, 0x43, 0xc2, 0xad, 0x45, 0xe3, 0xf1, 0x0b, 0x86, 0x17, 0x75, 0x98, 0x11, 0x62, 0xb4, 0xd9, 0xb5, 0x6c, 0x1e, 0xac, 0xf0, 0xaa, 0xf6, 0xc4, 0x4a, 0xcc, 0x5d, 0xb3, 0x3d, 0xd3, 0x40, 0x11, 0xbc, 0x73, 0x09, 0x49, 0x80, 0xe4, 0xef, 0x95, 0xb2, 0xc8, 0xb4, 0x48, 0x20, 0xe2, 0x49, 0x07, 0x94, 0x7c, 0xe7, 0xab, 0x73, 0x0b, 0xa9, 0x97, 0xbd, 0x8b, 0xf3, 0x90, 0x3c, 0x12, 0x29, 0xec, 0xe8, 0x5a, 0x8a, 0xbb, 0x35, 0x60, 0xf5, 0x6c, 0xaa, 0x93, 0xf1, 0x3c, 0x86, 0x8b, 0x63, 0x11, 0xa8, 0x22, 0x32, 0x90, 0x4a, 0x57, 0xa0, 0x91, 0xbf, 0x95, 0x45, 0xef, 0x98, 0xa1, 0x11, 0xf7, 0x59, 0x05, 0xc2, 0x0f, 0x13, 0x86, 0x27, 0xd4, 0x28, 0x21, 0x27, 0x74, 0x31, 0xdb, 0xe6, 0xf9, 0xa5, 0xbd, 0xe1, 0x19, 0x79, 0x9f, 0xc6, 0xd4, 0x49, 0x14, 0x06, 0x59, 0x03, 0x0f, 0x07, 0x16, 0x93, 0x95, 0x2e, 0xae, 0x82, 0x99, 0xbb, 0x86, 0x36, 0xda, 0x6e, 0xf2, 0x3e, 0x77, 0xa9, 0x74, 0x79, 0x92, 0x0c, 0x28, 0x24, 0x4a, 0x1f, 0x0e, 0x16, 0xf8, 0xdf, 0xa9, 0x4f, 0x67, 0xb8, 0xe1, 0xfa, 0x83, 0x42, 0x3a, 0x38, 0x2e, 0x12, 0xb3, 0x10, 0x54, 0x59, 0x0c, 0xdb, 0xa1, 0xe8, 0x64, 0x0d, 0x87, 0x6b, 0xac, 0xd1, 0x2c, 0x90, 0x15, 0xd6, 0xed, 0x5e, 0x78, 0x94, 0x05, 0x2d, 0x15, 0xb0, 0xf0, 0xbd, 0xd6, 0x02, 0xa1, 0xf7, 0xf2, 0x2a, 0xf1, 0x1a, 0xe7, 0xf0, 0x3c, 0x36, 0xd9, 0x24, 0xb6, 0x98, 0x4e, 0xda, 0x73, 0x39, 0x33, 0xda, 0x5c, 0xb1, 0x4e, 0x4c, 0xca, 0x01, 0xa2, 0x96, 0x13, 0x9f, 0x8a, 0x87, 0xdb, 0x3f, 0x09, 0x14, 0x6b, 0x2c, 0x99, 0x55, 0x37, 0xe4, 0xe5, 0x08, 0x61, 0xf5, 0x4e, 0xb2, 0x0a, 0x3e, 0xbd, 0xb8, 0x52, 0xdf, 0xea, 0x8b, 0xee, 0x49, 0x98, 0x7d, 0x94, 0x95, 0x2c, 0x1c, 0x29, 0x67, 0xb5, 0xa2, 0x38, 0x06, 0x91, 0x25, 0x22, 0xd3, 0x3c, 0xd6, 0xd0, 0x7d, 0x2f, 0xd1, 0x03, 0x2a, 0xec, 0xea, 0xa9, 0xfa, 0xab, 0x70, 0x49, 0xf4, 0xde, 0x5d, 0xfd, 0xda, 0x7f, 0xf7, 0xf1, 0xfc, 0x56, 0x52, 0x1e, 0x85, 0x1c, 0x9f, 0xaa, 0xbd, 0x6f, 0x3d, 0xda, 0x46, 0xe2, 0x58, 0x80, 0xe1, 0x3d, 0xae, 0xed, 0x51, 0xf8, 0x1c, 0x65, 0x71, 0x9b, 0xf0, 0x04, 0x31, 0xdd, 0xb8, 0x4b, 0x15, 0x29, 0xf5, 0xaa, 0x82, 0x11, 0x26, 0x1c, 0xaf, 0xb7, 0x47, 0xa0, 0x72, 0x26, 0x48, 0x73, 0x7e, 0xa2, 0xaa, 0x0b, 0x9f, 0x86, 0x44, 0xa5, 0xfd, 0xfa, 0xb5, 0x58, 0x21, 0x1b, 0x46, 0x4b, 0x46, 0xd7, 0x6b, 0xc8, 0xcd, 0xc3, 0x08, 0x60, 0x6d, 0xc8, 0x65, 0xd7, 0xa5, 0x67, 0x63, 0xf9, 0x07, 0x8b, 0x68, 0x16, 0xce, 0x35, 0x37, 0xe3, 0x71, 0xb7, 0xb1, 0x5f, 0x73, 0x3b, 0xba, 0xe3, 0xd6, 0x38, 0x2b, 0x35, 0x77, 0x06, 0x11, 0x2e, 0x63, 0xf2, 0xdc, 0x26, 0x34, 0x8d, 0x86, 0x81, 0x05, 0x70, 0xd3, 0x0a, 0x65, 0xec, 0x7a, 0xf4, 0x6a, 0xcc, 0x36, 0x93, 0x19, 0xe4, 0x40, 0xd7, 0x2c, 0x33, 0x8e, 0xab, 0xc0, 0x19, 0x2c, 0xd5, 0x60, 0x7f, 0x5e, 0x99, 0x82, 0x09, 0x23, 0x08, 0xe0, 0x2c, 0x50, 0x5d, 0x88, 0xd7, 0x7f, 0x13, 0xdc, 0xb3, 0x0f, 0xfb, 0x99, 0x4b, 0x75, 0xfb, 0x40, 0xa0, 0x8b, 0x13, 0xbc, 0x74, 0x03, 0xe4, 0x36, 0x18, 0x28, 0x50, 0x26, 0xfa, 0x94, 0x3d, 0xa6, 0x41, 0x6c, 0xff, 0xd1, 0x14, 0x35, 0x81, 0xe8, 0x9c, 0x6d, 0x15, 0xea, 0xc2, 0xef, 0x61, 0x80, 0x56, 0x97, 0xd9, 0xec, 0x66, 0x3d, 0xf8, 0x6f, 0x74, 0xb0, 0x18, 0x50, 0x9d, 0x12, 0x4a, 0x96, 0xdc, 0x86, 0xf7, 0x2c, 0xa1, 0x8a, 0x43, 0x03, 0x3b, 0xe8, 0x4e, 0xd9, 0x43, 0x2e, 0x46, 0xa5, 0xa9, 0xce, 0x92, 0x38, 0x12, 0xab, 0xe1, 0x47, 0x18, 0x8e, 0x26, 0x44, 0x17, 0x7a, 0xe9, 0xd0, 0x58, 0xd7, 0x6a, 0xc4, 0xc5, 0x00, 0x04, 0x24, 0x47, 0xb5, 0xaa, 0x61, 0x2b, 0x9d, 0x41, 0xbd, 0x97, 0x63, 0x03, 0x3c, 0x55, 0x85, 0x15, 0x2d, 0x3f, 0xb8, 0x68, 0x3f, 0xb9, 0x80, 0x57, 0x59, 0xcf, 0x50, 0xb1, 0x53, 0xda, 0x5e, 0xfb, 0xff, 0xde, 0x85, 0x1e, 0xaf, 0x1c, 0x95, 0x87, 0xe1, 0xbd, 0xe2, 0x10, 0x42, 0xb4, 0x44, 0x42, 0xf3, 0x5a, 0xbf, 0x16, 0xd9, 0xbf, 0x45, 0x36, 0x8e, 0x00, 0xd7, 0xaf, 0x72, 0xb3, 0x85, 0xf7, 0x98, 0xbf, 0x3d, 0xb0, 0x20, 0xc3, 0x92, 0xb6, 0x2e, 0xda, 0x71, 0x0c, 0x6e, 0xdd, 0x05, 0x74, 0x30, 0xbe, 0xd7, 0x35, 0x66, 0x07, 0xcc, 0x43, 0x75, 0xc8, 0xbe, 0xce, 0x66, 0x1e, 0x36, 0xcd, 0x1f, 0xe8, 0x63, 0x7e, 0x0b, 0x9c, 0x86, 0x85, 0xdc, 0xfd, 0xd4, 0x71, 0xb1, 0x0f, 0xe8, 0x69, 0xd8, 0x91, 0xa9, 0xc4, 0x26, 0xde, 0x57, 0xd7, 0xcd, 0x35, 0xd8, 0x40, 0xb8, 0x79, 0xfa, 0x33, 0xb0, 0x4c, 0xb2, 0xe2, 0x76, 0x21, 0x75, 0x72, 0x08, 0xf4, 0x45, 0x3d, 0x0b, 0xaa, 0x98, 0x0c, 0x5a, 0x72, 0x15, 0x17, 0x49, 0xf0, 0xcc, 0x33, 0x0d, 0x55, 0x04, 0x58, 0x17, 0x1a, 0x5c, 0x52, 0xb6, 0x5d, 0x22, 0xaa, 0x73, 0x24, 0xe5, 0x3b, 0x3e, 0xa4, 0x6a, 0xcc, 0xc9, 0x23, 0x6d, 0xc6, 0x9b, 0xc6, 0x5c, 0xe8, 0x6e, 0x7c, 0x0a, 0xa1, 0xa0, 0xb8, 0x05, 0x17, 0xb1, 0x18, 0xb8, 0x27, 0x38, 0x7e, 0xff, 0x89, 0x73, 0x6e, 0x79, 0x7d, 0xf9, 0x1e, 0x35, 0xb3, 0x2f, 0x9b, 0x85, 0xda, 0xd0, 0xbc, 0xb2, 0xd2, 0x2c, 0x56, 0x99, 0x12, 0xba, 0x62, 0xbc, 0x62, 0xc9, 0xaf, 0x3c, 0x62, 0x53, 0xf3, 0xb6, 0x64, 0xa1, 0xd8, 0x43, 0xda, 0x97, 0xe7, 0x68, 0xb0, 0x2f, 0xba, 0x8a, 0x0f, 0x09, 0x13, 0xbf, 0x6a, 0xe2, 0x92, 0x04, 0xf5, 0xb3, 0xd3, 0x3b, 0xfa, 0x28, 0xb8, 0x76, 0x55, 0x7a, 0xe8, 0x0a, 0xa5, 0x41, 0x62, 0xfe, 0xf3, 0xd9, 0x50, 0x68, 0xc7, 0xb2, 0x6d, 0xde, 0xc7, 0x77, 0x1e, 0x7e, 0x50, 0x79, 0xc0, 0xf2, 0xd2, 0x83, 0x8b, 0x0d, 0x9e, 0xce, 0xaf, 0x9b, 0x33, 0x20, 0xd9, 0x8b, 0xf5, 0xf1, 0x3c, 0x92, 0xb7, 0x5d, 0x93, 0x38, 0xda, 0xd4, 0x84, 0xe6, 0xbe, 0xcd, 0x65, 0x14, 0x94, 0x60, 0xae, 0x26, 0x6b, 0x1d, 0x15, 0x99, 0xf3, 0x56, 0xac, 0x20, 0x5e, 0x0a, 0x2e, 0x90, 0x5e, 0x69, 0x6a, 0xb2, 0xf0, 0xe2, 0xf2, 0x96, 0xfe, 0x61, 0xcd, 0xe8, 0x44, 0x52, 0x0a, 0x5d, 0x2a, 0x3d, 0x4e, 0x45, 0xc5, 0xef, 0x95, 0x55, 0x59, 0xe1, 0x7f, 0x9e, 0x43, 0x05, 0x7c, 0x09, 0xd5, 0xd8, 0x32, 0x9a, 0x41, 0x40, 0xa5, 0x22, 0x3a, 0xf9, 0x28, 0x85, 0x5c, 0x94, 0x89, 0x63, 0xbf, 0x81, 0x06, 0xa9, 0x6f, 0x63, 0x66, 0x2d, 0x9d, 0x66, 0x87, 0xfb, 0x6f, 0xdc, 0x0d, 0x75, 0x76, 0x61, 0x16, 0xd1, 0xda, 0xfa, 0x65, 0xe1, 0x3d, 0x32, 0xea, 0x01, 0x11, 0x71, 0x38, 0x20, 0x5a, 0x27, 0x3b, 0x6c, 0x9d, 0xb2, 0xda, 0xb0, 0x7b, 0xef, 0x26, 0x6a, 0x96, 0x86, 0x62, 0x80, 0x6f, 0x59, 0xcb, 0xc2, 0x79, 0x49, 0x24, 0x95, 0x8f, 0xd0, 0x4a, 0x65, 0x94, 0x0f, 0xbf, 0x9f, 0x9b, 0xc7, 0x1f, 0x7b, 0x46, 0x56, 0x29, 0x21, 0xa9, 0xdf, 0x01, 0xba, 0x89, 0x3d, 0x24, 0xef, 0x6f, 0x4b, 0x24, 0x6f, 0x4c, 0x74, 0xe7, 0x40, 0x0c, 0xde, 0x60, 0xbf, 0xe2, 0x86, 0x78, 0x26, 0xe6, 0xd6, 0xd3, 0x27, 0x10, 0x57, 0xd9, 0xb8, 0x09, 0x85, 0x3f, 0x28, 0xdf, 0x36, 0xba, 0x4d, 0x4f, 0xc3, 0xa3, 0x65, 0x81, 0x6d, 0xdf, 0x94, 0xc9, 0x90, 0xce, 0x65, 0xc2, 0x7e, 0x42, 0xc9, 0x4c, 0x11, 0x53, 0x45, 0xa7, 0x95, 0x97, 0x7a, 0x2d, 0xc5, 0x79, 0xee, 0x61, 0x0e, 0x5c, 0xe9, 0x9c, 0xfc, 0xb4, 0xba, 0x19, 0x7f, 0x64, 0x60, 0xac, 0x94, 0x6e, 0xc7, 0xc1, 0x5c, 0x3f, 0x9e, 0xac, 0xfb, 0x78, 0x52, 0xc8, 0x8a, 0x91, 0x02, 0x5c, 0x3c, 0xf7, 0x45, 0xe0, 0x14, 0x94, 0x2f, 0x8a, 0x02, 0x43, 0x83, 0x03, 0x67, 0x82, 0x5b, 0xc6, 0xc7, 0xe0, 0x0a, 0xe2, 0x06, 0x90, 0x1f, 0x80, 0xdc, 0x12, 0x52, 0xc7, 0x1d, 0x6a, 0x2a, 0xcd, 0x71, 0x85, 0xb1, 0x51, 0x9a, 0xa1, 0x2e, 0xc6, 0x07, 0x88, 0xa3, 0x3a, 0xd5, 0xdd, 0x8f, 0x55, 0xbd, 0xe4, 0x63, 0xd1, 0x95, 0x98, 0x4b, 0xe6, 0xea, 0x8f, 0x70, 0x0e, 0x67, 0x91, 0x74, 0xd6, 0x79, 0x9c, 0x17, 0xfd, 0xef, 0xf2, 0x8b, 0xa6, 0xf6, 0x26, 0xdf, 0x0e, 0x5c, 0xc2, 0x5d, 0x1d, 0x9c, 0x4d, 0xe5, 0xb2, 0xf7, 0xb4, 0xb5, 0x63, 0x15, 0x4b, 0x16, 0x3f, 0x21, 0x77, 0xe1, 0x8b, 0x1a, 0x92, 0x0d, 0x02, 0x39, 0x9b, 0xdf, 0x74, 0x79, 0x73, 0x08, 0x78, 0x69, 0x6d, 0xcb, 0x8d, 0xc2, 0x57, 0x85, 0xed, 0x2a, 0x54, 0xeb, 0x75, 0x14, 0xfe, 0x16, 0xc8, 0x4a, 0x72, 0xa2, 0x58, 0x01, 0xd9, 0x2c, 0xbb, 0x9e, 0x42, 0x8f, 0xf6, 0x1d, 0x6c, 0x50, 0x7c, 0x78, 0x69, 0x15, 0x57, 0x05, 0x6a, 0x55, 0x04, 0x66, 0x31, 0xf2, 0x2e, 0x96, 0x28, 0x59, 0x86, 0xbd, 0xcf, 0x83, 0x69, 0xc8, 0x76, 0xe7, 0xf3, 0x61, 0xac, 0x1a, 0x02, 0x7a, 0xf3, 0x2e, 0x8a, 0x65, 0x99, 0x6a, 0x06, 0xc0, 0xca, 0x3d, 0x28, 0x83, 0x66, 0xde, 0x2b, 0xf4, 0xfa, 0x78, 0xa0, 0x3b, 0xa8, 0x6c, 0x75, 0x22, 0x51, 0xd4, 0x0e, 0xcb, 0x3e, 0x88, 0x65, 0x02, 0x9e, 0xde, 0xb1, 0x2a, 0x46, 0x00, 0xc0, 0x50, 0x57, 0x67, 0xdf, 0x8d, 0xa5, 0x78, 0x68, 0xbb, 0x6f, 0x8d, 0x41, 0xb6, 0x37, 0x6d, 0x4e, 0x1a, 0x78, 0x71, 0x3b, 0x72, 0xe9, 0x04, 0x84, 0x8b, 0x84, 0xc9, 0x79, 0xce, 0x83, 0xaa, 0x2b, 0xeb, 0x2c, 0x61, 0xa2, 0x8a, 0xc8, 0x91, 0xe4, 0x77, 0x9b, 0x89, 0x4a, 0x46, 0x34, 0x39, 0x74, 0xdb, 0xe4, 0x29, 0x8f, 0x94, 0xf1, 0x2f, 0xf6, 0x58, 0x0d, 0x3f, 0x4f, 0x4e, 0x99, 0x59, 0xda, 0x14, 0x4c, 0x8f, 0xd6, 0x80, 0x7b, 0x6f, 0xb4, 0x71, 0x52, 0xaf, 0x2a, 0x9e, 0xe5, 0x61, 0x75, 0xec, 0x45, 0x21, 0x45, 0x27, 0x72, 0x0b, 0xa3, 0xe2, 0xf0, 0x02, 0xd7, 0xd6, 0xf3, 0xc9, 0xe1, 0x6b, 0xb1, 0xbd, 0x33, 0xda, 0x38, 0xa9, 0x69, 0x4e, 0x27, 0x9d, 0x1c, 0xf4, 0x77, 0x47, 0xc5, 0xa2, 0x31, 0x3d, 0x46, 0xbe, 0x21, 0xd3, 0xdb, 0x41, 0xbe, 0x09, 0x94, 0x2a, 0x6e, 0xfe, 0x7a, 0x21, 0xaa, 0x00, 0x0b, 0x5a, 0xa6, 0x3c, 0xd6, 0x5a, 0xb0, 0xe4, 0x3e, 0x74, 0x5d, 0x82, 0x15, 0x0a, 0x83, 0x58, 0x08, 0x03, 0xb4, 0x32, 0x44, 0x4c, 0xb3, 0x2b, 0xd8, 0x28, 0x3a, 0x6c, 0xca, 0x4d, 0xa9, 0xec, 0x8b, 0x87, 0x0b, 0x1b, 0xd4, 0x17, 0x24, 0x4e, 0xf5, 0x05, 0xd8, 0x37, 0xa7, 0x60, 0xee, 0x8a, 0x17, 0xa8, 0x7b, 0x1e, 0xd2, 0x79, 0xe1, 0x91, 0x39, 0xab, 0x96, 0x5c, 0x63, 0xcc, 0x1c, 0xc9, 0x54, 0xdd, 0xdb, 0xbd, 0x36, 0xec, 0x95, 0x87, 0x4b, 0xb8, 0x43, 0xfe, 0x0f, 0x16, 0xcd, 0x36, 0x55, 0x7c, 0x64, 0xd8, 0x69, 0x88, 0x31, 0x79, 0xc0, 0x53, 0xe5, 0x93, 0x5c, 0x94, 0x66, 0x87, 0xb2, 0x63, 0x39, 0x66, 0x7e, 0x79, 0x3a, 0x0e, 0xbe, 0x66, 0xdb, 0xac, 0x0a, 0xbe, 0xdb, 0x28, 0x69, 0xd9, 0x8e, 0x5c, 0x39, 0xea, 0x6f, 0xec, 0x83, 0x72, 0xee, 0xd7, 0x66, 0x82, 0xce, 0xa4, 0x4e, 0x25, 0x26, 0xbc, 0x8c, 0x4c, 0x19, 0x69, 0x0c, 0x35, 0x39, 0x42, 0x6d, 0xde, 0x7b, 0x23, 0x63, 0x82, 0x4f, 0xfd, 0x1a, 0x02, 0xbf, 0x38, 0x76, 0xc0, 0xb1, 0x4b, 0x85, 0x3f, 0xde, 0x99, 0x7b, 0x8d, 0x13, 0x76, 0x45, 0x77, 0x69, 0x2b, 0x6a, 0xf5, 0x3b, 0x5c, 0x55, 0xbb, 0x25, 0x9d, 0x02, 0xb1, 0xd8, 0xa7, 0xae, 0x89, 0xc6, 0x4d, 0x56, 0x82, 0xb4, 0x86, 0x01, 0x25, 0xc4, 0x25, 0x1e, 0xd4, 0xed, 0x99, 0xc4, 0x46, 0xba, 0xea, 0xb9, 0x6c, 0xa2, 0x49, 0xe4, 0x59, 0xf6, 0xfd, 0x4c, 0x24, 0xcb, 0xc5, 0x38, 0xc9, 0x11, 0xa1, 0x81, 0x1d, 0xae, 0xab, 0x28, 0x5d, 0x61, 0xda, 0xb9, 0x16, 0x5a, 0x9b, 0x9f, 0xda, 0x94, 0xa0, 0xb3, 0x5e, 0x65, 0x6a, 0xd6, 0x64, 0x68, 0x98, 0xb4, 0x4c, 0x82, 0xf0, 0x98, 0xeb, 0xf0, 0x05, 0xb8, 0xbf, 0x3f, 0x61, 0xb2, 0xe3, 0x99, 0x4a, 0x36, 0x52, 0x4b, 0x08, 0x7c, 0x47, 0x55, 0x1e, 0xfa, 0x9d, 0x5f, 0xbe, 0x77, 0xf9, 0x51, 0x81, 0x02, 0x0a, 0x9c, 0x4c, 0xd1, 0xdf, 0x3a, 0xcc, 0x13, 0x6c, 0x1b, 0xd1, 0xbb, 0xd9, 0x30, 0x09, 0x66, 0xb0, 0x2b, 0xdc, 0xe4, 0xcd, 0x0e, 0xb8, 0xf5, 0xf9, 0x2c, 0x0c, 0x7d, 0x26, 0xc0, 0xe4, 0xc1, 0x6f, 0x4f, 0x32, 0xe6, 0x60, 0xbf, 0x5e, 0xae, 0x0b, 0xb3, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xfc, 0x48, 0xcd, 0xae, 0x7c, 0x9e, 0x15, 0x6f, 0x47, 0x49, 0x2b, 0x4d, 0x91, 0xaf, 0x65, 0x47, 0x4d, 0x69, 0x82, 0xc6, 0x41, 0xa3, 0xe8, 0x85, 0xb0, 0x66, 0x08, 0x86, 0xb8, 0x99, 0x25, 0x99, 0xa5, 0xb1, 0xdf, 0x6b, 0x6d, 0x6f, 0xd0, 0x08, 0xfe, 0x3d, 0xee, 0x78, 0xfc, 0xa8, 0xb5, 0x9a, 0x9b, 0xb8, 0x8e, 0x8e, 0x20, 0x89, 0x9a, 0x5c, 0x4e, 0x4f, 0x9e, 0x4f, 0x22, 0xc9, 0x77, 0xe9, 0xa7, 0x9c, 0x68, 0xaf, 0xe2, 0xcc, 0xf6, 0xa0, 0x3b, 0x71, 0x83, 0xdd, 0x64, 0xc4, 0xf2, 0x9a, 0x17, 0x98, 0x44, 0xc6, 0xa8, 0x9a, 0xe0, 0xcd, 0x0c, 0x64, 0xd8, 0x34, 0x90, 0x43, 0x16, 0x5a, 0x65, 0x25, 0x4d, 0x14, 0x1b, 0x53, 0x03, 0x24, 0x89, 0xf8, 0x00, 0x3b, 0xfe, 0xe8, 0x91, 0xf9, 0x4e, 0x4d, 0x8a, 0xb6, 0x4d, 0xb8, 0x81, 0xa9, 0x13, 0x53, 0x9f, 0x6a, 0xd3, 0xeb, 0xa2, 0x5c, 0x17, 0xcc, 0x7a, 0x53, 0xd5, 0xad, 0x44, 0x3a, 0xa5, 0xd7, 0x2e, 0x45, 0xe5, 0xff, 0x55, 0x79, 0xc8, 0x99, 0x2e, 0x4c, 0x6f, 0x70, 0xc8, 0xf1, 0x8b, 0x02, 0xe5, 0x19, 0x38, 0xcc, 0xb7, 0xe4, 0x12, 0xac, 0x53, 0x07, 0x93, 0xd0, 0x69, 0xd2, 0xb1, 0x5c, 0x79, 0x8f, 0xbc, 0xcb, 0x19, 0x2f, 0xe0, 0x11, 0xea, 0xb2, 0x6f, 0x0a, 0x0e, 0x53, 0x2d, 0xc1, 0x05, 0x09, 0x67, 0xd7, 0x58, 0xed, 0x1c, 0xf2, 0xc4, 0x20, 0x6d, 0x12, 0x75, 0x71, 0xc6, 0xeb, 0xe7, 0x51, 0x81, 0x08, 0x4a, 0x95, 0x24, 0x55, 0xd8, 0x7a, 0x26, 0x79, 0xe6, 0x76, 0x26, 0x63, 0x1c, 0x10, 0xeb, 0xab, 0xba, 0x2a, 0x92, 0x35, 0xc8, 0x0f, 0x2c, 0x2b, 0x6f, 0x76, 0x45, 0xf1, 0x30, 0x07, 0x11, 0xc1, 0x8c, 0xb2, 0xe4, 0x44, 0x81, 0x20, 0xaa, 0xab, 0xc4, 0xa6, 0x31, 0xe7, 0x6f, 0xd8, 0x1f, 0x92, 0x2b, 0x57, 0xe3, 0xbe, 0x66, 0xe0, 0xe1, 0xa2, 0x76, 0x13, 0xb0, 0x6d, 0xfa, 0x36, 0x9f, 0x6d, 0x35, 0x14, 0xd3, 0xf0, 0xc3, 0x94, 0x9e, 0x88, 0x52, 0x1d, 0x41, 0xff, 0xe1, 0xc3, 0xa7, 0xd7, 0xa8, 0x6e, 0x3c, 0xf5, 0xba, 0xc8, 0xca, 0x4c, 0x06, 0x0c, 0x26, 0xbc, 0x0e, 0xda, 0xc0, 0x3c, 0x49, 0x65, 0x09, 0x64, 0x0a, 0x1e, 0xaf, 0x35, 0x32, 0x03, 0x04, 0x7d, 0x85, 0xc3, 0xd5, 0x87, 0x22, 0x3a, 0x1f, 0xe0, 0xd2, 0x18, 0xfc, 0x5f, 0xc5, 0xa9, 0x1c, 0x6f, 0x6f, 0x54, 0x6c, 0x6b, 0xd5, 0x3f, 0xa7, 0x7a, 0x09, 0x22, 0xad, 0x88, 0x4a, 0xf1, 0x16, 0xae, 0x17, 0x38, 0x6d, 0x9a, 0x89, 0x4f, 0x6b, 0xb7, 0x23, 0x80, 0x4e, 0x1a, 0x53, 0x80, 0x25, 0xd9, 0xc8, 0x1b, 0xb5, 0xf3, 0x8a, 0xb7, 0x5f, 0x1e, 0x5f, 0xa7, 0x86, 0xf7, 0x1f, 0x43, 0x17, 0xfd, 0xb5, 0x49, 0x27, 0x91, 0x6f, 0xa9, 0x03, 0x2d, 0x94, 0x35, 0x9d, 0x64, 0x53, 0x2a, 0x44, 0xce, 0x4d, 0x47, 0x8e, 0x7a, 0xf9, 0x8e, 0x22, 0x19, 0xb4, 0xe8, 0x59, 0x54, 0x1b, 0xa5, 0x75, 0xda, 0xdc, 0xc0, 0x16, 0xf4, 0x45, 0x75, 0x83, 0xe7, 0x31, 0xc0, 0x3f, 0x1b, 0x50, 0x0f, 0xae, 0x33, 0x46, 0x29, 0xf2, 0x1d, 0xb2, 0xcf, 0xe8, 0xb0, 0x2d, 0x7e, 0x73, 0x3d, 0xfd, 0x7d, 0xdc, 0x85, 0x7a, 0x8f, 0x8d, 0xbc, 0x14, 0x60, 0xaf, 0xcf, 0x44, 0x48, 0x6f, 0xd3, 0x05, 0x5d, 0x37, 0x77, 0x81, 0xd5, 0x63, 0xb5, 0x31, 0x72, 0xc0, 0xd4, 0x3e, 0x79, 0x56, 0x9c, 0x2d, 0xb4, 0xe9, 0xed, 0xa6, 0x00, 0x87, 0x47, 0x26, 0xf9, 0x1c, 0x0b, 0xe1, 0xce, 0x0b, 0xaa, 0x20, 0x71, 0x6c, 0x80, 0x88, 0x65, 0x17, 0x9b, 0x06, 0x66, 0x25, 0xcc, 0x57, 0x55, 0x3d, 0x7c, 0x4a, 0x8a, 0xde, 0x6d, 0xe1, 0x53, 0x46, 0x8d, 0xd7, 0xaa, 0xb5, 0x7d, 0xd8, 0x4a, 0x85, 0xe0, 0x2a, 0x1d, 0x1c, 0xab, 0x72, 0x10, 0xd8, 0x83, 0x77, 0x36, 0xe9, 0xab, 0x7a, 0xe8, 0xc3, 0xd3, 0xf7, 0x9f, 0xb1, 0xc6, 0x36, 0x3b, 0x8c, 0x78, 0x80, 0x04, 0x4f, 0x29, 0x33, 0x77, 0x17, 0xa3, 0x68, 0xf4, 0x00, 0x37, 0xf1, 0x11, 0x20, 0x14, 0x61, 0x90, 0x70, 0xe7, 0xaf, 0xd4, 0x1b, 0xc4, 0x3d, 0x84, 0x47, 0x35, 0xfa, 0x11, 0xc1, 0x8a, 0xe6, 0x8c, 0x9b, 0x8b, 0x07, 0xbd, 0xd4, 0x8b, 0x99, 0x94, 0xeb, 0x85, 0x28, 0x90, 0x36, 0x8d, 0x4a, 0x86, 0xad, 0xbd, 0xf1, 0x33, 0x95, 0xd4, 0x99, 0xfa, 0xa2, 0x3f, 0x03, 0x56, 0xb5, 0x72, 0x30, 0xcb, 0x4d, 0x39, 0x91, 0x64, 0x47, 0xa9, 0xa8, 0xa3, 0x0a, 0x3f, 0x91, 0xac, 0xd7, 0xca, 0xaf, 0x7b, 0x46, 0x9d, 0x9d, 0xc3, 0xa3, 0x28, 0x23, 0x9f, 0x01, 0xae, 0xf8, 0x07, 0xec, 0x79, 0xa0, 0x4f, 0x97, 0x25, 0x85, 0xa3, 0x39, 0xae, 0x0e, 0x37, 0xc1, 0x32, 0x98, 0xae, 0xb5, 0xa2, 0x5a, 0x11, 0xa6, 0xd3, 0x38, 0x90, 0xda, 0x92, 0xc2, 0x2b, 0x7c, 0xd0, 0xd2, 0xf3, 0xe4, 0x23, 0x11, 0x9b, 0x7b, 0x0f, 0x61, 0xfa, 0xe3, 0x4b, 0x4c, 0xbb, 0x0a, 0x4c, 0x93, 0x60, 0x15, 0x51, 0x0d, 0x76, 0x41, 0x37, 0x39, 0x02, 0x21, 0x80, 0x3b, 0x04, 0xc5, 0x1b, 0xf0, 0x97, 0x2e, 0x17, 0x5f, 0xf4, 0xa0, 0x19, 0x55, 0xf3, 0x2f, 0xe8, 0x53, 0x07, 0xae, 0xdf, 0x98, 0xe4, 0xc3, 0xd0, 0xe9, 0x32, 0x0e, 0x34, 0x00, 0x3b, 0x7d, 0xe8, 0x1b, 0xb6, 0x9d, 0x60, 0x67, 0xff, 0x57, 0xa4, 0x04, 0x32, 0x14, 0x95, 0xe5, 0xa5, 0x06, 0xb0, 0xd4, 0x94, 0xb2, 0xc1, 0xe4, 0x5b, 0x14, 0x44, 0x7d, 0xee, 0x01, 0x81, 0xe2, 0x2d, 0xfb, 0x01, 0x39, 0x41, 0xdf, 0x6b, 0xd8, 0x16, 0x80, 0x4c, 0x9c, 0xdf, 0x5c, 0x8a, 0x75, 0xf6, 0x99, 0xcb, 0xcc, 0xf3, 0x39, 0x78, 0x31, 0xfe, 0x8c, 0xe8, 0x74, 0x02, 0xa3, 0x44, 0x36, 0xf8, 0x20, 0xcf, 0x3a, 0x97, 0xcd, 0x18, 0xec, 0xda, 0xac, 0x4c, 0xbe, 0xe6, 0x03, 0xf3, 0xe2, 0x4b, 0x62, 0x93, 0x0e, 0xc5, 0xb9, 0x17, 0x33, 0xc9, 0xa1, 0xef, 0x2c, 0x7e, 0x02, 0x41, 0xec, 0xe8, 0x8e, 0x86, 0x71, 0xd3, 0xe7, 0xad, 0x42, 0x5e, 0x37, 0xb0, 0x05, 0xba, 0x8c, 0xdd, 0xb1, 0x35, 0x66, 0xe8, 0xf5, 0x41, 0x22, 0xd0, 0x1b, 0xd5, 0x38, 0x85, 0x5d, 0xc0, 0xa1, 0x79, 0x1e, 0x29, 0x9c, 0x1c, 0xc3, 0x2d, 0x4b, 0x42, 0xe7, 0xe6, 0xcc, 0x0b, 0x19, 0xc1, 0xa4, 0x4b, 0xb2, 0x74, 0x4c, 0xf7, 0x07, 0x52, 0x34, 0x9d, 0xf4, 0x69, 0xd7, 0xd1, 0x80, 0xe5, 0xfb, 0x41, 0xf9, 0x08, 0xef, 0x03, 0x26, 0xd8, 0xf4, 0xd5, 0xf0, 0x25, 0xcf, 0x90, 0xb4, 0x39, 0x39, 0x55, 0x6b, 0x2c, 0x1b, 0x16, 0xde, 0x81, 0xce, 0x4f, 0x39, 0x65, 0xad, 0xee, 0x69, 0x7c, 0xd0, 0xf9, 0x10, 0xcd, 0x94, 0xf5, 0x5c, 0xa9, 0x6c, 0xb4, 0x60, 0x1f, 0xbf, 0x64, 0x18, 0x43, 0x7e, 0xf2, 0x22, 0xcc, 0x9d, 0xde, 0x21, 0xbb, 0xcb, 0x2c, 0xdf, 0xb4, 0xda, 0xb6, 0xb8, 0xe1, 0x79, 0x02, 0xb2, 0x06, 0xcb, 0xec, 0xf0, 0x6d, 0x38, 0xf5, 0x99, 0xe6, 0x72, 0xce, 0x68, 0x79, 0x15, 0x90, 0x35, 0x6b, 0xdd, 0x51, 0x31, 0x21, 0xcd, 0x3b, 0x2d, 0x54, 0x0d, 0x49, 0x80, 0xa1, 0x22, 0x70, 0x50, 0x4c, 0x97, 0xd2, 0xef, 0x68, 0xaa, 0x65, 0xa9, 0x0f, 0x1c, 0x46, 0xcf, 0x55, 0x15, 0xbc, 0x60, 0x93, 0xd9, 0xbc, 0xd7, 0x53, 0xef, 0xdb, 0xbf, 0x9d, 0xc1, 0xc9, 0xac, 0xa3, 0x88, 0x23, 0x59, 0xff, 0x78, 0xb0, 0x6b, 0x6d, 0x7e, 0x31, 0xf3, 0xc2, 0xf0, 0x02, 0xc6, 0xa6, 0x5a, 0x1b, 0x97, 0xc3, 0x77, 0xa6, 0xb9, 0x0b, 0xdd, 0x3b, 0x70, 0x6b, 0x20, 0xbd, 0xd7, 0xdb, 0x2d, 0xd6, 0x28, 0xc8, 0x1c, 0x09, 0x8d, 0x43, 0x4f, 0xb5, 0x5b, 0x56, 0xb0, 0x57, 0x67, 0xf2, 0xb4, 0x01, 0xa4, 0x13, 0x9f, 0x24, 0xf0, 0x93, 0x7a, 0x9d, 0xbe, 0xdf, 0xfa, 0xcc, 0x60, 0x4d, 0xdf, 0x8e, 0xeb, 0x43, 0x48, 0x82, 0x2a, 0x38, 0xec, 0xdd, 0x4a, 0x0e, 0x2e, 0x34, 0x14, 0x13, 0xee, 0x52, 0x39, 0x04, 0x48, 0x82, 0x6d, 0x03, 0xa0, 0xac, 0xef, 0x3b, 0x3d, 0x37, 0x58, 0x4d, 0x9c, 0x2a, 0xb1, 0x88, 0x38, 0x1b, 0x1f, 0xfc, 0x97, 0x40, 0xa5, 0xfe, 0xdb, 0xc0, 0x51, 0x2a, 0x96, 0x77, 0xa3, 0x4d, 0xec, 0x61, 0xe4, 0x27, 0x21, 0x30, 0x45, 0x70, 0xd9, 0x67, 0xae, 0x62, 0xb2, 0xba, 0x98, 0xf1, 0x4d, 0x4b, 0x70, 0x88, 0x21, 0x03, 0x98, 0xc8, 0x4d, 0xf8, 0xe6, 0x00, 0x64, 0x34, 0x9b, 0x80, 0x87, 0xc9, 0x48, 0xa2, 0x5c, 0x18, 0x1e, 0xd9, 0x40, 0xfc, 0x27, 0x8f, 0xbf, 0x9f, 0x9a, 0xe6, 0x83, 0x8d, 0xcb, 0xde, 0x18, 0x66, 0x13, 0xa9, 0x7b, 0x86, 0x22, 0x90, 0x8f, 0x0a, 0xbd, 0x1d, 0x07, 0x08, 0xe6, 0x03, 0x26, 0x08, 0xa3, 0x4b, 0xcd, 0xf9, 0xf1, 0x8b, 0x67, 0xd0, 0xe6, 0x81, 0xa9, 0xbd, 0x71, 0x88, 0x66, 0xe4, 0xef, 0xe8, 0xd6, 0x3e, 0xb4, 0x33, 0x0e, 0x48, 0x8f, 0x1f, 0x12, 0x86, 0x92, 0xa1, 0xc8, 0x13, 0x42, 0xe0, 0xe3, 0x3c, 0xb7, 0x68, 0x9f, 0x56, 0xc9, 0xe2, 0xd1, 0x40, 0xa5, 0x40, 0x27, 0xa6, 0xce, 0x2b, 0x63, 0xa7, 0xd7, 0xeb, 0xdf, 0x16, 0xa2, 0xb6, 0xe0, 0xfa, 0x78, 0x77, 0xae, 0x9f, 0x46, 0xe7, 0x63, 0xe2, 0x19, 0xd0, 0xa9, 0x00, 0x2b, 0x5d, 0xb0, 0x5e, 0x8a, 0x1e, 0xd9, 0xaa, 0x57, 0x03, 0x92, 0x52, 0x30, 0xda, 0x8e, 0xba, 0x0c, 0x61, 0xbe, 0x1c, 0xbf, 0x99, 0xf2, 0x08, 0x24, 0x57, 0xd3, 0x81, 0x48, 0xc6, 0x7d, 0x36, 0x8b, 0xd1, 0x9c, 0xa8, 0x21, 0x91, 0xdb, 0xf0, 0x2c, 0x44, 0x61, 0x82, 0x25, 0x11, 0x08, 0xcc, 0x67, 0xe3, 0x71, 0x58, 0xf3, 0x09, 0xd5, 0x66, 0x99, 0x6d, 0x4e, 0x97, 0x20, 0x50, 0x90, 0x91, 0xbd, 0x79, 0x5b, 0x7e, 0x32, 0xab, 0x65, 0x1d, 0x2d, 0xdc, 0xda, 0x4c, 0x0c, 0x5c, 0xae, 0xbd, 0xa2, 0x61, 0xc9, 0xb4, 0x5f, 0x7e, 0x65, 0x8b, 0xc3, 0xc2, 0xdd, 0x60, 0xf0, 0x8d, 0xec, 0x83, 0x6f, 0x3e, 0x33, 0x5b, 0x76, 0x2d, 0x28, 0x19, 0xe1, 0xb9, 0x60, 0x6c, 0x1d, 0xcb, 0x3a, 0x68, 0x94, 0x5a, 0xca, 0xc8, 0xa4, 0x11, 0x03, 0x6d, 0xcb, 0x20, 0x68, 0x70, 0x26, 0x60, 0xdb, 0xc4, 0x6c, 0xe8, 0x57, 0xa7, 0xcb, 0xea, 0x86, 0xb6, 0xb4, 0x7b, 0x48, 0xe9, 0xfc, 0x06, 0x87, 0x1c, 0x38, 0xe9, 0xdb, 0x93, 0xd0, 0x69, 0xd8, 0xee, 0x0c, 0x2c, 0x80, 0x67, 0x3f, 0xcc, 0xbf, 0xd1, 0x49, 0x9f, 0x04, 0x30, 0x1a, 0x33, 0xd8, 0x31, 0x50, 0x0c, 0x26, 0x66, 0xd6, 0x02, 0x47, 0x56, 0xdb, 0x8f, 0x55, 0x9d, 0x53, 0xf2, 0x4b, 0x96, 0x46, 0xa1, 0x8b, 0xf0, 0x9f, 0xcf, 0xf4, 0x6a, 0x0f, 0x0b, 0x98, 0xd9, 0x9d, 0xa7, 0xbb, 0xe3, 0xd5, 0xc0, 0xbc, 0x0b, 0xc3, 0xcb, 0x5a, 0x87, 0x17, 0xf6, 0xe2, 0x86, 0xad, 0x00, 0x27, 0x4d, 0xe3, 0x63, 0x72, 0x7d, 0x7f, 0xe8, 0xec, 0xf6, 0x7e, 0xbe, 0x57, 0x60, 0x16, 0xd7, 0xa8, 0x03, 0x7d, 0x0d, 0xbf, 0xc3, 0x1e, 0x0d, 0xd6, 0x54, 0x79, 0x2b, 0xe5, 0xc1, 0x59, 0x51, 0xcd, 0x16, 0xf4, 0x97, 0xd2, 0x09, 0xe6, 0x90, 0xff, 0x43, 0x8b, 0x25, 0x35, 0xd1, 0xb3, 0x53, 0x34, 0x68, 0xcf, 0x9d, 0xde, 0x13, 0x38, 0xca, 0x3a, 0x2a, 0xd2, 0xd8, 0xd1, 0xf4, 0xb6, 0x7d, 0x1b, 0x38, 0x9b, 0x37, 0xcb, 0xd5, 0xb8, 0x00, 0x09, 0xc4, 0x0b, 0x6b, 0x46, 0x2e, 0x63, 0x85, 0x72, 0x37, 0xce, 0x25, 0x1f, 0xf3, 0xfa, 0x2b, 0xd1, 0xf7, 0x73, 0x3d, 0xa0, 0xec, 0x8e, 0x7d, 0xe8, 0x0f, 0x44, 0x07, 0x89, 0xf6, 0xc0, 0xca, 0xb6, 0xaf, 0x39, 0x94, 0x78, 0x3d, 0xfe, 0x92, 0x26, 0xf1, 0x88, 0x78, 0x7f, 0xf8, 0x37, 0x3f, 0x5a, 0x4c, 0x62, 0xfe, 0x5d, 0x4a, 0xa8, 0xd1, 0x5e, 0xfd, 0xc3, 0x92, 0x78, 0x53, 0x87, 0x31, 0xbd, 0x44, 0x87, 0x7e, 0xac, 0x59, 0x86, 0x93, 0x23, 0x48, 0xad, 0x57, 0x31, 0x1c, 0x60, 0x24, 0xef, 0x13, 0x07, 0xa0, 0x01, 0x0f, 0xb3, 0xc7, 0x81, 0x62, 0xfa, 0x26, 0xfb, 0x6e, 0x1b, 0xd6, 0x9e, 0x1f, 0xbe, 0x0e, 0x38, 0x1d, 0x11, 0xb3, 0xbc, 0x24, 0x76, 0x9d, 0x57, 0xd6, 0xb1, 0x27, 0x04, 0xbd, 0x54, 0x15, 0x57, 0xa1, 0xb3, 0x38, 0x11, 0x0f, 0x2f, 0x40, 0x42, 0xdc, 0x8d, 0x88, 0x9f, 0xbb, 0x6b, 0xf1, 0x02, 0x4c, 0x11, 0x29, 0xa7, 0xb9, 0xc3, 0x19, 0xc1, 0xeb, 0x3d, 0x8a, 0xa9, 0x7a, 0x02, 0x02, 0x4f, 0xe8, 0xa6, 0x39, 0xbb, 0xa7, 0x80, 0xa5, 0xfb, 0x1e, 0x05, 0x04, 0x8f, 0x56, 0xba, 0xb1, 0x85, 0xf0, 0xc0, 0xd9, 0xe7, 0xf2, 0xf7, 0x15, 0x85, 0x0f, 0x84, 0x0e, 0x31, 0x24, 0xae, 0x19, 0xc5, 0xaa, 0xfb, 0xf2, 0x80, 0x8d, 0x7e, 0x1e, 0x00, 0xd3, 0x89, 0xac, 0x0a, 0x9c, 0x29, 0xfe, 0xe3, 0xc2, 0x8b, 0xa4, 0xb9, 0x19, 0xc9, 0xf7, 0x37, 0x4e, 0x60, 0xb6, 0x93, 0x20, 0xf8, 0xcf, 0xf9, 0xe1, 0xbf, 0x25, 0x33, 0x67, 0xa9, 0xd4, 0xce, 0xc5, 0x74, 0xdd, 0xbf, 0x62, 0xfa, 0xfd, 0x5f, 0x25, 0xcc, 0x36, 0x30, 0x28, 0xf1, 0x4c, 0x67, 0x31, 0x2d, 0x1e, 0xc0, 0x93, 0x0d, 0x2e, 0xb1, 0xfe, 0xe1, 0xf7, 0x17, 0xe8, 0x55, 0x83, 0x24, 0x94, 0x44, 0xe1, 0xaf, 0xa5, 0xa4, 0x90, 0x42, 0x93, 0x16, 0x7c, 0xdf, 0x7b, 0xc7, 0x03, 0x5a, 0x61, 0x1c, 0x04, 0xdd, 0xf7, 0x7a, 0xf6, 0xc7, 0xe9, 0xa7, 0x9e, 0x94, 0xe8, 0x36, 0x6e, 0x4d, 0x75, 0xa7, 0x60, 0x68, 0x28, 0x5d, 0x27, 0xce, 0xc5, 0x79, 0x88, 0x13, 0xfc, 0x88, 0x6a, 0x16, 0x3e, 0x02, 0x49, 0xed, 0x8f, 0x32, 0x0e, 0x80, 0x7e, 0x19, 0x7d, 0xcf, 0xbe, 0x84, 0xcb, 0x4b, 0xfd, 0x8d, 0xba, 0x85, 0x75, 0x10, 0x87, 0x6a, 0xd5, 0xfd, 0xe1, 0x54, 0xd4, 0x8f, 0xaf, 0x0c, 0xf7, 0x37, 0x04, 0x19, 0xbe, 0xfa, 0x3e, 0xf8, 0xb4, 0x14, 0x73, 0x7c, 0x33, 0x99, 0xdf, 0xe7, 0x09, 0xb4, 0x3f, 0x2d, 0x1f, 0x6f, 0x5b, 0x40, 0xa0, 0x6b, 0x6d, 0x9b, 0x51, 0xac, 0xa9, 0x96, 0x65, 0x13, 0xbf, 0xf2, 0xb5, 0xe3, 0xab, 0xc5, 0xe2, 0xa8, 0x3a, 0x4c, 0x38, 0xf6, 0x30, 0xd4, 0xfe, 0xf9, 0x59, 0x66, 0x74, 0x8a, 0x99, 0xa3, 0xa2, 0x7c, 0x81, 0xae, 0xbc, 0xa3, 0xdc, 0x16, 0xe4, 0xd4, 0xa4, 0x3b, 0x62, 0x9f, 0x56, 0xb2, 0x21, 0xee, 0xb0, 0x86, 0xe7, 0xae, 0xa8, 0x4c, 0x09, 0x4a, 0xc4, 0x52, 0x92, 0x73, 0x2e, 0xa2, 0x5d, 0x8b, 0x38, 0x76, 0xcc, 0x43, 0xe1, 0x1a, 0xbf, 0x9e, 0x89, 0x82, 0xd6, 0x5c, 0x5f, 0x4c, 0x00, 0xb7, 0xd5, 0x56, 0x72, 0x63, 0x64, 0xca, 0xde, 0xaf, 0x58, 0xe9, 0xf9, 0x3f, 0x17, 0x3a, 0xa4, 0x49, 0x0a, 0x21, 0xea, 0x9f, 0x4a, 0xbf, 0xe9, 0x4e, 0x82, 0x43, 0x29, 0xbc, 0x70, 0xad, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0xc6, 0xd7, 0x7c, 0xc0, 0xcb, 0xc4, 0x09, 0x67, 0x85, 0xf1, 0x8e, 0x19, 0x9c, 0xef, 0x6b, 0xd4, 0xab, 0x9e, 0x39, 0xa2, 0xa3, 0x6d, 0x71, 0xea, 0x44, 0x1e, 0x39, 0xc7, 0x45, 0x57, 0xe2, 0x12, 0xe8, 0x75, 0x6f, 0x3b, 0xc0, 0x3e, 0x83, 0x3b, 0x76, 0x00, 0x4f, 0x11, 0x4e, 0xdd, 0x59, 0x7e, 0xb3, 0xaf, 0x36, 0x2f, 0x90, 0xfb, 0xb5, 0x14, 0x64, 0x62, 0xb8, 0x40, 0x6e, 0xfa, 0xb3, 0xee, 0x58, 0xdf, 0xb8, 0xfe, 0xcb, 0x89, 0x65, 0x04, 0x03, 0xca, 0x5c, 0xc5, 0xf8, 0x39, 0x08, 0xb6, 0x4a, 0xe1, 0xc4, 0x1a, 0x92, 0xf3, 0x4b, 0x47, 0xcf, 0x00, 0xcd, 0xfd, 0xce, 0xb4, 0x7a, 0x25, 0x31, 0x43, 0xc4, 0xf9, 0x9d, 0x9b, 0x62, 0x30, 0x25, 0xae, 0xcb, 0x56, 0x8a, 0xd0, 0xa9, 0x47, 0x3e, 0x29, 0x3b, 0x0f, 0x69, 0x80, 0xb9, 0x4b, 0x7f, 0x52, 0xa3, 0xa6, 0x62, 0xe8, 0xa8, 0x62, 0x2f, 0x64, 0x82, 0xad, 0xba, 0x18, 0x75, 0xcb, 0x09, 0x68, 0x96, 0x65, 0x48, 0xae, 0x2c, 0xfb, 0x88, 0xb9, 0x0c, 0x07, 0x9a, 0x4c, 0x3b, 0x85, 0xe8, 0x30, 0xce, 0xbb, 0xce, 0x24, 0x9d, 0x6d, 0xcb, 0xd9, 0xed, 0x05, 0x22, 0xb0, 0x65, 0x62, 0x45, 0x28, 0xb9, 0xcf, 0x2d, 0x06, 0xe3, 0xef, 0x93, 0x42, 0x68, 0xa5, 0xce, 0xe3, 0x57, 0x6a, 0x8b, 0x69, 0x7e, 0x0e, 0xeb, 0x12, 0x6a, 0x1f, 0xf5, 0xf4, 0x8a, 0x96, 0x5a, 0x4f, 0x82, 0xbf, 0x1c, 0x5c, 0xb0, 0xdb, 0xad, 0x21, 0x28, 0xd4, 0x05, 0x70, 0xf6, 0x14, 0x7d, 0x6b, 0xa0, 0xca, 0x22, 0xd2, 0xbb, 0x55, 0x4c, 0x52, 0x84, 0xc9, 0x8c, 0xe3, 0x59, 0xb3, 0x1b, 0xd9, 0x0e, 0x87, 0x2e, 0x6e, 0x49, 0x27, 0xc9, 0xe4, 0xbf, 0xb2, 0x2e, 0xa7, 0xde, 0x16, 0xc6, 0x62, 0x9b, 0x72, 0xbe, 0x11, 0x20, 0x38, 0xaa, 0x35, 0x54, 0xc1, 0xc6, 0x5f, 0xd2, 0x4d, 0x42, 0xce, 0xc4, 0xe6, 0xf1, 0x81, 0x11, 0x59, 0x8d, 0x0c, 0x43, 0x35, 0x7f, 0x3f, 0x74, 0xc2, 0xdd, 0x05, 0x12, 0x8b, 0x05, 0x40, 0xd8, 0x74, 0x9d, 0x5b, 0x45, 0x36, 0xff, 0x6e, 0xe9, 0xb7, 0xa2, 0x9e, 0x95, 0x95, 0x41, 0xef, 0x2c, 0x39, 0xc5, 0x78, 0xe8, 0xe5, 0x9d, 0x54, 0x68, 0x78, 0x7f, 0x81, 0xb1, 0x40, 0xbf, 0x7c, 0x11, 0xb0, 0x41, 0xbf, 0xaa, 0x04, 0x71, 0x34, 0x2a, 0x72, 0x30, 0xbb, 0x75, 0xb1, 0xf5, 0x0c, 0x07, 0x9b, 0xa7, 0xab, 0xd0, 0x38, 0x80, 0x9e, 0x64, 0xb1, 0x19, 0x70, 0x6f, 0xa9, 0x45, 0x66, 0x1b, 0x41, 0xd6, 0xd9, 0x73, 0xb3, 0xa6, 0xfb, 0x2e, 0x62, 0xf6, 0x18, 0x1d, 0x38, 0x63, 0x07, 0x68, 0x5e, 0xbf, 0x14, 0xa4, 0xec, 0x4e, 0x47, 0x49, 0xa7, 0x8c, 0xdf, 0x32, 0xfb, 0xec, 0x80, 0xca, 0xbd, 0xfc, 0xc3, 0x70, 0xac, 0x7c, 0x5e, 0x6a, 0xc9, 0x67, 0x4b, 0x7a, 0x07, 0xd5, 0xcd, 0xef, 0xba, 0x70, 0x73, 0x5b, 0x66, 0x75, 0xd9, 0x85, 0xfe, 0x72, 0x65, 0xcb, 0x74, 0xe8, 0x14, 0xde, 0xf1, 0xb9, 0xe7, 0xac, 0x6e, 0x2f, 0xc9, 0x8f, 0x94, 0x06, 0x05, 0x9d, 0x48, 0xe7, 0xc6, 0x96, 0xeb, 0x8c, 0x44, 0x84, 0x91, 0x14, 0xa8, 0x76, 0x4c, 0x4f, 0x78, 0xba, 0x9a, 0x18, 0x23, 0x56, 0x86, 0xb9, 0x38, 0xd9, 0x62, 0xeb, 0x36, 0x5b, 0xbf, 0x1b, 0x9a, 0xb6, 0x6d, 0x8a, 0x0a, 0x69, 0x33, 0x87, 0x1e, 0xbd, 0x07, 0x85, 0xcc, 0x86, 0xa0, 0x17, 0x2b, 0x00, 0x51, 0x19, 0x93, 0xbd, 0x64, 0x25, 0x47, 0xad, 0x5d, 0xa1, 0xbd, 0x84, 0xe6, 0xf5, 0xaa, 0x1d, 0x61, 0x19, 0x49, 0x9a, 0x59, 0xdc, 0x40, 0xd7, 0xf7, 0x4e, 0xda, 0x0a, 0x93, 0x39, 0x0f, 0x39, 0xdd, 0x54, 0x95, 0x14, 0x30, 0xcb, 0x25, 0xdd, 0x63, 0xf5, 0x00, 0xf2, 0x75, 0x33, 0x5c, 0xea, 0x7a, 0xac, 0x20, 0xb2, 0xfe, 0xab, 0x94, 0x76, 0x7d, 0x0b, 0x15, 0x67, 0x3a, 0x4c, 0x1e, 0xa8, 0x3d, 0x75, 0x0d, 0x31, 0xcb, 0xdf, 0xe2, 0xa7, 0xa3, 0xff, 0xb1, 0x95, 0x14, 0x42, 0x3b, 0x53, 0xba, 0x1b, 0xb4, 0xcd, 0x97, 0x02, 0xc3, 0x63, 0x91, 0x7d, 0x91, 0xa0, 0xbf, 0xca, 0x29, 0xd5, 0xfb, 0xc0, 0x54, 0x49, 0x1c, 0xff, 0x15, 0x65, 0xff, 0xda, 0x36, 0x66, 0xe6, 0x66, 0xbc, 0x1e, 0x29, 0x46, 0xca, 0xf2, 0xaf, 0x93, 0xb8, 0x82, 0xb4, 0xdc, 0x43, 0x05, 0xf1, 0xd9, 0x71, 0x3f, 0x18, 0x75, 0xb6, 0x5f, 0x12, 0x7f, 0x2e, 0x35, 0xf7, 0xf8, 0x50, 0xf3, 0xa8, 0xd7, 0xc4, 0xad, 0xa6, 0x11, 0x6d, 0x83, 0xe5, 0xe1, 0x14, 0x3a, 0x33, 0x32, 0xb0, 0x17, 0x36, 0xd9, 0xcd, 0x98, 0x05, 0x81, 0x13, 0xcd, 0x87, 0x37, 0x87, 0x48, 0x3e, 0x12, 0x61, 0xea, 0x09, 0x7e, 0x93, 0x4d, 0x10, 0x98, 0x6d, 0xa6, 0x6f, 0x87, 0x4f, 0x7c, 0x05, 0x41, 0x7e, 0x3d, 0x6b, 0xc8, 0x5b, 0x63, 0xdb, 0x34, 0x2b, 0xc4, 0xc9, 0x2a, 0x25, 0xbe, 0x03, 0x67, 0x0c, 0xd4, 0xc8, 0x95, 0x3e, 0xb5, 0xf5, 0xf4, 0xc2, 0xa7, 0xce, 0x23, 0xd9, 0x0c, 0x0f, 0x73, 0xc3, 0x19, 0xeb, 0xae, 0x18, 0x47, 0x4c, 0xdc, 0xba, 0x7e, 0xdd, 0x5a, 0xec, 0x8f, 0x41, 0xef, 0x8d, 0xf9, 0x7f, 0x23, 0x88, 0xf2, 0xda, 0x3e, 0x8e, 0x78, 0xef, 0xeb, 0x46, 0xae, 0xea, 0xa6, 0x86, 0x49, 0x6e, 0x01, 0x37, 0xf8, 0x83, 0x9c, 0x21, 0x73, 0x68, 0xff, 0x29, 0x3e, 0xf4, 0x65, 0xab, 0x22, 0x15, 0xdf, 0x5f, 0xfb, 0x53, 0x98, 0xec, 0xee, 0xe5, 0x6e, 0xa5, 0xae, 0x1f, 0xac, 0x58, 0x3e, 0x1a, 0x53, 0xc3, 0x3a, 0xa8, 0x72, 0xf2, 0x53, 0xf9, 0x37, 0x7d, 0xba, 0xec, 0xb3, 0x91, 0xfd, 0x05, 0xd2, 0x5c, 0xb9, 0x24, 0xb1, 0xf6, 0xe5, 0xb1, 0x1f, 0xc8, 0x27, 0x2d, 0xa7, 0x49, 0x77, 0xfd, 0xe2, 0x39, 0x37, 0x48, 0x5b, 0x70, 0x28, 0x85, 0x83, 0x12, 0xdf, 0xbc, 0xb0, 0x65, 0x05, 0x02, 0xa1, 0x73, 0x71, 0x0e, 0xd3, 0x8f, 0x3a, 0x27, 0xb8, 0xc4, 0x5b, 0xe4, 0xb1, 0x12, 0x18, 0xa8, 0x6c, 0xc8, 0x17, 0xe2, 0x13, 0xf1, 0x51, 0x49, 0xb4, 0x0f, 0xb7, 0xf5, 0x88, 0x94, 0x20, 0xc2, 0x8e, 0x95, 0xff, 0xd6, 0xfd, 0xda, 0x4f, 0xda, 0x19, 0xa9, 0x59, 0x49, 0x71, 0x01, 0xf5, 0x5c, 0xdf, 0x2c, 0x93, 0xb5, 0x7f, 0xd7, 0x42, 0x57, 0x6e, 0x76, 0xd8, 0x03, 0x5e, 0xd6, 0xfa, 0x0e, 0x79, 0xa2, 0x91, 0xbd, 0xb0, 0xda, 0x6b, 0xa7, 0x38, 0xfc, 0x95, 0x39, 0x8b, 0xf0, 0x7f, 0xfe, 0xb9, 0x5a, 0xbb, 0x5d, 0xa2, 0x8e, 0x3a, 0xd1, 0x6e, 0x60, 0xb6, 0x55, 0x24, 0xfb, 0x05, 0xce, 0x75, 0x19, 0xb3, 0x53, 0x79, 0x11, 0xb9, 0xbb, 0xff, 0x0c, 0xcf, 0x3c, 0x42, 0xaf, 0xec, 0xf4, 0x51, 0x20, 0x0b, 0xc0, 0x10, 0xc8, 0x69, 0x52, 0x03, 0xc4, 0xcb, 0x67, 0x08, 0xb5, 0x06, 0x4c, 0x78, 0xf5, 0x39, 0xd9, 0x44, 0x03, 0x17, 0xbf, 0xa2, 0xb6, 0x45, 0x0b, 0xfb, 0xca, 0xb5, 0xe1, 0xc0, 0x33, 0x53, 0xe5, 0x4f, 0x0b, 0x40, 0xc5, 0xda, 0x6a, 0x40, 0x35, 0x10, 0x24, 0xac, 0x43, 0xd5, 0x8b, 0x17, 0xac, 0x84, 0x64, 0x37, 0xc6, 0x48, 0x1f, 0x9f, 0x68, 0x52, 0xa5, 0x5a, 0x08, 0xac, 0x92, 0x19, 0xa9, 0xe0, 0xa7, 0xbe, 0x98, 0x8c, 0x9e, 0xd1, 0xfe, 0x14, 0x27, 0x76, 0x58, 0x9d, 0x02, 0xc6, 0x9a, 0x31, 0xbc, 0x73, 0x9a, 0x73, 0x26, 0x65, 0x05, 0xe0, 0x7f, 0x46, 0xe3, 0x2f, 0xf9, 0x6d, 0xd5, 0x96, 0x35, 0xab, 0xea, 0x75, 0x04, 0xe6, 0xa1, 0x57, 0x3d, 0x8c, 0x3b, 0x76, 0xcc, 0xfc, 0x50, 0x25, 0xe8, 0xbe, 0x8d, 0x6b, 0xe2, 0x60, 0xdc, 0xa2, 0x41, 0xc0, 0xa9, 0xc8, 0x92, 0xdb, 0xa0, 0x7f, 0xb4, 0xb3, 0x80, 0xcb, 0x68, 0xc0, 0x4a, 0x0a, 0xda, 0xda, 0x79, 0xed, 0x3d, 0x66, 0x62, 0xcb, 0x28, 0xdf, 0x06, 0x88, 0x74, 0x09, 0x24, 0xe9, 0xeb, 0x55, 0xd4, 0x15, 0x45, 0x28, 0xb2, 0xb1, 0xc7, 0x0e, 0x7c, 0x67, 0x73, 0xe3, 0x57, 0xf7, 0x26, 0x79, 0x7c, 0x0f, 0x7a, 0x31, 0x8f, 0x4b, 0xf4, 0xd9, 0xef, 0xc3, 0xda, 0x12, 0x8b, 0x24, 0x8d, 0x41, 0xde, 0xbb, 0x6f, 0x20, 0x19, 0xae, 0x0f, 0xad, 0xd5, 0xd5, 0xba, 0x4a, 0x8e, 0x74, 0x36, 0x2e, 0x86, 0x02, 0xbf, 0x87, 0x33, 0x2f, 0xd3, 0x52, 0xe5, 0xce, 0x06, 0xc2, 0x00, 0xa0, 0x93, 0x54, 0x47, 0x27, 0x6e, 0xa5, 0xdd, 0xee, 0x51, 0x24, 0xdb, 0x19, 0xb2, 0x9d, 0xf0, 0x6d, 0x5e, 0x01, 0x73, 0x91, 0x4f, 0x89, 0x70, 0xc5, 0x41, 0x7c, 0xc4, 0x31, 0x4d, 0xc0, 0x1e, 0x7a, 0x0a, 0x07, 0xbb, 0xce, 0x45, 0x8c, 0x7c, 0x70, 0x44, 0xc4, 0x26, 0xa6, 0xc9, 0x98, 0xaa, 0x3a, 0xf2, 0xaf, 0xc9, 0x03, 0x62, 0x10, 0xa6, 0xa5, 0x7c, 0x66, 0x2f, 0xc7, 0x00, 0x43, 0x9d, 0xf7, 0x04, 0x9f, 0x66, 0x58, 0x78, 0x3a, 0xc9, 0xc3, 0xca, 0x98, 0x7a, 0x56, 0x1f, 0xa6, 0x6b, 0x5b, 0x60, 0x45, 0xb0, 0x57, 0x17, 0x90, 0x1f, 0xa9, 0x5c, 0x0a, 0x4c, 0xbb, 0x7c, 0x63, 0xf0, 0x0c, 0x77, 0x63, 0x11, 0x52, 0xc8, 0xa9, 0x36, 0x39, 0x4b, 0xfb, 0x07, 0x07, 0x60, 0xd7, 0x77, 0x1e, 0x4e, 0xbe, 0x0d, 0xf6, 0xdb, 0xcd, 0x09, 0x98, 0x66, 0x5a, 0xc2, 0xc7, 0x36, 0x79, 0x92, 0xa8, 0x21, 0xc3, 0x03, 0xb7, 0x40, 0x93, 0x58, 0x28, 0xb3, 0xa4, 0x2d, 0x7c, 0xa2, 0xeb, 0x33, 0x3c, 0x97, 0xbd, 0x34, 0x37, 0x38, 0x33, 0x6b, 0xe6, 0x0c, 0xfd, 0x95, 0x39, 0x8b, 0x72, 0xbc, 0x49, 0x6d, 0xfc, 0x74, 0x97, 0x59, 0xfe, 0x87, 0x7c, 0xec, 0x50, 0xde, 0x72, 0x83, 0x9c, 0x35, 0x3b, 0x84, 0xa4, 0x96, 0x46, 0x7e, 0xe0, 0x3a, 0xcd, 0xb8, 0xa3, 0x4e, 0x82, 0x4b, 0x59, 0xaa, 0x0b, 0xc4, 0x18, 0xb4, 0x36, 0x97, 0x41, 0xad, 0x6c, 0xd7, 0x13, 0x6a, 0x4e, 0x86, 0x0e, 0x8f, 0xe5, 0x90, 0xf2, 0x03, 0x94, 0x5c, 0x1d, 0x13, 0xda, 0x0f, 0x7c, 0xd7, 0xc1, 0x31, 0xa0, 0x50, 0x2a, 0x4c, 0x7e, 0xb0, 0xd2, 0x9b, 0xc0, 0xd7, 0x7e, 0x72, 0x2d, 0xf2, 0x25, 0x2e, 0xd5, 0x85, 0xf2, 0xb2, 0x82, 0x9f, 0x8e, 0x5c, 0x8e, 0x5a, 0xe9, 0x5f, 0x1d, 0x87, 0xcd, 0xdb, 0x14, 0x5f, 0x9d, 0xa3, 0x95, 0x35, 0x73, 0x48, 0x10, 0xc1, 0xd4, 0x30, 0x0d, 0x79, 0xe9, 0x56, 0xab, 0xbf, 0xf7, 0x07, 0xe6, 0x06, 0xa2, 0x24, 0xa1, 0x78, 0x6b, 0x2a, 0xdf, 0xd1, 0x6a, 0x8a, 0x18, 0x84, 0x4e, 0xaf, 0x37, 0x9a, 0xea, 0x86, 0x08, 0xf0, 0x8f, 0x88, 0xa4, 0xb5, 0x3d, 0x94, 0x5e, 0xec, 0x24, 0x15, 0x06, 0xbf, 0x09, 0x8b, 0x4e, 0xa5, 0xef, 0x48, 0x87, 0xa1, 0xe5, 0x1a, 0x4f, 0x32, 0x16, 0x8c, 0x5a, 0x97, 0xae, 0x68, 0x6f, 0x66, 0x15, 0xb0, 0x24, 0x62, 0x1b, 0x17, 0x51, 0xc9, 0xab, 0x51, 0x69, 0x63, 0x96, 0x0f, 0x59, 0x61, 0xad, 0xe7, 0x34, 0x4e, 0xad, 0xc6, 0x74, 0xaa, 0x79, 0xfb, 0x67, 0x66, 0x58, 0xf8, 0x6f, 0x98, 0x03, 0x17, 0x09, 0x86, 0x1e, 0x5b, 0xe4, 0x13, 0x50, 0xcc, 0xaf, 0x1a, 0x4d, 0xd1, 0x5b, 0xf2, 0x8e, 0x85, 0x8d, 0x29, 0x2d, 0xc8, 0x71, 0x74, 0xcd, 0x68, 0xa3, 0x4c, 0xc1, 0xc8, 0xc8, 0x02, 0x20, 0xc7, 0xcd, 0x26, 0x7f, 0x7f, 0xa3, 0x83, 0xa0, 0x4f, 0x9f, 0x50, 0x9d, 0x99, 0x21, 0x0c, 0x05, 0xd4, 0x68, 0xd2, 0xfe, 0xd3, 0x3e, 0x64, 0xf7, 0xe0, 0x81, 0x45, 0x91, 0x25, 0xeb, 0x0a, 0x81, 0x5c, 0xdb, 0xc6, 0x9d, 0x0e, 0xe0, 0x83, 0x4c, 0x4d, 0x6f, 0x66, 0xf9, 0x91, 0xf0, 0x91, 0x5a, 0x1f, 0x74, 0x57, 0xcd, 0x20, 0x6b, 0x79, 0x72, 0xf5, 0x51, 0xb0, 0x51, 0x68, 0x34, 0x6f, 0xf0, 0x80, 0x85, 0x7e, 0x68, 0x6c, 0xd5, 0x10, 0x63, 0x35, 0x77, 0x37, 0xf2, 0xf5, 0x91, 0x06, 0xfd, 0x81, 0xa4, 0x20, 0xf2, 0x72, 0x66, 0xe3, 0x7d, 0x32, 0x05, 0xa3, 0xdc, 0xf4, 0xda, 0x36, 0x58, 0x8d, 0xae, 0x20, 0x34, 0x0f, 0x8e, 0xe0, 0xd1, 0x82, 0x0c, 0xcf, 0x2b, 0xac, 0x5e, 0xda, 0x3e, 0x49, 0x4d, 0xb4, 0x2c, 0x17, 0x15, 0xda, 0xe2, 0x30, 0x9b, 0x86, 0x01, 0xa8, 0x71, 0x20, 0xdb, 0x18, 0x27, 0x3d, 0xcf, 0xe6, 0x01, 0x09, 0x6d, 0xe0, 0xf2, 0xa9, 0xea, 0x4c, 0x80, 0x56, 0xab, 0x3b, 0x56, 0xfb, 0x4f, 0xe2, 0x5f, 0x80, 0x7a, 0x6c, 0xe8, 0x02, 0x6b, 0x65, 0xef, 0x3a, 0xd6, 0x66, 0xe4, 0x9f, 0xa1, 0x15, 0x50, 0x21, 0xe1, 0x37, 0x84, 0xfc, 0x5e, 0xe4, 0xf2, 0x89, 0xdb, 0x57, 0xa7, 0x9a, 0x68, 0x7c, 0x3d, 0x75, 0xf3, 0x64, 0x54, 0xca, 0xff, 0x83, 0xc7, 0x9e, 0x39, 0xc5, 0x98, 0x6a, 0xb3, 0x79, 0x30, 0x6f, 0xd6, 0x96, 0x2e, 0xa3, 0xdd, 0x4a, 0x84, 0xa7, 0x9f, 0x48, 0x06, 0xa1, 0x47, 0x1b, 0x79, 0xeb, 0x75, 0x12, 0x8d, 0xf9, 0xde, 0x43, 0xbd, 0x5f, 0xfa, 0xba, 0x28, 0x5e, 0x8e, 0x4c, 0xfb, 0xf9, 0x85, 0xd0, 0xe5, 0x37, 0xdc, 0x23, 0x99, 0x56, 0xea, 0x84, 0x8a, 0xa2, 0xe6, 0xdd, 0x1f, 0x25, 0xfb, 0x7d, 0x1c, 0x0b, 0xd9, 0x46, 0xc1, 0xfe, 0xf9, 0xc9, 0xf4, 0x51, 0xaf, 0xe9, 0x67, 0x1a, 0xe0, 0xeb, 0x22, 0xf2, 0xe0, 0x66, 0xa6, 0x2a, 0x47, 0x8d, 0xdd, 0xe3, 0x82, 0x9c, 0x92, 0x51, 0x05, 0x4f, 0x3f, 0x2f, 0xf6, 0xfb, 0x40, 0x3b, 0xf7, 0x73, 0xbb, 0xcd, 0xe1, 0xc5, 0xd1, 0xe4, 0xe0, 0x5d, 0xd2, 0xbb, 0xa8, 0xe4, 0x9e, 0x83, 0xf0, 0x7a, 0x77, 0x0e, 0x42, 0x7c, 0xd9, 0xeb, 0xe4, 0xb9, 0xb1, 0x17, 0x53, 0x8c, 0x17, 0xdc, 0xaf, 0x64, 0x99, 0x49, 0x76, 0xf3, 0xf0, 0x8e, 0x76, 0x54, 0xba, 0xde, 0x81, 0xce, 0x52, 0xaf, 0x0c, 0xb8, 0xec, 0x28, 0x99, 0x36, 0x75, 0xb8, 0x6e, 0x2f, 0x07, 0xd5, 0x41, 0x1b, 0x27, 0xfe, 0x12, 0xc6, 0x00, 0xe7, 0x90, 0x8b, 0x8d, 0xcb, 0xb4, 0x61, 0x09, 0x29, 0x7d, 0x50, 0xc0, 0xdc, 0x93, 0xb0, 0xa3, 0x2e, 0x2f, 0x7e, 0x2b, 0xc5, 0x0e, 0x87, 0x7a, 0xa4, 0x32, 0xd1, 0x90, 0xf0, 0xbe, 0x3c, 0x78, 0x9d, 0xd5, 0xae, 0xe5, 0xa8, 0xa1, 0x9b, 0xed, 0x03, 0xa2, 0xfe, 0x1a, 0x26, 0x13, 0x15, 0x99, 0xe6, 0xf5, 0x2d, 0x58, 0x68, 0x42, 0xe1, 0xda, 0x25, 0x1c, 0x8e, 0x0d, 0xed, 0x78, 0x4f, 0x42, 0x10, 0x8f, 0x1d, 0x2b, 0x24, 0x01, 0x94, 0xcf, 0xe1, 0x65, 0xeb, 0x67, 0x96, 0x08, 0xa2, 0x10, 0xa0, 0x9c, 0x45, 0x0b, 0x01, 0xf3, 0xa9, 0x38, 0x1e, 0xab, 0xbe, 0x24, 0x80, 0x07, 0xff, 0x43, 0xa7, 0x32, 0xf2, 0x54, 0xee, 0xba, 0xe7, 0x63, 0xfd, 0xc3, 0x5b, 0x26, 0x27, 0x36, 0x4f, 0xf0, 0xbd, 0x5b, 0xfc, 0x9a, 0x58, 0xe1, 0xb9, 0x60, 0x60, 0x3f, 0x5b, 0x92, 0x61, 0xa8, 0x7b, 0x13, 0xf2, 0xed, 0x9d, 0x9d, 0xee, 0x2a, 0x6b, 0x06, 0xd4 }; +constexpr AccessUnit LPCM_LARGE_AU_EXPECTED_AU = { 0x15f90, 0x159b2, false, 3, { 0x00, 0x00, 0x00, 0xfb, 0xba, 0x2b, 0xca, 0x88, 0x65, 0x2b, 0x01, 0xf4, 0xe6, 0xfc, 0x43, 0xc2 }, { 0xd6, 0x8c, 0x4f, 0x4c, 0xff, 0x84, 0x68, 0xd3, 0xb0, 0x60, 0x67, 0xd0, 0x3c, 0x90, 0x37, 0x14, 0x84, 0xba, 0x7a, 0xca } }; + +const std::vector USER_DATA_LARGE_AU_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x20, 0x4d, 0x00, 0x00, 0x12, 0x34, 0x5c, 0x19, 0xda, 0x7f, 0x08, 0x26, 0x84, 0x39, 0xe1, 0xd5, 0x52, 0x91, 0x8d, 0x44, 0xa5, 0xfc, 0xcb, 0x75, 0x65, 0x49, 0xd3, 0xaf, 0x92, 0x68, 0x5b, 0x79, 0x65, 0x28, 0x51, 0x4e, 0x74, 0xf7, 0xe6, 0xe6, 0x11, 0xe6, 0x89, 0x46, 0xcf, 0x5c, 0x3b, 0xe3, 0xaa, 0x7e, 0xc0, 0xee, 0xb8, 0x5a, 0xca, 0xf1, 0x60, 0x2d, 0x4b, 0xcd, 0x10, 0x79, 0xca, 0xf5, 0xc6, 0x3a, 0x63, 0x4e, 0xc5, 0x7e, 0x09, 0xeb, 0x6b, 0x12, 0x8f, 0x74, 0x71, 0x4f, 0x7e, 0x73, 0xd1, 0xe8, 0xe6, 0x6d, 0x4a, 0xc9, 0xf7, 0x0b, 0x96, 0x55, 0xcc, 0xcf, 0x96, 0x5b, 0xd2, 0x3e, 0x26, 0x5d, 0x61, 0x46, 0x73, 0xfe, 0x20, 0x18, 0xe7, 0xc7, 0xeb, 0x69, 0x02, 0xd8, 0xd8, 0x34, 0x94, 0xf9, 0xcb, 0x3c, 0xd7, 0x41, 0xd9, 0x8c, 0xa8, 0xbd, 0x8b, 0x18, 0xf1, 0x5d, 0xa0, 0x94, 0xdf, 0xd0, 0xa0, 0x72, 0xb2, 0xc3, 0x29, 0x05, 0xd6, 0x61, 0x95, 0xba, 0xa8, 0x13, 0x8b, 0x56, 0xcf, 0xb7, 0x65, 0x0d, 0xbb, 0xaf, 0xe9, 0x4e, 0x4c, 0xb6, 0xd4, 0xf6, 0x43, 0x81, 0x46, 0x46, 0xb1, 0x0f, 0xf7, 0x59, 0x3d, 0xe3, 0xc5, 0x1b, 0x3f, 0x60, 0x9f, 0x80, 0x71, 0x60, 0x19, 0x37, 0xb6, 0x7a, 0x2b, 0x7e, 0x1e, 0x6a, 0x13, 0x64, 0x5a, 0x80, 0xcb, 0xda, 0xaa, 0x6f, 0xd0, 0xfd, 0x9c, 0x32, 0x36, 0xac, 0x24, 0x9d, 0x9e, 0xfa, 0xfc, 0x26, 0xcb, 0x5d, 0x3a, 0x74, 0x56, 0xd3, 0x0e, 0xea, 0xc2, 0x4e, 0xaf, 0xef, 0x02, 0x5c, 0x2b, 0x68, 0x13, 0x5f, 0x71, 0xae, 0x27, 0x5d, 0xe2, 0xb2, 0x10, 0x01, 0x0e, 0x6e, 0xed, 0x9e, 0x7b, 0x6f, 0xb6, 0xdc, 0x99, 0x62, 0x0b, 0x48, 0x03, 0xa5, 0x8c, 0x6c, 0x82, 0xa1, 0xe3, 0x69, 0x72, 0x90, 0x47, 0xb6, 0x36, 0xe2, 0xfb, 0x9d, 0x14, 0x8a, 0xcd, 0x67, 0x48, 0xb9, 0x01, 0x2d, 0xf0, 0x9d, 0xc3, 0xf1, 0xb3, 0x92, 0x0c, 0x3a, 0xb4, 0xb5, 0x3a, 0x5c, 0x22, 0x0f, 0x31, 0x3c, 0xf9, 0xd1, 0xd8, 0x0c, 0xc9, 0xc9, 0x06, 0xbf, 0x86, 0x4e, 0x5a, 0xe7, 0xd8, 0xd2, 0x6b, 0x7a, 0x2c, 0xb3, 0xb3, 0x40, 0xd3, 0x49, 0xa0, 0xf9, 0xc3, 0x31, 0x18, 0x75, 0x95, 0x34, 0xce, 0x25, 0x3c, 0x48, 0x47, 0xeb, 0x4f, 0xde, 0xa6, 0xd2, 0x5f, 0xf3, 0x4d, 0x09, 0xb1, 0xc0, 0x65, 0xbc, 0xe6, 0x27, 0x3b, 0x8a, 0x08, 0xf6, 0x6e, 0x48, 0x1a, 0x1e, 0xc9, 0x8d, 0x6d, 0x7b, 0x67, 0x6c, 0xdb, 0x67, 0xcc, 0xe2, 0x67, 0x8e, 0x61, 0xf0, 0x3c, 0x0b, 0xac, 0x40, 0x37, 0x57, 0xae, 0x6d, 0x12, 0xfd, 0x2b, 0xec, 0x7e, 0xd3, 0xc0, 0x4e, 0x67, 0x44, 0x5e, 0xa7, 0x88, 0x14, 0x89, 0x44, 0x0e, 0x10, 0x20, 0xf8, 0x01, 0xe2, 0xea, 0xb3, 0xea, 0xd7, 0x04, 0x79, 0x6d, 0x7c, 0xd9, 0x32, 0xc9, 0x6e, 0x0e, 0xb5, 0xa7, 0x16, 0x23, 0x01, 0xdd, 0xb5, 0x6e, 0x51, 0xd8, 0x46, 0x9c, 0x1e, 0x14, 0x1c, 0x48, 0xb6, 0x3b, 0xb7, 0xcd, 0x0f, 0x1c, 0x8a, 0x3a, 0x69, 0xec, 0xb5, 0xa3, 0x31, 0x30, 0x3f, 0x44, 0x45, 0xd1, 0x5b, 0x4f, 0x94, 0xba, 0x01, 0x3f, 0xcd, 0x1f, 0x24, 0x2e, 0xe8, 0x4e, 0x48, 0x59, 0xeb, 0x86, 0xb8, 0x52, 0x1a, 0xcf, 0x1b, 0x02, 0x84, 0x8f, 0x81, 0x52, 0x8c, 0xfc, 0xb7, 0xf0, 0x15, 0xe1, 0xab, 0xa8, 0xc4, 0xd5, 0x61, 0x3c, 0x29, 0xd1, 0x32, 0x40, 0x84, 0xd1, 0xc9, 0x48, 0x03, 0x09, 0x4d, 0x3e, 0xd0, 0x07, 0xaf, 0xc9, 0x90, 0xad, 0xd6, 0x77, 0x27, 0x29, 0x17, 0x51, 0x2d, 0x37, 0x5f, 0xed, 0xe1, 0xef, 0xb1, 0xa0, 0xd5, 0xd6, 0xb6, 0x78, 0x89, 0xb2, 0x98, 0xec, 0x28, 0x05, 0x9e, 0x2b, 0x18, 0xa5, 0x59, 0x00, 0x6d, 0x0c, 0x62, 0x06, 0x85, 0xe4, 0xa7, 0x6f, 0xf9, 0x38, 0x42, 0x59, 0x43, 0xc0, 0xe0, 0xaf, 0xe5, 0x66, 0x7d, 0x70, 0x3c, 0x36, 0x0b, 0x6e, 0x47, 0x8b, 0xcf, 0x19, 0x91, 0xa3, 0xff, 0xe4, 0x98, 0x75, 0x46, 0x45, 0x64, 0x59, 0x9c, 0x9e, 0xf6, 0xe2, 0x47, 0xb0, 0xeb, 0xb6, 0x87, 0x3f, 0xc2, 0xbd, 0xcc, 0x76, 0x49, 0x38, 0xc6, 0x60, 0xb1, 0xc6, 0x57, 0x61, 0x3c, 0xc2, 0xd9, 0x68, 0xde, 0x61, 0x57, 0x0b, 0x7d, 0x80, 0x93, 0xb2, 0xc4, 0xdb, 0x54, 0x2d, 0xa2, 0x9d, 0x30, 0x2e, 0x77, 0x52, 0x64, 0xa0, 0xa5, 0x40, 0x61, 0xa5, 0x17, 0xd0, 0x10, 0x4e, 0x34, 0x1b, 0x15, 0x3b, 0x82, 0xc7, 0xea, 0x9e, 0xf8, 0xdd, 0x6b, 0xc4, 0xa9, 0xc4, 0x69, 0x76, 0xa9, 0x8b, 0x9f, 0x52, 0x06, 0xa3, 0xc4, 0x6b, 0xd9, 0x2e, 0x4a, 0xd6, 0x65, 0xb9, 0x51, 0xde, 0x5c, 0x7b, 0xd1, 0x26, 0x97, 0x80, 0xe1, 0x03, 0xa6, 0x79, 0x30, 0x73, 0xe8, 0x36, 0x91, 0xbe, 0x6b, 0xe1, 0xef, 0x5a, 0xdc, 0x2a, 0x73, 0x03, 0x72, 0x6a, 0x39, 0x60, 0x2a, 0xe3, 0x7f, 0x2f, 0x67, 0x95, 0x19, 0x38, 0x94, 0x27, 0xc0, 0x77, 0xba, 0x9e, 0xe1, 0x98, 0xb4, 0x2f, 0xa9, 0xe2, 0x9c, 0xf3, 0xea, 0x80, 0x5e, 0x6b, 0xee, 0x81, 0x38, 0x80, 0x29, 0xa1, 0x28, 0x84, 0x5f, 0x8e, 0xbb, 0xbe, 0x63, 0xcf, 0x85, 0x7f, 0x9a, 0x22, 0xae, 0x89, 0xfe, 0x87, 0xf5, 0x4f, 0x03, 0x70, 0xc9, 0xeb, 0xf9, 0x90, 0x6e, 0x7c, 0x25, 0xf3, 0xd7, 0xc5, 0xbf, 0x2b, 0x13, 0x8a, 0xbe, 0xbc, 0x50, 0x38, 0xf5, 0x38, 0xd9, 0xf7, 0x3b, 0xa7, 0xb0, 0x57, 0x85, 0x0b, 0xd0, 0x3d, 0x05, 0xfc, 0xe3, 0xf5, 0x3f, 0xc4, 0x23, 0x8c, 0x26, 0x94, 0xe9, 0xf0, 0xb0, 0x84, 0x77, 0xb4, 0x88, 0xa1, 0x5d, 0xd0, 0xb9, 0xb2, 0x9c, 0xf5, 0x18, 0x13, 0x3a, 0x3a, 0x69, 0x90, 0x35, 0x4e, 0x4f, 0xcb, 0xc9, 0xab, 0xb3, 0x1a, 0x5d, 0x8a, 0xff, 0x84, 0xf5, 0x43, 0x45, 0xdd, 0x99, 0x79, 0x48, 0x32, 0x7a, 0x5b, 0xed, 0xc5, 0x9e, 0x8c, 0xef, 0xfd, 0x23, 0x6d, 0x92, 0xf9, 0xef, 0xa2, 0x86, 0xe2, 0x09, 0xdf, 0x06, 0x85, 0x9e, 0xbc, 0x83, 0x88, 0xb3, 0x24, 0x8f, 0xbf, 0x00, 0x84, 0x91, 0x90, 0xc9, 0xc8, 0xaf, 0x4b, 0xbe, 0xa0, 0xa3, 0xff, 0x66, 0xb1, 0x8a, 0x8a, 0x78, 0x37, 0x4e, 0x32, 0xa9, 0x61, 0x9a, 0xbd, 0x13, 0xf5, 0xbc, 0x39, 0x41, 0x8d, 0x4f, 0xfa, 0xe3, 0xdc, 0x76, 0x65, 0x26, 0xf1, 0x23, 0xdb, 0x71, 0xc6, 0x0b, 0x31, 0x4e, 0x05, 0x0c, 0xb4, 0x8d, 0x3c, 0x12, 0x37, 0x86, 0x59, 0x40, 0x1a, 0x40, 0x26, 0xcb, 0x04, 0x29, 0x23, 0x48, 0x76, 0xee, 0x6c, 0x00, 0x22, 0x9d, 0x8e, 0x70, 0x0e, 0x37, 0x9e, 0x78, 0x53, 0x76, 0x91, 0x10, 0x00, 0x23, 0xb0, 0x6c, 0x70, 0x78, 0x1b, 0xeb, 0x3e, 0x26, 0xb0, 0x0b, 0x8b, 0x3b, 0xa2, 0x9c, 0x72, 0x5e, 0xb9, 0x93, 0x17, 0x6b, 0x25, 0x06, 0xd0, 0x87, 0x52, 0x66, 0x0f, 0xe2, 0x28, 0xc9, 0xbb, 0x3a, 0x7f, 0xdb, 0x03, 0x53, 0x58, 0xb0, 0x59, 0x9a, 0xb7, 0x08, 0x57, 0x5a, 0xd9, 0xaa, 0xd9, 0xd5, 0x3b, 0xb1, 0xb2, 0xa7, 0x01, 0xae, 0x5b, 0xb5, 0xef, 0xb7, 0x2a, 0x83, 0x0e, 0x5c, 0x17, 0x70, 0x5f, 0xbf, 0xa5, 0x59, 0xbc, 0xa8, 0xee, 0x8a, 0x88, 0x47, 0x14, 0xfc, 0x59, 0x99, 0x85, 0x4e, 0x52, 0x14, 0x6b, 0x76, 0xd7, 0x23, 0xd2, 0xdf, 0x32, 0x97, 0x70, 0x89, 0x4e, 0xc0, 0xa8, 0x5d, 0x03, 0x64, 0xca, 0x89, 0x63, 0x9b, 0xc8, 0x57, 0x1f, 0xed, 0xf2, 0xbb, 0x87, 0x7c, 0xbf, 0x4c, 0xda, 0xf0, 0x71, 0x62, 0xda, 0x1e, 0xd9, 0x3d, 0x3c, 0xa2, 0xc6, 0xa7, 0x16, 0xdf, 0x6c, 0x8a, 0xd8, 0xbc, 0x7b, 0x8c, 0x23, 0x2e, 0xce, 0x42, 0x48, 0xe7, 0x91, 0xf9, 0x4a, 0xcf, 0xe6, 0xfd, 0xd3, 0xb1, 0x99, 0x86, 0xd9, 0xcc, 0x6f, 0x9d, 0x73, 0x5a, 0x8f, 0x63, 0x5d, 0x4d, 0x07, 0x65, 0x46, 0x7a, 0x1f, 0x60, 0x94, 0x89, 0x53, 0x49, 0x38, 0x1e, 0x3e, 0xf2, 0xeb, 0x55, 0x52, 0x90, 0xa8, 0x76, 0x75, 0x90, 0xd9, 0x16, 0x4a, 0x63, 0x90, 0xe3, 0x02, 0xcf, 0x0f, 0x94, 0xb4, 0xb4, 0xa2, 0x96, 0xb4, 0xc2, 0x6c, 0x8e, 0x87, 0x86, 0x8d, 0x1c, 0x18, 0xbd, 0xc4, 0x1a, 0xd9, 0x8a, 0x0d, 0x87, 0x72, 0xdb, 0x6d, 0xbe, 0x05, 0xb6, 0x79, 0xbe, 0x5d, 0x19, 0x4f, 0x1c, 0x34, 0x89, 0xa5, 0xe2, 0x37, 0x2d, 0x2c, 0xbd, 0x58, 0xf5, 0xb7, 0xb9, 0x8b, 0x2c, 0xf9, 0x0c, 0xa0, 0x4d, 0x10, 0x8d, 0xda, 0x7f, 0x0d, 0x87, 0xcb, 0x6d, 0x59, 0xe9, 0x22, 0xc4, 0x29, 0x8e, 0x4b, 0x40, 0xf1, 0x94, 0xc7, 0xca, 0x37, 0x3d, 0x3f, 0xc9, 0x15, 0xa5, 0x22, 0xaf, 0xec, 0xb9, 0x52, 0xd2, 0x0c, 0xb5, 0x41, 0x33, 0x4f, 0xa1, 0x5b, 0x75, 0x01, 0x40, 0x63, 0x74, 0x52, 0xff, 0x3e, 0x5c, 0x50, 0xab, 0x63, 0x78, 0xad, 0xf5, 0x7c, 0x1e, 0xb1, 0x89, 0x5f, 0xc3, 0xb6, 0x44, 0xc5, 0xd1, 0x1f, 0xb7, 0xa6, 0x32, 0x78, 0x63, 0xc0, 0x68, 0xbe, 0x38, 0x33, 0x6b, 0x81, 0x2f, 0xfa, 0x0e, 0x9f, 0x70, 0x80, 0x6b, 0x0d, 0x29, 0xf9, 0x91, 0x65, 0x36, 0x3f, 0x71, 0x1e, 0x1c, 0xd1, 0xf7, 0xe7, 0xef, 0x82, 0x64, 0x68, 0x1d, 0x8e, 0x9e, 0x04, 0x48, 0x3e, 0xff, 0xc3, 0xff, 0x60, 0x01, 0xda, 0x26, 0x81, 0x42, 0x32, 0x33, 0xf2, 0x66, 0xa5, 0xed, 0xf0, 0x7c, 0x24, 0x54, 0xc7, 0xa6, 0xde, 0x05, 0x3f, 0x89, 0x7c, 0xbc, 0x72, 0x57, 0x23, 0xb4, 0x1e, 0x9b, 0xb0, 0x64, 0x62, 0x4d, 0x59, 0x99, 0x0a, 0xef, 0x7d, 0x69, 0xff, 0x8b, 0xd0, 0x8f, 0xac, 0x38, 0x94, 0xcc, 0xda, 0x15, 0x39, 0x34, 0xd3, 0x0b, 0x91, 0x71, 0x16, 0xf5, 0xf0, 0xa7, 0xd6, 0xe3, 0x1b, 0x02, 0x9d, 0x68, 0x45, 0x70, 0x5c, 0x70, 0x76, 0xdd, 0x0b, 0x3b, 0xcf, 0xbe, 0x81, 0x36, 0x46, 0x5e, 0xd1, 0x23, 0x16, 0xa6, 0x44, 0x71, 0x9f, 0x37, 0x03, 0xa9, 0xf8, 0x44, 0x4c, 0xe9, 0x1c, 0xbd, 0x02, 0xad, 0x66, 0x4f, 0x5c, 0x86, 0xe1, 0xc4, 0xde, 0x4e, 0x8c, 0x23, 0x2b, 0x53, 0x1b, 0xf7, 0x67, 0x2c, 0xb9, 0x2c, 0x32, 0xad, 0x9c, 0x95, 0x97, 0xfa, 0x9e, 0x4d, 0x85, 0xac, 0x46, 0xe0, 0x72, 0x83, 0x56, 0x1d, 0x38, 0x5d, 0x75, 0x3f, 0xb5, 0xf6, 0x23, 0xb2, 0xba, 0x9e, 0xda, 0xe3, 0x3a, 0x3e, 0x02, 0x66, 0x52, 0xd4, 0x9d, 0x3b, 0x52, 0x98, 0x58, 0x89, 0x6f, 0x24, 0xe1, 0x67, 0xd7, 0x1c, 0xea, 0xca, 0xce, 0x9f, 0xe9, 0xa9, 0xca, 0xc6, 0x59, 0x7b, 0x9d, 0xe4, 0x19, 0xeb, 0x86, 0x6f, 0x8a, 0xa0, 0x8a, 0x7a, 0x74, 0x98, 0x71, 0x5d, 0x8b, 0x03, 0xb4, 0xce, 0x13, 0x08, 0x15, 0xa1, 0x38, 0xbb, 0x00, 0xb1, 0xb3, 0x52, 0x27, 0x0f, 0x8c, 0x24, 0x10, 0x9e, 0x18, 0xfd, 0x3e, 0xa7, 0xaa, 0x5d, 0x4f, 0xa4, 0x83, 0x5e, 0xbe, 0x63, 0x9f, 0xc4, 0xf2, 0x0a, 0x95, 0x46, 0xef, 0x84, 0x90, 0x5e, 0xd7, 0xbe, 0x5c, 0xad, 0xc2, 0x85, 0x03, 0xbc, 0xc5, 0xf4, 0x63, 0xb1, 0x9d, 0x4c, 0x9f, 0x2a, 0x6d, 0x4e, 0x1b, 0xf8, 0xf4, 0x24, 0x32, 0xf0, 0x4e, 0xe0, 0x3f, 0xca, 0x5c, 0xc0, 0xb8, 0x71, 0xc3, 0x1c, 0x7a, 0xec, 0xbf, 0x5b, 0xd0, 0xdc, 0xf4, 0x7d, 0x6d, 0x5e, 0x6c, 0xd0, 0x0e, 0xe0, 0x64, 0x22, 0x82, 0x84, 0xb4, 0xeb, 0xc8, 0x79, 0xdf, 0xcc, 0x0b, 0x60, 0x14, 0x9c, 0xa3, 0x7e, 0x9d, 0xb1, 0xb0, 0xf2, 0x1d, 0x7e, 0x3f, 0x28, 0xf3, 0x6a, 0x50, 0x67, 0x14, 0x56, 0xe1, 0x05, 0x89, 0x9b, 0xf2, 0x41, 0x75, 0xc9, 0x59, 0x03, 0xa1, 0xf0, 0x85, 0xa7, 0xca, 0x23, 0x09, 0xaf, 0x3a, 0xe5, 0x92, 0x14, 0xa7, 0xbe, 0x71, 0x7e, 0x99, 0x07, 0x91, 0xef, 0xdf, 0xab, 0x4a, 0x69, 0x97, 0xa5, 0x37, 0xfe, 0xe5, 0x12, 0xf7, 0x3f, 0x82, 0x2a, 0x9d, 0xd2, 0xa5, 0x27, 0xd3, 0xf5, 0xc1, 0x99, 0xc2, 0x78, 0x10, 0x14, 0x8d, 0x2f, 0xf0, 0x5c, 0x0f, 0x15, 0xc8, 0x0c, 0x8c, 0x93, 0x52, 0x1d, 0x3e, 0xb0, 0x2b, 0x35, 0xda, 0xac, 0x00, 0x80, 0x10, 0xb6, 0x56, 0xea, 0x1a, 0x43, 0xdd, 0x14, 0x0e, 0x75, 0x40, 0x96, 0x5f, 0x1d, 0xcb, 0xf6, 0x26, 0x3e, 0x88, 0x9e, 0xf8, 0x1a, 0x1e, 0x74, 0xed, 0x6b, 0xa7, 0xc8, 0x73, 0x83, 0xac, 0xe0, 0xe4, 0x38, 0x02, 0x7c, 0x4b, 0x67, 0x9a, 0xb3, 0xe1, 0xef, 0xc2, 0xb8, 0xc8, 0xc4, 0x19, 0xe0, 0xab, 0x73, 0x55, 0xd4, 0x99, 0x86, 0x22, 0x27, 0xd4, 0x7b, 0x05, 0x4b, 0x43, 0x2c, 0x43, 0x0b, 0xf2, 0x35, 0x37, 0x3c, 0x95, 0x63, 0xcd, 0x08, 0x24, 0x5f, 0x37, 0xce, 0xff, 0x99, 0xbe, 0x98, 0x0e, 0x0b, 0x87, 0x07, 0xe0, 0x70, 0x89, 0x0b, 0x82, 0x53, 0x57, 0x53, 0x62, 0xdb, 0xe7, 0x76, 0xc6, 0x41, 0x0f, 0x0c, 0x9b, 0x52, 0x37, 0xf8, 0x83, 0x40, 0x23, 0x26, 0x55, 0xf9, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x20, 0x91, 0x48, 0x5e, 0xa3, 0x75, 0x85, 0xfa, 0xa0, 0x8b, 0x71, 0x17, 0xb1, 0x83, 0x8f, 0x09, 0x16, 0x15, 0x34, 0x04, 0xa6, 0x6c, 0x2d, 0x5f, 0x2d, 0x6a, 0x6d, 0xf4, 0x63, 0x28, 0x9c, 0xcb, 0x76, 0x85, 0xd6, 0xdc, 0x0b, 0x75, 0x65, 0xa1, 0xcd, 0xcc, 0x10, 0xa9, 0x4a, 0x81, 0xa9, 0x43, 0x0f, 0xf7, 0x7a, 0xb8, 0xa2, 0x25, 0x44, 0xc5, 0x56, 0xbc, 0x22, 0x0b, 0xa6, 0xaf, 0x45, 0xe2, 0xe4, 0x02, 0x61, 0x4d, 0x5d, 0x1e, 0x47, 0x9a, 0xdc, 0x29, 0xc6, 0x80, 0xf5, 0x79, 0x55, 0x74, 0xd0, 0x19, 0x75, 0xa7, 0x89, 0x9a, 0x7d, 0x11, 0x65, 0x0b, 0x7c, 0xcd, 0x41, 0xcc, 0x9e, 0x41, 0x0c, 0x5e, 0x69, 0xd2, 0x16, 0x95, 0x71, 0xfe, 0xa3, 0x05, 0x41, 0xac, 0xaa, 0x63, 0xa3, 0x25, 0x1e, 0x3a, 0xa8, 0xf8, 0x59, 0x62, 0x42, 0x98, 0x0c, 0x42, 0xdc, 0x12, 0xe6, 0xaf, 0xe3, 0x35, 0x58, 0xba, 0x37, 0x02, 0x6d, 0xd0, 0xff, 0xa8, 0xfd, 0x25, 0x31, 0x29, 0x6c, 0xa9, 0x81, 0xa4, 0x24, 0x9c, 0x65, 0x3e, 0x2e, 0x4f, 0xcb, 0x9f, 0x3e, 0x12, 0x13, 0xf6, 0x52, 0x79, 0x38, 0xbf, 0x74, 0xae, 0x13, 0x7f, 0xa1, 0x4b, 0x90, 0xd6, 0x9e, 0x3d, 0x0e, 0xa5, 0x66, 0xea, 0xa7, 0xe8, 0x72, 0xcb, 0xb5, 0x2f, 0x62, 0xc0, 0xa7, 0xb2, 0xe4, 0x83, 0xfd, 0x9d, 0xcc, 0xb7, 0x1d, 0x44, 0x65, 0x78, 0x44, 0x4d, 0x97, 0x83, 0x38, 0xcc, 0xc2, 0x62, 0x8e, 0x4b, 0x4f, 0x98, 0x12, 0x69, 0x53, 0x1e, 0x4d, 0x4c, 0x86, 0xf5, 0x4f, 0x1a, 0x2c, 0x8c, 0x70, 0x9f, 0x4e, 0x75, 0x8e, 0x46, 0x96, 0xcd, 0xe0, 0x90, 0xcc, 0x8e, 0xa4, 0x8a, 0x5a, 0x1f, 0xe3, 0x4b, 0x9e, 0xa0, 0xfc, 0xc8, 0xb8, 0xa3, 0x7e, 0xc8, 0x11, 0xde, 0x62, 0xa9, 0xfe, 0xe0, 0x01, 0x79, 0x92, 0x00, 0x87, 0xef, 0x51, 0xd2, 0x73, 0x50, 0xaa, 0xad, 0xd6, 0x82, 0x50, 0x83, 0xda, 0x93, 0x96, 0xc5, 0x51, 0x21, 0x7e, 0xe3, 0x9d, 0x86, 0x1c, 0xbb, 0xdb, 0xca, 0x6a, 0x38, 0x32, 0xf9, 0x55, 0x63, 0x97, 0x44, 0x4f, 0x2e, 0xf9, 0x07, 0x43, 0x9e, 0x88, 0x25, 0x6a, 0xa5, 0x1f, 0x75, 0xdc, 0x5b, 0xd0, 0xbf, 0x24, 0x6d, 0xd7, 0x02, 0x6c, 0x82, 0x8b, 0x5f, 0x3b, 0xfc, 0x0d, 0xf4, 0x3a, 0xa5, 0x94, 0x36, 0x07, 0xe2, 0x07, 0x9f, 0x7e, 0xed, 0xb7, 0xc7, 0x3c, 0x9c, 0xdc, 0xbb, 0xd6, 0xa7, 0x8b, 0x1c, 0x8f, 0x1f, 0x26, 0x3d, 0x40, 0x87, 0xfe, 0x2b, 0xa8, 0xf4, 0x1e, 0x84, 0x7c, 0x48, 0xd7, 0xb1, 0xdf, 0xdb, 0xda, 0x8c, 0xa5, 0xea, 0xca, 0xa1, 0xe5, 0xc6, 0x54, 0x57, 0x15, 0x3f, 0xc5, 0x7b, 0xea, 0xc4, 0x58, 0x44, 0x81, 0xa1, 0xdc, 0x8d, 0x98, 0x09, 0x1c, 0xb2, 0x4f, 0xdf, 0x0a, 0x14, 0xb5, 0xfd, 0x34, 0x56, 0x8f, 0x33, 0xd7, 0xbb, 0x28, 0xa1, 0x29, 0x81, 0x53, 0xcb, 0xb7, 0x67, 0xd9, 0x78, 0x3e, 0x46, 0xd6, 0x64, 0x0b, 0x0c, 0x84, 0xc4, 0xb5, 0x65, 0xf9, 0x9f, 0x0e, 0x45, 0x2a, 0x2d, 0xbd, 0x92, 0x63, 0x61, 0x8e, 0xab, 0xb9, 0x22, 0x7e, 0x89, 0x77, 0x1a, 0x47, 0x92, 0x87, 0x77, 0xaf, 0xf7, 0x39, 0x2c, 0x52, 0x55, 0xf8, 0xab, 0xa8, 0x29, 0x18, 0xc4, 0xd3, 0xad, 0x9a, 0x1d, 0x2f, 0x8f, 0x0e, 0xdc, 0x1a, 0x06, 0x39, 0xcf, 0xec, 0x1a, 0xca, 0xde, 0x77, 0x43, 0x04, 0xe2, 0x05, 0xac, 0x6a, 0xa0, 0x0c, 0x51, 0x6c, 0x33, 0x4f, 0xa1, 0xdc, 0x7d, 0x28, 0x08, 0x90, 0x2f, 0x7d, 0x7a, 0x46, 0x21, 0x77, 0x7c, 0xcf, 0x19, 0x0e, 0x9e, 0xbb, 0xbf, 0xe6, 0x3b, 0x7c, 0x05, 0xf1, 0x89, 0xb3, 0x93, 0x90, 0x44, 0xa5, 0xe0, 0x39, 0x73, 0x88, 0x16, 0x0f, 0x99, 0xaf, 0x3b, 0x31, 0x81, 0x05, 0x52, 0x09, 0x46, 0x9e, 0x95, 0xe9, 0xf0, 0x86, 0x78, 0xc9, 0x18, 0x54, 0x9d, 0xe8, 0xfc, 0x3d, 0x0f, 0x77, 0x06, 0xc1, 0x6b, 0xf6, 0x8d, 0x67, 0x77, 0x1d, 0xb3, 0xd3, 0x6e, 0x1f, 0x25, 0x9e, 0x27, 0x5f, 0x35, 0x70, 0x52, 0x24, 0xde, 0xed, 0x3c, 0x5a, 0x35, 0xf1, 0x63, 0xe9, 0xc7, 0x58, 0xdb, 0x5f, 0xcc, 0x46, 0x25, 0xe1, 0xb7, 0x89, 0xfe, 0x81, 0xf1, 0x19, 0x65, 0x0d, 0xc7, 0xcf, 0xf0, 0x79, 0x33, 0xcd, 0xbf, 0xd0, 0xdd, 0x2c, 0xdd, 0xdc, 0x7d, 0x68, 0x66, 0x91, 0x51, 0x26, 0x76, 0x6d, 0x8e, 0xe0, 0xe1, 0x3f, 0xdc, 0xc9, 0xc0, 0xe9, 0x75, 0x50, 0x74, 0x23, 0x28, 0xd9, 0x55, 0x16, 0x53, 0xd8, 0x6c, 0xfe, 0xc8, 0xaa, 0x27, 0x0f, 0xd6, 0x46, 0x33, 0xde, 0x0c, 0xa1, 0x49, 0x57, 0xb5, 0xe9, 0x04, 0xc9, 0xc1, 0xfc, 0x4b, 0x10, 0x5b, 0x41, 0x92, 0xa9, 0x66, 0xda, 0x9a, 0x45, 0xf3, 0x57, 0x47, 0x43, 0x32, 0x64, 0xe2, 0xe9, 0x19, 0x2d, 0x53, 0xff, 0xae, 0x3f, 0x69, 0xc3, 0x14, 0x83, 0xdd, 0x2a, 0xdc, 0xc2, 0xeb, 0x2c, 0x9e, 0x2c, 0x26, 0x2b, 0x0b, 0x70, 0x57, 0xe8, 0x47, 0x6a, 0x83, 0xce, 0xf3, 0x3d, 0xda, 0xbf, 0x8c, 0x1f, 0x87, 0x67, 0xfa, 0x38, 0xaf, 0x57, 0xa8, 0x3d, 0x24, 0x1d, 0xf7, 0xf1, 0x82, 0x93, 0x8f, 0x6a, 0xed, 0x97, 0x60, 0xef, 0x10, 0x61, 0x8a, 0x5d, 0x17, 0x58, 0x5e, 0x4d, 0x2d, 0x4a, 0x61, 0x48, 0xc1, 0x1d, 0x17, 0x3e, 0x8c, 0x97, 0x24, 0xb8, 0x8e, 0xfc, 0xba, 0x47, 0x96, 0x52, 0x18, 0xec, 0xf9, 0xec, 0xa9, 0x32, 0x26, 0xe2, 0xe9, 0xb8, 0x59, 0xc7, 0xf2, 0x8b, 0xc7, 0x11, 0xf7, 0x76, 0x64, 0x63, 0x5a, 0x2a, 0x55, 0xef, 0x3b, 0xf2, 0x78, 0x50, 0x44, 0x03, 0xfe, 0xa3, 0xcb, 0x5b, 0xac, 0x9a, 0x2c, 0xef, 0x5f, 0x73, 0x08, 0x4b, 0xac, 0x63, 0x2b, 0xe7, 0x2f, 0x9a, 0x4a, 0x6f, 0xab, 0x1f, 0x53, 0x70, 0xea, 0x58, 0xa3, 0x1f, 0xbe, 0xbb, 0x7e, 0x11, 0xf7, 0x46, 0x0e, 0xfe, 0xe7, 0x7e, 0xd1, 0xfe, 0x88, 0xa1, 0x6e, 0x8a, 0x3e, 0xff, 0x9d, 0xda, 0xf0, 0x57, 0xbb, 0x41, 0xbf, 0xdd, 0x79, 0x0d, 0x47, 0x98, 0x23, 0x11, 0xa3, 0xea, 0x6d, 0x6b, 0x32, 0x70, 0x5f, 0x1a, 0xf5, 0xfc, 0x03, 0x2f, 0xc1, 0x52, 0x2d, 0xab, 0xb4, 0xb4, 0xb9, 0x2f, 0x53, 0x99, 0x3c, 0x93, 0xbe, 0xa3, 0x69, 0x17, 0x91, 0x89, 0x1b, 0xa6, 0x25, 0x9f, 0x84, 0x3e, 0xb5, 0x32, 0x41, 0xaa, 0x2a, 0x7c, 0x09, 0xde, 0xc2, 0xd6, 0xf6, 0x06, 0x99, 0xf2, 0x4e, 0x49, 0x26, 0x0c, 0x75, 0x39, 0x4d, 0x10, 0x41, 0x86, 0x70, 0xc6, 0xa6, 0x92, 0xfc, 0xe1, 0x84, 0xd2, 0xcb, 0xe2, 0x9c, 0x82, 0x62, 0x3c, 0x24, 0xa4, 0x62, 0xc4, 0xbe, 0xfb, 0xc4, 0xdc, 0xde, 0xa3, 0x89, 0x53, 0x26, 0x25, 0xdf, 0x7e, 0x53, 0x6f, 0x57, 0xca, 0x62, 0x22, 0x18, 0xe6, 0xde, 0x87, 0x31, 0x3b, 0x6f, 0x2b, 0x2f, 0xb0, 0x95, 0xc6, 0xa9, 0x36, 0xcf, 0x51, 0x40, 0xd5, 0xe6, 0x9d, 0xeb, 0x9f, 0x5b, 0x9e, 0x82, 0x47, 0xb9, 0x17, 0xdf, 0x94, 0x1e, 0x0c, 0x85, 0x48, 0xd3, 0x00, 0x7d, 0x3a, 0xe0, 0x45, 0xd2, 0xe7, 0x96, 0x27, 0xb6, 0xc5, 0x88, 0x0b, 0x3b, 0x52, 0xcc, 0xa7, 0xbf, 0x0b, 0x20, 0xa5, 0x84, 0x12, 0x29, 0xc9, 0xd9, 0xec, 0xd1, 0xd4, 0x05, 0x4d, 0x38, 0x96, 0x3e, 0x9e, 0xaa, 0x53, 0x85, 0xe2, 0x5f, 0x1c, 0x43, 0xce, 0x88, 0xb1, 0x49, 0x6a, 0x64, 0xc4, 0x11, 0x91, 0x4a, 0x06, 0xac, 0x4c, 0xe7, 0x03, 0x5b, 0x44, 0xf3, 0xbd, 0x9c, 0x7a, 0x56, 0xb5, 0xef, 0x98, 0x18, 0xa9, 0xbf, 0xb9, 0xb9, 0x7d, 0x16, 0xb8, 0x21, 0xe5, 0x2d, 0xfc, 0xb0, 0x16, 0x69, 0x6c, 0x81, 0x59, 0xf0, 0xcd, 0xd3, 0x2b, 0x0c, 0x5a, 0x8b, 0x5b, 0xa7, 0x38, 0x21, 0x85, 0x96, 0xd3, 0x43, 0xdf, 0xb3, 0x81, 0xde, 0xa2, 0x4a, 0x23, 0xb3, 0x95, 0x46, 0x41, 0x9c, 0x24, 0x76, 0x27, 0xe6, 0xc2, 0x8e, 0x52, 0xe9, 0xe5, 0x86, 0x0a, 0x0d, 0x45, 0x2f, 0x70, 0x6e, 0x9d, 0xcb, 0x3a, 0x70, 0xaa, 0xf4, 0xd8, 0xbf, 0xe1, 0x94, 0x95, 0x60, 0x4f, 0x11, 0x04, 0x0f, 0x23, 0x31, 0x1a, 0x35, 0x20, 0xe1, 0x3d, 0x2b, 0xf2, 0x6f, 0xfd, 0x7e, 0x25, 0x88, 0xd7, 0xdf, 0x25, 0x3e, 0x3c, 0x90, 0x09, 0xd3, 0x9b, 0x08, 0xbe, 0x5e, 0x23, 0x9a, 0x8f, 0xdc, 0x0a, 0xda, 0x7f, 0x00, 0xa3, 0xb2, 0x80, 0x86, 0xc4, 0x2d, 0x01, 0xbd, 0x50, 0x13, 0x28, 0xe3, 0xae, 0x87, 0x19, 0x71, 0xcc, 0x31, 0xdd, 0x7a, 0x81, 0x40, 0x55, 0x66, 0x54, 0xa3, 0x9b, 0x30, 0x19, 0x69, 0x91, 0xa0, 0xd6, 0x4c, 0xaa, 0x60, 0xf3, 0x28, 0xce, 0x86, 0xc3, 0xee, 0xff, 0x52, 0xcf, 0x97, 0x2e, 0x41, 0x6f, 0x35, 0xfe, 0x2f, 0x6b, 0x56, 0x51, 0x15, 0x68, 0x53, 0xbc, 0xab, 0xb6, 0xd0, 0x88, 0x80, 0x57, 0x75, 0x4b, 0x87, 0x3e, 0x15, 0x73, 0xba, 0x3c, 0xa8, 0xc7, 0x5b, 0x7a, 0xec, 0x56, 0xf2, 0x56, 0x6d, 0x8d, 0x62, 0x86, 0xeb, 0x9e, 0x8f, 0x50, 0x85, 0xd2, 0x1c, 0xd7, 0xeb, 0x37, 0xeb, 0x96, 0xc1, 0x11, 0xbd, 0xcd, 0xf7, 0xf0, 0xbf, 0x9e, 0xba, 0xaf, 0x66, 0xe1, 0x64, 0x06, 0x4e, 0x6b, 0x48, 0x31, 0xd8, 0x06, 0x8b, 0x5c, 0x14, 0x33, 0x47, 0x78, 0xab, 0xd0, 0x56, 0x52, 0xd2, 0xf1, 0x0f, 0xc0, 0x12, 0xd2, 0x07, 0x8f, 0xe7, 0xe9, 0x85, 0x6f, 0x17, 0x6d, 0xa5, 0x7e, 0xdf, 0xff, 0xd1, 0xb8, 0x12, 0x74, 0x0d, 0xf6, 0xd2, 0xcb, 0xc3, 0xae, 0x8c, 0x3a, 0x5e, 0xcf, 0x46, 0x6f, 0xb3, 0xc1, 0xfb, 0x87, 0xcd, 0x8c, 0x93, 0xf3, 0x10, 0x90, 0x2a, 0xea, 0x9d, 0x92, 0x38, 0x75, 0x07, 0x91, 0x7b, 0x5b, 0x5d, 0x64, 0x32, 0x63, 0x28, 0x58, 0xbf, 0x5c, 0xb4, 0x53, 0x6c, 0x1a, 0xcc, 0x95, 0xbb, 0x54, 0xb0, 0x81, 0x0a, 0xb8, 0x65, 0xf2, 0x71, 0x66, 0xa7, 0x84, 0x71, 0x20, 0x5b, 0x6c, 0x0e, 0x1b, 0xeb, 0x4d, 0xe0, 0xbd, 0x72, 0xa8, 0x9a, 0x6f, 0x6a, 0x44, 0x6d, 0xcc, 0x13, 0xdf, 0x5b, 0x76, 0xd8, 0x54, 0x0d, 0xee, 0xc9, 0xdd, 0x48, 0x1f, 0x80, 0xb2, 0x22, 0x20, 0xb7, 0xc4, 0x3a, 0x91, 0x34, 0x06, 0x97, 0xc2, 0xbb, 0xe8, 0x5f, 0x30, 0x4e, 0x3a, 0x2a, 0xb7, 0x26, 0xe8, 0x7e, 0xb9, 0xfc, 0x84, 0x04, 0x6e, 0xab, 0x93, 0x92, 0xd6, 0x18, 0xfc, 0x51, 0x52, 0x8d, 0x84, 0x45, 0x71, 0x0a, 0x6f, 0x38, 0x37, 0x40, 0x94, 0xc9, 0xbc, 0xa3, 0x87, 0x50, 0xaa, 0x3b, 0x76, 0xab, 0xb4, 0xc3, 0xa4, 0xf4, 0xa6, 0x07, 0xfe, 0x15, 0xac, 0x81, 0x31, 0x4b, 0xfa, 0xcb, 0x5e, 0xa1, 0xb3, 0x3a, 0x77, 0xc9, 0x85, 0xd8, 0xa7, 0x34, 0xf5, 0xf1, 0x06, 0x99, 0x20, 0x05, 0x50, 0xeb, 0x6d, 0x67, 0x2c, 0x51, 0xb8, 0x21, 0xe3, 0x79, 0x40, 0x18, 0x83, 0xbf, 0xa5, 0xe2, 0x9e, 0xb2, 0x9e, 0xc6, 0x58, 0x8d, 0xb6, 0x4b, 0xa8, 0xa6, 0x86, 0x16, 0xcb, 0x8f, 0x67, 0x00, 0xa5, 0x5c, 0x45, 0xdd, 0x46, 0x7b, 0xb3, 0x34, 0xd7, 0x55, 0x63, 0xfc, 0x2a, 0x3f, 0x6d, 0x24, 0xda, 0xd0, 0xf1, 0x5c, 0xe5, 0xbc, 0x80, 0xce, 0x09, 0x4d, 0xcf, 0x96, 0x6e, 0xfb, 0xb1, 0x69, 0xcd, 0xbb, 0x8e, 0x12, 0xfc, 0xb0, 0xe7, 0x39, 0xaf, 0xe8, 0x04, 0xea, 0x2a, 0xbd, 0x11, 0x2e, 0x14, 0xa8, 0x6b, 0x87, 0x4a, 0xe8, 0xda, 0xd2, 0xd2, 0xc1, 0x3b, 0x35, 0x9b, 0x25, 0xb2, 0x18, 0x23, 0xd4, 0xb6, 0xb6, 0x33, 0x53, 0x34, 0xbc, 0xb6, 0x3f, 0x25, 0xc5, 0x68, 0x1f, 0xe7, 0xcb, 0x1e, 0x1e, 0xeb, 0x33, 0x80, 0x7f, 0xa2, 0x29, 0x3e, 0xa7, 0xb6, 0xfb, 0x02, 0xfe, 0x71, 0xbc, 0x9a, 0x4d, 0x22, 0x9c, 0x7e, 0x4f, 0x7d, 0xe3, 0x9e, 0x5e, 0x19, 0x20, 0xf1, 0x59, 0x71, 0xd8, 0x23, 0x58, 0xbb, 0xd1, 0x04, 0x9e, 0xdf, 0x0d, 0x4b, 0x72, 0x94, 0x7c, 0xb4, 0x41, 0xbe, 0x64, 0x2d, 0x88, 0x5d, 0x2e, 0x29, 0x08, 0x96, 0xcf, 0xb8, 0xa9, 0x38, 0x2d, 0x84, 0x7f, 0xec, 0xa8, 0xf3, 0x54, 0x4d, 0x36, 0xc8, 0x3a, 0x3c, 0x3a, 0x3d, 0xdb, 0x60, 0xf7, 0x2e, 0x08, 0x19, 0xd4, 0x6a, 0xe8, 0x12, 0xb5, 0x75, 0x2b, 0xd1, 0xaa, 0xc5, 0xea, 0xd8, 0x75, 0x2e, 0xe0, 0x61, 0x25, 0xc9, 0x86, 0x92, 0x07, 0x6f, 0x7d, 0x4f, 0xd4, 0xda, 0x0c, 0x47, 0xa0, 0xbf, 0xb4, 0x08, 0x97, 0xe5, 0xb1, 0x4c, 0x3a, 0xac, 0xd3, 0xc4, 0xec, 0x5c, 0x2a, 0x6e, 0x3d, 0xb3, 0xf8, 0xf0, 0x7b, 0xdf, 0x02, 0xfa, 0xd9, 0x03, 0x10, 0x96, 0x52, 0x3b, 0xa7, 0x96, 0x51, 0x52, 0x87, 0xb1, 0x28, 0xf8, 0xd6, 0x74, 0x62, 0xa4, 0xfc, 0xa4, 0xd0, 0x5a, 0xbe, 0x9e, 0x9d, 0x72, 0x10, 0x92, 0xac, 0x85, 0x34, 0xfd, 0xcc, 0x1d, 0x1a, 0x37, 0xc7, 0xb0, 0x1e, 0xc2, 0xcc, 0x81, 0x85, 0x7b, 0x21, 0x97, 0xa1, 0x7e, 0x9a, 0xe4, 0x6d, 0x62, 0x93, 0x26, 0xcd, 0x19, 0xfc, 0x81, 0x36, 0xe9, 0x8f, 0x7a, 0x36, 0xd9, 0x22, 0x6e, 0x35, 0x1a, 0xae, 0x5f, 0xfb, 0x9d, 0xf1, 0xf3, 0x19, 0x40, 0x40, 0x4f, 0xab, 0xb7, 0xe2, 0xe7, 0x6e, 0x23, 0xe6, 0x11, 0xa1, 0x87, 0x13, 0x1d, 0x9b, 0xc0, 0x21, 0x9c, 0x7c, 0x6a, 0xfb, 0x5b, 0xc0, 0x53, 0x07, 0x09, 0xd0, 0x57, 0xa6, 0x82, 0xe8, 0x64, 0x61, 0x4b, 0x7c, 0x01, 0xa7, 0x58, 0xfe, 0xd8, 0x7a, 0x0d, 0x05, 0xf1, 0xda, 0xc3, 0x40, 0xf7, 0x3a, 0xd5, 0x8e, 0xba, 0xae, 0xe2, 0x5c, 0xf6, 0x4b, 0x13, 0x09, 0xba, 0x99, 0xb4, 0x97, 0x4d, 0x69, 0xd2, 0x8b, 0x94, 0x5d, 0x2d, 0x15, 0x60, 0xad, 0xb0, 0x4e, 0xfc, 0x07, 0xef, 0x0d, 0xb5, 0x4a, 0x84, 0x59, 0x2d, 0xb9, 0x53, 0x9e, 0x3c, 0x89, 0xbb, 0x24, 0x77, 0x88, 0x63, 0x88, 0xa4, 0x2f, 0x11, 0x5d, 0x7c, 0xd4, 0xd0, 0xf5, 0x89, 0x39, 0xc1, 0x95, 0xde, 0x39, 0x40, 0x53, 0xd6, 0xff, 0x46, 0x17, 0x83, 0x27, 0x94, 0x33, 0xb6, 0xb9, 0x0c, 0xe8, 0x28, 0xb0, 0xc9, 0xc7, 0x96, 0x3e, 0xdd, 0xb9, 0x64, 0x9d, 0xee, 0x99, 0x59, 0x88, 0x59, 0xfe, 0x99, 0x3e, 0xaa, 0xd8, 0xaf, 0x62, 0x6e, 0xb9, 0xc9, 0xb1, 0x86, 0x5a, 0xba, 0xa7, 0x9a, 0xbf, 0xf7, 0x58, 0xff, 0xb1, 0xe2, 0x6d, 0x31, 0x7f, 0x3a, 0xde, 0x51, 0x95, 0x4e, 0x60, 0x9b, 0x53, 0x29, 0x83, 0xd3, 0x8e, 0xf2, 0x47, 0x6f, 0x86, 0x99, 0x89, 0xfb, 0x7d, 0x3b, 0x6d, 0x85, 0xe0, 0xb0, 0x32, 0x0b, 0x26, 0xcb, 0x5f, 0xb8, 0xc7, 0x2d, 0x57, 0xe1, 0xe2, 0x44, 0x81, 0x0a, 0x0b, 0xd4, 0x21, 0x0b, 0x4d, 0x68, 0xfe, 0x31, 0x21, 0x25, 0x8e, 0xfc, 0x38, 0xe2, 0x69, 0x32, 0x67, 0xff, 0x43, 0xbd, 0xc3, 0x7e, 0x3c, 0x0d, 0x7f, 0x6a, 0x70, 0x31, 0x9b, 0x41, 0x45, 0xa9, 0x53, 0x1a, 0x62, 0xa2, 0xaf, 0x03, 0x45, 0xe2, 0xf4, 0x13, 0x04, 0x47, 0xf9, 0x52, 0x05, 0xf5, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x02, 0xe4, 0x61, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x81, 0x00, 0x00, 0x20, 0xa4, 0x0f, 0x2b, 0x24, 0x82, 0x3a, 0x10, 0xf0, 0xcc, 0x73, 0x05, 0x2a, 0x3a, 0x68, 0xdc, 0xd3, 0x1b, 0x96, 0x77, 0x74, 0x51, 0x86, 0x4d, 0xd8, 0x66, 0x59, 0xd5, 0xa6, 0x31, 0x22, 0x48, 0x96, 0x55, 0x98, 0xd0, 0xb6, 0x5b, 0x1a, 0xbd, 0xe4, 0x97, 0x27, 0x3a, 0xc8, 0x39, 0x64, 0x7b, 0x1e, 0x6e, 0x84, 0x92, 0x38, 0xff, 0xb6, 0xcf, 0xc8, 0x4f, 0x4c, 0x47, 0xd3, 0xea, 0x8b, 0xa4, 0xcd, 0xa4, 0x17, 0x8c, 0x14, 0x2e, 0xb2, 0xec, 0x18, 0x3f, 0xa6, 0x6c, 0x8c, 0xae, 0x93, 0x1a, 0x26, 0xd6, 0x5f, 0x7e, 0x1d, 0x88, 0x40, 0x81, 0x85, 0xf9, 0x14, 0x70, 0xf3, 0xbf, 0xd6, 0x4a, 0xdd, 0x90, 0x51, 0x88, 0x0e, 0x90, 0x89, 0x9c, 0x95, 0xec, 0x3c, 0xa1, 0xeb, 0xb6, 0x52, 0x30, 0x3c, 0xd1, 0x0f, 0xb8, 0xf3, 0xbf, 0x42, 0x87, 0x98, 0x0b, 0xad, 0xdf, 0xe7, 0x07, 0x6c, 0x05, 0xf5, 0xd8, 0xb8, 0xce, 0x4e, 0xdc, 0xa9, 0xb2, 0x7d, 0xeb, 0xc1, 0x1d, 0xae, 0x82, 0xbe, 0x15, 0x4a, 0x72, 0xb6, 0x15, 0x0f, 0x75, 0x93, 0xb7, 0x59, 0x4e, 0x22, 0x9e, 0x62, 0xab, 0x81, 0xc7, 0xb0, 0xa9, 0xb7, 0xbb, 0x8c, 0x45, 0x3d, 0x04, 0xcc, 0x1f, 0x15, 0x1a, 0xef, 0x5b, 0x89, 0x77, 0x87, 0xfb, 0x61, 0x91, 0x5d, 0x96, 0x2a, 0x28, 0x39, 0xd4, 0xa2, 0xfc, 0xfa, 0x5b, 0x74, 0x93, 0x30, 0x6c, 0x74, 0x50, 0x2b, 0x26, 0x25, 0xd2, 0xdb, 0xf2, 0xa8, 0xcf, 0xa1, 0x67, 0xe8, 0x63, 0x0e, 0xd8, 0x56, 0xbc, 0x2b, 0xa7, 0x7e, 0x78, 0xa8, 0xaf, 0xe0, 0xf2, 0x04, 0xed, 0xe2, 0xdd, 0xfb, 0xf2, 0x8a, 0x04, 0xf9, 0xdf, 0xba, 0x0e, 0x42, 0x2b, 0xaf, 0xeb, 0xca, 0x17, 0x36, 0x67, 0x5b, 0xf3, 0x93, 0x23, 0x64, 0xeb, 0x9e, 0xd0, 0x3e, 0x51, 0x5d, 0xb7, 0x18, 0x4e, 0x09, 0x2a, 0x91, 0x80, 0xca, 0x73, 0x3b, 0x3f, 0xd6, 0x8e, 0x40, 0xa1, 0x74, 0xd0, 0x03, 0xed, 0xdd, 0x99, 0xe3, 0x87, 0x30, 0x54, 0x65, 0xe8, 0x8b, 0x32, 0x1e, 0xff, 0xa2, 0x1e, 0x25, 0xe7, 0x96, 0xfc, 0xe1, 0xb4, 0x1c, 0xfc, 0x0d, 0x41, 0x48, 0xda, 0x8a, 0x5a, 0x4a, 0x43, 0x50, 0xe6, 0x8b, 0xfb, 0x41, 0xcc, 0xa5, 0xbc, 0x38, 0xc1, 0xc0, 0x8e, 0x11, 0x8d, 0x5f, 0xd9, 0x60, 0x8f, 0x7c, 0xb8, 0x86, 0x51, 0xdc, 0x07, 0x0a, 0xfc, 0x06, 0x48, 0xb4, 0x4b, 0xe2, 0x25, 0x1f, 0x89, 0xd0, 0xcd, 0xcf, 0x92, 0x60, 0xf9, 0x63, 0x31, 0xb1, 0x2b, 0x7c, 0x86, 0x7a, 0xb0, 0x7d, 0x4d, 0x4d, 0x9a, 0xb6, 0x67, 0xa5, 0x48, 0x02, 0x04, 0x53, 0x13, 0x4d, 0x44, 0x86, 0x0d, 0x3d, 0xc0, 0x8b, 0x86, 0x36, 0xfd, 0x50, 0x98, 0x42, 0x18, 0x2f, 0xe3, 0x48, 0x01, 0xed, 0xd8, 0xbf, 0x68, 0x42, 0xde, 0x08, 0xe2, 0xe0, 0xb0, 0x9d, 0x81, 0x05, 0x4b, 0x5b, 0x4d, 0xcc, 0xa4, 0x14, 0xf8, 0x09, 0x73, 0x62, 0xaa, 0x83, 0x27, 0x49, 0x67, 0x75, 0x61, 0xae, 0x6a, 0xa3, 0xf6, 0xa1, 0x87, 0x45, 0x9f, 0x29, 0x99, 0xa5, 0xba, 0x4c, 0x07, 0x5d, 0xb1, 0x42, 0x49, 0x68, 0x94, 0xb8, 0xf9, 0xa0, 0x3c, 0x89, 0xc7, 0x09, 0xa5, 0x0e, 0x62, 0xa6, 0x33, 0xd0, 0x79, 0xf3, 0xd4, 0xc2, 0x72, 0xcb, 0x6b, 0x97, 0xa8, 0x83, 0x92, 0xa1, 0x43, 0xc2, 0x26, 0x50, 0x48, 0xd0, 0x2f, 0x63, 0x5f, 0xff, 0x46, 0xac, 0x7b, 0xe0, 0xe6, 0x4d, 0x8a, 0x2f, 0xb6, 0xf0, 0xfc, 0x58, 0x06, 0xde, 0x5e, 0x42, 0xef, 0x15, 0x74, 0x8b, 0xc4, 0x9e, 0x32, 0x97, 0x19, 0x15, 0x9f, 0x7d, 0x3f, 0xe9, 0xe7, 0x0d, 0x47, 0xd3, 0x6b, 0xad, 0xaf, 0x58, 0x02, 0x5a, 0xd0, 0xb5, 0xa9, 0x94, 0xd8, 0xd4, 0xae, 0xb1, 0xab, 0x93, 0x0f, 0x1b, 0xb4, 0xcb, 0xf7, 0x13, 0x2e, 0x25, 0x80, 0x61, 0x32, 0x47, 0x7c, 0x5a, 0xfa, 0x08, 0x9b, 0x65, 0x42, 0xaf, 0xec, 0x65, 0xaf, 0x7d, 0xb3, 0x87, 0x04, 0xc2, 0x35, 0xb0, 0x23, 0xa4, 0x54, 0xfc, 0xb6, 0x9f, 0x9b, 0xa8, 0xfb, 0x21, 0x6f, 0x51, 0x57, 0x00, 0xb8, 0x9a, 0x48, 0x53, 0x18, 0x69, 0xc1, 0xdc, 0x12, 0x4b, 0xb5, 0x87, 0x3a, 0x5f, 0x48, 0x8f, 0x43, 0xfd, 0x67, 0x51, 0x24, 0x8b, 0xd9, 0xf5, 0xc8, 0xfd, 0xa2, 0x99, 0xac, 0xbd, 0x46, 0x5d, 0x8e, 0xa7, 0xe4, 0x40, 0x2b, 0x27, 0x77, 0x63, 0x3a, 0xbe, 0x03, 0xaf, 0x73, 0x3e, 0xdc, 0xae, 0x6f, 0xea, 0xf0, 0xe2, 0xa0, 0xc5, 0x81, 0xd5, 0x77, 0x72, 0x19, 0x2b, 0x3c, 0x1c, 0xc1, 0xd3, 0xf4, 0xda, 0xf1, 0x6b, 0xbc, 0x3a, 0xd9, 0x0b, 0x8f, 0x28, 0xaa, 0x50, 0x49, 0x0b, 0xda, 0x3f, 0x53, 0x40, 0xf3, 0x73, 0x4e, 0x41, 0x98, 0x96, 0x67, 0x0a, 0xc4, 0x98, 0xd6, 0x1d, 0xae, 0x57, 0xbe, 0x82, 0x77, 0x87, 0x5d, 0xb5, 0xfe, 0x90, 0xe0, 0xd6, 0x85, 0x34, 0x6d, 0x78, 0xf0, 0x7e, 0xba, 0xed, 0x2b, 0xf2, 0xb7, 0xe4, 0x00, 0xee, 0x6e, 0xd2, 0x57, 0x6b, 0xbc, 0xcd, 0x31, 0x20, 0xbd, 0xbe, 0x8f, 0xc4, 0x8a, 0x34, 0x8b, 0x50, 0xfe, 0x7d, 0x3a, 0x57, 0xf1, 0x75, 0x69, 0xa6, 0xdd, 0xf3, 0xae, 0x77, 0x14, 0x98, 0x25, 0x9b, 0xe0, 0x11, 0x68, 0x66, 0xa7, 0xbf, 0xc7, 0x0e, 0xb3, 0x75, 0xd5, 0x2b, 0x44, 0x27, 0xd6, 0x9c, 0x7f, 0x32, 0x9c, 0x54, 0x3e, 0x1c, 0x6b, 0xa6, 0x20, 0x9d, 0xd8, 0xbf, 0x4b, 0xf1, 0x30, 0x48, 0x8b, 0x0a, 0x6d, 0x92, 0x21, 0x5c, 0xd3, 0x21, 0x0a, 0x25, 0x54, 0x3b, 0xa0, 0x15, 0xc8, 0x73, 0xf5, 0x38, 0xce, 0xff, 0x96, 0xf9, 0xbb, 0xbb, 0x3e, 0x9b, 0xba, 0x08, 0x24, 0xcd, 0x14, 0x77, 0x2e, 0xc6, 0x3e, 0x2d, 0x30, 0x19, 0x2c, 0xd5, 0x6a, 0x1e, 0x6d, 0x16, 0x78, 0xa8, 0x9b, 0xca, 0x44, 0x51, 0x74, 0x3b, 0x0e, 0x76, 0x07, 0xf1, 0x81, 0x8f, 0x43, 0x44, 0xf1, 0xf5, 0xab, 0x6e, 0x0a, 0x9b, 0xbb, 0xf7, 0xf6, 0xfb, 0xba, 0x2a, 0x9e, 0xc3, 0xa4, 0xc6, 0x9b, 0x78, 0x6c, 0x01, 0x37, 0x9b, 0x45, 0x2d, 0xee, 0x70, 0x32, 0xd8, 0x29, 0x98, 0xcb, 0x92, 0x42, 0x12, 0x2d, 0x31, 0x1d, 0xd5, 0x39, 0xa3, 0xac, 0x1e, 0x79, 0xb6, 0x54, 0x15, 0x97, 0x8b, 0xa2, 0x82, 0x12, 0xbc, 0xc6, 0x0c, 0x94, 0xcd, 0xcc, 0xe4, 0xb6, 0xda, 0x8d, 0x69, 0xe1, 0xa7, 0x65, 0x66, 0xad, 0x8d, 0xf1, 0x85, 0x6e, 0x1b, 0xd5, 0x9c, 0xf6, 0x45, 0x37, 0xd2, 0x8a, 0xc4, 0x3a, 0x80, 0x23, 0x3d, 0xa7, 0xa5, 0xa5, 0x92, 0x6a, 0xf7, 0x30, 0x06, 0x5f, 0xad, 0x19, 0x0f, 0x64, 0x2c, 0x37, 0xa5, 0xc1, 0xfb, 0xaa, 0x80, 0x6b, 0x71, 0xd8, 0x87, 0xb7, 0x11, 0x03, 0x66, 0x0b, 0x8c, 0xff, 0xc2, 0x16, 0x41, 0xe2, 0x5c, 0x6d, 0xdc, 0x0d, 0x51, 0x09, 0x9f, 0xae, 0xf7, 0x2a, 0xa8, 0x07, 0x91, 0xe4, 0x59, 0x9b, 0x32, 0xd8, 0x68, 0x50, 0x32, 0x99, 0x9f, 0x77, 0xe6, 0x91, 0xd5, 0x80, 0x33, 0xe7, 0xd8, 0x6d, 0xa9, 0xc9, 0xf2, 0xed, 0x7f, 0xd8, 0x15, 0x57, 0xd7, 0x5b, 0x4e, 0xa0, 0x9f, 0xee, 0x81, 0x8a, 0x3d, 0x6e, 0x51, 0x99, 0x3a, 0xdf, 0x33, 0xe3, 0x2b, 0xa2, 0x45, 0xd3, 0xb1, 0x4b, 0x77, 0x69, 0xcb, 0xee, 0x4a, 0xa8, 0x9d, 0x96, 0x8b, 0x97, 0x66, 0xd5, 0xd3, 0x11, 0xad, 0x7a, 0xd5, 0x3a, 0x45, 0x7b, 0x97, 0x00, 0x6f, 0x0f, 0x5b, 0x42, 0x78, 0x0d, 0xd8, 0xa6, 0xb1, 0xc7, 0xc1, 0xc0, 0x94, 0xe5, 0x2b, 0xcb, 0xc8, 0xea, 0x88, 0xa7, 0x9b, 0xf8, 0x6a, 0x6d, 0xfd, 0xf6, 0xf1, 0x05, 0x75, 0x59, 0xd1, 0xdf, 0xd5, 0xfc, 0xa2, 0x40, 0x48, 0x56, 0xba, 0x80, 0xc0, 0xac, 0x11, 0x32, 0x23, 0x4f, 0x36, 0xda, 0x8d, 0x14, 0x65, 0xdd, 0xda, 0x73, 0xcc, 0x04, 0x02, 0x8d, 0xc4, 0x66, 0xf1, 0x0b, 0x91, 0x65, 0xc7, 0xda, 0x5a, 0x42, 0x8a, 0x00, 0xbc, 0xf0, 0x96, 0x7a, 0xd8, 0x05, 0xd3, 0xe7, 0xa1, 0x16, 0xba, 0xbd, 0x2b, 0x38, 0x3d, 0x62, 0x76, 0xbf, 0x6e, 0x82, 0xdb, 0x56, 0xeb, 0x5c, 0x01, 0xd9, 0x58, 0x0d, 0x7e, 0x91, 0x33, 0xdd, 0x4c, 0x0c, 0x50, 0xcc, 0xdf, 0x7f, 0x11, 0x3a, 0x9a, 0x62, 0x21, 0xdb, 0x94, 0xe1, 0xdc, 0xbb, 0x86, 0x3f, 0x6d, 0x34, 0x42, 0x4b, 0xbb, 0x22, 0x4e, 0x1b, 0x7b, 0x6c, 0x75, 0x6f, 0x8c, 0xc0, 0xcf, 0x48, 0xe5, 0xc0, 0x53, 0xd5, 0x77, 0x2e, 0x58, 0x94, 0x36, 0x2c, 0xf0, 0xc8, 0x08, 0x55, 0xb2, 0xae, 0xec, 0x06, 0x8b, 0x1b, 0xa0, 0x51, 0x6f, 0x6b, 0xfa, 0x6f, 0xa0, 0x0a, 0xc5, 0x51, 0xf7, 0xe3, 0x21, 0x6d, 0x2b, 0x8c, 0x9d, 0x94, 0xe4, 0x9a, 0x26, 0xe5, 0x55, 0xdb, 0x35, 0xcf, 0x09, 0x8e, 0x2f, 0x59, 0x50, 0xa9, 0xd7, 0x4d, 0x92, 0xcf, 0x63, 0xfb, 0xc7, 0xe1, 0x41, 0xab, 0x1b, 0x77, 0xfe, 0xdc, 0xaf, 0x2e, 0x2a, 0x90, 0x50, 0x59, 0x73, 0x5b, 0x82, 0x34, 0x6a, 0xcd, 0xcf, 0x3c, 0x67, 0x3f, 0x1e, 0x8c, 0x02, 0xa0, 0x6b, 0xf2, 0xdb, 0x9e, 0xa3, 0xd0, 0xdd, 0xc2, 0x8b, 0x09, 0xf2, 0xb1, 0x17, 0x1c, 0x74, 0x38, 0x1e, 0xc1, 0xcf, 0x36, 0x25, 0x3d, 0x6f, 0x9c, 0x49, 0x79, 0x17, 0x73, 0xb6, 0x6f, 0x81, 0xf6, 0x65, 0xd7, 0xfa, 0xcd, 0x8a, 0x6f, 0x30, 0x42, 0x95, 0xf7, 0xa9, 0xa4, 0xeb, 0x4b, 0xae, 0x22, 0xab, 0x8b, 0x80, 0x38, 0x93, 0x5a, 0x63, 0x44, 0x61, 0x78, 0x54, 0xc6, 0x23, 0xe3, 0x47, 0xe6, 0x63, 0x23, 0x82, 0xb2, 0x53, 0x4b, 0x1b, 0xe2, 0x8f, 0xfe, 0x81, 0x21, 0xb4, 0xfd, 0x12, 0xb9, 0x4a, 0x84, 0x1c, 0xf8, 0x8a, 0xdd, 0x3c, 0xef, 0xef, 0x07, 0x35, 0x80, 0x90, 0x78, 0x03, 0x75, 0x46, 0xfb, 0x09, 0xf4, 0x18, 0x34, 0x70, 0x1c, 0xd8, 0xd7, 0x15, 0x30, 0xaa, 0x6e, 0x66, 0x1c, 0x2b, 0x62, 0x63, 0xd1, 0x28, 0x34, 0x37, 0x93, 0xb9, 0x83, 0x9f, 0x11, 0xae, 0xc5, 0x77, 0xdf, 0xf3, 0xdf, 0x2e, 0x22, 0x24, 0xf4, 0xe7, 0x3e, 0xb6, 0x14, 0x67, 0xc6, 0x4e, 0xae, 0x88, 0x19, 0xcb, 0x3a, 0x64, 0x34, 0xc8, 0xa2, 0xf6, 0xcb, 0x35, 0xc7, 0x93, 0x65, 0x65, 0xd7, 0xd9, 0x98, 0xa6, 0x93, 0xb1, 0xb7, 0xd3, 0xe0, 0xa9, 0x55, 0x02, 0x3f, 0xf9, 0x47, 0x01, 0x09, 0xa0, 0x92, 0xf0, 0x91, 0x98, 0xd4, 0x9b, 0xc8, 0xf1, 0x47, 0xf8, 0x19, 0x6b, 0x4c, 0xa0, 0x23, 0x67, 0xeb, 0xb5, 0xfa, 0x3b, 0xb1, 0x2b, 0xe0, 0xfb, 0x74, 0x7e, 0x3b, 0x58, 0x1a, 0xcf, 0x49, 0x24, 0xb0, 0x52, 0x44, 0x78, 0x8e, 0x06, 0xbf, 0x06, 0x4e, 0x3b, 0x24, 0x9f, 0x4c, 0x5a, 0x68, 0xd4, 0xef, 0x53, 0x4a, 0x4a, 0x85, 0x61, 0x7e, 0x45, 0x2d, 0x3f, 0x69, 0x2f, 0x6b, 0xa3, 0x2f, 0x24, 0x99, 0x9d, 0x45, 0x27, 0x71, 0xce, 0xd3, 0x2d, 0x9c, 0xa6, 0x7f, 0xd7, 0x74, 0x7a, 0x16, 0xd8, 0x07, 0x05, 0x94, 0x93, 0x4c, 0xc5, 0xb9, 0xe8, 0x45, 0xd4, 0x53, 0x65, 0x8b, 0xce, 0x08, 0x98, 0x45, 0xd9, 0x16, 0x0c, 0x45, 0xeb, 0x7a, 0xb3, 0x69, 0x70, 0xbd, 0xef, 0xbc, 0xcb, 0x6f, 0xaf, 0xfd, 0x67, 0x14, 0xcb, 0x34, 0xeb, 0x82, 0x45, 0xb7, 0x7f, 0x13, 0x24, 0xa0, 0xd2, 0x01, 0x86, 0x34, 0xb9, 0xa1, 0x79, 0x45, 0xe9, 0x01, 0x1e, 0x90, 0x01, 0x61, 0x6a, 0x19, 0xce, 0x50, 0xc0, 0xff, 0xc6, 0xb6, 0x83, 0x79, 0xf6, 0x2d, 0x6a, 0x83, 0x8c, 0x2a, 0x6a, 0xe3, 0xe9, 0x7c, 0x6c, 0x69, 0x60, 0xee, 0xb0, 0xaa, 0x14, 0x2a, 0x32, 0x64, 0x08, 0x92, 0x6c, 0x1f, 0x9b, 0xf7, 0xfe, 0xec, 0x46, 0x26, 0x90, 0xe0, 0x23, 0xa3, 0x29, 0xc7, 0x48, 0xde, 0x71, 0x18, 0x69, 0xe9, 0x26, 0x30, 0x01, 0x86, 0x46, 0xe4, 0x6a, 0xac, 0xa8, 0x2f, 0xf7, 0xd1, 0x5a, 0x71, 0xba, 0x6b, 0xa5, 0x20, 0xf1, 0x59, 0xe3, 0x45, 0xc2, 0x85, 0xe7, 0x98, 0xcc, 0x32, 0xef, 0xa8, 0x4b, 0xe9, 0xd3, 0xc6, 0x8e, 0x14, 0x87, 0xee, 0x06, 0xe6, 0xc0, 0x8e, 0x7a, 0xb9, 0x28, 0x71, 0x91, 0xdf, 0xe9, 0xc2, 0x0f, 0x2e, 0xa1, 0xc9, 0xf5, 0x1c, 0x69, 0x1a, 0x9f, 0xfb, 0x9b, 0x31, 0xcf, 0x25, 0xdb, 0xdc, 0x78, 0xb3, 0x12, 0x3a, 0x39, 0x78, 0xa7, 0xc6, 0xb6, 0x6f, 0x9d, 0x4b, 0x65, 0xdc, 0xf8, 0xcd, 0x67, 0x78, 0xee, 0xb0, 0x98, 0xb4, 0x05, 0xd6, 0x8f, 0x8b, 0xba, 0x65, 0x27, 0x07, 0xbf, 0x6a, 0x40, 0x94, 0xcc, 0x29, 0xb4, 0xe2, 0x5c, 0xb6, 0x42, 0x97, 0xb7, 0x0a, 0x98, 0x97, 0x5f, 0x86, 0xe2, 0x38, 0x4a, 0x39, 0xe3, 0x20, 0x00, 0xae, 0xe4, 0x78, 0xe4, 0xf0, 0xa0, 0x37, 0x11, 0xe1, 0x74, 0xad, 0xb4, 0x6e, 0x31, 0xad, 0xb5, 0xe9, 0xb1, 0x48, 0x44, 0x42, 0xbd, 0xc8, 0x25, 0x73, 0xd7, 0x45, 0x83, 0x85, 0x38, 0xfd, 0xcc, 0x69, 0xd4, 0xc7, 0x35, 0x2f, 0xb6, 0xd7, 0xcd, 0x6b, 0x8e, 0x87, 0x2d, 0xe3, 0x81, 0x44, 0x58, 0x16, 0x00, 0x88, 0x8e, 0x41, 0x13, 0xd1, 0xfe, 0x5a, 0xf7, 0xe1, 0x68, 0x5f, 0xeb, 0x0b, 0x04, 0x2e, 0x10, 0xf3, 0x67, 0xc2, 0xe3, 0x1c, 0xea, 0x3c, 0xf6, 0xc5, 0x4d, 0x85, 0x8c, 0x6e, 0x10, 0xa4, 0x52, 0x3f, 0xfd, 0x36, 0x45, 0xd8, 0x03, 0x45, 0xe8, 0xd5, 0x73, 0x1f, 0x00, 0x90, 0x3e, 0x7b, 0x9f, 0x4b, 0x2a, 0x59, 0x4a, 0x01, 0xfa, 0xfc, 0x7d, 0xe6, 0x9b, 0x08, 0x46, 0x0b, 0x20, 0x13, 0xdc, 0xaa, 0x86, 0x47, 0x17, 0x4a, 0xe8, 0xa9, 0xab, 0x16, 0x73, 0x45, 0xc7, 0xb5, 0x84, 0x1c, 0x36, 0x9f, 0x48, 0x89, 0x22, 0xe2, 0xf1, 0xd1, 0x62, 0x7c, 0xb8, 0x79, 0x6d, 0x97, 0x4a, 0x80, 0x6b, 0x69, 0x13, 0xb4, 0x7e, 0x21, 0x34, 0xe7, 0xb3, 0xf1, 0xe2, 0x9b, 0x1f, 0x53, 0x24, 0xcd, 0x07, 0x40, 0x92, 0xed, 0xf1, 0xb3, 0x91, 0x15, 0x76, 0xac, 0xdd, 0x1c, 0x3f, 0xb0, 0xdb, 0x73, 0xfd, 0xb2, 0x98, 0xa1, 0x87, 0x2d, 0xe8, 0x43, 0x19, 0x86, 0x4f, 0xe7, 0x22, 0x77, 0x25, 0x16, 0x10, 0x97, 0xfb, 0x02, 0x0a, 0x57, 0xb4, 0xa1, 0x34, 0x1f, 0x5e, 0x04, 0xea, 0xdc, 0x0b, 0xac, 0x41, 0x9d, 0xe0, 0xd2, 0x9e, 0x5c, 0x3f, 0x25, 0x0a, 0x1d, 0x0a, 0xf6, 0x36, 0x7b, 0xc7, 0x33, 0x16, 0xb0, 0xc3, 0xca, 0x36, 0x1c, 0xd8, 0xd3, 0x09, 0x72, 0xd2, 0xee, 0x00, 0x98, 0xea, 0x30, 0x06, 0x20, 0x5b, 0x15, 0x9a, 0xa0, 0x42, 0x60, 0x5d, 0x41, 0x9b, 0x37, 0xbe, 0x3c, 0x22, 0x2f, 0x15, 0xeb, 0x3e, 0x28, 0x24, 0x1d, 0x54, 0xb0, 0x0f, 0xd3, 0xba, 0xb3, 0x9c, 0xa7, 0x3f, 0x0e, 0x23, 0x35, 0xc3, 0x0e, 0x33, 0x98, 0x98, 0xf7, 0x08, 0xb0, 0x65, 0xd6, 0x3c, 0xdd, 0x58, 0x9c, 0x1c, 0x2c, 0xc2, 0x94, 0x0c, 0x86, 0x87, 0xab, 0x8a, 0x64, 0xd3, 0x88, 0x3b, 0x97, 0xb7, 0x31, 0x38, 0x1a, 0x08, 0xa2, 0x2f, 0xc9, 0xf1 }; +constexpr AccessUnit USER_DATA_LARGE_AU_EXPECTED_AU = { 0x15f90, 0x159b2, false, 0, {}, { 0xe6, 0xce, 0x63, 0xc5, 0xc9, 0x69, 0xbf, 0x14, 0xa4, 0x80, 0x23, 0x67, 0xff, 0x8f, 0x9f, 0x76, 0xf7, 0x10, 0x3e, 0xdb } }; + + +const std::vector ATRACX_SKIP_BYTES_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xf3, 0x3b, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0x7a, 0x01, 0xe0, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x1a, 0x00, 0x80, 0x20, 0x90, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x00, 0x01, 0xbd, 0x07, 0x5a, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xa7, 0xab, 0x1e, 0x73, 0x3b, 0x00, 0x4f, 0x02, 0xa0, 0x0f, 0xd0, 0x00, 0x52, 0x18, 0x4c, 0xe5, 0xfb, 0x25, 0x04, 0x81, 0xb1, 0x90, 0x29, 0x50, 0x65, 0x71, 0xac, 0x47, 0xdb, 0x51, 0x9e, 0x31, 0x43, 0xd2, 0x41, 0xc8, 0x06, 0xd3, 0xd3, 0x41, 0x13, 0x26, 0xef, 0xff, 0xba, 0x03, 0xc3, 0x38, 0x5c, 0xb5, 0x79, 0xb1, 0x88, 0x4e, 0x7c, 0xea, 0x44, 0x40, 0x9d, 0x56, 0x92, 0x3c, 0x21, 0x79, 0x83, 0xa9, 0x45, 0x10, 0x5c, 0x9c, 0xca, 0x93, 0x9c, 0x24, 0x89, 0xb5, 0xf5, 0x41, 0x2a, 0xc1, 0x70, 0xfa, 0x7d, 0xe3, 0x6c, 0x48, 0x0a, 0xc8, 0x16, 0xdf, 0xd9, 0x29, 0x95, 0xfc, 0x91, 0x18, 0x0c, 0xd4, 0x63, 0x3e, 0x1f, 0x3a, 0x60, 0x4b, 0xb2, 0x9b, 0x37, 0x4b, 0xf7, 0x70, 0x78, 0xb4, 0x19, 0xb9, 0xd4, 0x3f, 0x03, 0x28, 0xf7, 0x00, 0x22, 0x0b, 0xff, 0x39, 0x21, 0x23, 0xa0, 0xe5, 0xe9, 0xeb, 0x77, 0xfc, 0x81, 0x06, 0x32, 0xe0, 0x83, 0xde, 0x72, 0x7d, 0x49, 0x94, 0x98, 0x08, 0x0b, 0xde, 0xf1, 0x72, 0x8a, 0x78, 0xdb, 0xe9, 0x03, 0x2a, 0xe2, 0x03, 0x87, 0xa5, 0xf1, 0x77, 0xfb, 0xaa, 0x10, 0x78, 0x49, 0x3e, 0x53, 0x0c, 0x96, 0xe2, 0xe6, 0x08, 0x84, 0x11, 0x69, 0x78, 0xd1, 0x78, 0xff, 0xab, 0xd1, 0xa7, 0xc2, 0x20, 0xa5, 0x42, 0x88, 0x07, 0x2c, 0xd9, 0x1e, 0x2d, 0xf4, 0xf2, 0x72, 0x63, 0xcf, 0x01, 0x48, 0xce, 0xc1, 0x42, 0x74, 0xaf, 0x58, 0xb3, 0xbe, 0x85, 0xc7, 0xb8, 0xdb, 0x9f, 0x3b, 0x09, 0x2c, 0x86, 0x4d, 0x00, 0x2b, 0xcc, 0x88, 0x7d, 0xb5, 0x29, 0x5b, 0xc9, 0x94, 0xf4, 0x03, 0x57, 0x0c, 0x69, 0x32, 0x3a, 0x31, 0x3b, 0x88, 0xa8, 0x5e, 0x71, 0x1e, 0xf7, 0x16, 0x84, 0xc0, 0xd4, 0x0d, 0x3e, 0x90, 0xb4, 0x3b, 0xda, 0x28, 0xaf, 0x35, 0x4f, 0x8c, 0x56, 0x23, 0xe5, 0x65, 0x27, 0x0c, 0x62, 0x69, 0xad, 0x64, 0x76, 0xea, 0x82, 0x05, 0x44, 0xdd, 0xae, 0xf8, 0x18, 0xed, 0x25, 0xcd, 0xb2, 0x58, 0x50, 0x5a, 0xbd, 0xae, 0xf5, 0x15, 0x4a, 0xb7, 0xe7, 0x7d, 0x74, 0x0c, 0x40, 0x2b, 0x84, 0x3b, 0x20, 0x8c, 0x2e, 0x69, 0x36, 0x61, 0xc9, 0xdc, 0x7d, 0x78, 0x99, 0xe1, 0xf9, 0x49, 0x8e, 0xe4, 0x6c, 0x5e, 0x07, 0xce, 0x37, 0xbe, 0x86, 0xde, 0xec, 0xb7, 0x62, 0xa1, 0x51, 0xbf, 0xde, 0xce, 0xbd, 0x58, 0x37, 0x04, 0x20, 0x15, 0x90, 0xc9, 0x26, 0x45, 0xcd, 0x4f, 0x36, 0xd0, 0xc3, 0xc7, 0x46, 0x52, 0x4d, 0x51, 0xc0, 0x8c, 0x35, 0xd8, 0xfa, 0x97, 0xf5, 0x63, 0xf2, 0x92, 0xc2, 0xa1, 0x52, 0xac, 0x68, 0xce, 0xfd, 0xb9, 0xed, 0x25, 0x8f, 0xbf, 0xfb, 0x80, 0xa0, 0x26, 0x84, 0xe6, 0xbb, 0x75, 0x27, 0x14, 0x42, 0xd1, 0x4d, 0x53, 0xca, 0x9a, 0xf9, 0x22, 0x9a, 0xe2, 0xc6, 0x83, 0x51, 0xa2, 0x9b, 0xed, 0xf0, 0xd8, 0xf6, 0x45, 0xc4, 0xfe, 0xef, 0x50, 0xe5, 0x84, 0x4a, 0x7c, 0x50, 0x3a, 0x33, 0x07, 0xd0, 0xd2, 0x87, 0xcc, 0x38, 0xef, 0xba, 0x46, 0xa7, 0x61, 0xc6, 0x98, 0xce, 0x86, 0xb0, 0x9d, 0x7f, 0xf1, 0x88, 0x53, 0xf3, 0xca, 0xfa, 0x2e, 0x93, 0x96, 0xd3, 0x17, 0x2c, 0x46, 0x12, 0x2f, 0xe2, 0xc7, 0x60, 0x96, 0x45, 0x79, 0x0a, 0xc7, 0x5a, 0x36, 0xdf, 0x5a, 0x85, 0xd9, 0x8a, 0x2d, 0x94, 0x77, 0x42, 0x4d, 0x6f, 0xa1, 0xee, 0x1e, 0x1d, 0x7c, 0x1d, 0x9a, 0x1c, 0x75, 0x69, 0xa6, 0x55, 0xdf, 0x1b, 0xda, 0x16, 0xd1, 0x40, 0x34, 0xd9, 0xfa, 0x74, 0x94, 0xd2, 0x4f, 0x0f, 0xfe, 0x7d, 0xd0, 0x86, 0x02, 0xaf, 0xb5, 0x24, 0xb8, 0xdb, 0xc9, 0x6f, 0x3a, 0x8f, 0xd4, 0xe0, 0x98, 0x69, 0x39, 0xbc, 0x93, 0xf3, 0x42, 0x35, 0xaf, 0x2c, 0xbb, 0x2c, 0x66, 0xb7, 0xd2, 0x2b, 0x89, 0xed, 0x59, 0x7e, 0x1a, 0x4d, 0xea, 0x78, 0x28, 0x07, 0x87, 0x4f, 0x83, 0x98, 0xa8, 0x24, 0x47, 0xc2, 0xec, 0x0d, 0x90, 0x4a, 0xcb, 0x48, 0xed, 0x41, 0xfc, 0xf0, 0x45, 0xda, 0x7c, 0xcd, 0x58, 0xfa, 0x60, 0x14, 0x0d, 0x09, 0xdc, 0x49, 0x2e, 0xc1, 0x31, 0x8c, 0x53, 0xf0, 0x66, 0xe7, 0x05, 0x69, 0x00, 0x9b, 0x74, 0xab, 0x20, 0x5a, 0x38, 0xea, 0xb2, 0xf1, 0xfa, 0x86, 0x9f, 0xe7, 0x09, 0x1d, 0x28, 0xf7, 0x28, 0xc4, 0xd6, 0x7d, 0x1e, 0x0d, 0xb6, 0x34, 0xdc, 0xf2, 0xb8, 0xde, 0x3e, 0xc9, 0x72, 0xb8, 0xb7, 0xca, 0xa9, 0xd1, 0x6e, 0xda, 0x49, 0x9e, 0xa0, 0xa6, 0xc1, 0x70, 0x50, 0x36, 0xe8, 0x9b, 0x8d, 0x85, 0xdc, 0x7b, 0xdf, 0xd1, 0x7c, 0x9d, 0xa3, 0x9e, 0x7e, 0xc1, 0x6d, 0xb4, 0x34, 0x7a, 0xa4, 0x81, 0x81, 0x98, 0x01, 0xac, 0x9d, 0xb1, 0xa8, 0xce, 0x98, 0x64, 0xf4, 0x87, 0xcc, 0x38, 0x31, 0x78, 0xa2, 0x2e, 0xed, 0xd8, 0x5b, 0x5f, 0x85, 0x97, 0xca, 0x4d, 0x24, 0x5c, 0x2a, 0xb9, 0x5a, 0xcb, 0x77, 0x1c, 0xf6, 0xdd, 0x00, 0x22, 0xd7, 0x0f, 0xd0, 0x01, 0x00, 0xb5, 0xf3, 0xca, 0xde, 0xeb, 0x93, 0x07, 0x58, 0xd3, 0x9e, 0x57, 0xaf, 0x26, 0xe3, 0x44, 0x06, 0x5a, 0x2e, 0x14, 0xcd, 0xcb, 0x11, 0x4f, 0xe8, 0xce, 0x3a, 0x30, 0x44, 0xae, 0x50, 0x8b, 0xcf, 0xcb, 0x6f, 0x0e, 0x31, 0x51, 0xca, 0xfa, 0xc4, 0x53, 0x05, 0xa4, 0xa3, 0x35, 0x42, 0x6a, 0xce, 0xab, 0x0c, 0xeb, 0xc4, 0xc4, 0x84, 0xa8, 0x05, 0xc4, 0x84, 0x5e, 0x5c, 0xbe, 0xa6, 0xc8, 0xf9, 0x37, 0x48, 0x8a, 0x9b, 0x40, 0x1c, 0x7f, 0x69, 0xd1, 0xc5, 0xac, 0x37, 0x56, 0x98, 0x89, 0x48, 0xaf, 0x86, 0x65, 0xda, 0x20, 0xbd, 0x1f, 0x0d, 0xe8, 0x8d, 0x73, 0xe0, 0x84, 0x9c, 0x83, 0x63, 0xcb, 0x79, 0x84, 0x62, 0x73, 0x0c, 0x91, 0x13, 0xa7, 0x86, 0x18, 0x97, 0xba, 0xc6, 0x6c, 0x54, 0x29, 0x52, 0x6a, 0x7f, 0x6a, 0x5a, 0x34, 0x58, 0x48, 0x75, 0x78, 0xe3, 0xc5, 0xef, 0xcb, 0x37, 0xbe, 0xf1, 0x08, 0x9e, 0xc9, 0xd4, 0xb1, 0x21, 0x15, 0x64, 0xd2, 0x40, 0x3b, 0xe3, 0xe7, 0xe8, 0x43, 0x17, 0xa9, 0xf7, 0x54, 0xb1, 0xe8, 0xcf, 0xd5, 0xd4, 0xbc, 0xce, 0xed, 0x91, 0xb4, 0xb5, 0xa9, 0x93, 0xe5, 0x3e, 0x44, 0x63, 0xe3, 0x88, 0x3e, 0xef, 0x0d, 0x05, 0x54, 0x3a, 0xcc, 0x0c, 0x88, 0x73, 0x92, 0x57, 0xb3, 0xc8, 0xc1, 0xef, 0x64, 0xb3, 0xd2, 0x5e, 0x07, 0xa4, 0x54, 0x2f, 0x8c, 0x36, 0xb0, 0x02, 0x0c, 0x59, 0xae, 0xcf, 0x29, 0x2a, 0x3b, 0x0b, 0x24, 0x49, 0xf5, 0xde, 0xe9, 0xf7, 0x5b, 0x8d, 0x7e, 0x5b, 0xc9, 0xcb, 0xae, 0x91, 0x8f, 0xa3, 0xc6, 0x83, 0xc7, 0x0c, 0xc8, 0xa5, 0xbe, 0x81, 0x6b, 0x0d, 0x1e, 0x11, 0xc1, 0x8f, 0x17, 0xcf, 0x70, 0xc2, 0x40, 0x8d, 0xb9, 0x29, 0xfe, 0x56, 0xd4, 0xda, 0x8f, 0x09, 0x50, 0x60, 0x10, 0x00, 0x80, 0xb9, 0x2a, 0xc7, 0x23, 0x9f, 0x2d, 0x22, 0xcf, 0x88, 0xc5, 0x00, 0xd6, 0x09, 0x9c, 0x1d, 0x9f, 0x8d, 0x8d, 0x60, 0xb2, 0x81, 0x38, 0x47, 0xf4, 0x68, 0x23, 0xe1, 0xcc, 0x85, 0xb1, 0x2f, 0x2f, 0x08, 0xa1, 0xf0, 0xc8, 0xd8, 0xdb, 0x25, 0xcc, 0x86, 0xb4, 0xde, 0x1b, 0xf8, 0x0b, 0x18, 0xbc, 0xc4, 0xc1, 0xaf, 0xee, 0x6c, 0x8b, 0xc7, 0x43, 0xdc, 0x1c, 0x23, 0x88, 0xdc, 0x16, 0x3c, 0x6f, 0x63, 0x54, 0x28, 0x45, 0xf6, 0x6b, 0x99, 0x18, 0x7b, 0x86, 0x84, 0x27, 0x31, 0xe8, 0x36, 0x78, 0xb6, 0xc5, 0xe6, 0xf0, 0x4c, 0x3b, 0x11, 0x4b, 0xc3, 0x94, 0x30, 0xcb, 0xb1, 0x25, 0x87, 0x83, 0xf3, 0x6d, 0x26, 0x8b, 0x95, 0x46, 0x64, 0x8e, 0x8c, 0x54, 0x5e, 0x75, 0xfb, 0xfb, 0x43, 0x26, 0xe5, 0x86, 0x86, 0x99, 0x76, 0x53, 0x35, 0xb7, 0x5c, 0x29, 0xfe, 0xc4, 0xc7, 0x09, 0xc4, 0x51, 0x16, 0x1a, 0xb6, 0xf2, 0x44, 0x96, 0x41, 0x2d, 0x92, 0xc0, 0x6a, 0x64, 0x13, 0x52, 0x5b, 0xbe, 0x07, 0xf5, 0x25, 0x34, 0x33, 0xa0, 0x15, 0x59, 0x9a, 0xfd, 0x16, 0x83, 0x54, 0x2f, 0xfc, 0x62, 0x5b, 0x90, 0xb6, 0x34, 0x85, 0x4c, 0xbb, 0x17, 0x1a, 0xed, 0x3e, 0x39, 0xa8, 0x3d, 0xf2, 0xdc, 0x08, 0x07, 0x6b, 0x6c, 0xbf, 0xb3, 0x69, 0x25, 0x25, 0xb6, 0x97, 0xfe, 0x03, 0x4b, 0x8c, 0xe4, 0x87, 0x05, 0x19, 0x6e, 0xd1, 0x1a, 0x34, 0x93, 0x67, 0x84, 0x00, 0x19, 0x5f, 0x4f, 0xce, 0xbf, 0x06, 0xb5, 0x15, 0x51, 0x4c, 0xe2, 0xbd, 0x33, 0x59, 0x7e, 0x32, 0x0e, 0xf1, 0x52, 0x22, 0xa9, 0x93, 0xb3, 0xe7, 0x23, 0x91, 0x78, 0xe3, 0x0b, 0x6f, 0xb0, 0x6e, 0x10, 0x50, 0x62, 0x09, 0x4b, 0x53, 0xfc, 0xf4, 0x58, 0xff, 0x46, 0x70, 0xc2, 0x46, 0x02, 0x2e, 0x70, 0xb0, 0x80, 0xc5, 0x26, 0xab, 0xdb, 0x39, 0x3a, 0x25, 0xa2, 0xcf, 0x0f, 0xd9, 0x21, 0xd6, 0x0e, 0x08, 0x92, 0x11, 0x08, 0x76, 0xdd, 0xed, 0x59, 0xf1, 0xbd, 0xb5, 0x30, 0xae, 0x75, 0x63, 0xc6, 0x47, 0x53, 0xcc, 0x75, 0x31, 0xbe, 0x3e, 0xfb, 0xe1, 0x97, 0xd7, 0x52, 0x05, 0x0c, 0x1c, 0xe5, 0x85, 0x40, 0xa9, 0x21, 0x2b, 0x5c, 0xf8, 0x8f, 0x30, 0xe9, 0x94, 0x04, 0x0a, 0x8d, 0x40, 0x94, 0x2f, 0x8d, 0xe3, 0x55, 0xb7, 0xa4, 0x3a, 0x17, 0x0a, 0x1a, 0xab, 0xa9, 0x04, 0x28, 0xb9, 0x4d, 0x3c, 0x10, 0xe8, 0x66, 0x5e, 0x84, 0x23, 0x9c, 0xbb, 0x2e, 0xe8, 0xe5, 0x85, 0x84, 0xc8, 0x45, 0x57, 0x35, 0x0b, 0x25, 0x08, 0xd8, 0x84, 0x9e, 0xdd, 0xb4, 0xd2, 0x10, 0x9b, 0xb1, 0xb8, 0xe1, 0x15, 0xea, 0x37, 0x4c, 0x33, 0x60, 0x8e, 0x83, 0x8a, 0xb6, 0x60, 0xa1, 0xfe, 0x17, 0x8e, 0x24, 0x21, 0x94, 0x99, 0xed, 0x20, 0xa9, 0x85, 0x07, 0x89, 0xae, 0xa4, 0xa5, 0x44, 0x67, 0xb9, 0x20, 0x6d, 0xfb, 0x55, 0xe9, 0x01, 0x02, 0x6e, 0xad, 0x83, 0x6f, 0x7f, 0x28, 0x0a, 0x89, 0xbe, 0x64, 0x63, 0x3b, 0x34, 0x56, 0x2f, 0x6a, 0xbe, 0x2d, 0x30, 0x1a, 0x4f, 0xab, 0x2c, 0x8e, 0xef, 0x6d, 0x13, 0xb4, 0x54, 0xf9, 0x52, 0x7f, 0x0c, 0xe6, 0x85, 0x0a, 0x9d, 0x8f, 0xf0, 0x9f, 0x16, 0x19, 0x67, 0xfa, 0xd9, 0xae, 0x99, 0x71, 0xc0, 0x4d, 0xea, 0xca, 0xd3, 0x89, 0x36, 0xee, 0x24, 0x11, 0x4c, 0xc8, 0x9a, 0xdd, 0xe5, 0xef, 0xc4, 0x5d, 0x61, 0xfd, 0xe6, 0x0f, 0xab, 0xa4, 0xe6, 0x3d, 0xea, 0xea, 0xd8, 0xe6, 0xf9, 0x1a, 0x33, 0x09, 0xc1, 0x56, 0x08, 0xca, 0xf7, 0xd3, 0xb2, 0x35, 0xcf, 0x31, 0xe3, 0x97, 0xac, 0x71, 0x09, 0xa8, 0xdc, 0x81, 0x58, 0x04, 0xec, 0x66, 0x15, 0xad, 0x20, 0x63, 0xd5, 0x95, 0x19, 0xc4, 0xd2, 0x69, 0xb5, 0x19, 0x95, 0x1c, 0xb8, 0x0e, 0x89, 0xe9, 0xca, 0x2a, 0x3c, 0xd0, 0xce, 0x23, 0x5d, 0xbf, 0xae, 0x93, 0xc7, 0xef, 0x14, 0x36, 0x1e, 0x2a, 0xaf, 0x05, 0xca, 0xf3, 0xfb, 0xd1, 0x6b, 0xd5, 0xb9, 0x33, 0xf8, 0x51, 0x76, 0xf1, 0x2d, 0x21, 0xda, 0x35, 0x07, 0xd3, 0xa5, 0x2d, 0x5b, 0xfd, 0x86, 0xf6, 0xdb, 0x75, 0x3e, 0xeb, 0x0c, 0x5a, 0xaf, 0xb2, 0x02, 0xdf, 0xd3, 0xa9, 0xe1, 0x1d, 0xb4, 0x87, 0x83, 0x62, 0xe5, 0x55, 0xb7, 0xb3, 0x16, 0x16, 0xfc, 0x90, 0xc0, 0xb6, 0xaf, 0x24, 0x03, 0x2e, 0x0e, 0x5f, 0x33, 0xde, 0x84, 0xc5, 0xb0, 0x90, 0x1d, 0xda, 0x0a, 0xb6, 0x2b, 0x73, 0x81, 0xbd, 0x5a, 0x68, 0x68, 0x97, 0xd1, 0x5a, 0x05, 0xd4, 0x23, 0x9b, 0xe2, 0xeb, 0x1a, 0x56, 0xbc, 0x84, 0xb0, 0x99, 0x73, 0xed, 0xb9, 0xd9, 0xb4, 0x0a, 0x11, 0x0a, 0x3d, 0x68, 0x54, 0x6c, 0xf0, 0x40, 0x78, 0xa1, 0x1b, 0x95, 0xbd, 0xb7, 0xa3, 0xbe, 0xdd, 0x2b, 0x02, 0xfe, 0x71, 0x1d, 0x39, 0x08, 0xe5, 0x33, 0x32, 0x7b, 0x77, 0xe8, 0x30, 0xcf, 0x24, 0x39, 0x3b, 0x31, 0x7b, 0x04, 0xc2, 0x40, 0xa9, 0x72, 0xc9, 0xb4, 0x4c, 0x4c, 0x60, 0x62, 0xda, 0x1c, 0x49, 0x24, 0x9d, 0x64, 0xf7, 0x89, 0xa3, 0x64, 0x59, 0x6d, 0x1b, 0x25, 0x78, 0xd1, 0xf5, 0xd5, 0x0b, 0x89, 0xd3, 0x97, 0xb0, 0xb0, 0x8f, 0x60, 0xf6, 0xdc, 0xe9, 0x81, 0x20, 0x96, 0x21, 0xee, 0x16, 0x95, 0x9a, 0x7d, 0x68, 0x9d, 0xb6, 0x82, 0x81, 0x2d, 0x96, 0xab, 0xaf, 0x45, 0x72, 0xee, 0xe3, 0xeb, 0xa2, 0xea, 0x65, 0x47, 0x9f, 0x17, 0xd6, 0x78, 0x8d, 0xa0, 0x3c, 0x73, 0x0e, 0xc5, 0xfd, 0x8e, 0x7a, 0x1a, 0xf0, 0x9f, 0xbe, 0x0c, 0xfb, 0xaa, 0x70, 0x77, 0xe1, 0xa8, 0xe1, 0x3c, 0x32, 0xd4, 0x43, 0x20, 0x7f, 0x82, 0x23, 0x64, 0x53, 0x7a, 0xca, 0xd8, 0x7f, 0x53, 0x4d, 0x68, 0xc3, 0x51, 0x60, 0x55, 0xe9, 0xaa, 0x2d, 0xa2, 0xca, 0x55, 0xc0, 0x6a, 0xed, 0xb7, 0xd9, 0x17, 0x96, 0xca, 0x25, 0xc6, 0x92, 0x9f, 0x38, 0x3b, 0x0b, 0x54, 0xc2, 0x3e, 0x2a, 0xd9, 0xe2, 0x43, 0x63, 0x22, 0x87, 0xa9, 0x9f, 0xa1, 0x4c, 0x08, 0xa7, 0x24, 0x37, 0x48, 0x8c, 0x75, 0x48, 0x7e, 0xe6, 0x51, 0xcc, 0xc9, 0x24, 0xa2, 0xcf, 0xb3, 0x4f, 0xff, 0x2f, 0xc8, 0xf0, 0xe8, 0x22, 0x65, 0x0d, 0x4b, 0x3b, 0xf7, 0x54, 0x2a, 0x35, 0x28, 0xfc, 0x92, 0x14, 0xbd, 0xec, 0x28, 0x6c, 0x11, 0x29, 0xf6, 0xcd, 0x7b, 0xb1, 0x52, 0xad, 0x05, 0x25, 0xf6, 0x5b, 0x52, 0xfa, 0xaa, 0xb8, 0x84, 0x21, 0x18, 0xdc, 0x82, 0x3d, 0xad, 0xd7, 0x0d, 0xe9, 0x9c, 0x2f, 0x54, 0xaf, 0x5a, 0xdc, 0x18, 0x9d, 0x47, 0xd4, 0x92, 0xa9, 0x3c, 0x6d, 0x92, 0x46, 0x3a, 0x26, 0x9a, 0xe4, 0x89, 0xa3, 0xf5, 0x38, 0xda, 0xe0, 0xf4, 0x83, 0x59, 0xf7, 0xe9, 0x42, 0xc3, 0xf0, 0x7e, 0xec, 0xdb, 0x7e, 0x7b, 0xb1, 0xd7, 0x9f, 0x29, 0x6d, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x79, 0x01, 0x00, 0x00, 0x24, 0xff, 0xdb, 0x8c, 0xe6, 0x0a, 0xfa, 0x51, 0x85, 0xb8, 0xdb, 0x2d, 0x4f, 0x23, 0x96, 0x35, 0xb2, 0x4b, 0x26, 0x29, 0x13, 0x41, 0x05, 0xb5, 0x52, 0x96, 0xed, 0xe2, 0x23, 0x2f, 0xc8, 0x03, 0xd7, 0xf8, 0xc1, 0x9e, 0x30, 0x12, 0xf0, 0x30, 0x52, 0x75, 0x34, 0xcb, 0x09, 0x30, 0x0c, 0xd7, 0xd4, 0xa7, 0x9a, 0x96, 0x86, 0x8e, 0x28, 0x1a, 0x63, 0x7e, 0xc7, 0xd1, 0x04, 0x56, 0xe1, 0xee, 0x7c, 0xd4, 0xeb, 0x28, 0x70, 0x11, 0x7d, 0xbc, 0x2a, 0xcd, 0x7a, 0xa3, 0xe0, 0xb2, 0x22, 0x4e, 0xa5, 0x07, 0x24, 0x82, 0xc4, 0x6d, 0x12, 0x09, 0x88, 0x9f, 0xd7, 0x5d, 0x9d, 0x62, 0xfa, 0x7f, 0x96, 0x02, 0xde, 0x9d, 0x49, 0x19, 0x51, 0x47, 0x08, 0x89, 0x03, 0x3f, 0xe2, 0xc6, 0xcb, 0x32, 0x79, 0xf2, 0xca, 0xea, 0x23, 0x67, 0xd5, 0xc1, 0x50, 0x9e, 0xb6, 0x58, 0x6c, 0xaa, 0x26, 0xf7, 0xcb, 0xa2, 0xa1, 0x05, 0x44, 0xad, 0x60, 0x77, 0x8e, 0xa8, 0x26, 0x3b, 0x7e, 0x7d, 0xba, 0xed, 0xc9, 0xa4, 0x16, 0x62, 0xdd, 0x7a, 0xc6, 0xd0, 0xec, 0x53, 0xfd, 0xa9, 0x53, 0x7a, 0x3e, 0x6d, 0xe5, 0x8a, 0xc1, 0xfd, 0xcc, 0xc4, 0xe2, 0x46, 0xfd, 0x5d, 0x42, 0x48, 0x8a, 0xdd, 0x6a, 0xbf, 0x32, 0x0d, 0x16, 0x0d, 0x0e, 0x19, 0x81, 0x1b, 0x31, 0xca, 0x4d, 0x4b, 0x98, 0x10, 0xdd, 0xc9, 0xd5, 0x44, 0x4e, 0xb3, 0x2f, 0xcd, 0x56, 0x03, 0x3f, 0x0f, 0xb7, 0x2b, 0x3c, 0xdd, 0x17, 0xbc, 0x75, 0xd0, 0x5c, 0xa7, 0xb8, 0x2a, 0xe5, 0xda, 0x9a, 0x44, 0x48, 0x4d, 0xe6, 0x9f, 0x62, 0xc8, 0xac, 0x0e, 0xdb, 0xec, 0xa1, 0xa5, 0x79, 0x50, 0xde, 0x57, 0xea, 0xbd, 0x31, 0x6d, 0xc5, 0xca, 0x76, 0x45, 0xfe, 0x7d, 0xc0, 0x07, 0x12, 0x9a, 0x43, 0x36, 0x95, 0xd2, 0x4b, 0x4e, 0xf1, 0xfd, 0xa1, 0xd8, 0x80, 0xff, 0x39, 0xa2, 0x53, 0x24, 0x0b, 0x05, 0x44, 0xea, 0x6a, 0x6b, 0x37, 0xa6, 0x3e, 0x77, 0x96, 0x71, 0xee, 0x7e, 0x71, 0x3c, 0xf0, 0x49, 0xe0, 0x11, 0x39, 0x3a, 0x72, 0xcb, 0x0f, 0x49, 0xda, 0xd9, 0xf2, 0x8b, 0x29, 0xc8, 0x99, 0x1b, 0x32, 0xb4, 0x4b, 0xaf, 0xf8, 0xde, 0xfa, 0x26, 0x42, 0xf9, 0xf1, 0xd0, 0xab, 0xf3, 0x05, 0x58, 0xd6, 0x53, 0x87, 0x70, 0x8e, 0x6e, 0x75, 0xbb, 0x08, 0xf9, 0xab, 0x9d, 0x8a, 0xdb, 0x00, 0xd0, 0x25, 0x2f, 0x9b, 0x00, 0x55, 0xd6, 0xfa, 0xc5, 0xa3, 0x1e, 0x5a, 0xb5, 0xaf, 0x47, 0x8d, 0x75, 0x2b, 0xf9, 0x6d, 0xf3, 0xb7, 0xf1, 0x75, 0x3a, 0xaf, 0x74, 0x28, 0x87, 0x46, 0x50, 0x5b, 0x8e, 0x92, 0x85, 0x88, 0x1c, 0x5f, 0xc6, 0xb4, 0x7d, 0xe1, 0x7f, 0x9e, 0x56, 0xb0, 0x16, 0x34, 0xe4, 0x96, 0x75, 0x8c, 0xbc, 0x20, 0x37, 0xd6, 0x89, 0xf6, 0xf0, 0x11, 0x6e, 0x71, 0x6d, 0x6b, 0xf4, 0xf3, 0xf1, 0xa1, 0xb4, 0xec, 0xb7, 0xbf, 0x28, 0xa4, 0x4d, 0x15, 0x52, 0x23, 0x8f, 0x4d, 0x1e, 0xd3, 0x4f, 0xe4, 0xb4, 0xc0, 0xf5, 0x83, 0x1d, 0x89, 0xaa, 0xef, 0xea, 0x63, 0xcc, 0x93, 0xd5, 0xc3, 0xc6, 0xda, 0x6d, 0x95, 0x8b, 0xd9, 0x56, 0x1c, 0x4f, 0xfe, 0xb9, 0xc3, 0x37, 0xce, 0xcf, 0x9f, 0x1f, 0xdd, 0xbc, 0x52, 0xb3, 0x8a, 0x1f, 0x99, 0x71, 0x32, 0x07, 0xba, 0x5b, 0xdb, 0x37, 0x83, 0x67, 0x8d, 0xe0, 0xb8, 0xf4, 0xe2, 0x4c, 0xf9, 0xde, 0x79, 0x14, 0x90, 0xe7, 0x50, 0xd8, 0xbe, 0x97, 0x40, 0xa6, 0x56, 0xd0, 0x1f, 0x6e, 0xd9, 0xaa, 0x2f, 0x46, 0x55, 0x5e, 0xee, 0x54, 0xba, 0x4d, 0x74, 0x9a, 0x77, 0x79, 0xe0, 0x00, 0x71, 0xe7, 0x8c, 0x5b, 0xca, 0xc9, 0x10, 0x4e, 0xf6, 0x78, 0x3c, 0x39, 0x10, 0x7d, 0x97, 0x9a, 0x81, 0x4d, 0x1b, 0x99, 0x58, 0x69, 0x1c, 0x9c, 0xf0, 0x06, 0xb1, 0x1d, 0xb6, 0x77, 0xfa, 0x1e, 0x40, 0xed, 0x20, 0xfc, 0xfa, 0x60, 0xf1, 0x79, 0x14, 0x99, 0x62, 0x30, 0x87, 0xbf, 0x6e, 0xea, 0x4d, 0xe4, 0x12, 0x69, 0x2e, 0x68, 0x59, 0xb1, 0xb1, 0x32, 0xc6, 0xdd, 0x1f, 0x1b, 0x7d, 0x06, 0xa2, 0x06, 0x02, 0xdb, 0xe8, 0x7f, 0x79, 0xd5, 0xe5, 0xe5, 0xaa, 0x3b, 0x86, 0x6f, 0x42, 0x56, 0x7e, 0x91, 0x13, 0x93, 0x81, 0x99, 0x3d, 0x69, 0x8f, 0x87, 0x9e, 0x43, 0x20, 0x6a, 0xdb, 0xcb, 0x59, 0xdb, 0xe3, 0x4c, 0xa5, 0x4e, 0xd0, 0x14, 0x3d, 0x5c, 0x6c, 0xfc, 0x9d, 0x3b, 0xd7, 0x8e, 0xd6, 0x06, 0x01, 0x66, 0x43, 0x8e, 0x04, 0xd0, 0x1f, 0xcc, 0x83, 0xad, 0xea, 0x3b, 0xa8, 0x35, 0xad, 0xab, 0x59, 0x1a, 0x81, 0x4b, 0xc5, 0x1a, 0xf3, 0x0f, 0x69, 0x0b, 0x20, 0xa4, 0x12, 0xf1, 0x9f, 0x8c, 0xa5, 0xa7, 0xa7, 0x2e, 0x8c, 0x23, 0xfa, 0xdf, 0x27, 0x6c, 0x73, 0x3a, 0x9c, 0xbf, 0x66, 0x46, 0xdd, 0x84, 0x14, 0x44, 0xca, 0xff, 0xe2, 0x26, 0xc0, 0xf3, 0x25, 0xff, 0xff, 0x96, 0xa3, 0xfe, 0xa8, 0x8e, 0x27, 0xb2, 0x5a, 0xd5, 0xbc, 0xb0, 0x9d, 0xc4, 0x73, 0xe5, 0x95, 0x99, 0xd2, 0x14, 0xcb, 0x3a, 0x38, 0x5f, 0xf4, 0x10, 0xe8, 0xdb, 0xfa, 0x32, 0x88, 0x77, 0xcc, 0x47, 0x34, 0xcd, 0x9a, 0xc6, 0x7f, 0xaa, 0x96, 0x8d, 0x07, 0x45, 0x5d, 0xef, 0x6a, 0x90, 0x93, 0xa7, 0xc4, 0x0d, 0x66, 0x64, 0x0b, 0x14, 0xfc, 0xce, 0xe9, 0x7e, 0x00, 0x3d, 0xea, 0x07, 0xcc, 0x80, 0x25, 0xfc, 0xee, 0x70, 0xaf, 0xf7, 0x0b, 0x26, 0x7f, 0xb7, 0x44, 0x1b, 0x97, 0x62, 0xba, 0x81, 0x91, 0xc0, 0xd2, 0x9b, 0xf1, 0x30, 0x41, 0xed, 0xcb, 0x71, 0x20, 0x79, 0xac, 0x82, 0x75, 0x74, 0xcd, 0x24, 0xfd, 0x20, 0xfc, 0xd1, 0x29, 0xae, 0xce, 0x31, 0x92, 0x67, 0x01, 0x57, 0x54, 0x71, 0x14, 0x4e, 0x19, 0xee, 0x8f, 0xd2, 0xc5, 0x56, 0x7a, 0x2b, 0x0d, 0x42, 0xf8, 0x8d, 0x63, 0x08, 0x0a, 0x6f, 0xff, 0xdd, 0xee, 0xc0, 0x04, 0x67, 0xa1, 0xf6, 0xeb, 0xe9, 0x71, 0x3e, 0xce, 0x78, 0xaa, 0x6e, 0x27, 0xde, 0xd0, 0x6f, 0x2d, 0x26, 0xe5, 0x37, 0xdd, 0xe8, 0xb7, 0xa1, 0x54, 0xd6, 0x3a, 0x9d, 0x16, 0x25, 0x51, 0xa5, 0x53, 0xc9, 0xa8, 0x59, 0x58, 0xab, 0x74, 0xba, 0xb7, 0x48, 0x36, 0x69, 0xcf, 0x61, 0xe2, 0xcf, 0xc2, 0xac, 0xfe, 0x34, 0x0a, 0xad, 0x9a, 0x0b, 0x2b, 0x2c, 0x43, 0x04, 0x44, 0xe7, 0x37, 0xb0, 0x72, 0x7d, 0x99, 0xc4, 0x44, 0x0f, 0xd0, 0x43, 0xc5, 0x4a, 0x1d, 0x1c, 0xc5, 0xe3, 0x38, 0x0f, 0x73, 0x6e, 0xbc, 0xd5, 0x71, 0xc8, 0x15, 0xed, 0xee, 0x0a, 0x8b, 0x8a, 0x70, 0x88, 0xf3, 0x68, 0x61, 0x63, 0x2c, 0xb4, 0x80, 0xd4, 0x3e, 0x24, 0xd0, 0x9a, 0x49, 0xfb, 0xa3, 0x40, 0x89, 0x9f, 0xd1, 0x25, 0x1b, 0xdf, 0x57, 0x65, 0x5e, 0x3d, 0xc9, 0x26, 0xff, 0xab, 0x66, 0xde, 0x1f, 0xa9, 0xc3, 0x8d, 0x1e, 0xb8, 0x41, 0x12, 0xf5, 0x7c, 0xd6, 0x06, 0x29, 0x0b, 0xe5, 0xcb, 0xce, 0x43, 0x12, 0xbc, 0x19, 0x30, 0x60, 0xa2, 0x71, 0xc3, 0x09, 0xeb, 0x0e, 0x77, 0xd5, 0x38, 0x96, 0x9f, 0xe1, 0x42, 0x31, 0x77, 0x1d, 0x3c, 0x1c, 0x3f, 0xf6, 0x13, 0xae, 0xe5, 0xed, 0x4b, 0x47, 0x14, 0x10, 0x55, 0xba, 0xb4, 0x97, 0x93, 0x0d, 0x1d, 0x22, 0x76, 0xae, 0x92, 0x50, 0x53, 0x79, 0xcd, 0xa6, 0xfc, 0x29, 0xaa, 0x12, 0x1e, 0x16, 0x34, 0x76, 0xf7, 0x4e, 0xb7, 0x00, 0xf8, 0x75, 0x7d, 0x47, 0x43, 0x04, 0x6f, 0x6a, 0x3f, 0xb7, 0xbc, 0xab, 0x97, 0x55, 0x34, 0x4e, 0x66, 0x2c, 0x0b, 0xa1, 0x6a, 0xd0, 0x1c, 0x7f, 0x7a, 0x09, 0xf7, 0xbe, 0xa0, 0x03, 0x43, 0x48, 0x81, 0x20, 0x41, 0xdb, 0x11, 0x03, 0x78, 0xab, 0xa1, 0xb5, 0xe6, 0x5e, 0xd6, 0x00, 0x29, 0x2b, 0x17, 0x59, 0x81, 0x07, 0x6e, 0xf2, 0xae, 0x2a, 0x05, 0xdd, 0xf6, 0x5b, 0xab, 0x4c, 0xe0, 0xd2, 0x09, 0x51, 0x72, 0x91, 0x79, 0xc4, 0xfa, 0x91, 0xa7, 0x12, 0xb3, 0x1a, 0x6b, 0x2e, 0xd0, 0x25, 0x83, 0xea, 0xaf, 0xb0, 0x1f, 0x60, 0x57, 0x3a, 0x68, 0x40, 0x76, 0xfe, 0x6d, 0x29, 0x77, 0x8e, 0x25, 0x58, 0x79, 0xd3, 0x6d, 0x5c, 0x33, 0xb5, 0x36, 0xa0, 0x12, 0xe1, 0x44, 0xe5, 0x1d, 0x61, 0x3a, 0x12, 0x66, 0x16, 0xc7, 0xef, 0x1d, 0x3b, 0x40, 0xa5, 0x8c, 0x7b, 0xee, 0x22, 0x85, 0x52, 0xe0, 0xc6, 0x12, 0xad, 0x12, 0xe1, 0x76, 0x17, 0x24, 0xf0, 0xc4, 0xda, 0x03, 0xaa, 0x99, 0xe4, 0x40, 0x44, 0xcd, 0x59, 0xe6, 0x79, 0x1f, 0x1d, 0x1e, 0x3f, 0x26, 0xcc, 0xe8, 0x40, 0x1d, 0xd8, 0xb7, 0x72, 0x4e, 0xd3, 0xc0, 0x6e, 0x2b, 0x18, 0x3b, 0x5a, 0x5d, 0x51, 0xfd, 0x8c, 0x5e, 0x9b, 0x0d, 0x39, 0x5f, 0x3e, 0x2e, 0xda, 0x8d, 0x36, 0x87, 0x08, 0x02, 0x0c, 0xcd, 0x10, 0xe4, 0xbc, 0x9d, 0x77, 0x91, 0x99, 0x03, 0x23, 0xcc, 0x7e, 0x3d, 0xfb, 0x48, 0xb3, 0x3c, 0x18, 0x1e, 0x54, 0x8d, 0xee, 0xea, 0x2c, 0x85, 0xf3, 0xac, 0x3c, 0xf5, 0xbf, 0xb3, 0x7d, 0xbe, 0xa6, 0x15, 0x7f, 0xf8, 0x33, 0x94, 0xf9, 0x9d, 0x21, 0xe7, 0x73, 0xda, 0xb0, 0xc1, 0x94, 0x7e, 0x65, 0x1b, 0xc4, 0x20, 0xa2, 0x90, 0x2e, 0x06, 0xa9, 0xd5, 0x4d, 0xd4, 0xe4, 0xa1, 0x83, 0x94, 0x97, 0x7d, 0x88, 0xa1, 0x48, 0x62, 0x92, 0x81, 0x91, 0x21, 0x87, 0xbb, 0x1e, 0xdb, 0xa9, 0x33, 0x08, 0x2e, 0x80, 0x9b, 0xda, 0xc7, 0xf6, 0x00, 0xe5, 0x75, 0xe8, 0x86, 0xbe, 0x07, 0xa7, 0xda, 0xfd, 0x7d, 0x1f, 0xe8, 0x9e, 0x5d, 0xa5, 0xd8, 0x9d, 0x2f, 0xab, 0x7b, 0xad, 0x7a, 0xcf, 0x82, 0x20, 0xc2, 0x05, 0x46, 0x5f, 0x5a, 0xd5, 0xf4, 0x94, 0x0c, 0xf2, 0x60, 0x54, 0xf5, 0x96, 0xd0, 0x75, 0x0d, 0x71, 0x8d, 0x5d, 0x38, 0x17, 0x00, 0x74, 0xdc, 0x35, 0xa8, 0x87, 0x57, 0x03, 0x45, 0x4a, 0xd8, 0xb1, 0xdc, 0x3d, 0x67, 0xc4, 0x9b, 0xa7, 0x2f, 0xb2, 0xaa, 0x76, 0x4d, 0x20, 0x6a, 0x23, 0xb5, 0x8f, 0xc8, 0xf6, 0xfc, 0x72, 0x88, 0x48, 0x07, 0xdf, 0xc7, 0x9d, 0x00, 0x69, 0x08, 0xac, 0x02, 0xc7, 0x8d, 0xc5, 0x60, 0x5f, 0xde, 0x13, 0x88, 0xd6, 0xa2, 0xb4, 0xc8, 0x3b, 0x4f, 0xab, 0x94, 0x70, 0x41, 0x1a, 0x05, 0xe4, 0x35, 0x52, 0xa4, 0xc8, 0x4d, 0xa1, 0xd7, 0x46, 0x0d, 0xed, 0x6a, 0x70, 0xff, 0x8b, 0x0d, 0xe5, 0xdb, 0xd1, 0xd2, 0x6d, 0x79, 0x64, 0xf0, 0xfe, 0xdb, 0x38, 0x5b, 0xeb, 0x19, 0xc0, 0x03, 0x3f, 0x00, 0xcc, 0xb9, 0x73, 0xa6, 0x0b, 0x11, 0x07, 0x4b, 0x3a, 0xca, 0xb6, 0x48, 0x67, 0x86, 0x3a, 0xe7, 0xde, 0xe0, 0xec, 0x54, 0x9f, 0x25, 0xa7, 0x1d, 0x78, 0x50, 0xbf, 0xdf, 0x22, 0xfb, 0xf8, 0x15, 0xf4, 0x60, 0xc4, 0x31, 0x16, 0xb6, 0x39, 0x6a, 0x42, 0x73, 0x88, 0xf6, 0x77, 0x3b, 0xe6, 0x86, 0x3b, 0x95, 0x2b, 0x4c, 0x43, 0x9a, 0x97, 0xd9, 0x9d, 0x4e, 0x8c, 0x9b, 0xbf, 0x46, 0x2a, 0xd6, 0x0f, 0xc9, 0x07, 0x41, 0xc3, 0xa5, 0x8f, 0xc8, 0x39, 0x48, 0xb9, 0x07, 0x1a, 0xee, 0x15, 0x74, 0xed, 0xea, 0x65, 0x6e, 0xf1, 0x57, 0x51, 0x86, 0x5c, 0xc1, 0xd8, 0xc7, 0x59, 0x8f, 0x91, 0xd5, 0x62, 0x36, 0xe9, 0x1e, 0xd4, 0x8e, 0xe0, 0x78, 0xd7, 0xc2, 0x02, 0xf0, 0xf5, 0xb5, 0x95, 0xa0, 0xfc, 0xed, 0x3c, 0xbb, 0x0f, 0x37, 0x7f, 0x50, 0xf1, 0xac, 0x0c, 0x50, 0xf7, 0xd7, 0x90, 0xdd, 0x39, 0xff, 0x7d, 0x01, 0xd1, 0xf8, 0x26, 0x3d, 0x59, 0xeb, 0xfc, 0x9a, 0xf0, 0xfb, 0xdc, 0x2b, 0x77, 0xbe, 0xb3, 0x29, 0xc6, 0xe9, 0xee, 0x17, 0x17, 0xa5, 0x75, 0x99, 0xe3, 0x72, 0x2d, 0xea, 0xe3, 0x8e, 0x39, 0x1c, 0x06, 0x3b, 0x81, 0x93, 0x7d, 0x65, 0x28, 0x60, 0x39, 0x7e, 0x00, 0x6d, 0x6d, 0x93, 0x33, 0xa1, 0x04, 0xf0, 0x8e, 0x6f, 0x13, 0xd5, 0x44, 0x78, 0xac, 0x08, 0x10, 0x2c, 0xb3, 0x62, 0xf8, 0x8f, 0x81, 0x85, 0x33, 0xa6, 0x89, 0x63, 0xe8, 0x13, 0x3f, 0x4f, 0xbe, 0xf0, 0xf4, 0xb3, 0x87, 0xff, 0x7a, 0xa5, 0x73, 0x9e, 0xa9, 0xfa, 0x8f, 0x37, 0x24, 0x97, 0x88, 0x07, 0xff, 0x0e, 0xf7, 0x74, 0x17, 0xd7, 0xa3, 0x58, 0x44, 0xac, 0x83, 0xf6, 0xb6, 0xd7, 0x75, 0x73, 0x24, 0x7c, 0xe1, 0x14, 0x9e, 0x32, 0xd8, 0xa1, 0x24, 0x66, 0x4e, 0x94, 0x62, 0x6d, 0x86, 0x16, 0x23, 0x38, 0xd8, 0xd4, 0x5f, 0x07, 0x5b, 0x3f, 0xfa, 0x9d, 0x26, 0xc1, 0x98, 0xce, 0x28, 0xe8, 0x1c, 0x5f, 0xd4, 0xa7, 0xa8, 0x96, 0x18, 0x27, 0x51, 0xff, 0x60, 0xc2, 0xbf, 0x31, 0xa1, 0x23, 0x7e, 0x9c, 0xa3, 0xaf, 0xb4, 0xbd, 0x0b, 0xa5, 0x7b, 0x25, 0xdf, 0xa2, 0x21, 0x43, 0x80, 0x75, 0xfa, 0xe0, 0x59, 0x6f, 0x97, 0xa5, 0xb3, 0x93, 0x82, 0x3f, 0x84, 0x65, 0xdc, 0xd9, 0x11, 0x30, 0x72, 0x09, 0xc0, 0xf8, 0x36, 0x77, 0x84, 0x86, 0x1b, 0xc8, 0xa1, 0x37, 0xd1, 0x99, 0xc8, 0x77, 0xe6, 0x98, 0xcb, 0x1a, 0x2d, 0x07, 0x50, 0xfe, 0x8f, 0x34, 0x61, 0x99, 0x99, 0xb2, 0xb5, 0x71, 0x67, 0x60, 0xff, 0x96, 0xc8, 0x87, 0xbe, 0x95, 0xb2, 0x9f, 0x43, 0x95, 0x6d, 0x86, 0xf1, 0x7a, 0x07, 0x35, 0xeb, 0x49, 0xed, 0x36, 0xb6, 0x32, 0xcf, 0x7f, 0x80, 0xeb, 0x1d, 0x5d, 0x0f, 0x4f, 0xc8, 0x9a, 0x92, 0x64, 0x11, 0x12, 0xd4, 0x37, 0x9b, 0x54, 0x81, 0x44, 0xfd, 0x15, 0x8d, 0x42, 0x8b, 0x76, 0xdd, 0x92, 0x23, 0x83, 0xd3, 0xb0, 0x22, 0x57, 0x4e, 0x5e, 0xd4, 0xcf, 0x43, 0x67, 0x03, 0xc1, 0x0e, 0x31, 0x7e, 0x10, 0x03, 0x86, 0xb8, 0x28, 0xfb, 0x40, 0xce, 0xa3, 0xd4, 0xc5, 0xa2, 0x32, 0x2a, 0x7c, 0xdb, 0x0f, 0xb6, 0xab, 0xea, 0x84, 0x1b, 0x68, 0x6b, 0x4e, 0x1c, 0x89, 0x8b, 0xf3, 0x6f, 0xc0, 0x3e, 0x51, 0xaf, 0x4f, 0x6c, 0xef, 0x32, 0xf9, 0x70, 0xe3, 0xa3, 0x2f, 0x81, 0x50, 0xbe, 0x86, 0xe7, 0xd4, 0x95, 0x0b, 0x0b, 0x66, 0x1e, 0xf2, 0xd5, 0x78, 0x08, 0xb9, 0xd8, 0x64, 0x11, 0xc9, 0x43, 0x74, 0x5f, 0x14, 0x9e, 0x10, 0x8f, 0x54, 0xb7, 0xdb, 0x78, 0xdc, 0x63, 0xcd, 0xe8, 0x21, 0xdc, 0x9b, 0xc9, 0x6a, 0xf1, 0xed, 0x70, 0x5b, 0x48, 0x3d, 0x18, 0x30, 0x64, 0x5a, 0xb8, 0xf6, 0x47, 0xef, 0xbe, 0x0a, 0xbb, 0x87, 0x5c, 0xa8, 0xbd, 0xe6, 0x78, 0x61, 0x82, 0xbb, 0x5d, 0xb1, 0x14, 0x05, 0xcd, 0x1a, 0x8a, 0xa1, 0x0d, 0x75, 0x3f, 0xe1, 0x02, 0x5c, 0xaf, 0x31, 0x8a, 0xe7, 0xc3, 0x0c, 0x99, 0x1c, 0xb3, 0xe5, 0xca, 0xc1, 0xdd, 0xb2, 0x6a, 0x83, 0x21, 0x3e, 0x36, 0x8f, 0xad, 0x84, 0x44, 0x1d, 0x46, 0x99, 0xd7, 0x86, 0x78, 0xfd, 0x1f, 0xdb, 0x3c, 0x57, 0x10, 0x2e, 0xf8, 0xe8, 0xe3, 0xe6, 0xfa, 0x4c, 0xea, 0xe0, 0xb8, 0x41 }; +constexpr AccessUnit ATRACX_SKIP_BYTES_EXPECTED_AU = { 0x15f90, 0x153d5, false, 0, {}, { 0x8a, 0x7b, 0xc9, 0x69, 0xee, 0xca, 0xcb, 0xab, 0xc6, 0x61, 0xa1, 0xf4, 0x75, 0x2e, 0x59, 0xb5, 0xf2, 0x90, 0x9d, 0x29 } }; + +const std::vector AC3_SKIP_BYTES_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xf3, 0x3b, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0x7a, 0x01, 0xe0, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x1a, 0x00, 0x80, 0x20, 0x90, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x00, 0x01, 0xbd, 0x07, 0x5a, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xa7, 0xab, 0x1e, 0x73, 0x3b, 0x30, 0x05, 0x05, 0x00, 0x0b, 0x77, 0xf4, 0x4b, 0x1b, 0x17, 0x5a, 0xaf, 0x1a, 0x24, 0x2c, 0x25, 0xa4, 0x37, 0x51, 0x53, 0xb0, 0x42, 0x4c, 0x69, 0x01, 0x21, 0x3f, 0x19, 0xf9, 0xfe, 0x1f, 0x8c, 0x15, 0x45, 0x98, 0xc8, 0x74, 0xa5, 0x58, 0x15, 0xba, 0xfd, 0xc4, 0x76, 0xf0, 0x2d, 0x4c, 0x44, 0x42, 0x21, 0xe0, 0x5a, 0xa7, 0xab, 0x3c, 0x92, 0xdf, 0xdf, 0x11, 0x67, 0x62, 0x1a, 0x35, 0xf5, 0x0b, 0xea, 0x6d, 0xc4, 0xb5, 0x70, 0x78, 0xfd, 0x0b, 0xfc, 0x67, 0x0b, 0x41, 0x02, 0x74, 0xba, 0x7d, 0x9c, 0xd8, 0x5a, 0x13, 0x3c, 0x59, 0x7a, 0xf9, 0x42, 0x91, 0x37, 0x02, 0xa3, 0x29, 0x4c, 0xc0, 0xc1, 0x7c, 0x3e, 0x2f, 0x2e, 0x6c, 0x62, 0x7b, 0x04, 0x54, 0xa4, 0x88, 0xeb, 0x67, 0x33, 0x0b, 0x1e, 0xe5, 0x8a, 0x60, 0xf1, 0xec, 0x79, 0x67, 0xa7, 0xe1, 0x1d, 0x17, 0xaa, 0xad, 0xf7, 0x8e, 0x68, 0x42, 0x86, 0xd2, 0x66, 0x31, 0x17, 0x86, 0x59, 0x41, 0xb0, 0x43, 0x42, 0x4b, 0x4e, 0x68, 0xef, 0xd5, 0xd3, 0x7d, 0x5a, 0x24, 0x5c, 0x85, 0xba, 0xc9, 0xde, 0xe1, 0x54, 0xda, 0x4e, 0x93, 0x52, 0x60, 0xa0, 0x0f, 0x32, 0xaa, 0x60, 0xe8, 0x93, 0x88, 0xcd, 0xf5, 0x76, 0xaa, 0x9d, 0xd4, 0x2d, 0x9a, 0xd3, 0xdf, 0x9c, 0x16, 0x62, 0xf5, 0x7f, 0xb6, 0x76, 0xa5, 0xdb, 0x2d, 0x9f, 0xaf, 0xc8, 0x9b, 0x42, 0xdd, 0x3f, 0x9b, 0xf8, 0xa6, 0xa0, 0x26, 0xd2, 0x26, 0x0a, 0x71, 0xa8, 0x52, 0xc9, 0x4c, 0xdb, 0x7f, 0x6d, 0xb8, 0x40, 0x9a, 0x4c, 0xb6, 0x12, 0xad, 0x8a, 0x86, 0x2b, 0x88, 0xe4, 0xdb, 0xd9, 0xb4, 0xcf, 0xad, 0x0d, 0x04, 0xbf, 0xbd, 0xc7, 0x60, 0x95, 0xf4, 0xea, 0xa9, 0x89, 0x25, 0xf1, 0xd6, 0x19, 0xe6, 0x1d, 0x9d, 0xca, 0x7d, 0x16, 0x45, 0xc4, 0x29, 0x27, 0x88, 0x96, 0x2d, 0x85, 0x94, 0xb8, 0x6e, 0x42, 0x8f, 0x85, 0x93, 0xe7, 0x32, 0xc1, 0xe9, 0xd2, 0x65, 0x06, 0x84, 0xcf, 0xe7, 0x82, 0x5e, 0x97, 0x6a, 0xf9, 0x64, 0x17, 0xbe, 0xfb, 0xbe, 0xbb, 0xdc, 0xa1, 0xc1, 0x18, 0xfe, 0x65, 0x4e, 0xac, 0x13, 0xda, 0x91, 0xa5, 0x2f, 0x4f, 0x69, 0xc3, 0xd5, 0xad, 0xec, 0xe2, 0x34, 0x19, 0xc4, 0xbc, 0xb2, 0x2b, 0x65, 0xe2, 0x6d, 0xba, 0x51, 0x06, 0x76, 0x9c, 0xed, 0x1a, 0xf8, 0xd2, 0xa4, 0xd6, 0xa4, 0xfa, 0x46, 0xa4, 0xef, 0x0f, 0xf3, 0x14, 0x0e, 0xbf, 0x03, 0x4b, 0xc5, 0xe8, 0xa0, 0x12, 0x6a, 0x06, 0x21, 0x73, 0xac, 0x9e, 0x53, 0x18, 0x0a, 0xf3, 0x2a, 0xc8, 0x0c, 0x91, 0xa8, 0xbd, 0x98, 0x76, 0xe2, 0xbc, 0x0c, 0xea, 0x69, 0x10, 0xe5, 0x53, 0x93, 0xe0, 0x27, 0x8a, 0x5b, 0x8f, 0xc9, 0x84, 0xdc, 0xfe, 0xd7, 0x56, 0x97, 0xf6, 0x14, 0xe5, 0x13, 0x10, 0x48, 0xc9, 0x77, 0xd4, 0x8a, 0x32, 0x3c, 0xfc, 0x25, 0xd9, 0x12, 0xf6, 0xf8, 0x51, 0x8d, 0x2c, 0x6a, 0x2f, 0x47, 0xa1, 0x4d, 0xf4, 0x7c, 0xea, 0x4f, 0xa9, 0x57, 0x9a, 0xcf, 0x86, 0x69, 0x96, 0x3e, 0x34, 0x13, 0x01, 0x58, 0x66, 0xce, 0xa2, 0xdd, 0xed, 0x98, 0xfa, 0xa5, 0x76, 0x02, 0x6a, 0x21, 0x9a, 0x95, 0x27, 0xb6, 0xf8, 0x8e, 0xd1, 0x79, 0x6e, 0x2e, 0xf0, 0x3e, 0xbd, 0x12, 0xb2, 0x05, 0x1e, 0x61, 0x9d, 0x24, 0x9f, 0xb4, 0x25, 0xdb, 0x47, 0x08, 0x42, 0x0f, 0x54, 0x29, 0x8e, 0x65, 0x24, 0x5d, 0x75, 0x07, 0x87, 0x42, 0x81, 0xbe, 0x93, 0x1c, 0xd9, 0x26, 0x44, 0x16, 0x6f, 0x35, 0x6b, 0x2a, 0x78, 0x56, 0xbe, 0xb2, 0x2b, 0xcb, 0x1f, 0xd5, 0xea, 0x1f, 0x51, 0x5d, 0x3d, 0xd1, 0xb9, 0x9a, 0x54, 0x8b, 0xea, 0x6b, 0xfd, 0x8b, 0x19, 0x86, 0x2b, 0x11, 0xad, 0xa2, 0x8d, 0xed, 0x8f, 0x1c, 0xd2, 0x96, 0xf6, 0x2c, 0x93, 0x5a, 0xbf, 0xd6, 0x01, 0x7f, 0xa6, 0x1c, 0xcf, 0x85, 0xd7, 0x2c, 0x00, 0xcc, 0xc0, 0x80, 0x7c, 0x51, 0x13, 0x34, 0xaf, 0x4b, 0x59, 0x06, 0x80, 0xce, 0x95, 0xc7, 0x63, 0xd5, 0xcf, 0x2c, 0x6c, 0x3c, 0x87, 0x44, 0x89, 0xa0, 0x5f, 0x0a, 0x54, 0x26, 0x94, 0xd6, 0x28, 0x9d, 0xfc, 0x9f, 0x69, 0x86, 0xac, 0x52, 0x22, 0xe7, 0x80, 0xb7, 0x6e, 0x00, 0x1a, 0xa1, 0x53, 0x44, 0x27, 0x61, 0xfb, 0x29, 0xe8, 0xbe, 0x7b, 0xb4, 0x5f, 0xed, 0x8c, 0xdb, 0x96, 0x3e, 0xf1, 0x10, 0xe4, 0x85, 0x61, 0xa1, 0x20, 0x70, 0x35, 0xb9, 0x2e, 0x4e, 0x35, 0x2c, 0x51, 0x03, 0x43, 0x5a, 0x2d, 0x1b, 0xba, 0xda, 0x6b, 0x46, 0xc0, 0xb3, 0x5a, 0xb0, 0xed, 0x50, 0x1b, 0xf1, 0xf3, 0x7f, 0x12, 0x98, 0x38, 0x20, 0xc0, 0x83, 0x8c, 0x5c, 0x41, 0x88, 0x63, 0x7f, 0xff, 0x2a, 0x42, 0xd1, 0x66, 0x50, 0x53, 0x0f, 0xa9, 0x42, 0x38, 0x59, 0xdb, 0x61, 0xa0, 0xb6, 0xf1, 0x5d, 0x39, 0x86, 0x74, 0xdf, 0xf4, 0x84, 0xf0, 0xe3, 0xc2, 0x2d, 0x6e, 0x45, 0x3b, 0xac, 0x7e, 0x38, 0xd8, 0xa8, 0xaa, 0x9b, 0xa8, 0x8b, 0x52, 0x7c, 0xb7, 0x25, 0x3e, 0x33, 0x84, 0xc0, 0x7c, 0xd6, 0x45, 0x05, 0x4d, 0x05, 0xd0, 0x52, 0x2b, 0x4b, 0x63, 0x4d, 0x3b, 0x8a, 0xb7, 0xe9, 0xbc, 0x31, 0xff, 0x21, 0x26, 0x38, 0xce, 0x15, 0x28, 0x23, 0x02, 0x5b, 0xb5, 0x7d, 0x2a, 0xa6, 0xb5, 0x92, 0x0a, 0x22, 0x34, 0x5a, 0x3a, 0x12, 0xfb, 0xdd, 0xf0, 0xa1, 0x05, 0x80, 0x80, 0xef, 0x3b, 0xff, 0x24, 0xc3, 0x5a, 0x0a, 0x0d, 0x78, 0x03, 0xc9, 0xd6, 0x01, 0x44, 0xba, 0xd7, 0x24, 0x27, 0xad, 0xf0, 0x44, 0x12, 0xd2, 0xd5, 0x53, 0x03, 0x1c, 0xdf, 0xd4, 0xf9, 0x74, 0xf7, 0xfd, 0x10, 0x6f, 0x2a, 0x58, 0x1a, 0x96, 0xe2, 0x82, 0xcd, 0x64, 0xe2, 0xe4, 0xc6, 0x88, 0x84, 0x7b, 0xf2, 0x27, 0xd4, 0x18, 0x25, 0x05, 0x77, 0xa4, 0xf6, 0xf1, 0x98, 0x53, 0x1c, 0xd5, 0x2f, 0xd5, 0xe5, 0x33, 0x67, 0xef, 0x8e, 0x06, 0xfd, 0x07, 0x85, 0x4e, 0xc8, 0x92, 0xe8, 0xab, 0xe1, 0x6d, 0xca, 0xe2, 0x05, 0x8e, 0x01, 0x2e, 0x2c, 0x9b, 0x9f, 0xa8, 0x79, 0x4f, 0x45, 0x3a, 0xf0, 0xbd, 0xf9, 0xdf, 0xa5, 0xda, 0x33, 0x28, 0x43, 0xde, 0x3d, 0x45, 0x20, 0x60, 0x2b, 0x08, 0x4e, 0x44, 0xf6, 0xa5, 0x48, 0xdb, 0x8c, 0x2d, 0x31, 0xd1, 0xda, 0xf1, 0xd2, 0xbd, 0x20, 0x5a, 0xb6, 0x7e, 0x1d, 0xed, 0x22, 0xaf, 0xda, 0x6e, 0xcc, 0xe2, 0x1e, 0x6c, 0x74, 0x65, 0xe5, 0xaa, 0xe0, 0xcb, 0x53, 0x1d, 0xd4, 0x78, 0x00, 0x86, 0xfa, 0xe2, 0x97, 0xe0, 0xe9, 0x57, 0x2b, 0x13, 0xaf, 0x66, 0xb7, 0xba, 0x91, 0x4a, 0xa6, 0xec, 0xe5, 0xf1, 0x61, 0x4a, 0x44, 0x7c, 0x3e, 0xc1, 0x62, 0x52, 0x51, 0xd7, 0x56, 0x20, 0xea, 0x61, 0x2c, 0x70, 0xdb, 0xee, 0xf7, 0x9b, 0xba, 0x64, 0xc1, 0x48, 0xc1, 0x87, 0x16, 0xc8, 0x10, 0x5e, 0x10, 0xef, 0x55, 0xc7, 0x03, 0x45, 0x35, 0x9f, 0x51, 0xac, 0xf2, 0x48, 0xed, 0xd5, 0x2f, 0x89, 0xa3, 0x20, 0xc0, 0xf6, 0x29, 0xca, 0xf4, 0x74, 0x81, 0x7a, 0xc8, 0x79, 0x4d, 0x8c, 0xe4, 0x20, 0xff, 0xc3, 0x5e, 0x32, 0x18, 0x24, 0x99, 0xba, 0xde, 0x33, 0xd0, 0xc6, 0x2f, 0xda, 0x03, 0x6d, 0x6d, 0x9a, 0x72, 0x39, 0xa0, 0xe9, 0xd5, 0x8a, 0x3d, 0xd4, 0xef, 0x9a, 0x43, 0x3d, 0x43, 0x34, 0x9c, 0x57, 0xf1, 0x3f, 0xdf, 0x3f, 0x12, 0xae, 0xef, 0xc2, 0x15, 0x8e, 0x22, 0xb8, 0xf2, 0x23, 0xcd, 0x8c, 0x32, 0x5c, 0x71, 0xc3, 0x2c, 0x13, 0xf2, 0xb7, 0xeb, 0xd7, 0x7b, 0xc4, 0xa1, 0x41, 0x04, 0x9a, 0x9b, 0x02, 0xa5, 0x82, 0x18, 0x38, 0x1e, 0xb8, 0x83, 0xbd, 0x44, 0x3d, 0xd9, 0x61, 0x39, 0xa6, 0x55, 0x66, 0xa6, 0xe4, 0x9c, 0xa0, 0x56, 0x03, 0x07, 0x01, 0xea, 0x51, 0xc6, 0x8f, 0x87, 0x80, 0xf9, 0xf6, 0xaf, 0x9c, 0xf8, 0x3d, 0xb9, 0xfb, 0x6c, 0x57, 0x84, 0x77, 0xeb, 0x83, 0x5c, 0x9d, 0x43, 0xc4, 0x3a, 0x9d, 0x9d, 0xe6, 0xd0, 0x1e, 0x1f, 0xce, 0xfc, 0x56, 0xcf, 0x0d, 0xa9, 0x00, 0xbf, 0xf9, 0x33, 0x02, 0xd5, 0xc2, 0xc0, 0xf1, 0xb8, 0x44, 0x46, 0xbc, 0xaf, 0x89, 0x14, 0x2c, 0x7a, 0x45, 0x20, 0x05, 0x7a, 0xbd, 0x7a, 0xf2, 0xa6, 0xd6, 0x8d, 0x89, 0x2c, 0x4c, 0x24, 0x14, 0xb3, 0x4e, 0x5c, 0xeb, 0xa2, 0xf3, 0xea, 0x87, 0xe3, 0x4e, 0x62, 0xa4, 0xfe, 0x16, 0x74, 0x0d, 0x8a, 0x1e, 0x3d, 0xf5, 0x52, 0x22, 0x26, 0xee, 0xa2, 0x96, 0xf0, 0xe5, 0xa1, 0x81, 0xe3, 0xbb, 0xb1, 0x9a, 0x4a, 0x51, 0x2a, 0x1d, 0xa0, 0x2d, 0xfe, 0x25, 0x25, 0x0b, 0x07, 0x73, 0xc3, 0x03, 0x19, 0xe8, 0x69, 0x0a, 0x19, 0x37, 0x56, 0x03, 0xac, 0x71, 0xea, 0xa9, 0x47, 0x88, 0x08, 0xf9, 0xa6, 0xe8, 0x10, 0xf5, 0x0a, 0xb4, 0x00, 0xd6, 0xac, 0x5d, 0x4f, 0x13, 0x4d, 0x8a, 0x7b, 0xd3, 0xde, 0x9e, 0x72, 0xa6, 0x96, 0x3e, 0x42, 0xa7, 0xf3, 0xab, 0xb7, 0x56, 0x8a, 0xdd, 0xe7, 0xd4, 0x97, 0xa6, 0xb4, 0x5b, 0xb3, 0x4f, 0xdb, 0xfc, 0x24, 0x6b, 0x13, 0x1f, 0xa5, 0xed, 0x4b, 0x44, 0xa8, 0xa1, 0x19, 0xee, 0x65, 0xa2, 0xd5, 0xd4, 0x38, 0x6d, 0x05, 0xb3, 0x2a, 0xa2, 0x08, 0x58, 0x9a, 0xa7, 0x33, 0xc8, 0x6b, 0xa7, 0xb6, 0x99, 0x33, 0xac, 0x1a, 0xd8, 0x3a, 0xcd, 0x15, 0x25, 0x56, 0xb5, 0x13, 0xcc, 0xf4, 0xef, 0xb0, 0x0b, 0x77, 0xc4, 0xf4, 0x1b, 0x3c, 0x9c, 0x29, 0x93, 0xd9, 0x20, 0x7a, 0x4a, 0x4a, 0x9d, 0xcd, 0x2d, 0x39, 0x5f, 0x7f, 0x2b, 0x8b, 0x04, 0xf0, 0x46, 0xde, 0x02, 0x08, 0xed, 0x87, 0xd6, 0x25, 0x46, 0xa8, 0xa3, 0x1c, 0xec, 0x28, 0x36, 0x3a, 0x98, 0x93, 0xac, 0x6d, 0xae, 0x3f, 0x32, 0x0b, 0xbd, 0x29, 0xa6, 0xd4, 0x47, 0x69, 0x0d, 0x27, 0x7d, 0x48, 0x15, 0x97, 0xc3, 0x36, 0x2b, 0x3d, 0x13, 0x3b, 0xde, 0x86, 0xa9, 0xaa, 0x7e, 0x5f, 0x7e, 0x1c, 0x49, 0xe4, 0x8a, 0x0b, 0x8d, 0x13, 0xd1, 0x8b, 0x8a, 0x1b, 0xa2, 0xfb, 0x08, 0x38, 0xa2, 0xb1, 0xdd, 0x50, 0x82, 0xeb, 0xe7, 0xb3, 0x79, 0x9c, 0x2d, 0xb1, 0xd7, 0xd3, 0x24, 0xf1, 0x60, 0xda, 0x59, 0xa8, 0x34, 0x42, 0xf9, 0x3b, 0x75, 0x7b, 0xf5, 0xce, 0xf3, 0xbc, 0xb7, 0xd6, 0x91, 0x4b, 0xae, 0xb8, 0x26, 0x67, 0x27, 0xf6, 0x06, 0x6f, 0x30, 0x8f, 0x93, 0x07, 0x7f, 0x7c, 0x5e, 0x49, 0xa3, 0x7a, 0xc8, 0x4f, 0xe6, 0x8b, 0x37, 0xa9, 0xb7, 0xd8, 0x38, 0x7a, 0x84, 0xe7, 0x2d, 0x5a, 0x42, 0x49, 0x6a, 0x3c, 0x2c, 0x2a, 0xcc, 0xf1, 0x66, 0x85, 0x39, 0xff, 0x89, 0xc2, 0x99, 0xf6, 0x50, 0x0c, 0x25, 0x6e, 0xe4, 0xc2, 0x2e, 0x59, 0x83, 0x99, 0xb1, 0x57, 0x7a, 0x87, 0x5a, 0x36, 0xb4, 0x6e, 0xd1, 0xd2, 0x5a, 0x28, 0x61, 0xed, 0x35, 0x6f, 0x6d, 0xc6, 0xde, 0xb6, 0x3a, 0x85, 0xef, 0x06, 0xef, 0xb2, 0xac, 0x50, 0xf0, 0x62, 0xf0, 0xe6, 0xe2, 0xc9, 0x48, 0x03, 0x02, 0x4d, 0x85, 0x8b, 0xa0, 0x64, 0x3a, 0x79, 0xde, 0xc6, 0xe6, 0x99, 0x15, 0xbe, 0xc1, 0x82, 0x6b, 0xac, 0x72, 0x03, 0x8d, 0xfe, 0x17, 0x5d, 0x99, 0x31, 0x78, 0x06, 0x93, 0x9b, 0x42, 0xdd, 0x10, 0x1f, 0x98, 0xf3, 0xe0, 0x02, 0x79, 0x5f, 0xe3, 0x72, 0xa6, 0xc6, 0x5b, 0x9a, 0x18, 0x82, 0x5b, 0x37, 0x2f, 0x91, 0x2b, 0x0e, 0x25, 0xc0, 0xca, 0xc4, 0x7d, 0xb6, 0xd2, 0xeb, 0x60, 0xce, 0x76, 0x42, 0x36, 0xf0, 0x5f, 0x8e, 0xe7, 0xcc, 0x91, 0x7b, 0x87, 0x2c, 0xaf, 0x63, 0x6c, 0x46, 0x3d, 0x04, 0xe1, 0x34, 0xb8, 0x6d, 0xf9, 0x08, 0xa6, 0xb1, 0x40, 0x25, 0xee, 0x2b, 0x3d, 0xbf, 0x29, 0x5e, 0x0b, 0xbf, 0xaf, 0xc1, 0x79, 0x68, 0x8d, 0xfe, 0x7e, 0x76, 0xc1, 0x74, 0xec, 0x93, 0x47, 0x06, 0xa5, 0x62, 0xe8, 0x7e, 0xd8, 0x77, 0xd5, 0x1f, 0xe9, 0xf9, 0x7d, 0x23, 0x1a, 0xcf, 0x22, 0x30, 0x27, 0x90, 0x24, 0x25, 0x48, 0x8f, 0x03, 0xae, 0x64, 0xc1, 0x8a, 0x80, 0x67, 0x63, 0xb6, 0x44, 0xff, 0x88, 0x31, 0x75, 0x2e, 0x02, 0x84, 0xca, 0x83, 0x48, 0x56, 0xeb, 0xda, 0x51, 0x5f, 0x89, 0x00, 0x8a, 0xd9, 0x1a, 0x91, 0x50, 0x9f, 0xcf, 0xf6, 0x9f, 0x43, 0x89, 0x56, 0xe0, 0xc8, 0xd9, 0xd4, 0x04, 0xd1, 0x5d, 0x8a, 0x52, 0x62, 0x01, 0xfd, 0x9f, 0x96, 0xf3, 0x05, 0xca, 0xdf, 0x4d, 0x6b, 0xc5, 0xaa, 0xd3, 0xb4, 0x6f, 0x93, 0x3d, 0xd6, 0x17, 0x6a, 0xe9, 0xd0, 0x6d, 0xbf, 0xf3, 0x6e, 0x29, 0xc5, 0x11, 0x9e, 0xc6, 0xa3, 0xa9, 0x2b, 0xfe, 0x9e, 0xfd, 0xc7, 0xf7, 0xa7, 0x77, 0xa7, 0x4e, 0x3a, 0x58, 0x37, 0x64, 0x97, 0x65, 0x88, 0xa2, 0xc3, 0x4a, 0x94, 0x9c, 0xe4, 0xd6, 0x7b, 0x3e, 0xd2, 0x45, 0x41, 0x6e, 0xff, 0x39, 0xc4, 0xd9, 0x11, 0x1f, 0xee, 0xc4, 0xc2, 0x11, 0x53, 0x26, 0xee, 0x84, 0xc1, 0x59, 0xe3, 0x48, 0x76, 0x2f, 0x54, 0xaf, 0x04, 0x39, 0x80, 0xd8, 0x0f, 0x7f, 0x7e, 0x7d, 0x6d, 0x11, 0xb3, 0xbe, 0x63, 0x18, 0xd9, 0xc4, 0xef, 0x56, 0x3c, 0x3e, 0x74, 0x42, 0xef, 0x06, 0x1f, 0x2f, 0x36, 0x7d, 0xed, 0x20, 0x66, 0x6b, 0x25, 0x3d, 0xe3, 0x65, 0x9e, 0x1a, 0x17, 0x12, 0x42, 0x28, 0x7e, 0xd6, 0x87, 0x18, 0x22, 0xa9, 0xe3, 0x02, 0x25, 0x32, 0x71, 0x8d, 0x2a, 0x61, 0x43, 0x7a, 0xd2, 0x2e, 0x1f, 0x4b, 0x8a, 0x27, 0xa1, 0xdb, 0x60, 0xfa, 0x04, 0x32, 0xaa, 0x32, 0x02, 0xe0, 0xa3, 0xb6, 0x93, 0xa8, 0x5d, 0xe2, 0x83, 0x53, 0x93, 0x35, 0x17, 0xe1, 0x52, 0xee, 0x40, 0x81, 0x7c, 0x17, 0x20, 0xf9, 0x90, 0xde, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x79, 0x01, 0x00, 0x30, 0x40, 0x7b, 0x72, 0x2a, 0xee, 0x84, 0xd7, 0xa4, 0x09, 0x4a, 0x05, 0xac, 0x4a, 0xac, 0x61, 0xf7, 0xf9, 0x62, 0xd3, 0x8d, 0x35, 0xe0, 0x7d, 0x1e, 0x7c, 0x57, 0xe5, 0xe0, 0x7c, 0x5a, 0x5d, 0x7e, 0xa1, 0xf1, 0xa4, 0x26, 0xe4, 0x34, 0xf2, 0xc7, 0xc8, 0x9d, 0xfc, 0xfe, 0x9e, 0xc3, 0x98, 0x51, 0x60, 0x29, 0x10, 0xab, 0xd4, 0x3b, 0x52, 0x44, 0xb9, 0xd0, 0x7e, 0x60, 0x3b, 0xa1, 0x93, 0x91, 0xe2, 0x9d, 0x2f, 0x66, 0xbf, 0x15, 0x9e, 0xbc, 0x97, 0x12, 0xbf, 0x0b, 0x37, 0x64, 0x6d, 0x74, 0x45, 0x29, 0x6f, 0x64, 0x2f, 0xf0, 0x2f, 0x02, 0x14, 0xa2, 0xa8, 0xde, 0xbe, 0xe4, 0x53, 0x7a, 0x0d, 0x9d, 0x0c, 0xa3, 0x6f, 0xbc, 0x23, 0xcb, 0x42, 0x8c, 0x20, 0xc5, 0x36, 0x16, 0xf0, 0x97, 0x14, 0x6d, 0x7a, 0x47, 0x7a, 0xdc, 0xcf, 0xda, 0x7d, 0x5a, 0x85, 0xe7, 0x2b, 0xb6, 0xdf, 0x68, 0x74, 0x63, 0xd7, 0xc5, 0xeb, 0xd3, 0x2d, 0x32, 0x5c, 0xc2, 0x13, 0xd5, 0x43, 0xfa, 0x18, 0x62, 0x80, 0x50, 0x5d, 0x19, 0xed, 0xf4, 0x4f, 0x44, 0xa5, 0x59, 0x5c, 0xbc, 0x2a, 0x3f, 0x60, 0x00, 0xac, 0xb7, 0xcf, 0xed, 0x2b, 0x15, 0xcc, 0x22, 0x3a, 0x3f, 0xb1, 0x95, 0x86, 0xbf, 0xbc, 0xf5, 0x80, 0x82, 0xeb, 0xd3, 0xc1, 0x3d, 0x33, 0xac, 0x06, 0xd4, 0x98, 0x53, 0xe6, 0x3b, 0xdf, 0x5a, 0x8b, 0xb1, 0xd8, 0x6c, 0x2b, 0x55, 0x75, 0xc0, 0xf5, 0x64, 0x36, 0x15, 0xae, 0xad, 0x9a, 0xe1, 0x4f, 0x08, 0x99, 0xa1, 0x36, 0xd4, 0x5f, 0x6f, 0xdb, 0xd6, 0xc7, 0x55, 0x0f, 0xf9, 0x4d, 0x02, 0x30, 0xc1, 0x7a, 0xca, 0xee, 0x76, 0x00, 0x25, 0x6b, 0x62, 0x04, 0x19, 0xed, 0x9d, 0x54, 0x2d, 0x6a, 0x3a, 0x82, 0xe4, 0x8f, 0xe6, 0x1f, 0xe1, 0x22, 0x3b, 0x9c, 0x21, 0xd9, 0x5e, 0x7c, 0xf8, 0xfb, 0xa8, 0xe1, 0xc0, 0x77, 0x71, 0xa9, 0x5f, 0x3d, 0x42, 0x9f, 0x19, 0xf9, 0xfd, 0x6f, 0x52, 0x95, 0xc5, 0x62, 0x31, 0x13, 0xb6, 0x21, 0x4a, 0xa0, 0xb5, 0xef, 0xc8, 0xfc, 0xe2, 0x24, 0xb0, 0x8f, 0x7e, 0xdb, 0x3d, 0xd0, 0x54, 0x10, 0x6d, 0xd1, 0xdf, 0xb0, 0x40, 0x0b, 0xce, 0x53, 0x3f, 0xd5, 0xa9, 0x9c, 0x4f, 0x0d, 0x9c, 0x76, 0xe4, 0xdc, 0xc9, 0xb9, 0x0f, 0xb5, 0x8f, 0xe1, 0x13, 0xd6, 0xad, 0xde, 0x65, 0x06, 0x40, 0x39, 0x47, 0x50, 0xe9, 0x03, 0x6d, 0xe2, 0xa7, 0x4f, 0x56, 0x76, 0x39, 0x35, 0x3d, 0xcb, 0x02, 0x95, 0xde, 0x22, 0xd1, 0x19, 0x70, 0xc2, 0x06, 0x30, 0x7c, 0xfc, 0xf2, 0x00, 0x8c, 0x2a, 0x68, 0x8d, 0xd3, 0xa7, 0xe1, 0x89, 0x35, 0xbf, 0xb2, 0x93, 0x6d, 0x05, 0x20, 0xd7, 0x2a, 0xa2, 0x67, 0x59, 0x5c, 0x80, 0xf9, 0x53, 0x18, 0x69, 0x7b, 0x76, 0xbb, 0x71, 0x61, 0xfe, 0x3f, 0x51, 0x16, 0xd8, 0xf0, 0x89, 0x21, 0x58, 0xdf, 0x6a, 0xac, 0x34, 0xcd, 0x55, 0x58, 0xd0, 0x7a, 0xe7, 0x5e, 0xff, 0x75, 0xcb, 0x43, 0xcc, 0xea, 0x71, 0xb6, 0xf9, 0x14, 0xea, 0x5f, 0x36, 0x3c, 0x93, 0xa8, 0x4a, 0x6f, 0x6b, 0xd3, 0xa6, 0x66, 0x2a, 0xe7, 0x2d, 0x36, 0x7d, 0x33, 0x12, 0xc8, 0xf5, 0x8e, 0x2a, 0x33, 0x99, 0x85, 0x10, 0x7a, 0x26, 0x12, 0xe5, 0x41, 0x5f, 0xa7, 0xae, 0x75, 0x0e, 0x8c, 0x77, 0x15, 0x85, 0xf7, 0xd3, 0x7a, 0xd0, 0x48, 0x59, 0xd9, 0xd7, 0x54, 0x31, 0xde, 0x2c, 0xb1, 0xe0, 0x96, 0x47, 0x67, 0xcd, 0x13, 0x58, 0x80, 0x4a, 0x6c, 0x0e, 0x47, 0x0c, 0x10, 0xe2, 0x49, 0x58, 0x9a, 0xd6, 0x60, 0x5d, 0x7f, 0xab, 0x4b, 0x7a, 0x5c, 0x01, 0xb4, 0x69, 0xbe, 0xef, 0x96, 0x8b, 0x91, 0xf4, 0x68, 0xd6, 0xb2, 0x5b, 0x83, 0x1a, 0x36, 0x75, 0xa2, 0x9c, 0x6a, 0x35, 0x78, 0x48, 0xac, 0x1f, 0xb5, 0xea, 0x8c, 0x94, 0xfc, 0x36, 0xda, 0xdf, 0x9f, 0x96, 0x9c, 0xa5, 0x21, 0xf9, 0x0b, 0x83, 0xd8, 0x21, 0x08, 0x07, 0x33, 0x2c, 0xb2, 0x5a, 0x6e, 0x4e, 0x0f, 0x30, 0xc4, 0x09, 0xed, 0xe6, 0x71, 0x43, 0x6d, 0xe2, 0xfe, 0x15, 0xe0, 0x96, 0xb6, 0x3a, 0xe1, 0xb8, 0x47, 0x5d, 0xdf, 0x9b, 0xdd, 0xca, 0x7c, 0xb8, 0x3c, 0x3c, 0x6a, 0xc6, 0xb5, 0xb6, 0xc3, 0xff, 0xf0, 0xbe, 0xf0, 0x73, 0xe5, 0x54, 0xa1, 0x75, 0x86, 0x86, 0x70, 0x91, 0xed, 0x7c, 0x29, 0x98, 0x15, 0x4c, 0xdd, 0xa9, 0x70, 0x0f, 0x26, 0x4e, 0xde, 0xe9, 0x59, 0x04, 0xd5, 0x0c, 0x7c, 0xd4, 0xcf, 0xef, 0x40, 0xd5, 0x33, 0x2e, 0x5d, 0xaf, 0x5e, 0xd8, 0x01, 0x7e, 0x8c, 0x24, 0x46, 0xb7, 0xa8, 0xc2, 0xc6, 0xdc, 0x5f, 0x53, 0xd4, 0x2d, 0xec, 0xb6, 0xef, 0xa5, 0x82, 0x2a, 0x65, 0xe3, 0x4b, 0x88, 0x59, 0xdd, 0x53, 0x05, 0x87, 0x75, 0xe4, 0x42, 0x7d, 0x50, 0x30, 0xa0, 0x3b, 0x8c, 0x34, 0x1c, 0x53, 0x26, 0x4a, 0x1c, 0x09, 0x3c, 0xce, 0x0a, 0x10, 0x7e, 0xc1, 0x92, 0xdd, 0x66, 0x61, 0xdb, 0xad, 0x20, 0x0f, 0x33, 0xbb, 0x31, 0x0a, 0xdc, 0x42, 0x3e, 0xc9, 0x03, 0x42, 0x93, 0x00, 0x1c, 0x2f, 0x40, 0xa8, 0x16, 0xf9, 0xe9, 0x0b, 0x77, 0x15, 0xa7, 0x8d, 0x65, 0x75, 0x99, 0x21, 0x9a, 0xa0, 0xf8, 0x08, 0x34, 0x49, 0xd1, 0x56, 0xa4, 0x21, 0xc1, 0x9b, 0x02, 0xc6, 0x4b, 0x65, 0xe9, 0xf8, 0xa5, 0xc1, 0x0f, 0x8e, 0x22, 0x94, 0x5b, 0xc9, 0x7c, 0xb4, 0x6d, 0xf5, 0xbd, 0xb0, 0xdf, 0x8c, 0x82, 0xb1, 0xa9, 0x35, 0xc8, 0xe1, 0x7b, 0x62, 0x99, 0x3e, 0xf6, 0x69, 0x36, 0xd4, 0x0c, 0x41, 0x95, 0xb3, 0xf9, 0x66, 0x5c, 0x2b, 0x99, 0x6c, 0x9f, 0x18, 0x16, 0x58, 0x18, 0x47, 0x25, 0xbd, 0xa0, 0x51, 0x56, 0x35, 0x0b, 0x5a, 0x26, 0xbe, 0xc8, 0xa0, 0xde, 0xfe, 0xdb, 0xcf, 0x75, 0x93, 0x13, 0x70, 0xec, 0xd2, 0x8e, 0xfe, 0xe3, 0xb4, 0xf3, 0x6f, 0x6d, 0xcb, 0xf3, 0x33, 0x4d, 0x75, 0xf8, 0x03, 0x25, 0xb6, 0x14, 0x02, 0x4e, 0x63, 0x93, 0x01, 0x5b, 0x90, 0xc5, 0xed, 0x76, 0x67, 0xe6, 0xbc, 0x21, 0xd8, 0x35, 0x59, 0x15, 0xc7, 0xa1, 0x65, 0x29, 0xb2, 0x99, 0xd5, 0x9b, 0xe9, 0xf2, 0xb7, 0x22, 0xd7, 0xe2, 0xaf, 0x81, 0xf3, 0x87, 0xf3, 0xb3, 0x59, 0x84, 0xd8, 0xc3, 0x2d, 0x9a, 0x31, 0xf9, 0x31, 0x51, 0x1b, 0xed, 0x42, 0x15, 0xf7, 0x9f, 0xa2, 0x5d, 0xa1, 0xb8, 0x42, 0xbc, 0x30, 0x7d, 0x6c, 0xf5, 0xcb, 0x30, 0x84, 0x70, 0x61, 0xcf, 0xcb, 0xaa, 0x50, 0x79, 0x48, 0x7f, 0x3c, 0xf5, 0x4b, 0x66, 0xb8, 0x10, 0x34, 0x00, 0x78, 0xcc, 0x67, 0x93, 0x21, 0xae, 0x9e, 0x48, 0x06, 0x8c, 0xfc, 0x55, 0x54, 0xb0, 0xa1, 0x62, 0x87, 0x3d, 0x8b, 0xce, 0x27, 0x14, 0x10, 0xa7, 0x1e, 0xbd, 0x49, 0x7b, 0x38, 0x04, 0x23, 0x57, 0x8e, 0x50, 0x00, 0x94, 0xf1, 0xef, 0x0c, 0x4a, 0xa3, 0xc7, 0xc4, 0x8a, 0xdb, 0x3a, 0x48, 0xb3, 0xf4, 0x2f, 0x71, 0x8c, 0x44, 0x78, 0xce, 0xeb, 0xee, 0x1a, 0xfe, 0x1c, 0x38, 0x34, 0x06, 0x51, 0x33, 0x6e, 0x9e, 0xbd, 0xc3, 0x4f, 0x92, 0x3e, 0x47, 0xcb, 0xb2, 0xa4, 0x86, 0xec, 0xea, 0xfe, 0x8d, 0x7b, 0x89, 0x81, 0xa2, 0xea, 0xc3, 0xe5, 0x94, 0xfd, 0xf5, 0x5b, 0xe6, 0xef, 0x49, 0x6c, 0xb6, 0x0f, 0xa8, 0x70, 0x5a, 0x06, 0x2a, 0x5f, 0x06, 0x58, 0x6a, 0x33, 0x10, 0xc6, 0x6e, 0x21, 0x8b, 0x9b, 0x15, 0x52, 0x51, 0x15, 0x21, 0xf6, 0xce, 0x76, 0x6c, 0xc9, 0x07, 0x6a, 0xcd, 0x68, 0xba, 0xbd, 0xba, 0x10, 0x90, 0xb0, 0xc1, 0xb0, 0xc7, 0x54, 0x43, 0x4f, 0xc7, 0xaf, 0x2e, 0x3e, 0x79, 0xab, 0x88, 0xa5, 0x71, 0x06, 0x10, 0x41, 0x2c, 0xbe, 0x54, 0x80, 0xf3, 0xba, 0xe4, 0x48, 0x33, 0xa8, 0xa1, 0x08, 0x51, 0x4d, 0x37, 0xf6, 0x45, 0xc6, 0x4b, 0x91, 0x8b, 0x6e, 0xf8, 0xbd, 0x38, 0x6e, 0x8c, 0x25, 0x3c, 0x31, 0xaa, 0xad, 0x15, 0x2f, 0x52, 0x1c, 0x95, 0xdc, 0x59, 0x57, 0x6e, 0xa5, 0x60, 0xc4, 0x9f, 0x43, 0x61, 0xbb, 0x0f, 0xdd, 0x3f, 0x16, 0xd3, 0xb2, 0x0e, 0x46, 0xfd, 0x4a, 0xfc, 0x80, 0xfb, 0x6b, 0xff, 0xe5, 0xc2, 0x47, 0xc1, 0xbc, 0x6d, 0xae, 0x66, 0x1f, 0x07, 0x2a, 0x2e, 0xb5, 0x8f, 0x58, 0x09, 0x4b, 0x89, 0xe8, 0x93, 0x82, 0xa8, 0x0b, 0x26, 0xca, 0x50, 0x48, 0xea, 0x6b, 0x0d, 0xee, 0x12, 0x5a, 0x3f, 0xaa, 0xad, 0x04, 0x90, 0xf4, 0xbb, 0x73, 0xf2, 0x54, 0x76, 0x4f, 0x48, 0xb9, 0xed, 0xb9, 0xeb, 0x5c, 0x70, 0xf9, 0xa9, 0xf0, 0x6a, 0x7a, 0x0d, 0x16, 0x9a, 0x9c, 0xe5, 0x59, 0xda, 0x3e, 0x6f, 0x1e, 0xd9, 0x1d, 0xa4, 0x55, 0x7e, 0x65, 0x75, 0xe3, 0xd5, 0x0d, 0x09, 0xf7, 0xbc, 0xd9, 0x3f, 0xd2, 0xb0, 0x26, 0x08, 0xb8, 0x79, 0xcd, 0x64, 0x06, 0x85, 0xce, 0x0a, 0xaf, 0x42, 0x89, 0x07, 0x8d, 0xba, 0xb2, 0xe6, 0x5c, 0x45, 0x4f, 0x2a, 0x39, 0x1f, 0x6b, 0x21, 0x05, 0x1c, 0x46, 0x64, 0x06, 0xe4, 0x37, 0x89, 0xa1, 0x64, 0x7d, 0xdb, 0x0d, 0x58, 0xc0, 0x17, 0x54, 0x28, 0xf7, 0x90, 0x07, 0x79, 0xf3, 0x26, 0x78, 0x1d, 0xcd, 0x00, 0x5f, 0x5a, 0xe9, 0xf7, 0x3c, 0xa6, 0x5d, 0x0f, 0xda, 0x4f, 0xdd, 0x88, 0xcd, 0xb1, 0x89, 0x48, 0xc6, 0xf2, 0x34, 0xbb, 0x13, 0x61, 0x07, 0x67, 0xbe, 0xf5, 0x94, 0xc7, 0xfd, 0x86, 0xc0, 0x38, 0x01, 0xc0, 0x0e, 0x01, 0x58, 0xe7, 0x3f, 0xc7, 0x52, 0xe8, 0x50, 0xf2, 0x41, 0x5b, 0x33, 0xd9, 0x6d, 0x69, 0xf3, 0xb8, 0x1c, 0xb3, 0xfc, 0xbe, 0x09, 0xbe, 0xce, 0xc1, 0x21, 0x75, 0x4b, 0x6a, 0x6e, 0xc1, 0x61, 0xce, 0x79, 0x61, 0xd2, 0xe6, 0x97, 0x5b, 0x48, 0x48, 0xe5, 0xd0, 0x02, 0x0e, 0xe5, 0x05, 0xc6, 0xe5, 0xff, 0xda, 0xdf, 0x99, 0x63, 0x1b, 0x5c, 0xa1, 0x01, 0x50, 0xd8, 0x7f, 0x9a, 0xff, 0x70, 0xff, 0x23, 0x88, 0xbf, 0x9d, 0xf5, 0x08, 0x03, 0xa1, 0x88, 0xf1, 0xc0, 0xbb, 0xb3, 0x42, 0x7d, 0x2e, 0xb3, 0x84, 0x7a, 0xd4, 0x6a, 0x05, 0x7e, 0x86, 0x9b, 0xf5, 0x13, 0xb6, 0x1d, 0x51, 0xcc, 0x47, 0x40, 0xc5, 0x73, 0xee, 0xde, 0x02, 0x45, 0xa6, 0xd9, 0x16, 0x7c, 0x08, 0x01, 0xd2, 0xeb, 0x90, 0x92, 0x54, 0x5c, 0x1a, 0xe1, 0x29, 0xf8, 0x23, 0xd2, 0xb4, 0xec, 0xa1, 0x32, 0xc0, 0x09, 0x70, 0x1a, 0x77, 0x21, 0xb1, 0xe5, 0x11, 0x52, 0x96, 0x70, 0x07, 0x88, 0x73, 0x9c, 0x99, 0xe6, 0x20, 0x5b, 0xe2, 0xda, 0xed, 0xae, 0xea, 0x12, 0xc1, 0x6e, 0x50, 0x99, 0x27, 0xb9, 0x8c, 0xc1, 0x58, 0x62, 0xec, 0xbd, 0x92, 0xc4, 0xb0, 0xf4, 0x41, 0xc4, 0x04, 0x7b, 0xca, 0xf8, 0xaf, 0x4a, 0x03, 0x2f, 0xb9, 0x0b, 0xa3, 0xde, 0x5c, 0x99, 0x3c, 0x19, 0x01, 0x90, 0x81, 0x35, 0x28, 0x91, 0xed, 0x2c, 0x6c, 0x02, 0x0f, 0x57, 0x10, 0x7a, 0x0f, 0x33, 0xbd, 0x2e, 0x34, 0x28, 0x61, 0xf6, 0x74, 0x96, 0x58, 0xca, 0x64, 0x9d, 0x4a, 0xdb, 0x03, 0x64, 0x74, 0x8f, 0x3f, 0xd0, 0x39, 0x1a, 0x36, 0xf1, 0x2e, 0x6c, 0x7c, 0xa0, 0x13, 0xb6, 0xf8, 0xfe, 0x5a, 0xc2, 0x27, 0xe7, 0x30, 0xcc, 0xc3, 0x78, 0x31, 0xb7, 0x2f, 0x40, 0x31, 0xad, 0x1a, 0xc1, 0xca, 0x97, 0x43, 0x53, 0x9b, 0x00, 0x4c, 0x8d, 0x61, 0x3d, 0x00, 0xc2, 0x52, 0xfd, 0x8b, 0x4b, 0x74, 0x97, 0x70, 0x93, 0xd1, 0xb5, 0x52, 0x25, 0xad, 0x36, 0xd4, 0x3c, 0x42, 0x67, 0x19, 0x88, 0x94, 0xeb, 0xc2, 0x30, 0x5c, 0xec, 0x5b, 0x3f, 0x24, 0x7a, 0x98, 0x07, 0x17, 0x29, 0xea, 0x23, 0x1a, 0x54, 0x30, 0x2d, 0x2b, 0x99, 0x99, 0x4f, 0x25, 0x6d, 0xf9, 0x2c, 0x63, 0xe6, 0x2c, 0xf4, 0xdd, 0x15, 0x48, 0x79, 0xbf, 0x8d, 0xa3, 0xad, 0x8e, 0xed, 0x11, 0xdb, 0x63, 0x99, 0xca, 0x72, 0xcd, 0xe5, 0xdb, 0x5c, 0x5c, 0x69, 0x21, 0x0b, 0xea, 0x48, 0x17, 0xd0, 0x2e, 0x88, 0x13, 0x23, 0x20, 0xe0, 0xb2, 0xe7, 0x39, 0x91, 0x7b, 0xdf, 0xa6, 0x24, 0x0f, 0xce, 0xe8, 0x2e, 0x75, 0x0a, 0x84, 0x77, 0x3a, 0x68, 0xb3, 0x18, 0x8a, 0xd4, 0x3e, 0x77, 0x7b, 0x09, 0xf0, 0x32, 0x22, 0x1a, 0x4e, 0x5d, 0x34, 0xd5, 0x77, 0xb0, 0x4a, 0x3f, 0x6c, 0x70, 0x0a, 0x97, 0x10, 0x4f, 0xc9, 0xa1, 0xa9, 0x7d, 0x68, 0x3f, 0xb6, 0x9c, 0x86, 0x89, 0x59, 0x6d, 0x48, 0xdf, 0xac, 0x4b, 0x40, 0xbf, 0xc9, 0xe1, 0x52, 0x42, 0x56, 0xa0, 0x8d, 0xa0, 0xce, 0xe6, 0x4d, 0x0d, 0xf4, 0xe5, 0x37, 0xcf, 0x19, 0xad, 0xd6, 0x74, 0x1e, 0xf1, 0xdd, 0x6b, 0x64, 0x61, 0x71, 0x7d, 0x15, 0xc1, 0xe9, 0xdd, 0x6b, 0x04, 0xe9, 0xd3, 0xe9, 0x20, 0xd6, 0x35, 0xb3, 0x60, 0xf8, 0x2d, 0x66, 0x5e, 0xc7, 0x12, 0x28, 0x02, 0x48, 0x3b, 0xd6, 0x14, 0x69, 0x20, 0x81, 0x50, 0xc1, 0x30, 0x97, 0x02, 0x2d, 0x25, 0x7f, 0xa3, 0x25, 0xd0, 0x77, 0xf1, 0xc9, 0x96, 0xf4, 0x7b, 0xfd, 0x18, 0xe8, 0xac, 0x11, 0xcb, 0x9a, 0xba, 0xd7, 0x70, 0x00, 0x0b, 0x1a, 0xb4, 0xc1, 0xf8, 0xd5, 0x6c, 0x4f, 0x03, 0xb1, 0x28, 0xc6, 0xde, 0x8a, 0x23, 0x7a, 0xd7, 0x20, 0xbf, 0x5d, 0x99, 0x24, 0x13, 0xb1, 0x46, 0x44, 0x3f, 0xaa, 0x58, 0xef, 0x8e, 0xf0, 0xda, 0xf1, 0x93, 0x2c, 0xdd, 0x1f, 0xc7, 0x0e, 0x3c, 0xab, 0x5f, 0x74, 0x19, 0xed, 0x5e, 0xa1, 0x9b, 0xa6, 0x12, 0xb0, 0xdf, 0x09, 0x85, 0xaf, 0x25, 0x20, 0x97, 0x07, 0xa3, 0x50, 0xb9, 0x2e, 0x8e, 0x06, 0x91, 0xbd, 0x46, 0xe2, 0xfc, 0x5a, 0xa9, 0xe7, 0x24, 0x30, 0xfd, 0x7a, 0xb2, 0xce, 0x0b, 0x3d, 0x51, 0xa2, 0x37, 0x1a, 0xfd, 0x7f, 0xc7, 0xfd, 0xe1, 0xd2, 0xa4, 0x0f, 0xb5, 0x78, 0x36, 0x84, 0xe4, 0x62, 0x40, 0x3a, 0xeb, 0x6c, 0xc9, 0xc7, 0xb7, 0x17, 0x76, 0xad, 0xf9, 0x18, 0xf6, 0xd9, 0xa5, 0xdc, 0x4e, 0x9c, 0x2b, 0x77, 0x2e, 0xc4, 0x0e, 0xa1, 0xaa, 0x95, 0xed, 0xbd, 0xe3, 0xa9, 0x25, 0x00, 0x99, 0x13, 0x94, 0x32, 0xa1, 0x3c, 0x3b, 0xb9, 0xb6, 0x5b, 0xad, 0xca, 0x33, 0x84, 0x9b, 0xfb, 0x96, 0xa4, 0xe2, 0x9c, 0xe1, 0xf2, 0xf3, 0x7f, 0x47, 0x9c, 0x6e, 0x23, 0xae, 0xc9, 0x53, 0x6d, 0xe5, 0x2f, 0x21, 0x4e, 0xb8, 0xd0, 0xb7, 0x1a, 0x30, 0xa9, 0xf0, 0x62, 0x9a, 0x5a, 0x22, 0x91, 0x43, 0x20, 0x1d, 0xac, 0xe8, 0x9a, 0x49, 0xe9, 0x53, 0x36, 0xeb, 0x02, 0xb1, 0x4a, 0xd1, 0x74, 0x7b, 0x23, 0xda, 0x15, 0x1b, 0x9e, 0x17, 0xb2, 0x9c, 0x11, 0x83, 0x93, 0x09, 0x8c, 0x8f, 0x21, 0x51, 0xce, 0xf8, 0x15, 0xd2, 0x62, 0xae, 0xd2, 0xe8, 0xc8, 0xb7, 0xe6, 0x0b, 0xa7, 0x63, 0xd4, 0x77, 0x57, 0x2f, 0x91, 0x59, 0x55, 0x29, 0x6e, 0xf8, 0x61, 0xd0, 0x43, 0x5f, 0x5f, 0x9a, 0x26 }; +constexpr AccessUnit AC3_SKIP_BYTES_EXPECTED_AU = { 0x15f90, 0x153d5, false, 0, {}, { 0x8e, 0x59, 0x01, 0x62, 0x21, 0xbd, 0x77, 0x65, 0x7b, 0xfe, 0x1e, 0xb1, 0x20, 0x49, 0xce, 0x3f, 0xed, 0x98, 0xe4, 0x99 } }; + +const std::vector LPCM_SKIP_BYTES_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xf3, 0x3b, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0x7a, 0x01, 0xe0, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x1a, 0x00, 0x80, 0x20, 0x90, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x00, 0x01, 0xbd, 0x07, 0x5a, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xa7, 0xab, 0x1e, 0x73, 0x3b, 0x40, 0xe6, 0xa3, 0x12, 0x99, 0xcf, 0x16, 0x2e, 0x6d, 0x67, 0x2a, 0xa2, 0xb4, 0xf7, 0xa2, 0x3c, 0xdf, 0x11, 0xe7, 0x98, 0x98, 0xef, 0x5c, 0x93, 0xbf, 0x51, 0x56, 0xaa, 0x5c, 0x26, 0x2d, 0x8e, 0xc3, 0x34, 0x23, 0x45, 0x68, 0x25, 0x5c, 0x60, 0x1a, 0xa9, 0x35, 0x12, 0xa7, 0x95, 0x8f, 0x35, 0x23, 0x2e, 0x00, 0x85, 0x49, 0x0c, 0xe8, 0xa6, 0xbc, 0x42, 0xe5, 0xb8, 0x18, 0xb0, 0x29, 0x86, 0x1c, 0xb6, 0x9d, 0xa0, 0x5c, 0xa9, 0xea, 0xee, 0x40, 0xc2, 0xbc, 0x80, 0x4c, 0x63, 0xe7, 0xab, 0xe4, 0xf1, 0x41, 0xc0, 0x15, 0x9e, 0x52, 0xeb, 0x12, 0xd9, 0x34, 0x02, 0x6b, 0x50, 0x3c, 0x4c, 0x38, 0xa5, 0x53, 0x99, 0xac, 0x57, 0x0b, 0x29, 0xd9, 0x6f, 0xd8, 0x3c, 0x96, 0xfa, 0x05, 0x45, 0xc7, 0x66, 0x43, 0x1e, 0x88, 0x3b, 0xe5, 0xbe, 0xed, 0xd0, 0x3b, 0x42, 0x59, 0xf8, 0x84, 0x52, 0xab, 0xc8, 0x7e, 0x2f, 0xb5, 0xdd, 0x8c, 0xf7, 0xc2, 0xe1, 0xe1, 0xeb, 0x1a, 0x25, 0x47, 0xbf, 0xd1, 0x8c, 0x5f, 0x49, 0x51, 0x2c, 0x71, 0x0e, 0x5c, 0x51, 0x36, 0x86, 0x89, 0xcf, 0x21, 0x1c, 0x6d, 0xd6, 0x1c, 0x36, 0xf9, 0xa1, 0xc0, 0xd2, 0xbc, 0xc0, 0x77, 0x85, 0x11, 0xe7, 0x11, 0xe8, 0x6b, 0x56, 0xb6, 0xbe, 0x41, 0x6f, 0x4b, 0x5f, 0x52, 0xfc, 0xa7, 0x30, 0x48, 0x30, 0x39, 0x44, 0xdc, 0x7d, 0xed, 0x56, 0x1b, 0xb8, 0x7c, 0xc7, 0x6d, 0x26, 0x7c, 0x56, 0x5b, 0x72, 0xca, 0xa2, 0x65, 0xab, 0x2b, 0xcf, 0x50, 0xb6, 0x49, 0xc9, 0xa8, 0xe7, 0x52, 0xf8, 0x84, 0xa9, 0xf5, 0x76, 0xb6, 0x5f, 0x78, 0x09, 0xe5, 0xef, 0x1b, 0x79, 0x5d, 0xa0, 0x17, 0xfe, 0x77, 0x31, 0xdc, 0xe4, 0xf1, 0x66, 0x67, 0xa1, 0x98, 0x66, 0xc8, 0xf8, 0x16, 0x99, 0x3f, 0xe7, 0xd2, 0xfc, 0x81, 0x72, 0x20, 0x0f, 0x06, 0xa0, 0x44, 0xc6, 0x7a, 0x1d, 0xfa, 0x30, 0xb1, 0x73, 0x8d, 0xbf, 0xb7, 0xb8, 0xa4, 0x57, 0xd3, 0x2d, 0x58, 0x98, 0xa7, 0x2c, 0x3e, 0x0d, 0xba, 0x5b, 0x6a, 0x21, 0x0f, 0x0f, 0xa3, 0x18, 0xa5, 0x3c, 0x47, 0x70, 0xfc, 0x7e, 0x95, 0x4d, 0xfa, 0xfc, 0x65, 0xed, 0xcd, 0x3f, 0x7a, 0x3f, 0xa0, 0x40, 0xf3, 0x1b, 0x4c, 0x38, 0xa2, 0xb5, 0x55, 0x07, 0x27, 0x41, 0xd3, 0x34, 0x2a, 0xf6, 0x25, 0xd6, 0x24, 0xd4, 0x86, 0xf8, 0x64, 0xe1, 0xe4, 0x35, 0x6e, 0xbf, 0x96, 0xaa, 0xcf, 0xd7, 0xc1, 0x7c, 0xf0, 0x70, 0x9e, 0x85, 0xa9, 0x74, 0xe4, 0xf2, 0x88, 0xbc, 0x07, 0xdf, 0x07, 0x1b, 0x1d, 0x3a, 0x33, 0x8f, 0x82, 0x7e, 0x7f, 0x4a, 0xa5, 0x10, 0xba, 0x6f, 0x8c, 0x41, 0x67, 0xc9, 0xee, 0x56, 0xf9, 0xcd, 0x08, 0x0e, 0x9f, 0xa3, 0x7e, 0x75, 0xb2, 0x24, 0xe7, 0x20, 0xec, 0xea, 0xe5, 0x06, 0xf1, 0xef, 0xde, 0x96, 0xe4, 0x59, 0xb8, 0xcf, 0xfd, 0x69, 0x2e, 0x5e, 0x68, 0xb5, 0xa1, 0x18, 0xdd, 0x1a, 0x7b, 0x63, 0xb3, 0x0d, 0x81, 0x40, 0x6d, 0x3f, 0xb5, 0x1c, 0xf3, 0xbf, 0x9c, 0xad, 0xff, 0xf9, 0x0d, 0x5a, 0x73, 0x56, 0xdb, 0x0d, 0x91, 0x4d, 0x00, 0x6f, 0x5b, 0x2e, 0x1c, 0xdf, 0x8e, 0xf6, 0x6b, 0xa8, 0xf8, 0xf2, 0x8d, 0x53, 0x23, 0x09, 0xb5, 0x0d, 0x97, 0xe5, 0x23, 0x77, 0x22, 0x64, 0x04, 0x77, 0xf3, 0xf9, 0x11, 0xb4, 0x75, 0xf0, 0x88, 0xf9, 0x30, 0xba, 0x60, 0x9c, 0x24, 0xb3, 0xd0, 0x95, 0x65, 0x07, 0x87, 0x07, 0xe5, 0xb0, 0x30, 0x3e, 0xb9, 0x9e, 0x1e, 0x80, 0x42, 0x9c, 0x3d, 0x2f, 0x0c, 0x72, 0xa0, 0x06, 0x4a, 0x5b, 0x52, 0x92, 0x38, 0xd5, 0x36, 0x15, 0xd9, 0x55, 0x25, 0x76, 0xd6, 0x74, 0x96, 0x04, 0x67, 0x96, 0x73, 0x11, 0x9c, 0x1f, 0xf9, 0xb7, 0x6c, 0xbf, 0xb7, 0x58, 0x4c, 0x62, 0x13, 0x97, 0x56, 0xca, 0xeb, 0xe5, 0x2a, 0xee, 0x28, 0xe2, 0x0c, 0x97, 0x6b, 0x2a, 0x09, 0xd5, 0x28, 0x4a, 0xff, 0x52, 0xd0, 0xfa, 0xea, 0x91, 0xe0, 0xdf, 0xd0, 0xa3, 0xf2, 0x5c, 0x24, 0xe0, 0x38, 0x4e, 0x83, 0xad, 0xe8, 0xaf, 0x1f, 0x68, 0x87, 0x72, 0x40, 0xe6, 0x25, 0x26, 0xa2, 0x3a, 0xed, 0x2a, 0xab, 0xae, 0x9b, 0x83, 0xae, 0x11, 0xc4, 0x8f, 0xc5, 0xcd, 0xf6, 0x39, 0xe9, 0x74, 0x22, 0x69, 0x5d, 0xab, 0xe0, 0xc6, 0x96, 0x9b, 0x66, 0x05, 0x1b, 0x63, 0x5c, 0x70, 0x29, 0x27, 0xd9, 0x4c, 0x09, 0xaa, 0x2d, 0x48, 0x59, 0xb1, 0x5b, 0x33, 0xc1, 0x7b, 0x4b, 0x80, 0xd3, 0x1f, 0x27, 0x78, 0x18, 0x9a, 0x6c, 0x0e, 0x72, 0xd0, 0x9c, 0xb2, 0x68, 0xe5, 0x9c, 0xa6, 0x08, 0x93, 0x25, 0xa9, 0x5e, 0x78, 0xe4, 0xfb, 0xbf, 0x74, 0x1f, 0x0a, 0x62, 0x9b, 0x01, 0x58, 0x02, 0x51, 0xe1, 0x5f, 0x88, 0x87, 0x2d, 0x84, 0xf7, 0x48, 0xc5, 0x60, 0x15, 0xf5, 0xf4, 0xf6, 0x8e, 0xec, 0x4d, 0x90, 0x90, 0x7e, 0x1f, 0x5f, 0x18, 0x55, 0x4d, 0x27, 0x69, 0xd2, 0xa9, 0x7a, 0x75, 0xf1, 0x2f, 0x56, 0x28, 0xb4, 0xc4, 0x35, 0xd2, 0x35, 0x41, 0xcf, 0x36, 0xd0, 0xf9, 0x26, 0xef, 0x12, 0x12, 0xa3, 0x2c, 0x57, 0x45, 0x4e, 0xff, 0x40, 0xe7, 0x73, 0xba, 0x76, 0x58, 0x34, 0x12, 0x59, 0x34, 0xea, 0x15, 0x95, 0xda, 0x45, 0x0f, 0xbf, 0x32, 0x8e, 0x01, 0x68, 0xc2, 0x8b, 0x42, 0x58, 0x78, 0xd8, 0x62, 0xd1, 0xbe, 0xb5, 0x77, 0x17, 0xb1, 0xa7, 0x5d, 0x93, 0xaa, 0x69, 0xa2, 0x57, 0x56, 0xfb, 0x10, 0xd2, 0x19, 0xeb, 0x22, 0x00, 0x80, 0x03, 0xd1, 0x63, 0xec, 0x14, 0x83, 0x86, 0xc8, 0x7a, 0x05, 0xf0, 0xf6, 0x94, 0xe2, 0xae, 0x36, 0x54, 0x81, 0xea, 0xbb, 0x8d, 0xd3, 0x2c, 0x3f, 0xbe, 0x80, 0x48, 0x30, 0x71, 0xdf, 0x08, 0xd7, 0x69, 0x25, 0x1a, 0x96, 0x3e, 0x90, 0xd9, 0x8f, 0x27, 0x8d, 0xe9, 0x60, 0x01, 0x09, 0xd0, 0xc3, 0xdd, 0x75, 0xed, 0x2f, 0xaa, 0x35, 0x90, 0xdf, 0xba, 0x6f, 0x07, 0xd2, 0x36, 0x83, 0xda, 0xa6, 0xcb, 0x9f, 0x9a, 0x64, 0x1e, 0x2d, 0x00, 0x9b, 0xd2, 0x15, 0x28, 0x70, 0x24, 0x18, 0x38, 0x69, 0x79, 0xaf, 0xc4, 0x2a, 0x60, 0x3e, 0xac, 0x66, 0xcd, 0x89, 0x55, 0xb6, 0xad, 0xa9, 0x6c, 0x21, 0x0c, 0x06, 0xa7, 0x7f, 0x5d, 0x00, 0x20, 0x03, 0xc0, 0x5e, 0x4b, 0x9f, 0x39, 0x9f, 0x98, 0xff, 0x34, 0xc6, 0x4b, 0x57, 0xde, 0x47, 0x1f, 0x4d, 0x26, 0x21, 0xa9, 0x97, 0x5e, 0x56, 0xab, 0x3f, 0xe0, 0x44, 0xb4, 0x92, 0x35, 0x96, 0xd2, 0x1f, 0x8d, 0xcb, 0x24, 0x69, 0x93, 0x93, 0x95, 0x1c, 0xec, 0x78, 0x11, 0xe4, 0xec, 0x8d, 0xfa, 0x4c, 0x4a, 0x2d, 0xb1, 0x33, 0xe2, 0xef, 0x74, 0xbb, 0xd1, 0x6a, 0xec, 0xcd, 0x77, 0x5d, 0x20, 0x8a, 0x26, 0x63, 0xba, 0x51, 0x95, 0x4c, 0xc8, 0x1f, 0x56, 0xa5, 0x5a, 0x83, 0xe3, 0x32, 0xc3, 0x64, 0x11, 0x45, 0x4d, 0x4f, 0xe9, 0x3f, 0x9c, 0x16, 0x3d, 0xa8, 0x2c, 0xe4, 0x86, 0xa8, 0x8b, 0x27, 0xce, 0xd3, 0x48, 0xda, 0xb9, 0x85, 0xbe, 0x2a, 0xf8, 0x52, 0x1d, 0x69, 0xb1, 0x3a, 0x75, 0xd6, 0xa8, 0xa9, 0x91, 0xb0, 0x35, 0x73, 0xe9, 0x92, 0x54, 0x56, 0x06, 0x10, 0x5b, 0xb0, 0x07, 0x18, 0x1c, 0x6b, 0x3d, 0x8f, 0x8b, 0x60, 0xf9, 0x21, 0x2e, 0xa8, 0x07, 0x51, 0xd7, 0x8f, 0x7e, 0x28, 0x8e, 0x39, 0xc6, 0x23, 0x9d, 0x1e, 0x17, 0x37, 0x1d, 0xef, 0xbf, 0x0c, 0x81, 0x8e, 0xfd, 0xc6, 0xf8, 0x1d, 0x3a, 0xfb, 0xad, 0x61, 0xed, 0xae, 0x27, 0xf4, 0x76, 0x79, 0x6c, 0xff, 0xbf, 0x26, 0x99, 0x7b, 0x69, 0x75, 0x0d, 0x36, 0xd3, 0xd0, 0x70, 0x11, 0x51, 0x18, 0x17, 0x43, 0x48, 0xdf, 0xa3, 0x82, 0x17, 0xaf, 0xd2, 0x6f, 0x03, 0x40, 0xe7, 0xa5, 0x7f, 0xa2, 0x1a, 0x39, 0x8f, 0xdd, 0xf0, 0x99, 0x2a, 0x79, 0x83, 0x15, 0xc3, 0xed, 0xfb, 0x80, 0x6a, 0xf8, 0xd3, 0xb1, 0x38, 0x51, 0xae, 0xd5, 0xde, 0x42, 0xc5, 0xd7, 0x99, 0x06, 0x45, 0x52, 0xe8, 0x13, 0x1d, 0xd5, 0xb9, 0xa1, 0x10, 0x61, 0xef, 0x08, 0x11, 0x79, 0x7c, 0xe2, 0x3e, 0x21, 0x6b, 0xf4, 0xfb, 0x9f, 0x92, 0x53, 0xff, 0xf6, 0xce, 0x4b, 0x61, 0x48, 0x00, 0xf7, 0xec, 0xb2, 0x2e, 0x96, 0x49, 0x49, 0x79, 0x07, 0xc9, 0x45, 0xce, 0xfb, 0xdf, 0x86, 0x89, 0xc5, 0x91, 0xff, 0x6a, 0xd9, 0x6c, 0xba, 0x8d, 0x5c, 0xd1, 0x69, 0x58, 0x68, 0x98, 0xd9, 0x7c, 0xcc, 0x5c, 0xa3, 0x80, 0x78, 0x03, 0xf5, 0x83, 0xea, 0x40, 0x59, 0x16, 0x32, 0x28, 0xc0, 0x6b, 0x73, 0x1b, 0x4a, 0x30, 0xf5, 0x95, 0x8f, 0xd6, 0x29, 0xb3, 0x56, 0x25, 0x3a, 0x6e, 0xff, 0x47, 0x02, 0xe8, 0x25, 0xc2, 0x8b, 0x44, 0x1a, 0xfb, 0x68, 0xed, 0x59, 0x47, 0x90, 0x3a, 0x12, 0x93, 0x8b, 0xda, 0x54, 0x3b, 0x1d, 0x7b, 0x93, 0x8d, 0xba, 0x86, 0x88, 0x0e, 0x65, 0xfd, 0x99, 0x8f, 0xdb, 0xb7, 0x5c, 0x8b, 0x26, 0x9c, 0xdb, 0x7e, 0xe5, 0x9c, 0xa1, 0x14, 0xfb, 0x79, 0xcb, 0x6a, 0x6f, 0xa0, 0x93, 0xbc, 0xaa, 0xb4, 0x1d, 0x46, 0x54, 0x81, 0x6f, 0x23, 0x92, 0x71, 0xe5, 0x73, 0x5d, 0xc4, 0x92, 0x26, 0x23, 0x2a, 0x43, 0x17, 0x06, 0x6e, 0x34, 0x68, 0x5e, 0x5a, 0x9c, 0x49, 0x49, 0x11, 0xa3, 0x9a, 0x76, 0x6d, 0x67, 0x09, 0x7f, 0x2f, 0x1e, 0xa5, 0x70, 0x0a, 0x89, 0x09, 0x51, 0xe0, 0x33, 0x94, 0xdf, 0xa6, 0x3f, 0x4a, 0xb6, 0xd7, 0xb6, 0x5e, 0xa9, 0x69, 0x9c, 0x81, 0x03, 0xa8, 0x42, 0x6e, 0xe2, 0x66, 0x91, 0x52, 0x08, 0x6d, 0x4e, 0x0b, 0xae, 0x1f, 0x8c, 0x5b, 0x45, 0xad, 0xd1, 0x89, 0x3c, 0x59, 0xa0, 0x0d, 0x24, 0x9f, 0x72, 0x47, 0xcf, 0x45, 0x45, 0x54, 0x4c, 0xb1, 0x53, 0x84, 0xd2, 0xf3, 0x9f, 0x11, 0xff, 0x6e, 0x5a, 0x00, 0x01, 0x17, 0x5b, 0x65, 0x89, 0xa3, 0x1e, 0x6a, 0xa1, 0x21, 0x1c, 0xca, 0xc1, 0xe2, 0x56, 0xa8, 0xc6, 0xa1, 0x75, 0xf9, 0x98, 0x4e, 0xdc, 0xe0, 0xae, 0xfb, 0x76, 0xaa, 0xb8, 0xb8, 0x42, 0x62, 0x23, 0x70, 0xc5, 0x19, 0x33, 0x0d, 0xf6, 0x55, 0xf7, 0x70, 0x4b, 0x5e, 0x45, 0x12, 0xf3, 0x38, 0x8a, 0x43, 0x6f, 0x9c, 0x1f, 0x75, 0x08, 0x36, 0x61, 0xfb, 0xe4, 0xf6, 0xcd, 0x26, 0xa9, 0x45, 0xd0, 0xba, 0x00, 0x5b, 0x1e, 0x0a, 0x53, 0x19, 0xac, 0x4c, 0xaa, 0x1c, 0xaa, 0xf2, 0x7f, 0xe6, 0x19, 0x07, 0xd9, 0x5b, 0xdd, 0xed, 0x27, 0xf0, 0x5d, 0x01, 0x96, 0x40, 0x19, 0xe6, 0xd8, 0xee, 0xc9, 0x9b, 0xa2, 0x04, 0xe6, 0x8e, 0x57, 0x39, 0xb2, 0x4e, 0x9c, 0xca, 0x2d, 0x9a, 0xd4, 0xd1, 0x76, 0xda, 0xbc, 0x4a, 0x8f, 0x56, 0xaa, 0x2c, 0x94, 0xb3, 0x20, 0x49, 0xbd, 0x7b, 0xba, 0xd9, 0xd4, 0xca, 0xaa, 0xe2, 0xec, 0x60, 0x47, 0xee, 0x80, 0xa5, 0x65, 0xa3, 0x53, 0x78, 0x6e, 0x4f, 0xa9, 0xc1, 0x87, 0x70, 0xbb, 0x20, 0x29, 0xbc, 0x7f, 0x47, 0xfa, 0x44, 0x1e, 0xba, 0xde, 0xb3, 0xb9, 0x15, 0x76, 0xb6, 0x57, 0x1b, 0x8b, 0x21, 0x0f, 0x12, 0x2e, 0x53, 0xe1, 0x4b, 0x71, 0xe7, 0xbc, 0xd1, 0x0d, 0xc8, 0xa7, 0x91, 0x5d, 0x24, 0x19, 0x8a, 0x7e, 0xdb, 0xe3, 0xce, 0x79, 0x5f, 0x46, 0x5d, 0xcb, 0x35, 0x0f, 0x2f, 0xb0, 0x74, 0xdd, 0xeb, 0x05, 0xfe, 0xe1, 0x28, 0xa2, 0x56, 0xbe, 0x36, 0x83, 0x40, 0x5f, 0xc1, 0x27, 0x9f, 0xe3, 0xdb, 0xec, 0x28, 0xfa, 0x35, 0x62, 0xf5, 0x4b, 0x97, 0xd1, 0x08, 0xc3, 0xdf, 0x67, 0x3a, 0xec, 0x1a, 0xda, 0x39, 0xa3, 0x77, 0x6a, 0x8c, 0xc3, 0x2e, 0x84, 0xbd, 0xb2, 0x9f, 0x88, 0x19, 0xe1, 0x2f, 0x67, 0xbc, 0x93, 0xc2, 0x15, 0xbe, 0x9d, 0xcb, 0x14, 0xcc, 0xe3, 0x43, 0x84, 0x7a, 0xc3, 0x1c, 0x84, 0x0f, 0xb8, 0xf4, 0xd8, 0xb0, 0xe0, 0x0d, 0x0e, 0x15, 0x35, 0xeb, 0xa7, 0x36, 0x9a, 0x0d, 0xf5, 0x9b, 0x23, 0x9c, 0xb2, 0x3a, 0x15, 0x16, 0x85, 0x13, 0xa5, 0x45, 0x00, 0xf0, 0x6a, 0x04, 0xbb, 0xfd, 0xf8, 0xb0, 0x9c, 0x18, 0x32, 0x39, 0x83, 0x13, 0x3d, 0x3b, 0xd0, 0x49, 0x1b, 0x91, 0x84, 0x3d, 0x61, 0x3b, 0xfb, 0x1a, 0x90, 0xaa, 0x78, 0x60, 0x03, 0xa5, 0xf8, 0x0c, 0x71, 0x78, 0x1c, 0xf6, 0x7c, 0x0f, 0x0d, 0x52, 0xd7, 0x2d, 0xaa, 0x76, 0xe8, 0xf9, 0x70, 0x5e, 0xf4, 0x6a, 0xe1, 0x8e, 0x4a, 0x70, 0x5a, 0x45, 0xf0, 0xca, 0xa5, 0x2e, 0xba, 0xf7, 0xad, 0xf1, 0xe8, 0xb1, 0x2a, 0x60, 0xa3, 0xc9, 0x25, 0x1e, 0xf3, 0x78, 0xac, 0xf1, 0x14, 0x61, 0x1a, 0x31, 0x38, 0xde, 0xbf, 0xee, 0x1f, 0x5b, 0xda, 0xd7, 0x19, 0xd0, 0x28, 0xe9, 0x4b, 0xa2, 0x83, 0x76, 0x66, 0x7d, 0x48, 0x14, 0x0f, 0xeb, 0x80, 0x70, 0xab, 0xbc, 0x18, 0x2d, 0x64, 0x85, 0x86, 0x34, 0x7f, 0xc3, 0xb7, 0x32, 0x65, 0x74, 0x2a, 0x1d, 0xf3, 0x29, 0x81, 0xf3, 0xfd, 0xe1, 0x03, 0x28, 0x06, 0x3e, 0xc0, 0x62, 0xa8, 0xd6, 0x62, 0x72, 0x2b, 0xb2, 0xe0, 0xc2, 0x41, 0x4b, 0x8e, 0x8c, 0xe6, 0x86, 0x43, 0xa2, 0xe3, 0x85, 0x9e, 0x9a, 0x6d, 0x55, 0x96, 0xa0, 0xd8, 0xc3, 0xda, 0x46, 0x64, 0xea, 0xbf, 0x0a, 0x96, 0x3c, 0xaf, 0x81, 0xa3, 0x22, 0x5a, 0x42, 0xd5, 0x6b, 0x30, 0x14, 0x0c, 0x25, 0xc2, 0xf9, 0xa6, 0xac, 0x2e, 0x05, 0x77, 0xea, 0xad, 0x3a, 0xa9, 0x2d, 0xd8, 0xc3, 0xa3, 0x0b, 0x57, 0x44, 0x4a, 0x28, 0xf1, 0x2b, 0x44, 0xed, 0x41, 0x2e, 0x11, 0x73, 0x8f, 0x59, 0x16, 0x36, 0x78, 0x68, 0xff, 0x5b, 0x76, 0x49, 0x06, 0x8f, 0x73, 0xfe, 0x03, 0xd4, 0xf3, 0x48, 0x69, 0xbe, 0x7d, 0xb9, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbd, 0x07, 0xec, 0x79, 0x01, 0x00, 0x40, 0xbf, 0x45, 0x1f, 0x31, 0x39, 0xc2, 0x40, 0x77, 0xfb, 0x6a, 0x24, 0xa5, 0xcb, 0x6c, 0xd8, 0x05, 0xaa, 0x6f, 0x6b, 0xa1, 0x51, 0x5c, 0xbf, 0x4c, 0xc1, 0x76, 0xed, 0x59, 0x4e, 0xa6, 0xc4, 0x9e, 0x1f, 0x40, 0x72, 0xa2, 0x7b, 0xaf, 0xe7, 0x0b, 0x6c, 0xcc, 0x79, 0xb2, 0x80, 0xe2, 0xd8, 0xea, 0xbe, 0x8d, 0x61, 0xec, 0x07, 0xf7, 0x4d, 0x6d, 0x8f, 0x38, 0x63, 0xc7, 0x14, 0xc2, 0x1c, 0x4e, 0x01, 0x22, 0x5b, 0xb4, 0xb4, 0x97, 0x33, 0x82, 0x77, 0x27, 0x5f, 0x24, 0xfe, 0x41, 0x57, 0x01, 0x75, 0x26, 0x2f, 0x97, 0x5d, 0x82, 0xb5, 0xa1, 0xda, 0x10, 0x68, 0xb9, 0x2c, 0x27, 0x45, 0xfa, 0x65, 0x81, 0x8f, 0x93, 0xa9, 0xac, 0xfa, 0x5d, 0x87, 0xeb, 0xed, 0xca, 0x2d, 0x73, 0xc4, 0xde, 0x8e, 0x54, 0x38, 0xf0, 0xbe, 0x7f, 0x64, 0x6c, 0x17, 0x9f, 0x09, 0xe0, 0xe9, 0xd0, 0x08, 0xf6, 0xd1, 0x67, 0x2a, 0x32, 0xf0, 0x43, 0xc1, 0xa1, 0x9a, 0x6f, 0xac, 0xb8, 0x4a, 0x04, 0xc5, 0x17, 0xdd, 0x97, 0xd3, 0x0b, 0x6e, 0x95, 0x7b, 0xd6, 0x4c, 0x6d, 0xce, 0x31, 0xf1, 0x5f, 0x64, 0x9b, 0xe4, 0x46, 0x3f, 0x47, 0xf5, 0xd4, 0xc6, 0xda, 0x42, 0xeb, 0x9b, 0x42, 0xe4, 0x28, 0x32, 0x47, 0x48, 0xd0, 0x4b, 0xbf, 0x80, 0x90, 0xd1, 0x6a, 0x17, 0xe6, 0xac, 0xe3, 0x3e, 0x15, 0x8c, 0x46, 0x4e, 0xf2, 0xd3, 0x4a, 0xf7, 0xfc, 0xf7, 0x15, 0xa4, 0x7c, 0x2c, 0x04, 0xe0, 0xb4, 0x17, 0x0e, 0xcb, 0x45, 0xe0, 0xa8, 0xdf, 0x5d, 0xef, 0x38, 0xa7, 0x58, 0x10, 0x74, 0x20, 0xdc, 0xe7, 0x58, 0xd4, 0xa3, 0xdd, 0x79, 0x09, 0x7b, 0xf9, 0xe4, 0x7a, 0x24, 0x44, 0xdb, 0x77, 0x20, 0xb5, 0x0d, 0x73, 0xa1, 0xc4, 0x26, 0x47, 0x0f, 0x12, 0xd5, 0xbf, 0xf7, 0xdb, 0xa4, 0x37, 0x3e, 0x0e, 0x36, 0x4f, 0xf6, 0x4c, 0xd5, 0x14, 0x37, 0x22, 0x5d, 0x13, 0xc8, 0x3e, 0xec, 0x34, 0x8c, 0x81, 0xed, 0x63, 0xf1, 0x1e, 0x5b, 0x4a, 0xde, 0x2c, 0x58, 0xb8, 0xda, 0x60, 0x77, 0x7b, 0x6c, 0xd1, 0x4a, 0xab, 0xe9, 0x01, 0x25, 0xd1, 0x37, 0x7f, 0x1a, 0x4f, 0x31, 0xe8, 0x9b, 0xf9, 0xb9, 0x24, 0x57, 0xd4, 0xa9, 0x0a, 0x2f, 0x4d, 0x24, 0xe3, 0x5e, 0x7e, 0xfd, 0x67, 0x35, 0x35, 0x39, 0x33, 0xff, 0xea, 0xe3, 0xd0, 0xf1, 0xd1, 0xa9, 0xce, 0x39, 0xa5, 0x0c, 0x84, 0x94, 0xf9, 0x0c, 0x79, 0xfc, 0x0e, 0x9e, 0x97, 0x4e, 0x8b, 0x5f, 0x90, 0x06, 0x8f, 0x3d, 0x2d, 0x6c, 0x05, 0x1d, 0x69, 0xc8, 0x21, 0xaa, 0x83, 0x87, 0x77, 0xde, 0x84, 0x5b, 0x1f, 0xeb, 0xc8, 0xc5, 0xbd, 0x90, 0x93, 0x8e, 0x72, 0x0b, 0xc8, 0x27, 0x0f, 0x33, 0x41, 0xf5, 0x01, 0x60, 0x62, 0xeb, 0xdc, 0x33, 0xde, 0x18, 0x77, 0x6b, 0xd7, 0xf8, 0xfa, 0x4a, 0xc8, 0xa8, 0x5b, 0x43, 0xdf, 0x60, 0x21, 0xf1, 0xb4, 0xc3, 0x72, 0x8d, 0x93, 0xee, 0xbb, 0x65, 0xb7, 0x52, 0x7b, 0x0b, 0x2a, 0xc7, 0x01, 0x3c, 0x31, 0x6a, 0x2a, 0xb7, 0x7d, 0x1a, 0x65, 0x86, 0x76, 0xc8, 0x89, 0x6d, 0x66, 0xdc, 0x55, 0x82, 0x56, 0x58, 0x95, 0x27, 0xe4, 0x9a, 0x46, 0xe1, 0x69, 0xc9, 0x38, 0xcb, 0x1c, 0x59, 0x0e, 0x9b, 0x35, 0x39, 0x8c, 0x81, 0x1a, 0x86, 0x98, 0x4e, 0x1d, 0xea, 0x71, 0xa6, 0x6f, 0xf3, 0xdf, 0x39, 0x25, 0x0a, 0x76, 0x40, 0x9b, 0xe6, 0x37, 0xb6, 0x7f, 0x8a, 0x09, 0x0e, 0x46, 0x3d, 0xf1, 0x58, 0xf0, 0x07, 0x00, 0x22, 0x46, 0x9b, 0x47, 0x84, 0x02, 0xc0, 0x6c, 0x0f, 0x80, 0xa4, 0x95, 0xae, 0x02, 0x9e, 0xc4, 0xa0, 0x8a, 0x53, 0xec, 0x16, 0xd7, 0x4a, 0x52, 0x27, 0x26, 0x54, 0xee, 0x1e, 0x9c, 0x5a, 0xff, 0x57, 0x91, 0x78, 0xf8, 0x2c, 0x1a, 0x80, 0x68, 0xaf, 0x95, 0x31, 0xa5, 0x21, 0x02, 0xd9, 0x40, 0x78, 0x77, 0x22, 0x98, 0x58, 0x03, 0x40, 0x09, 0x4e, 0xb2, 0xc2, 0x9a, 0x3d, 0xa9, 0xb9, 0x17, 0x3e, 0x79, 0x04, 0x07, 0x6f, 0x13, 0xe3, 0xe7, 0x6d, 0x1a, 0x21, 0x8f, 0x72, 0x6d, 0x87, 0xc4, 0x58, 0x20, 0x5d, 0x3e, 0xf7, 0x48, 0x28, 0xbc, 0x9f, 0x50, 0xef, 0xdf, 0xa9, 0xb9, 0xb1, 0xe8, 0x5c, 0x75, 0xe9, 0xa3, 0x99, 0x4b, 0x28, 0xfe, 0x17, 0xf1, 0x3e, 0x27, 0x28, 0xbf, 0x76, 0x38, 0x37, 0x31, 0xcf, 0xb9, 0x91, 0xa0, 0x43, 0x8a, 0x17, 0xdf, 0x88, 0xfc, 0xfa, 0xa3, 0xd5, 0x27, 0x9b, 0xed, 0x93, 0xd0, 0x1b, 0xd7, 0x11, 0x58, 0x5e, 0x72, 0x97, 0x96, 0x6b, 0xaa, 0x7b, 0x9c, 0x70, 0x4f, 0x06, 0xb6, 0xe8, 0xb9, 0x53, 0x27, 0x9b, 0xac, 0x7d, 0x83, 0x6c, 0x70, 0xb8, 0x9e, 0x45, 0x3f, 0x34, 0x65, 0x8a, 0x7d, 0xe3, 0x32, 0xd8, 0x66, 0x7e, 0x57, 0x0a, 0x12, 0x6b, 0x04, 0xc0, 0x11, 0x56, 0xb6, 0xe5, 0xf4, 0xa1, 0xda, 0xf1, 0x7d, 0x03, 0x5b, 0xdd, 0x60, 0xf4, 0x28, 0x0b, 0xbb, 0xc2, 0x0d, 0x9f, 0xba, 0x68, 0x5e, 0xfe, 0x0a, 0x8a, 0x3c, 0x77, 0x0f, 0x22, 0x94, 0x49, 0x02, 0xd8, 0x0e, 0x91, 0x67, 0xfa, 0x80, 0xc6, 0x8a, 0x93, 0xbe, 0xd2, 0xe6, 0x26, 0xb6, 0x02, 0xa7, 0xf6, 0x48, 0x36, 0xbd, 0xf0, 0x41, 0x2d, 0x12, 0x94, 0x68, 0xc6, 0xee, 0x7f, 0x8a, 0x93, 0xa0, 0x66, 0xe8, 0x1c, 0xdb, 0xac, 0x89, 0xa7, 0x7f, 0xe8, 0x3a, 0x73, 0xa7, 0xee, 0x4e, 0xa1, 0x3d, 0x4d, 0x68, 0x51, 0x6a, 0x4b, 0xd7, 0xa6, 0xa3, 0xbc, 0x55, 0xb3, 0x30, 0x64, 0x33, 0x87, 0x5f, 0x9f, 0x41, 0xd7, 0xce, 0x8d, 0xf7, 0x09, 0x7b, 0x04, 0xb1, 0x8b, 0x7f, 0xf2, 0x08, 0xc5, 0xa8, 0x0a, 0x96, 0x13, 0x58, 0xcf, 0xac, 0x8e, 0x58, 0x64, 0xcd, 0x9a, 0x8a, 0xb9, 0xf2, 0x73, 0xa6, 0xe8, 0xeb, 0x3a, 0x72, 0xf4, 0x64, 0x42, 0x13, 0x2a, 0x5b, 0x8a, 0x7c, 0xda, 0x12, 0xae, 0x0a, 0x31, 0xcd, 0xc5, 0xd2, 0x57, 0xe3, 0xef, 0x3d, 0x33, 0xf2, 0x7e, 0xe3, 0x68, 0x3e, 0x09, 0xd1, 0x67, 0x8a, 0xe0, 0xab, 0xbc, 0xfa, 0x8e, 0x4f, 0xe3, 0xc1, 0x84, 0xe0, 0x54, 0xab, 0x00, 0x78, 0x8c, 0xdc, 0xd5, 0xda, 0x40, 0x31, 0xb6, 0xd7, 0x13, 0x38, 0x04, 0x58, 0x61, 0xa2, 0x61, 0x2d, 0x09, 0x99, 0x86, 0x15, 0x46, 0x7b, 0x0d, 0xab, 0xfd, 0xc1, 0x30, 0x23, 0x11, 0x2a, 0xc3, 0xfc, 0xa8, 0x66, 0xd3, 0x21, 0x6c, 0xe1, 0xb4, 0x1a, 0x32, 0x68, 0x75, 0x3b, 0x96, 0xbc, 0xf3, 0xff, 0x7c, 0x64, 0x10, 0xfc, 0x77, 0x38, 0xc0, 0x18, 0x86, 0x8f, 0xea, 0xee, 0xc5, 0xf5, 0x64, 0x91, 0xd7, 0xe2, 0x74, 0x10, 0x42, 0xcf, 0x9f, 0x71, 0x03, 0x9d, 0xc0, 0x0f, 0xff, 0x41, 0x9f, 0x7d, 0x35, 0x0d, 0x85, 0x61, 0xb5, 0x1c, 0x8f, 0xf8, 0xd4, 0xaf, 0xb2, 0x21, 0x29, 0x8c, 0xb3, 0x2b, 0x2f, 0x9d, 0x90, 0xc2, 0xab, 0x6e, 0x0e, 0xb9, 0x3c, 0x48, 0x76, 0x8e, 0x97, 0xf6, 0xa6, 0xd3, 0x6a, 0x89, 0x0f, 0xfc, 0xec, 0xa0, 0x47, 0x25, 0x7f, 0xfe, 0x2c, 0xcf, 0xfb, 0x7e, 0xe4, 0xac, 0x88, 0xb5, 0x24, 0x06, 0x16, 0xc4, 0x81, 0x46, 0xd8, 0x2d, 0xcd, 0xa3, 0x5b, 0xb6, 0xaf, 0x1f, 0x17, 0xa2, 0xfd, 0x34, 0x8e, 0xd6, 0x95, 0xeb, 0x64, 0xa8, 0x9e, 0x3c, 0xfb, 0x70, 0x8f, 0xf0, 0x81, 0x1e, 0x50, 0x62, 0x74, 0x6b, 0x57, 0xdc, 0x15, 0x65, 0x9f, 0x70, 0x50, 0x70, 0xc3, 0x75, 0xfc, 0xf4, 0xc2, 0x28, 0x8d, 0x61, 0x0d, 0x09, 0xa2, 0x31, 0xf6, 0xe5, 0xef, 0x1b, 0xb1, 0xc2, 0x7a, 0x9f, 0x5b, 0x0b, 0x3b, 0x8f, 0xd7, 0xb7, 0x76, 0x5c, 0x69, 0x48, 0x7d, 0x27, 0x52, 0xdb, 0x9b, 0xf2, 0xf4, 0xa9, 0x62, 0x78, 0x26, 0xb4, 0x9f, 0x6e, 0xc3, 0x78, 0x9f, 0x4c, 0xa9, 0xe4, 0x9a, 0xd3, 0xab, 0xee, 0x65, 0xf4, 0x29, 0x84, 0xc1, 0xd3, 0x4d, 0xb6, 0xec, 0xf2, 0x2d, 0x4d, 0x3d, 0xe1, 0x4f, 0x3e, 0x09, 0xe8, 0x3c, 0x93, 0x04, 0x8b, 0x38, 0xd7, 0xd8, 0xe3, 0x3c, 0x0a, 0xc9, 0x2a, 0xcf, 0xaa, 0x70, 0xef, 0x5b, 0x65, 0x01, 0xdd, 0x10, 0x74, 0xac, 0xfc, 0x8b, 0xbb, 0xed, 0xd9, 0xb0, 0x1e, 0x02, 0x11, 0x8e, 0xc3, 0xbb, 0x05, 0x2d, 0x19, 0x6c, 0xd7, 0x93, 0xdf, 0xb6, 0xe7, 0x76, 0x93, 0x5f, 0x6a, 0x5d, 0xf0, 0x85, 0xe3, 0x1e, 0x75, 0xd0, 0x83, 0x05, 0x06, 0x7b, 0xc9, 0x1a, 0x39, 0x50, 0xe5, 0x3f, 0xd5, 0xf6, 0xae, 0x1e, 0x89, 0x2f, 0x2e, 0x38, 0xff, 0xd1, 0x21, 0x76, 0xb0, 0xb6, 0xe3, 0x06, 0x30, 0xdd, 0xa2, 0x82, 0xdc, 0x6f, 0xda, 0x9a, 0x07, 0x79, 0x71, 0xb5, 0x30, 0x7b, 0xef, 0x55, 0xcb, 0x2a, 0xae, 0x2a, 0xa5, 0xfa, 0x07, 0x1e, 0xfb, 0x50, 0xc5, 0xc7, 0xbd, 0x81, 0x6c, 0x0e, 0xa3, 0xe2, 0x29, 0xf0, 0x01, 0xa5, 0x69, 0xad, 0x87, 0x40, 0x67, 0xcb, 0x57, 0x90, 0x48, 0xbd, 0x1e, 0x0f, 0x7e, 0x16, 0x20, 0xaa, 0x92, 0x97, 0x58, 0x2d, 0x90, 0x86, 0x29, 0x4c, 0xa9, 0x12, 0x3a, 0xae, 0xde, 0xbc, 0x73, 0x90, 0xa9, 0x03, 0x47, 0xea, 0x14, 0xcb, 0xba, 0x1f, 0xa1, 0x46, 0xbb, 0xa1, 0xed, 0x49, 0xe0, 0x20, 0x14, 0x5c, 0x90, 0x25, 0x17, 0xf3, 0x19, 0x2b, 0xd7, 0x94, 0x5c, 0x9e, 0x77, 0xe1, 0xf0, 0x53, 0x2b, 0xc6, 0xfa, 0x58, 0xb7, 0x47, 0x15, 0x3b, 0x3d, 0xa3, 0x01, 0x20, 0x1d, 0xcf, 0x00, 0x41, 0xe6, 0x8a, 0x0f, 0xb9, 0xc1, 0x74, 0x64, 0xb3, 0xc1, 0xf9, 0xe3, 0x56, 0x03, 0xfb, 0x6e, 0x15, 0x90, 0x86, 0x46, 0x2c, 0x5f, 0xf7, 0xda, 0x7d, 0xf6, 0x6a, 0xc4, 0x12, 0x84, 0x57, 0xcf, 0xf7, 0x75, 0xa0, 0x36, 0x30, 0xc8, 0x90, 0x95, 0xfd, 0xca, 0x8a, 0x1d, 0xbc, 0xe1, 0x4e, 0x16, 0x01, 0x90, 0x33, 0xc6, 0xcf, 0xf2, 0xb4, 0x2e, 0x00, 0x35, 0x18, 0x4d, 0x3f, 0xed, 0x35, 0xce, 0x2d, 0x41, 0x82, 0x5c, 0x92, 0x3e, 0xf6, 0xec, 0xa9, 0xe8, 0x74, 0xcb, 0xfa, 0xc5, 0xbe, 0x44, 0x46, 0xf5, 0x25, 0x0b, 0x6b, 0x2a, 0x7e, 0xec, 0x64, 0xfc, 0x0b, 0x57, 0xb3, 0x77, 0x62, 0xb6, 0xd6, 0xdf, 0x7a, 0xc8, 0xac, 0x04, 0x75, 0x5c, 0xd7, 0xa5, 0xc9, 0x25, 0x80, 0xb8, 0xd5, 0x88, 0x17, 0x9f, 0x15, 0x96, 0xf1, 0x94, 0x77, 0xa1, 0xce, 0xde, 0x87, 0x13, 0xbe, 0x93, 0x77, 0x70, 0xe5, 0xf8, 0x32, 0xb7, 0x11, 0xd2, 0x02, 0x8e, 0x2f, 0x1e, 0x7e, 0xd4, 0xfe, 0x16, 0xc1, 0xa7, 0xab, 0x7f, 0x22, 0xdf, 0x13, 0x92, 0xe6, 0x8e, 0xe5, 0x03, 0xb3, 0x25, 0xf6, 0x07, 0xb6, 0x1b, 0x55, 0x4a, 0xbc, 0x5c, 0x16, 0x34, 0xb6, 0xdc, 0xd9, 0x06, 0x6c, 0xf7, 0x6d, 0x90, 0x54, 0x9a, 0x3d, 0x9e, 0xce, 0x50, 0x49, 0x58, 0x36, 0xa9, 0xd6, 0x27, 0x9a, 0xa5, 0x32, 0xef, 0x03, 0x09, 0x21, 0xa4, 0x8c, 0xdb, 0xd5, 0x89, 0xcd, 0x56, 0x83, 0xa1, 0x50, 0x1f, 0x06, 0x29, 0x04, 0x8e, 0x0c, 0x15, 0xff, 0x4a, 0xcf, 0xf0, 0xf5, 0x9c, 0x50, 0x99, 0x2b, 0x1e, 0x8f, 0xb8, 0xe4, 0xc2, 0x4c, 0xec, 0x79, 0x4d, 0x2a, 0x04, 0xa9, 0xc1, 0x61, 0xcb, 0xcb, 0x52, 0x48, 0x3b, 0xe9, 0xe2, 0x5b, 0x14, 0x00, 0x1f, 0xa4, 0x38, 0xd7, 0xc4, 0x87, 0x3e, 0x3a, 0x92, 0x6e, 0x25, 0x85, 0xff, 0xd9, 0x1b, 0xc0, 0xbf, 0xb7, 0xac, 0xce, 0x93, 0x27, 0xf1, 0x63, 0x0b, 0xf4, 0x71, 0xc3, 0x2c, 0x71, 0xde, 0x7e, 0x17, 0x8b, 0xe0, 0xb1, 0x33, 0x33, 0x8d, 0x7e, 0x74, 0x72, 0x4d, 0xb7, 0xaf, 0xd2, 0x3d, 0x02, 0x32, 0xe2, 0x5e, 0xe5, 0xf8, 0x10, 0x14, 0x81, 0x38, 0x13, 0xcc, 0xf2, 0x34, 0x98, 0xc8, 0x8c, 0x6b, 0x0c, 0x6e, 0x7f, 0xae, 0x90, 0x92, 0x92, 0xac, 0x86, 0x98, 0xed, 0xf6, 0x43, 0x73, 0x41, 0x9c, 0xd5, 0xc7, 0xba, 0xcf, 0xda, 0xcb, 0x72, 0x3a, 0xee, 0xa0, 0x7c, 0xf6, 0x21, 0x3f, 0x9f, 0x4c, 0x73, 0x3c, 0x60, 0xaf, 0x5f, 0xed, 0xf4, 0x96, 0xd4, 0xc7, 0x3e, 0x29, 0x3f, 0xf2, 0xe8, 0xe5, 0x7c, 0x6d, 0x3e, 0xad, 0xc7, 0x40, 0x68, 0xe9, 0x13, 0x96, 0x0b, 0xa4, 0x5d, 0xc1, 0x94, 0xdd, 0xcf, 0x77, 0x95, 0x64, 0xe2, 0xd2, 0x0a, 0x03, 0xcd, 0xeb, 0x66, 0xd3, 0xd9, 0x03, 0xc1, 0xeb, 0x89, 0xa5, 0x86, 0xd9, 0x9b, 0x2f, 0x77, 0x8d, 0x56, 0xaa, 0xed, 0xa2, 0xa4, 0xe1, 0xa6, 0x34, 0xf3, 0x1f, 0x86, 0xae, 0xe3, 0x09, 0x19, 0x36, 0x26, 0x6b, 0xc6, 0xb3, 0x2b, 0x03, 0xfe, 0x7e, 0x71, 0x9b, 0xcb, 0x4e, 0xa3, 0xec, 0x98, 0xe5, 0xcc, 0x2c, 0x13, 0x88, 0x50, 0x45, 0x7a, 0x5d, 0x76, 0x0b, 0xbe, 0x22, 0xad, 0xed, 0x4d, 0xe8, 0xd8, 0x00, 0x61, 0xbb, 0x52, 0x63, 0x32, 0x65, 0x6f, 0x4f, 0x6a, 0xbd, 0xab, 0x5c, 0xaa, 0x24, 0xd0, 0xc0, 0x14, 0x24, 0xc1, 0xa9, 0x14, 0x57, 0xb6, 0xe4, 0xeb, 0x0b, 0x10, 0x6f, 0xb4, 0x45, 0x1f, 0xba, 0xde, 0x11, 0x6e, 0xa7, 0x55, 0x57, 0x80, 0x95, 0xe2, 0x88, 0x06, 0xf8, 0x6d, 0xef, 0x01, 0x5f, 0x29, 0x75, 0x5a, 0xdc, 0xdf, 0x15, 0xcf, 0x27, 0xe0, 0x66, 0x60, 0x12, 0x44, 0xec, 0x7a, 0xfa, 0x89, 0x12, 0xa5, 0x68, 0xc4, 0x02, 0x64, 0x12, 0xd9, 0xc7, 0x89, 0xcf, 0x86, 0x60, 0x41, 0x56, 0x2f, 0xb0, 0x81, 0xbf, 0xaa, 0x7d, 0xa8, 0x26, 0x94, 0xe5, 0x74, 0xee, 0xd2, 0xf3, 0xb0, 0x77, 0x52, 0x4a, 0x1f, 0x54, 0x3f, 0x07, 0xa2, 0x43, 0x18, 0x46, 0x8e, 0x83, 0x7f, 0x06, 0xb6, 0x45, 0xa8, 0x3e, 0xa2, 0x4b, 0xa6, 0x6c, 0xe6, 0x05, 0x21, 0xec, 0xaa, 0xaf, 0x19, 0xe0, 0xc7, 0x3c, 0xcd, 0x7d, 0x0b, 0x2a, 0xea, 0xfb, 0x83, 0x77, 0x8b, 0x85, 0xf4, 0x49, 0xc5, 0xe2, 0xcb, 0x1f, 0xba, 0xbc, 0x2b, 0x89, 0x6a, 0xb0, 0xf5, 0xbf, 0xc6, 0x97, 0xe1, 0x94, 0xba, 0xb1, 0x0e, 0xa6, 0x32, 0xa2, 0x97, 0xc1, 0x4e, 0x67, 0x14, 0xd1, 0x43, 0xb6, 0x1c, 0xeb, 0xa9, 0x6b, 0x2f, 0x2d, 0x30, 0xfa, 0x90, 0x9a, 0xf1, 0x0c, 0x04, 0x20, 0xe6, 0xb1, 0x7c, 0x72, 0x36, 0xd4, 0x13, 0x68, 0x5b, 0xe2, 0xfa, 0x00, 0x56, 0xc9, 0x27, 0x49, 0x74, 0xc2, 0xed, 0xc7, 0x60, 0x8a, 0xd4, 0x0d, 0x75, 0xfc, 0x48, 0xae, 0x97, 0x38, 0x08, 0xca, 0x07, 0xbe, 0x64, 0xb1, 0xf6, 0x2f, 0xac, 0x88, 0xca, 0x9d, 0x6d, 0xe4, 0x53, 0x91, 0x51, 0xab, 0x78, 0x45, 0xd0, 0xcf, 0x04, 0x9e, 0xef, 0xad, 0x9d, 0xfc, 0xcc, 0x0f, 0x20, 0xde, 0xb8, 0x32, 0xd5, 0xf2, 0x05, 0x4c, 0x45, 0x1a, 0xf2, 0x61, 0xa0, 0x64, 0x2b, 0x7f, 0xf9, 0x25, 0xec, 0x94, 0xcb, 0x8d, 0xb3, 0x89, 0x92, 0x7a, 0xfd, 0x52, 0x5d, 0x65, 0x9e, 0x15, 0xb1, 0x9e, 0x8e, 0xa2, 0x1d, 0x37, 0x4f, 0xd3, 0x52, 0x5f, 0x3f, 0xa1, 0xa8, 0x00, 0x78, 0xf5, 0x60, 0xdd, 0x16, 0x79, 0x0e, 0x9e, 0x09 }; +constexpr AccessUnit LPCM_SKIP_BYTES_EXPECTED_AU = { 0x15f90, 0x153d5, false, 3, { 0xe6, 0xa3, 0x12, 0x99, 0xcf, 0x16, 0x2e, 0x6d, 0x67, 0x2a, 0xa2, 0xb4, 0xf7, 0xa2, 0x3c, 0xdf }, { 0x97, 0xf7, 0x18, 0x4f, 0xfc, 0x6b, 0x28, 0x74, 0x44, 0x69, 0x80, 0xc1, 0x76, 0x96, 0x50, 0xe6, 0x23, 0x0d, 0xdf, 0xf2 } }; + + +const std::vector AVC_BEGIN_OF_AU_SPLIT_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x5c, 0xe5, 0xaf, 0x00, 0xfd, 0x0f, 0x1d, 0x27, 0x61, 0x90, 0xd4, 0x77, 0x92, 0x34, 0x4a, 0x7a, 0xdc, 0x96, 0x0d, 0x27, 0x58, 0x2f, 0xfa, 0x88, 0x0d, 0xb6, 0x88, 0xcb, 0x5f, 0xd7, 0x79, 0xc8, 0xe5, 0x46, 0xe3, 0x99, 0xc7, 0x10, 0x1d, 0xfa, 0x50, 0xd7, 0x07, 0xcf, 0xcf, 0x21, 0x1c, 0xaf, 0x1c, 0x74, 0x86, 0x02, 0x33, 0x71, 0xc2, 0x81, 0x7f, 0xd6, 0xea, 0xe0, 0xab, 0xa5, 0xf2, 0x66, 0xb1, 0x69, 0x10, 0x40, 0x36, 0x55, 0x95, 0x09, 0xba, 0x8d, 0x65, 0xc8, 0x33, 0x21, 0xa0, 0xcf, 0x1c, 0xfc, 0xa4, 0x2b, 0x3e, 0x8f, 0x92, 0x74, 0xbe, 0x8a, 0x75, 0xed, 0x8a, 0x33, 0x85, 0x42, 0xcf, 0xf2, 0x0e, 0xaa, 0xe2, 0x8f, 0x51, 0xee, 0xd9, 0x67, 0x8c, 0x24, 0x75, 0x18, 0xf5, 0x90, 0x3e, 0x74, 0xf8, 0x43, 0x4e, 0xc5, 0x5d, 0xe1, 0xd5, 0x6f, 0x97, 0x6d, 0xef, 0xd5, 0x5f, 0x12, 0xb0, 0x83, 0xcb, 0x07, 0x02, 0x8c, 0x1d, 0x91, 0x2e, 0x04, 0x2c, 0x34, 0xff, 0xa0, 0xdc, 0x27, 0xe9, 0x53, 0x9e, 0x6f, 0xe2, 0x75, 0xd6, 0x2a, 0x73, 0x17, 0x71, 0xe7, 0x5e, 0x93, 0x98, 0x9f, 0xca, 0x01, 0xd9, 0x03, 0x60, 0x6d, 0x49, 0xed, 0x54, 0x79, 0x90, 0x09, 0x1a, 0x52, 0xfa, 0x36, 0x06, 0x5f, 0x7a, 0x0a, 0xd7, 0x9e, 0xbc, 0x97, 0xbb, 0xa1, 0xc5, 0xfd, 0xaa, 0xb8, 0x62, 0x82, 0x72, 0x73, 0xfb, 0x29, 0x86, 0x35, 0x25, 0x8c, 0x2b, 0x9b, 0xdd, 0xf2, 0x73, 0xd6, 0xc0, 0x34, 0x32, 0x14, 0x43, 0x3d, 0x48, 0xe0, 0x58, 0x2e, 0x7d, 0x81, 0x6f, 0xb0, 0x05, 0xde, 0x7c, 0xff, 0xb6, 0xa2, 0xc6, 0x3d, 0x57, 0xea, 0x84, 0xa8, 0x28, 0x32, 0x51, 0x67, 0xf5, 0xa8, 0x0d, 0x58, 0x34, 0x1e, 0x59, 0x05, 0x4a, 0xff, 0x3b, 0x44, 0x0e, 0xe1, 0x21, 0x49, 0xe5, 0x4f, 0x6c, 0x6e, 0xe0, 0x52, 0xc1, 0x86, 0xec, 0x17, 0x61, 0xdd, 0xc5, 0x6a, 0x15, 0x6e, 0x76, 0x9d, 0xe0, 0x51, 0x55, 0x0c, 0x63, 0x12, 0xe4, 0x9c, 0x3c, 0xa5, 0x7c, 0xb5, 0xc1, 0xd7, 0xbf, 0xc2, 0x3c, 0x2f, 0xb9, 0xca, 0x69, 0x15, 0xfd, 0x02, 0x06, 0xb9, 0xc9, 0xa2, 0x1f, 0x25, 0xc1, 0x18, 0x54, 0x76, 0x8c, 0x7b, 0x8c, 0x12, 0x55, 0xb6, 0xa2, 0xbb, 0x08, 0xa2, 0x9b, 0xab, 0xa1, 0xfd, 0xb2, 0x1d, 0x42, 0x38, 0x71, 0x5a, 0xba, 0xc4, 0x09, 0x3b, 0x06, 0x51, 0x38, 0x16, 0xb8, 0x94, 0xab, 0x41, 0x24, 0x9e, 0x93, 0x3f, 0x82, 0x85, 0x0c, 0x97, 0xf0, 0x78, 0x21, 0xeb, 0x4f, 0xe9, 0xf3, 0xf1, 0x3a, 0x53, 0x90, 0x91, 0xe6, 0xdb, 0x5f, 0x56, 0x22, 0x1d, 0x33, 0x61, 0x83, 0xf5, 0xf2, 0xac, 0x0b, 0x9b, 0xb4, 0xda, 0x58, 0xc4, 0xac, 0xf2, 0xa6, 0xd6, 0xfb, 0x37, 0x7a, 0x97, 0x70, 0x22, 0x4c, 0x6c, 0x1d, 0x4d, 0x52, 0x29, 0x07, 0xdf, 0xc9, 0x79, 0x30, 0xd4, 0x63, 0x92, 0x15, 0xc8, 0x3c, 0xd9, 0x51, 0x56, 0xb9, 0xc9, 0x41, 0x8c, 0x45, 0xe1, 0x18, 0x99, 0xa7, 0xbf, 0xb8, 0x3b, 0x45, 0x84, 0x48, 0xdd, 0x39, 0x36, 0xb3, 0x21, 0x48, 0xf1, 0x69, 0x51, 0x62, 0x45, 0x6b, 0xf4, 0xf6, 0xed, 0x41, 0xda, 0x8f, 0x64, 0xb8, 0x39, 0xa5, 0x36, 0x58, 0x8a, 0xc1, 0xd6, 0x6c, 0x3a, 0xc6, 0xf5, 0x41, 0x9f, 0xe5, 0x7b, 0x68, 0x9e, 0xbf, 0x39, 0x55, 0x9d, 0xda, 0x14, 0xa3, 0x2e, 0x4a, 0xa8, 0x25, 0x31, 0x4c, 0x9f, 0xb6, 0xf2, 0x1d, 0x67, 0x20, 0x2a, 0x49, 0x0e, 0x65, 0x04, 0xa5, 0x3d, 0x6e, 0xb7, 0x95, 0x4d, 0x90, 0x63, 0xec, 0xa0, 0xc7, 0xd4, 0x16, 0x5e, 0xe7, 0x33, 0x77, 0xbf, 0x03, 0x09, 0xfc, 0x5e, 0xe9, 0x21, 0x3f, 0x58, 0x76, 0xdc, 0x4a, 0x19, 0x89, 0x8e, 0x05, 0x90, 0x45, 0xb1, 0x55, 0xfc, 0x79, 0x44, 0x88, 0xcc, 0xc3, 0x24, 0x1d, 0xf4, 0xc5, 0x07, 0x65, 0xcf, 0x39, 0x2e, 0xeb, 0x43, 0x70, 0x41, 0xb3, 0x56, 0x8c, 0x51, 0x64, 0xc1, 0xcd, 0x42, 0x1b, 0xfa, 0x05, 0x80, 0x6c, 0xa6, 0x15, 0xe5, 0xa2, 0x2d, 0xd3, 0x34, 0x5f, 0x9e, 0xab, 0x7b, 0xd4, 0x0f, 0x14, 0x67, 0x3e, 0x28, 0x4f, 0xab, 0x30, 0xa7, 0x02, 0xb5, 0x59, 0xcd, 0x35, 0xfa, 0x43, 0xe4, 0x77, 0xe6, 0xa5, 0x04, 0xba, 0x91, 0xf8, 0x0a, 0xc5, 0xb0, 0xef, 0x48, 0xf3, 0x8a, 0x97, 0x9f, 0x9a, 0x01, 0xd3, 0x28, 0x99, 0x70, 0xf6, 0xa2, 0x50, 0xe5, 0x75, 0xcc, 0xd4, 0x22, 0x50, 0x95, 0x6b, 0xc2, 0x95, 0xcd, 0xeb, 0x8e, 0x01, 0xa9, 0x70, 0x25, 0x9f, 0x62, 0xf4, 0x3a, 0xfe, 0xec, 0x41, 0xb3, 0x49, 0xce, 0x68, 0xfe, 0xc3, 0xe5, 0x1f, 0x7d, 0xfd, 0x7d, 0x78, 0x4c, 0xa4, 0x55, 0x8b, 0x19, 0xf0, 0x4c, 0xce, 0xb8, 0xa4, 0xb1, 0xe1, 0x86, 0x03, 0xb0, 0x46, 0xc5, 0xa4, 0xdc, 0xcb, 0x20, 0xc5, 0x55, 0xbb, 0xf4, 0xc6, 0xba, 0x70, 0x27, 0xa2, 0xd5, 0xd8, 0x05, 0x25, 0x60, 0xb6, 0x6e, 0xe0, 0xda, 0x03, 0x6c, 0x98, 0x0c, 0xa4, 0xf1, 0xe7, 0x33, 0x6d, 0xf2, 0x1d, 0xf6, 0x99, 0x4b, 0xd6, 0x84, 0x4c, 0x2e, 0xf1, 0x54, 0x42, 0x78, 0xa2, 0x2f, 0x08, 0x9e, 0x95, 0xf7, 0x1f, 0xfa, 0xe1, 0x50, 0x1a, 0x1c, 0x69, 0x98, 0x1d, 0x2b, 0x1d, 0xe0, 0xec, 0xe6, 0x28, 0xda, 0xc8, 0x76, 0xf3, 0xaa, 0x91, 0xf8, 0x63, 0x04, 0x6b, 0xa6, 0x3b, 0x8e, 0xbb, 0x45, 0x8c, 0x7b, 0x5a, 0x75, 0xe7, 0x26, 0xe0, 0x07, 0xd7, 0xb7, 0x6d, 0x6f, 0x4c, 0xc5, 0xda, 0xde, 0xc7, 0xcf, 0xf2, 0x07, 0xd8, 0xad, 0x23, 0x0e, 0xa2, 0xc2, 0xcf, 0xe7, 0xbd, 0x9e, 0x41, 0x09, 0xb2, 0x9b, 0x6b, 0xcc, 0x4d, 0xe9, 0xe1, 0x88, 0xbb, 0x12, 0x76, 0x20, 0xe9, 0x60, 0x73, 0x08, 0x87, 0x7e, 0xb8, 0x71, 0x7b, 0x7c, 0x75, 0xb1, 0x5a, 0xfb, 0x9e, 0xd1, 0xb4, 0x2b, 0x94, 0xa8, 0x5f, 0x88, 0xf2, 0xec, 0x9a, 0x21, 0x42, 0x9f, 0x70, 0xd9, 0xf5, 0x6a, 0xad, 0xbd, 0x88, 0x75, 0x86, 0xc2, 0x1e, 0xcb, 0x45, 0xe7, 0x27, 0xa2, 0xe8, 0x48, 0xd7, 0xaf, 0x22, 0x30, 0xe6, 0x47, 0x36, 0x9e, 0x5d, 0x0d, 0xa3, 0x5c, 0x84, 0xcd, 0x14, 0x63, 0xae, 0x12, 0x28, 0x6a, 0xcf, 0x75, 0xfb, 0x62, 0x84, 0x25, 0x8c, 0xda, 0x10, 0xaa, 0xf8, 0xa7, 0xfa, 0xee, 0xa5, 0x9e, 0x54, 0x62, 0xe7, 0xdd, 0x11, 0xb3, 0x3e, 0x1f, 0x9e, 0x00, 0x0a, 0x89, 0x55, 0x7c, 0x28, 0x56, 0xb9, 0xb2, 0x0a, 0xc9, 0x83, 0xc4, 0x51, 0x6b, 0x6d, 0x9f, 0xec, 0xd0, 0x49, 0x41, 0xd2, 0xcb, 0xf7, 0xc9, 0x38, 0x85, 0x2f, 0x0b, 0xd4, 0x84, 0x6c, 0xd9, 0xd3, 0x35, 0x44, 0x4d, 0x9a, 0x80, 0xee, 0xca, 0xf5, 0x77, 0x2e, 0xf5, 0xd8, 0xc0, 0xd9, 0x05, 0xfc, 0xfe, 0xaf, 0x0b, 0x62, 0x90, 0x21, 0x73, 0x2f, 0x3f, 0x39, 0x44, 0x74, 0x7f, 0x18, 0x20, 0xa1, 0x34, 0x49, 0xeb, 0x1f, 0x69, 0x50, 0xa5, 0x1c, 0x51, 0x3b, 0x4c, 0xeb, 0x1e, 0xc9, 0x5f, 0xe9, 0xe4, 0xed, 0x6e, 0x37, 0x3d, 0x44, 0xd9, 0x86, 0x8e, 0x5c, 0x05, 0x48, 0x4a, 0x64, 0x9d, 0x9e, 0x6c, 0x26, 0x03, 0x1d, 0xa3, 0x2d, 0xa0, 0x7c, 0x51, 0xf6, 0xaf, 0xe1, 0xaf, 0xeb, 0x05, 0xdc, 0x6a, 0x05, 0xc7, 0x15, 0x0e, 0xc8, 0xd6, 0x99, 0x9b, 0xdf, 0x7f, 0x9b, 0x25, 0x40, 0x17, 0x96, 0x97, 0xa7, 0x15, 0x6a, 0xbd, 0x70, 0x11, 0x64, 0x26, 0x90, 0x55, 0x80, 0x7d, 0xed, 0xd5, 0xb5, 0xc3, 0x86, 0x9a, 0x5b, 0xb5, 0x31, 0x83, 0x35, 0x69, 0x81, 0x59, 0x32, 0x1a, 0x72, 0xf8, 0xae, 0xe0, 0xfa, 0x1e, 0x34, 0xa1, 0xc3, 0xfd, 0x88, 0xa1, 0xa8, 0x27, 0xa9, 0x95, 0x61, 0x09, 0xfb, 0xc5, 0xd5, 0x48, 0x97, 0x19, 0x1e, 0xfb, 0xd5, 0x03, 0xdf, 0xa4, 0x59, 0x82, 0x12, 0x5e, 0x63, 0x93, 0xe3, 0x8b, 0xd2, 0x3c, 0x13, 0x4d, 0x14, 0x90, 0xe0, 0x28, 0x95, 0xb3, 0x79, 0xcb, 0x62, 0x7d, 0x80, 0xfd, 0xa8, 0xbd, 0xdc, 0x69, 0x07, 0x0e, 0x59, 0xc3, 0x57, 0xca, 0xbe, 0xd9, 0xfb, 0xd0, 0x34, 0x26, 0x98, 0x7c, 0x6a, 0xa7, 0x3f, 0x4f, 0x70, 0x8f, 0x9c, 0x63, 0x3e, 0xa0, 0x84, 0x14, 0x95, 0x59, 0xe6, 0xb3, 0x38, 0x99, 0xf7, 0xb4, 0x9d, 0x34, 0x50, 0x35, 0xe2, 0x74, 0x90, 0xa4, 0x1a, 0x93, 0xe1, 0xd1, 0x46, 0x68, 0xa7, 0x4d, 0x02, 0x48, 0x32, 0x49, 0x39, 0xd8, 0xd1, 0x57, 0x85, 0xc5, 0x36, 0xd7, 0x87, 0x08, 0x7a, 0xca, 0xb8, 0xb5, 0x4a, 0x02, 0x1a, 0x98, 0x5e, 0x5b, 0x57, 0xb5, 0x14, 0x8f, 0x10, 0x33, 0xbe, 0xbd, 0x43, 0x17, 0xf3, 0x0e, 0x15, 0x49, 0x84, 0xc9, 0xcd, 0xbb, 0xe6, 0x19, 0x0b, 0x2e, 0xf4, 0xfc, 0xdf, 0x48, 0x3e, 0xe4, 0x5f, 0x72, 0x8a, 0x8d, 0x08, 0x90, 0x93, 0x0d, 0x64, 0xd6, 0xa2, 0xf6, 0x28, 0x63, 0x73, 0x56, 0x8a, 0xfb, 0xd5, 0x6f, 0xf9, 0x80, 0x88, 0xeb, 0xe4, 0x42, 0x7c, 0x62, 0x7e, 0xd7, 0xe2, 0x8c, 0x1f, 0x68, 0x5b, 0xae, 0x6c, 0xba, 0x3a, 0x43, 0xc5, 0x52, 0x55, 0xe0, 0x3c, 0x7d, 0x52, 0x75, 0xd5, 0x6a, 0x0f, 0xd8, 0xdd, 0x53, 0x1d, 0xcb, 0x61, 0xa7, 0x80, 0x2a, 0x82, 0x70, 0x25, 0x8e, 0xb5, 0xdf, 0xea, 0xb1, 0x2a, 0xab, 0x1f, 0x6b, 0xad, 0x86, 0x78, 0xba, 0x98, 0x9b, 0x3a, 0x64, 0xf2, 0xdd, 0x5b, 0x65, 0x89, 0x20, 0xc4, 0xa6, 0x98, 0x67, 0x67, 0x3c, 0xe6, 0xaf, 0x1c, 0x3a, 0xd9, 0xf0, 0x1e, 0x14, 0x96, 0xbe, 0x08, 0x21, 0xad, 0xa1, 0x44, 0xa1, 0xed, 0xdf, 0x52, 0xad, 0x3d, 0x84, 0x4d, 0x2c, 0x7d, 0xd9, 0xe5, 0xed, 0x80, 0xda, 0x3f, 0x22, 0xd7, 0xe6, 0xb3, 0xa5, 0xc6, 0xe9, 0x50, 0x3b, 0xbc, 0xf8, 0xc5, 0xc6, 0x7e, 0x7f, 0xce, 0x40, 0x69, 0x91, 0xe9, 0xc1, 0x8b, 0xfb, 0xa7, 0xca, 0xae, 0xab, 0xee, 0x14, 0x2b, 0x3c, 0x35, 0x01, 0xfa, 0x91, 0x2f, 0xea, 0xcf, 0x8c, 0xc7, 0x01, 0xed, 0x9b, 0x86, 0xff, 0xea, 0x7d, 0xb6, 0xf2, 0x43, 0x46, 0xf7, 0x3f, 0xf1, 0x96, 0x02, 0x00, 0xde, 0xfc, 0x3b, 0xc5, 0x0a, 0x6e, 0xe8, 0xd6, 0x47, 0x4e, 0xb9, 0x65, 0xcb, 0x59, 0xe9, 0x05, 0x08, 0xb6, 0x67, 0x13, 0x02, 0x8a, 0x61, 0xbe, 0x20, 0x15, 0x87, 0x7a, 0x8e, 0x56, 0xc4, 0x04, 0x18, 0xd4, 0xa0, 0x48, 0x24, 0x73, 0x90, 0xe0, 0x0f, 0xbb, 0xca, 0x98, 0xeb, 0x77, 0xc9, 0x70, 0x7a, 0x8e, 0x1a, 0xd1, 0x57, 0xc8, 0x09, 0xe7, 0x6f, 0x54, 0xd9, 0xe5, 0xf9, 0xc3, 0x79, 0xb8, 0xb7, 0xa3, 0x11, 0x3b, 0x08, 0xc7, 0x7e, 0xe2, 0xcb, 0xc8, 0x5e, 0x87, 0x68, 0x3a, 0xee, 0x78, 0x45, 0x67, 0x8b, 0xed, 0x22, 0xde, 0x7f, 0x4b, 0xad, 0xc3, 0xc8, 0x88, 0xa8, 0x54, 0x86, 0xbe, 0x82, 0x08, 0xaa, 0xee, 0x81, 0xd0, 0xa6, 0x29, 0xf0, 0x66, 0x91, 0xa5, 0xa7, 0xc4, 0x2f, 0x26, 0x72, 0x10, 0xea, 0x92, 0x9c, 0xde, 0x23, 0x90, 0x9e, 0x4e, 0x01, 0x7f, 0x93, 0x13, 0x7f, 0x95, 0x89, 0x22, 0x8e, 0x53, 0xcd, 0xa7, 0x68, 0x7f, 0xa4, 0x78, 0xef, 0xaf, 0x30, 0x3e, 0xfe, 0xbe, 0x62, 0x22, 0x10, 0x8e, 0x52, 0xbd, 0xbe, 0x30, 0x5c, 0x5d, 0x84, 0xbe, 0x1d, 0xeb, 0x27, 0x4a, 0x8c, 0x17, 0xd8, 0x91, 0x97, 0x85, 0x7b, 0xb8, 0x65, 0x11, 0x84, 0xe2, 0x6f, 0x4f, 0x61, 0x3e, 0x05, 0x5d, 0x53, 0x60, 0x8b, 0xe8, 0x35, 0x4d, 0x58, 0x12, 0x60, 0x3a, 0xaf, 0x7a, 0x2a, 0xe4, 0xb3, 0x82, 0x33, 0x02, 0x15, 0x1b, 0xc9, 0x37, 0xd9, 0xa5, 0x81, 0xe3, 0x91, 0xf3, 0x08, 0xb4, 0x8d, 0xad, 0x90, 0x96, 0xb5, 0xb0, 0xe9, 0x23, 0xba, 0xb7, 0x25, 0x38, 0xb9, 0x07, 0x0f, 0xc1, 0x4c, 0xdc, 0x6d, 0x3c, 0x85, 0x6b, 0xea, 0x96, 0x04, 0xf0, 0xca, 0xfb, 0xb7, 0xdc, 0x45, 0xcb, 0x11, 0x5b, 0x7e, 0x14, 0xcc, 0x2d, 0xc4, 0x23, 0xcb, 0x68, 0xeb, 0x56, 0xe9, 0xaa, 0x7a, 0x44, 0x5b, 0x92, 0xc1, 0x3b, 0xff, 0xfd, 0x16, 0x5a, 0xf3, 0x4c, 0xbc, 0x38, 0x1e, 0x36, 0x68, 0xd4, 0xa2, 0x4d, 0xcb, 0xc7, 0xee, 0x3a, 0x1d, 0xa5, 0x69, 0x67, 0xd8, 0xd7, 0x34, 0x7a, 0xe9, 0x2b, 0x6d, 0xb0, 0xe4, 0x47, 0x81, 0x1f, 0xef, 0xf6, 0xd4, 0x60, 0x07, 0x92, 0x1d, 0xd1, 0x61, 0xbb, 0x8f, 0x3f, 0x4a, 0x0b, 0xbd, 0xae, 0x6f, 0x77, 0xf2, 0x46, 0x64, 0x77, 0x34, 0xf1, 0x93, 0xa5, 0x3a, 0x2b, 0x98, 0x79, 0x93, 0x54, 0xc2, 0x7b, 0x63, 0xad, 0xdb, 0xb8, 0x9e, 0xa2, 0x9c, 0xba, 0xe6, 0x7c, 0xf1, 0x0f, 0x0e, 0x68, 0x04, 0xba, 0x63, 0xf2, 0xe5, 0xbd, 0x85, 0xf3, 0x6c, 0x96, 0x40, 0xb0, 0xf2, 0x71, 0x2f, 0x50, 0xe8, 0x20, 0x38, 0xac, 0xe6, 0xc9, 0x26, 0x7a, 0xca, 0xeb, 0xec, 0xd0, 0x40, 0x65, 0x34, 0x4b, 0x70, 0x07, 0x23, 0xee, 0x65, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x3e, 0x95, 0xcd, 0x33, 0x75, 0xc9, 0xd6, 0x9d, 0x2c, 0xad, 0x4c, 0x70, 0xfc, 0x00, 0x01, 0x09, 0x40, 0x7f, 0x53, 0xed, 0xb3, 0xc2, 0xfd, 0xff, 0x4c, 0x6a, 0x04, 0x34, 0xc8, 0x0f, 0xc1, 0xcf, 0x3b, 0xdf, 0xb1, 0x07, 0xa8, 0x0e, 0x59, 0xb2, 0xc9, 0x3c, 0x27, 0x51, 0x67, 0xb2, 0x07, 0x0b, 0x30, 0xd3, 0xa0, 0x73, 0x14, 0xda, 0x1b, 0xd1, 0xbc, 0xf8, 0x42, 0xca, 0xe5, 0xab, 0x6b, 0x49, 0x7a, 0xe7, 0xe5, 0x3a, 0xf1, 0x9d, 0x4a, 0x8b, 0x34, 0x18, 0x12, 0xcb, 0x1c, 0x10, 0x5e, 0x2c, 0x53, 0x16, 0xfc, 0x65, 0x00, 0x49, 0x27, 0x30, 0xb2, 0x5e, 0xc8, 0x35, 0x44, 0xab, 0x9e, 0x18, 0xc8, 0x0c, 0xda, 0x5b, 0xc2, 0xb8, 0xf3, 0x2b, 0xcc, 0xc8, 0x5c, 0x0c, 0x14, 0x79, 0x58, 0x4d, 0x69, 0xd2, 0xff, 0x06, 0x58, 0x20, 0xca, 0xe4, 0xac, 0x1a, 0x3f, 0x5a, 0x53, 0x82, 0x3b, 0x5b, 0xd3, 0x8f, 0x6c, 0x63, 0xc0, 0xf0, 0x84, 0x9e, 0x4b, 0x61, 0x67, 0xd6, 0xc3, 0xd3, 0x61, 0xb2, 0xec, 0xd5, 0xb0, 0xdf, 0x41, 0x13, 0xcb, 0x66, 0x4c, 0x13, 0x69, 0x69, 0x8d, 0x27, 0xa7, 0x06, 0x1b, 0xca, 0x00, 0xf6, 0x82, 0xfb, 0xeb, 0x1b, 0xc1, 0x36, 0x95, 0x9d, 0x44, 0x98, 0x33, 0x8f, 0x26, 0x7a, 0x41, 0x9c, 0x00, 0x2c, 0xeb, 0xcc, 0xc0, 0x05, 0x5a, 0xf5, 0x58, 0xec, 0x23, 0x4f, 0x44, 0x79, 0x97, 0x8d, 0x0c, 0xe0, 0xa5, 0xc9, 0x14, 0xcf, 0x2b, 0xe3, 0xa1, 0x3f, 0x12, 0x8d, 0xde, 0xbf, 0x46, 0x4e, 0xca, 0x9c, 0xe4, 0xa5, 0x1c, 0x61, 0x35, 0x57, 0xae, 0xa8, 0xfa, 0x3a, 0x55, 0x11, 0x05, 0x7b, 0x55, 0xb7, 0x90, 0x5c, 0x03, 0xb3, 0x00, 0x31, 0xfa, 0xa0, 0x16, 0xa7, 0x03, 0x99, 0xc1, 0x5d, 0xfe, 0x7e, 0xc7, 0x61, 0x7e, 0xb1, 0x4b, 0x4d, 0x05, 0x65, 0xdf, 0x14, 0x47, 0x81, 0xd1, 0xe7, 0xd9, 0x5e, 0x8a, 0xbd, 0xa5, 0xfb, 0x48, 0x2f, 0x7f, 0x33, 0x90, 0x26, 0x1e, 0x28, 0xbb, 0x45, 0xd0, 0x8d, 0x98, 0x83, 0x5f, 0x4c, 0xe4, 0xa6, 0x20, 0x2b, 0xc2, 0x7b, 0x11, 0xf9, 0x6a, 0x9c, 0x46, 0x21, 0x5b, 0x32, 0x8c, 0x51, 0x11, 0x06, 0x00, 0xb6, 0xf6, 0xc2, 0xcf, 0x26, 0x50, 0x2c, 0xd6, 0x39, 0xa8, 0xe4, 0xf6, 0xc9, 0x97, 0x3d, 0xe2, 0x54, 0x88, 0x4b, 0x0c, 0x93, 0xbe, 0xd5, 0x17, 0xa6, 0xfc, 0x65, 0x1f, 0xa6, 0x0f, 0xe9, 0xa5, 0xcb, 0xdf, 0x61, 0xc4, 0xea, 0x87, 0x83, 0xc8, 0x50, 0xe7, 0x51, 0x96, 0x96, 0x7e, 0x2b, 0x5b, 0x58, 0x3e, 0x87, 0x3b, 0x8f, 0x92, 0x23, 0x7f, 0xb3, 0x10, 0xe8, 0xd9, 0xdd, 0x74, 0x21, 0x37, 0x7d, 0x0f, 0x91, 0x7c, 0xef, 0xaa, 0x9c, 0x1f, 0x18, 0xee, 0xe8, 0x20, 0xfe, 0xb4, 0x7e, 0x7a, 0xe6, 0x1a, 0xa3, 0x04, 0x6a, 0xfa, 0x82, 0x4c, 0xe1, 0xdd, 0xf5, 0xb0, 0x45, 0x52, 0x32, 0x71, 0x87, 0x7a, 0xd2, 0xc6, 0x38, 0xcb, 0xff, 0xce, 0x22, 0x01, 0xb5, 0x28, 0x69, 0x8d, 0xef, 0x37, 0xd2, 0xb3, 0xc4, 0x53, 0x9f, 0x49, 0x10, 0xda, 0xa4, 0x10, 0x13, 0x8d, 0x76, 0x2f, 0x0d, 0xc0, 0x24, 0x96, 0x96, 0x6d, 0xe5, 0xe9, 0x97, 0xf2, 0xce, 0x10, 0x9e, 0x53, 0xab, 0x3a, 0x46, 0xdf, 0xed, 0xab, 0x6b, 0xf3, 0xd6, 0x92, 0xba, 0x92, 0xb0, 0xb3, 0xbb, 0x2b, 0xd9, 0xd5, 0xc3, 0xbd, 0xb1, 0xeb, 0x8b, 0x86, 0x3d, 0xf9, 0xfb, 0x08, 0x64, 0x62, 0x24, 0xa5, 0x0e, 0x46, 0x27, 0x32, 0x67, 0x8d, 0x19, 0x5e, 0xe7, 0x95, 0x1d, 0x42, 0x92, 0xde, 0xaf, 0x15, 0xd3, 0x98, 0x7f, 0x08, 0xd2, 0xd5, 0xcb, 0xd6, 0x20, 0xb4, 0x3d, 0x96, 0xff, 0x3a, 0x0c, 0x1a, 0x77, 0xe0, 0x2f, 0xe1, 0x09, 0x99, 0x82, 0x00, 0x11, 0x7c, 0xe3, 0xb7, 0xc4, 0x1f, 0x97, 0xa1, 0xd9, 0xff, 0x6b, 0x1c, 0x6a, 0xd9, 0xf4, 0x28, 0x30, 0xa7, 0x06, 0xef, 0x22, 0xab, 0x6e, 0x3a, 0xc0, 0x40, 0xc0, 0x1e, 0x47, 0xb8, 0x01, 0x25, 0xfd, 0x44, 0xf6, 0x3b, 0xf1, 0xe3, 0xd5, 0x5f, 0x35, 0x4b, 0x0f, 0xe8, 0x32, 0xca, 0x7b, 0xe7, 0x73, 0xb0, 0x2a, 0xe8, 0xe5, 0x5e, 0xd5, 0x19, 0xfd, 0x7c, 0x7a, 0x3c, 0x33, 0xdb, 0x50, 0xa4, 0xec, 0x09, 0x94, 0xaa, 0xbc, 0x6f, 0xf1, 0x43, 0xa3, 0x36, 0x6f, 0xcd, 0x0b, 0x34, 0xfc, 0x5a, 0x2c, 0xee, 0x2a, 0x69, 0x98, 0x06, 0x41, 0xa7, 0x9d, 0xe1, 0x97, 0x80, 0x6e, 0x2f, 0x4b, 0x17, 0xd9, 0x39, 0xf9, 0xbd, 0x8b, 0x64, 0x1c, 0x0b, 0x51, 0xde, 0x2d, 0x73, 0x39, 0x9d, 0xed, 0x5d, 0x9f, 0x3e, 0xb9, 0xa8, 0x0d, 0xf1, 0xf7, 0x21, 0xd4, 0xf8, 0x05, 0x1c, 0x95, 0x9e, 0x5e, 0xb1, 0xc3, 0xc4, 0x45, 0x2c, 0xcc, 0x55, 0x15, 0xba, 0x4b, 0xae, 0x84, 0x7f, 0x2f, 0x23, 0xf1, 0x47, 0x46, 0x9b, 0x3a, 0xed, 0x06, 0xe1, 0xff, 0x3e, 0x55, 0x44, 0xfe, 0xdc, 0xb3, 0x8f, 0xde, 0xa6, 0x3e, 0x33, 0xf8, 0x8c, 0x52, 0x0c, 0x77, 0xa8, 0x12, 0xeb, 0xfa, 0x0d, 0x05, 0x6c, 0x5c, 0x86, 0x4f, 0x46, 0x2f, 0x9c, 0xa7, 0x58, 0x4f, 0x50, 0xa1, 0x21, 0xde, 0xda, 0x54, 0xd2, 0x51, 0x84, 0x5e, 0xa3, 0x7c, 0x55, 0xd0, 0xc7, 0x73, 0x80, 0xfe, 0x99, 0x26, 0xb2, 0x6f, 0xd8, 0x4f, 0x75, 0x09, 0xe0, 0xb7, 0xde, 0xec, 0x04, 0xc9, 0x8c, 0xc9, 0xaf, 0xb0, 0x9a, 0x41, 0x52, 0xff, 0xf9, 0x29, 0x64, 0xe8, 0xa2, 0x7f, 0x7c, 0x65, 0x81, 0xf6, 0x0d, 0x16, 0x71, 0x2f, 0xca, 0xf7, 0xac, 0xe3, 0x3e, 0x91, 0xf9, 0xdb, 0xb4, 0x0f, 0x87, 0xa6, 0x74, 0x9b, 0x5e, 0xbf, 0x6a, 0x74, 0x78, 0x75, 0x76, 0x9f, 0x5f, 0xc7, 0x75, 0x36, 0x60, 0xe5, 0xe5, 0x86, 0x2c, 0xbf, 0xf1, 0xa2, 0x68, 0x14, 0xc9, 0x44, 0x80, 0x08, 0xe6, 0x55, 0x06, 0x5e, 0xd6, 0xbf, 0x70, 0x8b, 0x2a, 0x9a, 0xa6, 0xe2, 0x79, 0xe2, 0xa7, 0x54, 0x7d, 0x59, 0xe0, 0x82, 0xc9, 0xd8, 0x72, 0x88, 0x5e, 0xe1, 0x11, 0x59, 0xa4, 0x6d, 0xaf, 0x6e, 0x97, 0xa3, 0xe3, 0xa2, 0x60, 0xe1, 0x8e, 0x02, 0x72, 0xe6, 0xcc, 0x46, 0x12, 0x15, 0x28, 0x37, 0xfc, 0x5c, 0x2b, 0x65, 0x63, 0x6a, 0x90, 0x46, 0x20, 0x6a, 0xa1, 0xca, 0xb5, 0x6d, 0x9a, 0xba, 0xb5, 0x8c, 0x77, 0x03, 0xdf, 0x28, 0x08, 0x03, 0xb1, 0x21, 0x42, 0x79, 0xbb, 0x55, 0xf1, 0x14, 0x76, 0x00, 0x04, 0xb2, 0x1c, 0x4d, 0x31, 0x5c, 0x74, 0x7a, 0xdf, 0x4a, 0xd6, 0x82, 0x7d, 0x1d, 0xe3, 0x05, 0x04, 0x9c, 0xdc, 0xe3, 0x39, 0xeb, 0x3a, 0xe9, 0xa0, 0x9a, 0x95, 0x54, 0x8f, 0x3b, 0x28, 0x98, 0x7e, 0xd3, 0x44, 0xcf, 0xf5, 0x9e, 0x98, 0xdd, 0xa9, 0x53, 0xe4, 0x2a, 0xfb, 0xb7, 0x25, 0x0a, 0x39, 0x76, 0x4b, 0x5f, 0xaa, 0x86, 0xad, 0xf2, 0x65, 0xce, 0x31, 0x90, 0xd1, 0xc7, 0x2c, 0x3b, 0x3c, 0x13, 0x91, 0x49, 0xe6, 0xb8, 0x93, 0xd9, 0x98, 0x70, 0x2b, 0x6c, 0xe2, 0xd0, 0x6a, 0xd4, 0xd8, 0x13, 0x73, 0xae, 0x77, 0x9d, 0xda, 0xe1, 0xbc, 0x23, 0x52, 0x4b, 0x43, 0x62, 0xb5, 0x46, 0xb9, 0x75, 0x40, 0x75, 0xef, 0x1e, 0x1a, 0x2b, 0xa5, 0x13, 0x9e, 0x16, 0xdb, 0x7e, 0x08, 0x35, 0xe8, 0x83, 0xc4, 0x6b, 0xfe, 0x92, 0x74, 0x9f, 0xf0, 0xfc, 0xf4, 0x0b, 0xd6, 0x98, 0x24, 0xc1, 0x4a, 0xeb, 0x91, 0xc4, 0x21, 0x71, 0x4b, 0x36, 0x2c, 0xe8, 0xb2, 0xc5, 0xd9, 0xd0, 0x59, 0xbe, 0x1d, 0xcc, 0x23, 0x52, 0xc8, 0x27, 0x36, 0x95, 0x53, 0xf9, 0x2a, 0xb9, 0x17, 0x03, 0xa8, 0xbc, 0x22, 0x03, 0x62, 0x0d, 0x98, 0xec, 0xd2, 0x4e, 0x2f, 0x42, 0x52, 0x52, 0x47, 0xc6, 0x68, 0x8b, 0xa6, 0x5d, 0x58, 0x53, 0x1a, 0xf3, 0x82, 0x7c, 0xea, 0xdd, 0xda, 0x4f, 0x88, 0x66, 0x62, 0x58, 0x05, 0xf8, 0x82, 0x95, 0xa5, 0x6f, 0x15, 0x1b, 0xcf, 0xaa, 0x7c, 0x64, 0xd4, 0xd0, 0xf3, 0x5d, 0x8a, 0xce, 0xf9, 0x53, 0x87, 0x03, 0xf9, 0xc9, 0x8c, 0x16, 0x43, 0xd6, 0x92, 0x83, 0x7f, 0xb3, 0x03, 0x8f, 0x7e, 0xc7, 0xec, 0x90, 0x2f, 0xc6, 0xf3, 0x1d, 0xd8, 0x55, 0xf6, 0x65, 0x7e, 0xfe, 0xa1, 0x95, 0x90, 0x0d, 0xd9, 0xca, 0xd5, 0xb6, 0x3d, 0x32, 0x75, 0x47, 0xd6, 0xc4, 0x8a, 0x29, 0x34, 0x3d, 0xd8, 0xdc, 0x2e, 0xe7, 0x8b, 0xc6, 0x25, 0x0f, 0xa7, 0xd0, 0xf9, 0x86, 0x60, 0x2d, 0xa5, 0xdb, 0x90, 0xc6, 0x99, 0x63, 0xc8, 0xcd, 0xe4, 0xa0, 0x42, 0x52, 0x2a, 0xad, 0x88, 0x62, 0x9a, 0x41, 0xc8, 0x7f, 0xf3, 0x35, 0x10, 0xc5, 0xe5, 0x48, 0x6b, 0x91, 0x41, 0x58, 0x9b, 0x0e, 0x54, 0x34, 0xef, 0x09, 0x4b, 0x11, 0xf9, 0x83, 0xed, 0xd2, 0x0d, 0x7e, 0xc8, 0x03, 0xf1, 0x26, 0x04, 0xc1, 0x46, 0x18, 0xc7, 0x6e, 0xb4, 0xeb, 0x27, 0x33, 0x64, 0x23, 0x1b, 0xa1, 0xb5, 0x5d, 0xec, 0x56, 0xc0, 0x98, 0x0c, 0xde, 0x08, 0x1b, 0x2e, 0x7d, 0xad, 0xc0, 0x6c, 0x7a, 0x76, 0x48, 0xd9, 0x59, 0xca, 0x93, 0x44, 0x95, 0x74, 0x5c, 0x9d, 0x66, 0x7c, 0x3a, 0x26, 0xc0, 0x9e, 0xee, 0x49, 0xae, 0x6e, 0x28, 0x13, 0xb3, 0xb0, 0xd5, 0x1f, 0xad, 0x33, 0x00, 0xba, 0xc6, 0x1b, 0x92, 0x6b, 0xda, 0x70, 0xef, 0x61, 0x13, 0x71, 0x37, 0xe3, 0xe3, 0x1d, 0x7a, 0x10, 0xb4, 0x22, 0xed, 0xc4, 0xd0, 0x31, 0x1f, 0xc2, 0x45, 0x2c, 0xa5, 0x7d, 0x3e, 0x1f, 0xd8, 0xd0, 0xa8, 0x52, 0x5a, 0x0d, 0x6c, 0xb4, 0x0a, 0xfc, 0x3f, 0x5e, 0x4d, 0xe1, 0x5d, 0xc2, 0x2d, 0x10, 0x18, 0x9c, 0xd6, 0x3e, 0xc3, 0x48, 0x68, 0x3e, 0xd3, 0xcd, 0x87, 0x91, 0xdb, 0x9c, 0x3e, 0x86, 0x87, 0xf7, 0x28, 0xa9, 0xb5, 0xd1, 0x2c, 0x71, 0x12, 0x3e, 0x0a, 0x8d, 0x0b, 0xe9, 0x7c, 0x92, 0x1e, 0xd8, 0xbf, 0xd4, 0x9f, 0x0a, 0x1a, 0x0e, 0x5f, 0xac, 0xcd, 0x23, 0xc0, 0xa2, 0x6c, 0x43, 0xa2, 0x00, 0x1c, 0x7e, 0x6a, 0x7b, 0xf5, 0x46, 0x47, 0xd4, 0x4e, 0x3c, 0x37, 0x36, 0x79, 0x52, 0xfb, 0xa0, 0x7d, 0x13, 0x73, 0x7f, 0xe2, 0xff, 0xbf, 0x0b, 0x68, 0xdd, 0x81, 0xcf, 0x48, 0x40, 0x1e, 0x16, 0x06, 0x26, 0x87, 0xd1, 0xa6, 0x2c, 0xec, 0x10, 0x6d, 0x2c, 0xcc, 0x95, 0x79, 0x1f, 0xb7, 0x41, 0x30, 0x01, 0x34, 0x30, 0x00, 0x31, 0x90, 0xce, 0x59, 0x4d, 0x85, 0xda, 0x51, 0x1e, 0x08, 0x81, 0x06, 0x1c, 0x71, 0x41, 0x5d, 0xac, 0xf2, 0x75, 0x27, 0x81, 0xe7, 0xa6, 0x8b, 0x6d, 0x84, 0x44, 0x32, 0xb1, 0xfb, 0xf2, 0x45, 0xfd, 0x9e, 0xb5, 0xa7, 0x9e, 0xf9, 0x3f, 0xa0, 0x0b, 0x03, 0x21, 0x90, 0xa0, 0x4a, 0x83, 0x39, 0x81, 0xa7, 0x90, 0xe0, 0xfd, 0xd1, 0x1a, 0xea, 0xaf, 0xea, 0x72, 0x5a, 0xfc, 0xe4, 0xfe, 0xbb, 0xf2, 0x3a, 0xbb, 0x75, 0x3c, 0x9f, 0xed, 0xcb, 0xbe, 0x2c, 0x8e, 0xd8, 0x18, 0x0c, 0x58, 0x39, 0x98, 0x9c, 0x9e, 0x92, 0x1f, 0x3f, 0x80, 0xe0, 0xda, 0x61, 0x54, 0x07, 0xe0, 0x65, 0x66, 0x12, 0x29, 0x09, 0x4d, 0x47, 0x8f, 0x8d, 0x26, 0x6c, 0xbe, 0x30, 0x71, 0x51, 0x0a, 0x65, 0x94, 0x74, 0x87, 0x32, 0xf8, 0x4d, 0xc0, 0x57, 0x4d, 0x31, 0x42, 0xc2, 0xc0, 0xf2, 0x2c, 0x29, 0x15, 0xc1, 0xa0, 0x93, 0xf2, 0x36, 0xd2, 0x9b, 0x4c, 0xf3, 0xb5, 0x40, 0xff, 0xad, 0x9e, 0xd5, 0x28, 0x63, 0xfd, 0xfc, 0x1f, 0x40, 0xdb, 0x6d, 0x91, 0x90, 0x39, 0xc5, 0x98, 0x00, 0xb9, 0x29, 0x74, 0x68, 0xba, 0xbb, 0x8b, 0x5d, 0x27, 0x8c, 0x75, 0x2d, 0xa6, 0x27, 0xed, 0xd5, 0x62, 0x48, 0x65, 0x70, 0xb3, 0xef, 0xd8, 0x19, 0x23, 0x49, 0x33, 0x94, 0x9c, 0xb2, 0x08, 0xcf, 0x51, 0x72, 0x47, 0x44, 0x07, 0x9a, 0x14, 0x40, 0x8f, 0xbd, 0x36, 0x90, 0xc5, 0x9a, 0xcb, 0x8d, 0xcd, 0xae, 0xe4, 0xb5, 0x4b, 0x63, 0x81, 0xbb, 0x27, 0x55, 0xb5, 0x17, 0xf0, 0x66, 0xb6, 0x87, 0xc6, 0xc1, 0xc7, 0x1c, 0x73, 0xa0, 0xb4, 0xcb, 0xb8, 0xd4, 0xf9, 0xf4, 0xbb, 0x1b, 0x1b, 0x4e, 0x59, 0xf6, 0x99, 0x8d, 0xa2, 0xb7, 0x5b, 0x34, 0x58, 0x7d, 0x09, 0x99, 0xa0, 0x44, 0x7d, 0xa6, 0xf6, 0xc9, 0xe8, 0x8a, 0x74, 0xfe, 0xd1, 0xcd, 0xa4, 0xad, 0xa0, 0x3e, 0x8b, 0xde, 0x25, 0x92, 0xc9, 0x92, 0x34, 0xee, 0x01, 0x3d, 0xe9, 0xe2, 0x4d, 0x8a, 0xcb, 0xc6, 0x88, 0xe7, 0x09, 0xb7, 0x7d, 0x9f, 0xf6, 0x6e, 0xd9, 0x20, 0x62, 0x0b, 0x06, 0x7b, 0xa3, 0x94, 0xfb, 0x26, 0x8b, 0x3f, 0x13, 0x71, 0x77, 0xeb, 0x0e, 0x19, 0xd5, 0xb0, 0x71, 0x22, 0xec, 0xf5, 0x92, 0xda, 0x16, 0x14, 0x88, 0xa6, 0xc5, 0x88, 0xd7, 0x0f, 0x1f, 0x73, 0xbd, 0xec, 0x5c, 0xc4, 0x64, 0xf6, 0x82, 0x31, 0x00, 0x8b, 0x96, 0xee, 0x4a, 0x38, 0x88, 0x85, 0x2d, 0x3b, 0x0b, 0xaa, 0x0e, 0x75, 0xc6, 0xff, 0x94, 0xaf, 0x7c, 0xce, 0x4c, 0x5b, 0xdd, 0x00, 0x00, 0x01, 0x09 }; +constexpr AccessUnit AVC_BEGIN_OF_AU_SPLIT_EXPECTED_AU = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0x8d, 0x36, 0x09, 0x27, 0xb3, 0xab, 0x69, 0x2f, 0x0f, 0x43, 0xb5, 0x3e, 0x2d, 0x6d, 0x54, 0x1a, 0x9f, 0x91, 0x90, 0xbd } }; + +const std::vector M2V_BEGIN_OF_AU_SPLIT_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x5c, 0xe5, 0xaf, 0x00, 0xfd, 0x0f, 0x1d, 0x27, 0x61, 0x90, 0xd4, 0x77, 0x92, 0x34, 0x4a, 0x7a, 0xdc, 0x96, 0x0d, 0x89, 0x63, 0xef, 0x89, 0xa1, 0x9a, 0x65, 0xc3, 0x28, 0xba, 0x93, 0x62, 0x56, 0x1b, 0xfc, 0x5e, 0xac, 0xaf, 0x7d, 0x30, 0xaa, 0x20, 0x5f, 0x98, 0xf7, 0x81, 0x50, 0xa1, 0x73, 0xa2, 0xd1, 0x35, 0x49, 0x93, 0xed, 0xdf, 0xc4, 0x4b, 0xb7, 0xc3, 0x26, 0x8d, 0xaa, 0x93, 0xb6, 0x62, 0x9a, 0xd4, 0xc8, 0x2c, 0x35, 0x7b, 0x38, 0x78, 0x5c, 0x8c, 0xb8, 0x93, 0x40, 0x3c, 0x0a, 0x11, 0x77, 0x63, 0x33, 0x4b, 0xe0, 0xfd, 0x79, 0x75, 0x87, 0x30, 0x65, 0xbd, 0x20, 0x28, 0x8c, 0x98, 0x4a, 0x60, 0x44, 0xfa, 0xfc, 0x13, 0x31, 0xa5, 0x6f, 0x0b, 0xea, 0x2d, 0x4b, 0xf6, 0x21, 0xd8, 0xa2, 0x0f, 0x19, 0x82, 0x2f, 0x39, 0x45, 0x86, 0xea, 0x54, 0x21, 0xcf, 0xcb, 0xce, 0x75, 0x05, 0x75, 0x96, 0xe8, 0x40, 0x3f, 0x0b, 0x28, 0x96, 0xe2, 0x2a, 0x4a, 0x21, 0xb7, 0xb5, 0xaf, 0x16, 0xce, 0xfe, 0xdd, 0xf4, 0x67, 0x26, 0xcc, 0x95, 0xd3, 0xc3, 0x19, 0x73, 0xeb, 0xce, 0x67, 0x4e, 0x1e, 0x0c, 0xf6, 0x68, 0xbe, 0xde, 0x22, 0x61, 0xf8, 0x87, 0x3e, 0xdf, 0x9b, 0xbf, 0x57, 0x36, 0x19, 0x18, 0x02, 0x49, 0xb4, 0xcc, 0x17, 0xe8, 0x64, 0x03, 0x01, 0x8d, 0xbd, 0x18, 0x60, 0xcc, 0x3f, 0x8f, 0xe8, 0x89, 0xe3, 0x40, 0x4b, 0x3e, 0xf5, 0x0c, 0xa3, 0xd1, 0x32, 0x9f, 0x6c, 0x2e, 0xbd, 0xd4, 0xb7, 0x38, 0x09, 0x40, 0x43, 0x0d, 0xd5, 0x7a, 0x85, 0xf5, 0x30, 0xd7, 0x22, 0xe2, 0x17, 0x7c, 0x7e, 0x26, 0xa0, 0x4b, 0xc4, 0xcf, 0x95, 0xe9, 0x99, 0xbe, 0xc8, 0x1a, 0xda, 0x14, 0x61, 0x4e, 0x49, 0x89, 0x54, 0x8d, 0x0b, 0x0a, 0x27, 0x67, 0x45, 0xd0, 0xb6, 0x18, 0x70, 0x47, 0x11, 0x13, 0x77, 0x46, 0x4b, 0xc4, 0x87, 0x7e, 0x7e, 0x5c, 0x05, 0x4a, 0x2d, 0x2e, 0xf4, 0xbf, 0x0b, 0xf4, 0xd2, 0x7c, 0xa7, 0x9b, 0x29, 0x50, 0x60, 0xc5, 0x3b, 0xa2, 0x72, 0x45, 0x3d, 0xe7, 0x0e, 0x83, 0x68, 0x17, 0x92, 0xc8, 0x80, 0xf4, 0x04, 0x74, 0x5c, 0x61, 0xfc, 0x17, 0x77, 0x32, 0x72, 0xd7, 0xde, 0xcb, 0x3e, 0x00, 0x2c, 0x0b, 0x6b, 0x7c, 0xf1, 0xf6, 0x57, 0x4a, 0xe7, 0x84, 0x38, 0x3c, 0x0e, 0xcd, 0x47, 0xc1, 0x29, 0xa4, 0xab, 0x0f, 0x1a, 0x77, 0x15, 0x5b, 0x90, 0xe4, 0x14, 0xee, 0x03, 0xf2, 0xda, 0x1f, 0x08, 0xd4, 0x2b, 0x1b, 0xa5, 0x35, 0x76, 0x2b, 0x13, 0x8a, 0xe8, 0x70, 0x43, 0x96, 0x86, 0x1d, 0xeb, 0x85, 0x7c, 0x52, 0x8f, 0x22, 0x6b, 0xaf, 0x2d, 0xab, 0xd9, 0x0f, 0xdb, 0x2d, 0xed, 0xd1, 0xd0, 0x2c, 0xa9, 0x7c, 0xcd, 0x66, 0x1d, 0x45, 0x6d, 0x0e, 0x60, 0xd7, 0xa7, 0xca, 0xcd, 0xf5, 0x46, 0x07, 0xcb, 0x60, 0xbb, 0x45, 0x75, 0xe3, 0x61, 0xa2, 0x11, 0x97, 0x05, 0x80, 0xc6, 0x95, 0x76, 0xff, 0x4c, 0x62, 0x86, 0x58, 0xa4, 0x20, 0x14, 0x24, 0x57, 0xa3, 0x10, 0x21, 0x16, 0x42, 0xd6, 0x96, 0x00, 0x0a, 0x15, 0x2e, 0x69, 0x6a, 0x7e, 0x1a, 0x3b, 0x4e, 0x23, 0xc3, 0xb5, 0x41, 0x1b, 0x0b, 0xa3, 0x11, 0xfb, 0xd2, 0x22, 0x79, 0x38, 0xb8, 0xa9, 0x90, 0x31, 0x9e, 0x2d, 0x94, 0xdd, 0x45, 0x1b, 0x3e, 0x2c, 0xbc, 0x43, 0x42, 0x39, 0x00, 0x17, 0xce, 0x9d, 0xb5, 0x25, 0x02, 0x03, 0x9f, 0xb8, 0xdc, 0x18, 0x5d, 0xdc, 0x68, 0xa2, 0xd1, 0xf8, 0xca, 0xdd, 0xce, 0xf8, 0xe1, 0xa5, 0xe9, 0xec, 0xf1, 0x1a, 0xcd, 0xa0, 0x6d, 0xd7, 0xcc, 0xc9, 0xd5, 0x60, 0x59, 0xe6, 0xc7, 0x5b, 0x56, 0x31, 0x83, 0x58, 0x30, 0x7d, 0x54, 0x66, 0x7b, 0x34, 0xc9, 0x4c, 0xda, 0xab, 0xcf, 0x5c, 0xae, 0x82, 0x75, 0x28, 0xa1, 0xed, 0x4a, 0x55, 0xca, 0x8d, 0x2c, 0xd3, 0x52, 0x58, 0xb6, 0xf8, 0x2d, 0x1d, 0x00, 0x82, 0x0f, 0xff, 0x7f, 0x6d, 0x80, 0xe0, 0xa1, 0x77, 0x23, 0x77, 0x3e, 0x67, 0xff, 0x82, 0x18, 0xff, 0x17, 0x00, 0xeb, 0xa9, 0x92, 0x3c, 0x2e, 0x16, 0x27, 0x36, 0xde, 0xef, 0x72, 0x42, 0xb8, 0x9a, 0xbb, 0xa7, 0x46, 0x9b, 0xa6, 0x75, 0xce, 0x8b, 0x71, 0x1c, 0x76, 0x4e, 0x84, 0xaa, 0x15, 0x24, 0xe4, 0xf5, 0x93, 0x4f, 0x19, 0xf3, 0x7b, 0x57, 0xd7, 0x3f, 0x70, 0xfe, 0xb4, 0x75, 0x52, 0x4e, 0x52, 0x88, 0x75, 0xfe, 0x4a, 0x16, 0x22, 0x00, 0xe8, 0xa7, 0x41, 0x7a, 0x8a, 0xb4, 0x15, 0x30, 0xda, 0x3f, 0x5c, 0x54, 0xc3, 0x00, 0xef, 0x4e, 0x65, 0x14, 0xa2, 0xca, 0x8d, 0x4c, 0x3a, 0x4c, 0xba, 0x07, 0xaf, 0xdc, 0x7b, 0x2c, 0x5b, 0x95, 0x79, 0x6e, 0x0b, 0xc4, 0xf3, 0x09, 0x80, 0x5e, 0xc1, 0x18, 0x23, 0x49, 0xe6, 0x81, 0xcd, 0x8b, 0x5d, 0xfc, 0xa2, 0xed, 0xd2, 0x46, 0x30, 0x6d, 0x21, 0x77, 0xde, 0xa3, 0x1f, 0xeb, 0x29, 0x51, 0xd0, 0xae, 0x06, 0x92, 0x1c, 0xd3, 0x80, 0x4f, 0x85, 0x3e, 0xbc, 0xf1, 0xb9, 0x59, 0x1a, 0x25, 0xe2, 0xdb, 0x0a, 0x51, 0xad, 0x2f, 0x62, 0xbd, 0xf6, 0x1a, 0x59, 0xd7, 0x84, 0x48, 0xc7, 0xd3, 0x82, 0xd8, 0xb8, 0x63, 0x02, 0xf4, 0x77, 0x0b, 0x7e, 0xe9, 0x54, 0xc7, 0x5f, 0x04, 0xcd, 0x38, 0xf9, 0xb3, 0xc1, 0x29, 0x7e, 0x69, 0x01, 0x7b, 0xa7, 0x03, 0x4d, 0xd0, 0x2a, 0x3e, 0x5f, 0x06, 0xf2, 0xcb, 0x15, 0x5a, 0x91, 0xe8, 0x02, 0xbc, 0x9c, 0xfb, 0x54, 0x26, 0xb3, 0xec, 0xe1, 0xb7, 0x8b, 0x14, 0x71, 0xc2, 0xc7, 0x8b, 0x61, 0x91, 0x25, 0xcd, 0x51, 0x79, 0x90, 0xcf, 0x26, 0x34, 0x1b, 0x1b, 0xc9, 0x8d, 0xc7, 0x60, 0x15, 0x41, 0x06, 0x48, 0x08, 0xbd, 0x30, 0x28, 0xfc, 0x5e, 0x06, 0x5a, 0x58, 0x89, 0xd1, 0x0c, 0xb6, 0x4c, 0x23, 0x6a, 0xe5, 0x10, 0x97, 0xba, 0x97, 0x01, 0xee, 0x9d, 0x3e, 0x2e, 0x2a, 0x88, 0xa6, 0x86, 0xdc, 0xb1, 0xd8, 0x6a, 0xc0, 0x1c, 0xa8, 0x64, 0xb6, 0x7f, 0x65, 0x7c, 0x5d, 0x91, 0xdf, 0x64, 0x93, 0x93, 0xcf, 0xa7, 0xc6, 0xf9, 0xd3, 0x76, 0xc1, 0x89, 0x90, 0x48, 0xd2, 0x24, 0xa2, 0x68, 0x2f, 0xa8, 0x32, 0xd6, 0x28, 0x27, 0x74, 0x44, 0xdf, 0x56, 0x0d, 0x6a, 0x85, 0xf7, 0xe7, 0x5c, 0x33, 0xcc, 0x2c, 0xa1, 0x55, 0x93, 0xe8, 0x9e, 0x18, 0x08, 0xd3, 0x11, 0x69, 0xb8, 0xb6, 0x03, 0x50, 0x80, 0x2b, 0xce, 0x66, 0x67, 0x13, 0xd8, 0x88, 0x67, 0xcd, 0xb8, 0x51, 0x48, 0xd3, 0xd7, 0x8e, 0x0f, 0x11, 0x5f, 0x89, 0xdd, 0x84, 0xe2, 0xb0, 0x5b, 0x1e, 0xb9, 0x0f, 0x41, 0x6e, 0xfb, 0x3c, 0x13, 0x56, 0xcd, 0x4b, 0x06, 0x05, 0x4d, 0x82, 0xed, 0xa3, 0xa5, 0x37, 0xfb, 0x4f, 0x14, 0x3b, 0x20, 0xdf, 0x13, 0xf1, 0x34, 0x14, 0x31, 0xea, 0xd3, 0x17, 0x4c, 0xa3, 0x7f, 0xf6, 0x9b, 0x13, 0x08, 0x3c, 0x31, 0x85, 0xb2, 0xae, 0xf3, 0x3f, 0x46, 0x70, 0x75, 0x26, 0x77, 0x58, 0xb8, 0x4c, 0x33, 0xad, 0x4c, 0xb1, 0x1b, 0xc7, 0x93, 0xde, 0xd1, 0xaf, 0xfe, 0xc6, 0x96, 0x82, 0x58, 0x0a, 0xfb, 0x70, 0xfd, 0xa4, 0x8f, 0x7d, 0x4b, 0x9b, 0xbf, 0x1f, 0xc4, 0x50, 0x7d, 0x4a, 0x11, 0x10, 0x69, 0x68, 0x23, 0xa4, 0xfe, 0xbe, 0x39, 0x3b, 0xc2, 0x4c, 0x66, 0x4d, 0xed, 0x71, 0x66, 0x96, 0x9c, 0xc0, 0x0b, 0xc5, 0x02, 0x75, 0x79, 0xf6, 0x24, 0xc7, 0x2e, 0x5c, 0x7f, 0x17, 0x16, 0x78, 0x8d, 0x9f, 0xcc, 0xe2, 0x58, 0x62, 0x74, 0x0d, 0xbf, 0xea, 0x9b, 0xc8, 0x65, 0x8a, 0x44, 0x24, 0xcf, 0x8e, 0x79, 0x20, 0x4a, 0x4e, 0x74, 0xc2, 0xef, 0x31, 0x31, 0xe7, 0x73, 0xeb, 0xbe, 0x28, 0x5b, 0x58, 0xb5, 0x4b, 0xd7, 0x36, 0xf5, 0xda, 0x4b, 0x75, 0xb4, 0xbd, 0x81, 0xd0, 0x52, 0x7d, 0xd7, 0xfa, 0x6c, 0x59, 0xbd, 0x02, 0x8d, 0x28, 0x41, 0x21, 0xe9, 0xdb, 0x45, 0x7d, 0xf3, 0x23, 0xc8, 0x9f, 0xf2, 0xed, 0x2e, 0xfa, 0x6f, 0x22, 0x0e, 0x58, 0x6c, 0x96, 0x94, 0xde, 0xa3, 0xce, 0x14, 0x6c, 0xb9, 0x21, 0x43, 0xd1, 0x6d, 0x26, 0x11, 0x80, 0xb2, 0xdf, 0xc9, 0x64, 0xb7, 0xd5, 0xf7, 0x4b, 0xb8, 0x04, 0x29, 0x23, 0x4c, 0xb3, 0x4b, 0x0a, 0x03, 0xc9, 0x22, 0xfc, 0x05, 0xa6, 0x2d, 0x99, 0xf3, 0xc1, 0x19, 0xb4, 0xf6, 0x1c, 0xeb, 0x9b, 0xa0, 0xb6, 0x98, 0x28, 0x04, 0x54, 0x12, 0x8d, 0x4d, 0xc7, 0xa7, 0x2a, 0x55, 0x28, 0xe6, 0x6b, 0xd7, 0xaa, 0x00, 0x31, 0x5e, 0x9b, 0xa1, 0xfc, 0xcd, 0x86, 0xe7, 0xd1, 0x4e, 0xea, 0x93, 0x5a, 0x02, 0x2c, 0xa7, 0x3a, 0x48, 0xde, 0xb0, 0xe3, 0x35, 0x2c, 0x53, 0x9e, 0x65, 0xe2, 0xaf, 0x2f, 0xb7, 0xe4, 0x62, 0xb8, 0x26, 0xb9, 0x2f, 0x2f, 0x4c, 0xd7, 0xda, 0xaf, 0x3d, 0xbe, 0x1c, 0x0a, 0xcb, 0x64, 0xb3, 0xcb, 0xc9, 0xd1, 0xf1, 0xdf, 0x8f, 0xa1, 0xd7, 0xba, 0x1d, 0xf8, 0xdd, 0x68, 0xf6, 0x54, 0x9c, 0x59, 0x90, 0x93, 0x7a, 0x10, 0x88, 0x97, 0x0a, 0xc8, 0x34, 0xfc, 0xd0, 0x30, 0xea, 0xd2, 0x2d, 0x39, 0x3a, 0xe0, 0x5c, 0x05, 0xc1, 0x35, 0x1e, 0xfb, 0x77, 0x38, 0xb7, 0xb3, 0xf8, 0x84, 0x75, 0xfd, 0x11, 0xec, 0x6a, 0xef, 0xc6, 0xd6, 0x8b, 0x5f, 0xb3, 0xf7, 0x05, 0x05, 0x36, 0x94, 0x02, 0x7a, 0xf2, 0xc1, 0x40, 0x46, 0xf9, 0x38, 0xb6, 0x37, 0xc5, 0xec, 0x00, 0xe3, 0xd0, 0x6f, 0xfb, 0xa5, 0x0a, 0x05, 0xc7, 0x97, 0x20, 0xe5, 0xd7, 0x8f, 0x33, 0x05, 0x18, 0xc3, 0x14, 0xed, 0x72, 0x00, 0x1d, 0x7a, 0x5e, 0xa1, 0xb4, 0x32, 0x2d, 0xe4, 0xf1, 0xd7, 0x43, 0xb2, 0x27, 0xf8, 0x43, 0x41, 0x9d, 0x02, 0xf8, 0xc4, 0xb8, 0x7d, 0x84, 0x1f, 0xc3, 0xb6, 0xcf, 0x73, 0xdf, 0x6b, 0xad, 0xda, 0x7b, 0xe6, 0xa0, 0x55, 0xa4, 0x5a, 0x48, 0x8a, 0xd0, 0xe3, 0xe1, 0x62, 0x86, 0xe3, 0x7d, 0xab, 0x23, 0x2b, 0x1e, 0x57, 0x64, 0x25, 0xf7, 0x67, 0x71, 0x07, 0x90, 0x7f, 0xf5, 0x59, 0xc1, 0x89, 0x3e, 0x9f, 0xfe, 0x16, 0xbc, 0x5e, 0xc9, 0xcd, 0x6d, 0x60, 0x97, 0x00, 0x33, 0x4c, 0x10, 0x9f, 0x24, 0xcc, 0x7f, 0x9f, 0x43, 0x4c, 0x80, 0x7e, 0x94, 0x0d, 0x5d, 0xb4, 0x00, 0x6b, 0xbf, 0x3a, 0xb4, 0xee, 0x9a, 0x75, 0xa4, 0x2a, 0x20, 0xa0, 0x1a, 0x04, 0x5d, 0xf5, 0x06, 0x19, 0x9a, 0x1a, 0xd8, 0xb4, 0xca, 0xd7, 0xbb, 0xe5, 0x8b, 0x0d, 0x34, 0x30, 0x9e, 0xf4, 0x45, 0xa5, 0xd5, 0x2d, 0x0d, 0x68, 0x3a, 0xb9, 0xef, 0x86, 0x47, 0x38, 0xda, 0xc7, 0x05, 0xd4, 0x60, 0xc8, 0x99, 0x2f, 0xb7, 0x34, 0x44, 0x1c, 0x5b, 0xa2, 0x97, 0x2a, 0xb2, 0xa5, 0xe1, 0x24, 0x44, 0x01, 0x79, 0x60, 0x0f, 0x12, 0xe3, 0x9b, 0x01, 0xe1, 0x23, 0x94, 0x0d, 0x46, 0x59, 0x78, 0x53, 0xe1, 0x39, 0x7e, 0x3e, 0xe3, 0x80, 0xe3, 0xe9, 0xf1, 0xa9, 0x29, 0xf0, 0xc4, 0x96, 0xd5, 0x8d, 0xe6, 0x40, 0xc5, 0x40, 0x22, 0x8f, 0x96, 0xa0, 0xc5, 0x90, 0xa9, 0x8b, 0xba, 0xb4, 0xcc, 0x77, 0x07, 0x95, 0xb7, 0x37, 0xe3, 0x84, 0xc5, 0xb0, 0xc8, 0x8d, 0x53, 0xa9, 0x57, 0xa5, 0x73, 0xbb, 0xea, 0x5a, 0xcc, 0x79, 0x22, 0x6e, 0x65, 0x63, 0x5c, 0xd5, 0xb4, 0x35, 0xf9, 0xed, 0x95, 0xfa, 0xad, 0x73, 0x7c, 0x20, 0x53, 0x45, 0xac, 0x76, 0x36, 0x50, 0x65, 0xe0, 0x63, 0x14, 0x1e, 0xa2, 0x82, 0x73, 0x43, 0x22, 0xe4, 0xce, 0xbb, 0x6f, 0xb5, 0xa3, 0x99, 0xd8, 0x0a, 0x8d, 0xc2, 0x0d, 0xb7, 0xa6, 0x56, 0xbf, 0x13, 0x84, 0x63, 0x18, 0x8f, 0xaf, 0x15, 0xc9, 0x21, 0xe3, 0xf6, 0x08, 0x48, 0x90, 0xe2, 0x95, 0xf9, 0x69, 0x7f, 0xce, 0xa7, 0x83, 0xdb, 0x5c, 0x35, 0x24, 0x3e, 0x8c, 0x98, 0x3d, 0xd7, 0xe6, 0xc2, 0xe8, 0x23, 0xde, 0xf5, 0x35, 0xa5, 0x98, 0x2a, 0x96, 0x9c, 0xe3, 0xf7, 0x97, 0x17, 0x16, 0x24, 0x46, 0xd2, 0x30, 0x5f, 0x38, 0xd4, 0x63, 0x7c, 0x9e, 0xa1, 0x92, 0xa8, 0xbb, 0x20, 0x79, 0x25, 0xe6, 0x79, 0x6a, 0x73, 0x2a, 0xb9, 0xe8, 0x8b, 0x4d, 0xd4, 0x97, 0x37, 0x5f, 0x05, 0x86, 0xb5, 0x91, 0x0e, 0xc9, 0xd5, 0x75, 0xa0, 0xb4, 0xb4, 0xe7, 0x6f, 0x9a, 0xf5, 0xb9, 0x24, 0x22, 0x21, 0x15, 0x0c, 0x18, 0x4a, 0x65, 0x18, 0x80, 0xa6, 0x11, 0x67, 0x8f, 0x97, 0x6a, 0x68, 0x5a, 0x4d, 0xca, 0xae, 0x2a, 0x05, 0x40, 0xe7, 0x01, 0xef, 0xa5, 0xdb, 0xa3, 0x96, 0x19, 0x59, 0xa7, 0x7c, 0x0f, 0xae, 0xc3, 0xd1, 0xf5, 0x8e, 0x4a, 0xa3, 0xc7, 0x8c, 0x54, 0x85, 0x01, 0x14, 0x3b, 0x44, 0x16, 0xe0, 0xa0, 0xdb, 0x0b, 0x66, 0xe8, 0x9d, 0x2c, 0xea, 0xc3, 0xf5, 0x43, 0xaf, 0xfd, 0xec, 0x3d, 0x1a, 0x83, 0x96, 0x4a, 0xfb, 0x5a, 0x7c, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0xc3, 0xa9, 0xb9, 0x73, 0x50, 0xe3, 0x6a, 0x08, 0xb4, 0x2e, 0x64, 0x6d, 0x05, 0x00, 0x01, 0xb3, 0xd5, 0x92, 0x5f, 0xdd, 0xe1, 0xe6, 0xd0, 0x9d, 0xc5, 0x1c, 0xa6, 0xe6, 0x19, 0xb0, 0x16, 0x15, 0xa1, 0xc3, 0xa6, 0xdb, 0xdb, 0x47, 0x92, 0xa0, 0x40, 0x59, 0x85, 0x23, 0xde, 0x43, 0x26, 0x81, 0x72, 0xab, 0xd9, 0x0b, 0x02, 0xc4, 0x4e, 0x6d, 0xc8, 0x02, 0x67, 0x4a, 0x2f, 0x79, 0x49, 0x22, 0x44, 0xbf, 0x58, 0xec, 0xef, 0x55, 0x29, 0xc6, 0x17, 0xba, 0xf0, 0xcf, 0x1b, 0x94, 0x1d, 0x83, 0x10, 0x85, 0x07, 0x90, 0xda, 0xb9, 0x45, 0xa5, 0x1f, 0xc0, 0x79, 0x00, 0x6d, 0x0b, 0x14, 0xbc, 0xed, 0xde, 0x1f, 0xd4, 0x4a, 0x0c, 0x6e, 0x82, 0x26, 0xeb, 0x37, 0xb8, 0x0b, 0xd0, 0xa5, 0xb3, 0x5f, 0xc1, 0x8e, 0xb8, 0x3d, 0x6d, 0x39, 0x17, 0x64, 0x86, 0x7b, 0x56, 0x01, 0x8d, 0x54, 0x17, 0x5a, 0x16, 0xec, 0x64, 0x3b, 0x4a, 0xce, 0x4a, 0x1d, 0xd7, 0x57, 0xbe, 0x0e, 0x8b, 0x2e, 0xad, 0xd4, 0xbd, 0xab, 0x6b, 0x58, 0xa3, 0xee, 0xd3, 0x2e, 0x38, 0x3c, 0x34, 0x68, 0xe5, 0x37, 0x3b, 0x1d, 0xff, 0xb6, 0x08, 0x3a, 0xed, 0x46, 0x70, 0xc4, 0x85, 0xcd, 0x1e, 0xce, 0xcd, 0x90, 0xa5, 0x7e, 0x42, 0x21, 0xe4, 0x1b, 0x0a, 0xe9, 0xda, 0x9f, 0x42, 0x12, 0xec, 0x89, 0xb8, 0xd3, 0x3e, 0x3e, 0x5e, 0xa0, 0x5e, 0xae, 0x9e, 0x13, 0xa4, 0x7f, 0x89, 0x52, 0x92, 0x36, 0x87, 0x2e, 0x34, 0xaf, 0x3b, 0x0d, 0xaa, 0xe2, 0xfe, 0x85, 0xe5, 0x96, 0x49, 0x2f, 0xf3, 0x28, 0x8a, 0x00, 0xe4, 0x67, 0x9e, 0xf8, 0xc9, 0x70, 0xbd, 0x7c, 0x05, 0x6a, 0x57, 0x81, 0x6f, 0xbc, 0x59, 0x59, 0xc0, 0xce, 0x04, 0xe5, 0x67, 0xc4, 0xac, 0xba, 0xda, 0xd3, 0xe1, 0xbc, 0xf0, 0x79, 0xbb, 0x53, 0x4b, 0xc7, 0x85, 0x63, 0xbf, 0xf0, 0x4f, 0x2f, 0xfa, 0x8d, 0xcf, 0xd7, 0x75, 0x7b, 0x1d, 0x05, 0xea, 0x00, 0x00, 0x01, 0x00, 0x19, 0x48, 0xbb, 0x1d, 0x3e, 0xd9, 0x98, 0x87, 0x8e, 0x14, 0x43, 0x97, 0xd5, 0xae, 0x1f, 0xcc, 0x45, 0x84, 0x6b, 0x27, 0xad, 0x5d, 0x2f, 0x93, 0x5f, 0xac, 0xca, 0x18, 0x34, 0x36, 0x1e, 0x99, 0xf7, 0x9c, 0xf2, 0x93, 0x6a, 0x9c, 0x39, 0xe8, 0x70, 0x87, 0xce, 0x64, 0xf2, 0xa4, 0x70, 0x40, 0x50, 0x49, 0xc2, 0xa7, 0xa9, 0xe9, 0x78, 0xe7, 0xec, 0x6b, 0x33, 0xca, 0x7a, 0xfa, 0x49, 0x4c, 0x39, 0xe4, 0x8d, 0xe5, 0x3d, 0x3f, 0x63, 0xd4, 0xf6, 0xc2, 0x45, 0xc8, 0x93, 0xb6, 0xf9, 0xaa, 0x4d, 0xd2, 0xbc, 0x7e, 0x56, 0x99, 0x6f, 0x43, 0x98, 0x4f, 0xfa, 0xdc, 0x4f, 0x15, 0x30, 0xcf, 0x70, 0x9d, 0x47, 0x0b, 0xb7, 0x42, 0xf8, 0xa6, 0xd3, 0x00, 0x75, 0xff, 0x3a, 0xe4, 0x2f, 0x13, 0xa1, 0xc9, 0xc6, 0x11, 0xa1, 0x51, 0xbb, 0xd4, 0x3d, 0x69, 0x7e, 0xfc, 0xec, 0xa5, 0x77, 0xa4, 0x04, 0x5b, 0xd0, 0x6c, 0x78, 0xe7, 0xbe, 0x10, 0x4c, 0xfc, 0xa2, 0x1f, 0x03, 0x5c, 0x28, 0x57, 0x2f, 0x95, 0xff, 0xdd, 0x65, 0x0b, 0x38, 0xcd, 0x2e, 0x72, 0xcc, 0xcd, 0x2d, 0x80, 0x80, 0x5a, 0xa3, 0x09, 0xaa, 0xd8, 0x9e, 0xe3, 0x96, 0xc2, 0x86, 0xed, 0xe7, 0x55, 0xd6, 0x9e, 0xce, 0xfc, 0x3c, 0x28, 0x40, 0xc8, 0x21, 0xbf, 0x9b, 0x51, 0x64, 0xce, 0x21, 0xe8, 0xc4, 0xab, 0x72, 0xc2, 0x07, 0x5d, 0xa5, 0x26, 0x0e, 0xdc, 0xfb, 0xa4, 0xec, 0x63, 0x00, 0x08, 0x50, 0x4e, 0x27, 0xd6, 0xf7, 0x7f, 0x92, 0xe9, 0xe0, 0x11, 0x50, 0xea, 0xbe, 0xf8, 0x53, 0x5d, 0x02, 0xec, 0x8f, 0x0a, 0xb0, 0xbd, 0x33, 0x22, 0x37, 0x0f, 0x80, 0xff, 0x45, 0xcf, 0x71, 0xdc, 0x00, 0xad, 0x25, 0x1b, 0xee, 0x88, 0x71, 0x69, 0xa5, 0x06, 0x07, 0x21, 0x55, 0x10, 0x43, 0xe0, 0xdb, 0x1a, 0x99, 0xdd, 0xa6, 0xdf, 0xd4, 0x63, 0x2c, 0x1b, 0x0c, 0x17, 0x61, 0xd5, 0x97, 0x2c, 0x71, 0xcf, 0xd7, 0x15, 0x5a, 0x49, 0x83, 0x2a, 0x32, 0x29, 0x19, 0x75, 0x65, 0xc3, 0x6f, 0x17, 0xbb, 0xd2, 0x94, 0x76, 0x2f, 0x7a, 0x96, 0xb5, 0x1f, 0xa1, 0xce, 0xa1, 0x63, 0x0b, 0x29, 0x1c, 0x84, 0x73, 0x37, 0x5f, 0x00, 0x22, 0x41, 0x24, 0x78, 0xb0, 0x34, 0x6e, 0xcf, 0xe7, 0x26, 0x49, 0x61, 0x5b, 0x04, 0x81, 0xe6, 0x15, 0x18, 0x81, 0xc9, 0xd7, 0x2f, 0x28, 0x64, 0xea, 0x30, 0x08, 0x8e, 0x7b, 0x7c, 0x09, 0x79, 0x43, 0x27, 0xa8, 0x9d, 0x38, 0x01, 0xbe, 0xbd, 0xe5, 0xeb, 0xea, 0x83, 0xbd, 0x37, 0x77, 0x71, 0xf2, 0x1d, 0x24, 0x1b, 0x64, 0x57, 0xd7, 0x30, 0x20, 0xa6, 0xa9, 0x3d, 0xe7, 0xd1, 0x34, 0x53, 0x72, 0x3a, 0xb8, 0xe1, 0x39, 0x3e, 0xef, 0x46, 0x94, 0x97, 0xb4, 0x2c, 0xf6, 0x5f, 0xd6, 0x8d, 0xb5, 0x77, 0xf4, 0x38, 0x25, 0x90, 0xc7, 0x21, 0x9d, 0xa7, 0x4a, 0xd2, 0x09, 0xf1, 0xb9, 0x64, 0xed, 0x88, 0xb5, 0xfa, 0xce, 0x60, 0x44, 0x46, 0xf5, 0xb1, 0x02, 0x8e, 0x0f, 0x44, 0xe9, 0x2d, 0x93, 0x4c, 0xd7, 0xa5, 0x03, 0xb1, 0xff, 0x3d, 0xad, 0xf3, 0x7f, 0x00, 0xbb, 0x70, 0x34, 0x74, 0xd7, 0x89, 0x19, 0xb7, 0x17, 0x6a, 0xf1, 0xdb, 0x6c, 0x7a, 0xb1, 0x9f, 0x34, 0xff, 0x51, 0x68, 0x41, 0x9c, 0xea, 0x2b, 0x69, 0x7e, 0x73, 0xf0, 0xb6, 0xa9, 0x3e, 0x65, 0x00, 0xd9, 0xf1, 0xb4, 0x95, 0xdc, 0xdf, 0x89, 0xbc, 0x97, 0xf2, 0x56, 0xf0, 0xa6, 0x01, 0xfa, 0x67, 0x16, 0x0f, 0x38, 0xb1, 0x28, 0x23, 0x0b, 0xef, 0xaa, 0x5f, 0x43, 0xd3, 0x3b, 0xad, 0xe7, 0xf7, 0xe3, 0xa6, 0xdb, 0x84, 0x78, 0x12, 0xda, 0x7d, 0xcb, 0x59, 0xda, 0x76, 0x7f, 0x23, 0x88, 0xe4, 0x9b, 0x7b, 0x4d, 0xb4, 0x29, 0xce, 0xd7, 0x81, 0xa7, 0x0a, 0x12, 0xbb, 0x4f, 0x31, 0x2a, 0x45, 0x6f, 0xbb, 0x68, 0x0a, 0x8b, 0xe2, 0xba, 0xe4, 0x49, 0xa0, 0x13, 0x45, 0x7b, 0xd4, 0x65, 0x01, 0xc8, 0x68, 0x8c, 0xe4, 0x00, 0x4f, 0x15, 0xdf, 0x0a, 0x08, 0x0f, 0x2c, 0x2d, 0x8a, 0x2b, 0xfc, 0xa2, 0x37, 0x96, 0xfb, 0xaf, 0x92, 0x7d, 0xcf, 0x2a, 0x71, 0xec, 0x47, 0x9d, 0x44, 0x49, 0x23, 0x84, 0x3e, 0x4d, 0x97, 0x36, 0x1a, 0xf8, 0xc6, 0x0c, 0x6e, 0x52, 0x29, 0xeb, 0xc1, 0xbd, 0x74, 0xf1, 0x9e, 0x02, 0xf6, 0x21, 0xd0, 0x87, 0x67, 0x29, 0x2a, 0xcd, 0xfc, 0xfb, 0xd5, 0x6e, 0xe1, 0xe3, 0xec, 0xdd, 0x0b, 0x64, 0x16, 0xe8, 0xf6, 0x4b, 0x0f, 0x1e, 0x49, 0x92, 0xd4, 0x5d, 0x0c, 0xcb, 0x1b, 0x41, 0x85, 0x30, 0x42, 0x91, 0xe8, 0x4a, 0x31, 0xbc, 0x0d, 0x4f, 0x3e, 0xd4, 0x9f, 0x9b, 0x51, 0xaa, 0x84, 0x9a, 0x25, 0x85, 0x5e, 0x87, 0x4b, 0x95, 0x12, 0x52, 0x69, 0x54, 0x31, 0x45, 0xde, 0xe2, 0x58, 0xf6, 0xde, 0x8f, 0xce, 0xa1, 0x66, 0x0e, 0x35, 0xd6, 0xc5, 0x7c, 0x70, 0x83, 0x71, 0xe7, 0x96, 0x06, 0x06, 0x75, 0xcd, 0x0b, 0xeb, 0x9b, 0x98, 0x8d, 0x79, 0xc2, 0x33, 0xad, 0x4b, 0x1c, 0x08, 0x67, 0x63, 0x65, 0xd0, 0xbd, 0x67, 0x56, 0x7a, 0x0a, 0x46, 0x9c, 0xe8, 0x10, 0x25, 0x9d, 0xb1, 0x97, 0x4c, 0x20, 0xe8, 0x5a, 0x07, 0xc7, 0x82, 0x36, 0x9b, 0xfd, 0x32, 0x15, 0xf2, 0xe6, 0x86, 0x20, 0x61, 0xb8, 0xc9, 0xc5, 0x86, 0xcb, 0x84, 0x35, 0x86, 0x96, 0xb2, 0x5e, 0xcc, 0x00, 0x2e, 0x14, 0x1c, 0x64, 0xd0, 0xdb, 0x59, 0x0e, 0x5e, 0x53, 0x07, 0x94, 0x05, 0xa8, 0xa8, 0x5e, 0x9f, 0xb6, 0x44, 0xbd, 0x7d, 0x03, 0x47, 0xf7, 0xb2, 0xbf, 0x21, 0xf6, 0xc6, 0xe5, 0xe3, 0xaa, 0x1a, 0xab, 0xca, 0x79, 0x0d, 0x72, 0xa7, 0xc4, 0x48, 0x97, 0x1e, 0xbc, 0x8c, 0xf9, 0xbf, 0xe5, 0xc2, 0xfa, 0x64, 0x0f, 0xa6, 0x1c, 0x98, 0xa8, 0x74, 0x47, 0x51, 0x55, 0xc3, 0x53, 0x64, 0xc6, 0x17, 0xed, 0x44, 0x02, 0x25, 0x33, 0xcd, 0x13, 0xa8, 0x1e, 0x9c, 0x3f, 0x49, 0xf1, 0xac, 0x6a, 0xd5, 0xfb, 0xb3, 0xde, 0xcc, 0xf5, 0x3e, 0xb7, 0x73, 0x2e, 0xb2, 0x0e, 0x0c, 0x03, 0x5c, 0xbb, 0xf6, 0x0e, 0x57, 0x0a, 0x15, 0xd5, 0x84, 0xdb, 0x3c, 0x6c, 0x64, 0xca, 0xe7, 0x41, 0x52, 0xb3, 0x62, 0xb8, 0xf5, 0xd0, 0xdd, 0xc5, 0xba, 0x1a, 0x26, 0x6b, 0xb7, 0xe5, 0xb5, 0x9d, 0xdb, 0x21, 0x4a, 0x28, 0x1a, 0xe4, 0x6a, 0xd0, 0x32, 0x71, 0xf0, 0x7d, 0x02, 0x67, 0x75, 0x9d, 0xe0, 0x0f, 0x61, 0x2b, 0x60, 0xb2, 0xc0, 0x1f, 0x73, 0x72, 0x10, 0x43, 0x7e, 0xcc, 0x28, 0x52, 0xb0, 0xa9, 0x75, 0x20, 0x79, 0xf8, 0xd6, 0x20, 0x6e, 0x3d, 0x38, 0x0e, 0x5d, 0x61, 0xe6, 0x32, 0xfc, 0x34, 0xa6, 0x6d, 0xd9, 0xd0, 0x59, 0x7b, 0xc7, 0xdb, 0xb7, 0x23, 0x7c, 0x4a, 0x66, 0x2d, 0xae, 0x6c, 0xde, 0x6d, 0x50, 0x14, 0x83, 0xb3, 0x28, 0xa3, 0x4c, 0x7e, 0xde, 0x3b, 0x24, 0x6b, 0xb7, 0x5f, 0x08, 0xa2, 0x06, 0x0f, 0x31, 0x13, 0x66, 0x1e, 0xf9, 0x8e, 0x85, 0x61, 0xb9, 0xbb, 0x67, 0x28, 0x2a, 0xe7, 0xd9, 0xb4, 0x7e, 0xab, 0x1b, 0xb2, 0xdb, 0x2d, 0x7e, 0xb9, 0x96, 0x10, 0x82, 0x20, 0xa9, 0xa2, 0x44, 0x72, 0xb3, 0xe7, 0x97, 0x63, 0x3e, 0xdc, 0x81, 0x2a, 0xbf, 0x2b, 0xf5, 0x02, 0x37, 0xe8, 0x77, 0x3b, 0x8b, 0xe1, 0x11, 0xcf, 0xab, 0x26, 0xbd, 0x07, 0x62, 0x45, 0x0a, 0x5f, 0x55, 0x10, 0x7a, 0x52, 0xa0, 0xea, 0x1c, 0x84, 0xdf, 0xa2, 0x31, 0xaf, 0x2a, 0xe5, 0x50, 0x39, 0x58, 0xc0, 0x65, 0x3f, 0xad, 0x3b, 0x27, 0xa1, 0x3b, 0x26, 0x91, 0x02, 0x82, 0xaf, 0xfc, 0x36, 0xc3, 0x42, 0xc7, 0x2b, 0x40, 0xd1, 0xf1, 0xde, 0x6d, 0x0c, 0x35, 0x28, 0xa1, 0x7d, 0x62, 0x51, 0xeb, 0x04, 0xca, 0xdb, 0x81, 0x8e, 0x4e, 0x2a, 0x41, 0xb9, 0x6e, 0x2f, 0x58, 0x53, 0x1b, 0xa6, 0x34, 0x06, 0x50, 0x56, 0xa2, 0x66, 0x8e, 0xc5, 0xfb, 0xe1, 0x38, 0x54, 0x10, 0x98, 0x56, 0xda, 0xda, 0x24, 0xb9, 0xc9, 0xba, 0x3e, 0xcf, 0xa9, 0x89, 0x52, 0xcb, 0x6c, 0x21, 0x37, 0xdc, 0xdb, 0xdc, 0x86, 0x0e, 0xa3, 0x6e, 0xe4, 0x56, 0x03, 0xbc, 0x7c, 0x61, 0xd8, 0x12, 0xeb, 0x7e, 0x92, 0xe4, 0xfc, 0xc9, 0xbc, 0x3b, 0x18, 0x5b, 0x09, 0xdb, 0x97, 0xd6, 0xd6, 0x6f, 0xa1, 0xbd, 0x17, 0xfb, 0x18, 0xc1, 0x8e, 0xf2, 0x03, 0x32, 0x4d, 0x18, 0x38, 0x31, 0x50, 0xe7, 0x56, 0xc6, 0xaa, 0x1c, 0x27, 0xb9, 0xb6, 0x0f, 0x5c, 0x11, 0x0d, 0x7d, 0xd2, 0xdd, 0x72, 0xe4, 0x30, 0xbb, 0xa9, 0x7a, 0xa4, 0x17, 0xeb, 0x0e, 0x8d, 0xe4, 0xfd, 0x3c, 0xd5, 0x81, 0xea, 0x53, 0x05, 0xbb, 0xa5, 0x5a, 0xdc, 0xfe, 0x58, 0x40, 0x4d, 0xf0, 0xdb, 0xc8, 0x48, 0x05, 0x2e, 0x06, 0xb4, 0x21, 0x43, 0x10, 0x3d, 0x3d, 0x66, 0xfe, 0x47, 0x91, 0x5b, 0x93, 0xde, 0x27, 0x60, 0x9e, 0x15, 0x23, 0xc7, 0x24, 0xeb, 0xa3, 0x1b, 0x4c, 0xff, 0x32, 0x95, 0xb5, 0xd2, 0x96, 0x1c, 0x1d, 0xa9, 0x02, 0x57, 0xbe, 0x93, 0xd0, 0x35, 0x3d, 0xc9, 0xeb, 0xd9, 0x6a, 0x3e, 0x9b, 0x0d, 0x33, 0x20, 0xa2, 0x44, 0x5f, 0x81, 0x75, 0x8c, 0xd7, 0x92, 0xbc, 0xb6, 0x57, 0x84, 0xce, 0xe8, 0xe4, 0x7e, 0x6b, 0xed, 0x95, 0x69, 0x25, 0x96, 0xae, 0x78, 0x67, 0x90, 0x02, 0xee, 0xe5, 0x11, 0x2d, 0x6c, 0x68, 0x02, 0x49, 0xb1, 0x00, 0xda, 0x84, 0x07, 0x7a, 0x53, 0x24, 0x9d, 0xb4, 0xfc, 0xf3, 0x38, 0x3b, 0xc2, 0xed, 0x36, 0x66, 0x62, 0x84, 0xee, 0x53, 0x37, 0x48, 0xb2, 0xb8, 0xfa, 0x1a, 0x40, 0xf7, 0xa1, 0x10, 0x49, 0x32, 0x97, 0xf8, 0xee, 0x64, 0x94, 0x20, 0x82, 0xac, 0x19, 0x34, 0x6a, 0x33, 0xce, 0x1a, 0x14, 0xb3, 0x9e, 0x62, 0x83, 0xea, 0xc5, 0x29, 0xf1, 0xed, 0xa4, 0xae, 0xb8, 0x54, 0x72, 0xd1, 0xce, 0x94, 0xfc, 0x94, 0xf6, 0x52, 0x6b, 0xbb, 0xde, 0x34, 0x37, 0xec, 0xdd, 0x82, 0x94, 0xae, 0xae, 0x20, 0x0b, 0x4a, 0xa2, 0xc5, 0x89, 0x96, 0xbd, 0x07, 0xf1, 0x02, 0xf5, 0x45, 0x9c, 0xb3, 0xa4, 0x91, 0xcd, 0xf5, 0xb3, 0x7c, 0xaf, 0x82, 0xe8, 0xeb, 0xd7, 0xc9, 0xd8, 0x72, 0x46, 0x2e, 0xd5, 0x4e, 0x89, 0x96, 0x3f, 0x67, 0xe4, 0xba, 0x6c, 0x7f, 0xef, 0x49, 0x39, 0xef, 0x17, 0x33, 0x4b, 0x24, 0xf6, 0xb2, 0x5d, 0x01, 0x58, 0x91, 0x95, 0x80, 0xfe, 0xd8, 0x99, 0xc6, 0x0c, 0xfa, 0x85, 0x77, 0xc8, 0x18, 0x44, 0x2e, 0xa6, 0xbb, 0x2b, 0xfb, 0xa4, 0x23, 0x8e, 0xbe, 0xde, 0x19, 0xff, 0x71, 0x83, 0x72, 0x4e, 0xbb, 0x84, 0xae, 0x8a, 0x0a, 0x52, 0xa4, 0x0a, 0x90, 0x7b, 0xb3, 0x11, 0xf3, 0xe8, 0xb3, 0x6f, 0x91, 0xf3, 0xca, 0xd0, 0x9e, 0xf8, 0xfb, 0x7a, 0x1f, 0x4a, 0x9a, 0x26, 0x3c, 0x5f, 0x96, 0xf4, 0x58, 0x2a, 0x27, 0x8c, 0x00, 0x00, 0x01, 0xb7 }; +constexpr AccessUnit M2V_BEGIN_OF_AU_SPLIT_EXPECTED_AU = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0x82, 0x81, 0xd3, 0x81, 0x99, 0x42, 0xa6, 0x9c, 0xe1, 0x8b, 0xab, 0x3e, 0xdb, 0x1c, 0x00, 0x03, 0xae, 0x8a, 0x24, 0x51 } }; + +const std::vector ATRACX_BEGIN_OF_AU_SPLIT_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xa7, 0x0b, 0x60, 0xfe, 0xb5, 0x2e, 0x3b, 0x4e, 0x6b, 0xaf, 0x90, 0x57, 0xc4, 0x99, 0xd0, 0x4e, 0xfa, 0x04, 0x29, 0x4c, 0x23, 0xcd, 0x8a, 0x9a, 0xac, 0x64, 0x27, 0x65, 0x27, 0xa2, 0x5c, 0xe2, 0xfd, 0x1d, 0x15, 0xcf, 0x72, 0x70, 0x1e, 0xb5, 0x08, 0x9f, 0x6f, 0x3d, 0x0b, 0x22, 0x22, 0x99, 0xbe, 0xcc, 0xb6, 0xdd, 0x99, 0xb9, 0x2b, 0xba, 0xa6, 0x32, 0xf0, 0x11, 0xc3, 0x4f, 0xc9, 0x06, 0xb1, 0x27, 0xee, 0x0b, 0xc6, 0xa6, 0x26, 0xf8, 0x02, 0x35, 0x7f, 0x64, 0x4a, 0x5d, 0x87, 0x1e, 0x20, 0xc9, 0x2a, 0x48, 0x0a, 0x57, 0x90, 0x65, 0x5a, 0x14, 0x62, 0x31, 0xbd, 0x5c, 0x28, 0xc6, 0x17, 0xcc, 0x3f, 0xde, 0x22, 0x35, 0xcf, 0xe2, 0xdc, 0xbe, 0x9b, 0xcb, 0x22, 0x96, 0xf1, 0xe1, 0xe9, 0x74, 0x7f, 0xc4, 0x79, 0xca, 0xe4, 0xeb, 0x68, 0x16, 0xeb, 0x39, 0x57, 0x65, 0xed, 0xd1, 0xc3, 0x6b, 0x21, 0x3a, 0xca, 0x94, 0xa2, 0xf7, 0x5f, 0x8e, 0x02, 0x47, 0x02, 0x35, 0xe4, 0x65, 0x3a, 0x34, 0x09, 0x15, 0x63, 0x8d, 0x0b, 0x1f, 0xdc, 0x1e, 0x8d, 0x6b, 0xfd, 0x16, 0xe7, 0xf6, 0x12, 0x2f, 0x75, 0x30, 0x31, 0x72, 0xd1, 0x8e, 0x68, 0x15, 0x5a, 0xbc, 0xf7, 0x0e, 0x89, 0x07, 0x20, 0xa0, 0x9b, 0xae, 0x14, 0x2a, 0xe7, 0x74, 0xa5, 0xf0, 0x15, 0xec, 0xda, 0x9a, 0x6e, 0xe2, 0x80, 0xc6, 0x98, 0xac, 0x8e, 0xc4, 0xfb, 0xdc, 0x73, 0x5b, 0x97, 0xcd, 0xa4, 0x99, 0xea, 0x92, 0x39, 0x50, 0xcc, 0xdc, 0x09, 0x89, 0x09, 0xed, 0x61, 0x44, 0x7a, 0xa3, 0x9b, 0x70, 0x86, 0x36, 0x0e, 0x81, 0x1d, 0xc5, 0x7a, 0x6a, 0xcf, 0x2e, 0x3f, 0x17, 0xbb, 0xf3, 0xe1, 0x4b, 0x44, 0xe9, 0xdb, 0x1b, 0x69, 0x59, 0x4e, 0xd2, 0x37, 0x1f, 0xe1, 0x22, 0x9a, 0x10, 0xdd, 0x72, 0xc7, 0x56, 0x7c, 0x6f, 0xc4, 0xb3, 0x36, 0xa7, 0xce, 0x69, 0xad, 0x7c, 0xc4, 0x1c, 0xed, 0xa0, 0x55, 0x8d, 0x44, 0x37, 0x96, 0x41, 0x3c, 0x06, 0xe1, 0xbb, 0x58, 0x35, 0x43, 0x5b, 0x0b, 0x10, 0xed, 0x13, 0x02, 0xe2, 0xb2, 0xeb, 0x2f, 0x7d, 0x89, 0x63, 0x2e, 0x52, 0x51, 0xc5, 0x05, 0x06, 0xbd, 0x64, 0xc0, 0x16, 0xfe, 0x41, 0xc9, 0x36, 0xb1, 0xe6, 0xf3, 0x14, 0x06, 0x6e, 0x05, 0x04, 0x02, 0x70, 0x1f, 0xa7, 0xed, 0x2b, 0x80, 0x94, 0x7f, 0x73, 0xb6, 0xf8, 0xa6, 0x16, 0x10, 0x6d, 0x1d, 0xe2, 0xf4, 0x9e, 0x5c, 0x48, 0x1f, 0x00, 0x9d, 0xb4, 0xc1, 0x9f, 0xd4, 0x1a, 0x69, 0x3d, 0xbc, 0x1c, 0x2b, 0x07, 0xeb, 0xe4, 0xe4, 0xdc, 0xf1, 0x82, 0x5b, 0xba, 0xab, 0x9f, 0xf2, 0xdc, 0x21, 0x0c, 0xcd, 0xef, 0x70, 0x1b, 0xf4, 0xbd, 0x9a, 0x7d, 0xde, 0xad, 0x0e, 0x58, 0x68, 0xd2, 0x1b, 0x9b, 0x23, 0x4d, 0xe6, 0x94, 0x6c, 0xe3, 0x75, 0x4f, 0x7e, 0x0e, 0x89, 0x14, 0x79, 0x54, 0x2c, 0x93, 0x4f, 0x05, 0x52, 0x1d, 0x92, 0x15, 0x80, 0x58, 0x8d, 0x42, 0xb6, 0xf4, 0x14, 0x9c, 0x46, 0x45, 0x69, 0x9f, 0xbd, 0x74, 0xd8, 0xe4, 0x30, 0x31, 0x23, 0x78, 0xce, 0x4b, 0xb7, 0x2c, 0x51, 0x2d, 0x76, 0x21, 0x83, 0x41, 0x64, 0x29, 0xce, 0x29, 0x09, 0x4c, 0xe8, 0x0a, 0xbe, 0x12, 0x77, 0xaf, 0x31, 0x0e, 0x6c, 0xc8, 0x4f, 0x29, 0xe4, 0x84, 0xfa, 0x05, 0xe2, 0x1c, 0xdb, 0x56, 0x64, 0xe2, 0xb4, 0x28, 0x6e, 0x51, 0x4b, 0xc4, 0x90, 0xad, 0x5d, 0x8f, 0x90, 0x64, 0x78, 0xf9, 0x7b, 0x3e, 0xd4, 0xf7, 0x2a, 0xdc, 0x40, 0xc0, 0xee, 0x48, 0x69, 0x61, 0x53, 0xe7, 0x76, 0x2e, 0xcb, 0x73, 0x4b, 0xad, 0xe5, 0x93, 0x45, 0x3f, 0x4a, 0x8c, 0x1d, 0x02, 0x2f, 0x02, 0xb7, 0xb3, 0xf7, 0xbe, 0x95, 0x6e, 0x48, 0x63, 0x55, 0x1d, 0xe2, 0xdb, 0xf0, 0x17, 0x22, 0x5f, 0xda, 0xb3, 0xf2, 0x54, 0x08, 0xbc, 0x8b, 0x08, 0x7b, 0x99, 0x37, 0xef, 0xfb, 0x10, 0xc6, 0x7b, 0xdd, 0x43, 0x98, 0xa2, 0x67, 0x55, 0x41, 0xd6, 0x43, 0x5b, 0xfe, 0x59, 0xfa, 0xd1, 0x1c, 0xb5, 0x18, 0x1b, 0x50, 0x68, 0xbd, 0x8f, 0x4a, 0x6f, 0x81, 0xfd, 0x6e, 0x85, 0x76, 0xe3, 0x43, 0x8d, 0x18, 0x2a, 0x1f, 0x8d, 0x75, 0x78, 0xa5, 0xaa, 0xb1, 0x6a, 0xa1, 0x59, 0xa1, 0x7e, 0x40, 0x62, 0x2c, 0x8d, 0x5b, 0xda, 0xa7, 0x32, 0x1c, 0xe2, 0xb9, 0xdd, 0x8d, 0x77, 0x6d, 0xcc, 0x13, 0x5f, 0x0a, 0x40, 0x74, 0xf6, 0xe1, 0x66, 0xda, 0xc3, 0x9c, 0x5c, 0x60, 0x61, 0xed, 0x2f, 0x2c, 0x89, 0x72, 0x7a, 0x11, 0xc6, 0x3a, 0x5d, 0x45, 0x55, 0xd8, 0xa8, 0x03, 0xc6, 0x6d, 0x78, 0xe7, 0xb9, 0x54, 0xb3, 0x6b, 0xc9, 0xcb, 0xc1, 0xac, 0x41, 0x1c, 0x4a, 0x10, 0x21, 0x56, 0x17, 0xa3, 0x1d, 0x1e, 0x78, 0x40, 0x6f, 0x0c, 0x3b, 0x9e, 0xcc, 0x7b, 0xc4, 0xa3, 0x07, 0x90, 0xa3, 0x6c, 0xa4, 0x63, 0xdf, 0xc3, 0xea, 0xf0, 0x85, 0xbc, 0x14, 0xda, 0x31, 0xd8, 0x49, 0x2f, 0x70, 0x85, 0x5c, 0x7b, 0x6d, 0x92, 0x3c, 0x0d, 0x35, 0x61, 0xe7, 0x8d, 0x65, 0xee, 0x37, 0xa3, 0x9a, 0xdb, 0x9c, 0x44, 0x18, 0x7c, 0x01, 0x90, 0xc2, 0xae, 0x37, 0x34, 0x07, 0x9e, 0xb9, 0xf2, 0x53, 0x86, 0x09, 0x9b, 0xc4, 0xda, 0xf8, 0xe2, 0x0e, 0x52, 0x8d, 0xb5, 0xe8, 0xd5, 0x0c, 0x75, 0xe9, 0x1d, 0x9e, 0x52, 0xa3, 0x60, 0x15, 0x50, 0x94, 0xcd, 0x1c, 0x7d, 0xfe, 0x75, 0x61, 0x30, 0x6b, 0xc9, 0x4f, 0xcd, 0xe1, 0x97, 0xa0, 0x9d, 0xaf, 0xfc, 0x57, 0xdf, 0xca, 0xff, 0xd6, 0x23, 0x8d, 0x0a, 0x38, 0x89, 0xdd, 0x9c, 0x60, 0x1e, 0xaf, 0xed, 0x83, 0xac, 0x84, 0x6b, 0xd8, 0xc2, 0x06, 0x5f, 0xc0, 0xef, 0xac, 0x59, 0x96, 0xcc, 0xc4, 0x2f, 0xb7, 0x57, 0x73, 0x9f, 0xee, 0x70, 0x82, 0xe9, 0x3c, 0x54, 0xdf, 0x2c, 0x35, 0xfe, 0xa4, 0x78, 0x0c, 0x75, 0xb4, 0x90, 0x12, 0xd3, 0x1f, 0xbf, 0x92, 0x7b, 0xd7, 0x10, 0xa6, 0x98, 0xb2, 0x0e, 0x9f, 0xfd, 0x3d, 0xd5, 0x4e, 0x4d, 0x55, 0xa8, 0xf0, 0xa8, 0xa4, 0x74, 0x74, 0xf7, 0x8b, 0x7f, 0xda, 0xfe, 0xe3, 0xc2, 0xf7, 0x31, 0xe4, 0x0a, 0x20, 0xf2, 0x2e, 0x0b, 0x47, 0x5c, 0x29, 0x2c, 0x33, 0xd7, 0xf7, 0x7d, 0x02, 0x0b, 0x17, 0x01, 0xf4, 0x0b, 0x1e, 0xa2, 0x63, 0xaa, 0x80, 0xbc, 0xb9, 0x21, 0xd7, 0xfa, 0x7c, 0x2a, 0xa1, 0xa6, 0x90, 0x30, 0x8e, 0xd1, 0x94, 0x75, 0x78, 0xc1, 0x6e, 0x78, 0xdc, 0x8a, 0xd8, 0x65, 0x00, 0x2e, 0xf7, 0x99, 0x67, 0xfe, 0x5e, 0x49, 0x3e, 0x87, 0xd5, 0x92, 0x7b, 0x2d, 0x23, 0xfe, 0xcc, 0xce, 0x44, 0xe4, 0x94, 0x1b, 0xa6, 0xd3, 0x48, 0x88, 0x17, 0x71, 0x2b, 0x94, 0xd8, 0x47, 0xc6, 0x68, 0xc2, 0x89, 0xa7, 0xab, 0x65, 0x9a, 0xa0, 0x9a, 0xcd, 0xee, 0x36, 0x8c, 0x1d, 0xba, 0xcb, 0x75, 0x8d, 0xbe, 0x09, 0x1e, 0x48, 0xad, 0xd4, 0xc6, 0x16, 0xf3, 0xfe, 0x6a, 0x77, 0xa9, 0xed, 0x2c, 0xd3, 0x7e, 0xc0, 0x16, 0xa9, 0x11, 0x57, 0xfc, 0x36, 0xa8, 0x68, 0xb1, 0xb0, 0x57, 0x59, 0x1b, 0xea, 0x4c, 0xe9, 0x47, 0xf7, 0xa9, 0xa1, 0x88, 0xfc, 0x47, 0x84, 0x28, 0xd6, 0xca, 0x39, 0xc4, 0xde, 0x88, 0xad, 0x8c, 0xff, 0x68, 0xe1, 0x81, 0x83, 0x8b, 0x71, 0x0e, 0xda, 0x98, 0xf8, 0x63, 0xd7, 0x25, 0x81, 0x57, 0x12, 0x19, 0x8e, 0x03, 0xf3, 0x3a, 0x58, 0xfb, 0xf5, 0x34, 0x93, 0xc0, 0x1f, 0xd5, 0x43, 0xac, 0x92, 0x13, 0x48, 0x7e, 0x9a, 0x66, 0x69, 0x2e, 0x0c, 0x77, 0x00, 0xa3, 0xe1, 0x16, 0x76, 0x51, 0x7a, 0xc7, 0x5f, 0xf4, 0x66, 0x3f, 0xe6, 0x2f, 0x90, 0x6a, 0xfd, 0xba, 0x0b, 0x87, 0x29, 0x3e, 0xa4, 0xc4, 0xde, 0x6b, 0x25, 0xce, 0x45, 0x16, 0x9f, 0x54, 0x10, 0x20, 0xea, 0xb5, 0xd7, 0x9b, 0xc0, 0x79, 0xef, 0x90, 0x0e, 0xca, 0x5e, 0x4c, 0x1a, 0x73, 0x5a, 0xc2, 0xfb, 0x7a, 0x1a, 0x6b, 0x6b, 0x51, 0x55, 0x85, 0x0b, 0xf3, 0x60, 0x6c, 0x92, 0x94, 0x37, 0x45, 0x89, 0x48, 0xb8, 0x17, 0xa3, 0xa4, 0x70, 0x33, 0xdb, 0x12, 0x39, 0x48, 0x6e, 0x8f, 0x83, 0x9c, 0xea, 0x24, 0x46, 0x50, 0x15, 0xa4, 0xe5, 0x57, 0x5f, 0x73, 0xdc, 0xbb, 0xad, 0xc9, 0x08, 0x03, 0x8d, 0x7a, 0x89, 0x17, 0x91, 0x9e, 0x7e, 0x1c, 0x9f, 0xdc, 0xb3, 0x61, 0x0a, 0x0b, 0x16, 0x2c, 0x5a, 0xbf, 0xab, 0x25, 0x66, 0xdc, 0x21, 0x4a, 0xd6, 0xb9, 0x22, 0xa1, 0x0b, 0xed, 0x54, 0xf9, 0xf3, 0xc6, 0xe9, 0xf5, 0xd8, 0x39, 0x90, 0xa2, 0xad, 0xd8, 0xa4, 0xdf, 0x6e, 0xe0, 0x07, 0x3b, 0xe2, 0x6c, 0x04, 0x92, 0x03, 0x05, 0x6a, 0x54, 0x2c, 0xfb, 0xb3, 0xcb, 0xe8, 0xd0, 0x19, 0x0a, 0x4d, 0xdd, 0xf6, 0xf8, 0x84, 0xb6, 0xe8, 0xae, 0xd9, 0x73, 0xbe, 0xca, 0x44, 0x59, 0x2c, 0x7a, 0x83, 0xf4, 0xd3, 0x45, 0x72, 0x78, 0x50, 0xd8, 0xc7, 0xcd, 0xb6, 0x93, 0x1f, 0x87, 0x56, 0xc3, 0xf8, 0xb1, 0xb7, 0x6f, 0xd5, 0xda, 0x98, 0xa4, 0x84, 0x59, 0x69, 0x6d, 0xa0, 0xcc, 0xcf, 0x29, 0xac, 0x6a, 0x1a, 0xd8, 0x08, 0x73, 0x2a, 0x70, 0x2e, 0x76, 0xc0, 0x87, 0xc5, 0xb7, 0x06, 0xec, 0x9d, 0x1d, 0xce, 0xbe, 0x99, 0x1c, 0xcf, 0x45, 0x2d, 0xdf, 0x35, 0x14, 0xad, 0xa6, 0x9e, 0xcb, 0x86, 0xe8, 0x24, 0xaa, 0x79, 0x36, 0x84, 0xe1, 0x78, 0x1e, 0xdd, 0x41, 0x67, 0x74, 0x45, 0x24, 0x41, 0x62, 0x59, 0x77, 0xf1, 0xaf, 0xbf, 0xca, 0x21, 0x85, 0xf9, 0xc8, 0xb2, 0xf6, 0x07, 0x91, 0x54, 0x98, 0x25, 0xc8, 0x66, 0x3b, 0xb7, 0x1b, 0xcf, 0x3e, 0xe7, 0xb0, 0x53, 0x6b, 0x11, 0x6f, 0xb1, 0x79, 0xd5, 0xdb, 0xbd, 0x72, 0x0a, 0xdb, 0x79, 0x86, 0x4a, 0x22, 0x55, 0x09, 0xf2, 0x0d, 0x19, 0x90, 0xc9, 0x0a, 0x20, 0x7a, 0x67, 0x7e, 0x8b, 0x26, 0x47, 0x04, 0x60, 0xd7, 0x04, 0x6b, 0x9b, 0x85, 0x32, 0x12, 0xc6, 0x3e, 0xbb, 0x2d, 0xd3, 0x0f, 0x1a, 0x33, 0x6d, 0x4d, 0x88, 0xda, 0xd8, 0xb5, 0xe3, 0x0c, 0xc6, 0x12, 0x4d, 0x7b, 0x77, 0x55, 0x5c, 0xf6, 0xfd, 0xaa, 0xed, 0xa5, 0x13, 0x3e, 0x8b, 0xce, 0x37, 0xc3, 0x21, 0xe2, 0x80, 0x25, 0x35, 0x55, 0xae, 0x03, 0x38, 0x60, 0x8f, 0x57, 0x05, 0x65, 0xd8, 0x35, 0x5c, 0x8a, 0xd7, 0x9b, 0x6b, 0x7c, 0x31, 0xe0, 0x82, 0xbf, 0xf5, 0x21, 0x41, 0x6c, 0xab, 0xe3, 0x44, 0xd0, 0x64, 0x80, 0xce, 0x9a, 0x28, 0x50, 0x5d, 0x71, 0x28, 0xab, 0x2a, 0x1f, 0x5b, 0xca, 0x16, 0xc4, 0x81, 0x52, 0xa1, 0x39, 0xa6, 0x08, 0xb4, 0x31, 0x38, 0x0d, 0xe6, 0xc6, 0x2a, 0xaf, 0x1f, 0xd9, 0x52, 0x43, 0xd8, 0xb5, 0xb9, 0xc9, 0x84, 0x08, 0x53, 0x91, 0xd9, 0x5d, 0xa8, 0x78, 0x21, 0x2c, 0x2c, 0xa5, 0x49, 0x8c, 0xf5, 0x0d, 0xee, 0xdc, 0xb7, 0xdf, 0xf3, 0x66, 0x40, 0x68, 0xc9, 0xa4, 0x94, 0xbc, 0x37, 0x16, 0x2d, 0x61, 0xb5, 0x58, 0x47, 0x09, 0x95, 0xc5, 0xf0, 0xbb, 0x43, 0xee, 0x60, 0x32, 0xa4, 0xd1, 0xbf, 0xe1, 0xf1, 0xd5, 0x54, 0xbc, 0xd3, 0x81, 0xc5, 0x26, 0xbb, 0xb4, 0xd8, 0xd6, 0x3d, 0x4a, 0x86, 0x50, 0x90, 0xbd, 0x8b, 0x36, 0xfb, 0xe3, 0x78, 0x94, 0xb2, 0x3a, 0x76, 0x1b, 0x72, 0x5a, 0x29, 0x20, 0x31, 0x12, 0xf3, 0xaa, 0x03, 0xcb, 0x2c, 0xdf, 0x59, 0x78, 0x8e, 0x99, 0x1f, 0x23, 0x9d, 0x13, 0xd1, 0xac, 0xef, 0x2e, 0xfb, 0x0b, 0x86, 0xfa, 0x28, 0xc6, 0x62, 0x82, 0x9b, 0xf2, 0x75, 0x13, 0x24, 0xfe, 0x2f, 0x06, 0x0c, 0xf1, 0x4f, 0x92, 0xda, 0xda, 0x67, 0x58, 0x5e, 0x52, 0x85, 0xbf, 0xa0, 0x6d, 0xdb, 0xb2, 0x2f, 0xf0, 0xe9, 0x98, 0x97, 0xd1, 0x4c, 0x53, 0x93, 0xf9, 0x44, 0xc6, 0x84, 0x0e, 0x8a, 0xcd, 0xfb, 0x60, 0xf3, 0xfe, 0x75, 0xbc, 0x46, 0xef, 0x00, 0x5c, 0xfa, 0xbc, 0xf7, 0x60, 0x30, 0xc3, 0xe7, 0xc0, 0x10, 0xa4, 0xd4, 0x69, 0xf3, 0xea, 0x10, 0x54, 0x9e, 0xd1, 0x1d, 0xd7, 0x09, 0xcc, 0x0f, 0x7b, 0x28, 0xec, 0x75, 0x82, 0x3c, 0x18, 0xb4, 0x5d, 0x1b, 0xbb, 0x90, 0xe0, 0xd3, 0xed, 0x99, 0x67, 0x72, 0x2d, 0xa6, 0x35, 0xf4, 0x92, 0x2d, 0x77, 0x62, 0x67, 0x32, 0x1d, 0xba, 0xd6, 0xb2, 0x97, 0xe9, 0xd7, 0x83, 0x0d, 0x37, 0xe9, 0xad, 0x3a, 0xd8, 0x7b, 0xfb, 0xd3, 0xc8, 0xeb, 0xd2, 0xb1, 0x3a, 0xab, 0xc9, 0xd6, 0x61, 0x8f, 0x90, 0xbc, 0xa5, 0x1b, 0xa9, 0xc6, 0x47, 0x8d, 0x75, 0xb7, 0x59, 0x84, 0xb7, 0xb4, 0x07, 0x22, 0x66, 0xac, 0x7f, 0xbf, 0xbb, 0xe2, 0x13, 0x6d, 0x41, 0x74, 0xcf, 0x1e, 0x67, 0xd3, 0x01, 0x9a, 0xd8, 0xe9, 0x3c, 0xcc, 0xde, 0x08, 0x5b, 0x32, 0xa5, 0xf2, 0x8b, 0x0f, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0xc3, 0xa9, 0xb9, 0x73, 0x50, 0xe3, 0x6a, 0x08, 0xb4, 0x2e, 0x64, 0x6d, 0x05, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0xd8, 0x1d, 0xe7, 0x46, 0x25, 0x7e, 0x8c, 0x8e, 0xd0, 0xf5, 0xbe, 0x16, 0xfa, 0x95, 0x4f, 0xf2, 0x70, 0xa3, 0xbb, 0x44, 0x58, 0x07, 0x72, 0xef, 0x92, 0xaf, 0x0e, 0x5b, 0xcb, 0x5f, 0x84, 0x33, 0x71, 0xad, 0x81, 0x00, 0x39, 0xab, 0x5c, 0xfa, 0x36, 0xfd, 0xcd, 0x41, 0xdb, 0x64, 0xfc, 0xba, 0x97, 0x46, 0x3f, 0x10, 0x92, 0xe6, 0xa1, 0xb4, 0xc4, 0x2d, 0x53, 0x2c, 0x11, 0xea, 0x7a, 0x8b, 0x82, 0x44, 0x7d, 0xdf, 0xf6, 0x7c, 0x8b, 0xf4, 0xd5, 0x1e, 0xcf, 0xb7, 0x49, 0xdd, 0x14, 0x81, 0x08, 0x6d, 0x2b, 0x90, 0x99, 0x57, 0x44, 0xfe, 0xb2, 0xf2, 0xaa, 0x3e, 0x9a, 0xac, 0x55, 0x30, 0x73, 0x3e, 0x97, 0xcd, 0x99, 0x08, 0x37, 0x65, 0x14, 0x55, 0xf2, 0xaf, 0xfd, 0x2f, 0x02, 0xbc, 0x80, 0x52, 0x16, 0x46, 0xd2, 0xae, 0xcc, 0x8c, 0xd1, 0x1a, 0x3d, 0xd6, 0x6a, 0x53, 0xdb, 0xcf, 0x75, 0x55, 0x39, 0x97, 0x1b, 0x21, 0x38, 0x04, 0xbe, 0xb2, 0xfb, 0xb2, 0x5b, 0x4d, 0x50, 0x41, 0xf4, 0x8a, 0xc4, 0xb2, 0xcd, 0xba, 0x98, 0xf3, 0x87, 0x58, 0xdc, 0xc7, 0xdf, 0x70, 0xc6, 0x91, 0xa2, 0x67, 0x0f, 0xe4, 0xe0, 0x61, 0xfa, 0xa8, 0xc6, 0x0c, 0xf9, 0xfe, 0x3d, 0xee, 0xb7, 0xdc, 0x94, 0x02, 0xd1, 0x62, 0x76, 0x7f, 0xa2, 0xf5, 0xca, 0xfb, 0x7d, 0x00, 0x92, 0xf0, 0x50, 0xc9, 0xac, 0x39, 0x53, 0x59, 0x67, 0x2f, 0x55, 0x00, 0x8d, 0x07, 0xdb, 0x6d, 0x0b, 0x32, 0xe3, 0x18, 0x55, 0xc4, 0x8d, 0xb7, 0x1b, 0x28, 0xf3, 0x65, 0xb7, 0x1c, 0x8c, 0xf5, 0x39, 0x7e, 0xa4, 0x29, 0x31, 0x28, 0x4f, 0x23, 0x44, 0xd6, 0xbd, 0x46, 0x00, 0x18, 0x0d, 0x2a, 0x2c, 0xcc, 0x4f, 0xb3, 0x1b, 0x78, 0x43, 0x36, 0x41, 0x4a, 0x9c, 0xd7, 0x02, 0xfc, 0x80, 0xfb, 0x2b, 0xcc, 0x0f, 0x51, 0x78, 0x1e, 0x82, 0xf5, 0x75, 0xb8, 0x1c, 0xf6, 0x43, 0xd0, 0x50, 0x27, 0x66, 0x18, 0x2d, 0x99, 0x4e, 0x16, 0x52, 0x1e, 0x62, 0x17, 0x87, 0x3e, 0xeb, 0xa6, 0x02, 0xa8, 0xc3, 0x3e, 0x03, 0x3b, 0x1a, 0x59, 0xe4, 0xe3, 0xfa, 0xb2, 0x9b, 0xae, 0x91, 0x0d, 0x45, 0x18, 0xac, 0x25, 0x52, 0x07, 0xf4, 0x15, 0xcc, 0x8b, 0xe3, 0xc2, 0xce, 0x63, 0xc6, 0x2c, 0xc9, 0xa0, 0x7c, 0xa1, 0x98, 0x31, 0x97, 0xc5, 0x5e, 0x48, 0xbb, 0x0e, 0x52, 0x14, 0xf3, 0x36, 0xcf, 0x2c, 0x4d, 0x6d, 0xaa, 0x73, 0x67, 0x63, 0x0a, 0xcb, 0x11, 0xe1, 0x6d, 0xf9, 0x78, 0x1c, 0x46, 0x38, 0xd3, 0xea, 0x1e, 0xe0, 0x92, 0xfa, 0xc1, 0x9e, 0x6c, 0x8f, 0x30, 0x96, 0x50, 0xd6, 0x3c, 0x19, 0x04, 0xdb, 0x72, 0x1b, 0x52, 0xd0, 0x6c, 0x59, 0x59, 0x29, 0x92, 0xdb, 0x75, 0x9c, 0xbf, 0x84, 0x60, 0x02, 0x31, 0x4f, 0x67, 0x6a, 0x1e, 0x2d, 0x6a, 0x2c, 0x0e, 0xc3, 0x7f, 0xa3, 0x34, 0xaa, 0x58, 0x08, 0xd7, 0xc2, 0xf5, 0xef, 0x9a, 0xd0, 0x72, 0xda, 0xb1, 0xb8, 0xd4, 0xd0, 0x69, 0x13, 0x81, 0x48, 0x7e, 0x99, 0x65, 0x39, 0x63, 0x4b, 0x66, 0x69, 0x34, 0x26, 0xb4, 0x52, 0xc0, 0xce, 0x47, 0x9a, 0x2c, 0xdf, 0x17, 0x7f, 0x78, 0xbf, 0x69, 0x8e, 0x54, 0x70, 0x9d, 0xb1, 0x87, 0x2a, 0xa7, 0x7c, 0x32, 0x84, 0xc5, 0xf7, 0x91, 0x5b, 0xd3, 0x8f, 0xf9, 0x3a, 0x03, 0xfa, 0xf0, 0x20, 0x9e, 0xe0, 0xc9, 0x0d, 0xb1, 0x03, 0x07, 0x62, 0xd8, 0xbc, 0x48, 0x42, 0x42, 0x81, 0x29, 0x14, 0x84, 0x44, 0x0a, 0x66, 0x8e, 0xc5, 0xb6, 0xea, 0x0d, 0xdc, 0x7c, 0xde, 0xd3, 0xfc, 0xcb, 0x9b, 0xc2, 0xcc, 0x1d, 0xe4, 0x65, 0xe8, 0xae, 0xe6, 0x82, 0x50, 0xfe, 0x8c, 0x7a, 0x34, 0xdb, 0x0f, 0xa7, 0x6e, 0x02, 0x9b, 0x85, 0xc1, 0xed, 0xa0, 0x4c, 0x68, 0x91, 0x5d, 0xd5, 0xae, 0x7c, 0x18, 0xbd, 0xb1, 0x04, 0x5f, 0x28, 0xe6, 0x75, 0x25, 0x22, 0xe0, 0x6e, 0xb1, 0xe7, 0xc5, 0xfb, 0x69, 0x91, 0x39, 0x59, 0x4f, 0x5f, 0xda, 0xc9, 0x39, 0x30, 0xad, 0x5d, 0x2d, 0x4c, 0x88, 0xc5, 0xff, 0x17, 0xc9, 0x76, 0x30, 0x25, 0xda, 0x86, 0x37, 0x1c, 0x7e, 0x97, 0xc3, 0xf1, 0xea, 0xcc, 0xef, 0x20, 0xc0, 0xf3, 0xbd, 0xdf, 0xbf, 0x92, 0x28, 0xba, 0x93, 0x5f, 0x4a, 0xae, 0xc0, 0xa3, 0xbb, 0xbb, 0xaf, 0xa0, 0x8e, 0x3e, 0x5a, 0xb5, 0x44, 0x7a, 0x08, 0x33, 0x60, 0x3a, 0xf1, 0xcd, 0x52, 0x05, 0xb8, 0x16, 0x3b, 0xab, 0xde, 0xd1, 0x3d, 0x7b, 0x3d, 0xc5, 0x44, 0xda, 0xdc, 0xa5, 0xe2, 0x80, 0x0b, 0x7f, 0xfa, 0xb9, 0xc9, 0xa9, 0x32, 0x46, 0xf3, 0x89, 0x0f, 0xe3, 0xeb, 0x68, 0xac, 0x96, 0x13, 0xff, 0xef, 0x76, 0xec, 0x19, 0xca, 0x09, 0x1a, 0x9a, 0x49, 0x4d, 0xa8, 0x54, 0xfa, 0x74, 0x85, 0x80, 0x3d, 0xb8, 0x61, 0x75, 0x13, 0xc2, 0x2e, 0xe9, 0x4c, 0x9a, 0x69, 0x41, 0xcb, 0x72, 0xce, 0x9e, 0x30, 0x4b, 0x1d, 0x3f, 0x8d, 0xc6, 0xfe, 0xe4, 0x87, 0xb9, 0x29, 0x1a, 0xdc, 0xb8, 0x57, 0xd0, 0xe8, 0xbf, 0x04, 0xb7, 0x06, 0x97, 0x38, 0x21, 0xb7, 0xd8, 0x17, 0xb1, 0xef, 0xb6, 0xfc, 0x09, 0x74, 0xdf, 0x73, 0x4a, 0x42, 0x1f, 0x13, 0x49, 0x48, 0x7b, 0x28, 0x0d, 0x3e, 0x55, 0x02, 0x93, 0x39, 0x1c, 0x87, 0x66, 0x7b, 0x2e, 0x10, 0x75, 0x1e, 0x85, 0x64, 0xf9, 0xd6, 0x2f, 0x40, 0x18, 0x86, 0xf3, 0xa7, 0x81, 0xb7, 0xb0, 0x1e, 0x95, 0x57, 0x02, 0x3e, 0x0f, 0xe5, 0x87, 0x9f, 0x72, 0x30, 0x80, 0xaa, 0x63, 0xf1, 0xdd, 0xb0, 0xd5, 0x83, 0x3b, 0x58, 0x19, 0x42, 0xc6, 0x27, 0xdf, 0x6b, 0x26, 0x07, 0x15, 0x4d, 0xc1, 0x83, 0x40, 0xa0, 0x36, 0xbb, 0xaf, 0xf7, 0x7a, 0xcf, 0x9e, 0x37, 0x9d, 0x32, 0xec, 0x64, 0x49, 0xd5, 0xe0, 0x17, 0x2d, 0x25, 0x67, 0xce, 0x31, 0xb7, 0x1e, 0x09, 0x2f, 0x2a, 0x6e, 0x7c, 0xcd, 0xf4, 0x1c, 0xc4, 0x74, 0x63, 0x89, 0xc1, 0xd8, 0xda, 0x33, 0x6c, 0x46, 0x44, 0x74, 0x7c, 0xa4, 0xf0, 0x4f, 0x03, 0x16, 0xd5, 0x27, 0x38, 0xda, 0x36, 0x08, 0x62, 0xd0, 0xbc, 0xa2, 0x00, 0x7a, 0x58, 0x2e, 0x1a, 0x7b, 0x2d, 0xf6, 0x02, 0xf1, 0x41, 0xce, 0x60, 0x32, 0xdd, 0x11, 0xd9, 0x7f, 0x2b, 0xbe, 0x40, 0x1b, 0xdb, 0xe5, 0x65, 0xc4, 0xa2, 0x7f, 0xc6, 0x78, 0x38, 0x16, 0xe2, 0x13, 0x29, 0x34, 0x1c, 0xda, 0x36, 0xae, 0xc3, 0x05, 0xc9, 0xf2, 0x5c, 0xa5, 0xa2, 0x5b, 0xae, 0xb6, 0xe3, 0x5d, 0x6e, 0xa6, 0x36, 0x9e, 0x5b, 0x11, 0x1f, 0x0a, 0xa4, 0xf0, 0x35, 0x70, 0x3d, 0x82, 0xee, 0xa2, 0x9f, 0xd9, 0x7b, 0xec, 0xe8, 0xaa, 0xee, 0xb9, 0x05, 0x2f, 0xca, 0xdd, 0x80, 0x19, 0x0f, 0x81, 0x6e, 0xdd, 0x8f, 0x71, 0xc1, 0x0b, 0x36, 0x2d, 0xcf, 0x8e, 0xfa, 0xb9, 0x8a, 0x70, 0xf5, 0x20, 0x61, 0x9f, 0x20, 0xde, 0xe7, 0xed, 0xd6, 0x24, 0x4a, 0xd7, 0x29, 0xb7, 0x3a, 0xe1, 0x88, 0x64, 0x20, 0x0c, 0x43, 0x57, 0xa5, 0x18, 0x85, 0x52, 0x92, 0xb1, 0xc5, 0x98, 0xf6, 0x6e, 0x4c, 0x3d, 0x60, 0x03, 0xb7, 0x82, 0x01, 0xec, 0x7d, 0xcf, 0xe3, 0x4e, 0xcb, 0x58, 0x49, 0xdb, 0xb1, 0xb3, 0xd3, 0xfc, 0x31, 0x9d, 0x1d, 0xb1, 0xcd, 0x50, 0x64, 0x2b, 0x2c, 0x30, 0x0f, 0xd7, 0x11, 0x42, 0xa5, 0x1f, 0x45, 0x33, 0xf8, 0xc6, 0x97, 0x9f, 0xc7, 0xce, 0x96, 0xb0, 0x0c, 0xf2, 0xcd, 0x5f, 0x65, 0x9e, 0x81, 0x58, 0x41, 0xfa, 0xf8, 0xe2, 0x79, 0xa6, 0x73, 0x65, 0xf6, 0xbe, 0x15, 0x98, 0x20, 0xe5, 0xda, 0xd3, 0x31, 0x9d, 0x97, 0x13, 0x47, 0x14, 0xf6, 0x2c, 0xd8, 0xbc, 0xea, 0xbb, 0x80, 0x60, 0x9d, 0x1f, 0x50, 0x59, 0x9f, 0x82, 0x4f, 0x6f, 0x18, 0x4d, 0x79, 0x48, 0x99, 0x6b, 0x55, 0xcb, 0x1a, 0xac, 0x4c, 0x8c, 0x38, 0xbf, 0x80, 0xad, 0x0b, 0x7c, 0xc5, 0x72, 0xe7, 0x60, 0x0e, 0xbf, 0xc3, 0x78, 0x73, 0xd7, 0x98, 0x96, 0xc1, 0x31, 0x16, 0xd5, 0x92, 0x06, 0x09, 0xd6, 0x92, 0x85, 0x1a, 0xa6, 0xd1, 0x77, 0x8e, 0x0f, 0xbe, 0x82, 0x40, 0x25, 0x97, 0x7b, 0x10, 0xfc, 0x1f, 0x65, 0x40, 0x58, 0x19, 0x7d, 0x08, 0x28, 0xfa, 0x8a, 0x85, 0xfa, 0x4b, 0x9b, 0xe6, 0xa0, 0xea, 0x62, 0xa9, 0x64, 0xd5, 0x3c, 0x60, 0x6b, 0x33, 0x33, 0x95, 0x97, 0xc2, 0x6f, 0x9e, 0x9d, 0xe2, 0x85, 0x83, 0x10, 0x7d, 0x56, 0x69, 0x68, 0x07, 0x2e, 0xb6, 0x84, 0x79, 0x1a, 0xc6, 0x7e, 0x75, 0x89, 0x42, 0xc7, 0xdf, 0x46, 0x35, 0x82, 0x3d, 0x42, 0x35, 0x95, 0x41, 0x33, 0x8b, 0xdd, 0xf7, 0xc2, 0x27, 0xbd, 0x3a, 0xc4, 0xb2, 0x5d, 0xc5, 0xc6, 0xa9, 0xea, 0x05, 0x33, 0x3c, 0x3a, 0xe0, 0xe5, 0x0e, 0x44, 0xfd, 0xc8, 0xb0, 0x90, 0x29, 0x70, 0xae, 0x3a, 0x55, 0x42, 0xd0, 0x08, 0x7e, 0xc2, 0xc8, 0xf2, 0x1e, 0xd9, 0x2e, 0x9d, 0xc8, 0xa0, 0x8a, 0x77, 0x3e, 0xf4, 0xa0, 0xfb, 0x0a, 0xbd, 0xaf, 0x12, 0xde, 0xc9, 0x5a, 0xa8, 0xcd, 0xa8, 0x3c, 0xb0, 0x6d, 0x27, 0x9c, 0xed, 0xb5, 0x41, 0x84, 0x55, 0x4e, 0x61, 0x30, 0x15, 0x71, 0x7a, 0x19, 0xd9, 0x79, 0x78, 0x9a, 0x9e, 0xd8, 0xed, 0x9e, 0x24, 0x2c, 0x6b, 0x7d, 0x8c, 0x2c, 0x3c, 0x70, 0x5d, 0x16, 0x56, 0x19, 0x0c, 0xac, 0xde, 0x76, 0xef, 0xb3, 0xaa, 0xe7, 0xac, 0xac, 0x27, 0x75, 0xd9, 0xb9, 0x47, 0x4d, 0x86, 0x8d, 0xdd, 0xc5, 0xd6, 0x70, 0xd1, 0xd3, 0x00, 0xfa, 0xf6, 0x84, 0x60, 0x16, 0x53, 0x4b, 0x35, 0x74, 0xd9, 0x0d, 0xd5, 0xa1, 0x13, 0x1a, 0x08, 0xdd, 0x73, 0x9b, 0xf0, 0x44, 0xf3, 0xdd, 0x0a, 0x5b, 0xbb, 0x01, 0xf1, 0x74, 0x81, 0x79, 0x4d, 0x80, 0x5f, 0xfd, 0x0d, 0x7a, 0x1b, 0x7b, 0x78, 0xde, 0x1a, 0x95, 0x53, 0xf1, 0xf7, 0x91, 0x5d, 0xac, 0xa2, 0x53, 0x10, 0xe8, 0xd0, 0x86, 0x77, 0x83, 0x6f, 0x92, 0x0b, 0x84, 0x6c, 0xb3, 0x1a, 0xfa, 0x14, 0x25, 0xbb, 0x72, 0xbc, 0xd1, 0xa9, 0xae, 0xc0, 0xb8, 0x1b, 0x42, 0x32, 0x25, 0x30, 0xc2, 0xfa, 0xb1, 0x63, 0xbf, 0x7e, 0x81, 0xb8, 0xa9, 0x0a, 0x8c, 0x18, 0x1b, 0x44, 0x02, 0xa8, 0xe3, 0xc0, 0xff, 0xbf, 0x62, 0xf0, 0x18, 0x08, 0xdc, 0xfa, 0x0a, 0xe6, 0x1c, 0x04, 0xff, 0xd8, 0x08, 0x0c, 0x08, 0x62, 0xaf, 0x3c, 0x0e, 0xde, 0x24, 0x54, 0x47, 0x3e, 0x2c, 0x8c, 0xec, 0xe7, 0xcd, 0x5a, 0x04, 0x90, 0x7a, 0x70, 0x6f, 0x66, 0x44, 0xab, 0x57, 0x08, 0x70, 0x17, 0xb8, 0xf0, 0xc2, 0xff, 0x2f, 0xa2, 0x2d, 0x6e, 0x5f, 0xbd, 0x8c, 0x8a, 0xf0, 0x28, 0x37, 0xff, 0x04, 0x01, 0x5f, 0xac, 0x11, 0x53, 0x25, 0xc1, 0x52, 0xf3, 0x27, 0xed, 0xac, 0x60, 0x99, 0xd8, 0x25, 0x8e, 0x54, 0x7f, 0x9c, 0xf1, 0x3f, 0x4e, 0xc8, 0x57, 0xf6, 0x2b, 0x2d, 0x6d, 0xe9, 0xa4, 0xd8, 0x4b, 0x2e, 0x7a, 0xc3, 0x63, 0xcd, 0xe1, 0xf0, 0xf0, 0x39, 0xf0, 0x0a, 0xbb, 0x0c, 0x82, 0xd3, 0x0e, 0x49, 0x9e, 0x22, 0xcb, 0x5c, 0xd7, 0x3d, 0xd3, 0x58, 0x76, 0x84, 0x2d, 0x8a, 0x5a, 0x02, 0xee, 0x2a, 0xbc, 0xc1, 0xc4, 0x57, 0x5d, 0x06, 0x04, 0x6c, 0x93, 0x70, 0x26, 0x2b, 0x5c, 0xc9, 0xff, 0x22, 0x51, 0x25, 0xa2, 0xec, 0xdc, 0x29, 0xf2, 0x72, 0x41, 0x8c, 0x47, 0x70, 0xa9, 0xe3, 0x4c, 0xf5, 0xef, 0x16, 0xbf, 0x6d, 0x7d, 0xc1, 0xad, 0x1f, 0x14, 0x9e, 0x86, 0x43, 0xd1, 0x7e, 0xac, 0x67, 0x6f, 0x4c, 0x84, 0xdc, 0x68, 0x25, 0xd3, 0xfc, 0x5f, 0x5e, 0x01, 0xb4, 0xe6, 0x6b, 0xf0, 0x19, 0x54, 0x5a, 0xc7, 0x1c, 0xff, 0x0b, 0x4a, 0x73, 0xda, 0xc4, 0xb5, 0xd4, 0x79, 0xc4, 0x79, 0x7c, 0x13, 0x61, 0xd4, 0x77, 0x86, 0x57, 0x90, 0x91, 0x4b, 0x27, 0x81, 0x2a, 0xd6, 0x52, 0x33, 0x04, 0x2e, 0xc5, 0xa9, 0x56, 0xbf, 0xbc, 0x44, 0x40, 0x0f, 0x6b, 0x92, 0xda, 0xb0, 0x39, 0xe5, 0x45, 0x51, 0x21, 0x5d, 0x26, 0xb4, 0x4e, 0xe5, 0x2f, 0x4e, 0x7a, 0x02, 0x7c, 0x20, 0xa0, 0xe3, 0x74, 0x8c, 0xdb, 0x76, 0xf2, 0xcd, 0xdc, 0x29, 0x13, 0x63, 0x84, 0x54, 0xe7, 0x3e, 0xb2, 0x14, 0xf4, 0x90, 0x98, 0x51, 0x67, 0x66, 0xbd, 0x36, 0xc7, 0x5f, 0x00, 0x61, 0x93, 0x30, 0xe5, 0x9f, 0xe9, 0xb1, 0x15, 0x4e, 0xf3, 0x55, 0x7b, 0x34, 0x41, 0xa4, 0xc0, 0xc1, 0xa7, 0xe3, 0x58, 0xb3, 0x6b, 0x13, 0x4e, 0xca, 0x54, 0xca, 0x31, 0xe7, 0xc3, 0x5b, 0xfa, 0x58, 0x83, 0x5e, 0xf5, 0x82, 0xe8, 0x67, 0xb8, 0x33, 0xe1, 0x71, 0x5d, 0x6c, 0xa9, 0xc1, 0x3a, 0x4a, 0x7f, 0xa7, 0x02, 0xac, 0x96, 0xc2, 0x06, 0x89, 0x0f, 0xd0, 0x13, 0x34, 0xd9, 0x12, 0x6c, 0xf7, 0x0f, 0xd0, 0x46, 0x29, 0x4a }; +constexpr AccessUnit ATRACX_BEGIN_OF_AU_SPLIT_EXPECTED_AU = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0x35, 0x1b, 0x6a, 0x2a, 0x8e, 0x4d, 0xf1, 0x87, 0x92, 0xa9, 0xff, 0x8c, 0x7f, 0xcc, 0x5c, 0x1c, 0x5a, 0x7a, 0x05, 0xdd } }; + +const std::vector AC3_BEGIN_OF_AU_SPLIT_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x6e, 0xc9, 0xfb, 0xc5, 0x67, 0xe9, 0x8d, 0x68, 0x46, 0x00, 0x0d, 0x37, 0x95, 0xba, 0x1f, 0xda, 0x13, 0x05, 0xa0, 0x64, 0xd4, 0x9f, 0x4f, 0xdb, 0x14, 0x4d, 0x18, 0x4e, 0xb1, 0x3c, 0x2c, 0x9e, 0xca, 0x40, 0x57, 0xbd, 0x5e, 0xce, 0xf5, 0xd7, 0xae, 0x0d, 0xce, 0x9f, 0x77, 0xc5, 0xbf, 0xff, 0x00, 0x19, 0xbf, 0xe2, 0x98, 0x6a, 0x77, 0xd7, 0x78, 0x6a, 0xe4, 0x97, 0x5a, 0x98, 0xd1, 0xe2, 0x55, 0x64, 0xff, 0x08, 0x0b, 0xe5, 0xab, 0x41, 0xb5, 0x30, 0x25, 0xa9, 0x10, 0xd4, 0x4c, 0xd6, 0x33, 0xb0, 0x14, 0x2d, 0x2e, 0xb4, 0xc8, 0x9b, 0x5b, 0x9b, 0xd8, 0x1a, 0x88, 0xab, 0xf2, 0xdb, 0x6f, 0xc1, 0x99, 0xd6, 0x5c, 0x3a, 0xc5, 0xcf, 0xf7, 0x30, 0x90, 0x76, 0x26, 0x8f, 0x22, 0x20, 0xc7, 0xdb, 0xec, 0xad, 0xee, 0x45, 0x12, 0x52, 0x01, 0x8e, 0x1c, 0xa7, 0xb5, 0x71, 0x76, 0x33, 0x44, 0xf8, 0x54, 0xf6, 0x5c, 0x92, 0x38, 0xfc, 0xf3, 0x2d, 0x53, 0x73, 0xbe, 0x7a, 0x7d, 0x26, 0xb1, 0x93, 0xda, 0x46, 0xae, 0xae, 0xf2, 0xc7, 0x1c, 0xc2, 0x57, 0xfd, 0x5d, 0xaf, 0xdd, 0xf0, 0x4d, 0xad, 0xb6, 0x70, 0x8c, 0x09, 0xd7, 0xbd, 0x53, 0x9e, 0xbc, 0x64, 0x78, 0x0d, 0xe0, 0x7d, 0xf9, 0xd7, 0x6f, 0xf9, 0x31, 0x71, 0xc5, 0xef, 0xfe, 0xaf, 0x50, 0x74, 0x3a, 0x62, 0x82, 0x4f, 0x72, 0x9d, 0x68, 0xf7, 0x12, 0xd9, 0x3a, 0x0e, 0xc9, 0x77, 0x86, 0x9d, 0xe8, 0x86, 0x78, 0x03, 0xc2, 0x1a, 0x97, 0xa8, 0xfc, 0xac, 0x41, 0x9f, 0x58, 0x5c, 0x33, 0xf0, 0x04, 0x11, 0xd6, 0x8d, 0x09, 0x5e, 0xb3, 0xee, 0x99, 0x89, 0x33, 0xdc, 0x76, 0x4a, 0xf5, 0x4a, 0xb7, 0x14, 0x95, 0x31, 0x7e, 0x0f, 0xf4, 0xac, 0x89, 0xdb, 0x24, 0x9b, 0x92, 0x08, 0x3b, 0xca, 0xd4, 0x87, 0x84, 0x65, 0xad, 0x8b, 0x77, 0xb7, 0x81, 0xec, 0x1b, 0xcd, 0x9f, 0x8f, 0x38, 0x65, 0xbe, 0xda, 0x7a, 0x78, 0x9a, 0xbf, 0x9a, 0xc7, 0x24, 0x97, 0x37, 0x6b, 0x56, 0x30, 0xc4, 0xa9, 0xe7, 0xd1, 0x15, 0x47, 0xe6, 0x8d, 0xce, 0x25, 0xc6, 0xc3, 0x2c, 0xb9, 0xa1, 0x65, 0xda, 0x26, 0x25, 0x5c, 0x66, 0x22, 0x95, 0x5d, 0xa4, 0xcb, 0x9c, 0x72, 0xd5, 0xd6, 0xd6, 0x02, 0xe9, 0xa2, 0x10, 0xe7, 0xf6, 0xbe, 0x10, 0x03, 0x1a, 0xee, 0xdb, 0x60, 0xc2, 0x0f, 0x09, 0x1c, 0x56, 0x48, 0x04, 0x72, 0x95, 0x5d, 0x88, 0x7d, 0x77, 0xe0, 0x22, 0x3d, 0xc9, 0x15, 0x3f, 0x9c, 0x12, 0x9f, 0xf2, 0xcc, 0xc7, 0xc5, 0xb8, 0x1c, 0x76, 0x26, 0x87, 0x61, 0x3b, 0xe2, 0x31, 0x22, 0x33, 0x4e, 0x3f, 0xb1, 0x7f, 0x2d, 0x31, 0xd7, 0x0f, 0x93, 0x1d, 0x91, 0xc2, 0x3a, 0xb1, 0xdb, 0xbe, 0xf3, 0x16, 0x5a, 0x64, 0x52, 0x3d, 0x33, 0x7b, 0x6c, 0xa9, 0xf2, 0x26, 0x69, 0xb7, 0xb8, 0xcd, 0x1d, 0xde, 0x8b, 0x88, 0xc5, 0xdc, 0x55, 0x05, 0x6f, 0x17, 0x61, 0x60, 0x44, 0xf8, 0x87, 0xba, 0xd2, 0xa3, 0x66, 0x00, 0xf5, 0x4d, 0xc1, 0x74, 0x6d, 0x51, 0x5b, 0x3d, 0x95, 0xbb, 0x89, 0xb5, 0xf5, 0x05, 0xf5, 0x67, 0x65, 0x2a, 0xc8, 0x86, 0xe6, 0x45, 0x10, 0x09, 0x81, 0xce, 0x19, 0x94, 0x57, 0xe1, 0xeb, 0xde, 0xa9, 0xda, 0x44, 0xd6, 0x33, 0xd5, 0xaf, 0x2c, 0x77, 0xa5, 0x2d, 0xe9, 0xa7, 0xce, 0xe5, 0xc2, 0x49, 0x30, 0x53, 0x04, 0x1d, 0xa9, 0xa3, 0xf4, 0x8e, 0x05, 0xf1, 0x8a, 0x7f, 0x0d, 0xc4, 0x0f, 0x80, 0x0f, 0x67, 0x29, 0x44, 0x17, 0x7d, 0x4d, 0x28, 0x20, 0xf2, 0x05, 0xcd, 0x33, 0x05, 0xac, 0x5d, 0x65, 0xbd, 0xe3, 0x5e, 0xdc, 0x08, 0xf4, 0x80, 0x11, 0x73, 0x39, 0x31, 0x15, 0xe1, 0x24, 0xd0, 0x4d, 0xd5, 0xf5, 0xa4, 0x02, 0x84, 0x34, 0x74, 0x25, 0x7c, 0x4e, 0x4a, 0x93, 0xa6, 0x84, 0x38, 0x88, 0x0a, 0x07, 0x68, 0xab, 0xa2, 0xe9, 0x06, 0x86, 0xfc, 0x17, 0x3b, 0x2d, 0xf6, 0xef, 0x99, 0x41, 0xdc, 0x35, 0xb2, 0xb2, 0x0a, 0x00, 0x11, 0xcb, 0x26, 0x8a, 0xa1, 0xab, 0xe1, 0xea, 0x40, 0xb2, 0xa6, 0xf2, 0x6d, 0x19, 0x8a, 0xb1, 0x57, 0xe4, 0x27, 0xb5, 0x4e, 0x11, 0xa8, 0xc5, 0x39, 0x44, 0xca, 0x6b, 0x8c, 0xfa, 0x35, 0x19, 0xfa, 0x91, 0xfe, 0xb5, 0xc8, 0x5b, 0xc4, 0x42, 0x39, 0x8a, 0xfe, 0x4e, 0xd3, 0x8b, 0xcc, 0x80, 0xf0, 0x15, 0xa1, 0xa8, 0x61, 0x2a, 0xb2, 0x4a, 0x7c, 0xf3, 0x0e, 0xda, 0x91, 0xfa, 0x30, 0xdf, 0x21, 0xda, 0x43, 0x42, 0x21, 0xf4, 0x5a, 0x89, 0xcc, 0xef, 0xda, 0xb3, 0xae, 0x16, 0xc8, 0x98, 0xc9, 0x47, 0xc5, 0xf0, 0x6a, 0xac, 0xaf, 0x16, 0x6b, 0x44, 0x77, 0xa6, 0x89, 0x87, 0x3d, 0x21, 0xb8, 0x2e, 0x4a, 0xc2, 0x53, 0xe7, 0xdc, 0x2f, 0x93, 0x35, 0x37, 0xf7, 0xf3, 0x83, 0xdf, 0xa2, 0x56, 0x14, 0x43, 0x4e, 0x0e, 0x8d, 0x09, 0x5c, 0x6c, 0x5b, 0x63, 0xbc, 0xe8, 0xa1, 0x55, 0x0b, 0x66, 0xdd, 0x44, 0x2e, 0x16, 0x08, 0xf2, 0x96, 0x28, 0x71, 0x28, 0xa3, 0xe8, 0xb8, 0x4a, 0xfb, 0x22, 0x00, 0xbe, 0x01, 0x79, 0x2d, 0x82, 0x88, 0xb7, 0xfa, 0xcb, 0xe0, 0xdf, 0x22, 0xe0, 0x8b, 0xe1, 0x78, 0xcb, 0x08, 0xd1, 0x2e, 0x93, 0xf7, 0x28, 0xbc, 0x43, 0x27, 0x57, 0x55, 0x2c, 0x11, 0x81, 0xe0, 0xc6, 0xd6, 0x28, 0x60, 0x80, 0x66, 0x01, 0x68, 0x47, 0x7b, 0x7b, 0x83, 0xa3, 0x0c, 0x34, 0x9b, 0x3b, 0x54, 0xc6, 0x59, 0x38, 0x56, 0x22, 0xc9, 0xa1, 0xc6, 0x7f, 0xc5, 0x1a, 0x77, 0xec, 0x13, 0x90, 0x2d, 0x56, 0x16, 0x59, 0x6a, 0x26, 0x48, 0x81, 0x3a, 0x25, 0xfe, 0xd2, 0x08, 0x7f, 0x04, 0x10, 0xd1, 0x48, 0xa1, 0x08, 0x26, 0xb8, 0x6f, 0x5a, 0xbe, 0x93, 0x77, 0x6f, 0x9e, 0x8c, 0x2d, 0x5f, 0xf6, 0xf0, 0xa0, 0x72, 0xa3, 0xad, 0x5e, 0x8b, 0x7a, 0x84, 0x47, 0x49, 0xcc, 0x72, 0xf8, 0x5e, 0x5a, 0xdf, 0x62, 0x11, 0x5c, 0x25, 0x8d, 0xde, 0x32, 0x00, 0x3b, 0x0f, 0xe1, 0x26, 0x7b, 0x93, 0xa9, 0xf1, 0x4c, 0x1b, 0x7e, 0x98, 0x14, 0xc8, 0xbf, 0x79, 0x34, 0x93, 0x6c, 0x9f, 0x8c, 0x9d, 0x98, 0x17, 0x40, 0x14, 0x9f, 0x9d, 0x09, 0x07, 0xd2, 0x3f, 0x9f, 0x25, 0xa9, 0x4c, 0x3b, 0x3f, 0x5c, 0x94, 0x7b, 0xa8, 0x49, 0x29, 0x6c, 0xd9, 0xc2, 0x26, 0xe7, 0xce, 0x90, 0x20, 0x96, 0x5e, 0xd8, 0x87, 0x2e, 0x02, 0xf8, 0xc4, 0xf2, 0xf2, 0x3e, 0x0b, 0x9f, 0x1c, 0xcc, 0x4b, 0x3f, 0x7d, 0x56, 0x41, 0xb3, 0x47, 0xbb, 0xd7, 0x52, 0xc4, 0xdd, 0x0f, 0xeb, 0x4c, 0xd8, 0x84, 0x3e, 0xdd, 0xd9, 0xad, 0xc3, 0xf2, 0xdc, 0xaa, 0xd9, 0xfd, 0x19, 0xdc, 0xf1, 0x02, 0xb4, 0x6e, 0xdd, 0xe5, 0x99, 0xc2, 0x09, 0xbc, 0x98, 0x6a, 0x48, 0xcb, 0x49, 0x89, 0x8c, 0xd2, 0x59, 0x4e, 0x55, 0xbc, 0x6b, 0xee, 0x1d, 0xf4, 0x30, 0x91, 0x67, 0x27, 0x05, 0x55, 0x97, 0xb9, 0x68, 0x52, 0x82, 0xc3, 0x57, 0xd1, 0xf7, 0xee, 0xd4, 0x80, 0x12, 0xd1, 0x6c, 0xa7, 0x03, 0xc9, 0x10, 0x63, 0xce, 0x20, 0x63, 0x05, 0x03, 0x77, 0xdc, 0xe2, 0xe3, 0x37, 0x6d, 0x43, 0x47, 0xd6, 0x83, 0xea, 0x09, 0xdf, 0xd3, 0x57, 0x86, 0x86, 0xee, 0x86, 0x01, 0x6b, 0x5e, 0xe9, 0x24, 0x5e, 0x6f, 0x8a, 0xba, 0xda, 0x79, 0x44, 0x0d, 0x42, 0xe6, 0x69, 0x43, 0xb3, 0x5d, 0xc4, 0x9a, 0x2d, 0x87, 0x5e, 0xb2, 0x4c, 0xb4, 0xe8, 0x06, 0x02, 0x73, 0x3f, 0x8e, 0x15, 0x37, 0x68, 0x8c, 0x97, 0x14, 0xb2, 0xbc, 0x28, 0x4c, 0xb9, 0x09, 0x4b, 0x7b, 0xae, 0xf6, 0x08, 0xd1, 0xf2, 0x2f, 0x0b, 0x98, 0x2a, 0xb0, 0xeb, 0xed, 0xf8, 0x71, 0xd9, 0x5b, 0xdf, 0xdd, 0x87, 0x9b, 0xdf, 0x2a, 0x62, 0x8a, 0x00, 0x4b, 0x03, 0xae, 0x37, 0x38, 0xf1, 0x94, 0x59, 0x1b, 0x2e, 0x79, 0x56, 0x8d, 0xcc, 0x36, 0xe5, 0xcc, 0xec, 0x50, 0x38, 0x37, 0x15, 0xb8, 0x84, 0x07, 0x5a, 0x6e, 0xb1, 0xda, 0xa1, 0x52, 0xe6, 0x32, 0x08, 0x4a, 0x91, 0xce, 0x0e, 0x2e, 0x78, 0xe2, 0xcc, 0xac, 0xb3, 0xc1, 0xeb, 0xce, 0x3e, 0x51, 0x21, 0x18, 0x5d, 0x96, 0xf9, 0x31, 0x18, 0xab, 0x54, 0x67, 0x61, 0xa5, 0x5c, 0x5b, 0x4b, 0x27, 0xa4, 0x61, 0xe1, 0x82, 0x58, 0xfd, 0xa7, 0x18, 0x1c, 0x8e, 0xdc, 0xbe, 0xd3, 0xdf, 0x3f, 0xdf, 0x2e, 0xbf, 0xe5, 0x5c, 0xb1, 0x5a, 0xcf, 0x49, 0xac, 0x66, 0x3b, 0x90, 0xf0, 0xba, 0x41, 0x3f, 0x6d, 0x4f, 0x1c, 0x60, 0x27, 0x76, 0xfe, 0x1a, 0xbf, 0x32, 0x8c, 0x81, 0xec, 0xe0, 0x56, 0xd3, 0x8b, 0xac, 0x17, 0x67, 0x7e, 0x8b, 0x22, 0x4a, 0x61, 0x38, 0xf8, 0xa6, 0x37, 0xcb, 0x1f, 0x29, 0x80, 0x0b, 0x48, 0x19, 0xff, 0x9f, 0x83, 0x48, 0x83, 0x62, 0x33, 0xa6, 0xd8, 0xac, 0xf7, 0xe8, 0x10, 0x01, 0xea, 0x6d, 0x0b, 0x2f, 0x2d, 0x57, 0x02, 0x89, 0xcc, 0x07, 0x2a, 0x79, 0x6d, 0x22, 0x1f, 0x1c, 0xb8, 0xe8, 0x89, 0xcb, 0x24, 0x3a, 0x90, 0x30, 0xd2, 0xc1, 0xf1, 0x6f, 0x26, 0xea, 0x91, 0xfb, 0xcb, 0xa1, 0xb1, 0x0f, 0xd9, 0x22, 0xd9, 0x53, 0x14, 0xa2, 0x12, 0x8c, 0x0f, 0x9a, 0x38, 0x53, 0x34, 0x43, 0x54, 0x77, 0x6c, 0x60, 0xc0, 0xca, 0xf2, 0x88, 0xc0, 0xba, 0x96, 0x15, 0xf7, 0xd9, 0x11, 0x44, 0x5b, 0x57, 0x9b, 0xc9, 0x72, 0x6d, 0x93, 0xda, 0xc6, 0xaa, 0xb2, 0x01, 0x0b, 0x0e, 0x44, 0x0d, 0xb4, 0x1e, 0xff, 0xf4, 0x7e, 0x3e, 0xe3, 0x1a, 0xa1, 0xae, 0x5c, 0x02, 0xe4, 0x89, 0x29, 0x56, 0x4d, 0xc8, 0x13, 0x43, 0x56, 0x72, 0x65, 0xda, 0x4f, 0x2a, 0xb4, 0xb9, 0x7a, 0x6a, 0x93, 0x1c, 0x23, 0x6d, 0x77, 0x19, 0x82, 0x26, 0x2e, 0xc0, 0x5c, 0x9c, 0x5c, 0x2f, 0xeb, 0x19, 0x19, 0x7a, 0x62, 0x3b, 0x0f, 0xa5, 0xde, 0xb6, 0x56, 0x68, 0xb3, 0x06, 0x01, 0x81, 0x1d, 0x89, 0x23, 0xd5, 0xa4, 0xa1, 0xd6, 0xd2, 0x81, 0x0d, 0x0e, 0xcf, 0xb7, 0x5d, 0xe8, 0x57, 0x78, 0xbc, 0x09, 0x5d, 0xd4, 0x0f, 0x91, 0x66, 0xad, 0x4f, 0xc3, 0x0b, 0x37, 0x9d, 0x7d, 0xb5, 0x3d, 0xc5, 0x66, 0x63, 0x49, 0x40, 0xa8, 0x85, 0xcb, 0x4b, 0x74, 0xec, 0x4d, 0xc9, 0xee, 0x0f, 0xc4, 0xf8, 0x73, 0x29, 0xf2, 0x5e, 0xa0, 0x84, 0x95, 0x7f, 0x98, 0x65, 0x9f, 0x20, 0x1d, 0x12, 0x60, 0x9a, 0xec, 0x33, 0xf5, 0x66, 0x33, 0x32, 0xa8, 0x98, 0x7c, 0x91, 0xf5, 0x9f, 0x63, 0xeb, 0xf1, 0x77, 0x1c, 0xc6, 0x0f, 0x15, 0xc7, 0x18, 0xf8, 0x38, 0xe1, 0x87, 0x2b, 0xaf, 0x89, 0x1d, 0xb4, 0xe2, 0x76, 0x35, 0x51, 0x02, 0xf6, 0x4e, 0xd5, 0xd3, 0xda, 0xbe, 0x03, 0x3a, 0x0d, 0xaa, 0xf0, 0xe9, 0x0e, 0x3a, 0xaa, 0x5f, 0x8f, 0x56, 0x2e, 0x08, 0x10, 0x3c, 0x37, 0x81, 0xae, 0xfc, 0xae, 0x21, 0x44, 0x92, 0x88, 0x4f, 0xd8, 0x98, 0xf7, 0x08, 0x1c, 0x37, 0x62, 0xb5, 0xad, 0x4b, 0x03, 0x63, 0x4a, 0x05, 0x65, 0x31, 0xf8, 0xd6, 0x3c, 0x26, 0x6c, 0xf4, 0x26, 0x08, 0xaa, 0xfe, 0x5c, 0xb7, 0x62, 0x73, 0x1e, 0xc8, 0x48, 0xcf, 0x04, 0xb8, 0x16, 0x7c, 0x27, 0x91, 0x45, 0x7a, 0x88, 0xb8, 0x0f, 0xa3, 0x7f, 0x00, 0x4f, 0xf3, 0xc7, 0x09, 0xef, 0x99, 0xae, 0x05, 0x93, 0x6a, 0xd5, 0xd7, 0x2f, 0x94, 0xc8, 0xb5, 0x5a, 0x87, 0x7b, 0xaf, 0x7c, 0x62, 0xe5, 0x85, 0x3a, 0x95, 0x58, 0xeb, 0xf0, 0x77, 0x6b, 0x97, 0x4a, 0xc7, 0xb8, 0xc8, 0x36, 0x0e, 0x8f, 0xf5, 0x61, 0x82, 0xcf, 0x91, 0x4b, 0xd5, 0x76, 0xab, 0xb6, 0x00, 0x92, 0xcd, 0x6c, 0x71, 0x69, 0x7a, 0x10, 0x9d, 0x0b, 0xa8, 0xff, 0x34, 0x4c, 0x7f, 0xab, 0x4e, 0xc2, 0x4f, 0xba, 0x4b, 0xe9, 0xe2, 0xc5, 0x8e, 0x18, 0xa1, 0x8f, 0x91, 0x3e, 0x38, 0x8b, 0x9a, 0xac, 0xe1, 0x4f, 0x67, 0xf7, 0x23, 0xab, 0x4f, 0xa7, 0x47, 0x72, 0xed, 0xfc, 0x90, 0xa0, 0x3a, 0x67, 0x69, 0x7d, 0xea, 0xa5, 0x70, 0x3b, 0x65, 0xc5, 0xb7, 0xa4, 0x30, 0x19, 0x8e, 0x10, 0x70, 0xe3, 0x8a, 0x79, 0xf6, 0xae, 0xa0, 0x51, 0x17, 0xfa, 0xdc, 0x93, 0xe7, 0x38, 0x81, 0xd0, 0xe2, 0xed, 0xf6, 0x23, 0x5e, 0xb4, 0x73, 0x09, 0x89, 0xf7, 0x9e, 0x18, 0x0e, 0x42, 0xdf, 0x74, 0x36, 0x9d, 0x28, 0xd6, 0xea, 0x8f, 0x6a, 0xfa, 0x03, 0x44, 0x1d, 0x0b, 0x70, 0x72, 0xea, 0x2c, 0x50, 0x8f, 0xcb, 0x58, 0x14, 0x7e, 0x3d, 0x4a, 0x2a, 0xbf, 0x1e, 0xf8, 0x94, 0xa8, 0x8b, 0xd9, 0xcd, 0x92, 0x19, 0xe0, 0x01, 0x49, 0xa2, 0x55, 0x4e, 0x28, 0xb0, 0x64, 0xf0, 0x7f, 0x4b, 0x2f, 0x5d, 0x96, 0xa7, 0x40, 0x54, 0x56, 0x1b, 0xe1, 0x13, 0x30, 0xa0, 0x48, 0x2c, 0xb6, 0x30, 0xe9, 0xc8, 0x0b, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0xc3, 0xa9, 0xb9, 0x73, 0x50, 0xe3, 0x6a, 0x08, 0xb4, 0x2e, 0x64, 0x6d, 0x05, 0x30, 0x00, 0x00, 0x00, 0x77, 0x82, 0x23, 0x5c, 0x1a, 0x92, 0xad, 0x54, 0xb4, 0x09, 0xb7, 0x88, 0x9e, 0x52, 0x6e, 0x19, 0xc5, 0xfa, 0xfe, 0x28, 0x20, 0x5a, 0x4d, 0xcf, 0xd3, 0xbc, 0x46, 0xbe, 0x0e, 0x52, 0xef, 0x4d, 0xad, 0x71, 0xdb, 0x51, 0x79, 0x70, 0x29, 0xd9, 0x13, 0x91, 0x58, 0x41, 0x68, 0x62, 0x25, 0x52, 0xf9, 0x8e, 0xc9, 0xca, 0x46, 0x94, 0x70, 0xc4, 0xa4, 0x78, 0xad, 0xc5, 0xd4, 0x07, 0x07, 0x0f, 0xd5, 0x15, 0x7b, 0x4e, 0x8d, 0x13, 0x9f, 0x26, 0x02, 0xb1, 0x1a, 0x95, 0xc9, 0x89, 0xbc, 0x38, 0xbb, 0x3f, 0xb3, 0x41, 0x8e, 0xc9, 0x76, 0x7e, 0x97, 0xeb, 0xb3, 0xcc, 0x4a, 0x3b, 0xf9, 0x97, 0xbc, 0x7e, 0xd3, 0x9e, 0x6e, 0x0c, 0x76, 0x85, 0x6f, 0xff, 0x4a, 0x56, 0x67, 0xe7, 0x51, 0xc0, 0x91, 0xa0, 0xc9, 0xa8, 0xef, 0xe0, 0x98, 0x69, 0x41, 0x6b, 0x86, 0x16, 0x94, 0x2a, 0x37, 0x31, 0xfa, 0x54, 0x7d, 0x73, 0x6f, 0x81, 0xff, 0xdd, 0xc3, 0x8c, 0x89, 0xa6, 0xb7, 0x8f, 0xbf, 0x1d, 0x7c, 0xab, 0x18, 0xef, 0x49, 0x0b, 0xa1, 0x59, 0x42, 0x2d, 0x51, 0xec, 0x36, 0xdd, 0xb9, 0x24, 0x48, 0xee, 0xa4, 0xbb, 0x42, 0xfd, 0x5a, 0x27, 0x41, 0x70, 0xb7, 0xe5, 0x2c, 0xb7, 0x68, 0x62, 0x6c, 0x9c, 0xe0, 0x1c, 0xf0, 0x59, 0x1b, 0xd7, 0x8f, 0x0e, 0xc4, 0x9f, 0x55, 0x23, 0x3c, 0x8d, 0x4d, 0x6a, 0xbe, 0x10, 0xb4, 0xa9, 0x18, 0x9c, 0xd1, 0x23, 0x5f, 0xb8, 0x8f, 0xc0, 0x8e, 0x65, 0xec, 0x2c, 0x39, 0x10, 0x31, 0x0e, 0xb9, 0x5a, 0xad, 0xeb, 0x26, 0x1a, 0x62, 0xc1, 0xd4, 0xa2, 0xdf, 0x09, 0x2b, 0xc6, 0x8b, 0x0c, 0xa3, 0xf8, 0x0f, 0x21, 0xc3, 0x9a, 0x7f, 0x4d, 0xc3, 0xe8, 0xc9, 0xa5, 0x46, 0x67, 0xfc, 0x2d, 0x76, 0x85, 0xa4, 0x8a, 0xa7, 0x49, 0xed, 0xf8, 0x45, 0xb4, 0xab, 0xbe, 0x48, 0xff, 0xe1, 0x4c, 0x25, 0x0a, 0x2d, 0x85, 0x8b, 0x75, 0x1a, 0x7e, 0x21, 0xf6, 0xf5, 0x91, 0x12, 0xae, 0xc9, 0x9c, 0x27, 0x4e, 0x8f, 0x29, 0xd7, 0x01, 0xa1, 0x3c, 0x32, 0xf4, 0xda, 0xf7, 0x25, 0x24, 0xfa, 0xfb, 0x9c, 0xa5, 0x96, 0xd2, 0x99, 0x9c, 0xcf, 0xba, 0x12, 0x40, 0x93, 0x9b, 0xf3, 0x40, 0xf6, 0x86, 0xaf, 0xcd, 0xe7, 0x53, 0xa4, 0x7d, 0x02, 0xeb, 0x91, 0x1b, 0x8c, 0xbc, 0xe4, 0xfb, 0x7e, 0x3e, 0x40, 0x14, 0x0d, 0xe2, 0x79, 0x97, 0x18, 0x1c, 0x7d, 0x58, 0x9d, 0xe2, 0x9e, 0x83, 0x81, 0xf7, 0x23, 0x68, 0xae, 0xcd, 0x2f, 0x1f, 0x54, 0x8e, 0xbb, 0x4a, 0x41, 0xc9, 0x19, 0xfd, 0x93, 0xe9, 0x65, 0x4f, 0x2d, 0xaa, 0x1e, 0x04, 0x67, 0x99, 0xfa, 0xe4, 0x58, 0xe2, 0x03, 0x9d, 0xd0, 0x76, 0x2b, 0x69, 0x15, 0x23, 0x19, 0x13, 0x8d, 0xa5, 0x61, 0x08, 0x3b, 0x91, 0xe5, 0x64, 0xd1, 0xa8, 0xb7, 0xfb, 0xa5, 0xc6, 0xac, 0xd8, 0x2a, 0xf1, 0xbb, 0xb2, 0x40, 0x5a, 0x40, 0xab, 0x57, 0xcb, 0x96, 0x55, 0x41, 0x04, 0x90, 0x1f, 0x40, 0xe8, 0xc6, 0x05, 0xd0, 0x78, 0x16, 0x81, 0x6b, 0xe3, 0xbb, 0x8a, 0xc8, 0xd4, 0x61, 0x7b, 0x16, 0xef, 0x09, 0xf5, 0x38, 0x2f, 0xf4, 0xa0, 0x6f, 0x4d, 0x0b, 0x04, 0x47, 0xdd, 0xf9, 0x7f, 0x24, 0x6a, 0x21, 0x18, 0x2f, 0xcf, 0xc9, 0xba, 0x22, 0x11, 0xdb, 0x45, 0xbd, 0xe3, 0x20, 0x48, 0xac, 0xa2, 0x83, 0x5c, 0xa4, 0xbd, 0xb7, 0xc3, 0x08, 0xd0, 0x90, 0xb1, 0x6e, 0xce, 0x1c, 0x7f, 0xc1, 0xff, 0xed, 0x5d, 0xbd, 0x38, 0x26, 0x39, 0x97, 0x22, 0x26, 0xf4, 0x71, 0x80, 0xd6, 0xae, 0xf3, 0x17, 0x98, 0x30, 0x36, 0xf8, 0x53, 0xf3, 0x66, 0x93, 0xf3, 0x7c, 0x3d, 0x4b, 0x6d, 0x8b, 0xb0, 0xa1, 0x52, 0xb5, 0x33, 0x4f, 0x71, 0x93, 0xfa, 0x11, 0xd2, 0xb1, 0x70, 0x92, 0x20, 0x19, 0x83, 0x8f, 0xb5, 0xc0, 0xa0, 0x94, 0x5c, 0xcf, 0x85, 0xcd, 0x46, 0xef, 0x79, 0xb9, 0xd1, 0xdf, 0xaa, 0x87, 0x71, 0x8e, 0x8c, 0x0b, 0x50, 0x0f, 0x8f, 0x1d, 0x78, 0x1f, 0x89, 0x44, 0x37, 0x67, 0xe1, 0x9d, 0xb5, 0x88, 0xee, 0x07, 0xe6, 0x59, 0x87, 0xcf, 0x49, 0xce, 0xab, 0x3f, 0x59, 0x83, 0x4e, 0xe1, 0x49, 0x97, 0x05, 0x67, 0x03, 0xff, 0x1e, 0x4a, 0x00, 0xcc, 0x1d, 0x3c, 0x6f, 0x14, 0x70, 0xa7, 0x1d, 0xc0, 0x0c, 0x02, 0x49, 0x62, 0xb6, 0x18, 0xaf, 0x08, 0xb4, 0xf7, 0xe8, 0xfe, 0x1c, 0x1c, 0x07, 0x1e, 0x13, 0x4d, 0xbd, 0x5d, 0x98, 0x02, 0x31, 0xb0, 0x88, 0x69, 0x14, 0x0e, 0x75, 0xa0, 0xc0, 0xb8, 0x0e, 0xe2, 0xe7, 0x43, 0x43, 0x80, 0x9b, 0x3a, 0x6e, 0xde, 0x33, 0xd7, 0xca, 0xba, 0x6e, 0x11, 0x56, 0x87, 0x71, 0x86, 0xa6, 0xf4, 0xf7, 0x43, 0xfe, 0x88, 0x76, 0xed, 0x65, 0x62, 0x2c, 0xc0, 0x93, 0xe5, 0x67, 0xf9, 0x03, 0xf3, 0x47, 0x14, 0xa3, 0xd6, 0xe5, 0x3e, 0x4d, 0xc4, 0x17, 0x4f, 0x20, 0xaf, 0x3e, 0x4a, 0x40, 0x4c, 0x1b, 0x77, 0x5b, 0x02, 0xac, 0x9e, 0xd6, 0x95, 0xf0, 0xf7, 0x49, 0xc8, 0x6e, 0x9b, 0x6d, 0x0f, 0x30, 0x48, 0x8a, 0x56, 0xcd, 0xd1, 0x73, 0x04, 0x97, 0x5a, 0x89, 0x17, 0x80, 0x0f, 0x60, 0x1a, 0x34, 0x77, 0x83, 0x57, 0x95, 0xda, 0x35, 0xda, 0x69, 0xef, 0xb8, 0x01, 0x31, 0x47, 0x4f, 0x19, 0x6f, 0x46, 0x0c, 0x3b, 0x8c, 0xe1, 0xb0, 0x47, 0x38, 0xe8, 0x72, 0x7d, 0x87, 0x14, 0x48, 0x46, 0x39, 0x35, 0x22, 0xc5, 0x53, 0x3f, 0x4e, 0x98, 0x81, 0xb5, 0x0c, 0x29, 0x5e, 0x0e, 0x28, 0x4e, 0xe1, 0x33, 0xb1, 0x8d, 0x22, 0x65, 0x24, 0x38, 0x2d, 0xac, 0xdf, 0xbc, 0x76, 0x67, 0xfe, 0x91, 0xae, 0x6f, 0x45, 0x1c, 0xce, 0xa8, 0xdc, 0xde, 0x96, 0x53, 0x49, 0xf9, 0x2b, 0x1a, 0xf9, 0x89, 0xe3, 0xff, 0xcc, 0x83, 0xbf, 0x09, 0xa5, 0x88, 0x26, 0xb4, 0x96, 0xd5, 0x1f, 0x12, 0x26, 0x5a, 0x5a, 0xd3, 0x82, 0xf1, 0xe0, 0xe3, 0x97, 0xf7, 0xc7, 0x5c, 0x1f, 0x80, 0x8b, 0x8e, 0x03, 0x7c, 0x51, 0x15, 0x10, 0x79, 0x36, 0x84, 0x3e, 0x58, 0xc7, 0xb5, 0x37, 0xa9, 0x8d, 0xb3, 0x1a, 0x32, 0xac, 0xc4, 0x49, 0xe1, 0xaa, 0xec, 0xd2, 0xe5, 0x8d, 0x26, 0x5b, 0x07, 0x4a, 0xb0, 0x09, 0xb8, 0x7a, 0x1b, 0x0e, 0xaf, 0x4f, 0x41, 0x4f, 0x8f, 0xf8, 0x70, 0x77, 0x36, 0x0c, 0x4f, 0x8c, 0xeb, 0xda, 0x67, 0x6a, 0xa8, 0x31, 0x01, 0x00, 0x08, 0x31, 0x72, 0x9b, 0xde, 0x2b, 0x4b, 0x2d, 0xad, 0x3c, 0x53, 0x55, 0x12, 0xb8, 0x24, 0xd8, 0xb4, 0xc1, 0xed, 0xad, 0x56, 0x5e, 0x93, 0x69, 0x3e, 0xf3, 0x4d, 0xf6, 0x60, 0xa0, 0x8e, 0x3f, 0xb7, 0x43, 0x30, 0x2a, 0xbd, 0x80, 0xf4, 0x5d, 0x2d, 0xb6, 0xa2, 0xab, 0x10, 0x06, 0xd5, 0xa7, 0xac, 0xb0, 0x7e, 0x41, 0xb9, 0x4b, 0xa5, 0x94, 0x5f, 0x90, 0xa1, 0x08, 0xd6, 0x38, 0x0a, 0x2d, 0xf9, 0xed, 0xa0, 0x94, 0x7f, 0xbe, 0xd4, 0xce, 0x04, 0xaf, 0xfa, 0xdb, 0x91, 0xb0, 0xc0, 0xa9, 0xf0, 0x6f, 0x30, 0x10, 0xab, 0x6c, 0xed, 0xa5, 0x50, 0x26, 0x12, 0x1f, 0x53, 0x0c, 0xc0, 0x0c, 0x29, 0x40, 0x4b, 0xff, 0x7e, 0x26, 0x42, 0x62, 0xfb, 0xe8, 0x9c, 0xf2, 0x27, 0xbb, 0xf8, 0xf3, 0xb2, 0x93, 0x37, 0x02, 0x32, 0x88, 0x24, 0x3d, 0x5f, 0x8a, 0x04, 0x8d, 0x9b, 0x08, 0x53, 0x3e, 0x30, 0xba, 0x32, 0x25, 0x0a, 0x3f, 0x81, 0x1e, 0xf2, 0x16, 0x74, 0xf0, 0x37, 0x4d, 0x69, 0x49, 0x87, 0xa0, 0x77, 0xb5, 0x87, 0xbf, 0x5d, 0x72, 0x7b, 0xf0, 0x61, 0x73, 0xa8, 0xb3, 0x18, 0x09, 0xce, 0x29, 0x78, 0x8e, 0x48, 0xdf, 0x2a, 0xd7, 0x23, 0xa9, 0xaf, 0x46, 0x43, 0xbb, 0x9b, 0xdb, 0x06, 0x2d, 0xaf, 0x6a, 0x16, 0x15, 0x74, 0x24, 0xe5, 0xa6, 0xc8, 0x39, 0x47, 0xb3, 0x43, 0x62, 0xc9, 0xdb, 0x75, 0xc1, 0x06, 0xad, 0xee, 0x08, 0x47, 0xf5, 0x77, 0xae, 0x3d, 0x50, 0x96, 0x4f, 0x24, 0x20, 0x6b, 0x52, 0x03, 0x80, 0x72, 0x2b, 0xed, 0xdc, 0x9f, 0x24, 0xda, 0xe2, 0xba, 0x81, 0x87, 0xa9, 0x7d, 0xfc, 0x06, 0xd4, 0x23, 0xb5, 0x0a, 0x52, 0x94, 0xc7, 0x63, 0xe7, 0x51, 0xad, 0x1e, 0xac, 0xa6, 0xbe, 0x8f, 0xd2, 0x9d, 0xe5, 0xb8, 0xa3, 0xdf, 0x99, 0x7a, 0xe7, 0x5e, 0x06, 0x38, 0x70, 0x5b, 0x4d, 0x27, 0x03, 0x2c, 0xa8, 0x0a, 0x3f, 0x93, 0xbb, 0x38, 0x7e, 0xae, 0xab, 0x9a, 0x7b, 0xd2, 0xc9, 0x8b, 0x53, 0xee, 0xcb, 0x80, 0x6f, 0x1d, 0x83, 0xdd, 0x81, 0xb8, 0xa0, 0xb9, 0xb6, 0xdc, 0x5e, 0xf1, 0x6b, 0x54, 0x0d, 0x32, 0xd7, 0xb0, 0xd5, 0x2b, 0xa2, 0xe5, 0xf7, 0x18, 0xa5, 0x28, 0x54, 0xb6, 0x7c, 0xea, 0x47, 0x8c, 0x7a, 0xb5, 0xce, 0x9d, 0x0f, 0xd8, 0x89, 0xfa, 0xd7, 0x21, 0xd8, 0x4b, 0x14, 0x5a, 0x86, 0x32, 0xd1, 0x40, 0x21, 0x75, 0x4b, 0xbf, 0x1c, 0x66, 0x54, 0x84, 0xb2, 0x1f, 0xd4, 0x32, 0x06, 0x7a, 0x52, 0x1f, 0x7d, 0xd8, 0x59, 0x99, 0x86, 0xb9, 0x6c, 0x26, 0xec, 0x5e, 0x74, 0x38, 0x5d, 0xb9, 0x38, 0xdd, 0xf6, 0x9c, 0x28, 0xbe, 0xb6, 0x87, 0xb7, 0xaf, 0x5b, 0x47, 0x7a, 0xbb, 0x79, 0x55, 0x53, 0x9c, 0xc1, 0x74, 0x73, 0x01, 0xde, 0xdf, 0xae, 0x0f, 0x63, 0xb2, 0xab, 0xc6, 0x01, 0x63, 0xea, 0xf2, 0xb5, 0x9a, 0x57, 0x2b, 0x62, 0x8b, 0x35, 0x21, 0x4d, 0xbc, 0x06, 0xb8, 0x7c, 0x31, 0x98, 0xc4, 0xbb, 0xd5, 0x83, 0x45, 0xd8, 0xc3, 0xa9, 0x2b, 0xab, 0x3e, 0x10, 0xfc, 0x60, 0x63, 0x6f, 0xbb, 0xbf, 0x90, 0x54, 0xe8, 0x63, 0x3f, 0x78, 0xf8, 0x8b, 0xa2, 0xa0, 0x75, 0x26, 0x21, 0x8d, 0xa4, 0xb2, 0xde, 0xa8, 0xaf, 0xb3, 0x3b, 0x65, 0x90, 0xac, 0xba, 0xad, 0x37, 0xac, 0xaf, 0x6a, 0xc1, 0x0a, 0x36, 0x5d, 0x95, 0x86, 0x13, 0x15, 0xbc, 0xf9, 0x7b, 0xc6, 0x62, 0x27, 0x88, 0xdb, 0x50, 0x16, 0x1a, 0x16, 0xe0, 0x5c, 0x3c, 0x0a, 0xf7, 0x5c, 0x69, 0xb8, 0xfe, 0x2a, 0x3c, 0x54, 0x3c, 0x57, 0x7e, 0xb5, 0xbb, 0x30, 0x32, 0x23, 0x59, 0x98, 0x0f, 0x83, 0x81, 0x3c, 0x2c, 0xaa, 0xdf, 0x28, 0xf3, 0xa0, 0x97, 0x28, 0xee, 0xad, 0x9a, 0xb5, 0x22, 0x2a, 0x43, 0x99, 0x51, 0x08, 0x82, 0x04, 0x9d, 0x9f, 0x56, 0x77, 0xfd, 0x8f, 0xaa, 0xb9, 0x17, 0xfb, 0xd0, 0x3b, 0x20, 0xc6, 0x31, 0xf4, 0x4f, 0x8b, 0x5d, 0x4b, 0x7c, 0x5e, 0x87, 0x9b, 0x75, 0x21, 0x31, 0x98, 0x05, 0xa8, 0x4c, 0xac, 0xde, 0x2b, 0x10, 0x4a, 0xc9, 0x79, 0x75, 0xd2, 0xb1, 0xf4, 0x23, 0x10, 0x80, 0x99, 0x52, 0xa6, 0x9c, 0xcb, 0x70, 0x75, 0xa2, 0x7a, 0x8b, 0x12, 0x50, 0xa4, 0x2c, 0x00, 0xe0, 0x86, 0x39, 0xda, 0x57, 0x10, 0x80, 0xe6, 0x4a, 0xc7, 0x0a, 0x9e, 0x40, 0x53, 0x22, 0xf3, 0xca, 0x19, 0x7c, 0xa2, 0xb2, 0xab, 0xa8, 0x2c, 0x32, 0xd5, 0x5e, 0xce, 0x54, 0x67, 0x02, 0x66, 0xe1, 0x00, 0x01, 0x60, 0xf8, 0xa5, 0x58, 0xd9, 0x44, 0x9d, 0xa2, 0x26, 0x2b, 0xd0, 0x9c, 0x99, 0xf3, 0x0c, 0x33, 0x57, 0xc0, 0xcf, 0x83, 0x02, 0xdb, 0x8f, 0x06, 0xa9, 0x12, 0x4b, 0xa9, 0x35, 0x00, 0x14, 0xab, 0xd5, 0x9f, 0x66, 0x4c, 0x57, 0xb8, 0xfc, 0xa9, 0x47, 0xb4, 0xae, 0xd3, 0xaa, 0xc8, 0x39, 0x70, 0x82, 0x93, 0x7d, 0xfb, 0xa3, 0x67, 0x33, 0x44, 0x2b, 0xf1, 0x09, 0xa7, 0x24, 0x5a, 0x66, 0x64, 0x72, 0xa3, 0xb8, 0x29, 0x65, 0xfc, 0xbe, 0xfa, 0xc9, 0x6b, 0x8e, 0x3d, 0xcf, 0x0f, 0x6f, 0x75, 0x30, 0xcf, 0xa7, 0x92, 0x7c, 0x5a, 0xcd, 0x0f, 0xd1, 0xcb, 0xa2, 0x9f, 0x75, 0xb9, 0x59, 0x67, 0x54, 0x24, 0xc0, 0xc1, 0x64, 0x14, 0xfa, 0xee, 0x14, 0xb8, 0x80, 0x56, 0x66, 0x18, 0xc3, 0xb4, 0xd2, 0x74, 0x83, 0x28, 0x8d, 0xe8, 0x1a, 0xb7, 0x5f, 0x6a, 0xb6, 0x9e, 0x2e, 0x27, 0xdd, 0xa1, 0xfb, 0xac, 0xeb, 0x9a, 0x76, 0xc3, 0x61, 0xf2, 0x2b, 0x70, 0x26, 0xf5, 0x35, 0x6b, 0x68, 0x12, 0x20, 0x1c, 0xa5, 0x6b, 0x51, 0x8a, 0xcc, 0xb4, 0xf8, 0x33, 0xbf, 0x00, 0x01, 0x46, 0x2d, 0x31, 0xc3, 0xd5, 0x98, 0x8f, 0xc5, 0x05, 0x39, 0x77, 0xde, 0x18, 0x8d, 0x0b, 0x77, 0x5a, 0x6a, 0x68, 0xe0, 0x27, 0x99, 0xdd, 0x6f, 0x79, 0x2c, 0xcb, 0x00, 0x9b, 0x1b, 0xf3, 0xf5, 0x76, 0x7b, 0x7c, 0xc2, 0xea, 0xb9, 0x60, 0xcc, 0x03, 0xbb, 0x9c, 0xa7, 0xc6, 0xad, 0x78, 0x26, 0xac, 0xa8, 0xfb, 0xdf, 0xaa, 0x1e, 0xab, 0x6f, 0x6c, 0xe3, 0xd6, 0x8f, 0x94, 0x5c, 0x30, 0x82, 0xba, 0xc9, 0xe5, 0xd2, 0x80, 0xe1, 0x30, 0xb4, 0x30, 0x24, 0xae, 0xb4, 0x6b, 0xcd, 0xf7, 0x17, 0x68, 0x65, 0x26, 0x0c, 0xa2, 0x8b, 0x9d, 0x7c, 0x03, 0xf2, 0xcd, 0xec, 0xee }; +constexpr AccessUnit AC3_BEGIN_OF_AU_SPLIT_EXPECTED_AU = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0xa6, 0x64, 0x73, 0xed, 0x20, 0xed, 0x51, 0x95, 0x8a, 0xc7, 0xee, 0xd3, 0x6f, 0x52, 0x8c, 0x0b, 0xcd, 0xe2, 0x4f, 0x51 } }; + + +const std::vector ATRACX_SIZE_MISMATCH_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xd0, 0x00, 0xbf, 0xa6, 0x85, 0x42, 0x98, 0xc5, 0x19, 0x4a, 0xdc, 0xcf, 0x38, 0x32, 0x5d, 0x7d, 0x6f, 0x99, 0xb2, 0x66, 0x6a, 0x9c, 0xe6, 0x6d, 0xaa, 0x5a, 0xbc, 0xcb, 0x31, 0x5c, 0xc9, 0x53, 0x87, 0x6c, 0x69, 0x2d, 0x3e, 0x35, 0xe6, 0xba, 0x7b, 0x05, 0x66, 0xfe, 0xea, 0xfe, 0x76, 0x9e, 0x0e, 0x53, 0xfe, 0xdc, 0x52, 0x31, 0x7a, 0xea, 0x93, 0xab, 0x90, 0xba, 0x50, 0x17, 0xc9, 0xe8, 0xa4, 0xdc, 0x3a, 0x10, 0xdc, 0x91, 0xed, 0x0d, 0x95, 0xdd, 0x15, 0x21, 0x8e, 0xf3, 0xd3, 0xfc, 0x6b, 0xb5, 0xc6, 0xac, 0x63, 0x72, 0x78, 0x72, 0xfe, 0x04, 0xc3, 0x55, 0xa3, 0x65, 0x7e, 0x50, 0x83, 0xfe, 0x92, 0xdd, 0x47, 0xa0, 0x5b, 0x10, 0x8f, 0xb6, 0x30, 0xb6, 0xac, 0x5e, 0x9a, 0x0b, 0xb0, 0xea, 0x43, 0xcf, 0x4c, 0xc5, 0x64, 0xe1, 0x14, 0x58, 0x5e, 0x4a, 0x4b, 0xc5, 0x14, 0xf0, 0xe7, 0xbc, 0xeb, 0x10, 0xc5, 0xf1, 0x98, 0x25, 0xab, 0x77, 0x04, 0xb2, 0xaf, 0x2c, 0xa1, 0x7e, 0x3f, 0x59, 0x9d, 0x6d, 0xc0, 0x64, 0xea, 0x1d, 0x98, 0x8f, 0x00, 0xfd, 0xc5, 0x8e, 0x57, 0x38, 0x71, 0x64, 0x9f, 0xd7, 0x57, 0x7d, 0xc4, 0x6c, 0x0f, 0x15, 0x8d, 0x84, 0xaa, 0xf3, 0xef, 0xd2, 0x66, 0x5b, 0xc4, 0xf0, 0xcf, 0x6b, 0x89, 0xd5, 0x9b, 0x55, 0x87, 0x5d, 0x63, 0xf6, 0x24, 0x17, 0x28, 0x33, 0xb8, 0xcd, 0x78, 0x9f, 0x2c, 0x00, 0x91, 0xe1, 0x79, 0x45, 0xe6, 0x8b, 0x3e, 0xab, 0xf5, 0xc4, 0x98, 0x00, 0xb7, 0xc7, 0x2a, 0x34, 0x47, 0xd6, 0xd3, 0x29, 0xd2, 0x2b, 0x4c, 0x3f, 0x6e, 0x06, 0x43, 0x60, 0x60, 0x69, 0xf1, 0x1b, 0xd4, 0x3f, 0xb8, 0x47, 0x7f, 0x2f, 0xc9, 0x4b, 0x23, 0xdd, 0x83, 0x6e, 0x4f, 0x69, 0x21, 0xc4, 0x48, 0x98, 0x53, 0x81, 0xd9, 0xfa, 0xba, 0xb6, 0xf9, 0x38, 0x64, 0x27, 0x24, 0xc9, 0xcb, 0x5f, 0xd8, 0x4f, 0x97, 0x06, 0x8e, 0xce, 0xa2, 0x88, 0xe2, 0x8b, 0x4c, 0xb6, 0x76, 0x49, 0xf6, 0x0c, 0x2a, 0xab, 0x06, 0x59, 0xde, 0x51, 0x73, 0xd6, 0x33, 0xe7, 0x4a, 0x85, 0x4a, 0x46, 0x83, 0x4e, 0xe4, 0x73, 0x19, 0xc4, 0x6d, 0x3c, 0xea, 0x36, 0xb8, 0xdb, 0x0d, 0xc7, 0x67, 0xe3, 0x72, 0x52, 0x08, 0x9d, 0x88, 0xd4, 0xb2, 0xec, 0x24, 0x0b, 0x9f, 0x2a, 0x9c, 0x34, 0xf2, 0x20, 0x6d, 0x7a, 0xbd, 0x2e, 0x9c, 0x22, 0x52, 0x34, 0xc6, 0xa6, 0x7a, 0x88, 0x76, 0xe4, 0x7a, 0x6d, 0x23, 0xba, 0xd7, 0x12, 0x3f, 0xf3, 0x31, 0x8b, 0x6d, 0xd8, 0xdc, 0x23, 0xf1, 0x21, 0x45, 0xdb, 0x2b, 0x92, 0x3f, 0x52, 0xbc, 0xf6, 0x1f, 0x57, 0x91, 0x5c, 0xef, 0xde, 0x64, 0x60, 0xcb, 0xfc, 0x27, 0x54, 0x9b, 0xf3, 0x94, 0x6a, 0x88, 0xed, 0x7c, 0xaf, 0x0c, 0x01, 0xc6, 0xb6, 0xe1, 0x98, 0x61, 0xbd, 0x17, 0xd8, 0x41, 0xc3, 0x89, 0x81, 0x55, 0x01, 0x80, 0xb7, 0x39, 0x0e, 0x18, 0x46, 0xea, 0xdb, 0x41, 0x59, 0xc0, 0x89, 0x11, 0x49, 0x99, 0x37, 0xcb, 0x1f, 0xe5, 0x21, 0xbf, 0xa0, 0x4d, 0xf9, 0x48, 0x8d, 0xef, 0xb3, 0x48, 0x90, 0xa5, 0xa1, 0x6a, 0xd6, 0x2b, 0xf2, 0x7e, 0x55, 0x34, 0xa9, 0xe6, 0x44, 0x8d, 0x26, 0xf3, 0x4c, 0xe4, 0x2c, 0xe5, 0x95, 0xc4, 0x63, 0x1c, 0x8d, 0x44, 0x81, 0x6a, 0x11, 0x16, 0xa9, 0xe3, 0x26, 0x49, 0xe3, 0x24, 0x04, 0x5d, 0x2d, 0xe0, 0x50, 0xfd, 0x47, 0x4c, 0x47, 0xfa, 0x8a, 0xa8, 0xf5, 0x82, 0x45, 0xff, 0x13, 0xda, 0x71, 0x93, 0x3b, 0x1c, 0xcc, 0x04, 0x75, 0x17, 0x2f, 0x2e, 0x5d, 0x0f, 0xdd, 0x33, 0xfa, 0x9c, 0xfa, 0x50, 0x9e, 0xa8, 0x29, 0x84, 0xee, 0x9c, 0xb2, 0xb2, 0xc4, 0xa9, 0xc0, 0x85, 0xb1, 0x42, 0x54, 0x4c, 0xd2, 0x62, 0x0d, 0xa4, 0xdf, 0xb3, 0xb7, 0x70, 0xe4, 0x9e, 0x8e, 0xa1, 0x9d, 0x4c, 0x1d, 0xdd, 0xc5, 0xfa, 0x8e, 0x67, 0x06, 0x17, 0x7d, 0xc6, 0xef, 0xb4, 0x5c, 0x1b, 0xac, 0xfd, 0xf7, 0x89, 0x36, 0xd0, 0x45, 0xf5, 0x95, 0xaa, 0xad, 0x23, 0x61, 0x4f, 0xd5, 0xdb, 0x52, 0x83, 0xa0, 0xb3, 0xba, 0xd1, 0x66, 0x0d, 0xe0, 0xa2, 0xb9, 0xd6, 0x73, 0xfa, 0xed, 0xe6, 0x96, 0xaf, 0x8b, 0x27, 0xf9, 0xe9, 0x24, 0xcb, 0x18, 0xc2, 0xa3, 0x41, 0x70, 0x89, 0xe9, 0x99, 0xb4, 0x58, 0x53, 0x80, 0x79, 0x55, 0x60, 0x88, 0x1e, 0xd7, 0x5d, 0x06, 0x5d, 0x67, 0x16, 0x48, 0xef, 0x25, 0xff, 0x76, 0x1d, 0x76, 0x94, 0x73, 0xf4, 0x48, 0xcd, 0x88, 0x58, 0xda, 0x46, 0x26, 0xf2, 0x0a, 0x0e, 0xc5, 0x59, 0x1c, 0xc4, 0xbd, 0xae, 0x00, 0x90, 0xe2, 0xed, 0xc8, 0xe7, 0xc4, 0x36, 0x5c, 0x5f, 0x1a, 0x72, 0xce, 0xbe, 0x7f, 0x9a, 0x25, 0xf1, 0x7d, 0x62, 0x78, 0x21, 0xe9, 0x8a, 0xca, 0x6f, 0x08, 0x91, 0x26, 0x9c, 0xa8, 0xeb, 0xf4, 0x56, 0xc9, 0x4d, 0x04, 0xaa, 0x36, 0x05, 0x4f, 0x67, 0x03, 0xf0, 0x7d, 0xd5, 0x60, 0x19, 0xd9, 0x5d, 0xf7, 0x05, 0xa1, 0x7c, 0xe9, 0xa2, 0x5b, 0xc7, 0x9b, 0x8e, 0x3d, 0xae, 0xc5, 0x89, 0x06, 0x4a, 0x4e, 0x66, 0x7d, 0x22, 0xbc, 0x98, 0x54, 0x7e, 0x95, 0xa5, 0x6d, 0xce, 0x09, 0xbc, 0x8e, 0x59, 0xda, 0x34, 0x7d, 0x9e, 0x83, 0x99, 0xf6, 0x0b, 0xa3, 0x69, 0xc3, 0xf1, 0xc7, 0x50, 0x1a, 0xde, 0xe3, 0xe3, 0x85, 0xf2, 0xc5, 0x7f, 0x01, 0x96, 0xb9, 0x68, 0x83, 0xd8, 0xb6, 0x62, 0x12, 0x52, 0x09, 0xf0, 0xf6, 0x73, 0xad, 0xe4, 0x40, 0xaa, 0x21, 0x7b, 0x5c, 0xf7, 0x1a, 0x39, 0x7d, 0x41, 0xed, 0x9b, 0x42, 0x3a, 0x48, 0xa9, 0xac, 0x5c, 0x16, 0x9c, 0x52, 0x7b, 0xcb, 0x5b, 0x48, 0xf0, 0x3c, 0x30, 0xeb, 0xae, 0x9e, 0xc0, 0xc9, 0xa7, 0xb7, 0x18, 0x84, 0x4e, 0x55, 0x44, 0xe1, 0xf1, 0x2b, 0x80, 0xfd, 0x4f, 0x63, 0xa7, 0x57, 0x35, 0x39, 0xba, 0xaa, 0xb4, 0xc7, 0x22, 0x08, 0xed, 0x0e, 0x2a, 0x24, 0x83, 0x62, 0xc9, 0x59, 0x19, 0xec, 0xa5, 0xb7, 0x36, 0xff, 0x10, 0xe9, 0x67, 0x82, 0x27, 0x38, 0xdc, 0x66, 0x66, 0x4f, 0xb9, 0x03, 0xb3, 0xd8, 0x6f, 0xc4, 0x7f, 0x61, 0xac, 0x72, 0x29, 0x71, 0x4b, 0x49, 0x99, 0xbc, 0x01, 0x6c, 0x30, 0x78, 0x94, 0x06, 0x7d, 0xd6, 0x75, 0xbb, 0xa4, 0x1b, 0x4a, 0xc9, 0xd4, 0x6c, 0x41, 0x65, 0xac, 0x7f, 0x47, 0xf6, 0x59, 0x75, 0xda, 0xfe, 0x0c, 0xf6, 0xa9, 0xb2, 0xf3, 0xfb, 0xac, 0xdd, 0x77, 0x02, 0x3b, 0x28, 0xd4, 0x0b, 0xa1, 0x1e, 0x2c, 0xef, 0xf9, 0x0d, 0x5b, 0x31, 0x8c, 0xeb, 0xbe, 0x07, 0x19, 0x2b, 0x67, 0xb1, 0x78, 0xe9, 0x0e, 0x08, 0x20, 0x51, 0xf1, 0xc2, 0x55, 0xa1, 0x33, 0xfe, 0x28, 0xc2, 0x75, 0xc0, 0xeb, 0x41, 0x0b, 0xaa, 0x98, 0x98, 0x92, 0x1d, 0xd6, 0xbf, 0xbd, 0xa9, 0x9e, 0xc8, 0x34, 0xd8, 0xeb, 0x3b, 0x9a, 0x65, 0x8f, 0x5f, 0xdd, 0x0d, 0x8b, 0xc5, 0x77, 0x31, 0x1b, 0x04, 0x42, 0x97, 0x16, 0x96, 0x89, 0x39, 0xac, 0xc3, 0x06, 0xe8, 0x9d, 0x36, 0x83, 0xf9, 0x8d, 0x71, 0x83, 0xe2, 0x5c, 0x0e, 0xc1, 0xc0, 0x12, 0x21, 0x0e, 0xae, 0x40, 0x67, 0xbd, 0x0c, 0xe0, 0xf6, 0x8b, 0x18, 0x77, 0x1a, 0x19, 0x8f, 0x1b, 0x9f, 0x6e, 0x5b, 0xfa, 0xf7, 0xf5, 0x93, 0xaf, 0xe6, 0xd2, 0x80, 0x4d, 0x11, 0x33, 0xc6, 0xff, 0x0c, 0x1b, 0x92, 0x99, 0x2e, 0xc3, 0x33, 0xc1, 0x89, 0x51, 0x36, 0x35, 0xda, 0x52, 0x60, 0x7c, 0x94, 0x33, 0xff, 0xd8, 0x46, 0x70, 0xd5, 0x6d, 0x0f, 0x17, 0x42, 0x20, 0x50, 0xc7, 0xb6, 0xa4, 0xa9, 0x35, 0x91, 0x8b, 0x4c, 0xdf, 0xe4, 0xd1, 0x5d, 0xfc, 0x45, 0x27, 0x5a, 0x66, 0x99, 0x63, 0xa2, 0x6b, 0x01, 0x84, 0x7a, 0x28, 0x34, 0x74, 0x29, 0xa6, 0x1e, 0xbf, 0x47, 0x3a, 0x90, 0x93, 0x62, 0xc1, 0xf1, 0xbc, 0xd3, 0x8b, 0x16, 0x45, 0xd5, 0xd0, 0xf9, 0xab, 0x58, 0x75, 0x0e, 0xa2, 0x10, 0xfe, 0xa3, 0xcc, 0x56, 0x8b, 0x2a, 0x9d, 0x62, 0x9e, 0x80, 0x68, 0xd4, 0x35, 0xd8, 0xd5, 0x0d, 0x73, 0x2b, 0xac, 0x90, 0x0c, 0xbe, 0xd3, 0x36, 0xd7, 0x0b, 0xfc, 0x10, 0x0e, 0x3d, 0x49, 0x7b, 0xa8, 0x64, 0xad, 0xa5, 0x2f, 0x04, 0x8d, 0x9d, 0x42, 0xee, 0x7e, 0x24, 0xb6, 0xd8, 0x8a, 0x47, 0x23, 0x87, 0xf9, 0xee, 0x6d, 0x39, 0x5c, 0x4a, 0x7e, 0xdc, 0xfa, 0x03, 0x5f, 0xe9, 0x18, 0xbe, 0x0b, 0x12, 0x10, 0x64, 0xae, 0x5a, 0x17, 0x20, 0x41, 0xaf, 0x82, 0xb1, 0x6c, 0x14, 0xa8, 0x9b, 0x1e, 0x43, 0x7c, 0x45, 0x24, 0xe0, 0x81, 0xf5, 0x13, 0xe0, 0x92, 0xc1, 0x77, 0xb2, 0x7c, 0xd6, 0x54, 0xad, 0x8f, 0x69, 0x32, 0xcf, 0x87, 0x8e, 0x72, 0xa5, 0xac, 0x8c, 0x89, 0xec, 0x22, 0x99, 0x84, 0x08, 0x70, 0x9e, 0xcb, 0x48, 0x0d, 0xe3, 0x2e, 0x06, 0x16, 0xe3, 0x4a, 0x08, 0x24, 0xf7, 0xfb, 0xda, 0x91, 0x7b, 0xd5, 0x95, 0xf0, 0x8a, 0x2f, 0x23, 0x40, 0xaf, 0x64, 0x7b, 0xab, 0x46, 0x41, 0x49, 0x01, 0xd1, 0xd3, 0x08, 0xf9, 0x07, 0x69, 0x4d, 0x78, 0x69, 0xbf, 0x4c, 0x6c, 0x12, 0x7c, 0xb1, 0xc2, 0xaf, 0x7c, 0x23, 0x30, 0x29, 0x70, 0xc4, 0x79, 0xcc, 0x97, 0xe1, 0x23, 0x01, 0xeb, 0x02, 0xe8, 0x01, 0x53, 0xbb, 0x37, 0x3b, 0xf5, 0x46, 0x9e, 0x00, 0xba, 0xf6, 0x5d, 0xae, 0xeb, 0x02, 0xae, 0xf6, 0x45, 0x22, 0x00, 0xac, 0x7a, 0x3b, 0x53, 0xbd, 0x6a, 0x6c, 0x59, 0xd5, 0x24, 0x4a, 0xb3, 0x41, 0xf1, 0x27, 0x0f, 0x5e, 0x12, 0x33, 0x91, 0xad, 0xc8, 0xac, 0x2a, 0x06, 0x73, 0x5a, 0x94, 0x19, 0x7a, 0x8b, 0x0f, 0x8a, 0xd6, 0x86, 0x9d, 0x18, 0x22, 0x5f, 0x7d, 0x7d, 0xd9, 0x8e, 0x0f, 0x36, 0xcc, 0x6d, 0x39, 0xe9, 0x9d, 0xd7, 0xfd, 0xe3, 0xdc, 0xbe, 0xff, 0x72, 0x67, 0xa8, 0x27, 0xb8, 0x56, 0xcd, 0xde, 0x79, 0x11, 0x0d, 0xf7, 0x8e, 0xaf, 0xad, 0xd3, 0x32, 0x57, 0x2a, 0xb0, 0xaa, 0xe5, 0xf3, 0x9a, 0xc4, 0xa6, 0x62, 0x89, 0x53, 0x5a, 0x89, 0xd7, 0x04, 0x96, 0xfd, 0x3d, 0x78, 0x61, 0x44, 0xfe, 0x43, 0x8d, 0xda, 0x00, 0xc2, 0x95, 0xcf, 0x4d, 0x6b, 0xd4, 0x17, 0x0d, 0xec, 0x4a, 0x84, 0x1c, 0xc1, 0x6f, 0x29, 0xe5, 0xf8, 0x40, 0x1a, 0x5f, 0x29, 0x97, 0x7a, 0xa3, 0x1c, 0x2d, 0x7a, 0x31, 0x9b, 0xd1, 0x7f, 0x44, 0x0b, 0x7d, 0xa9, 0xbf, 0xb8, 0xf8, 0xdb, 0x2b, 0x94, 0x32, 0x21, 0xa5, 0x8f, 0xb8, 0x13, 0x13, 0x43, 0x84, 0x18, 0x90, 0xc9, 0x0c, 0x5a, 0x6d, 0x07, 0x8b, 0xff, 0xe1, 0x67, 0xec, 0x10, 0x51, 0x25, 0xe3, 0x2a, 0xd9, 0x7a, 0xaa, 0x82, 0x17, 0xb8, 0x4d, 0xa5, 0xd9, 0xb8, 0x3f, 0x55, 0x88, 0xa1, 0x1c, 0x7f, 0x07, 0x06, 0x43, 0xce, 0xf5, 0x1e, 0x5d, 0x31, 0x02, 0x12, 0xfc, 0x5d, 0xd6, 0x9b, 0xfb, 0xb7, 0xf7, 0x0c, 0xd5, 0x57, 0xac, 0xbe, 0x28, 0x62, 0xc3, 0xfa, 0xa0, 0x4c, 0x39, 0xaf, 0xfa, 0x65, 0xe2, 0x12, 0x11, 0x7a, 0x42, 0x11, 0x07, 0xb6, 0x66, 0x23, 0xb5, 0xa5, 0xbc, 0xbc, 0xa5, 0x04, 0x97, 0x6d, 0xc7, 0x25, 0x36, 0xa8, 0x44, 0xaa, 0x84, 0x38, 0xec, 0xe4, 0x54, 0xd2, 0x2b, 0xaa, 0x8c, 0x06, 0x21, 0xed, 0x40, 0x3b, 0x02, 0x02, 0x38, 0xa8, 0xbf, 0x41, 0x74, 0xbe, 0x78, 0x76, 0x2f, 0xb9, 0x85, 0x32, 0xf6, 0xca, 0x93, 0x1f, 0x2e, 0xfd, 0xe9, 0x4c, 0xcc, 0x09, 0x9d, 0x32, 0x9c, 0xec, 0xb5, 0x1c, 0x96, 0xe9, 0x09, 0xea, 0x6d, 0x64, 0x12, 0xd6, 0x45, 0x3a, 0x57, 0x19, 0x51, 0x5f, 0x27, 0x30, 0x0a, 0x28, 0xbd, 0x65, 0x25, 0xbb, 0xc8, 0x28, 0x9d, 0x95, 0x7c, 0xd6, 0x03, 0x2c, 0xaf, 0x01, 0x3d, 0x68, 0x38, 0x3f, 0x92, 0xee, 0x7e, 0xe8, 0x39, 0xfc, 0x39, 0x4f, 0xb8, 0x3b, 0x1e, 0x00, 0xef, 0x00, 0x5a, 0x0e, 0x2d, 0xf9, 0x3e, 0x74, 0x57, 0x91, 0x03, 0xa0, 0x33, 0x4a, 0xea, 0x07, 0x3b, 0x1e, 0x3d, 0x48, 0x7d, 0xb1, 0x01, 0xd1, 0xf7, 0xbf, 0x89, 0x3f, 0x2d, 0x3e, 0x81, 0x41, 0xd8, 0xc0, 0xf6, 0x2d, 0x4a, 0x26, 0xe0, 0x2b, 0x0f, 0xee, 0x42, 0xc5, 0x47, 0x88, 0xa9, 0x81, 0x6b, 0xbc, 0xb3, 0x13, 0x66, 0xc5, 0x9f, 0x18, 0x41, 0xdb, 0xd4, 0xff, 0x1c, 0x9a, 0x4a, 0xb9, 0x3e, 0x29, 0xd0, 0xaf, 0xd1, 0xf6, 0xce, 0x64, 0x46, 0xc4, 0xcb, 0x95, 0xeb, 0x90, 0x31, 0xb1, 0xd9, 0xf2, 0x2c, 0x6c, 0xee, 0x96, 0x9b, 0xee, 0x0c, 0x46, 0x18, 0xa7, 0xa8, 0xaa, 0x0b, 0xfe, 0x30, 0x94, 0x00, 0x51, 0x48, 0xd8, 0x6b, 0xe5, 0x4d, 0x87, 0x46, 0x10, 0x8f, 0x50, 0x1c, 0xec, 0xe0, 0xc7, 0xf7, 0x24, 0x16, 0xf5, 0xa8, 0xa2, 0xd4, 0x71, 0xd7, 0x1f, 0x26, 0xa6, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xc5, 0x39, 0x13, 0xe5, 0x6a, 0x03, 0x84, 0xdc, 0x27, 0x67, 0xc2, 0x48, 0xe4, 0xb1, 0xab, 0x7b, 0x3f, 0x94, 0xdd, 0x91, 0x15, 0x9e, 0xd7, 0xdb, 0x7a, 0x60, 0xc4, 0x79, 0xa0, 0x79, 0x4f, 0x93, 0x16, 0xb0, 0x48, 0x25, 0x38, 0x29, 0x1e, 0x45, 0x14, 0x77, 0x05, 0x1d, 0x29, 0x39, 0x03, 0xdd, 0xcd, 0xff, 0x2d, 0x6a, 0x02, 0xf1, 0x26, 0x6a, 0xd4, 0x7e, 0x3c, 0x19, 0xae, 0x69, 0xf7, 0xc4, 0xc6, 0xa5, 0xe6, 0x21, 0x31, 0x08, 0xc8, 0x05, 0x0e, 0xf2, 0x7a, 0x53, 0x86, 0x3e, 0xe7, 0xf4, 0x57, 0x6b, 0x44, 0x1d, 0x10, 0xdf, 0xfb, 0x77, 0x15, 0x5d, 0x65, 0xf8, 0x59, 0x95, 0xbd, 0x66, 0xbc, 0x00, 0xec, 0xbf, 0xf8, 0xa3, 0x15, 0x99, 0x7b, 0x7f, 0xea, 0x11, 0x8d, 0x0b, 0x77, 0xee, 0x8e, 0x0b, 0x34, 0xb9, 0x39, 0x70, 0xb7, 0x2d, 0x2d, 0x58, 0x54, 0x34, 0x7b, 0x4c, 0x5f, 0x8d, 0x23, 0xe0, 0xa6, 0xc6, 0xb9, 0xc4, 0x77, 0x0f, 0xfc, 0x55, 0xb7, 0x3f, 0xd8, 0x39, 0x61, 0x37, 0xa4, 0xb8, 0xf2, 0x8a, 0xc8, 0x17, 0x82, 0x42, 0x08, 0xee, 0x26, 0x1b, 0x75, 0xb7, 0xdc, 0x44, 0x60, 0x57, 0x85, 0x8e, 0x3b, 0xaa, 0x71, 0xb5, 0x73, 0xb9, 0x1f, 0x59, 0x35, 0x35, 0xe6, 0x3d, 0x56, 0x61, 0x20, 0xc5, 0x4e, 0xf0, 0xc2, 0x4b, 0xa9, 0x01, 0xfb, 0xd0, 0xba, 0xa1, 0x7e, 0xf7, 0x00, 0x4b, 0xe1, 0x56, 0x22, 0x16, 0xd3, 0xbf, 0x1d, 0x17, 0xb6, 0x8d, 0xac, 0xdb, 0xba, 0x5a, 0x09, 0x15, 0x06, 0x2f, 0x77, 0x8a, 0xd5, 0xbd, 0xc4, 0x3c, 0x2d, 0xe3, 0xcc, 0x4e, 0x42, 0x68, 0xad, 0x44, 0xf9, 0x05, 0x13, 0xf2, 0x4d, 0x70, 0x50, 0x74, 0x62, 0xcd, 0xfa, 0xc8, 0xba, 0x19, 0x51, 0x38, 0xcc, 0x37, 0x63, 0x27, 0x94, 0xbc, 0x8f, 0x3d, 0x64, 0xba, 0xfd, 0x3f, 0x11, 0xcd, 0x6c, 0x45, 0x28, 0xf3, 0xf9, 0x65, 0x92, 0xd3, 0x05, 0xdb, 0xc5, 0xc4, 0x2a, 0xe0, 0x99, 0xbe, 0x26, 0x9e, 0xf5, 0xc3, 0x0b, 0xd1, 0x03, 0x45, 0xaf, 0xc6, 0x8f, 0x37, 0x25, 0x6b, 0xf0, 0x1a, 0x04, 0x6e, 0x8e, 0x4c, 0x82, 0x8a, 0x9b, 0x02, 0x10, 0x4a, 0x02, 0xf5, 0x25, 0x56, 0xbb, 0x5e, 0x24, 0x95, 0x94, 0x69, 0x8a, 0x98, 0xd3, 0x62, 0x9c, 0x5f, 0x5e, 0xe1, 0x3c, 0xc7, 0x20, 0x7c, 0xd9, 0xcd, 0x17, 0x90, 0x53, 0xa0, 0xb7, 0x8b, 0x87, 0x8c, 0x14, 0x54, 0xdd, 0x46, 0x70, 0xfc, 0x43, 0x9d, 0xb3, 0xb1, 0xe9, 0x58, 0xcd, 0x7b, 0x72, 0x4c, 0xf1, 0x6e, 0x56, 0x61, 0x1b, 0xaf, 0xda, 0x58, 0xee, 0xaa, 0x81, 0x72, 0xe6, 0xa5, 0x9e, 0xee, 0xde, 0x76, 0x2d, 0x71, 0x23, 0x7c, 0x54, 0x03, 0x46, 0x19, 0x5b, 0x3d, 0x08, 0x5e, 0x87, 0x30, 0x89, 0xb2, 0xdb, 0x79, 0x52, 0x80, 0xd9, 0x58, 0xb4, 0x74, 0x2b, 0x83, 0xa1, 0x8d, 0x41, 0x59, 0xd4, 0x75, 0xbe, 0xe6, 0xf5, 0x9e, 0x65, 0x82, 0x38, 0x4c, 0x5a, 0xae, 0x3b, 0x37, 0x5e, 0xe1, 0xcc, 0xac, 0x69, 0xf0, 0x38, 0x64, 0x1f, 0xfe, 0xdf, 0xff, 0xf6, 0x6f, 0x07, 0x55, 0x18, 0x8f, 0xab, 0x52, 0x4a, 0x5d, 0xbd, 0x4f, 0x31, 0xa5, 0x87, 0xda, 0x25, 0xf3, 0xbf, 0xcf, 0x70, 0xae, 0xa3, 0x97, 0xb9, 0x33, 0x2c, 0xb7, 0xc9, 0x7f, 0x1b, 0x82, 0xe2, 0x19, 0x97, 0x0b, 0xab, 0xf8, 0xbc, 0x53, 0x75, 0xcd, 0x7e, 0x07, 0x4b, 0xea, 0x60, 0x05, 0xfb, 0x0a, 0xde, 0xcc, 0xe2, 0x18, 0xf7, 0xb6, 0x92, 0x75, 0x4b, 0x10, 0x6f, 0x04, 0x33, 0x08, 0xfb, 0x03, 0x0a, 0x02, 0x28, 0xb2, 0xe8, 0xcd, 0x03, 0xf0, 0x9c, 0x22, 0xa3, 0x2e, 0x5c, 0x99, 0x7d, 0xa5, 0xd2, 0xf2, 0xf4, 0x00, 0x60, 0xf0, 0xe0, 0x67, 0x1b, 0xe4, 0x1a, 0x79, 0xea, 0xa0, 0x68, 0xe8, 0x6e, 0xd5, 0x75, 0xa2, 0xbb, 0xca, 0x5d, 0x66, 0xe8, 0x0f, 0x1e, 0x1a, 0xd5, 0x16, 0xe7, 0x99, 0x13, 0x69, 0x0a, 0xec, 0x43, 0x9e, 0x66, 0x44, 0xc4, 0x89, 0xfe, 0x87, 0x9c, 0x34, 0x7e, 0x3c, 0x5b, 0x9c, 0x95, 0x9c, 0x88, 0x56, 0xd2, 0x16, 0xa3, 0x49, 0x0e, 0x30, 0x50, 0x2e, 0x84, 0x79, 0xe0, 0xdb, 0x82, 0x68, 0x37, 0x96, 0x0d, 0x85, 0x0c, 0x47, 0x85, 0xbd, 0xb5, 0x27, 0xb7, 0x70, 0x91, 0xad, 0x7f, 0x81, 0xb4, 0x04, 0xd3, 0x78, 0x0b, 0xab, 0xd4, 0xf0, 0xe4, 0x6d, 0xcd, 0xa4, 0x1a, 0xd2, 0xdb, 0x58, 0x10, 0x36, 0x92, 0xe2, 0xdd, 0xc8, 0xb1, 0xda, 0xc4, 0x00, 0xe5, 0x0c, 0xdc, 0x25, 0xdc, 0xac, 0x95, 0xff, 0x0a, 0xaa, 0x2b, 0xa3, 0x28, 0x34, 0xbd, 0x66, 0xd4, 0xaa, 0x76, 0x31, 0x05, 0x04, 0x70, 0xc1, 0x3a, 0x46, 0x6c, 0x98, 0x24, 0x74, 0x02, 0xd4, 0xd3, 0xb0, 0x0c, 0x5b, 0x4f, 0x8d, 0x83, 0x74, 0xa2, 0x72, 0x58, 0xc2, 0xa2, 0x43, 0x11, 0x13, 0x45, 0xe6, 0x50, 0xc7, 0x3e, 0x80, 0x45, 0x43, 0xf8, 0x8a, 0x03, 0xa3, 0x29, 0x6c, 0x9a, 0x03, 0x64, 0x05, 0xf9, 0xd9, 0x59, 0xfc, 0xbc, 0x29, 0xd5, 0x6d, 0xb2, 0x94, 0xeb, 0xbc, 0x23, 0xc1, 0x4f, 0xba, 0xaa, 0x08, 0x7b, 0x73, 0x0c, 0x51, 0x36, 0xef, 0xaf, 0xb4, 0x06, 0xf1, 0x9f, 0xbd, 0x03, 0x95, 0xcb, 0x16, 0xf0, 0xc1, 0xab, 0x94, 0xc6, 0xd2, 0xc9, 0xf9, 0xe4, 0x84, 0x49, 0xd3, 0x9a, 0x58, 0x11, 0x98, 0x69, 0x48, 0xcb, 0xc2, 0xfb, 0x73, 0x5b, 0xc6, 0x9d, 0xdc, 0x27, 0x81, 0xca, 0x8c, 0xf2, 0x00, 0x6a, 0x19, 0xc3, 0xc2, 0x89, 0xbe, 0x01, 0x2f, 0xda, 0x6a, 0xad, 0x6f, 0x93, 0x4c, 0x0e, 0xc9, 0x0b, 0x99, 0xc0, 0xaf, 0xc0, 0xa9, 0x7a, 0xc9, 0x68, 0x2c, 0xca, 0x94, 0xae, 0xfa, 0xcc, 0xbe, 0x59, 0x5f, 0xe7, 0xc3, 0x41, 0xa4, 0x13, 0xf0, 0x64, 0xa5, 0x7c, 0xd1, 0x96, 0x22, 0x1a, 0x79, 0x81, 0x3d, 0x28, 0x22, 0xfe, 0xa1, 0x4f, 0x87, 0x17, 0x0b, 0xa3, 0x75, 0x91, 0xae, 0x9d, 0xa7, 0xfc, 0xfc, 0xba, 0x02, 0xe1, 0xcb, 0x0d, 0x20, 0xae, 0x53, 0xa3, 0x93, 0x10, 0xc5, 0x58, 0xd8, 0x01, 0xc1, 0xc9, 0x62, 0x5a, 0xc4, 0x5a, 0x77, 0xd8, 0x67, 0x3f, 0x2c, 0xd3, 0x2d, 0x0d, 0x16, 0x2f, 0xbe, 0x14, 0x66, 0x63, 0xd6, 0x7a, 0x52, 0xb6, 0xbf, 0x22, 0x69, 0xc4, 0xad, 0x20, 0x66, 0x0e, 0x65, 0x4a, 0x33, 0x8a, 0x74, 0x0b, 0x67, 0xe9, 0x72, 0x1d, 0x67, 0x48, 0xcb, 0xde, 0x02, 0xb0, 0xd3, 0xc3, 0x5e, 0x3c, 0xe8, 0xd2, 0x9d, 0x36, 0x9c, 0x66, 0x1c, 0xa9, 0x54, 0x3e, 0x3c, 0x7d, 0x77, 0x2e, 0x63, 0x8c, 0x07, 0x77, 0x69, 0xf4, 0x0a, 0x24, 0x9a, 0x06, 0x04, 0x9a, 0xa9, 0xcc, 0x4b, 0x3d, 0x23, 0x4a, 0x34, 0x14, 0x67, 0x16, 0xb0, 0x9b, 0x77, 0xa2, 0x59, 0xa2, 0x31, 0xda, 0xd6, 0xa9, 0x68, 0x2f, 0xa3, 0xea, 0x29, 0xd0, 0xab, 0x2c, 0x10, 0xde, 0x65, 0x57, 0x15, 0x70, 0xdc, 0xa9, 0x3c, 0x87, 0x07, 0x13, 0x1a, 0x10, 0xfb, 0x57, 0xac, 0xb3, 0x03, 0x76, 0x5a, 0xc7, 0x8e, 0x38, 0xf3, 0xce, 0x00, 0xf1, 0x68, 0x5a, 0xc6, 0x92, 0x74, 0x21, 0x81, 0xa9, 0x0f, 0x98, 0x22, 0x2b, 0xa5, 0x00, 0x5d, 0x63, 0x6a, 0x89, 0xc6, 0xfa, 0x6c, 0x87, 0x2c, 0xe6, 0x07, 0x1e, 0x6e, 0x99, 0xa2, 0x44, 0xa8, 0x8b, 0xef, 0xdb, 0x03, 0x02, 0x7e, 0x44, 0x4b, 0xdc, 0xeb, 0xe7, 0x90, 0xc7, 0x11, 0x91, 0xd3, 0x80, 0xd3, 0x4e, 0x46, 0x20, 0x67, 0xbe, 0xbb, 0x30, 0x0f, 0x4c, 0x2a, 0xdc, 0x58, 0x4a, 0xf8, 0xed, 0x56, 0x6d, 0x4e, 0xe2, 0x1b, 0xea, 0x2c, 0x09, 0x72, 0x83, 0x95, 0xb3, 0xe5, 0x53, 0x15, 0x66, 0x31, 0xcb, 0x32, 0x10, 0xac, 0xe2, 0xab, 0xa7, 0x33, 0xeb, 0xcc, 0xeb, 0xb5, 0xd4, 0xbf, 0x1d, 0x47, 0xd3, 0x61, 0x08, 0x87, 0x48, 0xa9, 0x5e, 0xa7, 0xa6, 0x06, 0xfd, 0xc5, 0xb4, 0xe6, 0x60, 0xcc, 0xf0, 0xcf, 0x35, 0xaf, 0x41, 0xee, 0xa2, 0x1e, 0x72, 0xd5, 0xda, 0x16, 0x33, 0xd6, 0xd5, 0x03, 0x05, 0xec, 0x6e, 0x3d, 0x51, 0x7d, 0x22, 0xf3, 0x9d, 0x2a, 0xef, 0x45, 0xb9, 0x61, 0x5f, 0x53, 0xcf, 0x9c, 0xd3, 0xeb, 0x46, 0x36, 0xf3, 0xf5, 0xe8, 0xcc, 0xec, 0xcf, 0x9b, 0xf6, 0x83, 0x52, 0xd4, 0x12, 0x88, 0x0d, 0xbd, 0x65, 0x41, 0x5a, 0x11, 0x37, 0x64, 0xad, 0xbc, 0x15, 0xb1, 0xd6, 0x37, 0x3d, 0xcd, 0xca, 0x07, 0xa1, 0x0d, 0x65, 0x42, 0x56, 0xae, 0x86, 0x0d, 0xec, 0xc1, 0x6c, 0x4b, 0x52, 0x7b, 0x42, 0x3d, 0x4b, 0xf9, 0x04, 0x86, 0xb1, 0xdf, 0x34, 0x24, 0xd7, 0x8e, 0xc4, 0x51, 0x0e, 0x37, 0x7b, 0xfc, 0x5d, 0xf8, 0x3c, 0x18, 0x69, 0xaa, 0xce, 0xca, 0x3c, 0xce, 0x76, 0xbf, 0x98, 0x91, 0x9b, 0xcc, 0x07, 0x9c, 0x0a, 0x9a, 0xa4, 0x21, 0x95, 0x91, 0xee, 0x73, 0xc3, 0xc9, 0x4a, 0xf2, 0x4e, 0x46, 0x32, 0x51, 0xd2, 0xd9, 0x5f, 0xb5, 0x31, 0x4f, 0x33, 0xc7, 0x14, 0xe2, 0x23, 0xe3, 0x22, 0xbe, 0xe0, 0xe2, 0xa5, 0x95, 0x2f, 0x2a, 0xf8, 0x7f, 0x6e, 0x96, 0x88, 0x36, 0x9b, 0xca, 0x66, 0x6b, 0xac, 0xd3, 0x93, 0x5f, 0x93, 0xfb, 0x5c, 0xf3, 0xb7, 0xb2, 0x16, 0xfb, 0x95, 0x21, 0xcc, 0xdc, 0xdb, 0xe9, 0xbf, 0x87, 0x9b, 0x23, 0xf9, 0x50, 0x8f, 0x58, 0xfa, 0x4e, 0xe5, 0x8c, 0xcd, 0xda, 0x0e, 0x3a, 0xca, 0x8a, 0x5e, 0x12, 0xc5, 0xdb, 0x08, 0xec, 0x36, 0x4a, 0xac, 0x5b, 0xf0, 0x53, 0x30, 0x47, 0xbf, 0xa7, 0x45, 0xb3, 0x37, 0x7b, 0x1b, 0xad, 0x47, 0x85, 0x4d, 0xdd, 0xcb, 0x11, 0x08, 0x26, 0x31, 0x5d, 0x33, 0xa1, 0x02, 0xba, 0xe1, 0x9c, 0x53, 0x0e, 0xee, 0xaa, 0xc0, 0x96, 0x37, 0xc0, 0xf5, 0xec, 0xa4, 0x89, 0xd9, 0xaf, 0x18, 0x2a, 0x24, 0x36, 0x07, 0xff, 0x1c, 0x56, 0x08, 0x06, 0x8f, 0xb5, 0xb0, 0x04, 0x49, 0x36, 0xa4, 0xcb, 0x66, 0x69, 0xae, 0x48, 0xd1, 0xc6, 0x60, 0x7b, 0x51, 0x06, 0x34, 0xe9, 0x25, 0x5e, 0x41, 0xfc, 0x8a, 0xf3, 0xbb, 0xe3, 0xa4, 0xc0, 0x45, 0xed, 0x12, 0x8e, 0x47, 0xdb, 0x94, 0x4f, 0x1d, 0x23, 0xfe, 0x97, 0x7d, 0x05, 0x11, 0x78, 0x67, 0x46, 0xcd, 0x01, 0x2e, 0x22, 0x3b, 0x48, 0x87, 0x08, 0xb1, 0x2d, 0x5d, 0xe4, 0xd8, 0x3d, 0xe2, 0x10, 0xab, 0xf1, 0xcd, 0xd6, 0x5f, 0x5c, 0xde, 0x11, 0x5f, 0xd3, 0xf4, 0x8d, 0xcd, 0xb7, 0xd1, 0x44, 0xd5, 0xf7, 0x10, 0x64, 0x00, 0x7c, 0xf7, 0xcd, 0xfb, 0x24, 0x03, 0x7a, 0xd4, 0x70, 0x16, 0x2f, 0x02, 0x0b, 0xca, 0x81, 0x9f, 0x6e, 0x9e, 0x9e, 0x22, 0x29, 0x24, 0x23, 0xdf, 0xe5, 0x8f, 0xba, 0x82, 0x7b, 0x50, 0xfe, 0x91, 0x3c, 0x22, 0x4d, 0xbc, 0xd8, 0xec, 0x4a, 0x66, 0xa8, 0xf0, 0x0e, 0xbb, 0x13, 0x12, 0x82, 0x38, 0x82, 0x9f, 0xa0, 0x99, 0x20, 0x9f, 0x4d, 0x3b, 0x69, 0x50, 0xeb, 0x78, 0x51, 0x6d, 0xaf, 0x09, 0xdb, 0x72, 0xb3, 0x16, 0x80, 0x61, 0xc4, 0x1b, 0x2f, 0x46, 0x0a, 0xbf, 0xda, 0x67, 0x44, 0xd2, 0xd8, 0xbf, 0xca, 0x1a, 0x50, 0x01, 0x47, 0x78, 0x05, 0x2f, 0x8a, 0x88, 0x11, 0xd0, 0xe3, 0x93, 0xd5, 0x3f, 0xe7, 0xe9, 0xc3, 0x3b, 0xe0, 0x6e, 0x63, 0xf6, 0x77, 0x1f, 0x90, 0xa7, 0x62, 0xe6, 0x33, 0x98, 0x16, 0xdd, 0x3c, 0x8a, 0xa6, 0x35, 0x43, 0x0a, 0x16, 0xb9, 0x08, 0x4d, 0x24, 0x60, 0xe8, 0x1c, 0x47, 0x21, 0x38, 0x6e, 0xd1, 0x74, 0x7c, 0x40, 0x07, 0x92, 0xc4, 0x0b, 0x07, 0x91, 0x74, 0xc2, 0x10, 0xe9, 0x50, 0xf9, 0xe7, 0x68, 0x44, 0x16, 0x99, 0x96, 0x67, 0x2b, 0xa3, 0x8b, 0x60, 0x90, 0xdc, 0x63, 0xcc, 0x8c, 0xf5, 0x29, 0xdc, 0x19, 0xe5, 0xda, 0x95, 0x9a, 0x35, 0x2a, 0xa8, 0x3c, 0x36, 0xb1, 0x47, 0xff, 0xcd, 0xeb, 0xc5, 0x01, 0xa6, 0x41, 0x79, 0x2d, 0x9e, 0x4b, 0x22, 0xa0, 0x4f, 0x37, 0x62, 0x10, 0xc5, 0x70, 0x1b, 0x8a, 0xa0, 0xd9, 0x81, 0xdf, 0xf7, 0x33, 0x4d, 0xa1, 0x88, 0x92, 0x07, 0xee, 0xcb, 0xa1, 0x23, 0x1d, 0x8c, 0xd7, 0xbb, 0x1f, 0xbb, 0xa7, 0xa8, 0xe1, 0xbc, 0xcf, 0x7d, 0x8e, 0x79, 0xf6, 0x7b, 0x60, 0x76, 0x86, 0xba, 0x23, 0x7a, 0x4b, 0x18, 0x4c, 0xac, 0xc5, 0xdf, 0xc8, 0xab, 0x98, 0xab, 0x17, 0x1a, 0x5c, 0x76, 0x06, 0x14, 0xc3, 0x69, 0xde, 0x66, 0x95, 0x5b, 0x3a, 0x03, 0xb0, 0x83, 0x9e, 0x5d, 0x4d, 0x8f, 0x4e, 0x60, 0x77, 0x2e, 0x79, 0xbe, 0x05, 0x2a, 0x2e, 0x24, 0xe2, 0x9f, 0x62, 0x93, 0x28, 0x97, 0x3f, 0x82, 0x5a, 0x1a, 0x15, 0xa6, 0x74, 0x1d, 0x38, 0x9d, 0xab, 0x48, 0x93, 0x44, 0x0e, 0xc3, 0x58, 0xb9, 0x22, 0xa1, 0x9a, 0x75, 0x95, 0x32, 0x82, 0xd4, 0x61, 0x1f, 0xc5, 0x06, 0x8f, 0x89, 0xb6, 0x17, 0xfd, 0x01, 0x27, 0xff, 0x92, 0x0f, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x9f, 0xb4, 0x80, 0x33, 0x18, 0xed, 0x17, 0xb5, 0xc4, 0xac, 0xb0, 0xba, 0xa8, 0x13, 0xe4, 0x96, 0x6f, 0x61, 0xe8, 0xce, 0xdf, 0x72, 0xb2, 0x56, 0xaa, 0xa4, 0xe7, 0xd1, 0x0b, 0x8c, 0xa4, 0x7e, 0x94, 0x28, 0x2c, 0xba, 0x69, 0x66, 0xdc, 0x96, 0x79, 0xab, 0x7f, 0xcc, 0x41, 0xf6, 0xd7, 0x48, 0x8a, 0x15, 0x40, 0xf3, 0x4f, 0x31, 0x90, 0x1e, 0xb6, 0xfd, 0xda, 0x34, 0xe5, 0xbb, 0x69, 0x1c, 0xf4, 0x17, 0x18, 0xd0, 0x89, 0x17, 0xe6, 0x83, 0xfa, 0x3d, 0xd6, 0x28, 0x3c, 0x92, 0x2d, 0x58, 0x46, 0xcb, 0x4f, 0x4c, 0x29, 0xa5, 0xdb, 0x56, 0xfb, 0xce, 0xf9, 0x0a, 0xe3, 0x4e, 0x3d, 0x2e, 0xdc, 0x42, 0x47, 0x15, 0x3a, 0xea, 0x66, 0xc8, 0x79, 0xc3, 0xd5, 0x93, 0x8d, 0xc9, 0xa1, 0xae, 0x8c, 0xed, 0x35, 0xd6, 0xcb, 0xea, 0x15, 0x72, 0xb5, 0x54, 0xf2, 0x10, 0xbb, 0x01, 0x5e, 0x68, 0x3b, 0xdb, 0x17, 0xf3, 0x18, 0x49, 0x72, 0xbc, 0x24, 0xba, 0xc3, 0x2f, 0x30, 0xf6, 0x79, 0x7f, 0x38, 0x6a, 0x36, 0x36, 0x04, 0xdb, 0xd3, 0x17, 0xd1, 0x34, 0x81, 0x4d, 0xcf, 0x67, 0x74, 0xb3, 0x21, 0x94, 0xd9, 0xb1, 0x1d, 0x3a, 0x1c, 0x22, 0x24, 0xd2, 0xe3, 0x3b, 0xf5, 0x60, 0x97, 0xaa, 0xdf, 0xba, 0x16, 0xf8, 0x27, 0xf4, 0xcd, 0xc1, 0x3b, 0xc1, 0xe6, 0x0a, 0x6a, 0x59, 0xa7, 0xff, 0xef, 0x5d, 0xc8, 0xd3, 0x4c, 0xb4, 0xa8, 0x86, 0x00, 0x53, 0x50, 0x6b, 0x1c, 0x7b, 0xa5, 0x61, 0x20, 0xe3, 0x01, 0x1b, 0x66, 0x1a, 0x4e, 0xf4, 0x80, 0xdc, 0x46, 0xb6, 0x94, 0x77, 0x43, 0xc1, 0x37, 0xf0, 0xe9, 0x7c, 0x27, 0xae, 0x30, 0x70, 0x67, 0x84, 0x3f, 0x33, 0x1c, 0x27, 0xc9, 0xda, 0xc2, 0x7d, 0xc6, 0x5d, 0x9f, 0x7a, 0xb9, 0x71, 0xbc, 0xb6, 0x3c, 0xfe, 0x4a, 0x28, 0xd3, 0x24, 0x94, 0x43, 0x7d, 0xee, 0x84, 0xb1, 0xc5, 0xe6, 0x27, 0x54, 0xb9, 0xef, 0x0a, 0xf8, 0xdb, 0xb5, 0xa8, 0x5c, 0xb8, 0x42, 0x11, 0x12, 0x94, 0xd5, 0xa6, 0xba, 0x5e, 0x54, 0xe2, 0x6f, 0x79, 0x48, 0xd2, 0xf0, 0xd7, 0x1f, 0x23, 0x90, 0x95, 0x3e, 0x8f, 0xed, 0x2b, 0xf7, 0xe6, 0x65, 0xfe, 0x1d, 0x68, 0x4a, 0xb8, 0xef, 0x9f, 0xce, 0x74, 0x92, 0xa9, 0xb0, 0xa4, 0x83, 0x91, 0x2b, 0xa8, 0x90, 0xa3, 0x23, 0x09, 0x58, 0x7a, 0x51, 0x36, 0x6b, 0xaa, 0x06, 0x73, 0x46, 0xd2, 0x38, 0x6e, 0x2c, 0x87, 0x2c, 0x20, 0x6f, 0xf3, 0x13, 0xf3, 0x49, 0x2b, 0xc8, 0x0c, 0x1d, 0xfe, 0x0d, 0xa1, 0x20, 0x13, 0x0b, 0x5a, 0xac, 0x16, 0xac, 0x0e, 0xb1, 0x1f, 0xc9, 0x14, 0xf7, 0xbc, 0xda, 0x48, 0xd3, 0x5d, 0xf5, 0x4b, 0x3d, 0x9c, 0xc8, 0x56, 0xd8, 0x9e, 0x88, 0xbc, 0x60, 0xae, 0xe6, 0xfc, 0xaa, 0xba, 0x3d, 0xb1, 0xe1, 0x07, 0x8c, 0xc8, 0x05, 0xae, 0x2d, 0x4c, 0x1b, 0x2a, 0xec, 0xad, 0xe6, 0x2b, 0x34, 0xb2, 0x29, 0xf2, 0x19, 0x51, 0x5e, 0xb6, 0x55, 0x8e, 0xa5, 0x4d, 0x74, 0x1c, 0x5f, 0xa0, 0xc8, 0xae, 0x21, 0x2d, 0x48, 0x15, 0x04, 0x78, 0x0e, 0xd0, 0xce, 0xfd, 0x6b, 0x80, 0xcf, 0xc5, 0xa2, 0x32, 0x15, 0xfb, 0x8c, 0xa8, 0x38, 0xc3, 0xab, 0x70, 0x04, 0x04, 0xcc, 0x75, 0x40, 0x2c, 0x5b, 0x78, 0x2e, 0x1d, 0xfc, 0x71, 0xff, 0x38, 0x63, 0x1b, 0x77, 0x3b, 0x0c, 0x65, 0x8f, 0xc9, 0x21, 0xdd, 0x28, 0x40, 0x5c, 0xaa, 0xf2, 0x9e, 0x5a, 0x24, 0x7b, 0x44, 0xc0, 0xdb, 0x7c, 0x82, 0x1e, 0x1d, 0xe8, 0xb7, 0x99, 0xe2, 0x7d, 0xfa, 0x75, 0xc9, 0x74, 0x43, 0xcf, 0x95, 0x23, 0xe2, 0xe8, 0x08, 0x49, 0x5e, 0x45, 0x2d, 0xb5, 0x5a, 0x2a, 0x57, 0xf3, 0xe0, 0x01, 0xf7, 0xd8, 0x41, 0x83, 0xae, 0xac, 0xd3, 0x1d, 0xe4, 0x3b, 0x89, 0x2f, 0x2d, 0xdc, 0x2f, 0x3c, 0xd2, 0x3b, 0x9b, 0xd5, 0x0b, 0x8c, 0x68, 0xee, 0xee, 0x72, 0x7e, 0x0e, 0x97, 0x9e, 0xd6, 0x8b, 0x57, 0xaa, 0xd8, 0x61, 0xb1, 0x10, 0xf1, 0xc1, 0x5c, 0x29, 0x72, 0x48, 0xc0, 0xaf, 0x8c, 0x58, 0x1b, 0x3d, 0xad, 0xe5, 0xe8, 0x10, 0x90, 0x35, 0x0f, 0xa6, 0xfe, 0x7b, 0xc9, 0xbf, 0x2f, 0x3d, 0x60, 0xc5, 0x1f, 0xc2, 0x27, 0x14, 0xff, 0x14, 0xd3, 0x10, 0x1d, 0xa0, 0x96, 0xa6, 0xb4, 0x5f, 0x93, 0xd5, 0xc5, 0xd9, 0x5f, 0xea, 0x99, 0xac, 0x4b, 0xa7, 0x89, 0x09, 0x59, 0x94, 0xb2, 0x77, 0x37, 0xa9, 0x85, 0xc6, 0xa5, 0xb9, 0x2c, 0x1d, 0xe8, 0x7e, 0x16, 0xa1, 0xb5, 0xbe, 0xfa, 0x12, 0x54, 0x32, 0xb9, 0x55, 0xf8, 0x0b, 0x77, 0x89, 0xf6, 0xc5, 0xd7, 0x8f, 0x8f, 0x0b, 0xa1, 0x3c, 0x08, 0x08, 0x90, 0xe9, 0xdd, 0x97, 0xc4, 0xb0, 0xdd, 0x23, 0x55, 0x69, 0x8a, 0x80, 0x8e, 0x7f, 0x7c, 0xe1, 0x38, 0xe7, 0xff, 0xe1, 0xfd, 0x79, 0x55, 0x11, 0xbf, 0x1d, 0x94, 0x87, 0xa1, 0x38, 0x66, 0x82, 0xc6, 0x9d, 0x4d, 0x46, 0x3b, 0x64, 0x99, 0x28, 0x0c, 0x2f, 0xe1, 0x63, 0x2b, 0xdb, 0x2f, 0x08, 0xcb, 0x42, 0xa8, 0xd7, 0xcd, 0x48, 0x58, 0x64, 0xa0, 0xa4, 0x08, 0x98, 0x55, 0x9c, 0x4f, 0xcf, 0x1a, 0x83, 0xf6, 0xfa, 0x59, 0x61, 0x7c, 0x34, 0x8c, 0xb6, 0x20, 0x2e, 0xea, 0x5d, 0x3a, 0x0c, 0xc0, 0x33, 0xbd, 0xf5, 0x64, 0x26, 0x67, 0xba, 0xc5, 0x6e, 0xa5, 0x09, 0xe4, 0x95, 0x3e, 0x9b, 0x8e, 0x52, 0x0b, 0xdc, 0x31, 0x06, 0xe4, 0xc5, 0x61, 0x89, 0xa1, 0x89, 0x16, 0x47, 0x23, 0xa9, 0x51, 0x2d, 0x93, 0xd5, 0x34, 0xbd, 0x77, 0x3d, 0x02, 0x7b, 0x86, 0x42, 0xe4, 0xfc, 0xab, 0xa7, 0x20, 0xa7, 0x3e, 0xc3, 0x89, 0x35, 0xf5, 0xd8, 0x18, 0x1c, 0xa6, 0x03, 0x39, 0x6d, 0xdd, 0x82, 0xfa, 0x35, 0xaa, 0x9b, 0xfa, 0x05, 0xc5, 0x56, 0x7c, 0x09, 0xd7, 0xa9, 0x7d, 0xbf, 0xf1, 0x3d, 0xed, 0xec, 0xa3, 0x3b, 0x4c, 0x28, 0x23, 0x96, 0x8a, 0x76, 0xf5, 0x29, 0xda, 0x72, 0x01, 0x7b, 0xb7, 0x20, 0x11, 0x1d, 0xd0, 0xea, 0xdc, 0x8d, 0x96, 0x6e, 0x2f, 0x53, 0x9e, 0xb5, 0x4c, 0x8e, 0x45, 0xbf, 0xf0, 0x0d, 0x88, 0xad, 0x0b, 0x4c, 0xd3, 0x9d, 0x90, 0xef, 0x53, 0x65, 0x8d, 0xc0, 0xee, 0x3e, 0x7d, 0x8b, 0xbd, 0xc8, 0x15, 0xdf, 0x51, 0x83, 0xbe, 0x61, 0x76, 0xac, 0xfc, 0x34, 0x07, 0xc0, 0x26, 0xd1, 0x6a, 0xe4, 0xd0, 0x20, 0xfb, 0x15, 0x07, 0x5d, 0xea, 0x40, 0xca, 0x4e, 0xff, 0x8d, 0xf7, 0x44, 0xd5, 0x6f, 0x1e, 0x26, 0x1d, 0x87, 0xce, 0xdc, 0xbe, 0xc9, 0x04, 0x3f, 0x45, 0xd3, 0x36, 0x7d, 0x04, 0xa2, 0x46, 0xcd, 0xb1, 0x62, 0x82, 0x07, 0xc3, 0x76, 0x63, 0xc6, 0x2b, 0x83, 0x5a, 0xe3, 0x27, 0xb6, 0xad, 0x67, 0x3d, 0x82, 0x6f, 0x5c, 0xae, 0x09, 0xcf, 0xca, 0xc2, 0x48, 0xc8, 0x35, 0xe1, 0x2a, 0x4a, 0xf2, 0xbd, 0xa0, 0x4e, 0xe1, 0xf9, 0xb3, 0x23, 0x1f, 0x1f, 0x01, 0xd7, 0x64, 0x20, 0x1c, 0xdb, 0xfe, 0x92, 0xd2, 0x99, 0x8c, 0x26, 0x1f, 0x8e, 0x40, 0x77, 0x03, 0x72, 0x78, 0x18, 0x6f, 0xe2, 0x0a, 0x77, 0x4c, 0x55, 0x6e, 0x98, 0x08, 0x20, 0xe9, 0x44, 0x79, 0x9c, 0xbc, 0x33, 0x72, 0x6e, 0x69, 0x84, 0x6e, 0xb7, 0x2f, 0x63, 0x29, 0xe0, 0x6a, 0xd2, 0x66, 0xd3, 0x19, 0x7f, 0x03, 0x39, 0x4d, 0xee, 0x66, 0xcb, 0xca, 0xcb, 0x8f, 0xb3, 0xf8, 0x98, 0x6d, 0x37, 0xca, 0xf3, 0x5b, 0xc6, 0x5a, 0xe8, 0x6f, 0xe4, 0x7a, 0x8d, 0xb9, 0xe2, 0x28, 0x25, 0x56, 0xd1, 0x56, 0xc9, 0xb3, 0x8e, 0x12, 0xb9, 0x55, 0x9b, 0x7b, 0x19, 0xc8, 0xf5, 0x7a, 0x9c, 0x53, 0xac, 0xe9, 0xac, 0xe1, 0xce, 0xb9, 0x29, 0xca, 0x2d, 0x4e, 0xfc, 0x81, 0xfb, 0xfe, 0x0d, 0x7c, 0xec, 0x0c, 0x83, 0x6f, 0x4d, 0xba, 0xf9, 0xde, 0xc9, 0xa0, 0xf1, 0x37, 0xbc, 0x12, 0xeb, 0xcb, 0xa7, 0x95, 0x47, 0x5c, 0x78, 0x3a, 0xdd, 0x4f, 0xfa, 0xdf, 0x2b, 0x36, 0x82, 0xf8, 0x7d, 0x97, 0x8e, 0x74, 0x73, 0xc3, 0x8e, 0x43, 0xcf, 0x63, 0x40, 0xc0, 0x01, 0x99, 0x9a, 0xe8, 0x11, 0xef, 0x69, 0xd2, 0x95, 0x30, 0xee, 0x5d, 0x52, 0x94, 0x04, 0xf5, 0x53, 0xbc, 0x8f, 0x46, 0xde, 0xbd, 0x31, 0x35, 0x18, 0x74, 0x67, 0x16, 0x9e, 0x67, 0xd3, 0x47, 0x41, 0x6e, 0xb5, 0x79, 0x06, 0x6d, 0x73, 0xd1, 0xdb, 0x4a, 0x58, 0x7c, 0x1e, 0xb7, 0xb0, 0xab, 0x76, 0x81, 0x5e, 0xee, 0x3d, 0x38, 0x2c, 0xb1, 0x06, 0x25, 0xbc, 0xd6, 0x69, 0xa6, 0x41, 0xea, 0x7d, 0xfa, 0x89, 0x27, 0xd6, 0x75, 0x55, 0x08, 0x4a, 0x93, 0xe1, 0x2a, 0x2b, 0xac, 0x10, 0x86, 0xf6, 0xc8, 0x7a, 0xa4, 0xc4, 0x2c, 0x49, 0xc1, 0x80, 0x94, 0x3d, 0xc5, 0xb9, 0x9b, 0x7b, 0xee, 0xfe, 0x1d, 0x79, 0x81, 0x5a, 0x69, 0xa3, 0x02, 0x5b, 0x26, 0xbd, 0xeb, 0x28, 0xa0, 0x3e, 0x6c, 0xc7, 0xe3, 0x23, 0xb9, 0x8c, 0xd4, 0x3c, 0x61, 0x66, 0xf6, 0xb7, 0xf4, 0x3b, 0x60, 0x1f, 0xb8, 0x66, 0xcb, 0x97, 0xd8, 0x58, 0x26, 0x2f, 0x2e, 0xb2, 0xfb, 0xe0, 0xf5, 0x3e, 0xd2, 0x49, 0x87, 0xc0, 0xb6, 0xa4, 0x84, 0x38, 0xa7, 0xc6, 0x14, 0xf3, 0x98, 0x8a, 0x01, 0x70, 0xd8, 0x7c, 0x14, 0x6b, 0x5a, 0x56, 0x06, 0x39, 0xa5, 0x4c, 0x2d, 0x00, 0x91, 0x6a, 0x6b, 0x92, 0x56, 0x21, 0x18, 0x59, 0xe6, 0x2d, 0xb9, 0x78, 0xdd, 0x8e, 0xe6, 0x07, 0xfe, 0x81, 0x7f, 0x0e, 0x7c, 0xf4, 0x1b, 0x78, 0x78, 0x86, 0xbb, 0x9d, 0xb1, 0x43, 0xad, 0x78, 0x04, 0x00, 0x88, 0x8e, 0xcd, 0x7b, 0x33, 0xdc, 0x1c, 0x8b, 0x7b, 0x7e, 0x33, 0x1f, 0x03, 0xdc, 0xc7, 0x84, 0xd9, 0x9e, 0xdd, 0x41, 0x8d, 0xe1, 0xe4, 0x4d, 0x04, 0xc1, 0xd5, 0x51, 0x08, 0x5f, 0x3e, 0xf8, 0x55, 0x8c, 0x9b, 0xae, 0xe4, 0x5a, 0x6a, 0x3f, 0x42, 0xb9, 0x01, 0x6b, 0x83, 0xb7, 0x3e, 0x82, 0x0a, 0xde, 0xfd, 0x67, 0x9a, 0x76, 0xfe, 0x16, 0x38, 0x0f, 0xc3, 0x6e, 0x24, 0xe5, 0xbf, 0x25, 0xfb, 0x41, 0x00, 0x0b, 0xfb, 0xd2, 0x5d, 0x37, 0x39, 0xaa, 0xb3, 0xb8, 0x8b, 0xee, 0x74, 0xb3, 0xfb, 0x0e, 0xe3, 0xed, 0xc2, 0x00, 0x7f, 0xd9, 0xd9, 0xd1, 0x2c, 0x9e, 0x30, 0xda, 0x7a, 0x71, 0x0d, 0x28, 0x38, 0x14, 0xe7, 0x64, 0xae, 0x88, 0x28, 0x15, 0x24, 0x0f, 0x46, 0x78, 0x23, 0xdd, 0x98, 0x4a, 0xdb, 0x39, 0xd6, 0x2d, 0x2c, 0x6a, 0x34, 0xd5, 0xe7, 0xce, 0x81, 0xdb, 0x0f, 0x77, 0x5f, 0xa1, 0xcc, 0x50, 0x1f, 0xe3, 0x2a, 0x3e, 0x1b, 0xf5, 0xb8, 0x72, 0x8a, 0x73, 0xf8, 0x80, 0x64, 0x41, 0xd2, 0x35, 0xa8, 0x59, 0xa3, 0x4f, 0x46, 0xd4, 0xac, 0x9f, 0x72, 0xe2, 0x62, 0x65, 0x04, 0xfb, 0x7e, 0x4a, 0x21, 0x33, 0xcf, 0x91, 0x2e, 0x5d, 0xa9, 0xfa, 0xee, 0xb8, 0x0e, 0xe2, 0xf0, 0x57, 0x2c, 0xa9, 0x45, 0xb7, 0x6d, 0xc3, 0x96, 0x6d, 0x52, 0xae, 0x69, 0xd8, 0x1a, 0x15, 0x54, 0x3c, 0x6b, 0xa3, 0x1e, 0x51, 0x24, 0x25, 0x83, 0xb6, 0x89, 0x98, 0xc4, 0xc7, 0xa3, 0x6e, 0x50, 0xda, 0x3d, 0xaf, 0xd5, 0x32, 0x7c, 0xb8, 0xd1, 0xf4, 0x08, 0x15, 0x7f, 0x95, 0xfe, 0x8d, 0x37, 0x25, 0xe9, 0x8b, 0x0c, 0x2a, 0xd4, 0x03, 0x57, 0xec, 0xee, 0x53, 0x38, 0x29, 0xe6, 0x60, 0xa7, 0x82, 0x8b, 0xa6, 0x33, 0xee, 0xb7, 0xbc, 0x0c, 0x30, 0x5e, 0xc1, 0x92, 0x4b, 0x21, 0xa6, 0xcb, 0xf7, 0xa6, 0x77, 0xc2, 0xac, 0xc6, 0x0a, 0x1d, 0x05, 0xae, 0xee, 0xe9, 0x7d, 0x91, 0xe6, 0xce, 0xed, 0x3d, 0xcc, 0xf5, 0x67, 0xa4, 0x08, 0x49, 0xf0, 0xd8, 0x28, 0x4c, 0xb4, 0x57, 0x36, 0x79, 0xc4, 0xdf, 0xc4, 0x40, 0x10, 0xc5, 0x96, 0xb5, 0xc1, 0x43, 0xc9, 0x14, 0x3d, 0x86, 0x65, 0x5d, 0xc4, 0xb4, 0xbf, 0x69, 0x10, 0x76, 0xb5, 0xe5, 0x98, 0xe1, 0xe7, 0x19, 0x47, 0xcf, 0x9d, 0x77, 0x0e, 0xcb, 0xd1, 0x20, 0x1d, 0xee, 0x2e, 0x6e, 0x5b, 0xa5, 0xd3, 0x59, 0x28, 0x3b, 0x1b, 0xa3, 0xf4, 0xcc, 0x55, 0xfa, 0x27, 0x9e, 0xf8, 0x00, 0xe8, 0x3f, 0xa0, 0xa5, 0x14, 0x9b, 0x31, 0x13, 0x6c, 0xa5, 0xfb, 0x05, 0x04, 0xa1, 0xba, 0x1c, 0xf4, 0xfd, 0xf4, 0x30, 0x16, 0x55, 0x61, 0x4a, 0x0f, 0xda, 0x7f, 0xa1, 0x1d, 0xf6, 0xa9, 0x2d, 0x4c, 0x2d, 0xec, 0x1e, 0xac, 0xba, 0x31, 0xae, 0x28, 0xaa, 0xc9, 0xb0, 0xc8, 0x39, 0x8c, 0x4f, 0xa1, 0xb8, 0xb4, 0x92, 0x4b, 0xe7, 0xf7, 0x6c, 0xf0, 0x07, 0x5b, 0xd1, 0x70, 0x40, 0xef, 0x67, 0x7b, 0x9e, 0xde, 0x54, 0x0a, 0xb9, 0x04, 0xfd, 0xb3, 0xc4, 0xdb, 0x90, 0x9b, 0xab, 0xa9, 0xc5, 0xdd, 0xaf, 0x9b, 0x9f, 0x46, 0x1f, 0x9c, 0xe8, 0x01, 0xa3, 0x92, 0xe7, 0x3b, 0xea, 0x31, 0xef, 0xba, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x58, 0xd0, 0xfa, 0x1b, 0xf4, 0x76, 0x32, 0x3a, 0xc8, 0xd3, 0x5d, 0x75, 0x65, 0x33, 0x3a, 0x8d, 0x35, 0xc3, 0x0b, 0x5c, 0xba, 0xb8, 0xb1, 0xe5, 0x97, 0x00, 0x66, 0x7f, 0x98, 0xd3, 0xa0, 0x41, 0xb7, 0x4b, 0xd5, 0xe6, 0x54, 0x49, 0x8d, 0x21, 0x29, 0xd8, 0xbf, 0x41, 0x56, 0x2f, 0xb5, 0xe6, 0x78, 0x3c, 0x91, 0xdf, 0x78, 0x9f, 0xf3, 0x87, 0xcc, 0xce, 0x27, 0x87, 0xa3, 0x9f, 0x32, 0x62, 0x70, 0x5a, 0x9a, 0x50, 0x4e, 0xeb, 0x74, 0x4f, 0xae, 0x35, 0x19, 0x7f, 0xf8, 0x49, 0xdc, 0xbc, 0x95, 0x7d, 0x7e, 0x21, 0xdb, 0x69, 0x11, 0x4f, 0xb1, 0xd5, 0x12, 0x7b, 0x69, 0xe0, 0x60, 0x8a, 0xeb, 0x82, 0x6b, 0x92, 0xbd, 0xa4, 0x6f, 0x78, 0xb9, 0x93, 0xdd, 0x7e, 0x6c, 0x63, 0xfb, 0x91, 0xfc, 0xb1, 0x65, 0xf7, 0xbd, 0xd5, 0x7c, 0xcc, 0xb0, 0xb3, 0x95, 0x96, 0xd6, 0x90, 0x4b, 0x39, 0xd0, 0x8a, 0xf0, 0xfb, 0xae, 0x00, 0x20, 0x07, 0x81, 0x79, 0xc6, 0x03, 0x23, 0x11, 0x68, 0x97, 0x3a, 0x3a, 0x8c, 0x84, 0xa4, 0x97, 0xcf, 0x71, 0x2b, 0x2a, 0x67, 0x5b, 0x19, 0x40, 0x3e, 0xf3, 0x72, 0x68, 0x8b, 0x0f, 0x7f, 0xd7, 0x72, 0xe0, 0x61, 0x13, 0x8a, 0xfd, 0x81, 0xbb, 0x88, 0x32, 0x83, 0x70, 0xb2, 0x21, 0xf7, 0xf2, 0xf9, 0x62, 0xb6, 0x95, 0xd7, 0x86, 0xec, 0x0e, 0xdc, 0x45, 0x2b, 0x82, 0x03, 0x7e, 0x96, 0x64, 0xb8, 0x60, 0xa2, 0x34, 0xa9, 0x43, 0x1b, 0xb4, 0x1e, 0xbe, 0x50, 0x8c, 0x47, 0x15, 0x29, 0xc1, 0xa1, 0x0d, 0x0f, 0x11, 0xde, 0xa1, 0xbb, 0x79, 0x60, 0x4b, 0x3b, 0xc1, 0xf5, 0xd1, 0x32, 0x21, 0x02, 0xf3, 0x94, 0x1f, 0xa1, 0xbb, 0x10, 0x05, 0x3e, 0xcf, 0x37, 0xef, 0x16, 0x63, 0x0f, 0x5d, 0x89, 0x2b, 0x1c, 0xe9, 0x95, 0x48, 0xab, 0x1f, 0xa3, 0xfb, 0x12, 0xe6, 0xd3, 0xd8, 0xa7, 0x48, 0xa4, 0xd0, 0xc9, 0x91, 0xe4, 0xfb, 0x1a, 0xe7, 0xf2, 0x2c, 0xaf, 0x0e, 0xa4, 0x9d, 0x2e, 0xac, 0x0f, 0x6c, 0x33, 0xdb, 0xdd, 0x8b, 0x14, 0x1c, 0x98, 0x06, 0x93, 0x1f, 0x27, 0x7a, 0x35, 0xea, 0xcb, 0x89, 0x05, 0x76, 0x09, 0x23, 0x5a, 0x1a, 0x75, 0x8c, 0x1d, 0xea, 0x10, 0x91, 0xcc, 0x56, 0x29, 0x7d, 0x71, 0x38, 0x29, 0x44, 0x68, 0x8a, 0xdb, 0xf1, 0x24, 0x36, 0xb1, 0x85, 0x78, 0x6e, 0x46, 0x1e, 0xb7, 0x92, 0x99, 0x1d, 0xf3, 0x02, 0x03, 0x80, 0x84, 0x67, 0x23, 0x7c, 0xe1, 0xe0, 0x3b, 0xbf, 0x21, 0x26, 0x4f, 0xad, 0xfb, 0x1c, 0xc1, 0xc7, 0x68, 0x2b, 0xde, 0x47, 0xf6, 0x41, 0xd8, 0xc6, 0xd6, 0x39, 0x41, 0x58, 0xce, 0x60, 0x1c, 0xb6, 0x49, 0x41, 0xb8, 0x66, 0x41, 0x02, 0x0e, 0xed, 0x62, 0x44, 0x8e, 0x19, 0x2f, 0xe9, 0x19, 0x85, 0x9c, 0x17, 0xc9, 0xdf, 0xa3, 0xf5, 0xc8, 0x45, 0x7c, 0x05, 0x7f, 0x74, 0xfd, 0x77, 0x7b, 0xab, 0xc4, 0xfc, 0x36, 0xb9, 0xa6, 0xbc, 0x70, 0xbc, 0x0d, 0x88, 0x32, 0xfa, 0xeb, 0x9c, 0x98, 0x7d, 0x50, 0x09, 0x80, 0xfa, 0xfc, 0xa1, 0x16, 0xf1, 0x84, 0x4e, 0xf9, 0xca, 0x5f, 0xdc, 0x18, 0x6b, 0x2b, 0xce, 0xf6, 0xfe, 0x37, 0xa9, 0x6b, 0xf2, 0x6d, 0xa3, 0xa2, 0x37, 0xfa, 0x8d, 0xd8, 0x7d, 0xa6, 0x82, 0xe0, 0xb8, 0x2e, 0x1e, 0x3c, 0xa3, 0x9d, 0x8f, 0x5d, 0xe3, 0x45, 0xec, 0x80, 0x3d, 0x9d, 0x61, 0x43, 0xbd, 0x93, 0xca, 0x27, 0xfd, 0x6d, 0x29, 0x2d, 0xff, 0x7a, 0xe2, 0x13, 0xc1, 0xc1, 0x16, 0x62, 0x80, 0x7c, 0xa1, 0x38, 0x2b, 0x41, 0x2e, 0x3c, 0x4f, 0xf8, 0xc0, 0xd9, 0xd8, 0x96, 0x17, 0xbb, 0x42, 0x89, 0x74, 0x8c, 0x4b, 0x4c, 0x4b, 0xeb, 0x58, 0xfc, 0x12, 0x97, 0x1b, 0xc0, 0x98, 0xbe, 0x82, 0x33, 0x40, 0x92, 0x7b, 0xb2, 0x87, 0xf8, 0xca, 0x9e, 0xa8, 0x8a, 0x44, 0x49, 0xe5, 0x9d, 0x59, 0x37, 0x8d, 0xe9, 0x30, 0xaf, 0x46, 0x4f, 0xb0, 0xe3, 0xc7, 0xff, 0x89, 0x81, 0x33, 0xa7, 0x9e, 0xe2, 0x73, 0x1d, 0x97, 0x4d, 0x54, 0x62, 0xf9, 0xf6, 0x5d, 0x27, 0x56, 0xef, 0x72, 0xd5, 0x30, 0xfc, 0x5e, 0x9a, 0xc8, 0xb9, 0x32, 0x7b, 0xbd, 0x2f, 0x54, 0x2d, 0xa5, 0x40, 0x9f, 0xf8, 0xe4, 0xc8, 0x7a, 0x11, 0x6c, 0xdf, 0x39, 0xcb, 0xc1, 0x9d, 0xef, 0x40, 0x13, 0xac, 0x26, 0xb8, 0xa4, 0x21, 0xf8, 0x1d, 0x66, 0x46, 0xa7, 0xee, 0x2f, 0xa2, 0x4a, 0xf3, 0x9f, 0x0d, 0x63, 0x87, 0x6d, 0xe0, 0x4f, 0x41, 0x62, 0xad, 0xbb, 0x6a, 0x27, 0x70, 0x10, 0xd3, 0x8a, 0x84, 0x12, 0xb5, 0x17, 0xef, 0x72, 0x64, 0x87, 0xdd, 0xda, 0xca, 0x21, 0x52, 0x17, 0x83, 0x53, 0x70, 0x41, 0x7d, 0x93, 0x17, 0x9d, 0x30, 0x8a, 0x99, 0x79, 0xd1, 0xf2, 0x6a, 0x2d, 0x04, 0x65, 0x97, 0x07, 0x37, 0xc0, 0x7a, 0x16, 0xbf, 0x2a, 0x31, 0x52, 0xe2, 0x8c, 0x91, 0x70, 0xf5, 0x1c, 0xe1, 0xa3, 0xe4, 0x8d, 0x48, 0xbb, 0x41, 0xf1, 0x2f, 0x2a, 0x46, 0x9f, 0xa1, 0x20, 0x0b, 0x9e, 0xe9, 0xc5, 0x14, 0x37, 0x0e, 0x4f, 0x99, 0x3d, 0x3e, 0x6d, 0x33, 0x47, 0x02, 0x5e, 0xae, 0x29, 0x61, 0x16, 0x46, 0xe2, 0x37, 0x62, 0x44, 0xcc, 0x3d, 0xfe, 0x5e, 0xcf, 0xb6, 0x92, 0x09, 0x6a, 0x55, 0xa5, 0x20, 0x48, 0xd1, 0x37, 0x80, 0xa8, 0xf5, 0x37, 0x17, 0x36, 0x99, 0x1b, 0x49, 0x30, 0x99, 0x11, 0x6b, 0x29, 0x6b, 0x16, 0x5b, 0x63, 0x81, 0x74, 0xbc, 0xcb, 0xc8, 0x37, 0xff, 0xff, 0x61, 0x85, 0x3a, 0x50, 0xb7, 0xaf, 0x88, 0x38, 0x2f, 0xc1, 0x0b, 0xa6, 0xa8, 0xcc, 0x27, 0x19, 0x2e, 0xb0, 0x0c, 0xfb, 0x8e, 0xfa, 0xa4, 0x4f, 0xb2, 0x1c, 0x16, 0x8c, 0xc6, 0xae, 0x7b, 0x4e, 0xf4, 0x19, 0x27, 0x7e, 0xdb, 0xa6, 0x4b, 0xb0, 0x93, 0xa8, 0x46, 0x1c, 0x5b, 0xcc, 0x89, 0x37, 0x64, 0x04, 0x3c, 0x96, 0x74, 0xba, 0x60, 0x55, 0x74, 0x33, 0xbf, 0xbc, 0xad, 0x07, 0x35, 0xef, 0xbc, 0x6e, 0x49, 0x94, 0x47, 0xc3, 0xdb, 0x70, 0x53, 0x80, 0x08, 0xd1, 0x7d, 0x0e, 0xbc, 0x2c, 0x3c, 0xb1, 0x5d, 0x77, 0xae, 0x76, 0xb5, 0x5c, 0x5f, 0xf3, 0x3a, 0x5a, 0xfc, 0xad, 0x2d, 0x7b, 0x8e, 0xf5, 0x33, 0x60, 0x04, 0x6b, 0x9c, 0xec, 0x89, 0x5c, 0xa6, 0x5c, 0xb7, 0xfa, 0x2d, 0x09, 0xa7, 0x96, 0x7f, 0x84, 0xc6, 0x26, 0xae, 0x75, 0x46, 0x39, 0x2b, 0x4b, 0xd5, 0x26, 0x0c, 0x4e, 0x57, 0x30, 0xf8, 0x57, 0x3b, 0xb7, 0x2b, 0xfa, 0x7b, 0x45, 0x4e, 0xb8, 0xf0, 0x05, 0x76, 0x9d, 0xc1, 0xbe, 0x64, 0x59, 0xce, 0xc7, 0xbb, 0x55, 0xd2, 0x05, 0x15, 0x34, 0xc8, 0x52, 0x31, 0xdf, 0x43, 0x92, 0xe3, 0x3a, 0x09, 0x50, 0xe4, 0x18, 0x43, 0x0c, 0x42, 0xc0, 0x56, 0x80, 0x5c, 0xeb, 0xc0, 0x58, 0xec, 0x3e, 0x58, 0xc7, 0x0a, 0xd2, 0xb8, 0x06, 0x6a, 0xeb, 0xec, 0x30, 0xf8, 0x0a, 0xa5, 0xde, 0xa4, 0x4d, 0x87, 0xde, 0xc7, 0x36, 0x41, 0xb0, 0xea, 0xad, 0x5e, 0x00, 0x2a, 0x73, 0x9d, 0x92, 0x34, 0xae, 0x96, 0xfb, 0xb5, 0xb8, 0x05, 0xa6, 0x62, 0x33, 0xb6, 0xb5, 0xad, 0xa2, 0x0a, 0x06, 0x69, 0x5d, 0xcb, 0x47, 0xe8, 0x6a, 0xbc, 0x5f, 0x6c, 0xc8, 0xea, 0x60, 0x53, 0x9e, 0x42, 0x80, 0x37, 0x45, 0xc7, 0x3e, 0xfd, 0xa8, 0xb2, 0x3c, 0x4e, 0x77, 0xd0, 0x25, 0xb4, 0x5a, 0x24, 0xce, 0x55, 0xc9, 0xa7, 0x88, 0x83, 0x71, 0x8e, 0x67, 0xda, 0x3b, 0xd3, 0x4a, 0x45, 0xae, 0x57, 0xbe, 0x9b, 0x64, 0xf8, 0x98, 0x11, 0xbe, 0x17, 0xa9, 0xac, 0xbe, 0x79, 0x98, 0x38, 0xc7, 0x5d, 0x19, 0x9e, 0x96, 0x41, 0xa4, 0x5d, 0xc7, 0x97, 0xa6, 0x0b, 0x4d, 0xdf, 0x0a, 0xf3, 0x85, 0xb9, 0x57, 0x34, 0x4d, 0xd3, 0xb3, 0xdd, 0x69, 0x6f, 0x42, 0x5c, 0xf7, 0xb7, 0x36, 0xff, 0x1e, 0x2c, 0x7f, 0xe9, 0x6b, 0xb8, 0x44, 0x5e, 0xbe, 0xd3, 0xed, 0x1f, 0xed, 0x1d, 0x81, 0xf1, 0x5b, 0xb4, 0x10, 0xc9, 0x7f, 0x03, 0x7a, 0xaa, 0x03, 0x7f, 0x0e, 0x4e, 0xba, 0x8a, 0x6e, 0x76, 0x60, 0xd7, 0xb7, 0x9a, 0xc2, 0xbf, 0x72, 0x54, 0x39, 0x23, 0x67, 0x55, 0x31, 0x27, 0xf7, 0xb1, 0x96, 0xd9, 0x6e, 0x55, 0xeb, 0x4f, 0xd9, 0xca, 0xe6, 0x12, 0xa9, 0x12, 0xd8, 0x12, 0xba, 0x71, 0x5d, 0xff, 0x69, 0xf3, 0xc2, 0x54, 0x1e, 0xd6, 0xb8, 0x7f, 0x72, 0x0c, 0x7c, 0xa5, 0xaf, 0x74, 0xff, 0xcb, 0x35, 0x77, 0x33, 0x14, 0xc3, 0xdc, 0x4e, 0xe8, 0xd8, 0xf0, 0x95, 0x05, 0xef, 0x8c, 0xf1, 0xf3, 0x5f, 0x5a, 0xed, 0xbd, 0x1d, 0x3e, 0x8c, 0xf9, 0x51, 0xad, 0xef, 0x97, 0x57, 0xaf, 0x82, 0x3f, 0x96, 0x96, 0x4b, 0x8e, 0xe2, 0x3c, 0x80, 0x71, 0x24, 0xd7, 0x68, 0x6d, 0xda, 0x0c, 0x1f, 0xa8, 0x0c, 0x5c, 0x6c, 0x7d, 0x83, 0xab, 0x6f, 0x9f, 0x23, 0xee, 0xbb, 0xc1, 0xca, 0x4c, 0xb4, 0x04, 0xcc, 0xb6, 0x9b, 0x5b, 0x9f, 0x61, 0x57, 0xf8, 0x25, 0x10, 0xa9, 0x18, 0x9d, 0x2f, 0x77, 0xdd, 0xc4, 0xe7, 0xa7, 0x7e, 0x0b, 0x97, 0x28, 0xc3, 0x31, 0xd4, 0xd9, 0xf9, 0x18, 0x07, 0x43, 0x19, 0xdd, 0x81, 0x08, 0x43, 0x73, 0xe4, 0x69, 0x0e, 0x91, 0x0a, 0xf1, 0x88, 0xbb, 0x9b, 0xa2, 0x90, 0x9d, 0xd1, 0x95, 0xc8, 0x0f, 0xdf, 0x70, 0x36, 0x60, 0xcb, 0xb4, 0x2e, 0xca, 0x02, 0x95, 0x52, 0x64, 0x64, 0x62, 0x60, 0x3d, 0xb1, 0xaa, 0x1f, 0xbc, 0xd5, 0x56, 0x6c, 0x42, 0x92, 0xdd, 0xfc, 0xb6, 0x4c, 0x91, 0x6c, 0x33, 0x04, 0xad, 0x74, 0x89, 0x46, 0x5a, 0xef, 0xa7, 0xe8, 0x30, 0xd8, 0x63, 0x9d, 0x4f, 0xe5, 0x8e, 0xa3, 0x14, 0x31, 0x65, 0x4b, 0x37, 0xde, 0x88, 0x29, 0xa6, 0x3b, 0x6a, 0x87, 0x2b, 0x1c, 0xa3, 0x0e, 0xef, 0x6f, 0x2f, 0xa3, 0x6d, 0xad, 0x32, 0x5e, 0x69, 0xed, 0x00, 0x1b, 0x8b, 0x90, 0xc3, 0xfa, 0x52, 0xfe, 0xea, 0xb5, 0x9f, 0xc4, 0xc5, 0x46, 0x31, 0x61, 0xf7, 0xa1, 0x33, 0xf0, 0x7c, 0xbf, 0xb4, 0xa4, 0x58, 0x16, 0xd8, 0x88, 0xba, 0xef, 0xc1, 0x9b, 0x6d, 0x3c, 0x6d, 0x1a, 0x48, 0x3c, 0x4f, 0x0b, 0x2b, 0x5e, 0x67, 0xb6, 0x62, 0x17, 0x50, 0xe4, 0x69, 0xfb, 0x0c, 0x5c, 0xe7, 0x3f, 0xfa, 0x4f, 0x68, 0x6b, 0x27, 0x52, 0x7e, 0xdc, 0xa1, 0xf3, 0x8e, 0xeb, 0x67, 0x06, 0xe0, 0xee, 0xe8, 0x13, 0xab, 0xa1, 0x6c, 0xe1, 0x3b, 0x19, 0x2f, 0xed, 0xa8, 0xe0, 0x57, 0x4f, 0x28, 0xd6, 0x67, 0xd2, 0x33, 0x7d, 0xb5, 0x54, 0x41, 0x79, 0x5a, 0x8e, 0xbd, 0x74, 0xeb, 0x50, 0x01, 0xf2, 0x59, 0x0b, 0xa0, 0x84, 0xbd, 0x9c, 0xcb, 0x18, 0xf0, 0x89, 0x17, 0xef, 0x54, 0x33, 0x7b, 0x08, 0xda, 0x59, 0xaa, 0x60, 0x83, 0xa2, 0x5c, 0xdc, 0x3d, 0xae, 0x38, 0xf8, 0x2f, 0x73, 0xfb, 0x25, 0x4b, 0x97, 0xee, 0xfd, 0xc6, 0x50, 0x4a, 0x50, 0xc8, 0x40, 0x0e, 0x49, 0xd1, 0x29, 0x0a, 0xc9, 0xf3, 0x87, 0x6d, 0x0b, 0x0f, 0x90, 0x7b, 0x77, 0xeb, 0x1f, 0x31, 0x23, 0x9b, 0x7d, 0xfd, 0xa8, 0x83, 0xe8, 0x93, 0x01, 0x29, 0x52, 0x10, 0xb0, 0x9e, 0x74, 0x52, 0x87, 0x92, 0x96, 0x1a, 0xf5, 0xf2, 0x99, 0xf0, 0x96, 0xe8, 0x08, 0xd2, 0xcc, 0x54, 0x5a, 0xe1, 0xd8, 0xf6, 0x42, 0xc2, 0xb6, 0x20, 0x7e, 0x41, 0x33, 0xb5, 0xdf, 0xde, 0x20, 0x0a, 0x94, 0x8b, 0x7f, 0xec, 0x23, 0x29, 0x77, 0xfb, 0xda, 0xc9, 0x53, 0x28, 0x5e, 0x45, 0x0a, 0xd6, 0xa3, 0x13, 0x45, 0x17, 0x24, 0x22, 0xa3, 0x7e, 0x2a, 0xf2, 0x78, 0xce, 0x9b, 0x78, 0xc9, 0xe5, 0x22, 0xad, 0x6b, 0xcd, 0xcb, 0xc6, 0x7b, 0x41, 0xce, 0x38, 0x38, 0x46, 0x6a, 0xe0, 0xff, 0xc7, 0x42, 0x06, 0x1a, 0x38, 0xe0, 0xa1, 0x92, 0xab, 0x4b, 0x79, 0xf2, 0x22, 0x1b, 0x86, 0xeb, 0x9c, 0x2c, 0xcd, 0x75, 0x3d, 0xc3, 0xe8, 0x6c, 0x2f, 0xb0, 0x51, 0x70, 0x9f, 0x5f, 0x08, 0x9c, 0xb1, 0x15, 0x12, 0x75, 0x60, 0xfc, 0x15, 0x15, 0x95, 0x90, 0x55, 0x42, 0x3a, 0xc4, 0x2a, 0x25, 0x98, 0x89, 0x74, 0xbe, 0x5c, 0x4b, 0xd7, 0xcb, 0xa8, 0xed, 0x36, 0xe3, 0x82, 0x41, 0xd3, 0x7e, 0x57, 0xb7, 0x06, 0x36, 0x6e, 0xcf, 0xbb, 0x04, 0xbc, 0xa0, 0xfa, 0x61, 0xcb, 0x29, 0x8a, 0x49, 0xbb, 0x70, 0xc1, 0x9e, 0xc7, 0xea, 0xe3, 0xd5, 0xfb, 0x8c, 0xff, 0xee, 0xc4, 0x7e, 0x0f, 0xe0, 0x7a, 0xd9, 0xc3, 0x81, 0x2d, 0x97, 0x4a, 0x1f, 0x6a, 0xd2, 0x1f, 0xa3, 0x36, 0x05, 0x84, 0xda, 0xa8, 0x24, 0x8b, 0x73, 0x64, 0x18, 0xa2, 0x05, 0x5a, 0x6c, 0x87, 0xfc, 0x94, 0x4a, 0x41, 0x6e, 0x13, 0xbd, 0x5c, 0x4b, 0xa6, 0x17, 0x08, 0xaa, 0x8d, 0x99, 0xe1, 0xb2, 0x2b, 0x77, 0x37, 0x0f, 0xd0, 0x72, 0x24 }; +constexpr AccessUnit ATRACX_SIZE_MISMATCH_EXPECTED_AU = { 0x15f90, 0x159b2, false, 0, {}, { 0x71, 0x4c, 0x99, 0x14, 0x4a, 0x0d, 0x1d, 0xe6, 0x05, 0x5c, 0x64, 0xa7, 0xa0, 0xd6, 0x4b, 0x56, 0x87, 0xe4, 0x5d, 0x61 } }; + +const std::vector AC3_SIZE_MISMATCH_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x77, 0x31, 0xfd, 0x1c, 0x4b, 0x30, 0xa2, 0xe6, 0x40, 0x35, 0x38, 0xe1, 0xea, 0xc9, 0xcb, 0x6d, 0x24, 0x95, 0x8e, 0x57, 0xe6, 0xfc, 0x72, 0xf4, 0x35, 0xca, 0x6d, 0x13, 0x5a, 0x24, 0xa4, 0x03, 0x6e, 0x1d, 0x22, 0x04, 0xc8, 0xdc, 0xa5, 0x99, 0x12, 0x6d, 0xc6, 0x69, 0x0e, 0x5e, 0x59, 0x09, 0x64, 0x9d, 0xa7, 0x5e, 0xc2, 0x7f, 0xe8, 0x0f, 0xe9, 0x09, 0xc5, 0x5f, 0xe7, 0x52, 0x84, 0x3a, 0xdf, 0xf7, 0x73, 0x53, 0x36, 0x46, 0xc9, 0x02, 0xf5, 0x33, 0xc9, 0x73, 0x46, 0xe2, 0x68, 0xbd, 0xf5, 0x51, 0x86, 0xd9, 0x16, 0x54, 0x38, 0x35, 0xf5, 0xf6, 0xe5, 0x0b, 0xa4, 0xe3, 0xcb, 0xea, 0x2d, 0x55, 0x22, 0xcb, 0xf8, 0xbc, 0x8f, 0x53, 0xa4, 0xf9, 0xaa, 0x85, 0x4f, 0x7e, 0xab, 0xec, 0x25, 0xca, 0x27, 0x9c, 0xc6, 0x99, 0xbf, 0xb0, 0x68, 0x5d, 0x57, 0xb8, 0x60, 0x0a, 0xa6, 0x95, 0xeb, 0xdf, 0x64, 0x11, 0x73, 0x18, 0x6e, 0x64, 0x19, 0xd3, 0x7b, 0x52, 0xc9, 0x05, 0x18, 0x39, 0x20, 0xb2, 0x07, 0xf0, 0x87, 0xe7, 0xff, 0xa3, 0x3a, 0x4b, 0xa4, 0xf2, 0xce, 0xcf, 0xf5, 0xcc, 0x0f, 0xdf, 0x67, 0x31, 0x8c, 0x77, 0x99, 0x81, 0x28, 0xa9, 0x58, 0xd3, 0x55, 0x8e, 0xdc, 0xb6, 0xae, 0x9f, 0x1f, 0x3b, 0xb8, 0x45, 0x97, 0xfb, 0x7b, 0xc1, 0x27, 0x4e, 0x65, 0xcc, 0xc7, 0xa5, 0xea, 0xd2, 0x0e, 0xbf, 0x68, 0x9a, 0xb5, 0x4e, 0xee, 0x2e, 0xc7, 0x18, 0x5a, 0x89, 0x3d, 0x54, 0x0d, 0x62, 0x6d, 0x68, 0x71, 0x91, 0xc4, 0x05, 0x4e, 0xd5, 0x11, 0x8e, 0x16, 0xe3, 0x54, 0xd2, 0xa4, 0x30, 0xf4, 0xde, 0xfc, 0xdc, 0x85, 0xf0, 0xf4, 0x0a, 0xb4, 0xd5, 0xfb, 0xc7, 0xff, 0x65, 0xfe, 0x1c, 0x4f, 0xda, 0xfc, 0x38, 0x77, 0xc4, 0xfc, 0xfe, 0xe4, 0x78, 0x25, 0xe6, 0xd4, 0x0c, 0x43, 0x51, 0xea, 0x9d, 0x46, 0xb2, 0xd6, 0x9e, 0xc2, 0x82, 0x5f, 0xbb, 0x0e, 0xfb, 0xb6, 0xf4, 0x7a, 0x54, 0x92, 0x08, 0x8d, 0xcf, 0xae, 0x37, 0x63, 0x18, 0xc1, 0xf3, 0xa9, 0xa2, 0x8f, 0x7c, 0xd1, 0x06, 0xfe, 0xfc, 0x4d, 0xdc, 0xb1, 0x6b, 0x42, 0xa1, 0xaf, 0xc6, 0x4a, 0xd4, 0x3b, 0x4a, 0xad, 0xb4, 0x16, 0x9b, 0x39, 0x85, 0x29, 0x97, 0xd0, 0x94, 0xc6, 0x48, 0x6c, 0x2f, 0xa9, 0x19, 0x48, 0x62, 0x8d, 0x3b, 0x8b, 0xec, 0x0b, 0xa7, 0xca, 0xd0, 0x11, 0x93, 0xe3, 0xb8, 0x50, 0x93, 0x16, 0x42, 0xe9, 0x4f, 0x8c, 0x43, 0xfe, 0x6b, 0xcc, 0x17, 0x06, 0x18, 0x3b, 0xd3, 0xd1, 0xb0, 0xb4, 0xa5, 0x4c, 0xb3, 0xaa, 0x04, 0xbf, 0xc3, 0x08, 0xe1, 0x7d, 0xae, 0xa4, 0x5f, 0x95, 0xf3, 0x17, 0x62, 0x9c, 0x64, 0x5f, 0x26, 0x4f, 0x06, 0xed, 0x4e, 0x79, 0x18, 0x03, 0x4b, 0xb8, 0x61, 0xbb, 0x5a, 0xae, 0x51, 0xc3, 0x32, 0x67, 0xe5, 0xc2, 0x5a, 0x03, 0x2e, 0xee, 0xf8, 0x07, 0x4b, 0x06, 0x47, 0xe2, 0x6b, 0x0e, 0x08, 0xa7, 0xab, 0x1e, 0xa0, 0xf4, 0x85, 0x87, 0xb9, 0xec, 0xf7, 0x53, 0x8b, 0xbf, 0xf3, 0x4e, 0xb6, 0x5f, 0x00, 0x32, 0x5c, 0x07, 0xce, 0xc6, 0x9a, 0x24, 0xb7, 0x9e, 0x31, 0x20, 0xb5, 0x54, 0x79, 0x0e, 0x53, 0xca, 0x95, 0x09, 0xc9, 0x12, 0x8a, 0xb5, 0xed, 0xab, 0xc8, 0xb4, 0xdb, 0x76, 0x13, 0xb5, 0x10, 0x0a, 0x45, 0x78, 0xd4, 0xc5, 0x81, 0x3a, 0x11, 0x99, 0x14, 0x44, 0x43, 0x19, 0x6f, 0x16, 0x77, 0xd7, 0x14, 0xeb, 0x59, 0xb3, 0x55, 0xae, 0x69, 0xc9, 0x72, 0xd7, 0xb6, 0x50, 0x2a, 0xc2, 0xe0, 0x27, 0x72, 0xc7, 0xcf, 0xc5, 0x42, 0x32, 0x38, 0x92, 0xf3, 0x1a, 0x46, 0x90, 0x90, 0xa5, 0x5d, 0xf6, 0x46, 0xf7, 0x3e, 0x0c, 0x1a, 0x6e, 0x71, 0x2b, 0x30, 0xe0, 0x46, 0x6b, 0x9a, 0xc6, 0x2b, 0xb2, 0x89, 0xdb, 0x52, 0xe9, 0x5a, 0x58, 0x9b, 0x50, 0xb5, 0x75, 0xe1, 0xfa, 0x66, 0xf3, 0x69, 0x1e, 0x78, 0x90, 0x8e, 0x4c, 0x0c, 0xd4, 0xe7, 0x9d, 0xbc, 0x02, 0x8b, 0x2a, 0xfb, 0xca, 0x6d, 0x68, 0x28, 0x1e, 0x47, 0x08, 0x24, 0xe3, 0xce, 0x05, 0x60, 0x0e, 0xe1, 0xf7, 0x17, 0x13, 0xbd, 0xd3, 0x97, 0x78, 0x9b, 0xd5, 0x5a, 0x61, 0x4a, 0x16, 0x41, 0xb9, 0x37, 0xa4, 0x92, 0xe5, 0xc5, 0x99, 0x73, 0xf6, 0x14, 0xcd, 0xfd, 0x7a, 0x9e, 0x6c, 0x39, 0x00, 0x9a, 0xb1, 0x77, 0x7e, 0x78, 0x11, 0xef, 0x63, 0xec, 0xcc, 0x47, 0x71, 0xed, 0xd5, 0x64, 0xa5, 0xe6, 0xc7, 0xf9, 0x6c, 0xf0, 0x6d, 0x7b, 0x3a, 0x78, 0x4e, 0x8e, 0x8e, 0x9c, 0xbd, 0x69, 0x62, 0xdc, 0xec, 0xd1, 0xe0, 0xe1, 0x8f, 0x7f, 0x4e, 0x57, 0x7a, 0x7e, 0xa6, 0x25, 0x7d, 0xfa, 0xee, 0x6a, 0x7c, 0x67, 0x79, 0x80, 0x83, 0xb7, 0x7e, 0xb0, 0x4b, 0xba, 0x2f, 0x97, 0xba, 0x90, 0xae, 0x1a, 0xe5, 0x28, 0xea, 0xf6, 0x18, 0xe8, 0xc7, 0xfb, 0xb8, 0xa9, 0xa0, 0x0f, 0x15, 0x34, 0x14, 0x92, 0xfe, 0x84, 0x9b, 0x76, 0x83, 0xba, 0xe4, 0x07, 0xd6, 0x4e, 0x7f, 0xa7, 0xfc, 0x42, 0xd2, 0x50, 0x37, 0x96, 0x25, 0xe3, 0x2e, 0xfd, 0x4c, 0xdc, 0xbe, 0x6b, 0x09, 0x49, 0xa5, 0x46, 0xa9, 0xdd, 0x96, 0x70, 0x5c, 0x30, 0x6d, 0xbf, 0x5a, 0x3c, 0xda, 0x39, 0x56, 0xe4, 0x42, 0x73, 0x99, 0xcf, 0x1c, 0x0b, 0x2d, 0x22, 0x8d, 0x6e, 0xd0, 0x1e, 0xb9, 0x6c, 0x90, 0xc8, 0xfd, 0xe2, 0xe8, 0xdc, 0x81, 0x14, 0x34, 0xdd, 0x59, 0xdf, 0xce, 0xc0, 0x05, 0x43, 0xb6, 0x49, 0xbd, 0x15, 0xd0, 0xe9, 0xea, 0x3b, 0x46, 0xe3, 0x88, 0xe8, 0xa2, 0x0a, 0xf4, 0x28, 0xf1, 0xc5, 0xa4, 0xdd, 0x8b, 0x06, 0xe2, 0x84, 0x93, 0xa2, 0xc1, 0x87, 0xdd, 0x3f, 0x49, 0x4b, 0x88, 0x58, 0x2c, 0x93, 0x3c, 0x89, 0xb9, 0x30, 0xda, 0x8b, 0x22, 0x23, 0x35, 0x57, 0x3e, 0x2c, 0xe9, 0x09, 0x2c, 0xd0, 0x3a, 0x12, 0xe7, 0x9b, 0xb0, 0xd5, 0x3c, 0x12, 0xc3, 0x7c, 0x41, 0x51, 0x5b, 0x58, 0x3e, 0xf0, 0x0c, 0x90, 0x26, 0xa5, 0xab, 0xe9, 0x50, 0x6b, 0x50, 0x89, 0x0d, 0x66, 0xc0, 0x82, 0x5e, 0x49, 0xd2, 0x55, 0xea, 0xbc, 0x8f, 0x98, 0x21, 0x91, 0xad, 0xed, 0x23, 0x58, 0xda, 0xa9, 0x7f, 0xa6, 0xd5, 0xca, 0x63, 0x37, 0x72, 0x95, 0x6e, 0x76, 0xc7, 0x0c, 0x6e, 0xe1, 0xa9, 0x5e, 0x91, 0x66, 0xab, 0xa3, 0x63, 0xa1, 0xc0, 0xd9, 0x6a, 0x28, 0x79, 0xdd, 0xf1, 0xb1, 0x8a, 0x8f, 0x3c, 0xee, 0x62, 0x22, 0x73, 0x72, 0xb4, 0xfa, 0x2d, 0x1a, 0x2e, 0x72, 0x97, 0xb0, 0x8f, 0x78, 0x8c, 0x8e, 0xd8, 0xa7, 0xfb, 0xb4, 0xfa, 0x78, 0xee, 0x16, 0x6c, 0x8c, 0x7e, 0x64, 0xc3, 0xe6, 0xef, 0x3b, 0xbb, 0x95, 0xbb, 0xc4, 0x92, 0xaf, 0x9d, 0x0a, 0xca, 0xd5, 0x29, 0xf8, 0x51, 0x2f, 0xbf, 0x53, 0xc3, 0x6f, 0x0d, 0x90, 0x7e, 0x82, 0x05, 0x01, 0xfb, 0xee, 0x15, 0x8b, 0x04, 0xcc, 0xae, 0xb6, 0x7c, 0x57, 0x58, 0xba, 0x6a, 0xfc, 0x17, 0x40, 0x3b, 0x1c, 0x08, 0xed, 0xb9, 0xdf, 0x70, 0x4f, 0x3b, 0x02, 0xbf, 0x2d, 0x5f, 0x06, 0x74, 0x34, 0x45, 0x41, 0x4f, 0xef, 0x40, 0x79, 0x02, 0x57, 0x0d, 0x74, 0xd7, 0x2a, 0x03, 0x92, 0xa1, 0xa8, 0x03, 0xc2, 0x4e, 0x34, 0x82, 0xf5, 0x70, 0x6d, 0x00, 0x6b, 0x3a, 0xc3, 0x87, 0x73, 0x74, 0xec, 0xa4, 0xf6, 0xd7, 0xda, 0x8f, 0x4d, 0xef, 0x73, 0x79, 0x3e, 0x1b, 0x45, 0xcb, 0x27, 0x10, 0x5b, 0xc8, 0xe7, 0xd4, 0x88, 0x7d, 0x45, 0xdd, 0x12, 0x26, 0xf1, 0x66, 0x7e, 0xfd, 0xa9, 0xb7, 0x71, 0xd8, 0xbd, 0x43, 0x69, 0x51, 0xe4, 0x37, 0xcd, 0x27, 0x21, 0x9a, 0x73, 0xef, 0xf0, 0x7f, 0x76, 0xbf, 0xfe, 0x15, 0xbd, 0x68, 0x6e, 0x6e, 0xbc, 0xf8, 0xad, 0x76, 0xd5, 0x19, 0xd0, 0x01, 0x0c, 0xa1, 0xe1, 0x9e, 0xdd, 0x61, 0x37, 0xe8, 0x80, 0x08, 0xcb, 0x21, 0x4a, 0xf9, 0x2c, 0x32, 0xf4, 0x30, 0x9e, 0x51, 0x2a, 0x7f, 0xda, 0x74, 0x72, 0x23, 0x7f, 0x33, 0xa7, 0xe8, 0xa6, 0x18, 0xe5, 0x3c, 0xa6, 0x11, 0x1c, 0x43, 0x1f, 0x4f, 0xb0, 0xb6, 0x15, 0x8b, 0x89, 0x6e, 0xb8, 0xc3, 0x97, 0xe3, 0xe5, 0x65, 0x32, 0x0a, 0xe4, 0xae, 0x28, 0x4d, 0x76, 0x33, 0x66, 0x8a, 0x58, 0x36, 0xce, 0xa3, 0xab, 0x36, 0x3f, 0x42, 0x21, 0xa8, 0xa9, 0x8f, 0xe5, 0x6b, 0x75, 0x36, 0x4e, 0x60, 0x8f, 0x06, 0x1d, 0x19, 0x9d, 0x99, 0xba, 0x34, 0xe7, 0xe9, 0x6c, 0xd7, 0x90, 0xc7, 0x3d, 0x12, 0x9b, 0xc8, 0x6b, 0x11, 0xb5, 0x46, 0xf0, 0x1b, 0x1d, 0xdb, 0xe3, 0xc0, 0x3d, 0x2f, 0x85, 0xa9, 0x64, 0xe2, 0x74, 0xa7, 0x2d, 0xe1, 0x81, 0x6b, 0x96, 0xa2, 0x81, 0xba, 0xd4, 0x5c, 0x95, 0xc8, 0xa3, 0xc1, 0x95, 0x65, 0x5d, 0x91, 0xca, 0xa6, 0x9e, 0x1d, 0x1b, 0x41, 0x3a, 0x9a, 0x68, 0x9f, 0x51, 0x59, 0x1a, 0x81, 0xe0, 0x46, 0x02, 0x46, 0xfd, 0x11, 0xe5, 0x48, 0x86, 0xde, 0x7d, 0xed, 0x3c, 0xb1, 0x0d, 0x81, 0x1d, 0x13, 0x47, 0x0c, 0xda, 0xd8, 0xe0, 0xac, 0x42, 0xb8, 0x0b, 0x29, 0x73, 0x36, 0xe8, 0xaf, 0x95, 0xf3, 0xce, 0xdf, 0xe1, 0xd7, 0x62, 0xe3, 0xac, 0x53, 0x8d, 0x7b, 0x63, 0x13, 0x3b, 0xdc, 0xd3, 0x9a, 0xc9, 0x3f, 0x71, 0x2d, 0x3e, 0xef, 0x48, 0x82, 0x15, 0xe6, 0xb7, 0xbb, 0x56, 0xd6, 0x7d, 0xda, 0xb3, 0x97, 0x39, 0x96, 0xc5, 0xa6, 0x66, 0x8b, 0xb1, 0x94, 0x93, 0x40, 0xa5, 0xc2, 0x52, 0x34, 0x04, 0xce, 0xeb, 0xf4, 0x93, 0x37, 0xa5, 0xa6, 0xd0, 0xef, 0x38, 0x74, 0x19, 0x82, 0x7d, 0x77, 0x47, 0x8c, 0xac, 0x33, 0x82, 0x2b, 0x23, 0x71, 0x28, 0xb0, 0xa5, 0x93, 0xc2, 0x30, 0x5b, 0x6e, 0x30, 0xa8, 0xcc, 0x42, 0x59, 0x10, 0xb9, 0x91, 0x1f, 0xe0, 0x2c, 0x5d, 0x54, 0x1e, 0xef, 0x03, 0x3d, 0x76, 0xb4, 0x6e, 0xc3, 0x0a, 0x6a, 0x35, 0xac, 0xda, 0x96, 0xfa, 0xb5, 0x33, 0x48, 0xe1, 0x2f, 0x2d, 0x7d, 0xec, 0x53, 0x6e, 0xfe, 0xa5, 0x50, 0x2f, 0x9b, 0x00, 0x22, 0xed, 0x02, 0x90, 0xe1, 0xa1, 0xb8, 0x5d, 0x86, 0x28, 0xac, 0x45, 0xee, 0xfa, 0x84, 0x72, 0x30, 0xe8, 0xe7, 0x21, 0x65, 0xe6, 0xcc, 0x3c, 0x99, 0x35, 0x97, 0x25, 0xd8, 0x86, 0x49, 0x14, 0xde, 0x2e, 0x5e, 0x1c, 0xf7, 0x8f, 0x2c, 0xd1, 0x5d, 0x83, 0x9a, 0x43, 0xf8, 0x6c, 0x93, 0xdd, 0x8a, 0x51, 0xab, 0xd4, 0x3b, 0x28, 0xa8, 0x02, 0x40, 0x94, 0x67, 0x23, 0x83, 0x7b, 0xff, 0xc2, 0xcd, 0x2a, 0x06, 0x05, 0x9e, 0xae, 0x0f, 0x5f, 0xfa, 0x80, 0x2a, 0xa9, 0x25, 0xab, 0x32, 0x38, 0x4a, 0x19, 0x2b, 0x51, 0x57, 0xb1, 0x15, 0xba, 0x1b, 0x1d, 0x0b, 0xee, 0xfb, 0xe5, 0xe1, 0x31, 0x10, 0x63, 0x36, 0x56, 0x0a, 0xd9, 0x13, 0xfe, 0x85, 0x0a, 0xf0, 0x8e, 0x2b, 0xc0, 0x9e, 0xf8, 0x90, 0xc5, 0x9d, 0xf3, 0x8c, 0x2f, 0xb8, 0x3f, 0x67, 0x46, 0x8c, 0xec, 0xab, 0xac, 0x29, 0xcf, 0x5d, 0x6b, 0x26, 0x94, 0x9a, 0x90, 0x65, 0x6a, 0x7f, 0x76, 0xad, 0xc2, 0x88, 0x31, 0xcc, 0x10, 0xaa, 0xf2, 0x26, 0xc8, 0x0c, 0x30, 0xf0, 0x9d, 0xcd, 0xf0, 0x9c, 0xfe, 0xb3, 0x34, 0x4f, 0x7f, 0x9d, 0x8d, 0xdb, 0x46, 0x04, 0x2c, 0x8a, 0xa3, 0x1a, 0xab, 0x9a, 0xa6, 0x7f, 0xad, 0xec, 0x05, 0x76, 0x43, 0xd5, 0x42, 0x94, 0x2c, 0xea, 0x4e, 0x2a, 0x89, 0x11, 0x4a, 0x0b, 0x3c, 0x80, 0xde, 0x86, 0x61, 0x90, 0x9d, 0x2a, 0x44, 0xe7, 0xcf, 0x8c, 0xae, 0xce, 0xf0, 0xa8, 0xeb, 0xaf, 0x8d, 0xd9, 0xd0, 0x09, 0xb8, 0xce, 0x28, 0x0e, 0x28, 0xe0, 0xeb, 0x7b, 0x7c, 0x44, 0xc4, 0x91, 0x02, 0x34, 0xdc, 0x27, 0xbf, 0xd4, 0x75, 0x7a, 0xa5, 0x4f, 0x6b, 0x70, 0xf5, 0x96, 0x80, 0x43, 0xe0, 0xb3, 0x93, 0x06, 0xc0, 0xdc, 0x29, 0x14, 0x3e, 0x80, 0x82, 0x95, 0x03, 0xf8, 0xe5, 0xbf, 0x95, 0xe9, 0x9c, 0xdc, 0xbf, 0xc6, 0xac, 0xb6, 0x0f, 0xb7, 0x95, 0xc9, 0x8e, 0x15, 0xc7, 0x9c, 0x1f, 0xd4, 0xae, 0xfe, 0xa1, 0xe8, 0x19, 0x72, 0xb4, 0xd1, 0x14, 0x49, 0xe3, 0x53, 0x9a, 0x3a, 0x30, 0x45, 0x2c, 0xa0, 0xf0, 0x21, 0xc8, 0xee, 0x61, 0x8e, 0x2a, 0xa3, 0x1b, 0xfd, 0xd7, 0xf4, 0x04, 0xd6, 0xa6, 0xa4, 0xb7, 0x1e, 0x13, 0x5c, 0x5a, 0x0e, 0xf8, 0x7b, 0xb6, 0x10, 0x07, 0x35, 0xc4, 0x1b, 0x50, 0xa9, 0xd9, 0x9b, 0xdb, 0xa8, 0xf0, 0x67, 0xa8, 0x3e, 0x50, 0x71, 0x32, 0xfb, 0x37, 0xfe, 0x51, 0xff, 0x18, 0x57, 0x3a, 0xce, 0x7f, 0x50, 0x5c, 0xb2, 0xd6, 0xd8, 0xc1, 0xb4, 0x71, 0xe6, 0x49, 0xf1, 0x39, 0xad, 0x9b, 0xea, 0x2e, 0x8f, 0x68, 0x6d, 0x05, 0x34, 0x30, 0x9d, 0x72, 0x7c, 0x50, 0x98, 0xcc, 0x55, 0x18, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x1d, 0x0b, 0x3f, 0xee, 0x73, 0x16, 0x75, 0x44, 0x00, 0x0f, 0x64, 0xd1, 0x17, 0xe7, 0x11, 0xdc, 0x36, 0x45, 0xe8, 0x21, 0x49, 0x4b, 0x34, 0x34, 0xc7, 0x5a, 0x5d, 0x6e, 0xbd, 0x78, 0xae, 0xcd, 0x9d, 0xe4, 0xe3, 0xfc, 0x3c, 0x3c, 0x75, 0x39, 0x2e, 0xb3, 0x7b, 0xea, 0x8f, 0xd8, 0x26, 0xca, 0x15, 0x60, 0xd2, 0x25, 0x61, 0xf9, 0x0b, 0x6a, 0x6b, 0x98, 0xc3, 0x6b, 0x8c, 0xeb, 0x17, 0xa5, 0x44, 0x7a, 0xe0, 0x82, 0xc5, 0xc8, 0xc2, 0xb5, 0xdd, 0x62, 0x10, 0xce, 0x4c, 0xa8, 0x1f, 0x5d, 0xe1, 0x84, 0xdd, 0xa4, 0x45, 0xdf, 0x0c, 0xb1, 0xb8, 0x93, 0x2b, 0x4d, 0x72, 0x4d, 0x1c, 0x4b, 0xbe, 0x9c, 0x89, 0xfa, 0xdf, 0xb4, 0x10, 0x9b, 0x67, 0x40, 0xa2, 0xaf, 0x44, 0xd2, 0x3f, 0xe1, 0x8d, 0xbb, 0xa0, 0x3f, 0x04, 0xf5, 0xd2, 0x29, 0x7d, 0x2e, 0xfa, 0x10, 0x0f, 0x1d, 0x94, 0xa1, 0x69, 0x0f, 0x42, 0x69, 0x21, 0xb0, 0xf3, 0x7a, 0x0a, 0xb3, 0xee, 0xc6, 0x31, 0x48, 0xb8, 0xda, 0xb7, 0x1a, 0xcf, 0xdb, 0x7f, 0x77, 0x5e, 0x10, 0xa3, 0x7b, 0xfa, 0x4e, 0x51, 0x9d, 0x20, 0x92, 0x11, 0x15, 0x2e, 0xed, 0x22, 0x77, 0x17, 0x3d, 0xf9, 0x98, 0x3f, 0x86, 0x9f, 0x96, 0x62, 0xbf, 0xdf, 0x1c, 0xb9, 0x46, 0xfa, 0xa0, 0xf5, 0x6c, 0x08, 0x9f, 0x61, 0x9a, 0x79, 0x6b, 0xc2, 0x4b, 0x47, 0xfc, 0x8d, 0xfd, 0x49, 0x10, 0x63, 0x32, 0x5b, 0x78, 0xd7, 0x36, 0x75, 0x42, 0x86, 0xba, 0xf4, 0x23, 0x12, 0xe1, 0xa2, 0x4d, 0x8f, 0x33, 0x0d, 0xb6, 0xe6, 0x87, 0x93, 0x8b, 0xdd, 0x01, 0x60, 0x80, 0xb3, 0xe9, 0xa5, 0x8f, 0xd4, 0x91, 0x5d, 0x8f, 0xd5, 0x52, 0x75, 0x11, 0xae, 0xb1, 0xd1, 0x3b, 0x18, 0x01, 0x6b, 0x83, 0xba, 0xa9, 0x9d, 0xa5, 0x8d, 0x4d, 0xe9, 0x7c, 0x11, 0x19, 0x45, 0x30, 0xb7, 0xbb, 0xd2, 0x07, 0x60, 0x92, 0x54, 0x2f, 0xb4, 0xe3, 0xa1, 0xb2, 0xa6, 0xf9, 0x14, 0xb5, 0xc4, 0x30, 0x7a, 0x4d, 0x27, 0x1c, 0x2f, 0xfc, 0xbf, 0x34, 0xa6, 0xa8, 0xf4, 0x78, 0x0f, 0x16, 0x27, 0xeb, 0xac, 0xd7, 0x0f, 0x6a, 0xac, 0xf4, 0x4a, 0x73, 0xff, 0x30, 0x2b, 0xeb, 0x6a, 0x92, 0xd3, 0x09, 0xc4, 0xcd, 0x8a, 0x74, 0x3c, 0x6d, 0x61, 0x18, 0xd2, 0xcb, 0x61, 0xfb, 0x0c, 0x61, 0x6d, 0xb2, 0xe9, 0x1e, 0xfb, 0x8b, 0x15, 0xc3, 0x84, 0xdd, 0x7b, 0x34, 0xd7, 0x81, 0x99, 0xae, 0x1f, 0xce, 0xc3, 0x61, 0x95, 0x27, 0x5d, 0x25, 0xb2, 0x41, 0x7f, 0x9e, 0x21, 0xc7, 0xb9, 0x56, 0x6b, 0x41, 0xcd, 0x19, 0xef, 0xaa, 0x85, 0x14, 0x62, 0x53, 0xde, 0x13, 0x52, 0x8d, 0x25, 0x4b, 0xf8, 0xad, 0x8d, 0x09, 0xfb, 0x86, 0x30, 0xcf, 0xb5, 0xb9, 0xc8, 0x4e, 0x2b, 0x45, 0x74, 0x03, 0x87, 0xde, 0x51, 0x28, 0x65, 0x20, 0xfc, 0xf1, 0x27, 0x73, 0x7a, 0xfb, 0xff, 0xa6, 0x2f, 0x82, 0xb1, 0xe1, 0xca, 0xb7, 0x92, 0x60, 0x4e, 0xfd, 0xf6, 0x4f, 0x48, 0x4e, 0xfe, 0xd8, 0x80, 0xaf, 0xc7, 0xb2, 0xc4, 0xa1, 0x82, 0x86, 0x7d, 0x4b, 0x1e, 0x3c, 0xac, 0x76, 0xc5, 0x9b, 0x9b, 0x46, 0x5f, 0xdc, 0x67, 0xde, 0xa1, 0x04, 0x2a, 0x1e, 0x6e, 0x7f, 0xf8, 0x92, 0x60, 0x4d, 0xa4, 0x09, 0x1b, 0xeb, 0x38, 0x70, 0x5a, 0xd5, 0xa0, 0x61, 0xae, 0xf7, 0x7b, 0xcd, 0xa6, 0xe8, 0x75, 0x81, 0xb4, 0x11, 0x8b, 0xa0, 0x1e, 0x3a, 0xeb, 0x3a, 0xa8, 0x57, 0xcb, 0x61, 0xa2, 0x7a, 0x59, 0x91, 0xc1, 0x4e, 0x1a, 0x17, 0x22, 0x68, 0xd9, 0x9e, 0xa1, 0x82, 0xb4, 0xf9, 0xd4, 0x22, 0x32, 0xc5, 0x2f, 0x5f, 0xd5, 0xee, 0x73, 0xef, 0x81, 0xf1, 0x86, 0x3d, 0x73, 0xc2, 0xd0, 0x39, 0x2f, 0xe8, 0x1c, 0xb4, 0xbf, 0x28, 0xdb, 0xe6, 0xc0, 0xd2, 0xff, 0x03, 0xdb, 0xa2, 0xe6, 0x68, 0x2e, 0xfa, 0x67, 0xd4, 0x21, 0x25, 0xa6, 0xce, 0x5c, 0x97, 0xb8, 0x7d, 0x5b, 0x3d, 0x68, 0x2a, 0x35, 0x47, 0xf8, 0x7d, 0x26, 0x6f, 0x1d, 0x8a, 0x34, 0x6a, 0x3f, 0x0c, 0x58, 0x93, 0xa2, 0x4b, 0x8b, 0xa2, 0xd7, 0x4f, 0x14, 0x8b, 0xac, 0x4a, 0x0e, 0xbc, 0xee, 0x97, 0x14, 0xb7, 0x43, 0x41, 0xee, 0xda, 0xb3, 0xbf, 0x30, 0x1d, 0xba, 0xdf, 0x44, 0x50, 0x17, 0x80, 0x9b, 0x63, 0x6a, 0xf5, 0x3b, 0x9c, 0x34, 0x67, 0x8d, 0x99, 0x89, 0x61, 0x41, 0xf4, 0x6a, 0x0f, 0x5e, 0x4a, 0x0a, 0x29, 0x0f, 0x35, 0x5c, 0xa1, 0x17, 0xae, 0x6e, 0xdd, 0x78, 0x0a, 0xbc, 0xfd, 0x96, 0x5c, 0x4f, 0x89, 0xdc, 0x33, 0x59, 0x38, 0xfb, 0x29, 0xd5, 0xcc, 0xa7, 0x3c, 0x3c, 0xeb, 0xfc, 0xc8, 0x11, 0xfc, 0xbe, 0xe1, 0x61, 0x97, 0xad, 0x12, 0x98, 0xed, 0xc5, 0x45, 0x6f, 0xbc, 0x49, 0xd8, 0x45, 0x32, 0xe4, 0x2a, 0xa5, 0x4d, 0x86, 0xae, 0xb4, 0xfd, 0x4a, 0xf5, 0xa0, 0x0d, 0x01, 0x61, 0x52, 0x8c, 0x43, 0x6f, 0x8a, 0xb6, 0x43, 0xd3, 0x0c, 0x53, 0x2b, 0xf1, 0x66, 0xb5, 0x3c, 0xc8, 0xde, 0xe7, 0x93, 0x08, 0x61, 0x74, 0xb4, 0x4e, 0x42, 0xfb, 0x37, 0x65, 0xa6, 0x30, 0x43, 0x59, 0x26, 0x7a, 0x9d, 0x22, 0x36, 0x1a, 0xee, 0x3f, 0x77, 0x79, 0xc6, 0xcd, 0xb6, 0xd3, 0x2c, 0x57, 0xea, 0xad, 0x14, 0x57, 0x77, 0x60, 0xae, 0x05, 0xee, 0x61, 0xa3, 0xad, 0x9c, 0x85, 0xe2, 0xe5, 0xf7, 0x6a, 0x8f, 0xf2, 0x8b, 0xed, 0xde, 0xb8, 0x2a, 0xbe, 0xaa, 0x67, 0x7f, 0xf9, 0x2a, 0x40, 0xc9, 0x8c, 0x19, 0x8d, 0x35, 0x7d, 0x15, 0x39, 0x64, 0x92, 0x02, 0x0d, 0x93, 0x48, 0x9f, 0x4a, 0xda, 0x91, 0xc7, 0x5b, 0x51, 0xba, 0xf0, 0x2e, 0x0a, 0xc0, 0xa4, 0x37, 0xd7, 0x66, 0x03, 0x43, 0xc7, 0x03, 0x40, 0x8b, 0xd1, 0x90, 0x0b, 0x96, 0x84, 0x5d, 0x25, 0x64, 0xda, 0x8c, 0xbf, 0x4d, 0x3e, 0x21, 0x2a, 0x53, 0xef, 0x33, 0xd1, 0xb5, 0xe2, 0x39, 0xbf, 0x91, 0xbf, 0x53, 0xd7, 0xef, 0xcf, 0x5d, 0xd9, 0xd7, 0x28, 0x5f, 0x8c, 0xde, 0x3e, 0x9e, 0x74, 0x64, 0x39, 0xb5, 0x3f, 0xd6, 0xaa, 0x76, 0xe8, 0x0d, 0x0c, 0xc2, 0x96, 0x74, 0x09, 0xc6, 0xc4, 0x4e, 0x15, 0x9b, 0x7b, 0x27, 0xb5, 0xcd, 0x8c, 0x12, 0x49, 0xc2, 0x3c, 0xbb, 0x40, 0x04, 0x0f, 0x02, 0x85, 0xe7, 0x48, 0x7b, 0xb8, 0xac, 0x4c, 0xf0, 0x25, 0x9a, 0xd1, 0x97, 0x41, 0x71, 0xb6, 0x53, 0xd5, 0xe7, 0xfe, 0x23, 0x97, 0x91, 0xff, 0x98, 0x7a, 0x3c, 0xfb, 0x34, 0x25, 0xef, 0x21, 0xe9, 0x46, 0x3d, 0x18, 0x84, 0x20, 0xab, 0x85, 0x1d, 0x4e, 0x0c, 0x80, 0x19, 0xf2, 0x88, 0xb5, 0xae, 0x10, 0x8f, 0x6e, 0xaf, 0x52, 0xe4, 0x42, 0x4c, 0xa5, 0xe3, 0x62, 0x73, 0x87, 0xa6, 0x46, 0x83, 0xbc, 0xba, 0x42, 0x9f, 0x9f, 0xd3, 0x04, 0xc0, 0x09, 0x21, 0xfe, 0x1d, 0x42, 0xdf, 0x01, 0xb8, 0x93, 0x11, 0x9f, 0x67, 0x4b, 0xf4, 0x0a, 0x9b, 0xf9, 0x40, 0x2c, 0x6f, 0x6f, 0x28, 0x9d, 0xb2, 0x23, 0xd1, 0x3c, 0x34, 0x86, 0x99, 0xab, 0xd1, 0x92, 0xc1, 0x18, 0xbb, 0x10, 0xef, 0xda, 0xbe, 0x66, 0xe8, 0x86, 0xdf, 0xd9, 0xff, 0x2f, 0xf6, 0x67, 0xe1, 0x91, 0xfc, 0x59, 0x11, 0xdb, 0xfb, 0x05, 0x89, 0x36, 0x25, 0x55, 0xfd, 0x98, 0xb8, 0xaa, 0x06, 0xaa, 0x39, 0x18, 0x1f, 0xcf, 0x36, 0xd3, 0x18, 0xc3, 0xd7, 0x46, 0x90, 0x6a, 0x12, 0x19, 0x10, 0xef, 0x50, 0x80, 0x7d, 0xe4, 0xcd, 0xf1, 0x91, 0xc7, 0x61, 0xfd, 0x50, 0x9b, 0xc4, 0x34, 0x47, 0x80, 0x23, 0x76, 0x05, 0x95, 0x9a, 0xff, 0xbb, 0x6d, 0xb7, 0xa1, 0xa7, 0x07, 0x42, 0x42, 0x60, 0x24, 0x5f, 0x2f, 0xbc, 0xd6, 0x77, 0x88, 0x35, 0x19, 0x50, 0x12, 0x11, 0xbc, 0xf0, 0x48, 0xd4, 0xee, 0x85, 0x26, 0xb1, 0xfb, 0x22, 0xef, 0xe0, 0x7f, 0x82, 0x7c, 0x49, 0xca, 0x9b, 0xfc, 0x30, 0x44, 0xa7, 0x10, 0xee, 0xbb, 0x5e, 0x36, 0x61, 0x65, 0xb5, 0x4a, 0xdf, 0xb2, 0x94, 0x7c, 0x87, 0x39, 0xc4, 0x46, 0x4a, 0xa4, 0x2d, 0x31, 0xe7, 0x58, 0xb9, 0xb8, 0x30, 0x6f, 0x87, 0x12, 0xc0, 0x02, 0xca, 0x5e, 0xbf, 0xae, 0xff, 0xf0, 0xd6, 0xde, 0x64, 0x4a, 0xa5, 0x75, 0x47, 0xfd, 0xd5, 0xfb, 0x6e, 0xa7, 0xce, 0x45, 0xfb, 0xf2, 0xc4, 0xe2, 0x24, 0xf9, 0x05, 0xc4, 0xbc, 0xd0, 0x79, 0xb2, 0xec, 0x8f, 0x8d, 0x29, 0x9d, 0xca, 0x68, 0xe4, 0x3a, 0x92, 0xd8, 0x9c, 0xad, 0x66, 0x2c, 0x80, 0x65, 0x93, 0x78, 0x7f, 0xd1, 0x86, 0x2d, 0xc7, 0xf9, 0x2a, 0xe1, 0x8a, 0xd1, 0xba, 0xd0, 0xf7, 0xef, 0xaf, 0x70, 0x85, 0x1a, 0xb1, 0xda, 0x78, 0x77, 0x9e, 0xa5, 0x04, 0x9a, 0xd7, 0x1d, 0x56, 0x49, 0x5d, 0xa2, 0x66, 0x0f, 0xbc, 0x6d, 0x34, 0x0c, 0x1f, 0xf3, 0xd7, 0x91, 0x58, 0xeb, 0x49, 0x1b, 0xf3, 0xf1, 0xde, 0xb1, 0xd9, 0x70, 0xf5, 0x10, 0x10, 0xf9, 0xcc, 0x80, 0x1b, 0x4e, 0xac, 0x01, 0xde, 0x54, 0x0e, 0x8b, 0xe0, 0xd2, 0xa3, 0xbe, 0x99, 0x65, 0x8f, 0x68, 0x0a, 0x97, 0x32, 0xc1, 0xeb, 0xdb, 0x8d, 0xb8, 0x7d, 0xcc, 0xe7, 0x65, 0x94, 0xf6, 0x9a, 0xc2, 0x8f, 0x93, 0xfc, 0xe5, 0xf6, 0x16, 0x0c, 0xb9, 0x5f, 0xec, 0x59, 0xae, 0x81, 0x6f, 0x83, 0x16, 0x83, 0xb7, 0xd5, 0xa4, 0x18, 0xaa, 0xff, 0x22, 0xed, 0x77, 0x6e, 0xf3, 0x47, 0x82, 0xe9, 0xd3, 0x12, 0x59, 0x6f, 0x62, 0xd3, 0xd6, 0x61, 0x24, 0x61, 0x85, 0xdb, 0x4b, 0xe0, 0x41, 0xd4, 0x87, 0x68, 0x1c, 0x22, 0xb9, 0x0a, 0x70, 0xda, 0xba, 0xe0, 0x80, 0xb4, 0x38, 0x8f, 0x68, 0x7b, 0x58, 0x5c, 0x5e, 0x45, 0xf4, 0xae, 0x6f, 0x39, 0xed, 0xbd, 0xdb, 0x16, 0xe9, 0x35, 0x05, 0xfb, 0xe4, 0x0b, 0x72, 0x89, 0xa6, 0xd1, 0x6e, 0x29, 0x54, 0xb9, 0x78, 0x94, 0x4c, 0xaa, 0xb8, 0x9f, 0x3d, 0xd6, 0x3e, 0x60, 0x4a, 0xdf, 0x14, 0xfb, 0x4f, 0x7b, 0xfb, 0xd0, 0x39, 0xbb, 0xb9, 0x85, 0x26, 0xc5, 0xae, 0xc7, 0x99, 0x43, 0xbd, 0x5a, 0x89, 0x82, 0x5b, 0x5c, 0x8a, 0xb7, 0x90, 0xe5, 0x49, 0xa4, 0xdd, 0x87, 0x16, 0x0c, 0x60, 0xe3, 0xb9, 0x1b, 0x42, 0x62, 0x9d, 0x2c, 0x2d, 0xba, 0x60, 0x13, 0x34, 0x09, 0xe2, 0x30, 0xdd, 0xf0, 0xe9, 0xbe, 0x02, 0x1f, 0xdf, 0x17, 0x18, 0x06, 0x1f, 0x66, 0xa5, 0x92, 0x0c, 0x6f, 0xee, 0xef, 0x64, 0x82, 0x4e, 0x3e, 0x98, 0xb6, 0xaa, 0xf7, 0x2f, 0xdd, 0xe8, 0xc3, 0x3e, 0xd6, 0x43, 0x8a, 0x54, 0x2f, 0xd3, 0xc9, 0x04, 0xf8, 0xa0, 0x2c, 0x0b, 0x44, 0x67, 0x0c, 0xb7, 0xe2, 0xa9, 0x7a, 0x48, 0x5d, 0x8a, 0x50, 0x67, 0xc1, 0x25, 0xde, 0x32, 0x43, 0x94, 0x37, 0xd3, 0xe2, 0x47, 0xa2, 0xe9, 0x7f, 0x99, 0x5d, 0x04, 0x9b, 0x79, 0xe2, 0x29, 0x6d, 0x21, 0x27, 0x28, 0x67, 0x75, 0xd3, 0x98, 0xbd, 0x61, 0x4f, 0xcd, 0x20, 0x5a, 0x1d, 0x17, 0xca, 0x65, 0x37, 0xb1, 0x7d, 0xb7, 0xc0, 0xc8, 0x80, 0x6c, 0x3b, 0xe7, 0xc1, 0xdf, 0xdc, 0x97, 0xe6, 0xf3, 0x20, 0xdc, 0xd9, 0x4b, 0xd9, 0xe3, 0x1f, 0x6d, 0xba, 0xf5, 0x68, 0x2d, 0x74, 0xbd, 0xb9, 0x49, 0x23, 0x2b, 0x36, 0x89, 0x10, 0x1d, 0xe2, 0x30, 0xe6, 0x3e, 0xb1, 0x6f, 0x6e, 0x50, 0x6b, 0x1b, 0x23, 0x5b, 0xcb, 0xf0, 0x3f, 0x32, 0x03, 0xf5, 0x0d, 0x37, 0x78, 0x60, 0x49, 0x86, 0x52, 0x84, 0x5a, 0xf0, 0x80, 0xf9, 0x8f, 0xfa, 0x61, 0x0d, 0x50, 0x67, 0x62, 0x5b, 0x7e, 0x41, 0x2b, 0xf3, 0x2a, 0xc4, 0xe2, 0xbc, 0x25, 0x5a, 0xb0, 0x3c, 0xe5, 0x56, 0x5c, 0x8d, 0x00, 0x5f, 0x67, 0x41, 0x35, 0x16, 0x1d, 0x12, 0x4d, 0xa1, 0x6c, 0x99, 0xe7, 0x09, 0x45, 0xd9, 0x65, 0x7d, 0x05, 0x13, 0x61, 0x84, 0xc8, 0x3b, 0x23, 0x5e, 0x72, 0xe7, 0x04, 0x4d, 0xf9, 0xf6, 0x58, 0x8d, 0x2f, 0x9f, 0x32, 0xc0, 0x1b, 0x90, 0x9e, 0xc2, 0x6f, 0x71, 0xf3, 0xe1, 0x74, 0x94, 0x9b, 0x18, 0xa7, 0xdd, 0x6b, 0xb5, 0xf4, 0xa8, 0x00, 0x2b, 0x90, 0x8f, 0x21, 0xd2, 0xd3, 0xb8, 0x09, 0xf0, 0x68, 0x61, 0x9a, 0xb2, 0x12, 0x3c, 0x52, 0x30, 0xae, 0x77, 0x25, 0x97, 0xbb, 0xa0, 0x23, 0x81, 0x41, 0x09, 0x0b, 0x91, 0x21, 0x95, 0x34, 0x55, 0x5e, 0xb2, 0x1d, 0x6f, 0xb7, 0x07, 0x5d, 0xe5, 0xd8, 0xea, 0xf9, 0xa1, 0x6f, 0x81, 0x98, 0x8c, 0x8b, 0x95, 0x15, 0x26, 0xaa, 0xf6, 0xd1, 0x27, 0xb7, 0x9c, 0x34, 0xbb, 0xe8, 0x1f, 0xa1, 0xd1, 0x92, 0xcc, 0x01, 0x77, 0xad, 0x9d, 0xba, 0x35, 0x71, 0x58, 0xdd, 0xb8, 0x8f, 0xe5, 0x34, 0xfc, 0x6b, 0x26, 0x70, 0xeb, 0x17, 0x50, 0xec, 0x66, 0x4a, 0xde, 0xdb, 0x5b, 0x3b, 0xcc, 0xae, 0x2e, 0x53, 0x66, 0xbd, 0x1e, 0xdd, 0xe5, 0xed, 0x0b, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x77, 0xd0, 0x5d, 0x32, 0xfd, 0x25, 0x3e, 0x44, 0x23, 0x3c, 0xe7, 0x13, 0xca, 0xd0, 0xef, 0x0b, 0x0c, 0xc8, 0xc4, 0xd7, 0x20, 0x45, 0x2b, 0x66, 0x0e, 0x64, 0x5d, 0x30, 0x85, 0x34, 0x31, 0x64, 0x76, 0x7a, 0x23, 0xa1, 0x02, 0xda, 0x0e, 0xf9, 0x2a, 0xa2, 0x4a, 0xc1, 0xa5, 0xfb, 0xa3, 0xb8, 0x07, 0x0d, 0x03, 0xb0, 0x33, 0xa4, 0x09, 0x49, 0xe3, 0x15, 0x4c, 0x98, 0x56, 0xef, 0xac, 0x71, 0xa0, 0xa1, 0x22, 0x1c, 0xc1, 0x26, 0xdc, 0xd8, 0xee, 0xeb, 0x71, 0xb8, 0xd7, 0x9e, 0x7a, 0xb9, 0x3e, 0x55, 0x7f, 0x9a, 0xf7, 0x4a, 0xfe, 0x75, 0x1a, 0x3a, 0x16, 0xf7, 0x69, 0xfb, 0x63, 0x5a, 0x90, 0xfa, 0xf3, 0xb4, 0x82, 0x60, 0x78, 0x96, 0x88, 0x1c, 0x3a, 0x3f, 0xb5, 0x8c, 0xda, 0x10, 0x56, 0x9c, 0x82, 0x10, 0x36, 0x9c, 0x44, 0x3a, 0xab, 0xe8, 0xfb, 0x47, 0xd9, 0xd2, 0x80, 0xfe, 0xc9, 0x2c, 0x27, 0xb4, 0xe0, 0xfc, 0xf6, 0xa4, 0x92, 0x3a, 0x4f, 0x70, 0x60, 0xe6, 0x16, 0x31, 0x71, 0x5b, 0x06, 0x6d, 0xa3, 0xc3, 0xfb, 0xe8, 0xcc, 0xfe, 0x08, 0x10, 0x22, 0x1d, 0xc4, 0x73, 0xe1, 0x79, 0xf4, 0xbf, 0x07, 0xa1, 0x8d, 0x37, 0xe2, 0x10, 0xfb, 0x89, 0x7b, 0x47, 0x3e, 0xac, 0xc8, 0x7e, 0xa0, 0xf8, 0x0a, 0x16, 0xaa, 0x4a, 0x60, 0x20, 0x4f, 0x2e, 0x1f, 0xaf, 0xd3, 0xe1, 0x97, 0xb4, 0xea, 0x60, 0x82, 0x76, 0x17, 0x77, 0x0a, 0x59, 0xb7, 0x7a, 0x41, 0x4a, 0x34, 0xd2, 0x3a, 0x4d, 0x03, 0xab, 0x4f, 0x11, 0x0a, 0x1a, 0x23, 0xc5, 0x17, 0x13, 0x5a, 0x4c, 0xca, 0x90, 0xbf, 0xa1, 0x31, 0x9c, 0xed, 0x66, 0xc3, 0x2f, 0x30, 0x9e, 0xdb, 0x3f, 0xfe, 0x0b, 0x46, 0xfa, 0xc8, 0x20, 0x61, 0x87, 0xa2, 0xb0, 0xa9, 0x41, 0x70, 0x2b, 0x6e, 0x67, 0x0c, 0x7f, 0x35, 0xbd, 0xe8, 0xa9, 0x44, 0xd3, 0xa2, 0x73, 0xa7, 0x9f, 0x15, 0xa6, 0xda, 0xba, 0x27, 0xe0, 0x43, 0x6e, 0x61, 0xd9, 0x16, 0x2a, 0x7f, 0x31, 0xad, 0xb7, 0x80, 0xad, 0x03, 0x0a, 0x34, 0x54, 0xf4, 0x85, 0x45, 0xd1, 0x9c, 0x8b, 0x87, 0xf7, 0x0c, 0xa8, 0x47, 0x07, 0x64, 0xb2, 0x18, 0x07, 0x25, 0x70, 0x14, 0xc3, 0xf7, 0xc3, 0xd6, 0xab, 0x00, 0x94, 0x17, 0x74, 0x9b, 0xa3, 0x46, 0xb3, 0x5e, 0x52, 0xf5, 0x9b, 0x04, 0xea, 0x33, 0xc8, 0xe9, 0xc9, 0xe7, 0xf9, 0x77, 0x2a, 0x15, 0xda, 0xee, 0x44, 0x70, 0xc7, 0xc4, 0xce, 0x3f, 0xea, 0x69, 0xb2, 0xab, 0xd8, 0xb0, 0xc5, 0xfc, 0x7a, 0x75, 0x12, 0x53, 0xf1, 0x95, 0xfe, 0x70, 0xfa, 0xed, 0x4f, 0xc4, 0xfd, 0xdc, 0x35, 0xea, 0x8e, 0x6e, 0x1e, 0xef, 0xe3, 0x89, 0x52, 0x7b, 0xba, 0x35, 0x85, 0x37, 0x54, 0x52, 0x0e, 0x29, 0x3e, 0xff, 0xc4, 0xb9, 0x36, 0x91, 0x8d, 0x8a, 0xdb, 0xa7, 0x3f, 0xaa, 0x47, 0x2b, 0x40, 0x35, 0xfc, 0x1c, 0xdb, 0xfa, 0x4d, 0x8a, 0x60, 0x7b, 0xba, 0xf3, 0x22, 0x3e, 0xeb, 0xfd, 0x79, 0x7b, 0x43, 0xb2, 0x91, 0x95, 0xce, 0x22, 0x49, 0xa7, 0x50, 0x12, 0xd5, 0xfe, 0x55, 0x78, 0xa8, 0xc8, 0x4a, 0x66, 0xaa, 0x5e, 0xa7, 0xc6, 0x20, 0x02, 0xab, 0x82, 0x23, 0xf2, 0xee, 0xd0, 0x6c, 0xf5, 0xa3, 0x90, 0x3f, 0x50, 0x02, 0xc8, 0xbe, 0x97, 0xac, 0x70, 0x83, 0xa5, 0xb3, 0xa0, 0x58, 0x29, 0xc7, 0x20, 0x27, 0x35, 0x3a, 0xaf, 0xa5, 0x1a, 0xaa, 0xaa, 0x87, 0x7c, 0x10, 0x2d, 0xd9, 0x04, 0xa9, 0x29, 0x8e, 0x02, 0x73, 0x70, 0x21, 0xe1, 0x71, 0xe7, 0x9d, 0xaf, 0x3d, 0xf7, 0xc1, 0x10, 0x27, 0x1a, 0x97, 0x7c, 0x68, 0xc5, 0x7e, 0xeb, 0x80, 0xbf, 0x19, 0x1d, 0x2b, 0x33, 0xd1, 0xfb, 0x90, 0xac, 0xb3, 0xf7, 0xe1, 0x7d, 0x96, 0x94, 0xea, 0xd9, 0xc6, 0x45, 0x3b, 0xaf, 0x6e, 0x70, 0xf4, 0x03, 0x00, 0x80, 0x76, 0x64, 0x1c, 0xef, 0x6e, 0x71, 0xda, 0x92, 0x59, 0x0d, 0x47, 0xbb, 0x73, 0x38, 0xec, 0x27, 0xec, 0x8a, 0x28, 0xf2, 0x42, 0xb7, 0x6d, 0xf9, 0x0c, 0xdd, 0x3e, 0xbe, 0xfe, 0x4b, 0x12, 0xeb, 0x86, 0xdb, 0x11, 0x1a, 0xf1, 0xb7, 0x41, 0x8a, 0xff, 0x30, 0x5f, 0xed, 0x2c, 0x03, 0xa4, 0xca, 0xac, 0xd8, 0xec, 0xe2, 0x3f, 0x19, 0x57, 0x91, 0x72, 0x7a, 0xf4, 0x5f, 0x37, 0x8d, 0x8c, 0x0e, 0xd1, 0x80, 0xd6, 0x6a, 0x65, 0x6c, 0x42, 0x0f, 0x3a, 0x81, 0x7a, 0x83, 0xf9, 0xf3, 0xdc, 0x52, 0x44, 0x34, 0x3a, 0xed, 0xc0, 0xb2, 0x56, 0x27, 0x4f, 0x31, 0xb1, 0x05, 0x73, 0xb0, 0x86, 0x3c, 0x3f, 0x6b, 0xe4, 0xaf, 0x83, 0x4f, 0x19, 0xae, 0x5c, 0xdf, 0xcf, 0x1d, 0x5b, 0x7e, 0x88, 0xbb, 0xa4, 0x44, 0x05, 0xcf, 0xb3, 0x4e, 0xe3, 0x42, 0xa8, 0x45, 0x44, 0x58, 0x24, 0xb9, 0xd8, 0x62, 0x12, 0x34, 0x27, 0x47, 0xc4, 0x0b, 0x30, 0x05, 0x2f, 0x2e, 0x38, 0x81, 0x9d, 0xcd, 0xd0, 0x27, 0x79, 0x3a, 0xbc, 0x27, 0x29, 0x19, 0xd6, 0xe8, 0x69, 0x2e, 0x1b, 0x4d, 0xc0, 0xf6, 0x47, 0x2a, 0x70, 0xad, 0x36, 0x37, 0xf9, 0x31, 0x0c, 0xe2, 0x95, 0x97, 0x36, 0xad, 0xd3, 0xd1, 0x46, 0x3f, 0x3c, 0x3e, 0xd0, 0xef, 0xb5, 0x1a, 0x7a, 0xf8, 0x33, 0xcb, 0xb8, 0x46, 0x23, 0x3e, 0x9d, 0x2d, 0x53, 0x65, 0x99, 0x67, 0x8f, 0x2e, 0x37, 0x6a, 0xbe, 0x22, 0x94, 0x1c, 0xc9, 0xbd, 0x87, 0x0b, 0xea, 0xfe, 0xf0, 0xe2, 0xd4, 0xf5, 0xd5, 0xcc, 0x1a, 0xbb, 0x19, 0x17, 0x73, 0x24, 0x8e, 0xa5, 0xfa, 0x07, 0x12, 0x61, 0x48, 0x32, 0x7b, 0x5b, 0x06, 0x6c, 0xf0, 0x11, 0xbb, 0xd1, 0xed, 0x4e, 0x5e, 0x4e, 0x18, 0xc3, 0x5a, 0x64, 0x00, 0xba, 0x3d, 0x0b, 0x80, 0x5c, 0x50, 0x02, 0x20, 0xcc, 0xa3, 0x05, 0x9f, 0xab, 0x4a, 0x28, 0x26, 0x78, 0xc0, 0xd0, 0x5f, 0xc3, 0x32, 0x86, 0xaa, 0x00, 0xfa, 0xfb, 0x52, 0xe0, 0x62, 0x4a, 0xa9, 0xcd, 0x49, 0x4e, 0x24, 0x64, 0xf0, 0x3f, 0x67, 0xd8, 0x96, 0xcd, 0xe4, 0xaf, 0xb4, 0xcc, 0x15, 0x1a, 0x96, 0x67, 0x3b, 0xc1, 0xd3, 0x97, 0xe4, 0x8e, 0x2e, 0xa2, 0x86, 0xd0, 0x82, 0x68, 0x3a, 0x9e, 0xf7, 0x89, 0x5c, 0x79, 0xb9, 0x92, 0x90, 0xe3, 0x28, 0x39, 0x0e, 0xc2, 0xc3, 0xd6, 0xb8, 0x39, 0x31, 0x54, 0x9a, 0x9c, 0x6c, 0x46, 0x40, 0xb1, 0xa2, 0xc1, 0x53, 0xe7, 0x12, 0x92, 0x18, 0xd4, 0x61, 0x63, 0x36, 0xd8, 0xda, 0xb3, 0xef, 0x64, 0x54, 0x5b, 0xa7, 0xce, 0x08, 0xff, 0xc6, 0xe7, 0x65, 0x1f, 0x15, 0xc3, 0xc8, 0x93, 0x56, 0xc9, 0xe0, 0x2e, 0xdb, 0x9e, 0x3c, 0x30, 0x28, 0x41, 0x15, 0x41, 0x46, 0xda, 0xb9, 0xda, 0x98, 0x6e, 0x62, 0x30, 0xc3, 0x9a, 0x87, 0x3f, 0x5f, 0x71, 0x65, 0x27, 0x80, 0x8b, 0xe5, 0xd3, 0xc3, 0x67, 0xd1, 0xab, 0x8b, 0xa7, 0xcc, 0x63, 0x75, 0xe7, 0x8e, 0x4a, 0x70, 0x1a, 0xfc, 0x5f, 0xc5, 0x25, 0x52, 0xa5, 0xaa, 0x35, 0x82, 0x03, 0xfd, 0x74, 0xb4, 0x42, 0x55, 0x6a, 0xf6, 0xc3, 0x89, 0x0f, 0x09, 0x2a, 0x1a, 0x82, 0xb3, 0xa4, 0x9e, 0x50, 0xff, 0x3c, 0x99, 0xbf, 0x98, 0x91, 0xce, 0xbf, 0x63, 0x54, 0x65, 0x07, 0xd8, 0xfe, 0x71, 0xe6, 0xb4, 0xf9, 0x45, 0x68, 0xbb, 0x08, 0xe6, 0xa7, 0x0f, 0x58, 0x9c, 0x2b, 0x01, 0xe8, 0x41, 0xe9, 0xe0, 0xb8, 0xc2, 0x96, 0xe6, 0x76, 0x9b, 0xd5, 0x3f, 0x87, 0xbb, 0x5d, 0xf4, 0x90, 0xdb, 0xa9, 0x62, 0xe6, 0x3f, 0xb6, 0xdb, 0x9a, 0xf7, 0x2c, 0xfb, 0xf5, 0xcf, 0x72, 0xbe, 0xdd, 0xd9, 0xa0, 0xde, 0xe4, 0x31, 0x12, 0x4f, 0x8c, 0x3c, 0x13, 0xfb, 0xcb, 0x77, 0x53, 0x6a, 0xdf, 0x77, 0x87, 0x6e, 0x0b, 0x2b, 0x9a, 0x0d, 0x6f, 0xe3, 0xa9, 0x20, 0x6b, 0x75, 0x57, 0x31, 0xad, 0x84, 0x2f, 0xa9, 0x51, 0xc7, 0x1f, 0xd2, 0x03, 0xc2, 0xca, 0xf9, 0xdc, 0xab, 0xf2, 0x23, 0xd6, 0x9c, 0x83, 0xce, 0x9a, 0x63, 0x92, 0x04, 0xe0, 0xc4, 0x91, 0x24, 0xb5, 0x78, 0xdb, 0x4a, 0xb6, 0x8b, 0xa5, 0x0f, 0xbd, 0x7d, 0x01, 0x6f, 0xb5, 0x79, 0x18, 0x83, 0x78, 0x65, 0x0b, 0x88, 0xd0, 0xbe, 0x8e, 0x77, 0xe0, 0x10, 0xd2, 0x2c, 0xa1, 0x7d, 0x47, 0x09, 0x63, 0x99, 0xfc, 0xec, 0x92, 0x9a, 0xee, 0x3e, 0xf0, 0x6b, 0x4f, 0xda, 0x54, 0x03, 0x5d, 0x87, 0x77, 0x13, 0x7c, 0x72, 0xf6, 0xa3, 0x45, 0x6f, 0x27, 0x74, 0xdb, 0xc9, 0x8f, 0x5e, 0xb2, 0xd9, 0x60, 0xe8, 0xf3, 0x89, 0x9d, 0x7d, 0x68, 0x23, 0xf3, 0x60, 0x7e, 0xc7, 0x96, 0x90, 0x60, 0xad, 0x2d, 0x40, 0x61, 0x4f, 0x8d, 0x1b, 0x03, 0xbf, 0x0e, 0x1f, 0x7c, 0xf8, 0x89, 0xfc, 0xa3, 0xf6, 0xff, 0x80, 0x3a, 0x5d, 0x5d, 0x79, 0x39, 0x4e, 0xad, 0xa7, 0x8a, 0xb7, 0x04, 0x68, 0x8d, 0x80, 0x51, 0xa6, 0x99, 0x06, 0xda, 0x23, 0xae, 0x31, 0xcf, 0x95, 0x4b, 0xb9, 0x5b, 0x45, 0x0c, 0x96, 0x80, 0x0e, 0x93, 0xdc, 0x0c, 0x65, 0xe2, 0x19, 0x88, 0x69, 0x18, 0x1c, 0x46, 0x8f, 0xba, 0xd5, 0xdb, 0xb2, 0xc3, 0xd2, 0xb3, 0x8a, 0xd0, 0xb2, 0x71, 0x95, 0xeb, 0x48, 0x0d, 0x70, 0x39, 0x93, 0x1e, 0x05, 0xcd, 0xc7, 0x54, 0xa3, 0xb4, 0xad, 0x88, 0xce, 0x6f, 0x53, 0x35, 0xeb, 0x5e, 0xe7, 0x83, 0xad, 0xb2, 0x9a, 0xb8, 0x17, 0x23, 0x69, 0x87, 0x77, 0xb5, 0x99, 0xb3, 0x78, 0x7b, 0x03, 0xb3, 0xfa, 0x5a, 0xa1, 0xc5, 0x6c, 0x36, 0xf5, 0x67, 0x77, 0xbf, 0x4b, 0xe7, 0x02, 0x34, 0xc5, 0xfd, 0x5e, 0xa4, 0x3e, 0x3d, 0xc9, 0x50, 0xcc, 0x15, 0x0e, 0xef, 0x09, 0x69, 0xf5, 0x4f, 0x18, 0x52, 0x21, 0xb0, 0x84, 0x59, 0x4b, 0xc3, 0x30, 0xc7, 0x7a, 0xe3, 0xfa, 0xfa, 0xfa, 0x53, 0xa4, 0x08, 0x5e, 0xd0, 0x58, 0xef, 0x93, 0x08, 0xac, 0x8a, 0x5b, 0xfa, 0x0d, 0x02, 0x4a, 0x7b, 0x23, 0x7f, 0x08, 0x72, 0xb9, 0x39, 0x1a, 0x52, 0x3d, 0x64, 0x7b, 0x49, 0x22, 0x0d, 0xb0, 0xb2, 0x81, 0x07, 0xe1, 0xce, 0x8a, 0x46, 0x18, 0x99, 0xeb, 0x9a, 0xd7, 0x0e, 0x0f, 0x58, 0x02, 0x36, 0xed, 0x43, 0x3e, 0x6d, 0xef, 0xcc, 0x24, 0x11, 0x4e, 0x9d, 0xd5, 0x08, 0x99, 0x37, 0x28, 0x8e, 0x2e, 0x67, 0x2b, 0xad, 0x4e, 0x5c, 0x27, 0x0f, 0x7a, 0xc3, 0x99, 0xa0, 0xa0, 0x7f, 0x74, 0xb8, 0x17, 0x59, 0x7c, 0x5e, 0x5e, 0x70, 0xc9, 0x5a, 0x81, 0x93, 0xb0, 0xeb, 0x6d, 0xfb, 0x1a, 0x4b, 0x18, 0x0d, 0x63, 0x0c, 0xff, 0x90, 0xff, 0x9e, 0xd5, 0x26, 0x75, 0x1f, 0x04, 0x1e, 0x65, 0xbe, 0xb8, 0x82, 0xc4, 0x71, 0xc3, 0x97, 0xc5, 0x98, 0x74, 0x98, 0xa9, 0xeb, 0x93, 0x46, 0xfa, 0xdd, 0x95, 0x72, 0x30, 0x71, 0xf9, 0xb6, 0xd1, 0x9f, 0x96, 0x30, 0xc6, 0x6d, 0x42, 0xf9, 0x24, 0x24, 0xf8, 0x58, 0xca, 0xd8, 0x72, 0x1b, 0x26, 0x08, 0xac, 0x77, 0x3c, 0xd2, 0x5d, 0x4b, 0xbd, 0xb7, 0xfd, 0xa7, 0x71, 0x2f, 0xed, 0x3d, 0x59, 0x87, 0x18, 0xb3, 0x36, 0x88, 0xf6, 0xe9, 0x4d, 0xb6, 0x62, 0xa7, 0x87, 0xc1, 0x6b, 0x83, 0x6c, 0x7c, 0x82, 0xa3, 0x7a, 0x5c, 0x1e, 0x81, 0xb5, 0xd6, 0x36, 0x91, 0x11, 0x5e, 0xbd, 0xf4, 0x3e, 0xdd, 0xe8, 0xfc, 0x89, 0x25, 0x12, 0x00, 0x26, 0x46, 0x58, 0xf7, 0xb3, 0x03, 0x4c, 0x5b, 0x37, 0x8b, 0x9c, 0xd1, 0x02, 0x80, 0x1a, 0xb3, 0x48, 0xf9, 0xb2, 0x10, 0x98, 0x67, 0x3f, 0x99, 0x59, 0xf3, 0xd9, 0x00, 0xb1, 0x83, 0xe1, 0xb2, 0x1c, 0xd9, 0x4d, 0x2f, 0xf5, 0x56, 0x1b, 0xc6, 0x80, 0x36, 0xa0, 0x5d, 0x41, 0x67, 0xf8, 0xbf, 0xfe, 0x00, 0x18, 0xdb, 0x52, 0xf2, 0xac, 0x7c, 0xd6, 0x42, 0x70, 0xe2, 0x14, 0xc2, 0x33, 0xac, 0x14, 0x1f, 0xfa, 0xd0, 0x28, 0x30, 0x22, 0xba, 0xbe, 0xac, 0x53, 0xf7, 0xc5, 0x42, 0xb0, 0x64, 0xc7, 0xe5, 0xf7, 0xc4, 0x2a, 0x4d, 0xc5, 0x83, 0xa0, 0x6b, 0x4f, 0x49, 0x8d, 0xd7, 0x9a, 0x23, 0xdf, 0xbd, 0x07, 0x75, 0x34, 0xc0, 0xde, 0x66, 0x34, 0x7e, 0x94, 0xb7, 0x17, 0x02, 0xd1, 0x39, 0x5a, 0x42, 0xe9, 0x52, 0x54, 0xbf, 0x9d, 0xb6, 0xfe, 0xe2, 0x8e, 0x7d, 0x19, 0xa9, 0x85, 0xdb, 0x67, 0xee, 0xdd, 0x74, 0xa8, 0xba, 0x16, 0x81, 0x01, 0x37, 0x40, 0x30, 0x32, 0x88, 0x66, 0xa4, 0x85, 0x60, 0x0f, 0x5b, 0x4a, 0x0d, 0xd5, 0x81, 0x02, 0xb5, 0xf4, 0xd3, 0xda, 0x6e, 0x03, 0x63, 0xa5, 0x0e, 0xdc, 0x81, 0xfe, 0x9b, 0xf7, 0x59, 0xc1, 0x38, 0xeb, 0x2c, 0xed, 0x12, 0xf9, 0x15, 0x45, 0x7d, 0x90, 0x0f, 0xea, 0x7d, 0x41, 0xd7, 0x7e, 0xf3, 0x68, 0x78, 0xe5, 0x4c, 0x77, 0xa7, 0x83, 0x4b, 0xe4, 0xed, 0xd6, 0x6f, 0xb9, 0x98, 0x04, 0x76, 0x82, 0x1e, 0xb2, 0xc9, 0x8f, 0xc6, 0x5b, 0xdc, 0x1e, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x9b, 0x7b, 0xdc, 0x1c, 0xac, 0x09, 0x71, 0x73, 0x18, 0x64, 0x92, 0xcc, 0x53, 0x4f, 0x46, 0xe1, 0x19, 0xe2, 0x23, 0x65, 0x1b, 0x3b, 0xb1, 0xd7, 0xc0, 0xb3, 0x61, 0x3b, 0x42, 0x1f, 0xb1, 0xec, 0x08, 0x2b, 0x15, 0x32, 0xa1, 0xe7, 0xf1, 0x02, 0x9e, 0x60, 0x44, 0x1b, 0x7d, 0x6b, 0x87, 0xe9, 0xcf, 0xdc, 0x0c, 0x54, 0x1e, 0x96, 0xf7, 0xb6, 0x7b, 0x6a, 0x47, 0x37, 0xb1, 0xa6, 0xf7, 0x60, 0x73, 0xe2, 0x4b, 0x9e, 0xc6, 0x8d, 0xfe, 0x12, 0xfc, 0xee, 0x1d, 0x40, 0xb4, 0x91, 0x5d, 0x37, 0x0b, 0xfd, 0x13, 0x17, 0xdb, 0x75, 0x52, 0x68, 0x3b, 0x02, 0x56, 0x42, 0x6b, 0x75, 0x6c, 0x1f, 0xcc, 0x5d, 0xfc, 0x3e, 0x6d, 0xc2, 0xb5, 0x8f, 0x08, 0x66, 0x58, 0x93, 0x74, 0x3a, 0x28, 0xc1, 0x9d, 0x57, 0x13, 0x0c, 0xad, 0x1a, 0xb5, 0x2f, 0xc8, 0x2b, 0x0d, 0x4a, 0x20, 0x5a, 0x49, 0xb1, 0x8c, 0x1e, 0x34, 0x93, 0x7c, 0xe8, 0x15, 0x04, 0xa2, 0xf6, 0x15, 0x00, 0x9c, 0xd4, 0x5a, 0x63, 0xa2, 0x1a, 0xef, 0x92, 0x6e, 0x39, 0xf3, 0xe1, 0x6e, 0x55, 0x7b, 0xfe, 0xbc, 0x45, 0x1f, 0x4e, 0x42, 0xa2, 0xdf, 0x08, 0xd6, 0x67, 0x0b, 0x47, 0x27, 0x4b, 0x40, 0x31, 0xef, 0x0f, 0xc7, 0xbd, 0x20, 0xe9, 0x4e, 0x95, 0xa7, 0x5f, 0xe3, 0x04, 0x70, 0xb7, 0x02, 0x76, 0x6b, 0xe7, 0x13, 0xc4, 0x18, 0x9e, 0xf7, 0xad, 0x38, 0xdf, 0x9b, 0x65, 0xcd, 0x69, 0xf4, 0xc0, 0xb4, 0x88, 0x59, 0x28, 0x14, 0xfe, 0x68, 0x84, 0x30, 0x80, 0xae, 0x2d, 0x11, 0xb0, 0xd6, 0x02, 0xdd, 0x48, 0xbf, 0x25, 0x48, 0xa5, 0x05, 0xa3, 0xbc, 0x02, 0xef, 0xad, 0xa0, 0xb1, 0xc3, 0xbb, 0xe1, 0xc8, 0xc3, 0x42, 0x4c, 0x37, 0x29, 0x10, 0x91, 0x7a, 0x11, 0x1b, 0x19, 0x24, 0x5b, 0xb5, 0x81, 0xa2, 0xdc, 0x51, 0xa0, 0x85, 0x15, 0x5a, 0x7c, 0xb3, 0xfa, 0x56, 0x0a, 0xc3, 0x16, 0xfa, 0x7d, 0x51, 0xdc, 0x4a, 0x87, 0xad, 0x97, 0x2e, 0xb6, 0x1c, 0xca, 0x6d, 0x12, 0x3b, 0xc9, 0x27, 0x41, 0x02, 0xe4, 0x8c, 0x40, 0x28, 0xf0, 0xff, 0x83, 0x6c, 0x85, 0x27, 0xad, 0xf4, 0xaf, 0x53, 0x28, 0x8b, 0x3e, 0xbd, 0x45, 0x1c, 0x2c, 0xe5, 0x5c, 0xbd, 0x51, 0xa7, 0xa0, 0x1b, 0xe7, 0x72, 0x77, 0xea, 0x08, 0xb2, 0xad, 0x0d, 0x38, 0x3f, 0x4f, 0x4e, 0xfa, 0x68, 0x0d, 0x74, 0xaa, 0x70, 0x2c, 0x1b, 0x96, 0x04, 0x73, 0x63, 0x20, 0x4a, 0x84, 0xad, 0xfd, 0x5b, 0xd2, 0xee, 0x43, 0x2a, 0xad, 0xbb, 0x77, 0x46, 0x59, 0x7c, 0x82, 0x51, 0xf3, 0x5b, 0xaf, 0x39, 0xe5, 0xdf, 0xed, 0x7c, 0xff, 0x9e, 0xf7, 0x9a, 0xd3, 0x2d, 0x5c, 0x58, 0x44, 0x99, 0x2d, 0xed, 0x89, 0xac, 0x99, 0x72, 0x73, 0x06, 0x0e, 0x33, 0xd3, 0x43, 0xe6, 0x68, 0x47, 0x51, 0xcc, 0x52, 0xb6, 0x76, 0x06, 0xa2, 0xa2, 0x79, 0x57, 0x92, 0x01, 0xc8, 0xfd, 0x0d, 0x8e, 0xb1, 0x84, 0x0f, 0x66, 0x6c, 0x3f, 0xb5, 0x1c, 0xef, 0x15, 0x8e, 0x70, 0xfa, 0x55, 0x86, 0x4a, 0x61, 0x56, 0x9b, 0xad, 0x2a, 0xa2, 0xfa, 0x83, 0x6c, 0xc3, 0x42, 0xee, 0x1b, 0x84, 0x13, 0xa3, 0xed, 0x47, 0xde, 0x99, 0x50, 0x06, 0xfa, 0xf4, 0x39, 0x6c, 0xc0, 0x99, 0xe9, 0xf9, 0x3d, 0xb4, 0x31, 0x40, 0xdf, 0xde, 0x57, 0xf4, 0x49, 0x3f, 0xfd, 0xd7, 0x46, 0xad, 0x57, 0xb3, 0x25, 0x2c, 0xef, 0x5a, 0x7c, 0xe8, 0x7e, 0x03, 0xaf, 0x8e, 0x2c, 0xa6, 0xce, 0xe9, 0x35, 0xb4, 0x9e, 0x0d, 0xa8, 0x7e, 0x47, 0xcb, 0x3c, 0xe0, 0xd6, 0x5e, 0xeb, 0xb8, 0xcf, 0x74, 0xc2, 0xea, 0x09, 0xba, 0x4b, 0x51, 0x48, 0x0f, 0xef, 0x6a, 0xb7, 0xc1, 0x30, 0xf6, 0x65, 0xdc, 0x0f, 0x9c, 0xe6, 0x15, 0xff, 0xcc, 0x0a, 0x3f, 0x8f, 0xde, 0x86, 0x14, 0x66, 0xdd, 0xbe, 0xbf, 0x1e, 0xb4, 0x68, 0x92, 0xda, 0x12, 0xab, 0x88, 0xb9, 0x2f, 0x94, 0x8b, 0xf1, 0x9a, 0x5c, 0xee, 0x38, 0x5a, 0x30, 0xb3, 0x94, 0x52, 0x50, 0x0b, 0x2a, 0x76, 0xc3, 0x90, 0x81, 0xf5, 0xe9, 0xc7, 0x2b, 0xfa, 0x2d, 0x91, 0xf8, 0xfc, 0x6b, 0x13, 0x75, 0x71, 0x4c, 0xb4, 0x54, 0xa5, 0xd7, 0xe6, 0xc9, 0x7b, 0xd0, 0xb8, 0xf4, 0x2f, 0x61, 0xcf, 0x49, 0xb6, 0x57, 0x1a, 0xd4, 0xdb, 0x5e, 0x4d, 0x4f, 0x18, 0x53, 0xe0, 0xf4, 0x27, 0x03, 0xd8, 0x08, 0xcb, 0x9e, 0xee, 0xdc, 0x72, 0x5a, 0x6c, 0x3b, 0x13, 0x78, 0x28, 0xf5, 0xf8, 0x6e, 0x1a, 0x7a, 0xda, 0xc5, 0xf0, 0x4f, 0xbd, 0x0e, 0xcd, 0xc7, 0x22, 0x44, 0x0c, 0x85, 0x4b, 0xe0, 0x96, 0x80, 0xbb, 0x96, 0x26, 0xd2, 0x5d, 0x5f, 0x69, 0x8b, 0x3a, 0x29, 0xaa, 0x04, 0xdd, 0x95, 0xd2, 0x71, 0xf2, 0x62, 0x32, 0x11, 0x3b, 0xe1, 0x00, 0x94, 0x8d, 0x94, 0xf7, 0x9d, 0x5f, 0x57, 0xc3, 0xca, 0x82, 0xc4, 0x70, 0x9f, 0x47, 0x4c, 0x2a, 0xdc, 0x3a, 0x2b, 0xc6, 0x86, 0xc1, 0x9e, 0xf6, 0x04, 0x39, 0x8c, 0x1f, 0x67, 0xd4, 0xc2, 0x5c, 0x8d, 0xf4, 0x80, 0xc7, 0x6b, 0x71, 0xcb, 0x2a, 0x8d, 0x5a, 0x17, 0xae, 0x48, 0x2e, 0x9e, 0x90, 0xd2, 0xdb, 0x6e, 0x8b, 0xd3, 0x8c, 0xe1, 0x06, 0xd4, 0xef, 0x43, 0xf0, 0x3f, 0x53, 0x4c, 0xfb, 0x38, 0x0d, 0xc9, 0xa5, 0x0c, 0xc5, 0x37, 0x92, 0x50, 0x5b, 0x10, 0x39, 0xa5, 0x9e, 0x01, 0x18, 0x62, 0x6c, 0xd6, 0x48, 0xdc, 0xec, 0x68, 0x3a, 0x60, 0xb2, 0x0d, 0x7a, 0x0b, 0x07, 0x93, 0x4b, 0xa3, 0x2d, 0xfb, 0x8b, 0x8b, 0x14, 0x14, 0x4f, 0x3a, 0x47, 0xff, 0xdd, 0x5a, 0x8f, 0x07, 0x7b, 0x01, 0x16, 0x06, 0xe3, 0xe9, 0x02, 0xdc, 0xa8, 0x28, 0x34, 0x0b, 0xcb, 0x7a, 0x6d, 0xe6, 0xce, 0x41, 0xac, 0x13, 0xfa, 0x22, 0x54, 0x03, 0x46, 0x7a, 0x66, 0x75, 0x25, 0x7d, 0xfc, 0x74, 0x67, 0xa4, 0x4d, 0x3e, 0x0c, 0x4a, 0x19, 0xa3, 0xd9, 0x1d, 0x08, 0xc2, 0xe0, 0x4e, 0x90, 0xfd, 0x74, 0x4b, 0x8a, 0x07, 0xcd, 0x49, 0x23, 0x58, 0xe9, 0xa7, 0x1e, 0xbc, 0x64, 0x6d, 0xcb, 0xe0, 0xd3, 0x06, 0x7e, 0x39, 0x57, 0x03, 0x50, 0x76, 0x81, 0x25, 0xe4, 0x7e, 0xc6, 0xf9, 0x8c, 0xce, 0x05, 0x53, 0xaa, 0x7f, 0x0d, 0xe5, 0xef, 0x5f, 0xdb, 0xec, 0x91, 0xd9, 0x34, 0x34, 0xef, 0x27, 0xc1, 0x82, 0x56, 0x16, 0x02, 0x20, 0x23, 0xfc, 0x36, 0xa7, 0x62, 0x57, 0x9e, 0xe6, 0x62, 0x9f, 0xc6, 0x52, 0x2c, 0x2f, 0x33, 0x22, 0xd4, 0x94, 0x87, 0x17, 0xd8, 0x0d, 0x3d, 0x72, 0x2e, 0x8f, 0x04, 0x3b, 0x1e, 0xf4, 0xac, 0x90, 0x5d, 0x27, 0x63, 0x7b, 0xdb, 0xa5, 0x0a, 0x10, 0x9e, 0x79, 0xbd, 0x21, 0x2a, 0xa3, 0x7e, 0xf6, 0x72, 0x34, 0xa6, 0x64, 0x09, 0x0f, 0x82, 0x6e, 0xe7, 0x71, 0xca, 0x21, 0x2f, 0x29, 0x0c, 0xe2, 0x9d, 0x28, 0x6b, 0x5f, 0x9a, 0xd9, 0x68, 0x6f, 0xbe, 0xd8, 0x3b, 0xd3, 0x75, 0x52, 0x58, 0x7c, 0x51, 0x35, 0xfc, 0x6e, 0xe7, 0x7d, 0xab, 0xa9, 0x8b, 0x0d, 0x47, 0x42, 0x00, 0x73, 0x4c, 0xae, 0x02, 0xef, 0xd4, 0xa0, 0x6f, 0x6f, 0xa6, 0x56, 0xf9, 0x0d, 0xb2, 0x8a, 0xa3, 0x09, 0xe2, 0x7f, 0xc3, 0x22, 0x0f, 0x9a, 0xf5, 0x0f, 0x6e, 0x66, 0xe6, 0x85, 0x99, 0x67, 0x13, 0x94, 0xe8, 0x15, 0x55, 0x0f, 0x37, 0x8f, 0x43, 0x8e, 0x32, 0x88, 0x35, 0x8d, 0xb8, 0x19, 0xbd, 0xe4, 0x3f, 0x1d, 0x44, 0x65, 0xd3, 0xbe, 0xbb, 0x7f, 0xdf, 0x21, 0x4e, 0xe6, 0x87, 0x81, 0x86, 0xc0, 0x66, 0xad, 0x52, 0x98, 0x56, 0x02, 0xe0, 0x09, 0xe9, 0xa1, 0x96, 0x0a, 0xfb, 0x75, 0xbd, 0x9c, 0xeb, 0x23, 0x99, 0x9e, 0x93, 0x5c, 0x65, 0xde, 0xc4, 0x68, 0xa4, 0xac, 0x1d, 0xbb, 0xa7, 0x7f, 0x7c, 0x7f, 0xe8, 0xc7, 0x41, 0xd6, 0xd5, 0x1f, 0x1f, 0x66, 0x59, 0xb2, 0xe0, 0xab, 0xa0, 0x52, 0x9a, 0x07, 0x8c, 0xb6, 0x6d, 0x9f, 0x61, 0xfe, 0x06, 0x55, 0x19, 0x22, 0x38, 0x25, 0xc6, 0x20, 0x99, 0xd5, 0xac, 0x8e, 0xfb, 0x43, 0x3d, 0xa6, 0x23, 0xb4, 0xa0, 0x6d, 0x83, 0x4e, 0xd5, 0xd6, 0x14, 0xfd, 0xb6, 0xe2, 0xf2, 0xe2, 0xae, 0x15, 0x39, 0x66, 0x12, 0x5c, 0x0c, 0xa5, 0xc2, 0xa3, 0x5c, 0x4e, 0x55, 0x9a, 0x71, 0xcf, 0xfc, 0x26, 0x54, 0x44, 0x72, 0x2f, 0xed, 0x2a, 0x93, 0x94, 0xce, 0x04, 0x66, 0x66, 0x51, 0x37, 0x02, 0x11, 0xee, 0xbc, 0xe9, 0x6c, 0x4e, 0xba, 0x2c, 0x54, 0x15, 0x25, 0x40, 0x19, 0x2c, 0x6e, 0x2b, 0x1b, 0x7e, 0x8d, 0x29, 0x45, 0x1e, 0x12, 0xbb, 0xe3, 0x44, 0xc8, 0x8b, 0xe6, 0xd3, 0x6b, 0xc8, 0x27, 0x22, 0x40, 0x33, 0xa5, 0x2e, 0x87, 0x59, 0xa9, 0xac, 0x78, 0x23, 0x98, 0x84, 0x75, 0xfc, 0xa4, 0x4e, 0x6f, 0x1b, 0x76, 0x45, 0xd9, 0x91, 0x58, 0xf7, 0x1a, 0x95, 0xad, 0xdd, 0x33, 0x20, 0xfb, 0x25, 0x5d, 0x3d, 0xfb, 0xdf, 0xc2, 0x35, 0x27, 0x38, 0xf0, 0x2f, 0x0f, 0xb6, 0x82, 0x3e, 0x61, 0x64, 0x4b, 0xa4, 0xc2, 0x9d, 0x9e, 0x86, 0xa7, 0xd9, 0xfe, 0xea, 0x0d, 0x87, 0x7a, 0xad, 0x9e, 0x8d, 0x13, 0xd1, 0x0b, 0x53, 0x0c, 0x17, 0x29, 0x40, 0x17, 0xe7, 0x07, 0x75, 0x75, 0x66, 0xfb, 0x94, 0x9d, 0xa7, 0x2c, 0xc2, 0x65, 0x6b, 0xc9, 0xd9, 0xc0, 0xe6, 0x87, 0x31, 0x54, 0x29, 0x64, 0x28, 0x45, 0x63, 0x6c, 0x22, 0xed, 0xaa, 0x3b, 0xcf, 0x97, 0xd5, 0x7a, 0x16, 0xec, 0x06, 0x1d, 0xbf, 0x8a, 0x92, 0x68, 0xa9, 0xdd, 0x6d, 0xe2, 0x2a, 0x4b, 0xca, 0x49, 0xa9, 0x3d, 0x37, 0x2b, 0xdc, 0x00, 0xd9, 0xe4, 0x5e, 0x3c, 0x2c, 0xfd, 0x5f, 0x01, 0xcf, 0xd4, 0x08, 0x38, 0x1b, 0x2a, 0xa5, 0x56, 0xea, 0xfe, 0x53, 0xc9, 0xbd, 0x39, 0xcb, 0x93, 0x5f, 0x06, 0xf0, 0x14, 0x82, 0xfb, 0xe6, 0xe1, 0x24, 0x93, 0xeb, 0xdc, 0x71, 0xc1, 0xac, 0xd9, 0x0a, 0xda, 0x94, 0x1e, 0xcf, 0xf4, 0x27, 0x36, 0x91, 0x8a, 0x47, 0x8c, 0xda, 0xe2, 0x7c, 0x31, 0xbf, 0x70, 0x04, 0x98, 0xfb, 0x3b, 0xf3, 0x70, 0x78, 0xa3, 0x0b, 0x67, 0x69, 0x2b, 0x82, 0x5d, 0xb1, 0xcc, 0x1a, 0x0d, 0x82, 0xf2, 0xe3, 0x6f, 0x0e, 0x54, 0x5b, 0x2a, 0xb9, 0x6a, 0x1d, 0x11, 0x10, 0x20, 0x35, 0x76, 0x7b, 0xa1, 0x64, 0x2b, 0x96, 0x12, 0x5c, 0x62, 0xab, 0x42, 0xc8, 0x30, 0xb7, 0xba, 0x51, 0xb3, 0xe7, 0xe1, 0xa5, 0x21, 0xdc, 0xe5, 0xad, 0x7c, 0xf8, 0x83, 0x7a, 0xa3, 0xec, 0xd4, 0xd1, 0x0d, 0x8b, 0x2e, 0xbf, 0xc4, 0x1f, 0x47, 0x07, 0x5a, 0xa6, 0x4d, 0x5b, 0xc9, 0x21, 0xa2, 0x31, 0x7b, 0xcd, 0x61, 0xcd, 0x1b, 0x51, 0xfc, 0xea, 0xff, 0xb0, 0x86, 0xce, 0x10, 0x51, 0xcb, 0x77, 0xcd, 0x74, 0x4b, 0x3e, 0x6b, 0x0b, 0x9d, 0x42, 0x9f, 0x0f, 0xfa, 0x7c, 0x1e, 0x30, 0x11, 0xc1, 0x02, 0x96, 0x85, 0xd3, 0x8b, 0xdd, 0xc2, 0xc7, 0xf8, 0x09, 0x77, 0x8b, 0x8d, 0xef, 0x3a, 0xfc, 0x21, 0xd6, 0x0b, 0x62, 0x8a, 0x16, 0xd8, 0x5a, 0x75, 0x32, 0x23, 0xd3, 0x5f, 0xa8, 0x9b, 0xba, 0xac, 0x78, 0x64, 0xbf, 0xc4, 0x41, 0x01, 0x5e, 0x95, 0x17, 0x52, 0xf1, 0x53, 0x77, 0xdc, 0x68, 0x62, 0x64, 0xd0, 0x62, 0x9b, 0x73, 0xd0, 0xec, 0xd6, 0x1e, 0x9a, 0xef, 0xea, 0xc6, 0xb7, 0x17, 0x83, 0x74, 0x70, 0xb4, 0x0b, 0x77, 0x7b, 0xab, 0xb5, 0x00, 0x9f, 0xe9, 0x20, 0xc6, 0xf3, 0x3e, 0xc3, 0x1b, 0x5f, 0x53, 0x08, 0x46, 0x5c, 0x4e, 0x03, 0x8f, 0x4f, 0xdc, 0xdc, 0x9d, 0x02, 0xba, 0x9c, 0xd9, 0xca, 0x0d, 0x6d, 0xe7, 0x33, 0xb0, 0xf5, 0x37, 0x8d, 0x04, 0xb0, 0x95, 0x1e, 0x39, 0x8b, 0x15, 0xcc, 0xc6, 0x15, 0x86, 0x41, 0x41, 0x71, 0xb4, 0x48, 0x29, 0x67, 0x97, 0x3f, 0x96, 0x52, 0x1f, 0x75, 0x8a, 0xd7, 0x68, 0xae, 0xa1, 0x66, 0xc5, 0x18, 0xaa, 0x59, 0xbd, 0x8e, 0xa5, 0xa2, 0x89, 0x73, 0x36, 0xa5, 0x01, 0xdd, 0x4a, 0x29, 0xea, 0x1f, 0x03, 0x9a, 0xbc, 0x30, 0x80, 0xa5, 0xa8, 0x44, 0xc1, 0xa3, 0xc5, 0x32, 0xd7, 0x6b, 0xdf, 0x6e, 0xba, 0xd2, 0x02, 0x61, 0x7a, 0x48, 0xb4, 0x8a, 0xa3, 0x7b, 0x26, 0x4d, 0x1b, 0x76, 0x52, 0xd7, 0x4b, 0x18, 0xe7, 0xa0, 0x5a, 0xf3, 0x5e, 0x05, 0x4a, 0x16, 0x65, 0xe3, 0xa2, 0x52, 0x06, 0xa2, 0x68, 0x52, 0x14, 0x00, 0xd3, 0x76, 0xb4, 0xdc, 0x9c, 0xe1, 0x7d, 0xaf, 0xb3, 0x78, 0xe1, 0x1a, 0xf0, 0xa6, 0xbb, 0x72, 0x8b, 0x4c, 0xc7, 0x44, 0xc7, 0xa0, 0xcf, 0x96, 0xf1, 0xfc, 0x1b, 0x6d, 0x92, 0x96, 0x37, 0x21, 0x2b, 0xfa, 0x00, 0xa5, 0x92, 0xd2, 0x38, 0xe9, 0x22, 0xf9, 0x7c, 0xf6, 0x6e, 0x7d, 0xad, 0xd0, 0xd6, 0x46, 0xa7, 0x4a, 0x38, 0x85, 0xe8, 0xce, 0x6a, 0xc8 }; +constexpr AccessUnit AC3_SIZE_MISMATCH_EXPECTED_AU = { 0x15f90, 0x159b2, false, 0, {}, { 0x3d, 0x29, 0x18, 0x6e, 0x61, 0xd9, 0x34, 0x8e, 0x10, 0xc9, 0x57, 0xdd, 0x59, 0x03, 0xca, 0x97, 0x43, 0xdf, 0x1f, 0xd3 } }; + + +const std::vector M2V_SEQUENCE_END_SPLIT_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0xf2, 0x26, 0xae, 0xd3, 0xf0, 0x56, 0xd2, 0xd9, 0x26, 0xf4, 0x15, 0xd1, 0x9e, 0xbd, 0x68, 0x9d, 0x99, 0x4a, 0x77, 0xcf, 0x6c, 0xea, 0x0a, 0xbe, 0x4b, 0x6c, 0x0f, 0x35, 0x82, 0xf6, 0xef, 0x3d, 0xce, 0x01, 0x6c, 0x79, 0x05, 0x4e, 0xff, 0xb3, 0xca, 0x99, 0xd2, 0x78, 0xdd, 0xf0, 0x68, 0xea, 0x2c, 0x8f, 0x8f, 0xcd, 0x9e, 0x33, 0x8e, 0x0d, 0xe4, 0xf7, 0x67, 0x03, 0x2c, 0x75, 0x5c, 0x05, 0xbf, 0x6a, 0x5d, 0x78, 0x35, 0x68, 0x9d, 0x40, 0xaa, 0xde, 0x5e, 0xde, 0x1c, 0xcb, 0x4a, 0x61, 0x54, 0x61, 0x3a, 0x88, 0xee, 0x47, 0x71, 0xe1, 0xaa, 0xca, 0x47, 0x21, 0xeb, 0x9d, 0x5b, 0xf5, 0x51, 0xc1, 0x08, 0xdb, 0xd1, 0x75, 0x1b, 0x7f, 0x41, 0x7c, 0x99, 0xd1, 0xaa, 0x55, 0x84, 0xf7, 0xac, 0x85, 0xdc, 0x38, 0xf2, 0x26, 0x70, 0xe1, 0x73, 0x49, 0x19, 0x19, 0x1d, 0xbd, 0x77, 0x7a, 0xc5, 0xc5, 0xeb, 0xb9, 0x12, 0x07, 0x4b, 0x06, 0x6b, 0x83, 0x27, 0x8b, 0x66, 0x5a, 0xf5, 0x81, 0x38, 0x77, 0x75, 0x59, 0xda, 0xac, 0x22, 0x41, 0xe6, 0xcf, 0x49, 0xaf, 0xd1, 0x9e, 0x1d, 0xf3, 0x3d, 0x98, 0x05, 0x11, 0x64, 0x8c, 0x86, 0x36, 0x75, 0xc5, 0xd4, 0x7a, 0xda, 0x76, 0x11, 0x6f, 0x5f, 0x60, 0x6f, 0x36, 0xd5, 0x8f, 0x0a, 0xa4, 0x1c, 0x51, 0x9e, 0x10, 0x9c, 0x66, 0xe9, 0xac, 0xdf, 0xde, 0x6f, 0xcc, 0xb8, 0xd6, 0x24, 0xee, 0xe2, 0xaf, 0x5e, 0x75, 0xe0, 0x34, 0x0f, 0x6f, 0xa3, 0x85, 0xbe, 0x61, 0x7b, 0xcf, 0xca, 0x06, 0xf6, 0xf5, 0x02, 0x3b, 0xdb, 0x4f, 0x17, 0x0f, 0x72, 0xdf, 0x97, 0x88, 0xe5, 0xb1, 0x89, 0x9e, 0xec, 0xca, 0x65, 0xb2, 0x40, 0x1f, 0xb4, 0x94, 0x15, 0x34, 0xfe, 0xa6, 0xb4, 0x0f, 0xa7, 0x7f, 0x0b, 0xf7, 0x59, 0x1d, 0x39, 0xef, 0x70, 0xcc, 0x5d, 0x70, 0x01, 0x0e, 0xee, 0xb5, 0x85, 0x56, 0xe8, 0x04, 0xe3, 0x10, 0xae, 0x6e, 0x70, 0xad, 0xf5, 0x56, 0xbe, 0xc8, 0x01, 0xbc, 0x42, 0xd0, 0x50, 0x05, 0xb7, 0xfd, 0x0f, 0x5a, 0x73, 0xec, 0x0c, 0xef, 0x18, 0xd0, 0xb5, 0xbb, 0x9f, 0x62, 0xac, 0x8e, 0xfc, 0x53, 0xac, 0xec, 0x4f, 0x81, 0x98, 0xef, 0x24, 0x46, 0xb5, 0x71, 0x8d, 0xd2, 0xf4, 0xec, 0x60, 0x34, 0x2c, 0x5f, 0xd3, 0x33, 0xfb, 0xcb, 0x20, 0xe1, 0xb2, 0x66, 0x66, 0x4d, 0x39, 0xb0, 0xa2, 0xfc, 0x0f, 0x5f, 0x52, 0xfc, 0x69, 0xe7, 0xf7, 0xc0, 0xd7, 0xc6, 0x36, 0xe0, 0x23, 0x1d, 0x62, 0xe3, 0x3f, 0xb2, 0x66, 0x6b, 0x1f, 0x88, 0x39, 0x21, 0xf0, 0x81, 0xf7, 0x73, 0x41, 0x07, 0xb7, 0x04, 0xb6, 0x22, 0xd3, 0xbb, 0x19, 0xd9, 0xfb, 0xd0, 0xb9, 0xa7, 0xfc, 0x58, 0x88, 0x4d, 0xa9, 0xaa, 0x19, 0xaf, 0xfb, 0x1d, 0x1a, 0xec, 0x50, 0x3f, 0xb2, 0x02, 0x63, 0x30, 0xba, 0x50, 0x42, 0xa0, 0x5e, 0x55, 0xc9, 0xb4, 0x92, 0xb6, 0x31, 0x0d, 0x21, 0xa3, 0x8f, 0xd1, 0x76, 0x48, 0x26, 0xa5, 0xf5, 0xa8, 0x86, 0x26, 0xc3, 0x52, 0x66, 0x92, 0x38, 0x0d, 0x0e, 0xf5, 0xd7, 0xee, 0x53, 0x3c, 0x34, 0xa4, 0xf0, 0xdf, 0xda, 0x98, 0x33, 0x28, 0x3e, 0x66, 0xea, 0x5e, 0xec, 0xd1, 0xf1, 0xca, 0x0b, 0x1e, 0x6d, 0x93, 0x59, 0xf8, 0xb6, 0xec, 0x75, 0x1f, 0xe0, 0xa9, 0xd9, 0x47, 0x34, 0xfc, 0x40, 0x10, 0x72, 0xc7, 0xd3, 0x9c, 0x35, 0x58, 0x02, 0x1d, 0x2b, 0x5f, 0x2c, 0xa1, 0xcc, 0xbf, 0x3a, 0x4d, 0xde, 0x03, 0xc8, 0x51, 0x1e, 0x6a, 0xd3, 0xe8, 0x71, 0x50, 0x63, 0x9f, 0x1b, 0x42, 0xd0, 0x0a, 0xca, 0xc5, 0x19, 0xc8, 0x7f, 0xf0, 0xd1, 0x65, 0x6e, 0x49, 0xb1, 0x55, 0xbf, 0x14, 0x12, 0x07, 0xfe, 0xb1, 0xda, 0x1b, 0xc0, 0xd0, 0xf8, 0x5f, 0x03, 0xf0, 0x92, 0x8e, 0x45, 0xd8, 0xe7, 0xaa, 0x46, 0x4b, 0x10, 0xfa, 0xc2, 0xdd, 0x67, 0xdd, 0x9b, 0x67, 0x3d, 0xbd, 0xa9, 0xd7, 0x63, 0x08, 0x74, 0xd9, 0xc3, 0x64, 0x2c, 0x5f, 0xc8, 0x1c, 0x70, 0x8e, 0x01, 0x4f, 0x72, 0x83, 0xe3, 0x80, 0x21, 0x9e, 0xb6, 0x3f, 0x31, 0xe4, 0x07, 0xb0, 0xfa, 0xf6, 0xd3, 0x4b, 0xc7, 0xb8, 0xf0, 0xf9, 0xcd, 0xe3, 0x81, 0x30, 0xfd, 0x29, 0xdd, 0xc5, 0xc7, 0x01, 0xc3, 0xef, 0xd2, 0xff, 0x45, 0x1c, 0xff, 0x16, 0x72, 0x5a, 0x65, 0x4a, 0x61, 0x97, 0xf1, 0xc1, 0xa6, 0xee, 0x69, 0xa5, 0xc8, 0x74, 0x79, 0x9e, 0xf0, 0x27, 0xc4, 0x2d, 0x45, 0xb3, 0x91, 0x41, 0x41, 0xd9, 0x7b, 0x6f, 0x12, 0x55, 0xe9, 0x01, 0x6c, 0xeb, 0x3c, 0x90, 0x2f, 0xee, 0x9d, 0xa0, 0x5c, 0x7a, 0x5b, 0xa7, 0x01, 0xca, 0x31, 0xd3, 0xfc, 0xb4, 0xd7, 0x25, 0xbe, 0xc3, 0xa0, 0xde, 0xa9, 0x74, 0x7c, 0x6d, 0x0b, 0xd2, 0x47, 0x76, 0x4a, 0x3f, 0x8e, 0xbd, 0xe3, 0x38, 0xfb, 0xf6, 0x4d, 0xaf, 0x9b, 0xb8, 0xba, 0x50, 0xaa, 0x60, 0x81, 0xd4, 0x0e, 0x8d, 0x6b, 0xd2, 0x9b, 0xdd, 0x22, 0xf0, 0x38, 0x4b, 0xef, 0xd0, 0x34, 0x76, 0x65, 0x53, 0xd5, 0xcf, 0x41, 0xe3, 0xaa, 0x1d, 0xa1, 0xd7, 0xa7, 0xdb, 0x75, 0xc6, 0x69, 0xf0, 0x71, 0xbe, 0x98, 0x06, 0xe6, 0x8e, 0x04, 0x6e, 0x7d, 0x89, 0x24, 0xc6, 0xa4, 0x53, 0xa6, 0x68, 0x6a, 0xdf, 0xb3, 0xa3, 0xbe, 0x9f, 0x76, 0x17, 0xa1, 0x5f, 0x18, 0x60, 0xf0, 0x85, 0x23, 0x67, 0x04, 0x0e, 0x48, 0x93, 0x49, 0x84, 0xc3, 0x11, 0x30, 0x0a, 0x8c, 0x86, 0x00, 0x42, 0x0b, 0x71, 0xc0, 0x6c, 0xb9, 0xd2, 0x7f, 0x75, 0x30, 0x17, 0x37, 0x2c, 0x48, 0x96, 0xe0, 0xc0, 0x1d, 0xdf, 0x45, 0xcd, 0xc5, 0x69, 0xcb, 0xcf, 0x2d, 0x55, 0x7b, 0x0a, 0x14, 0x44, 0x68, 0xd2, 0x55, 0x6a, 0xa4, 0xe7, 0x62, 0x87, 0x43, 0x9d, 0x04, 0x26, 0x7e, 0x2e, 0x6a, 0x25, 0x8c, 0x45, 0x20, 0x26, 0xfc, 0x6a, 0x10, 0xfc, 0x41, 0x04, 0x1e, 0x1c, 0x0e, 0xf4, 0x0c, 0xdb, 0x82, 0x60, 0xc5, 0x1c, 0x11, 0xdc, 0x17, 0x07, 0x01, 0xa5, 0x83, 0x24, 0xa8, 0x7b, 0x4c, 0x7f, 0xf2, 0x92, 0x3e, 0x5f, 0xce, 0x2a, 0xf5, 0xa9, 0x73, 0x17, 0xe2, 0x0a, 0x11, 0x8b, 0x10, 0x18, 0x1e, 0x0a, 0x97, 0xcd, 0xdb, 0x51, 0x6a, 0x5a, 0x69, 0x69, 0x61, 0x06, 0x9c, 0xd3, 0x80, 0xfa, 0xe3, 0xac, 0x8b, 0x9e, 0xbf, 0xbe, 0x1d, 0x35, 0x95, 0xda, 0xd8, 0x42, 0xcf, 0x01, 0xca, 0x88, 0x02, 0x2a, 0x93, 0x1e, 0xa8, 0xd7, 0xe6, 0x60, 0x4c, 0xac, 0x14, 0x16, 0x4b, 0x50, 0xda, 0x7a, 0x72, 0x3b, 0x1a, 0xeb, 0x52, 0x2e, 0xc9, 0xa1, 0x96, 0x2a, 0xc9, 0x73, 0x4d, 0xe2, 0x06, 0xff, 0xe4, 0xa7, 0xf1, 0xf4, 0x32, 0x4e, 0x5f, 0xd8, 0x37, 0x9a, 0x51, 0x48, 0x6c, 0xf3, 0x31, 0xb5, 0x3c, 0x10, 0xb6, 0xc1, 0x74, 0xa8, 0xad, 0x53, 0x64, 0x7d, 0xc5, 0xa2, 0x21, 0xf8, 0x00, 0xd6, 0x48, 0x05, 0xd7, 0x59, 0x65, 0xa2, 0xaa, 0x61, 0xd1, 0x69, 0xd6, 0xf7, 0xe2, 0xf3, 0x03, 0xd3, 0x1a, 0xeb, 0xaa, 0x99, 0x5f, 0xc6, 0x9f, 0x97, 0x3b, 0x80, 0x7c, 0x98, 0xdd, 0xf0, 0x87, 0xde, 0x03, 0xec, 0x82, 0xe4, 0xce, 0x55, 0xb3, 0xda, 0x42, 0x8b, 0xbf, 0xc6, 0x51, 0xfd, 0x20, 0x21, 0xff, 0xb6, 0x0b, 0xe6, 0x80, 0x74, 0x6d, 0x45, 0x9a, 0xe5, 0x63, 0x6d, 0x30, 0xe9, 0x1a, 0x0c, 0x42, 0x48, 0x08, 0xa5, 0xd5, 0xa3, 0x91, 0x38, 0x7d, 0xc7, 0x73, 0x4c, 0xef, 0x26, 0xad, 0x46, 0x61, 0xed, 0x38, 0x73, 0xa5, 0x1e, 0x0d, 0x7b, 0x1e, 0xe6, 0x62, 0xa4, 0x96, 0x7b, 0x6d, 0x52, 0x7b, 0x3d, 0x56, 0xb5, 0x41, 0xab, 0xe4, 0xa8, 0xd0, 0x94, 0x1a, 0x15, 0x7e, 0x34, 0x23, 0xab, 0x77, 0xcb, 0xee, 0x85, 0x0e, 0x2f, 0x1b, 0xf7, 0x74, 0x49, 0x31, 0xd8, 0xfa, 0xf2, 0xd5, 0xbf, 0xa6, 0xe3, 0x65, 0x46, 0x77, 0x9c, 0x69, 0x67, 0x13, 0x20, 0xd9, 0xfc, 0xed, 0xc7, 0x12, 0x2b, 0xf1, 0x98, 0x40, 0xbb, 0x8e, 0x87, 0xe2, 0x1a, 0xa2, 0x4a, 0xe7, 0x7f, 0x3a, 0xda, 0xbe, 0x21, 0xcd, 0x9e, 0xb2, 0x75, 0x52, 0xb2, 0x97, 0x48, 0x08, 0xe2, 0x9a, 0x84, 0x2b, 0x47, 0x00, 0x94, 0xe0, 0x81, 0x4e, 0xef, 0x6c, 0xca, 0x21, 0x7c, 0x65, 0xe9, 0x0a, 0x41, 0x16, 0xc3, 0x8e, 0x59, 0xd2, 0xf1, 0x18, 0x31, 0xde, 0xef, 0xfb, 0x2b, 0xcc, 0x42, 0x8d, 0x51, 0xcd, 0x07, 0x98, 0x76, 0x90, 0x3c, 0x75, 0xb7, 0x68, 0x77, 0xec, 0x41, 0xd5, 0xca, 0x38, 0x8e, 0x10, 0xf8, 0xe3, 0xa5, 0x05, 0x14, 0xac, 0xc7, 0x78, 0x61, 0xde, 0xc6, 0xe0, 0x14, 0x2e, 0x04, 0x4f, 0xe6, 0x3b, 0x76, 0x9c, 0xa5, 0x43, 0xbf, 0xdb, 0x7e, 0x24, 0xae, 0x62, 0x28, 0x7d, 0xbb, 0x98, 0x98, 0x30, 0x29, 0x56, 0x9f, 0x9d, 0xb5, 0x8a, 0xa5, 0x3d, 0x75, 0xb8, 0x35, 0xea, 0x34, 0xa9, 0x4c, 0xdb, 0xf3, 0x4d, 0x62, 0x19, 0x52, 0x4b, 0xfa, 0x3d, 0xee, 0x93, 0xb6, 0xff, 0x4f, 0x56, 0x0e, 0x84, 0x36, 0x4a, 0xb3, 0x58, 0x36, 0x30, 0x23, 0x72, 0xc8, 0x7c, 0xd2, 0x01, 0x5d, 0xbc, 0xb0, 0xc5, 0x4b, 0x78, 0xf9, 0x7c, 0x66, 0x17, 0x5e, 0x10, 0x80, 0xef, 0x7c, 0xa5, 0x2a, 0xe8, 0x67, 0x01, 0xfa, 0xe3, 0xf0, 0x6d, 0x50, 0x80, 0x6b, 0x88, 0x74, 0x6e, 0x46, 0xd3, 0xad, 0x04, 0xc1, 0x43, 0xb3, 0xe5, 0x17, 0x87, 0x24, 0xfe, 0x6d, 0x46, 0x58, 0x4b, 0xec, 0xf3, 0x62, 0x66, 0x39, 0x2d, 0xbb, 0xfe, 0x8b, 0x5c, 0xf0, 0x81, 0x4f, 0xe4, 0x82, 0xdd, 0x9d, 0xac, 0x6d, 0x7e, 0xb1, 0x77, 0x16, 0x49, 0xbe, 0xd3, 0x28, 0x10, 0xc7, 0xb3, 0xbf, 0xc6, 0x0b, 0x5e, 0x8a, 0xd9, 0x5c, 0xf6, 0xd7, 0x5d, 0xde, 0x27, 0xba, 0x04, 0xd9, 0xdc, 0xbb, 0x01, 0xd4, 0x10, 0x46, 0xdd, 0x60, 0x75, 0xea, 0xf1, 0x8e, 0xbc, 0xf1, 0xf8, 0x67, 0xee, 0x1c, 0x51, 0x3e, 0xe9, 0xd5, 0xfa, 0x25, 0xa1, 0x0f, 0x0f, 0x8c, 0x8e, 0x89, 0x77, 0xe5, 0xe6, 0x1a, 0x28, 0x34, 0x16, 0x85, 0x94, 0x5b, 0x83, 0xe3, 0xde, 0xfe, 0x91, 0x29, 0xdc, 0xe1, 0xe6, 0x75, 0xa8, 0x0a, 0xf6, 0x08, 0xe7, 0x4d, 0xad, 0xb5, 0x0e, 0x63, 0xf3, 0x7d, 0xbf, 0xf0, 0xeb, 0xd1, 0x71, 0xe7, 0x76, 0xf5, 0x87, 0xb6, 0x95, 0x44, 0xae, 0x2f, 0x6b, 0x72, 0xbd, 0xd6, 0x69, 0xa3, 0x42, 0xf4, 0x2f, 0x3b, 0xaf, 0x03, 0x6b, 0x56, 0x86, 0x50, 0xa1, 0xfd, 0xcc, 0x93, 0xb3, 0xff, 0xc3, 0x72, 0xfe, 0x7c, 0x56, 0x94, 0x74, 0x70, 0xeb, 0xea, 0x83, 0x9c, 0xcc, 0xed, 0xcf, 0xf9, 0xae, 0xce, 0xfe, 0xa7, 0x3f, 0xf8, 0xb0, 0xba, 0xdf, 0x7a, 0x85, 0x13, 0xb4, 0xe5, 0xf7, 0x6b, 0x24, 0xc6, 0x2c, 0x01, 0x8e, 0x32, 0xb9, 0x29, 0xa6, 0xf1, 0xb7, 0x2e, 0xc6, 0x94, 0xbd, 0xd4, 0x3e, 0x07, 0x57, 0x3a, 0x32, 0xaf, 0x27, 0xbe, 0x66, 0x90, 0x20, 0x8a, 0x45, 0xc9, 0xc5, 0x4f, 0x21, 0x42, 0x95, 0x4a, 0xbc, 0x3c, 0x0d, 0x8d, 0x4e, 0x22, 0xaa, 0x1c, 0xba, 0x2a, 0xb3, 0x69, 0xd9, 0xf4, 0x33, 0x89, 0xde, 0x8f, 0xc5, 0xf0, 0x65, 0x90, 0xb3, 0x62, 0x74, 0x4c, 0x7f, 0xb3, 0xa5, 0x1e, 0x67, 0x62, 0x1e, 0x29, 0x88, 0x6d, 0x2e, 0x65, 0x5c, 0x57, 0xbe, 0x3b, 0xed, 0x77, 0xd0, 0x55, 0xe9, 0xcc, 0x6b, 0xe6, 0x0a, 0xcb, 0xa6, 0x7a, 0x6e, 0x9d, 0xbe, 0x6f, 0xba, 0x87, 0xc8, 0x0d, 0x5d, 0x03, 0xaf, 0xeb, 0xa9, 0x24, 0xb8, 0x51, 0x17, 0x17, 0x1b, 0x73, 0xd8, 0x26, 0xbf, 0x89, 0xeb, 0x27, 0x68, 0xff, 0x18, 0x9e, 0xc6, 0x70, 0xb5, 0x15, 0x17, 0xc0, 0x5b, 0x7b, 0x93, 0xd2, 0x0f, 0x26, 0x8f, 0x36, 0x2a, 0x1b, 0x6c, 0x97, 0x77, 0x69, 0xaf, 0x17, 0xeb, 0xf2, 0x3e, 0x96, 0xc4, 0xf9, 0xba, 0xce, 0xb8, 0x57, 0xb7, 0x46, 0x79, 0xe8, 0x5c, 0xfd, 0x75, 0xdd, 0x03, 0xbe, 0x37, 0x5a, 0x51, 0x63, 0xc1, 0xa7, 0xb9, 0x92, 0x68, 0x23, 0xb1, 0x74, 0x84, 0x49, 0xe2, 0x8e, 0xc1, 0x94, 0x6f, 0x3d, 0x89, 0xdf, 0x11, 0xc0, 0xfc, 0x00, 0x0c, 0x17, 0xdc, 0x6e, 0x92, 0x37, 0xde, 0x91, 0xf5, 0x18, 0x41, 0x90, 0x58, 0xc4, 0x8b, 0xe8, 0x58, 0xd7, 0x87, 0xe6, 0x27, 0xc9, 0xce, 0x7b, 0xc0, 0x2e, 0xf8, 0xd6, 0x1c, 0x7c, 0xe3, 0x79, 0x95, 0x92, 0x1f, 0x71, 0xd8, 0x9f, 0x7f, 0x0f, 0x25, 0x99, 0x03, 0x0f, 0x81, 0xc7, 0x25, 0x5d, 0x49, 0x51, 0xad, 0xc8, 0x9b, 0x62, 0x87, 0x32, 0x62, 0x18, 0xef, 0xb0, 0x72, 0x56, 0xb4, 0x99, 0xa9, 0xff, 0xb0, 0xa0, 0x6a, 0xb6, 0xf7, 0xad, 0x53, 0xd4, 0xd8, 0xc4, 0xc5, 0xa6, 0x9a, 0x94, 0x37, 0x8d, 0x0d, 0xc6, 0x56, 0x90, 0x00, 0x4a, 0x35, 0x70, 0x60, 0x5a, 0x47, 0x58, 0x1d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x11, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0xb7, 0xde, 0xad, 0xbe, 0xef, 0x92, 0x8a, 0xad, 0x4b, 0x8e, 0x21, 0x89, 0xdc, 0xf8, 0x7d, 0x5b, 0x89, 0xa1, 0x2a, 0x4b, 0xb2, 0xff, 0xa4, 0x5e, 0x1c, 0x1f, 0x0f, 0x4c, 0x68, 0x16, 0x18, 0x5e, 0x99, 0xbd, 0x17, 0x33, 0x71, 0x7c, 0x20, 0x20, 0xf1, 0xab, 0x29, 0x8f, 0x99, 0xd3, 0x63, 0xd7, 0x9d, 0x6e, 0xac, 0x4e, 0x23, 0x68, 0xb2, 0x51, 0x45, 0x5e, 0xe4, 0x57, 0x53, 0x49, 0x2c, 0x78, 0x20, 0xba, 0xd2, 0xb6, 0xf8, 0xef, 0x3e, 0xb8, 0xea, 0x7f, 0x79, 0xc8, 0x57, 0xbc, 0x72, 0xdf, 0x31, 0x38, 0x38, 0x33, 0xd8, 0xe4, 0x9c, 0x00, 0x0f, 0x94, 0xb3, 0x63, 0xbd, 0x27, 0xde, 0x73, 0x58, 0x24, 0xaf, 0x73, 0xd1, 0x63, 0xc8, 0xb3, 0x28, 0x0d, 0x12, 0x88, 0x20, 0xbe, 0x3c, 0x67, 0x72, 0x1c, 0x71, 0x92, 0x8a, 0xc8, 0x64, 0x53, 0xaf, 0x30, 0xd3, 0xd3, 0xe8, 0xa1, 0x36, 0x39, 0xba, 0xe6, 0x63, 0xe7, 0x72, 0x8d, 0x78, 0xc0, 0xf3, 0xf4, 0xaf, 0x5a, 0xc8, 0xc1, 0x08, 0x20, 0xe4, 0xf9, 0x49, 0xdb, 0xcf, 0x85, 0x4e, 0x5d, 0x63, 0x7f, 0xcf, 0x8e, 0xdf, 0xaf, 0xa3, 0x3a, 0xfd, 0x6e, 0x05, 0xc7, 0xe7, 0x34, 0xd6, 0x78, 0x05, 0x0c, 0x8a, 0xe3, 0xd6, 0x4c, 0x55, 0x2a, 0xfa, 0xa4, 0xd0, 0x27, 0x0a, 0xde, 0x36, 0xa1, 0x92, 0xd2, 0xb2, 0xf0, 0x09, 0xd5, 0xf6, 0xf2, 0x8b, 0x2c, 0x13, 0x7c, 0xe9, 0xfc, 0xe5, 0x3f, 0xd3, 0x74, 0x5d, 0xd4, 0x99, 0x3c, 0xd4, 0xc9, 0x64, 0x7a, 0x65, 0xf8, 0x13, 0x34, 0x50, 0x0b, 0xab, 0x08, 0x77, 0x1a, 0x94, 0x0d, 0xcd, 0xb2, 0xef, 0x20, 0x15, 0xf9, 0x5b, 0x33, 0x21, 0x95, 0xf5, 0x70, 0x19, 0x3d, 0x1c, 0x26, 0xe9, 0x54, 0x8c, 0x46, 0x32, 0x13, 0x36, 0x64, 0x4f, 0x19, 0x7f, 0xd9, 0xa4, 0xc8, 0xd7, 0x1a, 0x8d, 0x20, 0xb0, 0x30, 0xba, 0xcf, 0x2b, 0xd5, 0x7e, 0x30, 0x67, 0x83, 0x88, 0xf0, 0x3b, 0x5d, 0x9d, 0x6b, 0x59, 0x51, 0xd4, 0xdc, 0x5d, 0xa4, 0x65, 0xa2, 0x81, 0x4b, 0x19, 0xcd, 0xab, 0x37, 0x76, 0x67, 0x44, 0xaf, 0x3f, 0xdd, 0x74, 0xd6, 0x01, 0x61, 0xf2, 0x8f, 0xf7, 0x9e, 0x55, 0x3f, 0xe7, 0xb0, 0x2f, 0x65, 0x1f, 0x64, 0xf2, 0x47, 0x61, 0x48, 0xa3, 0x12, 0xad, 0x4e, 0x0d, 0x11, 0x31, 0x08, 0xba, 0x4a, 0x76, 0x32, 0x3f, 0x90, 0x69, 0x80, 0x3e, 0x42, 0x8e, 0xea, 0x18, 0xac, 0x25, 0x6f, 0x7e, 0x95, 0xff, 0x41, 0xd7, 0x84, 0xf3, 0x5e, 0xea, 0x7f, 0x40, 0x2d, 0xbf, 0x63, 0xf7, 0xe3, 0xe7, 0xbf, 0x6b, 0xdb, 0xdb, 0x44, 0x40, 0x78, 0x9c, 0x6c, 0xa1, 0x17, 0x1a, 0xf4, 0x8f, 0x55, 0xe7, 0x41, 0xf2, 0x1b, 0x52, 0xe9, 0x6e, 0x3b, 0x66, 0x63, 0x3a, 0x89, 0x81, 0x40, 0x02, 0xea, 0x6b, 0x2e, 0x81, 0xd5, 0xbe, 0x4b, 0x73, 0x8a, 0x7f, 0xd7, 0x9e, 0x8b, 0xf7, 0xc8, 0x05, 0xb7, 0x48, 0xbb, 0xb0, 0x5d, 0xe8, 0x92, 0xbf, 0x5f, 0xdd, 0xcc, 0x32, 0x42, 0xb7, 0x6b, 0xff, 0x1d, 0x8a, 0x45, 0xe3, 0x9e, 0xaa, 0x57, 0x3a, 0x2d, 0x54, 0xbf, 0x98, 0x04, 0x5b, 0x29, 0xb3, 0x10, 0xcc, 0x4c, 0x4b, 0xec, 0x5f, 0x94, 0xfe, 0xba, 0x04, 0x77, 0x4d, 0x1c, 0x84, 0x2e, 0x44, 0xe5, 0x4a, 0xcb, 0x11, 0x89, 0x30, 0x46, 0xda, 0x8a, 0xb9, 0x1a, 0xda, 0x6f, 0xc5, 0x24, 0xc9, 0xff, 0xa1, 0xf8, 0x00, 0x6c, 0x44, 0xaa, 0x48, 0x5d, 0x9e, 0xa8, 0xe5, 0x74, 0xda, 0xe3, 0x11, 0x0a, 0x67, 0xca, 0xb9, 0x5c, 0x68, 0xed, 0x21, 0x5b, 0x09, 0x65, 0x3c, 0x4a, 0xe9, 0x72, 0xbb, 0xad, 0x27, 0xb7, 0xe5, 0x5f, 0x0c, 0xfd, 0x04, 0xba, 0xcf, 0x71, 0x6b, 0x71, 0x62, 0x9e, 0xec, 0x40, 0x2c, 0x29, 0x87, 0x5a, 0xef, 0x1d, 0x18, 0xee, 0xd1, 0x67, 0x9b, 0xea, 0x35, 0x9c, 0xc5, 0xb3, 0x7a, 0xcd, 0x80, 0xbc, 0xd4, 0xf7, 0x38, 0x56, 0x9b, 0x02, 0xca, 0x61, 0xa1, 0x8d, 0x15, 0x96, 0x8d, 0xe8, 0xb6, 0xb1, 0xb9, 0x74, 0xc8, 0x9b, 0x73, 0xa9, 0xe6, 0x16, 0x81, 0x9e, 0xb2, 0xf8, 0x5a, 0x56, 0xf2, 0x9e, 0x8c, 0x26, 0xe7, 0xc5, 0x40, 0xbf, 0xd8, 0x94, 0x91, 0x04, 0x21, 0x0c, 0x00, 0x04, 0x7e, 0x78, 0x12, 0x5a, 0x6e, 0x69, 0x57, 0xb6, 0x27, 0xa6, 0x39, 0xe5, 0xe5, 0x00, 0x0e, 0xe0, 0xe2, 0xe1, 0x4b, 0x83, 0x9d, 0x51, 0x00, 0xfc, 0xa9, 0xb7, 0x46, 0xf5, 0x99, 0x1a, 0x1f, 0xfe, 0x54, 0x81, 0x2e, 0xe3, 0x63, 0xcb, 0x73, 0xc4, 0x5b, 0x8f, 0x2d, 0x0d, 0x76, 0x83, 0x3c, 0xe1, 0x80, 0x5b, 0xdc, 0x71, 0x8d, 0x6a, 0x3b, 0xdc, 0xae, 0x31, 0x4a, 0xc6, 0xfb, 0x6e, 0x1c, 0xc3, 0xdb, 0xb5, 0xa0, 0x83, 0x7a, 0x34, 0xfb, 0x89, 0x57, 0xa4, 0xd5, 0x7c, 0x46, 0x5e, 0xa7, 0x79, 0xf7, 0x75, 0x01, 0x72, 0x43, 0x4e, 0x7a, 0x1f, 0xed, 0x39, 0x09, 0xd2, 0x80, 0xf9, 0x6a, 0x7a, 0x41, 0x6f, 0x0b, 0x5f, 0x97, 0x35, 0xe1, 0xa0, 0x35, 0xaf, 0xba, 0x36, 0x45, 0xad, 0xf4, 0x49, 0xbe, 0x74, 0x42, 0x02, 0xea, 0x3b, 0xf1, 0xd5, 0x7d, 0x67, 0x7e, 0x66, 0x98, 0xb8, 0x76, 0x00, 0xbd, 0x9a, 0xd7, 0x17, 0x2d, 0xd7, 0x8e, 0xe6, 0xed, 0xe6, 0xf0, 0x3e, 0x67, 0x94, 0xde, 0xf3, 0x39, 0x78, 0xb6, 0x0e, 0x60, 0xe2, 0xd6, 0xeb, 0xfe, 0x98, 0x84, 0xcc, 0xb1, 0xa2, 0xa2, 0x8f, 0x28, 0x10, 0x60, 0xd8, 0xbd, 0x6d, 0x00, 0x8d, 0xb8, 0x9d, 0x11, 0x2f, 0x73, 0xf9, 0x79, 0xbf, 0xf2, 0x18, 0xa2, 0x27, 0x5d, 0x0c, 0xef, 0x29, 0x40, 0x3f, 0x0d, 0x45, 0xb4, 0x96, 0xb9, 0xc6, 0x44, 0xa3, 0x4b, 0x09, 0x9b, 0xa9, 0x93, 0x16, 0xf0, 0x3d, 0xa1, 0x24, 0x13, 0xcd, 0x39, 0x13, 0xe6, 0x4b, 0x48, 0x1b, 0xc7, 0x3a, 0x4a, 0xcf, 0x9e, 0xbd, 0x5c, 0x18, 0x1f, 0x36, 0xbc, 0x83, 0xbe, 0x79, 0xb6, 0x96, 0xe0, 0x55, 0x76, 0x9f, 0x29, 0x7b, 0xac, 0x6e, 0x5a, 0x7b, 0xb2, 0x83, 0x5a, 0xbe, 0xe4, 0x41, 0xe1, 0xfb, 0x1c, 0x3f, 0x32, 0x42, 0x79, 0x57, 0x93, 0xdd, 0x8a, 0xd6, 0x65, 0x8d, 0x60, 0x46, 0xda, 0xc8, 0x84, 0x60, 0xdb, 0x78, 0x69, 0xef, 0xe5, 0xa6, 0x7c, 0xa5, 0x22, 0xef, 0x23, 0x51, 0xd6, 0xe6, 0x04, 0x66, 0xf4, 0x62, 0xd8, 0x0b, 0x59, 0x94, 0x16, 0xd8, 0xf6, 0xce, 0x9f, 0xa1, 0x0e, 0x81, 0x8a, 0x54, 0xab, 0x78, 0x1e, 0xff, 0xb9, 0x59, 0x25, 0x7a, 0x43, 0x52, 0xa3, 0xc2, 0x74, 0xb8, 0xa0, 0x29, 0x27, 0x98, 0xf3, 0x18, 0xe2, 0x6e, 0x7b, 0x8b, 0x48, 0x73, 0xc5, 0x3b, 0x1c, 0xb1, 0x16, 0x40, 0x68, 0x9a, 0xbe, 0x16, 0x48, 0xaa, 0x07, 0xef, 0x40, 0xc1, 0xec, 0xdb, 0x4e, 0x94, 0x8c, 0x54, 0x41, 0xa2, 0x66, 0x33, 0x53, 0xf9, 0x6e, 0x4d, 0xcc, 0x62, 0x24, 0x33, 0x02, 0xeb, 0x91, 0xbe, 0xeb, 0x95, 0x34, 0xa7, 0x0e, 0x88, 0xfb, 0x1f, 0x6b, 0x15, 0xe0, 0x17, 0xc0, 0x6d, 0x6b, 0x4f, 0x78, 0x5a, 0x7c, 0x36, 0xae, 0xe6, 0x7e, 0xff, 0x00, 0x7c, 0x27, 0x94, 0xc6, 0xd4, 0xa1, 0x22, 0x2a, 0x83, 0x64, 0x34, 0x6e, 0x71, 0x0f, 0x4e, 0x98, 0xb7, 0xc9, 0x78, 0xfb, 0x6d, 0xaa, 0x35, 0x56, 0x16, 0x20, 0x1b, 0x9f, 0x66, 0xec, 0x80, 0x3e, 0xc5, 0x5a, 0x61, 0x69, 0x7c, 0x0c, 0x70, 0x03, 0x23, 0xe8, 0xa5, 0x97, 0x22, 0x62, 0xe6, 0x47, 0xb3, 0x61, 0xe3, 0x1e, 0xd7, 0x77, 0xc8, 0xea, 0x12, 0xf6, 0x0c, 0xf5, 0xca, 0xde, 0xc3, 0x33, 0x97, 0x93, 0xcc, 0x7b, 0xca, 0x75, 0xda, 0x71, 0x10, 0x3e, 0xf5, 0xd3, 0xa7, 0x61, 0xd9, 0xa0, 0x3e, 0xe3, 0x2f, 0x78, 0x5f, 0xef, 0xcb, 0x89, 0xa8, 0x8d, 0xb6, 0xb6, 0x9c, 0xe9, 0x55, 0x45, 0x12, 0xf9, 0x46, 0x55, 0x94, 0xd3, 0x96, 0x0f, 0x12, 0xb6, 0xdc, 0x25, 0x86, 0xbe, 0xca, 0x13, 0x04, 0xbe, 0xda, 0x39, 0xab, 0x9a, 0x1e, 0xa3, 0x0d, 0x13, 0xa6, 0x19, 0x7b, 0xe5, 0x17, 0xcd, 0x63, 0x4a, 0x6a, 0xab, 0xd7, 0xde, 0xbc, 0x0f, 0x70, 0xa6, 0xb2, 0x60, 0xae, 0xcd, 0xdb, 0x52, 0x59, 0x17, 0x28, 0x15, 0x5c, 0xa9, 0xec, 0x89, 0x29, 0xb4, 0x39, 0x7d, 0x71, 0x2b, 0x47, 0x75, 0x29, 0x43, 0x66, 0xa8, 0x8f, 0x15, 0xb9, 0xb5, 0xc5, 0x69, 0x03, 0xc1, 0x12, 0xf5, 0x3c, 0xeb, 0x22, 0x76, 0x2a, 0x36, 0xc1, 0x5b, 0x5b, 0x4a, 0x21, 0xa7, 0xdd, 0xb5, 0xed, 0x53, 0x93, 0x9f, 0xd1, 0xac, 0xb2, 0x51, 0x08, 0x68, 0x03, 0x73, 0xd3, 0x36, 0x40, 0xca, 0xe1, 0x7b, 0xb8, 0x0b, 0xca, 0xc0, 0x3d, 0xa6, 0x12, 0xf2, 0x55, 0x30, 0xe5, 0xba, 0x2c, 0x26, 0x43, 0xbf, 0x2b, 0x0a, 0x94, 0xcc, 0xb1, 0xd9, 0x3a, 0x2a, 0xa0, 0xb4, 0xeb, 0xaf, 0x7e, 0xb5, 0xad, 0xb9, 0x0b, 0xda, 0xd3, 0xa1, 0x91, 0x25, 0x11, 0x2e, 0x52, 0xc8, 0xf1, 0xd1, 0xba, 0x1a, 0xef, 0xd5, 0x5d, 0x7b, 0x02, 0x1b, 0x8d, 0x34, 0x0f, 0x48, 0x5b, 0x66, 0x68, 0xd2, 0xab, 0xda, 0x16, 0x76, 0x63, 0x13, 0x17, 0x83, 0x60, 0xe4, 0xb0, 0x8f, 0x19, 0x33, 0x2d, 0x38, 0x6a, 0x28, 0x85, 0x5c, 0xbc, 0xea, 0xb1, 0xfb, 0xf8, 0xf2, 0xc6, 0x59, 0x21, 0xb6, 0xf2, 0xc7, 0x7a, 0x7c, 0x9b, 0xd4, 0x58, 0x82, 0xed, 0xac, 0x6a, 0xc4, 0x71, 0xfc, 0x2e, 0xeb, 0x9c, 0xe4, 0xcc, 0x8b, 0x66, 0x5d, 0xd5, 0x1c, 0x12, 0x16, 0x5d, 0x98, 0xa6, 0x2d, 0x6b, 0x75, 0x4e, 0x00, 0x59, 0x36, 0xfe, 0x6c, 0x87, 0x48, 0x47, 0xa9, 0xec, 0x8f, 0xc8, 0x88, 0x66, 0x00, 0xb3, 0x5a, 0xe7, 0xe1, 0xc5, 0x13, 0x3b, 0x57, 0xe7, 0x81, 0xa1, 0xfb, 0x6a, 0x7e, 0xc4, 0xf4, 0xf4, 0xc7, 0xbe, 0x90, 0x3c, 0x6a, 0xcc, 0xed, 0xe9, 0x35, 0xf2, 0x19, 0xc8, 0x6e, 0xa2, 0xbe, 0xae, 0xa0, 0x46, 0x3d, 0x76, 0xcd, 0xcf, 0xae, 0xd6, 0x8c, 0x0c, 0xdf, 0x05, 0x1a, 0x2b, 0xde, 0xbf, 0xc4, 0x0d, 0xe6, 0x74, 0x88, 0xad, 0xc7, 0x39, 0x8c, 0x33, 0x9b, 0x31, 0xaa, 0xc4, 0xc0, 0xab, 0x8d, 0x4b, 0xad, 0x88, 0x87, 0x4a, 0x1b, 0x59, 0xaa, 0xac, 0xfe, 0x11, 0x19, 0xa4, 0xd5, 0x40, 0xb1, 0x18, 0x7c, 0x05, 0xa8, 0x7e, 0x89, 0xda, 0x57, 0x0e, 0xfd, 0x70, 0x59, 0xa5, 0xa0, 0x8c, 0x85, 0x3f, 0xaf, 0x05, 0x8a, 0xe5, 0xd3, 0x98, 0x0a, 0x30, 0x8b, 0x47, 0x44, 0x32, 0x13, 0x97, 0x61, 0xef, 0x11, 0xca, 0x96, 0x8f, 0x78, 0x9c, 0x64, 0x4f, 0x56, 0xb1, 0x5c, 0xdc, 0x3f, 0x24, 0x6c, 0xf2, 0xb6, 0x5b, 0x8b, 0x59, 0x8b, 0xab, 0xa6, 0xb8, 0xfc, 0x41, 0xf5, 0xc1, 0xa4, 0xcc, 0x98, 0xdf, 0x72, 0xe7, 0x8a, 0x3a, 0x9a, 0xe6, 0xd7, 0xbf, 0xf7, 0xa0, 0xc7, 0xf9, 0xe1, 0x51, 0xc0, 0xce, 0x7f, 0x8f, 0x07, 0x36, 0xfa, 0x9d, 0xe6, 0x9f, 0xe6, 0x44, 0xe1, 0xf8, 0xe7, 0x0b, 0xe9, 0xde, 0xe9, 0x6a, 0x7f, 0x08, 0x9e, 0xe9, 0x08, 0xfa, 0x28, 0x87, 0xc5, 0xc7, 0x6d, 0x08, 0xd2, 0xf2, 0x59, 0x20, 0x81, 0xdf, 0xe6, 0x0c, 0x85, 0xb5, 0x10, 0xcd, 0x39, 0x24, 0x0d, 0x67, 0x5c, 0x49, 0x2d, 0x84, 0xc8, 0x2c, 0x81, 0x11, 0xfa, 0x49, 0x27, 0x25, 0xd4, 0x40, 0x7d, 0xee, 0xb1, 0x83, 0x72, 0xd6, 0x53, 0xf2, 0xb6, 0x3c, 0x08, 0x58, 0xf8, 0xe3, 0x5a, 0x9e, 0xa7, 0x9d, 0x8d, 0x26, 0xf2, 0xfd, 0xe0, 0x4e, 0xb8, 0x4a, 0x49, 0x99, 0x97, 0x38, 0xe8, 0xc8, 0xc6, 0x6f, 0x54, 0xe7, 0xa7, 0x64, 0xdd, 0x34, 0x2b, 0x71, 0x60, 0xc9, 0x80, 0xbb, 0x42, 0xbe, 0xfb, 0x57, 0x4f, 0x6c, 0xc4, 0x4d, 0x9b, 0x53, 0x07, 0x58, 0x5b, 0xec, 0x02, 0x00, 0x83, 0xec, 0x5b, 0x6d, 0x9f, 0x03, 0xe2, 0x75, 0x60, 0x18, 0x10, 0xa2, 0xa1, 0xc0, 0xad, 0xe2, 0xad, 0xd1, 0x27, 0x7f, 0x45, 0xdf, 0xa6, 0x9c, 0xb7, 0x9b, 0x62, 0xc7, 0x9c, 0x78, 0xc0, 0x5a, 0x63, 0x7b, 0xda, 0x57, 0x89, 0x32, 0x4c, 0x10, 0x25, 0x38, 0xa8, 0x0a, 0x45, 0x37, 0x3e, 0x10, 0x73, 0xe5, 0x12, 0x65, 0x12, 0x21, 0x9f, 0x1a, 0x74, 0x03, 0x8c, 0xf7, 0x6e, 0xdd, 0x14, 0x32, 0x58, 0xea, 0x80, 0xe0, 0xa7, 0xa1, 0xb5, 0xa8, 0x16, 0xab, 0x62, 0x6f, 0x7d, 0xf1, 0xf2, 0x8f, 0xb0, 0x9d, 0xd6, 0x32, 0x31, 0x15, 0x2e, 0xfe, 0xec, 0xcd, 0xb8, 0x5c, 0xe6, 0x9e, 0x64, 0x04, 0x29, 0xe8, 0xe2, 0xcb, 0x48, 0x11, 0x02, 0x68, 0x56, 0xce, 0xad, 0xf6, 0x5d, 0xd3, 0x90, 0x65, 0x32, 0x75, 0x4d, 0x85, 0xce, 0x15, 0x47, 0x77, 0x90, 0x00, 0xbc, 0x08, 0x38, 0x30, 0xb3, 0x71, 0x49, 0x61, 0xcd, 0x1f, 0xea, 0x12, 0xbe, 0x87, 0x77, 0x32, 0x4c, 0x31, 0xe6, 0xc8, 0x7e, 0x22, 0x37, 0x9b, 0x44, 0x2f, 0xb9, 0x73, 0x46, 0x7a, 0x40, 0x24, 0x07, 0x54, 0x0c, 0xc2, 0x31, 0x9d, 0xa4, 0x2b, 0xc5, 0x7f, 0x4f, 0x6d, 0x23, 0x8a, 0xf9, 0x3e, 0xc0, 0x71, 0xab, 0x32, 0xa9, 0xb9, 0x99, 0x40, 0xbd, 0x06, 0x7b, 0xa4, 0xf6, 0x92, 0xec, 0xab, 0x51, 0xec, 0x46, 0x79, 0x6e, 0x40, 0xbf, 0x69, 0x33, 0xad, 0xe6, 0x37, 0x2b, 0x2d, 0xf9, 0xf5, 0x8b, 0x03, 0xeb, 0x72, 0x80, 0x15, 0xb2, 0x0e, 0xb7, 0x60, 0x51, 0xad, 0xdf, 0x6b, 0x5f, 0xf2, 0x2f, 0xe9, 0x48, 0x86, 0x17, 0xe9, 0x21, 0x9d, 0x00, 0x9c, 0x72, 0x14, 0xa3, 0x6c, 0x08, 0xdb, 0x78, 0x3d, 0x3c, 0xce, 0x60, 0x55, 0x5c, 0x12, 0x17, 0x09, 0x7e, 0xaf, 0x0e, 0x5c, 0x2e, 0xc0, 0x5d, 0xf7, 0xec, 0xb1, 0xa7, 0x84, 0xa7, 0x7c, 0x78, 0x52, 0x9c, 0x75, 0xf6, 0x8d, 0x3b, 0x65, 0x18, 0xc5, 0x06, 0x15, 0x95, 0xdb, 0x35, 0x36, 0x16, 0xc4, 0xd3, 0x78, 0xe9, 0x6c, 0x2c, 0x9a, 0x01, 0x90, 0x80, 0x8c, 0xd4, 0x76, 0xf7, 0x19, 0x84, 0x08, 0xfa, 0x62, 0x6d, 0x97, 0xce, 0x60, 0xd9, 0xdf, 0x2b, 0xb5, 0x72, 0xb0, 0x85, 0x69, 0x76, 0x50, 0x08, 0x84, 0xd2, 0xf2, 0xf1, 0xe0, 0x5e, 0x42, 0x97, 0xac, 0x03, 0x76, 0x9a, 0x1a, 0x88, 0xaa, 0x3a, 0x51, 0xb8, 0x49, 0xa0, 0x0c, 0xf4, 0xd2, 0xca, 0x43, 0x7c, 0xa2, 0xf5, 0xb2, 0x2d, 0x78, 0x19, 0x31, 0x7c, 0xe7, 0x4f, 0xdd, 0x67, 0x57, 0x7c, 0x8e, 0x4b, 0x00, 0x54, 0x17, 0xa0, 0xe5, 0xfc, 0x39, 0x9c, 0x37, 0x91, 0x4e, 0xae, 0xe3, 0xb3, 0x78, 0xa4, 0xcd, 0xc6, 0x7d, 0xe5, 0xa9, 0x63, 0x5e, 0xfd, 0x77, 0x78, 0x98, 0x81, 0x53, 0x5e, 0x25, 0x53, 0x79, 0x9b, 0x21, 0x74, 0xc7, 0x66, 0x63, 0x2c, 0x7c, 0x1e, 0x75, 0x3e, 0x86, 0x98, 0x6b, 0xf1, 0xaa, 0xe6, 0x6f, 0xea, 0x62, 0x84, 0xa4, 0xc1, 0x6e, 0x42, 0x93, 0x06, 0x18, 0x4c, 0xdf, 0x9a, 0xd8, 0x8e, 0x03, 0x08, 0xdb }; +constexpr AccessUnit M2V_SEQUENCE_END_SPLIT_EXPECTED_AU = { 0x15f90, 0x159b2, true, 0, {}, { 0x6f, 0x00, 0x25, 0xed, 0x88, 0xfb, 0x17, 0x95, 0x83, 0x1c, 0xb6, 0x9b, 0xbc, 0x41, 0xd8, 0x1f, 0xf1, 0xb0, 0xd5, 0xa3 } }; + + +constexpr std::array AVC_MULTIPLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x01, 0x09, 0x9a, 0xc4, 0xa0, 0x2d, 0x49, 0x55, 0xcd, 0xbe, 0x15, 0xf7, 0x93, 0xa4, 0xa7, 0x61, 0x41, 0x82, 0xa8, 0x30, 0xe6, 0xd2, 0x14, 0xe8, 0xa4, 0xa4, 0xdd, 0xf1, 0x6b, 0x6a, 0x44, 0x31, 0xfe, 0x64, 0x2c, 0x95, 0x3a, 0x7a, 0x71, 0x21, 0xc7, 0x11, 0x98, 0x91, 0x63, 0x6d, 0x79, 0xec, 0x89, 0x63, 0x3d, 0xe8, 0x95, 0x7e, 0x4e, 0xfc, 0xe3, 0x7e, 0x8b, 0xd8, 0xe7, 0xe8, 0xaa, 0x15, 0x68, 0x28, 0x35, 0x8e, 0x0a, 0x1c, 0xc3, 0x99, 0xeb, 0x1a, 0x76, 0xae, 0x86, 0x8a, 0x33, 0xb3, 0x11, 0xd7, 0x96, 0x2b, 0x32, 0xaf, 0x6b, 0x07, 0xb0, 0x9d, 0x53, 0xb0, 0x5a, 0x87, 0x2f, 0xe8, 0xa0, 0x30, 0xc2, 0x06, 0x69, 0xfa, 0x07, 0x49, 0xf5, 0x6f, 0xca, 0x0a, 0x3d, 0xcc, 0x72, 0x79, 0x9f, 0x66, 0x1f, 0x8f, 0x75, 0x24, 0x79, 0x2a, 0x43, 0x38, 0x07, 0xd4, 0x6e, 0xcc, 0x2b, 0xf1, 0x81, 0xff, 0x73, 0x99, 0x1e, 0x1a, 0x74, 0xfe, 0xc7, 0xad, 0x01, 0x10, 0x40, 0xb7, 0x48, 0x48, 0x1f, 0x00, 0xd3, 0x02, 0xec, 0x3c, 0x60, 0x1e, 0xf0, 0x4f, 0xa3, 0x97, 0xfb, 0x45, 0x49, 0xc8, 0x4a, 0x6a, 0x9b, 0x6f, 0xbc, 0x82, 0xda, 0x6b, 0xfb, 0x40, 0x5b, 0x04, 0x02, 0x6e, 0x19, 0x1d, 0xc7, 0x90, 0x68, 0xa9, 0xde, 0xae, 0x17, 0xf8, 0xed, 0x64, 0x2c, 0xef, 0x7e, 0x4c, 0x83, 0x1f, 0x28, 0xe9, 0x60, 0x09, 0x80, 0xa4, 0x22, 0x38, 0xd6, 0x0c, 0x2b, 0xb0, 0xc8, 0x46, 0x51, 0x0c, 0x56, 0xe9, 0xc3, 0x94, 0x66, 0x35, 0xc0, 0x13, 0x26, 0xda, 0x02, 0x45, 0x07, 0x89, 0x11, 0xcf, 0xdc, 0x4d, 0x31, 0x95, 0x29, 0xbf, 0x31, 0xe1, 0xfc, 0x44, 0x90, 0x1d, 0x56, 0xbe, 0x33, 0x75, 0x9a, 0xc8, 0xc9, 0xe4, 0xb6, 0x74, 0x89, 0x25, 0xd8, 0x55, 0xa2, 0xa4, 0x66, 0xd2, 0x09, 0xbd, 0x88, 0xfa, 0x89, 0x8a, 0x38, 0x8c, 0x20, 0x8c, 0xd2, 0x32, 0xf5, 0x99, 0x26, 0xb9, 0x90, 0x12, 0xe2, 0x40, 0xd9, 0xce, 0xda, 0x39, 0x8d, 0xb7, 0x14, 0xe3, 0x0b, 0x7f, 0xf7, 0x3f, 0xf7, 0x5c, 0x9a, 0xd2, 0xd6, 0x79, 0xe2, 0xc7, 0x5a, 0xd9, 0x8b, 0xd7, 0x37, 0x63, 0x27, 0x5e, 0x8f, 0x41, 0xc5, 0xff, 0x24, 0x28, 0xcc, 0x27, 0x88, 0xc2, 0x87, 0xd0, 0xf2, 0xd6, 0x21, 0x0e, 0x91, 0x3a, 0x40, 0xc5, 0x3c, 0x44, 0x63, 0x6c, 0xa1, 0x20, 0x13, 0x86, 0x86, 0x03, 0x72, 0x91, 0x37, 0x08, 0x35, 0xfb, 0xb8, 0xf0, 0xdb, 0x23, 0x69, 0x33, 0xe0, 0xde, 0x3d, 0x5b, 0x20, 0xc2, 0xf6, 0x35, 0x0f, 0x32, 0xc9, 0x98, 0xdc, 0x7e, 0xf8, 0xd8, 0x7a, 0xd1, 0x54, 0x34, 0xa2, 0x10, 0x0c, 0xf6, 0x2d, 0xab, 0x24, 0x95, 0x50, 0xbf, 0xd9, 0xb4, 0x42, 0xc3, 0x55, 0xb0, 0x97, 0xec, 0x74, 0x72, 0x6e, 0x61, 0xf6, 0x03, 0x97, 0x47, 0x51, 0xf1, 0x64, 0xb2, 0x90, 0x05, 0xa4, 0x6f, 0xda, 0xeb, 0xf8, 0xa2, 0x77, 0x02, 0x5b, 0xb9, 0xbc, 0xe7, 0x5b, 0xfd, 0x34, 0xb5, 0xf4, 0x8c, 0xd1, 0xe0, 0xa0, 0xd6, 0x06, 0xa8, 0x44, 0x85, 0x8c, 0xfb, 0x9b, 0x73, 0x87, 0xfa, 0x25, 0xeb, 0x17, 0x0c, 0xf3, 0x41, 0xe4, 0x2c, 0xe8, 0xfb, 0x7d, 0x6b, 0x1a, 0x16, 0xe4, 0x5c, 0x20, 0xf6, 0x80, 0x2d, 0x4f, 0x19, 0x61, 0x98, 0x48, 0x22, 0xc2, 0x32, 0xfc, 0xb8, 0x96, 0x92, 0xa1, 0xcd, 0x1d, 0x71, 0x2e, 0xb5, 0x2d, 0xe9, 0xe4, 0xc3, 0x0b, 0x5b, 0xe9, 0xee, 0x85, 0xb2, 0xaf, 0x8d, 0x25, 0xe0, 0x43, 0xb0, 0x1d, 0x82, 0x78, 0xa4, 0xd0, 0x66, 0x61, 0x96, 0x2f, 0xd6, 0x3d, 0x10, 0xe6, 0xda, 0xab, 0xcf, 0xa5, 0x59, 0x95, 0x07, 0x88, 0x7d, 0xae, 0xd7, 0x1d, 0x64, 0x76, 0xdc, 0x60, 0x8e, 0x38, 0x4f, 0xe6, 0x6d, 0xb5, 0xc2, 0x72, 0xd1, 0xe1, 0xa0, 0x84, 0x1f, 0x98, 0x9b, 0x11, 0x31, 0xd3, 0x93, 0x0f, 0x23, 0xff, 0xa8, 0xb0, 0x2b, 0x90, 0xe0, 0x60, 0x40, 0xeb, 0x0c, 0x98, 0xd5, 0x98, 0xe6, 0x3a, 0x42, 0x95, 0xb3, 0x76, 0x90, 0x4c, 0x4a, 0x8d, 0x46, 0x42, 0x0e, 0x39, 0x4c, 0x54, 0x52, 0xa6, 0x2b, 0x87, 0x72, 0x6f, 0xee, 0x41, 0xaf, 0xe0, 0xc3, 0xfe, 0x0d, 0xd1, 0xa2, 0xd4, 0xc6, 0x9d, 0xa9, 0x89, 0x78, 0xcd, 0x8a, 0x33, 0x50, 0x89, 0xe7, 0x4c, 0x85, 0x7a, 0xc2, 0x91, 0x0b, 0x88, 0xd2, 0xe0, 0x0d, 0xfe, 0x21, 0x82, 0x81, 0xc7, 0x4c, 0xfc, 0x00, 0xcb, 0x86, 0x03, 0x38, 0xa5, 0xa4, 0xcf, 0xb0, 0xbd, 0x77, 0x38, 0x4a, 0xd5, 0x92, 0x45, 0x07, 0x96, 0x23, 0xe7, 0x3e, 0x43, 0x50, 0x48, 0xe6, 0x1d, 0x3f, 0x79, 0x67, 0x61, 0xe1, 0xae, 0x5f, 0x57, 0xe4, 0xbd, 0x79, 0x67, 0x0d, 0x11, 0x81, 0xb9, 0xa5, 0xc5, 0xb8, 0x11, 0x0c, 0x02, 0x78, 0x0f, 0x93, 0x51, 0xd6, 0x6a, 0x07, 0xd8, 0xcd, 0x50, 0x60, 0xf6, 0x59, 0x97, 0x0f, 0x25, 0xb6, 0xc7, 0x1f, 0x94, 0xbb, 0xd4, 0xe0, 0x05, 0x44, 0x88, 0x03, 0x44, 0xa4, 0x93, 0x07, 0x80, 0x23, 0x7c, 0x3f, 0xf7, 0xf9, 0x7a, 0x60, 0x39, 0xf6, 0x37, 0xbc, 0x67, 0x56, 0xa1, 0x58, 0x79, 0xd8, 0xb6, 0x05, 0x53, 0xb4, 0xa1, 0x77, 0x57, 0x94, 0xb0, 0xfd, 0xbc, 0xaf, 0x25, 0x52, 0x2c, 0x73, 0xcb, 0x4a, 0x87, 0xcd, 0xda, 0xb4, 0x51, 0x57, 0xaa, 0x1e, 0xc9, 0x2d, 0x6c, 0x51, 0x9d, 0xc6, 0x67, 0x7a, 0x82, 0x76, 0xf2, 0x8b, 0xef, 0xc4, 0xd9, 0xad, 0x96, 0xc4, 0x1c, 0x78, 0x0a, 0x98, 0xb0, 0x16, 0x43, 0x1d, 0xea, 0x3a, 0xe6, 0x77, 0xe8, 0xc7, 0x86, 0x28, 0x1e, 0x24, 0xe3, 0xd4, 0xa8, 0x4a, 0x6f, 0xe1, 0xde, 0x3b, 0x38, 0xd6, 0xa4, 0x3b, 0x4f, 0xaa, 0x6e, 0xe6, 0x80, 0x00, 0xa9, 0xbd, 0xd1, 0xca, 0x35, 0x41, 0x97, 0xca, 0x94, 0x28, 0xd1, 0x0f, 0x52, 0x62, 0x29, 0x23, 0xa3, 0xfd, 0x35, 0x34, 0x57, 0xd3, 0x22, 0x69, 0xf1, 0x17, 0xf5, 0x29, 0x0d, 0xf1, 0xa7, 0xee, 0xe2, 0x2d, 0xe5, 0x19, 0x8c, 0x6b, 0xe7, 0x93, 0x3e, 0xb4, 0xb7, 0xee, 0x65, 0x70, 0x45, 0x4c, 0xd6, 0xe8, 0xe9, 0x9f, 0x33, 0x9a, 0xf8, 0xf9, 0xbd, 0x21, 0xe6, 0x3c, 0x93, 0x5f, 0xa4, 0x5e, 0x6d, 0xab, 0x26, 0xf3, 0xab, 0x07, 0x01, 0x81, 0xa7, 0xea, 0x11, 0xb1, 0x58, 0x5e, 0x49, 0xc2, 0xab, 0x1b, 0xaf, 0xba, 0xe5, 0xae, 0xb4, 0x2d, 0x98, 0x00, 0x40, 0x89, 0x76, 0x18, 0x03, 0x6f, 0x43, 0xe9, 0x48, 0x39, 0x91, 0xa3, 0x6d, 0x61, 0xe7, 0xea, 0x6a, 0xd9, 0x3d, 0x43, 0xf1, 0xd5, 0xcc, 0xb1, 0x2e, 0xe6, 0xbb, 0x10, 0xd9, 0x6a, 0xd2, 0x34, 0x4e, 0xd1, 0x83, 0xef, 0x8a, 0x26, 0x15, 0x04, 0xbf, 0x74, 0xce, 0xbb, 0x25, 0x80, 0xea, 0xc1, 0xc7, 0x25, 0xee, 0x99, 0x9a, 0x25, 0x73, 0x2c, 0xe7, 0x99, 0x33, 0xad, 0x89, 0x69, 0x9e, 0x58, 0xf5, 0x0f, 0x26, 0xba, 0x3e, 0x33, 0x06, 0x0b, 0xb7, 0x8d, 0xbd, 0xc1, 0x38, 0x04, 0x66, 0xa0, 0x94, 0xf6, 0xe2, 0x87, 0x56, 0xde, 0x3f, 0x60, 0x48, 0x1a, 0xbb, 0xc2, 0x05, 0x20, 0xc5, 0xfb, 0x27, 0xcf, 0xc8, 0x2e, 0xb9, 0x6a, 0x8b, 0x37, 0x6f, 0x1e, 0x0f, 0xa5, 0xd1, 0x34, 0xaf, 0x3b, 0xea, 0xfe, 0xc6, 0xaa, 0x76, 0x13, 0x16, 0x1b, 0xb3, 0x33, 0xd2, 0x4b, 0x1e, 0x4d, 0x9c, 0xe6, 0xa7, 0x76, 0x49, 0xe7, 0x6d, 0xf1, 0xb5, 0x9f, 0x6f, 0xbb, 0xaf, 0x49, 0x38, 0xb9, 0xdd, 0x4e, 0xb3, 0x05, 0x00, 0x7d, 0x5a, 0xb8, 0xe0, 0x5c, 0xb7, 0x61, 0xf0, 0x72, 0x7c, 0x71, 0xf8, 0x7d, 0x46, 0xf0, 0x15, 0x30, 0xd7, 0x0a, 0x19, 0x71, 0x1c, 0x68, 0x5e, 0xa9, 0xcf, 0x16, 0xbe, 0x94, 0xa5, 0x95, 0xc9, 0xd7, 0x78, 0x95, 0x29, 0x43, 0x0d, 0xb7, 0xa2, 0xce, 0x7c, 0xa3, 0x1a, 0xc6, 0x2d, 0x55, 0x6f, 0x4c, 0x2f, 0xee, 0x48, 0x20, 0x32, 0x60, 0x60, 0xa7, 0xe3, 0x1c, 0xdf, 0xad, 0x53, 0xb1, 0x88, 0x0d, 0x55, 0x25, 0xd5, 0xf2, 0xb6, 0x80, 0x37, 0xe5, 0x3a, 0x64, 0xfd, 0x02, 0xae, 0xf3, 0x57, 0xfa, 0x6c, 0xbd, 0x69, 0xa3, 0x6e, 0x32, 0x6e, 0xc2, 0x0f, 0x6e, 0x9b, 0xb9, 0x3a, 0xa6, 0x39, 0x27, 0xb4, 0xa0, 0x0f, 0xe0, 0x51, 0xdf, 0xf5, 0x8d, 0xaf, 0x35, 0xc6, 0x55, 0x86, 0x54, 0xcc, 0x28, 0xa0, 0xf8, 0xd2, 0x32, 0x03, 0x6c, 0x09, 0xd3, 0xfb, 0x3b, 0x74, 0x69, 0x44, 0x71, 0x49, 0xf8, 0x87, 0xb6, 0xd9, 0xdc, 0xfc, 0xc4, 0x7f, 0x45, 0xdc, 0x73, 0x99, 0x79, 0xf0, 0x95, 0x78, 0xce, 0x4e, 0x99, 0xc8, 0x35, 0xca, 0xea, 0xba, 0x92, 0xab, 0xe3, 0xba, 0xed, 0x4d, 0xec, 0x32, 0x99, 0x65, 0x48, 0x12, 0x24, 0x3a, 0xaa, 0xbe, 0x54, 0xf0, 0x63, 0xbb, 0x3c, 0x06, 0x38, 0x35, 0x2c, 0xbc, 0x05, 0xe6, 0x1c, 0xc6, 0xe1, 0x9f, 0x77, 0x2c, 0xfa, 0xd5, 0x8d, 0xec, 0x7e, 0x19, 0x8c, 0x7c, 0x29, 0x26, 0xb9, 0x61, 0xba, 0x7c, 0xfa, 0xc4, 0x10, 0x8f, 0x48, 0xf0, 0xed, 0x11, 0xbd, 0xd0, 0xc8, 0x46, 0x9d, 0x06, 0x67, 0xbe, 0xd5, 0x3e, 0x7c, 0xbe, 0x37, 0x7c, 0xf5, 0x4e, 0x77, 0x63, 0xb4, 0x12, 0xb9, 0x37, 0xe3, 0x8d, 0xdc, 0x2f, 0xa4, 0xb8, 0x92, 0xec, 0x06, 0xd7, 0x56, 0x77, 0x02, 0xa2, 0x55, 0xbc, 0x1b, 0x9a, 0x12, 0x05, 0x89, 0x40, 0x59, 0x27, 0x80, 0x74, 0x01, 0xb9, 0xa2, 0x12, 0xb8, 0xcb, 0xd5, 0xd5, 0xe0, 0xd6, 0x78, 0xc1, 0x23, 0x24, 0x23, 0x77, 0x84, 0x60, 0xb3, 0xe5, 0x9c, 0x35, 0x3f, 0xb1, 0x7b, 0x38, 0xe2, 0x35, 0x68, 0xbc, 0x14, 0x8b, 0x89, 0xc2, 0xae, 0x41, 0x48, 0x2e, 0x3a, 0xad, 0x28, 0x3c, 0xc2, 0x7b, 0x8c, 0x20, 0x63, 0xfe, 0x74, 0x6d, 0xf9, 0xd8, 0x57, 0xe0, 0xdb, 0x6e, 0xba, 0xd0, 0x06, 0x4a, 0x50, 0x2f, 0x05, 0x5f, 0x0f, 0x64, 0x7c, 0x6a, 0xe2, 0x10, 0x1f, 0xd7, 0x18, 0xe0, 0x40, 0xcd, 0x06, 0x2c, 0x7c, 0xe9, 0x1a, 0x7c, 0x2c, 0x93, 0x9a, 0x6a, 0x9d, 0x92, 0x26, 0xb6, 0x7e, 0xb6, 0xb7, 0xdf, 0x51, 0xc4, 0x48, 0x0d, 0x37, 0x15, 0xba, 0x02, 0x58, 0xda, 0x07, 0x64, 0x4b, 0x59, 0xf7, 0x0a, 0x1f, 0x2c, 0xe3, 0xce, 0x74, 0x3f, 0x84, 0xb8, 0xd1, 0x96, 0x4a, 0x9f, 0x93, 0x3a, 0x1d, 0x7b, 0x0b, 0x53, 0x5b, 0xc8, 0x6a, 0xc0, 0x60, 0x37, 0xa6, 0xd5, 0x5f, 0x96, 0x12, 0x08, 0xd1, 0x41, 0x3b, 0xc0, 0xde, 0x51, 0x4a, 0x0a, 0x4d, 0xdc, 0xcf, 0x3f, 0xe5, 0x58, 0x76, 0x99, 0x2d, 0x41, 0x16, 0xbf, 0xc7, 0x08, 0x11, 0x80, 0x88, 0x12, 0xd7, 0xba, 0x85, 0x2b, 0xe7, 0x55, 0x23, 0x74, 0x69, 0x62, 0xfe, 0xb4, 0x1b, 0x47, 0xe3, 0xc3, 0xac, 0x74, 0xa6, 0xcd, 0x96, 0xa9, 0x84, 0x60, 0xb6, 0x4d, 0xdc, 0xcb, 0xa1, 0x18, 0xd2, 0xce, 0xb2, 0xa6, 0x37, 0x49, 0x1e, 0x99, 0xbd, 0xdc, 0xcf, 0x69, 0xd0, 0x3f, 0x90, 0xee, 0x52, 0x33, 0x45, 0xd3, 0x9a, 0x3f, 0x64, 0x0d, 0x00, 0x0b, 0xff, 0x5d, 0xbf, 0x1b, 0x43, 0xd3, 0xf5, 0x53, 0xe8, 0x37, 0x63, 0xba, 0xad, 0x01, 0xd6, 0x01, 0x86, 0x48, 0x2d, 0xee, 0x07, 0x0d, 0x0b, 0x85, 0xb2, 0xf9, 0x7e, 0x6c, 0xb9, 0x79, 0x77, 0x1d, 0xff, 0x68, 0xfa, 0xb6, 0xad, 0x0a, 0x0f, 0x43, 0xf6, 0x65, 0x6f, 0x7d, 0x79, 0x5d, 0x70, 0x55, 0x29, 0xc7, 0x41, 0x67, 0xfb, 0x3e, 0xc5, 0xcd, 0x0f, 0x7d, 0x69, 0x5b, 0x64, 0x47, 0x5a, 0x2e, 0x6a, 0xfb, 0xe9, 0x8e, 0x66, 0x80, 0x38, 0x3d, 0x4c, 0xdd, 0x64, 0xc0, 0xfc, 0x07, 0x86, 0xae, 0x90, 0x30, 0xa9, 0x81, 0x18, 0x35, 0xd3, 0x65, 0xf6, 0x7b, 0xe8, 0x1c, 0x92, 0x4b, 0xcb, 0x7a, 0x9d, 0x96, 0x8a, 0x42, 0x2c, 0x72, 0x6f, 0x24, 0xce, 0x7f, 0x5d, 0x6b, 0xf2, 0x50, 0x6c, 0x6a, 0x8d, 0x63, 0x4b, 0x64, 0x23, 0x31, 0x21, 0x60, 0xe2, 0xdb, 0xb9, 0xe6, 0x4e, 0x8e, 0x95, 0x91, 0x59, 0xc1, 0xb3, 0x49, 0x6c, 0x81, 0x1a, 0x08, 0xc0, 0xf3, 0xf4, 0x99, 0x5a, 0x2f, 0x53, 0xfd, 0x6e, 0xf7, 0x58, 0x0e, 0x50, 0xc7, 0x1d, 0xc9, 0xe0, 0xcb, 0x49, 0x07, 0x3b, 0x70, 0xa0, 0x43, 0xc8, 0xb1, 0x88, 0xd2, 0x7d, 0x98, 0xd7, 0xf7, 0xff, 0xdd, 0xdf, 0x6d, 0x06, 0x79, 0x80, 0xcf, 0xfa, 0xa2, 0x39, 0xd0, 0xe9, 0xe1, 0xee, 0x70, 0xc9, 0xb5, 0x03, 0xd3, 0x4a, 0xec, 0x3f, 0x1c, 0xa6, 0xe5, 0x73, 0x35, 0xe7, 0x01, 0xe3, 0xea, 0xd5, 0xab, 0xc1, 0xa7, 0x3d, 0x4c, 0xbd, 0x2f, 0xce, 0x49, 0x0c, 0x48, 0x34, 0xc5, 0xd3, 0x17, 0x62, 0xeb, 0x5f, 0x3c, 0xf8, 0x33, 0x7a, 0x60, 0x71, 0x87, 0x12, 0xb2, 0x46, 0x50, 0x85, 0xad, 0x0e, 0x0b, 0xa3, 0x29, 0xa4, 0xd3, 0x9b, 0xd1, 0xcb, 0x1a, 0x00, 0x30, 0xbc, 0xaa, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x33, 0x91, 0x85, 0xbf, 0x29, 0x11, 0xf0, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x09, 0xc2, 0x61, 0x4a, 0x53, 0xd7, 0x45, 0xd1, 0xb4, 0x87, 0x33, 0x02, 0x7c, 0xce, 0x58, 0xc3, 0x62, 0x61, 0xb1, 0x48, 0x93, 0x12, 0x99, 0x43, 0x51, 0x04, 0x23, 0xaf, 0x35, 0x48, 0x29, 0xe1, 0x71, 0x0d, 0x4b, 0x12, 0x0e, 0x9a, 0xf5, 0x1a, 0x64, 0xc1, 0xa6, 0xf5, 0x7d, 0x99, 0x0a, 0x1b, 0x9f, 0xa1, 0xf5, 0xa4, 0xc1, 0xa9, 0x7b, 0xe7, 0x7f, 0xd1, 0x0a, 0x25, 0xc8, 0x04, 0x30, 0xf8, 0x89, 0x9e, 0xa9, 0x58, 0xff, 0xb0, 0x9c, 0x72, 0x25, 0x10, 0xe6, 0xb1, 0xdd, 0xf1, 0x62, 0xde, 0x8f, 0x4c, 0x36, 0x9b, 0xdd, 0xa1, 0xd2, 0x4f, 0x82, 0x65, 0xa8, 0xb6, 0xd3, 0x69, 0x30, 0x4f, 0x53, 0x17, 0xc6, 0x34, 0x6e, 0x99, 0x6c, 0xf3, 0x2d, 0x43, 0x24, 0x27, 0xc8, 0x3f, 0xda, 0x62, 0x8e, 0xa0, 0xbe, 0x8d, 0xa6, 0x4f, 0x72, 0x2b, 0x21, 0x62, 0x45, 0x69, 0x3f, 0x00, 0x97, 0xf3, 0xee, 0x7f, 0x5a, 0xb2, 0xf8, 0x99, 0x69, 0x28, 0xa0, 0x94, 0x52, 0x0b, 0x98, 0x9e, 0xb1, 0xfa, 0x6a, 0x0f, 0x22, 0x23, 0x63, 0xb5, 0xbf, 0x64, 0x05, 0xb6, 0xcc, 0x2d, 0x69, 0x99, 0x12, 0x8f, 0xa8, 0xbb, 0xc4, 0x8a, 0x61, 0xb2, 0x6b, 0x94, 0x66, 0x7f, 0x61, 0xab, 0xc5, 0xc5, 0x9b, 0x75, 0x31, 0x73, 0x58, 0xaa, 0x3b, 0x37, 0xd1, 0x28, 0xbc, 0x45, 0x3b, 0xb2, 0x75, 0x95, 0xb5, 0x42, 0xd0, 0xd6, 0xe3, 0x13, 0xbd, 0xfd, 0x69, 0xde, 0xe6, 0xb3, 0x61, 0x44, 0x93, 0xa0, 0xb0, 0x95, 0xa5, 0x90, 0x1e, 0x05, 0xcd, 0x4e, 0xf3, 0x83, 0x64, 0xd3, 0x51, 0xf6, 0x58, 0x6c, 0x12, 0x2d, 0xe5, 0x3c, 0x79, 0x60, 0xf8, 0x5c, 0xab, 0x41, 0x47, 0x3d, 0xb7, 0xb5, 0x65, 0xd0, 0xd8, 0x23, 0xf4, 0xb5, 0xcd, 0x2e, 0xbb, 0x5b, 0x73, 0xf7, 0xdd, 0xa9, 0x1d, 0xe1, 0xea, 0x9c, 0x43, 0xef, 0x86, 0x05, 0x7d, 0x0a, 0x8a, 0x82, 0xc0, 0xc4, 0x27, 0xc2, 0xa9, 0xc8, 0x8e, 0x0a, 0xeb, 0x42, 0xd9, 0x83, 0x7c, 0xd5, 0x95, 0x71, 0x9c, 0x61, 0x5e, 0xb7, 0x22, 0xe6, 0xa7, 0xa9, 0x7c, 0x18, 0x29, 0x7c, 0x10, 0xcd, 0x6d, 0x2e, 0xfc, 0xc6, 0x3e, 0xc1, 0xfe, 0x32, 0x60, 0x1f, 0xa5, 0xfa, 0x01, 0x29, 0xf1, 0x25, 0xa2, 0xad, 0x80, 0xc4, 0xfe, 0x2d, 0xf8, 0x94, 0x43, 0x01, 0xe3, 0xe9, 0x8e, 0x4f, 0x57, 0xfa, 0xca, 0xc5, 0xb2, 0x4d, 0xb5, 0x36, 0xf6, 0xda, 0x5a, 0xaa, 0x5f, 0xd5, 0xff, 0x1e, 0xc0, 0x7f, 0xea, 0x36, 0xfe, 0xc8, 0xdf, 0x83, 0x83, 0xa2, 0xca, 0xb8, 0xcd, 0x36, 0x78, 0x24, 0xbd, 0xb6, 0x58, 0x66, 0x0a, 0x3e, 0x08, 0x5a, 0x18, 0x73, 0x18, 0xaf, 0x11, 0x4d, 0x3e, 0xd8, 0x29, 0xd1, 0x48, 0x11, 0xad, 0x36, 0x98, 0x8a, 0xe5, 0xb4, 0xcd, 0x53, 0x56, 0xd2, 0xfc, 0x89, 0x00, 0x14, 0x8e, 0x2c, 0xde, 0xbc, 0x05, 0x97, 0x72, 0xe2, 0xad, 0x40, 0xae, 0x8f, 0x3d, 0x7f, 0xb6, 0x4a, 0x55, 0x55, 0x03, 0x8a, 0xa3, 0x08, 0x0b, 0xbd, 0xcb, 0x76, 0x0a, 0xf8, 0x6d, 0x3d, 0x32, 0xb2, 0xe5, 0x50, 0xa4, 0x3c, 0x2d, 0xdf, 0x42, 0x70, 0x9f, 0x64, 0xdb, 0x5c, 0x8e, 0xd9, 0xfe, 0x48, 0xd5, 0x43, 0x3e, 0x97, 0x25, 0x52, 0x0a, 0xef, 0x60, 0x38, 0xba, 0x6c, 0x51, 0xed, 0xbc, 0xbc, 0x8b, 0xe9, 0x59, 0x8f, 0xe7, 0xdf, 0xa2, 0x18, 0xcb, 0x11, 0x0e, 0x90, 0x6d, 0x30, 0xcd, 0x24, 0xff, 0x62, 0x62, 0x2c, 0x0d, 0x9c, 0x9a, 0x12, 0x65, 0x37, 0x2c, 0xdf, 0x64, 0x76, 0xf6, 0x60, 0x8d, 0x9a, 0x2d, 0xdd, 0x94, 0xec, 0xee, 0x87, 0x9c, 0x01, 0xcc, 0x34, 0x68, 0x6d, 0xe6, 0xb6, 0x42, 0x33, 0x3c, 0xef, 0x7a, 0x0f, 0xb7, 0xbf, 0x69, 0x7e, 0x72, 0x71, 0x74, 0xd8, 0xc1, 0xd8, 0x1a, 0x7a, 0x9f, 0x4d, 0x47, 0xd7, 0x63, 0x00, 0x0d, 0xdb, 0x1b, 0x20, 0x06, 0x91, 0x0a, 0xf1, 0x20, 0x8c, 0x96, 0x1c, 0x13, 0x7b, 0xd2, 0x3d, 0x0f, 0x01, 0x27, 0x60, 0xbc, 0xd6, 0xa7, 0x8d, 0x7c, 0x05, 0x30, 0x92, 0x35, 0xfc, 0xa8, 0x06, 0x52, 0x2b, 0xda, 0xd4, 0x2c, 0x42, 0x4a, 0xb3, 0x0d, 0x6d, 0x66, 0xec, 0x28, 0xb6, 0x8f, 0xc7, 0x0b, 0xcd, 0x9d, 0xae, 0xcb, 0xad, 0xd8, 0x9d, 0xec, 0x08, 0x4e, 0xab, 0x25, 0x09, 0x3a, 0xd8, 0xc7, 0x01, 0x49, 0x5e, 0xa9, 0x83, 0x65, 0x34, 0x87, 0x67, 0x38, 0x1e, 0x40, 0x91, 0xf2, 0x54, 0x18, 0x92, 0xa7, 0x17, 0xf3, 0x6c, 0xa1, 0x8a, 0x28, 0xe0, 0x56, 0xb3, 0xbf, 0x6c, 0xe2, 0xb7, 0xb8, 0xa3, 0x72, 0x6c, 0x3c, 0x81, 0x16, 0xdc, 0x7b, 0xd3, 0xa7, 0xfa, 0xb3, 0x0b, 0x24, 0xfa, 0x4c, 0xdb, 0xa2, 0x14, 0xd1, 0xb1, 0xab, 0x68, 0x20, 0x2e, 0x7b, 0xc2, 0x9d, 0x85, 0x12, 0xf6, 0xc8, 0x09, 0xf0, 0xec, 0x10, 0x0a, 0x69, 0x97, 0x11, 0x55, 0x19, 0xad, 0x3b, 0x93, 0xb5, 0xf1, 0x75, 0x2e, 0x7c, 0x16, 0xc6, 0xd4, 0xfc, 0xff, 0xc6, 0x8b, 0x7a, 0x38, 0x5a, 0x6c, 0x8b, 0xe7, 0x61, 0x15, 0x6e, 0x70, 0xd1, 0x44, 0xa3, 0x39, 0xae, 0x09, 0xe2, 0x52, 0x1c, 0xbb, 0xa0, 0xcc, 0x55, 0x3b, 0x4c, 0x11, 0x9e, 0x2f, 0x59, 0x9a, 0x57, 0x72, 0xce, 0xf7, 0xcd, 0x50, 0x37, 0x5d, 0xea, 0x83, 0xfd, 0x67, 0x7e, 0x64, 0x8b, 0x7e, 0x88, 0xd2, 0x31, 0xf3, 0xae, 0xcd, 0xfd, 0x65, 0x6a, 0x07, 0x92, 0x63, 0xc2, 0xaf, 0x4e, 0x8a, 0x54, 0x87, 0x77, 0xb9, 0x0e, 0x8c, 0x21, 0x16, 0x5f, 0xa7, 0xf2, 0xd6, 0x50, 0xfa, 0xe0, 0x5d, 0x3e, 0x63, 0x9c, 0x67, 0x0b, 0xde, 0x91, 0xe6, 0xe1, 0x09, 0x87, 0x4f, 0xcb, 0xf2, 0xa8, 0x24, 0xa7, 0x92, 0xc5, 0x1b, 0xec, 0x44, 0x74, 0xf4, 0xe7, 0x53, 0xaf, 0x6d, 0xc2, 0x46, 0x38, 0xd5, 0x6f, 0x9e, 0x93, 0x7f, 0x46, 0xfb, 0x41, 0x3f, 0x83, 0x74, 0x7b, 0xbb, 0x39, 0xe3, 0x2d, 0x5d, 0x85, 0x7c, 0xb3, 0xce, 0xdb, 0xa6, 0x39, 0x5d, 0xea, 0xd2, 0xe2, 0x9d, 0x4e, 0xfa, 0x70, 0xcc, 0x77, 0x37, 0xf0, 0x58, 0xa9, 0xef, 0x56, 0x12, 0x59, 0xd9, 0x4a, 0xbf, 0xba, 0xa5, 0x30, 0x15, 0xa3, 0xa1, 0x85, 0x4a, 0x7b, 0xf4, 0x5f, 0x84, 0x92, 0x54, 0xce, 0x4a, 0x68, 0x9c, 0x9c, 0x6d, 0x0a, 0x95, 0x9a, 0xfc, 0xe6, 0x1c, 0xa4, 0x6f, 0x2c, 0x8e, 0x5a, 0xd8, 0x4b, 0x3c, 0xbc, 0x62, 0x6b, 0x18, 0x5a, 0x1e, 0x3d, 0xb6, 0xac, 0x29, 0x69, 0x22, 0xda, 0xca, 0xd6, 0xdf, 0x48, 0x36, 0xeb, 0x7f, 0x15, 0x98, 0xb1, 0xcd, 0x92, 0xce, 0x14, 0x32, 0x93, 0x5c, 0x44, 0xe9, 0x17, 0x05, 0x22, 0xb0, 0xc6, 0x16, 0xf2, 0x22, 0x9c, 0x80, 0x48, 0xbf, 0x29, 0x98, 0x48, 0x85, 0xa8, 0xdd, 0xc2, 0x40, 0x22, 0x3f, 0x5c, 0xde, 0x52, 0x7e, 0xf1, 0xf2, 0x8b, 0xbe, 0xfe, 0x6f, 0xfa, 0xb0, 0xa2, 0x42, 0x22, 0xc4, 0x0b, 0x87, 0xdd, 0xc8, 0x69, 0x0c, 0x92, 0xaa, 0x37, 0xe6, 0x59, 0xe9, 0x8d, 0xa9, 0xed, 0xeb, 0xb8, 0xb8, 0x80, 0x33, 0xa7, 0xf5, 0xde, 0x33, 0xd2, 0xfc, 0x1c, 0x00, 0x6c, 0x18, 0x82, 0xb5, 0xc8, 0x11, 0x65, 0xfe, 0x7c, 0x9a, 0x0d, 0x43, 0xba, 0x16, 0x5c, 0xb1, 0xa0, 0xf6, 0xec, 0x4f, 0x24, 0x56, 0xfb, 0x70, 0x01, 0x21, 0x42, 0xd0, 0x5f, 0xbb, 0xbc, 0x8d, 0xad, 0x5c, 0x83, 0x70, 0x58, 0x23, 0xb5, 0x91, 0xa7, 0x1a, 0x1d, 0xc6, 0x00, 0x25, 0xd3, 0x7a, 0x7b, 0xc3, 0xbf, 0xbd, 0x57, 0xe6, 0xab, 0xf9, 0x91, 0x4d, 0xb4, 0x91, 0x92, 0x64, 0x2e, 0x02, 0xd8, 0x6e, 0x37, 0x6b, 0x19, 0x8d, 0xbb, 0xf1, 0x8e, 0xfe, 0x58, 0xd7, 0xe7, 0xac, 0x33, 0x46, 0x01, 0xd5, 0x15, 0x51, 0x7a, 0xae, 0x07, 0x9f, 0x6e, 0xca, 0x22, 0x94, 0x87, 0xfd, 0xc9, 0xad, 0x13, 0x85, 0xd5, 0xca, 0x97, 0xcc, 0x0a, 0x07, 0x7b, 0x33, 0xd1, 0x7a, 0x5e, 0x0d, 0xff, 0xa6, 0x44, 0xfe, 0x20, 0xbf, 0xfa, 0x7e, 0xc7, 0x46, 0x0d, 0x15, 0x5c, 0xaa, 0xaa, 0xe2, 0x41, 0x9f, 0x42, 0x03, 0xfe, 0x60, 0x79, 0xdf, 0x5e, 0xdf, 0x60, 0x94, 0xa1, 0x01, 0x8a, 0xf7, 0xc3, 0xf4, 0x02, 0x8a, 0xf0, 0xb4, 0x27, 0xf4, 0x19, 0x45, 0x00, 0x71, 0xe4, 0x44, 0xd0, 0xd3, 0xec, 0x39, 0x0c, 0xa7, 0xe7, 0xf7, 0x5a, 0xeb, 0x43, 0xfb, 0x2c, 0x4f, 0x38, 0xe7, 0x0f, 0xd6, 0x08, 0x44, 0x2e, 0xd2, 0x88, 0xcb, 0xa6, 0x0f, 0x28, 0x84, 0xf0, 0x78, 0xee, 0x5a, 0x60, 0xea, 0x49, 0x3a, 0x09, 0xff, 0xf0, 0x25, 0xbf, 0x50, 0x62, 0xcf, 0x4e, 0x43, 0x5b, 0xd7, 0xbb, 0x44, 0xe5, 0x3d, 0x1e, 0xf5, 0x88, 0x38, 0xe0, 0x3f, 0xe2, 0x07, 0xaa, 0x8a, 0x42, 0x40, 0x1a, 0x9e, 0x32, 0x14, 0x37, 0x40, 0x1a, 0x7b, 0xe1, 0x6b, 0x07, 0x63, 0xb0, 0xbb, 0x92, 0x6c, 0x94, 0xe4, 0x13, 0x88, 0x4d, 0x5e, 0x00, 0xbf, 0xb4, 0xc3, 0xc2, 0x10, 0xdd, 0x2f, 0x1c, 0xdf, 0xc8, 0xdb, 0xab, 0x61, 0xa7, 0x6a, 0x67, 0x19, 0x4f, 0xa2, 0xfc, 0x3e, 0x11, 0xa0, 0xf4, 0x83, 0x58, 0xb8, 0x88, 0x29, 0xe4, 0xc4, 0x7c, 0x85, 0x74, 0x26, 0xb8, 0x76, 0xe4, 0xce, 0xcc, 0x19, 0xf6, 0xaa, 0x64, 0x4e, 0xfa, 0xe0, 0x2f, 0xa1, 0xca, 0x5b, 0x67, 0x92, 0x76, 0x15, 0x2b, 0x03, 0x8e, 0x97, 0x8c, 0x4c, 0xb0, 0x4b, 0xc0, 0x99, 0xc1, 0xf8, 0xa3, 0x0c, 0xa9, 0xfe, 0x96, 0x9f, 0x09, 0xfd, 0xe9, 0xa2, 0x2b, 0x7e, 0xe2, 0xe2, 0xb8, 0x21, 0xb6, 0x35, 0xde, 0x72, 0xdf, 0xbb, 0x2b, 0xdc, 0x90, 0xee, 0x66, 0x64, 0xe6, 0xd4, 0x2b, 0x93, 0xf3, 0xb9, 0xc7, 0xd9, 0x77, 0xdf, 0x47, 0x21, 0xbe, 0x53, 0x9d, 0xe9, 0xc9, 0x69, 0xcd, 0xa0, 0x7b, 0x4b, 0xd6, 0x00, 0x2b, 0x80, 0x50, 0x4c, 0x38, 0x03, 0x77, 0xac, 0xa1, 0x43, 0xcb, 0xb7, 0xdb, 0x12, 0x23, 0x45, 0x14, 0x8c, 0x36, 0x60, 0x84, 0xbd, 0x5e, 0x53, 0xa4, 0x94, 0xbc, 0x40, 0x0f, 0x9c, 0x3e, 0x4a, 0x8b, 0x98, 0xb4, 0xc7, 0xe6, 0xc9, 0xbf, 0x45, 0x42, 0x3c, 0xf8, 0xea, 0x54, 0x8c, 0xdf, 0x3f, 0x75, 0x39, 0x82, 0xd8, 0xc4, 0x72, 0x40, 0x8a, 0xd8, 0x5b, 0x8c, 0x27, 0x2a, 0x37, 0xdc, 0x17, 0xec, 0x1e, 0xff, 0x15, 0x98, 0xeb, 0xf9, 0xd0, 0x46, 0x38, 0x5a, 0x58, 0xb9, 0x0a, 0xba, 0xb1, 0x56, 0x10, 0x3d, 0xcb, 0x25, 0x13, 0x02, 0x8b, 0x50, 0x5e, 0xb4, 0x93, 0x14, 0xd9, 0x2e, 0x4c, 0x35, 0xf0, 0xa2, 0xf0, 0xf9, 0xbc, 0x17, 0xe2, 0x53, 0xb1, 0x56, 0xe4, 0x7c, 0x87, 0x06, 0xbe, 0xe2, 0xdd, 0x08, 0x17, 0xbe, 0x04, 0xdb, 0xb5, 0x30, 0x0c, 0xdf, 0x2d, 0x18, 0x5f, 0x85, 0x3d, 0x99, 0xe2, 0xe4, 0xd0, 0xe5, 0x38, 0x04, 0x56, 0x01, 0xc3, 0x05, 0xdc, 0x68, 0xc1, 0xf3, 0x39, 0x67, 0x60, 0xea, 0x94, 0x8f, 0xd8, 0xd7, 0xa6, 0x23, 0x45, 0x80, 0x5f, 0xc9, 0x97, 0x57, 0xf0, 0x92, 0xb7, 0x4b, 0xa3, 0x05, 0x84, 0x06, 0x15, 0xcd, 0x2a, 0xc9, 0xf8, 0xd6, 0x15, 0x37, 0xcd, 0x74, 0xc0, 0x63, 0x4b, 0xf2, 0xa3, 0x10, 0x64, 0x0f, 0xf9, 0xdb, 0xb6, 0xe6, 0x54, 0x1e, 0xde, 0x01, 0x94, 0x81, 0x26, 0x45, 0xb8, 0x5b, 0x93, 0x7a, 0x3b, 0xea, 0xf4, 0x98, 0x48, 0x09, 0x15, 0xc0, 0x16, 0x4a, 0xba, 0x19, 0x91, 0x6e, 0xe5, 0xcf, 0x02, 0xbb, 0xe5, 0xa2, 0x84, 0x3a, 0xc8, 0x25, 0x0d, 0x11, 0x25, 0x21, 0x76, 0x02, 0x5a, 0xa6, 0x8a, 0xb5, 0x7e, 0xd5, 0xda, 0x00, 0x61, 0xb3, 0x30, 0x4d, 0x48, 0x53, 0x78, 0xf3, 0x0c, 0x6d, 0xd0, 0x81, 0xf9, 0x89, 0x04, 0x1a, 0x9e, 0xac, 0xb7, 0x3e, 0x7f, 0x95, 0x92, 0x67, 0xb4, 0x63, 0x66, 0xe6, 0x9d, 0x34, 0x98, 0xa4, 0x17, 0xdc, 0xc9, 0xbc, 0x47, 0xc9, 0xf4, 0xa6, 0xa5, 0x9e, 0x9f, 0x18, 0x91, 0xe5, 0xc8, 0xf2, 0x76, 0x88, 0xc4, 0xec, 0x10, 0x06, 0x0a, 0x70, 0x58, 0xed, 0x9a, 0xa8, 0x68, 0x57, 0xfb, 0xf6, 0x2d, 0x1c, 0x78, 0xb3, 0xdd, 0x3f, 0x26, 0xc2, 0x5b, 0x88, 0xb1, 0x3b, 0x55, 0x26, 0x2b, 0x74, 0xba, 0xc3, 0x4c, 0xa0, 0xec, 0x25, 0x85, 0x18, 0x18, 0xd5, 0x50, 0xd5, 0xb4, 0x46, 0x77, 0x3e, 0x01, 0x6b, 0x56, 0xe6, 0xb0, 0x83, 0x8e, 0x5b, 0x36, 0x82, 0x4b, 0xe9, 0x6a, 0x5a, 0x40, 0xe1, 0xd2, 0xe5, 0xb9, 0x27, 0xc3, 0xce, 0x67, 0x86, 0x46, 0x25, 0xa7, 0x53, 0x1b, 0x63, 0x92, 0x9c, 0x5a, 0xd9, 0x82, 0x4b, 0x4f, 0x71, 0x90, 0x7b, 0xf4, 0x6b, 0x7d, 0x13, 0x6a, 0xe6, 0xb7, 0x5c, 0x11, 0x9e, 0x2e, 0x1c, 0x17, 0x7f, 0x6f, 0x90, 0x4f, 0x20, 0xc7, 0xe0, 0x75, 0x68, 0x40, 0x59, 0xa0, 0x8e, 0xea, 0x91, 0xb6, 0xc2, 0x8e, 0xfa, 0xb2, 0x16, 0xe8, 0xe2, 0x02, 0xdb, 0xac, 0x7c, 0x0b, 0x9d, 0xa2, 0x4a, 0x89, 0xc2, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x01, 0x09, 0x2d, 0x0f, 0xf6, 0x4d, 0x95, 0x3e, 0x3a, 0x22, 0xe0, 0x6b, 0xf4, 0xc7, 0x1f, 0x61, 0x19, 0x68, 0x7d, 0xa1, 0x25, 0xe0, 0x51, 0xca, 0x80, 0x6f, 0x30, 0x0e, 0xdb, 0x46, 0x51, 0xe6, 0x18, 0x8a, 0x85, 0x40, 0x01, 0xef, 0xca, 0x74, 0xd7, 0xc1, 0xb9, 0x01, 0x17, 0x45, 0xda, 0x3f, 0x12, 0x94, 0xd8, 0x72, 0xb8, 0xdd, 0x39, 0x4c, 0x3f, 0x60, 0x8b, 0x42, 0xae, 0x7d, 0x0f, 0x89, 0xc1, 0xc7, 0xae, 0x86, 0x0e, 0xd5, 0x3d, 0xd1, 0x95, 0x7b, 0xd7, 0x0a, 0xac, 0x14, 0x9e, 0xd5, 0x2b, 0xa0, 0x9b, 0xff, 0x30, 0xc9, 0xcc, 0x8d, 0x07, 0xc2, 0x17, 0xe5, 0xe1, 0xdf, 0x9b, 0x4d, 0xa8, 0xf7, 0x0f, 0x81, 0x72, 0xb5, 0x8d, 0xa5, 0x89, 0xac, 0x43, 0x78, 0x9a, 0x88, 0x9c, 0x76, 0xa0, 0x37, 0x63, 0x43, 0xbd, 0xbb, 0x54, 0x93, 0x27, 0x92, 0x87, 0x1d, 0x35, 0xca, 0x03, 0x36, 0x20, 0x8e, 0xa0, 0x56, 0x49, 0xd6, 0x76, 0x7d, 0x79, 0x69, 0x0d, 0x5f, 0xfc, 0xe6, 0x7d, 0xf6, 0x81, 0x0c, 0xec, 0xd1, 0x33, 0x3f, 0xd2, 0xb3, 0x5d, 0x27, 0x07, 0x70, 0x1a, 0x64, 0xd8, 0x5e, 0x47, 0x93, 0xfa, 0xbc, 0x61, 0x68, 0x15, 0xe4, 0x93, 0x7e, 0xba, 0x34, 0x0b, 0xb2, 0x60, 0x8f, 0xf1, 0x01, 0x86, 0xc1, 0x04, 0xdf, 0x40, 0x0a, 0x01, 0x25, 0x86, 0xb0, 0xf8, 0xe5, 0xf5, 0xb4, 0xee, 0x0a, 0xa6, 0xb1, 0x89, 0x6e, 0x93, 0xca, 0x11, 0x4b, 0xe9, 0x35, 0x2b, 0x4e, 0x3b, 0x9f, 0x69, 0xc4, 0xe5, 0x77, 0x85, 0x72, 0xd2, 0x73, 0x2a, 0x08, 0xbb, 0xd3, 0x4b, 0x9d, 0xb7, 0xc8, 0xe5, 0x25, 0xcb, 0x99, 0x5f, 0xe3, 0xd5, 0x5b, 0x24, 0x8e, 0xcd, 0xdd, 0x3c, 0x8a, 0x91, 0xef, 0x99, 0xf0, 0x0c, 0xe8, 0xb5, 0xe5, 0x43, 0x32, 0x26, 0x0c, 0x0f, 0xe4, 0x38, 0x17, 0xc9, 0x56, 0x39, 0x58, 0x1b, 0x04, 0x71, 0xf3, 0xd2, 0x3d, 0x0d, 0x29, 0x80, 0x74, 0x84, 0x8d, 0x91, 0x2c, 0xaf, 0x8c, 0xa4, 0x90, 0x6a, 0x3c, 0x15, 0xda, 0x4c, 0xda, 0x7f, 0x3f, 0xfe, 0xeb, 0x96, 0x09, 0x3d, 0x44, 0x32, 0x49, 0x06, 0x65, 0x63, 0xab, 0xc2, 0x6a, 0xdd, 0xe5, 0x02, 0x7a, 0x33, 0x9c, 0x54, 0x16, 0x4c, 0x0b, 0x5b, 0xe9, 0xe4, 0x27, 0xe9, 0x7a, 0x92, 0xbd, 0x22, 0xd7, 0x5e, 0x21, 0x6d, 0xb8, 0x97, 0x0d, 0xb5, 0x09, 0xe2, 0x55, 0xc0, 0xbd, 0x95, 0x17, 0xd0, 0x52, 0xcc, 0xee, 0x6d, 0x1e, 0xf8, 0x45, 0xe6, 0x34, 0x6d, 0xb2, 0xf1, 0xa8, 0x37, 0x7d, 0x4b, 0x3e, 0x86, 0x9d, 0x4f, 0xc0, 0x3c, 0xfa, 0xa2, 0x9a, 0xad, 0x06, 0xba, 0x83, 0x10, 0xb2, 0xc0, 0x02, 0xf0, 0x82, 0xce, 0x01, 0x07, 0xc4, 0x01, 0xb2, 0x6c, 0x01, 0x70, 0x68, 0x01, 0x42, 0xd4, 0x9a, 0x90, 0x16, 0xb3, 0x1d, 0xb0, 0x4b, 0xc7, 0x47, 0x6d, 0x4c, 0x04, 0x4a, 0xb3, 0x83, 0x31, 0x46, 0x75, 0xf4, 0x4c, 0x28, 0x10, 0x88, 0x7b, 0x03, 0x48, 0x4d, 0xd3, 0xdb, 0xba, 0xbd, 0xca, 0x74, 0xbc, 0xfd, 0x43, 0x60, 0x76, 0xba, 0xa5, 0x63, 0xbc, 0xe9, 0x46, 0x00, 0xd7, 0x31, 0xff, 0xff, 0x19, 0x29, 0x4d, 0x01, 0x9d, 0x09, 0x30, 0xc1, 0xe7, 0x79, 0x66, 0xe1, 0x74, 0xbd, 0x03, 0x31, 0xe9, 0xbf, 0xca, 0x7e, 0xc1, 0x06, 0x94, 0x7b, 0x64, 0xe5, 0xc3, 0xe9, 0x99, 0x3f, 0x2f, 0x0c, 0x41, 0x57, 0x4c, 0x0d, 0xcd, 0xfd, 0xb2, 0x62, 0x9b, 0xf0, 0xc1, 0x34, 0x39, 0xaa, 0x04, 0x67, 0x3c, 0x23, 0x71, 0x28, 0xa9, 0x87, 0x90, 0x7d, 0x64, 0xc0, 0x0b, 0xa8, 0x51, 0x34, 0x6f, 0xfe, 0xe2, 0xf1, 0xdd, 0xa0, 0x0e, 0x68, 0xa6, 0xf1, 0x35, 0x6a, 0xbd, 0x92, 0x4c, 0x45, 0xd6, 0x3e, 0x9c, 0xea, 0x20, 0xd6, 0x67, 0xcf, 0xdb, 0xc4, 0x0b, 0xf6, 0xdd, 0xaf, 0xb8, 0xd5, 0x97, 0xc4, 0x05, 0xe1, 0x5a, 0x5e, 0xef, 0xb7, 0x0d, 0x82, 0x1f, 0xdc, 0x5e, 0x3a, 0x50, 0x51, 0x8a, 0xce, 0x6e, 0x88, 0x75, 0xea, 0x9f, 0x90, 0x17, 0x12, 0x99, 0xed, 0xe8, 0x2b, 0x44, 0x8d, 0xf2, 0xab, 0x93, 0xc1, 0xf0, 0xad, 0xd1, 0x6d, 0x14, 0x63, 0x21, 0x97, 0x83, 0x32, 0x9c, 0xeb, 0x6c, 0x59, 0xd6, 0x2a, 0x27, 0xac, 0xde, 0xb5, 0x7f, 0x69, 0x37, 0x4c, 0x60, 0xb6, 0xfd, 0x0e, 0x21, 0x27, 0x37, 0xc4, 0x3a, 0xdd, 0x4f, 0xd7, 0x1f, 0xfd, 0x28, 0x4a, 0xf1, 0x5e, 0x9d, 0x8b, 0x87, 0x3c, 0x71, 0xdc, 0xc7, 0x58, 0x88, 0x11, 0x7a, 0xb3, 0x63, 0x31, 0x48, 0x6d, 0xe6, 0x7a, 0x3d, 0xad, 0xf8, 0xd7, 0xd7, 0xe3, 0x7d, 0x19, 0x69, 0x4b, 0xf9, 0xdf, 0xab, 0x44, 0x4d, 0x8b, 0x03, 0x9d, 0x8d, 0xf5, 0x43, 0xa0, 0x12, 0x15, 0x04, 0x85, 0x2c, 0x7b, 0xec, 0x78, 0x94, 0x71, 0x50, 0xcc, 0xb8, 0x88, 0x5e, 0xe8, 0xcd, 0xbe, 0xa6, 0xe7, 0x14, 0xe1, 0x93, 0xe5, 0x68, 0x86, 0xe0, 0x0d, 0xbc, 0x0a, 0x46, 0xc6, 0xe3, 0x63, 0xaf, 0x06, 0x99, 0x22, 0x16, 0x8c, 0x8d, 0x6f, 0x2a, 0xbf, 0x21, 0xb8, 0xf3, 0x7c, 0x75, 0xf1, 0xca, 0x03, 0x26, 0x95, 0x4b, 0xad, 0xa4, 0xf4, 0x29, 0xb5, 0x07, 0x16, 0x8e, 0xb3, 0x79, 0x9b, 0xbe, 0xec, 0xdd, 0x64, 0x1f, 0xf8, 0x93, 0x7c, 0xf5, 0x0c, 0xb6, 0xdc, 0x74, 0xf6, 0xe8, 0xe7, 0x63, 0x26, 0xe8, 0x86, 0x18, 0x59, 0x5d, 0xc3, 0x13, 0xa8, 0x8d, 0xe1, 0xe9, 0x43, 0xc9, 0x58, 0xb2, 0x6c, 0x8c, 0x6d, 0x95, 0xc9, 0xa7, 0xcd, 0xc2, 0xc3, 0xfb, 0x77, 0xda, 0xf7, 0x05, 0x5e, 0x16, 0xf5, 0xc9, 0xa3, 0xa4, 0x33, 0xfe, 0x3f, 0xd4, 0x4e, 0xa1, 0xb2, 0xd7, 0x79, 0xcd, 0x5a, 0xce, 0x2b, 0xfc, 0xda, 0x5e, 0x5a, 0x8b, 0x79, 0x1c, 0xd7, 0x86, 0x62, 0x87, 0x8d, 0xcf, 0x6f, 0x52, 0x28, 0x30, 0x03, 0x97, 0xb1, 0x49, 0x7c, 0x7b, 0x05, 0x04, 0x07, 0xae, 0x2d, 0x94, 0x54, 0x06, 0xd5, 0x3d, 0x1b, 0x18, 0x3c, 0x13, 0xce, 0x3d, 0x2b, 0x4e, 0xac, 0xbe, 0x98, 0x60, 0x89, 0xcf, 0xb8, 0x25, 0xf4, 0x44, 0xe9, 0xda, 0x9d, 0xc8, 0x77, 0x33, 0x4f, 0xc0, 0x4b, 0x58, 0xaf, 0xf2, 0xd4, 0x12, 0xb8, 0xfb, 0x3a, 0x07, 0x3b, 0x7f, 0xaf, 0x3d, 0x7b, 0xf1, 0x8b, 0x09, 0xe3, 0x9c, 0x97, 0xb1, 0xd0, 0x4a, 0x32, 0xb6, 0x04, 0x23, 0x2e, 0xe5, 0xbe, 0xca, 0xad, 0xeb, 0x53, 0xfa, 0xc2, 0x8c, 0xea, 0xa0, 0x91, 0xe2, 0x9f, 0xa8, 0x68, 0xb2, 0x3f, 0x28, 0x80, 0xd6, 0x3f, 0x98, 0x5a, 0xf9, 0x96, 0xae, 0x01, 0x4c, 0xce, 0x2f, 0x1b, 0x6a, 0xd3, 0x1a, 0xd9, 0x69, 0x32, 0xc7, 0xba, 0x0e, 0x06, 0xed, 0x3e, 0x74, 0xec, 0xce, 0x80, 0x39, 0xee, 0x2e, 0x9b, 0x43, 0x44, 0xd2, 0x2d, 0x52, 0x8d, 0xf2, 0x9a, 0x20, 0x77, 0xb5, 0x21, 0xdf, 0x72, 0x8c, 0x85, 0x46, 0x4d, 0x93, 0xd1, 0x6a, 0xb2, 0x21, 0xcc, 0x97, 0x31, 0x3a, 0x89, 0xa6, 0x19, 0x77, 0x76, 0xc1, 0x7c, 0xfd, 0x2b, 0x99, 0x10, 0xbb, 0xfa, 0x16, 0xe5, 0xbf, 0xa3, 0x27, 0x19, 0x7c, 0xaf, 0xdc, 0xf9, 0x11, 0xb0, 0x00, 0xc6, 0x16, 0x9e, 0x5c, 0x4a, 0xcc, 0x19, 0x3f, 0xe0, 0x8b, 0x9a, 0x40, 0xfc, 0x44, 0x51, 0xa0, 0xab, 0xa4, 0x31, 0x13, 0x22, 0x0a, 0x1b, 0x4b, 0x37, 0x33, 0xdb, 0x21, 0x45, 0x7a, 0xfc, 0xfc, 0xc3, 0xd0, 0x3b, 0xe2, 0x27, 0x38, 0x78, 0x17, 0x0d, 0xd6, 0x4b, 0x8f, 0x77, 0x05, 0x52, 0x8f, 0xdd, 0x8d, 0xcd, 0x22, 0x48, 0x6e, 0x8d, 0x47, 0xfe, 0xbd, 0x66, 0x73, 0x2d, 0xa5, 0x95, 0x13, 0xe8, 0x5d, 0xce, 0xc2, 0xa7, 0xa5, 0xc0, 0xf5, 0x98, 0x78, 0xf5, 0x7d, 0x1a, 0x19, 0x8b, 0xd2, 0xbd, 0x7f, 0xe3, 0x07, 0xef, 0x57, 0x20, 0xd4, 0xbd, 0x4e, 0x29, 0xfc, 0xf6, 0x3c, 0x5e, 0xc8, 0x99, 0xa5, 0xf5, 0xf6, 0x26, 0x98, 0x6a, 0x1d, 0x83, 0x47, 0x52, 0x7e, 0x71, 0x53, 0xa5, 0x93, 0x95, 0x57, 0xf5, 0x12, 0x0f, 0x03, 0xc4, 0xdf, 0x30, 0x6d, 0x9d, 0x1f, 0x3a, 0x09, 0x03, 0xcc, 0x54, 0xf1, 0xea, 0xd8, 0x67, 0x30, 0x61, 0x2a, 0xa1, 0x85, 0xce, 0x62, 0x97, 0xb8, 0x90, 0x87, 0x5b, 0x34, 0x55, 0x59, 0xe8, 0x6d, 0x5b, 0x11, 0x3c, 0x0b, 0xa0, 0x3c, 0x4d, 0x15, 0x8a, 0xe8, 0x99, 0xda, 0x37, 0xce, 0xf9, 0xd2, 0x47, 0xf1, 0x84, 0xcf, 0x97, 0x1f, 0x50, 0x57, 0x5c, 0xa0, 0x74, 0x2c, 0x97, 0x83, 0x92, 0x1d, 0x2d, 0x2f, 0x00, 0xcd, 0xa9, 0xb9, 0x77, 0xad, 0x84, 0xe1, 0xaa, 0x4a, 0xdb, 0x9f, 0x83, 0x8a, 0x43, 0xc3, 0xb7, 0xe2, 0x94, 0xb1, 0xe6, 0xc5, 0x7d, 0xa9, 0x50, 0x46, 0x78, 0x1a, 0x4d, 0xcf, 0xad, 0x3f, 0x88, 0x3a, 0x30, 0x7c, 0x63, 0x9a, 0x2f, 0xb8, 0x86, 0x6f, 0xff, 0x55, 0x90, 0xa7, 0x5d, 0xd7, 0x92, 0xee, 0xe9, 0x2e, 0x39, 0xa6, 0x4c, 0x98, 0xeb, 0xa8, 0xd6, 0x06, 0xd5, 0xd3, 0x2c, 0xe1, 0x6d, 0xe0, 0xaa, 0x82, 0x4f, 0xac, 0x81, 0xa2, 0xd3, 0xdf, 0x3e, 0xf6, 0x2d, 0x0f, 0x3b, 0x93, 0x1c, 0xc4, 0xdc, 0x82, 0x13, 0xf1, 0xc4, 0x97, 0x56, 0xcc, 0x64, 0xeb, 0xf9, 0x40, 0xb5, 0x90, 0x5a, 0xc7, 0x05, 0xed, 0xa4, 0xc6, 0x53, 0x62, 0xe0, 0x3d, 0xc2, 0xde, 0xc6, 0xaf, 0xbf, 0x47, 0x8e, 0x51, 0x51, 0x3e, 0x74, 0xe8, 0x4a, 0xc6, 0x88, 0xfc, 0xcd, 0xcb, 0x19, 0xa4, 0x59, 0x4e, 0x72, 0x15, 0x73, 0xa9, 0x32, 0xa3, 0x48, 0x94, 0xd7, 0x4a, 0x4b, 0xbf, 0xec, 0x0a, 0x1f, 0xef, 0x00, 0x63, 0x4e, 0xf6, 0x12, 0x8f, 0x27, 0x97, 0x59, 0x93, 0xdd, 0xb8, 0xaa, 0xca, 0xe9, 0x23, 0x83, 0x68, 0x8f, 0x67, 0x47, 0xbd, 0xa4, 0x0f, 0x69, 0x37, 0xdc, 0xa2, 0xae, 0x4d, 0x2c, 0xcb, 0x9c, 0xb9, 0x4f, 0x56, 0xf9, 0x1e, 0xd5, 0x04, 0x55, 0xfc, 0x11, 0xfe, 0x3e, 0xd8, 0x72, 0x22, 0x03, 0xc7, 0x2d, 0x0d, 0x56, 0x19, 0xf0, 0x4b, 0xa8, 0x10, 0x2d, 0xc6, 0xed, 0xfe, 0xbe, 0xe1, 0x2d, 0x23, 0x2f, 0x5c, 0x56, 0x95, 0xde, 0xdd, 0x1b, 0x25, 0x71, 0x64, 0xfc, 0x69, 0x29, 0xf5, 0x21, 0x84, 0xb4, 0x6f, 0xab, 0x3f, 0x09, 0xdf, 0x7c, 0xd4, 0xc1, 0x11, 0x86, 0xd3, 0x8b, 0xaa, 0xfd, 0x71, 0xcf, 0xb7, 0x74, 0x81, 0xa9, 0x9e, 0xae, 0xa4, 0x8b, 0x95, 0x0d, 0x9b, 0x29, 0x63, 0xd6, 0x76, 0x08, 0xa5, 0xff, 0x6c, 0x20, 0x46, 0x4e, 0xba, 0xac, 0xa9, 0xa6, 0x68, 0xa0, 0x1a, 0xdb, 0x09, 0x38, 0x02, 0x45, 0xf9, 0xf0, 0x5d, 0x0e, 0x4a, 0xaf, 0x34, 0x40, 0x53, 0x60, 0x61, 0x91, 0x65, 0xcd, 0xb5, 0xe2, 0x5b, 0xee, 0xa4, 0x88, 0x16, 0x55, 0xbf, 0xf2, 0x82, 0x77, 0x79, 0x54, 0x2b, 0x0d, 0xec, 0xc3, 0x4b, 0xab, 0xec, 0x16, 0xd7, 0x48, 0xb8, 0x4b, 0x25, 0x79, 0xa9, 0x94, 0x1e, 0xb3, 0x22, 0xaf, 0x56, 0x10, 0x9d, 0x8f, 0x3c, 0x54, 0x65, 0xf0, 0xf4, 0xd7, 0x82, 0x21, 0x39, 0x0f, 0x42, 0x1e, 0xc9, 0xfb, 0x7f, 0x4a, 0xa1, 0x36, 0x92, 0xcd, 0x40, 0x00, 0xa2, 0xda, 0xe3, 0x4b, 0x42, 0xad, 0x00, 0xf0, 0x56, 0x37, 0x7a, 0x88, 0xfc, 0x2f, 0x88, 0xde, 0x56, 0x28, 0xe8, 0x22, 0x75, 0x38, 0x41, 0x32, 0xd5, 0x09, 0xdf, 0x75, 0x70, 0x11, 0x3c, 0x00, 0x41, 0x89, 0x8f, 0xb8, 0xda, 0xbc, 0x5a, 0x21, 0x0a, 0x3d, 0x21, 0xd5, 0x1c, 0x03, 0x73, 0x60, 0x12, 0xfc, 0x5e, 0x2a, 0x1f, 0xb7, 0x27, 0x11, 0xd8, 0x8b, 0x73, 0x1b, 0x46, 0xe1, 0xaa, 0xcc, 0x16, 0xa9, 0x04, 0xa0, 0xde, 0xbe, 0x17, 0xc8, 0x53, 0xd9, 0xcf, 0xf0, 0x93, 0x1d, 0x54, 0x9b, 0x38, 0x40, 0x23, 0xff, 0xc8, 0x86, 0xb2, 0xae, 0xd0, 0xb8, 0x25, 0xc2, 0xf5, 0x05, 0xcd, 0x68, 0xa2, 0x11, 0xcd, 0x8b, 0xe4, 0x00, 0xa9, 0xaa, 0x90, 0x31, 0x70, 0x8d, 0x4f, 0x6b, 0x48, 0x3c, 0x02, 0x76, 0x70, 0x4a, 0x91, 0xca, 0x13, 0x59, 0xbb, 0x7a, 0x87, 0x30, 0xaf, 0xbe, 0x71, 0x26, 0xa3, 0x70, 0xfa, 0xe2, 0x73, 0x41, 0xac, 0xc0, 0x5a, 0xf2, 0x7b, 0xa2, 0xaa, 0xbb, 0xb5, 0x04, 0x51, 0x36, 0x3c, 0x7f, 0x14, 0x69, 0x61, 0x99, 0xbd, 0xde, 0xb1, 0xfa, 0xa1, 0xf0, 0x1a, 0x10, 0x76, 0x2e, 0xce, 0x86, 0x2d, 0x91, 0x51, 0x76, 0x53, 0xeb, 0x89, 0x6e, 0xf2, 0x55, 0x31, 0x40, 0x84, 0x91, 0x15, 0xcc, 0x3d, 0x2b, 0xf8, 0xd1, 0x9a, 0x33, 0xa2, 0x60, 0x17, 0x1c, 0xa0, 0x73, 0x1f, 0xe9, 0x1d, 0x28, 0xa4, 0xc6, 0xd3, 0xbd, 0x0c, 0x82, 0xc0, 0xad, 0xbc, 0xf8, 0x17, 0x8d, 0x07, 0x5c, 0x3d, 0x78, 0x65, 0xb0, 0x67, 0x69, 0x8f, 0xdb, 0xbb, 0x48, 0x93, 0x26, 0x00, 0x81, 0xa7, 0x56, 0xb1, 0xec, 0xf9, 0xfe, 0x6f, 0xd6, 0x47, 0xa6, 0x0f, 0x57, 0xfb, 0x7d, 0x52, 0x2a, 0x73, 0xdc, 0x44, 0x25, 0x4b, 0x52, 0x90, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x44, 0x22, 0x38, 0x56, 0x12, 0x66, 0x2a, 0x86, 0x62, 0x1e, 0x69, 0xb2, 0x00, 0x01, 0x09, 0x0a, 0xf8, 0x53, 0x35, 0x2a, 0xd5, 0xc7, 0x8e, 0x2f, 0xdd, 0xc0, 0x53, 0xbe, 0x03, 0xea, 0x3f, 0x96, 0xbe, 0xe9, 0x75, 0xac, 0xf3, 0xda, 0xd1, 0x24, 0xed, 0xda, 0x19, 0x6d, 0xf3, 0xad, 0xb8, 0xa9, 0xc4, 0xe6, 0xde, 0xd9, 0x9c, 0xc6, 0x58, 0xcf, 0xe0, 0x17, 0xc5, 0x8e, 0xd9, 0x49, 0x65, 0x41, 0x1b, 0xb4, 0x1f, 0x79, 0xef, 0x0a, 0x37, 0x83, 0x1f, 0xf5, 0x08, 0x60, 0x31, 0xe1, 0x87, 0xfd, 0x04, 0x06, 0xe5, 0xe2, 0x8c, 0xc2, 0x02, 0xdc, 0x6b, 0xe0, 0xf4, 0x4a, 0x7b, 0x36, 0x4e, 0xfc, 0xeb, 0xc6, 0x5f, 0xc0, 0xaa, 0x8c, 0x19, 0xd4, 0xad, 0x90, 0xd3, 0xad, 0xbe, 0xc3, 0xef, 0x0c, 0xed, 0xfa, 0x26, 0xac, 0xcd, 0x04, 0x24, 0x3a, 0xa0, 0x23, 0x89, 0xa0, 0x13, 0x59, 0xdf, 0xed, 0x6d, 0x91, 0xa1, 0x9e, 0xd7, 0x8e, 0xaa, 0xf4, 0xc3, 0xd5, 0xb2, 0xa2, 0xe4, 0xab, 0xcf, 0x92, 0xab, 0x69, 0x16, 0xa7, 0x01, 0x26, 0x8f, 0x1c, 0xa5, 0x4d, 0x4f, 0x39, 0x1e, 0x46, 0x16, 0xa9, 0x59, 0x20, 0xf0, 0xb4, 0x14, 0x14, 0xe2, 0x75, 0x59, 0x50, 0x31, 0x30, 0x57, 0x91, 0x66, 0x76, 0xdd, 0xef, 0xe2, 0xb2, 0xb3, 0xbe, 0x0c, 0x5b, 0x06, 0xf5, 0xd7, 0x33, 0x83, 0x3a, 0x5b, 0x33, 0x94, 0x6b, 0x88, 0xbf, 0x87, 0xe7, 0xbb, 0xd1, 0x7d, 0xc3, 0xaf, 0x02, 0xd7, 0x96, 0xfb, 0x5b, 0xe9, 0xb9, 0x98, 0xea, 0x95, 0xda, 0x2e, 0x45, 0x05, 0x0d, 0x2c, 0x76, 0x67, 0xc7, 0x5e, 0x7a, 0x89, 0x00, 0x5a, 0xb7, 0x34, 0x1f, 0xb6, 0x1c, 0xa0, 0xe1, 0x1c, 0x58, 0xc9, 0x70, 0xc2, 0xf5, 0xb0, 0x5b, 0x2e, 0x5d, 0xf2, 0x07, 0x02, 0xed, 0xf1, 0x71, 0x87, 0x83, 0x56, 0x24, 0xf0, 0x9d, 0x74, 0x2e, 0x10, 0x8e, 0x60, 0x3b, 0x14, 0xde, 0xa1, 0xae, 0xbf, 0x96, 0x30, 0x1d, 0x3a, 0x3e, 0xaa, 0x65, 0x37, 0x31, 0x5b, 0x98, 0x82, 0x80, 0xc3, 0x59, 0x82, 0x61, 0xfd, 0xfa, 0x1f, 0x01, 0x85, 0x97, 0x16, 0x19, 0x73, 0xaf, 0x45, 0x16, 0x67, 0x62, 0x2b, 0x46, 0x15, 0x50, 0x9d, 0xc7, 0x86, 0x7a, 0xd5, 0xc2, 0x2a, 0x7d, 0x52, 0x4b, 0x8f, 0x86, 0x73, 0x30, 0x2c, 0x0d, 0xdc, 0x5a, 0x99, 0xb3, 0x8e, 0x82, 0xdf, 0x1a, 0x2e, 0x43, 0xa8, 0x54, 0xea, 0x7f, 0x78, 0x10, 0x9c, 0xe0, 0x81, 0x0c, 0xac, 0x45, 0x7c, 0x15, 0xaa, 0x9f, 0x7a, 0xca, 0x9d, 0x88, 0x08, 0xe6, 0xfa, 0xf2, 0xe1, 0xb5, 0xdd, 0x90, 0x0e, 0x4e, 0xb5, 0xf9, 0xbc, 0x7c, 0x57, 0xeb, 0xd4, 0x69, 0x28, 0xef, 0x0e, 0xcd, 0x5f, 0x73, 0xa8, 0xb6, 0x49, 0xf9, 0xfc, 0x55, 0x20, 0xa3, 0x0b, 0x72, 0xfd, 0x69, 0x4f, 0x6b, 0x99, 0x1d, 0x0f, 0xf9, 0xd7, 0x95, 0x82, 0x7a, 0x1c, 0x34, 0x21, 0x49, 0xbe, 0x26, 0x9b, 0x48, 0x5f, 0x1e, 0x92, 0x76, 0x93, 0xce, 0x4e, 0xbc, 0x82, 0xe7, 0x19, 0x62, 0xf2, 0x3d, 0x76, 0x1e, 0x64, 0xb2, 0xd5, 0xaa, 0xc8, 0x35, 0x76, 0xaf, 0x65, 0xb4, 0xce, 0xe5, 0x76, 0xc9, 0xce, 0xc8, 0x5c, 0xa4, 0x8d, 0xd2, 0x49, 0xac, 0xae, 0x97, 0xdd, 0x86, 0x4d, 0xe8, 0x6c, 0x70, 0x7e, 0x1b, 0x2f, 0xea, 0xe7, 0x3c, 0xdf, 0x17, 0x92, 0x20, 0x88, 0x00, 0xa5, 0x61, 0xc6, 0x4c, 0x12, 0xf9, 0x43, 0xa8, 0xee, 0x6d, 0x15, 0x69, 0xd6, 0x68, 0x9a, 0xbb, 0xcf, 0x07, 0x0b, 0x17, 0x00, 0x11, 0xc0, 0x95, 0x6e, 0xce, 0x69, 0x35, 0x9b, 0xd6, 0x48, 0xc4, 0xf9, 0xd3, 0x35, 0x3d, 0xb9, 0x17, 0xae, 0x31, 0x28, 0xf2, 0xc9, 0xf8, 0xde, 0xbd, 0x40, 0x29, 0x03, 0xc8, 0x0f, 0x7a, 0xe9, 0x06, 0x86, 0x33, 0x9f, 0x09, 0x2d, 0xa3, 0x00, 0x8b, 0x06, 0x4c, 0x3d, 0xde, 0x45, 0x39, 0xd5, 0xc8, 0xa6, 0x4d, 0x23, 0x00, 0x46, 0x82, 0xea, 0x54, 0x69, 0x59, 0xa0, 0xcb, 0x09, 0x85, 0x42, 0x83, 0x7c, 0xab, 0x6e, 0xab, 0xf9, 0x95, 0x6f, 0x1c, 0xb5, 0x59, 0x28, 0x63, 0xed, 0x2c, 0xae, 0x55, 0x80, 0xce, 0x67, 0x2f, 0xf0, 0xff, 0x42, 0x48, 0x43, 0x79, 0x32, 0xbb, 0x4d, 0x66, 0x89, 0x95, 0xcd, 0x83, 0x8f, 0x02, 0xa2, 0x2f, 0xe9, 0xea, 0x06, 0x59, 0x6f, 0x24, 0xa6, 0xe0, 0xd7, 0x23, 0x03, 0x5e, 0xe1, 0x23, 0x10, 0xee, 0x65, 0x94, 0x49, 0x88, 0xf5, 0xf4, 0x0b, 0x6a, 0x12, 0xab, 0xe7, 0xe0, 0x7d, 0x34, 0xa0, 0xcd, 0xfc, 0x39, 0x6a, 0xb3, 0xc7, 0x55, 0xe6, 0xe6, 0x2c, 0xc6, 0x38, 0xec, 0x13, 0xf8, 0x77, 0x27, 0x29, 0xad, 0x64, 0x5c, 0x76, 0x6a, 0x71, 0x62, 0xf8, 0x19, 0x92, 0x78, 0xba, 0x96, 0x1d, 0x0c, 0x39, 0x66, 0xe5, 0x3e, 0x71, 0x01, 0x96, 0x3a, 0x29, 0x6e, 0x9d, 0x82, 0x22, 0x58, 0x35, 0xa6, 0xef, 0xb3, 0x88, 0xbe, 0x34, 0xca, 0x44, 0xdb, 0x8f, 0x4b, 0xcf, 0x6d, 0xff, 0x5d, 0x5f, 0xa0, 0x27, 0x52, 0x8c, 0x5d, 0xd0, 0x50, 0xf4, 0xd2, 0x42, 0x14, 0x98, 0xbd, 0xdb, 0x36, 0xa1, 0x5b, 0x86, 0x60, 0x4a, 0x37, 0x0f, 0x80, 0xf0, 0x47, 0xb6, 0x79, 0xff, 0xe7, 0xd3, 0xec, 0x13, 0x0c, 0x99, 0x97, 0xd2, 0x3d, 0x85, 0x51, 0x28, 0xe4, 0x89, 0xa4, 0x3f, 0x2a, 0x1c, 0xda, 0x06, 0x1b, 0xf9, 0xc1, 0x8c, 0xaa, 0x50, 0x50, 0x43, 0x6e, 0x4f, 0xae, 0x13, 0x20, 0x6f, 0x1f, 0x60, 0xe1, 0xf6, 0x65, 0x94, 0xf2, 0x67, 0xbc, 0x0e, 0x1f, 0x6a, 0xb6, 0xf3, 0xcc, 0x8f, 0x81, 0x7b, 0xab, 0xfb, 0xbb, 0x24, 0xf1, 0x00, 0x29, 0x01, 0xce, 0xfe, 0x79, 0xdc, 0x7b, 0xc8, 0xa5, 0x3a, 0x6f, 0x0d, 0x38, 0x06, 0x15, 0xe4, 0xc3, 0xb7, 0x91, 0xf8, 0xff, 0xc5, 0x2c, 0x20, 0x25, 0x2d, 0x8e, 0x5f, 0x5f, 0x7d, 0xc9, 0x10, 0x3b, 0x84, 0xf8, 0x2b, 0x91, 0x39, 0x96, 0x19, 0x8b, 0x0d, 0x15, 0x12, 0xff, 0xab, 0x25, 0xbf, 0xde, 0x48, 0x78, 0x3e, 0xb4, 0x8c, 0xa6, 0x2d, 0xf0, 0xa1, 0x9d, 0x17, 0xe7, 0x00, 0xfe, 0x86, 0x58, 0x01, 0x1f, 0x14, 0x9a, 0x84, 0xb1, 0x99, 0x79, 0xaa, 0xcb, 0x32, 0x77, 0x92, 0x0d, 0x4c, 0xa3, 0xf3, 0xd9, 0xf5, 0xbd, 0xb8, 0x54, 0x1e, 0x92, 0x76, 0x71, 0x26, 0x0d, 0xc9, 0x50, 0x32, 0xda, 0xeb, 0xbc, 0xd8, 0x22, 0x9d, 0xf5, 0xed, 0xa2, 0xa8, 0xe6, 0xc7, 0x7b, 0xf3, 0x99, 0xfd, 0xbf, 0x6d, 0xed, 0x79, 0xe8, 0xc4, 0xc8, 0xed, 0xbb, 0xb3, 0x92, 0xf7, 0x28, 0xb8, 0x0a, 0x38, 0x32, 0xfd, 0xa4, 0xbc, 0xd5, 0x6b, 0x8f, 0x37, 0x41, 0xd5, 0xab, 0x63, 0x59, 0xb4, 0xd4, 0x4f, 0x95, 0x25, 0xcc, 0x8d, 0x1b, 0x3b, 0xc2, 0x9f, 0xff, 0xa0, 0x52, 0xfd, 0x3b, 0x4e, 0x52, 0x31, 0x91, 0x86, 0xbd, 0xbf, 0xda, 0xc6, 0x1d, 0x87, 0x9c, 0x94, 0x9b, 0xe7, 0x4f, 0x23, 0x2c, 0x9d, 0xe0, 0x04, 0x18, 0x64, 0x30, 0x98, 0xe9, 0xcd, 0x1c, 0x1a, 0x93, 0xe7, 0x5e, 0x9b, 0x13, 0x23, 0x7b, 0xc1, 0x0c, 0xa1, 0xc3, 0x62, 0x49, 0x6b, 0x39, 0x12, 0x48, 0x70, 0xbe, 0xe5, 0xa7, 0xb8, 0xcf, 0x4e, 0xaa, 0x49, 0x91, 0xb8, 0x06, 0x7c, 0xf4, 0x63, 0xb0, 0x19, 0x52, 0x39, 0xbd, 0x4f, 0x5e, 0xf8, 0xf7, 0x60, 0x83, 0x49, 0x5c, 0x31, 0x7e, 0x91, 0x47, 0x3c, 0x98, 0x5c, 0x04, 0x86, 0x7d, 0xf1, 0x0f, 0xac, 0x29, 0xae, 0x5a, 0xaf, 0x25, 0x11, 0x3e, 0xf7, 0xd8, 0xed, 0x64, 0x70, 0xe9, 0x14, 0x8a, 0xfd, 0x28, 0xbe, 0x9a, 0x95, 0x64, 0x98, 0x56, 0x7f, 0x64, 0x49, 0x33, 0x4a, 0xa6, 0x94, 0xfc, 0x5a, 0xf7, 0xff, 0xe9, 0x26, 0x93, 0x85, 0xae, 0xe4, 0x05, 0x99, 0xf4, 0x6d, 0x95, 0x25, 0x0c, 0x79, 0xc4, 0x20, 0xc8, 0x81, 0xe3, 0x03, 0x76, 0x7e, 0xa1, 0xb6, 0xd1, 0x2c, 0xa6, 0x16, 0xbb, 0x95, 0xa9, 0xcd, 0x41, 0x47, 0xba, 0x7b, 0x76, 0xad, 0x4b, 0x06, 0xbb, 0x8e, 0x0a, 0x17, 0xec, 0xcc, 0x67, 0x89, 0x53, 0xd3, 0x22, 0x7f, 0x11, 0xc1, 0x09, 0x91, 0xd3, 0xf6, 0x0e, 0x5a, 0x09, 0x9e, 0x1b, 0x41, 0x2b, 0x80, 0x80, 0x2f, 0xfe, 0x65, 0xcd, 0x34, 0x9b, 0x91, 0x1e, 0x32, 0xce, 0xe2, 0x6d, 0xc5, 0xd5, 0xf8, 0x0c, 0xbd, 0x6a, 0xd7, 0x16, 0x32, 0x79, 0x75, 0xc5, 0x2d, 0xc4, 0xa1, 0x22, 0x9e, 0x52, 0xaa, 0x95, 0x2a, 0x2f, 0xa6, 0x0d, 0x93, 0x25, 0x52, 0x79, 0xf8, 0x32, 0x6a, 0xba, 0x03, 0x08, 0xdb, 0xf6, 0x3c, 0x96, 0x25, 0x85, 0x4b, 0x76, 0x71, 0x92, 0x29, 0x48, 0x29, 0x4f, 0xac, 0x53, 0xe2, 0xdf, 0x41, 0xf3, 0xc0, 0xb7, 0x19, 0x1b, 0x50, 0xa8, 0x3b, 0x45, 0x38, 0x9a, 0x35, 0x4a, 0x20, 0xf1, 0x21, 0x20, 0xb5, 0x96, 0x03, 0xe2, 0x9f, 0x02, 0xd9, 0x9c, 0xc4, 0x37, 0xd9, 0xd4, 0x95, 0x32, 0xfb, 0x6e, 0x74, 0xe8, 0xf7, 0x42, 0xcb, 0x57, 0xbb, 0x2c, 0xe8, 0x54, 0x9f, 0x6a, 0x65, 0xa2, 0x2a, 0x75, 0x2e, 0xb9, 0x47, 0x14, 0xcd, 0xd2, 0xb9, 0xa0, 0xff, 0x5b, 0x45, 0x26, 0x89, 0x7e, 0xf9, 0xac, 0xef, 0x42, 0xc0, 0xd8, 0x73, 0x35, 0x67, 0x32, 0x8e, 0xcc, 0x65, 0x79, 0xf8, 0x42, 0x07, 0xa1, 0x86, 0xd8, 0x22, 0xe6, 0x07, 0xda, 0x10, 0xc9, 0xfa, 0xf5, 0x1f, 0x21, 0xb7, 0xef, 0x4b, 0x7c, 0x17, 0x5c, 0x7b, 0x69, 0x8a, 0x22, 0x56, 0x2c, 0xb8, 0xb2, 0x7c, 0xc7, 0x1b, 0x7d, 0x00, 0x7d, 0x4b, 0xb8, 0xab, 0x17, 0x5f, 0xd9, 0xc2, 0x1b, 0x4d, 0xac, 0x9b, 0xa0, 0xbd, 0xc8, 0xd1, 0xf7, 0x52, 0xeb, 0xf7, 0x90, 0x66, 0x3c, 0x6d, 0x17, 0x7d, 0xee, 0x06, 0xbb, 0xbf, 0xb1, 0xed, 0x8a, 0x6b, 0x76, 0x74, 0x89, 0x7d, 0xc8, 0xc6, 0x8f, 0xfc, 0x5f, 0x68, 0xba, 0x70, 0x4c, 0x44, 0xa4, 0x4c, 0xb4, 0x0d, 0x7a, 0x3e, 0xb0, 0x46, 0x8e, 0xc9, 0x5c, 0xff, 0x26, 0x94, 0x71, 0x4c, 0x3b, 0xaa, 0xa4, 0x22, 0xbe, 0xaa, 0x28, 0x5b, 0xc6, 0x63, 0xac, 0xe0, 0x8c, 0x5f, 0xf8, 0xfe, 0xc7, 0x14, 0xb4, 0x56, 0xa9, 0xcc, 0xc2, 0x67, 0xd4, 0x38, 0x6d, 0x18, 0x99, 0x3f, 0xb4, 0x79, 0x9b, 0x02, 0x37, 0xa1, 0x67, 0x01, 0x07, 0xd3, 0x57, 0xee, 0x42, 0x95, 0xc4, 0xee, 0x02, 0x6c, 0x64, 0x07, 0xc5, 0x10, 0xc5, 0xc1, 0x42, 0xe6, 0x12, 0x84, 0xc1, 0xac, 0xc7, 0x99, 0xaa, 0x82, 0x4f, 0x61, 0xb3, 0x90, 0xa5, 0x49, 0xec, 0x0b, 0x3e, 0xf7, 0x18, 0xad, 0x2f, 0xc6, 0x61, 0x88, 0xc8, 0xc9, 0x8e, 0x7c, 0x6d, 0x8e, 0x07, 0x74, 0x0c, 0x03, 0x95, 0x8b, 0x47, 0x27, 0x37, 0xa7, 0xeb, 0xc6, 0xe2, 0x7d, 0x7e, 0x1c, 0x12, 0x48, 0xef, 0x60, 0x4a, 0xd5, 0x34, 0x21, 0x6e, 0xc0, 0x11, 0x8d, 0xec, 0x1e, 0x50, 0x91, 0x99, 0xce, 0x67, 0xa4, 0xf0, 0xcb, 0x25, 0x0e, 0x00, 0xca, 0xd7, 0xc6, 0x40, 0xaf, 0xd4, 0x35, 0xb4, 0xd8, 0x39, 0x27, 0xde, 0x05, 0x4f, 0xb1, 0xa4, 0x74, 0x92, 0x59, 0x03, 0xc1, 0xd9, 0x71, 0xfd, 0xf3, 0x2d, 0x3a, 0xf9, 0xf0, 0x67, 0xd3, 0xd0, 0x95, 0x2a, 0x3f, 0x76, 0xf9, 0x9f, 0x8e, 0x22, 0xd1, 0xb9, 0x11, 0x02, 0x42, 0x1a, 0x95, 0xb0, 0x4d, 0x58, 0x5e, 0x2f, 0x24, 0xbe, 0xbb, 0xb8, 0x43, 0xa3, 0xcd, 0xc8, 0x87, 0x3d, 0x4b, 0xeb, 0x80, 0x4a, 0x5e, 0x77, 0x0e, 0xec, 0x06, 0xc4, 0x31, 0x7b, 0x82, 0xa4, 0x69, 0x72, 0x94, 0x1d, 0xc5, 0x13, 0x85, 0x2a, 0x84, 0xeb, 0xd4, 0x2f, 0x8f, 0xcb, 0xcb, 0xa2, 0x3f, 0x48, 0xa4, 0xed, 0x59, 0x40, 0x57, 0xca, 0x8b, 0xe2, 0xa2, 0x10, 0x59, 0x92, 0x92, 0x4f, 0x29, 0x50, 0x2d, 0x52, 0xe1, 0xee, 0xdc, 0xbe, 0x05, 0x2a, 0x76, 0x7d, 0x38, 0x6c, 0x3e, 0x7f, 0xb7, 0x48, 0x57, 0x35, 0x75, 0xc4, 0x41, 0x93, 0x21, 0xca, 0x87, 0xd5, 0x14, 0x30, 0x95, 0x2b, 0x4f, 0xc3, 0x6c, 0xc3, 0xce, 0x78, 0xe5, 0x25, 0x8e, 0xfe, 0x6b, 0x09, 0xfa, 0xf3, 0xd3, 0x13, 0x22, 0x14, 0xfb, 0x95, 0x50, 0x15, 0x67, 0x09, 0x4d, 0xc8, 0x91, 0x4b, 0x82, 0xd3, 0x9b, 0xf3, 0x14, 0xd9, 0xfb, 0xb2, 0x1a, 0x03, 0x30, 0xff, 0x58, 0x7e, 0xce, 0x51, 0xda, 0xb2, 0xa2, 0xc9, 0x66, 0x18, 0x84, 0x04, 0xd3, 0xe0, 0x57, 0x60, 0x8b, 0xf8, 0x74, 0x15, 0x12, 0x1c, 0x41, 0x09, 0x16, 0x59, 0xf9, 0xe7, 0x3b, 0x89, 0xe3, 0x27, 0xa9, 0x3d, 0xcd, 0x08, 0xa8, 0xe3, 0x14, 0x49, 0x87, 0x61, 0x28, 0x8f, 0x54, 0x7f, 0xdb, 0x9d, 0x0f, 0x7c, 0xde, 0x1f, 0xa2, 0x3a, 0xc8, 0xad, 0x37, 0x0a, 0x60, 0x86, 0x3f, 0x81, 0xd9, 0x52, 0x21, 0x5b, 0x49, 0xae, 0xb5, 0x18, 0x03, 0x56, 0xba, 0x46, 0x7c, 0xcc, 0xc7, 0xec, 0xf2, 0x6a, 0x29, 0xc8, 0xcc, 0x64, 0xbe, 0x5a, 0x61, 0xda, 0xf0, 0x34, 0xcb, 0x95, 0xed, 0xf2, 0x23, 0xbd, 0x6c, 0x09, 0x79, 0x68, 0x00, 0x00, 0x01, 0x09 }; +constexpr AccessUnit AVC_MULTIPLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, true, 0, {}, { 0xf3, 0x15, 0xed, 0x63, 0x82, 0x7c, 0xb1, 0xdf, 0x38, 0x75, 0x9e, 0x5e, 0xb4, 0x5e, 0xe2, 0xf2, 0x65, 0x6a, 0x58, 0xb1 } }; +constexpr AccessUnit AVC_MULTIPLE_PACK_EXPECTED_AU_2 = { 0x64615f94, 0x3c0159b2, true, 0, {}, { 0xbe, 0x1d, 0x4f, 0x54, 0x72, 0x44, 0x77, 0xa3, 0x09, 0x5e, 0x3b, 0xea, 0xc2, 0x55, 0x07, 0xb5, 0xcd, 0x33, 0x28, 0x6d } }; +constexpr AccessUnit AVC_MULTIPLE_PACK_EXPECTED_AU_3 = { 0x15f90, 0x159b2, true, 0, {}, { 0x37, 0xdb, 0xa1, 0x63, 0x9e, 0x5a, 0xff, 0x92, 0xd0, 0x54, 0xbd, 0xc6, 0x69, 0x4f, 0x38, 0x89, 0xb8, 0x4b, 0x66, 0xfb } }; +constexpr AccessUnit AVC_MULTIPLE_PACK_EXPECTED_AU_4 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0x03, 0x31, 0xd3, 0xe2, 0xbb, 0x54, 0x06, 0xdb, 0xda, 0xe8, 0x0b, 0xfd, 0x96, 0x81, 0x66, 0x3f, 0x5c, 0x1b, 0xac, 0x69 } }; + +constexpr std::array M2V_MULTIPLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x01, 0x00, 0xb8, 0x7e, 0x91, 0xe4, 0x7b, 0x15, 0x52, 0x0c, 0x57, 0xe9, 0x3f, 0x05, 0x04, 0x5c, 0x02, 0x72, 0xde, 0x18, 0x8d, 0x08, 0x31, 0x03, 0x75, 0xa4, 0xfe, 0xfe, 0x0b, 0xda, 0xcb, 0x2f, 0x8d, 0xe9, 0xb3, 0xc0, 0x32, 0x65, 0x3e, 0xf8, 0x3d, 0x89, 0xd1, 0xe1, 0xcb, 0xe0, 0x8f, 0xad, 0x2e, 0x97, 0x22, 0x75, 0x3a, 0xcd, 0xcd, 0x6d, 0x5b, 0x4b, 0xff, 0x04, 0x9a, 0x4a, 0xfe, 0x8a, 0xc5, 0xd7, 0x10, 0x0a, 0xb1, 0x71, 0x50, 0x0b, 0x2f, 0x82, 0x31, 0x15, 0x66, 0xab, 0xe4, 0x4b, 0x25, 0x4f, 0x62, 0xf6, 0xe0, 0xab, 0x11, 0x11, 0x55, 0x65, 0x65, 0x79, 0x9b, 0xfb, 0xd8, 0x85, 0x6b, 0x53, 0x19, 0xc6, 0x1c, 0xfc, 0x24, 0x2d, 0x00, 0x9b, 0x57, 0xac, 0xd9, 0xfa, 0x7a, 0xe4, 0x60, 0x1f, 0x8a, 0xb8, 0x0c, 0x92, 0x85, 0x06, 0xe4, 0x34, 0xd7, 0xce, 0x80, 0x4b, 0x21, 0xf4, 0x5a, 0xea, 0x19, 0x4a, 0xb2, 0x0d, 0x25, 0x58, 0x07, 0x96, 0x58, 0xad, 0xde, 0x80, 0x5d, 0xa8, 0xff, 0x74, 0x8d, 0xaf, 0x1d, 0xd7, 0x90, 0x0e, 0x3d, 0xa1, 0x85, 0x1f, 0xf8, 0x1e, 0xf5, 0xc5, 0x35, 0x0d, 0x79, 0x50, 0xd4, 0x28, 0x5a, 0x8e, 0xae, 0xe3, 0x2f, 0xd1, 0x6b, 0xb7, 0xaf, 0x30, 0xa3, 0xf5, 0x00, 0xf6, 0xe5, 0x05, 0x8c, 0x5b, 0x3f, 0xb3, 0xb0, 0xb2, 0x35, 0x88, 0x65, 0x62, 0x3a, 0x7e, 0x20, 0x17, 0x61, 0x1f, 0x98, 0x10, 0x68, 0xf7, 0x8b, 0x29, 0x5a, 0x7e, 0x51, 0xe0, 0x09, 0x72, 0xde, 0xbc, 0xfd, 0x0b, 0xdf, 0x67, 0xa3, 0x8b, 0x4f, 0xe0, 0xa3, 0xfe, 0x33, 0x09, 0x38, 0x77, 0x97, 0x53, 0xa6, 0x11, 0x57, 0xb8, 0x5e, 0xed, 0x75, 0x1a, 0x66, 0xf4, 0x5f, 0x74, 0x13, 0x80, 0xb5, 0xfe, 0xce, 0x8c, 0x04, 0x32, 0x7e, 0xa1, 0x91, 0x39, 0x04, 0xcd, 0x4c, 0x2f, 0x59, 0x5d, 0xa6, 0xdb, 0x3d, 0xc6, 0x58, 0x66, 0x55, 0xca, 0x53, 0xdc, 0xc2, 0x3a, 0x07, 0xc0, 0xc2, 0x74, 0x7b, 0x28, 0xfc, 0xd0, 0xcd, 0x17, 0x03, 0xb3, 0x26, 0x8c, 0x9e, 0x60, 0x9c, 0x5b, 0x57, 0x3e, 0x44, 0x6f, 0xc0, 0x1f, 0xa7, 0xdd, 0xb7, 0xa0, 0x59, 0x3e, 0xd8, 0x25, 0x2d, 0xf1, 0x4e, 0x8a, 0x76, 0x86, 0x84, 0xe3, 0xf0, 0xec, 0x70, 0x87, 0xc2, 0x45, 0xc9, 0x3a, 0xd6, 0x4d, 0x4d, 0xc5, 0x18, 0x4b, 0x11, 0x29, 0x13, 0x05, 0x8e, 0x44, 0x90, 0xbb, 0x8c, 0xa6, 0x77, 0xeb, 0x64, 0x0c, 0xcf, 0x06, 0x14, 0x72, 0xeb, 0xfa, 0xca, 0x63, 0xc5, 0x7e, 0x9b, 0x7f, 0x83, 0x7c, 0x18, 0xd8, 0x21, 0xf0, 0xb7, 0xb6, 0x51, 0xf3, 0x7d, 0x4c, 0xb4, 0x38, 0xb4, 0xca, 0x25, 0xa9, 0x16, 0x5f, 0x48, 0xea, 0xef, 0x83, 0x92, 0x07, 0xc5, 0xec, 0x08, 0xa2, 0x39, 0x26, 0x98, 0xbb, 0x1c, 0x02, 0xba, 0xf8, 0xe4, 0xe4, 0xbb, 0xb3, 0x77, 0x19, 0x17, 0xbf, 0x97, 0xb2, 0x9a, 0x1f, 0x20, 0xea, 0xdc, 0xc5, 0x4e, 0xaa, 0xf5, 0x2e, 0x0e, 0xb8, 0x8a, 0x73, 0x8d, 0xab, 0xc3, 0x2b, 0x05, 0xe4, 0x43, 0xed, 0xbc, 0xc0, 0xd2, 0x18, 0x96, 0x1f, 0x65, 0xd0, 0xc7, 0x2b, 0x59, 0x12, 0xf3, 0x69, 0x48, 0x32, 0x47, 0xc5, 0x58, 0x8c, 0x00, 0x60, 0x8f, 0xb4, 0x29, 0x0b, 0xf0, 0x03, 0x88, 0x88, 0xfc, 0xbe, 0x7b, 0x80, 0x7d, 0x9f, 0x99, 0xae, 0xa4, 0xf2, 0x2d, 0xc0, 0x1a, 0xb3, 0xa5, 0x79, 0x7e, 0x16, 0x72, 0x74, 0xf4, 0x92, 0x77, 0x19, 0xbc, 0xfc, 0x8d, 0xf1, 0x0c, 0xf8, 0x32, 0xfc, 0xf3, 0xcf, 0x93, 0xb8, 0xc0, 0x82, 0x4e, 0x0e, 0x2f, 0x5c, 0xf2, 0x88, 0x38, 0x39, 0x0e, 0x30, 0x00, 0x7e, 0xcd, 0x84, 0xd1, 0xc6, 0x8b, 0xdd, 0x8c, 0x12, 0x0a, 0x81, 0x27, 0x23, 0x3f, 0xd4, 0x78, 0x99, 0xec, 0x53, 0xa1, 0xad, 0x71, 0x8a, 0xf8, 0x73, 0xf7, 0xb6, 0x6c, 0x07, 0xa9, 0xc0, 0xa7, 0x59, 0x70, 0x4f, 0xbd, 0x00, 0x7f, 0x65, 0xf3, 0x7f, 0x6a, 0xec, 0xe9, 0xec, 0x66, 0xce, 0x86, 0x26, 0x90, 0x01, 0x87, 0x0f, 0x4a, 0xfe, 0x13, 0xbc, 0x1b, 0xe5, 0xb2, 0x82, 0x1c, 0x5c, 0xf0, 0xcc, 0x89, 0x69, 0x0e, 0xda, 0x58, 0x2b, 0x4b, 0x42, 0x87, 0xcc, 0xd1, 0x6f, 0xc1, 0x7a, 0x80, 0x63, 0x2a, 0xc3, 0x28, 0x4c, 0xae, 0xf0, 0x77, 0x9a, 0xa9, 0xc2, 0x8b, 0x0d, 0xa3, 0xfc, 0x7e, 0xb9, 0x99, 0xa6, 0x2c, 0x9a, 0x4e, 0x10, 0x77, 0x20, 0xb5, 0x7c, 0x2d, 0xa6, 0x02, 0xee, 0x08, 0x6e, 0x4f, 0x5b, 0x8b, 0x9e, 0x35, 0xc3, 0x64, 0xdf, 0xf7, 0x7f, 0xc0, 0x57, 0xf4, 0x42, 0x0f, 0x16, 0xdd, 0xb2, 0xf5, 0x9c, 0x04, 0x0c, 0x97, 0x64, 0x48, 0x94, 0x0c, 0xc4, 0xfe, 0x9e, 0xce, 0x78, 0x0c, 0xda, 0xcd, 0x9d, 0x3d, 0x38, 0xd6, 0x83, 0x5d, 0x36, 0x52, 0x61, 0x86, 0x19, 0x23, 0x39, 0x51, 0x9f, 0xb5, 0x95, 0x5f, 0x1a, 0x6d, 0x36, 0x68, 0x3a, 0x01, 0xe3, 0x5f, 0x6e, 0x73, 0x0f, 0xf4, 0xb6, 0xc2, 0x3f, 0x14, 0x66, 0xa3, 0x77, 0x07, 0x6b, 0xed, 0x76, 0x85, 0x81, 0x0b, 0x16, 0x48, 0x34, 0x68, 0x8d, 0x50, 0xe7, 0x91, 0x85, 0x61, 0xff, 0x30, 0x37, 0x1f, 0x2f, 0x15, 0x9c, 0xa6, 0xa8, 0xcb, 0xfc, 0x0a, 0x92, 0x1a, 0xd8, 0x4c, 0x09, 0xa5, 0x40, 0x65, 0xb6, 0xbd, 0x2b, 0xca, 0xce, 0x72, 0xac, 0xb8, 0xe8, 0x46, 0xd5, 0x9a, 0x7e, 0xf1, 0x5f, 0x60, 0x2c, 0x05, 0xce, 0xb5, 0x71, 0xdd, 0xb8, 0x2f, 0xa8, 0xaa, 0x0e, 0xea, 0x37, 0x86, 0xf5, 0x06, 0x73, 0xae, 0x24, 0x9c, 0x02, 0x9d, 0x36, 0xb7, 0x0b, 0x3f, 0x5e, 0x0f, 0x80, 0xa0, 0xb1, 0xcb, 0x2a, 0x56, 0xe6, 0xa6, 0xac, 0x57, 0xe0, 0x92, 0x8d, 0xee, 0x28, 0x2b, 0x8f, 0xd3, 0x09, 0x67, 0xac, 0x37, 0xa9, 0x2d, 0x5b, 0x67, 0x38, 0xf0, 0xe0, 0x87, 0x8c, 0x6f, 0x05, 0x12, 0xff, 0xd4, 0x72, 0x02, 0x8c, 0x08, 0x05, 0xc3, 0x50, 0x3b, 0xaf, 0x97, 0x5f, 0xa3, 0xa9, 0x39, 0x63, 0xfa, 0x5a, 0x31, 0x32, 0x08, 0xbb, 0x5b, 0xf6, 0xd0, 0xd4, 0xd4, 0xb3, 0xe3, 0xa1, 0x52, 0x74, 0xd7, 0x46, 0xfe, 0x00, 0x88, 0x09, 0xaa, 0xed, 0xa3, 0x7b, 0x6b, 0x88, 0x72, 0x75, 0xf1, 0xdb, 0xc7, 0x8e, 0x49, 0x45, 0xd1, 0x02, 0x8f, 0xc0, 0x52, 0x33, 0x15, 0x9b, 0xd8, 0x5a, 0xb1, 0xf1, 0x61, 0x2a, 0x4a, 0xb0, 0x59, 0x0c, 0x5d, 0x08, 0xa7, 0xc6, 0x62, 0x4f, 0xfe, 0x5d, 0xa9, 0x54, 0xb5, 0xcd, 0xb8, 0x49, 0x7f, 0x62, 0x06, 0xa4, 0x38, 0x90, 0x0f, 0x39, 0x06, 0x3a, 0x52, 0xf1, 0x69, 0xd2, 0x98, 0x23, 0x0c, 0xbd, 0x72, 0x98, 0xf5, 0xf1, 0x12, 0x5e, 0xdf, 0xfc, 0x22, 0x00, 0x9e, 0x4b, 0xc8, 0x12, 0xf3, 0xe4, 0x84, 0xb0, 0x4e, 0xd3, 0xf2, 0xec, 0xf0, 0x11, 0x44, 0x0f, 0xb3, 0x8f, 0x12, 0xbf, 0x92, 0xa9, 0x51, 0x00, 0x00, 0x1b, 0x07, 0xf4, 0x2d, 0xa0, 0xce, 0xa3, 0x2b, 0xa0, 0x4c, 0xa7, 0x07, 0xa2, 0x05, 0x0e, 0xca, 0xd0, 0x1e, 0x85, 0xb9, 0x10, 0x6b, 0x4f, 0x21, 0x7e, 0x11, 0x46, 0x12, 0x64, 0xde, 0x78, 0x02, 0x97, 0xee, 0xe6, 0xfa, 0x1b, 0xa7, 0xe7, 0x77, 0x91, 0x11, 0x07, 0xfe, 0x06, 0x10, 0x8c, 0x8f, 0x4c, 0x3c, 0x72, 0x9d, 0x14, 0x57, 0xb1, 0x93, 0xbf, 0xad, 0x8b, 0xb1, 0x27, 0xfa, 0x0d, 0xdb, 0xae, 0xac, 0x76, 0xd8, 0xed, 0x76, 0x56, 0xb3, 0x2c, 0x5f, 0x56, 0x69, 0x06, 0x27, 0xaa, 0x24, 0x13, 0x71, 0x8d, 0x41, 0x93, 0x4f, 0x70, 0xa1, 0x3b, 0x16, 0xc6, 0xeb, 0x06, 0x20, 0x06, 0x7a, 0xb2, 0x9e, 0x48, 0x3c, 0x41, 0x94, 0xf7, 0x84, 0x41, 0x7d, 0x35, 0x58, 0xfe, 0x26, 0x8c, 0xb5, 0xd3, 0x69, 0x0f, 0x59, 0xf6, 0xf6, 0x36, 0x35, 0xb6, 0xa8, 0x9e, 0xa5, 0xbf, 0xfe, 0x7f, 0x31, 0x92, 0x28, 0x8c, 0xc3, 0x8b, 0x4a, 0x3b, 0x25, 0x9b, 0x2d, 0xdf, 0xc5, 0xfd, 0xa5, 0x45, 0x35, 0xfa, 0x7a, 0xec, 0x30, 0x4d, 0x9e, 0x10, 0x69, 0xf3, 0xa0, 0x10, 0x73, 0x67, 0x94, 0xc9, 0x63, 0x24, 0xe5, 0x46, 0x71, 0x48, 0x29, 0x48, 0x0e, 0xfa, 0xa6, 0xa8, 0xa6, 0x4b, 0x9f, 0xe6, 0x3c, 0xb5, 0x76, 0xa3, 0x4a, 0xc0, 0xeb, 0x34, 0x8f, 0x13, 0x44, 0x5e, 0x83, 0x44, 0x72, 0xd3, 0x99, 0xa1, 0x34, 0x35, 0x9c, 0x3b, 0xfd, 0xb4, 0x6c, 0x3b, 0x58, 0xf7, 0x86, 0xc0, 0x22, 0xa3, 0x02, 0xfc, 0xef, 0x4c, 0x3a, 0xf3, 0x89, 0x5f, 0x31, 0x7f, 0x9f, 0x5d, 0xab, 0x07, 0x0e, 0x8a, 0x0a, 0xed, 0x5d, 0x94, 0x90, 0xea, 0xf5, 0x28, 0x1c, 0x96, 0x3d, 0xd0, 0x3e, 0x5a, 0x6a, 0xef, 0xb4, 0x81, 0x06, 0x3c, 0x29, 0xfc, 0x1d, 0x5c, 0x79, 0x43, 0x91, 0xe0, 0xec, 0x93, 0xdc, 0xa4, 0xed, 0x66, 0xe0, 0xb9, 0xc0, 0x6a, 0x3e, 0x3b, 0x64, 0x39, 0x37, 0x8d, 0xb4, 0x0a, 0xdf, 0x46, 0x0d, 0x3f, 0xdb, 0x1a, 0x37, 0xd5, 0x96, 0x7b, 0x70, 0x09, 0x6d, 0xf8, 0xa5, 0xe0, 0xe8, 0x6e, 0xf3, 0xa4, 0x68, 0x16, 0xa3, 0xc5, 0x3a, 0x06, 0xc5, 0x31, 0x38, 0xa5, 0x15, 0xf2, 0x4d, 0xc3, 0x9f, 0x50, 0x04, 0xc9, 0x67, 0x90, 0x1a, 0x13, 0x8f, 0x48, 0x05, 0xec, 0x1d, 0x71, 0xf2, 0xae, 0x4a, 0x2b, 0x7f, 0x50, 0xef, 0x57, 0x7d, 0x68, 0x31, 0x7f, 0xf5, 0xfd, 0x9f, 0xf0, 0x74, 0x7c, 0x3b, 0xfb, 0x39, 0x33, 0xe9, 0xbb, 0x49, 0xb9, 0x01, 0xd7, 0x32, 0xb5, 0x64, 0x23, 0xb1, 0x83, 0x91, 0x2a, 0xbd, 0x4e, 0xdf, 0x84, 0x3a, 0x55, 0x62, 0x66, 0x9d, 0x16, 0x51, 0xd8, 0x2d, 0xf8, 0x86, 0x21, 0x15, 0x09, 0x00, 0xf1, 0xd2, 0x9f, 0x3b, 0x17, 0x25, 0x60, 0x5f, 0xbe, 0x6e, 0x40, 0x40, 0x58, 0xa3, 0xe3, 0x0b, 0x64, 0x34, 0x00, 0x00, 0x01, 0xba, 0xea, 0xd0, 0xb3, 0x4e, 0x2a, 0x24, 0x44, 0x53, 0xdd, 0x8b, 0x0d, 0x11, 0xf4, 0xb1, 0x4e, 0xe4, 0x56, 0x1b, 0xc1, 0x19, 0x97, 0xca, 0xf7, 0x5f, 0x52, 0x60, 0x22, 0xac, 0xa3, 0x25, 0xcd, 0xbd, 0x5c, 0xb7, 0x93, 0xaa, 0x44, 0x18, 0x08, 0xc8, 0x87, 0xa6, 0x5a, 0x21, 0x2d, 0x37, 0x8c, 0x92, 0xb4, 0xe8, 0xf2, 0x18, 0x97, 0x04, 0x71, 0x9a, 0xda, 0x51, 0x77, 0x05, 0xd7, 0x24, 0xdb, 0x9d, 0x32, 0x46, 0xbf, 0xcd, 0x6a, 0xbd, 0x05, 0xac, 0x29, 0xca, 0x20, 0x95, 0xc9, 0x6a, 0x1f, 0x02, 0x76, 0x11, 0xf6, 0x62, 0x7b, 0x09, 0x85, 0xca, 0x2e, 0x75, 0x09, 0xf9, 0x62, 0x00, 0x3f, 0x9f, 0x2d, 0xbc, 0x8f, 0x36, 0x45, 0xe0, 0x8d, 0xd1, 0xa7, 0xac, 0x32, 0x90, 0x73, 0xde, 0xd7, 0x1c, 0x98, 0xd4, 0xea, 0x97, 0x3c, 0x8b, 0xa2, 0x2a, 0x17, 0xaa, 0xfe, 0xf6, 0x20, 0x05, 0x1a, 0xdd, 0xf3, 0xef, 0xec, 0xa2, 0x2e, 0xc0, 0x57, 0x15, 0x48, 0xa0, 0x1d, 0x8a, 0x2f, 0x37, 0xc0, 0x31, 0xaa, 0x22, 0xb2, 0x1b, 0x13, 0xf4, 0xd0, 0xeb, 0xb6, 0x12, 0xeb, 0x87, 0xd1, 0xa0, 0x49, 0xe2, 0x71, 0x0b, 0x36, 0x91, 0xa2, 0x0b, 0x42, 0x1e, 0x9c, 0xfd, 0x88, 0x55, 0x41, 0x4a, 0x03, 0x05, 0xb9, 0xba, 0xfd, 0x3a, 0x39, 0x44, 0x5e, 0xf6, 0xf4, 0x29, 0x75, 0x67, 0xec, 0xfb, 0xad, 0x97, 0xf6, 0x14, 0x4f, 0xa9, 0xce, 0x6d, 0xb4, 0x6f, 0xa7, 0xec, 0xcd, 0xb8, 0xe8, 0xda, 0x38, 0x6e, 0x12, 0xa5, 0x29, 0xa9, 0x21, 0x08, 0xde, 0x5d, 0x45, 0x5b, 0x07, 0x35, 0xa5, 0xa6, 0x87, 0x55, 0x5e, 0xe8, 0x35, 0xbf, 0xe4, 0x4b, 0xcb, 0x82, 0xa0, 0x40, 0xe9, 0x2c, 0xfa, 0x5b, 0xc0, 0xee, 0xb4, 0x6b, 0xae, 0x1e, 0xeb, 0xa3, 0x2e, 0x94, 0x56, 0x14, 0x08, 0x54, 0x32, 0xcc, 0xcf, 0xa1, 0x66, 0x09, 0x9e, 0xdc, 0xae, 0xcd, 0xb7, 0x59, 0x82, 0x98, 0x55, 0x49, 0x5c, 0x64, 0xd0, 0x20, 0x90, 0x55, 0x8b, 0x68, 0xcf, 0x52, 0x3a, 0x4f, 0x66, 0xcd, 0x45, 0x6c, 0xe6, 0x55, 0xcf, 0xfe, 0x08, 0x05, 0x20, 0xae, 0xd1, 0x75, 0xed, 0x81, 0xf8, 0x07, 0xa1, 0xb7, 0xeb, 0xf4, 0x4b, 0xa2, 0x05, 0x32, 0x15, 0xbe, 0x72, 0x3c, 0x30, 0x93, 0xa1, 0xe8, 0x6a, 0x0c, 0x17, 0xb9, 0x59, 0x9f, 0xcf, 0x40, 0xdf, 0x3b, 0xe2, 0xc6, 0xb5, 0x17, 0x7f, 0xd3, 0x35, 0xdd, 0xcc, 0x90, 0x65, 0x06, 0x3a, 0x87, 0x77, 0xea, 0x45, 0x03, 0xe9, 0xbc, 0x4b, 0xf7, 0xcd, 0x4a, 0x94, 0x95, 0xc0, 0x48, 0xa9, 0xa5, 0xb0, 0x09, 0x3a, 0x2a, 0x9a, 0xf5, 0xda, 0x0b, 0x53, 0xbc, 0xf4, 0x3d, 0x29, 0xb2, 0x91, 0x9c, 0xe4, 0xc4, 0xa0, 0x31, 0xa3, 0xb6, 0x00, 0x00, 0x01, 0x09, 0x00, 0xb7, 0x03, 0xa3, 0xb5, 0xa3, 0x5f, 0x54, 0x42, 0x54, 0x25, 0x60, 0x23, 0x30, 0x0c, 0xfe, 0xe7, 0x82, 0x82, 0x62, 0x90, 0x80, 0x7e, 0x8a, 0x42, 0x04, 0x28, 0x08, 0x05, 0x25, 0x3c, 0x3e, 0xc8, 0xf0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0xe8, 0xa0, 0x50, 0x98, 0x8e, 0xe2, 0xf9, 0x39, 0xd1, 0xa9, 0x47, 0x6e, 0x97, 0x00, 0x1b, 0x58, 0x50, 0x91, 0xc4, 0x7e, 0x2b, 0xe4, 0x20, 0x64, 0xba, 0x27, 0x9b, 0x42, 0x41, 0x03, 0x96, 0x5e, 0x5c, 0x00, 0x5b, 0x9d, 0x08, 0xbf, 0x25, 0xba, 0xf7, 0x3b, 0xbb, 0xc0, 0xe9, 0x38, 0x77, 0x14, 0x46, 0x4b, 0xca, 0xa3, 0x1c, 0x3f, 0xa9, 0xed, 0x5c, 0x88, 0x99, 0x54, 0x00, 0xeb, 0xe5, 0xb0, 0x23, 0xe5, 0x84, 0x74, 0x29, 0x15, 0xb1, 0x37, 0x2d, 0xf6, 0x5b, 0xa4, 0xf0, 0x63, 0x9d, 0x9c, 0xd4, 0x78, 0x41, 0xfb, 0xd6, 0xf9, 0xf2, 0xb8, 0xe2, 0x0d, 0xc0, 0x32, 0x28, 0x29, 0xc5, 0xa7, 0x7b, 0xec, 0x87, 0x5f, 0x58, 0xdd, 0x8d, 0xb9, 0x47, 0xe9, 0x89, 0x27, 0x86, 0x3e, 0x9e, 0x69, 0x40, 0x15, 0xd6, 0xfc, 0x1a, 0x1c, 0xdb, 0x22, 0xbb, 0xd2, 0xc7, 0x80, 0x25, 0x6a, 0x1a, 0xe1, 0xef, 0x2d, 0x4a, 0xc8, 0xe2, 0xab, 0x48, 0x16, 0x45, 0x04, 0xad, 0x04, 0xb2, 0x63, 0x99, 0xc9, 0x09, 0x3b, 0xa3, 0xda, 0x4b, 0xcd, 0xd0, 0x5e, 0xe0, 0x64, 0x95, 0x8f, 0x48, 0x9a, 0x39, 0x99, 0x43, 0x6d, 0x4c, 0x5d, 0x03, 0xee, 0x15, 0xa2, 0x7a, 0xfa, 0xb2, 0x5f, 0xa1, 0xfe, 0x52, 0x6b, 0xc3, 0x25, 0xf6, 0xc7, 0x5b, 0x48, 0x9d, 0xea, 0xfc, 0xbd, 0xc9, 0x41, 0x8b, 0xbd, 0x3b, 0x29, 0x65, 0x92, 0xc5, 0x9f, 0x03, 0xec, 0xda, 0xa4, 0x61, 0xe3, 0x67, 0x47, 0xaf, 0xd0, 0x4b, 0xe3, 0xa4, 0x01, 0xd2, 0xef, 0x78, 0x29, 0x39, 0xf1, 0xcf, 0xf3, 0x56, 0x82, 0x0c, 0xb0, 0x37, 0x4a, 0xae, 0x48, 0x78, 0xd1, 0x7d, 0x8a, 0xdf, 0x05, 0x70, 0xe5, 0xed, 0x72, 0xe0, 0x22, 0xed, 0x16, 0x4b, 0xee, 0xec, 0x72, 0xf6, 0xb1, 0x54, 0x53, 0x2c, 0x25, 0xcb, 0x26, 0x02, 0xf3, 0x66, 0xf4, 0xec, 0x33, 0x4f, 0x99, 0x1a, 0xce, 0x2d, 0x4b, 0x9d, 0x50, 0x71, 0xd0, 0x1d, 0xf3, 0x06, 0xdb, 0x70, 0xe4, 0x58, 0xf2, 0xde, 0xb0, 0xef, 0x0d, 0xff, 0xfc, 0xe4, 0xe5, 0x0c, 0x36, 0x3a, 0x68, 0xa9, 0x25, 0x13, 0x1f, 0xed, 0xde, 0x39, 0x5e, 0x56, 0x4e, 0x29, 0x78, 0x12, 0x5c, 0x64, 0xff, 0xb2, 0xf7, 0x9e, 0x52, 0x14, 0x2d, 0xdd, 0x7d, 0x5f, 0x14, 0x87, 0xb4, 0x14, 0xa0, 0x58, 0xae, 0xf4, 0x9b, 0x83, 0x17, 0x30, 0x9c, 0xd1, 0x0c, 0x96, 0x55, 0xfb, 0x1d, 0x28, 0xc5, 0x97, 0xcf, 0x67, 0x4c, 0x68, 0x09, 0x33, 0x38, 0x4e, 0x39, 0xcc, 0x48, 0x88, 0xd6, 0xe8, 0x46, 0x95, 0x11, 0x28, 0xca, 0xf4, 0x18, 0xfc, 0xd3, 0x62, 0xea, 0x7e, 0x85, 0x3b, 0x1e, 0xbc, 0x13, 0x99, 0xc0, 0x3c, 0xd4, 0xad, 0x76, 0x8b, 0x54, 0xd4, 0x9b, 0x7c, 0xa7, 0xa1, 0xc8, 0x35, 0x2f, 0xa7, 0xfa, 0x6e, 0xe4, 0x5f, 0xf8, 0x14, 0xe5, 0x02, 0x0e, 0x7b, 0xee, 0x95, 0x21, 0x02, 0x31, 0x83, 0xd1, 0x8b, 0x40, 0x37, 0x54, 0xb4, 0x2d, 0x57, 0xad, 0x6d, 0xac, 0x1e, 0xf7, 0x13, 0x9b, 0x6b, 0xaf, 0xa3, 0x99, 0xa6, 0x06, 0x5e, 0x2d, 0x6b, 0x7c, 0xb7, 0xb0, 0x5f, 0x19, 0x0e, 0x73, 0x48, 0x77, 0xf5, 0x19, 0xb1, 0xa6, 0xec, 0x28, 0xa8, 0xb7, 0x60, 0xf4, 0x59, 0x18, 0x9f, 0xe1, 0x5a, 0x59, 0x60, 0x17, 0xa7, 0xdf, 0x53, 0x54, 0x74, 0x18, 0x50, 0x1a, 0x85, 0xb0, 0xe1, 0xa2, 0x13, 0x58, 0xd3, 0x94, 0x11, 0xa4, 0x8f, 0x63, 0xaa, 0xf0, 0x43, 0xb2, 0x64, 0x7b, 0x4f, 0x05, 0x86, 0xa0, 0xcf, 0xec, 0x4a, 0x93, 0x07, 0xf9, 0xbf, 0x85, 0x8a, 0x47, 0x24, 0x06, 0xf1, 0x56, 0xc4, 0xf3, 0x77, 0xe3, 0xb9, 0x40, 0x10, 0x73, 0x07, 0x68, 0x01, 0xce, 0x35, 0xfc, 0x5a, 0x53, 0x02, 0xfc, 0x99, 0xde, 0xdd, 0xbc, 0x0d, 0xf8, 0x8a, 0xe6, 0x0f, 0x80, 0xaf, 0xbd, 0xb1, 0xb6, 0x4f, 0x1b, 0x7a, 0x4a, 0xc4, 0x85, 0xec, 0xc1, 0x31, 0x36, 0xd7, 0x50, 0x8f, 0x98, 0x14, 0xd2, 0x1f, 0xb3, 0xac, 0x5c, 0xaa, 0x43, 0x07, 0xb0, 0xca, 0x6a, 0x0a, 0x14, 0x39, 0xfa, 0xa1, 0x0a, 0x59, 0xc8, 0xab, 0x50, 0x6a, 0xb7, 0x45, 0xbe, 0x97, 0xe4, 0xf0, 0xd3, 0xde, 0x26, 0x52, 0x55, 0x2f, 0x80, 0xc0, 0x60, 0x13, 0x08, 0xb3, 0x52, 0xbf, 0xac, 0xc0, 0xdd, 0x01, 0xac, 0xb9, 0xc8, 0xd7, 0x85, 0xf1, 0x3e, 0xf1, 0x95, 0xb5, 0x13, 0xc5, 0xc1, 0x99, 0x75, 0xf2, 0x13, 0xf3, 0x1f, 0x3b, 0x0d, 0x5b, 0xa2, 0xa7, 0x53, 0x78, 0xde, 0x12, 0x94, 0xda, 0x47, 0x67, 0xe6, 0x48, 0x9b, 0xb4, 0x0a, 0x24, 0x57, 0x46, 0x44, 0x57, 0x5e, 0x67, 0x56, 0x9c, 0xde, 0x40, 0x8b, 0x2e, 0x7c, 0xe1, 0x8a, 0x55, 0x9f, 0x7d, 0xe0, 0x91, 0xa8, 0xae, 0xbc, 0x00, 0x47, 0x7f, 0x71, 0x36, 0x7b, 0x5c, 0x6d, 0x0e, 0x64, 0xa7, 0x90, 0xf0, 0x21, 0x9c, 0x3d, 0xff, 0xcf, 0x34, 0x70, 0x7c, 0x52, 0x32, 0x79, 0xa6, 0x4d, 0xb3, 0x44, 0xb8, 0x19, 0x89, 0x10, 0x35, 0x19, 0xb7, 0x4e, 0xb5, 0x6e, 0x55, 0xe9, 0x98, 0x1c, 0x55, 0xfe, 0x83, 0xaf, 0xa4, 0xe9, 0x63, 0xa9, 0xa2, 0xd2, 0x30, 0xdb, 0xaf, 0xcb, 0xc3, 0x06, 0x59, 0xc3, 0x67, 0x98, 0x6a, 0xf4, 0xb4, 0x9c, 0x2b, 0x73, 0x97, 0x43, 0x47, 0x37, 0xec, 0x0c, 0xe9, 0x31, 0x8b, 0xf0, 0x2e, 0x6b, 0x50, 0x5f, 0x2f, 0xb4, 0x90, 0xde, 0x70, 0xc7, 0xcb, 0x68, 0x7a, 0x28, 0xa7, 0x78, 0x5d, 0xf2, 0x30, 0x80, 0xdb, 0x34, 0xde, 0xac, 0x78, 0x5a, 0xcd, 0xd8, 0xb4, 0x14, 0x77, 0x47, 0xa4, 0xdd, 0x88, 0xcf, 0x68, 0x86, 0x1b, 0x20, 0xec, 0xb1, 0xb8, 0x03, 0xc4, 0xca, 0x4e, 0x48, 0x34, 0x27, 0x24, 0x79, 0x55, 0x63, 0x9f, 0x39, 0x6c, 0x36, 0x47, 0x75, 0x91, 0x37, 0xaf, 0xb8, 0x96, 0x4e, 0x2d, 0xd3, 0x2d, 0xda, 0x60, 0x32, 0xd1, 0xc1, 0x4b, 0x6d, 0xd4, 0x68, 0xd1, 0x0f, 0x77, 0x27, 0x78, 0x22, 0x2f, 0xb6, 0x65, 0x98, 0xee, 0xd1, 0x9f, 0xa6, 0xa3, 0xeb, 0xe9, 0x12, 0xd3, 0x7a, 0x96, 0x25, 0x32, 0x29, 0xce, 0x70, 0x8b, 0xe5, 0xfc, 0x14, 0x8f, 0xd6, 0x9f, 0x0c, 0x19, 0x0d, 0x45, 0x55, 0x95, 0x5a, 0xcb, 0x97, 0x44, 0x1a, 0x5b, 0x80, 0xac, 0x62, 0xe9, 0xce, 0x14, 0x87, 0x42, 0x44, 0x04, 0x20, 0xc8, 0x88, 0x19, 0x25, 0x3a, 0xeb, 0x84, 0x67, 0x9a, 0xf2, 0x5a, 0x99, 0x3c, 0x28, 0x43, 0xb3, 0xca, 0xac, 0xfe, 0x01, 0x60, 0x91, 0x6f, 0x0d, 0x48, 0xbf, 0xf1, 0x46, 0xb9, 0x06, 0x2a, 0x95, 0xb9, 0x40, 0x6a, 0x8d, 0x9b, 0x1e, 0x61, 0xf3, 0x72, 0x43, 0xd5, 0x58, 0x2d, 0x73, 0x2f, 0x38, 0xc4, 0xc1, 0xd3, 0x15, 0xcf, 0x85, 0x82, 0x42, 0x52, 0x7d, 0x25, 0x29, 0xd0, 0x08, 0xcf, 0xab, 0x0a, 0x27, 0x60, 0x8c, 0xd3, 0xbb, 0x79, 0x73, 0x53, 0xc6, 0xdf, 0x74, 0x77, 0x3e, 0xfe, 0x6a, 0x83, 0x0a, 0x87, 0x45, 0x35, 0x9c, 0xb0, 0x1a, 0xb8, 0x41, 0x92, 0x95, 0xc7, 0x0e, 0xca, 0x0d, 0x4b, 0xf6, 0xf8, 0xf0, 0x97, 0xfc, 0x7b, 0x67, 0xb0, 0xc2, 0xe9, 0x3b, 0x38, 0xbe, 0x0a, 0x3d, 0x41, 0xed, 0x7f, 0x9c, 0x5d, 0xeb, 0x1a, 0xc6, 0x71, 0x06, 0xb4, 0x74, 0xbf, 0x7c, 0x39, 0x2f, 0xd2, 0x9a, 0x56, 0xec, 0xaf, 0x90, 0x0c, 0x57, 0x6e, 0xd7, 0x28, 0x86, 0xf4, 0xd7, 0x4f, 0x48, 0x02, 0xe7, 0xb4, 0xa9, 0xd0, 0x3e, 0x18, 0xe1, 0xc3, 0xdf, 0xf4, 0x99, 0xa6, 0x3b, 0xd1, 0xfe, 0x7f, 0xd3, 0x99, 0xbc, 0x11, 0xaf, 0x22, 0xaa, 0xcf, 0x55, 0x05, 0x45, 0x61, 0x5b, 0x3b, 0x84, 0x31, 0x53, 0x44, 0xab, 0x71, 0x57, 0xc7, 0x69, 0xd8, 0x40, 0xa4, 0x2f, 0x26, 0x3c, 0x7c, 0xc7, 0xfc, 0x61, 0x8b, 0xb3, 0xba, 0x7e, 0xf3, 0x43, 0xed, 0x71, 0xd7, 0x73, 0xa9, 0x36, 0x5d, 0x2f, 0x12, 0xe1, 0xe9, 0x18, 0x5a, 0xca, 0x6a, 0xce, 0x48, 0x54, 0x9b, 0x4c, 0x96, 0x02, 0xa8, 0xa6, 0x23, 0x92, 0x5e, 0xeb, 0x1d, 0xd2, 0x88, 0x50, 0x01, 0xa1, 0x40, 0x2b, 0x31, 0xba, 0xd4, 0x61, 0x8a, 0x4c, 0x22, 0x74, 0x87, 0x99, 0x74, 0x12, 0xff, 0x21, 0x13, 0x43, 0x7f, 0x25, 0x08, 0x8d, 0xf0, 0xdd, 0x47, 0x3a, 0x83, 0x17, 0xb4, 0x33, 0x4d, 0x25, 0xcc, 0xdd, 0x5c, 0xce, 0xed, 0xf4, 0xf1, 0x89, 0x2c, 0x2b, 0x89, 0x9a, 0x39, 0xf4, 0xc3, 0x0c, 0x7f, 0x37, 0x9d, 0xb6, 0xde, 0xdb, 0xb2, 0x3f, 0x01, 0xde, 0x1c, 0xd0, 0xe1, 0xf8, 0x2d, 0xf4, 0xbf, 0x1a, 0x8a, 0x8a, 0xc3, 0x15, 0x04, 0x4e, 0x83, 0x00, 0x06, 0xfd, 0xcb, 0xf4, 0xd1, 0x1c, 0xa7, 0x43, 0x02, 0x75, 0xa6, 0x66, 0x7d, 0x56, 0x60, 0xcc, 0x22, 0x51, 0xa9, 0xf9, 0x13, 0x51, 0x13, 0x89, 0x41, 0xfc, 0x13, 0x24, 0xcb, 0xf5, 0x2c, 0x65, 0x79, 0x7f, 0x7b, 0x6d, 0xa8, 0xdf, 0xee, 0xa2, 0x42, 0xf2, 0xe7, 0x23, 0xfe, 0xc0, 0x67, 0x5b, 0x27, 0x1c, 0x8f, 0x00, 0x3d, 0xe4, 0x37, 0x94, 0x05, 0x2e, 0xb7, 0x03, 0xb9, 0xa7, 0x69, 0xd4, 0xb8, 0xc4, 0x3c, 0xd0, 0x19, 0xc3, 0xf9, 0x78, 0x70, 0xf0, 0x2b, 0x6f, 0x89, 0xec, 0x73, 0xb7, 0xd6, 0xda, 0x55, 0xa5, 0xed, 0x39, 0xe1, 0x84, 0x7f, 0x87, 0x2a, 0x46, 0x3e, 0xa7, 0xa9, 0xcb, 0x04, 0x3e, 0xd2, 0xbf, 0xb2, 0x09, 0x35, 0x36, 0xa0, 0x14, 0xed, 0x51, 0xf8, 0x1d, 0xba, 0x98, 0xdc, 0x4e, 0x37, 0x93, 0x28, 0xc0, 0xda, 0xc9, 0xaa, 0x30, 0x8c, 0x87, 0x47, 0x60, 0xf2, 0xcc, 0xdf, 0xa2, 0xb6, 0xa6, 0xef, 0x1c, 0xa7, 0x02, 0x21, 0x39, 0xc1, 0x07, 0x85, 0xe1, 0x1a, 0x6f, 0xe3, 0xc8, 0xa4, 0x20, 0xb2, 0x8f, 0x7b, 0xfb, 0xfc, 0xe4, 0xc1, 0x9d, 0xaa, 0x10, 0x3d, 0x34, 0x70, 0xcf, 0xdf, 0x4a, 0x77, 0x40, 0x37, 0x0f, 0x81, 0xea, 0x5e, 0x96, 0x9c, 0xaf, 0x16, 0x79, 0x96, 0xb7, 0x0e, 0x5f, 0x1a, 0xc9, 0x97, 0x15, 0x79, 0x7f, 0x8e, 0x63, 0xdb, 0xcd, 0x99, 0xed, 0xdc, 0xf8, 0xbe, 0x82, 0xa6, 0x91, 0xe8, 0xb2, 0x3f, 0x91, 0x4a, 0x85, 0xf3, 0xe6, 0x1e, 0xa3, 0x55, 0x68, 0xdc, 0xe3, 0xaa, 0xe0, 0xf3, 0x20, 0x9e, 0xed, 0x30, 0x2f, 0xfb, 0xf6, 0x7e, 0x4c, 0xf3, 0x4b, 0xe8, 0xbb, 0x7c, 0x02, 0xc8, 0x78, 0x60, 0x4e, 0xe1, 0xe8, 0xe7, 0x5f, 0x0e, 0x66, 0xf1, 0x67, 0x3b, 0x98, 0x0c, 0xc2, 0xf4, 0x81, 0x95, 0xc4, 0x91, 0x94, 0xdf, 0xf9, 0x2e, 0x6d, 0x92, 0xd2, 0xf5, 0xa0, 0xa8, 0x13, 0x84, 0x5c, 0x3f, 0xc5, 0x9e, 0xf1, 0xab, 0xb7, 0x73, 0x16, 0x7b, 0x83, 0xe5, 0xf8, 0xe9, 0xfd, 0xce, 0xa4, 0x32, 0xa5, 0xeb, 0x33, 0x3c, 0x62, 0x14, 0x71, 0x2e, 0xe5, 0xa7, 0xa6, 0xf2, 0x48, 0x98, 0x2e, 0x43, 0xf0, 0xbb, 0xd0, 0xba, 0x26, 0x02, 0xd5, 0x41, 0xba, 0xcf, 0xd3, 0xf0, 0xcd, 0xd1, 0xf2, 0x49, 0xcf, 0x59, 0x30, 0x6a, 0x4c, 0xbf, 0xea, 0xb3, 0x03, 0x0b, 0x59, 0xb4, 0x13, 0x85, 0x03, 0xbe, 0xe2, 0xa1, 0xe3, 0x4c, 0x86, 0x71, 0xc7, 0x21, 0xdd, 0x80, 0xc1, 0x6a, 0x29, 0xda, 0x81, 0xb1, 0x0c, 0xcc, 0xa9, 0x75, 0xa0, 0x0c, 0x1f, 0xf1, 0x6b, 0xd5, 0x04, 0xf1, 0x9e, 0x46, 0x69, 0x07, 0x7b, 0x05, 0x76, 0xbf, 0xe3, 0x9a, 0xb5, 0x03, 0x2f, 0xb8, 0x4d, 0xbc, 0xca, 0x88, 0xa1, 0x06, 0x5b, 0xce, 0x06, 0x52, 0xa6, 0x1d, 0x80, 0x9d, 0x14, 0xca, 0xc0, 0xd8, 0x08, 0x7c, 0x94, 0x75, 0x17, 0x42, 0xd4, 0x1f, 0x6a, 0xca, 0x98, 0xf9, 0x0f, 0x45, 0x34, 0x8a, 0xac, 0xbe, 0x4d, 0x65, 0x7f, 0x4e, 0x9e, 0xd2, 0x2c, 0xc3, 0x75, 0x9e, 0x6f, 0xe9, 0x83, 0xb0, 0x3e, 0x9c, 0xee, 0x7c, 0x52, 0x04, 0x53, 0x59, 0x77, 0x68, 0x5c, 0x8c, 0x10, 0xfb, 0x3a, 0x34, 0x57, 0x22, 0xa2, 0xf0, 0xb1, 0x32, 0x3c, 0x4d, 0x33, 0xf9, 0xd8, 0x7b, 0xed, 0xbc, 0xbc, 0x00, 0xf2, 0x7d, 0x1a, 0xd9, 0x0e, 0x6e, 0xdc, 0x43, 0xa1, 0x7d, 0xa4, 0x1c, 0x72, 0x59, 0xf2, 0x04, 0xb7, 0x4e, 0x9e, 0xe0, 0x03, 0xe7, 0xcd, 0xe7, 0xfe, 0x2c, 0x81, 0x54, 0xc4, 0x48, 0x30, 0x9b, 0x4e, 0x07, 0x85, 0x34, 0x5b, 0xf5, 0xb5, 0x55, 0xb9, 0xb7, 0x36, 0xc8, 0xa2, 0xa7, 0xbb, 0xfd, 0xfd, 0x9b, 0x26, 0x91, 0xa6, 0x37, 0x0a, 0xbf, 0x7f, 0xd5, 0x4d, 0xf9, 0xf3, 0x50, 0x7c, 0x84, 0x63, 0xbe, 0x66, 0x01, 0x5f, 0x37, 0x78, 0xc5, 0x23, 0x93, 0x30, 0xbe, 0x1d, 0x8f, 0x51, 0xa3, 0x87, 0x38, 0x24, 0xb1, 0x36, 0xc6, 0xd5, 0xf3, 0x85, 0x47, 0xf0, 0xef, 0x1f, 0xdb, 0x95, 0xcf, 0x10, 0xb7, 0x10, 0x0c, 0xb5, 0x5d, 0x0a, 0x0d, 0x2a, 0xde, 0xd1, 0x0e, 0xf0, 0x28, 0x53, 0x24, 0x9b, 0xd6, 0x91, 0x79, 0x6e, 0x8f, 0xae, 0x73, 0x3c, 0x86, 0x2e, 0xb8, 0x9b, 0xf2, 0x29, 0xe8, 0xfc, 0x4b, 0x08, 0xd6, 0x81, 0xbb, 0xc3, 0x50, 0xb0, 0x5b, 0x8a, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x01, 0x00, 0x0e, 0x6b, 0x6b, 0x11, 0xaf, 0x22, 0x52, 0x3f, 0x82, 0x51, 0x0e, 0xd7, 0x03, 0xd4, 0xae, 0x54, 0x10, 0x1e, 0x83, 0x75, 0x11, 0x11, 0xeb, 0xa0, 0xde, 0x5e, 0x24, 0x28, 0x9d, 0x29, 0x11, 0x45, 0xfd, 0x06, 0x89, 0xb8, 0x3a, 0x3d, 0xd5, 0x11, 0xa1, 0xb8, 0xb7, 0x7f, 0x02, 0xf0, 0x1b, 0xcf, 0xbd, 0x27, 0xf3, 0xd9, 0x5a, 0x92, 0xf4, 0x7f, 0x18, 0x4f, 0x61, 0x9a, 0x26, 0x0d, 0xe4, 0x2a, 0x7a, 0xab, 0x21, 0x9a, 0xa5, 0xa0, 0x12, 0x9a, 0x81, 0xab, 0xff, 0x10, 0xdb, 0x43, 0x61, 0x77, 0x7f, 0xe6, 0xc8, 0xb8, 0x96, 0x84, 0xa1, 0x2d, 0x0f, 0x0a, 0xbb, 0x8c, 0x68, 0x83, 0x7b, 0x6b, 0xc0, 0xf8, 0x51, 0xbb, 0x27, 0x2d, 0x70, 0x80, 0x90, 0x71, 0x4e, 0x0e, 0x21, 0x23, 0xe9, 0xb8, 0x6e, 0x49, 0x1f, 0x67, 0xea, 0x01, 0x3d, 0xff, 0xe0, 0xdc, 0xbe, 0xd6, 0x6a, 0x95, 0xcb, 0xda, 0xaf, 0x2b, 0x51, 0xce, 0xba, 0xa4, 0x9f, 0x09, 0x29, 0x8a, 0x20, 0x48, 0xd8, 0xa0, 0x37, 0xed, 0xc8, 0x75, 0xba, 0xc0, 0x01, 0x47, 0x47, 0x15, 0x15, 0xb5, 0xe2, 0x07, 0x7d, 0x89, 0x9d, 0x07, 0x7e, 0xa7, 0xcf, 0xe3, 0xe8, 0xe0, 0xa2, 0xf5, 0x12, 0x2b, 0xc0, 0x79, 0xc6, 0x64, 0xcd, 0xa4, 0x9f, 0xda, 0x2a, 0x39, 0x30, 0x69, 0x07, 0xbe, 0x6d, 0xb5, 0x4a, 0xa4, 0x79, 0x31, 0xef, 0xda, 0x88, 0x65, 0xc6, 0x95, 0x17, 0xfd, 0x0e, 0x13, 0xf2, 0x76, 0x47, 0xb4, 0x64, 0x7e, 0x4f, 0xb4, 0xa5, 0xb5, 0x36, 0xa3, 0x92, 0xa7, 0xc5, 0x3d, 0xfe, 0xb1, 0x64, 0x4a, 0xd6, 0x76, 0x5e, 0x17, 0xf9, 0x01, 0xe1, 0xc5, 0x42, 0x11, 0xf0, 0xcf, 0x96, 0x8f, 0x1c, 0x21, 0x64, 0x8a, 0x14, 0xb4, 0x80, 0xc8, 0x97, 0x31, 0x04, 0xff, 0xce, 0x5e, 0xc0, 0x10, 0xf7, 0xf2, 0x09, 0x80, 0x6f, 0x05, 0xc3, 0x44, 0xdc, 0xa7, 0xfe, 0x2f, 0x47, 0x43, 0xcc, 0xc7, 0xf9, 0xdf, 0x0f, 0xe4, 0x23, 0xdc, 0x44, 0xc5, 0xd8, 0x32, 0x5d, 0x8b, 0xda, 0x51, 0xf7, 0x34, 0xc7, 0xc1, 0xc8, 0xdd, 0x79, 0x18, 0x68, 0xb2, 0x33, 0xa5, 0x85, 0xec, 0xa2, 0x5f, 0xf8, 0x21, 0xf5, 0x4c, 0xb6, 0xb4, 0xd4, 0xdf, 0xaa, 0x7a, 0x93, 0x1b, 0x83, 0x26, 0x11, 0x06, 0xdb, 0x45, 0x65, 0x83, 0xb7, 0xe2, 0xba, 0xc9, 0x02, 0x4a, 0x29, 0x0f, 0xa7, 0x74, 0x3a, 0xd7, 0x8f, 0x23, 0xfa, 0x90, 0x29, 0x27, 0xf9, 0xa9, 0x13, 0x0b, 0x2d, 0x3d, 0x6c, 0xbe, 0xd4, 0xa0, 0xbc, 0x37, 0x28, 0x7f, 0x69, 0x82, 0x2f, 0xa7, 0x4d, 0x22, 0x87, 0x9f, 0xa7, 0x96, 0x6c, 0x93, 0xaa, 0x91, 0xe9, 0x1d, 0x36, 0x9c, 0x87, 0x7e, 0x27, 0x7f, 0x13, 0x59, 0xea, 0x3d, 0xcb, 0xe4, 0xe3, 0x56, 0x31, 0xb0, 0x9a, 0xfb, 0xc1, 0xe8, 0xe6, 0xf3, 0x10, 0x62, 0x80, 0x8b, 0x53, 0x1d, 0xe0, 0x1d, 0xc5, 0x44, 0x59, 0xb1, 0x29, 0xe2, 0x0c, 0x24, 0x37, 0xad, 0x7a, 0xd6, 0x7c, 0xb2, 0x68, 0xf6, 0x00, 0xa4, 0x30, 0x22, 0x0a, 0xd9, 0x06, 0xf5, 0xd2, 0x36, 0x0a, 0x27, 0x90, 0x11, 0x2b, 0x17, 0x6f, 0x22, 0x77, 0x6f, 0x19, 0x91, 0x73, 0x24, 0xfd, 0x42, 0x1f, 0x1d, 0x20, 0x8b, 0x45, 0x76, 0x35, 0x10, 0xcb, 0x21, 0x9f, 0x6d, 0x01, 0xd7, 0xda, 0x1c, 0xbd, 0x98, 0x0d, 0x19, 0x8b, 0x4a, 0x5b, 0x3d, 0x99, 0x73, 0x98, 0x7d, 0x7e, 0xac, 0xeb, 0x5b, 0xad, 0x97, 0xd1, 0xbc, 0x6f, 0x60, 0xed, 0x05, 0xe5, 0xa6, 0xa8, 0xda, 0x87, 0x34, 0x22, 0xad, 0x6d, 0xd9, 0x71, 0x3c, 0xc0, 0xce, 0x25, 0xef, 0xcd, 0x30, 0x54, 0x6d, 0xc5, 0xd4, 0xf7, 0x37, 0x89, 0x78, 0x89, 0x41, 0x0b, 0x65, 0xaa, 0x7b, 0x1b, 0xe0, 0xe7, 0x14, 0x0d, 0xe7, 0x24, 0xe4, 0x73, 0xa6, 0xd4, 0x06, 0xd2, 0x7a, 0x36, 0x44, 0xfe, 0xfd, 0xb8, 0x8f, 0xd9, 0x45, 0xc6, 0xb2, 0x35, 0x82, 0xdc, 0x1c, 0xf5, 0x9b, 0x2a, 0x4f, 0x30, 0xa0, 0xb6, 0x27, 0xc6, 0x93, 0x95, 0x35, 0x00, 0x2f, 0xfe, 0xab, 0x6f, 0xc6, 0x9d, 0xa3, 0x1e, 0x31, 0xe6, 0xd9, 0x7f, 0x53, 0x5d, 0x14, 0x56, 0xb2, 0x79, 0xf9, 0x06, 0xd9, 0x9e, 0x6a, 0x1d, 0x39, 0xba, 0x40, 0x26, 0xa1, 0xae, 0xf8, 0x57, 0xaf, 0x45, 0xad, 0x54, 0x31, 0x0c, 0x4a, 0xb0, 0xdd, 0x8c, 0xc2, 0xa3, 0x9b, 0x10, 0xe9, 0x67, 0x0f, 0x25, 0x5b, 0x53, 0x12, 0x0a, 0xda, 0x90, 0x7f, 0x62, 0x01, 0xba, 0x03, 0xd0, 0x6a, 0x61, 0x19, 0x88, 0x18, 0x0f, 0xe5, 0x7f, 0xec, 0x0e, 0x49, 0x25, 0x16, 0x0e, 0x9a, 0xd1, 0x3d, 0xf7, 0x5f, 0xfb, 0x92, 0x27, 0x85, 0x68, 0x9a, 0xbb, 0xf2, 0xc6, 0x98, 0xa2, 0xf2, 0x3b, 0xb5, 0x9f, 0xfe, 0x5f, 0x9d, 0x6e, 0xb6, 0xc4, 0x1e, 0xcc, 0xbd, 0x4c, 0x10, 0x24, 0xe1, 0x03, 0xf9, 0x7f, 0x7f, 0x3f, 0xb2, 0xe1, 0x63, 0x28, 0xfd, 0x8c, 0x2b, 0xff, 0xa7, 0xf9, 0x05, 0xc9, 0xd3, 0x92, 0xfd, 0xf8, 0x2f, 0x48, 0x0d, 0xa2, 0x9f, 0xb3, 0x23, 0xfe, 0xae, 0x95, 0x1d, 0x6e, 0xa6, 0xd8, 0xe1, 0x1a, 0x9f, 0xd7, 0x98, 0x34, 0x5e, 0x97, 0x26, 0xd1, 0xfe, 0x2e, 0xbb, 0xe2, 0x39, 0xa8, 0x09, 0xec, 0xcb, 0xb2, 0x07, 0xf1, 0x83, 0xf4, 0x5e, 0x1d, 0xa9, 0x0c, 0xa4, 0x8f, 0xa4, 0x7e, 0xba, 0x09, 0x30, 0xb0, 0x20, 0x1e, 0x23, 0xb6, 0x45, 0x1b, 0x6f, 0x44, 0x39, 0x3b, 0x28, 0xf0, 0xca, 0xe1, 0xf1, 0xc3, 0x4e, 0x6c, 0xb0, 0x57, 0xcf, 0x4f, 0xb5, 0x7a, 0x1b, 0xce, 0x45, 0x67, 0xd3, 0x36, 0xc7, 0x41, 0x84, 0x01, 0xb5, 0x57, 0xab, 0x0f, 0x34, 0xe0, 0xb8, 0x2c, 0xfe, 0xb2, 0x2f, 0x70, 0x9a, 0x5c, 0xbe, 0x7b, 0xc3, 0xdc, 0x6c, 0x9e, 0x76, 0xf2, 0xa2, 0xf6, 0x76, 0xbe, 0xb0, 0x70, 0x44, 0x77, 0x17, 0x69, 0x55, 0x7e, 0x85, 0xc5, 0xd7, 0x09, 0x55, 0x5a, 0x7f, 0x9b, 0xf3, 0x6a, 0xfa, 0x1e, 0x37, 0xad, 0xac, 0xe8, 0x75, 0x22, 0x32, 0xe8, 0x5c, 0xfc, 0x11, 0xf7, 0xb2, 0x5b, 0xce, 0xb7, 0x3b, 0x90, 0xaf, 0xec, 0xe2, 0x14, 0x1c, 0xdd, 0x56, 0x2e, 0xaf, 0x00, 0x24, 0x8c, 0x46, 0x64, 0x60, 0x51, 0x55, 0x1f, 0x62, 0xf5, 0x1c, 0x20, 0x11, 0x0a, 0x3a, 0x2c, 0x5c, 0xf1, 0x21, 0x04, 0x2e, 0x6a, 0x61, 0xc2, 0x11, 0x27, 0x1e, 0xf3, 0xfc, 0x26, 0x5a, 0x9f, 0x52, 0x02, 0xd4, 0x3a, 0xd8, 0x6d, 0xca, 0x4e, 0x34, 0x2a, 0x36, 0x41, 0xd3, 0x29, 0x71, 0xf7, 0x8c, 0xdb, 0x6d, 0x76, 0x3b, 0x1f, 0xfa, 0x3b, 0x83, 0xeb, 0x56, 0xcc, 0x46, 0x57, 0x43, 0x5e, 0x24, 0x2d, 0x6a, 0xb1, 0x42, 0xb8, 0x14, 0xe4, 0xdd, 0xc6, 0xed, 0xb0, 0x93, 0xc7, 0xbf, 0x1c, 0xcc, 0xc3, 0x0c, 0x5c, 0x32, 0x42, 0x73, 0xca, 0x71, 0x39, 0x5b, 0xfa, 0x8e, 0x8f, 0x28, 0xba, 0xa8, 0x69, 0xdc, 0xe7, 0x08, 0xa1, 0x8f, 0xdf, 0x6f, 0x3f, 0xb3, 0x8f, 0xc0, 0x86, 0xfb, 0xf7, 0xb2, 0x55, 0xd5, 0x77, 0x48, 0x27, 0x60, 0xdb, 0x12, 0x6f, 0x32, 0xc2, 0x4d, 0xd3, 0xbc, 0x34, 0xf4, 0xb4, 0x28, 0xf4, 0x90, 0xab, 0x78, 0x4d, 0xc8, 0x9f, 0x34, 0x4a, 0x66, 0x2c, 0x57, 0x73, 0xac, 0xde, 0x00, 0xc2, 0x23, 0x29, 0xd7, 0x47, 0x52, 0xc6, 0x36, 0x4d, 0xba, 0xbc, 0xce, 0x6c, 0xe6, 0xf2, 0xa2, 0x05, 0x59, 0x32, 0x99, 0x18, 0x38, 0xd4, 0x1b, 0x02, 0x03, 0x99, 0x06, 0xbe, 0x77, 0x42, 0xa0, 0x22, 0x2d, 0x01, 0x41, 0x58, 0x4b, 0x58, 0x7a, 0x0e, 0xf8, 0x94, 0x53, 0xe5, 0x24, 0xe5, 0xec, 0x23, 0x6b, 0x88, 0x65, 0xd3, 0x4e, 0xb9, 0xe9, 0x26, 0xab, 0x0b, 0x4f, 0xfb, 0xb3, 0x2d, 0x30, 0x7e, 0x27, 0x6d, 0x22, 0xd7, 0x06, 0x34, 0x35, 0xf4, 0x8b, 0x7f, 0xe0, 0x38, 0xc2, 0xbb, 0x30, 0xc9, 0x0d, 0xbe, 0x57, 0xcd, 0x5c, 0xa2, 0x48, 0x91, 0x9b, 0x11, 0x03, 0x65, 0xb1, 0x18, 0x81, 0x84, 0x56, 0x20, 0x54, 0xfc, 0x86, 0xc0, 0x6e, 0x7c, 0xdf, 0x59, 0x01, 0x67, 0x0e, 0x1d, 0x44, 0x21, 0xf7, 0xb4, 0xf4, 0xcc, 0x44, 0xb7, 0xf7, 0x27, 0xd7, 0xd4, 0xde, 0x82, 0xba, 0xac, 0xd2, 0x6c, 0x7d, 0x70, 0x79, 0x76, 0xf7, 0x56, 0x12, 0xf6, 0x69, 0x7c, 0x1c, 0x08, 0x98, 0x84, 0x90, 0x3e, 0x8d, 0x7f, 0x81, 0x2a, 0x65, 0xe4, 0x08, 0xc2, 0xb7, 0x84, 0x15, 0x92, 0x14, 0x70, 0xc9, 0xb3, 0xaf, 0x2c, 0x21, 0x32, 0x9d, 0x99, 0x60, 0xed, 0xf7, 0x81, 0x48, 0x41, 0x57, 0x51, 0xfb, 0x34, 0x26, 0x98, 0x8e, 0x21, 0x28, 0x29, 0xb9, 0x5c, 0xf3, 0x3b, 0x3b, 0x92, 0x49, 0x01, 0xd4, 0x71, 0x92, 0x39, 0x2a, 0x1a, 0xda, 0x2c, 0xa0, 0x2b, 0xed, 0xba, 0xe2, 0xb4, 0x27, 0x1f, 0x6a, 0x7c, 0x9b, 0xe9, 0xf7, 0x8a, 0x50, 0xc5, 0xc8, 0x89, 0xb4, 0xfe, 0x50, 0x46, 0x5b, 0xae, 0x52, 0x03, 0x52, 0x87, 0xcf, 0x2e, 0x31, 0x39, 0xa8, 0x16, 0xa6, 0xee, 0x39, 0x12, 0x55, 0x3c, 0xc7, 0xec, 0xba, 0x63, 0x14, 0xc8, 0x15, 0xa5, 0xae, 0x1f, 0x7e, 0x31, 0xb8, 0x05, 0xf7, 0xb8, 0xf3, 0x72, 0xb0, 0xe6, 0x6b, 0x63, 0x0e, 0x18, 0x5c, 0x87, 0xde, 0x18, 0x83, 0xc6, 0xb4, 0x44, 0x7c, 0xcc, 0x19, 0xdc, 0x63, 0x1d, 0x87, 0x9b, 0x39, 0xd5, 0x07, 0x90, 0xf0, 0x7b, 0x22, 0x7e, 0x72, 0x17, 0xe0, 0x6a, 0x87, 0x6f, 0xf3, 0x42, 0x62, 0x17, 0x84, 0x23, 0x63, 0xc0, 0xf4, 0x9a, 0x05, 0x63, 0x37, 0xae, 0x97, 0xf0, 0x33, 0xe0, 0x40, 0xc5, 0x2c, 0x27, 0x4e, 0x5a, 0xf3, 0x88, 0xab, 0xe8, 0x35, 0x84, 0x59, 0x50, 0xeb, 0x03, 0xab, 0x3a, 0x9e, 0xe9, 0x50, 0xf7, 0x54, 0x04, 0xb5, 0x9b, 0xf2, 0x75, 0xbd, 0xad, 0x03, 0xca, 0xce, 0x94, 0x79, 0x7c, 0x78, 0x4b, 0x0b, 0xd0, 0x3d, 0xfb, 0x99, 0xd3, 0x71, 0x35, 0x10, 0x4c, 0x29, 0xbd, 0xee, 0xcb, 0x16, 0xd8, 0xdc, 0x41, 0xb4, 0x81, 0xe3, 0xd9, 0x3a, 0xf8, 0x2f, 0x5c, 0xb4, 0x58, 0xb5, 0xf8, 0x55, 0x73, 0x56, 0xa2, 0x68, 0x8f, 0xb4, 0xe1, 0xe5, 0x5c, 0xbc, 0xe8, 0xad, 0x78, 0x72, 0x6e, 0x7d, 0x1e, 0xed, 0x4d, 0x1f, 0xa4, 0x95, 0x5d, 0x3b, 0x96, 0x3e, 0x8c, 0x2c, 0x2e, 0x7a, 0x34, 0x0d, 0xb2, 0xd7, 0x94, 0x44, 0x75, 0x92, 0x2d, 0xa3, 0x80, 0x49, 0x5f, 0x58, 0x2d, 0x08, 0x63, 0x50, 0x98, 0x59, 0x14, 0xe7, 0xf0, 0x0c, 0xb3, 0xb9, 0x38, 0x43, 0xaf, 0x93, 0x22, 0x2f, 0x73, 0x0a, 0xed, 0xd2, 0xdc, 0xe8, 0xdf, 0x02, 0x4b, 0xfe, 0x55, 0xda, 0x37, 0xfe, 0x4c, 0xb4, 0xce, 0x8f, 0x23, 0xb8, 0x2d, 0x1d, 0xde, 0x3b, 0x07, 0x48, 0x39, 0xee, 0x80, 0x36, 0x9f, 0x54, 0xfb, 0x99, 0xfe, 0xad, 0xac, 0x30, 0x08, 0x8e, 0x96, 0xdd, 0xca, 0x51, 0xd0, 0x69, 0xbd, 0x5d, 0xec, 0x34, 0x33, 0x41, 0xa9, 0x62, 0xbd, 0xac, 0x86, 0x83, 0x3e, 0xb9, 0x11, 0x2d, 0x35, 0xa8, 0xac, 0xba, 0x8c, 0xd2, 0xfe, 0x6c, 0xdf, 0xc5, 0xf6, 0x6b, 0xd8, 0x90, 0xc6, 0x9c, 0x6f, 0xf3, 0x49, 0x5d, 0x44, 0x89, 0xc7, 0x74, 0xdb, 0xe4, 0xf5, 0x7c, 0xe1, 0x82, 0x23, 0x74, 0x53, 0xf7, 0xa1, 0x22, 0xbf, 0xc0, 0xb7, 0xaf, 0xb6, 0xdf, 0x47, 0x15, 0x81, 0x64, 0x1c, 0xca, 0x21, 0xb9, 0x78, 0xa8, 0x70, 0x1d, 0x43, 0xdd, 0x54, 0xda, 0xd8, 0x51, 0xb9, 0xc8, 0x02, 0x7f, 0xe0, 0xaa, 0xd1, 0x35, 0x6c, 0x66, 0x2b, 0xff, 0x64, 0x5f, 0x82, 0xca, 0xd2, 0x09, 0xb8, 0x95, 0x23, 0xd5, 0x32, 0x31, 0xef, 0x30, 0xa3, 0x3b, 0x4d, 0xe5, 0xe2, 0x04, 0x07, 0xb6, 0x09, 0x1e, 0x4f, 0x87, 0xb8, 0x7e, 0xae, 0x34, 0x62, 0xe3, 0x73, 0x5c, 0xa8, 0x67, 0x76, 0x9f, 0xd2, 0xf5, 0x08, 0x36, 0x5a, 0x15, 0x42, 0x77, 0x54, 0x7f, 0x38, 0x54, 0x33, 0x11, 0xda, 0xf4, 0x96, 0x3e, 0x41, 0xcf, 0xe8, 0x59, 0x47, 0xb5, 0xd2, 0x77, 0x69, 0xf2, 0x7e, 0xd9, 0x12, 0x1c, 0xfa, 0x3f, 0x67, 0xc3, 0x12, 0x1e, 0x8d, 0x94, 0xa3, 0x5e, 0x8c, 0x5c, 0x89, 0x0d, 0xe6, 0xf4, 0x18, 0xeb, 0x8b, 0x03, 0x97, 0xd7, 0x3f, 0xb1, 0xda, 0x8d, 0x76, 0x2c, 0x04, 0x86, 0xd1, 0x20, 0xbf, 0x93, 0xa5, 0x79, 0xb3, 0xe2, 0x33, 0x22, 0xef, 0x14, 0x59, 0x03, 0x03, 0x7a, 0x05, 0xed, 0x30, 0x2c, 0x76, 0xf1, 0x12, 0x00, 0x5f, 0x0c, 0x13, 0xc0, 0xef, 0x79, 0xd6, 0xb6, 0x19, 0xa0, 0xb3, 0xc8, 0xf7, 0xbf, 0x29, 0xc7, 0x78, 0xcf, 0x6a, 0x39, 0x04, 0x1e, 0x78, 0x78, 0xbf, 0x52, 0xc1, 0x56, 0xfd, 0xd1, 0x2c, 0xcf, 0xe2, 0xb8, 0x6c, 0x2a, 0xbc, 0xdb, 0x57, 0xec, 0xba, 0x83, 0xaa, 0x07, 0xb4, 0xd4, 0x11, 0xe2, 0xbf, 0xa7, 0x24, 0x79, 0x3b, 0x84, 0x8c, 0x05, 0x4d, 0x94, 0xaf, 0x7a, 0x0d, 0x96, 0xc5, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x08, 0x5c, 0x89, 0xee, 0xed, 0x68, 0xbf, 0x74, 0xa2, 0x2c, 0x63, 0x0a, 0x80, 0x00, 0x01, 0x00, 0x25, 0x9f, 0xe4, 0x7b, 0xd4, 0xac, 0x7c, 0x3f, 0x34, 0xd5, 0xc5, 0xca, 0x11, 0xd2, 0xef, 0xbf, 0x83, 0xf5, 0x1e, 0x60, 0x48, 0x26, 0x6e, 0x5a, 0xe3, 0x03, 0xe5, 0x7e, 0xa4, 0xee, 0x66, 0xbb, 0x68, 0xcb, 0x37, 0x3a, 0x38, 0x3b, 0x0f, 0x99, 0x78, 0xa7, 0x2c, 0x99, 0x9c, 0xc3, 0xa6, 0xc5, 0x97, 0x72, 0x0e, 0x27, 0x48, 0xce, 0xe6, 0x5e, 0x98, 0xd0, 0xb2, 0xce, 0x4f, 0xf3, 0xff, 0xe7, 0x80, 0x16, 0xb9, 0x0a, 0x4f, 0xcc, 0x5c, 0xc6, 0x05, 0x48, 0xd2, 0xfa, 0x56, 0x3b, 0x7e, 0xe8, 0x8c, 0x67, 0x6a, 0xb0, 0x86, 0x95, 0x56, 0x86, 0x94, 0x7b, 0xac, 0x41, 0xef, 0xdf, 0x25, 0xcf, 0xc3, 0x91, 0xc7, 0x8e, 0xfb, 0xde, 0xca, 0x7b, 0xe5, 0x0c, 0x6d, 0x72, 0x27, 0xad, 0x5e, 0xd0, 0x57, 0x20, 0x99, 0x62, 0x02, 0x13, 0x26, 0x28, 0x1f, 0xc8, 0x38, 0x49, 0xcd, 0x80, 0xe6, 0x50, 0xea, 0xcd, 0x4c, 0xe9, 0x71, 0xfa, 0xca, 0xfd, 0xbd, 0xb1, 0xc5, 0x42, 0x31, 0x04, 0xa4, 0x79, 0x92, 0x55, 0xdf, 0x08, 0x8d, 0x42, 0x9d, 0xd6, 0x3c, 0x38, 0x0c, 0x5f, 0x3d, 0xf9, 0x0d, 0x41, 0xc4, 0xc5, 0xf7, 0x41, 0x9f, 0x37, 0x05, 0xc3, 0x94, 0xa6, 0x45, 0xbe, 0x9e, 0x32, 0x07, 0x64, 0x71, 0x2d, 0x04, 0x4c, 0x36, 0x89, 0xd3, 0xbc, 0x36, 0xfb, 0x40, 0x9c, 0x4f, 0x74, 0x34, 0xd1, 0x44, 0x75, 0x34, 0x3c, 0xf2, 0x2c, 0x89, 0x02, 0xe2, 0xc3, 0xb7, 0xcd, 0x37, 0x39, 0xa6, 0xf1, 0x5a, 0x84, 0xff, 0xfe, 0xbb, 0xd2, 0x50, 0xa8, 0x71, 0x16, 0xc7, 0xf1, 0x37, 0x4f, 0xc5, 0x2b, 0x14, 0x56, 0x7d, 0xd2, 0x94, 0xa3, 0x57, 0x36, 0xb0, 0xe6, 0x40, 0x6b, 0xa6, 0xbb, 0x43, 0xe9, 0x08, 0x41, 0x07, 0x65, 0x96, 0x7c, 0x60, 0xaa, 0x6d, 0x9b, 0xc6, 0x22, 0x56, 0x93, 0x55, 0x08, 0x60, 0x21, 0xa9, 0xd4, 0xda, 0x4d, 0x53, 0x05, 0x4f, 0xf1, 0x30, 0x06, 0xc4, 0x19, 0xa4, 0xc8, 0x2e, 0xd5, 0x82, 0xca, 0x79, 0xff, 0xea, 0x4f, 0x4f, 0xd4, 0xf7, 0x40, 0x84, 0x2e, 0x00, 0x23, 0xc6, 0x0a, 0xd8, 0xfc, 0xb8, 0x58, 0xd8, 0x71, 0xf6, 0xb0, 0xbc, 0x77, 0x7a, 0x12, 0xf2, 0xbe, 0xb1, 0xdb, 0x7f, 0xe4, 0xde, 0x2e, 0x49, 0x19, 0xb4, 0xc6, 0xee, 0xa0, 0x07, 0x51, 0x96, 0x81, 0xa1, 0xc2, 0x05, 0xac, 0xb5, 0x1d, 0x58, 0xc0, 0xba, 0x11, 0xb7, 0x93, 0xbf, 0x32, 0xd3, 0x0c, 0xb3, 0x0a, 0xbe, 0x96, 0xc7, 0x89, 0xfc, 0x89, 0x80, 0x4f, 0x59, 0x2a, 0xf8, 0xf4, 0x7d, 0x2c, 0x27, 0xf9, 0x3b, 0x6e, 0xc7, 0xc0, 0x86, 0x68, 0x08, 0xe8, 0x7f, 0x69, 0x22, 0xab, 0xe3, 0x1d, 0x1e, 0x5d, 0x2d, 0x0a, 0xd9, 0x40, 0x0b, 0x3d, 0x62, 0x18, 0xac, 0xf0, 0x77, 0x89, 0xd1, 0x96, 0x12, 0xdb, 0x07, 0xc5, 0x7e, 0xa3, 0xf5, 0x59, 0xcb, 0xd7, 0xae, 0x1d, 0x91, 0xf9, 0xb8, 0x62, 0xf4, 0x5f, 0x95, 0x79, 0x2f, 0x3c, 0x2e, 0x53, 0xb5, 0x85, 0xe0, 0x60, 0x68, 0x5b, 0x30, 0xf3, 0xa7, 0xd9, 0xc0, 0x69, 0x4b, 0xf7, 0x80, 0x5b, 0x52, 0x3d, 0xf8, 0x82, 0x9e, 0x08, 0xfb, 0xfe, 0x1d, 0x19, 0xc9, 0xd0, 0xb6, 0xaf, 0x56, 0x55, 0xd4, 0x92, 0x3b, 0x74, 0x5b, 0x71, 0x4b, 0x59, 0x36, 0xed, 0x8b, 0x40, 0xb8, 0xd6, 0xd2, 0x84, 0x49, 0x86, 0x29, 0x3d, 0x7e, 0xb7, 0x54, 0xdc, 0x87, 0xf8, 0xc9, 0x05, 0x8a, 0xef, 0x58, 0x80, 0x6b, 0xa3, 0x93, 0x04, 0x85, 0x7c, 0x88, 0xbf, 0x7b, 0xba, 0x22, 0xa5, 0x7f, 0x6f, 0xd3, 0x3e, 0xa2, 0x10, 0x8c, 0x7d, 0x7c, 0x93, 0x72, 0xb4, 0xb0, 0xed, 0xd7, 0x5e, 0xd4, 0x7c, 0x6b, 0x2b, 0x56, 0xca, 0x72, 0x14, 0xbd, 0xef, 0xe9, 0xb5, 0xa8, 0x49, 0x96, 0x62, 0x08, 0x09, 0x2c, 0xdd, 0xe7, 0xc7, 0x93, 0x7c, 0x58, 0x0b, 0x39, 0x94, 0x4b, 0xac, 0x82, 0x88, 0x3a, 0x99, 0x17, 0x21, 0xad, 0x27, 0x9a, 0xb4, 0x30, 0x72, 0x88, 0x4c, 0x17, 0x42, 0xe8, 0x34, 0xb9, 0xcb, 0x0d, 0xc8, 0x16, 0x18, 0xde, 0x70, 0xdd, 0xdf, 0x95, 0x36, 0xd1, 0x95, 0x98, 0x69, 0x42, 0x84, 0x11, 0xfa, 0xd1, 0xee, 0x17, 0xe5, 0x3d, 0xdc, 0x85, 0x6d, 0x05, 0xfe, 0x8c, 0x80, 0x48, 0x8e, 0x8a, 0xdc, 0xe7, 0x67, 0x16, 0x81, 0xeb, 0xfc, 0xae, 0x00, 0x3f, 0x23, 0xdc, 0xd1, 0x75, 0x12, 0x7c, 0x70, 0x71, 0xb6, 0x4b, 0xdd, 0xc8, 0x1e, 0x2f, 0x28, 0x62, 0x53, 0x72, 0xfe, 0x9e, 0x07, 0xe4, 0x5e, 0x23, 0x1a, 0xb5, 0xf0, 0xd9, 0xd7, 0xcb, 0x21, 0x22, 0xa8, 0x2b, 0x48, 0x46, 0xed, 0x96, 0xfc, 0x01, 0x65, 0x91, 0xe4, 0x5b, 0xd9, 0x9d, 0xdd, 0x74, 0x0c, 0x49, 0x11, 0x16, 0x60, 0x08, 0x14, 0x95, 0x88, 0x4f, 0x2a, 0xb6, 0x1a, 0x7a, 0x1c, 0x15, 0x79, 0xd2, 0x7e, 0xbc, 0xa3, 0x4f, 0x58, 0xd8, 0xa9, 0x85, 0xf4, 0xf5, 0x78, 0x72, 0x78, 0xd5, 0xcd, 0x61, 0x24, 0xef, 0x3b, 0xcf, 0x88, 0xa9, 0x86, 0xd5, 0xea, 0x8e, 0x66, 0xbb, 0x9d, 0x68, 0xd4, 0xca, 0x8a, 0x9b, 0x71, 0x31, 0x7c, 0x30, 0x9d, 0xb3, 0x33, 0xc4, 0xfd, 0xfb, 0x5d, 0xae, 0xe5, 0x8f, 0x63, 0xcd, 0xa0, 0x09, 0x5d, 0x8d, 0x63, 0xf3, 0xba, 0xa1, 0x80, 0xa5, 0xe3, 0x53, 0x84, 0x2c, 0x37, 0x4f, 0xf1, 0x82, 0xb6, 0x3a, 0x74, 0x12, 0xc3, 0xc2, 0x76, 0x3d, 0x2e, 0x87, 0x8b, 0xd6, 0x47, 0x38, 0x8b, 0x04, 0x19, 0x74, 0x86, 0xc5, 0x0e, 0xd0, 0x8c, 0xd4, 0x2c, 0xeb, 0x75, 0xfc, 0xa6, 0xaf, 0x3e, 0xdf, 0xac, 0x41, 0x17, 0x69, 0x91, 0x16, 0x73, 0xe0, 0x0e, 0xdc, 0x4b, 0x41, 0xa3, 0x7a, 0x72, 0x2c, 0xaa, 0xa7, 0xb6, 0x10, 0x3f, 0x51, 0x8c, 0xfe, 0xdb, 0x64, 0x2b, 0x3f, 0xee, 0xca, 0x94, 0x74, 0x13, 0x53, 0xa3, 0xa8, 0x8f, 0x5f, 0x6b, 0x4e, 0xbd, 0x8c, 0xc9, 0xd2, 0x9f, 0x4f, 0x01, 0xf0, 0xec, 0xdd, 0x4b, 0x77, 0x01, 0xd8, 0x7f, 0x16, 0x37, 0xec, 0xbf, 0xee, 0x54, 0xcc, 0xf0, 0x8d, 0xb0, 0x34, 0xc6, 0x31, 0x2d, 0xea, 0x07, 0x31, 0xe8, 0xdd, 0x45, 0x9b, 0x35, 0x47, 0x56, 0xe4, 0x61, 0x2f, 0xfe, 0x45, 0xdf, 0xde, 0xe8, 0x59, 0x25, 0x75, 0x75, 0xef, 0x69, 0x66, 0x33, 0x89, 0x9e, 0x2b, 0x8a, 0x44, 0x04, 0x6a, 0x81, 0x50, 0xad, 0x78, 0x39, 0xad, 0x16, 0x3e, 0x21, 0xd6, 0xbd, 0x98, 0x7e, 0x58, 0x25, 0xf5, 0x31, 0x3f, 0xcc, 0xce, 0xd0, 0x7d, 0x62, 0xe2, 0x2b, 0x3e, 0xe5, 0xec, 0xe3, 0x3b, 0xb5, 0x3d, 0x34, 0xc7, 0xf8, 0x64, 0x4b, 0x8c, 0xa2, 0xd8, 0x28, 0x32, 0xaa, 0xa7, 0x16, 0x55, 0xd8, 0xe8, 0x44, 0x20, 0x90, 0xc8, 0xb1, 0x7c, 0xbb, 0x99, 0xd1, 0xb1, 0x2d, 0x7a, 0x25, 0xaa, 0x85, 0x0d, 0xb1, 0xaf, 0x71, 0x66, 0x9a, 0x12, 0xc9, 0xe6, 0xc2, 0x27, 0xea, 0x69, 0x7d, 0xd3, 0x42, 0x6a, 0x3e, 0x22, 0x47, 0x62, 0xa6, 0x46, 0x21, 0xd3, 0xbe, 0x9d, 0x83, 0x0c, 0xcf, 0xa6, 0x11, 0xe7, 0x7a, 0xfd, 0x15, 0xd1, 0x9b, 0xd4, 0x2b, 0x0b, 0x16, 0x93, 0x68, 0x49, 0xdc, 0xd2, 0x00, 0x92, 0x1d, 0x08, 0xb2, 0x2f, 0x84, 0xfd, 0xda, 0xc0, 0xb1, 0x90, 0xf2, 0x63, 0x9c, 0xe0, 0x9e, 0x16, 0x91, 0x18, 0xc1, 0x6e, 0xc0, 0xde, 0xbf, 0x1d, 0xa3, 0xa7, 0xaa, 0x24, 0xde, 0x41, 0x1a, 0x8a, 0x77, 0xba, 0x18, 0xcc, 0x0e, 0xc7, 0x77, 0x34, 0x07, 0xaa, 0xfb, 0xfd, 0x6e, 0x6e, 0x8b, 0x34, 0x52, 0x20, 0x9d, 0x1c, 0xdf, 0x0e, 0x57, 0xef, 0x8c, 0xba, 0xcc, 0x26, 0x00, 0x7c, 0xf5, 0xf6, 0x23, 0x3e, 0x9b, 0x48, 0x54, 0xe3, 0x9c, 0x6e, 0xdc, 0x98, 0x79, 0xa7, 0xc4, 0x74, 0x7e, 0x15, 0xcf, 0x0e, 0x9c, 0xae, 0xca, 0xaa, 0x98, 0xbf, 0xaf, 0xd7, 0x0a, 0x05, 0x6d, 0xf7, 0xf3, 0xcb, 0x7c, 0x07, 0x03, 0x2a, 0x11, 0xc0, 0x66, 0x8e, 0x27, 0x16, 0xfc, 0x67, 0x58, 0x6f, 0x01, 0x21, 0x92, 0xe4, 0x68, 0x0c, 0x80, 0x97, 0x37, 0xb8, 0x1e, 0xd2, 0x07, 0x57, 0x8f, 0xe0, 0x3f, 0xc3, 0xda, 0xf1, 0x22, 0x43, 0xc2, 0x95, 0xa8, 0x0d, 0xd5, 0x7c, 0xc9, 0x8a, 0x87, 0x14, 0xd7, 0xb2, 0xb9, 0x4e, 0xdf, 0x5b, 0xd1, 0x59, 0xe5, 0xe4, 0x99, 0x8f, 0xa4, 0x8d, 0xdc, 0x2a, 0x6f, 0x87, 0x00, 0x22, 0x23, 0xe0, 0x58, 0xff, 0xf6, 0x37, 0xea, 0x7a, 0xbf, 0x38, 0x98, 0x56, 0x64, 0xb6, 0x01, 0xc3, 0x7a, 0x43, 0x1b, 0x15, 0x96, 0x80, 0x69, 0xc2, 0xa8, 0x9c, 0xac, 0x79, 0x8b, 0x2a, 0xd6, 0x58, 0xa3, 0x0b, 0x9a, 0x41, 0xc3, 0x57, 0xc0, 0x91, 0x91, 0x2a, 0x68, 0x23, 0xd2, 0x0f, 0xeb, 0x80, 0x16, 0xf0, 0x0c, 0xff, 0x96, 0xb4, 0x4f, 0x31, 0x65, 0xd7, 0x3d, 0xc5, 0xaf, 0xf6, 0x68, 0x15, 0xa5, 0x34, 0x3a, 0xb7, 0x34, 0x16, 0x0d, 0x42, 0xda, 0xde, 0x1e, 0x4a, 0x3c, 0xde, 0x3a, 0xd0, 0x5e, 0x25, 0xb6, 0x95, 0x8c, 0x07, 0x8d, 0xd6, 0x22, 0xac, 0x83, 0x75, 0x64, 0xa4, 0x8d, 0x8a, 0xa4, 0x60, 0x21, 0xb7, 0x1d, 0xf5, 0xc9, 0xb0, 0x0c, 0xca, 0xfe, 0x63, 0xd8, 0x16, 0xb2, 0xad, 0x63, 0xc4, 0xdb, 0x61, 0x36, 0xb0, 0x11, 0x29, 0xe6, 0xa2, 0xfe, 0xdf, 0x66, 0x89, 0x3f, 0xa9, 0xe0, 0xfc, 0x4e, 0x6b, 0x83, 0xdd, 0x75, 0x09, 0x66, 0x36, 0x20, 0x95, 0xb3, 0xdb, 0x3c, 0x9b, 0xba, 0x3e, 0xb4, 0xe8, 0xad, 0xc2, 0x24, 0xee, 0xb3, 0x8d, 0xc3, 0xa8, 0x0b, 0xa2, 0x95, 0x0d, 0xd0, 0x68, 0xba, 0x64, 0x4b, 0xdb, 0x20, 0x10, 0xb7, 0xcc, 0x1d, 0xb9, 0x3a, 0x4c, 0xf5, 0x7c, 0xf5, 0xd6, 0x24, 0x82, 0xec, 0x17, 0x3e, 0x1f, 0x7b, 0xdd, 0x63, 0xfe, 0xc3, 0xed, 0x93, 0x34, 0x78, 0x90, 0xe4, 0xe1, 0xec, 0xa8, 0x3d, 0xb9, 0x52, 0xee, 0x7b, 0x1b, 0xf6, 0x33, 0x53, 0x5b, 0xb9, 0x08, 0x96, 0x04, 0x9e, 0xdd, 0xe1, 0x6f, 0xbe, 0x59, 0xe0, 0xdf, 0x8a, 0x9a, 0x2c, 0x64, 0x17, 0xf1, 0x2d, 0x86, 0x1e, 0xe1, 0x66, 0xa9, 0xe6, 0xaa, 0x81, 0x2c, 0xca, 0x6f, 0xc9, 0xe1, 0xe1, 0x48, 0x0b, 0xdf, 0xe6, 0x39, 0xda, 0xd0, 0x14, 0x45, 0x44, 0x6b, 0x96, 0xe1, 0xb2, 0xbb, 0x78, 0xaa, 0x40, 0xe5, 0x89, 0xba, 0x56, 0x83, 0x8b, 0x70, 0x80, 0x18, 0xa4, 0x9f, 0x4c, 0xb2, 0x94, 0x72, 0xdf, 0x42, 0xa6, 0x3b, 0xd4, 0xe4, 0x88, 0x97, 0x29, 0x4f, 0x82, 0xa1, 0xde, 0xfd, 0x6c, 0x4c, 0xdc, 0x01, 0x6b, 0x1f, 0x89, 0x31, 0x6b, 0x71, 0x87, 0x7f, 0xe8, 0x9f, 0xf3, 0xd4, 0x0b, 0xc8, 0x0e, 0x1c, 0xc8, 0x4f, 0x4c, 0x66, 0xe3, 0x86, 0x7c, 0xb4, 0x8b, 0x24, 0xb6, 0x3d, 0x65, 0x13, 0x97, 0xea, 0x3f, 0x3c, 0xfd, 0x02, 0x1f, 0xe6, 0x10, 0x0f, 0x6d, 0xc4, 0xe3, 0x5a, 0x82, 0xd5, 0x61, 0xc1, 0xd3, 0x72, 0x66, 0x7f, 0xf4, 0x0c, 0xe5, 0x9f, 0x98, 0x04, 0xd2, 0x59, 0x5b, 0x89, 0xe1, 0xc4, 0x13, 0x66, 0x8b, 0xed, 0x49, 0x44, 0x06, 0x00, 0x0a, 0xab, 0x6b, 0x45, 0x4a, 0x44, 0x51, 0xcc, 0x02, 0x0b, 0x86, 0x9f, 0xdb, 0xb2, 0xa7, 0x49, 0x58, 0x09, 0xdc, 0x81, 0x79, 0x5b, 0x7c, 0x36, 0x8c, 0x8f, 0x88, 0x37, 0x8e, 0x22, 0x3e, 0x39, 0xb6, 0xa7, 0x46, 0x8f, 0x4f, 0x2c, 0x3c, 0x7a, 0xe8, 0xf2, 0xaf, 0x1b, 0x34, 0xa7, 0xf7, 0x1f, 0x3f, 0x94, 0x6e, 0xba, 0xb5, 0xae, 0xdb, 0xe5, 0xfa, 0x7d, 0x41, 0x3f, 0xfe, 0xdd, 0x67, 0x62, 0x31, 0xa7, 0xdf, 0x7f, 0x0a, 0x81, 0xcb, 0x56, 0x0f, 0xde, 0x40, 0x0c, 0x75, 0x22, 0xe4, 0x06, 0x0a, 0x97, 0x9a, 0x81, 0x33, 0xa3, 0xd5, 0xa9, 0x5d, 0xb6, 0x4b, 0xf9, 0x11, 0x9b, 0xb7, 0xac, 0x7a, 0xc9, 0x70, 0x33, 0x9a, 0xa8, 0x63, 0x13, 0x84, 0x94, 0x69, 0x41, 0xf1, 0x82, 0xf3, 0x03, 0x6c, 0x27, 0x64, 0xb9, 0xfc, 0x8c, 0xc9, 0x66, 0x6d, 0x89, 0x87, 0x64, 0x04, 0xa8, 0xf6, 0x93, 0x5e, 0x0a, 0x73, 0x9e, 0xcb, 0xee, 0x60, 0x15, 0x04, 0xeb, 0x9b, 0x7e, 0x8d, 0x51, 0x45, 0xc5, 0x54, 0x3e, 0x51, 0xdb, 0x7e, 0x07, 0x51, 0x0d, 0x1d, 0x2f, 0xf8, 0x9f, 0xe7, 0xef, 0x0b, 0x13, 0x4d, 0xa2, 0x37, 0xdd, 0x38, 0x7d, 0x8c, 0x97, 0x2f, 0xcb, 0xbd, 0x1b, 0x56, 0x61, 0xd1, 0xe3, 0x6c, 0x02, 0x4f, 0x6b, 0x41, 0xff, 0xc9, 0xa5, 0x49, 0x0f, 0x92, 0x03, 0x1b, 0x32, 0xc4, 0xdf, 0x84, 0x02, 0x4f, 0x59, 0x47, 0xe4, 0x1d, 0xd2, 0x95, 0x5f, 0x6c, 0x9f, 0x3e, 0x2c, 0x6a, 0xd4, 0xae, 0x69, 0x5a, 0xed, 0xc6, 0xcb, 0x92, 0xee, 0xb8, 0x58, 0x0e, 0x51, 0xed, 0x72, 0xab, 0x58, 0xa4, 0x11, 0x5c, 0x02, 0x9f, 0xb0, 0xce, 0x88, 0x7d, 0x35, 0xca, 0xa3, 0xd9, 0xc7, 0x62, 0x75, 0x36, 0xc4, 0xe4, 0xec, 0x00, 0x00, 0x01, 0xb7 }; +constexpr AccessUnit M2V_MULTIPLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, true, 0, {}, { 0xb2, 0x5b, 0x25, 0x1f, 0x28, 0xf8, 0x1e, 0x80, 0xc8, 0x72, 0x45, 0xed, 0xef, 0xe9, 0xb6, 0x08, 0x83, 0x20, 0xcd, 0x25 } }; +constexpr AccessUnit M2V_MULTIPLE_PACK_EXPECTED_AU_2 = { 0x28144c47, 0x7e4e68d4, true, 0, {}, { 0xee, 0x18, 0x40, 0x38, 0x32, 0xb8, 0x2d, 0xa4, 0xe1, 0x8b, 0x09, 0x40, 0xfa, 0x8e, 0xfa, 0x64, 0x8f, 0xcd, 0x7c, 0x40 } }; +constexpr AccessUnit M2V_MULTIPLE_PACK_EXPECTED_AU_3 = { 0x15f90, 0x159b2, true, 0, {}, { 0xf5, 0x44, 0x36, 0x81, 0xe4, 0xff, 0x09, 0x3d, 0xd8, 0x28, 0xd3, 0x2b, 0xf2, 0x04, 0x7e, 0xdb, 0x90, 0x88, 0x6d, 0xdb } }; +constexpr AccessUnit M2V_MULTIPLE_PACK_EXPECTED_AU_4 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0xd4, 0x82, 0xc6, 0x8d, 0x56, 0x01, 0x42, 0x07, 0x40, 0x81, 0xeb, 0x25, 0xc5, 0xc9, 0x24, 0x24, 0x0e, 0x24, 0x39, 0x4c } }; + +constexpr std::array ATRACX_MULTIPLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xb7, 0x4a, 0xf6, 0xa9, 0xd4, 0x7b, 0x0f, 0xd0, 0x00, 0xd7, 0xe7, 0x12, 0x43, 0x4c, 0x62, 0x89, 0xa6, 0xc2, 0x04, 0x70, 0x0e, 0x0b, 0xab, 0x92, 0x01, 0x5f, 0x90, 0x5c, 0x0a, 0x9a, 0xc6, 0x0c, 0x34, 0x34, 0xae, 0x42, 0x9c, 0x20, 0x2e, 0x01, 0x84, 0xbf, 0xf9, 0x16, 0x5f, 0xab, 0xb7, 0x91, 0xaa, 0xb2, 0x45, 0xc8, 0x5d, 0x83, 0x75, 0xbc, 0x67, 0xfe, 0xe8, 0x4f, 0xa0, 0xf6, 0x59, 0x01, 0xbb, 0xeb, 0x58, 0x61, 0x1f, 0x2b, 0xe1, 0x29, 0x19, 0x59, 0x6f, 0xfc, 0x3b, 0x04, 0xb2, 0xb2, 0x44, 0x0a, 0xa3, 0xd7, 0x5c, 0x13, 0x8c, 0x75, 0x89, 0x3a, 0x08, 0xce, 0xe8, 0x88, 0xc7, 0x31, 0xf1, 0x29, 0x74, 0x45, 0xc8, 0x34, 0x89, 0xac, 0xc3, 0x83, 0x21, 0xed, 0x1c, 0x70, 0xea, 0x47, 0x92, 0x74, 0x8f, 0xa7, 0x81, 0xf6, 0xae, 0x70, 0x64, 0xae, 0xb0, 0x73, 0xad, 0x6e, 0x54, 0xcb, 0x98, 0x46, 0x39, 0x41, 0x45, 0x11, 0xad, 0xde, 0xb6, 0xdd, 0xa9, 0xe3, 0x8c, 0xf8, 0xe2, 0x3d, 0x73, 0x98, 0x81, 0xf3, 0x70, 0x8e, 0x7e, 0xe1, 0x5a, 0x1f, 0x25, 0x47, 0x01, 0x74, 0x39, 0xf0, 0xe7, 0x85, 0x9d, 0x37, 0x6e, 0x12, 0xed, 0x4d, 0x12, 0x52, 0x21, 0xc1, 0xc2, 0xf8, 0x68, 0xc1, 0xf9, 0x18, 0x0d, 0xda, 0x8c, 0x6f, 0x30, 0xa2, 0x39, 0x5d, 0x4d, 0x7f, 0x7f, 0x93, 0x72, 0xf6, 0x17, 0x9a, 0xd5, 0xa0, 0x03, 0x64, 0xfe, 0x33, 0xfd, 0xc9, 0x9d, 0xab, 0x30, 0xb1, 0x04, 0x4c, 0x85, 0x95, 0xb5, 0x00, 0xb4, 0x53, 0x8c, 0xbb, 0x93, 0x99, 0x86, 0x47, 0x51, 0x94, 0x39, 0x96, 0xcc, 0x87, 0x1a, 0x05, 0xdf, 0xff, 0xbe, 0x29, 0xa3, 0x76, 0xe4, 0x4a, 0xcd, 0xaf, 0x68, 0xc8, 0xb2, 0x30, 0x03, 0x29, 0x51, 0x15, 0xd5, 0xb1, 0x75, 0xcb, 0x5d, 0xba, 0xfe, 0x04, 0x98, 0x48, 0x86, 0xe1, 0xcc, 0x16, 0x3d, 0x9e, 0x3c, 0xab, 0x8f, 0xf2, 0xec, 0x05, 0x24, 0xeb, 0xa8, 0x17, 0xd5, 0xe1, 0x86, 0xac, 0x86, 0x49, 0xb8, 0xe3, 0xc5, 0x88, 0x2e, 0xc7, 0xaf, 0xa4, 0xa9, 0x56, 0x98, 0x82, 0xc3, 0x1f, 0x4c, 0x4c, 0x39, 0x3a, 0x40, 0x58, 0x16, 0x1f, 0x75, 0x35, 0x47, 0x13, 0x82, 0x58, 0xb6, 0xd7, 0xb6, 0x31, 0xed, 0x6e, 0x4b, 0x69, 0x7e, 0x9d, 0x51, 0xc6, 0x43, 0x95, 0x42, 0xd7, 0xe7, 0x25, 0x94, 0x3c, 0xf0, 0xd1, 0xc8, 0x2d, 0xac, 0x01, 0x31, 0x1a, 0x13, 0xe5, 0xf7, 0x0f, 0x51, 0xef, 0x59, 0xb8, 0x36, 0x02, 0xe1, 0xe5, 0x47, 0x98, 0x36, 0x90, 0x2a, 0xc0, 0x20, 0x50, 0xc4, 0xe2, 0x32, 0x02, 0xa0, 0xe2, 0x91, 0x7d, 0x6f, 0x54, 0xf0, 0x84, 0xf9, 0xcb, 0xcf, 0x8b, 0x45, 0xe9, 0x88, 0x5c, 0xa9, 0x56, 0xe5, 0x3a, 0x0d, 0x98, 0x9d, 0xf8, 0x23, 0x3d, 0x90, 0xd6, 0x59, 0x5e, 0x1c, 0xdc, 0x4e, 0x4a, 0x70, 0x80, 0x20, 0xdb, 0x52, 0x8a, 0x08, 0x69, 0xea, 0x78, 0x2f, 0xf9, 0x72, 0xd5, 0x66, 0x1d, 0x9f, 0x9f, 0xc0, 0x00, 0xd0, 0x55, 0x55, 0xd1, 0x5d, 0xbd, 0x3a, 0x41, 0x13, 0x39, 0x71, 0x1c, 0xbc, 0x9a, 0xd1, 0x00, 0x17, 0x6a, 0xfd, 0x81, 0xea, 0xb6, 0x83, 0xb9, 0xb0, 0x86, 0xd2, 0x65, 0xcc, 0xc8, 0x2b, 0x15, 0x07, 0xb1, 0x13, 0xde, 0xf5, 0x30, 0xac, 0x48, 0x75, 0xd9, 0x50, 0xee, 0x0a, 0x12, 0x91, 0x4d, 0x5f, 0xe8, 0x0d, 0x6c, 0xc3, 0x50, 0xc8, 0xdb, 0x33, 0x2c, 0x9f, 0x01, 0xe9, 0x97, 0xe9, 0xb8, 0x4d, 0x18, 0xe2, 0xcd, 0x65, 0x6d, 0xcb, 0x18, 0x46, 0x00, 0x1f, 0x76, 0x96, 0x22, 0x5d, 0x1d, 0xa7, 0x6c, 0xf6, 0x5c, 0x4f, 0x60, 0xf0, 0xe3, 0x47, 0xd5, 0xef, 0x8f, 0xba, 0x37, 0xcc, 0xc0, 0x33, 0xe9, 0x60, 0x0f, 0xaa, 0x8d, 0x13, 0xca, 0xda, 0x04, 0xd7, 0xab, 0x51, 0x1d, 0x9d, 0x11, 0x5e, 0x9b, 0x7b, 0x59, 0x65, 0x4e, 0x43, 0x47, 0x84, 0xb2, 0x2a, 0x9c, 0xb3, 0x9f, 0x1d, 0x6e, 0x72, 0x3b, 0x18, 0x30, 0x19, 0xee, 0xba, 0xd7, 0x50, 0xce, 0x76, 0x3e, 0x6d, 0xe2, 0x68, 0xd3, 0xe3, 0xa8, 0x26, 0x63, 0x1b, 0x3b, 0xbb, 0x03, 0x15, 0x6d, 0xb7, 0x6b, 0x84, 0x38, 0xe7, 0x6e, 0xc0, 0x0f, 0x24, 0x60, 0x81, 0xfc, 0x3d, 0x4c, 0x07, 0xd9, 0xb4, 0xd0, 0x87, 0xc5, 0x1c, 0xb4, 0x2c, 0x8a, 0xe4, 0xb3, 0xbc, 0x0e, 0x39, 0xd5, 0x8b, 0xd3, 0xd1, 0xaf, 0x5c, 0x48, 0x54, 0x2e, 0xa9, 0xeb, 0x49, 0xa4, 0x36, 0x6f, 0x60, 0xc4, 0x88, 0x48, 0x75, 0xc2, 0x9b, 0xf7, 0x61, 0x15, 0x1e, 0xd4, 0x8d, 0x70, 0xbe, 0x73, 0x6d, 0xb7, 0x5c, 0x8b, 0xd1, 0x30, 0x61, 0x0e, 0xec, 0x5d, 0x9a, 0x03, 0x19, 0xc9, 0xe4, 0xce, 0x65, 0x38, 0xee, 0x2c, 0xf5, 0x58, 0x84, 0x83, 0x9a, 0x8f, 0xe1, 0x73, 0xe6, 0x38, 0x04, 0xc1, 0x54, 0x6b, 0xde, 0x22, 0x73, 0xd6, 0x3c, 0x91, 0x91, 0x38, 0x64, 0xda, 0x6e, 0x39, 0x76, 0xac, 0x85, 0x46, 0x48, 0x3b, 0x50, 0x17, 0x7f, 0x55, 0xd0, 0x50, 0x96, 0xbc, 0xb0, 0x46, 0xc4, 0x9e, 0xfb, 0xfe, 0xaf, 0xc4, 0x50, 0xb5, 0xc8, 0xfd, 0x38, 0xda, 0x50, 0xde, 0x46, 0x8f, 0xa7, 0x9f, 0xb2, 0x75, 0x04, 0x8f, 0xad, 0x7e, 0x95, 0x51, 0x53, 0xb2, 0xb0, 0xe4, 0xde, 0x31, 0x90, 0x5c, 0x2d, 0x34, 0x5c, 0xc7, 0x8e, 0x77, 0x39, 0xc8, 0xe8, 0x18, 0x66, 0x30, 0x38, 0x87, 0xc9, 0x6d, 0x27, 0x80, 0xf9, 0x89, 0xf3, 0xcf, 0x6b, 0x40, 0x20, 0x39, 0x29, 0x3e, 0xb2, 0xf7, 0x05, 0x08, 0x0e, 0xdc, 0x34, 0xf5, 0x9e, 0x55, 0xce, 0xa5, 0xa7, 0x1a, 0xaf, 0xcf, 0x1c, 0x1a, 0x4d, 0x89, 0x94, 0x4f, 0x02, 0x7b, 0x0c, 0x5a, 0xb9, 0x8e, 0x62, 0x76, 0x1d, 0x3e, 0x5b, 0x74, 0x2f, 0x8d, 0x7c, 0x54, 0xf4, 0x54, 0x3c, 0x9a, 0x0c, 0xde, 0x7a, 0xfc, 0x65, 0x39, 0x95, 0x39, 0xf8, 0x93, 0xde, 0x57, 0x32, 0x41, 0xfd, 0xb4, 0x15, 0xc4, 0xce, 0xdb, 0xab, 0x6d, 0xf5, 0x37, 0x0c, 0x1a, 0x92, 0x4b, 0xe9, 0xb5, 0xfc, 0x82, 0xa7, 0x3e, 0xee, 0xa2, 0x98, 0x85, 0xfb, 0x52, 0xdb, 0xd8, 0x19, 0xf2, 0xdf, 0xe1, 0xcb, 0x62, 0x57, 0x49, 0x80, 0x74, 0x63, 0xfc, 0x1e, 0xbb, 0x8b, 0x8f, 0xf7, 0x7a, 0x7b, 0xd8, 0x43, 0x14, 0x25, 0xb9, 0x13, 0x33, 0xed, 0x32, 0x7c, 0x92, 0x18, 0x05, 0xd0, 0xe6, 0xc9, 0xd2, 0x6d, 0x7b, 0xb8, 0x4c, 0x87, 0x51, 0x53, 0xde, 0x07, 0x41, 0xc7, 0xa7, 0x18, 0xb5, 0x78, 0xa6, 0x62, 0xa3, 0x75, 0xaf, 0x58, 0x52, 0x11, 0x0b, 0xd6, 0x8d, 0x6e, 0x37, 0x07, 0x88, 0xa7, 0x12, 0xaa, 0xce, 0x58, 0xa2, 0x9e, 0x50, 0x3b, 0x58, 0xfe, 0xff, 0x96, 0x37, 0x85, 0xec, 0xa9, 0x0b, 0x64, 0x5a, 0x2d, 0xbc, 0xa4, 0x57, 0x25, 0x4e, 0x97, 0x4d, 0x0c, 0x81, 0xeb, 0xb7, 0xa1, 0xcc, 0x1e, 0x27, 0x8c, 0xd5, 0x80, 0x41, 0x52, 0xab, 0x0b, 0x89, 0xa3, 0x97, 0x27, 0xeb, 0x17, 0x32, 0x92, 0x9c, 0x83, 0x81, 0xaf, 0x7e, 0xb4, 0x5e, 0x80, 0xa3, 0x6e, 0x68, 0x13, 0x49, 0x82, 0x8b, 0x80, 0x23, 0xb2, 0x5e, 0x4c, 0x6f, 0xb1, 0x51, 0xb9, 0xf8, 0xb6, 0xea, 0xbf, 0x07, 0x7a, 0x3c, 0x47, 0x69, 0x68, 0xc1, 0x14, 0xc7, 0xfe, 0x46, 0x74, 0x47, 0x5c, 0xca, 0xb7, 0x80, 0xa8, 0xeb, 0x0b, 0x78, 0x4e, 0xd0, 0x95, 0x56, 0x8d, 0x65, 0x21, 0x03, 0xf1, 0x2a, 0x3f, 0xcf, 0x7a, 0xd2, 0x7c, 0xef, 0x5c, 0xcf, 0x22, 0x6f, 0x88, 0xf2, 0x40, 0xec, 0x02, 0x0c, 0x5e, 0xd4, 0x2a, 0x9e, 0x07, 0xfc, 0x51, 0xd4, 0xf2, 0x10, 0xcc, 0x27, 0xee, 0x69, 0xbf, 0xbd, 0x72, 0x9c, 0x72, 0xf2, 0x99, 0x11, 0xf9, 0xd0, 0xcf, 0x56, 0x4a, 0x56, 0x2c, 0x10, 0x94, 0x08, 0x41, 0x84, 0xc5, 0x06, 0x07, 0x30, 0x13, 0x29, 0x9a, 0xc3, 0x06, 0x97, 0xe4, 0x1d, 0x61, 0x4d, 0x78, 0x93, 0xe3, 0xd5, 0xf7, 0xc8, 0xd1, 0x21, 0x76, 0x14, 0x25, 0xaf, 0xb6, 0x3d, 0x74, 0x48, 0xa8, 0xcb, 0x4d, 0x99, 0x03, 0xed, 0xc1, 0x37, 0x2e, 0x80, 0x05, 0xab, 0x79, 0xdc, 0x74, 0x60, 0x79, 0x35, 0xf0, 0xb2, 0xac, 0x20, 0xe5, 0xb2, 0x7a, 0x31, 0xe0, 0x74, 0x9b, 0x3d, 0x95, 0x22, 0x97, 0xa0, 0x28, 0x6f, 0x30, 0x2c, 0x0b, 0xe5, 0x5e, 0x9b, 0x01, 0xef, 0x46, 0xf4, 0x4b, 0xd9, 0x83, 0xe7, 0xcc, 0x56, 0x92, 0x5a, 0x96, 0xa1, 0x57, 0xcb, 0xec, 0xa4, 0x32, 0x0e, 0x5d, 0xc7, 0x3e, 0x5b, 0x8b, 0x67, 0xca, 0xd9, 0xf9, 0xb0, 0xfb, 0x11, 0xe5, 0x12, 0x0e, 0xe4, 0xf0, 0x98, 0x91, 0xbf, 0x1d, 0xea, 0x2d, 0x7a, 0x4a, 0x29, 0x85, 0x14, 0x75, 0xb1, 0xa9, 0x5c, 0xd2, 0xb1, 0x79, 0x01, 0x70, 0xcf, 0x83, 0x01, 0xff, 0xc0, 0x1d, 0xa5, 0x28, 0xd3, 0x38, 0xe6, 0xc5, 0x38, 0xee, 0x0b, 0xf7, 0x13, 0x10, 0x34, 0xf1, 0x78, 0xfc, 0x93, 0x14, 0xa4, 0x7a, 0x03, 0x0a, 0x77, 0x30, 0xae, 0xe2, 0x48, 0x78, 0x4e, 0x2c, 0xff, 0xac, 0x83, 0xee, 0x31, 0x6d, 0x49, 0x55, 0x41, 0x4a, 0x4a, 0x44, 0xf4, 0x65, 0x66, 0x79, 0xb0, 0x05, 0xb7, 0xf9, 0x9b, 0xb4, 0x12, 0xb8, 0xa0, 0xdc, 0xe2, 0xec, 0x90, 0x5b, 0xca, 0x70, 0xdf, 0xb0, 0x2c, 0x63, 0x75, 0x6b, 0x2a, 0x4c, 0x14, 0x3d, 0x0e, 0x01, 0x6b, 0x1a, 0x25, 0xac, 0x9b, 0x4b, 0x2b, 0x62, 0x1d, 0x71, 0xb8, 0x86, 0x65, 0x16, 0xc4, 0x37, 0xa2, 0x7f, 0x63, 0x43, 0xb9, 0xf9, 0x44, 0x2b, 0x0a, 0x32, 0xa8, 0xdd, 0x7d, 0x33, 0x1d, 0xb7, 0xae, 0x6a, 0x97, 0xa2, 0xb4, 0xd5, 0xe8, 0x3e, 0x7d, 0x87, 0x5e, 0x88, 0x8d, 0xa8, 0x3c, 0xb0, 0x76, 0xab, 0x0b, 0x12, 0x73, 0x2f, 0x3c, 0xfe, 0xcb, 0x17, 0xbf, 0xa5, 0x8a, 0xf1, 0xef, 0x99, 0x25, 0xd6, 0x63, 0x15, 0xb7, 0xb8, 0xbc, 0xf3, 0x21, 0x05, 0x0f, 0x03, 0x75, 0xa8, 0xb4, 0xca, 0xeb, 0x37, 0xa7, 0x29, 0x5a, 0x9c, 0xf3, 0xcc, 0x08, 0x2b, 0xaa, 0x39, 0x74, 0xcd, 0xeb, 0xc7, 0xd5, 0xd3, 0x4a, 0xf3, 0x34, 0x05, 0xba, 0xd0, 0xbe, 0x00, 0x90, 0xb0, 0x2f, 0x33, 0xfd, 0x34, 0x51, 0xc6, 0xd5, 0xf5, 0x68, 0xd3, 0xa6, 0xb9, 0xa9, 0xf4, 0xcb, 0xdb, 0x28, 0x1a, 0x8e, 0x06, 0x04, 0xd7, 0x75, 0x82, 0x11, 0x6f, 0x88, 0xaf, 0x9b, 0x71, 0x53, 0x90, 0xe7, 0xb9, 0x7b, 0xc9, 0x4c, 0xe3, 0x13, 0x20, 0xdd, 0x1e, 0xa3, 0x7c, 0x27, 0x0b, 0xdd, 0x66, 0xc9, 0xad, 0x02, 0xf9, 0x50, 0xd6, 0xe6, 0x01, 0x83, 0x85, 0x8c, 0x60, 0x9c, 0x45, 0xee, 0xed, 0x91, 0x2d, 0xa4, 0xc5, 0x80, 0x37, 0x08, 0x00, 0x2b, 0x52, 0xe6, 0x1b, 0xb5, 0x14, 0x3b, 0x67, 0x3e, 0x6b, 0x02, 0xc5, 0x4f, 0x39, 0xf2, 0xc7, 0x48, 0xf2, 0x25, 0x4c, 0x9b, 0x07, 0x41, 0x05, 0x43, 0xa5, 0x2e, 0xb7, 0xbb, 0x9f, 0x07, 0xdb, 0x5e, 0xc8, 0x91, 0x75, 0x3a, 0x06, 0x46, 0x01, 0x5f, 0xd1, 0x8b, 0x6c, 0xab, 0xcd, 0xe2, 0xe7, 0xde, 0x29, 0x6b, 0x2e, 0x41, 0xbc, 0x97, 0x21, 0x51, 0x1d, 0x9e, 0x45, 0x2e, 0x4d, 0x88, 0xe0, 0xea, 0x61, 0x87, 0xf6, 0xed, 0x3f, 0xa0, 0x28, 0x70, 0x59, 0x89, 0xe4, 0x9c, 0xba, 0xd0, 0x14, 0x8f, 0x10, 0x85, 0x34, 0xd6, 0x9a, 0x2e, 0xc0, 0x23, 0xc7, 0xfe, 0x64, 0xd2, 0xdd, 0x11, 0x31, 0x1e, 0xbc, 0xb5, 0x2e, 0x06, 0xbb, 0xd0, 0x44, 0xbe, 0xeb, 0x4b, 0x90, 0x2a, 0xb9, 0x1a, 0x61, 0x05, 0x52, 0x13, 0x4f, 0x4b, 0xed, 0xb0, 0x22, 0x2d, 0xe4, 0xef, 0x2d, 0x40, 0x02, 0x58, 0x6f, 0xb5, 0x7b, 0x90, 0x1e, 0x41, 0xb7, 0xab, 0x40, 0x53, 0x72, 0x35, 0x10, 0x0c, 0x6e, 0xf0, 0xd1, 0x88, 0x21, 0x75, 0x29, 0xd0, 0xc3, 0xe4, 0x39, 0xa1, 0xec, 0x53, 0xc1, 0xf2, 0xcb, 0x62, 0x4c, 0x8d, 0xee, 0x78, 0x80, 0x34, 0x60, 0x5f, 0x64, 0x3f, 0x3f, 0xe1, 0x1e, 0xca, 0x56, 0x7f, 0xb5, 0xde, 0x0f, 0x85, 0x33, 0x93, 0x8f, 0x85, 0x6c, 0xf9, 0xd3, 0xf4, 0x86, 0xe8, 0xc2, 0xb7, 0x48, 0xe3, 0x3a, 0xa4, 0xf2, 0x43, 0xf6, 0x21, 0xff, 0xec, 0x11, 0x7c, 0x11, 0x53, 0xbb, 0xf1, 0x15, 0x9b, 0xdb, 0x30, 0x95, 0xa0, 0xa3, 0x42, 0x6e, 0x7d, 0x70, 0xc5, 0x6e, 0x8c, 0x58, 0x35, 0xf5, 0xde, 0xd1, 0xce, 0x2e, 0xb0, 0x15, 0x97, 0xbc, 0x33, 0x88, 0x62, 0xdd, 0xfe, 0xe2, 0x69, 0xe7, 0x46, 0x98, 0x41, 0xc8, 0xe6, 0x55, 0x60, 0x3e, 0xf6, 0x1c, 0x3d, 0x8e, 0x46, 0x0c, 0xf3, 0xff, 0xd0, 0xbd, 0xc5, 0x96, 0x62, 0x1a, 0x50, 0x02, 0x45, 0xc0, 0xe6, 0xa5, 0x98, 0x62, 0xea, 0x41, 0xa9, 0x56, 0x28, 0x45, 0xc8, 0xc3, 0x64, 0xf7, 0x37, 0x95, 0x29, 0x97, 0xbd, 0xe2, 0x39, 0x46, 0x1b, 0xa6, 0xbb, 0x4b, 0x10, 0x0f, 0xd0, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x10, 0xf0, 0xab, 0xbf, 0xd5, 0xd3, 0xcb, 0xde, 0x20, 0x0d, 0x88, 0x1e, 0x69, 0xb2, 0x13, 0xa2, 0x45, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x35, 0xf3, 0xe5, 0x17, 0x48, 0x8f, 0x5c, 0x96, 0xa3, 0xe9, 0x58, 0xd7, 0xb6, 0xfc, 0xc5, 0xc5, 0x11, 0xa2, 0x74, 0x77, 0x19, 0xfa, 0x7b, 0x58, 0x17, 0x28, 0x89, 0x78, 0xf3, 0xd7, 0x98, 0x7b, 0x1a, 0xf9, 0x9f, 0x05, 0x83, 0x87, 0xd3, 0x42, 0x55, 0xa2, 0x02, 0x37, 0x64, 0xa7, 0xac, 0x38, 0x13, 0x5d, 0x06, 0xe7, 0xbb, 0xcb, 0xc2, 0x92, 0x4d, 0x2a, 0xc4, 0xcd, 0x2a, 0x4b, 0x85, 0x5a, 0xfc, 0x70, 0xbc, 0x79, 0xbe, 0x48, 0x90, 0x35, 0xfb, 0x25, 0x0e, 0x93, 0xfd, 0x7d, 0x6c, 0x25, 0x8a, 0x59, 0x7e, 0x9c, 0x0b, 0xa0, 0x58, 0x3b, 0x4e, 0x64, 0x16, 0xef, 0x30, 0x6b, 0xcc, 0xf9, 0x62, 0xe0, 0x68, 0x57, 0x65, 0x93, 0xcc, 0xeb, 0x07, 0xfd, 0xd6, 0x31, 0x38, 0xb5, 0x75, 0xd8, 0xfe, 0x27, 0x86, 0x3b, 0x0a, 0xdc, 0x7c, 0x6f, 0x65, 0x9c, 0x82, 0x08, 0x43, 0x2a, 0x91, 0x45, 0x23, 0x3e, 0x8a, 0x49, 0x34, 0x7e, 0x0a, 0x99, 0x84, 0x11, 0xd2, 0x4a, 0x93, 0xae, 0x8e, 0xcd, 0x1b, 0x6f, 0xa7, 0xf1, 0x50, 0xed, 0x95, 0x97, 0x5c, 0x3d, 0xf9, 0xd1, 0xb7, 0xdb, 0xcf, 0xa3, 0x60, 0xb1, 0x28, 0xe6, 0xa5, 0x22, 0x2c, 0xd1, 0xac, 0x1f, 0x9c, 0x15, 0xd0, 0xe8, 0x8c, 0xab, 0xcb, 0xe0, 0xe9, 0xe9, 0x77, 0xa0, 0x7f, 0x02, 0x02, 0xa1, 0xfb, 0x07, 0x42, 0x72, 0xb8, 0x1a, 0x0f, 0x8d, 0x97, 0x53, 0xa9, 0x75, 0xa1, 0x8b, 0xff, 0x09, 0x21, 0xc3, 0x1e, 0xf9, 0xa3, 0xb9, 0xd7, 0x72, 0x33, 0x62, 0x6e, 0x9a, 0x54, 0xb0, 0x0c, 0x98, 0x0a, 0xbd, 0x30, 0xc7, 0x38, 0x7e, 0x4d, 0xf9, 0xb6, 0x38, 0x3c, 0xb3, 0x7b, 0x4f, 0x86, 0xa2, 0xf3, 0xac, 0x9e, 0x81, 0x15, 0xe4, 0xb5, 0x30, 0x3c, 0xe5, 0x11, 0xf9, 0x05, 0x5c, 0x28, 0x50, 0x0a, 0xa2, 0x9d, 0xe1, 0xfa, 0xe9, 0x80, 0xcd, 0x9d, 0x9e, 0x9a, 0x90, 0x42, 0x2e, 0xe6, 0xd2, 0x4b, 0x1a, 0xd6, 0x84, 0x6e, 0x95, 0x0c, 0x17, 0xc1, 0xb8, 0x1d, 0x84, 0x3a, 0x76, 0xae, 0xfc, 0xd1, 0x32, 0x4d, 0x52, 0xac, 0x6e, 0xe6, 0x4f, 0xcd, 0x76, 0x4d, 0xd3, 0x00, 0x81, 0x82, 0x2e, 0x60, 0xbd, 0x8f, 0x42, 0xe8, 0x83, 0xcb, 0xdd, 0x9b, 0xeb, 0x6f, 0x5a, 0xe8, 0xf9, 0x0c, 0x7a, 0x90, 0x92, 0x2b, 0xe6, 0x04, 0x17, 0x58, 0xd4, 0xa4, 0x43, 0x13, 0xb3, 0xec, 0xb1, 0x39, 0xfe, 0x0e, 0x1d, 0x55, 0xe8, 0x96, 0xa1, 0x13, 0xd6, 0x59, 0xef, 0x36, 0x69, 0xa5, 0x20, 0x83, 0x21, 0xfc, 0xed, 0x6f, 0xa5, 0x85, 0x64, 0x27, 0xeb, 0xdb, 0x53, 0x7f, 0x89, 0x4d, 0xe6, 0x00, 0x61, 0xc4, 0x1e, 0x69, 0x36, 0x74, 0xbe, 0xa3, 0xc0, 0x28, 0x7e, 0x60, 0x97, 0x46, 0xae, 0x9b, 0x9c, 0x70, 0xee, 0xa2, 0x5f, 0x67, 0x18, 0x98, 0x21, 0xef, 0x79, 0xad, 0x4d, 0xff, 0x42, 0xc8, 0x32, 0x28, 0x6e, 0x45, 0xa4, 0x3c, 0x0a, 0x75, 0x19, 0x8a, 0x6b, 0x68, 0x71, 0x11, 0x97, 0x07, 0x8d, 0xf7, 0xcf, 0xdc, 0x61, 0x99, 0xb4, 0xfb, 0xab, 0x7e, 0x0a, 0xf0, 0xf5, 0x08, 0x8b, 0x7e, 0x03, 0x75, 0x4d, 0x75, 0xd8, 0xef, 0x07, 0x3f, 0x80, 0xf4, 0xa1, 0x8e, 0xbc, 0x5b, 0xa5, 0x25, 0x4f, 0xfe, 0x9c, 0xb7, 0x0e, 0x76, 0x39, 0xad, 0xc8, 0x07, 0x2b, 0x87, 0x16, 0xdc, 0x2b, 0xb2, 0xcf, 0x40, 0x4d, 0x23, 0xc0, 0xb9, 0x26, 0xf9, 0xe2, 0x52, 0x55, 0x4a, 0x34, 0x95, 0x5f, 0xbb, 0x14, 0x52, 0x8f, 0xfa, 0x59, 0xd1, 0x2e, 0x53, 0x35, 0x9e, 0x43, 0xcf, 0xa3, 0x1b, 0x24, 0x0b, 0x03, 0xb3, 0x2e, 0x42, 0xb9, 0x85, 0x23, 0x47, 0x4f, 0xfb, 0x69, 0xb6, 0x15, 0x1e, 0x2b, 0xa6, 0xa5, 0x2b, 0xf9, 0x60, 0xae, 0x36, 0x5d, 0x13, 0x10, 0x33, 0xf8, 0x40, 0x61, 0x3a, 0x07, 0x6f, 0xa2, 0x57, 0xdf, 0x38, 0xc1, 0xaf, 0x5b, 0xe6, 0x15, 0x3a, 0x24, 0xaf, 0xb8, 0x2a, 0x43, 0x9f, 0x18, 0xe6, 0xec, 0x89, 0x4d, 0x98, 0x4a, 0xc9, 0x8b, 0x77, 0xf2, 0x4e, 0x50, 0xbd, 0x1e, 0x80, 0x45, 0xcf, 0x94, 0xff, 0xfd, 0xca, 0x9e, 0x57, 0x01, 0x28, 0xf1, 0x4d, 0x1c, 0xd5, 0xd7, 0xe6, 0x1b, 0xcb, 0x4c, 0xac, 0xc7, 0x7c, 0x84, 0x3e, 0xbe, 0x16, 0xab, 0xf2, 0xe8, 0x72, 0xee, 0x69, 0xe4, 0xe2, 0x73, 0x9f, 0x34, 0xec, 0xdb, 0x56, 0x8a, 0xa1, 0x01, 0x53, 0x65, 0x85, 0x23, 0x45, 0xb7, 0xed, 0x85, 0x7a, 0xc5, 0x4e, 0xb4, 0x1e, 0x06, 0x13, 0x54, 0x43, 0xa5, 0x4b, 0xf9, 0x90, 0x8e, 0x4e, 0x55, 0xb5, 0xeb, 0xa7, 0xb6, 0xc0, 0x1f, 0x8d, 0x7d, 0x35, 0xd4, 0xc8, 0xa2, 0xc6, 0x2d, 0x2f, 0xb1, 0xcd, 0xf0, 0xa3, 0x71, 0x5a, 0x67, 0x76, 0xd7, 0x51, 0x3c, 0x9b, 0x14, 0xc8, 0xb6, 0x30, 0x16, 0xbc, 0x4c, 0x95, 0xf0, 0xd2, 0x4f, 0xb0, 0xc3, 0x68, 0x0c, 0xb0, 0x57, 0x3f, 0xa1, 0xa4, 0xda, 0x9d, 0x8f, 0xeb, 0x27, 0xf7, 0x72, 0x22, 0x84, 0xc8, 0xae, 0x81, 0x7e, 0xb9, 0x0f, 0x98, 0xb3, 0x60, 0xa7, 0x27, 0x7b, 0x45, 0x12, 0xf3, 0xa8, 0x2d, 0xff, 0x1b, 0xac, 0x95, 0x80, 0xa3, 0x67, 0x83, 0x5e, 0x6c, 0x54, 0xe9, 0xdb, 0x6e, 0x9a, 0xdf, 0x96, 0x42, 0xbb, 0x44, 0xb9, 0xd2, 0x60, 0xd2, 0x68, 0x37, 0x2a, 0x8d, 0x09, 0xdf, 0xa5, 0x0e, 0x23, 0xc8, 0xb4, 0x65, 0x65, 0x05, 0xc5, 0x75, 0x3e, 0x6b, 0xe5, 0xa4, 0x3e, 0xbe, 0xed, 0xa8, 0x8e, 0xd1, 0x32, 0xe8, 0x5f, 0x5c, 0x0c, 0x73, 0x4a, 0xc9, 0xc7, 0x07, 0xc8, 0x2c, 0xa0, 0x95, 0x79, 0x42, 0xe9, 0x46, 0xae, 0x99, 0x41, 0xc8, 0xfa, 0xa8, 0xdf, 0x36, 0xa2, 0x1d, 0x6e, 0x3d, 0x90, 0x76, 0x59, 0xd4, 0x53, 0x96, 0xa7, 0x66, 0x3c, 0x42, 0x30, 0xfa, 0x41, 0xe5, 0x38, 0x76, 0x51, 0x1a, 0x8d, 0x94, 0x57, 0xee, 0x5d, 0x0b, 0x30, 0xbe, 0x91, 0x72, 0x18, 0xf4, 0x87, 0xd0, 0xc7, 0x7e, 0xbc, 0xce, 0xe8, 0x98, 0x17, 0xc1, 0x0d, 0xbf, 0xa7, 0xe9, 0x1c, 0x57, 0x47, 0x03, 0x88, 0x04, 0xb4, 0x8f, 0xbb, 0x15, 0xda, 0x12, 0x0a, 0x7d, 0xc6, 0x66, 0xb8, 0x74, 0xf0, 0xaa, 0x3c, 0xf0, 0x96, 0x6a, 0x59, 0x3d, 0x5a, 0x28, 0x08, 0xdd, 0x56, 0xb6, 0x8b, 0xa2, 0xbf, 0x45, 0xcc, 0xe5, 0x2f, 0xa1, 0x56, 0xa9, 0xe5, 0x4e, 0x73, 0x3f, 0x41, 0xf9, 0x0b, 0x05, 0x25, 0xcd, 0x65, 0x15, 0x83, 0xf8, 0xfc, 0x12, 0x97, 0x3b, 0xfb, 0xe6, 0xac, 0xab, 0x29, 0x3f, 0x57, 0xb3, 0x8f, 0x40, 0xd7, 0x70, 0x81, 0x55, 0xa2, 0x1d, 0xc7, 0x2a, 0x4a, 0x5c, 0xc4, 0x08, 0x2b, 0x11, 0x5a, 0xac, 0x22, 0x34, 0xcd, 0xa0, 0x42, 0x4b, 0xf6, 0xd2, 0x1f, 0x14, 0xeb, 0xce, 0x99, 0x70, 0xcf, 0xe8, 0x45, 0xbe, 0xd2, 0xe3, 0x12, 0x27, 0xc5, 0x16, 0xc8, 0xa8, 0xa3, 0xff, 0xd3, 0x3b, 0xff, 0x7e, 0x88, 0xef, 0xff, 0xbf, 0xa1, 0x8b, 0x9d, 0xfe, 0xbc, 0xb1, 0xbe, 0x6b, 0xd3, 0x92, 0x9b, 0x7a, 0xaf, 0xfd, 0x2b, 0x5d, 0xc4, 0xe1, 0x3f, 0xfc, 0x1e, 0xc5, 0xe8, 0xf3, 0xd2, 0x84, 0x86, 0x07, 0x06, 0x96, 0xd3, 0x61, 0x72, 0x75, 0x6b, 0xa3, 0x02, 0x2c, 0x9e, 0x75, 0x27, 0xa4, 0xfd, 0x8d, 0x57, 0xde, 0x00, 0xa8, 0x9c, 0x80, 0xa6, 0xe2, 0x53, 0xa2, 0x66, 0x5c, 0x5b, 0x0d, 0xb1, 0x97, 0xc2, 0x40, 0xda, 0x55, 0x51, 0x9a, 0x36, 0x4c, 0xc6, 0xbc, 0x29, 0xd9, 0x1e, 0x5a, 0x58, 0x36, 0x2d, 0x3d, 0x8e, 0x7d, 0xa1, 0x50, 0x58, 0x6a, 0x0f, 0xf2, 0x4f, 0xb0, 0x6f, 0xc9, 0x20, 0x09, 0x55, 0xc4, 0x86, 0x2a, 0x0f, 0x75, 0x85, 0x6d, 0x51, 0xbd, 0x75, 0x5f, 0xc3, 0xfe, 0x0c, 0xee, 0x03, 0xd0, 0xad, 0x89, 0x20, 0x4c, 0x92, 0xda, 0xbf, 0xa5, 0x3c, 0x87, 0xc0, 0x34, 0x08, 0x44, 0x58, 0x6c, 0x66, 0xee, 0x93, 0x6f, 0x46, 0x1b, 0x2e, 0x97, 0x86, 0x6c, 0x9a, 0x2c, 0xb7, 0x55, 0x48, 0xb4, 0x16, 0x9d, 0xcd, 0xfc, 0x89, 0xb5, 0x72, 0x9b, 0x89, 0xc1, 0x6c, 0x16, 0x8d, 0x9d, 0x15, 0x14, 0x5c, 0xe6, 0x5d, 0x08, 0x4d, 0x75, 0xf1, 0x7f, 0x40, 0xee, 0x9e, 0x51, 0xaf, 0xf6, 0xca, 0x29, 0x1b, 0xca, 0x8e, 0x5a, 0x43, 0x3b, 0x3f, 0x39, 0x4e, 0xff, 0x02, 0xdb, 0x76, 0x56, 0x33, 0xfe, 0xf4, 0xfb, 0xc3, 0xbe, 0x5f, 0xf9, 0x42, 0xe9, 0x53, 0x1c, 0x83, 0x52, 0x44, 0x07, 0x91, 0x0e, 0x11, 0x20, 0x60, 0xdd, 0xaf, 0x16, 0xc8, 0xca, 0xf1, 0x89, 0x31, 0x7c, 0xd3, 0xde, 0xe2, 0xd3, 0x59, 0x17, 0x27, 0xc5, 0xc0, 0x2b, 0x80, 0x7a, 0xdc, 0x25, 0x31, 0x83, 0xc4, 0x00, 0xb5, 0x4e, 0x88, 0x8a, 0x71, 0xdc, 0xcc, 0xbe, 0xfc, 0xbd, 0x47, 0x81, 0xac, 0xa1, 0xa6, 0xa8, 0x29, 0x2d, 0x91, 0x41, 0xff, 0x2e, 0x2c, 0xc7, 0x3c, 0x76, 0x3a, 0xa1, 0x0f, 0x1e, 0xbe, 0xf1, 0x09, 0xd5, 0x32, 0xfe, 0x63, 0x7e, 0x50, 0x06, 0x0f, 0x42, 0x0d, 0xf6, 0xa6, 0xb1, 0xd2, 0x76, 0x78, 0x13, 0xe1, 0xd3, 0x44, 0x33, 0xda, 0x48, 0xe5, 0x96, 0x4f, 0xa2, 0x1a, 0x9d, 0x21, 0xf1, 0x29, 0x28, 0x38, 0x47, 0xff, 0xc6, 0xb1, 0xbd, 0x93, 0xc5, 0x4a, 0x55, 0x33, 0xcb, 0xd7, 0x0d, 0x06, 0x7d, 0x06, 0xd9, 0x9b, 0x35, 0x62, 0x32, 0x6e, 0x35, 0xe5, 0x95, 0x9f, 0x68, 0x11, 0xba, 0x80, 0x22, 0xd7, 0xdb, 0x9d, 0x9f, 0x3b, 0xd6, 0x3d, 0x61, 0x75, 0x47, 0x07, 0x64, 0x2e, 0x3e, 0x72, 0xdc, 0x1b, 0xfe, 0x0e, 0xc3, 0x33, 0xa9, 0x13, 0x82, 0xa7, 0x54, 0x67, 0x0c, 0xe2, 0x88, 0x30, 0x69, 0x73, 0xdb, 0xcb, 0x61, 0xab, 0x46, 0xb6, 0x5b, 0x2d, 0x7b, 0x92, 0xfb, 0xe9, 0xb0, 0xcc, 0x0d, 0xf2, 0x2c, 0xfb, 0x42, 0x15, 0x33, 0x73, 0x03, 0xc2, 0xf7, 0x96, 0x66, 0x22, 0xb5, 0x2f, 0x17, 0x29, 0xca, 0xd7, 0xfd, 0xec, 0x7d, 0x2c, 0x72, 0xcc, 0x88, 0x65, 0x07, 0x6d, 0x9b, 0x4f, 0xea, 0xf4, 0x20, 0x56, 0x8c, 0x7f, 0x5a, 0x17, 0x92, 0xa1, 0x30, 0x09, 0x05, 0x85, 0xab, 0x32, 0x42, 0x4c, 0x9f, 0x67, 0xcd, 0xcc, 0xa3, 0x4d, 0x1a, 0xe7, 0x22, 0x03, 0x8b, 0x0d, 0x6a, 0x36, 0xef, 0xf0, 0xcd, 0x50, 0x20, 0x53, 0xdf, 0xa2, 0xdf, 0xe8, 0xb9, 0x94, 0xc3, 0xec, 0x2e, 0x49, 0xe7, 0x24, 0xe0, 0x9d, 0x12, 0xe7, 0x2e, 0x31, 0xfe, 0xd6, 0xa3, 0x2b, 0xd2, 0x38, 0x62, 0x6e, 0x9a, 0xbd, 0xe1, 0xea, 0xe6, 0x82, 0xa5, 0xff, 0x7e, 0x63, 0x87, 0x22, 0xf5, 0x42, 0x56, 0xc9, 0xa8, 0xc4, 0xa2, 0xa4, 0xa1, 0xf3, 0x6a, 0x76, 0x8b, 0x47, 0xb9, 0xe7, 0xb4, 0xe1, 0xe2, 0x18, 0xa7, 0x70, 0x94, 0xc8, 0x2f, 0xa0, 0xd9, 0xac, 0x55, 0x7d, 0x41, 0x34, 0x50, 0xfa, 0x24, 0x1c, 0x8e, 0xcb, 0x7e, 0xaf, 0xe3, 0x81, 0x5f, 0xaa, 0x44, 0x16, 0xad, 0xd0, 0x26, 0x1c, 0x19, 0xf3, 0xcd, 0x08, 0xc7, 0x51, 0x0b, 0x8d, 0xc0, 0xe9, 0xa4, 0xa2, 0xea, 0xeb, 0x4f, 0x1c, 0xc6, 0x73, 0x5c, 0x0c, 0xae, 0x8d, 0x59, 0xe0, 0x85, 0xdf, 0x82, 0x04, 0xa7, 0x9a, 0xa2, 0x08, 0xf7, 0x97, 0xfe, 0xc8, 0xe6, 0x66, 0xd5, 0x66, 0x3e, 0xc3, 0x73, 0xbf, 0x45, 0x42, 0xbd, 0x68, 0xf0, 0x47, 0xd1, 0xcd, 0xcc, 0xeb, 0xfd, 0x45, 0xe7, 0xa6, 0x71, 0x1f, 0xef, 0x78, 0x74, 0x37, 0xdb, 0x3d, 0xea, 0xbb, 0xcb, 0xe3, 0xe7, 0xbc, 0x2c, 0x88, 0xa8, 0xe6, 0x69, 0x31, 0xa5, 0x39, 0x7b, 0x47, 0x89, 0x5f, 0x4b, 0xfa, 0x12, 0xca, 0xb3, 0xd7, 0xcf, 0xcc, 0xb7, 0xe8, 0x29, 0x13, 0xbe, 0x08, 0xc7, 0xc4, 0x8d, 0x00, 0x5a, 0x8e, 0x22, 0x54, 0x1e, 0x9b, 0x6c, 0x30, 0x57, 0x41, 0x87, 0x04, 0x5d, 0x4e, 0x57, 0xa5, 0x34, 0x08, 0xb1, 0xa6, 0x70, 0xf2, 0x9a, 0x3e, 0x52, 0xce, 0x4b, 0xb1, 0x67, 0x35, 0x92, 0xb8, 0x12, 0x01, 0x61, 0x9b, 0x51, 0x2d, 0x00, 0xfc, 0xf7, 0x1a, 0xe8, 0x83, 0x1c, 0xd1, 0x4a, 0x9b, 0xed, 0x5e, 0x79, 0xa1, 0x30, 0x59, 0x6d, 0x29, 0x5f, 0xe2, 0x9e, 0x56, 0x68, 0x04, 0xa2, 0x65, 0x82, 0x72, 0xa0, 0x09, 0x78, 0x54, 0x82, 0x86, 0xb8, 0x82, 0x33, 0x19, 0x1d, 0xa3, 0xf7, 0x5f, 0x31, 0x72, 0xf9, 0x53, 0x8f, 0x6d, 0x60, 0xd4, 0x5e, 0x56, 0x89, 0x11, 0x70, 0x46, 0x61, 0x4a, 0x2d, 0xc7, 0x89, 0xdb, 0x31, 0x9e, 0x6e, 0x9d, 0xbc, 0x2a, 0x1f, 0x3d, 0xda, 0x19, 0x9c, 0x90, 0x5a, 0x28, 0x9f, 0xc9, 0x5f, 0x7a, 0x4a, 0xf9, 0xc7, 0xe2, 0xbe, 0x51, 0x01, 0x37, 0x0e, 0x81, 0x41, 0xb6, 0x2a, 0x66, 0xed, 0xc2, 0xf0, 0x7b, 0xfc, 0xb2, 0x50, 0xf6, 0xbb, 0x59, 0xe3, 0x48, 0x24, 0xea, 0x29, 0x86, 0x87, 0x75, 0x37, 0xaf, 0xee, 0x43, 0x7d, 0xe7, 0x49, 0xb8, 0x16, 0x36, 0x0f, 0xd0, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x10, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0xda, 0xf8, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xb3, 0x11, 0x81, 0x8c, 0xab, 0xb1, 0x64, 0x58, 0x83, 0x44, 0xea, 0xca, 0x89, 0xf8, 0x82, 0x03, 0x91, 0x6b, 0xef, 0xe8, 0x74, 0x4a, 0x34, 0xeb, 0xfc, 0x82, 0xd4, 0xab, 0xbb, 0x0d, 0x01, 0x7d, 0x87, 0xbf, 0x9c, 0x88, 0x78, 0xa2, 0xdb, 0x7e, 0x84, 0x22, 0xd7, 0xc7, 0x1f, 0x0c, 0x11, 0xe4, 0xea, 0xfe, 0x86, 0x42, 0xff, 0xe2, 0x10, 0x43, 0x61, 0x8a, 0xa2, 0xc3, 0x22, 0x18, 0x71, 0xbe, 0x17, 0x50, 0x3e, 0xd0, 0x4f, 0x33, 0x63, 0x31, 0x25, 0x6e, 0x5c, 0xd9, 0xac, 0x33, 0xc9, 0x28, 0xda, 0xbc, 0x89, 0xc6, 0xac, 0xd6, 0x64, 0x77, 0xf2, 0x35, 0x51, 0xee, 0xdc, 0x9a, 0x68, 0x67, 0xbc, 0xd4, 0x4f, 0x55, 0xc9, 0x30, 0x2a, 0x4a, 0x75, 0x23, 0xb5, 0x84, 0x2f, 0x03, 0xd9, 0x1a, 0xe2, 0xc8, 0xbb, 0xae, 0x95, 0xc9, 0xb1, 0xf4, 0xe7, 0xa2, 0x19, 0x13, 0xad, 0xd8, 0xcc, 0x0c, 0x9f, 0x75, 0xc9, 0xd4, 0x31, 0x6c, 0xfa, 0xb5, 0x5e, 0x57, 0xad, 0xe3, 0xad, 0xbc, 0x21, 0xc6, 0x42, 0xf5, 0x11, 0x4a, 0x48, 0x3d, 0x22, 0xf8, 0x91, 0x73, 0xbe, 0x51, 0x81, 0x1f, 0xd3, 0x44, 0xf9, 0x2a, 0xce, 0x34, 0x62, 0x8e, 0x1e, 0x3d, 0x57, 0x77, 0x8e, 0x7c, 0x12, 0x5b, 0x82, 0x97, 0x83, 0x9b, 0x91, 0x21, 0x8c, 0xde, 0xfa, 0x2b, 0xcc, 0xd0, 0x04, 0x2e, 0xe4, 0xaf, 0x05, 0x97, 0x22, 0x2d, 0x08, 0xf0, 0x2c, 0x5d, 0x41, 0xb8, 0xc6, 0x5e, 0xf1, 0x32, 0x4e, 0xa7, 0xc6, 0x71, 0xe4, 0x37, 0xb8, 0x3d, 0x70, 0xee, 0x96, 0xf6, 0x0b, 0x67, 0xd3, 0xb7, 0x60, 0x49, 0x17, 0x20, 0xec, 0x9a, 0xab, 0x38, 0x54, 0xd8, 0xdd, 0x6e, 0xd5, 0x43, 0xfd, 0x30, 0xfb, 0x9c, 0xdd, 0x2c, 0x38, 0xe3, 0x04, 0xed, 0x2d, 0x2f, 0x7a, 0x00, 0x87, 0xcf, 0x8e, 0xd4, 0x29, 0x2e, 0x55, 0xbc, 0x31, 0x03, 0xe9, 0x84, 0x97, 0x77, 0xca, 0x59, 0x4a, 0x4f, 0x4b, 0x51, 0x71, 0xc8, 0x3d, 0x28, 0x15, 0xd8, 0x83, 0xad, 0x19, 0x04, 0x70, 0x8e, 0xf8, 0xfc, 0xe3, 0x19, 0xa2, 0x54, 0x76, 0x32, 0xd1, 0xac, 0xdb, 0x20, 0x43, 0xfb, 0xbd, 0xfb, 0x34, 0x44, 0x67, 0xfc, 0x30, 0x69, 0xb4, 0x7a, 0x30, 0x0b, 0xf8, 0x89, 0xa9, 0x2f, 0x72, 0xc5, 0x48, 0x2e, 0x38, 0x76, 0xaa, 0x2b, 0xb3, 0xf8, 0xe4, 0xf5, 0xcb, 0x5f, 0xd7, 0xb9, 0x89, 0xfc, 0xf1, 0x2c, 0x6e, 0x68, 0xd8, 0x42, 0x7b, 0xb7, 0x36, 0xee, 0xe0, 0xff, 0x5f, 0x88, 0x58, 0x53, 0x37, 0x2e, 0xd5, 0x73, 0x21, 0x04, 0x59, 0x57, 0x68, 0xcd, 0xcb, 0xfa, 0x42, 0x3e, 0x77, 0x30, 0x0d, 0x7f, 0x5e, 0x38, 0x4c, 0xf2, 0x90, 0xac, 0x42, 0x6c, 0xc7, 0x5f, 0x3c, 0xc7, 0x82, 0x75, 0x61, 0xa8, 0xd3, 0xea, 0x09, 0x8a, 0xaa, 0xaf, 0x18, 0x22, 0x1b, 0x35, 0x4a, 0x30, 0x0d, 0x69, 0x0f, 0x4e, 0x6b, 0x48, 0xa3, 0x44, 0x50, 0xc4, 0xa8, 0x18, 0xc0, 0x1a, 0x53, 0xce, 0x84, 0x88, 0xab, 0x38, 0x1b, 0xc5, 0xe1, 0x6b, 0x23, 0x4e, 0x87, 0x6a, 0x71, 0xbd, 0x04, 0x7d, 0x0f, 0x6e, 0x3a, 0x62, 0x8d, 0xf7, 0x9d, 0xcc, 0x3d, 0xf4, 0x8e, 0xe3, 0xdc, 0x53, 0x75, 0x17, 0x56, 0xd0, 0x13, 0xe1, 0x35, 0x8a, 0x4f, 0xe0, 0xc9, 0xce, 0x2c, 0x50, 0x90, 0xbb, 0xbe, 0xd6, 0xf0, 0x75, 0xf3, 0x2e, 0xbc, 0xbd, 0x91, 0x34, 0xf5, 0x71, 0x12, 0x48, 0x91, 0x4d, 0xe2, 0x9b, 0xcb, 0x53, 0xdf, 0xa4, 0x87, 0xaf, 0x90, 0xc7, 0x8a, 0xad, 0x1a, 0xc6, 0x28, 0xd6, 0x12, 0x59, 0x8f, 0x31, 0x39, 0xf3, 0xb0, 0xb2, 0x7a, 0x43, 0xef, 0x69, 0x4e, 0xa9, 0x6e, 0xaf, 0x94, 0xbc, 0x10, 0x78, 0x60, 0x44, 0x54, 0x35, 0x7a, 0xfe, 0x36, 0x4a, 0xdd, 0xca, 0x55, 0x01, 0x55, 0xd7, 0x78, 0xcc, 0x7d, 0x4c, 0xeb, 0xd6, 0x0b, 0x81, 0x23, 0x33, 0x27, 0x2f, 0xaf, 0xfd, 0x8b, 0xf2, 0x59, 0x42, 0x77, 0x1e, 0xb7, 0x51, 0x58, 0xaf, 0xcf, 0x21, 0xd5, 0x8b, 0x04, 0x41, 0xb3, 0xc6, 0xb3, 0x8a, 0xf0, 0x07, 0xcb, 0x57, 0x80, 0xac, 0xb3, 0x4a, 0xfc, 0xb4, 0x55, 0x10, 0x5f, 0xc6, 0x21, 0xba, 0xfb, 0x52, 0x5a, 0xbb, 0x4f, 0x76, 0x35, 0x10, 0xa3, 0x7a, 0xff, 0x65, 0xe6, 0x43, 0x7a, 0xb6, 0x85, 0x35, 0x23, 0xa0, 0x9b, 0x22, 0x49, 0x04, 0x7c, 0x4a, 0xb2, 0xb0, 0x9e, 0x2b, 0xda, 0x4f, 0xf5, 0xac, 0xeb, 0x25, 0x71, 0x90, 0x9a, 0x47, 0xd7, 0xc1, 0x5c, 0x54, 0x2b, 0x10, 0xeb, 0x9f, 0xdd, 0x06, 0x80, 0xe0, 0x09, 0xd9, 0x34, 0x39, 0x5d, 0x94, 0x0d, 0x27, 0x99, 0xd4, 0x37, 0xba, 0xdf, 0xd4, 0x29, 0x1e, 0x15, 0x34, 0xe5, 0xe1, 0x72, 0x6b, 0x67, 0x3e, 0x7a, 0x1b, 0xd0, 0x42, 0x90, 0x91, 0x4a, 0xbe, 0x7d, 0x5c, 0xdb, 0x52, 0x4c, 0x59, 0xab, 0x9d, 0xa4, 0x2b, 0x8b, 0x9a, 0xb3, 0xf5, 0x3b, 0xa6, 0x30, 0xa9, 0x0a, 0x15, 0xb0, 0xa9, 0xd8, 0x86, 0x8e, 0x37, 0xf5, 0xbe, 0x0d, 0xb2, 0x59, 0x86, 0x3b, 0xf0, 0xec, 0xd0, 0xcb, 0x3c, 0x25, 0x10, 0xa6, 0x13, 0x0a, 0x82, 0xc8, 0xc4, 0x6a, 0xc3, 0x91, 0xdb, 0x46, 0x9a, 0x70, 0xc5, 0x58, 0xca, 0x4d, 0x84, 0x48, 0x48, 0x69, 0xc8, 0x9c, 0x17, 0xe3, 0x10, 0x1a, 0xba, 0x0e, 0x80, 0x75, 0x1f, 0xc3, 0xf9, 0x88, 0x93, 0xf6, 0x6d, 0x18, 0x9c, 0x93, 0xe8, 0xa0, 0x44, 0xef, 0x32, 0x03, 0xdd, 0xc0, 0x6b, 0xf7, 0x1c, 0x2c, 0x3f, 0x2f, 0xaa, 0x95, 0x5c, 0x2f, 0xd9, 0x3c, 0xeb, 0xc3, 0xd4, 0x22, 0x41, 0x20, 0x95, 0xf7, 0x50, 0xdc, 0x29, 0x54, 0x64, 0xbf, 0x1c, 0x3e, 0x21, 0x88, 0x74, 0xb9, 0x1c, 0x63, 0x2d, 0x7c, 0xe3, 0x91, 0xe5, 0x0e, 0xee, 0xae, 0xbf, 0x16, 0xeb, 0x4c, 0xa0, 0x3a, 0xa2, 0xab, 0xec, 0x3b, 0x7c, 0x9f, 0xe4, 0x82, 0xf8, 0xae, 0x3e, 0x0a, 0xab, 0x3b, 0xba, 0x7a, 0xc6, 0x5b, 0x98, 0x6c, 0x65, 0x7a, 0xcb, 0x8c, 0x98, 0x93, 0x76, 0x00, 0xc6, 0x2b, 0xcf, 0x52, 0x71, 0xec, 0x85, 0x29, 0xa0, 0xbc, 0xe3, 0xfc, 0xa3, 0x4a, 0xd0, 0x71, 0xe0, 0x13, 0x54, 0xbb, 0x1d, 0xb1, 0xe4, 0xfe, 0xd8, 0x97, 0xb0, 0x76, 0xa2, 0x7b, 0x8f, 0x47, 0xec, 0xa2, 0xdf, 0xa2, 0xb4, 0xf4, 0xee, 0x6c, 0x8d, 0x0d, 0x81, 0x6b, 0x36, 0x9b, 0x09, 0xd0, 0xb6, 0x80, 0x05, 0x93, 0x5d, 0x64, 0x99, 0xf5, 0xeb, 0xa1, 0xce, 0x57, 0xc4, 0x4a, 0x2c, 0x41, 0x7d, 0xd1, 0x58, 0x48, 0x40, 0x46, 0x70, 0xf2, 0x89, 0x56, 0xcc, 0x3d, 0xdf, 0xb1, 0x92, 0xb1, 0x6f, 0x9b, 0x2a, 0x39, 0xb8, 0xa9, 0xc8, 0x6b, 0x03, 0xf8, 0x21, 0x85, 0xf4, 0x59, 0x54, 0xb4, 0x92, 0xa4, 0xb5, 0x8d, 0x13, 0xf7, 0x4e, 0xfb, 0xc4, 0x56, 0x50, 0xc6, 0xe0, 0xcf, 0x17, 0x7e, 0x13, 0x3b, 0xfd, 0x94, 0x37, 0xbb, 0xc1, 0x8a, 0x59, 0x13, 0x6f, 0x02, 0xfe, 0x3e, 0xea, 0xff, 0x9b, 0x40, 0xed, 0xe3, 0x35, 0x77, 0x6a, 0x2b, 0x18, 0xab, 0xce, 0xea, 0x36, 0xfb, 0x68, 0x00, 0xba, 0xf1, 0xaa, 0xc6, 0x19, 0x9d, 0x9f, 0x8a, 0x90, 0xa5, 0xc1, 0xd5, 0xb5, 0x62, 0xdd, 0x39, 0xa4, 0x4c, 0x71, 0x0c, 0x4e, 0x7d, 0x23, 0xaf, 0x55, 0x87, 0x7f, 0x07, 0xf7, 0x31, 0x87, 0x75, 0xb6, 0x9f, 0x9f, 0x28, 0xa2, 0x54, 0xa8, 0x21, 0x06, 0x47, 0x3f, 0xbf, 0x29, 0x26, 0x3f, 0x9a, 0x4c, 0x2b, 0x15, 0x28, 0xb1, 0x3a, 0xc2, 0x2d, 0xda, 0x28, 0x52, 0xbb, 0x58, 0x6e, 0x96, 0xa3, 0x50, 0xe3, 0xd0, 0x5c, 0xe1, 0xd6, 0x98, 0xa5, 0x2e, 0xbb, 0x33, 0x9f, 0xd8, 0x55, 0x37, 0x85, 0x99, 0xd9, 0x49, 0x31, 0xe0, 0xfe, 0xbb, 0x9a, 0x8d, 0x01, 0x09, 0x52, 0x38, 0xac, 0xe6, 0x56, 0x6d, 0x8b, 0x16, 0x32, 0x96, 0xce, 0xac, 0x8e, 0x94, 0xfc, 0xf7, 0xd8, 0x4e, 0x56, 0x16, 0xea, 0xf3, 0x94, 0xf1, 0xe6, 0xa7, 0xf8, 0x30, 0xbc, 0x26, 0x58, 0x6b, 0x72, 0x92, 0xe1, 0x06, 0x8c, 0x41, 0x1f, 0x78, 0x8d, 0xca, 0x4f, 0x76, 0xff, 0x46, 0xe2, 0x18, 0xe0, 0xac, 0x7c, 0x0e, 0x46, 0x91, 0x59, 0x2c, 0x20, 0x42, 0x24, 0x70, 0xa8, 0x3d, 0x35, 0xfe, 0xae, 0x06, 0x9f, 0xc0, 0x62, 0x20, 0x6e, 0xcb, 0x5c, 0xe3, 0xcd, 0x7c, 0x8b, 0xbd, 0x1d, 0xe6, 0x67, 0x0e, 0x6e, 0xf1, 0xb3, 0x9b, 0xf2, 0xd0, 0x68, 0x1c, 0x7c, 0x34, 0x6f, 0xa1, 0x0c, 0x03, 0x75, 0x9d, 0x9d, 0x0d, 0x11, 0xc7, 0x32, 0x60, 0x3c, 0x44, 0x0e, 0x8d, 0x4e, 0xa8, 0x90, 0xc7, 0x6b, 0x2a, 0x37, 0x7f, 0x9c, 0x8b, 0x27, 0xe9, 0xde, 0xb4, 0xe4, 0x1a, 0xe9, 0x4f, 0x6b, 0x03, 0x0e, 0x4a, 0xb4, 0x03, 0x35, 0x58, 0x6b, 0xf4, 0x44, 0x19, 0x45, 0xc7, 0xb6, 0xca, 0x4a, 0xe6, 0xc3, 0x77, 0x81, 0x74, 0x19, 0x66, 0x7b, 0xdf, 0x38, 0x5e, 0x9a, 0x44, 0xef, 0xf7, 0x30, 0x84, 0x11, 0xb6, 0x1e, 0xd8, 0xd1, 0x0e, 0x9c, 0x7c, 0x56, 0x86, 0xc4, 0xa9, 0x0f, 0x68, 0x1c, 0xfc, 0x7d, 0x08, 0xf7, 0x0b, 0x96, 0x32, 0xee, 0x77, 0xdd, 0xdd, 0xed, 0x79, 0x0c, 0x77, 0x0f, 0x7c, 0x87, 0xae, 0xbe, 0x3a, 0x8c, 0xcd, 0x68, 0x55, 0xa3, 0xc2, 0xca, 0x88, 0x19, 0x01, 0xa2, 0x25, 0x11, 0x1b, 0x5b, 0x3b, 0xad, 0xe6, 0x46, 0xe0, 0xac, 0xc6, 0xca, 0x58, 0x7f, 0xd1, 0x04, 0xa1, 0x45, 0x65, 0xff, 0x45, 0x2e, 0x68, 0xef, 0xcf, 0x2f, 0x20, 0x65, 0xaa, 0x86, 0x23, 0x2a, 0xa9, 0x42, 0xcd, 0x98, 0xd7, 0x09, 0x79, 0xff, 0xbb, 0xb3, 0x7e, 0x93, 0x51, 0x8a, 0x01, 0xcd, 0x19, 0xad, 0xd3, 0xb3, 0xf8, 0x07, 0xea, 0x58, 0xed, 0xe7, 0x96, 0x0a, 0xf7, 0x0c, 0x10, 0xf4, 0x51, 0xb9, 0xa9, 0x4e, 0x2e, 0x6a, 0xd4, 0x05, 0x11, 0x6d, 0xab, 0xf4, 0xc5, 0x0f, 0xc9, 0xa9, 0x86, 0xe5, 0x4e, 0x05, 0xcb, 0xab, 0xea, 0x01, 0x68, 0x1a, 0xd4, 0x21, 0x0f, 0x61, 0x08, 0x65, 0x98, 0xac, 0x22, 0xc6, 0xd5, 0x01, 0x04, 0xfc, 0x2f, 0x5f, 0xf7, 0x55, 0x21, 0x23, 0x90, 0xb7, 0xf9, 0x3e, 0x3a, 0x47, 0x8b, 0x60, 0x60, 0x13, 0x09, 0x92, 0xd1, 0xe3, 0x5c, 0x03, 0x42, 0xad, 0xc4, 0x24, 0x35, 0x02, 0xc5, 0x86, 0xbc, 0x62, 0x13, 0x43, 0x31, 0xa0, 0x93, 0x83, 0x3c, 0xe1, 0x11, 0x36, 0x15, 0xa7, 0x30, 0x7d, 0x9b, 0x28, 0x36, 0x79, 0x16, 0xf8, 0xfd, 0xd0, 0x9e, 0x61, 0x40, 0x10, 0x52, 0xa0, 0x3b, 0x2f, 0xe8, 0x92, 0x7d, 0x5d, 0x73, 0x96, 0xb9, 0xfc, 0xcb, 0x93, 0x49, 0xcb, 0xc1, 0xa5, 0x25, 0xb6, 0x7b, 0x2e, 0x76, 0x14, 0xb3, 0x42, 0x80, 0xf7, 0xdb, 0xfe, 0xf0, 0x44, 0x42, 0x35, 0xd7, 0x78, 0x14, 0x2c, 0x06, 0x73, 0xa2, 0x9a, 0x50, 0xaf, 0x5c, 0x11, 0x56, 0x6c, 0x99, 0x27, 0x85, 0xad, 0x29, 0x06, 0x28, 0x5f, 0x32, 0xd1, 0x3b, 0x08, 0x24, 0xdf, 0x78, 0x7d, 0xeb, 0x4f, 0x18, 0x66, 0x14, 0xab, 0x5e, 0x25, 0x03, 0x79, 0xd4, 0x96, 0x9f, 0x47, 0xe0, 0x94, 0x57, 0x27, 0x3a, 0xd1, 0x17, 0x1e, 0xa4, 0x58, 0x9f, 0x81, 0x06, 0x77, 0x45, 0xbc, 0x3a, 0x0a, 0x21, 0x11, 0x91, 0xa1, 0xf9, 0x0d, 0xd3, 0xfb, 0xb0, 0x94, 0x43, 0xee, 0x0c, 0xed, 0x73, 0x13, 0xb1, 0x1b, 0x1a, 0xd7, 0xd8, 0xbe, 0xc3, 0xe2, 0xde, 0x2b, 0x48, 0x77, 0xaa, 0x8e, 0x41, 0x69, 0x55, 0x37, 0xec, 0x65, 0xf2, 0x28, 0xf1, 0xcd, 0x31, 0xf8, 0xe5, 0x12, 0x01, 0xf6, 0x96, 0xd2, 0x68, 0x94, 0x22, 0xd0, 0x5b, 0x02, 0xf7, 0x58, 0x5d, 0x29, 0x35, 0xc8, 0xba, 0xeb, 0xf1, 0x79, 0x85, 0x35, 0x90, 0x78, 0x85, 0xd9, 0xe7, 0xd4, 0xa3, 0x80, 0x81, 0x76, 0x32, 0xb1, 0x0a, 0xfc, 0x75, 0xd9, 0x89, 0x87, 0x53, 0x4d, 0xf2, 0x98, 0x69, 0x81, 0xc3, 0xf9, 0x19, 0x61, 0xd2, 0x88, 0xf8, 0x62, 0xf7, 0x24, 0xf7, 0x27, 0x77, 0x51, 0x96, 0x83, 0x85, 0x86, 0xe9, 0xdc, 0x8e, 0xb6, 0x8f, 0xef, 0x35, 0xff, 0xa9, 0xe3, 0x34, 0x29, 0x6e, 0x3b, 0x94, 0x5a, 0xf6, 0x7b, 0x17, 0x76, 0x7a, 0x5b, 0xde, 0x2b, 0x09, 0xf1, 0x33, 0x5d, 0x74, 0xe4, 0x62, 0x99, 0xdb, 0x7a, 0x4b, 0xc3, 0xdc, 0x7c, 0x26, 0x72, 0xbe, 0x66, 0x11, 0xde, 0xcf, 0xdc, 0xed, 0x3c, 0x06, 0x51, 0xae, 0x51, 0x39, 0x68, 0x72, 0x00, 0x87, 0x1a, 0x97, 0x19, 0x61, 0x56, 0xe6, 0x1d, 0x8b, 0x47, 0xb2, 0x82, 0x19, 0x7d, 0xe3, 0x0f, 0xbc, 0xab, 0x72, 0xdc, 0x5d, 0x32, 0x71, 0x5e, 0xcc, 0x96, 0x3d, 0x2b, 0x02, 0x57, 0x34, 0x84, 0xfc, 0x3f, 0x81, 0xb6, 0x94, 0xca, 0x93, 0xef, 0x5b, 0x94, 0x0e, 0xc0, 0xaf, 0xb7, 0x57, 0x62, 0x31, 0x0e, 0x01, 0x8d, 0xef, 0x50, 0x12, 0x96, 0xca, 0xc7, 0x4b, 0x0f, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x14, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x33, 0xc2, 0x90, 0xa3, 0x43, 0x52, 0x20, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0xd7, 0x18, 0xa6, 0xad, 0x12, 0x6a, 0x1a, 0xaa, 0xe9, 0xfb, 0x77, 0x1a, 0x30, 0x10, 0x87, 0x2d, 0xaa, 0x12, 0x77, 0x0a, 0x87, 0x16, 0x82, 0x84, 0xc6, 0x2b, 0xc0, 0x05, 0x5f, 0xfd, 0x4c, 0x34, 0xd3, 0x24, 0xde, 0x70, 0xd4, 0xaa, 0x4f, 0x07, 0x56, 0x02, 0xca, 0xe5, 0xb1, 0x3d, 0xfa, 0x58, 0xbf, 0x2b, 0x08, 0x68, 0x8d, 0x6d, 0x9f, 0x2e, 0x81, 0x52, 0x95, 0xba, 0xcf, 0x71, 0xb3, 0xe2, 0xe9, 0x11, 0xce, 0x92, 0x3a, 0xab, 0x95, 0x92, 0x40, 0xb8, 0x35, 0xc0, 0x18, 0x0c, 0xff, 0x41, 0x16, 0x53, 0x0a, 0xe2, 0x36, 0xd8, 0x4c, 0x4e, 0xb3, 0x93, 0x88, 0xac, 0x8a, 0xcd, 0x23, 0xa5, 0xb7, 0x1b, 0x1c, 0x28, 0x91, 0x74, 0x1a, 0x86, 0x9a, 0xcb, 0x1b, 0x8d, 0xc4, 0x00, 0xf1, 0x30, 0x95, 0xa3, 0x36, 0x25, 0xd6, 0x07, 0x6b, 0x41, 0x23, 0xdc, 0x6b, 0x0b, 0xd3, 0x02, 0x7c, 0xd2, 0xfc, 0x03, 0xf6, 0xaa, 0xf6, 0x49, 0xc9, 0x8a, 0x45, 0x19, 0xc7, 0xf9, 0x96, 0x06, 0xc2, 0xe6, 0x6e, 0x2e, 0x1e, 0xbc, 0x49, 0x68, 0xa8, 0x37, 0x57, 0x7a, 0xeb, 0x09, 0x52, 0xd4, 0x1c, 0x8a, 0xe1, 0x09, 0x53, 0xe9, 0x93, 0x82, 0x96, 0xe7, 0x0a, 0x8b, 0x03, 0x63, 0x9a, 0x24, 0x96, 0xd0, 0xfc, 0x33, 0xe0, 0xdc, 0x77, 0x41, 0x1d, 0xd5, 0x1f, 0x1b, 0x05, 0x16, 0x1e, 0x31, 0x4f, 0xdf, 0x3c, 0xa6, 0x26, 0xc3, 0x4f, 0xef, 0x13, 0x3a, 0x39, 0x72, 0x18, 0x9b, 0x8e, 0x54, 0x77, 0x69, 0xf9, 0x71, 0x4a, 0x3e, 0x01, 0x19, 0x59, 0x8d, 0xe5, 0x3b, 0x45, 0xe4, 0xb1, 0x2a, 0xa7, 0xc8, 0x17, 0xb3, 0xd5, 0x9b, 0x75, 0xd5, 0xab, 0x03, 0x36, 0x11, 0xf7, 0xab, 0x75, 0xf3, 0x60, 0x4a, 0xc3, 0x5a, 0xed, 0xd4, 0x27, 0xfb, 0x1b, 0xc5, 0xce, 0x33, 0x5b, 0x76, 0x54, 0x8d, 0xe8, 0x00, 0xc9, 0xa7, 0x20, 0x4b, 0xc8, 0x1d, 0x67, 0xe8, 0xdd, 0x07, 0x47, 0x8d, 0x5c, 0xd9, 0xb3, 0x7d, 0xf7, 0x02, 0x64, 0x50, 0x89, 0xae, 0x89, 0x41, 0x65, 0x09, 0xa3, 0x86, 0x28, 0x38, 0xf0, 0x56, 0x19, 0x11, 0x0a, 0x6c, 0x8d, 0x0c, 0x1f, 0x3c, 0x3b, 0x83, 0x22, 0xd3, 0x1d, 0x8d, 0xab, 0x22, 0x13, 0xad, 0x7a, 0x82, 0x27, 0x66, 0x97, 0x02, 0x80, 0xc1, 0xa0, 0x29, 0xcc, 0x71, 0x1f, 0x34, 0xe6, 0x9f, 0x9e, 0x35, 0xb2, 0x5e, 0x78, 0x52, 0xa9, 0x23, 0xac, 0x23, 0xd5, 0x6b, 0x69, 0x2e, 0xa8, 0xdd, 0x3e, 0x33, 0x62, 0x99, 0x6d, 0x1f, 0xa1, 0xfb, 0xd4, 0x8f, 0xb7, 0xba, 0x05, 0x9b, 0x74, 0x36, 0x44, 0xa8, 0xe3, 0x4e, 0xfe, 0xba, 0x9f, 0x63, 0x10, 0x2f, 0x96, 0xfa, 0x9b, 0xec, 0x87, 0x41, 0x05, 0xc4, 0x41, 0xf4, 0x81, 0x02, 0xc8, 0x6e, 0x30, 0x81, 0x8b, 0xfa, 0x67, 0x55, 0x8a, 0xed, 0xa6, 0x6c, 0xd7, 0x66, 0xad, 0x15, 0x2b, 0x00, 0xbc, 0x69, 0x04, 0x4b, 0x7d, 0x81, 0x3e, 0x80, 0xed, 0x0a, 0xb2, 0x68, 0x88, 0xc5, 0x56, 0x32, 0x7c, 0x4c, 0xb2, 0x0f, 0xef, 0xfe, 0x3c, 0x1b, 0x6e, 0xfe, 0x28, 0x08, 0x2c, 0x71, 0x48, 0xaf, 0x4a, 0xe0, 0xb6, 0x42, 0x9e, 0x0b, 0x9e, 0xce, 0x8e, 0xed, 0x58, 0x8b, 0x50, 0x63, 0xd0, 0x2e, 0x66, 0x04, 0x7f, 0xc8, 0xea, 0xb4, 0x38, 0xc5, 0x76, 0xe6, 0xba, 0xb4, 0x17, 0xaa, 0x80, 0xd5, 0x64, 0x98, 0x50, 0x78, 0xa3, 0xce, 0x1d, 0x6d, 0x6f, 0xb4, 0x65, 0x7e, 0xd8, 0xd4, 0x17, 0xdf, 0x68, 0x0c, 0xf7, 0x56, 0xcf, 0xfa, 0x8a, 0xca, 0x5d, 0x1c, 0xa6, 0x72, 0x00, 0x39, 0xea, 0x79, 0xca, 0x4e, 0x63, 0x93, 0x51, 0x20, 0xbc, 0xea, 0xa4, 0x69, 0x05, 0xbd, 0xd4, 0xc1, 0xa4, 0xd4, 0xd0, 0xbd, 0x1a, 0x66, 0xc4, 0x4e, 0xbc, 0xca, 0xf7, 0x25, 0xd8, 0x9a, 0x42, 0x63, 0x7d, 0x15, 0x2f, 0xd1, 0x46, 0x63, 0xf3, 0xf9, 0x6a, 0x2e, 0x8f, 0x4e, 0xe5, 0xc2, 0x46, 0xee, 0x22, 0x93, 0x3b, 0x2c, 0x07, 0x5d, 0xfe, 0x7d, 0x8f, 0x75, 0x84, 0xbc, 0x22, 0x06, 0x58, 0x38, 0x76, 0x9e, 0x8f, 0x71, 0xbe, 0x07, 0x16, 0x61, 0xff, 0x25, 0x64, 0x7f, 0x32, 0x84, 0x88, 0xe1, 0xd2, 0xb1, 0x1e, 0xd6, 0xba, 0x24, 0x0c, 0x72, 0xbf, 0xf3, 0x7a, 0xa1, 0xfe, 0x76, 0x65, 0xab, 0xaa, 0x27, 0xfb, 0x68, 0x1e, 0x67, 0x59, 0x75, 0xec, 0x6d, 0x8d, 0x68, 0xe3, 0xb3, 0x42, 0x20, 0x3a, 0x76, 0x65, 0xa5, 0x7e, 0xd2, 0xca, 0x62, 0xb2, 0x7e, 0xe0, 0x24, 0xae, 0x2d, 0x3b, 0x2a, 0x89, 0xc2, 0xf0, 0xc2, 0xc8, 0x66, 0xb8, 0xbe, 0xfb, 0x53, 0x89, 0x2f, 0x1f, 0x2c, 0xda, 0x5c, 0x02, 0x50, 0x7a, 0x11, 0x6f, 0x64, 0x52, 0xd5, 0x53, 0x8f, 0x9a, 0xa9, 0x0f, 0x30, 0xfa, 0xee, 0x5c, 0x68, 0xd6, 0x6f, 0x5f, 0xb7, 0x95, 0xd9, 0xa9, 0x1b, 0x62, 0x51, 0xd2, 0xf8, 0x99, 0x7a, 0x6c, 0xb5, 0xe1, 0x27, 0x4b, 0x06, 0xed, 0xbc, 0x18, 0x25, 0x21, 0xe5, 0x62, 0x5e, 0x15, 0xb8, 0x14, 0x24, 0xb1, 0xd1, 0x40, 0x1f, 0x36, 0xf7, 0x8d, 0xa6, 0xb2, 0xfd, 0xb2, 0x07, 0x2c, 0xbe, 0x05, 0x4d, 0x75, 0x72, 0xbc, 0xf1, 0xbc, 0xc9, 0x0c, 0xc2, 0x8c, 0xa0, 0x70, 0x63, 0x14, 0xe2, 0xec, 0x7e, 0x6c, 0xf6, 0xbb, 0xb5, 0x40, 0x53, 0xda, 0x97, 0x27, 0x21, 0xb0, 0x46, 0x48, 0x59, 0x35, 0xd2, 0x98, 0xb0, 0x84, 0x03, 0x3d, 0xe6, 0x0f, 0xb5, 0xf0, 0x5d, 0xa6, 0xc0, 0xcd, 0xf3, 0x75, 0x02, 0xda, 0xce, 0x20, 0x06, 0x12, 0x15, 0xac, 0xe0, 0xae, 0xe8, 0x06, 0x1c, 0x8d, 0x4c, 0x96, 0xca, 0x03, 0x45, 0x74, 0xc1, 0x02, 0x96, 0xe1, 0xeb, 0x8f, 0xc4, 0xb2, 0xe7, 0xae, 0xd8, 0xbe, 0x52, 0x9b, 0x0f, 0xa5, 0xf9, 0x4e, 0x27, 0xab, 0x96, 0xc9, 0x27, 0xed, 0x89, 0xd0, 0x3c, 0x45, 0xa5, 0x18, 0xe3, 0x4c, 0xc6, 0x99, 0x9d, 0xe9, 0x9b, 0x83, 0x56, 0x16, 0x5d, 0x3a, 0x21, 0x82, 0x19, 0x48, 0x6c, 0x73, 0x3f, 0x0d, 0xf0, 0x3e, 0xce, 0x86, 0x70, 0x0c, 0x22, 0xdb, 0x7d, 0x0e, 0x5b, 0x35, 0x98, 0x73, 0x7a, 0x33, 0x94, 0xf9, 0x78, 0xd8, 0xee, 0x5e, 0x99, 0xb0, 0x8b, 0xd3, 0x42, 0x46, 0x6e, 0x93, 0x1b, 0x25, 0xa8, 0x14, 0xa8, 0x7d, 0xc4, 0x7b, 0x01, 0xc1, 0x01, 0xdd, 0x05, 0x57, 0x4b, 0x2a, 0x74, 0xda, 0x2e, 0x04, 0x58, 0x10, 0x34, 0xd1, 0x0e, 0xaa, 0xca, 0x24, 0x66, 0x91, 0x17, 0xec, 0xca, 0xab, 0xb7, 0x6d, 0xa2, 0x35, 0xad, 0x0d, 0x60, 0xd3, 0x36, 0xbd, 0x63, 0x40, 0x8b, 0xd7, 0xad, 0x42, 0x17, 0xe8, 0xda, 0xc6, 0x27, 0x72, 0xcc, 0x78, 0x18, 0x8e, 0x66, 0x20, 0xc8, 0x84, 0x86, 0x5d, 0x59, 0x20, 0x3e, 0x7c, 0x08, 0x7e, 0x66, 0xaf, 0x9b, 0x38, 0x3e, 0x34, 0x4b, 0x33, 0xa7, 0xf1, 0xce, 0x27, 0x04, 0x6d, 0xc1, 0x2f, 0xa7, 0x85, 0xa7, 0x96, 0x84, 0x42, 0xb0, 0x73, 0x70, 0xd1, 0xe5, 0xa1, 0xbf, 0xec, 0x65, 0xce, 0xe8, 0x50, 0x2e, 0x2d, 0x61, 0xca, 0xb6, 0x7d, 0xb5, 0x4e, 0x48, 0x20, 0xc8, 0x80, 0x52, 0x5e, 0xca, 0xfb, 0xc4, 0x1b, 0xdb, 0x4e, 0x46, 0xcc, 0x49, 0xe6, 0xef, 0xb5, 0xde, 0x99, 0xdb, 0x65, 0x74, 0x05, 0xe3, 0x47, 0xa1, 0xd6, 0x7a, 0xed, 0x91, 0xd6, 0xe3, 0x81, 0x27, 0x50, 0x0d, 0x41, 0xc1, 0xf5, 0x93, 0x25, 0x2b, 0x6c, 0xa9, 0x36, 0xd2, 0xe7, 0x1e, 0xe0, 0xa9, 0x95, 0x68, 0xfd, 0xa7, 0xb9, 0xe1, 0x81, 0x95, 0xd0, 0x8c, 0xce, 0x12, 0x6c, 0xe0, 0x29, 0x6f, 0xee, 0x33, 0xde, 0x46, 0x64, 0xa9, 0x72, 0x82, 0x03, 0x1d, 0x1e, 0xdd, 0x9f, 0x1c, 0x5c, 0x39, 0xc8, 0xc3, 0x16, 0x9c, 0xf6, 0xb3, 0x7b, 0xec, 0x86, 0xb9, 0xe9, 0xe1, 0x4e, 0xb1, 0x14, 0xfd, 0x9c, 0x26, 0xb3, 0x81, 0x25, 0xab, 0x4c, 0x25, 0xd3, 0xa2, 0x63, 0x77, 0x0f, 0xf5, 0x26, 0xc5, 0x14, 0x0c, 0x53, 0x35, 0xa7, 0x7d, 0xa0, 0xce, 0x4e, 0x04, 0x97, 0x77, 0x4c, 0x74, 0x6a, 0xf3, 0xeb, 0xdd, 0xae, 0x27, 0xfc, 0xf7, 0xd0, 0x21, 0xea, 0x46, 0x36, 0x22, 0x84, 0x9d, 0x8c, 0x0c, 0xef, 0x87, 0x0c, 0xac, 0x3a, 0xcc, 0x76, 0xf3, 0xe0, 0xac, 0x7d, 0x01, 0x93, 0x1a, 0x2d, 0x42, 0x99, 0xf3, 0x46, 0xc6, 0xa5, 0x87, 0x9a, 0x44, 0xa4, 0x29, 0xa2, 0xb3, 0x5c, 0x80, 0x10, 0xd4, 0xb7, 0x35, 0x58, 0xc0, 0xd2, 0x65, 0x14, 0x28, 0x4c, 0x95, 0x6e, 0x9b, 0x33, 0x6c, 0x9a, 0xf5, 0x8b, 0x33, 0x92, 0xae, 0x89, 0x53, 0xc5, 0x08, 0x62, 0xbd, 0x57, 0x72, 0xad, 0xb9, 0x35, 0x2c, 0xe5, 0xf6, 0xa4, 0x70, 0xeb, 0xd1, 0xb9, 0x6c, 0x58, 0x1a, 0x12, 0xf5, 0x55, 0xb9, 0x95, 0x69, 0x22, 0xcf, 0x58, 0x88, 0xd2, 0x3f, 0x1f, 0x1d, 0xca, 0x90, 0x5e, 0x73, 0xce, 0x9d, 0xd6, 0x8b, 0x74, 0xa6, 0xb2, 0xe2, 0x62, 0x64, 0xa0, 0xa6, 0xc4, 0xfe, 0xf7, 0x4b, 0x07, 0x37, 0xd2, 0xe3, 0xd3, 0xe1, 0xc3, 0x92, 0xd0, 0x7c, 0x1c, 0x93, 0x08, 0xd9, 0xc5, 0x76, 0xeb, 0x45, 0x58, 0x3f, 0xf4, 0x2c, 0xab, 0xca, 0x93, 0x5c, 0x6c, 0x75, 0xed, 0x1e, 0xa1, 0x15, 0xc5, 0xfa, 0x22, 0xbd, 0x40, 0x6b, 0x96, 0xae, 0xa3, 0x4c, 0x49, 0xee, 0xb9, 0xd7, 0x85, 0x53, 0x24, 0x11, 0x86, 0x43, 0x18, 0x9b, 0x3c, 0x00, 0x89, 0x48, 0x73, 0x5c, 0x91, 0x1b, 0x80, 0x9a, 0x19, 0x0e, 0x19, 0x35, 0x95, 0x51, 0x88, 0xfe, 0xe9, 0x2c, 0x59, 0xdd, 0x95, 0x19, 0xe6, 0x71, 0xa8, 0x84, 0x66, 0x2c, 0xee, 0xc0, 0xa7, 0x4b, 0x9d, 0xef, 0xbf, 0xa5, 0x11, 0x0f, 0xb7, 0xa8, 0x38, 0xb6, 0x4c, 0x1c, 0x93, 0x3d, 0x00, 0x32, 0xb2, 0x4b, 0xb4, 0x7d, 0xf7, 0xb0, 0xab, 0xdb, 0xc0, 0xb0, 0xb1, 0x71, 0x2c, 0xd7, 0x83, 0x2e, 0xe8, 0x88, 0x3b, 0x01, 0xb2, 0xe1, 0x36, 0xcf, 0xca, 0xea, 0x6f, 0xd8, 0x09, 0xa7, 0x6d, 0x61, 0x99, 0x29, 0x35, 0x46, 0xc7, 0xf2, 0x0d, 0xf6, 0xd0, 0x4b, 0x4e, 0x8f, 0x66, 0x58, 0x31, 0x0b, 0x0b, 0x74, 0xbc, 0x31, 0x3d, 0x35, 0x7d, 0x54, 0x3f, 0x13, 0x47, 0x24, 0xb9, 0xea, 0x94, 0x48, 0x4c, 0x3c, 0xdc, 0xb1, 0x97, 0x0f, 0xbe, 0x74, 0x07, 0xe1, 0x80, 0x98, 0x6c, 0xd4, 0xc5, 0xd1, 0xe9, 0x7e, 0x00, 0xf1, 0x79, 0x26, 0x2e, 0xdc, 0x90, 0x91, 0xb1, 0x44, 0x7e, 0xa4, 0x8f, 0x48, 0xe8, 0xea, 0xbf, 0x39, 0xfe, 0xb3, 0xb3, 0x60, 0xed, 0xfa, 0xc8, 0x89, 0x27, 0x16, 0x76, 0x58, 0x51, 0xcc, 0xa7, 0xf5, 0x24, 0x8a, 0xaa, 0x0d, 0x19, 0x36, 0x92, 0x66, 0x1b, 0xe9, 0x9f, 0xb7, 0x26, 0x12, 0xf2, 0xf5, 0x36, 0x33, 0x88, 0x5a, 0xa1, 0x54, 0x41, 0x69, 0x3b, 0x05, 0x46, 0x2d, 0x8c, 0x00, 0xc5, 0xf9, 0x67, 0x63, 0xba, 0x6f, 0x01, 0x4f, 0x4a, 0x82, 0x2f, 0x28, 0x35, 0xc8, 0x01, 0xce, 0x6f, 0xc8, 0x5a, 0xea, 0x24, 0x14, 0xeb, 0xf0, 0x69, 0x93, 0x50, 0xd9, 0x2b, 0x96, 0x8c, 0x5e, 0x75, 0x50, 0x92, 0xea, 0xcb, 0xcd, 0x38, 0x87, 0xdf, 0x0f, 0x21, 0x2c, 0x8c, 0x28, 0xdc, 0xc7, 0x94, 0x6a, 0x32, 0xe6, 0x67, 0xa0, 0x6b, 0x54, 0xf8, 0x10, 0x92, 0xa6, 0xc7, 0x55, 0xba, 0xc5, 0x82, 0xf8, 0x98, 0x96, 0x1f, 0x0a, 0x54, 0x86, 0xd9, 0x6c, 0xa7, 0xa8, 0x1f, 0x0d, 0x15, 0x54, 0x89, 0x10, 0xe5, 0xe5, 0xb5, 0xe2, 0x2c, 0x21, 0x0a, 0x70, 0x49, 0x43, 0x5f, 0xa5, 0xa2, 0xa9, 0x38, 0x4a, 0x93, 0x43, 0x5f, 0xfe, 0xd2, 0x8b, 0xfb, 0x56, 0x3d, 0x05, 0x2a, 0xd8, 0x74, 0x49, 0x15, 0x2b, 0xcb, 0xcb, 0xf0, 0x01, 0x54, 0x50, 0x9d, 0xed, 0x71, 0xcf, 0x19, 0x6d, 0x49, 0x68, 0xdc, 0x66, 0x33, 0xa9, 0x42, 0x6f, 0xe6, 0x0e, 0xd8, 0xd1, 0x4b, 0x22, 0x3a, 0x8c, 0xc0, 0x81, 0x5a, 0xc8, 0x64, 0x97, 0x18, 0x3d, 0xf8, 0xad, 0x3b, 0xec, 0x6f, 0x9a, 0x17, 0x90, 0x47, 0x38, 0x6a, 0xfe, 0x56, 0x80, 0x88, 0x6d, 0xfa, 0x00, 0xba, 0xda, 0x66, 0x45, 0x41, 0x06, 0x3e, 0xe0, 0x4b, 0xdc, 0x83, 0x59, 0x71, 0x16, 0x99, 0x6e, 0xc9, 0x41, 0xa3, 0x81, 0x3c, 0xf7, 0x18, 0x96, 0x6a, 0x24, 0x40, 0x6f, 0x3f, 0xbd, 0x6a, 0xe7, 0xba, 0x63, 0xff, 0xe8, 0x4b, 0x75, 0xc3, 0x54, 0xe6, 0xc8, 0xc8, 0x89, 0x29, 0x2e, 0x4a, 0xec, 0xf3, 0x2f, 0x6d, 0x84, 0x31, 0xc3, 0x22, 0x52, 0x62, 0xf6, 0xf3, 0x16, 0xe9, 0xcb, 0xe0, 0x68, 0xef, 0x75, 0x7a, 0x6d, 0x95, 0xdf, 0x84, 0x3a, 0xa0, 0x10, 0xf8, 0x7d, 0x1d, 0xda, 0x8d, 0x8a, 0x30, 0x18, 0xd5, 0x80, 0xd4, 0x4e, 0x05, 0xbc, 0x6d, 0x1f, 0x79, 0x3d, 0x83, 0x0a, 0x5a, 0x4f, 0x41, 0xb4, 0x46, 0xba, 0xcb, 0x0f, 0xd0, 0xc9, 0x87 }; +constexpr AccessUnit ATRACX_MULTIPLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, false, 0, {}, { 0x78, 0x2c, 0xd2, 0xa4, 0x87, 0x7f, 0xa1, 0xcb, 0x57, 0xad, 0x86, 0x2b, 0xe4, 0xb7, 0xe1, 0xfc, 0xa1, 0xab, 0xb8, 0xc5 } }; +constexpr AccessUnit ATRACX_MULTIPLE_PACK_EXPECTED_AU_2 = { 0x2aefeae9, 0x778806c4, false, 0, {}, { 0x0e, 0x9b, 0x87, 0x06, 0x59, 0x9a, 0xe2, 0xb6, 0xf7, 0x53, 0xfc, 0x2d, 0x47, 0x60, 0xca, 0x7d, 0x52, 0xd1, 0xd4, 0x61 } }; +constexpr AccessUnit ATRACX_MULTIPLE_PACK_EXPECTED_AU_3 = { 0x15f90, 0x159b2, false, 0, {}, { 0x59, 0x1b, 0x5e, 0x0b, 0x45, 0xe7, 0x7d, 0xfb, 0x30, 0x15, 0xce, 0x9b, 0xc1, 0xea, 0xc7, 0x1c, 0xad, 0x4d, 0x3c, 0xe1 } }; +constexpr AccessUnit ATRACX_MULTIPLE_PACK_EXPECTED_AU_4 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0x8c, 0xf7, 0x8f, 0x01, 0x95, 0x33, 0x90, 0xf4, 0xb6, 0x81, 0xcf, 0x8f, 0x9f, 0xd2, 0xf0, 0x9c, 0xd9, 0xae, 0xc1, 0xe4 } }; + +constexpr std::array AC3_MULTIPLE_PACK_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x30, 0x00, 0x00, 0x00, 0xf3, 0xa6, 0x50, 0x0c, 0xe5, 0x05, 0x0e, 0x03, 0xb9, 0x70, 0x70, 0xf1, 0xfa, 0xac, 0xf5, 0xd7, 0x56, 0x65, 0xa2, 0x06, 0x5a, 0x45, 0xc1, 0x76, 0x51, 0xd2, 0xde, 0x1a, 0xcd, 0x4c, 0xa4, 0x7c, 0xb5, 0xaf, 0xc9, 0xc6, 0xd5, 0xd5, 0x4b, 0x35, 0x5b, 0x70, 0x38, 0xbb, 0xf4, 0x89, 0x9d, 0x5d, 0xa7, 0x5d, 0x05, 0x21, 0x84, 0x11, 0xf6, 0xdc, 0x83, 0x0a, 0x1b, 0xa7, 0x79, 0x16, 0xe5, 0xdc, 0x2a, 0x8e, 0x45, 0x86, 0xe5, 0x45, 0xdc, 0x0b, 0x77, 0x56, 0xd1, 0x5d, 0x71, 0x45, 0xf7, 0xb6, 0xbe, 0x24, 0xd4, 0x03, 0xce, 0xea, 0x38, 0xed, 0xc8, 0x79, 0xb8, 0x05, 0xbb, 0x49, 0x5c, 0x08, 0xbe, 0x2c, 0x9c, 0x06, 0xf2, 0x58, 0x04, 0x30, 0x62, 0x75, 0x45, 0x3e, 0xf8, 0xda, 0x1b, 0x63, 0x06, 0x96, 0x35, 0xc2, 0x03, 0xdc, 0x2a, 0x0c, 0xb9, 0x61, 0x06, 0x1f, 0x66, 0x46, 0xa1, 0xe5, 0x40, 0xba, 0xec, 0xf2, 0x4b, 0xdc, 0xab, 0xd3, 0xaa, 0xe0, 0xf4, 0x3a, 0xdd, 0x40, 0x0d, 0xfb, 0x6e, 0x2e, 0x79, 0x1c, 0xd4, 0xd0, 0x41, 0xd1, 0x3b, 0x26, 0x9e, 0x84, 0xc7, 0x3b, 0x94, 0x6a, 0x46, 0x67, 0xef, 0x5c, 0x96, 0x31, 0xa4, 0xea, 0xb8, 0xfa, 0x4d, 0x1b, 0xac, 0x8b, 0x4c, 0xe5, 0x90, 0xc7, 0xda, 0x98, 0x01, 0xcb, 0x2e, 0x86, 0x8f, 0xbe, 0xb5, 0x8a, 0x3e, 0x01, 0xad, 0xb9, 0x24, 0x7f, 0x7a, 0xb8, 0xe9, 0xb3, 0x64, 0xe0, 0xe1, 0x32, 0x40, 0xed, 0x30, 0x1c, 0x3a, 0x1b, 0x22, 0x86, 0xee, 0xf1, 0x7d, 0xbc, 0x2a, 0xd6, 0x2e, 0x01, 0x1b, 0x0d, 0xb0, 0x54, 0xdd, 0xe7, 0xbf, 0x64, 0x98, 0x4a, 0x48, 0x9e, 0x7c, 0x6e, 0x35, 0x04, 0x38, 0x5b, 0x7b, 0x18, 0xbd, 0x06, 0x51, 0x66, 0xe3, 0x1b, 0x61, 0xf4, 0x35, 0xbc, 0x4b, 0xbd, 0x67, 0x2d, 0x95, 0x55, 0x06, 0xb8, 0x20, 0xb3, 0xac, 0xdc, 0x1c, 0x2c, 0x5d, 0x3f, 0xf1, 0xb6, 0x6c, 0xdb, 0x81, 0x6c, 0xc3, 0xca, 0x03, 0x7e, 0x6a, 0x39, 0x53, 0x8e, 0xd0, 0xbe, 0xe3, 0x24, 0x45, 0x3a, 0x40, 0xd1, 0xba, 0x90, 0x61, 0x4d, 0xfe, 0x59, 0xfd, 0x90, 0x0d, 0x66, 0xe9, 0x79, 0x78, 0x5f, 0x52, 0xce, 0xf8, 0xc6, 0x6c, 0xeb, 0x83, 0x42, 0xba, 0xe2, 0x20, 0x93, 0x39, 0xa7, 0x3b, 0xfc, 0x7c, 0x6a, 0x44, 0xaa, 0x21, 0x76, 0xbb, 0xc2, 0x05, 0x7e, 0xfd, 0xc7, 0x0c, 0x9e, 0x1b, 0xf0, 0xa3, 0x09, 0x0d, 0xb8, 0xbe, 0x4a, 0x14, 0xa6, 0x1c, 0x8a, 0xf0, 0xf8, 0x8a, 0x63, 0x9a, 0xb6, 0x87, 0xd4, 0x40, 0xd9, 0x65, 0xa3, 0x14, 0xf2, 0x43, 0x75, 0x47, 0x0a, 0xb1, 0xf6, 0x8a, 0xa1, 0x5c, 0xe9, 0x44, 0xe6, 0x89, 0x6d, 0x94, 0xc3, 0x2b, 0xe4, 0x6e, 0xba, 0xb8, 0x3b, 0x40, 0xff, 0xa0, 0x9a, 0x32, 0x17, 0x35, 0x3c, 0xea, 0x5e, 0x7b, 0x43, 0x4e, 0x0e, 0x7c, 0xfd, 0x2a, 0x19, 0x3d, 0xf7, 0x88, 0xab, 0x64, 0xa8, 0x3b, 0xcd, 0x6b, 0x41, 0x09, 0x21, 0xa9, 0xe3, 0x0c, 0x20, 0x5a, 0xa5, 0xda, 0x0e, 0xb7, 0xb4, 0x4a, 0xe2, 0xef, 0x14, 0x38, 0xe3, 0xc0, 0xac, 0x0e, 0x24, 0xb3, 0xc4, 0xbe, 0x01, 0x9c, 0x61, 0xbf, 0x04, 0x85, 0xc2, 0x9b, 0x18, 0xea, 0x65, 0xf0, 0x3f, 0x1b, 0xba, 0xff, 0x40, 0x70, 0x46, 0x86, 0x74, 0x7b, 0x45, 0x5a, 0x36, 0x8f, 0x7c, 0xc6, 0x4a, 0xfd, 0x97, 0xa8, 0x13, 0x3e, 0xf1, 0xec, 0xe5, 0x88, 0x13, 0xfc, 0xc6, 0x0c, 0x4a, 0x14, 0xb3, 0xa4, 0x04, 0x97, 0x66, 0x8c, 0x9d, 0xb3, 0x65, 0x4e, 0x05, 0xf9, 0x33, 0x0d, 0x3f, 0xe8, 0x5f, 0x69, 0xf2, 0x2b, 0x6b, 0x1b, 0x8f, 0x81, 0xf7, 0xca, 0x06, 0xf4, 0xe4, 0x81, 0x1f, 0xa8, 0x20, 0x25, 0xc5, 0x48, 0x3c, 0xaa, 0xfb, 0x78, 0x01, 0x3c, 0xc4, 0x04, 0x46, 0x80, 0x57, 0x77, 0x2d, 0x64, 0x60, 0x8e, 0x60, 0x52, 0x5e, 0x9e, 0xc1, 0x3e, 0x22, 0x4d, 0xc8, 0x39, 0x76, 0x24, 0x11, 0x9a, 0x1f, 0xfc, 0xe7, 0x4f, 0x04, 0xfa, 0xed, 0x65, 0xca, 0x83, 0xc4, 0xfe, 0x30, 0xd8, 0xe5, 0x2c, 0x99, 0x5d, 0x2c, 0x86, 0xb8, 0xf2, 0x88, 0x3d, 0x2c, 0x84, 0xe2, 0x62, 0x94, 0xa6, 0x1a, 0x3f, 0xed, 0x56, 0x29, 0x18, 0x7b, 0x0b, 0x02, 0xa7, 0x3b, 0xe5, 0x48, 0xfe, 0xbf, 0xa6, 0xad, 0x55, 0x52, 0x1b, 0x50, 0x5a, 0xec, 0x4f, 0x1b, 0xa2, 0x09, 0x52, 0xc3, 0xbc, 0x8a, 0x6b, 0xb1, 0x8e, 0xd1, 0x44, 0xc4, 0x96, 0x21, 0xc0, 0x03, 0x04, 0xfd, 0x7f, 0x49, 0x0a, 0x66, 0x24, 0x58, 0x00, 0xb1, 0x1a, 0xbe, 0x91, 0x0c, 0x92, 0x9a, 0xe8, 0x53, 0x67, 0x08, 0xfc, 0xbf, 0xb0, 0x7c, 0xaa, 0x38, 0xf8, 0x03, 0x88, 0x44, 0xd0, 0xeb, 0x48, 0x08, 0xa4, 0x35, 0x17, 0x98, 0x7a, 0x12, 0xb2, 0x30, 0x8c, 0xa4, 0xbd, 0xa1, 0xf2, 0x19, 0x60, 0xd2, 0xa2, 0xb9, 0x7f, 0x2a, 0xbc, 0xc6, 0x8e, 0xbd, 0xa8, 0xc0, 0xcb, 0xb3, 0x57, 0xea, 0x67, 0x0e, 0xd2, 0xf8, 0x22, 0x93, 0x1e, 0x71, 0x0e, 0xb5, 0x08, 0x00, 0x74, 0xd3, 0x8a, 0x9c, 0xc6, 0xf5, 0x61, 0x31, 0x65, 0xb8, 0x1c, 0x31, 0xa0, 0xb6, 0x20, 0x1d, 0xf8, 0x5d, 0x50, 0xdd, 0xde, 0x0f, 0x41, 0xd9, 0x67, 0xac, 0xc2, 0x42, 0x65, 0x34, 0xb9, 0x8d, 0x02, 0x2c, 0x10, 0x33, 0x77, 0x25, 0x9d, 0xfc, 0x0c, 0x1b, 0xed, 0xe8, 0x21, 0x06, 0x33, 0xf5, 0xe8, 0xb9, 0x63, 0x9a, 0x89, 0x34, 0x1b, 0x4e, 0xb5, 0x0c, 0xbe, 0x10, 0xd1, 0x30, 0x30, 0x92, 0xef, 0x34, 0xf8, 0xcf, 0x61, 0x90, 0x29, 0x9b, 0x65, 0x64, 0x95, 0xac, 0xbe, 0xce, 0x24, 0x38, 0x1c, 0xa3, 0xd6, 0xf4, 0x7d, 0x7a, 0x47, 0x2e, 0xa4, 0x0d, 0x86, 0x6b, 0xd2, 0xa1, 0x7c, 0xce, 0x30, 0x37, 0xfa, 0x9b, 0x9e, 0xe2, 0xbe, 0x0f, 0xad, 0x0e, 0x43, 0xed, 0x5f, 0xa0, 0x9f, 0xae, 0xaf, 0x93, 0x5f, 0xcd, 0x44, 0xc5, 0xc7, 0x87, 0xaf, 0xc2, 0xda, 0x30, 0x50, 0x8d, 0x71, 0xda, 0x0c, 0x81, 0x16, 0x7a, 0xb3, 0xb9, 0xda, 0x49, 0x49, 0x3f, 0x46, 0xcd, 0x5f, 0xe6, 0xda, 0xa0, 0xf5, 0x76, 0x5d, 0x78, 0xeb, 0x9e, 0xdf, 0xf3, 0x59, 0x7c, 0x1a, 0x12, 0x54, 0x9b, 0xe9, 0x39, 0x4c, 0xe2, 0x89, 0xe6, 0x33, 0x96, 0xf9, 0xd7, 0xe8, 0x21, 0x9f, 0x20, 0x5c, 0xd8, 0xdb, 0x3d, 0xeb, 0x20, 0xc9, 0xa1, 0x42, 0xad, 0x54, 0xb7, 0xbd, 0x88, 0x13, 0x58, 0x11, 0x3c, 0xcc, 0x77, 0xae, 0xfe, 0xef, 0xea, 0xd5, 0x7b, 0x5f, 0xde, 0xf5, 0xbb, 0xd9, 0xbf, 0xea, 0x2c, 0x33, 0xca, 0xaa, 0xf2, 0x1e, 0x76, 0xbb, 0x52, 0x12, 0x00, 0xf7, 0xcb, 0xe1, 0x4a, 0x9b, 0x77, 0x25, 0xdf, 0xff, 0x4f, 0x5f, 0x26, 0x53, 0x76, 0x62, 0x0e, 0x9d, 0xe8, 0x04, 0x58, 0x4a, 0x41, 0x19, 0xe4, 0x21, 0x70, 0xe6, 0xd1, 0xef, 0x43, 0x45, 0x36, 0x6d, 0xf9, 0x66, 0xa3, 0x3e, 0xc3, 0xac, 0x2a, 0x92, 0x84, 0xf3, 0xea, 0xbd, 0x0c, 0xac, 0xd8, 0x6e, 0x83, 0x32, 0x5f, 0x3f, 0x0b, 0xbb, 0x52, 0xa8, 0xdc, 0xec, 0xcf, 0x2c, 0xe0, 0xbc, 0x39, 0x2c, 0xb2, 0xf2, 0x10, 0x96, 0x35, 0xf1, 0xe7, 0xff, 0x21, 0xe4, 0x3f, 0x24, 0xeb, 0x6c, 0x10, 0x9e, 0x35, 0xcb, 0x4f, 0x29, 0xcf, 0xcf, 0x00, 0xe6, 0xc2, 0xd0, 0xda, 0x11, 0x30, 0xb2, 0x38, 0x24, 0x5c, 0x18, 0xec, 0x96, 0x1f, 0x69, 0x30, 0xc9, 0x74, 0xc0, 0x20, 0xd7, 0x04, 0xe1, 0xb1, 0x4c, 0xc9, 0x04, 0x70, 0x39, 0xbf, 0xae, 0x53, 0x0e, 0xa4, 0x21, 0xfe, 0xd5, 0xdb, 0x88, 0x6a, 0x75, 0xf0, 0xb8, 0x51, 0xf6, 0xd2, 0xce, 0x45, 0x9a, 0x03, 0x0c, 0x64, 0xea, 0x3f, 0x16, 0x41, 0x7e, 0x79, 0xe1, 0x99, 0x41, 0x40, 0xa6, 0x71, 0x06, 0x15, 0x31, 0x8f, 0x88, 0x24, 0x41, 0xf7, 0xed, 0x1a, 0xb9, 0xac, 0x22, 0x74, 0x74, 0xf6, 0x90, 0x41, 0x57, 0x9f, 0xae, 0x85, 0xb7, 0xe2, 0xa4, 0x20, 0xa1, 0x56, 0xec, 0x76, 0x3c, 0x5c, 0x60, 0x24, 0xc9, 0xd5, 0xca, 0xfb, 0xa1, 0x8c, 0x34, 0x6e, 0xca, 0x3a, 0xea, 0x25, 0xaf, 0xd8, 0x49, 0x87, 0xf7, 0x39, 0x25, 0x52, 0x7c, 0xc0, 0xec, 0xb3, 0x36, 0x62, 0xac, 0xc3, 0x5f, 0x84, 0x5a, 0x26, 0xb6, 0xd2, 0x95, 0x2a, 0xf2, 0x7e, 0xc7, 0x70, 0x4e, 0xbf, 0x45, 0x57, 0x04, 0x4b, 0xb5, 0xb7, 0xfc, 0xd6, 0x6c, 0xae, 0xef, 0xe2, 0x25, 0xff, 0xa1, 0xaa, 0x2c, 0x31, 0xd9, 0xe5, 0xa0, 0xca, 0x8d, 0x87, 0xbc, 0x45, 0x42, 0x7a, 0x87, 0x1d, 0x29, 0x18, 0x9f, 0xa1, 0x75, 0x41, 0x0d, 0x96, 0xa7, 0x61, 0xf1, 0xd9, 0x24, 0x35, 0xc6, 0xdf, 0xf8, 0x6e, 0x66, 0x36, 0xea, 0x59, 0xcd, 0xb8, 0xd8, 0xb0, 0x10, 0xa2, 0x03, 0xf2, 0x3f, 0x83, 0x12, 0xb7, 0xc3, 0x85, 0x47, 0x03, 0x95, 0xea, 0x3a, 0x6d, 0xe4, 0xb9, 0xa6, 0x3b, 0x9b, 0x4d, 0xb4, 0x29, 0x63, 0xf4, 0xac, 0x14, 0xc6, 0xb7, 0xca, 0xec, 0xe7, 0xab, 0x9a, 0xf9, 0x68, 0xb6, 0xf1, 0x8c, 0x7b, 0xca, 0x9a, 0xb2, 0xc5, 0x90, 0x47, 0xbc, 0x39, 0xd0, 0x36, 0x1f, 0xe1, 0x2e, 0xaa, 0x1d, 0x08, 0x1c, 0xfa, 0x7e, 0x30, 0x31, 0x82, 0x5f, 0xf8, 0x98, 0xc7, 0x9d, 0xaa, 0x5c, 0x99, 0xbc, 0x0c, 0x0c, 0xa0, 0x31, 0xf5, 0x75, 0xb8, 0x03, 0x5d, 0xa1, 0x13, 0x6c, 0xd9, 0x0b, 0x50, 0xca, 0x7b, 0xad, 0x9b, 0xbc, 0x7a, 0xde, 0x31, 0x67, 0xad, 0xf9, 0x35, 0x61, 0xf5, 0x26, 0xcc, 0x27, 0xb7, 0xa1, 0x70, 0xa5, 0x60, 0x5e, 0xf2, 0x94, 0xe6, 0xf9, 0x88, 0x59, 0xdf, 0xf1, 0xc1, 0x50, 0x2e, 0xed, 0xe7, 0x92, 0xf5, 0x2a, 0x40, 0x75, 0x5f, 0x40, 0xa3, 0xce, 0x8e, 0xb0, 0x4b, 0xcb, 0x0e, 0x6f, 0x11, 0xb3, 0xa1, 0x46, 0x7f, 0x3f, 0x2a, 0xcb, 0x7f, 0xa8, 0xb0, 0x9f, 0x31, 0xcd, 0x85, 0xcb, 0x35, 0xde, 0x34, 0x12, 0x16, 0x61, 0x5d, 0x18, 0xda, 0x04, 0x85, 0xa1, 0xf0, 0x08, 0xe7, 0xad, 0x57, 0xab, 0x37, 0xbd, 0x63, 0x38, 0x0f, 0xc5, 0x5f, 0x89, 0x0a, 0x40, 0xa7, 0x38, 0x3c, 0x95, 0x5a, 0x33, 0xa3, 0x12, 0x19, 0x73, 0x72, 0x1b, 0x58, 0x0b, 0x65, 0xd7, 0x11, 0x9c, 0x6e, 0x44, 0xff, 0x6f, 0x31, 0x64, 0x50, 0x57, 0xe1, 0x88, 0xad, 0x11, 0xa8, 0xd8, 0x71, 0x67, 0x52, 0xa1, 0xcb, 0x49, 0x88, 0x05, 0x3f, 0x6e, 0xb1, 0x50, 0x33, 0xd0, 0x28, 0x08, 0x2f, 0x5a, 0xd4, 0x6b, 0xcb, 0xc0, 0x08, 0xfd, 0xbc, 0xc2, 0x49, 0xe7, 0x29, 0x15, 0x4f, 0x01, 0x82, 0x0c, 0x75, 0xd8, 0xc1, 0xc5, 0xa4, 0x98, 0x78, 0x61, 0x3f, 0xf5, 0x72, 0x78, 0x7c, 0x15, 0xb6, 0xe9, 0x58, 0x90, 0x1c, 0x0c, 0xa8, 0x63, 0xe3, 0x37, 0x15, 0xc3, 0xfe, 0xa9, 0xe0, 0x22, 0x0f, 0x35, 0x28, 0xd9, 0x6c, 0x33, 0x92, 0x3a, 0x3c, 0xd2, 0x26, 0x52, 0x7f, 0xa8, 0x0b, 0xde, 0x77, 0x93, 0x03, 0xc2, 0x1c, 0x19, 0xa7, 0x99, 0x6f, 0x98, 0xa2, 0x12, 0x5b, 0xa2, 0xce, 0xcc, 0x7b, 0x87, 0x30, 0x13, 0x04, 0x9e, 0x6b, 0xc1, 0xf5, 0x4d, 0x4d, 0xb1, 0xcb, 0xf2, 0x7e, 0xce, 0x90, 0x14, 0x7f, 0xd4, 0xca, 0x52, 0xb0, 0xf9, 0xd6, 0xeb, 0x16, 0x3f, 0x4a, 0x55, 0x43, 0xdf, 0x09, 0xa8, 0xfa, 0x8e, 0x7c, 0xed, 0x61, 0x62, 0xc3, 0x89, 0x96, 0xc3, 0x67, 0xef, 0x8d, 0xf4, 0x34, 0xde, 0x87, 0x20, 0x8e, 0x70, 0x93, 0x0e, 0xf8, 0x34, 0xc0, 0x99, 0xb5, 0x8c, 0xa9, 0xe8, 0x40, 0x99, 0x03, 0xb9, 0xe1, 0x00, 0x10, 0x3e, 0xd0, 0x1c, 0x7e, 0xd5, 0xa0, 0x71, 0x10, 0xef, 0xf3, 0xc2, 0x54, 0x14, 0x57, 0x22, 0xf5, 0x5b, 0xc8, 0x6c, 0x60, 0xbd, 0xe9, 0x20, 0x7b, 0x67, 0xbe, 0x3b, 0xfe, 0xa4, 0xcf, 0x32, 0x1c, 0x45, 0xa7, 0xd8, 0x20, 0x22, 0xf7, 0xac, 0x16, 0x00, 0xb1, 0x5a, 0xcc, 0x41, 0x6d, 0x76, 0x6b, 0x85, 0xc7, 0x22, 0x29, 0x21, 0x78, 0x21, 0xf0, 0x5c, 0xa0, 0xfb, 0x52, 0x50, 0x40, 0xbb, 0x7a, 0x9e, 0x65, 0x08, 0xd8, 0x84, 0xa5, 0xed, 0x22, 0x87, 0x65, 0x88, 0x07, 0x6b, 0x19, 0x3d, 0x43, 0xc9, 0x81, 0xa6, 0x9a, 0xd9, 0x45, 0xb7, 0x1d, 0xf5, 0x18, 0x35, 0x5c, 0x0e, 0xbc, 0x69, 0x0d, 0x5a, 0x63, 0x6a, 0x88, 0x24, 0xb2, 0x83, 0x1f, 0x7c, 0xb4, 0x4d, 0xc9, 0xd9, 0x52, 0x98, 0xbf, 0x64, 0x2d, 0x6c, 0xe9, 0x03, 0x39, 0xb9, 0x26, 0x2f, 0x61, 0x62, 0xed, 0x21, 0xf5, 0x8d, 0xce, 0x86, 0xa5, 0x68, 0xe3, 0x79, 0x4c, 0x8e, 0x2e, 0xb7, 0xee, 0x47, 0xe6, 0xb5, 0x3f, 0xff, 0x51, 0x0f, 0x93, 0x10, 0xd9, 0x0d, 0xd8, 0x16, 0x01, 0xb1, 0x65, 0x6a, 0x3f, 0x63, 0x7a, 0x43, 0x4b, 0x8e, 0x1b, 0x3a, 0xee, 0x99, 0x1f, 0x76, 0xe1, 0xf6, 0x05, 0xff, 0x29, 0xf0, 0x94, 0x0b, 0x77, 0xc6, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x58, 0x2a, 0xb0, 0xc3, 0x77, 0x15, 0xd0, 0x4c, 0xad, 0x77, 0xba, 0x27, 0x93, 0x3f, 0x6d, 0x5e, 0x55, 0x75, 0x54, 0x80, 0x0d, 0x1b, 0xba, 0x36, 0x05, 0xea, 0xe2, 0xca, 0xcb, 0xc8, 0xfd, 0xae, 0x63, 0x21, 0xc6, 0xc9, 0x82, 0x33, 0x86, 0xd2, 0x0e, 0x59, 0xa4, 0xf9, 0xc6, 0x89, 0xfa, 0xd2, 0x68, 0x50, 0x72, 0xee, 0x6e, 0xc5, 0xdb, 0x28, 0xf9, 0x98, 0x8d, 0xdd, 0x19, 0x58, 0x92, 0xa2, 0x82, 0x6a, 0xcd, 0x22, 0x7f, 0xc8, 0x39, 0x19, 0x43, 0x3c, 0x65, 0x09, 0xe8, 0x61, 0x95, 0xc8, 0x66, 0x33, 0x0b, 0xf7, 0x1e, 0x92, 0x43, 0xee, 0x1a, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x5d, 0xfc, 0x8d, 0xcc, 0x31, 0x59, 0x61, 0x35, 0xf5, 0xd4, 0xcb, 0x69, 0xca, 0x76, 0x4f, 0x43, 0x12, 0xb0, 0xf5, 0x3c, 0xd7, 0x74, 0x3f, 0xef, 0x25, 0xd5, 0x66, 0xa2, 0xd5, 0x87, 0x45, 0x2f, 0x90, 0x3b, 0x55, 0x86, 0x64, 0xc3, 0x57, 0xba, 0x23, 0xaf, 0x96, 0x69, 0xa8, 0x66, 0xf2, 0x35, 0x58, 0xbd, 0xe5, 0x3a, 0xef, 0x07, 0x20, 0x06, 0xf1, 0x83, 0xc9, 0xdc, 0x34, 0xb0, 0xd6, 0x6c, 0x23, 0x1f, 0xb2, 0x5b, 0x49, 0xa7, 0xdf, 0x85, 0x3b, 0x04, 0x27, 0x61, 0x8d, 0x83, 0xa6, 0xce, 0x31, 0xd4, 0x08, 0x40, 0xaf, 0x7c, 0x0b, 0xcf, 0x9b, 0xfc, 0xe7, 0x54, 0x6d, 0x7a, 0x17, 0x04, 0xe8, 0xcb, 0xd7, 0x14, 0x45, 0x31, 0xcd, 0x79, 0xd5, 0x68, 0x33, 0xdf, 0x6a, 0x15, 0x1d, 0x8d, 0x0f, 0xb6, 0x9e, 0xf2, 0xde, 0x8a, 0x16, 0x4c, 0x79, 0xd1, 0x03, 0x74, 0xb6, 0x09, 0xf0, 0x1e, 0x22, 0xd6, 0x62, 0x79, 0x58, 0x35, 0x60, 0xfc, 0x2f, 0xec, 0xb8, 0xf4, 0xf2, 0x30, 0x87, 0x6f, 0xc6, 0x53, 0x21, 0xf1, 0x8f, 0x27, 0x57, 0x93, 0xe2, 0xb0, 0x89, 0x95, 0x26, 0xd3, 0x03, 0xfe, 0xfa, 0x81, 0x0e, 0x78, 0xeb, 0x96, 0xca, 0x6e, 0x02, 0x31, 0x07, 0xcb, 0x1c, 0x7e, 0x82, 0xe2, 0xbb, 0x89, 0xce, 0xaa, 0xf3, 0xfd, 0x7d, 0x19, 0x7c, 0x6c, 0x21, 0xb6, 0x6c, 0xba, 0xa0, 0xf7, 0x7e, 0xb0, 0xb3, 0x87, 0xfd, 0xe9, 0x69, 0x59, 0x8a, 0xdf, 0x50, 0x0e, 0x38, 0x28, 0x46, 0x21, 0x7f, 0x98, 0x3e, 0x94, 0x63, 0x24, 0xcb, 0xbb, 0xad, 0x62, 0xaa, 0x24, 0x4b, 0x62, 0x7d, 0xa7, 0x3a, 0x1b, 0xfe, 0xa4, 0xa4, 0xbb, 0x0a, 0x88, 0x7d, 0x7d, 0x49, 0xe0, 0x3f, 0xce, 0x91, 0xf0, 0x73, 0xc0, 0x56, 0x31, 0xee, 0x6c, 0xec, 0x66, 0x2e, 0x27, 0xa3, 0x32, 0xc8, 0xbb, 0xd9, 0x19, 0x66, 0x48, 0x32, 0x66, 0xd3, 0x33, 0xc1, 0x59, 0xf1, 0xeb, 0xab, 0x73, 0x42, 0x81, 0xb5, 0x57, 0x48, 0x93, 0xda, 0x74, 0x58, 0x4a, 0x39, 0xbd, 0x98, 0x4a, 0x88, 0xf8, 0xdc, 0x87, 0x9d, 0xa2, 0xb0, 0xde, 0xc7, 0x36, 0xbb, 0xb0, 0x8d, 0x37, 0xe0, 0x84, 0x34, 0x28, 0xca, 0x35, 0xc9, 0xeb, 0x00, 0x3f, 0xd1, 0x1f, 0x84, 0x7e, 0xf3, 0x58, 0x5f, 0x7c, 0x6d, 0x66, 0x7d, 0xdc, 0x8e, 0x9c, 0xa8, 0xed, 0x1f, 0x81, 0x62, 0x0f, 0x92, 0xae, 0x75, 0x60, 0x6b, 0x75, 0x5c, 0x48, 0x19, 0x90, 0xea, 0x45, 0x63, 0x5a, 0x2f, 0x79, 0x78, 0x06, 0x20, 0x8f, 0xc4, 0xc2, 0x29, 0xa6, 0x03, 0xc9, 0xd9, 0x05, 0x06, 0xbe, 0xf4, 0x98, 0x28, 0x48, 0x8f, 0xa6, 0xb9, 0xee, 0xd1, 0x6d, 0x41, 0x3e, 0x35, 0x63, 0x87, 0x49, 0x5b, 0x35, 0x51, 0x27, 0x3e, 0x1d, 0x8c, 0x5b, 0x25, 0xeb, 0x98, 0x47, 0x0f, 0x5b, 0xed, 0x76, 0x36, 0x5f, 0x03, 0xc8, 0xdd, 0x6f, 0x2f, 0x8c, 0x4f, 0xf4, 0xe7, 0xda, 0xaa, 0xbc, 0x47, 0xdc, 0x76, 0xca, 0xe0, 0x67, 0xcc, 0x3d, 0xb8, 0xcf, 0xe3, 0x0b, 0x81, 0x64, 0xbf, 0x89, 0x60, 0x54, 0x32, 0xcd, 0x90, 0x5c, 0xc9, 0x5d, 0xc0, 0xdb, 0xdf, 0x4d, 0x3f, 0xce, 0x96, 0x87, 0xe5, 0x29, 0x7d, 0xbd, 0xde, 0x9e, 0xda, 0xd3, 0xfa, 0x8c, 0xd1, 0xa0, 0x2b, 0x6b, 0x84, 0x66, 0xb8, 0x65, 0x31, 0xcf, 0x56, 0xaf, 0x79, 0x43, 0x48, 0x70, 0x5f, 0xaa, 0xb1, 0xd9, 0xe9, 0x40, 0xc0, 0xfd, 0xc6, 0xa2, 0xf5, 0xf5, 0x6f, 0x9d, 0xa6, 0x0a, 0x22, 0x84, 0xa8, 0x28, 0x59, 0xdf, 0x1d, 0xff, 0xbf, 0x05, 0xe9, 0x5f, 0x3f, 0x65, 0x31, 0x2c, 0x2a, 0x02, 0x14, 0x92, 0xcf, 0xab, 0x70, 0xc0, 0x5d, 0x60, 0xe3, 0x29, 0x34, 0x7a, 0x8a, 0x73, 0xdd, 0xa1, 0x85, 0x31, 0x49, 0x35, 0x90, 0xba, 0xf3, 0x2e, 0x0f, 0xf9, 0x1f, 0xbb, 0xa9, 0x75, 0x0c, 0xc8, 0xf4, 0xe9, 0x1f, 0x2e, 0x79, 0x7f, 0xc3, 0x84, 0x0e, 0x71, 0xb0, 0x82, 0x1b, 0x59, 0x75, 0xae, 0xe8, 0xe1, 0xf9, 0x86, 0x31, 0xa8, 0x6e, 0x05, 0xa8, 0x35, 0x76, 0x68, 0x22, 0x45, 0xd7, 0x55, 0x5b, 0x70, 0x36, 0xb2, 0x46, 0x36, 0x7d, 0xcc, 0x3b, 0xe7, 0x9e, 0x43, 0x2c, 0xd2, 0xb0, 0x2b, 0x3e, 0x17, 0xe6, 0xf7, 0xd9, 0xaf, 0x16, 0xba, 0x5c, 0x86, 0x20, 0x22, 0x1d, 0xee, 0xa1, 0x32, 0x7c, 0xc1, 0xa2, 0x9a, 0xc2, 0xde, 0xe1, 0xd9, 0x8d, 0x3d, 0x9a, 0xbe, 0xf4, 0x46, 0x83, 0x07, 0x91, 0x01, 0xa4, 0x89, 0xa3, 0x1e, 0x1f, 0x6e, 0xbe, 0x9c, 0x54, 0x41, 0x0a, 0x5a, 0x74, 0x1b, 0xfb, 0x0c, 0xd7, 0xb6, 0x43, 0x6d, 0x8b, 0x61, 0x5e, 0x86, 0x34, 0x87, 0xb1, 0x9e, 0xce, 0xa4, 0xa2, 0x06, 0x3f, 0x69, 0xfe, 0xfd, 0xb1, 0x20, 0xfe, 0x4b, 0x23, 0x8d, 0x99, 0x26, 0x14, 0xb8, 0x4c, 0x24, 0xcf, 0xd7, 0xf8, 0x10, 0x94, 0xb5, 0x29, 0xe6, 0x7b, 0xb6, 0xdf, 0xc1, 0xb6, 0x71, 0xd2, 0x55, 0x1e, 0x1c, 0x26, 0x3a, 0x63, 0x02, 0x20, 0x89, 0xf3, 0xd4, 0xe1, 0x89, 0xc5, 0x1c, 0xdc, 0x2f, 0xf2, 0x54, 0x10, 0xc7, 0xa2, 0x25, 0x49, 0x7d, 0x1f, 0x67, 0x21, 0x53, 0xc8, 0x30, 0xdc, 0xf2, 0x2d, 0x55, 0x5d, 0xf0, 0x8e, 0x56, 0x18, 0xc0, 0xf4, 0xd1, 0x17, 0xd2, 0x9e, 0x94, 0x71, 0x6b, 0x5e, 0xa4, 0xa4, 0xf7, 0xb6, 0xe1, 0x04, 0xf3, 0xfe, 0xcf, 0x39, 0xa1, 0x71, 0xe6, 0xb6, 0xa9, 0x54, 0x73, 0xb2, 0xf0, 0xfb, 0x4f, 0x08, 0x85, 0x99, 0xb7, 0x74, 0xe5, 0x5e, 0x0c, 0x41, 0xc3, 0x87, 0x3d, 0x33, 0x51, 0x51, 0x81, 0xe9, 0xce, 0x26, 0x4f, 0x9c, 0x0c, 0xc3, 0xc3, 0xef, 0xea, 0xd2, 0x45, 0x3c, 0xbf, 0xd1, 0x79, 0xb0, 0x7a, 0x95, 0x80, 0xae, 0x79, 0x56, 0xca, 0xe0, 0x1d, 0x02, 0xf9, 0xb3, 0xab, 0x37, 0x84, 0x52, 0x09, 0x1e, 0x7d, 0x3b, 0xf7, 0x89, 0x97, 0x4e, 0xc0, 0x7b, 0xb0, 0x75, 0xae, 0xef, 0x47, 0x9a, 0xbd, 0x21, 0x13, 0xfc, 0x12, 0x5f, 0x66, 0xd6, 0xad, 0xab, 0xe8, 0x38, 0xdf, 0x8b, 0xf3, 0xbe, 0xee, 0x53, 0xb0, 0x07, 0x89, 0xa6, 0xed, 0x18, 0x6d, 0x5d, 0xa8, 0x15, 0xfd, 0x8e, 0x8f, 0x28, 0x29, 0x89, 0xec, 0x99, 0xf6, 0x17, 0x39, 0xbe, 0xdb, 0x67, 0xb3, 0x4b, 0x87, 0x1a, 0x70, 0x5d, 0xf1, 0xdc, 0xae, 0x3f, 0x38, 0xaa, 0x44, 0xae, 0xcb, 0x6d, 0xdc, 0x13, 0x16, 0xbc, 0x84, 0x30, 0xb0, 0xeb, 0xe1, 0xa6, 0x74, 0x7d, 0x60, 0x55, 0x26, 0x29, 0x60, 0xce, 0xda, 0x4a, 0xb2, 0x82, 0xb0, 0x46, 0xd9, 0xae, 0xea, 0x7b, 0x30, 0x79, 0x29, 0xe3, 0x79, 0x08, 0x06, 0xbd, 0x2a, 0x20, 0x34, 0xba, 0xf4, 0xe7, 0x3e, 0x4c, 0x40, 0xb2, 0x6e, 0x49, 0xeb, 0x6e, 0x75, 0x03, 0xec, 0xe8, 0xb6, 0x1f, 0xd1, 0xd4, 0x4f, 0x07, 0x72, 0x05, 0x16, 0x8d, 0x3e, 0xc7, 0xa6, 0xe3, 0x88, 0x3b, 0xb4, 0xdf, 0xcb, 0x0d, 0x4e, 0xd2, 0xf4, 0xad, 0x31, 0x2d, 0xca, 0x33, 0x03, 0x8a, 0x2b, 0xc9, 0x2b, 0xdf, 0x3d, 0xe2, 0xf3, 0xfd, 0xfc, 0xe9, 0x09, 0x07, 0xbd, 0xee, 0x03, 0xf6, 0xbf, 0xbd, 0x93, 0x2b, 0x6a, 0x38, 0xa8, 0xc3, 0x20, 0x8e, 0x8f, 0x6b, 0xef, 0x29, 0x9b, 0x92, 0xee, 0x88, 0x99, 0x8c, 0xf2, 0x28, 0xba, 0xe4, 0x53, 0x4b, 0x2a, 0x5b, 0x3e, 0x0b, 0x96, 0x6c, 0xa7, 0x16, 0x86, 0x95, 0xf7, 0x0e, 0xed, 0xff, 0xc5, 0xe6, 0xbb, 0x6f, 0xa0, 0x95, 0xf7, 0xf4, 0xd1, 0xc6, 0xb6, 0xed, 0x0e, 0xc1, 0xbb, 0xf5, 0x2d, 0xcf, 0x04, 0x30, 0x19, 0xf9, 0x53, 0x38, 0x68, 0x5e, 0x07, 0xe1, 0xd3, 0x9b, 0xd0, 0x28, 0x41, 0xdb, 0x99, 0x74, 0x59, 0xc2, 0xc0, 0x92, 0xcb, 0xf1, 0xcc, 0xc3, 0xdd, 0x9c, 0x87, 0x20, 0x41, 0x8c, 0xcf, 0x2e, 0xfe, 0x95, 0x4b, 0xb1, 0x84, 0xbb, 0xed, 0x37, 0xa4, 0x51, 0xb3, 0xd9, 0xa5, 0x1f, 0xe8, 0xdc, 0x97, 0x7a, 0x8d, 0xa7, 0x86, 0xa4, 0x25, 0x05, 0xa4, 0x5e, 0x2a, 0xe7, 0x6a, 0xf2, 0x93, 0x97, 0xca, 0xb4, 0x72, 0xaf, 0xd4, 0x52, 0x66, 0x12, 0x90, 0x34, 0xa4, 0x20, 0x6f, 0x20, 0x56, 0x4c, 0xb2, 0x20, 0x39, 0x1c, 0x05, 0x30, 0x98, 0x76, 0x7a, 0x8d, 0x8f, 0x42, 0x48, 0x00, 0x81, 0xa7, 0xb7, 0x8c, 0xfd, 0xb1, 0xc1, 0xab, 0x47, 0x16, 0x37, 0x66, 0xbe, 0xf6, 0x02, 0x91, 0x4c, 0x75, 0x6a, 0x60, 0x2e, 0x5e, 0xfd, 0x5d, 0x00, 0x5a, 0x90, 0x37, 0x0f, 0xe2, 0x30, 0xf1, 0x61, 0xa2, 0xee, 0x35, 0x7a, 0x04, 0xcc, 0xae, 0x08, 0x11, 0xa5, 0xda, 0xee, 0x47, 0xff, 0x73, 0x6f, 0x7d, 0x4e, 0x2d, 0x6f, 0x57, 0x60, 0xd1, 0xf6, 0x53, 0xeb, 0xa7, 0x8b, 0x22, 0x41, 0x90, 0xc4, 0x9f, 0xd6, 0x0e, 0xa9, 0x24, 0x01, 0xdb, 0xe0, 0xbd, 0x9f, 0xe1, 0xf8, 0x5a, 0xf3, 0x84, 0xc0, 0xdf, 0xd9, 0xc8, 0x17, 0x43, 0x04, 0x3e, 0x2e, 0xf4, 0xba, 0x06, 0x76, 0xbe, 0x66, 0x48, 0x3f, 0xfe, 0x10, 0xf5, 0x0f, 0xbe, 0xad, 0x01, 0x4a, 0xb7, 0x43, 0xef, 0xf6, 0xb4, 0x1d, 0xed, 0x0a, 0x99, 0xd5, 0x4b, 0x97, 0xca, 0x69, 0xf3, 0xee, 0xaa, 0xff, 0x8e, 0x82, 0xb7, 0x89, 0x83, 0xfb, 0x2b, 0x39, 0xbb, 0x56, 0x15, 0xfc, 0x82, 0x32, 0x90, 0xcb, 0x88, 0x4c, 0x23, 0x4c, 0x3a, 0x87, 0x36, 0xe8, 0xf0, 0x79, 0x12, 0xf3, 0xb4, 0x35, 0x93, 0xaa, 0xac, 0xd1, 0x83, 0x19, 0x55, 0x0b, 0x9d, 0xaf, 0x50, 0x71, 0xb0, 0x48, 0x53, 0xa4, 0xb6, 0x4c, 0x58, 0x4a, 0x6a, 0xad, 0x0a, 0xa8, 0x8a, 0x33, 0x09, 0x09, 0x8e, 0xf0, 0x6f, 0x95, 0x83, 0xc2, 0xb1, 0x87, 0xd6, 0xa0, 0xf7, 0x9a, 0x0c, 0x68, 0x0a, 0x70, 0x36, 0xf2, 0x77, 0x8e, 0x06, 0x09, 0x1d, 0x44, 0x36, 0xaf, 0xb2, 0xb9, 0x05, 0x65, 0xf0, 0x7b, 0xb4, 0x30, 0x12, 0x14, 0xe9, 0x92, 0x69, 0x85, 0x11, 0xac, 0xd5, 0x50, 0xc4, 0x8e, 0x39, 0xf1, 0x38, 0x84, 0x55, 0x17, 0x88, 0xda, 0x52, 0x91, 0x80, 0xd5, 0xb8, 0x64, 0x23, 0x0e, 0x12, 0x4e, 0x59, 0xbb, 0x12, 0x90, 0xbd, 0xca, 0xd7, 0x93, 0xd1, 0x03, 0x73, 0xe5, 0x3f, 0x84, 0xb3, 0xc7, 0x51, 0x1b, 0x96, 0xcb, 0x87, 0x23, 0x01, 0x54, 0x20, 0x0b, 0xd5, 0x1e, 0xfc, 0x96, 0x36, 0xc4, 0x4e, 0x85, 0xbd, 0xb4, 0x8f, 0x07, 0x6c, 0x7e, 0x75, 0xb9, 0x54, 0x86, 0x0b, 0x07, 0x80, 0xe2, 0x5f, 0xe9, 0xfe, 0x5d, 0x71, 0xcd, 0xc2, 0x7b, 0xdd, 0x05, 0x73, 0xbf, 0x5b, 0x86, 0x2f, 0xe1, 0x0e, 0x12, 0x7d, 0x29, 0x4b, 0x2f, 0x30, 0xdd, 0xb0, 0xf9, 0xed, 0xfb, 0x97, 0x96, 0x9e, 0x7a, 0x1e, 0x73, 0x11, 0x9a, 0xa7, 0xe3, 0x33, 0xc4, 0x2e, 0xc7, 0x5f, 0x8a, 0x69, 0xe3, 0xfc, 0x85, 0xf0, 0x97, 0x45, 0xfd, 0x7f, 0x25, 0x1f, 0xa6, 0xd3, 0xf6, 0x12, 0x0d, 0x2e, 0xd1, 0x73, 0xdb, 0xc9, 0x48, 0x07, 0xa8, 0xbe, 0xfa, 0x5e, 0x95, 0x9c, 0xcf, 0x08, 0x28, 0xad, 0x16, 0x4c, 0x92, 0x58, 0xd0, 0x71, 0xa9, 0xb4, 0x84, 0x83, 0xa3, 0x3b, 0xa6, 0x2b, 0x2f, 0x08, 0x18, 0xe8, 0x5d, 0x7f, 0x21, 0xfd, 0x5d, 0x53, 0xe7, 0x2e, 0x6b, 0xed, 0x83, 0x2f, 0xab, 0x56, 0x88, 0xd5, 0x5c, 0x5d, 0xe3, 0x9f, 0xdd, 0xe2, 0x21, 0xd4, 0xda, 0x96, 0x7e, 0x56, 0x3d, 0xa3, 0xaf, 0x93, 0xb2, 0xbb, 0x9b, 0xb4, 0x2e, 0xe8, 0xe6, 0x73, 0x0b, 0x9b, 0xa8, 0xbe, 0x3b, 0x34, 0x7d, 0x2e, 0x20, 0x8b, 0x07, 0xfc, 0xfa, 0x5f, 0x78, 0x49, 0x66, 0xdc, 0xb6, 0x14, 0x25, 0xb2, 0x8e, 0x92, 0xb0, 0x9a, 0x40, 0xff, 0x1b, 0x66, 0xd2, 0xde, 0x32, 0x68, 0x79, 0xdf, 0x2f, 0x01, 0x57, 0x6e, 0x48, 0x6b, 0xc0, 0x3e, 0x49, 0x5f, 0x2e, 0xe3, 0x7f, 0xdc, 0xcf, 0x24, 0xcd, 0x7a, 0x01, 0x3d, 0x84, 0xf8, 0x5c, 0x70, 0x54, 0xfc, 0xe3, 0xdf, 0x4d, 0x56, 0x6a, 0xe3, 0xee, 0x20, 0xfa, 0x9b, 0xe2, 0x41, 0x62, 0x9c, 0x7d, 0x94, 0x03, 0xc7, 0x48, 0xf7, 0x02, 0x00, 0xa2, 0x78, 0xaa, 0x43, 0xa3, 0xdb, 0xf5, 0xf9, 0x13, 0x3b, 0x2b, 0x31, 0xbb, 0x65, 0xaf, 0xaf, 0x59, 0x09, 0x4e, 0x66, 0x43, 0x5d, 0xd6, 0xc5, 0xe4, 0x97, 0x89, 0x00, 0x10, 0xe4, 0x9d, 0x73, 0x0c, 0x9d, 0x23, 0x32, 0xf3, 0x0b, 0x77, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x58, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x8f, 0x30, 0xcd, 0xaf, 0x26, 0x40, 0x2c, 0xf9, 0x92, 0xb8, 0x31, 0x61, 0x6a, 0x06, 0x23, 0x24, 0xde, 0xf7, 0xf9, 0x2a, 0x7c, 0xa5, 0xc3, 0x99, 0x4d, 0x6f, 0x15, 0xcc, 0xdc, 0x17, 0xb3, 0x74, 0x5b, 0xd4, 0xa2, 0x68, 0xab, 0x5d, 0x2c, 0x2b, 0xc5, 0xc1, 0x3e, 0xc5, 0x6a, 0x17, 0xbd, 0xb0, 0x57, 0x61, 0x5a, 0x48, 0xe3, 0x48, 0x99, 0x93, 0xde, 0xb0, 0x5e, 0x70, 0x9f, 0xcd, 0xd0, 0xf2, 0x07, 0xd5, 0xed, 0xe9, 0x4c, 0x3a, 0x08, 0xe6, 0x8e, 0xec, 0x78, 0x30, 0x00, 0x00, 0x00, 0x81, 0x9f, 0x5d, 0x92, 0x15, 0xe7, 0x37, 0x10, 0x4c, 0x42, 0xec, 0x9a, 0x12, 0x0a, 0x79, 0x55, 0x54, 0x02, 0x01, 0xb1, 0xc1, 0x08, 0x02, 0x31, 0x10, 0xd0, 0x03, 0x32, 0xae, 0x6a, 0x84, 0x3f, 0xcf, 0x82, 0x53, 0x9d, 0xf0, 0x9a, 0xce, 0x68, 0x6f, 0x07, 0xeb, 0x47, 0x19, 0xa4, 0x62, 0x79, 0x4c, 0x83, 0xcd, 0xfa, 0x1c, 0x78, 0x4e, 0xad, 0x5a, 0x6e, 0xc9, 0x63, 0x1f, 0x6d, 0x56, 0x33, 0x11, 0xd3, 0xfb, 0x35, 0x29, 0xc1, 0x8e, 0x39, 0xf7, 0xaa, 0x54, 0x44, 0x05, 0x4e, 0x81, 0x96, 0x77, 0xc4, 0x22, 0xbd, 0x85, 0xaf, 0x4e, 0xf5, 0x1e, 0xfb, 0x09, 0x83, 0x8c, 0x8f, 0x1c, 0x51, 0x89, 0xc5, 0x9a, 0xaa, 0xa0, 0xc0, 0x81, 0xac, 0x35, 0xe0, 0x12, 0x88, 0xb0, 0xdd, 0x79, 0xa4, 0x13, 0x54, 0x83, 0xde, 0x57, 0x98, 0x0d, 0xcc, 0x4c, 0xf2, 0x14, 0x17, 0xcc, 0x5e, 0x31, 0x53, 0x7d, 0x01, 0xa1, 0x09, 0xe7, 0x0a, 0x73, 0xbc, 0x1f, 0xd8, 0x50, 0xfa, 0x4e, 0x9b, 0x3d, 0x46, 0x7a, 0x11, 0xf2, 0xfb, 0x16, 0xac, 0x4b, 0x61, 0x7a, 0xae, 0xbf, 0xd6, 0x32, 0x10, 0x66, 0xe6, 0xcb, 0xf5, 0x3b, 0x56, 0xa5, 0xed, 0xfa, 0x48, 0x48, 0xbf, 0x39, 0xf9, 0xba, 0xc3, 0x9b, 0xcb, 0xbf, 0x1d, 0x49, 0xd9, 0xb4, 0x5a, 0x70, 0xa0, 0x2c, 0x7b, 0x21, 0x8c, 0x84, 0xda, 0x44, 0x8a, 0xa8, 0x85, 0xdb, 0x06, 0x89, 0x43, 0xc1, 0x81, 0x2c, 0xd9, 0xad, 0xa6, 0xc3, 0xa1, 0x24, 0x90, 0x31, 0x15, 0xab, 0xc6, 0x58, 0xd9, 0x60, 0xac, 0xcf, 0x2e, 0x5a, 0xa1, 0x61, 0x14, 0x71, 0x72, 0xc0, 0x14, 0x0a, 0xb8, 0x9e, 0xc3, 0x93, 0xe2, 0xb2, 0x74, 0x54, 0x55, 0x72, 0x47, 0x21, 0xfb, 0xb5, 0x76, 0xde, 0x08, 0xfc, 0x57, 0xda, 0x3f, 0x56, 0x44, 0xbe, 0x69, 0x1d, 0x49, 0xad, 0xb9, 0xe0, 0x30, 0x68, 0xfd, 0xd4, 0xeb, 0xde, 0x96, 0x66, 0x13, 0xfa, 0x20, 0x7c, 0x82, 0xcb, 0xf6, 0x62, 0x70, 0xac, 0x62, 0x1a, 0xae, 0x1e, 0x50, 0xee, 0x78, 0x61, 0x79, 0x7f, 0xe7, 0xe2, 0xdd, 0x98, 0x56, 0x43, 0xfb, 0x1c, 0x37, 0x50, 0x31, 0xab, 0x0a, 0xd0, 0x99, 0x22, 0x5e, 0x85, 0xae, 0x64, 0xde, 0xdc, 0xb5, 0xae, 0x35, 0x20, 0x00, 0xfb, 0x0b, 0xb4, 0x81, 0xc4, 0xb7, 0x17, 0x04, 0xc0, 0x6e, 0xa0, 0x04, 0x74, 0x4f, 0xef, 0x9c, 0xa0, 0xb6, 0x77, 0xa7, 0x7d, 0x80, 0x0b, 0x46, 0x2b, 0xa9, 0x1f, 0x7c, 0xb1, 0xd6, 0x92, 0xc1, 0x53, 0x64, 0x7c, 0x7e, 0x44, 0xbb, 0x66, 0xee, 0xf0, 0x19, 0x1c, 0xc4, 0xff, 0xae, 0xde, 0x4f, 0xc7, 0x80, 0xe2, 0xb1, 0x54, 0x58, 0xf2, 0x7d, 0xe3, 0x0c, 0xd2, 0x8c, 0x38, 0x70, 0x2f, 0x88, 0xf5, 0x3e, 0x10, 0x91, 0xa5, 0x5b, 0x10, 0x6b, 0xad, 0xf1, 0x1b, 0x23, 0x36, 0xb9, 0x47, 0x57, 0xf7, 0x4c, 0x41, 0x14, 0xd4, 0x48, 0xb1, 0x2c, 0x6a, 0x6a, 0xcd, 0xc4, 0x90, 0x8c, 0x2e, 0xc5, 0xae, 0xaf, 0x5f, 0x96, 0xdd, 0x82, 0x07, 0x04, 0x47, 0x17, 0x98, 0x5c, 0xb1, 0x20, 0xcb, 0xae, 0xec, 0x57, 0x70, 0x9a, 0xbc, 0xd2, 0xd5, 0x42, 0x3a, 0x17, 0x05, 0xe8, 0xae, 0x98, 0x94, 0xe1, 0xc3, 0x70, 0xa0, 0x68, 0x1d, 0xf6, 0x3b, 0x08, 0xc7, 0xb3, 0xfc, 0xcb, 0x7e, 0xd2, 0xf6, 0xbc, 0x44, 0xe0, 0x05, 0x54, 0xfb, 0xd8, 0xfd, 0x25, 0x6d, 0xe6, 0x99, 0xca, 0x89, 0x58, 0x55, 0x53, 0xcb, 0x66, 0xc3, 0x70, 0x11, 0x09, 0xb6, 0xd7, 0x00, 0xb5, 0x74, 0xa7, 0x05, 0x74, 0xf0, 0xdc, 0x53, 0x17, 0x9b, 0x13, 0xe1, 0x25, 0xda, 0xc5, 0x16, 0x00, 0x9b, 0xc5, 0x06, 0x8b, 0xa2, 0x77, 0x22, 0x3d, 0xa4, 0x53, 0xff, 0x01, 0xfe, 0x6d, 0x61, 0x9b, 0x89, 0x5e, 0x26, 0xe0, 0xa7, 0xee, 0xb9, 0xbb, 0x4a, 0x29, 0x5b, 0x0e, 0xb9, 0x16, 0x94, 0xa8, 0x66, 0x80, 0xfd, 0xa1, 0x76, 0x02, 0x0b, 0x9c, 0x2f, 0x7e, 0x6a, 0x8b, 0xcb, 0xf3, 0x3b, 0xf8, 0x85, 0xc8, 0xef, 0x8b, 0xc2, 0xfd, 0xbd, 0xf0, 0x00, 0xd8, 0xe8, 0xed, 0xc7, 0xf5, 0x67, 0xa3, 0x64, 0xb5, 0x13, 0x87, 0xec, 0x11, 0x03, 0x47, 0xfb, 0x08, 0x99, 0x0b, 0x1f, 0x3e, 0x00, 0xb9, 0x9e, 0x41, 0x58, 0xd8, 0x0a, 0xa9, 0x20, 0x9a, 0xe8, 0xf2, 0x15, 0x39, 0xd1, 0xf9, 0xee, 0xe4, 0x8d, 0x18, 0xda, 0x27, 0x83, 0xe0, 0xea, 0xd0, 0xe2, 0x02, 0xda, 0xe3, 0xe2, 0xeb, 0x88, 0x87, 0xce, 0xc2, 0x85, 0x0f, 0xb5, 0x7b, 0x81, 0xba, 0x64, 0x26, 0x3a, 0xea, 0x57, 0x3e, 0xd5, 0xcc, 0xac, 0x5c, 0xf2, 0x36, 0x6c, 0x37, 0xcb, 0xe0, 0x5c, 0xec, 0xb1, 0x06, 0xe6, 0xfa, 0x0a, 0xe4, 0x43, 0x6c, 0xdf, 0xec, 0xe2, 0xfa, 0x6e, 0xd5, 0x13, 0xea, 0x6f, 0x14, 0x23, 0x2a, 0x0b, 0x56, 0x7c, 0x1e, 0xb8, 0x73, 0x18, 0xba, 0x47, 0x0c, 0x7f, 0xcd, 0xf1, 0x3d, 0xff, 0xb7, 0x96, 0x30, 0x12, 0x7a, 0xf0, 0x73, 0xa2, 0x52, 0xfc, 0x3b, 0xd1, 0x3b, 0xd2, 0xbf, 0x65, 0xfc, 0xc0, 0xdc, 0x51, 0x75, 0x69, 0xf9, 0xcf, 0x8b, 0x2f, 0xf3, 0x56, 0x1f, 0x18, 0x96, 0xdd, 0xa9, 0x36, 0x5e, 0x73, 0x3e, 0x52, 0xef, 0x58, 0x49, 0x65, 0x71, 0x88, 0xaf, 0xe9, 0x78, 0xc9, 0x45, 0xab, 0xb6, 0x50, 0xf2, 0xe9, 0xa5, 0x3e, 0xf1, 0xe6, 0x59, 0x0b, 0x4e, 0x7d, 0x76, 0x01, 0xfd, 0x0b, 0x20, 0x8f, 0xdc, 0x0d, 0xd5, 0xb6, 0xa8, 0xd1, 0xf7, 0x4d, 0xf8, 0x32, 0x5e, 0xcd, 0x9e, 0x52, 0x21, 0xc9, 0xa9, 0x70, 0x6f, 0x4e, 0x6d, 0x6b, 0x22, 0x36, 0x20, 0x8a, 0x5d, 0x46, 0x4b, 0x77, 0x36, 0x41, 0xe4, 0xc8, 0xed, 0x80, 0x93, 0x1f, 0x0f, 0xc1, 0x2e, 0x62, 0x80, 0x8e, 0x02, 0xf9, 0xa9, 0xaf, 0xf5, 0x0c, 0x78, 0xc5, 0x17, 0x87, 0xb0, 0x52, 0x0a, 0x9e, 0x58, 0x68, 0x22, 0x1f, 0x87, 0x78, 0xe1, 0x7b, 0x6c, 0x3e, 0x5c, 0x37, 0x28, 0x13, 0x5a, 0x74, 0x7e, 0x73, 0xf1, 0xf7, 0xc8, 0xf1, 0xb9, 0xa1, 0x75, 0x3b, 0x75, 0x81, 0xd2, 0xe3, 0x97, 0x0b, 0x86, 0x0f, 0x27, 0x9f, 0x62, 0x14, 0x6d, 0x84, 0x21, 0xf3, 0x2e, 0x52, 0x4d, 0x44, 0x6f, 0x98, 0x59, 0x14, 0xdc, 0x20, 0xb6, 0xe0, 0x70, 0xb2, 0xd5, 0x9a, 0xde, 0x24, 0xde, 0x90, 0x33, 0xba, 0x20, 0xc0, 0x85, 0x1e, 0x5d, 0x0e, 0xac, 0x14, 0x9e, 0xb9, 0x4d, 0x79, 0xb0, 0x08, 0x45, 0xdf, 0x33, 0x36, 0x38, 0x68, 0xb6, 0x99, 0xf5, 0x70, 0x9c, 0x14, 0xf0, 0x8e, 0xeb, 0x8f, 0xc9, 0xc7, 0x5c, 0x99, 0xd2, 0x9e, 0x16, 0x35, 0x00, 0x94, 0xf1, 0xb3, 0x62, 0x54, 0x1d, 0xbd, 0x2c, 0x21, 0x87, 0x4f, 0xe0, 0xa7, 0x22, 0x25, 0xf2, 0x43, 0x18, 0x0a, 0x5d, 0xf1, 0x4e, 0xf0, 0x48, 0x2d, 0x49, 0x2b, 0x02, 0x7c, 0xb4, 0x69, 0xb8, 0x2d, 0x6f, 0xa4, 0x67, 0x8a, 0x28, 0xc1, 0xaa, 0xe3, 0x8e, 0x41, 0x31, 0xd1, 0xbe, 0x81, 0xb5, 0x6f, 0xa2, 0xfd, 0x0c, 0xee, 0xea, 0x7a, 0x82, 0xd3, 0xb1, 0x4f, 0xc7, 0x35, 0x43, 0x22, 0x3d, 0xfa, 0xb8, 0x21, 0x2e, 0x43, 0xf9, 0x44, 0xcc, 0x7c, 0x3c, 0x01, 0xda, 0x0a, 0xd1, 0xcf, 0x9c, 0xba, 0x8b, 0x14, 0xac, 0x84, 0x98, 0xa6, 0x13, 0x0b, 0x54, 0xd3, 0xcd, 0x9e, 0x4b, 0x20, 0x19, 0x55, 0xae, 0xdc, 0x3b, 0xcd, 0x01, 0xda, 0xdd, 0xb7, 0x20, 0x39, 0x00, 0x16, 0x98, 0x4d, 0xee, 0x06, 0x0d, 0x1b, 0xf1, 0x4f, 0x1f, 0x97, 0x4d, 0x7f, 0x33, 0x05, 0xf6, 0x2b, 0x38, 0x50, 0xe9, 0xbc, 0xe6, 0xa1, 0xb7, 0x40, 0xcf, 0xed, 0x6e, 0x64, 0xce, 0xc7, 0xec, 0x12, 0x0d, 0x01, 0x3e, 0x56, 0x95, 0xd6, 0x32, 0x87, 0xd8, 0x82, 0x5b, 0x83, 0x58, 0xa8, 0xe1, 0xbd, 0x32, 0xea, 0x39, 0x69, 0x57, 0x34, 0x7a, 0x5a, 0xb3, 0xf7, 0x83, 0x39, 0xc7, 0x9a, 0xbc, 0x4c, 0x5d, 0xce, 0x93, 0xcf, 0x38, 0xf8, 0x03, 0x62, 0x78, 0xbf, 0x19, 0x8f, 0x8b, 0xdb, 0x35, 0x7b, 0xdf, 0x93, 0x1d, 0x49, 0x42, 0x6a, 0x6f, 0x87, 0xdf, 0xdf, 0xd3, 0x05, 0xe7, 0xbe, 0xec, 0x47, 0xc0, 0xb6, 0xa9, 0xa5, 0x40, 0x76, 0x53, 0x66, 0x8c, 0x54, 0x44, 0xc5, 0x09, 0xfa, 0x09, 0xa2, 0x4d, 0x78, 0x76, 0x96, 0x47, 0x4e, 0xbe, 0xda, 0xb3, 0xed, 0xc8, 0x7e, 0xb6, 0x69, 0x26, 0xed, 0x4e, 0x18, 0x4e, 0x9f, 0x02, 0x84, 0x6b, 0xb3, 0x73, 0x07, 0x16, 0x2e, 0x3d, 0xd5, 0xe8, 0x0e, 0xe2, 0x32, 0xa3, 0x20, 0x52, 0x26, 0x92, 0xa4, 0xcf, 0x47, 0xf9, 0xbc, 0x11, 0xc1, 0x1a, 0xa8, 0x6e, 0x0a, 0x7a, 0x44, 0x2f, 0xf7, 0xa4, 0xd4, 0x68, 0x24, 0x68, 0x1d, 0xc6, 0x18, 0x37, 0xb6, 0x3b, 0x2c, 0x55, 0x60, 0x6c, 0xd9, 0x29, 0x85, 0xbe, 0x72, 0xe8, 0xae, 0x74, 0xf1, 0xdb, 0x26, 0xc5, 0x27, 0xce, 0xad, 0x40, 0x76, 0x37, 0x70, 0x77, 0x17, 0x6c, 0xa0, 0x95, 0x99, 0x50, 0x25, 0xe1, 0xf7, 0x49, 0x6d, 0x41, 0x9f, 0x47, 0x1b, 0x9b, 0x87, 0x78, 0x2f, 0xc4, 0x69, 0xbb, 0x5f, 0xd0, 0x81, 0xac, 0x79, 0xe0, 0x43, 0xa3, 0x63, 0x9f, 0x74, 0x89, 0x7b, 0x7c, 0x9f, 0x75, 0xed, 0xa9, 0x0d, 0x4d, 0xb6, 0x7e, 0x03, 0x78, 0xa3, 0xa1, 0xe4, 0x63, 0x69, 0x54, 0x83, 0xe2, 0x2c, 0x60, 0xc9, 0xa9, 0x15, 0x90, 0xb4, 0xd6, 0x92, 0x6c, 0x1b, 0x09, 0x1e, 0x66, 0xae, 0xc7, 0xa4, 0x25, 0x25, 0x58, 0x1b, 0x49, 0xbb, 0xf3, 0x10, 0xcf, 0xf7, 0x88, 0x69, 0xc5, 0x5e, 0x78, 0x2e, 0xa6, 0x34, 0xce, 0x66, 0x61, 0xa3, 0x3e, 0xed, 0xea, 0xea, 0x1c, 0x35, 0xf8, 0xfa, 0xf1, 0xb7, 0x3c, 0xd5, 0x08, 0x63, 0x83, 0x54, 0x0c, 0x3b, 0x39, 0x30, 0x47, 0xce, 0x7d, 0x59, 0x5c, 0xf4, 0x98, 0xec, 0x0a, 0x2e, 0x78, 0xcb, 0x25, 0x77, 0x35, 0x97, 0x7a, 0x2b, 0x0a, 0x7b, 0x7d, 0x47, 0x3c, 0xf2, 0x7f, 0x3b, 0x6b, 0x20, 0x74, 0x7c, 0x8e, 0x1d, 0xad, 0x03, 0x57, 0x7f, 0x17, 0x14, 0x78, 0xb3, 0xfe, 0x1d, 0xc6, 0xcd, 0x0e, 0x8c, 0xc6, 0x4e, 0xe0, 0x80, 0x8f, 0x12, 0x88, 0x27, 0x79, 0x32, 0xd3, 0xcc, 0xa7, 0x42, 0x66, 0x7e, 0x30, 0x62, 0x6b, 0x64, 0xf8, 0x49, 0xf4, 0x0a, 0x04, 0x04, 0x2c, 0x70, 0xe7, 0x9b, 0xf0, 0xba, 0xb5, 0xd9, 0x8a, 0x54, 0x61, 0xfd, 0x38, 0x1f, 0x5a, 0x81, 0xc6, 0xca, 0x48, 0x3a, 0x32, 0x3a, 0xa4, 0xf1, 0xcd, 0x06, 0xde, 0x00, 0xa6, 0x69, 0x56, 0xeb, 0x36, 0x6a, 0x79, 0xad, 0x41, 0x59, 0x8f, 0xa2, 0x38, 0xd0, 0x2d, 0x36, 0x69, 0x3d, 0xb1, 0x1a, 0xb9, 0x78, 0x52, 0x49, 0xa0, 0x53, 0x15, 0xae, 0xb7, 0x98, 0xbe, 0x0c, 0xf5, 0x87, 0x3d, 0x2e, 0xfc, 0xf8, 0x76, 0xda, 0x20, 0xca, 0xfb, 0xbb, 0xf4, 0xc2, 0x7d, 0x40, 0xe6, 0x2e, 0x7c, 0x4e, 0x4f, 0xbd, 0x0e, 0xec, 0xe3, 0x3c, 0xf9, 0xc5, 0x20, 0xa0, 0x81, 0x05, 0xec, 0x6b, 0x53, 0xdc, 0x14, 0x58, 0xfb, 0xdc, 0x57, 0xdd, 0xca, 0x1d, 0x47, 0xa3, 0x7c, 0x55, 0x08, 0x2a, 0x67, 0xe2, 0xc9, 0xcb, 0x2b, 0x09, 0xd6, 0x9f, 0x20, 0x2e, 0xc6, 0xe6, 0x92, 0x23, 0x57, 0x37, 0x2b, 0x90, 0x1d, 0xa4, 0x99, 0x3c, 0x1c, 0x3c, 0x3a, 0xa7, 0x0e, 0x8d, 0x43, 0xba, 0x0b, 0xa8, 0x48, 0xfe, 0xed, 0xaf, 0x0c, 0xc9, 0x1c, 0x3a, 0xe6, 0xbe, 0x88, 0x33, 0x9f, 0x2e, 0x52, 0x0e, 0x5d, 0xa9, 0x56, 0xcc, 0xb1, 0xe9, 0x24, 0x57, 0xba, 0x84, 0x32, 0xbb, 0xbb, 0x70, 0xf3, 0x15, 0xc7, 0xb8, 0x9d, 0x65, 0xe4, 0x0e, 0x46, 0x0a, 0xcb, 0xe4, 0xc5, 0xe0, 0xd6, 0x97, 0x31, 0x61, 0xff, 0x7c, 0xd1, 0x58, 0xab, 0xf0, 0x49, 0x79, 0x6d, 0xce, 0x74, 0xdb, 0x1f, 0x61, 0xd6, 0xed, 0xb9, 0xa2, 0x3d, 0x45, 0x38, 0x2a, 0xa3, 0x01, 0xbd, 0x13, 0x9a, 0x89, 0x2d, 0xc0, 0xc6, 0x65, 0x7c, 0xa8, 0x78, 0x21, 0xf9, 0x14, 0xd6, 0xcf, 0x2e, 0xfb, 0xbb, 0x32, 0x8c, 0x54, 0x3a, 0xd2, 0x59, 0xef, 0xfa, 0x86, 0x8b, 0xa9, 0xf5, 0xca, 0x21, 0x43, 0x64, 0x04, 0x1f, 0xe6, 0x34, 0x86, 0x0a, 0x78, 0x7e, 0x5b, 0x77, 0xe2, 0x33, 0x4c, 0x03, 0x28, 0x4a, 0x43, 0x43, 0xed, 0xab, 0x5d, 0x71, 0x27, 0x89, 0x1e, 0x60, 0x0b, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xbd, 0x06, 0xe6, 0x81, 0xc1, 0x54, 0x6f, 0xac, 0xd2, 0xf5, 0x33, 0x2c, 0xba, 0x45, 0x07, 0x6c, 0x61, 0x0b, 0x2e, 0x28, 0x8f, 0x00, 0x2e, 0x36, 0x6f, 0xb2, 0x4f, 0x99, 0x02, 0xde, 0x8a, 0xe7, 0xa3, 0xf3, 0x49, 0xc1, 0x44, 0xab, 0x21, 0x21, 0x35, 0x07, 0x00, 0x05, 0x27, 0x1f, 0xe9, 0x04, 0xfe, 0xe7, 0xbb, 0x2e, 0x02, 0xfa, 0xc3, 0xd2, 0xb1, 0x9f, 0x33, 0x25, 0x56, 0xe2, 0x92, 0x48, 0x31, 0xaf, 0xf7, 0xbc, 0x67, 0x59, 0x3b, 0x2d, 0xb6, 0x46, 0x08, 0xd5, 0x59, 0xc2, 0x98, 0x68, 0xe2, 0xa6, 0xb9, 0x9a, 0x92, 0x53, 0xbc, 0xa8, 0xff, 0x6b, 0x30, 0x00, 0x00, 0x00, 0x77, 0xe3, 0xef, 0x5d, 0x5d, 0x23, 0xc7, 0x48, 0x6b, 0xe5, 0x94, 0xbe, 0x8f, 0x9b, 0xa7, 0x8b, 0xf2, 0xc3, 0x8e, 0x70, 0xde, 0xd2, 0xfb, 0x50, 0x7a, 0x12, 0x4a, 0x40, 0xcc, 0x9b, 0x51, 0x91, 0xef, 0x1c, 0xdc, 0x5a, 0x60, 0xf0, 0xfa, 0xab, 0xac, 0xaf, 0xfb, 0x9a, 0x29, 0xce, 0xa3, 0x6f, 0x23, 0x35, 0x9d, 0x23, 0x51, 0xc2, 0x2f, 0xda, 0x93, 0x03, 0x76, 0x77, 0x28, 0xd7, 0x6f, 0x00, 0xca, 0x1c, 0xf2, 0x59, 0x0a, 0xab, 0x03, 0xe3, 0x3f, 0xac, 0xbf, 0x3c, 0x75, 0x52, 0x00, 0xd0, 0xc4, 0x1f, 0x60, 0xc2, 0x01, 0x52, 0xed, 0xa3, 0x34, 0x87, 0x2e, 0xc1, 0xd5, 0x82, 0xe0, 0xb4, 0x2c, 0xa9, 0x27, 0xab, 0x7f, 0x46, 0x66, 0x21, 0xb9, 0x84, 0x85, 0x0a, 0xfe, 0x7a, 0x71, 0xe8, 0x1c, 0xa1, 0x3f, 0xae, 0xfe, 0x00, 0xf1, 0x09, 0x0b, 0xf0, 0x20, 0x8d, 0xcb, 0x0b, 0x96, 0x91, 0xbd, 0x27, 0xee, 0x7a, 0xee, 0xde, 0x42, 0x5c, 0x69, 0x4b, 0x07, 0xb3, 0xd1, 0xef, 0xec, 0xd6, 0x91, 0x0f, 0x22, 0x70, 0x81, 0x63, 0xd6, 0xfd, 0x7e, 0x29, 0xc6, 0xbe, 0xfe, 0x1a, 0x18, 0xe8, 0x14, 0x91, 0xc5, 0xee, 0x81, 0x63, 0xd8, 0xa9, 0x0a, 0x49, 0xb7, 0x1d, 0x8f, 0x8b, 0x5c, 0xb5, 0xce, 0x46, 0x92, 0x4c, 0xa5, 0xff, 0xc9, 0x54, 0x09, 0xa1, 0xf9, 0x73, 0x23, 0xbc, 0x16, 0x9d, 0xac, 0x28, 0xa5, 0x85, 0x68, 0xea, 0x09, 0xd5, 0x0d, 0xde, 0x11, 0xde, 0xf9, 0x9b, 0x0a, 0x30, 0xf0, 0x60, 0xa3, 0xb1, 0x96, 0x6c, 0x6d, 0x97, 0xa7, 0x0a, 0xb9, 0x3a, 0xba, 0xa1, 0xc6, 0xfa, 0x63, 0xc3, 0x01, 0x2d, 0x2e, 0xd5, 0xc5, 0x29, 0xfb, 0x12, 0x8a, 0x88, 0x5a, 0x92, 0x13, 0xc0, 0x9c, 0xc9, 0xe8, 0xa2, 0x93, 0xac, 0x1d, 0xdd, 0xa6, 0xda, 0xd0, 0xa0, 0xb6, 0xb2, 0xd1, 0xb8, 0xa7, 0x1a, 0x1f, 0x21, 0xef, 0x07, 0xd0, 0xae, 0xeb, 0x54, 0x12, 0x94, 0x80, 0x25, 0x3b, 0x0f, 0xaf, 0x4b, 0x0d, 0x8a, 0xa3, 0x8d, 0xf7, 0x4b, 0x3d, 0x37, 0x8b, 0xcf, 0xf0, 0x6b, 0x84, 0x30, 0x5d, 0x20, 0xd8, 0x9a, 0xf6, 0x73, 0xb6, 0xd3, 0xc4, 0x2a, 0xc2, 0xe9, 0xc0, 0x01, 0xa6, 0x9c, 0xc8, 0x56, 0xa0, 0x0e, 0xc0, 0x1f, 0x29, 0xb2, 0xcb, 0x8c, 0x49, 0xb9, 0xf9, 0xe0, 0x85, 0x47, 0x05, 0x7d, 0xb4, 0xe9, 0xdb, 0x92, 0xbd, 0x24, 0xd5, 0xda, 0x30, 0x4e, 0xd2, 0x75, 0x55, 0x42, 0x62, 0x6a, 0x74, 0xa9, 0xb8, 0xe8, 0xd0, 0xfd, 0x88, 0xa9, 0xde, 0x84, 0x89, 0x3d, 0x9e, 0x5b, 0xc8, 0x08, 0x3c, 0x8e, 0x96, 0x47, 0x4c, 0x2d, 0x91, 0x28, 0x5c, 0xee, 0xf9, 0x74, 0xb4, 0x74, 0xc3, 0xe5, 0xb8, 0xda, 0x35, 0xf9, 0x9c, 0x83, 0x0a, 0x21, 0x70, 0xff, 0xcb, 0x95, 0x0c, 0x5a, 0xfa, 0x96, 0x01, 0x0a, 0x2c, 0x7b, 0x7d, 0x3c, 0x0e, 0xcf, 0x78, 0x81, 0x83, 0x64, 0x8c, 0x55, 0x64, 0xe2, 0xdb, 0x6e, 0x4b, 0xdd, 0xd8, 0x49, 0x23, 0x89, 0x34, 0x97, 0x86, 0x87, 0xe7, 0x36, 0x31, 0x9a, 0xb9, 0xdc, 0x60, 0x80, 0xb2, 0xea, 0x55, 0xbc, 0x82, 0x31, 0xc7, 0xb2, 0x35, 0x46, 0x2a, 0x7a, 0xe7, 0x7f, 0x94, 0xfc, 0x18, 0xef, 0x90, 0x6b, 0xe1, 0x71, 0xf0, 0x79, 0xff, 0xf8, 0xcd, 0x05, 0x2b, 0x16, 0x00, 0x3e, 0x5a, 0x3c, 0x57, 0x7d, 0x67, 0x7c, 0x13, 0x02, 0x3d, 0xed, 0xc6, 0x05, 0x29, 0x90, 0x60, 0x1c, 0x46, 0x40, 0x6f, 0xf0, 0xaa, 0xb5, 0xde, 0x7c, 0x75, 0xeb, 0x51, 0x99, 0xa7, 0xbb, 0x40, 0x85, 0xd4, 0x13, 0x9c, 0x99, 0xb5, 0x03, 0xc6, 0xbc, 0x27, 0x04, 0x65, 0x49, 0xc7, 0x3b, 0xff, 0x21, 0xa0, 0x28, 0xb2, 0x06, 0x66, 0x29, 0xdf, 0x29, 0x00, 0x3c, 0x66, 0x30, 0x8b, 0x98, 0xe5, 0xea, 0x0d, 0xf9, 0xe3, 0xbb, 0x2c, 0x45, 0x06, 0x3b, 0xff, 0x78, 0xfb, 0xf8, 0x67, 0xca, 0xe4, 0x92, 0x29, 0xd7, 0xb2, 0x3c, 0x06, 0xf3, 0x16, 0x40, 0x35, 0x3b, 0xdd, 0xd2, 0xd2, 0xdb, 0xc0, 0x73, 0xd6, 0x3e, 0xd1, 0x0a, 0xb2, 0xa6, 0x82, 0x56, 0x75, 0xf2, 0xcf, 0xa4, 0x97, 0x21, 0xbf, 0xc2, 0xa6, 0x8c, 0xc6, 0xfb, 0x5b, 0xe2, 0xf4, 0xe0, 0x6f, 0x9c, 0x73, 0x36, 0x53, 0x05, 0x09, 0x59, 0x03, 0xba, 0x28, 0xca, 0xe2, 0x68, 0xd6, 0xee, 0x38, 0x14, 0x7a, 0x74, 0xe9, 0xe4, 0x13, 0xcb, 0x82, 0xde, 0x11, 0x1a, 0x97, 0x75, 0x6a, 0xc4, 0xa6, 0x55, 0x27, 0x11, 0xc1, 0x67, 0xfb, 0xe9, 0xbf, 0x0e, 0x45, 0x02, 0x0c, 0xbf, 0xce, 0x7e, 0xde, 0xdc, 0xf7, 0x18, 0xf5, 0xff, 0x8e, 0xc7, 0xf4, 0xd5, 0xe9, 0xa4, 0x37, 0x05, 0xc8, 0x68, 0xfc, 0xc3, 0x81, 0x62, 0x15, 0x97, 0x22, 0xa6, 0x61, 0x7d, 0x58, 0x8d, 0x21, 0xc8, 0x4e, 0x0f, 0x30, 0x20, 0x4f, 0x5a, 0x8c, 0x37, 0x81, 0x8d, 0x60, 0xcb, 0xd5, 0x7d, 0x89, 0x14, 0x16, 0xc2, 0x0e, 0x7f, 0x48, 0xe3, 0x26, 0x77, 0x75, 0xc2, 0x27, 0xa6, 0xa6, 0x43, 0xeb, 0xcf, 0x37, 0xc2, 0xfa, 0x10, 0x23, 0xa9, 0xdc, 0xe1, 0x94, 0xb8, 0xf9, 0xc9, 0xd8, 0xff, 0xc8, 0xd5, 0xc5, 0xd9, 0xdf, 0xb5, 0x9e, 0x60, 0x64, 0xe1, 0x6a, 0x62, 0x1a, 0xf5, 0xcb, 0x05, 0x88, 0x15, 0xd4, 0x94, 0xc5, 0x3f, 0xdc, 0x26, 0x89, 0x7f, 0x7d, 0x63, 0x34, 0x53, 0x81, 0x8e, 0xb9, 0x4b, 0xd9, 0x5c, 0x06, 0xf6, 0x95, 0xe3, 0x4a, 0xac, 0x67, 0xed, 0x2c, 0x8d, 0x1f, 0x53, 0xf0, 0xd4, 0x10, 0x34, 0xcd, 0x50, 0x3f, 0x1b, 0x1f, 0xbe, 0x9c, 0x68, 0x16, 0x9d, 0x02, 0x66, 0xc8, 0x92, 0x5c, 0x2e, 0x22, 0xe4, 0x25, 0xa1, 0x78, 0x93, 0xfb, 0xbe, 0x4b, 0x1f, 0x50, 0x7c, 0x10, 0x99, 0xda, 0x35, 0x16, 0xb8, 0x35, 0x38, 0x79, 0x5d, 0xcd, 0xa2, 0x1c, 0x5a, 0xa4, 0x9b, 0x26, 0x56, 0x37, 0x2f, 0xa3, 0x63, 0x7e, 0x8f, 0xf7, 0x89, 0xbd, 0x62, 0x30, 0xec, 0x49, 0x5b, 0xc9, 0x13, 0x75, 0x96, 0x6e, 0x2d, 0x92, 0x32, 0x27, 0x67, 0x31, 0x4d, 0xf9, 0xf3, 0x2e, 0xc7, 0xbe, 0x01, 0x69, 0xac, 0x5a, 0x20, 0xf3, 0xfd, 0xa0, 0xf1, 0x44, 0xba, 0x45, 0x89, 0x71, 0x2a, 0xca, 0x82, 0xfa, 0x28, 0x22, 0xd9, 0x4b, 0xc4, 0x00, 0xb7, 0xa1, 0x4d, 0xd1, 0xe8, 0xa3, 0xc7, 0xd5, 0x25, 0xf5, 0xcb, 0x0e, 0xa6, 0x3e, 0xa3, 0x7a, 0x10, 0x99, 0xad, 0xc0, 0x84, 0x4d, 0x43, 0x7d, 0x04, 0x9e, 0xea, 0xf1, 0xbe, 0xa7, 0xb0, 0xf0, 0x44, 0xfa, 0x0a, 0x2f, 0xed, 0x27, 0xf6, 0x9e, 0x96, 0x0a, 0x89, 0x10, 0x07, 0x6f, 0x19, 0x3b, 0x21, 0x0e, 0xac, 0x89, 0xa1, 0xb7, 0xeb, 0x25, 0x21, 0xea, 0x4c, 0x53, 0xcc, 0x4b, 0xfc, 0xc0, 0xb9, 0x86, 0x65, 0x98, 0x92, 0xbe, 0x64, 0x26, 0x12, 0xac, 0x80, 0x74, 0x90, 0x15, 0x47, 0x67, 0x3d, 0x69, 0x56, 0x98, 0x6c, 0xaf, 0x4d, 0x04, 0x4a, 0xac, 0xcb, 0xc8, 0xe2, 0xb1, 0x75, 0xf8, 0xcf, 0xf6, 0xa2, 0x3f, 0xa1, 0x6e, 0xfe, 0xd6, 0xf7, 0x03, 0x30, 0x1f, 0x0c, 0x59, 0x3a, 0xd0, 0x31, 0x75, 0xf2, 0xd2, 0x33, 0x94, 0xdc, 0xe1, 0xb4, 0xe2, 0xe8, 0xdf, 0xaf, 0x65, 0xa3, 0x92, 0x83, 0x56, 0x27, 0x71, 0x28, 0xf6, 0x38, 0x1d, 0x25, 0x60, 0xa7, 0x0f, 0x4b, 0xb8, 0x50, 0xe9, 0x67, 0xeb, 0x72, 0x5d, 0x31, 0xae, 0x5e, 0x40, 0x23, 0x38, 0xe1, 0x5f, 0x0f, 0x10, 0x27, 0xa6, 0x6d, 0x6f, 0x11, 0xf8, 0x62, 0xc3, 0x23, 0x4f, 0x19, 0x4a, 0x7d, 0x7f, 0xdb, 0x2a, 0x39, 0x34, 0x73, 0x65, 0x45, 0x0e, 0x0d, 0xee, 0x31, 0x55, 0x60, 0x64, 0x07, 0xb9, 0x59, 0x12, 0xa0, 0x27, 0xc1, 0xc7, 0xdf, 0x65, 0x31, 0x7f, 0x8e, 0x6f, 0x32, 0x7c, 0xcc, 0x6b, 0x1d, 0xfa, 0x42, 0x4b, 0x34, 0xec, 0xd3, 0x7e, 0x65, 0x68, 0x66, 0x64, 0x86, 0x47, 0x20, 0x69, 0x21, 0xfb, 0x82, 0x06, 0x75, 0x40, 0x37, 0x18, 0x6e, 0x30, 0x6c, 0xe6, 0x8d, 0xcb, 0x2f, 0x4d, 0xf7, 0xcb, 0xe4, 0x65, 0x06, 0x75, 0xe3, 0xd8, 0x9a, 0x47, 0x09, 0x5a, 0x50, 0x07, 0x1e, 0x2e, 0xe0, 0xda, 0x77, 0xe3, 0xbb, 0x52, 0x0d, 0xf5, 0x52, 0xc0, 0xd4, 0x33, 0xbf, 0xec, 0x6e, 0x41, 0x10, 0x4f, 0xda, 0x37, 0x53, 0xfa, 0x96, 0x4f, 0x39, 0xcf, 0x06, 0xf2, 0x92, 0x3a, 0xe5, 0x43, 0x7e, 0x5a, 0x3d, 0x8d, 0xcf, 0xb1, 0x6c, 0xc6, 0x09, 0xdd, 0xda, 0x64, 0x43, 0xd5, 0xbb, 0x46, 0x56, 0x59, 0x36, 0x3d, 0x70, 0xe4, 0xd1, 0xea, 0x48, 0xd4, 0x49, 0x49, 0xb5, 0x1a, 0xbc, 0x45, 0x16, 0x04, 0x78, 0xb6, 0xa1, 0x6e, 0xea, 0x53, 0x14, 0x1a, 0x77, 0xb9, 0x01, 0x98, 0xa3, 0x66, 0x19, 0x4d, 0x5d, 0x86, 0x24, 0x08, 0x3b, 0x21, 0xbb, 0x99, 0x96, 0x90, 0x9e, 0xe3, 0x2e, 0xcb, 0xb2, 0xc4, 0x38, 0x55, 0xb5, 0x4c, 0xe4, 0x5a, 0x52, 0xe2, 0xdf, 0xc2, 0xa1, 0xfc, 0xa4, 0xe7, 0x1f, 0x10, 0xc7, 0x20, 0xc4, 0xf3, 0x21, 0x41, 0xfd, 0x42, 0x34, 0x0d, 0xa2, 0x4c, 0x56, 0x7d, 0xd4, 0xc5, 0xc1, 0x7d, 0x97, 0x6a, 0xed, 0x8a, 0x10, 0xfc, 0xec, 0x21, 0xa6, 0xfb, 0x94, 0x16, 0x6b, 0xf9, 0xe4, 0xae, 0x85, 0xbd, 0x86, 0x08, 0x35, 0xbc, 0x7e, 0x52, 0x7a, 0xb5, 0x24, 0x70, 0x81, 0x29, 0xb2, 0x6e, 0x84, 0xbd, 0x53, 0xb1, 0x0b, 0x90, 0xf1, 0x6f, 0x87, 0xe8, 0x57, 0x2d, 0x5c, 0x57, 0x2a, 0x7a, 0x11, 0xc9, 0x39, 0x60, 0x3c, 0xda, 0x5f, 0xa5, 0x20, 0x85, 0xa7, 0x5d, 0x30, 0x56, 0x78, 0xfe, 0x2c, 0x6f, 0x92, 0x99, 0x32, 0x9d, 0x28, 0x26, 0xd5, 0x4a, 0x72, 0xb0, 0x86, 0x25, 0x43, 0x21, 0xc8, 0x7b, 0x93, 0x3e, 0xdb, 0x8a, 0x6b, 0xcd, 0x1d, 0x28, 0x99, 0x9f, 0x51, 0x4b, 0x1a, 0x49, 0x49, 0x93, 0x99, 0xa8, 0x4b, 0xfd, 0xe6, 0x42, 0xaa, 0x41, 0x66, 0xec, 0xf1, 0xf2, 0xae, 0x9d, 0x6f, 0x6f, 0x17, 0xf6, 0x6d, 0x74, 0x61, 0x81, 0xb9, 0x3e, 0x80, 0x17, 0xec, 0x08, 0xe8, 0x73, 0x63, 0x0b, 0x5e, 0x3e, 0x37, 0xfd, 0x1c, 0xd8, 0x5f, 0xf4, 0xfd, 0x65, 0x7b, 0x88, 0xe8, 0x45, 0xf6, 0x02, 0x29, 0x5c, 0xab, 0xbd, 0xed, 0x34, 0xff, 0x8b, 0xcb, 0x8c, 0xd6, 0x0c, 0x4c, 0xc4, 0xa7, 0xff, 0x44, 0xd2, 0xa9, 0xf1, 0x35, 0x55, 0x5f, 0x89, 0x08, 0xa5, 0x92, 0xd9, 0x69, 0xe0, 0x8e, 0x1b, 0xa3, 0xc4, 0xdb, 0x6e, 0x10, 0xfe, 0xb9, 0x2b, 0x89, 0xfa, 0x5d, 0xf0, 0x15, 0x4c, 0xa7, 0x5c, 0xa9, 0x0d, 0x23, 0x2f, 0xf0, 0xc1, 0x29, 0xf6, 0xd6, 0x7e, 0xce, 0xc1, 0x44, 0xd9, 0xdd, 0x13, 0x2f, 0x7a, 0x72, 0x17, 0xaf, 0x89, 0xd4, 0xee, 0x9d, 0x1b, 0xb6, 0xbf, 0xc1, 0x58, 0x98, 0x22, 0xc3, 0x24, 0x89, 0xf5, 0x48, 0xb1, 0xbb, 0x84, 0x1b, 0x67, 0x5e, 0x49, 0x0b, 0xd3, 0xee, 0xa4, 0xe6, 0x34, 0x45, 0xb7, 0xeb, 0x0b, 0xd4, 0x62, 0x2e, 0x4b, 0x4b, 0xe4, 0x41, 0xc2, 0xda, 0x41, 0x58, 0x56, 0x06, 0x83, 0xdc, 0xe1, 0xda, 0xff, 0x8f, 0x57, 0x83, 0xe6, 0xeb, 0x0d, 0xfd, 0x7a, 0x2f, 0x4b, 0x33, 0x48, 0x19, 0x5f, 0xa0, 0xe0, 0x8c, 0xae, 0x03, 0x92, 0xeb, 0xb9, 0xbd, 0xef, 0xdf, 0x6c, 0x66, 0x8e, 0x5e, 0xf0, 0xd7, 0x74, 0xaa, 0xf6, 0xe6, 0x56, 0x4e, 0x51, 0x0f, 0x23, 0x68, 0xb5, 0xcb, 0x6b, 0x41, 0x0d, 0x8a, 0x6c, 0x2b, 0x0f, 0x19, 0xaf, 0xf1, 0xe5, 0xc8, 0xad, 0x43, 0x2f, 0x94, 0x44, 0xa9, 0xcb, 0x54, 0x29, 0x79, 0xa1, 0xab, 0x76, 0xec, 0x29, 0x7c, 0xaf, 0xa0, 0x83, 0x9a, 0x89, 0x0d, 0x51, 0xed, 0x19, 0xec, 0x75, 0xea, 0x72, 0xa9, 0x7d, 0x67, 0x22, 0x7d, 0x65, 0x7d, 0xc4, 0xf6, 0xd5, 0xc9, 0x0a, 0x8b, 0xba, 0x16, 0x02, 0xb5, 0x98, 0x63, 0x4a, 0x36, 0xc2, 0xae, 0x68, 0xec, 0x60, 0x0b, 0xdc, 0x53, 0x8e, 0xfd, 0x3a, 0x9c, 0xf9, 0x4e, 0x8f, 0x70, 0xec, 0x69, 0x4f, 0x6e, 0x02, 0xf3, 0x5f, 0x23, 0xf8, 0xa4, 0x28, 0xdd, 0x42, 0xaf, 0xc9, 0xaa, 0xaf, 0x2f, 0x9c, 0x10, 0xa1, 0x78, 0x26, 0x76, 0x39, 0x6f, 0x4f, 0x32, 0x60, 0xf5, 0x88, 0xb6, 0xf4, 0x98, 0x01, 0x6e, 0x65, 0x92, 0xfd, 0x86, 0x98, 0xfe, 0x11, 0xaa, 0x0f, 0xb6, 0x4f, 0x36, 0xfb, 0x0b, 0x77, 0x58, 0xfa }; +constexpr AccessUnit AC3_MULTIPLE_PACK_EXPECTED_AU_1 = { 0x15f90, 0x159b2, false, 0, {}, { 0x79, 0x8a, 0x07, 0x46, 0x57, 0xa6, 0x1d, 0x59, 0x7a, 0xdd, 0x08, 0xfc, 0xd0, 0xe3, 0x3b, 0xef, 0xe7, 0x60, 0xa5, 0x91 } }; +constexpr AccessUnit AC3_MULTIPLE_PACK_EXPECTED_AU_2 = { 0x6c30bb8a, 0x132b3bdd, false, 0, {}, { 0x67, 0x41, 0xc8, 0x5f, 0xec, 0xad, 0xd9, 0x3d, 0xce, 0x0f, 0x03, 0x89, 0x70, 0x81, 0x04, 0x15, 0xa1, 0x4a, 0x0c, 0xf9 } }; +constexpr AccessUnit AC3_MULTIPLE_PACK_EXPECTED_AU_3 = { 0x15f90, 0x159b2, false, 0, {}, { 0x7c, 0x0e, 0xf4, 0xe5, 0x3e, 0x5d, 0x1f, 0xaa, 0x37, 0x09, 0x54, 0x74, 0x63, 0x57, 0xae, 0xc3, 0x72, 0x53, 0x53, 0xab } }; +constexpr AccessUnit AC3_MULTIPLE_PACK_EXPECTED_AU_4 = { std::numeric_limits::max(), std::numeric_limits::max(), false, 0, {}, { 0xa0, 0xcf, 0xde, 0x0c, 0x55, 0x6a, 0x70, 0x0b, 0x74, 0x55, 0x2b, 0x81, 0x2e, 0x70, 0xe7, 0x55, 0x67, 0xfa, 0x32, 0xa4 } }; + + +constexpr std::array AVC_TINY_PACKETS_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xe9, 0xb2, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0xee, 0x01, 0xe0, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x37, 0x00, 0x80, 0xbe, 0x21, 0x00, 0x80, 0x0a, 0xd8, 0x00, 0x00, 0x0a, 0xb1, 0x00, 0x80, 0x09, 0xa5, 0x00, 0x00, 0x0a, 0x1d, 0x00, 0x80, 0x09, 0x53, 0x00, 0x00, 0x09, 0x6d, 0x00, 0x80, 0x08, 0xca, 0x00, 0x00, 0x09, 0x5b, 0x00, 0x80, 0x08, 0xa2, 0x00, 0x00, 0x08, 0xda, 0x00, 0x80, 0x08, 0x73, 0x00, 0x00, 0x09, 0x16, 0x00, 0x80, 0x08, 0x61, 0x00, 0x00, 0x08, 0x9f, 0x00, 0x80, 0x08, 0x10, 0x00, 0x00, 0x08, 0x82, 0x00, 0x80, 0x08, 0x56, 0x00, 0x00, 0x08, 0x61, 0x00, 0x80, 0x08, 0x20, 0x00, 0x00, 0x08, 0x67, 0x00, 0x80, 0x07, 0xcc, 0x00, 0x00, 0x08, 0x8f, 0x00, 0x80, 0x08, 0x25, 0x00, 0x00, 0x08, 0x53, 0x00, 0x80, 0x07, 0xa0, 0x00, 0x00, 0x08, 0x14, 0x00, 0x80, 0x07, 0xc9, 0x00, 0x00, 0x08, 0x17, 0x00, 0x80, 0x07, 0xc1, 0x00, 0x00, 0x08, 0x46, 0x00, 0x80, 0x07, 0xb0, 0x00, 0x00, 0x07, 0xbd, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x80, 0x07, 0x91, 0x00, 0x00, 0x08, 0x5d, 0x00, 0x80, 0x07, 0x8f, 0x00, 0x00, 0x07, 0xdd, 0x00, 0x80, 0x07, 0x80, 0x00, 0x00, 0x07, 0xe6, 0x00, 0x80, 0x07, 0x7f, 0x00, 0x00, 0x08, 0x1e, 0x00, 0x80, 0x07, 0x94, 0x00, 0x00, 0x07, 0xc5, 0x00, 0x80, 0x07, 0xab, 0x00, 0x00, 0x07, 0xec, 0x00, 0x80, 0x07, 0x74, 0x00, 0x00, 0x07, 0xb6, 0x00, 0x80, 0x07, 0x79, 0x00, 0x00, 0x07, 0xff, 0x00, 0x80, 0x07, 0x76, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x80, 0x07, 0xd1, 0x00, 0x00, 0x07, 0xb3, 0x00, 0x00, 0x01, 0xe0, 0x06, 0xe6, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xb3, 0x65, 0x1e, 0x69, 0xb2, 0x75, 0x62, 0x06, 0xcf, 0xeb, 0x38, 0xf4, 0x91, 0x03, 0xac, 0x06, 0xee, 0xf5, 0x5a, 0x22, 0x5e, 0xb8, 0xd5, 0xf1, 0x50, 0x3f, 0x49, 0xd9, 0x47, 0x75, 0xc3, 0x8c, 0x6d, 0xac, 0x33, 0xc5, 0xc6, 0xc8, 0x25, 0x8f, 0x11, 0xd3, 0xf6, 0xf6, 0xdb, 0xad, 0xca, 0x5d, 0x4e, 0x0f, 0xb9, 0x1f, 0x1d, 0xe5, 0x5d, 0xb7, 0xd3, 0xfa, 0x76, 0xa6, 0x64, 0x99, 0xd0, 0x7b, 0x8e, 0xc2, 0x09, 0xf4, 0x2a, 0x11, 0x3c, 0x1b, 0xe4, 0xe5, 0xee, 0xe4, 0x36, 0x2f, 0x63, 0xa5, 0x8e, 0x5e, 0x32, 0x59, 0x17, 0xcd, 0x22, 0xb5, 0xf0, 0xea, 0xe3, 0xef, 0x22, 0x1e, 0x67, 0x46, 0xeb, 0x93, 0x3f, 0x90, 0x49, 0x38, 0x0c, 0xa0, 0xe5, 0x1c, 0x11, 0x94, 0xec, 0x8f, 0xc0, 0x4f, 0x87, 0xaf, 0xcb, 0x41, 0xa8, 0x17, 0xa7, 0x2e, 0xdb, 0x53, 0xaf, 0x6c, 0x07, 0x54, 0x78, 0xba, 0x15, 0xf0, 0xdc, 0xd7, 0x18, 0x9d, 0x9e, 0x6d, 0x3b, 0x0b, 0xac, 0xfb, 0x58, 0x04, 0x1c, 0x96, 0x36, 0xe8, 0x40, 0xf2, 0x76, 0xb8, 0x71, 0x30, 0x6c, 0x66, 0x24, 0x50, 0xa4, 0x58, 0x7b, 0x45, 0xaa, 0x5e, 0x04, 0x1d, 0x17, 0xf8, 0x5b, 0x68, 0x51, 0xf0, 0xb1, 0x06, 0xe5, 0x47, 0xb0, 0xb0, 0x55, 0xe1, 0xb8, 0x3d, 0x28, 0x6e, 0x8e, 0x63, 0x4c, 0x61, 0x05, 0xa8, 0xfd, 0x5a, 0x15, 0xf3, 0xed, 0xbb, 0x7d, 0xe3, 0x7e, 0x35, 0x93, 0x55, 0xb2, 0x8d, 0x0c, 0xe9, 0x89, 0xbc, 0xaa, 0xf5, 0x9f, 0x6a, 0xc3, 0x41, 0xf8, 0x34, 0xb7, 0xd5, 0x09, 0x41, 0x37, 0x6b, 0x0e, 0xc3, 0xd1, 0x1a, 0x3e, 0xe4, 0x68, 0xb9, 0x9a, 0xc4, 0x24, 0x7c, 0x57, 0x8a, 0x2c, 0x69, 0xf9, 0x4b, 0x4d, 0xd0, 0x4a, 0x9b, 0x0e, 0x93, 0x4f, 0xcd, 0xb5, 0x41, 0xc6, 0x2e, 0x98, 0x0a, 0x14, 0x8d, 0xa4, 0xf7, 0x3a, 0x19, 0x85, 0x89, 0x56, 0xcb, 0x85, 0x0b, 0x4b, 0x99, 0x8b, 0xb0, 0x11, 0xd6, 0xd7, 0xbe, 0x0c, 0xf8, 0x6d, 0x9c, 0xd9, 0xb0, 0x96, 0x8c, 0x70, 0x74, 0x10, 0x54, 0xd7, 0x98, 0x50, 0x5d, 0x4e, 0x24, 0xb9, 0x41, 0xe3, 0x6f, 0xe6, 0x9d, 0x89, 0x9e, 0xef, 0x3f, 0xc0, 0x4e, 0x11, 0x59, 0xe6, 0x5d, 0x94, 0x4e, 0x5d, 0xb6, 0x21, 0xf6, 0xcd, 0x2b, 0x8e, 0x82, 0x6e, 0xc1, 0xb1, 0x1a, 0xcd, 0xad, 0xfc, 0x4c, 0x16, 0x9b, 0x98, 0xa9, 0x6b, 0xcd, 0xd0, 0xd5, 0x78, 0x0e, 0x97, 0x97, 0x1a, 0xa2, 0xf0, 0xdb, 0xea, 0xf9, 0x88, 0x74, 0xea, 0x85, 0x8a, 0x80, 0xcb, 0x70, 0xfd, 0x6b, 0xf6, 0x31, 0xab, 0xaf, 0x8d, 0xee, 0xba, 0x67, 0xcc, 0xdc, 0x0c, 0xe7, 0x59, 0xd2, 0xba, 0x5e, 0xb7, 0x5d, 0x2f, 0x82, 0xa0, 0x77, 0xc1, 0x4a, 0x0b, 0x77, 0xd1, 0xaa, 0x1c, 0x19, 0xfa, 0xaf, 0xbc, 0xb3, 0xa0, 0x96, 0x27, 0x9c, 0x32, 0x9d, 0x34, 0x23, 0xc9, 0x7f, 0xf9, 0x7a, 0x68, 0x46, 0xa7, 0x8b, 0x0e, 0x2a, 0x4c, 0xe4, 0xbe, 0x3a, 0xca, 0x56, 0x52, 0xa2, 0x6c, 0x22, 0x00, 0x67, 0xef, 0x7a, 0x7d, 0x70, 0x7b, 0x52, 0x4a, 0xcd, 0xcd, 0xe9, 0x3e, 0x5e, 0x81, 0xf0, 0xe2, 0x41, 0x08, 0x6f, 0x4a, 0x93, 0xf2, 0xbd, 0xf2, 0xe0, 0xfa, 0x6b, 0x98, 0xc5, 0xdc, 0xea, 0xc5, 0xc3, 0x07, 0x6a, 0x15, 0x0e, 0x5e, 0xe7, 0xf0, 0xe6, 0x48, 0xe7, 0x48, 0x2f, 0x3c, 0xc1, 0x12, 0xa5, 0xe3, 0xdc, 0x49, 0x0a, 0xa2, 0x5c, 0x14, 0x37, 0x29, 0xf0, 0x29, 0x9a, 0x0c, 0xeb, 0xf9, 0xd0, 0x6e, 0x13, 0xb4, 0x3f, 0xc3, 0x35, 0x28, 0x63, 0x76, 0x89, 0xa4, 0xf2, 0x8a, 0x47, 0x7b, 0x97, 0xef, 0xa7, 0x0e, 0x86, 0xe3, 0x89, 0xa4, 0xae, 0x7d, 0xa3, 0x8e, 0x0a, 0xee, 0x17, 0x99, 0x1c, 0x56, 0xba, 0xc7, 0xca, 0x32, 0xea, 0x9b, 0x23, 0xf0, 0x88, 0xe3, 0x1c, 0xd6, 0xb0, 0xc7, 0xad, 0x6f, 0x58, 0x03, 0xaa, 0xd6, 0x79, 0x33, 0xc2, 0x43, 0xbd, 0xae, 0x1b, 0xcb, 0x17, 0xac, 0x34, 0x35, 0xfc, 0xb3, 0x50, 0x16, 0xd5, 0x1b, 0xcf, 0x99, 0xd3, 0xea, 0xe0, 0x59, 0xc8, 0x1b, 0x4d, 0xad, 0xb0, 0xa2, 0x37, 0x88, 0xa7, 0x2a, 0x45, 0x4a, 0x3b, 0xd2, 0xcd, 0x81, 0x7e, 0x4d, 0x3e, 0x9d, 0x9d, 0xe6, 0xb1, 0xfc, 0x7b, 0x1e, 0xf3, 0x7b, 0x74, 0x0a, 0x3d, 0x66, 0x02, 0x99, 0xf7, 0xdb, 0x6d, 0x4d, 0x3f, 0x1f, 0x7f, 0x6e, 0xd4, 0x57, 0x62, 0x51, 0xd6, 0x62, 0xae, 0x28, 0x3e, 0xed, 0xc3, 0x8b, 0x8d, 0x0f, 0xb9, 0x1a, 0xe6, 0x79, 0xa7, 0x28, 0xb8, 0xf9, 0xb3, 0x39, 0x50, 0x55, 0x30, 0x59, 0xef, 0x17, 0xb5, 0x18, 0xff, 0xdc, 0xbc, 0xd0, 0x22, 0x6b, 0x79, 0x62, 0x70, 0x5e, 0xdc, 0x5c, 0x24, 0xa1, 0x65, 0xb5, 0x5b, 0x96, 0x1f, 0x1b, 0xe2, 0x3c, 0xd4, 0x15, 0xf4, 0xc8, 0x1f, 0xd7, 0x9e, 0x72, 0xc2, 0x0f, 0x9e, 0x00, 0x9a, 0xf8, 0x68, 0x70, 0x6e, 0x07, 0xac, 0xde, 0x61, 0x03, 0x7d, 0x09, 0xd0, 0x45, 0xae, 0x2e, 0x98, 0x7c, 0x8f, 0xc9, 0x9a, 0x3a, 0x47, 0xd2, 0x70, 0x80, 0xd2, 0x9d, 0x19, 0xd7, 0xf4, 0x26, 0x05, 0x17, 0x0c, 0x12, 0x0d, 0x3a, 0xd8, 0xf6, 0xeb, 0x21, 0xa4, 0xd9, 0x2a, 0x83, 0x00, 0x8b, 0x9d, 0x07, 0xf9, 0x44, 0xc2, 0x18, 0x9e, 0xf9, 0xb9, 0xa0, 0xa0, 0x2a, 0x4c, 0x77, 0x50, 0xc4, 0x25, 0x3f, 0x9d, 0xab, 0x35, 0x9d, 0x41, 0x1e, 0x79, 0x5c, 0xd5, 0x61, 0x01, 0x95, 0x01, 0x26, 0x7f, 0x57, 0xe3, 0xaa, 0xfd, 0x02, 0x21, 0x01, 0xeb, 0x38, 0xfa, 0xa7, 0x4c, 0x99, 0x26, 0x46, 0xdf, 0x8d, 0x34, 0xc2, 0xc5, 0x6a, 0xc8, 0xda, 0x90, 0x48, 0xdc, 0x48, 0x3b, 0x87, 0x0b, 0x1b, 0x4f, 0xf1, 0x5b, 0x2f, 0x1e, 0x1e, 0x12, 0xd6, 0x9e, 0x7d, 0x01, 0x1d, 0x10, 0xd4, 0x1d, 0x42, 0x30, 0x08, 0xf1, 0xa8, 0x9e, 0x5c, 0xdf, 0xfe, 0xdb, 0x6f, 0x62, 0x12, 0x5d, 0x70, 0x7d, 0xe7, 0x81, 0x7b, 0xa7, 0xb2, 0xed, 0xa4, 0x3c, 0x6e, 0x51, 0xf3, 0x08, 0x44, 0xf7, 0x76, 0xaa, 0x6b, 0xb2, 0x8b, 0xd8, 0xb7, 0x9d, 0x65, 0x2a, 0x3d, 0x4a, 0xeb, 0x08, 0xfc, 0xd6, 0xc6, 0x4e, 0x52, 0xbb, 0x43, 0xd1, 0x05, 0x5e, 0x76, 0x09, 0x98, 0x2e, 0x6d, 0xcb, 0xfa, 0x22, 0x41, 0x73, 0x84, 0x00, 0x6b, 0xab, 0xf0, 0x38, 0x90, 0x2e, 0x3e, 0xe7, 0x9e, 0x16, 0xee, 0x67, 0x51, 0xb5, 0xe2, 0x49, 0x1d, 0xb1, 0x19, 0xa9, 0x0f, 0x58, 0x13, 0xec, 0x43, 0xb4, 0x6b, 0x82, 0xaf, 0x1b, 0x9a, 0x17, 0xe0, 0xab, 0x0f, 0xfe, 0xf7, 0x03, 0xf8, 0xf3, 0xc8, 0x6f, 0x09, 0x1a, 0x7a, 0x72, 0x4e, 0xa7, 0x2f, 0x9e, 0x1f, 0x63, 0x91, 0x7c, 0xca, 0x37, 0x3b, 0x26, 0x2b, 0x08, 0x75, 0x76, 0x38, 0xbb, 0xec, 0x9a, 0xa0, 0x6f, 0x8d, 0x33, 0xd4, 0xf2, 0x36, 0x31, 0x9d, 0x40, 0xb1, 0xc7, 0xe5, 0xbb, 0x8e, 0x0a, 0x23, 0xa3, 0x74, 0x90, 0xe3, 0xb2, 0x25, 0x1e, 0xab, 0x07, 0x1f, 0x0c, 0x5a, 0xe3, 0x56, 0x7f, 0xf4, 0x9c, 0x1b, 0x55, 0x07, 0x6b, 0x4b, 0xab, 0xd6, 0x49, 0xdf, 0xaf, 0x62, 0xdf, 0x6b, 0x53, 0x14, 0x1e, 0xa5, 0xa2, 0x54, 0xcb, 0xe7, 0xce, 0x41, 0x0c, 0x37, 0xe0, 0x3a, 0x54, 0xe3, 0x2e, 0x4e, 0x9a, 0x64, 0x11, 0x36, 0x59, 0xc2, 0xda, 0xc3, 0x60, 0xdd, 0x57, 0xab, 0xa4, 0xa5, 0xbc, 0x11, 0x7a, 0xf1, 0x34, 0x65, 0x95, 0x75, 0xc8, 0x60, 0x77, 0x36, 0x73, 0x7d, 0x12, 0x9b, 0xd7, 0xa5, 0x7c, 0x28, 0xe5, 0x71, 0x67, 0x6d, 0xd3, 0x0f, 0x3c, 0x10, 0x37, 0xc3, 0xfd, 0x04, 0x11, 0x3b, 0xdd, 0x83, 0x3c, 0xcc, 0x58, 0x11, 0x01, 0x4f, 0xbc, 0xb7, 0x9c, 0x0d, 0x63, 0x4a, 0xc5, 0x87, 0xf9, 0x53, 0xd2, 0xb1, 0x95, 0x2e, 0x41, 0x8d, 0x8b, 0x02, 0x0b, 0x81, 0x1d, 0x5c, 0x39, 0x5b, 0xfd, 0xc6, 0x73, 0xc9, 0x6e, 0xed, 0xe2, 0xe3, 0x2d, 0x4e, 0xb3, 0xfb, 0x95, 0xe6, 0x9a, 0x7f, 0x2f, 0x64, 0xc1, 0x83, 0xb8, 0x55, 0xb1, 0x05, 0xcb, 0xaa, 0xdc, 0xaa, 0xed, 0xf7, 0x56, 0xd5, 0xdc, 0x3b, 0x5b, 0x5c, 0xa8, 0xd4, 0x2b, 0x4c, 0xf1, 0xbf, 0xe7, 0x37, 0x47, 0x28, 0x9c, 0x74, 0x54, 0x19, 0x9d, 0xe7, 0x64, 0x09, 0x35, 0xd0, 0x78, 0xdf, 0x8d, 0xcc, 0x9e, 0x9c, 0x19, 0xa5, 0xb6, 0xe2, 0x85, 0x0e, 0x7e, 0x1f, 0x3b, 0x69, 0x63, 0xbd, 0xda, 0x79, 0xd0, 0x9d, 0x67, 0x31, 0x6a, 0xf3, 0xcf, 0xdf, 0x4e, 0x55, 0x01, 0xe8, 0x22, 0xf1, 0x4a, 0x2a, 0x06, 0xb3, 0x3a, 0x82, 0x6c, 0xa8, 0xb3, 0x39, 0xb3, 0x98, 0x09, 0xf8, 0xde, 0x66, 0x39, 0xaf, 0xb1, 0x71, 0xf9, 0x01, 0xe3, 0x5f, 0x83, 0x8c, 0x42, 0xbb, 0x66, 0x5c, 0xcc, 0xaf, 0xce, 0x38, 0xad, 0x08, 0x99, 0xf2, 0x47, 0xc7, 0xf4, 0xe6, 0xeb, 0x15, 0x68, 0x7f, 0xf3, 0xc7, 0xe6, 0x3e, 0xcc, 0x2c, 0x74, 0xdc, 0xb7, 0x57, 0xfc, 0x2d, 0x70, 0x74, 0xa0, 0xf0, 0xf2, 0x97, 0x69, 0x31, 0x14, 0x96, 0x18, 0x6d, 0x23, 0xb1, 0xd6, 0x69, 0x8d, 0x53, 0xeb, 0x46, 0xde, 0xb3, 0xb4, 0x2b, 0x45, 0x1b, 0x2c, 0x48, 0x44, 0x22, 0x72, 0x0d, 0xc2, 0xa4, 0x9b, 0x86, 0x16, 0xc7, 0x9c, 0x34, 0x47, 0xc5, 0x97, 0xd0, 0x56, 0xd7, 0x54, 0x63, 0x5d, 0x64, 0xa0, 0xbb, 0x85, 0x46, 0x77, 0xe7, 0x05, 0x1c, 0x50, 0x90, 0x4d, 0x73, 0xac, 0x2c, 0x38, 0xb5, 0xb1, 0x36, 0x34, 0x63, 0x13, 0xa8, 0x40, 0x8d, 0x6b, 0xea, 0x99, 0x71, 0x11, 0x43, 0xf3, 0x4b, 0x96, 0x19, 0x6b, 0x28, 0xf3, 0x8b, 0xaa, 0x7c, 0x19, 0x36, 0xc5, 0x64, 0xce, 0x05, 0x43, 0x58, 0x27, 0x21, 0xaa, 0x3e, 0xb1, 0xed, 0x06, 0x41, 0x8a, 0x54, 0x8f, 0xfc, 0x49, 0xbe, 0x4b, 0xf6, 0x9c, 0x39, 0xc8, 0xaa, 0xbd, 0xa8, 0xba, 0x0b, 0x8a, 0x5c, 0x2b, 0xc7, 0x85, 0x56, 0x0e, 0x75, 0x86, 0x9e, 0x24, 0x5e, 0x62, 0xd7, 0x9f, 0xbc, 0xc0, 0x55, 0x52, 0xb9, 0xca, 0xe0, 0xa8, 0xa0, 0xa6, 0x27, 0xc4, 0x26, 0xf7, 0x60, 0x5e, 0x67, 0xe8, 0xd4, 0x1b, 0x76, 0x7c, 0x9c, 0x74, 0x79, 0x1a, 0xe2, 0x14, 0xc3, 0x6e, 0xe8, 0xe1, 0x62, 0xba, 0x65, 0x06, 0xba, 0x86, 0xa1, 0x45, 0x75, 0x26, 0x18, 0xcd, 0x1f, 0x8c, 0x6e, 0x10, 0x9d, 0x7e, 0x3a, 0x21, 0x29, 0x25, 0xcb, 0x4f, 0x74, 0xb5, 0xff, 0x35, 0x2b, 0xd4, 0xd8, 0x66, 0x0b, 0x48, 0x6c, 0x86, 0xd5, 0x8b, 0x08, 0x70, 0xad, 0xf3, 0xf6, 0x5d, 0x3c, 0x36, 0x41, 0xd5, 0x92, 0xee, 0xab, 0x3e, 0x5b, 0x30, 0x4e, 0xf3, 0xfe, 0x2d, 0x1b, 0xe2, 0x68, 0xf2, 0x95, 0x8c, 0xd0, 0xb2, 0x09, 0x47, 0xc5, 0x57, 0x1b, 0x33, 0xcc, 0x63, 0xbd, 0x96, 0x44, 0xdc, 0xe1, 0xff, 0x17, 0x6a, 0xa1, 0x49, 0x2c, 0xc4, 0xab, 0x93, 0xc6, 0x3b, 0x8d, 0x60, 0xe8, 0x57, 0x43, 0x58, 0xe4, 0x20, 0x41, 0x43, 0xa3, 0x07, 0xc9, 0xeb, 0x3d, 0xfa, 0xd1, 0x21, 0xef, 0x82, 0x9a, 0x09, 0xcb, 0xf0, 0x09, 0x1f, 0xad, 0xa1, 0x0d, 0x41, 0xdd, 0x8d, 0x9d, 0x63, 0xbb, 0xcf, 0xf4, 0xee, 0x9a, 0xbc, 0x5a, 0xf2, 0x58, 0xdf, 0xcd, 0x08, 0x9c, 0xf3, 0x48, 0xd9, 0x52, 0xc2, 0x7d, 0x3a, 0xd3, 0x7b, 0x1e, 0xce, 0xf4, 0xd4, 0x90, 0x36, 0x88, 0xf3, 0xff, 0xb1, 0x11, 0x1c, 0x80, 0xa5, 0x78, 0xaa, 0xe7, 0x32, 0x78, 0x53, 0xba, 0x29, 0x24, 0x2f, 0x20, 0x6a, 0x67, 0x36, 0x66, 0x0e, 0x4b, 0x16, 0xc7, 0xf2, 0xc1, 0x69, 0xe2, 0x45, 0x04, 0x73, 0xc9, 0x15, 0x50, 0x04, 0x0d, 0x25, 0x96, 0xf6, 0xc9, 0x23, 0xb5, 0x61, 0x6c, 0x4b, 0x64, 0x3a, 0x9f, 0xa9, 0x3a, 0x13, 0xc6, 0x7c, 0x03, 0xda, 0xee, 0x4a, 0x67, 0xbb, 0xf3, 0xd7, 0xc2, 0x01, 0xd2, 0xd4, 0x9c, 0x32, 0x88, 0x04, 0x9a, 0x9b, 0xb8, 0x0d, 0xa5, 0x95, 0xe6, 0xd8, 0x10, 0x41, 0x6f, 0xf7, 0x1a, 0x00, 0x85, 0xa2, 0xa9, 0x26, 0xfd, 0xc3, 0x2c, 0xe1, 0xe7, 0xdf, 0x7d, 0x63, 0xba, 0x0d, 0xc9, 0x2e, 0x28, 0x34, 0xff, 0x40, 0x03, 0x79, 0x00, 0xcb, 0xa4, 0x97, 0xce, 0xd7, 0x14, 0x08, 0x54, 0x4c, 0xaa, 0x9e, 0x51, 0x03, 0x82, 0xcf, 0xf6, 0xe2, 0xee, 0x05, 0x2f, 0xd3, 0x70, 0x2b, 0x71, 0x62, 0xf7, 0xbc, 0xa6, 0x38, 0x56, 0x0b, 0xdc, 0xd0, 0x60, 0x59, 0x60, 0x99, 0x17, 0x7f, 0x7a, 0x0d, 0x2c, 0xd0, 0x94, 0x05, 0x88, 0x2b, 0x40, 0x91, 0xb2, 0x83, 0xd9, 0xfa, 0xd8, 0x05, 0x62, 0xec, 0xd0, 0xbf, 0xb4, 0xa2, 0xf4, 0x3d, 0x34, 0xbe, 0x8d, 0xcd, 0x9c, 0xbe, 0xe8, 0x16, 0xac, 0x4e, 0x2f, 0x71, 0xd9, 0xab, 0x3f, 0x84, 0x87, 0xb7, 0xce, 0xd7, 0xf5, 0x54, 0x75, 0x34, 0xb0, 0x63, 0xb7, 0x50, 0x3c, 0xb5, 0x32, 0xed, 0x7e, 0x6a, 0xd7, 0x7a, 0x00, 0x00, 0x01, 0x09, 0x06, 0x4c, 0x64, 0xe2, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x04, 0x81, 0x01, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x04, 0x81, 0x01, 0x00, 0x01, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x04, 0x81, 0x01, 0x00, 0x09, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +constexpr std::array M2V_TINY_PACKETS_STREAM = { 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x0c, 0x83, 0xa9, 0x81, 0x80, 0xf0, 0x7f, 0xb9, 0xf3, 0x3b, 0xbd, 0xe7, 0x28, 0x00, 0x00, 0x01, 0xbf, 0x00, 0x7a, 0x01, 0xe0, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x1a, 0x00, 0x80, 0x20, 0x90, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x80, 0x00, 0x4f, 0x00, 0x00, 0x01, 0xe0, 0x07, 0x5a, 0x81, 0xc1, 0x0d, 0x31, 0x00, 0x05, 0xbf, 0x21, 0x11, 0x00, 0x05, 0xa7, 0xab, 0x1e, 0x73, 0x3b, 0x00, 0x00, 0x01, 0x00, 0x35, 0xf9, 0x2e, 0x87, 0xf2, 0x56, 0x91, 0x4f, 0xcc, 0x26, 0x6b, 0x91, 0x91, 0xb1, 0x2f, 0x9c, 0xd6, 0xe6, 0x70, 0xa4, 0xe1, 0x20, 0xae, 0x5b, 0x09, 0x39, 0x8b, 0xc5, 0x59, 0xd8, 0x6a, 0xee, 0x7a, 0x50, 0x8e, 0x52, 0x41, 0x71, 0x40, 0x1f, 0x55, 0x19, 0x85, 0xa4, 0xd1, 0x7b, 0x75, 0x89, 0xdf, 0x58, 0x41, 0xba, 0x9b, 0xa2, 0x66, 0x0a, 0xcf, 0x53, 0xb2, 0xfe, 0x4c, 0x60, 0x09, 0x51, 0xf3, 0x12, 0x11, 0x11, 0xb6, 0x41, 0x3d, 0x71, 0x22, 0x23, 0xd6, 0xaa, 0x31, 0xb6, 0xf0, 0x8a, 0x86, 0xfb, 0x86, 0x89, 0x46, 0x7b, 0x0c, 0x82, 0x2f, 0xe0, 0x48, 0x71, 0xad, 0x88, 0x3a, 0xc9, 0xf9, 0x64, 0x55, 0x3f, 0x9b, 0x93, 0x74, 0x33, 0xc3, 0x72, 0x2a, 0x01, 0x60, 0x29, 0x67, 0xe1, 0xf0, 0xe7, 0x97, 0x2e, 0x54, 0x4c, 0xa3, 0x34, 0x42, 0xbf, 0x93, 0xdd, 0x13, 0xa9, 0x9e, 0xd8, 0xfd, 0xd1, 0xee, 0x1b, 0x0d, 0x5b, 0x82, 0xda, 0x5c, 0x1b, 0xc2, 0x4a, 0x61, 0x40, 0xd5, 0xd2, 0x56, 0x99, 0xc1, 0x8b, 0x0e, 0xd9, 0xe4, 0x16, 0xb7, 0xb7, 0x4d, 0x43, 0x6c, 0xc2, 0x08, 0x05, 0x81, 0x48, 0x63, 0xaa, 0xfd, 0xb0, 0xb8, 0x5e, 0xa5, 0x13, 0x52, 0x67, 0x89, 0x0b, 0x67, 0xa7, 0x06, 0xf0, 0x47, 0x44, 0x8d, 0x0c, 0x39, 0xa2, 0xcf, 0x70, 0xa5, 0xea, 0xf6, 0x73, 0x82, 0xdc, 0x1c, 0x19, 0x2f, 0xb2, 0x30, 0xcc, 0x1c, 0x3b, 0xe9, 0x38, 0x01, 0x83, 0x84, 0x3d, 0x80, 0xab, 0x5e, 0x32, 0x5c, 0xc5, 0xc3, 0xf1, 0xc2, 0xd7, 0x60, 0x77, 0x3b, 0x5d, 0x48, 0x04, 0x49, 0xeb, 0x78, 0xfd, 0xc0, 0xbf, 0x41, 0x0d, 0x67, 0x70, 0x0a, 0x47, 0x6d, 0x09, 0x4e, 0x2f, 0xb8, 0x31, 0xd6, 0x05, 0x2b, 0x3a, 0x72, 0xb8, 0x52, 0x36, 0xd4, 0x3e, 0x27, 0x6b, 0x48, 0x5d, 0xcc, 0xc5, 0x55, 0x0c, 0x7b, 0x20, 0x9b, 0xf8, 0x07, 0x81, 0x79, 0xe4, 0xa4, 0xaa, 0x3c, 0xdb, 0xd8, 0x8d, 0xf5, 0x77, 0xcf, 0x95, 0x31, 0xb7, 0x73, 0xc4, 0x29, 0x1b, 0xf2, 0x24, 0x34, 0x0c, 0xb0, 0x02, 0x85, 0xc5, 0xb5, 0xe9, 0x82, 0x31, 0x1e, 0x78, 0x3f, 0x96, 0x2b, 0x5a, 0xf2, 0xe2, 0xdf, 0x19, 0x58, 0xf8, 0x5b, 0x8a, 0x6c, 0x66, 0x5e, 0x0d, 0xf0, 0x45, 0x9b, 0xe1, 0x9c, 0x44, 0xcf, 0x3b, 0xf0, 0x0a, 0xbb, 0xf1, 0x09, 0x85, 0x81, 0xdf, 0xf9, 0xc8, 0xfa, 0x53, 0x6c, 0x87, 0x00, 0x30, 0xb0, 0x5b, 0xd3, 0x94, 0x62, 0x61, 0x03, 0x1e, 0x50, 0xdc, 0x94, 0x3c, 0x64, 0xd9, 0x34, 0x11, 0x8f, 0x21, 0xcb, 0xd5, 0x1b, 0xe1, 0xba, 0xc6, 0xd6, 0xca, 0x7e, 0x0f, 0xf8, 0xb2, 0xa1, 0x35, 0xc3, 0xd9, 0x30, 0xf0, 0x26, 0x6b, 0x07, 0x89, 0x02, 0xde, 0xc2, 0x12, 0xc4, 0x45, 0x20, 0x26, 0x72, 0xb5, 0x19, 0xf2, 0x74, 0x59, 0x07, 0xc2, 0x99, 0xcf, 0x95, 0x50, 0x38, 0xef, 0x5e, 0x8c, 0x68, 0x0f, 0x60, 0xa0, 0xa0, 0x66, 0xc1, 0x64, 0x54, 0x33, 0x81, 0x87, 0x49, 0xa6, 0x08, 0x21, 0xbc, 0xd4, 0xcb, 0x6d, 0xa6, 0x22, 0x59, 0xea, 0x9a, 0xee, 0x96, 0x1b, 0x22, 0x6d, 0x04, 0x9e, 0x8c, 0xfc, 0x43, 0x69, 0x8c, 0x5d, 0x3c, 0x49, 0x29, 0xa3, 0x9d, 0x01, 0xd3, 0xbf, 0x95, 0xad, 0xa4, 0xf1, 0xfd, 0x2e, 0x16, 0x99, 0x55, 0xdb, 0xc7, 0x22, 0x46, 0xb7, 0x01, 0x01, 0x96, 0xd8, 0x31, 0x05, 0xaf, 0xd4, 0x78, 0x7b, 0x38, 0x55, 0xf5, 0xdb, 0x59, 0x2b, 0x97, 0xf4, 0xa2, 0x79, 0x74, 0xe0, 0xbd, 0xdf, 0xff, 0x3b, 0x55, 0xb4, 0x9d, 0x4b, 0xe3, 0xb1, 0x73, 0x21, 0x4b, 0xab, 0x6c, 0xf1, 0x6c, 0x2f, 0xc8, 0x12, 0x28, 0x2c, 0xc2, 0x6d, 0x6b, 0xf2, 0x1c, 0x4f, 0x41, 0x38, 0x8b, 0xdd, 0x0f, 0xf0, 0x6c, 0xac, 0x67, 0x1a, 0x31, 0x2b, 0x1d, 0xda, 0x6b, 0xea, 0x8d, 0x8c, 0x65, 0xee, 0x39, 0x8c, 0xb2, 0x96, 0x35, 0xf2, 0x30, 0xce, 0xfe, 0x8a, 0x3f, 0x7e, 0xb6, 0xa1, 0x5d, 0x85, 0xea, 0x95, 0x3f, 0xb2, 0x4d, 0x33, 0x2c, 0x6b, 0x18, 0x46, 0x2e, 0x1c, 0xfb, 0xf4, 0x58, 0x8b, 0xc5, 0xbf, 0x44, 0xa0, 0x39, 0x53, 0x6e, 0x57, 0xaf, 0x69, 0x31, 0xd2, 0x7a, 0x6f, 0x6f, 0xdc, 0x39, 0xc5, 0x2f, 0x37, 0x7c, 0xae, 0xc8, 0x94, 0x6c, 0x4e, 0x58, 0x19, 0x68, 0x60, 0x98, 0xbd, 0xb7, 0x63, 0x5b, 0xed, 0xc5, 0x7b, 0x44, 0x44, 0x90, 0x49, 0x76, 0x42, 0x39, 0x61, 0x5d, 0x47, 0xdc, 0x3c, 0xc3, 0x4b, 0x1d, 0xb8, 0xd6, 0x17, 0x24, 0x8e, 0xd6, 0xea, 0xd9, 0x50, 0x88, 0xdb, 0xa6, 0xd4, 0xac, 0x5d, 0x1b, 0x1d, 0x0b, 0xb9, 0x28, 0x28, 0xe6, 0x68, 0xd4, 0x3a, 0xd6, 0x90, 0xa4, 0x79, 0x5a, 0x20, 0x0f, 0x07, 0xb9, 0x46, 0xc2, 0xe6, 0xeb, 0x3b, 0x8b, 0x4b, 0xa2, 0xa2, 0x21, 0x35, 0xbd, 0xf5, 0x34, 0x6e, 0xb0, 0xb9, 0x70, 0x4a, 0xae, 0x80, 0x60, 0xc0, 0x9c, 0xe6, 0x59, 0xbf, 0x24, 0x0d, 0xe2, 0xd2, 0x69, 0x59, 0x39, 0x7b, 0x2d, 0x5b, 0xeb, 0xd8, 0x72, 0x33, 0xa8, 0x6a, 0xea, 0xdd, 0xbb, 0x29, 0xb7, 0x2e, 0xd7, 0x35, 0x4d, 0x6d, 0x25, 0xc0, 0x86, 0xda, 0xb7, 0x16, 0x28, 0x8d, 0xdd, 0xd5, 0x73, 0x6b, 0xf1, 0xb6, 0x87, 0x3e, 0x9b, 0x85, 0x9a, 0x11, 0xb0, 0x1e, 0x62, 0x63, 0x10, 0x8c, 0x34, 0xe5, 0x3f, 0xb3, 0x99, 0xb4, 0x9f, 0x07, 0x80, 0x64, 0x6c, 0x49, 0x3d, 0x3d, 0xf8, 0x50, 0xf6, 0x7b, 0xc6, 0x43, 0x36, 0x7c, 0x74, 0xe3, 0xcd, 0x25, 0xa3, 0xbd, 0x29, 0xf8, 0x46, 0x59, 0xd1, 0x8f, 0xfe, 0xd2, 0xb9, 0x7f, 0x80, 0x69, 0x87, 0xdc, 0x1c, 0xe9, 0x13, 0xe2, 0xe3, 0x07, 0xa5, 0x6d, 0x09, 0x12, 0xce, 0x50, 0x7e, 0x87, 0xd6, 0xba, 0xde, 0x79, 0x16, 0x2f, 0x25, 0xd5, 0xcd, 0xc2, 0xde, 0xe6, 0x2a, 0x9b, 0xac, 0xbd, 0xb0, 0x03, 0x5e, 0x48, 0x2a, 0x62, 0xfd, 0x43, 0xca, 0x07, 0x7d, 0x58, 0x35, 0x9a, 0xf9, 0x0a, 0x90, 0x82, 0xb1, 0x48, 0xd4, 0xc2, 0x28, 0xcb, 0xf8, 0xf6, 0x1d, 0x84, 0xbc, 0x7c, 0x3b, 0xdd, 0x20, 0xf9, 0xbc, 0x7d, 0xd7, 0xc8, 0x5d, 0xce, 0x3e, 0xf6, 0x5b, 0x8d, 0xa4, 0xdd, 0x57, 0x08, 0xed, 0x90, 0x62, 0xe4, 0xe3, 0x8d, 0x7f, 0x88, 0xba, 0x8a, 0x6d, 0x88, 0x94, 0x35, 0x7a, 0xa9, 0x13, 0x45, 0xb6, 0x65, 0x44, 0x41, 0xcd, 0xfa, 0x73, 0xc7, 0x4e, 0x6e, 0xaa, 0x43, 0x40, 0x22, 0xf1, 0x5c, 0x29, 0xc8, 0x95, 0x92, 0x0e, 0xdf, 0x0c, 0xbe, 0xf4, 0x0e, 0x2a, 0x81, 0xea, 0x04, 0xd7, 0xd1, 0xbe, 0x9f, 0x6e, 0x21, 0xc9, 0x99, 0x6d, 0xef, 0x78, 0xfb, 0x2a, 0x95, 0x8c, 0xff, 0xb5, 0xc3, 0x35, 0x38, 0x13, 0xf4, 0x6d, 0x25, 0x7b, 0xb7, 0x4c, 0x7b, 0x4a, 0xbe, 0x3f, 0xd4, 0xcf, 0xeb, 0x3d, 0x8e, 0x27, 0xe0, 0x84, 0x05, 0xc2, 0xc4, 0x6b, 0x79, 0x5b, 0x45, 0x4c, 0x46, 0x67, 0x05, 0xa6, 0x38, 0x58, 0xfd, 0xe6, 0x35, 0x29, 0x7f, 0x84, 0x75, 0x14, 0xed, 0x62, 0x05, 0x72, 0x46, 0x8d, 0x5a, 0x13, 0x16, 0xe3, 0x18, 0x9f, 0x82, 0x30, 0x71, 0xc5, 0x65, 0xc1, 0xa4, 0x47, 0xc7, 0x91, 0x92, 0x33, 0x19, 0x79, 0x6c, 0xcd, 0x03, 0x1e, 0xe5, 0xfe, 0xb4, 0x6f, 0x85, 0x4c, 0xf8, 0xfd, 0xee, 0xad, 0x84, 0x80, 0x33, 0xc4, 0x48, 0x44, 0xae, 0x48, 0xae, 0x63, 0xcb, 0xa9, 0x0f, 0xff, 0xd4, 0x47, 0xd9, 0xdb, 0xac, 0xb0, 0x14, 0x0f, 0xfb, 0xcf, 0x5d, 0xb8, 0x48, 0xfa, 0x7a, 0xd1, 0xf9, 0x41, 0x0b, 0xa8, 0x3b, 0x85, 0x19, 0x95, 0x41, 0x16, 0x7a, 0xdc, 0x70, 0xf7, 0x4c, 0xed, 0x4e, 0x0b, 0xf1, 0x72, 0x5c, 0xb0, 0x67, 0xc2, 0x28, 0xa4, 0xd6, 0xc0, 0x44, 0x2f, 0xac, 0x3f, 0xf0, 0xf1, 0xb3, 0x5d, 0x96, 0xc9, 0xdc, 0x89, 0x7e, 0xb5, 0xcb, 0x0a, 0x4a, 0xae, 0x4c, 0x4d, 0x17, 0xcc, 0x3b, 0x5b, 0x65, 0x3f, 0x95, 0x15, 0x24, 0xec, 0xaf, 0x40, 0x75, 0xb3, 0xd0, 0x72, 0x09, 0xb2, 0xa6, 0x55, 0x30, 0x0c, 0x5d, 0xf7, 0xff, 0x00, 0x43, 0xff, 0x19, 0x5e, 0x44, 0x04, 0x07, 0xa8, 0xb3, 0x64, 0x36, 0x63, 0x1d, 0x59, 0xe9, 0x6d, 0x20, 0x26, 0xd9, 0x61, 0x8b, 0x8a, 0x40, 0x4e, 0x33, 0xf9, 0x7b, 0x5f, 0x4b, 0x2c, 0x9f, 0x25, 0x2a, 0x04, 0x9c, 0xad, 0xcd, 0xea, 0xbd, 0x93, 0x7a, 0x11, 0xbf, 0xb5, 0x5e, 0x56, 0xb5, 0x3f, 0x01, 0x90, 0x74, 0x26, 0xe8, 0x99, 0x55, 0x62, 0xcd, 0x66, 0x69, 0x64, 0x39, 0xe7, 0x80, 0xa6, 0x6d, 0x92, 0x98, 0x52, 0xe2, 0xcc, 0x9a, 0x54, 0xd5, 0x9b, 0x78, 0x64, 0x9e, 0x45, 0x0b, 0xcd, 0xd1, 0xd5, 0x4c, 0x06, 0x55, 0xa7, 0xcb, 0x5f, 0x0a, 0x6f, 0x73, 0x54, 0x9e, 0x5d, 0x05, 0x5a, 0x9a, 0x58, 0xd6, 0x1b, 0xe3, 0x6d, 0xc3, 0x86, 0x2b, 0xec, 0x5a, 0x30, 0x07, 0xd4, 0xb1, 0xd9, 0xb0, 0x7c, 0x6c, 0x19, 0xa4, 0xaf, 0xe9, 0x8a, 0xa6, 0x99, 0x95, 0xfb, 0xd6, 0x36, 0xd6, 0x6b, 0xe6, 0x38, 0x24, 0xe9, 0x96, 0x80, 0x21, 0x99, 0x0b, 0xa5, 0xe4, 0x71, 0xa6, 0x25, 0xb1, 0x77, 0x66, 0x43, 0xe4, 0x87, 0x0c, 0xa0, 0xbb, 0xfd, 0x7e, 0x1d, 0x4e, 0xb9, 0x20, 0x8d, 0xe2, 0x58, 0x27, 0x49, 0x52, 0xb1, 0x22, 0xb0, 0x63, 0xd0, 0x24, 0x18, 0x16, 0x10, 0x6b, 0xcf, 0x5b, 0x0f, 0x5b, 0x19, 0xc5, 0x92, 0x27, 0x5d, 0x11, 0xa6, 0x96, 0xe9, 0xa3, 0x80, 0x9e, 0xf3, 0x57, 0xf2, 0xdb, 0xd5, 0x35, 0x7a, 0x13, 0x98, 0xf9, 0x43, 0x4c, 0x4d, 0x77, 0x8b, 0x2c, 0xdb, 0x41, 0x5d, 0x9d, 0x23, 0x28, 0x8b, 0xf2, 0xbd, 0x7d, 0x45, 0x14, 0x9f, 0x1c, 0xda, 0x59, 0x6d, 0xaa, 0x60, 0x20, 0x5c, 0xa6, 0x64, 0x59, 0xcb, 0x1a, 0xdc, 0x7b, 0x01, 0x12, 0xf3, 0xa3, 0xc8, 0x38, 0x72, 0xde, 0xbb, 0x27, 0xd9, 0xd1, 0x8c, 0x58, 0x71, 0x43, 0x94, 0xc4, 0xe2, 0x3b, 0x71, 0x94, 0x1d, 0xba, 0x90, 0x83, 0x9d, 0x4a, 0xcd, 0xbe, 0x5b, 0xce, 0xa2, 0x74, 0xee, 0x6c, 0x87, 0x5c, 0xb1, 0x74, 0x6c, 0xe2, 0xf6, 0x8a, 0x87, 0xbd, 0x91, 0x92, 0xd2, 0x2c, 0x48, 0x13, 0xed, 0x12, 0xf6, 0x12, 0x57, 0xac, 0x6b, 0x9d, 0x17, 0xa3, 0x54, 0x85, 0x54, 0xae, 0xb7, 0x09, 0x7f, 0x0c, 0xd5, 0x58, 0xf8, 0x7a, 0x73, 0x45, 0x64, 0x8b, 0xe4, 0x93, 0x16, 0x67, 0x4f, 0x95, 0x40, 0x63, 0x0e, 0x32, 0xcc, 0x06, 0x27, 0x6d, 0x16, 0x4b, 0x2e, 0x06, 0xef, 0x58, 0x1b, 0xdf, 0x8a, 0xe8, 0xe8, 0x28, 0x52, 0x18, 0xf2, 0x93, 0xc7, 0x66, 0x7b, 0xfd, 0xd3, 0x0d, 0xd0, 0x59, 0x3e, 0x0d, 0xdc, 0xcb, 0x3d, 0x79, 0x3e, 0x3d, 0x2d, 0xfa, 0x24, 0xd9, 0xfe, 0x8b, 0x7c, 0x36, 0xf6, 0xd9, 0x4b, 0x26, 0xa4, 0xe5, 0x29, 0x65, 0x4d, 0xb4, 0x3c, 0xac, 0x2d, 0x3b, 0x37, 0xd8, 0x30, 0xd7, 0x2a, 0x30, 0x2e, 0xf4, 0x30, 0xee, 0xaf, 0xbb, 0x45, 0xaa, 0xc2, 0xe4, 0xd0, 0x3e, 0x85, 0x08, 0xb3, 0x38, 0x49, 0x12, 0xe3, 0x11, 0x85, 0xa3, 0xdf, 0x9c, 0xfe, 0x91, 0x82, 0x73, 0xd2, 0xcf, 0x16, 0x59, 0x54, 0x67, 0x93, 0xcb, 0xe5, 0x5b, 0x75, 0x4b, 0x2c, 0x86, 0xa0, 0x05, 0x6d, 0xe4, 0x27, 0xe7, 0xd3, 0xd0, 0x00, 0x52, 0xf2, 0x21, 0xf2, 0xf3, 0xd4, 0x32, 0x47, 0x6d, 0x53, 0x44, 0x12, 0xe1, 0x9b, 0xc7, 0x0f, 0x7f, 0x0e, 0x6c, 0x6c, 0xb0, 0xe6, 0x18, 0x0b, 0x83, 0x58, 0x1f, 0x05, 0x61, 0x06, 0x58, 0x72, 0x45, 0x0a, 0xe1, 0xd6, 0xf5, 0xf1, 0xc6, 0xad, 0xed, 0x58, 0xcb, 0xe5, 0x09, 0x21, 0x0d, 0x60, 0x1a, 0x59, 0x42, 0xb3, 0xf2, 0x4b, 0xc2, 0xc3, 0x90, 0x65, 0xb1, 0xfc, 0x77, 0xdb, 0xe3, 0x40, 0x6d, 0x3a, 0x5b, 0xc1, 0x0e, 0x49, 0x79, 0x6f, 0xfe, 0xf9, 0x3d, 0xa9, 0xb3, 0x38, 0x36, 0x59, 0x11, 0xec, 0xf7, 0xac, 0x4e, 0x6a, 0x32, 0x0c, 0xe8, 0x25, 0xd2, 0x11, 0xa0, 0x7e, 0xb2, 0x84, 0xb1, 0xdf, 0xcb, 0xbd, 0xcc, 0x87, 0xfc, 0xde, 0x8f, 0xc1, 0xb6, 0xcd, 0x69, 0x68, 0x43, 0xdd, 0x3c, 0xe1, 0x6a, 0x89, 0x24, 0x07, 0x9c, 0xc2, 0x2f, 0x9e, 0x03, 0x9c, 0xf5, 0x51, 0x06, 0x6f, 0x59, 0x4e, 0x30, 0xa5, 0xe3, 0x09, 0x82, 0xde, 0x74, 0xb2, 0x96, 0x13, 0x55, 0x6a, 0x38, 0xe6, 0xd6, 0x44, 0x3a, 0x52, 0xf9, 0x99, 0xb4, 0x51, 0xe8, 0x4b, 0x04, 0x4a, 0x34, 0xe6, 0x27, 0x41, 0x16, 0xcd, 0x4e, 0x2d, 0x28, 0xdf, 0xc3, 0xe6, 0x63, 0x3f, 0x83, 0x91, 0x47, 0x2d, 0x20, 0x80, 0x81, 0x16, 0xcf, 0xe1, 0x76, 0x4c, 0xb5, 0xbc, 0x7d, 0x1a, 0xdd, 0x1b, 0xbf, 0x08, 0x60, 0x53, 0xf8, 0x97, 0xab, 0xd8, 0x98, 0xcf, 0xfb, 0x02, 0xf9, 0x50, 0x64, 0xb1, 0xb4, 0xd5, 0x06, 0xcd, 0x40, 0x4c, 0x50, 0xe7, 0x67, 0x00, 0x5d, 0x99, 0x3a, 0x6d, 0xd9, 0x62, 0x9a, 0xb3, 0x00, 0x10, 0x76, 0xdc, 0x94, 0x01, 0x4f, 0x54, 0xeb, 0xd2, 0xb3, 0x42, 0x65, 0x32, 0xb1, 0xb6, 0xfc, 0x00, 0x7c, 0xd0, 0xac, 0xe6, 0xbd, 0x49, 0xed, 0x11, 0x53, 0x19, 0x85, 0xca, 0x71, 0x17, 0x59, 0xf5, 0x6b, 0xbe, 0x18, 0xd4, 0x92, 0x9a, 0xd5, 0x89, 0xf6, 0xe4, 0xf9, 0x27, 0xfd, 0x5d, 0x17, 0x64, 0x01, 0x7e, 0x95, 0x36, 0x8e, 0x6d, 0x2c, 0xe3, 0xdb, 0x6a, 0xb4, 0x4d, 0x91, 0x98, 0xc4, 0x96, 0x14, 0x55, 0x0a, 0x54, 0xc6, 0x83, 0x83, 0xf1, 0x09, 0x7a, 0xd2, 0xf5, 0xf5, 0x88, 0x6f, 0x54, 0xff, 0x28, 0x7e, 0x03, 0x11, 0x69, 0x64, 0x5f, 0xcb, 0x1c, 0x7d, 0x3c, 0xeb, 0xd0, 0x99, 0x99, 0x13, 0xac, 0x02, 0x55, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x04, 0x81, 0x01, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x04, 0x81, 0x01, 0x00, 0x01, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x04, 0x00, 0xf5, 0xb1, 0x07, 0x53, 0x03, 0xf8, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x04, 0x81, 0x01, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + +constexpr std::array AVC_FLUSH_EXPECTED_AU_SHA1 = { 0xe6, 0xf9, 0x41, 0x5f, 0x85, 0x80, 0x14, 0x9d, 0xf1, 0x24, 0x8a, 0xf5, 0x4d, 0x4b, 0x9f, 0x22, 0xd6, 0x60, 0x46, 0x0d }; diff --git a/rpcs3/tests/test_rsx_cfg.cpp b/rpcs3/tests/test_rsx_cfg.cpp index ded749fd24..81027e61db 100644 --- a/rpcs3/tests/test_rsx_cfg.cpp +++ b/rpcs3/tests/test_rsx_cfg.cpp @@ -228,4 +228,52 @@ namespace rsx::assembler ASSERT_EQ(graph.blocks.size(), 1); EXPECT_EQ(graph.blocks.front().instructions.size(), 1); } + + TEST(CFG, FpToCFG_EmptyIFWithELSE) + { + auto ir = FPIR::from_source( + "IF.LT;" // Empty branch + "ELSE;" // With real ELSE + " MOV R1, R2;" // Content. Should execute if branch cond fails (IF.GE) + "ENDIF;" + "MOV R0, R1;" // False merge block. + ); + + RSXFragmentProgram program{}; + auto bytecode = ir.compile(); + program.data = bytecode.data(); + + FlowGraph graph = deconstruct_fragment_program(program); + + ASSERT_EQ(graph.blocks.size(), 3); + ASSERT_EQ(graph.blocks.front().instructions.size(), 1); + EXPECT_EQ(SRC0{ .HEX = graph.blocks.front().instructions[0].bytecode[1] }.exec_if_lt, 0); + EXPECT_EQ(SRC0{ .HEX = graph.blocks.front().instructions[0].bytecode[1] }.exec_if_gr, 1); + EXPECT_EQ(SRC0{ .HEX = graph.blocks.front().instructions[0].bytecode[1] }.exec_if_eq, 1); + } + + TEST(CFG, FpToCFG_SkipOverImmediateOperand) + { + auto ir = FPIR::from_source( + "MOV R0, #{ 0.25 };" // NOP with real dst and one literal input + "MOV R0, R1;" // False merge block. + ); + + RSXFragmentProgram program{}; + auto bytecode = ir.compile(); + program.data = bytecode.data(); + + ASSERT_EQ(bytecode.size(), 12); + + // Patch the first instruction to be a NOP with a literal as input + const u32 decoded_d0 = ((bytecode[0] & 0xFF00FF00u) >> 16u) | ((bytecode[0] & 0x00FF00FFu) << 16u); + OPDEST d0{ .HEX = decoded_d0 }; + d0.opcode = RSX_FP_OPCODE_NOP; + bytecode[0] = ((d0.HEX & 0xFF00FF00u) >> 16u) | ((d0.HEX & 0x00FF00FFu) << 16u); + + FlowGraph graph = deconstruct_fragment_program(program); + + ASSERT_EQ(graph.blocks.size(), 1); + ASSERT_EQ(graph.blocks.front().instructions.size(), 1); + } } diff --git a/rpcs3/util/atomic.cpp b/rpcs3/util/atomic.cpp index 595162cd04..59f0eebe49 100644 --- a/rpcs3/util/atomic.cpp +++ b/rpcs3/util/atomic.cpp @@ -49,6 +49,7 @@ static bool has_waitv() #include #include #include +#include #include "asm.hpp" #include "endian.hpp" diff --git a/rpcs3/util/logs.cpp b/rpcs3/util/logs.cpp index 0276f90e0b..f0afc95bac 100644 --- a/rpcs3/util/logs.cpp +++ b/rpcs3/util/logs.cpp @@ -202,7 +202,7 @@ namespace logs for (auto&& pair : get_logger()->channels) { - pair.second->enabled.release(level::notice); + pair.second->enabled.release(level::_default); } } @@ -271,18 +271,17 @@ namespace logs } } - std::vector get_channels() + std::set get_channels() { - std::vector result; + std::set result; std::lock_guard lock(g_mutex); for (auto&& p : get_logger()->channels) { - // Copy names removing duplicates - if (result.empty() || result.back() != p.first) + if (!p.first.empty()) { - result.push_back(p.first); + result.insert(p.first); } } diff --git a/rpcs3/util/logs.hpp b/rpcs3/util/logs.hpp index 52d163ed43..1b75bd6499 100644 --- a/rpcs3/util/logs.hpp +++ b/rpcs3/util/logs.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include "util/atomic.hpp" #include "Utilities/StrFmt.h" @@ -20,6 +20,8 @@ namespace logs warning = 5, notice = 6, trace = 7, // Lowest severity (usually disabled) + + _default = notice }; struct channel; @@ -163,7 +165,7 @@ namespace logs registerer(channel& _ch); }; - // Log level control: set all channels to level::notice + // Log level control: set all channels to default level::notice void reset(); // Log level control: set all channels to level::always @@ -179,7 +181,7 @@ namespace logs void set_channel_levels(const std::map>& map); // Get all registered log channels - std::vector get_channels(); + std::set get_channels(); // Helper: no additional name specified consteval const char* make_channel_name(const char* name, const char* alt = nullptr) diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index eece173cad..ab91faac6f 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -736,6 +736,39 @@ std::string utils::get_firmware_version() return {}; } +std::pair utils::get_memory_usage() +{ +#ifdef _WIN32 + ::MEMORYSTATUSEX status{}; + status.dwLength = sizeof(status); + ::GlobalMemoryStatusEx(&status); + return { status.ullTotalPhys, status.ullTotalPhys - status.ullAvailPhys }; +#elif __linux__ + std::ifstream proc("/proc/meminfo"); + std::string line; + uint64_t mem_total = get_total_memory(); + uint64_t mem_available = 0; + + while (std::getline(proc, line)) + { + if (line.rfind("MemTotal:", 0) == 0 && line.find("kB") != std::string::npos) + { + mem_total = std::stoull(line.substr(line.find_first_of("0123456789"))) * 1024; + } + else if (line.rfind("MemAvailable:", 0) == 0 && line.find("kB") != std::string::npos) + { + mem_available = std::stoull(line.substr(line.find_first_of("0123456789"))) * 1024; + break; + } + } + + return { mem_total, mem_total - mem_available }; +#else + // TODO + return { get_total_memory(), 0 }; +#endif +} + utils::OS_version utils::get_OS_version() { OS_version res {}; @@ -1087,7 +1120,7 @@ static const bool s_tsc_freq_evaluated = []() -> bool u64 utils::get_total_memory() { #ifdef _WIN32 - ::MEMORYSTATUSEX memInfo; + ::MEMORYSTATUSEX memInfo{}; memInfo.dwLength = sizeof(memInfo); ::GlobalMemoryStatusEx(&memInfo); return memInfo.ullTotalPhys; diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index d9bd0c6660..491c54b088 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -71,6 +71,8 @@ namespace utils std::string get_firmware_version(); + std::pair get_memory_usage(); + struct OS_version { std::string type; diff --git a/rpcs3/util/video_source.h b/rpcs3/util/video_source.h index 9449ed238e..a18b564209 100644 --- a/rpcs3/util/video_source.h +++ b/rpcs3/util/video_source.h @@ -9,6 +9,7 @@ public: video_source() {}; virtual ~video_source() {}; virtual void set_video_path(const std::string& video_path) = 0; + virtual void set_audio_path(const std::string& audio_path) = 0; virtual void set_active(bool active) = 0; virtual bool get_active() const = 0; virtual bool has_new() const = 0; diff --git a/rpcs3/util/vm_native.cpp b/rpcs3/util/vm_native.cpp index ed1e0060c9..5821300fc3 100644 --- a/rpcs3/util/vm_native.cpp +++ b/rpcs3/util/vm_native.cpp @@ -253,7 +253,11 @@ namespace utils #ifdef __APPLE__ #ifdef ARCH_ARM64 - auto ptr = ::mmap(use_addr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_JIT | c_map_noreserve, -1, 0); + // Memory mapping regions will be replaced by file-backed MAP_FIXED mappings + // (via shm::map), which is incompatible with MAP_JIT. Only use MAP_JIT for + // non-mapping regions that need JIT executable support. + const int jit_flag = is_memory_mapping ? 0 : MAP_JIT; + auto ptr = ::mmap(use_addr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | jit_flag | c_map_noreserve, -1, 0); #else auto ptr = ::mmap(use_addr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_JIT | c_map_noreserve, -1, 0); #endif