diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000000..5309c5944e2
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,167 @@
+# This is a basic workflow that is manually triggered
+
+name: Build GroovyMAME on a new tag
+
+# Only build when a tag looks like gm0???sr????
+# It will, in the end, converted to the GM version + SR version
+# eg: gm0223sr017q
+on:
+  push:
+    tags:
+      - gm0*sr*
+
+jobs:
+# GM build jobs : on linux/mingw (corssbuild for windows), on windows/msys2 and linux
+#  mingw-windows-build:
+    # Cross ompilation job, runs on a Ubuntu Linux runner and builds windows binaries
+    # Disabled for now because of https://github.com/mamedev/mame/issues/7240
+    # Take care if you enable this job again, others must have changed since
+#    runs-on: ubuntu-latest
+#    steps:
+#    - uses: actions/checkout@v2
+#    - name: Setup environment
+#      run: |
+#        sudo apt update
+#        sudo apt-get install mingw-w64 wine64 p7zip
+#        sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
+#    - name: Build GroovyMAME
+#      run: |
+#        make -j$(nproc) TARGET=mame TOOLS=1 SEPARATE_BIN=1 PTR64=1 OPTIMIZE=3 SYMBOLS=0 SYMLEVEL=1 REGENIE=1 TARGETOS=windows OVERRIDE_CC=x86_64-w64-mingw32-gcc OVERRIDE_CXX=x86_64-w64-mingw32-g++ OVERRIDE_LD=x86_64-w64-mingw32-ld MINGW64=/usr
+#        make -f dist.mak PTR64=1 TARGETOS=windows
+#    - name: Create MAME basic configuration
+#      run: |
+#        cd build/release/x64/Release/mame
+#        wine64 mame64.exe -createconfig
+#    - name: Create Release Asset
+#      run: |
+#        cd build/release/x64/Release/mame
+#        7z a groovymame-mingw-win32-64bits.7z *
+#    - name: Upload artifact
+#      uses: actions/upload-artifact@v2
+#      with:
+#        name: binaries-win32-mingw
+#        path: build/release/x64/Release/mame/groovymame-mingw-win32-64bits.7z
+
+  msys-windows-build:
+    # Windows build in a MSYS2 environment on a Windows runner
+    runs-on: windows-latest
+    defaults:
+      run:
+        shell: msys2 {0}
+    steps:
+    - name: Setup MSYS2
+      uses: msys2/setup-msys2@v2
+      with:
+        update: true
+        install: |
+              git make mingw-w64-x86_64-gcc mingw-w64-x86_64-python mingw-w64-x86_64-lld mingw-w64-x86_64-llvm mingw-w64-x86_64-libc++ p7zip mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_ttf
+    - uses: actions/checkout@v4
+    - name: Build GroovyMAME
+      run: |
+        export MINGW64=/mingw64
+        export MINGW32=
+        export ARCHOPTS=-fuse-ld=lld
+        export OVERRIDE_AR=llvm-ar
+        make -j$(nproc) TARGET=mame TOOLS=1 SEPARATE_BIN=1 PTR64=1 OPTIMIZE=3 SYMBOLS=0 SYMLEVEL=1 REGENIE=1
+        make -f dist.mak PTR64=1
+        cp -rf artwork bgfx hlsl plugins samples build/release/x64/Release/mame/
+    - name: Create MAME basic configuration
+      run: |
+        cd build/release/x64/Release/mame
+        ./mame.exe -createconfig
+    - name: Create Release Asset
+      run: |
+        tag="${GITHUB_REF#refs/*/}"
+        # Now tag should be in the shape of gm0XXXsrYYYY, ex: gm0223sr017q
+        mamev="${tag:3:3}"
+        srv="${tag: -4}"
+        cd build/release/x64/Release
+        mv mame "groovymame_0${mamev}.${srv}_win-7-8-10"
+        7z a "groovymame_0${mamev}.${srv}_win-7-8-10.7z" "groovymame_0${mamev}.${srv}_win-7-8-10"
+    - name: Upload artifact
+      uses: actions/upload-artifact@v4
+      with:
+        name: binaries-win32-msys
+        path: build/release/x64/Release/groovymame*.7z
+
+  linux-build:
+    # Simple linux build on ubuntu
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Setup environment
+      run: |
+        sudo apt update
+        sudo apt-get install git build-essential python3 libsdl2-dev libsdl2-ttf-dev libfontconfig-dev libdrm-dev p7zip
+        sudo apt-get install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
+    - name: Build GroovyMAME
+      run: |
+        make -j$(nproc) TARGET=mame TOOLS=1 SEPARATE_BIN=1 PTR64=1 OPTIMIZE=3 SYMBOLS=0 SYMLEVEL=1 REGENIE=1
+        make -f dist.mak PTR64=1
+    - name: Create Release Asset
+      run: |
+        tag="${GITHUB_REF#refs/*/}"
+        # Now tag should be in the shape of gm0XXXsrYYYY, ex: gm0223sr017q
+        mamev="${tag:3:3}"
+        srv="${tag: -4}"
+        cd build/release/x64/Release/mame
+        mv mame groovymame
+        tar cvjf "groovymame_0${mamev}.${srv}_linux.tar.bz2" groovymame
+    - name: Upload artifact
+      uses: actions/upload-artifact@v4
+      with:
+        name: binaries-linux-gcc
+        path: build/release/x64/Release/mame/groovymame*.tar.bz2
+
+  make_patch:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+      with:
+        fetch-depth: 0
+    - name: Generate patch
+      run: |
+        tag="${GITHUB_REF#refs/*/}"
+        mamev="${tag:3:3}"
+        git diff mame0${mamev}..HEAD > GroovyMAME${mamev}.patch
+    - name: Upload artifact
+      uses: actions/upload-artifact@v4
+      with:
+        name: GroovyMAME-patch
+        path: GroovyMAME*.patch
+
+  release:
+    runs-on: ubuntu-latest
+    needs: [msys-windows-build, linux-build, make_patch]
+    steps:
+      - name: Prepare data
+        id: prepare_data
+        run: |
+          tag="${GITHUB_REF#refs/*/}"
+          # Now tag should be in the shape of gm0XXXsrYYYY, ex: gm0223sr017q
+          mamev="${tag:3:3}"
+          srv="${tag: -3}"
+          echo "GroovyMAME: 0.$mamev"
+          echo "Switchres : 2.$srv"
+          echo "mame_version=${mamev}" >> $GITHUB_OUTPUT
+          echo "switchres_version=${srv}" >> $GITHUB_OUTPUT
+          echo "current_tag=${tag}" >> $GITHUB_OUTPUT
+      - name: Download Artifacts
+        uses: actions/download-artifact@v4
+      - name: Create Release
+        id: create_release
+        uses: softprops/action-gh-release@v2
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          MAME_VERSION: ${{ steps.prepare_data.outputs.mame_version }}
+          SWITCHRES_VERSION: ${{ steps.prepare_data.outputs.switchres_version }}
+          CURRENT_TAG: ${{ steps.prepare_data.outputs.current_tag }}
+        with:
+          name: GroovyMAME 0.${{ steps.prepare_data.outputs.mame_version }} - Switchres 2.${{ steps.prepare_data.outputs.switchres_version }}
+          draft: true
+          prerelease: false
+          files: |
+            ./binaries-win32-msys/groovymame_*.7z
+            ./binaries-linux-gcc/groovymame_*.tar.bz2
+            ./GroovyMAME-patch/GroovyMAME*.patch
+
diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml
deleted file mode 100644
index 4610644aa1a..00000000000
--- a/.github/workflows/ci-linux.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: CI (Linux)
-
-on:
-  push:
-    paths:
-    - '.github/workflows/**'
-    - '3rdparty/**'
-    - 'hash/**'
-    - 'scripts/**'
-    - 'src/**'
-    - 'COPYING'
-    - 'makefile'
-  pull_request:
-    paths:
-    - '.github/workflows/**'
-    - '3rdparty/**'
-    - 'hash/**'
-    - 'scripts/**'
-    - 'src/**'
-    - 'COPYING'
-    - 'makefile'
-
-permissions:
-  contents: read
-
-jobs:
-  build-linux:
-    strategy:
-      matrix:
-        compiler: [gcc, clang]
-        include:
-          - compiler: gcc
-            cc: gcc
-            cxx: g++
-            archopts: -U_FORTIFY_SOURCE
-            subtarget: tiny
-            executable: mametiny
-          - compiler: clang
-            cc: clang
-            cxx: clang++
-            subtarget: mame
-            executable: mame
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@main
-      with:
-        fetch-depth: 0
-    - name: Install dependencies
-      run: |
-        sudo apt-get update
-        sudo apt-get install -y libsdl2-dev libsdl2-ttf-dev libfontconfig-dev libasound2-dev libxinerama-dev libxi-dev qtbase5-dev qtbase5-dev-tools
-    - name: Install clang
-      if: matrix.compiler == 'clang'
-      run: sudo apt-get install -y clang
-    - name: Build
-      env:
-        OVERRIDE_CC: ${{ matrix.cc }}
-        OVERRIDE_CXX: ${{ matrix.cxx }}
-        ARCHOPTS: ${{ matrix.archopts }}
-        SUBTARGET: ${{ matrix.subtarget }}
-        TOOLS: 1
-      run: make -j3
-    - name: Validate
-      run: ./${{ matrix.executable }} -validate
-    - name: Reconcile driver list
-      run: ./${{ matrix.executable }} -listxml | python scripts/build/makedep.py reconcilelist -l src/mame/${{ matrix.subtarget }}.lst -
-    - name: ORM check
-      run: python scripts/minimaws/minimaws.py load --executable ./${{ matrix.executable }} --softwarepath hash
-    - uses: actions/upload-artifact@main
-      with:
-        name: ${{ matrix.executable }}-linux-${{ matrix.compiler }}-${{ github.sha }}
-        path: |
-          ${{ matrix.executable }}
-          chdman
-          unidasm
-        if-no-files-found: error
diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml
deleted file mode 100644
index ba56880902e..00000000000
--- a/.github/workflows/ci-macos.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: CI (macOS)
-
-on:
-  push:
-    paths:
-    - '.github/workflows/**'
-    - '3rdparty/**'
-    - 'scripts/**'
-    - 'src/**'
-    - 'COPYING'
-    - 'makefile'
-  pull_request:
-    paths:
-    - '.github/workflows/**'
-    - '3rdparty/**'
-    - 'scripts/**'
-    - 'src/**'
-    - 'COPYING'
-    - 'makefile'
-
-permissions:
-  contents: read
-
-jobs:
-  build-macos:
-    runs-on: macOS-latest
-    steps:
-    - uses: actions/checkout@main
-      with:
-        fetch-depth: 0
-    - name: Install dependencies
-      run: brew install python3 sdl2
-    - name: Build
-      env:
-        USE_LIBSDL: 1
-        TOOLS: 1
-      run: make -j2
-    - name: Validate
-      run: ./mame -validate
-    - uses: actions/upload-artifact@main
-      with:
-        name: mame-macos-${{ github.sha }}
-        path: |
-          mame
-          chdman
-          unidasm
-        if-no-files-found: error
diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml
deleted file mode 100644
index 6f24659c323..00000000000
--- a/.github/workflows/ci-windows.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: CI (Windows)
-
-on:
-  push:
-    paths:
-    - '.github/workflows/**'
-    - '3rdparty/**'
-    - 'scripts/**'
-    - 'src/**'
-    - 'COPYING'
-    - 'makefile'
-  pull_request:
-    paths:
-    - '.github/workflows/**'
-    - '3rdparty/**'
-    - 'scripts/**'
-    - 'src/**'
-    - 'COPYING'
-    - 'makefile'
-
-permissions:
-  contents: read
-
-jobs:
-  build-windows:
-    strategy:
-      matrix:
-        compiler: [gcc-x64, clang-x64, clang-arm64]
-        include:
-          - compiler: gcc-x64
-            os: windows-latest
-            msys: MINGW64
-            slug: mingw-w64-x86_64
-            cc: gcc
-            cxx: g++
-            subtarget: mame
-            executable: mame
-          - compiler: clang-x64
-            os: windows-latest
-            msys: MINGW64
-            slug: mingw-w64-x86_64
-            cc: clang
-            cxx: clang++
-            subtarget: tiny
-            executable: mametiny
-          - compiler: clang-arm64
-            os: windows-11-arm
-            msys: CLANGARM64
-            slug: mingw-w64-clang-aarch64
-            extrapkg: mingw-w64-clang-aarch64-gcc-compat
-            cc: clang
-            cxx: clang++
-            subtarget: mame
-            executable: mame
-    runs-on: ${{ matrix.os }}
-    defaults:
-      run:
-        shell: msys2 {0}
-    steps:
-    - uses: msys2/setup-msys2@v2
-      with:
-        msystem: ${{ matrix.msys }}
-        install: git make ${{ matrix.slug }}-${{ matrix.cc }} ${{ matrix.slug }}-python ${{ matrix.slug }}-lld ${{ matrix.slug }}-llvm ${{ matrix.slug }}-libc++ ${{ matrix.extrapkg }}
-    - uses: actions/checkout@main
-      with:
-        fetch-depth: 0
-    - name: Build
-      env:
-        OVERRIDE_AR: "llvm-ar"
-        OVERRIDE_CC: ${{ matrix.cc }}
-        OVERRIDE_CXX: ${{ matrix.cxx }}
-        ARCHOPTS: "-fuse-ld=lld"
-        SUBTARGET: ${{ matrix.subtarget }}
-        TOOLS: 1
-      run: make -j3
-    - name: Validate
-      run: ./${{ matrix.executable }}.exe -validate
-    - uses: actions/upload-artifact@main
-      with:
-        name: ${{ matrix.executable }}-windows-${{ matrix.compiler }}-${{ github.sha }}
-        path: |
-          ${{ matrix.executable }}.exe
-          chdman.exe
-          unidasm.exe
-        if-no-files-found: error
diff --git a/.gitignore b/.gitignore
index f5231d17f98..c1ba2c658b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,5 @@ regtests/chdman/temp
 regtests/jedutil/output
 /CMakeLists.txt
 /samples
+/plugins/inputlag
+
diff --git a/3rdparty/lz4/LICENSE b/3rdparty/lz4/LICENSE
new file mode 100644
index 00000000000..488491695a6
--- /dev/null
+++ b/3rdparty/lz4/LICENSE
@@ -0,0 +1,24 @@
+LZ4 Library
+Copyright (c) 2011-2020, Yann Collet
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/3rdparty/lz4/Makefile b/3rdparty/lz4/Makefile
new file mode 100644
index 00000000000..ecbc1c9ce15
--- /dev/null
+++ b/3rdparty/lz4/Makefile
@@ -0,0 +1,231 @@
+# ################################################################
+# LZ4 library - Makefile
+# Copyright (C) Yann Collet 2011-2020
+# All rights reserved.
+#
+# This Makefile is validated for Linux, macOS, *BSD, Hurd, Solaris, MSYS2 targets
+#
+# BSD license
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+#   list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice, this
+#   list of conditions and the following disclaimer in the documentation and/or
+#   other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# You can contact the author at :
+#  - LZ4 source repository : https://github.com/lz4/lz4
+#  - LZ4 forum froup : https://groups.google.com/forum/#!forum/lz4c
+# ################################################################
+SED = sed
+
+# Version numbers
+LIBVER_MAJOR_SCRIPT:=`$(SED) -n '/define LZ4_VERSION_MAJOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ./lz4.h`
+LIBVER_MINOR_SCRIPT:=`$(SED) -n '/define LZ4_VERSION_MINOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ./lz4.h`
+LIBVER_PATCH_SCRIPT:=`$(SED) -n '/define LZ4_VERSION_RELEASE/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ./lz4.h`
+LIBVER_SCRIPT:= $(LIBVER_MAJOR_SCRIPT).$(LIBVER_MINOR_SCRIPT).$(LIBVER_PATCH_SCRIPT)
+LIBVER_MAJOR := $(shell echo $(LIBVER_MAJOR_SCRIPT))
+LIBVER_MINOR := $(shell echo $(LIBVER_MINOR_SCRIPT))
+LIBVER_PATCH := $(shell echo $(LIBVER_PATCH_SCRIPT))
+LIBVER  := $(shell echo $(LIBVER_SCRIPT))
+
+BUILD_SHARED:=yes
+BUILD_STATIC:=yes
+
+CPPFLAGS+= -DXXH_NAMESPACE=LZ4_
+CPPFLAGS+= $(MOREFLAGS)
+CFLAGS  ?= -O3
+DEBUGFLAGS:= -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
+             -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes \
+             -Wundef -Wpointer-arith -Wstrict-aliasing=1
+CFLAGS  += $(DEBUGFLAGS)
+FLAGS    = $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
+
+SRCFILES := $(sort $(wildcard *.c))
+
+include ../Makefile.inc
+
+# OS X linker doesn't support -soname, and use different extension
+# see : https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/DynamicLibraryDesignGuidelines.html
+ifeq ($(TARGET_OS), Darwin)
+	SHARED_EXT = dylib
+	SHARED_EXT_MAJOR = $(LIBVER_MAJOR).$(SHARED_EXT)
+	SHARED_EXT_VER = $(LIBVER).$(SHARED_EXT)
+	SONAME_FLAGS = -install_name $(libdir)/liblz4.$(SHARED_EXT_MAJOR) -compatibility_version $(LIBVER_MAJOR) -current_version $(LIBVER) -dynamiclib
+else
+	SONAME_FLAGS = -Wl,-soname=liblz4.$(SHARED_EXT).$(LIBVER_MAJOR)
+	SHARED_EXT = so
+	SHARED_EXT_MAJOR = $(SHARED_EXT).$(LIBVER_MAJOR)
+	SHARED_EXT_VER = $(SHARED_EXT).$(LIBVER)
+endif
+
+.PHONY: default
+default: lib-release
+
+# silent mode by default; verbose can be triggered by V=1 or VERBOSE=1
+$(V)$(VERBOSE).SILENT:
+
+lib-release: DEBUGFLAGS :=
+lib-release: lib
+
+.PHONY: lib
+lib: liblz4.a liblz4
+
+.PHONY: all
+all: lib
+
+.PHONY: all32
+all32: CFLAGS+=-m32
+all32: all
+
+liblz4.a: $(SRCFILES)
+ifeq ($(BUILD_STATIC),yes)  # can be disabled on command line
+	@echo compiling static library
+	$(COMPILE.c) $^
+	$(AR) rcs $@ *.o
+endif
+
+ifeq ($(WINBASED),yes)
+liblz4-dll.rc: liblz4-dll.rc.in
+	@echo creating library resource
+	$(SED) -e 's|@LIBLZ4@|$(LIBLZ4)|' \
+         -e 's|@LIBVER_MAJOR@|$(LIBVER_MAJOR)|g' \
+         -e 's|@LIBVER_MINOR@|$(LIBVER_MINOR)|g' \
+         -e 's|@LIBVER_PATCH@|$(LIBVER_PATCH)|g' \
+          $< >$@
+
+liblz4-dll.o: liblz4-dll.rc
+	$(WINDRES) -i liblz4-dll.rc -o liblz4-dll.o
+
+$(LIBLZ4): $(SRCFILES) liblz4-dll.o
+ifeq ($(BUILD_SHARED),yes)
+	@echo compiling dynamic library $(LIBVER)
+	$(CC) $(FLAGS) -DLZ4_DLL_EXPORT=1 -shared $^ -o dll/$@.dll -Wl,--out-implib,dll/$(LIBLZ4_EXP)
+endif
+
+else   # not windows
+
+$(LIBLZ4): $(SRCFILES)
+ifeq ($(BUILD_SHARED),yes)
+	@echo compiling dynamic library $(LIBVER)
+	$(CC) $(FLAGS) -shared $^ -fPIC -fvisibility=hidden $(SONAME_FLAGS) -o $@
+	@echo creating versioned links
+	$(LN_SF) $@ liblz4.$(SHARED_EXT_MAJOR)
+	$(LN_SF) $@ liblz4.$(SHARED_EXT)
+endif
+
+endif
+
+.PHONY: liblz4
+liblz4: $(LIBLZ4)
+
+.PHONY: clean
+clean:
+ifeq ($(WINBASED),yes)
+	$(RM) *.rc
+endif
+	$(RM) core *.o liblz4.pc dll/$(LIBLZ4).dll dll/$(LIBLZ4_EXP)
+	$(RM) *.a *.$(SHARED_EXT) *.$(SHARED_EXT_MAJOR) *.$(SHARED_EXT_VER)
+	@echo Cleaning library completed
+
+#-----------------------------------------------------------------------------
+# make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets
+#-----------------------------------------------------------------------------
+ifeq ($(POSIX_ENV),Yes)
+
+.PHONY: listL120
+listL120:  # extract lines >= 120 characters in *.{c,h}, by Takayuki Matsuoka (note : $$, for Makefile compatibility)
+	find . -type f -name '*.c' -o -name '*.h' | while read -r filename; do awk 'length > 120 {print FILENAME "(" FNR "): " $$0}' $$filename; done
+
+DESTDIR     ?=
+# directory variables : GNU conventions prefer lowercase
+# see https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html
+# support both lower and uppercase (BSD), use lower in script
+PREFIX      ?= /usr/local
+prefix      ?= $(PREFIX)
+EXEC_PREFIX ?= $(prefix)
+exec_prefix ?= $(EXEC_PREFIX)
+BINDIR      ?= $(exec_prefix)/bin
+bindir      ?= $(BINDIR)
+LIBDIR      ?= $(exec_prefix)/lib
+libdir      ?= $(LIBDIR)
+INCLUDEDIR  ?= $(prefix)/include
+includedir  ?= $(INCLUDEDIR)
+
+  ifneq (,$(filter $(TARGET_OS),OpenBSD FreeBSD NetBSD DragonFly MidnightBSD))
+PKGCONFIGDIR ?= $(prefix)/libdata/pkgconfig
+  else
+PKGCONFIGDIR ?= $(libdir)/pkgconfig
+  endif
+pkgconfigdir ?= $(PKGCONFIGDIR)
+
+liblz4.pc: liblz4.pc.in Makefile
+	@echo creating pkgconfig
+	$(SED) -e 's|@PREFIX@|$(prefix)|' \
+         -e 's|@LIBDIR@|$(libdir)|' \
+         -e 's|@INCLUDEDIR@|$(includedir)|' \
+         -e 's|@VERSION@|$(LIBVER)|' \
+         -e 's|=${prefix}/|=$${prefix}/|' \
+          $< >$@
+
+install: lib liblz4.pc
+	$(INSTALL_DIR) $(DESTDIR)$(pkgconfigdir)/ $(DESTDIR)$(includedir)/ $(DESTDIR)$(libdir)/ $(DESTDIR)$(bindir)/
+	$(INSTALL_DATA) liblz4.pc $(DESTDIR)$(pkgconfigdir)/
+	@echo Installing libraries in $(DESTDIR)$(libdir)
+  ifeq ($(BUILD_STATIC),yes)
+	$(INSTALL_DATA) liblz4.a $(DESTDIR)$(libdir)/liblz4.a
+	$(INSTALL_DATA) lz4frame_static.h $(DESTDIR)$(includedir)/lz4frame_static.h
+	$(INSTALL_DATA) lz4file.h $(DESTDIR)$(includedir)/lz4file.h
+  endif
+  ifeq ($(BUILD_SHARED),yes)
+# Traditionally, one installs the DLLs in the bin directory as programs
+# search them first in their directory. This allows to not pollute system
+# directories (like c:/windows/system32), nor modify the PATH variable.
+    ifeq ($(WINBASED),yes)
+	$(INSTALL_PROGRAM) dll/$(LIBLZ4).dll $(DESTDIR)$(bindir)
+	$(INSTALL_PROGRAM) dll/$(LIBLZ4_EXP) $(DESTDIR)$(libdir)
+    else
+	$(INSTALL_PROGRAM) liblz4.$(SHARED_EXT_VER) $(DESTDIR)$(libdir)
+	$(LN_SF) liblz4.$(SHARED_EXT_VER) $(DESTDIR)$(libdir)/liblz4.$(SHARED_EXT_MAJOR)
+	$(LN_SF) liblz4.$(SHARED_EXT_VER) $(DESTDIR)$(libdir)/liblz4.$(SHARED_EXT)
+    endif
+  endif
+	@echo Installing headers in $(DESTDIR)$(includedir)
+	$(INSTALL_DATA) lz4.h $(DESTDIR)$(includedir)/lz4.h
+	$(INSTALL_DATA) lz4hc.h $(DESTDIR)$(includedir)/lz4hc.h
+	$(INSTALL_DATA) lz4frame.h $(DESTDIR)$(includedir)/lz4frame.h
+	@echo lz4 libraries installed
+
+uninstall:
+	$(RM) $(DESTDIR)$(pkgconfigdir)/liblz4.pc
+  ifeq (WINBASED,1)
+	$(RM) $(DESTDIR)$(bindir)/$(LIBLZ4).dll
+	$(RM) $(DESTDIR)$(libdir)/$(LIBLZ4_EXP)
+  else
+	$(RM) $(DESTDIR)$(libdir)/liblz4.$(SHARED_EXT)
+	$(RM) $(DESTDIR)$(libdir)/liblz4.$(SHARED_EXT_MAJOR)
+	$(RM) $(DESTDIR)$(libdir)/liblz4.$(SHARED_EXT_VER)
+  endif
+	$(RM) $(DESTDIR)$(libdir)/liblz4.a
+	$(RM) $(DESTDIR)$(includedir)/lz4.h
+	$(RM) $(DESTDIR)$(includedir)/lz4hc.h
+	$(RM) $(DESTDIR)$(includedir)/lz4frame.h
+	$(RM) $(DESTDIR)$(includedir)/lz4frame_static.h
+	$(RM) $(DESTDIR)$(includedir)/lz4file.h
+	@echo lz4 libraries successfully uninstalled
+
+endif
diff --git a/3rdparty/lz4/README.md b/3rdparty/lz4/README.md
new file mode 100644
index 00000000000..a86cd0d19e4
--- /dev/null
+++ b/3rdparty/lz4/README.md
@@ -0,0 +1,187 @@
+LZ4 - Library Files
+================================
+
+The `/lib` directory contains many files, but depending on project's objectives,
+not all of them are required.
+Limited systems may want to reduce the nb of source files to include
+as a way to reduce binary size and dependencies.
+
+Capabilities are added at the "level" granularity, detailed below.
+
+#### Level 1 : Minimal LZ4 build
+
+The minimum required is **`lz4.c`** and **`lz4.h`**,
+which provides the fast compression and decompression algorithms.
+They generate and decode data using the [LZ4 block format].
+
+
+#### Level 2 : High Compression variant
+
+For more compression ratio at the cost of compression speed,
+the High Compression variant called **lz4hc** is available.
+Add files **`lz4hc.c`** and **`lz4hc.h`**.
+This variant also compresses data using the [LZ4 block format],
+and depends on regular `lib/lz4.*` source files.
+
+
+#### Level 3 : Frame support, for interoperability
+
+In order to produce compressed data compatible with `lz4` command line utility,
+it's necessary to use the [official interoperable frame format].
+This format is generated and decoded automatically by the **lz4frame** library.
+Its public API is described in `lib/lz4frame.h`.
+In order to work properly, lz4frame needs all other modules present in `/lib`,
+including, lz4 and lz4hc, and also **xxhash**.
+So it's necessary to also include `xxhash.c` and `xxhash.h`.
+
+
+#### Level 4 : File compression operations
+
+As a helper around file operations,
+the library has been recently extended with `lz4file.c` and `lz4file.h`
+(still considered experimental at the time of this writing).
+These helpers allow opening, reading, writing, and closing files
+using transparent LZ4 compression / decompression.
+As a consequence, using `lz4file` adds a dependency on `<stdio.h>`.
+
+`lz4file` relies on `lz4frame` in order to produce compressed data
+conformant to the [LZ4 Frame format] specification.
+Consequently, to enable this capability,
+it's necessary to include all `*.c` and `*.h` files from `lib/` directory.
+
+
+#### Advanced / Experimental API
+
+Definitions which are not guaranteed to remain stable in future versions,
+are protected behind macros, such as `LZ4_STATIC_LINKING_ONLY`.
+As the name suggests, these definitions should only be invoked
+in the context of static linking ***only***.
+Otherwise, dependent application may fail on API or ABI break in the future.
+The associated symbols are also not exposed by the dynamic library by default.
+Should they be nonetheless needed, it's possible to force their publication
+by using build macros `LZ4_PUBLISH_STATIC_FUNCTIONS`
+and `LZ4F_PUBLISH_STATIC_FUNCTIONS`.
+
+
+#### Build macros
+
+The following build macro can be selected to adjust source code behavior at compilation time :
+
+- `LZ4_FAST_DEC_LOOP` : this triggers a speed optimized decompression loop, more powerful on modern cpus.
+  This loop works great on `x86`, `x64` and `aarch64` cpus, and is automatically enabled for them.
+  It's also possible to enable or disable it manually, by passing `LZ4_FAST_DEC_LOOP=1` or `0` to the preprocessor.
+  For example, with `gcc` : `-DLZ4_FAST_DEC_LOOP=1`,
+  and with `make` : `CPPFLAGS+=-DLZ4_FAST_DEC_LOOP=1 make lz4`.
+
+- `LZ4_DISTANCE_MAX` : control the maximum offset that the compressor will allow.
+  Set to 65535 by default, which is the maximum value supported by lz4 format.
+  Reducing maximum distance will reduce opportunities for LZ4 to find matches,
+  hence will produce a worse compression ratio.
+  Setting a smaller max distance could allow compatibility with specific decoders with limited memory budget.
+  This build macro only influences the compressed output of the compressor.
+
+- `LZ4_DISABLE_DEPRECATE_WARNINGS` : invoking a deprecated function will make the compiler generate a warning.
+  This is meant to invite users to update their source code.
+  Should this be a problem, it's generally possible to make the compiler ignore these warnings,
+  for example with `-Wno-deprecated-declarations` on `gcc`,
+  or `_CRT_SECURE_NO_WARNINGS` for Visual Studio.
+  This build macro offers another project-specific method
+  by defining `LZ4_DISABLE_DEPRECATE_WARNINGS` before including the LZ4 header files.
+
+- `LZ4_FORCE_SW_BITCOUNT` : by default, the compression algorithm tries to determine lengths
+  by using bitcount instructions, generally implemented as fast single instructions in many cpus.
+  In case the target cpus doesn't support it, or compiler intrinsic doesn't work, or feature bad performance,
+  it's possible to use an optimized software path instead.
+  This is achieved by setting this build macros.
+  In most cases, it's not expected to be necessary,
+  but it can be legitimately considered for less common platforms.
+
+- `LZ4_ALIGN_TEST` : alignment test ensures that the memory area
+  passed as argument to become a compression state is suitably aligned.
+  This test can be disabled if it proves flaky, by setting this value to 0.
+
+- `LZ4_USER_MEMORY_FUNCTIONS` : replace calls to `<stdlib.h>`'s `malloc()`, `calloc()` and `free()`
+  by user-defined functions, which must be named `LZ4_malloc()`, `LZ4_calloc()` and `LZ4_free()`.
+  User functions must be available at link time.
+
+- `LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION` :
+  Remove support of dynamic memory allocation.
+  For more details, see description of this macro in `lib/lz4.c`.
+
+- `LZ4_FREESTANDING` : by setting this build macro to 1,
+  LZ4/HC removes dependencies on the C standard library,
+  including allocation functions and `memmove()`, `memcpy()`, and `memset()`.
+  This build macro is designed to help use LZ4/HC in restricted environments
+  (embedded, bootloader, etc).
+  For more details, see description of this macro in `lib/lz4.h`.
+
+- `LZ4_HEAPMODE` : Select how stateless compression functions like `LZ4_compress_default()`
+  allocate memory for their hash table,
+  in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
+
+- `LZ4HC_HEAPMODE` :  Select how stateless HC compression functions like `LZ4_compress_HC()`
+  allocate memory for their workspace:
+  in stack (0), or in heap (1:default).
+  Since workspace is rather large, stack can be inconvenient, hence heap mode is recommended.
+
+- `LZ4F_HEAPMODE` : selects how `LZ4F_compressFrame()` allocates the compression state,
+  either on stack (default, value 0) or using heap memory (value 1).
+
+
+#### Makefile variables
+
+The following `Makefile` variables can be selected to alter the profile of produced binaries :
+- `BUILD_SHARED` : generate `libzstd` dynamic library (enabled by default)
+- `BUILD_STATIC` : generate `libzstd` static library (enabled by default)
+
+
+#### Amalgamation
+
+lz4 source code can be amalgamated into a single file.
+One can combine all source code into `lz4_all.c` by using following command:
+```
+cat lz4.c lz4hc.c lz4frame.c > lz4_all.c
+```
+(`cat` file order is important) then compile `lz4_all.c`.
+All `*.h` files present in `/lib` remain necessary to compile `lz4_all.c`.
+
+
+#### Windows : using MinGW+MSYS to create DLL
+
+DLL can be created using MinGW+MSYS with the `make liblz4` command.
+This command creates `dll\liblz4.dll` and the import library `dll\liblz4.lib`.
+To override the `dlltool` command when cross-compiling on Linux, just set the `DLLTOOL` variable. Example of cross compilation on Linux with mingw-w64 64 bits:
+```
+make BUILD_STATIC=no CC=x86_64-w64-mingw32-gcc DLLTOOL=x86_64-w64-mingw32-dlltool OS=Windows_NT
+```
+The import library is only required with Visual C++.
+The header files `lz4.h`, `lz4hc.h`, `lz4frame.h` and the dynamic library
+`dll\liblz4.dll` are required to compile a project using gcc/MinGW.
+The dynamic library has to be added to linking options.
+It means that if a project that uses LZ4 consists of a single `test-dll.c`
+file it should be linked with `dll\liblz4.dll`. For example:
+```
+    $(CC) $(CFLAGS) -Iinclude/ test-dll.c -o test-dll dll\liblz4.dll
+```
+The compiled executable will require LZ4 DLL which is available at `dll\liblz4.dll`.
+
+
+#### Miscellaneous
+
+Other files present in the directory are not source code. They are :
+
+ - `LICENSE` : contains the BSD license text
+ - `Makefile` : `make` script to compile and install lz4 library (static and dynamic)
+ - `liblz4.pc.in` : for `pkg-config` (used in `make install`)
+ - `README.md` : this file
+
+[official interoperable frame format]: ../doc/lz4_Frame_format.md
+[LZ4 Frame format]: ../doc/lz4_Frame_format.md
+[LZ4 block format]: ../doc/lz4_Block_format.md
+
+
+#### License
+
+All source material within __lib__ directory are BSD 2-Clause licensed.
+See [LICENSE](LICENSE) for details.
+The license is also reminded at the top of each source file.
diff --git a/3rdparty/lz4/liblz4-dll.rc.in b/3rdparty/lz4/liblz4-dll.rc.in
new file mode 100644
index 00000000000..e2d84b6e7a4
--- /dev/null
+++ b/3rdparty/lz4/liblz4-dll.rc.in
@@ -0,0 +1,35 @@
+#include <windows.h>
+
+// DLL version information.
+1 VERSIONINFO
+FILEVERSION       @LIBVER_MAJOR@,@LIBVER_MINOR@,@LIBVER_PATCH@,0
+PRODUCTVERSION    @LIBVER_MAJOR@,@LIBVER_MINOR@,@LIBVER_PATCH@,0
+FILEFLAGSMASK      VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+  FILEFLAGS        VS_FF_DEBUG | VS_FF_PRERELEASE
+#else
+  FILEFLAGS        0
+#endif
+FILEOS             VOS_NT_WINDOWS32
+FILETYPE           VFT_DLL
+FILESUBTYPE        VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904B0"
+        BEGIN
+            VALUE "CompanyName", "Yann Collet"
+            VALUE "FileDescription", "Extremely fast compression"
+            VALUE "FileVersion", "@LIBVER_MAJOR@.@LIBVER_MINOR@.@LIBVER_PATCH@.0"
+            VALUE "InternalName", "@LIBLZ4@"
+            VALUE "LegalCopyright", "Copyright (C) 2013-2020, Yann Collet"
+            VALUE "OriginalFilename", "@LIBLZ4@.dll"
+            VALUE "ProductName", "LZ4"
+            VALUE "ProductVersion", "@LIBVER_MAJOR@.@LIBVER_MINOR@.@LIBVER_PATCH@.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 1200
+    END
+END
diff --git a/3rdparty/lz4/liblz4.pc.in b/3rdparty/lz4/liblz4.pc.in
new file mode 100644
index 00000000000..ed52214f26f
--- /dev/null
+++ b/3rdparty/lz4/liblz4.pc.in
@@ -0,0 +1,14 @@
+#   LZ4 - Fast LZ compression algorithm
+#   Copyright (C) 2011-2020, Yann Collet.
+#   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+prefix=@PREFIX@
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: lz4
+Description: extremely fast lossless compression algorithm library
+URL: http://www.lz4.org/
+Version: @VERSION@
+Libs: -L${libdir} -llz4
+Cflags: -I${includedir}
diff --git a/3rdparty/lz4/lz4.c b/3rdparty/lz4/lz4.c
new file mode 100644
index 00000000000..ff5c8f99ec4
--- /dev/null
+++ b/3rdparty/lz4/lz4.c
@@ -0,0 +1,2751 @@
+/*
+   LZ4 - Fast LZ compression algorithm
+   Copyright (C) 2011-2020, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+    - LZ4 homepage : http://www.lz4.org
+    - LZ4 source repository : https://github.com/lz4/lz4
+*/
+
+/*-************************************
+*  Tuning parameters
+**************************************/
+/*
+ * LZ4_HEAPMODE :
+ * Select how stateless compression functions like `LZ4_compress_default()`
+ * allocate memory for their hash table,
+ * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
+ */
+#ifndef LZ4_HEAPMODE
+#  define LZ4_HEAPMODE 0
+#endif
+
+/*
+ * LZ4_ACCELERATION_DEFAULT :
+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
+ */
+#define LZ4_ACCELERATION_DEFAULT 1
+/*
+ * LZ4_ACCELERATION_MAX :
+ * Any "acceleration" value higher than this threshold
+ * get treated as LZ4_ACCELERATION_MAX instead (fix #876)
+ */
+#define LZ4_ACCELERATION_MAX 65537
+
+
+/*-************************************
+*  CPU Feature Detection
+**************************************/
+/* LZ4_FORCE_MEMORY_ACCESS
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method is portable but violate C standard.
+ *            It can generate buggy code on targets which assembly generation depends on alignment.
+ *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef LZ4_FORCE_MEMORY_ACCESS   /* can be defined externally */
+#  if defined(__GNUC__) && \
+  ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
+  || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+#    define LZ4_FORCE_MEMORY_ACCESS 2
+#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)
+#    define LZ4_FORCE_MEMORY_ACCESS 1
+#  endif
+#endif
+
+/*
+ * LZ4_FORCE_SW_BITCOUNT
+ * Define this parameter if your target system or compiler does not support hardware bit count
+ */
+#if defined(_MSC_VER) && defined(_WIN32_WCE)   /* Visual Studio for WinCE doesn't support Hardware bit count */
+#  undef  LZ4_FORCE_SW_BITCOUNT  /* avoid double def */
+#  define LZ4_FORCE_SW_BITCOUNT
+#endif
+
+
+
+/*-************************************
+*  Dependency
+**************************************/
+/*
+ * LZ4_SRC_INCLUDED:
+ * Amalgamation flag, whether lz4.c is included
+ */
+#ifndef LZ4_SRC_INCLUDED
+#  define LZ4_SRC_INCLUDED 1
+#endif
+
+#ifndef LZ4_STATIC_LINKING_ONLY
+#define LZ4_STATIC_LINKING_ONLY
+#endif
+
+#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS
+#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */
+#endif
+
+#define LZ4_STATIC_LINKING_ONLY  /* LZ4_DISTANCE_MAX */
+#include "lz4.h"
+/* see also "memory routines" below */
+
+
+/*-************************************
+*  Compiler Options
+**************************************/
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Visual Studio 2005+ */
+#  include <intrin.h>               /* only present in VS2005+ */
+#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */
+#  pragma warning(disable : 6237)   /* disable: C6237: conditional expression is always 0 */
+#endif  /* _MSC_VER */
+
+#ifndef LZ4_FORCE_INLINE
+#  ifdef _MSC_VER    /* Visual Studio */
+#    define LZ4_FORCE_INLINE static __forceinline
+#  else
+#    if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#      ifdef __GNUC__
+#        define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
+#      else
+#        define LZ4_FORCE_INLINE static inline
+#      endif
+#    else
+#      define LZ4_FORCE_INLINE static
+#    endif /* __STDC_VERSION__ */
+#  endif  /* _MSC_VER */
+#endif /* LZ4_FORCE_INLINE */
+
+/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE
+ * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,
+ * together with a simple 8-byte copy loop as a fall-back path.
+ * However, this optimization hurts the decompression speed by >30%,
+ * because the execution does not go to the optimized loop
+ * for typical compressible data, and all of the preamble checks
+ * before going to the fall-back path become useless overhead.
+ * This optimization happens only with the -O3 flag, and -O2 generates
+ * a simple 8-byte copy loop.
+ * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8
+ * functions are annotated with __attribute__((optimize("O2"))),
+ * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute
+ * of LZ4_wildCopy8 does not affect the compression speed.
+ */
+#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)
+#  define LZ4_FORCE_O2  __attribute__((optimize("O2")))
+#  undef LZ4_FORCE_INLINE
+#  define LZ4_FORCE_INLINE  static __inline __attribute__((optimize("O2"),always_inline))
+#else
+#  define LZ4_FORCE_O2
+#endif
+
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )
+#else
+#  define expect(expr,value)    (expr)
+#endif
+
+#ifndef likely
+#define likely(expr)     expect((expr) != 0, 1)
+#endif
+#ifndef unlikely
+#define unlikely(expr)   expect((expr) != 0, 0)
+#endif
+
+/* Should the alignment test prove unreliable, for some reason,
+ * it can be disabled by setting LZ4_ALIGN_TEST to 0 */
+#ifndef LZ4_ALIGN_TEST  /* can be externally provided */
+# define LZ4_ALIGN_TEST 1
+#endif
+
+
+/*-************************************
+*  Memory routines
+**************************************/
+
+/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :
+ *  Disable relatively high-level LZ4/HC functions that use dynamic memory
+ *  allocation functions (malloc(), calloc(), free()).
+ *
+ *  Note that this is a compile-time switch. And since it disables
+ *  public/stable LZ4 v1 API functions, we don't recommend using this
+ *  symbol to generate a library for distribution.
+ *
+ *  The following public functions are removed when this symbol is defined.
+ *  - lz4   : LZ4_createStream, LZ4_freeStream,
+ *            LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)
+ *  - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,
+ *            LZ4_createHC (deprecated), LZ4_freeHC  (deprecated)
+ *  - lz4frame, lz4file : All LZ4F_* functions
+ */
+#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+#  define ALLOC(s)          lz4_error_memory_allocation_is_disabled
+#  define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled
+#  define FREEMEM(p)        lz4_error_memory_allocation_is_disabled
+#elif defined(LZ4_USER_MEMORY_FUNCTIONS)
+/* memory management functions can be customized by user project.
+ * Below functions must exist somewhere in the Project
+ * and be available at link time */
+void* LZ4_malloc(size_t s);
+void* LZ4_calloc(size_t n, size_t s);
+void  LZ4_free(void* p);
+# define ALLOC(s)          LZ4_malloc(s)
+# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)
+# define FREEMEM(p)        LZ4_free(p)
+#else
+# include <stdlib.h>   /* malloc, calloc, free */
+# define ALLOC(s)          malloc(s)
+# define ALLOC_AND_ZERO(s) calloc(1,s)
+# define FREEMEM(p)        free(p)
+#endif
+
+#if ! LZ4_FREESTANDING
+#  include <string.h>   /* memset, memcpy */
+#endif
+#if !defined(LZ4_memset)
+#  define LZ4_memset(p,v,s) memset((p),(v),(s))
+#endif
+#define MEM_INIT(p,v,s)   LZ4_memset((p),(v),(s))
+
+
+/*-************************************
+*  Common Constants
+**************************************/
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS   5   /* see ../doc/lz4_Block_format.md#parsing-restrictions */
+#define MFLIMIT       12   /* see ../doc/lz4_Block_format.md#parsing-restrictions */
+#define MATCH_SAFEGUARD_DISTANCE  ((2*WILDCOPYLENGTH) - MINMATCH)   /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */
+#define FASTLOOP_SAFE_DISTANCE 64
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define LZ4_DISTANCE_ABSOLUTE_MAX 65535
+#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX)   /* max supported by LZ4 format */
+#  error "LZ4_DISTANCE_MAX is too big : must be <= 65535"
+#endif
+
+#define ML_BITS  4
+#define ML_MASK  ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+
+/*-************************************
+*  Error detection
+**************************************/
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)
+#  include <assert.h>
+#else
+#  ifndef assert
+#    define assert(condition) ((void)0)
+#  endif
+#endif
+
+#define LZ4_STATIC_ASSERT(c)   { enum { LZ4_static_assert = 1/(int)(!!(c)) }; }   /* use after variable declarations */
+
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)
+#  include <stdio.h>
+   static int g_debuglog_enable = 1;
+#  define DEBUGLOG(l, ...) {                          \
+        if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) {  \
+            fprintf(stderr, __FILE__  " %i: ", __LINE__); \
+            fprintf(stderr, __VA_ARGS__);             \
+            fprintf(stderr, " \n");                   \
+    }   }
+#else
+#  define DEBUGLOG(l, ...) {}    /* disabled */
+#endif
+
+static int LZ4_isAligned(const void* ptr, size_t alignment)
+{
+    return ((size_t)ptr & (alignment -1)) == 0;
+}
+
+
+/*-************************************
+*  Types
+**************************************/
+#include <limits.h>
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# include <stdint.h>
+  typedef  uint8_t BYTE;
+  typedef uint16_t U16;
+  typedef uint32_t U32;
+  typedef  int32_t S32;
+  typedef uint64_t U64;
+  typedef uintptr_t uptrval;
+#else
+# if UINT_MAX != 4294967295UL
+#   error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4"
+# endif
+  typedef unsigned char       BYTE;
+  typedef unsigned short      U16;
+  typedef unsigned int        U32;
+  typedef   signed int        S32;
+  typedef unsigned long long  U64;
+  typedef size_t              uptrval;   /* generally true, except OpenVMS-64 */
+#endif
+
+#if defined(__x86_64__)
+  typedef U64    reg_t;   /* 64-bits in x32 mode */
+#else
+  typedef size_t reg_t;   /* 32-bits in x32 mode */
+#endif
+
+typedef enum {
+    notLimited = 0,
+    limitedOutput = 1,
+    fillOutput = 2
+} limitedOutput_directive;
+
+
+/*-************************************
+*  Reading and writing into memory
+**************************************/
+
+/**
+ * LZ4 relies on memcpy with a constant size being inlined. In freestanding
+ * environments, the compiler can't assume the implementation of memcpy() is
+ * standard compliant, so it can't apply its specialized memcpy() inlining
+ * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze
+ * memcpy() as if it were standard compliant, so it can inline it in freestanding
+ * environments. This is needed when decompressing the Linux Kernel, for example.
+ */
+#if !defined(LZ4_memcpy)
+#  if defined(__GNUC__) && (__GNUC__ >= 4)
+#    define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)
+#  else
+#    define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)
+#  endif
+#endif
+
+#if !defined(LZ4_memmove)
+#  if defined(__GNUC__) && (__GNUC__ >= 4)
+#    define LZ4_memmove __builtin_memmove
+#  else
+#    define LZ4_memmove memmove
+#  endif
+#endif
+
+static unsigned LZ4_isLittleEndian(void)
+{
+    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */
+    return one.c[0];
+}
+
+
+#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
+/* lie to the compiler about data alignment; use with caution */
+
+static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
+static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
+
+static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
+static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
+
+#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef struct { U16 u16; } __attribute__((packed)) LZ4_unalign16;
+typedef struct { U32 u32; } __attribute__((packed)) LZ4_unalign32;
+typedef struct { reg_t uArch; } __attribute__((packed)) LZ4_unalignST;
+
+static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; }
+static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; }
+static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalignST*)ptr)->uArch; }
+
+static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign16*)memPtr)->u16 = value; }
+static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign32*)memPtr)->u32 = value; }
+
+#else  /* safe and portable access using memcpy() */
+
+static U16 LZ4_read16(const void* memPtr)
+{
+    U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static U32 LZ4_read32(const void* memPtr)
+{
+    U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static reg_t LZ4_read_ARCH(const void* memPtr)
+{
+    reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static void LZ4_write16(void* memPtr, U16 value)
+{
+    LZ4_memcpy(memPtr, &value, sizeof(value));
+}
+
+static void LZ4_write32(void* memPtr, U32 value)
+{
+    LZ4_memcpy(memPtr, &value, sizeof(value));
+}
+
+#endif /* LZ4_FORCE_MEMORY_ACCESS */
+
+
+static U16 LZ4_readLE16(const void* memPtr)
+{
+    if (LZ4_isLittleEndian()) {
+        return LZ4_read16(memPtr);
+    } else {
+        const BYTE* p = (const BYTE*)memPtr;
+        return (U16)((U16)p[0] + (p[1]<<8));
+    }
+}
+
+static void LZ4_writeLE16(void* memPtr, U16 value)
+{
+    if (LZ4_isLittleEndian()) {
+        LZ4_write16(memPtr, value);
+    } else {
+        BYTE* p = (BYTE*)memPtr;
+        p[0] = (BYTE) value;
+        p[1] = (BYTE)(value>>8);
+    }
+}
+
+/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
+LZ4_FORCE_INLINE
+void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+    BYTE* d = (BYTE*)dstPtr;
+    const BYTE* s = (const BYTE*)srcPtr;
+    BYTE* const e = (BYTE*)dstEnd;
+
+    do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d<e);
+}
+
+static const unsigned inc32table[8] = {0, 1, 2,  1,  0,  4, 4, 4};
+static const int      dec64table[8] = {0, 0, 0, -1, -4,  1, 2, 3};
+
+
+#ifndef LZ4_FAST_DEC_LOOP
+#  if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64
+#    define LZ4_FAST_DEC_LOOP 1
+#  elif defined(__aarch64__) && defined(__APPLE__)
+#    define LZ4_FAST_DEC_LOOP 1
+#  elif defined(__aarch64__) && !defined(__clang__)
+     /* On non-Apple aarch64, we disable this optimization for clang because
+      * on certain mobile chipsets, performance is reduced with clang. For
+      * more information refer to https://github.com/lz4/lz4/pull/707 */
+#    define LZ4_FAST_DEC_LOOP 1
+#  else
+#    define LZ4_FAST_DEC_LOOP 0
+#  endif
+#endif
+
+#if LZ4_FAST_DEC_LOOP
+
+LZ4_FORCE_INLINE void
+LZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
+{
+    assert(srcPtr + offset == dstPtr);
+    if (offset < 8) {
+        LZ4_write32(dstPtr, 0);   /* silence an msan warning when offset==0 */
+        dstPtr[0] = srcPtr[0];
+        dstPtr[1] = srcPtr[1];
+        dstPtr[2] = srcPtr[2];
+        dstPtr[3] = srcPtr[3];
+        srcPtr += inc32table[offset];
+        LZ4_memcpy(dstPtr+4, srcPtr, 4);
+        srcPtr -= dec64table[offset];
+        dstPtr += 8;
+    } else {
+        LZ4_memcpy(dstPtr, srcPtr, 8);
+        dstPtr += 8;
+        srcPtr += 8;
+    }
+
+    LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);
+}
+
+/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd
+ * this version copies two times 16 bytes (instead of one time 32 bytes)
+ * because it must be compatible with offsets >= 16. */
+LZ4_FORCE_INLINE void
+LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+    BYTE* d = (BYTE*)dstPtr;
+    const BYTE* s = (const BYTE*)srcPtr;
+    BYTE* const e = (BYTE*)dstEnd;
+
+    do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);
+}
+
+/* LZ4_memcpy_using_offset()  presumes :
+ * - dstEnd >= dstPtr + MINMATCH
+ * - there is at least 8 bytes available to write after dstEnd */
+LZ4_FORCE_INLINE void
+LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
+{
+    BYTE v[8];
+
+    assert(dstEnd >= dstPtr + MINMATCH);
+
+    switch(offset) {
+    case 1:
+        MEM_INIT(v, *srcPtr, 8);
+        break;
+    case 2:
+        LZ4_memcpy(v, srcPtr, 2);
+        LZ4_memcpy(&v[2], srcPtr, 2);
+#if defined(_MSC_VER) && (_MSC_VER <= 1936) /* MSVC 2022 ver 17.6 or earlier */
+#  pragma warning(push)
+#  pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */
+#endif
+        LZ4_memcpy(&v[4], v, 4);
+#if defined(_MSC_VER) && (_MSC_VER <= 1936) /* MSVC 2022 ver 17.6 or earlier */
+#  pragma warning(pop)
+#endif
+        break;
+    case 4:
+        LZ4_memcpy(v, srcPtr, 4);
+        LZ4_memcpy(&v[4], srcPtr, 4);
+        break;
+    default:
+        LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);
+        return;
+    }
+
+    LZ4_memcpy(dstPtr, v, 8);
+    dstPtr += 8;
+    while (dstPtr < dstEnd) {
+        LZ4_memcpy(dstPtr, v, 8);
+        dstPtr += 8;
+    }
+}
+#endif
+
+
+/*-************************************
+*  Common functions
+**************************************/
+static unsigned LZ4_NbCommonBytes (reg_t val)
+{
+    assert(val != 0);
+    if (LZ4_isLittleEndian()) {
+        if (sizeof(val) == 8) {
+#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)
+/*-*************************************************************************************************
+* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.
+* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics
+* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.
+****************************************************************************************************/
+#         if defined(__clang__) && (__clang_major__ < 10)
+            /* Avoid undefined clang-cl intrinsics issue.
+             * See https://github.com/lz4/lz4/pull/1017 for details. */
+            return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;
+#         else
+            /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */
+            return (unsigned)_tzcnt_u64(val) >> 3;
+#         endif
+#       elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanForward64(&r, (U64)val);
+            return (unsigned)r >> 3;
+#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
+                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
+                                        !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (unsigned)__builtin_ctzll((U64)val) >> 3;
+#       else
+            const U64 m = 0x0101010101010101ULL;
+            val ^= val - 1;
+            return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);
+#       endif
+        } else /* 32 bits */ {
+#       if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r;
+            _BitScanForward(&r, (U32)val);
+            return (unsigned)r >> 3;
+#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
+                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
+                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (unsigned)__builtin_ctz((U32)val) >> 3;
+#       else
+            const U32 m = 0x01010101;
+            return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;
+#       endif
+        }
+    } else   /* Big Endian CPU */ {
+        if (sizeof(val)==8) {
+#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
+                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
+                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (unsigned)__builtin_clzll((U64)val) >> 3;
+#       else
+#if 1
+            /* this method is probably faster,
+             * but adds a 128 bytes lookup table */
+            static const unsigned char ctz7_tab[128] = {
+                7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+            };
+            U64 const mask = 0x0101010101010101ULL;
+            U64 const t = (((val >> 8) - mask) | val) & mask;
+            return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];
+#else
+            /* this method doesn't consume memory space like the previous one,
+             * but it contains several branches,
+             * that may end up slowing execution */
+            static const U32 by32 = sizeof(val)*4;  /* 32 on 64 bits (goal), 16 on 32 bits.
+            Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.
+            Note that this code path is never triggered in 32-bits mode. */
+            unsigned r;
+            if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }
+            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+            r += (!val);
+            return r;
+#endif
+#       endif
+        } else /* 32 bits */ {
+#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
+                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
+                                        !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (unsigned)__builtin_clz((U32)val) >> 3;
+#       else
+            val >>= 8;
+            val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |
+              (val + 0x00FF0000)) >> 24;
+            return (unsigned)val ^ 3;
+#       endif
+        }
+    }
+}
+
+
+#define STEPSIZE sizeof(reg_t)
+LZ4_FORCE_INLINE
+unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+    const BYTE* const pStart = pIn;
+
+    if (likely(pIn < pInLimit-(STEPSIZE-1))) {
+        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+        if (!diff) {
+            pIn+=STEPSIZE; pMatch+=STEPSIZE;
+        } else {
+            return LZ4_NbCommonBytes(diff);
+    }   }
+
+    while (likely(pIn < pInLimit-(STEPSIZE-1))) {
+        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+        if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+        pIn += LZ4_NbCommonBytes(diff);
+        return (unsigned)(pIn - pStart);
+    }
+
+    if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+    if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+    if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
+    return (unsigned)(pIn - pStart);
+}
+
+
+#ifndef LZ4_COMMONDEFS_ONLY
+/*-************************************
+*  Local Constants
+**************************************/
+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
+static const U32 LZ4_skipTrigger = 6;  /* Increase this value ==> compression run slower on incompressible data */
+
+
+/*-************************************
+*  Local Structures and types
+**************************************/
+typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
+
+/**
+ * This enum distinguishes several different modes of accessing previous
+ * content in the stream.
+ *
+ * - noDict        : There is no preceding content.
+ * - withPrefix64k : Table entries up to ctx->dictSize before the current blob
+ *                   blob being compressed are valid and refer to the preceding
+ *                   content (of length ctx->dictSize), which is available
+ *                   contiguously preceding in memory the content currently
+ *                   being compressed.
+ * - usingExtDict  : Like withPrefix64k, but the preceding content is somewhere
+ *                   else in memory, starting at ctx->dictionary with length
+ *                   ctx->dictSize.
+ * - usingDictCtx  : Everything concerning the preceding content is
+ *                   in a separate context, pointed to by ctx->dictCtx.
+ *                   ctx->dictionary, ctx->dictSize, and table entries
+ *                   in the current context that refer to positions
+ *                   preceding the beginning of the current compression are
+ *                   ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx
+ *                   ->dictSize describe the location and size of the preceding
+ *                   content, and matches are found by looking in the ctx
+ *                   ->dictCtx->hashTable.
+ */
+typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+
+/*-************************************
+*  Local Utils
+**************************************/
+int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
+const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
+int LZ4_compressBound(int isize)  { return LZ4_COMPRESSBOUND(isize); }
+int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }
+
+
+/*-****************************************
+*  Internal Definitions, used only in Tests
+*******************************************/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);
+
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
+                                     int compressedSize, int maxOutputSize,
+                                     const void* dictStart, size_t dictSize);
+int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,
+                                     int compressedSize, int targetOutputSize, int dstCapacity,
+                                     const void* dictStart, size_t dictSize);
+#if defined (__cplusplus)
+}
+#endif
+
+/*-******************************
+*  Compression functions
+********************************/
+LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+    if (tableType == byU16)
+        return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+    else
+        return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+    const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+    if (LZ4_isLittleEndian()) {
+        const U64 prime5bytes = 889523592379ULL;
+        return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+    } else {
+        const U64 prime8bytes = 11400714785074694791ULL;
+        return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+    }
+}
+
+LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+    if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+    return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)
+{
+    switch (tableType)
+    {
+    default: /* fallthrough */
+    case clearedTable: { /* illegal! */ assert(0); return; }
+    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }
+    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }
+    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }
+    }
+}
+
+LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)
+{
+    switch (tableType)
+    {
+    default: /* fallthrough */
+    case clearedTable: /* fallthrough */
+    case byPtr: { /* illegal! */ assert(0); return; }
+    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }
+    case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }
+    }
+}
+
+/* LZ4_putPosition*() : only used in byPtr mode */
+LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,
+                                  void* tableBase, tableType_t const tableType)
+{
+    const BYTE** const hashTable = (const BYTE**)tableBase;
+    assert(tableType == byPtr); (void)tableType;
+    hashTable[h] = p;
+}
+
+LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType)
+{
+    U32 const h = LZ4_hashPosition(p, tableType);
+    LZ4_putPositionOnHash(p, h, tableBase, tableType);
+}
+
+/* LZ4_getIndexOnHash() :
+ * Index of match position registered in hash table.
+ * hash position must be calculated by using base+index, or dictBase+index.
+ * Assumption 1 : only valid if tableType == byU32 or byU16.
+ * Assumption 2 : h is presumed valid (within limits of hash table)
+ */
+LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)
+{
+    LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);
+    if (tableType == byU32) {
+        const U32* const hashTable = (const U32*) tableBase;
+        assert(h < (1U << (LZ4_MEMORY_USAGE-2)));
+        return hashTable[h];
+    }
+    if (tableType == byU16) {
+        const U16* const hashTable = (const U16*) tableBase;
+        assert(h < (1U << (LZ4_MEMORY_USAGE-1)));
+        return hashTable[h];
+    }
+    assert(0); return 0;  /* forbidden case */
+}
+
+static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType)
+{
+    assert(tableType == byPtr); (void)tableType;
+    { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
+}
+
+LZ4_FORCE_INLINE const BYTE*
+LZ4_getPosition(const BYTE* p,
+                const void* tableBase, tableType_t tableType)
+{
+    U32 const h = LZ4_hashPosition(p, tableType);
+    return LZ4_getPositionOnHash(h, tableBase, tableType);
+}
+
+LZ4_FORCE_INLINE void
+LZ4_prepareTable(LZ4_stream_t_internal* const cctx,
+           const int inputSize,
+           const tableType_t tableType) {
+    /* If the table hasn't been used, it's guaranteed to be zeroed out, and is
+     * therefore safe to use no matter what mode we're in. Otherwise, we figure
+     * out if it's safe to leave as is or whether it needs to be reset.
+     */
+    if ((tableType_t)cctx->tableType != clearedTable) {
+        assert(inputSize >= 0);
+        if ((tableType_t)cctx->tableType != tableType
+          || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)
+          || ((tableType == byU32) && cctx->currentOffset > 1 GB)
+          || tableType == byPtr
+          || inputSize >= 4 KB)
+        {
+            DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
+            MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
+            cctx->currentOffset = 0;
+            cctx->tableType = (U32)clearedTable;
+        } else {
+            DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");
+        }
+    }
+
+    /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,
+     * is faster than compressing without a gap.
+     * However, compressing with currentOffset == 0 is faster still,
+     * so we preserve that case.
+     */
+    if (cctx->currentOffset != 0 && tableType == byU32) {
+        DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");
+        cctx->currentOffset += 64 KB;
+    }
+
+    /* Finally, clear history */
+    cctx->dictCtx = NULL;
+    cctx->dictionary = NULL;
+    cctx->dictSize = 0;
+}
+
+/** LZ4_compress_generic() :
+ *  inlined, to ensure branches are decided at compilation time.
+ *  The following conditions are presumed already validated:
+ *  - source != NULL
+ *  - inputSize > 0
+ */
+LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
+                 LZ4_stream_t_internal* const cctx,
+                 const char* const source,
+                 char* const dest,
+                 const int inputSize,
+                 int*  inputConsumed, /* only written when outputDirective == fillOutput */
+                 const int maxOutputSize,
+                 const limitedOutput_directive outputDirective,
+                 const tableType_t tableType,
+                 const dict_directive dictDirective,
+                 const dictIssue_directive dictIssue,
+                 const int acceleration)
+{
+    int result;
+    const BYTE* ip = (const BYTE*)source;
+
+    U32 const startIndex = cctx->currentOffset;
+    const BYTE* base = (const BYTE*)source - startIndex;
+    const BYTE* lowLimit;
+
+    const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
+    const BYTE* const dictionary =
+        dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
+    const U32 dictSize =
+        dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
+    const U32 dictDelta =
+        (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0;   /* make indexes in dictCtx comparable with indexes in current context */
+
+    int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
+    U32 const prefixIdxLimit = startIndex - dictSize;   /* used when dictDirective == dictSmall */
+    const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;
+    const BYTE* anchor = (const BYTE*) source;
+    const BYTE* const iend = ip + inputSize;
+    const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;
+    const BYTE* const matchlimit = iend - LASTLITERALS;
+
+    /* the dictCtx currentOffset is indexed on the start of the dictionary,
+     * while a dictionary in the current context precedes the currentOffset */
+    const BYTE* dictBase = (dictionary == NULL) ? NULL :
+                           (dictDirective == usingDictCtx) ?
+                            dictionary + dictSize - dictCtx->currentOffset :
+                            dictionary + dictSize - startIndex;
+
+    BYTE* op = (BYTE*) dest;
+    BYTE* const olimit = op + maxOutputSize;
+
+    U32 offset = 0;
+    U32 forwardH;
+
+    DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType);
+    assert(ip != NULL);
+    if (tableType == byU16) assert(inputSize<LZ4_64Klimit);  /* Size too large (not within 64K limit) */
+    if (tableType == byPtr) assert(dictDirective==noDict);   /* only supported use case with byPtr */
+    /* If init conditions are not met, we don't have to mark stream
+     * as having dirty context, since no action was taken yet */
+    if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */
+    assert(acceleration >= 1);
+
+    lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
+
+    /* Update context state */
+    if (dictDirective == usingDictCtx) {
+        /* Subsequent linked blocks can't use the dictionary. */
+        /* Instead, they use the block we just compressed. */
+        cctx->dictCtx = NULL;
+        cctx->dictSize = (U32)inputSize;
+    } else {
+        cctx->dictSize += (U32)inputSize;
+    }
+    cctx->currentOffset += (U32)inputSize;
+    cctx->tableType = (U32)tableType;
+
+    if (inputSize<LZ4_minLength) goto _last_literals;        /* Input too small, no compression (all literals) */
+
+    /* First Byte */
+    {   U32 const h = LZ4_hashPosition(ip, tableType);
+        if (tableType == byPtr) {
+            LZ4_putPositionOnHash(ip, h, cctx->hashTable, byPtr);
+        } else {
+            LZ4_putIndexOnHash(startIndex, h, cctx->hashTable, tableType);
+    }   }
+    ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+    /* Main Loop */
+    for ( ; ; ) {
+        const BYTE* match;
+        BYTE* token;
+        const BYTE* filledIp;
+
+        /* Find a match */
+        if (tableType == byPtr) {
+            const BYTE* forwardIp = ip;
+            int step = 1;
+            int searchMatchNb = acceleration << LZ4_skipTrigger;
+            do {
+                U32 const h = forwardH;
+                ip = forwardIp;
+                forwardIp += step;
+                step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+                assert(ip < mflimitPlusOne);
+
+                match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType);
+                forwardH = LZ4_hashPosition(forwardIp, tableType);
+                LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType);
+
+            } while ( (match+LZ4_DISTANCE_MAX < ip)
+                   || (LZ4_read32(match) != LZ4_read32(ip)) );
+
+        } else {   /* byU32, byU16 */
+
+            const BYTE* forwardIp = ip;
+            int step = 1;
+            int searchMatchNb = acceleration << LZ4_skipTrigger;
+            do {
+                U32 const h = forwardH;
+                U32 const current = (U32)(forwardIp - base);
+                U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+                assert(matchIndex <= current);
+                assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));
+                ip = forwardIp;
+                forwardIp += step;
+                step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+                assert(ip < mflimitPlusOne);
+
+                if (dictDirective == usingDictCtx) {
+                    if (matchIndex < startIndex) {
+                        /* there was no match, try the dictionary */
+                        assert(tableType == byU32);
+                        matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+                        match = dictBase + matchIndex;
+                        matchIndex += dictDelta;   /* make dictCtx index comparable with current context */
+                        lowLimit = dictionary;
+                    } else {
+                        match = base + matchIndex;
+                        lowLimit = (const BYTE*)source;
+                    }
+                } else if (dictDirective == usingExtDict) {
+                    if (matchIndex < startIndex) {
+                        DEBUGLOG(7, "extDict candidate: matchIndex=%5u  <  startIndex=%5u", matchIndex, startIndex);
+                        assert(startIndex - matchIndex >= MINMATCH);
+                        assert(dictBase);
+                        match = dictBase + matchIndex;
+                        lowLimit = dictionary;
+                    } else {
+                        match = base + matchIndex;
+                        lowLimit = (const BYTE*)source;
+                    }
+                } else {   /* single continuous memory segment */
+                    match = base + matchIndex;
+                }
+                forwardH = LZ4_hashPosition(forwardIp, tableType);
+                LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+
+                DEBUGLOG(7, "candidate at pos=%u  (offset=%u \n", matchIndex, current - matchIndex);
+                if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; }    /* match outside of valid area */
+                assert(matchIndex < current);
+                if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))
+                  && (matchIndex+LZ4_DISTANCE_MAX < current)) {
+                    continue;
+                } /* too far */
+                assert((current - matchIndex) <= LZ4_DISTANCE_MAX);  /* match now expected within distance */
+
+                if (LZ4_read32(match) == LZ4_read32(ip)) {
+                    if (maybe_extMem) offset = current - matchIndex;
+                    break;   /* match found */
+                }
+
+            } while(1);
+        }
+
+        /* Catch up */
+        filledIp = ip;
+        while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+        /* Encode Literals */
+        {   unsigned const litLength = (unsigned)(ip - anchor);
+            token = op++;
+            if ((outputDirective == limitedOutput) &&  /* Check output buffer overflow */
+                (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {
+                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
+            }
+            if ((outputDirective == fillOutput) &&
+                (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {
+                op--;
+                goto _last_literals;
+            }
+            if (litLength >= RUN_MASK) {
+                int len = (int)(litLength - RUN_MASK);
+                *token = (RUN_MASK<<ML_BITS);
+                for(; len >= 255 ; len-=255) *op++ = 255;
+                *op++ = (BYTE)len;
+            }
+            else *token = (BYTE)(litLength<<ML_BITS);
+
+            /* Copy Literals */
+            LZ4_wildCopy8(op, anchor, op+litLength);
+            op+=litLength;
+            DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",
+                        (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));
+        }
+
+_next_match:
+        /* at this stage, the following variables must be correctly set :
+         * - ip : at start of LZ operation
+         * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict
+         * - offset : if maybe_ext_memSegment==1 (constant)
+         * - lowLimit : must be == dictionary to mean "match is within extDict"; must be == source otherwise
+         * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written
+         */
+
+        if ((outputDirective == fillOutput) &&
+            (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {
+            /* the match was too close to the end, rewind and go to last literals */
+            op = token;
+            goto _last_literals;
+        }
+
+        /* Encode Offset */
+        if (maybe_extMem) {   /* static test */
+            DEBUGLOG(6, "             with offset=%u  (ext if > %i)", offset, (int)(ip - (const BYTE*)source));
+            assert(offset <= LZ4_DISTANCE_MAX && offset > 0);
+            LZ4_writeLE16(op, (U16)offset); op+=2;
+        } else  {
+            DEBUGLOG(6, "             with offset=%u  (same segment)", (U32)(ip - match));
+            assert(ip-match <= LZ4_DISTANCE_MAX);
+            LZ4_writeLE16(op, (U16)(ip - match)); op+=2;
+        }
+
+        /* Encode MatchLength */
+        {   unsigned matchCode;
+
+            if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)
+              && (lowLimit==dictionary) /* match within extDict */ ) {
+                const BYTE* limit = ip + (dictEnd-match);
+                assert(dictEnd > match);
+                if (limit > matchlimit) limit = matchlimit;
+                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+                ip += (size_t)matchCode + MINMATCH;
+                if (ip==limit) {
+                    unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);
+                    matchCode += more;
+                    ip += more;
+                }
+                DEBUGLOG(6, "             with matchLength=%u starting in extDict", matchCode+MINMATCH);
+            } else {
+                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+                ip += (size_t)matchCode + MINMATCH;
+                DEBUGLOG(6, "             with matchLength=%u", matchCode+MINMATCH);
+            }
+
+            if ((outputDirective) &&    /* Check output buffer overflow */
+                (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {
+                if (outputDirective == fillOutput) {
+                    /* Match description too long : reduce it */
+                    U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;
+                    ip -= matchCode - newMatchCode;
+                    assert(newMatchCode < matchCode);
+                    matchCode = newMatchCode;
+                    if (unlikely(ip <= filledIp)) {
+                        /* We have already filled up to filledIp so if ip ends up less than filledIp
+                         * we have positions in the hash table beyond the current position. This is
+                         * a problem if we reuse the hash table. So we have to remove these positions
+                         * from the hash table.
+                         */
+                        const BYTE* ptr;
+                        DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip));
+                        for (ptr = ip; ptr <= filledIp; ++ptr) {
+                            U32 const h = LZ4_hashPosition(ptr, tableType);
+                            LZ4_clearHash(h, cctx->hashTable, tableType);
+                        }
+                    }
+                } else {
+                    assert(outputDirective == limitedOutput);
+                    return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
+                }
+            }
+            if (matchCode >= ML_MASK) {
+                *token += ML_MASK;
+                matchCode -= ML_MASK;
+                LZ4_write32(op, 0xFFFFFFFF);
+                while (matchCode >= 4*255) {
+                    op+=4;
+                    LZ4_write32(op, 0xFFFFFFFF);
+                    matchCode -= 4*255;
+                }
+                op += matchCode / 255;
+                *op++ = (BYTE)(matchCode % 255);
+            } else
+                *token += (BYTE)(matchCode);
+        }
+        /* Ensure we have enough space for the last literals. */
+        assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));
+
+        anchor = ip;
+
+        /* Test end of chunk */
+        if (ip >= mflimitPlusOne) break;
+
+        /* Fill table */
+        {   U32 const h = LZ4_hashPosition(ip-2, tableType);
+            if (tableType == byPtr) {
+                LZ4_putPositionOnHash(ip-2, h, cctx->hashTable, byPtr);
+            } else {
+                U32 const idx = (U32)((ip-2) - base);
+                LZ4_putIndexOnHash(idx, h, cctx->hashTable, tableType);
+        }   }
+
+        /* Test next position */
+        if (tableType == byPtr) {
+
+            match = LZ4_getPosition(ip, cctx->hashTable, tableType);
+            LZ4_putPosition(ip, cctx->hashTable, tableType);
+            if ( (match+LZ4_DISTANCE_MAX >= ip)
+              && (LZ4_read32(match) == LZ4_read32(ip)) )
+            { token=op++; *token=0; goto _next_match; }
+
+        } else {   /* byU32, byU16 */
+
+            U32 const h = LZ4_hashPosition(ip, tableType);
+            U32 const current = (U32)(ip-base);
+            U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+            assert(matchIndex < current);
+            if (dictDirective == usingDictCtx) {
+                if (matchIndex < startIndex) {
+                    /* there was no match, try the dictionary */
+                    assert(tableType == byU32);
+                    matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+                    match = dictBase + matchIndex;
+                    lowLimit = dictionary;   /* required for match length counter */
+                    matchIndex += dictDelta;
+                } else {
+                    match = base + matchIndex;
+                    lowLimit = (const BYTE*)source;  /* required for match length counter */
+                }
+            } else if (dictDirective==usingExtDict) {
+                if (matchIndex < startIndex) {
+                    assert(dictBase);
+                    match = dictBase + matchIndex;
+                    lowLimit = dictionary;   /* required for match length counter */
+                } else {
+                    match = base + matchIndex;
+                    lowLimit = (const BYTE*)source;   /* required for match length counter */
+                }
+            } else {   /* single memory segment */
+                match = base + matchIndex;
+            }
+            LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+            assert(matchIndex < current);
+            if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)
+              && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))
+              && (LZ4_read32(match) == LZ4_read32(ip)) ) {
+                token=op++;
+                *token=0;
+                if (maybe_extMem) offset = current - matchIndex;
+                DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",
+                            (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));
+                goto _next_match;
+            }
+        }
+
+        /* Prepare next loop */
+        forwardH = LZ4_hashPosition(++ip, tableType);
+
+    }
+
+_last_literals:
+    /* Encode Last Literals */
+    {   size_t lastRun = (size_t)(iend - anchor);
+        if ( (outputDirective) &&  /* Check output buffer overflow */
+            (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {
+            if (outputDirective == fillOutput) {
+                /* adapt lastRun to fill 'dst' */
+                assert(olimit >= op);
+                lastRun  = (size_t)(olimit-op) - 1/*token*/;
+                lastRun -= (lastRun + 256 - RUN_MASK) / 256;  /*additional length tokens*/
+            } else {
+                assert(outputDirective == limitedOutput);
+                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
+            }
+        }
+        DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun);
+        if (lastRun >= RUN_MASK) {
+            size_t accumulator = lastRun - RUN_MASK;
+            *op++ = RUN_MASK << ML_BITS;
+            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+            *op++ = (BYTE) accumulator;
+        } else {
+            *op++ = (BYTE)(lastRun<<ML_BITS);
+        }
+        LZ4_memcpy(op, anchor, lastRun);
+        ip = anchor + lastRun;
+        op += lastRun;
+    }
+
+    if (outputDirective == fillOutput) {
+        *inputConsumed = (int) (((const char*)ip)-source);
+    }
+    result = (int)(((char*)op) - dest);
+    assert(result > 0);
+    DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result);
+    return result;
+}
+
+/** LZ4_compress_generic() :
+ *  inlined, to ensure branches are decided at compilation time;
+ *  takes care of src == (NULL, 0)
+ *  and forward the rest to LZ4_compress_generic_validated */
+LZ4_FORCE_INLINE int LZ4_compress_generic(
+                 LZ4_stream_t_internal* const cctx,
+                 const char* const src,
+                 char* const dst,
+                 const int srcSize,
+                 int *inputConsumed, /* only written when outputDirective == fillOutput */
+                 const int dstCapacity,
+                 const limitedOutput_directive outputDirective,
+                 const tableType_t tableType,
+                 const dict_directive dictDirective,
+                 const dictIssue_directive dictIssue,
+                 const int acceleration)
+{
+    DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i",
+                srcSize, dstCapacity);
+
+    if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; }  /* Unsupported srcSize, too large (or negative) */
+    if (srcSize == 0) {   /* src == NULL supported if srcSize == 0 */
+        if (outputDirective != notLimited && dstCapacity <= 0) return 0;  /* no output, can't write anything */
+        DEBUGLOG(5, "Generating an empty block");
+        assert(outputDirective == notLimited || dstCapacity >= 1);
+        assert(dst != NULL);
+        dst[0] = 0;
+        if (outputDirective == fillOutput) {
+            assert (inputConsumed != NULL);
+            *inputConsumed = 0;
+        }
+        return 1;
+    }
+    assert(src != NULL);
+
+    return LZ4_compress_generic_validated(cctx, src, dst, srcSize,
+                inputConsumed, /* only written into if outputDirective == fillOutput */
+                dstCapacity, outputDirective,
+                tableType, dictDirective, dictIssue, acceleration);
+}
+
+
+int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+    LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;
+    assert(ctx != NULL);
+    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
+    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
+    if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+        if (inputSize < LZ4_64Klimit) {
+            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+        } else {
+            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+        }
+    } else {
+        if (inputSize < LZ4_64Klimit) {
+            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+        } else {
+            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+        }
+    }
+}
+
+/**
+ * LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step. It is only safe
+ * to call if the state buffer is known to be correctly initialized already
+ * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of
+ * "correctly initialized").
+ */
+int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
+{
+    LZ4_stream_t_internal* const ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
+    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
+    assert(ctx != NULL);
+
+    if (dstCapacity >= LZ4_compressBound(srcSize)) {
+        if (srcSize < LZ4_64Klimit) {
+            const tableType_t tableType = byU16;
+            LZ4_prepareTable(ctx, srcSize, tableType);
+            if (ctx->currentOffset) {
+                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);
+            } else {
+                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+            }
+        } else {
+            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+            LZ4_prepareTable(ctx, srcSize, tableType);
+            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+        }
+    } else {
+        if (srcSize < LZ4_64Klimit) {
+            const tableType_t tableType = byU16;
+            LZ4_prepareTable(ctx, srcSize, tableType);
+            if (ctx->currentOffset) {
+                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);
+            } else {
+                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+            }
+        } else {
+            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+            LZ4_prepareTable(ctx, srcSize, tableType);
+            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+        }
+    }
+}
+
+
+int LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration)
+{
+    int result;
+#if (LZ4_HEAPMODE)
+    LZ4_stream_t* const ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */
+    if (ctxPtr == NULL) return 0;
+#else
+    LZ4_stream_t ctx;
+    LZ4_stream_t* const ctxPtr = &ctx;
+#endif
+    result = LZ4_compress_fast_extState(ctxPtr, src, dest, srcSize, dstCapacity, acceleration);
+
+#if (LZ4_HEAPMODE)
+    FREEMEM(ctxPtr);
+#endif
+    return result;
+}
+
+
+int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity)
+{
+    return LZ4_compress_fast(src, dst, srcSize, dstCapacity, 1);
+}
+
+
+/* Note!: This function leaves the stream in an unclean/broken state!
+ * It is not safe to subsequently use the same state with a _fastReset() or
+ * _continue() call without resetting it. */
+static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+    void* const s = LZ4_initStream(state, sizeof (*state));
+    assert(s != NULL); (void)s;
+
+    if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) {  /* compression success is guaranteed */
+        return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
+    } else {
+        if (*srcSizePtr < LZ4_64Klimit) {
+            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);
+        } else {
+            tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);
+    }   }
+}
+
+
+int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+#if (LZ4_HEAPMODE)
+    LZ4_stream_t* const ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */
+    if (ctx == NULL) return 0;
+#else
+    LZ4_stream_t ctxBody;
+    LZ4_stream_t* const ctx = &ctxBody;
+#endif
+
+    int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
+
+#if (LZ4_HEAPMODE)
+    FREEMEM(ctx);
+#endif
+    return result;
+}
+
+
+
+/*-******************************
+*  Streaming functions
+********************************/
+
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+LZ4_stream_t* LZ4_createStream(void)
+{
+    LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
+    LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));
+    DEBUGLOG(4, "LZ4_createStream %p", lz4s);
+    if (lz4s == NULL) return NULL;
+    LZ4_initStream(lz4s, sizeof(*lz4s));
+    return lz4s;
+}
+#endif
+
+static size_t LZ4_stream_t_alignment(void)
+{
+#if LZ4_ALIGN_TEST
+    typedef struct { char c; LZ4_stream_t t; } t_a;
+    return sizeof(t_a) - sizeof(LZ4_stream_t);
+#else
+    return 1;  /* effectively disabled */
+#endif
+}
+
+LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)
+{
+    DEBUGLOG(5, "LZ4_initStream");
+    if (buffer == NULL) { return NULL; }
+    if (size < sizeof(LZ4_stream_t)) { return NULL; }
+    if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;
+    MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));
+    return (LZ4_stream_t*)buffer;
+}
+
+/* resetStream is now deprecated,
+ * prefer initStream() which is more general */
+void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+    DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
+    MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));
+}
+
+void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
+    LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);
+}
+
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
+{
+    if (!LZ4_stream) return 0;   /* support free on NULL */
+    DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
+    FREEMEM(LZ4_stream);
+    return (0);
+}
+#endif
+
+
+#define HASH_UNIT sizeof(reg_t)
+int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
+{
+    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
+    const tableType_t tableType = byU32;
+    const BYTE* p = (const BYTE*)dictionary;
+    const BYTE* const dictEnd = p + dictSize;
+    U32 idx32;
+
+    DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
+
+    /* It's necessary to reset the context,
+     * and not just continue it with prepareTable()
+     * to avoid any risk of generating overflowing matchIndex
+     * when compressing using this dictionary */
+    LZ4_resetStream(LZ4_dict);
+
+    /* We always increment the offset by 64 KB, since, if the dict is longer,
+     * we truncate it to the last 64k, and if it's shorter, we still want to
+     * advance by a whole window length so we can provide the guarantee that
+     * there are only valid offsets in the window, which allows an optimization
+     * in LZ4_compress_fast_continue() where it uses noDictIssue even when the
+     * dictionary isn't a full 64k. */
+    dict->currentOffset += 64 KB;
+
+    if (dictSize < (int)HASH_UNIT) {
+        return 0;
+    }
+
+    if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
+    dict->dictionary = p;
+    dict->dictSize = (U32)(dictEnd - p);
+    dict->tableType = (U32)tableType;
+    idx32 = dict->currentOffset - dict->dictSize;
+
+    while (p <= dictEnd-HASH_UNIT) {
+        U32 const h = LZ4_hashPosition(p, tableType);
+        LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);
+        p+=3; idx32+=3;
+    }
+
+    return (int)dict->dictSize;
+}
+
+void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)
+{
+    const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :
+        &(dictionaryStream->internal_donotuse);
+
+    DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",
+             workingStream, dictionaryStream,
+             dictCtx != NULL ? dictCtx->dictSize : 0);
+
+    if (dictCtx != NULL) {
+        /* If the current offset is zero, we will never look in the
+         * external dictionary context, since there is no value a table
+         * entry can take that indicate a miss. In that case, we need
+         * to bump the offset to something non-zero.
+         */
+        if (workingStream->internal_donotuse.currentOffset == 0) {
+            workingStream->internal_donotuse.currentOffset = 64 KB;
+        }
+
+        /* Don't actually attach an empty dictionary.
+         */
+        if (dictCtx->dictSize == 0) {
+            dictCtx = NULL;
+        }
+    }
+    workingStream->internal_donotuse.dictCtx = dictCtx;
+}
+
+
+static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)
+{
+    assert(nextSize >= 0);
+    if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) {   /* potential ptrdiff_t overflow (32-bits mode) */
+        /* rescale hash table */
+        U32 const delta = LZ4_dict->currentOffset - 64 KB;
+        const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+        int i;
+        DEBUGLOG(4, "LZ4_renormDictT");
+        for (i=0; i<LZ4_HASH_SIZE_U32; i++) {
+            if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;
+            else LZ4_dict->hashTable[i] -= delta;
+        }
+        LZ4_dict->currentOffset = 64 KB;
+        if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
+        LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+    }
+}
+
+
+int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,
+                                const char* source, char* dest,
+                                int inputSize, int maxOutputSize,
+                                int acceleration)
+{
+    const tableType_t tableType = byU32;
+    LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;
+    const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;
+
+    DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize);
+
+    LZ4_renormDictT(streamPtr, inputSize);   /* fix index overflow */
+    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
+    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
+
+    /* invalidate tiny dictionaries */
+    if ( (streamPtr->dictSize < 4)     /* tiny dictionary : not enough for a hash */
+      && (dictEnd != source)           /* prefix mode */
+      && (inputSize > 0)               /* tolerance : don't lose history, in case next invocation would use prefix mode */
+      && (streamPtr->dictCtx == NULL)  /* usingDictCtx */
+      ) {
+        DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);
+        /* remove dictionary existence from history, to employ faster prefix mode */
+        streamPtr->dictSize = 0;
+        streamPtr->dictionary = (const BYTE*)source;
+        dictEnd = source;
+    }
+
+    /* Check overlapping input/dictionary space */
+    {   const char* const sourceEnd = source + inputSize;
+        if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {
+            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+            if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
+            if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
+            streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;
+        }
+    }
+
+    /* prefix mode : source data follows dictionary */
+    if (dictEnd == source) {
+        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);
+        else
+            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);
+    }
+
+    /* external dictionary mode */
+    {   int result;
+        if (streamPtr->dictCtx) {
+            /* We depend here on the fact that dictCtx'es (produced by
+             * LZ4_loadDict) guarantee that their tables contain no references
+             * to offsets between dictCtx->currentOffset - 64 KB and
+             * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe
+             * to use noDictIssue even when the dict isn't a full 64 KB.
+             */
+            if (inputSize > 4 KB) {
+                /* For compressing large blobs, it is faster to pay the setup
+                 * cost to copy the dictionary's tables into the active context,
+                 * so that the compression loop is only looking into one table.
+                 */
+                LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));
+                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+            } else {
+                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);
+            }
+        } else {  /* small data <= 4 KB */
+            if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);
+            } else {
+                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+            }
+        }
+        streamPtr->dictionary = (const BYTE*)source;
+        streamPtr->dictSize = (U32)inputSize;
+        return result;
+    }
+}
+
+
+/* Hidden debug function, to force-test external dictionary mode */
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
+{
+    LZ4_stream_t_internal* const streamPtr = &LZ4_dict->internal_donotuse;
+    int result;
+
+    LZ4_renormDictT(streamPtr, srcSize);
+
+    if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);
+    } else {
+        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+    }
+
+    streamPtr->dictionary = (const BYTE*)source;
+    streamPtr->dictSize = (U32)srcSize;
+
+    return result;
+}
+
+
+/*! LZ4_saveDict() :
+ *  If previously compressed data block is not guaranteed to remain available at its memory location,
+ *  save it into a safer place (char* safeBuffer).
+ *  Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,
+ *         one can therefore call LZ4_compress_fast_continue() right after.
+ * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
+{
+    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
+
+    DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer);
+
+    if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */
+    if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }
+
+    if (safeBuffer == NULL) assert(dictSize == 0);
+    if (dictSize > 0) {
+        const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
+        assert(dict->dictionary);
+        LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);
+    }
+
+    dict->dictionary = (const BYTE*)safeBuffer;
+    dict->dictSize = (U32)dictSize;
+
+    return dictSize;
+}
+
+
+
+/*-*******************************
+ *  Decompression functions
+ ********************************/
+
+typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;
+
+#undef MIN
+#define MIN(a,b)    ( (a) < (b) ? (a) : (b) )
+
+
+/* variant for decompress_unsafe()
+ * does not know end of input
+ * presumes input is well formed
+ * note : will consume at least one byte */
+static size_t read_long_length_no_check(const BYTE** pp)
+{
+    size_t b, l = 0;
+    do { b = **pp; (*pp)++; l += b; } while (b==255);
+    DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1)
+    return l;
+}
+
+/* core decoder variant for LZ4_decompress_fast*()
+ * for legacy support only : these entry points are deprecated.
+ * - Presumes input is correctly formed (no defense vs malformed inputs)
+ * - Does not know input size (presume input buffer is "large enough")
+ * - Decompress a full block (only)
+ * @return : nb of bytes read from input.
+ * Note : this variant is not optimized for speed, just for maintenance.
+ *        the goal is to remove support of decompress_fast*() variants by v2.0
+**/
+LZ4_FORCE_INLINE int
+LZ4_decompress_unsafe_generic(
+                 const BYTE* const istart,
+                 BYTE* const ostart,
+                 int decompressedSize,
+
+                 size_t prefixSize,
+                 const BYTE* const dictStart,  /* only if dict==usingExtDict */
+                 const size_t dictSize         /* note: =0 if dictStart==NULL */
+                 )
+{
+    const BYTE* ip = istart;
+    BYTE* op = (BYTE*)ostart;
+    BYTE* const oend = ostart + decompressedSize;
+    const BYTE* const prefixStart = ostart - prefixSize;
+
+    DEBUGLOG(5, "LZ4_decompress_unsafe_generic");
+    if (dictStart == NULL) assert(dictSize == 0);
+
+    while (1) {
+        /* start new sequence */
+        unsigned token = *ip++;
+
+        /* literals */
+        {   size_t ll = token >> ML_BITS;
+            if (ll==15) {
+                /* long literal length */
+                ll += read_long_length_no_check(&ip);
+            }
+            if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */
+            LZ4_memmove(op, ip, ll); /* support in-place decompression */
+            op += ll;
+            ip += ll;
+            if ((size_t)(oend-op) < MFLIMIT) {
+                if (op==oend) break;  /* end of block */
+                DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op);
+                /* incorrect end of block :
+                 * last match must start at least MFLIMIT==12 bytes before end of output block */
+                return -1;
+        }   }
+
+        /* match */
+        {   size_t ml = token & 15;
+            size_t const offset = LZ4_readLE16(ip);
+            ip+=2;
+
+            if (ml==15) {
+                /* long literal length */
+                ml += read_long_length_no_check(&ip);
+            }
+            ml += MINMATCH;
+
+            if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */
+
+            {   const BYTE* match = op - offset;
+
+                /* out of range */
+                if (offset > (size_t)(op - prefixStart) + dictSize) {
+                    DEBUGLOG(6, "offset out of range");
+                    return -1;
+                }
+
+                /* check special case : extDict */
+                if (offset > (size_t)(op - prefixStart)) {
+                    /* extDict scenario */
+                    const BYTE* const dictEnd = dictStart + dictSize;
+                    const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));
+                    size_t const extml = (size_t)(dictEnd - extMatch);
+                    if (extml > ml) {
+                        /* match entirely within extDict */
+                        LZ4_memmove(op, extMatch, ml);
+                        op += ml;
+                        ml = 0;
+                    } else {
+                        /* match split between extDict & prefix */
+                        LZ4_memmove(op, extMatch, extml);
+                        op += extml;
+                        ml -= extml;
+                    }
+                    match = prefixStart;
+                }
+
+                /* match copy - slow variant, supporting overlap copy */
+                {   size_t u;
+                    for (u=0; u<ml; u++) {
+                        op[u] = match[u];
+            }   }   }
+            op += ml;
+            if ((size_t)(oend-op) < LASTLITERALS) {
+                DEBUGLOG(5, "invalid: match ends at distance %zi from end of block", oend-op);
+                /* incorrect end of block :
+                 * last match must stop at least LASTLITERALS==5 bytes before end of output block */
+                return -1;
+            }
+        } /* match */
+    } /* main loop */
+    return (int)(ip - istart);
+}
+
+
+/* Read the variable-length literal or match length.
+ *
+ * @ip : input pointer
+ * @ilimit : position after which if length is not decoded, the input is necessarily corrupted.
+ * @initial_check - check ip >= ipmax before start of loop.  Returns initial_error if so.
+ * @error (output) - error code.  Must be set to 0 before call.
+**/
+typedef size_t Rvl_t;
+static const Rvl_t rvl_error = (Rvl_t)(-1);
+LZ4_FORCE_INLINE Rvl_t
+read_variable_length(const BYTE** ip, const BYTE* ilimit,
+                     int initial_check)
+{
+    Rvl_t s, length = 0;
+    assert(ip != NULL);
+    assert(*ip !=  NULL);
+    assert(ilimit != NULL);
+    if (initial_check && unlikely((*ip) >= ilimit)) {    /* read limit reached */
+        return rvl_error;
+    }
+    do {
+        s = **ip;
+        (*ip)++;
+        length += s;
+        if (unlikely((*ip) > ilimit)) {    /* read limit reached */
+            return rvl_error;
+        }
+        /* accumulator overflow detection (32-bit mode only) */
+        if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {
+            return rvl_error;
+        }
+    } while (s==255);
+
+    return length;
+}
+
+/*! LZ4_decompress_generic() :
+ *  This generic decompression function covers all use cases.
+ *  It shall be instantiated several times, using different sets of directives.
+ *  Note that it is important for performance that this function really get inlined,
+ *  in order to remove useless branches during compilation optimization.
+ */
+LZ4_FORCE_INLINE int
+LZ4_decompress_generic(
+                 const char* const src,
+                 char* const dst,
+                 int srcSize,
+                 int outputSize,         /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
+
+                 earlyEnd_directive partialDecoding,  /* full, partial */
+                 dict_directive dict,                 /* noDict, withPrefix64k, usingExtDict */
+                 const BYTE* const lowPrefix,  /* always <= dst, == dst when no prefix */
+                 const BYTE* const dictStart,  /* only if dict==usingExtDict */
+                 const size_t dictSize         /* note : = 0 if noDict */
+                 )
+{
+    if ((src == NULL) || (outputSize < 0)) { return -1; }
+
+    {   const BYTE* ip = (const BYTE*) src;
+        const BYTE* const iend = ip + srcSize;
+
+        BYTE* op = (BYTE*) dst;
+        BYTE* const oend = op + outputSize;
+        BYTE* cpy;
+
+        const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;
+
+        const int checkOffset = (dictSize < (int)(64 KB));
+
+
+        /* Set up the "end" pointers for the shortcut. */
+        const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;
+        const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;
+
+        const BYTE* match;
+        size_t offset;
+        unsigned token;
+        size_t length;
+
+
+        DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize);
+
+        /* Special cases */
+        assert(lowPrefix <= op);
+        if (unlikely(outputSize==0)) {
+            /* Empty output buffer */
+            if (partialDecoding) return 0;
+            return ((srcSize==1) && (*ip==0)) ? 0 : -1;
+        }
+        if (unlikely(srcSize==0)) { return -1; }
+
+    /* LZ4_FAST_DEC_LOOP:
+     * designed for modern OoO performance cpus,
+     * where copying reliably 32-bytes is preferable to an unpredictable branch.
+     * note : fast loop may show a regression for some client arm chips. */
+#if LZ4_FAST_DEC_LOOP
+        if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {
+            DEBUGLOG(6, "skip fast decode loop");
+            goto safe_decode;
+        }
+
+        /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */
+        DEBUGLOG(6, "using fast decode loop");
+        while (1) {
+            /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */
+            assert(oend - op >= FASTLOOP_SAFE_DISTANCE);
+            assert(ip < iend);
+            token = *ip++;
+            length = token >> ML_BITS;  /* literal length */
+
+            /* decode literal length */
+            if (length == RUN_MASK) {
+                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
+                if (addl == rvl_error) {
+                    DEBUGLOG(6, "error reading long literal length");
+                    goto _output_error;
+                }
+                length += addl;
+                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
+                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
+
+                /* copy literals */
+                LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
+                if ((op+length>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }
+                LZ4_wildCopy32(op, ip, op+length);
+                ip += length; op += length;
+            } else if (ip <= iend-(16 + 1/*max lit + offset + nextToken*/)) {
+                /* We don't need to check oend, since we check it once for each loop below */
+                DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);
+                /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */
+                LZ4_memcpy(op, ip, 16);
+                ip += length; op += length;
+            } else {
+                goto safe_literal_copy;
+            }
+
+            /* get offset */
+            offset = LZ4_readLE16(ip); ip+=2;
+            DEBUGLOG(6, " offset = %zu", offset);
+            match = op - offset;
+            assert(match <= op);  /* overflow check */
+
+            /* get matchlength */
+            length = token & ML_MASK;
+
+            if (length == ML_MASK) {
+                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
+                if (addl == rvl_error) {
+                    DEBUGLOG(6, "error reading long match length");
+                    goto _output_error;
+                }
+                length += addl;
+                length += MINMATCH;
+                if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */
+                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) {
+                    DEBUGLOG(6, "Error : offset outside buffers");
+                    goto _output_error;
+                }
+                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
+                    goto safe_match_copy;
+                }
+            } else {
+                length += MINMATCH;
+                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
+                    goto safe_match_copy;
+                }
+
+                /* Fastpath check: skip LZ4_wildCopy32 when true */
+                if ((dict == withPrefix64k) || (match >= lowPrefix)) {
+                    if (offset >= 8) {
+                        assert(match >= lowPrefix);
+                        assert(match <= op);
+                        assert(op + 18 <= oend);
+
+                        LZ4_memcpy(op, match, 8);
+                        LZ4_memcpy(op+8, match+8, 8);
+                        LZ4_memcpy(op+16, match+16, 2);
+                        op += length;
+                        continue;
+            }   }   }
+
+            if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) {
+                DEBUGLOG(6, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match);
+                goto _output_error;
+            }
+            /* match starting within external dictionary */
+            if ((dict==usingExtDict) && (match < lowPrefix)) {
+                assert(dictEnd != NULL);
+                if (unlikely(op+length > oend-LASTLITERALS)) {
+                    if (partialDecoding) {
+                        DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd");
+                        length = MIN(length, (size_t)(oend-op));
+                    } else {
+                        DEBUGLOG(6, "end-of-block condition violated")
+                        goto _output_error;
+                }   }
+
+                if (length <= (size_t)(lowPrefix-match)) {
+                    /* match fits entirely within external dictionary : just copy */
+                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);
+                    op += length;
+                } else {
+                    /* match stretches into both external dictionary and current block */
+                    size_t const copySize = (size_t)(lowPrefix - match);
+                    size_t const restSize = length - copySize;
+                    LZ4_memcpy(op, dictEnd - copySize, copySize);
+                    op += copySize;
+                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */
+                        BYTE* const endOfMatch = op + restSize;
+                        const BYTE* copyFrom = lowPrefix;
+                        while (op < endOfMatch) { *op++ = *copyFrom++; }
+                    } else {
+                        LZ4_memcpy(op, lowPrefix, restSize);
+                        op += restSize;
+                }   }
+                continue;
+            }
+
+            /* copy match within block */
+            cpy = op + length;
+
+            assert((op <= oend) && (oend-op >= 32));
+            if (unlikely(offset<16)) {
+                LZ4_memcpy_using_offset(op, match, cpy, offset);
+            } else {
+                LZ4_wildCopy32(op, match, cpy);
+            }
+
+            op = cpy;   /* wildcopy correction */
+        }
+    safe_decode:
+#endif
+
+        /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */
+        DEBUGLOG(6, "using safe decode loop");
+        while (1) {
+            assert(ip < iend);
+            token = *ip++;
+            length = token >> ML_BITS;  /* literal length */
+
+            /* A two-stage shortcut for the most common case:
+             * 1) If the literal length is 0..14, and there is enough space,
+             * enter the shortcut and copy 16 bytes on behalf of the literals
+             * (in the fast mode, only 8 bytes can be safely copied this way).
+             * 2) Further if the match length is 4..18, copy 18 bytes in a similar
+             * manner; but we ensure that there's enough space in the output for
+             * those 18 bytes earlier, upon entering the shortcut (in other words,
+             * there is a combined check for both stages).
+             */
+            if ( (length != RUN_MASK)
+                /* strictly "less than" on input, to re-enter the loop with at least one byte */
+              && likely((ip < shortiend) & (op <= shortoend)) ) {
+                /* Copy the literals */
+                LZ4_memcpy(op, ip, 16);
+                op += length; ip += length;
+
+                /* The second stage: prepare for match copying, decode full info.
+                 * If it doesn't work out, the info won't be wasted. */
+                length = token & ML_MASK; /* match length */
+                offset = LZ4_readLE16(ip); ip += 2;
+                match = op - offset;
+                assert(match <= op); /* check overflow */
+
+                /* Do not deal with overlapping matches. */
+                if ( (length != ML_MASK)
+                  && (offset >= 8)
+                  && (dict==withPrefix64k || match >= lowPrefix) ) {
+                    /* Copy the match. */
+                    LZ4_memcpy(op + 0, match + 0, 8);
+                    LZ4_memcpy(op + 8, match + 8, 8);
+                    LZ4_memcpy(op +16, match +16, 2);
+                    op += length + MINMATCH;
+                    /* Both stages worked, load the next token. */
+                    continue;
+                }
+
+                /* The second stage didn't work out, but the info is ready.
+                 * Propel it right to the point of match copying. */
+                goto _copy_match;
+            }
+
+            /* decode literal length */
+            if (length == RUN_MASK) {
+                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
+                if (addl == rvl_error) { goto _output_error; }
+                length += addl;
+                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
+                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
+            }
+
+#if LZ4_FAST_DEC_LOOP
+        safe_literal_copy:
+#endif
+            /* copy literals */
+            cpy = op+length;
+
+            LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
+            if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {
+                /* We've either hit the input parsing restriction or the output parsing restriction.
+                 * In the normal scenario, decoding a full block, it must be the last sequence,
+                 * otherwise it's an error (invalid input or dimensions).
+                 * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.
+                 */
+                if (partialDecoding) {
+                    /* Since we are partial decoding we may be in this block because of the output parsing
+                     * restriction, which is not valid since the output buffer is allowed to be undersized.
+                     */
+                    DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end")
+                    DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length);
+                    DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op));
+                    DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip));
+                    /* Finishing in the middle of a literals segment,
+                     * due to lack of input.
+                     */
+                    if (ip+length > iend) {
+                        length = (size_t)(iend-ip);
+                        cpy = op + length;
+                    }
+                    /* Finishing in the middle of a literals segment,
+                     * due to lack of output space.
+                     */
+                    if (cpy > oend) {
+                        cpy = oend;
+                        assert(op<=oend);
+                        length = (size_t)(oend-op);
+                    }
+                } else {
+                     /* We must be on the last sequence (or invalid) because of the parsing limitations
+                      * so check that we exactly consume the input and don't overrun the output buffer.
+                      */
+                    if ((ip+length != iend) || (cpy > oend)) {
+                        DEBUGLOG(6, "should have been last run of literals")
+                        DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend);
+                        DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend);
+                        goto _output_error;
+                    }
+                }
+                LZ4_memmove(op, ip, length);  /* supports overlapping memory regions, for in-place decompression scenarios */
+                ip += length;
+                op += length;
+                /* Necessarily EOF when !partialDecoding.
+                 * When partialDecoding, it is EOF if we've either
+                 * filled the output buffer or
+                 * can't proceed with reading an offset for following match.
+                 */
+                if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {
+                    break;
+                }
+            } else {
+                LZ4_wildCopy8(op, ip, cpy);   /* can overwrite up to 8 bytes beyond cpy */
+                ip += length; op = cpy;
+            }
+
+            /* get offset */
+            offset = LZ4_readLE16(ip); ip+=2;
+            match = op - offset;
+
+            /* get matchlength */
+            length = token & ML_MASK;
+
+    _copy_match:
+            if (length == ML_MASK) {
+                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
+                if (addl == rvl_error) { goto _output_error; }
+                length += addl;
+                if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */
+            }
+            length += MINMATCH;
+
+#if LZ4_FAST_DEC_LOOP
+        safe_match_copy:
+#endif
+            if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error;   /* Error : offset outside buffers */
+            /* match starting within external dictionary */
+            if ((dict==usingExtDict) && (match < lowPrefix)) {
+                assert(dictEnd != NULL);
+                if (unlikely(op+length > oend-LASTLITERALS)) {
+                    if (partialDecoding) length = MIN(length, (size_t)(oend-op));
+                    else goto _output_error;   /* doesn't respect parsing restriction */
+                }
+
+                if (length <= (size_t)(lowPrefix-match)) {
+                    /* match fits entirely within external dictionary : just copy */
+                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);
+                    op += length;
+                } else {
+                    /* match stretches into both external dictionary and current block */
+                    size_t const copySize = (size_t)(lowPrefix - match);
+                    size_t const restSize = length - copySize;
+                    LZ4_memcpy(op, dictEnd - copySize, copySize);
+                    op += copySize;
+                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */
+                        BYTE* const endOfMatch = op + restSize;
+                        const BYTE* copyFrom = lowPrefix;
+                        while (op < endOfMatch) *op++ = *copyFrom++;
+                    } else {
+                        LZ4_memcpy(op, lowPrefix, restSize);
+                        op += restSize;
+                }   }
+                continue;
+            }
+            assert(match >= lowPrefix);
+
+            /* copy match within block */
+            cpy = op + length;
+
+            /* partialDecoding : may end anywhere within the block */
+            assert(op<=oend);
+            if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
+                size_t const mlen = MIN(length, (size_t)(oend-op));
+                const BYTE* const matchEnd = match + mlen;
+                BYTE* const copyEnd = op + mlen;
+                if (matchEnd > op) {   /* overlap copy */
+                    while (op < copyEnd) { *op++ = *match++; }
+                } else {
+                    LZ4_memcpy(op, match, mlen);
+                }
+                op = copyEnd;
+                if (op == oend) { break; }
+                continue;
+            }
+
+            if (unlikely(offset<8)) {
+                LZ4_write32(op, 0);   /* silence msan warning when offset==0 */
+                op[0] = match[0];
+                op[1] = match[1];
+                op[2] = match[2];
+                op[3] = match[3];
+                match += inc32table[offset];
+                LZ4_memcpy(op+4, match, 4);
+                match -= dec64table[offset];
+            } else {
+                LZ4_memcpy(op, match, 8);
+                match += 8;
+            }
+            op += 8;
+
+            if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
+                BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);
+                if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+                if (op < oCopyLimit) {
+                    LZ4_wildCopy8(op, match, oCopyLimit);
+                    match += oCopyLimit - op;
+                    op = oCopyLimit;
+                }
+                while (op < cpy) { *op++ = *match++; }
+            } else {
+                LZ4_memcpy(op, match, 8);
+                if (length > 16)  { LZ4_wildCopy8(op+8, match+8, cpy); }
+            }
+            op = cpy;   /* wildcopy correction */
+        }
+
+        /* end of decoding */
+        DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst));
+        return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */
+
+        /* Overflow error detected */
+    _output_error:
+        return (int) (-(((const char*)ip)-src))-1;
+    }
+}
+
+
+/*===== Instantiate the API decoding functions. =====*/
+
+LZ4_FORCE_O2
+int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,
+                                  decode_full_block, noDict,
+                                  (BYTE*)dest, NULL, 0);
+}
+
+LZ4_FORCE_O2
+int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)
+{
+    dstCapacity = MIN(targetOutputSize, dstCapacity);
+    return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,
+                                  partial_decode,
+                                  noDict, (BYTE*)dst, NULL, 0);
+}
+
+LZ4_FORCE_O2
+int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
+{
+    DEBUGLOG(5, "LZ4_decompress_fast");
+    return LZ4_decompress_unsafe_generic(
+                (const BYTE*)source, (BYTE*)dest, originalSize,
+                0, NULL, 0);
+}
+
+/*===== Instantiate a few more decoding cases, used more than once. =====*/
+
+LZ4_FORCE_O2 /* Exported, an obsolete API function. */
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+                                  decode_full_block, withPrefix64k,
+                                  (BYTE*)dest - 64 KB, NULL, 0);
+}
+
+LZ4_FORCE_O2
+static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)
+{
+    dstCapacity = MIN(targetOutputSize, dstCapacity);
+    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
+                                  partial_decode, withPrefix64k,
+                                  (BYTE*)dest - 64 KB, NULL, 0);
+}
+
+/* Another obsolete API function, paired with the previous one. */
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+{
+    return LZ4_decompress_unsafe_generic(
+                (const BYTE*)source, (BYTE*)dest, originalSize,
+                64 KB, NULL, 0);
+}
+
+LZ4_FORCE_O2
+static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,
+                                               size_t prefixSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+                                  decode_full_block, noDict,
+                                  (BYTE*)dest-prefixSize, NULL, 0);
+}
+
+LZ4_FORCE_O2
+static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,
+                                               size_t prefixSize)
+{
+    dstCapacity = MIN(targetOutputSize, dstCapacity);
+    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
+                                  partial_decode, noDict,
+                                  (BYTE*)dest-prefixSize, NULL, 0);
+}
+
+LZ4_FORCE_O2
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
+                                     int compressedSize, int maxOutputSize,
+                                     const void* dictStart, size_t dictSize)
+{
+    DEBUGLOG(5, "LZ4_decompress_safe_forceExtDict");
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+                                  decode_full_block, usingExtDict,
+                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+LZ4_FORCE_O2
+int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,
+                                     int compressedSize, int targetOutputSize, int dstCapacity,
+                                     const void* dictStart, size_t dictSize)
+{
+    dstCapacity = MIN(targetOutputSize, dstCapacity);
+    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
+                                  partial_decode, usingExtDict,
+                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+LZ4_FORCE_O2
+static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,
+                                       const void* dictStart, size_t dictSize)
+{
+    return LZ4_decompress_unsafe_generic(
+                (const BYTE*)source, (BYTE*)dest, originalSize,
+                0, (const BYTE*)dictStart, dictSize);
+}
+
+/* The "double dictionary" mode, for use with e.g. ring buffers: the first part
+ * of the dictionary is passed as prefix, and the second via dictStart + dictSize.
+ * These routines are used only once, in LZ4_decompress_*_continue().
+ */
+LZ4_FORCE_INLINE
+int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,
+                                   size_t prefixSize, const void* dictStart, size_t dictSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+                                  decode_full_block, usingExtDict,
+                                  (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
+}
+
+/*===== streaming decompression functions =====*/
+
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+LZ4_streamDecode_t* LZ4_createStreamDecode(void)
+{
+    LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));
+    return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));
+}
+
+int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
+{
+    if (LZ4_stream == NULL) { return 0; }  /* support free on NULL */
+    FREEMEM(LZ4_stream);
+    return 0;
+}
+#endif
+
+/*! LZ4_setStreamDecode() :
+ *  Use this function to instruct where to find the dictionary.
+ *  This function is not necessary if previous data is still available where it was decoded.
+ *  Loading a size of 0 is allowed (same effect as no dictionary).
+ * @return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
+{
+    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+    lz4sd->prefixSize = (size_t)dictSize;
+    if (dictSize) {
+        assert(dictionary != NULL);
+        lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
+    } else {
+        lz4sd->prefixEnd = (const BYTE*) dictionary;
+    }
+    lz4sd->externalDict = NULL;
+    lz4sd->extDictSize  = 0;
+    return 1;
+}
+
+/*! LZ4_decoderRingBufferSize() :
+ *  when setting a ring buffer for streaming decompression (optional scenario),
+ *  provides the minimum size of this ring buffer
+ *  to be compatible with any source respecting maxBlockSize condition.
+ *  Note : in a ring buffer scenario,
+ *  blocks are presumed decompressed next to each other.
+ *  When not enough space remains for next block (remainingSize < maxBlockSize),
+ *  decoding resumes from beginning of ring buffer.
+ * @return : minimum ring buffer size,
+ *           or 0 if there is an error (invalid maxBlockSize).
+ */
+int LZ4_decoderRingBufferSize(int maxBlockSize)
+{
+    if (maxBlockSize < 0) return 0;
+    if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;
+    if (maxBlockSize < 16) maxBlockSize = 16;
+    return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);
+}
+
+/*
+*_continue() :
+    These decoding functions allow decompression of multiple blocks in "streaming" mode.
+    Previously decoded blocks must still be available at the memory position where they were decoded.
+    If it's not possible, save the relevant part of decoded data into a safe buffer,
+    and indicate where it stands using LZ4_setStreamDecode()
+*/
+LZ4_FORCE_O2
+int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+    int result;
+
+    if (lz4sd->prefixSize == 0) {
+        /* The first call, no dictionary yet. */
+        assert(lz4sd->extDictSize == 0);
+        result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize = (size_t)result;
+        lz4sd->prefixEnd = (BYTE*)dest + result;
+    } else if (lz4sd->prefixEnd == (BYTE*)dest) {
+        /* They're rolling the current segment. */
+        if (lz4sd->prefixSize >= 64 KB - 1)
+            result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
+        else if (lz4sd->extDictSize == 0)
+            result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,
+                                                         lz4sd->prefixSize);
+        else
+            result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,
+                                                    lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize += (size_t)result;
+        lz4sd->prefixEnd  += result;
+    } else {
+        /* The buffer wraps around, or they're switching to another buffer. */
+        lz4sd->extDictSize = lz4sd->prefixSize;
+        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+        result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,
+                                                  lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize = (size_t)result;
+        lz4sd->prefixEnd  = (BYTE*)dest + result;
+    }
+
+    return result;
+}
+
+LZ4_FORCE_O2 int
+LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,
+                        const char* source, char* dest, int originalSize)
+{
+    LZ4_streamDecode_t_internal* const lz4sd =
+        (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);
+    int result;
+
+    DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize);
+    assert(originalSize >= 0);
+
+    if (lz4sd->prefixSize == 0) {
+        DEBUGLOG(5, "first invocation : no prefix nor extDict");
+        assert(lz4sd->extDictSize == 0);
+        result = LZ4_decompress_fast(source, dest, originalSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize = (size_t)originalSize;
+        lz4sd->prefixEnd = (BYTE*)dest + originalSize;
+    } else if (lz4sd->prefixEnd == (BYTE*)dest) {
+        DEBUGLOG(5, "continue using existing prefix");
+        result = LZ4_decompress_unsafe_generic(
+                        (const BYTE*)source, (BYTE*)dest, originalSize,
+                        lz4sd->prefixSize,
+                        lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize += (size_t)originalSize;
+        lz4sd->prefixEnd  += originalSize;
+    } else {
+        DEBUGLOG(5, "prefix becomes extDict");
+        lz4sd->extDictSize = lz4sd->prefixSize;
+        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+        result = LZ4_decompress_fast_extDict(source, dest, originalSize,
+                                             lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize = (size_t)originalSize;
+        lz4sd->prefixEnd  = (BYTE*)dest + originalSize;
+    }
+
+    return result;
+}
+
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+    These decoding functions work the same as "_continue" ones,
+    the dictionary must be explicitly provided within parameters
+*/
+
+int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+    if (dictSize==0)
+        return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
+    if (dictStart+dictSize == dest) {
+        if (dictSize >= 64 KB - 1) {
+            return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
+        }
+        assert(dictSize >= 0);
+        return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);
+    }
+    assert(dictSize >= 0);
+    return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);
+}
+
+int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)
+{
+    if (dictSize==0)
+        return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);
+    if (dictStart+dictSize == dest) {
+        if (dictSize >= 64 KB - 1) {
+            return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);
+        }
+        assert(dictSize >= 0);
+        return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);
+    }
+    assert(dictSize >= 0);
+    return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);
+}
+
+int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
+{
+    if (dictSize==0 || dictStart+dictSize == dest)
+        return LZ4_decompress_unsafe_generic(
+                        (const BYTE*)source, (BYTE*)dest, originalSize,
+                        (size_t)dictSize, NULL, 0);
+    assert(dictSize >= 0);
+    return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);
+}
+
+
+/*=*************************************************
+*  Obsolete Functions
+***************************************************/
+/* obsolete compression functions */
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)
+{
+    return LZ4_compress_default(source, dest, inputSize, maxOutputSize);
+}
+int LZ4_compress(const char* src, char* dest, int srcSize)
+{
+    return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));
+}
+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)
+{
+    return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);
+}
+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)
+{
+    return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);
+}
+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)
+{
+    return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);
+}
+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)
+{
+    return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);
+}
+
+/*
+These decompression functions are deprecated and should no longer be used.
+They are only provided here for compatibility with older user programs.
+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
+*/
+int LZ4_uncompress (const char* source, char* dest, int outputSize)
+{
+    return LZ4_decompress_fast(source, dest, outputSize);
+}
+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)
+{
+    return LZ4_decompress_safe(source, dest, isize, maxOutputSize);
+}
+
+/* Obsolete Streaming functions */
+
+int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }
+
+int LZ4_resetStreamState(void* state, char* inputBuffer)
+{
+    (void)inputBuffer;
+    LZ4_resetStream((LZ4_stream_t*)state);
+    return 0;
+}
+
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+void* LZ4_create (char* inputBuffer)
+{
+    (void)inputBuffer;
+    return LZ4_createStream();
+}
+#endif
+
+char* LZ4_slideInputBuffer (void* state)
+{
+    /* avoid const char * -> char * conversion warning */
+    return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;
+}
+
+#endif   /* LZ4_COMMONDEFS_ONLY */
diff --git a/3rdparty/lz4/lz4.h b/3rdparty/lz4/lz4.h
new file mode 100644
index 00000000000..f85b0389a0c
--- /dev/null
+++ b/3rdparty/lz4/lz4.h
@@ -0,0 +1,862 @@
+/*
+ *  LZ4 - Fast LZ compression algorithm
+ *  Header File
+ *  Copyright (C) 2011-2020, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+    - LZ4 homepage : http://www.lz4.org
+    - LZ4 source repository : https://github.com/lz4/lz4
+*/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef LZ4_H_2983827168210
+#define LZ4_H_2983827168210
+
+/* --- Dependency --- */
+#include <stddef.h>   /* size_t */
+
+
+/**
+  Introduction
+
+  LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
+  scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+  multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+  The LZ4 compression library provides in-memory compression and decompression functions.
+  It gives full buffer control to user.
+  Compression can be done in:
+    - a single step (described as Simple Functions)
+    - a single step, reusing a context (described in Advanced Functions)
+    - unbounded multiple steps (described as Streaming compression)
+
+  lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
+  Decompressing such a compressed block requires additional metadata.
+  Exact metadata depends on exact decompression function.
+  For the typical case of LZ4_decompress_safe(),
+  metadata includes block's compressed size, and maximum bound of decompressed size.
+  Each application is free to encode and pass such metadata in whichever way it wants.
+
+  lz4.h only handle blocks, it can not generate Frames.
+
+  Blocks are different from Frames (doc/lz4_Frame_format.md).
+  Frames bundle both blocks and metadata in a specified manner.
+  Embedding metadata is required for compressed data to be self-contained and portable.
+  Frame format is delivered through a companion API, declared in lz4frame.h.
+  The `lz4` CLI can only manage frames.
+*/
+
+/*^***************************************************************
+*  Export parameters
+*****************************************************************/
+/*
+*  LZ4_DLL_EXPORT :
+*  Enable exporting of functions when building a Windows DLL
+*  LZ4LIB_VISIBILITY :
+*  Control library symbols visibility.
+*/
+#ifndef LZ4LIB_VISIBILITY
+#  if defined(__GNUC__) && (__GNUC__ >= 4)
+#    define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
+#  else
+#    define LZ4LIB_VISIBILITY
+#  endif
+#endif
+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
+#  define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
+#  define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+#  define LZ4LIB_API LZ4LIB_VISIBILITY
+#endif
+
+/*! LZ4_FREESTANDING :
+ *  When this macro is set to 1, it enables "freestanding mode" that is
+ *  suitable for typical freestanding environment which doesn't support
+ *  standard C library.
+ *
+ *  - LZ4_FREESTANDING is a compile-time switch.
+ *  - It requires the following macros to be defined:
+ *    LZ4_memcpy, LZ4_memmove, LZ4_memset.
+ *  - It only enables LZ4/HC functions which don't use heap.
+ *    All LZ4F_* functions are not supported.
+ *  - See tests/freestanding.c to check its basic setup.
+ */
+#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
+#  define LZ4_HEAPMODE 0
+#  define LZ4HC_HEAPMODE 0
+#  define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
+#  if !defined(LZ4_memcpy)
+#    error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
+#  endif
+#  if !defined(LZ4_memset)
+#    error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
+#  endif
+#  if !defined(LZ4_memmove)
+#    error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
+#  endif
+#elif ! defined(LZ4_FREESTANDING)
+#  define LZ4_FREESTANDING 0
+#endif
+
+
+/*------   Version   ------*/
+#define LZ4_VERSION_MAJOR    1    /* for breaking interface changes  */
+#define LZ4_VERSION_MINOR    9    /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE  4    /* for tweaks, bug-fixes, or development */
+
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)  /* requires v1.7.3+ */
+
+LZ4LIB_API int LZ4_versionNumber (void);  /**< library version number; useful to check dll version; requires v1.3.0+ */
+LZ4LIB_API const char* LZ4_versionString (void);   /**< library version string; useful to check dll version; requires v1.7.5+ */
+
+
+/*-************************************
+*  Tuning parameter
+**************************************/
+#define LZ4_MEMORY_USAGE_MIN 10
+#define LZ4_MEMORY_USAGE_DEFAULT 14
+#define LZ4_MEMORY_USAGE_MAX 20
+
+/*!
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )
+ * Increasing memory usage improves compression ratio, at the cost of speed.
+ * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#ifndef LZ4_MEMORY_USAGE
+# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
+#endif
+
+#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
+#  error "LZ4_MEMORY_USAGE is too small !"
+#endif
+
+#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
+#  error "LZ4_MEMORY_USAGE is too large !"
+#endif
+
+/*-************************************
+*  Simple Functions
+**************************************/
+/*! LZ4_compress_default() :
+ *  Compresses 'srcSize' bytes from buffer 'src'
+ *  into already allocated 'dst' buffer of size 'dstCapacity'.
+ *  Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
+ *  It also runs faster, so it's a recommended setting.
+ *  If the function cannot compress 'src' into a more limited 'dst' budget,
+ *  compression stops *immediately*, and the function result is zero.
+ *  In which case, 'dst' content is undefined (invalid).
+ *      srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
+ *      dstCapacity : size of buffer 'dst' (which must be already allocated)
+ *     @return  : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
+ *                or 0 if compression fails
+ * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
+ */
+LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
+
+/*! LZ4_decompress_safe() :
+ * @compressedSize : is the exact complete size of the compressed block.
+ * @dstCapacity : is the size of destination buffer (which must be already allocated),
+ *                is an upper bound of decompressed size.
+ * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
+ *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).
+ *           If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ * Note 1 : This function is protected against malicious data packets :
+ *          it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
+ *          even if the compressed block is maliciously modified to order the decoder to do these actions.
+ *          In such case, the decoder stops immediately, and considers the compressed block malformed.
+ * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
+ *          The implementation is free to send / store / derive this information in whichever way is most beneficial.
+ *          If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
+ */
+LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
+
+
+/*-************************************
+*  Advanced Functions
+**************************************/
+#define LZ4_MAX_INPUT_SIZE        0x7E000000   /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize)  ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+/*! LZ4_compressBound() :
+    Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
+    This function is primarily useful for memory allocation purposes (destination buffer size).
+    Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
+    Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
+        inputSize  : max supported value is LZ4_MAX_INPUT_SIZE
+        return : maximum output size in a "worst case" scenario
+              or 0, if input size is incorrect (too large or negative)
+*/
+LZ4LIB_API int LZ4_compressBound(int inputSize);
+
+/*! LZ4_compress_fast() :
+    Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
+    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
+    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
+    An acceleration value of "1" is the same as regular LZ4_compress_default()
+    Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
+    Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
+*/
+LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*! LZ4_compress_fast_extState() :
+ *  Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
+ *  Use LZ4_sizeofState() to know how much memory must be allocated,
+ *  and allocate it on 8-bytes boundaries (using `malloc()` typically).
+ *  Then, provide this buffer as `void* state` to compression function.
+ */
+LZ4LIB_API int LZ4_sizeofState(void);
+LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*! LZ4_compress_destSize() :
+ *  Reverse the logic : compresses as much data as possible from 'src' buffer
+ *  into already allocated buffer 'dst', of size >= 'targetDestSize'.
+ *  This function either compresses the entire 'src' content into 'dst' if it's large enough,
+ *  or fill 'dst' buffer completely with as much data as possible from 'src'.
+ *  note: acceleration parameter is fixed to "default".
+ *
+ * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
+ *               New value is necessarily <= input value.
+ * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
+ *           or 0 if compression fails.
+ *
+ * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+):
+ *        the produced compressed content could, in specific circumstances,
+ *        require to be decompressed into a destination buffer larger
+ *        by at least 1 byte than the content to decompress.
+ *        If an application uses `LZ4_compress_destSize()`,
+ *        it's highly recommended to update liblz4 to v1.9.2 or better.
+ *        If this can't be done or ensured,
+ *        the receiving decompression function should provide
+ *        a dstCapacity which is > decompressedSize, by at least 1 byte.
+ *        See https://github.com/lz4/lz4/issues/859 for details
+ */
+LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
+
+
+/*! LZ4_decompress_safe_partial() :
+ *  Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
+ *  into destination buffer 'dst' of size 'dstCapacity'.
+ *  Up to 'targetOutputSize' bytes will be decoded.
+ *  The function stops decoding on reaching this objective.
+ *  This can be useful to boost performance
+ *  whenever only the beginning of a block is required.
+ *
+ * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
+ *           If source stream is detected malformed, function returns a negative result.
+ *
+ *  Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
+ *
+ *  Note 2 : targetOutputSize must be <= dstCapacity
+ *
+ *  Note 3 : this function effectively stops decoding on reaching targetOutputSize,
+ *           so dstCapacity is kind of redundant.
+ *           This is because in older versions of this function,
+ *           decoding operation would still write complete sequences.
+ *           Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
+ *           it could write more bytes, though only up to dstCapacity.
+ *           Some "margin" used to be required for this operation to work properly.
+ *           Thankfully, this is no longer necessary.
+ *           The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
+ *
+ *  Note 4 : If srcSize is the exact size of the block,
+ *           then targetOutputSize can be any value,
+ *           including larger than the block's decompressed size.
+ *           The function will, at most, generate block's decompressed size.
+ *
+ *  Note 5 : If srcSize is _larger_ than block's compressed size,
+ *           then targetOutputSize **MUST** be <= block's decompressed size.
+ *           Otherwise, *silent corruption will occur*.
+ */
+LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
+
+
+/*-*********************************************
+*  Streaming Compression Functions
+***********************************************/
+typedef union LZ4_stream_u LZ4_stream_t;  /* incomplete type (defined later) */
+
+/**
+ Note about RC_INVOKED
+
+ - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
+   https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
+
+ - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
+   and reports warning "RC4011: identifier truncated".
+
+ - To eliminate the warning, we surround long preprocessor symbol with
+   "#if !defined(RC_INVOKED) ... #endif" block that means
+   "skip this block when rc.exe is trying to read it".
+*/
+#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
+LZ4LIB_API int           LZ4_freeStream (LZ4_stream_t* streamPtr);
+#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
+#endif
+
+/*! LZ4_resetStream_fast() : v1.9.0+
+ *  Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
+ *  (e.g., LZ4_compress_fast_continue()).
+ *
+ *  An LZ4_stream_t must be initialized once before usage.
+ *  This is automatically done when created by LZ4_createStream().
+ *  However, should the LZ4_stream_t be simply declared on stack (for example),
+ *  it's necessary to initialize it first, using LZ4_initStream().
+ *
+ *  After init, start any new stream with LZ4_resetStream_fast().
+ *  A same LZ4_stream_t can be re-used multiple times consecutively
+ *  and compress multiple streams,
+ *  provided that it starts each new stream with LZ4_resetStream_fast().
+ *
+ *  LZ4_resetStream_fast() is much faster than LZ4_initStream(),
+ *  but is not compatible with memory regions containing garbage data.
+ *
+ *  Note: it's only useful to call LZ4_resetStream_fast()
+ *        in the context of streaming compression.
+ *        The *extState* functions perform their own resets.
+ *        Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
+ */
+LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
+
+/*! LZ4_loadDict() :
+ *  Use this function to reference a static dictionary into LZ4_stream_t.
+ *  The dictionary must remain available during compression.
+ *  LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
+ *  The same dictionary will have to be loaded on decompression side for successful decoding.
+ *  Dictionary are useful for better compression of small data (KB range).
+ *  While LZ4 accept any input as dictionary,
+ *  results are generally better when using Zstandard's Dictionary Builder.
+ *  Loading a size of 0 is allowed, and is the same as reset.
+ * @return : loaded dictionary size, in bytes (necessarily <= 64 KB)
+ */
+LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
+
+/*! LZ4_compress_fast_continue() :
+ *  Compress 'src' content using data from previously compressed blocks, for better compression ratio.
+ * 'dst' buffer must be already allocated.
+ *  If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ *
+ * @return : size of compressed block
+ *           or 0 if there is an error (typically, cannot fit into 'dst').
+ *
+ *  Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
+ *           Each block has precise boundaries.
+ *           Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
+ *           It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
+ *
+ *  Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
+ *
+ *  Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
+ *           Make sure that buffers are separated, by at least one byte.
+ *           This construction ensures that each block only depends on previous block.
+ *
+ *  Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
+ *
+ *  Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
+ */
+LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_saveDict() :
+ *  If last 64KB data cannot be guaranteed to remain available at its current memory location,
+ *  save it into a safer place (char* safeBuffer).
+ *  This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
+ *  but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
+ * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
+ */
+LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
+
+
+/*-**********************************************
+*  Streaming Decompression Functions
+*  Bufferless synchronous API
+************************************************/
+typedef union LZ4_streamDecode_u LZ4_streamDecode_t;   /* tracking context */
+
+/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
+ *  creation / destruction of streaming decompression tracking context.
+ *  A tracking context can be re-used multiple times.
+ */
+#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
+LZ4LIB_API int                 LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
+#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
+#endif
+
+/*! LZ4_setStreamDecode() :
+ *  An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
+ *  Use this function to start decompression of a new stream of blocks.
+ *  A dictionary can optionally be set. Use NULL or size 0 for a reset order.
+ *  Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
+ * @return : 1 if OK, 0 if error
+ */
+LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
+
+/*! LZ4_decoderRingBufferSize() : v1.8.2+
+ *  Note : in a ring buffer scenario (optional),
+ *  blocks are presumed decompressed next to each other
+ *  up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
+ *  at which stage it resumes from beginning of ring buffer.
+ *  When setting such a ring buffer for streaming decompression,
+ *  provides the minimum size of this ring buffer
+ *  to be compatible with any source respecting maxBlockSize condition.
+ * @return : minimum ring buffer size,
+ *           or 0 if there is an error (invalid maxBlockSize).
+ */
+LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
+#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize))  /* for static allocation; maxBlockSize presumed valid */
+
+/*! LZ4_decompress_safe_continue() :
+ *  This decoding function allows decompression of consecutive blocks in "streaming" mode.
+ *  The difference with the usual independent blocks is that
+ *  new blocks are allowed to find references into former blocks.
+ *  A block is an unsplittable entity, and must be presented entirely to the decompression function.
+ *  LZ4_decompress_safe_continue() only accepts one block at a time.
+ *  It's modeled after `LZ4_decompress_safe()` and behaves similarly.
+ *
+ * @LZ4_streamDecode : decompression state, tracking the position in memory of past data
+ * @compressedSize : exact complete size of one compressed block.
+ * @dstCapacity : size of destination buffer (which must be already allocated),
+ *                must be an upper bound of decompressed size.
+ * @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
+ *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).
+ *           If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ *
+ *  The last 64KB of previously decoded data *must* remain available and unmodified
+ *  at the memory position where they were previously decoded.
+ *  If less than 64KB of data has been decoded, all the data must be present.
+ *
+ *  Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
+ *  - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
+ *    maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
+ *    In which case, encoding and decoding buffers do not need to be synchronized.
+ *    Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
+ *  - Synchronized mode :
+ *    Decompression buffer size is _exactly_ the same as compression buffer size,
+ *    and follows exactly same update rule (block boundaries at same positions),
+ *    and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
+ *    _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
+ *  - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ *    In which case, encoding and decoding buffers do not need to be synchronized,
+ *    and encoding ring buffer can have any size, including small ones ( < 64 KB).
+ *
+ *  Whenever these conditions are not possible,
+ *  save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
+ *  then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
+*/
+LZ4LIB_API int
+LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
+                        const char* src, char* dst,
+                        int srcSize, int dstCapacity);
+
+
+/*! LZ4_decompress_safe_usingDict() :
+ *  Works the same as
+ *  a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue()
+ *  However, it's stateless: it doesn't need any LZ4_streamDecode_t state.
+ *  Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
+ *  Performance tip : Decompression speed can be substantially increased
+ *                    when dst == dictStart + dictSize.
+ */
+LZ4LIB_API int
+LZ4_decompress_safe_usingDict(const char* src, char* dst,
+                              int srcSize, int dstCapacity,
+                              const char* dictStart, int dictSize);
+
+/*! LZ4_decompress_safe_partial_usingDict() :
+ *  Behaves the same as LZ4_decompress_safe_partial()
+ *  with the added ability to specify a memory segment for past data.
+ *  Performance tip : Decompression speed can be substantially increased
+ *                    when dst == dictStart + dictSize.
+ */
+LZ4LIB_API int
+LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
+                                      int compressedSize,
+                                      int targetOutputSize, int maxOutputSize,
+                                      const char* dictStart, int dictSize);
+
+#endif /* LZ4_H_2983827168210 */
+
+
+/*^*************************************
+ * !!!!!!   STATIC LINKING ONLY   !!!!!!
+ ***************************************/
+
+/*-****************************************************************************
+ * Experimental section
+ *
+ * Symbols declared in this section must be considered unstable. Their
+ * signatures or semantics may change, or they may be removed altogether in the
+ * future. They are therefore only safe to depend on when the caller is
+ * statically linked against the library.
+ *
+ * To protect against unsafe usage, not only are the declarations guarded,
+ * the definitions are hidden by default
+ * when building LZ4 as a shared/dynamic library.
+ *
+ * In order to access these declarations,
+ * define LZ4_STATIC_LINKING_ONLY in your application
+ * before including LZ4's headers.
+ *
+ * In order to make their implementations accessible dynamically, you must
+ * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
+ ******************************************************************************/
+
+#ifdef LZ4_STATIC_LINKING_ONLY
+
+#ifndef LZ4_STATIC_3504398509
+#define LZ4_STATIC_3504398509
+
+#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
+#define LZ4LIB_STATIC_API LZ4LIB_API
+#else
+#define LZ4LIB_STATIC_API
+#endif
+
+
+/*! LZ4_compress_fast_extState_fastReset() :
+ *  A variant of LZ4_compress_fast_extState().
+ *
+ *  Using this variant avoids an expensive initialization step.
+ *  It is only safe to call if the state buffer is known to be correctly initialized already
+ *  (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
+ *  From a high level, the difference is that
+ *  this function initializes the provided state with a call to something like LZ4_resetStream_fast()
+ *  while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
+ */
+LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_attach_dictionary() :
+ *  This is an experimental API that allows
+ *  efficient use of a static dictionary many times.
+ *
+ *  Rather than re-loading the dictionary buffer into a working context before
+ *  each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
+ *  working LZ4_stream_t, this function introduces a no-copy setup mechanism,
+ *  in which the working stream references the dictionary stream in-place.
+ *
+ *  Several assumptions are made about the state of the dictionary stream.
+ *  Currently, only streams which have been prepared by LZ4_loadDict() should
+ *  be expected to work.
+ *
+ *  Alternatively, the provided dictionaryStream may be NULL,
+ *  in which case any existing dictionary stream is unset.
+ *
+ *  If a dictionary is provided, it replaces any pre-existing stream history.
+ *  The dictionary contents are the only history that can be referenced and
+ *  logically immediately precede the data compressed in the first subsequent
+ *  compression call.
+ *
+ *  The dictionary will only remain attached to the working stream through the
+ *  first compression call, at the end of which it is cleared. The dictionary
+ *  stream (and source buffer) must remain in-place / accessible / unchanged
+ *  through the completion of the first compression call on the stream.
+ */
+LZ4LIB_STATIC_API void
+LZ4_attach_dictionary(LZ4_stream_t* workingStream,
+                const LZ4_stream_t* dictionaryStream);
+
+
+/*! In-place compression and decompression
+ *
+ * It's possible to have input and output sharing the same buffer,
+ * for highly constrained memory environments.
+ * In both cases, it requires input to lay at the end of the buffer,
+ * and decompression to start at beginning of the buffer.
+ * Buffer size must feature some margin, hence be larger than final size.
+ *
+ * |<------------------------buffer--------------------------------->|
+ *                             |<-----------compressed data--------->|
+ * |<-----------decompressed size------------------>|
+ *                                                  |<----margin---->|
+ *
+ * This technique is more useful for decompression,
+ * since decompressed size is typically larger,
+ * and margin is short.
+ *
+ * In-place decompression will work inside any buffer
+ * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
+ * This presumes that decompressedSize > compressedSize.
+ * Otherwise, it means compression actually expanded data,
+ * and it would be more efficient to store such data with a flag indicating it's not compressed.
+ * This can happen when data is not compressible (already compressed, or encrypted).
+ *
+ * For in-place compression, margin is larger, as it must be able to cope with both
+ * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
+ * and data expansion, which can happen when input is not compressible.
+ * As a consequence, buffer size requirements are much higher,
+ * and memory savings offered by in-place compression are more limited.
+ *
+ * There are ways to limit this cost for compression :
+ * - Reduce history size, by modifying LZ4_DISTANCE_MAX.
+ *   Note that it is a compile-time constant, so all compressions will apply this limit.
+ *   Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
+ *   so it's a reasonable trick when inputs are known to be small.
+ * - Require the compressor to deliver a "maximum compressed size".
+ *   This is the `dstCapacity` parameter in `LZ4_compress*()`.
+ *   When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
+ *   in which case, the return code will be 0 (zero).
+ *   The caller must be ready for these cases to happen,
+ *   and typically design a backup scheme to send data uncompressed.
+ * The combination of both techniques can significantly reduce
+ * the amount of margin required for in-place compression.
+ *
+ * In-place compression can work in any buffer
+ * which size is >= (maxCompressedSize)
+ * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
+ * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
+ * so it's possible to reduce memory requirements by playing with them.
+ */
+
+#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize)          (((compressedSize) >> 8) + 32)
+#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize)   ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize))  /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
+
+#ifndef LZ4_DISTANCE_MAX   /* history window size; can be user-defined at compile time */
+#  define LZ4_DISTANCE_MAX 65535   /* set to maximum value by default */
+#endif
+
+#define LZ4_COMPRESS_INPLACE_MARGIN                           (LZ4_DISTANCE_MAX + 32)   /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
+#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize)   ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN)  /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
+
+#endif   /* LZ4_STATIC_3504398509 */
+#endif   /* LZ4_STATIC_LINKING_ONLY */
+
+
+
+#ifndef LZ4_H_98237428734687
+#define LZ4_H_98237428734687
+
+/*-************************************************************
+ *  Private Definitions
+ **************************************************************
+ * Do not use these definitions directly.
+ * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
+ * Accessing members will expose user code to API and/or ABI break in future versions of the library.
+ **************************************************************/
+#define LZ4_HASHLOG   (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG)       /* required as macro for static allocation */
+
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# include <stdint.h>
+  typedef  int8_t  LZ4_i8;
+  typedef uint8_t  LZ4_byte;
+  typedef uint16_t LZ4_u16;
+  typedef uint32_t LZ4_u32;
+#else
+  typedef   signed char  LZ4_i8;
+  typedef unsigned char  LZ4_byte;
+  typedef unsigned short LZ4_u16;
+  typedef unsigned int   LZ4_u32;
+#endif
+
+/*! LZ4_stream_t :
+ *  Never ever use below internal definitions directly !
+ *  These definitions are not API/ABI safe, and may change in future versions.
+ *  If you need static allocation, declare or allocate an LZ4_stream_t object.
+**/
+
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
+    LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
+    const LZ4_byte* dictionary;
+    const LZ4_stream_t_internal* dictCtx;
+    LZ4_u32 currentOffset;
+    LZ4_u32 tableType;
+    LZ4_u32 dictSize;
+    /* Implicit padding to ensure structure is aligned */
+};
+
+#define LZ4_STREAM_MINSIZE  ((1UL << LZ4_MEMORY_USAGE) + 32)  /* static size, for inter-version compatibility */
+union LZ4_stream_u {
+    char minStateSize[LZ4_STREAM_MINSIZE];
+    LZ4_stream_t_internal internal_donotuse;
+}; /* previously typedef'd to LZ4_stream_t */
+
+
+/*! LZ4_initStream() : v1.9.0+
+ *  An LZ4_stream_t structure must be initialized at least once.
+ *  This is automatically done when invoking LZ4_createStream(),
+ *  but it's not when the structure is simply declared on stack (for example).
+ *
+ *  Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
+ *  It can also initialize any arbitrary buffer of sufficient size,
+ *  and will @return a pointer of proper type upon initialization.
+ *
+ *  Note : initialization fails if size and alignment conditions are not respected.
+ *         In which case, the function will @return NULL.
+ *  Note2: An LZ4_stream_t structure guarantees correct alignment and size.
+ *  Note3: Before v1.9.0, use LZ4_resetStream() instead
+**/
+LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
+
+
+/*! LZ4_streamDecode_t :
+ *  Never ever use below internal definitions directly !
+ *  These definitions are not API/ABI safe, and may change in future versions.
+ *  If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
+**/
+typedef struct {
+    const LZ4_byte* externalDict;
+    const LZ4_byte* prefixEnd;
+    size_t extDictSize;
+    size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#define LZ4_STREAMDECODE_MINSIZE 32
+union LZ4_streamDecode_u {
+    char minStateSize[LZ4_STREAMDECODE_MINSIZE];
+    LZ4_streamDecode_t_internal internal_donotuse;
+} ;   /* previously typedef'd to LZ4_streamDecode_t */
+
+
+
+/*-************************************
+*  Obsolete Functions
+**************************************/
+
+/*! Deprecation warnings
+ *
+ *  Deprecated functions make the compiler generate a warning when invoked.
+ *  This is meant to invite users to update their source code.
+ *  Should deprecation warnings be a problem, it is generally possible to disable them,
+ *  typically with -Wno-deprecated-declarations for gcc
+ *  or _CRT_SECURE_NO_WARNINGS in Visual.
+ *
+ *  Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
+ *  before including the header file.
+ */
+#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
+#  define LZ4_DEPRECATED(message)   /* disable deprecation warnings */
+#else
+#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+#    define LZ4_DEPRECATED(message) [[deprecated(message)]]
+#  elif defined(_MSC_VER)
+#    define LZ4_DEPRECATED(message) __declspec(deprecated(message))
+#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
+#    define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
+#  elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
+#    define LZ4_DEPRECATED(message) __attribute__((deprecated))
+#  else
+#    pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
+#    define LZ4_DEPRECATED(message)   /* disabled */
+#  endif
+#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
+
+/*! Obsolete compression functions (since v1.7.3) */
+LZ4_DEPRECATED("use LZ4_compress_default() instead")       LZ4LIB_API int LZ4_compress               (const char* src, char* dest, int srcSize);
+LZ4_DEPRECATED("use LZ4_compress_default() instead")       LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState               (void* state, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue                (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue  (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/*! Obsolete decompression functions (since v1.8.0) */
+LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
+LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
+
+/* Obsolete streaming functions (since v1.7.0)
+ * degraded functionality; do not use!
+ *
+ * In order to perform streaming compression, these functions depended on data
+ * that is no longer tracked in the state. They have been preserved as well as
+ * possible: using them will still produce a correct output. However, they don't
+ * actually retain any history between compression calls. The compression ratio
+ * achieved will therefore be no better than compressing each chunk
+ * independently.
+ */
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int   LZ4_sizeofStreamState(void);
+LZ4_DEPRECATED("Use LZ4_resetStream() instead")  LZ4LIB_API int   LZ4_resetStreamState(void* state, char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_saveDict() instead")     LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
+
+/*! Obsolete streaming decoding functions (since v1.7.0) */
+LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
+LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+
+/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
+ *  These functions used to be faster than LZ4_decompress_safe(),
+ *  but this is no longer the case. They are now slower.
+ *  This is because LZ4_decompress_fast() doesn't know the input size,
+ *  and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
+ *  On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
+ *  As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
+ *
+ *  The last remaining LZ4_decompress_fast() specificity is that
+ *  it can decompress a block without knowing its compressed size.
+ *  Such functionality can be achieved in a more secure manner
+ *  by employing LZ4_decompress_safe_partial().
+ *
+ *  Parameters:
+ *  originalSize : is the uncompressed size to regenerate.
+ *                 `dst` must be already allocated, its size must be >= 'originalSize' bytes.
+ * @return : number of bytes read from source buffer (== compressed size).
+ *           The function expects to finish at block's end exactly.
+ *           If the source stream is detected malformed, the function stops decoding and returns a negative result.
+ *  note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
+ *         However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
+ *         Also, since match offsets are not validated, match reads from 'src' may underflow too.
+ *         These issues never happen if input (compressed) data is correct.
+ *         But they may happen if input data is invalid (error or intentional tampering).
+ *         As a consequence, use these functions in trusted environments with trusted data **only**.
+ */
+LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead")
+LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
+LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
+LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
+LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead")
+LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
+
+/*! LZ4_resetStream() :
+ *  An LZ4_stream_t structure must be initialized at least once.
+ *  This is done with LZ4_initStream(), or LZ4_resetStream().
+ *  Consider switching to LZ4_initStream(),
+ *  invoking LZ4_resetStream() will trigger deprecation warnings in the future.
+ */
+LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+
+#endif /* LZ4_H_98237428734687 */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/3rdparty/lz4/lz4file.c b/3rdparty/lz4/lz4file.c
new file mode 100644
index 00000000000..19ff69e02ad
--- /dev/null
+++ b/3rdparty/lz4/lz4file.c
@@ -0,0 +1,312 @@
+/*
+ * LZ4 file library
+ * Copyright (C) 2022, Xiaomi Inc.
+ *
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following disclaimer
+ *   in the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at :
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "lz4.h"
+#include "lz4file.h"
+
+struct LZ4_readFile_s {
+  LZ4F_dctx* dctxPtr;
+  FILE* fp;
+  LZ4_byte* srcBuf;
+  size_t srcBufNext;
+  size_t srcBufSize;
+  size_t srcBufMaxSize;
+};
+
+struct LZ4_writeFile_s {
+  LZ4F_cctx* cctxPtr;
+  FILE* fp;
+  LZ4_byte* dstBuf;
+  size_t maxWriteSize;
+  size_t dstBufMaxSize;
+  LZ4F_errorCode_t errCode;
+};
+
+LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp)
+{
+  char buf[LZ4F_HEADER_SIZE_MAX];
+  size_t consumedSize;
+  LZ4F_errorCode_t ret;
+  LZ4F_frameInfo_t info;
+
+  if (fp == NULL || lz4fRead == NULL) {
+    return -LZ4F_ERROR_GENERIC;
+  }
+
+  *lz4fRead = (LZ4_readFile_t*)calloc(1, sizeof(LZ4_readFile_t));
+  if (*lz4fRead == NULL) {
+    return -LZ4F_ERROR_allocation_failed;
+  }
+
+  ret = LZ4F_createDecompressionContext(&(*lz4fRead)->dctxPtr, LZ4F_getVersion());
+  if (LZ4F_isError(ret)) {
+    free(*lz4fRead);
+    return ret;
+  }
+
+  (*lz4fRead)->fp = fp;
+  consumedSize = fread(buf, 1, sizeof(buf), (*lz4fRead)->fp);
+  if (consumedSize != sizeof(buf)) {
+    LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);
+    free(*lz4fRead);
+    return -LZ4F_ERROR_GENERIC;
+  }
+
+  ret = LZ4F_getFrameInfo((*lz4fRead)->dctxPtr, &info, buf, &consumedSize);
+  if (LZ4F_isError(ret)) {
+      LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);
+      free(*lz4fRead);
+      return ret;
+    }
+
+  switch (info.blockSizeID) {
+    case LZ4F_default :
+    case LZ4F_max64KB :
+      (*lz4fRead)->srcBufMaxSize = 64 * 1024;
+      break;
+    case LZ4F_max256KB:
+      (*lz4fRead)->srcBufMaxSize = 256 * 1024;
+      break;
+    case LZ4F_max1MB:
+      (*lz4fRead)->srcBufMaxSize = 1 * 1024 * 1024;
+      break;
+    case LZ4F_max4MB:
+      (*lz4fRead)->srcBufMaxSize = 4 * 1024 * 1024;
+      break;
+    default:
+      LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);
+      free(*lz4fRead);
+      return -LZ4F_ERROR_maxBlockSize_invalid;
+  }
+
+  (*lz4fRead)->srcBuf = (LZ4_byte*)malloc((*lz4fRead)->srcBufMaxSize);
+  if ((*lz4fRead)->srcBuf == NULL) {
+    LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);
+    free(lz4fRead);
+    return -LZ4F_ERROR_allocation_failed;
+  }
+
+  (*lz4fRead)->srcBufSize = sizeof(buf) - consumedSize;
+  memcpy((*lz4fRead)->srcBuf, buf + consumedSize, (*lz4fRead)->srcBufSize);
+
+  return ret;
+}
+
+size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size)
+{
+  LZ4_byte* p = (LZ4_byte*)buf;
+  size_t next = 0;
+
+  if (lz4fRead == NULL || buf == NULL)
+    return -LZ4F_ERROR_GENERIC;
+
+  while (next < size) {
+    size_t srcsize = lz4fRead->srcBufSize - lz4fRead->srcBufNext;
+    size_t dstsize = size - next;
+    size_t ret;
+
+    if (srcsize == 0) {
+      ret = fread(lz4fRead->srcBuf, 1, lz4fRead->srcBufMaxSize, lz4fRead->fp);
+      if (ret > 0) {
+        lz4fRead->srcBufSize = ret;
+        srcsize = lz4fRead->srcBufSize;
+        lz4fRead->srcBufNext = 0;
+      }
+      else if (ret == 0) {
+        break;
+      }
+      else {
+        return -LZ4F_ERROR_GENERIC;
+      }
+    }
+
+    ret = LZ4F_decompress(lz4fRead->dctxPtr,
+                          p, &dstsize,
+                          lz4fRead->srcBuf + lz4fRead->srcBufNext,
+                          &srcsize,
+                          NULL);
+    if (LZ4F_isError(ret)) {
+        return ret;
+    }
+
+    lz4fRead->srcBufNext += srcsize;
+    next += dstsize;
+    p += dstsize;
+  }
+
+  return next;
+}
+
+LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead)
+{
+  if (lz4fRead == NULL)
+    return -LZ4F_ERROR_GENERIC;
+  LZ4F_freeDecompressionContext(lz4fRead->dctxPtr);
+  free(lz4fRead->srcBuf);
+  free(lz4fRead);
+  return LZ4F_OK_NoError;
+}
+
+LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr)
+{
+  LZ4_byte buf[LZ4F_HEADER_SIZE_MAX];
+  size_t ret;
+
+  if (fp == NULL || lz4fWrite == NULL)
+    return -LZ4F_ERROR_GENERIC;
+
+  *lz4fWrite = (LZ4_writeFile_t*)malloc(sizeof(LZ4_writeFile_t));
+  if (*lz4fWrite == NULL) {
+    return -LZ4F_ERROR_allocation_failed;
+  }
+  if (prefsPtr != NULL) {
+    switch (prefsPtr->frameInfo.blockSizeID) {
+      case LZ4F_default :
+      case LZ4F_max64KB :
+        (*lz4fWrite)->maxWriteSize = 64 * 1024;
+        break;
+      case LZ4F_max256KB:
+        (*lz4fWrite)->maxWriteSize = 256 * 1024;
+        break;
+      case LZ4F_max1MB:
+        (*lz4fWrite)->maxWriteSize = 1 * 1024 * 1024;
+        break;
+      case LZ4F_max4MB:
+        (*lz4fWrite)->maxWriteSize = 4 * 1024 * 1024;
+        break;
+      default:
+        free(lz4fWrite);
+        return -LZ4F_ERROR_maxBlockSize_invalid;
+      }
+    } else {
+      (*lz4fWrite)->maxWriteSize = 64 * 1024;
+    }
+
+  (*lz4fWrite)->dstBufMaxSize = LZ4F_compressBound((*lz4fWrite)->maxWriteSize, prefsPtr);
+  (*lz4fWrite)->dstBuf = (LZ4_byte*)malloc((*lz4fWrite)->dstBufMaxSize);
+  if ((*lz4fWrite)->dstBuf == NULL) {
+    free(*lz4fWrite);
+    return -LZ4F_ERROR_allocation_failed;
+  }
+
+  ret = LZ4F_createCompressionContext(&(*lz4fWrite)->cctxPtr, LZ4F_getVersion());
+  if (LZ4F_isError(ret)) {
+      free((*lz4fWrite)->dstBuf);
+      free(*lz4fWrite);
+      return ret;
+  }
+
+  ret = LZ4F_compressBegin((*lz4fWrite)->cctxPtr, buf, LZ4F_HEADER_SIZE_MAX, prefsPtr);
+  if (LZ4F_isError(ret)) {
+      LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr);
+      free((*lz4fWrite)->dstBuf);
+      free(*lz4fWrite);
+      return ret;
+  }
+
+  if (ret != fwrite(buf, 1, ret, fp)) {
+    LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr);
+    free((*lz4fWrite)->dstBuf);
+    free(*lz4fWrite);
+    return -LZ4F_ERROR_GENERIC;
+  }
+
+  (*lz4fWrite)->fp = fp;
+  (*lz4fWrite)->errCode = LZ4F_OK_NoError;
+  return LZ4F_OK_NoError;
+}
+
+size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, const void* buf, size_t size)
+{
+  const LZ4_byte* p = (const LZ4_byte*)buf;
+  size_t remain = size;
+  size_t chunk;
+  size_t ret;
+
+  if (lz4fWrite == NULL || buf == NULL)
+    return -LZ4F_ERROR_GENERIC;
+  while (remain) {
+    if (remain > lz4fWrite->maxWriteSize)
+      chunk = lz4fWrite->maxWriteSize;
+    else
+      chunk = remain;
+
+    ret = LZ4F_compressUpdate(lz4fWrite->cctxPtr,
+                              lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize,
+                              p, chunk,
+                              NULL);
+    if (LZ4F_isError(ret)) {
+      lz4fWrite->errCode = ret;
+      return ret;
+    }
+
+    if(ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) {
+      lz4fWrite->errCode = -LZ4F_ERROR_GENERIC;
+      return -LZ4F_ERROR_GENERIC;
+    }
+
+    p += chunk;
+    remain -= chunk;
+  }
+
+  return size;
+}
+
+LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite)
+{
+  LZ4F_errorCode_t ret = LZ4F_OK_NoError;
+
+  if (lz4fWrite == NULL)
+    return -LZ4F_ERROR_GENERIC;
+
+  if (lz4fWrite->errCode == LZ4F_OK_NoError) {
+    ret =  LZ4F_compressEnd(lz4fWrite->cctxPtr,
+                            lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize,
+                            NULL);
+    if (LZ4F_isError(ret)) {
+      goto out;
+    }
+
+    if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) {
+      ret = -LZ4F_ERROR_GENERIC;
+    }
+  }
+
+out:
+  LZ4F_freeCompressionContext(lz4fWrite->cctxPtr);
+  free(lz4fWrite->dstBuf);
+  free(lz4fWrite);
+  return ret;
+}
diff --git a/3rdparty/lz4/lz4file.h b/3rdparty/lz4/lz4file.h
new file mode 100644
index 00000000000..723552cd89c
--- /dev/null
+++ b/3rdparty/lz4/lz4file.h
@@ -0,0 +1,93 @@
+/*
+   LZ4 file library
+   Header File
+   Copyright (C) 2022, Xiaomi Inc.
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - LZ4 source repository : https://github.com/lz4/lz4
+   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef LZ4FILE_H
+#define LZ4FILE_H
+
+#include <stdio.h>
+#include "lz4frame_static.h"
+
+typedef struct LZ4_readFile_s LZ4_readFile_t;
+typedef struct LZ4_writeFile_s LZ4_writeFile_t;
+
+/*! LZ4F_readOpen() :
+ * Set read lz4file handle.
+ * `lz4f` will set a lz4file handle.
+ * `fp` must be the return value of the lz4 file opened by fopen.
+ */
+LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp);
+
+/*! LZ4F_read() :
+ * Read lz4file content to buffer.
+ * `lz4f` must use LZ4_readOpen to set first.
+ * `buf` read data buffer.
+ * `size` read data buffer size.
+ */
+LZ4FLIB_STATIC_API size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size);
+
+/*! LZ4F_readClose() :
+ * Close lz4file handle.
+ * `lz4f` must use LZ4_readOpen to set first.
+ */
+LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead);
+
+/*! LZ4F_writeOpen() :
+ * Set write lz4file handle.
+ * `lz4f` will set a lz4file handle.
+ * `fp` must be the return value of the lz4 file opened by fopen.
+ */
+LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr);
+
+/*! LZ4F_write() :
+ * Write buffer to lz4file.
+ * `lz4f` must use LZ4F_writeOpen to set first.
+ * `buf` write data buffer.
+ * `size` write data buffer size.
+ */
+LZ4FLIB_STATIC_API size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, const void* buf, size_t size);
+
+/*! LZ4F_writeClose() :
+ * Close lz4file handle.
+ * `lz4f` must use LZ4F_writeOpen to set first.
+ */
+LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite);
+
+#endif /* LZ4FILE_H */
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/3rdparty/lz4/lz4frame.c b/3rdparty/lz4/lz4frame.c
new file mode 100644
index 00000000000..77857a5f29e
--- /dev/null
+++ b/3rdparty/lz4/lz4frame.c
@@ -0,0 +1,2076 @@
+/*
+ * LZ4 auto-framing library
+ * Copyright (C) 2011-2016, Yann Collet.
+ *
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following disclaimer
+ *   in the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at :
+ * - LZ4 homepage : http://www.lz4.org
+ * - LZ4 source repository : https://github.com/lz4/lz4
+ */
+
+/* LZ4F is a stand-alone API to create LZ4-compressed Frames
+ * in full conformance with specification v1.6.1 .
+ * This library rely upon memory management capabilities (malloc, free)
+ * provided either by <stdlib.h>,
+ * or redirected towards another library of user's choice
+ * (see Memory Routines below).
+ */
+
+
+/*-************************************
+*  Compiler Options
+**************************************/
+#ifdef _MSC_VER    /* Visual Studio */
+#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */
+#endif
+
+
+/*-************************************
+*  Tuning parameters
+**************************************/
+/*
+ * LZ4F_HEAPMODE :
+ * Control how LZ4F_compressFrame allocates the Compression State,
+ * either on stack (0:default, fastest), or in memory heap (1:requires malloc()).
+ */
+#ifndef LZ4F_HEAPMODE
+#  define LZ4F_HEAPMODE 0
+#endif
+
+
+/*-************************************
+*  Library declarations
+**************************************/
+#define LZ4F_STATIC_LINKING_ONLY
+#include "lz4frame.h"
+#define LZ4_STATIC_LINKING_ONLY
+#include "lz4.h"
+#define LZ4_HC_STATIC_LINKING_ONLY
+#include "lz4hc.h"
+#define XXH_STATIC_LINKING_ONLY
+#include "xxhash.h"
+
+
+/*-************************************
+*  Memory routines
+**************************************/
+/*
+ * User may redirect invocations of
+ * malloc(), calloc() and free()
+ * towards another library or solution of their choice
+ * by modifying below section.
+**/
+
+#include <string.h>   /* memset, memcpy, memmove */
+#ifndef LZ4_SRC_INCLUDED  /* avoid redefinition when sources are coalesced */
+#  define MEM_INIT(p,v,s)   memset((p),(v),(s))
+#endif
+
+#ifndef LZ4_SRC_INCLUDED   /* avoid redefinition when sources are coalesced */
+#  include <stdlib.h>   /* malloc, calloc, free */
+#  define ALLOC(s)          malloc(s)
+#  define ALLOC_AND_ZERO(s) calloc(1,(s))
+#  define FREEMEM(p)        free(p)
+#endif
+
+static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem)
+{
+    /* custom calloc defined : use it */
+    if (cmem.customCalloc != NULL) {
+        return cmem.customCalloc(cmem.opaqueState, s);
+    }
+    /* nothing defined : use default <stdlib.h>'s calloc() */
+    if (cmem.customAlloc == NULL) {
+        return ALLOC_AND_ZERO(s);
+    }
+    /* only custom alloc defined : use it, and combine it with memset() */
+    {   void* const p = cmem.customAlloc(cmem.opaqueState, s);
+        if (p != NULL) MEM_INIT(p, 0, s);
+        return p;
+}   }
+
+static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem)
+{
+    /* custom malloc defined : use it */
+    if (cmem.customAlloc != NULL) {
+        return cmem.customAlloc(cmem.opaqueState, s);
+    }
+    /* nothing defined : use default <stdlib.h>'s malloc() */
+    return ALLOC(s);
+}
+
+static void LZ4F_free(void* p, LZ4F_CustomMem cmem)
+{
+    /* custom malloc defined : use it */
+    if (cmem.customFree != NULL) {
+        cmem.customFree(cmem.opaqueState, p);
+        return;
+    }
+    /* nothing defined : use default <stdlib.h>'s free() */
+    FREEMEM(p);
+}
+
+
+/*-************************************
+*  Debug
+**************************************/
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)
+#  include <assert.h>
+#else
+#  ifndef assert
+#    define assert(condition) ((void)0)
+#  endif
+#endif
+
+#define LZ4F_STATIC_ASSERT(c)    { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; }   /* use only *after* variable declarations */
+
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG)
+#  include <stdio.h>
+static int g_debuglog_enable = 1;
+#  define DEBUGLOG(l, ...) {                                  \
+                if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) {  \
+                    fprintf(stderr, __FILE__ ": ");           \
+                    fprintf(stderr, __VA_ARGS__);             \
+                    fprintf(stderr, " \n");                   \
+            }   }
+#else
+#  define DEBUGLOG(l, ...)      {}    /* disabled */
+#endif
+
+
+/*-************************************
+*  Basic Types
+**************************************/
+#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+  typedef  uint8_t BYTE;
+  typedef uint16_t U16;
+  typedef uint32_t U32;
+  typedef  int32_t S32;
+  typedef uint64_t U64;
+#else
+  typedef unsigned char       BYTE;
+  typedef unsigned short      U16;
+  typedef unsigned int        U32;
+  typedef   signed int        S32;
+  typedef unsigned long long  U64;
+#endif
+
+
+/* unoptimized version; solves endianness & alignment issues */
+static U32 LZ4F_readLE32 (const void* src)
+{
+    const BYTE* const srcPtr = (const BYTE*)src;
+    U32 value32 = srcPtr[0];
+    value32 += ((U32)srcPtr[1])<< 8;
+    value32 += ((U32)srcPtr[2])<<16;
+    value32 += ((U32)srcPtr[3])<<24;
+    return value32;
+}
+
+static void LZ4F_writeLE32 (void* dst, U32 value32)
+{
+    BYTE* const dstPtr = (BYTE*)dst;
+    dstPtr[0] = (BYTE)value32;
+    dstPtr[1] = (BYTE)(value32 >> 8);
+    dstPtr[2] = (BYTE)(value32 >> 16);
+    dstPtr[3] = (BYTE)(value32 >> 24);
+}
+
+static U64 LZ4F_readLE64 (const void* src)
+{
+    const BYTE* const srcPtr = (const BYTE*)src;
+    U64 value64 = srcPtr[0];
+    value64 += ((U64)srcPtr[1]<<8);
+    value64 += ((U64)srcPtr[2]<<16);
+    value64 += ((U64)srcPtr[3]<<24);
+    value64 += ((U64)srcPtr[4]<<32);
+    value64 += ((U64)srcPtr[5]<<40);
+    value64 += ((U64)srcPtr[6]<<48);
+    value64 += ((U64)srcPtr[7]<<56);
+    return value64;
+}
+
+static void LZ4F_writeLE64 (void* dst, U64 value64)
+{
+    BYTE* const dstPtr = (BYTE*)dst;
+    dstPtr[0] = (BYTE)value64;
+    dstPtr[1] = (BYTE)(value64 >> 8);
+    dstPtr[2] = (BYTE)(value64 >> 16);
+    dstPtr[3] = (BYTE)(value64 >> 24);
+    dstPtr[4] = (BYTE)(value64 >> 32);
+    dstPtr[5] = (BYTE)(value64 >> 40);
+    dstPtr[6] = (BYTE)(value64 >> 48);
+    dstPtr[7] = (BYTE)(value64 >> 56);
+}
+
+
+/*-************************************
+*  Constants
+**************************************/
+#ifndef LZ4_SRC_INCLUDED   /* avoid double definition */
+#  define KB *(1<<10)
+#  define MB *(1<<20)
+#  define GB *(1<<30)
+#endif
+
+#define _1BIT  0x01
+#define _2BITS 0x03
+#define _3BITS 0x07
+#define _4BITS 0x0F
+#define _8BITS 0xFF
+
+#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U
+#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB
+
+static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN;   /*  7 */
+static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX;   /* 19 */
+static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE;  /* block header : size, and compress flag */
+static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE;  /* block footer : checksum (optional) */
+
+
+/*-************************************
+*  Structures and local types
+**************************************/
+
+typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_blockCompression_t;
+
+typedef struct LZ4F_cctx_s
+{
+    LZ4F_CustomMem cmem;
+    LZ4F_preferences_t prefs;
+    U32    version;
+    U32    cStage;     /* 0 : compression uninitialized ; 1 : initialized, can compress */
+    const LZ4F_CDict* cdict;
+    size_t maxBlockSize;
+    size_t maxBufferSize;
+    BYTE*  tmpBuff;    /* internal buffer, for streaming */
+    BYTE*  tmpIn;      /* starting position of data compress within internal buffer (>= tmpBuff) */
+    size_t tmpInSize;  /* amount of data to compress after tmpIn */
+    U64    totalInSize;
+    XXH32_state_t xxh;
+    void*  lz4CtxPtr;
+    U16    lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */
+    U16    lz4CtxState; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */
+    LZ4F_blockCompression_t  blockCompression;
+} LZ4F_cctx_t;
+
+
+/*-************************************
+*  Error management
+**************************************/
+#define LZ4F_GENERATE_STRING(STRING) #STRING,
+static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) };
+
+
+unsigned LZ4F_isError(LZ4F_errorCode_t code)
+{
+    return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode));
+}
+
+const char* LZ4F_getErrorName(LZ4F_errorCode_t code)
+{
+    static const char* codeError = "Unspecified error code";
+    if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];
+    return codeError;
+}
+
+LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult)
+{
+    if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError;
+    return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult);
+}
+
+static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code)
+{
+    /* A compilation error here means sizeof(ptrdiff_t) is not large enough */
+    LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t));
+    return (LZ4F_errorCode_t)-(ptrdiff_t)code;
+}
+
+#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e)
+
+#define RETURN_ERROR_IF(c,e) do { if (c) RETURN_ERROR(e); } while (0)
+
+#define FORWARD_IF_ERROR(r) do { if (LZ4F_isError(r)) return (r); } while (0)
+
+unsigned LZ4F_getVersion(void) { return LZ4F_VERSION; }
+
+int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; }
+
+size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID)
+{
+    static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB };
+
+    if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
+    if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB)
+        RETURN_ERROR(maxBlockSize_invalid);
+    {   int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB;
+        return blockSizes[blockSizeIdx];
+}   }
+
+/*-************************************
+*  Private functions
+**************************************/
+#define MIN(a,b)   ( (a) < (b) ? (a) : (b) )
+
+static BYTE LZ4F_headerChecksum (const void* header, size_t length)
+{
+    U32 const xxh = XXH32(header, length, 0);
+    return (BYTE)(xxh >> 8);
+}
+
+
+/*-************************************
+*  Simple-pass compression functions
+**************************************/
+static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID,
+                                           const size_t srcSize)
+{
+    LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB;
+    size_t maxBlockSize = 64 KB;
+    while (requestedBSID > proposedBSID) {
+        if (srcSize <= maxBlockSize)
+            return proposedBSID;
+        proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1);
+        maxBlockSize <<= 2;
+    }
+    return requestedBSID;
+}
+
+/*! LZ4F_compressBound_internal() :
+ *  Provides dstCapacity given a srcSize to guarantee operation success in worst case situations.
+ *  prefsPtr is optional : if NULL is provided, preferences will be set to cover worst case scenario.
+ * @return is always the same for a srcSize and prefsPtr, so it can be relied upon to size reusable buffers.
+ *  When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations.
+ */
+static size_t LZ4F_compressBound_internal(size_t srcSize,
+                                    const LZ4F_preferences_t* preferencesPtr,
+                                          size_t alreadyBuffered)
+{
+    LZ4F_preferences_t prefsNull = LZ4F_INIT_PREFERENCES;
+    prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;   /* worst case */
+    prefsNull.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled;   /* worst case */
+    {   const LZ4F_preferences_t* const prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;
+        U32 const flush = prefsPtr->autoFlush | (srcSize==0);
+        LZ4F_blockSizeID_t const blockID = prefsPtr->frameInfo.blockSizeID;
+        size_t const blockSize = LZ4F_getBlockSize(blockID);
+        size_t const maxBuffered = blockSize - 1;
+        size_t const bufferedSize = MIN(alreadyBuffered, maxBuffered);
+        size_t const maxSrcSize = srcSize + bufferedSize;
+        unsigned const nbFullBlocks = (unsigned)(maxSrcSize / blockSize);
+        size_t const partialBlockSize = maxSrcSize & (blockSize-1);
+        size_t const lastBlockSize = flush ? partialBlockSize : 0;
+        unsigned const nbBlocks = nbFullBlocks + (lastBlockSize>0);
+
+        size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag;
+        size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize);
+
+        return ((BHSize + blockCRCSize) * nbBlocks) +
+               (blockSize * nbFullBlocks) + lastBlockSize + frameEnd;
+    }
+}
+
+size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
+{
+    LZ4F_preferences_t prefs;
+    size_t const headerSize = maxFHSize;      /* max header size, including optional fields */
+
+    if (preferencesPtr!=NULL) prefs = *preferencesPtr;
+    else MEM_INIT(&prefs, 0, sizeof(prefs));
+    prefs.autoFlush = 1;
+
+    return headerSize + LZ4F_compressBound_internal(srcSize, &prefs, 0);;
+}
+
+
+/*! LZ4F_compressFrame_usingCDict() :
+ *  Compress srcBuffer using a dictionary, in a single step.
+ *  cdict can be NULL, in which case, no dictionary is used.
+ *  dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
+ *  The LZ4F_preferences_t structure is optional : you may provide NULL as argument,
+ *  however, it's the only way to provide a dictID, so it's not recommended.
+ * @return : number of bytes written into dstBuffer,
+ *           or an error code if it fails (can be tested using LZ4F_isError())
+ */
+size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,
+                                     void* dstBuffer, size_t dstCapacity,
+                               const void* srcBuffer, size_t srcSize,
+                               const LZ4F_CDict* cdict,
+                               const LZ4F_preferences_t* preferencesPtr)
+{
+    LZ4F_preferences_t prefs;
+    LZ4F_compressOptions_t options;
+    BYTE* const dstStart = (BYTE*) dstBuffer;
+    BYTE* dstPtr = dstStart;
+    BYTE* const dstEnd = dstStart + dstCapacity;
+
+    if (preferencesPtr!=NULL)
+        prefs = *preferencesPtr;
+    else
+        MEM_INIT(&prefs, 0, sizeof(prefs));
+    if (prefs.frameInfo.contentSize != 0)
+        prefs.frameInfo.contentSize = (U64)srcSize;   /* auto-correct content size if selected (!=0) */
+
+    prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
+    prefs.autoFlush = 1;
+    if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))
+        prefs.frameInfo.blockMode = LZ4F_blockIndependent;   /* only one block => no need for inter-block link */
+
+    MEM_INIT(&options, 0, sizeof(options));
+    options.stableSrc = 1;
+
+    RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall);
+
+    { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs);  /* write header */
+      FORWARD_IF_ERROR(headerSize);
+      dstPtr += headerSize;   /* header size */ }
+
+    assert(dstEnd >= dstPtr);
+    { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options);
+      FORWARD_IF_ERROR(cSize);
+      dstPtr += cSize; }
+
+    assert(dstEnd >= dstPtr);
+    { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options);   /* flush last block, and generate suffix */
+      FORWARD_IF_ERROR(tailSize);
+      dstPtr += tailSize; }
+
+    assert(dstEnd >= dstStart);
+    return (size_t)(dstPtr - dstStart);
+}
+
+
+/*! LZ4F_compressFrame() :
+ *  Compress an entire srcBuffer into a valid LZ4 frame, in a single step.
+ *  dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
+ *  The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.
+ * @return : number of bytes written into dstBuffer.
+ *           or an error code if it fails (can be tested using LZ4F_isError())
+ */
+size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,
+                    const void* srcBuffer, size_t srcSize,
+                    const LZ4F_preferences_t* preferencesPtr)
+{
+    size_t result;
+#if (LZ4F_HEAPMODE)
+    LZ4F_cctx_t* cctxPtr;
+    result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION);
+    FORWARD_IF_ERROR(result);
+#else
+    LZ4F_cctx_t cctx;
+    LZ4_stream_t lz4ctx;
+    LZ4F_cctx_t* const cctxPtr = &cctx;
+
+    MEM_INIT(&cctx, 0, sizeof(cctx));
+    cctx.version = LZ4F_VERSION;
+    cctx.maxBufferSize = 5 MB;   /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */
+    if ( preferencesPtr == NULL
+      || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) {
+        LZ4_initStream(&lz4ctx, sizeof(lz4ctx));
+        cctxPtr->lz4CtxPtr = &lz4ctx;
+        cctxPtr->lz4CtxAlloc = 1;
+        cctxPtr->lz4CtxState = 1;
+    }
+#endif
+    DEBUGLOG(4, "LZ4F_compressFrame");
+
+    result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity,
+                                           srcBuffer, srcSize,
+                                           NULL, preferencesPtr);
+
+#if (LZ4F_HEAPMODE)
+    LZ4F_freeCompressionContext(cctxPtr);
+#else
+    if ( preferencesPtr != NULL
+      && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) {
+        LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);
+    }
+#endif
+    return result;
+}
+
+
+/*-***************************************************
+*   Dictionary compression
+*****************************************************/
+
+struct LZ4F_CDict_s {
+    LZ4F_CustomMem cmem;
+    void* dictContent;
+    LZ4_stream_t* fastCtx;
+    LZ4_streamHC_t* HCCtx;
+}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */
+
+LZ4F_CDict*
+LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize)
+{
+    const char* dictStart = (const char*)dictBuffer;
+    LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem);
+    DEBUGLOG(4, "LZ4F_createCDict_advanced");
+    if (!cdict) return NULL;
+    cdict->cmem = cmem;
+    if (dictSize > 64 KB) {
+        dictStart += dictSize - 64 KB;
+        dictSize = 64 KB;
+    }
+    cdict->dictContent = LZ4F_malloc(dictSize, cmem);
+    cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem);
+    if (cdict->fastCtx)
+        LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t));
+    cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem);
+    if (cdict->HCCtx)
+        LZ4_initStream(cdict->HCCtx, sizeof(LZ4_streamHC_t));
+    if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) {
+        LZ4F_freeCDict(cdict);
+        return NULL;
+    }
+    memcpy(cdict->dictContent, dictStart, dictSize);
+    LZ4_loadDict (cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize);
+    LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT);
+    LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize);
+    return cdict;
+}
+
+/*! LZ4F_createCDict() :
+ *  When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.
+ *  LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.
+ *  LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
+ * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict
+ * @return : digested dictionary for compression, or NULL if failed */
+LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize)
+{
+    DEBUGLOG(4, "LZ4F_createCDict");
+    return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize);
+}
+
+void LZ4F_freeCDict(LZ4F_CDict* cdict)
+{
+    if (cdict==NULL) return;  /* support free on NULL */
+    LZ4F_free(cdict->dictContent, cdict->cmem);
+    LZ4F_free(cdict->fastCtx, cdict->cmem);
+    LZ4F_free(cdict->HCCtx, cdict->cmem);
+    LZ4F_free(cdict, cdict->cmem);
+}
+
+
+/*-*********************************
+*  Advanced compression functions
+***********************************/
+
+LZ4F_cctx*
+LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)
+{
+    LZ4F_cctx* const cctxPtr =
+        (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem);
+    if (cctxPtr==NULL) return NULL;
+
+    cctxPtr->cmem = customMem;
+    cctxPtr->version = version;
+    cctxPtr->cStage = 0;   /* Uninitialized. Next stage : init cctx */
+
+    return cctxPtr;
+}
+
+/*! LZ4F_createCompressionContext() :
+ *  The first thing to do is to create a compressionContext object, which will be used in all compression operations.
+ *  This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.
+ *  The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries.
+ *  The function will provide a pointer to an allocated LZ4F_compressionContext_t object.
+ *  If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.
+ *  Object can release its memory using LZ4F_freeCompressionContext();
+**/
+LZ4F_errorCode_t
+LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version)
+{
+    assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */
+    /* in case it nonetheless happen in production */
+    RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null);
+
+    *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version);
+    RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed);
+    return LZ4F_OK_NoError;
+}
+
+
+LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr)
+{
+    if (cctxPtr != NULL) {  /* support free on NULL */
+       LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);  /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */
+       LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);
+       LZ4F_free(cctxPtr, cctxPtr->cmem);
+    }
+    return LZ4F_OK_NoError;
+}
+
+
+/**
+ * This function prepares the internal LZ4(HC) stream for a new compression,
+ * resetting the context and attaching the dictionary, if there is one.
+ *
+ * It needs to be called at the beginning of each independent compression
+ * stream (i.e., at the beginning of a frame in blockLinked mode, or at the
+ * beginning of each block in blockIndependent mode).
+ */
+static void LZ4F_initStream(void* ctx,
+                            const LZ4F_CDict* cdict,
+                            int level,
+                            LZ4F_blockMode_t blockMode) {
+    if (level < LZ4HC_CLEVEL_MIN) {
+        if (cdict != NULL || blockMode == LZ4F_blockLinked) {
+            /* In these cases, we will call LZ4_compress_fast_continue(),
+             * which needs an already reset context. Otherwise, we'll call a
+             * one-shot API. The non-continued APIs internally perform their own
+             * resets at the beginning of their calls, where they know what
+             * tableType they need the context to be in. So in that case this
+             * would be misguided / wasted work. */
+            LZ4_resetStream_fast((LZ4_stream_t*)ctx);
+        }
+        LZ4_attach_dictionary((LZ4_stream_t *)ctx, cdict ? cdict->fastCtx : NULL);
+    } else {
+        LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level);
+        LZ4_attach_HC_dictionary((LZ4_streamHC_t *)ctx, cdict ? cdict->HCCtx : NULL);
+    }
+}
+
+static int ctxTypeID_to_size(int ctxTypeID) {
+    switch(ctxTypeID) {
+    case 1:
+        return LZ4_sizeofState();
+    case 2:
+        return LZ4_sizeofStateHC();
+    default:
+        return 0;
+    }
+}
+
+/*! LZ4F_compressBegin_usingCDict() :
+ *  init streaming compression AND writes frame header into @dstBuffer.
+ * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.
+ * @return : number of bytes written into @dstBuffer for the header
+ *           or an error code (can be tested using LZ4F_isError())
+ */
+size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr,
+                          void* dstBuffer, size_t dstCapacity,
+                          const LZ4F_CDict* cdict,
+                          const LZ4F_preferences_t* preferencesPtr)
+{
+    LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES;
+    BYTE* const dstStart = (BYTE*)dstBuffer;
+    BYTE* dstPtr = dstStart;
+
+    RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall);
+    if (preferencesPtr == NULL) preferencesPtr = &prefNull;
+    cctxPtr->prefs = *preferencesPtr;
+
+    /* cctx Management */
+    {   U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2;
+        int requiredSize = ctxTypeID_to_size(ctxTypeID);
+        int allocatedSize = ctxTypeID_to_size(cctxPtr->lz4CtxAlloc);
+        if (allocatedSize < requiredSize) {
+            /* not enough space allocated */
+            LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);
+            if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {
+                /* must take ownership of memory allocation,
+                 * in order to respect custom allocator contract */
+                cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctxPtr->cmem);
+                if (cctxPtr->lz4CtxPtr)
+                    LZ4_initStream(cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t));
+            } else {
+                cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem);
+                if (cctxPtr->lz4CtxPtr)
+                    LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));
+            }
+            RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed);
+            cctxPtr->lz4CtxAlloc = ctxTypeID;
+            cctxPtr->lz4CtxState = ctxTypeID;
+        } else if (cctxPtr->lz4CtxState != ctxTypeID) {
+            /* otherwise, a sufficient buffer is already allocated,
+             * but we need to reset it to the correct context type */
+            if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {
+                LZ4_initStream((LZ4_stream_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t));
+            } else {
+                LZ4_initStreamHC((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));
+                LZ4_setCompressionLevel((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
+            }
+            cctxPtr->lz4CtxState = ctxTypeID;
+    }   }
+
+    /* Buffer Management */
+    if (cctxPtr->prefs.frameInfo.blockSizeID == 0)
+        cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
+    cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);
+
+    {   size_t const requiredBuffSize = preferencesPtr->autoFlush ?
+                ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 64 KB : 0) :  /* only needs past data up to window size */
+                cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB : 0);
+
+        if (cctxPtr->maxBufferSize < requiredBuffSize) {
+            cctxPtr->maxBufferSize = 0;
+            LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);
+            cctxPtr->tmpBuff = (BYTE*)LZ4F_malloc(requiredBuffSize, cctxPtr->cmem);
+            RETURN_ERROR_IF(cctxPtr->tmpBuff == NULL, allocation_failed);
+            cctxPtr->maxBufferSize = requiredBuffSize;
+    }   }
+    cctxPtr->tmpIn = cctxPtr->tmpBuff;
+    cctxPtr->tmpInSize = 0;
+    (void)XXH32_reset(&(cctxPtr->xxh), 0);
+
+    /* context init */
+    cctxPtr->cdict = cdict;
+    if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) {
+        /* frame init only for blockLinked : blockIndependent will be init at each block */
+        LZ4F_initStream(cctxPtr->lz4CtxPtr, cdict, cctxPtr->prefs.compressionLevel, LZ4F_blockLinked);
+    }
+    if (preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) {
+        LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, (int)preferencesPtr->favorDecSpeed);
+    }
+
+    /* Magic Number */
+    LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);
+    dstPtr += 4;
+    {   BYTE* const headerStart = dstPtr;
+
+        /* FLG Byte */
+        *dstPtr++ = (BYTE)(((1 & _2BITS) << 6)    /* Version('01') */
+            + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5)
+            + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4)
+            + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3)
+            + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)
+            +  (cctxPtr->prefs.frameInfo.dictID > 0) );
+        /* BD Byte */
+        *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);
+        /* Optional Frame content size field */
+        if (cctxPtr->prefs.frameInfo.contentSize) {
+            LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize);
+            dstPtr += 8;
+            cctxPtr->totalInSize = 0;
+        }
+        /* Optional dictionary ID field */
+        if (cctxPtr->prefs.frameInfo.dictID) {
+            LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID);
+            dstPtr += 4;
+        }
+        /* Header CRC Byte */
+        *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart));
+        dstPtr++;
+    }
+
+    cctxPtr->cStage = 1;   /* header written, now request input data block */
+    return (size_t)(dstPtr - dstStart);
+}
+
+
+/*! LZ4F_compressBegin() :
+ *  init streaming compression AND writes frame header into @dstBuffer.
+ * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.
+ * @preferencesPtr can be NULL, in which case default parameters are selected.
+ * @return : number of bytes written into dstBuffer for the header
+ *        or an error code (can be tested using LZ4F_isError())
+ */
+size_t LZ4F_compressBegin(LZ4F_cctx* cctxPtr,
+                          void* dstBuffer, size_t dstCapacity,
+                          const LZ4F_preferences_t* preferencesPtr)
+{
+    return LZ4F_compressBegin_usingCDict(cctxPtr, dstBuffer, dstCapacity,
+                                         NULL, preferencesPtr);
+}
+
+
+/*  LZ4F_compressBound() :
+ * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario.
+ *  LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario.
+ *  This function cannot fail.
+ */
+size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
+{
+    if (preferencesPtr && preferencesPtr->autoFlush) {
+        return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0);
+    }
+    return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1);
+}
+
+
+typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict);
+
+
+/*! LZ4F_makeBlock():
+ *  compress a single block, add header and optional checksum.
+ *  assumption : dst buffer capacity is >= BHSize + srcSize + crcSize
+ */
+static size_t LZ4F_makeBlock(void* dst,
+                       const void* src, size_t srcSize,
+                             compressFunc_t compress, void* lz4ctx, int level,
+                       const LZ4F_CDict* cdict,
+                             LZ4F_blockChecksum_t crcFlag)
+{
+    BYTE* const cSizePtr = (BYTE*)dst;
+    U32 cSize;
+    assert(compress != NULL);
+    cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize),
+                          (int)(srcSize), (int)(srcSize-1),
+                          level, cdict);
+
+    if (cSize == 0 || cSize >= srcSize) {
+        cSize = (U32)srcSize;
+        LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG);
+        memcpy(cSizePtr+BHSize, src, srcSize);
+    } else {
+        LZ4F_writeLE32(cSizePtr, cSize);
+    }
+    if (crcFlag) {
+        U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0);  /* checksum of compressed data */
+        LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32);
+    }
+    return BHSize + cSize + ((U32)crcFlag)*BFSize;
+}
+
+
+static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
+{
+    int const acceleration = (level < 0) ? -level + 1 : 1;
+    DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize);
+    LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);
+    if (cdict) {
+        return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);
+    } else {
+        return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration);
+    }
+}
+
+static int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
+{
+    int const acceleration = (level < 0) ? -level + 1 : 1;
+    (void)cdict; /* init once at beginning of frame */
+    DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize);
+    return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);
+}
+
+static int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
+{
+    LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);
+    if (cdict) {
+        return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);
+    }
+    return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level);
+}
+
+static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
+{
+    (void)level; (void)cdict; /* init once at beginning of frame */
+    return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);
+}
+
+static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)
+{
+    (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict;
+    return 0;
+}
+
+static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_blockCompression_t  compressMode)
+{
+    if (compressMode == LZ4B_UNCOMPRESSED) return LZ4F_doNotCompressBlock;
+    if (level < LZ4HC_CLEVEL_MIN) {
+        if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock;
+        return LZ4F_compressBlock_continue;
+    }
+    if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC;
+    return LZ4F_compressBlockHC_continue;
+}
+
+/* Save history (up to 64KB) into @tmpBuff */
+static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr)
+{
+    if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN)
+        return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
+    return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
+}
+
+typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;
+
+static const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } };
+
+
+ /*! LZ4F_compressUpdateImpl() :
+ *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
+ *  When successful, the function always entirely consumes @srcBuffer.
+ *  src data is either buffered or compressed into @dstBuffer.
+ *  If the block compression does not match the compression of the previous block, the old data is flushed
+ *  and operations continue with the new compression mode.
+ * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on.
+ * @compressOptionsPtr is optional : provide NULL to mean "default".
+ * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ *  After an error, the state is left in a UB state, and must be re-initialized.
+ */
+static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr,
+                     void* dstBuffer, size_t dstCapacity,
+                     const void* srcBuffer, size_t srcSize,
+                     const LZ4F_compressOptions_t* compressOptionsPtr,
+                     LZ4F_blockCompression_t blockCompression)
+  {
+    size_t const blockSize = cctxPtr->maxBlockSize;
+    const BYTE* srcPtr = (const BYTE*)srcBuffer;
+    const BYTE* const srcEnd = srcPtr + srcSize;
+    BYTE* const dstStart = (BYTE*)dstBuffer;
+    BYTE* dstPtr = dstStart;
+    LZ4F_lastBlockStatus lastBlockCompressed = notDone;
+    compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression);
+    size_t bytesWritten;
+    DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize);
+
+    RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);   /* state must be initialized and waiting for next block */
+    if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize))
+        RETURN_ERROR(dstMaxSize_tooSmall);
+
+    if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize)
+        RETURN_ERROR(dstMaxSize_tooSmall);
+
+    /* flush currently written block, to continue with new block compression */
+    if (cctxPtr->blockCompression != blockCompression) {
+        bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);
+        dstPtr += bytesWritten;
+        cctxPtr->blockCompression = blockCompression;
+    }
+
+    if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull;
+
+    /* complete tmp buffer */
+    if (cctxPtr->tmpInSize > 0) {   /* some data already within tmp buffer */
+        size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize;
+        assert(blockSize > cctxPtr->tmpInSize);
+        if (sizeToCopy > srcSize) {
+            /* add src to tmpIn buffer */
+            memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);
+            srcPtr = srcEnd;
+            cctxPtr->tmpInSize += srcSize;
+            /* still needs some CRC */
+        } else {
+            /* complete tmpIn block and then compress it */
+            lastBlockCompressed = fromTmpBuffer;
+            memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);
+            srcPtr += sizeToCopy;
+
+            dstPtr += LZ4F_makeBlock(dstPtr,
+                                     cctxPtr->tmpIn, blockSize,
+                                     compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
+                                     cctxPtr->cdict,
+                                     cctxPtr->prefs.frameInfo.blockChecksumFlag);
+            if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize;
+            cctxPtr->tmpInSize = 0;
+    }   }
+
+    while ((size_t)(srcEnd - srcPtr) >= blockSize) {
+        /* compress full blocks */
+        lastBlockCompressed = fromSrcBuffer;
+        dstPtr += LZ4F_makeBlock(dstPtr,
+                                 srcPtr, blockSize,
+                                 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
+                                 cctxPtr->cdict,
+                                 cctxPtr->prefs.frameInfo.blockChecksumFlag);
+        srcPtr += blockSize;
+    }
+
+    if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) {
+        /* autoFlush : remaining input (< blockSize) is compressed */
+        lastBlockCompressed = fromSrcBuffer;
+        dstPtr += LZ4F_makeBlock(dstPtr,
+                                 srcPtr, (size_t)(srcEnd - srcPtr),
+                                 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
+                                 cctxPtr->cdict,
+                                 cctxPtr->prefs.frameInfo.blockChecksumFlag);
+        srcPtr = srcEnd;
+    }
+
+    /* preserve dictionary within @tmpBuff whenever necessary */
+    if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) {
+        /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */
+        assert(blockCompression == LZ4B_COMPRESSED);
+        if (compressOptionsPtr->stableSrc) {
+            cctxPtr->tmpIn = cctxPtr->tmpBuff;  /* src is stable : dictionary remains in src across invocations */
+        } else {
+            int const realDictSize = LZ4F_localSaveDict(cctxPtr);
+            assert(0 <= realDictSize && realDictSize <= 64 KB);
+            cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
+        }
+    }
+
+    /* keep tmpIn within limits */
+    if (!(cctxPtr->prefs.autoFlush)  /* no autoflush : there may be some data left within internal buffer */
+      && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) )  /* not enough room to store next block */
+    {
+        /* only preserve 64KB within internal buffer. Ensures there is enough room for next block.
+         * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */
+        int const realDictSize = LZ4F_localSaveDict(cctxPtr);
+        cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
+        assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize));
+    }
+
+    /* some input data left, necessarily < blockSize */
+    if (srcPtr < srcEnd) {
+        /* fill tmp buffer */
+        size_t const sizeToCopy = (size_t)(srcEnd - srcPtr);
+        memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
+        cctxPtr->tmpInSize = sizeToCopy;
+    }
+
+    if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)
+        (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);
+
+    cctxPtr->totalInSize += srcSize;
+    return (size_t)(dstPtr - dstStart);
+}
+
+/*! LZ4F_compressUpdate() :
+ *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
+ *  When successful, the function always entirely consumes @srcBuffer.
+ *  src data is either buffered or compressed into @dstBuffer.
+ *  If previously an uncompressed block was written, buffered data is flushed
+ *  before appending compressed data is continued.
+ * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).
+ * @compressOptionsPtr is optional : provide NULL to mean "default".
+ * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ *  After an error, the state is left in a UB state, and must be re-initialized.
+ */
+size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr,
+                           void* dstBuffer, size_t dstCapacity,
+                     const void* srcBuffer, size_t srcSize,
+                     const LZ4F_compressOptions_t* compressOptionsPtr)
+{
+     return LZ4F_compressUpdateImpl(cctxPtr,
+                                   dstBuffer, dstCapacity,
+                                   srcBuffer, srcSize,
+                                   compressOptionsPtr, LZ4B_COMPRESSED);
+}
+
+/*! LZ4F_compressUpdate() :
+ *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
+ *  When successful, the function always entirely consumes @srcBuffer.
+ *  src data is either buffered or compressed into @dstBuffer.
+ *  If previously an uncompressed block was written, buffered data is flushed
+ *  before appending compressed data is continued.
+ *  This is only supported when LZ4F_blockIndependent is used
+ * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).
+ * @compressOptionsPtr is optional : provide NULL to mean "default".
+ * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ *  After an error, the state is left in a UB state, and must be re-initialized.
+ */
+size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr,
+                               void* dstBuffer, size_t dstCapacity,
+                         const void* srcBuffer, size_t srcSize,
+                         const LZ4F_compressOptions_t* compressOptionsPtr) {
+    return LZ4F_compressUpdateImpl(cctxPtr,
+                                   dstBuffer, dstCapacity,
+                                   srcBuffer, srcSize,
+                                   compressOptionsPtr, LZ4B_UNCOMPRESSED);
+}
+
+
+/*! LZ4F_flush() :
+ *  When compressed data must be sent immediately, without waiting for a block to be filled,
+ *  invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx.
+ *  The result of the function is the number of bytes written into dstBuffer.
+ *  It can be zero, this means there was no data left within LZ4F_cctx.
+ *  The function outputs an error code if it fails (can be tested using LZ4F_isError())
+ *  LZ4F_compressOptions_t* is optional. NULL is a valid argument.
+ */
+size_t LZ4F_flush(LZ4F_cctx* cctxPtr,
+                  void* dstBuffer, size_t dstCapacity,
+            const LZ4F_compressOptions_t* compressOptionsPtr)
+{
+    BYTE* const dstStart = (BYTE*)dstBuffer;
+    BYTE* dstPtr = dstStart;
+    compressFunc_t compress;
+
+    if (cctxPtr->tmpInSize == 0) return 0;   /* nothing to flush */
+    RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);
+    RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall);
+    (void)compressOptionsPtr;   /* not useful (yet) */
+
+    /* select compression function */
+    compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompression);
+
+    /* compress tmp buffer */
+    dstPtr += LZ4F_makeBlock(dstPtr,
+                             cctxPtr->tmpIn, cctxPtr->tmpInSize,
+                             compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,
+                             cctxPtr->cdict,
+                             cctxPtr->prefs.frameInfo.blockChecksumFlag);
+    assert(((void)"flush overflows dstBuffer!", (size_t)(dstPtr - dstStart) <= dstCapacity));
+
+    if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)
+        cctxPtr->tmpIn += cctxPtr->tmpInSize;
+    cctxPtr->tmpInSize = 0;
+
+    /* keep tmpIn within limits */
+    if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) {  /* necessarily LZ4F_blockLinked */
+        int const realDictSize = LZ4F_localSaveDict(cctxPtr);
+        cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
+    }
+
+    return (size_t)(dstPtr - dstStart);
+}
+
+
+/*! LZ4F_compressEnd() :
+ *  When you want to properly finish the compressed frame, just call LZ4F_compressEnd().
+ *  It will flush whatever data remained within compressionContext (like LZ4_flush())
+ *  but also properly finalize the frame, with an endMark and an (optional) checksum.
+ *  LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
+ * @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))
+ *       or an error code if it fails (can be tested using LZ4F_isError())
+ *  The context can then be used again to compress a new frame, starting with LZ4F_compressBegin().
+ */
+size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr,
+                        void* dstBuffer, size_t dstCapacity,
+                  const LZ4F_compressOptions_t* compressOptionsPtr)
+{
+    BYTE* const dstStart = (BYTE*)dstBuffer;
+    BYTE* dstPtr = dstStart;
+
+    size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);
+    DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity);
+    FORWARD_IF_ERROR(flushSize);
+    dstPtr += flushSize;
+
+    assert(flushSize <= dstCapacity);
+    dstCapacity -= flushSize;
+
+    RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall);
+    LZ4F_writeLE32(dstPtr, 0);
+    dstPtr += 4;   /* endMark */
+
+    if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) {
+        U32 const xxh = XXH32_digest(&(cctxPtr->xxh));
+        RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall);
+        DEBUGLOG(5,"Writing 32-bit content checksum");
+        LZ4F_writeLE32(dstPtr, xxh);
+        dstPtr+=4;   /* content Checksum */
+    }
+
+    cctxPtr->cStage = 0;   /* state is now re-usable (with identical preferences) */
+
+    if (cctxPtr->prefs.frameInfo.contentSize) {
+        if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)
+            RETURN_ERROR(frameSize_wrong);
+    }
+
+    return (size_t)(dstPtr - dstStart);
+}
+
+
+/*-***************************************************
+*   Frame Decompression
+*****************************************************/
+
+typedef enum {
+    dstage_getFrameHeader=0, dstage_storeFrameHeader,
+    dstage_init,
+    dstage_getBlockHeader, dstage_storeBlockHeader,
+    dstage_copyDirect, dstage_getBlockChecksum,
+    dstage_getCBlock, dstage_storeCBlock,
+    dstage_flushOut,
+    dstage_getSuffix, dstage_storeSuffix,
+    dstage_getSFrameSize, dstage_storeSFrameSize,
+    dstage_skipSkippable
+} dStage_t;
+
+struct LZ4F_dctx_s {
+    LZ4F_CustomMem cmem;
+    LZ4F_frameInfo_t frameInfo;
+    U32    version;
+    dStage_t dStage;
+    U64    frameRemainingSize;
+    size_t maxBlockSize;
+    size_t maxBufferSize;
+    BYTE*  tmpIn;
+    size_t tmpInSize;
+    size_t tmpInTarget;
+    BYTE*  tmpOutBuffer;
+    const BYTE* dict;
+    size_t dictSize;
+    BYTE*  tmpOut;
+    size_t tmpOutSize;
+    size_t tmpOutStart;
+    XXH32_state_t xxh;
+    XXH32_state_t blockChecksum;
+    int    skipChecksum;
+    BYTE   header[LZ4F_HEADER_SIZE_MAX];
+};  /* typedef'd to LZ4F_dctx in lz4frame.h */
+
+
+LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)
+{
+    LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem);
+    if (dctx == NULL) return NULL;
+
+    dctx->cmem = customMem;
+    dctx->version = version;
+    return dctx;
+}
+
+/*! LZ4F_createDecompressionContext() :
+ *  Create a decompressionContext object, which will track all decompression operations.
+ *  Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object.
+ *  Object can later be released using LZ4F_freeDecompressionContext().
+ * @return : if != 0, there was an error during context creation.
+ */
+LZ4F_errorCode_t
+LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber)
+{
+    assert(LZ4F_decompressionContextPtr != NULL);  /* violation of narrow contract */
+    RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null);  /* in case it nonetheless happen in production */
+
+    *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber);
+    if (*LZ4F_decompressionContextPtr == NULL) {  /* failed allocation */
+        RETURN_ERROR(allocation_failed);
+    }
+    return LZ4F_OK_NoError;
+}
+
+LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx)
+{
+    LZ4F_errorCode_t result = LZ4F_OK_NoError;
+    if (dctx != NULL) {   /* can accept NULL input, like free() */
+      result = (LZ4F_errorCode_t)dctx->dStage;
+      LZ4F_free(dctx->tmpIn, dctx->cmem);
+      LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);
+      LZ4F_free(dctx, dctx->cmem);
+    }
+    return result;
+}
+
+
+/*==---   Streaming Decompression operations   ---==*/
+
+void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx)
+{
+    dctx->dStage = dstage_getFrameHeader;
+    dctx->dict = NULL;
+    dctx->dictSize = 0;
+    dctx->skipChecksum = 0;
+}
+
+
+/*! LZ4F_decodeHeader() :
+ *  input   : `src` points at the **beginning of the frame**
+ *  output  : set internal values of dctx, such as
+ *            dctx->frameInfo and dctx->dStage.
+ *            Also allocates internal buffers.
+ *  @return : nb Bytes read from src (necessarily <= srcSize)
+ *            or an error code (testable with LZ4F_isError())
+ */
+static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize)
+{
+    unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID;
+    size_t frameHeaderSize;
+    const BYTE* srcPtr = (const BYTE*)src;
+
+    DEBUGLOG(5, "LZ4F_decodeHeader");
+    /* need to decode header to get frameInfo */
+    RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete);   /* minimal frame header size */
+    MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo));
+
+    /* special case : skippable frames */
+    if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) {
+        dctx->frameInfo.frameType = LZ4F_skippableFrame;
+        if (src == (void*)(dctx->header)) {
+            dctx->tmpInSize = srcSize;
+            dctx->tmpInTarget = 8;
+            dctx->dStage = dstage_storeSFrameSize;
+            return srcSize;
+        } else {
+            dctx->dStage = dstage_getSFrameSize;
+            return 4;
+    }   }
+
+    /* control magic number */
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) {
+        DEBUGLOG(4, "frame header error : unknown magic number");
+        RETURN_ERROR(frameType_unknown);
+    }
+#endif
+    dctx->frameInfo.frameType = LZ4F_frame;
+
+    /* Flags */
+    {   U32 const FLG = srcPtr[4];
+        U32 const version = (FLG>>6) & _2BITS;
+        blockChecksumFlag = (FLG>>4) & _1BIT;
+        blockMode = (FLG>>5) & _1BIT;
+        contentSizeFlag = (FLG>>3) & _1BIT;
+        contentChecksumFlag = (FLG>>2) & _1BIT;
+        dictIDFlag = FLG & _1BIT;
+        /* validate */
+        if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */
+        if (version != 1) RETURN_ERROR(headerVersion_wrong);       /* Version Number, only supported value */
+    }
+
+    /* Frame Header Size */
+    frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);
+
+    if (srcSize < frameHeaderSize) {
+        /* not enough input to fully decode frame header */
+        if (srcPtr != dctx->header)
+            memcpy(dctx->header, srcPtr, srcSize);
+        dctx->tmpInSize = srcSize;
+        dctx->tmpInTarget = frameHeaderSize;
+        dctx->dStage = dstage_storeFrameHeader;
+        return srcSize;
+    }
+
+    {   U32 const BD = srcPtr[5];
+        blockSizeID = (BD>>4) & _3BITS;
+        /* validate */
+        if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set);   /* Reserved bit */
+        if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid);    /* 4-7 only supported values for the time being */
+        if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set);  /* Reserved bits */
+    }
+
+    /* check header */
+    assert(frameHeaderSize > 5);
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    {   BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);
+        RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid);
+    }
+#endif
+
+    /* save */
+    dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;
+    dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag;
+    dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;
+    dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;
+    dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID);
+    if (contentSizeFlag)
+        dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);
+    if (dictIDFlag)
+        dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5);
+
+    dctx->dStage = dstage_init;
+
+    return frameHeaderSize;
+}
+
+
+/*! LZ4F_headerSize() :
+ * @return : size of frame header
+ *           or an error code, which can be tested using LZ4F_isError()
+ */
+size_t LZ4F_headerSize(const void* src, size_t srcSize)
+{
+    RETURN_ERROR_IF(src == NULL, srcPtr_wrong);
+
+    /* minimal srcSize to determine header size */
+    if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH)
+        RETURN_ERROR(frameHeader_incomplete);
+
+    /* special case : skippable frames */
+    if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START)
+        return 8;
+
+    /* control magic number */
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER)
+        RETURN_ERROR(frameType_unknown);
+#endif
+
+    /* Frame Header Size */
+    {   BYTE const FLG = ((const BYTE*)src)[4];
+        U32 const contentSizeFlag = (FLG>>3) & _1BIT;
+        U32 const dictIDFlag = FLG & _1BIT;
+        return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);
+    }
+}
+
+/*! LZ4F_getFrameInfo() :
+ *  This function extracts frame parameters (max blockSize, frame checksum, etc.).
+ *  Usage is optional. Objective is to provide relevant information for allocation purposes.
+ *  This function works in 2 situations :
+ *   - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process.
+ *     Amount of input data provided must be large enough to successfully decode the frame header.
+ *     A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum.
+ *   - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx.
+ *  The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value).
+ *  Decompression must resume from (srcBuffer + *srcSizePtr).
+ * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call,
+ *           or an error code which can be tested using LZ4F_isError()
+ *  note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped.
+ *  note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.
+ */
+LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,
+                                   LZ4F_frameInfo_t* frameInfoPtr,
+                             const void* srcBuffer, size_t* srcSizePtr)
+{
+    LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader);
+    if (dctx->dStage > dstage_storeFrameHeader) {
+        /* frameInfo already decoded */
+        size_t o=0, i=0;
+        *srcSizePtr = 0;
+        *frameInfoPtr = dctx->frameInfo;
+        /* returns : recommended nb of bytes for LZ4F_decompress() */
+        return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL);
+    } else {
+        if (dctx->dStage == dstage_storeFrameHeader) {
+            /* frame decoding already started, in the middle of header => automatic fail */
+            *srcSizePtr = 0;
+            RETURN_ERROR(frameDecoding_alreadyStarted);
+        } else {
+            size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr);
+            if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; }
+            if (*srcSizePtr < hSize) {
+                *srcSizePtr=0;
+                RETURN_ERROR(frameHeader_incomplete);
+            }
+
+            {   size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);
+                if (LZ4F_isError(decodeResult)) {
+                    *srcSizePtr = 0;
+                } else {
+                    *srcSizePtr = decodeResult;
+                    decodeResult = BHSize;   /* block header size */
+                }
+                *frameInfoPtr = dctx->frameInfo;
+                return decodeResult;
+    }   }   }
+}
+
+
+/* LZ4F_updateDict() :
+ * only used for LZ4F_blockLinked mode
+ * Condition : @dstPtr != NULL
+ */
+static void LZ4F_updateDict(LZ4F_dctx* dctx,
+                      const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart,
+                      unsigned withinTmp)
+{
+    assert(dstPtr != NULL);
+    if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr;  /* will lead to prefix mode */
+    assert(dctx->dict != NULL);
+
+    if (dctx->dict + dctx->dictSize == dstPtr) {  /* prefix mode, everything within dstBuffer */
+        dctx->dictSize += dstSize;
+        return;
+    }
+
+    assert(dstPtr >= dstBufferStart);
+    if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) {  /* history in dstBuffer becomes large enough to become dictionary */
+        dctx->dict = (const BYTE*)dstBufferStart;
+        dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize;
+        return;
+    }
+
+    assert(dstSize < 64 KB);   /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */
+
+    /* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */
+    assert(dctx->tmpOutBuffer != NULL);
+
+    if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) {   /* continue history within tmpOutBuffer */
+        /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */
+        assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart);
+        dctx->dictSize += dstSize;
+        return;
+    }
+
+    if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
+        size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);
+        size_t copySize = 64 KB - dctx->tmpOutSize;
+        const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;
+        if (dctx->tmpOutSize > 64 KB) copySize = 0;
+        if (copySize > preserveSize) copySize = preserveSize;
+
+        memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
+
+        dctx->dict = dctx->tmpOutBuffer;
+        dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize;
+        return;
+    }
+
+    if (dctx->dict == dctx->tmpOutBuffer) {    /* copy dst into tmp to complete dict */
+        if (dctx->dictSize + dstSize > dctx->maxBufferSize) {  /* tmp buffer not large enough */
+            size_t const preserveSize = 64 KB - dstSize;
+            memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
+            dctx->dictSize = preserveSize;
+        }
+        memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize);
+        dctx->dictSize += dstSize;
+        return;
+    }
+
+    /* join dict & dest into tmp */
+    {   size_t preserveSize = 64 KB - dstSize;
+        if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize;
+        memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
+        memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize);
+        dctx->dict = dctx->tmpOutBuffer;
+        dctx->dictSize = preserveSize + dstSize;
+    }
+}
+
+
+/*! LZ4F_decompress() :
+ *  Call this function repetitively to regenerate compressed data in srcBuffer.
+ *  The function will attempt to decode up to *srcSizePtr bytes from srcBuffer
+ *  into dstBuffer of capacity *dstSizePtr.
+ *
+ *  The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
+ *
+ *  The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
+ *  If number of bytes read is < number of bytes provided, then decompression operation is not complete.
+ *  Remaining data will have to be presented again in a subsequent invocation.
+ *
+ *  The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.
+ *  Schematically, it's the size of the current (or remaining) compressed block + header of next block.
+ *  Respecting the hint provides a small boost to performance, since it allows less buffer shuffling.
+ *  Note that this is just a hint, and it's always possible to any srcSize value.
+ *  When a frame is fully decoded, @return will be 0.
+ *  If decompression failed, @return is an error code which can be tested using LZ4F_isError().
+ */
+size_t LZ4F_decompress(LZ4F_dctx* dctx,
+                       void* dstBuffer, size_t* dstSizePtr,
+                       const void* srcBuffer, size_t* srcSizePtr,
+                       const LZ4F_decompressOptions_t* decompressOptionsPtr)
+{
+    LZ4F_decompressOptions_t optionsNull;
+    const BYTE* const srcStart = (const BYTE*)srcBuffer;
+    const BYTE* const srcEnd = srcStart + *srcSizePtr;
+    const BYTE* srcPtr = srcStart;
+    BYTE* const dstStart = (BYTE*)dstBuffer;
+    BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL;
+    BYTE* dstPtr = dstStart;
+    const BYTE* selectedIn = NULL;
+    unsigned doAnotherStage = 1;
+    size_t nextSrcSizeHint = 1;
+
+
+    DEBUGLOG(5, "LZ4F_decompress : %p,%u => %p,%u",
+            srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr);
+    if (dstBuffer == NULL) assert(*dstSizePtr == 0);
+    MEM_INIT(&optionsNull, 0, sizeof(optionsNull));
+    if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;
+    *srcSizePtr = 0;
+    *dstSizePtr = 0;
+    assert(dctx != NULL);
+    dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */
+
+    /* behaves as a state machine */
+
+    while (doAnotherStage) {
+
+        switch(dctx->dStage)
+        {
+
+        case dstage_getFrameHeader:
+            DEBUGLOG(6, "dstage_getFrameHeader");
+            if ((size_t)(srcEnd-srcPtr) >= maxFHSize) {  /* enough to decode - shortcut */
+                size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr));  /* will update dStage appropriately */
+                FORWARD_IF_ERROR(hSize);
+                srcPtr += hSize;
+                break;
+            }
+            dctx->tmpInSize = 0;
+            if (srcEnd-srcPtr == 0) return minFHSize;   /* 0-size input */
+            dctx->tmpInTarget = minFHSize;   /* minimum size to decode header */
+            dctx->dStage = dstage_storeFrameHeader;
+            /* fall-through */
+
+        case dstage_storeFrameHeader:
+            DEBUGLOG(6, "dstage_storeFrameHeader");
+            {   size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr));
+                memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
+                dctx->tmpInSize += sizeToCopy;
+                srcPtr += sizeToCopy;
+            }
+            if (dctx->tmpInSize < dctx->tmpInTarget) {
+                nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize;   /* rest of header + nextBlockHeader */
+                doAnotherStage = 0;   /* not enough src data, ask for some more */
+                break;
+            }
+            FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */
+            break;
+
+        case dstage_init:
+            DEBUGLOG(6, "dstage_init");
+            if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0);
+            /* internal buffers allocation */
+            {   size_t const bufferNeeded = dctx->maxBlockSize
+                    + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0);
+                if (bufferNeeded > dctx->maxBufferSize) {   /* tmp buffers too small */
+                    dctx->maxBufferSize = 0;   /* ensure allocation will be re-attempted on next entry*/
+                    LZ4F_free(dctx->tmpIn, dctx->cmem);
+                    dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem);
+                    RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed);
+                    LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);
+                    dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem);
+                    RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed);
+                    dctx->maxBufferSize = bufferNeeded;
+            }   }
+            dctx->tmpInSize = 0;
+            dctx->tmpInTarget = 0;
+            dctx->tmpOut = dctx->tmpOutBuffer;
+            dctx->tmpOutStart = 0;
+            dctx->tmpOutSize = 0;
+
+            dctx->dStage = dstage_getBlockHeader;
+            /* fall-through */
+
+        case dstage_getBlockHeader:
+            if ((size_t)(srcEnd - srcPtr) >= BHSize) {
+                selectedIn = srcPtr;
+                srcPtr += BHSize;
+            } else {
+                /* not enough input to read cBlockSize field */
+                dctx->tmpInSize = 0;
+                dctx->dStage = dstage_storeBlockHeader;
+            }
+
+            if (dctx->dStage == dstage_storeBlockHeader)   /* can be skipped */
+        case dstage_storeBlockHeader:
+            {   size_t const remainingInput = (size_t)(srcEnd - srcPtr);
+                size_t const wantedData = BHSize - dctx->tmpInSize;
+                size_t const sizeToCopy = MIN(wantedData, remainingInput);
+                memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
+                srcPtr += sizeToCopy;
+                dctx->tmpInSize += sizeToCopy;
+
+                if (dctx->tmpInSize < BHSize) {   /* not enough input for cBlockSize */
+                    nextSrcSizeHint = BHSize - dctx->tmpInSize;
+                    doAnotherStage  = 0;
+                    break;
+                }
+                selectedIn = dctx->tmpIn;
+            }   /* if (dctx->dStage == dstage_storeBlockHeader) */
+
+        /* decode block header */
+            {   U32 const blockHeader = LZ4F_readLE32(selectedIn);
+                size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU;
+                size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize;
+                if (blockHeader==0) {  /* frameEnd signal, no more block */
+                    DEBUGLOG(5, "end of frame");
+                    dctx->dStage = dstage_getSuffix;
+                    break;
+                }
+                if (nextCBlockSize > dctx->maxBlockSize) {
+                    RETURN_ERROR(maxBlockSize_invalid);
+                }
+                if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) {
+                    /* next block is uncompressed */
+                    dctx->tmpInTarget = nextCBlockSize;
+                    DEBUGLOG(5, "next block is uncompressed (size %u)", (U32)nextCBlockSize);
+                    if (dctx->frameInfo.blockChecksumFlag) {
+                        (void)XXH32_reset(&dctx->blockChecksum, 0);
+                    }
+                    dctx->dStage = dstage_copyDirect;
+                    break;
+                }
+                /* next block is a compressed block */
+                dctx->tmpInTarget = nextCBlockSize + crcSize;
+                dctx->dStage = dstage_getCBlock;
+                if (dstPtr==dstEnd || srcPtr==srcEnd) {
+                    nextSrcSizeHint = BHSize + nextCBlockSize + crcSize;
+                    doAnotherStage = 0;
+                }
+                break;
+            }
+
+        case dstage_copyDirect:   /* uncompressed block */
+            DEBUGLOG(6, "dstage_copyDirect");
+            {   size_t sizeToCopy;
+                if (dstPtr == NULL) {
+                    sizeToCopy = 0;
+                } else {
+                    size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr));
+                    sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize);
+                    memcpy(dstPtr, srcPtr, sizeToCopy);
+                    if (!dctx->skipChecksum) {
+                        if (dctx->frameInfo.blockChecksumFlag) {
+                            (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy);
+                        }
+                        if (dctx->frameInfo.contentChecksumFlag)
+                            (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy);
+                    }
+                    if (dctx->frameInfo.contentSize)
+                        dctx->frameRemainingSize -= sizeToCopy;
+
+                    /* history management (linked blocks only)*/
+                    if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {
+                        LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0);
+                    }
+                    srcPtr += sizeToCopy;
+                    dstPtr += sizeToCopy;
+                }
+                if (sizeToCopy == dctx->tmpInTarget) {   /* all done */
+                    if (dctx->frameInfo.blockChecksumFlag) {
+                        dctx->tmpInSize = 0;
+                        dctx->dStage = dstage_getBlockChecksum;
+                    } else
+                        dctx->dStage = dstage_getBlockHeader;  /* new block */
+                    break;
+                }
+                dctx->tmpInTarget -= sizeToCopy;  /* need to copy more */
+            }
+            nextSrcSizeHint = dctx->tmpInTarget +
+                            +(dctx->frameInfo.blockChecksumFlag ? BFSize : 0)
+                            + BHSize /* next header size */;
+            doAnotherStage = 0;
+            break;
+
+        /* check block checksum for recently transferred uncompressed block */
+        case dstage_getBlockChecksum:
+            DEBUGLOG(6, "dstage_getBlockChecksum");
+            {   const void* crcSrc;
+                if ((srcEnd-srcPtr >= 4) && (dctx->tmpInSize==0)) {
+                    crcSrc = srcPtr;
+                    srcPtr += 4;
+                } else {
+                    size_t const stillToCopy = 4 - dctx->tmpInSize;
+                    size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr));
+                    memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
+                    dctx->tmpInSize += sizeToCopy;
+                    srcPtr += sizeToCopy;
+                    if (dctx->tmpInSize < 4) {  /* all input consumed */
+                        doAnotherStage = 0;
+                        break;
+                    }
+                    crcSrc = dctx->header;
+                }
+                if (!dctx->skipChecksum) {
+                    U32 const readCRC = LZ4F_readLE32(crcSrc);
+                    U32 const calcCRC = XXH32_digest(&dctx->blockChecksum);
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+                    DEBUGLOG(6, "compare block checksum");
+                    if (readCRC != calcCRC) {
+                        DEBUGLOG(4, "incorrect block checksum: %08X != %08X",
+                                readCRC, calcCRC);
+                        RETURN_ERROR(blockChecksum_invalid);
+                    }
+#else
+                    (void)readCRC;
+                    (void)calcCRC;
+#endif
+            }   }
+            dctx->dStage = dstage_getBlockHeader;  /* new block */
+            break;
+
+        case dstage_getCBlock:
+            DEBUGLOG(6, "dstage_getCBlock");
+            if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) {
+                dctx->tmpInSize = 0;
+                dctx->dStage = dstage_storeCBlock;
+                break;
+            }
+            /* input large enough to read full block directly */
+            selectedIn = srcPtr;
+            srcPtr += dctx->tmpInTarget;
+
+            if (0)  /* always jump over next block */
+        case dstage_storeCBlock:
+            {   size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize;
+                size_t const inputLeft = (size_t)(srcEnd-srcPtr);
+                size_t const sizeToCopy = MIN(wantedData, inputLeft);
+                memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
+                dctx->tmpInSize += sizeToCopy;
+                srcPtr += sizeToCopy;
+                if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */
+                    nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize)
+                                    + (dctx->frameInfo.blockChecksumFlag ? BFSize : 0)
+                                    + BHSize /* next header size */;
+                    doAnotherStage = 0;
+                    break;
+                }
+                selectedIn = dctx->tmpIn;
+            }
+
+            /* At this stage, input is large enough to decode a block */
+
+            /* First, decode and control block checksum if it exists */
+            if (dctx->frameInfo.blockChecksumFlag) {
+                assert(dctx->tmpInTarget >= 4);
+                dctx->tmpInTarget -= 4;
+                assert(selectedIn != NULL);  /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */
+                {   U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget);
+                    U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0);
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+                    RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid);
+#else
+                    (void)readBlockCrc;
+                    (void)calcBlockCrc;
+#endif
+            }   }
+
+            /* decode directly into destination buffer if there is enough room */
+            if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize)
+                 /* unless the dictionary is stored in tmpOut:
+                  * in which case it's faster to decode within tmpOut
+                  * to benefit from prefix speedup */
+              && !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) )
+            {
+                const char* dict = (const char*)dctx->dict;
+                size_t dictSize = dctx->dictSize;
+                int decodedSize;
+                assert(dstPtr != NULL);
+                if (dict && dictSize > 1 GB) {
+                    /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */
+                    dict += dictSize - 64 KB;
+                    dictSize = 64 KB;
+                }
+                decodedSize = LZ4_decompress_safe_usingDict(
+                        (const char*)selectedIn, (char*)dstPtr,
+                        (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,
+                        dict, (int)dictSize);
+                RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);
+                if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum))
+                    XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize);
+                if (dctx->frameInfo.contentSize)
+                    dctx->frameRemainingSize -= (size_t)decodedSize;
+
+                /* dictionary management */
+                if (dctx->frameInfo.blockMode==LZ4F_blockLinked) {
+                    LZ4F_updateDict(dctx, dstPtr, (size_t)decodedSize, dstStart, 0);
+                }
+
+                dstPtr += decodedSize;
+                dctx->dStage = dstage_getBlockHeader;  /* end of block, let's get another one */
+                break;
+            }
+
+            /* not enough place into dst : decode into tmpOut */
+
+            /* manage dictionary */
+            if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {
+                if (dctx->dict == dctx->tmpOutBuffer) {
+                    /* truncate dictionary to 64 KB if too big */
+                    if (dctx->dictSize > 128 KB) {
+                        memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB);
+                        dctx->dictSize = 64 KB;
+                    }
+                    dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize;
+                } else {  /* dict not within tmpOut */
+                    size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB);
+                    dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;
+            }   }
+
+            /* Decode block into tmpOut */
+            {   const char* dict = (const char*)dctx->dict;
+                size_t dictSize = dctx->dictSize;
+                int decodedSize;
+                if (dict && dictSize > 1 GB) {
+                    /* the dictSize param is an int, avoid truncation / sign issues */
+                    dict += dictSize - 64 KB;
+                    dictSize = 64 KB;
+                }
+                decodedSize = LZ4_decompress_safe_usingDict(
+                        (const char*)selectedIn, (char*)dctx->tmpOut,
+                        (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,
+                        dict, (int)dictSize);
+                RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);
+                if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum)
+                    XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize);
+                if (dctx->frameInfo.contentSize)
+                    dctx->frameRemainingSize -= (size_t)decodedSize;
+                dctx->tmpOutSize = (size_t)decodedSize;
+                dctx->tmpOutStart = 0;
+                dctx->dStage = dstage_flushOut;
+            }
+            /* fall-through */
+
+        case dstage_flushOut:  /* flush decoded data from tmpOut to dstBuffer */
+            DEBUGLOG(6, "dstage_flushOut");
+            if (dstPtr != NULL) {
+                size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr));
+                memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy);
+
+                /* dictionary management */
+                if (dctx->frameInfo.blockMode == LZ4F_blockLinked)
+                    LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/);
+
+                dctx->tmpOutStart += sizeToCopy;
+                dstPtr += sizeToCopy;
+            }
+            if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */
+                dctx->dStage = dstage_getBlockHeader;  /* get next block */
+                break;
+            }
+            /* could not flush everything : stop there, just request a block header */
+            doAnotherStage = 0;
+            nextSrcSizeHint = BHSize;
+            break;
+
+        case dstage_getSuffix:
+            RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong);   /* incorrect frame size decoded */
+            if (!dctx->frameInfo.contentChecksumFlag) {  /* no checksum, frame is completed */
+                nextSrcSizeHint = 0;
+                LZ4F_resetDecompressionContext(dctx);
+                doAnotherStage = 0;
+                break;
+            }
+            if ((srcEnd - srcPtr) < 4) {  /* not enough size for entire CRC */
+                dctx->tmpInSize = 0;
+                dctx->dStage = dstage_storeSuffix;
+            } else {
+                selectedIn = srcPtr;
+                srcPtr += 4;
+            }
+
+            if (dctx->dStage == dstage_storeSuffix)   /* can be skipped */
+        case dstage_storeSuffix:
+            {   size_t const remainingInput = (size_t)(srcEnd - srcPtr);
+                size_t const wantedData = 4 - dctx->tmpInSize;
+                size_t const sizeToCopy = MIN(wantedData, remainingInput);
+                memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
+                srcPtr += sizeToCopy;
+                dctx->tmpInSize += sizeToCopy;
+                if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */
+                    nextSrcSizeHint = 4 - dctx->tmpInSize;
+                    doAnotherStage=0;
+                    break;
+                }
+                selectedIn = dctx->tmpIn;
+            }   /* if (dctx->dStage == dstage_storeSuffix) */
+
+        /* case dstage_checkSuffix: */   /* no direct entry, avoid initialization risks */
+            if (!dctx->skipChecksum) {
+                U32 const readCRC = LZ4F_readLE32(selectedIn);
+                U32 const resultCRC = XXH32_digest(&(dctx->xxh));
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+                RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid);
+#else
+                (void)readCRC;
+                (void)resultCRC;
+#endif
+            }
+            nextSrcSizeHint = 0;
+            LZ4F_resetDecompressionContext(dctx);
+            doAnotherStage = 0;
+            break;
+
+        case dstage_getSFrameSize:
+            if ((srcEnd - srcPtr) >= 4) {
+                selectedIn = srcPtr;
+                srcPtr += 4;
+            } else {
+                /* not enough input to read cBlockSize field */
+                dctx->tmpInSize = 4;
+                dctx->tmpInTarget = 8;
+                dctx->dStage = dstage_storeSFrameSize;
+            }
+
+            if (dctx->dStage == dstage_storeSFrameSize)
+        case dstage_storeSFrameSize:
+            {   size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize,
+                                             (size_t)(srcEnd - srcPtr) );
+                memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
+                srcPtr += sizeToCopy;
+                dctx->tmpInSize += sizeToCopy;
+                if (dctx->tmpInSize < dctx->tmpInTarget) {
+                    /* not enough input to get full sBlockSize; wait for more */
+                    nextSrcSizeHint = dctx->tmpInTarget - dctx->tmpInSize;
+                    doAnotherStage = 0;
+                    break;
+                }
+                selectedIn = dctx->header + 4;
+            }   /* if (dctx->dStage == dstage_storeSFrameSize) */
+
+        /* case dstage_decodeSFrameSize: */   /* no direct entry */
+            {   size_t const SFrameSize = LZ4F_readLE32(selectedIn);
+                dctx->frameInfo.contentSize = SFrameSize;
+                dctx->tmpInTarget = SFrameSize;
+                dctx->dStage = dstage_skipSkippable;
+                break;
+            }
+
+        case dstage_skipSkippable:
+            {   size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr));
+                srcPtr += skipSize;
+                dctx->tmpInTarget -= skipSize;
+                doAnotherStage = 0;
+                nextSrcSizeHint = dctx->tmpInTarget;
+                if (nextSrcSizeHint) break;  /* still more to skip */
+                /* frame fully skipped : prepare context for a new frame */
+                LZ4F_resetDecompressionContext(dctx);
+                break;
+            }
+        }   /* switch (dctx->dStage) */
+    }   /* while (doAnotherStage) */
+
+    /* preserve history within tmpOut whenever necessary */
+    LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2);
+    if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked)  /* next block will use up to 64KB from previous ones */
+      && (dctx->dict != dctx->tmpOutBuffer)             /* dictionary is not already within tmp */
+      && (dctx->dict != NULL)                           /* dictionary exists */
+      && (!decompressOptionsPtr->stableDst)             /* cannot rely on dst data to remain there for next call */
+      && ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) )  /* valid stages : [init ... getSuffix[ */
+    {
+        if (dctx->dStage == dstage_flushOut) {
+            size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);
+            size_t copySize = 64 KB - dctx->tmpOutSize;
+            const BYTE* oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;
+            if (dctx->tmpOutSize > 64 KB) copySize = 0;
+            if (copySize > preserveSize) copySize = preserveSize;
+            assert(dctx->tmpOutBuffer != NULL);
+
+            memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
+
+            dctx->dict = dctx->tmpOutBuffer;
+            dctx->dictSize = preserveSize + dctx->tmpOutStart;
+        } else {
+            const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize;
+            size_t const newDictSize = MIN(dctx->dictSize, 64 KB);
+
+            memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);
+
+            dctx->dict = dctx->tmpOutBuffer;
+            dctx->dictSize = newDictSize;
+            dctx->tmpOut = dctx->tmpOutBuffer + newDictSize;
+        }
+    }
+
+    *srcSizePtr = (size_t)(srcPtr - srcStart);
+    *dstSizePtr = (size_t)(dstPtr - dstStart);
+    return nextSrcSizeHint;
+}
+
+/*! LZ4F_decompress_usingDict() :
+ *  Same as LZ4F_decompress(), using a predefined dictionary.
+ *  Dictionary is used "in place", without any preprocessing.
+ *  It must remain accessible throughout the entire frame decoding.
+ */
+size_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx,
+                       void* dstBuffer, size_t* dstSizePtr,
+                       const void* srcBuffer, size_t* srcSizePtr,
+                       const void* dict, size_t dictSize,
+                       const LZ4F_decompressOptions_t* decompressOptionsPtr)
+{
+    if (dctx->dStage <= dstage_init) {
+        dctx->dict = (const BYTE*)dict;
+        dctx->dictSize = dictSize;
+    }
+    return LZ4F_decompress(dctx, dstBuffer, dstSizePtr,
+                           srcBuffer, srcSizePtr,
+                           decompressOptionsPtr);
+}
diff --git a/3rdparty/lz4/lz4frame.h b/3rdparty/lz4/lz4frame.h
new file mode 100644
index 00000000000..e8ce66d3e2d
--- /dev/null
+++ b/3rdparty/lz4/lz4frame.h
@@ -0,0 +1,707 @@
+/*
+   LZ4F - LZ4-Frame library
+   Header File
+   Copyright (C) 2011-2020, Yann Collet.
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - LZ4 source repository : https://github.com/lz4/lz4
+   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+
+/* LZ4F is a stand-alone API able to create and decode LZ4 frames
+ * conformant with specification v1.6.1 in doc/lz4_Frame_format.md .
+ * Generated frames are compatible with `lz4` CLI.
+ *
+ * LZ4F also offers streaming capabilities.
+ *
+ * lz4.h is not required when using lz4frame.h,
+ * except to extract common constants such as LZ4_VERSION_NUMBER.
+ * */
+
+#ifndef LZ4F_H_09782039843
+#define LZ4F_H_09782039843
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* ---   Dependency   --- */
+#include <stddef.h>   /* size_t */
+
+
+/**
+ * Introduction
+ *
+ * lz4frame.h implements LZ4 frame specification: see doc/lz4_Frame_format.md .
+ * LZ4 Frames are compatible with `lz4` CLI,
+ * and designed to be interoperable with any system.
+**/
+
+/*-***************************************************************
+ *  Compiler specifics
+ *****************************************************************/
+/*  LZ4_DLL_EXPORT :
+ *  Enable exporting of functions when building a Windows DLL
+ *  LZ4FLIB_VISIBILITY :
+ *  Control library symbols visibility.
+ */
+#ifndef LZ4FLIB_VISIBILITY
+#  if defined(__GNUC__) && (__GNUC__ >= 4)
+#    define LZ4FLIB_VISIBILITY __attribute__ ((visibility ("default")))
+#  else
+#    define LZ4FLIB_VISIBILITY
+#  endif
+#endif
+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
+#  define LZ4FLIB_API __declspec(dllexport) LZ4FLIB_VISIBILITY
+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
+#  define LZ4FLIB_API __declspec(dllimport) LZ4FLIB_VISIBILITY
+#else
+#  define LZ4FLIB_API LZ4FLIB_VISIBILITY
+#endif
+
+#ifdef LZ4F_DISABLE_DEPRECATE_WARNINGS
+#  define LZ4F_DEPRECATE(x) x
+#else
+#  if defined(_MSC_VER)
+#    define LZ4F_DEPRECATE(x) x   /* __declspec(deprecated) x - only works with C++ */
+#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
+#    define LZ4F_DEPRECATE(x) x __attribute__((deprecated))
+#  else
+#    define LZ4F_DEPRECATE(x) x   /* no deprecation warning for this compiler */
+#  endif
+#endif
+
+
+/*-************************************
+ *  Error management
+ **************************************/
+typedef size_t LZ4F_errorCode_t;
+
+LZ4FLIB_API unsigned    LZ4F_isError(LZ4F_errorCode_t code);   /**< tells when a function result is an error code */
+LZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code);   /**< return error code string; for debugging */
+
+
+/*-************************************
+ *  Frame compression types
+ ************************************* */
+/* #define LZ4F_ENABLE_OBSOLETE_ENUMS   // uncomment to enable obsolete enums */
+#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS
+#  define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x
+#else
+#  define LZ4F_OBSOLETE_ENUM(x)
+#endif
+
+/* The larger the block size, the (slightly) better the compression ratio,
+ * though there are diminishing returns.
+ * Larger blocks also increase memory usage on both compression and decompression sides.
+ */
+typedef enum {
+    LZ4F_default=0,
+    LZ4F_max64KB=4,
+    LZ4F_max256KB=5,
+    LZ4F_max1MB=6,
+    LZ4F_max4MB=7
+    LZ4F_OBSOLETE_ENUM(max64KB)
+    LZ4F_OBSOLETE_ENUM(max256KB)
+    LZ4F_OBSOLETE_ENUM(max1MB)
+    LZ4F_OBSOLETE_ENUM(max4MB)
+} LZ4F_blockSizeID_t;
+
+/* Linked blocks sharply reduce inefficiencies when using small blocks,
+ * they compress better.
+ * However, some LZ4 decoders are only compatible with independent blocks */
+typedef enum {
+    LZ4F_blockLinked=0,
+    LZ4F_blockIndependent
+    LZ4F_OBSOLETE_ENUM(blockLinked)
+    LZ4F_OBSOLETE_ENUM(blockIndependent)
+} LZ4F_blockMode_t;
+
+typedef enum {
+    LZ4F_noContentChecksum=0,
+    LZ4F_contentChecksumEnabled
+    LZ4F_OBSOLETE_ENUM(noContentChecksum)
+    LZ4F_OBSOLETE_ENUM(contentChecksumEnabled)
+} LZ4F_contentChecksum_t;
+
+typedef enum {
+    LZ4F_noBlockChecksum=0,
+    LZ4F_blockChecksumEnabled
+} LZ4F_blockChecksum_t;
+
+typedef enum {
+    LZ4F_frame=0,
+    LZ4F_skippableFrame
+    LZ4F_OBSOLETE_ENUM(skippableFrame)
+} LZ4F_frameType_t;
+
+#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS
+typedef LZ4F_blockSizeID_t blockSizeID_t;
+typedef LZ4F_blockMode_t blockMode_t;
+typedef LZ4F_frameType_t frameType_t;
+typedef LZ4F_contentChecksum_t contentChecksum_t;
+#endif
+
+/*! LZ4F_frameInfo_t :
+ *  makes it possible to set or read frame parameters.
+ *  Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO,
+ *  setting all parameters to default.
+ *  It's then possible to update selectively some parameters */
+typedef struct {
+  LZ4F_blockSizeID_t     blockSizeID;         /* max64KB, max256KB, max1MB, max4MB; 0 == default */
+  LZ4F_blockMode_t       blockMode;           /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */
+  LZ4F_contentChecksum_t contentChecksumFlag; /* 1: frame terminated with 32-bit checksum of decompressed data; 0: disabled (default) */
+  LZ4F_frameType_t       frameType;           /* read-only field : LZ4F_frame or LZ4F_skippableFrame */
+  unsigned long long     contentSize;         /* Size of uncompressed content ; 0 == unknown */
+  unsigned               dictID;              /* Dictionary ID, sent by compressor to help decoder select correct dictionary; 0 == no dictID provided */
+  LZ4F_blockChecksum_t   blockChecksumFlag;   /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */
+} LZ4F_frameInfo_t;
+
+#define LZ4F_INIT_FRAMEINFO   { LZ4F_default, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U, LZ4F_noBlockChecksum }    /* v1.8.3+ */
+
+/*! LZ4F_preferences_t :
+ *  makes it possible to supply advanced compression instructions to streaming interface.
+ *  Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES,
+ *  setting all parameters to default.
+ *  All reserved fields must be set to zero. */
+typedef struct {
+  LZ4F_frameInfo_t frameInfo;
+  int      compressionLevel;    /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */
+  unsigned autoFlush;           /* 1: always flush; reduces usage of internal buffers */
+  unsigned favorDecSpeed;       /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */  /* v1.8.2+ */
+  unsigned reserved[3];         /* must be zero for forward compatibility */
+} LZ4F_preferences_t;
+
+#define LZ4F_INIT_PREFERENCES   { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } }    /* v1.8.3+ */
+
+
+/*-*********************************
+*  Simple compression function
+***********************************/
+
+LZ4FLIB_API int LZ4F_compressionLevel_max(void);   /* v1.8.0+ */
+
+/*! LZ4F_compressFrameBound() :
+ *  Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences.
+ * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences.
+ *  Note : this result is only usable with LZ4F_compressFrame().
+ *         It may also be relevant to LZ4F_compressUpdate() _only if_ no flush() operation is ever performed.
+ */
+LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr);
+
+/*! LZ4F_compressFrame() :
+ *  Compress srcBuffer content into an LZ4-compressed frame.
+ *  It's a one shot operation, all input content is consumed, and all output is generated.
+ *
+ *  Note : it's a stateless operation (no LZ4F_cctx state needed).
+ *  In order to reduce load on the allocator, LZ4F_compressFrame(), by default,
+ *  uses the stack to allocate space for the compression state and some table.
+ *  If this usage of the stack is too much for your application,
+ *  consider compiling `lz4frame.c` with compile-time macro LZ4F_HEAPMODE set to 1 instead.
+ *  All state allocations will use the Heap.
+ *  It also means each invocation of LZ4F_compressFrame() will trigger several internal alloc/free invocations.
+ *
+ * @dstCapacity MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
+ * @preferencesPtr is optional : one can provide NULL, in which case all preferences are set to default.
+ * @return : number of bytes written into dstBuffer.
+ *           or an error code if it fails (can be tested using LZ4F_isError())
+ */
+LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,
+                                const void* srcBuffer, size_t srcSize,
+                                const LZ4F_preferences_t* preferencesPtr);
+
+
+/*-***********************************
+*  Advanced compression functions
+*************************************/
+typedef struct LZ4F_cctx_s LZ4F_cctx;   /* incomplete type */
+typedef LZ4F_cctx* LZ4F_compressionContext_t;  /* for compatibility with older APIs, prefer using LZ4F_cctx */
+
+typedef struct {
+  unsigned stableSrc;    /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */
+  unsigned reserved[3];
+} LZ4F_compressOptions_t;
+
+/*---   Resource Management   ---*/
+
+#define LZ4F_VERSION 100    /* This number can be used to check for an incompatible API breaking change */
+LZ4FLIB_API unsigned LZ4F_getVersion(void);
+
+/*! LZ4F_createCompressionContext() :
+ *  The first thing to do is to create a compressionContext object,
+ *  which will keep track of operation state during streaming compression.
+ *  This is achieved using LZ4F_createCompressionContext(), which takes as argument a version,
+ *  and a pointer to LZ4F_cctx*, to write the resulting pointer into.
+ *  @version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL.
+ *  The function provides a pointer to a fully allocated LZ4F_cctx object.
+ *  @cctxPtr MUST be != NULL.
+ *  If @return != zero, context creation failed.
+ *  A created compression context can be employed multiple times for consecutive streaming operations.
+ *  Once all streaming compression jobs are completed,
+ *  the state object can be released using LZ4F_freeCompressionContext().
+ *  Note1 : LZ4F_freeCompressionContext() is always successful. Its return value can be ignored.
+ *  Note2 : LZ4F_freeCompressionContext() works fine with NULL input pointers (do nothing).
+**/
+LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version);
+LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx);
+
+
+/*----    Compression    ----*/
+
+#define LZ4F_HEADER_SIZE_MIN  7   /* LZ4 Frame header size can vary, depending on selected parameters */
+#define LZ4F_HEADER_SIZE_MAX 19
+
+/* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */
+#define LZ4F_BLOCK_HEADER_SIZE 4
+
+/* Size in bytes of a block checksum footer in little-endian format. */
+#define LZ4F_BLOCK_CHECKSUM_SIZE 4
+
+/* Size in bytes of the content checksum. */
+#define LZ4F_CONTENT_CHECKSUM_SIZE 4
+
+/*! LZ4F_compressBegin() :
+ *  will write the frame header into dstBuffer.
+ *  dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.
+ * `prefsPtr` is optional : NULL can be provided to set all preferences to default.
+ * @return : number of bytes written into dstBuffer for the header
+ *           or an error code (which can be tested using LZ4F_isError())
+ */
+LZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx,
+                                      void* dstBuffer, size_t dstCapacity,
+                                      const LZ4F_preferences_t* prefsPtr);
+
+/*! LZ4F_compressBound() :
+ *  Provides minimum dstCapacity required to guarantee success of
+ *  LZ4F_compressUpdate(), given a srcSize and preferences, for a worst case scenario.
+ *  When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() instead.
+ *  Note that the result is only valid for a single invocation of LZ4F_compressUpdate().
+ *  When invoking LZ4F_compressUpdate() multiple times,
+ *  if the output buffer is gradually filled up instead of emptied and re-used from its start,
+ *  one must check if there is enough remaining capacity before each invocation, using LZ4F_compressBound().
+ * @return is always the same for a srcSize and prefsPtr.
+ *  prefsPtr is optional : when NULL is provided, preferences will be set to cover worst case scenario.
+ *  tech details :
+ * @return if automatic flushing is not enabled, includes the possibility that internal buffer might already be filled by up to (blockSize-1) bytes.
+ *  It also includes frame footer (ending + checksum), since it might be generated by LZ4F_compressEnd().
+ * @return doesn't include frame header, as it was already generated by LZ4F_compressBegin().
+ */
+LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr);
+
+/*! LZ4F_compressUpdate() :
+ *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
+ *  Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations.
+ *  This value is provided by LZ4F_compressBound().
+ *  If this condition is not respected, LZ4F_compress() will fail (result is an errorCode).
+ *  After an error, the state is left in a UB state, and must be re-initialized or freed.
+ *  If previously an uncompressed block was written, buffered data is flushed
+ *  before appending compressed data is continued.
+ * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.
+ * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ */
+LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx,
+                                       void* dstBuffer, size_t dstCapacity,
+                                 const void* srcBuffer, size_t srcSize,
+                                 const LZ4F_compressOptions_t* cOptPtr);
+
+/*! LZ4F_flush() :
+ *  When data must be generated and sent immediately, without waiting for a block to be completely filled,
+ *  it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx.
+ * `dstCapacity` must be large enough to ensure the operation will be successful.
+ * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default.
+ * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx)
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ *  Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).
+ */
+LZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx,
+                              void* dstBuffer, size_t dstCapacity,
+                        const LZ4F_compressOptions_t* cOptPtr);
+
+/*! LZ4F_compressEnd() :
+ *  To properly finish an LZ4 frame, invoke LZ4F_compressEnd().
+ *  It will flush whatever data remained within `cctx` (like LZ4_flush())
+ *  and properly finalize the frame, with an endMark and a checksum.
+ * `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default.
+ * @return : nb of bytes written into dstBuffer, necessarily >= 4 (endMark),
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ *  Note : LZ4F_compressEnd() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).
+ *  A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task.
+ */
+LZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx,
+                                    void* dstBuffer, size_t dstCapacity,
+                              const LZ4F_compressOptions_t* cOptPtr);
+
+
+/*-*********************************
+*  Decompression functions
+***********************************/
+typedef struct LZ4F_dctx_s LZ4F_dctx;   /* incomplete type */
+typedef LZ4F_dctx* LZ4F_decompressionContext_t;   /* compatibility with previous API versions */
+
+typedef struct {
+  unsigned stableDst;     /* pledges that last 64KB decompressed data will remain available unmodified between invocations.
+                           * This optimization skips storage operations in tmp buffers. */
+  unsigned skipChecksums; /* disable checksum calculation and verification, even when one is present in frame, to save CPU time.
+                           * Setting this option to 1 once disables all checksums for the rest of the frame. */
+  unsigned reserved1;     /* must be set to zero for forward compatibility */
+  unsigned reserved0;     /* idem */
+} LZ4F_decompressOptions_t;
+
+
+/* Resource management */
+
+/*! LZ4F_createDecompressionContext() :
+ *  Create an LZ4F_dctx object, to track all decompression operations.
+ *  @version provided MUST be LZ4F_VERSION.
+ *  @dctxPtr MUST be valid.
+ *  The function fills @dctxPtr with the value of a pointer to an allocated and initialized LZ4F_dctx object.
+ *  The @return is an errorCode, which can be tested using LZ4F_isError().
+ *  dctx memory can be released using LZ4F_freeDecompressionContext();
+ *  Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released.
+ *  That is, it should be == 0 if decompression has been completed fully and correctly.
+ */
+LZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version);
+LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx);
+
+
+/*-***********************************
+*  Streaming decompression functions
+*************************************/
+
+#define LZ4F_MAGICNUMBER 0x184D2204U
+#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U
+#define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5
+
+/*! LZ4F_headerSize() : v1.9.0+
+ *  Provide the header size of a frame starting at `src`.
+ * `srcSize` must be >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH,
+ *  which is enough to decode the header length.
+ * @return : size of frame header
+ *           or an error code, which can be tested using LZ4F_isError()
+ *  note : Frame header size is variable, but is guaranteed to be
+ *         >= LZ4F_HEADER_SIZE_MIN bytes, and <= LZ4F_HEADER_SIZE_MAX bytes.
+ */
+LZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize);
+
+/*! LZ4F_getFrameInfo() :
+ *  This function extracts frame parameters (max blockSize, dictID, etc.).
+ *  Its usage is optional: user can also invoke LZ4F_decompress() directly.
+ *
+ *  Extracted information will fill an existing LZ4F_frameInfo_t structure.
+ *  This can be useful for allocation and dictionary identification purposes.
+ *
+ *  LZ4F_getFrameInfo() can work in the following situations :
+ *
+ *  1) At the beginning of a new frame, before any invocation of LZ4F_decompress().
+ *     It will decode header from `srcBuffer`,
+ *     consuming the header and starting the decoding process.
+ *
+ *     Input size must be large enough to contain the full frame header.
+ *     Frame header size can be known beforehand by LZ4F_headerSize().
+ *     Frame header size is variable, but is guaranteed to be >= LZ4F_HEADER_SIZE_MIN bytes,
+ *     and not more than <= LZ4F_HEADER_SIZE_MAX bytes.
+ *     Hence, blindly providing LZ4F_HEADER_SIZE_MAX bytes or more will always work.
+ *     It's allowed to provide more input data than the header size,
+ *     LZ4F_getFrameInfo() will only consume the header.
+ *
+ *     If input size is not large enough,
+ *     aka if it's smaller than header size,
+ *     function will fail and return an error code.
+ *
+ *  2) After decoding has been started,
+ *     it's possible to invoke LZ4F_getFrameInfo() anytime
+ *     to extract already decoded frame parameters stored within dctx.
+ *
+ *     Note that, if decoding has barely started,
+ *     and not yet read enough information to decode the header,
+ *     LZ4F_getFrameInfo() will fail.
+ *
+ *  The number of bytes consumed from srcBuffer will be updated in *srcSizePtr (necessarily <= original value).
+ *  LZ4F_getFrameInfo() only consumes bytes when decoding has not yet started,
+ *  and when decoding the header has been successful.
+ *  Decompression must then resume from (srcBuffer + *srcSizePtr).
+ *
+ * @return : a hint about how many srcSize bytes LZ4F_decompress() expects for next call,
+ *           or an error code which can be tested using LZ4F_isError().
+ *  note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely.
+ *  note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.
+ */
+LZ4FLIB_API size_t
+LZ4F_getFrameInfo(LZ4F_dctx* dctx,
+                  LZ4F_frameInfo_t* frameInfoPtr,
+            const void* srcBuffer, size_t* srcSizePtr);
+
+/*! LZ4F_decompress() :
+ *  Call this function repetitively to regenerate data compressed in `srcBuffer`.
+ *
+ *  The function requires a valid dctx state.
+ *  It will read up to *srcSizePtr bytes from srcBuffer,
+ *  and decompress data into dstBuffer, of capacity *dstSizePtr.
+ *
+ *  The nb of bytes consumed from srcBuffer will be written into *srcSizePtr (necessarily <= original value).
+ *  The nb of bytes decompressed into dstBuffer will be written into *dstSizePtr (necessarily <= original value).
+ *
+ *  The function does not necessarily read all input bytes, so always check value in *srcSizePtr.
+ *  Unconsumed source data must be presented again in subsequent invocations.
+ *
+ * `dstBuffer` can freely change between each consecutive function invocation.
+ * `dstBuffer` content will be overwritten.
+ *
+ *  Note: if `LZ4F_getFrameInfo()` is called before `LZ4F_decompress()`, srcBuffer must be updated to reflect
+ *  the number of bytes consumed after reading the frame header. Failure to update srcBuffer before calling
+ *  `LZ4F_decompress()` will cause decompression failure or, even worse, successful but incorrect decompression.
+ *  See the `LZ4F_getFrameInfo()` docs for details.
+ *
+ * @return : an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call.
+ *  Schematically, it's the size of the current (or remaining) compressed block + header of next block.
+ *  Respecting the hint provides some small speed benefit, because it skips intermediate buffers.
+ *  This is just a hint though, it's always possible to provide any srcSize.
+ *
+ *  When a frame is fully decoded, @return will be 0 (no more data expected).
+ *  When provided with more bytes than necessary to decode a frame,
+ *  LZ4F_decompress() will stop reading exactly at end of current frame, and @return 0.
+ *
+ *  If decompression failed, @return is an error code, which can be tested using LZ4F_isError().
+ *  After a decompression error, the `dctx` context is not resumable.
+ *  Use LZ4F_resetDecompressionContext() to return to clean state.
+ *
+ *  After a frame is fully decoded, dctx can be used again to decompress another frame.
+ */
+LZ4FLIB_API size_t
+LZ4F_decompress(LZ4F_dctx* dctx,
+                void* dstBuffer, size_t* dstSizePtr,
+          const void* srcBuffer, size_t* srcSizePtr,
+          const LZ4F_decompressOptions_t* dOptPtr);
+
+
+/*! LZ4F_resetDecompressionContext() : added in v1.8.0
+ *  In case of an error, the context is left in "undefined" state.
+ *  In which case, it's necessary to reset it, before re-using it.
+ *  This method can also be used to abruptly stop any unfinished decompression,
+ *  and start a new one using same context resources. */
+LZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx);   /* always successful */
+
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif  /* LZ4F_H_09782039843 */
+
+#if defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843)
+#define LZ4F_H_STATIC_09782039843
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* These declarations are not stable and may change in the future.
+ * They are therefore only safe to depend on
+ * when the caller is statically linked against the library.
+ * To access their declarations, define LZ4F_STATIC_LINKING_ONLY.
+ *
+ * By default, these symbols aren't published into shared/dynamic libraries.
+ * You can override this behavior and force them to be published
+ * by defining LZ4F_PUBLISH_STATIC_FUNCTIONS.
+ * Use at your own risk.
+ */
+#ifdef LZ4F_PUBLISH_STATIC_FUNCTIONS
+# define LZ4FLIB_STATIC_API LZ4FLIB_API
+#else
+# define LZ4FLIB_STATIC_API
+#endif
+
+
+/* ---   Error List   --- */
+#define LZ4F_LIST_ERRORS(ITEM) \
+        ITEM(OK_NoError) \
+        ITEM(ERROR_GENERIC) \
+        ITEM(ERROR_maxBlockSize_invalid) \
+        ITEM(ERROR_blockMode_invalid) \
+        ITEM(ERROR_contentChecksumFlag_invalid) \
+        ITEM(ERROR_compressionLevel_invalid) \
+        ITEM(ERROR_headerVersion_wrong) \
+        ITEM(ERROR_blockChecksum_invalid) \
+        ITEM(ERROR_reservedFlag_set) \
+        ITEM(ERROR_allocation_failed) \
+        ITEM(ERROR_srcSize_tooLarge) \
+        ITEM(ERROR_dstMaxSize_tooSmall) \
+        ITEM(ERROR_frameHeader_incomplete) \
+        ITEM(ERROR_frameType_unknown) \
+        ITEM(ERROR_frameSize_wrong) \
+        ITEM(ERROR_srcPtr_wrong) \
+        ITEM(ERROR_decompressionFailed) \
+        ITEM(ERROR_headerChecksum_invalid) \
+        ITEM(ERROR_contentChecksum_invalid) \
+        ITEM(ERROR_frameDecoding_alreadyStarted) \
+        ITEM(ERROR_compressionState_uninitialized) \
+        ITEM(ERROR_parameter_null) \
+        ITEM(ERROR_maxCode)
+
+#define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM,
+
+/* enum list is exposed, to handle specific errors */
+typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM)
+              _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes;
+
+LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult);
+
+
+/*! LZ4F_getBlockSize() :
+ *  Return, in scalar format (size_t),
+ *  the maximum block size associated with blockSizeID.
+**/
+LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID);
+
+/*! LZ4F_uncompressedUpdate() :
+ *  LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary.
+ *  Important rule: dstCapacity MUST be large enough to store the entire source buffer as
+ *  no compression is done for this operation
+ *  If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode).
+ *  After an error, the state is left in a UB state, and must be re-initialized or freed.
+ *  If previously a compressed block was written, buffered data is flushed
+ *  before appending uncompressed data is continued.
+ *  This is only supported when LZ4F_blockIndependent is used
+ * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.
+ * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).
+ *           or an error code if it fails (which can be tested using LZ4F_isError())
+ */
+LZ4FLIB_STATIC_API size_t
+LZ4F_uncompressedUpdate(LZ4F_cctx* cctx,
+                        void* dstBuffer, size_t dstCapacity,
+                  const void* srcBuffer, size_t srcSize,
+                  const LZ4F_compressOptions_t* cOptPtr);
+
+/**********************************
+ *  Bulk processing dictionary API
+ *********************************/
+
+/* A Dictionary is useful for the compression of small messages (KB range).
+ * It dramatically improves compression efficiency.
+ *
+ * LZ4 can ingest any input as dictionary, though only the last 64 KB are useful.
+ * Best results are generally achieved by using Zstandard's Dictionary Builder
+ * to generate a high-quality dictionary from a set of samples.
+ *
+ * Loading a dictionary has a cost, since it involves construction of tables.
+ * The Bulk processing dictionary API makes it possible to share this cost
+ * over an arbitrary number of compression jobs, even concurrently,
+ * markedly improving compression latency for these cases.
+ *
+ * The same dictionary will have to be used on the decompression side
+ * for decoding to be successful.
+ * To help identify the correct dictionary at decoding stage,
+ * the frame header allows optional embedding of a dictID field.
+ */
+typedef struct LZ4F_CDict_s LZ4F_CDict;
+
+/*! LZ4_createCDict() :
+ *  When compressing multiple messages / blocks using the same dictionary, it's recommended to load it just once.
+ *  LZ4_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.
+ *  LZ4_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
+ * `dictBuffer` can be released after LZ4_CDict creation, since its content is copied within CDict */
+LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize);
+LZ4FLIB_STATIC_API void        LZ4F_freeCDict(LZ4F_CDict* CDict);
+
+
+/*! LZ4_compressFrame_usingCDict() :
+ *  Compress an entire srcBuffer into a valid LZ4 frame using a digested Dictionary.
+ *  cctx must point to a context created by LZ4F_createCompressionContext().
+ *  If cdict==NULL, compress without a dictionary.
+ *  dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).
+ *  If this condition is not respected, function will fail (@return an errorCode).
+ *  The LZ4F_preferences_t structure is optional : you may provide NULL as argument,
+ *  but it's not recommended, as it's the only way to provide dictID in the frame header.
+ * @return : number of bytes written into dstBuffer.
+ *           or an error code if it fails (can be tested using LZ4F_isError()) */
+LZ4FLIB_STATIC_API size_t
+LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,
+                              void* dst, size_t dstCapacity,
+                        const void* src, size_t srcSize,
+                        const LZ4F_CDict* cdict,
+                        const LZ4F_preferences_t* preferencesPtr);
+
+
+/*! LZ4F_compressBegin_usingCDict() :
+ *  Inits streaming dictionary compression, and writes the frame header into dstBuffer.
+ *  dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.
+ * `prefsPtr` is optional : you may provide NULL as argument,
+ *  however, it's the only way to provide dictID in the frame header.
+ * @return : number of bytes written into dstBuffer for the header,
+ *           or an error code (which can be tested using LZ4F_isError()) */
+LZ4FLIB_STATIC_API size_t
+LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx,
+                              void* dstBuffer, size_t dstCapacity,
+                        const LZ4F_CDict* cdict,
+                        const LZ4F_preferences_t* prefsPtr);
+
+
+/*! LZ4F_decompress_usingDict() :
+ *  Same as LZ4F_decompress(), using a predefined dictionary.
+ *  Dictionary is used "in place", without any preprocessing.
+**  It must remain accessible throughout the entire frame decoding. */
+LZ4FLIB_STATIC_API size_t
+LZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr,
+                          void* dstBuffer, size_t* dstSizePtr,
+                    const void* srcBuffer, size_t* srcSizePtr,
+                    const void* dict, size_t dictSize,
+                    const LZ4F_decompressOptions_t* decompressOptionsPtr);
+
+
+/*! Custom memory allocation : v1.9.4+
+ *  These prototypes make it possible to pass custom allocation/free functions.
+ *  LZ4F_customMem is provided at state creation time, using LZ4F_create*_advanced() listed below.
+ *  All allocation/free operations will be completed using these custom variants instead of regular <stdlib.h> ones.
+ */
+typedef void* (*LZ4F_AllocFunction) (void* opaqueState, size_t size);
+typedef void* (*LZ4F_CallocFunction) (void* opaqueState, size_t size);
+typedef void  (*LZ4F_FreeFunction) (void* opaqueState, void* address);
+typedef struct {
+    LZ4F_AllocFunction customAlloc;
+    LZ4F_CallocFunction customCalloc; /* optional; when not defined, uses customAlloc + memset */
+    LZ4F_FreeFunction customFree;
+    void* opaqueState;
+} LZ4F_CustomMem;
+static
+#ifdef __GNUC__
+__attribute__((__unused__))
+#endif
+LZ4F_CustomMem const LZ4F_defaultCMem = { NULL, NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */
+
+LZ4FLIB_STATIC_API LZ4F_cctx* LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version);
+LZ4FLIB_STATIC_API LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version);
+LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict_advanced(LZ4F_CustomMem customMem, const void* dictBuffer, size_t dictSize);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif  /* defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) */
diff --git a/3rdparty/lz4/lz4frame_static.h b/3rdparty/lz4/lz4frame_static.h
new file mode 100644
index 00000000000..2b44a63155b
--- /dev/null
+++ b/3rdparty/lz4/lz4frame_static.h
@@ -0,0 +1,47 @@
+/*
+   LZ4 auto-framing library
+   Header File for static linking only
+   Copyright (C) 2011-2020, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - LZ4 source repository : https://github.com/lz4/lz4
+   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+
+#ifndef LZ4FRAME_STATIC_H_0398209384
+#define LZ4FRAME_STATIC_H_0398209384
+
+/* The declarations that formerly were made here have been merged into
+ * lz4frame.h, protected by the LZ4F_STATIC_LINKING_ONLY macro. Going forward,
+ * it is recommended to simply include that header directly.
+ */
+
+#define LZ4F_STATIC_LINKING_ONLY
+#include "lz4frame.h"
+
+#endif /* LZ4FRAME_STATIC_H_0398209384 */
diff --git a/3rdparty/lz4/lz4hc.c b/3rdparty/lz4/lz4hc.c
new file mode 100644
index 00000000000..651f190a093
--- /dev/null
+++ b/3rdparty/lz4/lz4hc.c
@@ -0,0 +1,1637 @@
+/*
+    LZ4 HC - High Compression Mode of LZ4
+    Copyright (C) 2011-2020, Yann Collet.
+
+    BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with the
+    distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    You can contact the author at :
+       - LZ4 source repository : https://github.com/lz4/lz4
+       - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */
+
+
+/* *************************************
+*  Tuning Parameter
+***************************************/
+
+/*! HEAPMODE :
+ *  Select how stateless HC compression functions like `LZ4_compress_HC()`
+ *  allocate memory for their workspace:
+ *  in stack (0:fastest), or in heap (1:default, requires malloc()).
+ *  Since workspace is rather large, heap mode is recommended.
+**/
+#ifndef LZ4HC_HEAPMODE
+#  define LZ4HC_HEAPMODE 1
+#endif
+
+
+/*===    Dependency    ===*/
+#define LZ4_HC_STATIC_LINKING_ONLY
+#include "lz4hc.h"
+
+
+/*===   Common definitions   ===*/
+#if defined(__GNUC__)
+#  pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#if defined (__clang__)
+#  pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+#define LZ4_COMMONDEFS_ONLY
+#ifndef LZ4_SRC_INCLUDED
+#include "lz4.c"   /* LZ4_count, constants, mem */
+#endif
+
+
+/*===   Enums   ===*/
+typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive;
+
+
+/*===   Constants   ===*/
+#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
+#define LZ4_OPT_NUM   (1<<12)
+
+
+/*===   Macros   ===*/
+#define MIN(a,b)   ( (a) < (b) ? (a) : (b) )
+#define MAX(a,b)   ( (a) > (b) ? (a) : (b) )
+#define HASH_FUNCTION(i)         (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG))
+#define DELTANEXTMAXD(p)         chainTable[(p) & LZ4HC_MAXD_MASK]    /* flexible, LZ4HC_MAXD dependent */
+#define DELTANEXTU16(table, pos) table[(U16)(pos)]   /* faster */
+/* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */
+#define UPDATABLE(ip, op, anchor) &ip, &op, &anchor
+
+#define LZ4HC_HASHSIZE 4
+static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); }
+
+
+/**************************************
+*  HC Compression
+**************************************/
+static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4)
+{
+    MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable));
+    MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));
+}
+
+static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start)
+{
+    size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart);
+    size_t newStartingOffset = bufferSize + hc4->dictLimit;
+    DEBUGLOG(5, "LZ4HC_init_internal");
+    assert(newStartingOffset >= bufferSize);  /* check overflow */
+    if (newStartingOffset > 1 GB) {
+        LZ4HC_clearTables(hc4);
+        newStartingOffset = 0;
+    }
+    newStartingOffset += 64 KB;
+    hc4->nextToUpdate = (U32)newStartingOffset;
+    hc4->prefixStart = start;
+    hc4->end = start;
+    hc4->dictStart = start;
+    hc4->dictLimit = (U32)newStartingOffset;
+    hc4->lowLimit = (U32)newStartingOffset;
+}
+
+
+/* Update chains up to ip (excluded) */
+LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip)
+{
+    U16* const chainTable = hc4->chainTable;
+    U32* const hashTable  = hc4->hashTable;
+    const BYTE* const prefixPtr = hc4->prefixStart;
+    U32 const prefixIdx = hc4->dictLimit;
+    U32 const target = (U32)(ip - prefixPtr) + prefixIdx;
+    U32 idx = hc4->nextToUpdate;
+    assert(ip >= prefixPtr);
+    assert(target >= prefixIdx);
+
+    while (idx < target) {
+        U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx);
+        size_t delta = idx - hashTable[h];
+        if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX;
+        DELTANEXTU16(chainTable, idx) = (U16)delta;
+        hashTable[h] = idx;
+        idx++;
+    }
+
+    hc4->nextToUpdate = target;
+}
+
+/** LZ4HC_countBack() :
+ * @return : negative value, nb of common bytes before ip/match */
+LZ4_FORCE_INLINE
+int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match,
+                    const BYTE* const iMin, const BYTE* const mMin)
+{
+    int back = 0;
+    int const min = (int)MAX(iMin - ip, mMin - match);
+    assert(min <= 0);
+    assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31));
+    assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31));
+    while ( (back > min)
+         && (ip[back-1] == match[back-1]) )
+            back--;
+    return back;
+}
+
+#if defined(_MSC_VER)
+#  define LZ4HC_rotl32(x,r) _rotl(x,r)
+#else
+#  define LZ4HC_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+#endif
+
+
+static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern)
+{
+    size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3;
+    if (bitsToRotate == 0) return pattern;
+    return LZ4HC_rotl32(pattern, (int)bitsToRotate);
+}
+
+/* LZ4HC_countPattern() :
+ * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */
+static unsigned
+LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32)
+{
+    const BYTE* const iStart = ip;
+    reg_t const pattern = (sizeof(pattern)==8) ?
+        (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32;
+
+    while (likely(ip < iEnd-(sizeof(pattern)-1))) {
+        reg_t const diff = LZ4_read_ARCH(ip) ^ pattern;
+        if (!diff) { ip+=sizeof(pattern); continue; }
+        ip += LZ4_NbCommonBytes(diff);
+        return (unsigned)(ip - iStart);
+    }
+
+    if (LZ4_isLittleEndian()) {
+        reg_t patternByte = pattern;
+        while ((ip<iEnd) && (*ip == (BYTE)patternByte)) {
+            ip++; patternByte >>= 8;
+        }
+    } else {  /* big endian */
+        U32 bitOffset = (sizeof(pattern)*8) - 8;
+        while (ip < iEnd) {
+            BYTE const byte = (BYTE)(pattern >> bitOffset);
+            if (*ip != byte) break;
+            ip ++; bitOffset -= 8;
+    }   }
+
+    return (unsigned)(ip - iStart);
+}
+
+/* LZ4HC_reverseCountPattern() :
+ * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!)
+ * read using natural platform endianness */
+static unsigned
+LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern)
+{
+    const BYTE* const iStart = ip;
+
+    while (likely(ip >= iLow+4)) {
+        if (LZ4_read32(ip-4) != pattern) break;
+        ip -= 4;
+    }
+    {   const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */
+        while (likely(ip>iLow)) {
+            if (ip[-1] != *bytePtr) break;
+            ip--; bytePtr--;
+    }   }
+    return (unsigned)(iStart - ip);
+}
+
+/* LZ4HC_protectDictEnd() :
+ * Checks if the match is in the last 3 bytes of the dictionary, so reading the
+ * 4 byte MINMATCH would overflow.
+ * @returns true if the match index is okay.
+ */
+static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex)
+{
+    return ((U32)((dictLimit - 1) - matchIndex) >= 3);
+}
+
+typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e;
+typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e;
+
+typedef struct {
+    int off;
+    int len;
+} LZ4HC_match_t;
+
+LZ4_FORCE_INLINE LZ4HC_match_t
+LZ4HC_InsertAndGetWiderMatch (
+        LZ4HC_CCtx_internal* const hc4,
+        const BYTE* const ip,
+        const BYTE* const iLowLimit, const BYTE* const iHighLimit,
+        int longest,
+        const BYTE** startpos,
+        const int maxNbAttempts,
+        const int patternAnalysis, const int chainSwap,
+        const dictCtx_directive dict,
+        const HCfavor_e favorDecSpeed)
+{
+    U16* const chainTable = hc4->chainTable;
+    U32* const hashTable = hc4->hashTable;
+    const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx;
+    const BYTE* const prefixPtr = hc4->prefixStart;
+    const U32 prefixIdx = hc4->dictLimit;
+    const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx;
+    const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex);
+    const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX;
+    const BYTE* const dictStart = hc4->dictStart;
+    const U32 dictIdx = hc4->lowLimit;
+    const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx;
+    int const lookBackLength = (int)(ip-iLowLimit);
+    int nbAttempts = maxNbAttempts;
+    U32 matchChainPos = 0;
+    U32 const pattern = LZ4_read32(ip);
+    U32 matchIndex;
+    repeat_state_e repeat = rep_untested;
+    size_t srcPatternLength = 0;
+    int offset = 0;
+
+    DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch");
+    assert(startpos != NULL);
+    *startpos = ip;  /* in case there is no solution */
+    /* First Match */
+    LZ4HC_Insert(hc4, ip);  /* insert all prior positions up to ip (excluded) */
+    matchIndex = hashTable[LZ4HC_hashPtr(ip)];
+    DEBUGLOG(7, "First candidate match for pos %u found at index %u / %u (lowestMatchIndex)",
+                ipIndex, matchIndex, lowestMatchIndex);
+
+    while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0)) {
+        int matchLength=0;
+        nbAttempts--;
+        assert(matchIndex < ipIndex);
+        if (favorDecSpeed && (ipIndex - matchIndex < 8)) {
+            /* do nothing:
+             * favorDecSpeed intentionally skips matches with offset < 8 */
+        } else if (matchIndex >= prefixIdx) {   /* within current Prefix */
+            const BYTE* const matchPtr = prefixPtr + (matchIndex - prefixIdx);
+            assert(matchPtr < ip);
+            assert(longest >= 1);
+            if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) {
+                if (LZ4_read32(matchPtr) == pattern) {
+                    int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0;
+                    matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit);
+                    matchLength -= back;
+                    if (matchLength > longest) {
+                        longest = matchLength;
+                        offset = (int)(ipIndex - matchIndex);
+                        *startpos = ip + back;
+                        DEBUGLOG(7, "Found match of len=%i within prefix, offset=%i, back=%i", longest, offset, -back);
+            }   }   }
+        } else {   /* lowestMatchIndex <= matchIndex < dictLimit : within Ext Dict */
+            const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx);
+            assert(matchIndex >= dictIdx);
+            if ( likely(matchIndex <= prefixIdx - 4)
+              && (LZ4_read32(matchPtr) == pattern) ) {
+                int back = 0;
+                const BYTE* vLimit = ip + (prefixIdx - matchIndex);
+                if (vLimit > iHighLimit) vLimit = iHighLimit;
+                matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;
+                if ((ip+matchLength == vLimit) && (vLimit < iHighLimit))
+                    matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit);
+                back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0;
+                matchLength -= back;
+                if (matchLength > longest) {
+                    longest = matchLength;
+                    offset = (int)(ipIndex - matchIndex);
+                    *startpos = ip + back;
+                    DEBUGLOG(7, "Found match of len=%i within dict, offset=%i, back=%i", longest, offset, -back);
+        }   }   }
+
+        if (chainSwap && matchLength==longest) {   /* better match => select a better chain */
+            assert(lookBackLength==0);   /* search forward only */
+            if (matchIndex + (U32)longest <= ipIndex) {
+                int const kTrigger = 4;
+                U32 distanceToNextMatch = 1;
+                int const end = longest - MINMATCH + 1;
+                int step = 1;
+                int accel = 1 << kTrigger;
+                int pos;
+                for (pos = 0; pos < end; pos += step) {
+                    U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos);
+                    step = (accel++ >> kTrigger);
+                    if (candidateDist > distanceToNextMatch) {
+                        distanceToNextMatch = candidateDist;
+                        matchChainPos = (U32)pos;
+                        accel = 1 << kTrigger;
+                }   }
+                if (distanceToNextMatch > 1) {
+                    if (distanceToNextMatch > matchIndex) break;   /* avoid overflow */
+                    matchIndex -= distanceToNextMatch;
+                    continue;
+        }   }   }
+
+        {   U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex);
+            if (patternAnalysis && distNextMatch==1 && matchChainPos==0) {
+                U32 const matchCandidateIdx = matchIndex-1;
+                /* may be a repeated pattern */
+                if (repeat == rep_untested) {
+                    if ( ((pattern & 0xFFFF) == (pattern >> 16))
+                      &  ((pattern & 0xFF)   == (pattern >> 24)) ) {
+                        DEBUGLOG(7, "Repeat pattern detected, char %02X", pattern >> 24);
+                        repeat = rep_confirmed;
+                        srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern);
+                    } else {
+                        repeat = rep_not;
+                }   }
+                if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex)
+                  && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) {
+                    const int extDict = matchCandidateIdx < prefixIdx;
+                    const BYTE* const matchPtr = extDict ? dictStart + (matchCandidateIdx - dictIdx) : prefixPtr + (matchCandidateIdx - prefixIdx);
+                    if (LZ4_read32(matchPtr) == pattern) {  /* good candidate */
+                        const BYTE* const iLimit = extDict ? dictEnd : iHighLimit;
+                        size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern);
+                        if (extDict && matchPtr + forwardPatternLength == iLimit) {
+                            U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern);
+                            forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern);
+                        }
+                        {   const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr;
+                            size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern);
+                            size_t currentSegmentLength;
+                            if (!extDict
+                              && matchPtr - backLength == prefixPtr
+                              && dictIdx < prefixIdx) {
+                                U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern);
+                                backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern);
+                            }
+                            /* Limit backLength not go further than lowestMatchIndex */
+                            backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex);
+                            assert(matchCandidateIdx - backLength >= lowestMatchIndex);
+                            currentSegmentLength = backLength + forwardPatternLength;
+                            /* Adjust to end of pattern if the source pattern fits, otherwise the beginning of the pattern */
+                            if ( (currentSegmentLength >= srcPatternLength)   /* current pattern segment large enough to contain full srcPatternLength */
+                              && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */
+                                U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength;  /* best position, full pattern, might be followed by more match */
+                                if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex))
+                                    matchIndex = newMatchIndex;
+                                else {
+                                    /* Can only happen if started in the prefix */
+                                    assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict);
+                                    matchIndex = prefixIdx;
+                                }
+                            } else {
+                                U32 const newMatchIndex = matchCandidateIdx - (U32)backLength;   /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */
+                                if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) {
+                                    assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict);
+                                    matchIndex = prefixIdx;
+                                } else {
+                                    matchIndex = newMatchIndex;
+                                    if (lookBackLength==0) {  /* no back possible */
+                                        size_t const maxML = MIN(currentSegmentLength, srcPatternLength);
+                                        if ((size_t)longest < maxML) {
+                                            assert(prefixPtr - prefixIdx + matchIndex != ip);
+                                            if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break;
+                                            assert(maxML < 2 GB);
+                                            longest = (int)maxML;
+                                            offset = (int)(ipIndex - matchIndex);
+                                            *startpos = ip;
+                                            DEBUGLOG(7, "Found repeat pattern match of len=%i, offset=%i", longest, offset);
+                                        }
+                                        {   U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex);
+                                            if (distToNextPattern > matchIndex) break;  /* avoid overflow */
+                                            matchIndex -= distToNextPattern;
+                        }   }   }   }   }
+                        continue;
+                }   }
+        }   }   /* PA optimization */
+
+        /* follow current chain */
+        matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos);
+
+    }  /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */
+
+    if ( dict == usingDictCtxHc
+      && nbAttempts > 0
+      && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) {
+        size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit;
+        U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)];
+        assert(dictEndOffset <= 1 GB);
+        matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset;
+        if (dictMatchIndex>0) DEBUGLOG(7, "dictEndOffset = %zu, dictMatchIndex = %u => relative matchIndex = %i", dictEndOffset, dictMatchIndex, (int)dictMatchIndex - (int)dictEndOffset);
+        while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) {
+            const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex;
+
+            if (LZ4_read32(matchPtr) == pattern) {
+                int mlt;
+                int back = 0;
+                const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex);
+                if (vLimit > iHighLimit) vLimit = iHighLimit;
+                mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;
+                back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0;
+                mlt -= back;
+                if (mlt > longest) {
+                    longest = mlt;
+                    offset = (int)(ipIndex - matchIndex);
+                    *startpos = ip + back;
+                    DEBUGLOG(7, "found match of length %i within extDictCtx", longest);
+            }   }
+
+            {   U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex);
+                dictMatchIndex -= nextOffset;
+                matchIndex -= nextOffset;
+    }   }   }
+
+    {   LZ4HC_match_t md;
+        assert(longest >= 0);
+        md.len = longest;
+        md.off = offset;
+        return md;
+    }
+}
+
+LZ4_FORCE_INLINE LZ4HC_match_t
+LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4,   /* Index table will be updated */
+                       const BYTE* const ip, const BYTE* const iLimit,
+                       const int maxNbAttempts,
+                       const int patternAnalysis,
+                       const dictCtx_directive dict)
+{
+    const BYTE* uselessPtr = ip;
+    DEBUGLOG(7, "LZ4HC_InsertAndFindBestMatch");
+    /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos),
+     * but this won't be the case here, as we define iLowLimit==ip,
+     * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */
+    return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, &uselessPtr, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio);
+}
+
+/* LZ4HC_encodeSequence() :
+ * @return : 0 if ok,
+ *           1 if buffer issue detected */
+LZ4_FORCE_INLINE int LZ4HC_encodeSequence (
+    const BYTE** _ip,
+    BYTE** _op,
+    const BYTE** _anchor,
+    int matchLength,
+    int offset,
+    limitedOutput_directive limit,
+    BYTE* oend)
+{
+#define ip      (*_ip)
+#define op      (*_op)
+#define anchor  (*_anchor)
+
+    size_t length;
+    BYTE* const token = op++;
+
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6)
+    static const BYTE* start = NULL;
+    static U32 totalCost = 0;
+    U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start);
+    U32 const ll = (U32)(ip - anchor);
+    U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0;
+    U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0;
+    U32 const cost = 1 + llAdd + ll + 2 + mlAdd;
+    if (start==NULL) start = anchor;  /* only works for single segment */
+    /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */
+    DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5i, cost:%4u + %5u",
+                pos,
+                (U32)(ip - anchor), matchLength, offset,
+                cost, totalCost);
+    totalCost += cost;
+#endif
+
+    /* Encode Literal length */
+    length = (size_t)(ip - anchor);
+    LZ4_STATIC_ASSERT(notLimited == 0);
+    /* Check output limit */
+    if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) {
+        DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)",
+                (int)length, (int)(oend - op));
+        return 1;
+    }
+    if (length >= RUN_MASK) {
+        size_t len = length - RUN_MASK;
+        *token = (RUN_MASK << ML_BITS);
+        for(; len >= 255 ; len -= 255) *op++ = 255;
+        *op++ = (BYTE)len;
+    } else {
+        *token = (BYTE)(length << ML_BITS);
+    }
+
+    /* Copy Literals */
+    LZ4_wildCopy8(op, anchor, op + length);
+    op += length;
+
+    /* Encode Offset */
+    assert(offset <= LZ4_DISTANCE_MAX );
+    assert(offset > 0);
+    LZ4_writeLE16(op, (U16)(offset)); op += 2;
+
+    /* Encode MatchLength */
+    assert(matchLength >= MINMATCH);
+    length = (size_t)matchLength - MINMATCH;
+    if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) {
+        DEBUGLOG(6, "Not enough room to write match length");
+        return 1;   /* Check output limit */
+    }
+    if (length >= ML_MASK) {
+        *token += ML_MASK;
+        length -= ML_MASK;
+        for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; }
+        if (length >= 255) { length -= 255; *op++ = 255; }
+        *op++ = (BYTE)length;
+    } else {
+        *token += (BYTE)(length);
+    }
+
+    /* Prepare next loop */
+    ip += matchLength;
+    anchor = ip;
+
+    return 0;
+}
+#undef ip
+#undef op
+#undef anchor
+
+LZ4_FORCE_INLINE int LZ4HC_compress_hashChain (
+    LZ4HC_CCtx_internal* const ctx,
+    const char* const source,
+    char* const dest,
+    int* srcSizePtr,
+    int const maxOutputSize,
+    int maxNbAttempts,
+    const limitedOutput_directive limit,
+    const dictCtx_directive dict
+    )
+{
+    const int inputSize = *srcSizePtr;
+    const int patternAnalysis = (maxNbAttempts > 128);   /* levels 9+ */
+
+    const BYTE* ip = (const BYTE*) source;
+    const BYTE* anchor = ip;
+    const BYTE* const iend = ip + inputSize;
+    const BYTE* const mflimit = iend - MFLIMIT;
+    const BYTE* const matchlimit = (iend - LASTLITERALS);
+
+    BYTE* optr = (BYTE*) dest;
+    BYTE* op = (BYTE*) dest;
+    BYTE* oend = op + maxOutputSize;
+
+    const BYTE* start0;
+    const BYTE* start2 = NULL;
+    const BYTE* start3 = NULL;
+    LZ4HC_match_t m0, m1, m2, m3;
+    const LZ4HC_match_t nomatch = {0, 0};
+
+    /* init */
+    DEBUGLOG(5, "LZ4HC_compress_hashChain (dict?=>%i)", dict);
+    *srcSizePtr = 0;
+    if (limit == fillOutput) oend -= LASTLITERALS;                  /* Hack for support LZ4 format restriction */
+    if (inputSize < LZ4_minLength) goto _last_literals;             /* Input too small, no compression (all literals) */
+
+    /* Main Loop */
+    while (ip <= mflimit) {
+        m1 = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, maxNbAttempts, patternAnalysis, dict);
+        if (m1.len<MINMATCH) { ip++; continue; }
+
+        /* saved, in case we would skip too much */
+        start0 = ip; m0 = m1;
+
+_Search2:
+        DEBUGLOG(7, "_Search2 (currently found match of size %i)", m1.len);
+        if (ip+m1.len <= mflimit) {
+            m2 = LZ4HC_InsertAndGetWiderMatch(ctx,
+                            ip + m1.len - 2, ip + 0, matchlimit, m1.len, &start2,
+                            maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio);
+        } else {
+            m2 = nomatch;  /* do not search further */
+        }
+
+        if (m2.len <= m1.len) { /* No better match => encode ML1 immediately */
+            optr = op;
+            if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) goto _dest_overflow;
+            continue;
+        }
+
+        if (start0 < ip) {   /* first match was skipped at least once */
+            if (start2 < ip + m0.len) {  /* squeezing ML1 between ML0(original ML1) and ML2 */
+                ip = start0; m1 = m0;  /* restore initial Match1 */
+        }   }
+
+        /* Here, start0==ip */
+        if ((start2 - ip) < 3) {  /* First Match too small : removed */
+            ip = start2;
+            m1 = m2;
+            goto _Search2;
+        }
+
+_Search3:
+        if ((start2 - ip) < OPTIMAL_ML) {
+            int correction;
+            int new_ml = m1.len;
+            if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML;
+            if (ip+new_ml > start2 + m2.len - MINMATCH)
+                new_ml = (int)(start2 - ip) + m2.len - MINMATCH;
+            correction = new_ml - (int)(start2 - ip);
+            if (correction > 0) {
+                start2 += correction;
+                m2.len -= correction;
+            }
+        }
+
+        if (start2 + m2.len <= mflimit) {
+            m3 = LZ4HC_InsertAndGetWiderMatch(ctx,
+                            start2 + m2.len - 3, start2, matchlimit, m2.len, &start3,
+                            maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio);
+        } else {
+            m3 = nomatch;  /* do not search further */
+        }
+
+        if (m3.len <= m2.len) {  /* No better match => encode ML1 and ML2 */
+            /* ip & ref are known; Now for ml */
+            if (start2 < ip+m1.len) m1.len = (int)(start2 - ip);
+            /* Now, encode 2 sequences */
+            optr = op;
+            if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend))
+                goto _dest_overflow;
+            ip = start2;
+            optr = op;
+            if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m2.len, m2.off, limit, oend)) {
+                m1 = m2;
+                goto _dest_overflow;
+            }
+            continue;
+        }
+
+        if (start3 < ip+m1.len+3) {  /* Not enough space for match 2 : remove it */
+            if (start3 >= (ip+m1.len)) {  /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */
+                if (start2 < ip+m1.len) {
+                    int correction = (int)(ip+m1.len - start2);
+                    start2 += correction;
+                    m2.len -= correction;
+                    if (m2.len < MINMATCH) {
+                        start2 = start3;
+                        m2 = m3;
+                    }
+                }
+
+                optr = op;
+                if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) goto _dest_overflow;
+                ip  = start3;
+                m1 = m3;
+
+                start0 = start2;
+                m0 = m2;
+                goto _Search2;
+            }
+
+            start2 = start3;
+            m2 = m3;
+            goto _Search3;
+        }
+
+        /*
+        * OK, now we have 3 ascending matches;
+        * let's write the first one ML1.
+        * ip & ref are known; Now decide ml.
+        */
+        if (start2 < ip+m1.len) {
+            if ((start2 - ip) < OPTIMAL_ML) {
+                int correction;
+                if (m1.len > OPTIMAL_ML) m1.len = OPTIMAL_ML;
+                if (ip + m1.len > start2 + m2.len - MINMATCH)
+                    m1.len = (int)(start2 - ip) + m2.len - MINMATCH;
+                correction = m1.len - (int)(start2 - ip);
+                if (correction > 0) {
+                    start2 += correction;
+                    m2.len -= correction;
+                }
+            } else {
+                m1.len = (int)(start2 - ip);
+            }
+        }
+        optr = op;
+        if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) goto _dest_overflow;
+
+        /* ML2 becomes ML1 */
+        ip = start2; m1 = m2;
+
+        /* ML3 becomes ML2 */
+        start2 = start3; m2 = m3;
+
+        /* let's find a new ML3 */
+        goto _Search3;
+    }
+
+_last_literals:
+    /* Encode Last Literals */
+    {   size_t lastRunSize = (size_t)(iend - anchor);  /* literals */
+        size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255;
+        size_t const totalSize = 1 + llAdd + lastRunSize;
+        if (limit == fillOutput) oend += LASTLITERALS;  /* restore correct value */
+        if (limit && (op + totalSize > oend)) {
+            if (limit == limitedOutput) return 0;
+            /* adapt lastRunSize to fill 'dest' */
+            lastRunSize  = (size_t)(oend - op) - 1 /*token*/;
+            llAdd = (lastRunSize + 256 - RUN_MASK) / 256;
+            lastRunSize -= llAdd;
+        }
+        DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize);
+        ip = anchor + lastRunSize;  /* can be != iend if limit==fillOutput */
+
+        if (lastRunSize >= RUN_MASK) {
+            size_t accumulator = lastRunSize - RUN_MASK;
+            *op++ = (RUN_MASK << ML_BITS);
+            for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255;
+            *op++ = (BYTE) accumulator;
+        } else {
+            *op++ = (BYTE)(lastRunSize << ML_BITS);
+        }
+        LZ4_memcpy(op, anchor, lastRunSize);
+        op += lastRunSize;
+    }
+
+    /* End */
+    *srcSizePtr = (int) (((const char*)ip) - source);
+    return (int) (((char*)op)-dest);
+
+_dest_overflow:
+    if (limit == fillOutput) {
+        /* Assumption : ip, anchor, ml and ref must be set correctly */
+        size_t const ll = (size_t)(ip - anchor);
+        size_t const ll_addbytes = (ll + 240) / 255;
+        size_t const ll_totalCost = 1 + ll_addbytes + ll;
+        BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */
+        DEBUGLOG(6, "Last sequence overflowing");
+        op = optr;  /* restore correct out pointer */
+        if (op + ll_totalCost <= maxLitPos) {
+            /* ll validated; now adjust match length */
+            size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost));
+            size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255);
+            assert(maxMlSize < INT_MAX); assert(m1.len >= 0);
+            if ((size_t)m1.len > maxMlSize) m1.len = (int)maxMlSize;
+            if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + m1.len >= MFLIMIT) {
+                LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, notLimited, oend);
+        }   }
+        goto _last_literals;
+    }
+    /* compression failed */
+    return 0;
+}
+
+
+static int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx,
+    const char* const source, char* dst,
+    int* srcSizePtr, int dstCapacity,
+    int const nbSearches, size_t sufficient_len,
+    const limitedOutput_directive limit, int const fullUpdate,
+    const dictCtx_directive dict,
+    const HCfavor_e favorDecSpeed);
+
+
+LZ4_FORCE_INLINE int
+LZ4HC_compress_generic_internal (
+            LZ4HC_CCtx_internal* const ctx,
+            const char* const src,
+            char* const dst,
+            int* const srcSizePtr,
+            int const dstCapacity,
+            int cLevel,
+            const limitedOutput_directive limit,
+            const dictCtx_directive dict
+            )
+{
+    typedef enum { lz4hc, lz4opt } lz4hc_strat_e;
+    typedef struct {
+        lz4hc_strat_e strat;
+        int nbSearches;
+        U32 targetLength;
+    } cParams_t;
+    static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = {
+        { lz4hc,     2, 16 },  /* 0, unused */
+        { lz4hc,     2, 16 },  /* 1, unused */
+        { lz4hc,     2, 16 },  /* 2, unused */
+        { lz4hc,     4, 16 },  /* 3 */
+        { lz4hc,     8, 16 },  /* 4 */
+        { lz4hc,    16, 16 },  /* 5 */
+        { lz4hc,    32, 16 },  /* 6 */
+        { lz4hc,    64, 16 },  /* 7 */
+        { lz4hc,   128, 16 },  /* 8 */
+        { lz4hc,   256, 16 },  /* 9 */
+        { lz4opt,   96, 64 },  /*10==LZ4HC_CLEVEL_OPT_MIN*/
+        { lz4opt,  512,128 },  /*11 */
+        { lz4opt,16384,LZ4_OPT_NUM },  /* 12==LZ4HC_CLEVEL_MAX */
+    };
+
+    DEBUGLOG(5, "LZ4HC_compress_generic_internal(src=%p, srcSize=%d)",
+                src, *srcSizePtr);
+
+    if (limit == fillOutput && dstCapacity < 1) return 0;   /* Impossible to store anything */
+    if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0;    /* Unsupported input size (too large or negative) */
+
+    ctx->end += *srcSizePtr;
+    if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT;   /* note : convention is different from lz4frame, maybe something to review */
+    cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel);
+    {   cParams_t const cParam = clTable[cLevel];
+        HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio;
+        int result;
+
+        if (cParam.strat == lz4hc) {
+            result = LZ4HC_compress_hashChain(ctx,
+                                src, dst, srcSizePtr, dstCapacity,
+                                cParam.nbSearches, limit, dict);
+        } else {
+            assert(cParam.strat == lz4opt);
+            result = LZ4HC_compress_optimal(ctx,
+                                src, dst, srcSizePtr, dstCapacity,
+                                cParam.nbSearches, cParam.targetLength, limit,
+                                cLevel == LZ4HC_CLEVEL_MAX,   /* ultra mode */
+                                dict, favor);
+        }
+        if (result <= 0) ctx->dirty = 1;
+        return result;
+    }
+}
+
+static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock);
+
+static int
+LZ4HC_compress_generic_noDictCtx (
+        LZ4HC_CCtx_internal* const ctx,
+        const char* const src,
+        char* const dst,
+        int* const srcSizePtr,
+        int const dstCapacity,
+        int cLevel,
+        limitedOutput_directive limit
+        )
+{
+    assert(ctx->dictCtx == NULL);
+    return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx);
+}
+
+static int
+LZ4HC_compress_generic_dictCtx (
+        LZ4HC_CCtx_internal* const ctx,
+        const char* const src,
+        char* const dst,
+        int* const srcSizePtr,
+        int const dstCapacity,
+        int cLevel,
+        limitedOutput_directive limit
+        )
+{
+    const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit);
+    assert(ctx->dictCtx != NULL);
+    if (position >= 64 KB) {
+        ctx->dictCtx = NULL;
+        return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);
+    } else if (position == 0 && *srcSizePtr > 4 KB) {
+        LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal));
+        LZ4HC_setExternalDict(ctx, (const BYTE *)src);
+        ctx->compressionLevel = (short)cLevel;
+        return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);
+    } else {
+        return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc);
+    }
+}
+
+static int
+LZ4HC_compress_generic (
+        LZ4HC_CCtx_internal* const ctx,
+        const char* const src,
+        char* const dst,
+        int* const srcSizePtr,
+        int const dstCapacity,
+        int cLevel,
+        limitedOutput_directive limit
+        )
+{
+    if (ctx->dictCtx == NULL) {
+        return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);
+    } else {
+        return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);
+    }
+}
+
+
+int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); }
+
+static size_t LZ4_streamHC_t_alignment(void)
+{
+#if LZ4_ALIGN_TEST
+    typedef struct { char c; LZ4_streamHC_t t; } t_a;
+    return sizeof(t_a) - sizeof(LZ4_streamHC_t);
+#else
+    return 1;  /* effectively disabled */
+#endif
+}
+
+/* state is presumed correctly initialized,
+ * in which case its size and alignment have already been validate */
+int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)
+{
+    LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;
+    if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0;
+    LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel);
+    LZ4HC_init_internal (ctx, (const BYTE*)src);
+    if (dstCapacity < LZ4_compressBound(srcSize))
+        return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput);
+    else
+        return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited);
+}
+
+int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)
+{
+    LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx));
+    if (ctx==NULL) return 0;   /* init failure */
+    return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel);
+}
+
+int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)
+{
+    int cSize;
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+    LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t));
+    if (statePtr==NULL) return 0;
+#else
+    LZ4_streamHC_t state;
+    LZ4_streamHC_t* const statePtr = &state;
+#endif
+    DEBUGLOG(5, "LZ4_compress_HC")
+    cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel);
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+    FREEMEM(statePtr);
+#endif
+    return cSize;
+}
+
+/* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */
+int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel)
+{
+    LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx));
+    if (ctx==NULL) return 0;   /* init failure */
+    LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source);
+    LZ4_setCompressionLevel(ctx, cLevel);
+    return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput);
+}
+
+
+
+/**************************************
+*  Streaming Functions
+**************************************/
+/* allocation */
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+LZ4_streamHC_t* LZ4_createStreamHC(void)
+{
+    LZ4_streamHC_t* const state =
+        (LZ4_streamHC_t*)ALLOC_AND_ZERO(sizeof(LZ4_streamHC_t));
+    if (state == NULL) return NULL;
+    LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT);
+    return state;
+}
+
+int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr)
+{
+    DEBUGLOG(4, "LZ4_freeStreamHC(%p)", LZ4_streamHCPtr);
+    if (!LZ4_streamHCPtr) return 0;  /* support free on NULL */
+    FREEMEM(LZ4_streamHCPtr);
+    return 0;
+}
+#endif
+
+
+LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size)
+{
+    LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer;
+    DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size);
+    /* check conditions */
+    if (buffer == NULL) return NULL;
+    if (size < sizeof(LZ4_streamHC_t)) return NULL;
+    if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL;
+    /* init */
+    { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse);
+      MEM_INIT(hcstate, 0, sizeof(*hcstate)); }
+    LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT);
+    return LZ4_streamHCPtr;
+}
+
+/* just a stub */
+void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)
+{
+    LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));
+    LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel);
+}
+
+void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)
+{
+    LZ4HC_CCtx_internal* const s = &LZ4_streamHCPtr->internal_donotuse;
+    DEBUGLOG(5, "LZ4_resetStreamHC_fast(%p, %d)", LZ4_streamHCPtr, compressionLevel);
+    if (s->dirty) {
+        LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));
+    } else {
+        assert(s->end >= s->prefixStart);
+        s->dictLimit += (U32)(s->end - s->prefixStart);
+        s->prefixStart = NULL;
+        s->end = NULL;
+        s->dictCtx = NULL;
+    }
+    LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel);
+}
+
+void LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)
+{
+    DEBUGLOG(5, "LZ4_setCompressionLevel(%p, %d)", LZ4_streamHCPtr, compressionLevel);
+    if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT;
+    if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX;
+    LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel;
+}
+
+void LZ4_favorDecompressionSpeed(LZ4_streamHC_t* LZ4_streamHCPtr, int favor)
+{
+    LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = (favor!=0);
+}
+
+/* LZ4_loadDictHC() :
+ * LZ4_streamHCPtr is presumed properly initialized */
+int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr,
+              const char* dictionary, int dictSize)
+{
+    LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+    DEBUGLOG(4, "LZ4_loadDictHC(ctx:%p, dict:%p, dictSize:%d)", LZ4_streamHCPtr, dictionary, dictSize);
+    assert(LZ4_streamHCPtr != NULL);
+    if (dictSize > 64 KB) {
+        dictionary += (size_t)dictSize - 64 KB;
+        dictSize = 64 KB;
+    }
+    /* need a full initialization, there are bad side-effects when using resetFast() */
+    {   int const cLevel = ctxPtr->compressionLevel;
+        LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));
+        LZ4_setCompressionLevel(LZ4_streamHCPtr, cLevel);
+    }
+    LZ4HC_init_internal (ctxPtr, (const BYTE*)dictionary);
+    ctxPtr->end = (const BYTE*)dictionary + dictSize;
+    if (dictSize >= LZ4HC_HASHSIZE) LZ4HC_Insert (ctxPtr, ctxPtr->end-3);
+    return dictSize;
+}
+
+void LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream) {
+    working_stream->internal_donotuse.dictCtx = dictionary_stream != NULL ? &(dictionary_stream->internal_donotuse) : NULL;
+}
+
+/* compression */
+
+static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock)
+{
+    DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock);
+    if (ctxPtr->end >= ctxPtr->prefixStart + 4)
+        LZ4HC_Insert (ctxPtr, ctxPtr->end-3);   /* Referencing remaining dictionary content */
+
+    /* Only one memory segment for extDict, so any previous extDict is lost at this stage */
+    ctxPtr->lowLimit  = ctxPtr->dictLimit;
+    ctxPtr->dictStart  = ctxPtr->prefixStart;
+    ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart);
+    ctxPtr->prefixStart = newBlock;
+    ctxPtr->end  = newBlock;
+    ctxPtr->nextToUpdate = ctxPtr->dictLimit;   /* match referencing will resume from there */
+
+    /* cannot reference an extDict and a dictCtx at the same time */
+    ctxPtr->dictCtx = NULL;
+}
+
+static int
+LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr,
+                                 const char* src, char* dst,
+                                 int* srcSizePtr, int dstCapacity,
+                                 limitedOutput_directive limit)
+{
+    LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+    DEBUGLOG(5, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)",
+                LZ4_streamHCPtr, src, *srcSizePtr, limit);
+    assert(ctxPtr != NULL);
+    /* auto-init if forgotten */
+    if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src);
+
+    /* Check overflow */
+    if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) {
+        size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart);
+        if (dictSize > 64 KB) dictSize = 64 KB;
+        LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize);
+    }
+
+    /* Check if blocks follow each other */
+    if ((const BYTE*)src != ctxPtr->end)
+        LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src);
+
+    /* Check overlapping input/dictionary space */
+    {   const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr;
+        const BYTE* const dictBegin = ctxPtr->dictStart;
+        const BYTE* const dictEnd   = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit);
+        if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) {
+            if (sourceEnd > dictEnd) sourceEnd = dictEnd;
+            ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart);
+            ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart);
+            /* invalidate dictionary is it's too small */
+            if (ctxPtr->dictLimit - ctxPtr->lowLimit < LZ4HC_HASHSIZE) {
+                ctxPtr->lowLimit = ctxPtr->dictLimit;
+                ctxPtr->dictStart = ctxPtr->prefixStart;
+    }   }   }
+
+    return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit);
+}
+
+int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity)
+{
+    DEBUGLOG(5, "LZ4_compress_HC_continue");
+    if (dstCapacity < LZ4_compressBound(srcSize))
+        return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput);
+    else
+        return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, notLimited);
+}
+
+int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize)
+{
+    return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput);
+}
+
+
+
+/* LZ4_saveDictHC :
+ * save history content
+ * into a user-provided buffer
+ * which is then used to continue compression
+ */
+int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize)
+{
+    LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse;
+    int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart);
+    DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize);
+    assert(prefixSize >= 0);
+    if (dictSize > 64 KB) dictSize = 64 KB;
+    if (dictSize < 4) dictSize = 0;
+    if (dictSize > prefixSize) dictSize = prefixSize;
+    if (safeBuffer == NULL) assert(dictSize == 0);
+    if (dictSize > 0)
+        LZ4_memmove(safeBuffer, streamPtr->end - dictSize, (size_t)dictSize);
+    {   U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit;
+        streamPtr->end = (safeBuffer == NULL) ? NULL : (const BYTE*)safeBuffer + dictSize;
+        streamPtr->prefixStart = (const BYTE*)safeBuffer;
+        streamPtr->dictLimit = endIndex - (U32)dictSize;
+        streamPtr->lowLimit = endIndex - (U32)dictSize;
+        streamPtr->dictStart = streamPtr->prefixStart;
+        if (streamPtr->nextToUpdate < streamPtr->dictLimit)
+            streamPtr->nextToUpdate = streamPtr->dictLimit;
+    }
+    return dictSize;
+}
+
+
+/***************************************************
+*  Deprecated Functions
+***************************************************/
+
+/* These functions currently generate deprecation warnings */
+
+/* Wrappers for deprecated compression functions */
+int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
+int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); }
+int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
+int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); }
+int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
+int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); }
+int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
+int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); }
+int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); }
+int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); }
+
+
+/* Deprecated streaming functions */
+int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); }
+
+/* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t)
+ * @return : 0 on success, !=0 if error */
+int LZ4_resetStreamStateHC(void* state, char* inputBuffer)
+{
+    LZ4_streamHC_t* const hc4 = LZ4_initStreamHC(state, sizeof(*hc4));
+    if (hc4 == NULL) return 1;   /* init failed */
+    LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer);
+    return 0;
+}
+
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+void* LZ4_createHC (const char* inputBuffer)
+{
+    LZ4_streamHC_t* const hc4 = LZ4_createStreamHC();
+    if (hc4 == NULL) return NULL;   /* not enough memory */
+    LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer);
+    return hc4;
+}
+
+int LZ4_freeHC (void* LZ4HC_Data)
+{
+    if (!LZ4HC_Data) return 0;  /* support free on NULL */
+    FREEMEM(LZ4HC_Data);
+    return 0;
+}
+#endif
+
+int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel)
+{
+    return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, notLimited);
+}
+
+int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel)
+{
+    return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput);
+}
+
+char* LZ4_slideInputBufferHC(void* LZ4HC_Data)
+{
+    LZ4HC_CCtx_internal* const s = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse;
+    const BYTE* const bufferStart = s->prefixStart - s->dictLimit + s->lowLimit;
+    LZ4_resetStreamHC_fast((LZ4_streamHC_t*)LZ4HC_Data, s->compressionLevel);
+    /* ugly conversion trick, required to evade (const char*) -> (char*) cast-qual warning :( */
+    return (char*)(uptrval)bufferStart;
+}
+
+
+/* ================================================
+ *  LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX])
+ * ===============================================*/
+typedef struct {
+    int price;
+    int off;
+    int mlen;
+    int litlen;
+} LZ4HC_optimal_t;
+
+/* price in bytes */
+LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen)
+{
+    int price = litlen;
+    assert(litlen >= 0);
+    if (litlen >= (int)RUN_MASK)
+        price += 1 + ((litlen-(int)RUN_MASK) / 255);
+    return price;
+}
+
+
+/* requires mlen >= MINMATCH */
+LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen)
+{
+    int price = 1 + 2 ; /* token + 16-bit offset */
+    assert(litlen >= 0);
+    assert(mlen >= MINMATCH);
+
+    price += LZ4HC_literalsPrice(litlen);
+
+    if (mlen >= (int)(ML_MASK+MINMATCH))
+        price += 1 + ((mlen-(int)(ML_MASK+MINMATCH)) / 255);
+
+    return price;
+}
+
+
+
+LZ4_FORCE_INLINE LZ4HC_match_t
+LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx,
+                      const BYTE* ip, const BYTE* const iHighLimit,
+                      int minLen, int nbSearches,
+                      const dictCtx_directive dict,
+                      const HCfavor_e favorDecSpeed)
+{
+    LZ4HC_match_t const match0 = { 0 , 0 };
+    /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos),
+     * but this won't be the case here, as we define iLowLimit==ip,
+    ** so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */
+    LZ4HC_match_t md = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, &ip, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed);
+    if (md.len <= minLen) return match0;
+    if (favorDecSpeed) {
+        if ((md.len>18) & (md.len<=36)) md.len=18;   /* favor shortcut */
+    }
+    return md;
+}
+
+
+static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx,
+                                    const char* const source,
+                                    char* dst,
+                                    int* srcSizePtr,
+                                    int dstCapacity,
+                                    int const nbSearches,
+                                    size_t sufficient_len,
+                                    const limitedOutput_directive limit,
+                                    int const fullUpdate,
+                                    const dictCtx_directive dict,
+                                    const HCfavor_e favorDecSpeed)
+{
+    int retval = 0;
+#define TRAILING_LITERALS 3
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+    LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS));
+#else
+    LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS];   /* ~64 KB, which is a bit large for stack... */
+#endif
+
+    const BYTE* ip = (const BYTE*) source;
+    const BYTE* anchor = ip;
+    const BYTE* const iend = ip + *srcSizePtr;
+    const BYTE* const mflimit = iend - MFLIMIT;
+    const BYTE* const matchlimit = iend - LASTLITERALS;
+    BYTE* op = (BYTE*) dst;
+    BYTE* opSaved = (BYTE*) dst;
+    BYTE* oend = op + dstCapacity;
+    int ovml = MINMATCH;  /* overflow - last sequence */
+    int ovoff = 0;
+
+    /* init */
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+    if (opt == NULL) goto _return_label;
+#endif
+    DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity);
+    *srcSizePtr = 0;
+    if (limit == fillOutput) oend -= LASTLITERALS;   /* Hack for support LZ4 format restriction */
+    if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1;
+
+    /* Main Loop */
+    while (ip <= mflimit) {
+         int const llen = (int)(ip - anchor);
+         int best_mlen, best_off;
+         int cur, last_match_pos = 0;
+
+         LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed);
+         if (firstMatch.len==0) { ip++; continue; }
+
+         if ((size_t)firstMatch.len > sufficient_len) {
+             /* good enough solution : immediate encoding */
+             int const firstML = firstMatch.len;
+             opSaved = op;
+             if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, firstMatch.off, limit, oend) ) {  /* updates ip, op and anchor */
+                 ovml = firstML;
+                 ovoff = firstMatch.off;
+                 goto _dest_overflow;
+             }
+             continue;
+         }
+
+         /* set prices for first positions (literals) */
+         {   int rPos;
+             for (rPos = 0 ; rPos < MINMATCH ; rPos++) {
+                 int const cost = LZ4HC_literalsPrice(llen + rPos);
+                 opt[rPos].mlen = 1;
+                 opt[rPos].off = 0;
+                 opt[rPos].litlen = llen + rPos;
+                 opt[rPos].price = cost;
+                 DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup",
+                             rPos, cost, opt[rPos].litlen);
+         }   }
+         /* set prices using initial match */
+         {   int mlen = MINMATCH;
+             int const matchML = firstMatch.len;   /* necessarily < sufficient_len < LZ4_OPT_NUM */
+             int const offset = firstMatch.off;
+             assert(matchML < LZ4_OPT_NUM);
+             for ( ; mlen <= matchML ; mlen++) {
+                 int const cost = LZ4HC_sequencePrice(llen, mlen);
+                 opt[mlen].mlen = mlen;
+                 opt[mlen].off = offset;
+                 opt[mlen].litlen = llen;
+                 opt[mlen].price = cost;
+                 DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i) -- initial setup",
+                             mlen, cost, mlen);
+         }   }
+         last_match_pos = firstMatch.len;
+         {   int addLit;
+             for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) {
+                 opt[last_match_pos+addLit].mlen = 1; /* literal */
+                 opt[last_match_pos+addLit].off = 0;
+                 opt[last_match_pos+addLit].litlen = addLit;
+                 opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit);
+                 DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup",
+                             last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit);
+         }   }
+
+         /* check further positions */
+         for (cur = 1; cur < last_match_pos; cur++) {
+             const BYTE* const curPtr = ip + cur;
+             LZ4HC_match_t newMatch;
+
+             if (curPtr > mflimit) break;
+             DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u",
+                     cur, opt[cur].price, opt[cur+1].price, cur+1);
+             if (fullUpdate) {
+                 /* not useful to search here if next position has same (or lower) cost */
+                 if ( (opt[cur+1].price <= opt[cur].price)
+                   /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */
+                   && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) )
+                     continue;
+             } else {
+                 /* not useful to search here if next position has same (or lower) cost */
+                 if (opt[cur+1].price <= opt[cur].price) continue;
+             }
+
+             DEBUGLOG(7, "search at rPos:%u", cur);
+             if (fullUpdate)
+                 newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed);
+             else
+                 /* only test matches of minimum length; slightly faster, but misses a few bytes */
+                 newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches, dict, favorDecSpeed);
+             if (!newMatch.len) continue;
+
+             if ( ((size_t)newMatch.len > sufficient_len)
+               || (newMatch.len + cur >= LZ4_OPT_NUM) ) {
+                 /* immediate encoding */
+                 best_mlen = newMatch.len;
+                 best_off = newMatch.off;
+                 last_match_pos = cur + 1;
+                 goto encode;
+             }
+
+             /* before match : set price with literals at beginning */
+             {   int const baseLitlen = opt[cur].litlen;
+                 int litlen;
+                 for (litlen = 1; litlen < MINMATCH; litlen++) {
+                     int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen);
+                     int const pos = cur + litlen;
+                     if (price < opt[pos].price) {
+                         opt[pos].mlen = 1; /* literal */
+                         opt[pos].off = 0;
+                         opt[pos].litlen = baseLitlen+litlen;
+                         opt[pos].price = price;
+                         DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)",
+                                     pos, price, opt[pos].litlen);
+             }   }   }
+
+             /* set prices using match at position = cur */
+             {   int const matchML = newMatch.len;
+                 int ml = MINMATCH;
+
+                 assert(cur + newMatch.len < LZ4_OPT_NUM);
+                 for ( ; ml <= matchML ; ml++) {
+                     int const pos = cur + ml;
+                     int const offset = newMatch.off;
+                     int price;
+                     int ll;
+                     DEBUGLOG(7, "testing price rPos %i (last_match_pos=%i)",
+                                 pos, last_match_pos);
+                     if (opt[cur].mlen == 1) {
+                         ll = opt[cur].litlen;
+                         price = ((cur > ll) ? opt[cur - ll].price : 0)
+                               + LZ4HC_sequencePrice(ll, ml);
+                     } else {
+                         ll = 0;
+                         price = opt[cur].price + LZ4HC_sequencePrice(0, ml);
+                     }
+
+                    assert((U32)favorDecSpeed <= 1);
+                     if (pos > last_match_pos+TRAILING_LITERALS
+                      || price <= opt[pos].price - (int)favorDecSpeed) {
+                         DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)",
+                                     pos, price, ml);
+                         assert(pos < LZ4_OPT_NUM);
+                         if ( (ml == matchML)  /* last pos of last match */
+                           && (last_match_pos < pos) )
+                             last_match_pos = pos;
+                         opt[pos].mlen = ml;
+                         opt[pos].off = offset;
+                         opt[pos].litlen = ll;
+                         opt[pos].price = price;
+             }   }   }
+             /* complete following positions with literals */
+             {   int addLit;
+                 for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) {
+                     opt[last_match_pos+addLit].mlen = 1; /* literal */
+                     opt[last_match_pos+addLit].off = 0;
+                     opt[last_match_pos+addLit].litlen = addLit;
+                     opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit);
+                     DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit);
+             }   }
+         }  /* for (cur = 1; cur <= last_match_pos; cur++) */
+
+         assert(last_match_pos < LZ4_OPT_NUM + TRAILING_LITERALS);
+         best_mlen = opt[last_match_pos].mlen;
+         best_off = opt[last_match_pos].off;
+         cur = last_match_pos - best_mlen;
+
+encode: /* cur, last_match_pos, best_mlen, best_off must be set */
+         assert(cur < LZ4_OPT_NUM);
+         assert(last_match_pos >= 1);  /* == 1 when only one candidate */
+         DEBUGLOG(6, "reverse traversal, looking for shortest path (last_match_pos=%i)", last_match_pos);
+         {   int candidate_pos = cur;
+             int selected_matchLength = best_mlen;
+             int selected_offset = best_off;
+             while (1) {  /* from end to beginning */
+                 int const next_matchLength = opt[candidate_pos].mlen;  /* can be 1, means literal */
+                 int const next_offset = opt[candidate_pos].off;
+                 DEBUGLOG(7, "pos %i: sequence length %i", candidate_pos, selected_matchLength);
+                 opt[candidate_pos].mlen = selected_matchLength;
+                 opt[candidate_pos].off = selected_offset;
+                 selected_matchLength = next_matchLength;
+                 selected_offset = next_offset;
+                 if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */
+                 assert(next_matchLength > 0);  /* can be 1, means literal */
+                 candidate_pos -= next_matchLength;
+         }   }
+
+         /* encode all recorded sequences in order */
+         {   int rPos = 0;  /* relative position (to ip) */
+             while (rPos < last_match_pos) {
+                 int const ml = opt[rPos].mlen;
+                 int const offset = opt[rPos].off;
+                 if (ml == 1) { ip++; rPos++; continue; }  /* literal; note: can end up with several literals, in which case, skip them */
+                 rPos += ml;
+                 assert(ml >= MINMATCH);
+                 assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX));
+                 opSaved = op;
+                 if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, offset, limit, oend) ) {  /* updates ip, op and anchor */
+                     ovml = ml;
+                     ovoff = offset;
+                     goto _dest_overflow;
+         }   }   }
+     }  /* while (ip <= mflimit) */
+
+_last_literals:
+     /* Encode Last Literals */
+     {   size_t lastRunSize = (size_t)(iend - anchor);  /* literals */
+         size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255;
+         size_t const totalSize = 1 + llAdd + lastRunSize;
+         if (limit == fillOutput) oend += LASTLITERALS;  /* restore correct value */
+         if (limit && (op + totalSize > oend)) {
+             if (limit == limitedOutput) { /* Check output limit */
+                retval = 0;
+                goto _return_label;
+             }
+             /* adapt lastRunSize to fill 'dst' */
+             lastRunSize  = (size_t)(oend - op) - 1 /*token*/;
+             llAdd = (lastRunSize + 256 - RUN_MASK) / 256;
+             lastRunSize -= llAdd;
+         }
+         DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize);
+         ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */
+
+         if (lastRunSize >= RUN_MASK) {
+             size_t accumulator = lastRunSize - RUN_MASK;
+             *op++ = (RUN_MASK << ML_BITS);
+             for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255;
+             *op++ = (BYTE) accumulator;
+         } else {
+             *op++ = (BYTE)(lastRunSize << ML_BITS);
+         }
+         LZ4_memcpy(op, anchor, lastRunSize);
+         op += lastRunSize;
+     }
+
+     /* End */
+     *srcSizePtr = (int) (((const char*)ip) - source);
+     retval = (int) ((char*)op-dst);
+     goto _return_label;
+
+_dest_overflow:
+if (limit == fillOutput) {
+     /* Assumption : ip, anchor, ovml and ovref must be set correctly */
+     size_t const ll = (size_t)(ip - anchor);
+     size_t const ll_addbytes = (ll + 240) / 255;
+     size_t const ll_totalCost = 1 + ll_addbytes + ll;
+     BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */
+     DEBUGLOG(6, "Last sequence overflowing (only %i bytes remaining)", (int)(oend-1-opSaved));
+     op = opSaved;  /* restore correct out pointer */
+     if (op + ll_totalCost <= maxLitPos) {
+         /* ll validated; now adjust match length */
+         size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost));
+         size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255);
+         assert(maxMlSize < INT_MAX); assert(ovml >= 0);
+         if ((size_t)ovml > maxMlSize) ovml = (int)maxMlSize;
+         if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ovml >= MFLIMIT) {
+             DEBUGLOG(6, "Space to end : %i + ml (%i)", (int)((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1), ovml);
+             DEBUGLOG(6, "Before : ip = %p, anchor = %p", ip, anchor);
+             LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ovml, ovoff, notLimited, oend);
+             DEBUGLOG(6, "After : ip = %p, anchor = %p", ip, anchor);
+     }   }
+     goto _last_literals;
+}
+_return_label:
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+     FREEMEM(opt);
+#endif
+     return retval;
+}
diff --git a/3rdparty/lz4/lz4hc.h b/3rdparty/lz4/lz4hc.h
new file mode 100644
index 00000000000..e937acfefd8
--- /dev/null
+++ b/3rdparty/lz4/lz4hc.h
@@ -0,0 +1,413 @@
+/*
+   LZ4 HC - High Compression Mode of LZ4
+   Header File
+   Copyright (C) 2011-2020, Yann Collet.
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - LZ4 source repository : https://github.com/lz4/lz4
+   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+#ifndef LZ4_HC_H_19834876238432
+#define LZ4_HC_H_19834876238432
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* --- Dependency --- */
+/* note : lz4hc requires lz4.h/lz4.c for compilation */
+#include "lz4.h"   /* stddef, LZ4LIB_API, LZ4_DEPRECATED */
+
+
+/* --- Useful constants --- */
+#define LZ4HC_CLEVEL_MIN         3
+#define LZ4HC_CLEVEL_DEFAULT     9
+#define LZ4HC_CLEVEL_OPT_MIN    10
+#define LZ4HC_CLEVEL_MAX        12
+
+
+/*-************************************
+ *  Block Compression
+ **************************************/
+/*! LZ4_compress_HC() :
+ *  Compress data from `src` into `dst`, using the powerful but slower "HC" algorithm.
+ * `dst` must be already allocated.
+ *  Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h")
+ *  Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h")
+ * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work.
+ *                      Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX.
+ * @return : the number of bytes written into 'dst'
+ *           or 0 if compression fails.
+ */
+LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel);
+
+
+/* Note :
+ *   Decompression functions are provided within "lz4.h" (BSD license)
+ */
+
+
+/*! LZ4_compress_HC_extStateHC() :
+ *  Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`.
+ * `state` size is provided by LZ4_sizeofStateHC().
+ *  Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly).
+ */
+LZ4LIB_API int LZ4_sizeofStateHC(void);
+LZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel);
+
+
+/*! LZ4_compress_HC_destSize() : v1.9.0+
+ *  Will compress as much data as possible from `src`
+ *  to fit into `targetDstSize` budget.
+ *  Result is provided in 2 parts :
+ * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize)
+ *           or 0 if compression fails.
+ * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src`
+ */
+LZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC,
+                                  const char* src, char* dst,
+                                        int* srcSizePtr, int targetDstSize,
+                                        int compressionLevel);
+
+
+/*-************************************
+ *  Streaming Compression
+ *  Bufferless synchronous API
+ **************************************/
+ typedef union LZ4_streamHC_u LZ4_streamHC_t;   /* incomplete type (defined later) */
+
+/*! LZ4_createStreamHC() and LZ4_freeStreamHC() :
+ *  These functions create and release memory for LZ4 HC streaming state.
+ *  Newly created states are automatically initialized.
+ *  A same state can be used multiple times consecutively,
+ *  starting with LZ4_resetStreamHC_fast() to start a new stream of blocks.
+ */
+LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void);
+LZ4LIB_API int             LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);
+
+/*
+  These functions compress data in successive blocks of any size,
+  using previous blocks as dictionary, to improve compression ratio.
+  One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks.
+  There is an exception for ring buffers, which can be smaller than 64 KB.
+  Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue().
+
+  Before starting compression, state must be allocated and properly initialized.
+  LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT.
+
+  Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream)
+  or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental).
+  LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once,
+  which is automatically the case when state is created using LZ4_createStreamHC().
+
+  After reset, a first "fictional block" can be designated as initial dictionary,
+  using LZ4_loadDictHC() (Optional).
+
+  Invoke LZ4_compress_HC_continue() to compress each successive block.
+  The number of blocks is unlimited.
+  Previous input blocks, including initial dictionary when present,
+  must remain accessible and unmodified during compression.
+
+  It's allowed to update compression level anytime between blocks,
+  using LZ4_setCompressionLevel() (experimental).
+
+  'dst' buffer should be sized to handle worst case scenarios
+  (see LZ4_compressBound(), it ensures compression success).
+  In case of failure, the API does not guarantee recovery,
+  so the state _must_ be reset.
+  To ensure compression success
+  whenever `dst` buffer size cannot be made >= LZ4_compressBound(),
+  consider using LZ4_compress_HC_continue_destSize().
+
+  Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks,
+  it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC().
+  Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB)
+
+  After completing a streaming compression,
+  it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state,
+  just by resetting it, using LZ4_resetStreamHC_fast().
+*/
+
+LZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel);   /* v1.9.0+ */
+LZ4LIB_API int  LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize);
+
+LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr,
+                                   const char* src, char* dst,
+                                         int srcSize, int maxDstSize);
+
+/*! LZ4_compress_HC_continue_destSize() : v1.9.0+
+ *  Similar to LZ4_compress_HC_continue(),
+ *  but will read as much data as possible from `src`
+ *  to fit into `targetDstSize` budget.
+ *  Result is provided into 2 parts :
+ * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize)
+ *           or 0 if compression fails.
+ * `srcSizePtr` : on success, *srcSizePtr will be updated to indicate how much bytes were read from `src`.
+ *           Note that this function may not consume the entire input.
+ */
+LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr,
+                                           const char* src, char* dst,
+                                                 int* srcSizePtr, int targetDstSize);
+
+LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize);
+
+
+
+/*^**********************************************
+ * !!!!!!   STATIC LINKING ONLY   !!!!!!
+ ***********************************************/
+
+/*-******************************************************************
+ * PRIVATE DEFINITIONS :
+ * Do not use these definitions directly.
+ * They are merely exposed to allow static allocation of `LZ4_streamHC_t`.
+ * Declare an `LZ4_streamHC_t` directly, rather than any type below.
+ * Even then, only do so in the context of static linking, as definitions may change between versions.
+ ********************************************************************/
+
+#define LZ4HC_DICTIONARY_LOGSIZE 16
+#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE)
+#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1)
+
+#define LZ4HC_HASH_LOG 15
+#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG)
+#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1)
+
+
+/* Never ever use these definitions directly !
+ * Declare or allocate an LZ4_streamHC_t instead.
+**/
+typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal;
+struct LZ4HC_CCtx_internal
+{
+    LZ4_u32   hashTable[LZ4HC_HASHTABLESIZE];
+    LZ4_u16   chainTable[LZ4HC_MAXD];
+    const LZ4_byte* end;       /* next block here to continue on current prefix */
+    const LZ4_byte* prefixStart;  /* Indexes relative to this position */
+    const LZ4_byte* dictStart; /* alternate reference for extDict */
+    LZ4_u32   dictLimit;       /* below that point, need extDict */
+    LZ4_u32   lowLimit;        /* below that point, no more dict */
+    LZ4_u32   nextToUpdate;    /* index from which to continue dictionary update */
+    short     compressionLevel;
+    LZ4_i8    favorDecSpeed;   /* favor decompression speed if this flag set,
+                                  otherwise, favor compression ratio */
+    LZ4_i8    dirty;           /* stream has to be fully reset if this flag is set */
+    const LZ4HC_CCtx_internal* dictCtx;
+};
+
+#define LZ4_STREAMHC_MINSIZE  262200  /* static size, for inter-version compatibility */
+union LZ4_streamHC_u {
+    char minStateSize[LZ4_STREAMHC_MINSIZE];
+    LZ4HC_CCtx_internal internal_donotuse;
+}; /* previously typedef'd to LZ4_streamHC_t */
+
+/* LZ4_streamHC_t :
+ * This structure allows static allocation of LZ4 HC streaming state.
+ * This can be used to allocate statically on stack, or as part of a larger structure.
+ *
+ * Such state **must** be initialized using LZ4_initStreamHC() before first use.
+ *
+ * Note that invoking LZ4_initStreamHC() is not required when
+ * the state was created using LZ4_createStreamHC() (which is recommended).
+ * Using the normal builder, a newly created state is automatically initialized.
+ *
+ * Static allocation shall only be used in combination with static linking.
+ */
+
+/* LZ4_initStreamHC() : v1.9.0+
+ * Required before first use of a statically allocated LZ4_streamHC_t.
+ * Before v1.9.0 : use LZ4_resetStreamHC() instead
+ */
+LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC(void* buffer, size_t size);
+
+
+/*-************************************
+*  Deprecated Functions
+**************************************/
+/* see lz4.h LZ4_DISABLE_DEPRECATE_WARNINGS to turn off deprecation warnings */
+
+/* deprecated compression functions */
+LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC               (const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC2              (const char* source, char* dest, int inputSize, int compressionLevel);
+LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
+LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC_withStateHC               (void* state, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC2_withStateHC              (void* state, const char* source, char* dest, int inputSize, int compressionLevel);
+LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
+LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC_continue               (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/* Obsolete streaming functions; degraded functionality; do not use!
+ *
+ * In order to perform streaming compression, these functions depended on data
+ * that is no longer tracked in the state. They have been preserved as well as
+ * possible: using them will still produce a correct output. However, use of
+ * LZ4_slideInputBufferHC() will truncate the history of the stream, rather
+ * than preserve a window-sized chunk of history.
+ */
+#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
+LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API void* LZ4_createHC (const char* inputBuffer);
+LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") LZ4LIB_API   int   LZ4_freeHC (void* LZ4HC_Data);
+#endif
+LZ4_DEPRECATED("use LZ4_saveDictHC() instead") LZ4LIB_API     char* LZ4_slideInputBufferHC (void* LZ4HC_Data);
+LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_continue               (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel);
+LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
+LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API int   LZ4_sizeofStreamStateHC(void);
+LZ4_DEPRECATED("use LZ4_initStreamHC() instead") LZ4LIB_API  int   LZ4_resetStreamStateHC(void* state, char* inputBuffer);
+
+
+/* LZ4_resetStreamHC() is now replaced by LZ4_initStreamHC().
+ * The intention is to emphasize the difference with LZ4_resetStreamHC_fast(),
+ * which is now the recommended function to start a new stream of blocks,
+ * but cannot be used to initialize a memory segment containing arbitrary garbage data.
+ *
+ * It is recommended to switch to LZ4_initStreamHC().
+ * LZ4_resetStreamHC() will generate deprecation warnings in a future version.
+ */
+LZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* LZ4_HC_H_19834876238432 */
+
+
+/*-**************************************************
+ * !!!!!     STATIC LINKING ONLY     !!!!!
+ * Following definitions are considered experimental.
+ * They should not be linked from DLL,
+ * as there is no guarantee of API stability yet.
+ * Prototypes will be promoted to "stable" status
+ * after successful usage in real-life scenarios.
+ ***************************************************/
+#ifdef LZ4_HC_STATIC_LINKING_ONLY   /* protection macro */
+#ifndef LZ4_HC_SLO_098092834
+#define LZ4_HC_SLO_098092834
+
+#define LZ4_STATIC_LINKING_ONLY   /* LZ4LIB_STATIC_API */
+#include "lz4.h"
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/*! LZ4_setCompressionLevel() : v1.8.0+ (experimental)
+ *  It's possible to change compression level
+ *  between successive invocations of LZ4_compress_HC_continue*()
+ *  for dynamic adaptation.
+ */
+LZ4LIB_STATIC_API void LZ4_setCompressionLevel(
+    LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel);
+
+/*! LZ4_favorDecompressionSpeed() : v1.8.2+ (experimental)
+ *  Opt. Parser will favor decompression speed over compression ratio.
+ *  Only applicable to levels >= LZ4HC_CLEVEL_OPT_MIN.
+ */
+LZ4LIB_STATIC_API void LZ4_favorDecompressionSpeed(
+    LZ4_streamHC_t* LZ4_streamHCPtr, int favor);
+
+/*! LZ4_resetStreamHC_fast() : v1.9.0+
+ *  When an LZ4_streamHC_t is known to be in a internally coherent state,
+ *  it can often be prepared for a new compression with almost no work, only
+ *  sometimes falling back to the full, expensive reset that is always required
+ *  when the stream is in an indeterminate state (i.e., the reset performed by
+ *  LZ4_resetStreamHC()).
+ *
+ *  LZ4_streamHCs are guaranteed to be in a valid state when:
+ *  - returned from LZ4_createStreamHC()
+ *  - reset by LZ4_resetStreamHC()
+ *  - memset(stream, 0, sizeof(LZ4_streamHC_t))
+ *  - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast()
+ *  - the stream was in a valid state and was then used in any compression call
+ *    that returned success
+ *  - the stream was in an indeterminate state and was used in a compression
+ *    call that fully reset the state (LZ4_compress_HC_extStateHC()) and that
+ *    returned success
+ *
+ *  Note:
+ *  A stream that was last used in a compression call that returned an error
+ *  may be passed to this function. However, it will be fully reset, which will
+ *  clear any existing history and settings from the context.
+ */
+LZ4LIB_STATIC_API void LZ4_resetStreamHC_fast(
+    LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel);
+
+/*! LZ4_compress_HC_extStateHC_fastReset() :
+ *  A variant of LZ4_compress_HC_extStateHC().
+ *
+ *  Using this variant avoids an expensive initialization step. It is only safe
+ *  to call if the state buffer is known to be correctly initialized already
+ *  (see above comment on LZ4_resetStreamHC_fast() for a definition of
+ *  "correctly initialized"). From a high level, the difference is that this
+ *  function initializes the provided state with a call to
+ *  LZ4_resetStreamHC_fast() while LZ4_compress_HC_extStateHC() starts with a
+ *  call to LZ4_resetStreamHC().
+ */
+LZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset (
+    void* state,
+    const char* src, char* dst,
+    int srcSize, int dstCapacity,
+    int compressionLevel);
+
+/*! LZ4_attach_HC_dictionary() :
+ *  This is an experimental API that allows for the efficient use of a
+ *  static dictionary many times.
+ *
+ *  Rather than re-loading the dictionary buffer into a working context before
+ *  each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a
+ *  working LZ4_streamHC_t, this function introduces a no-copy setup mechanism,
+ *  in which the working stream references the dictionary stream in-place.
+ *
+ *  Several assumptions are made about the state of the dictionary stream.
+ *  Currently, only streams which have been prepared by LZ4_loadDictHC() should
+ *  be expected to work.
+ *
+ *  Alternatively, the provided dictionary stream pointer may be NULL, in which
+ *  case any existing dictionary stream is unset.
+ *
+ *  A dictionary should only be attached to a stream without any history (i.e.,
+ *  a stream that has just been reset).
+ *
+ *  The dictionary will remain attached to the working stream only for the
+ *  current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the
+ *  dictionary context association from the working stream. The dictionary
+ *  stream (and source buffer) must remain in-place / accessible / unchanged
+ *  through the lifetime of the stream session.
+ */
+LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary(
+          LZ4_streamHC_t *working_stream,
+    const LZ4_streamHC_t *dictionary_stream);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif   /* LZ4_HC_SLO_098092834 */
+#endif   /* LZ4_HC_STATIC_LINKING_ONLY */
diff --git a/3rdparty/lz4/xxhash.c b/3rdparty/lz4/xxhash.c
new file mode 100644
index 00000000000..ff28749e316
--- /dev/null
+++ b/3rdparty/lz4/xxhash.c
@@ -0,0 +1,1030 @@
+/*
+*  xxHash - Fast Hash algorithm
+*  Copyright (C) 2012-2016, Yann Collet
+*
+*  BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+*
+*  Redistribution and use in source and binary forms, with or without
+*  modification, are permitted provided that the following conditions are
+*  met:
+*
+*  * Redistributions of source code must retain the above copyright
+*  notice, this list of conditions and the following disclaimer.
+*  * Redistributions in binary form must reproduce the above
+*  copyright notice, this list of conditions and the following disclaimer
+*  in the documentation and/or other materials provided with the
+*  distribution.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+*  You can contact the author at :
+*  - xxHash homepage: http://www.xxhash.com
+*  - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+
+/* *************************************
+*  Tuning parameters
+***************************************/
+/*!XXH_FORCE_MEMORY_ACCESS :
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
+ *            It can generate buggy code on targets which do not support unaligned memory accesses.
+ *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See http://stackoverflow.com/a/32095106/646947 for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */
+#  if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
+                        || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
+                        || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+#    define XXH_FORCE_MEMORY_ACCESS 2
+#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \
+  (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
+                    || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
+                    || defined(__ARM_ARCH_7S__) ))
+#    define XXH_FORCE_MEMORY_ACCESS 1
+#  endif
+#endif
+
+/*!XXH_ACCEPT_NULL_INPUT_POINTER :
+ * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault.
+ * When this macro is enabled, xxHash actively checks input for null pointer.
+ * It it is, result for null input pointers is the same as a null-length input.
+ */
+#ifndef XXH_ACCEPT_NULL_INPUT_POINTER   /* can be defined externally */
+#  define XXH_ACCEPT_NULL_INPUT_POINTER 0
+#endif
+
+/*!XXH_FORCE_NATIVE_FORMAT :
+ * By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
+ * Results are therefore identical for little-endian and big-endian CPU.
+ * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+ * Should endian-independence be of no importance for your application, you may set the #define below to 1,
+ * to improve speed for Big-endian CPU.
+ * This option has no impact on Little_Endian CPU.
+ */
+#ifndef XXH_FORCE_NATIVE_FORMAT   /* can be defined externally */
+#  define XXH_FORCE_NATIVE_FORMAT 0
+#endif
+
+/*!XXH_FORCE_ALIGN_CHECK :
+ * This is a minor performance trick, only useful with lots of very small keys.
+ * It means : check for aligned/unaligned input.
+ * The check costs one initial branch per hash;
+ * set it to 0 when the input is guaranteed to be aligned,
+ * or when alignment doesn't matter for performance.
+ */
+#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
+#  if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+#    define XXH_FORCE_ALIGN_CHECK 0
+#  else
+#    define XXH_FORCE_ALIGN_CHECK 1
+#  endif
+#endif
+
+
+/* *************************************
+*  Includes & Memory related functions
+***************************************/
+/*! Modify the local functions below should you wish to use some other memory routines
+*   for malloc(), free() */
+#include <stdlib.h>
+static void* XXH_malloc(size_t s) { return malloc(s); }
+static void  XXH_free  (void* p)  { free(p); }
+/*! and for memcpy() */
+#include <string.h>
+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
+
+#include <assert.h>   /* assert */
+
+#define XXH_STATIC_LINKING_ONLY
+#include "xxhash.h"
+
+
+/* *************************************
+*  Compiler Specific Options
+***************************************/
+#ifdef _MSC_VER    /* Visual Studio */
+#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */
+#  define FORCE_INLINE static __forceinline
+#else
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
+#  else
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
+#endif
+
+
+/* *************************************
+*  Basic Types
+***************************************/
+#ifndef MEM_MODULE
+# if !defined (__VMS) \
+  && (defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+#   include <stdint.h>
+    typedef uint8_t  BYTE;
+    typedef uint16_t U16;
+    typedef uint32_t U32;
+# else
+    typedef unsigned char      BYTE;
+    typedef unsigned short     U16;
+    typedef unsigned int       U32;
+# endif
+#endif
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U32 u32; } __attribute__((packed)) unalign;
+static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+
+#else
+
+/* portable and safe solution. Generally efficient.
+ * see : http://stackoverflow.com/a/32095106/646947
+ */
+static U32 XXH_read32(const void* memPtr)
+{
+    U32 val;
+    memcpy(&val, memPtr, sizeof(val));
+    return val;
+}
+
+#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+
+/* ****************************************
+*  Compiler-specific Functions and Macros
+******************************************/
+#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#if defined(_MSC_VER)
+#  define XXH_rotl32(x,r) _rotl(x,r)
+#  define XXH_rotl64(x,r) _rotl64(x,r)
+#else
+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+#  define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
+#endif
+
+#if defined(_MSC_VER)     /* Visual Studio */
+#  define XXH_swap32 _byteswap_ulong
+#elif XXH_GCC_VERSION >= 403
+#  define XXH_swap32 __builtin_bswap32
+#else
+static U32 XXH_swap32 (U32 x)
+{
+    return  ((x << 24) & 0xff000000 ) |
+            ((x <<  8) & 0x00ff0000 ) |
+            ((x >>  8) & 0x0000ff00 ) |
+            ((x >> 24) & 0x000000ff );
+}
+#endif
+
+
+/* *************************************
+*  Architecture Macros
+***************************************/
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+
+/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
+#ifndef XXH_CPU_LITTLE_ENDIAN
+static int XXH_isLittleEndian(void)
+{
+    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental  */
+    return one.c[0];
+}
+#   define XXH_CPU_LITTLE_ENDIAN   XXH_isLittleEndian()
+#endif
+
+
+/* ***************************
+*  Memory reads
+*****************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
+{
+    if (align==XXH_unaligned)
+        return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
+    else
+        return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
+}
+
+FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
+{
+    return XXH_readLE32_align(ptr, endian, XXH_unaligned);
+}
+
+static U32 XXH_readBE32(const void* ptr)
+{
+    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
+}
+
+
+/* *************************************
+*  Macros
+***************************************/
+#define XXH_STATIC_ASSERT(c)  { enum { XXH_sa = 1/(int)(!!(c)) }; }  /* use after variable declarations */
+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
+
+
+/* *******************************************************************
+*  32-bit hash functions
+*********************************************************************/
+static const U32 PRIME32_1 = 2654435761U;
+static const U32 PRIME32_2 = 2246822519U;
+static const U32 PRIME32_3 = 3266489917U;
+static const U32 PRIME32_4 =  668265263U;
+static const U32 PRIME32_5 =  374761393U;
+
+static U32 XXH32_round(U32 seed, U32 input)
+{
+    seed += input * PRIME32_2;
+    seed  = XXH_rotl32(seed, 13);
+    seed *= PRIME32_1;
+    return seed;
+}
+
+/* mix all bits */
+static U32 XXH32_avalanche(U32 h32)
+{
+    h32 ^= h32 >> 15;
+    h32 *= PRIME32_2;
+    h32 ^= h32 >> 13;
+    h32 *= PRIME32_3;
+    h32 ^= h32 >> 16;
+    return(h32);
+}
+
+#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
+
+static U32
+XXH32_finalize(U32 h32, const void* ptr, size_t len,
+                XXH_endianess endian, XXH_alignment align)
+
+{
+    const BYTE* p = (const BYTE*)ptr;
+
+#define PROCESS1               \
+    h32 += (*p++) * PRIME32_5; \
+    h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+
+#define PROCESS4                         \
+    h32 += XXH_get32bits(p) * PRIME32_3; \
+    p+=4;                                \
+    h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;
+
+    switch(len&15)  /* or switch(bEnd - p) */
+    {
+      case 12:      PROCESS4;
+                    /* fallthrough */
+      case 8:       PROCESS4;
+                    /* fallthrough */
+      case 4:       PROCESS4;
+                    return XXH32_avalanche(h32);
+
+      case 13:      PROCESS4;
+                    /* fallthrough */
+      case 9:       PROCESS4;
+                    /* fallthrough */
+      case 5:       PROCESS4;
+                    PROCESS1;
+                    return XXH32_avalanche(h32);
+
+      case 14:      PROCESS4;
+                    /* fallthrough */
+      case 10:      PROCESS4;
+                    /* fallthrough */
+      case 6:       PROCESS4;
+                    PROCESS1;
+                    PROCESS1;
+                    return XXH32_avalanche(h32);
+
+      case 15:      PROCESS4;
+                    /* fallthrough */
+      case 11:      PROCESS4;
+                    /* fallthrough */
+      case 7:       PROCESS4;
+                    /* fallthrough */
+      case 3:       PROCESS1;
+                    /* fallthrough */
+      case 2:       PROCESS1;
+                    /* fallthrough */
+      case 1:       PROCESS1;
+                    /* fallthrough */
+      case 0:       return XXH32_avalanche(h32);
+    }
+    assert(0);
+    return h32;   /* reaching this point is deemed impossible */
+}
+
+
+FORCE_INLINE U32
+XXH32_endian_align(const void* input, size_t len, U32 seed,
+                    XXH_endianess endian, XXH_alignment align)
+{
+    const BYTE* p = (const BYTE*)input;
+    const BYTE* bEnd = p + len;
+    U32 h32;
+
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+    if (p==NULL) {
+        len=0;
+        bEnd=p=(const BYTE*)(size_t)16;
+    }
+#endif
+
+    if (len>=16) {
+        const BYTE* const limit = bEnd - 15;
+        U32 v1 = seed + PRIME32_1 + PRIME32_2;
+        U32 v2 = seed + PRIME32_2;
+        U32 v3 = seed + 0;
+        U32 v4 = seed - PRIME32_1;
+
+        do {
+            v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
+            v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
+            v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
+            v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
+        } while (p < limit);
+
+        h32 = XXH_rotl32(v1, 1)  + XXH_rotl32(v2, 7)
+            + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+    } else {
+        h32  = seed + PRIME32_5;
+    }
+
+    h32 += (U32)len;
+
+    return XXH32_finalize(h32, p, len&15, endian, align);
+}
+
+
+XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
+{
+#if 0
+    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+    XXH32_state_t state;
+    XXH32_reset(&state, seed);
+    XXH32_update(&state, input, len);
+    return XXH32_digest(&state);
+#else
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if (XXH_FORCE_ALIGN_CHECK) {
+        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */
+            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+                return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+            else
+                return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+    }   }
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+    else
+        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+
+
+/*======   Hash streaming   ======*/
+
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
+{
+    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
+{
+    XXH_free(statePtr);
+    return XXH_OK;
+}
+
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
+{
+    memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
+{
+    XXH32_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+    memset(&state, 0, sizeof(state));
+    state.v1 = seed + PRIME32_1 + PRIME32_2;
+    state.v2 = seed + PRIME32_2;
+    state.v3 = seed + 0;
+    state.v4 = seed - PRIME32_1;
+    /* do not write into reserved, planned to be removed in a future version */
+    memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));
+    return XXH_OK;
+}
+
+
+FORCE_INLINE XXH_errorcode
+XXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
+{
+    if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+        return XXH_OK;
+#else
+        return XXH_ERROR;
+#endif
+
+    {   const BYTE* p = (const BYTE*)input;
+        const BYTE* const bEnd = p + len;
+
+        state->total_len_32 += (unsigned)len;
+        state->large_len |= (len>=16) | (state->total_len_32>=16);
+
+        if (state->memsize + len < 16)  {   /* fill in tmp buffer */
+            XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
+            state->memsize += (unsigned)len;
+            return XXH_OK;
+        }
+
+        if (state->memsize) {   /* some data left from previous update */
+            XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
+            {   const U32* p32 = state->mem32;
+                state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;
+                state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;
+                state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;
+                state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian));
+            }
+            p += 16-state->memsize;
+            state->memsize = 0;
+        }
+
+        if (p <= bEnd-16) {
+            const BYTE* const limit = bEnd - 16;
+            U32 v1 = state->v1;
+            U32 v2 = state->v2;
+            U32 v3 = state->v3;
+            U32 v4 = state->v4;
+
+            do {
+                v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;
+                v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;
+                v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;
+                v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;
+            } while (p<=limit);
+
+            state->v1 = v1;
+            state->v2 = v2;
+            state->v3 = v3;
+            state->v4 = v4;
+        }
+
+        if (p < bEnd) {
+            XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
+            state->memsize = (unsigned)(bEnd-p);
+        }
+    }
+
+    return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+    else
+        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+FORCE_INLINE U32
+XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
+{
+    U32 h32;
+
+    if (state->large_len) {
+        h32 = XXH_rotl32(state->v1, 1)
+            + XXH_rotl32(state->v2, 7)
+            + XXH_rotl32(state->v3, 12)
+            + XXH_rotl32(state->v4, 18);
+    } else {
+        h32 = state->v3 /* == seed */ + PRIME32_5;
+    }
+
+    h32 += state->total_len_32;
+
+    return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned);
+}
+
+
+XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_digest_endian(state_in, XXH_littleEndian);
+    else
+        return XXH32_digest_endian(state_in, XXH_bigEndian);
+}
+
+
+/*======   Canonical representation   ======*/
+
+/*! Default XXH result types are basic unsigned 32 and 64 bits.
+*   The canonical representation follows human-readable write convention, aka big-endian (large digits first).
+*   These functions allow transformation of hash result into and from its canonical format.
+*   This way, hash values can be written into a file or buffer, remaining comparable across different systems.
+*/
+
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
+{
+    XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
+    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
+    memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
+{
+    return XXH_readBE32(src);
+}
+
+
+#ifndef XXH_NO_LONG_LONG
+
+/* *******************************************************************
+*  64-bit hash functions
+*********************************************************************/
+
+/*======   Memory access   ======*/
+
+#ifndef MEM_MODULE
+# define MEM_MODULE
+# if !defined (__VMS) \
+  && (defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+#   include <stdint.h>
+    typedef uint64_t U64;
+# else
+    /* if compiler doesn't support unsigned long long, replace by another 64-bit type */
+    typedef unsigned long long U64;
+# endif
+#endif
+
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;
+static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }
+
+#else
+
+/* portable and safe solution. Generally efficient.
+ * see : http://stackoverflow.com/a/32095106/646947
+ */
+
+static U64 XXH_read64(const void* memPtr)
+{
+    U64 val;
+    memcpy(&val, memPtr, sizeof(val));
+    return val;
+}
+
+#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+#if defined(_MSC_VER)     /* Visual Studio */
+#  define XXH_swap64 _byteswap_uint64
+#elif XXH_GCC_VERSION >= 403
+#  define XXH_swap64 __builtin_bswap64
+#else
+static U64 XXH_swap64 (U64 x)
+{
+    return  ((x << 56) & 0xff00000000000000ULL) |
+            ((x << 40) & 0x00ff000000000000ULL) |
+            ((x << 24) & 0x0000ff0000000000ULL) |
+            ((x << 8)  & 0x000000ff00000000ULL) |
+            ((x >> 8)  & 0x00000000ff000000ULL) |
+            ((x >> 24) & 0x0000000000ff0000ULL) |
+            ((x >> 40) & 0x000000000000ff00ULL) |
+            ((x >> 56) & 0x00000000000000ffULL);
+}
+#endif
+
+FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
+{
+    if (align==XXH_unaligned)
+        return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
+    else
+        return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
+}
+
+FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
+{
+    return XXH_readLE64_align(ptr, endian, XXH_unaligned);
+}
+
+static U64 XXH_readBE64(const void* ptr)
+{
+    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
+}
+
+
+/*======   xxh64   ======*/
+
+static const U64 PRIME64_1 = 11400714785074694791ULL;
+static const U64 PRIME64_2 = 14029467366897019727ULL;
+static const U64 PRIME64_3 =  1609587929392839161ULL;
+static const U64 PRIME64_4 =  9650029242287828579ULL;
+static const U64 PRIME64_5 =  2870177450012600261ULL;
+
+static U64 XXH64_round(U64 acc, U64 input)
+{
+    acc += input * PRIME64_2;
+    acc  = XXH_rotl64(acc, 31);
+    acc *= PRIME64_1;
+    return acc;
+}
+
+static U64 XXH64_mergeRound(U64 acc, U64 val)
+{
+    val  = XXH64_round(0, val);
+    acc ^= val;
+    acc  = acc * PRIME64_1 + PRIME64_4;
+    return acc;
+}
+
+static U64 XXH64_avalanche(U64 h64)
+{
+    h64 ^= h64 >> 33;
+    h64 *= PRIME64_2;
+    h64 ^= h64 >> 29;
+    h64 *= PRIME64_3;
+    h64 ^= h64 >> 32;
+    return h64;
+}
+
+
+#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
+
+static U64
+XXH64_finalize(U64 h64, const void* ptr, size_t len,
+               XXH_endianess endian, XXH_alignment align)
+{
+    const BYTE* p = (const BYTE*)ptr;
+
+#define PROCESS1_64            \
+    h64 ^= (*p++) * PRIME64_5; \
+    h64 = XXH_rotl64(h64, 11) * PRIME64_1;
+
+#define PROCESS4_64          \
+    h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \
+    p+=4;                    \
+    h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
+
+#define PROCESS8_64 {        \
+    U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \
+    p+=8;                    \
+    h64 ^= k1;               \
+    h64  = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \
+}
+
+    switch(len&31) {
+      case 24: PROCESS8_64;
+                    /* fallthrough */
+      case 16: PROCESS8_64;
+                    /* fallthrough */
+      case  8: PROCESS8_64;
+               return XXH64_avalanche(h64);
+
+      case 28: PROCESS8_64;
+                    /* fallthrough */
+      case 20: PROCESS8_64;
+                    /* fallthrough */
+      case 12: PROCESS8_64;
+                    /* fallthrough */
+      case  4: PROCESS4_64;
+               return XXH64_avalanche(h64);
+
+      case 25: PROCESS8_64;
+                    /* fallthrough */
+      case 17: PROCESS8_64;
+                    /* fallthrough */
+      case  9: PROCESS8_64;
+               PROCESS1_64;
+               return XXH64_avalanche(h64);
+
+      case 29: PROCESS8_64;
+                    /* fallthrough */
+      case 21: PROCESS8_64;
+                    /* fallthrough */
+      case 13: PROCESS8_64;
+                    /* fallthrough */
+      case  5: PROCESS4_64;
+               PROCESS1_64;
+               return XXH64_avalanche(h64);
+
+      case 26: PROCESS8_64;
+                    /* fallthrough */
+      case 18: PROCESS8_64;
+                    /* fallthrough */
+      case 10: PROCESS8_64;
+               PROCESS1_64;
+               PROCESS1_64;
+               return XXH64_avalanche(h64);
+
+      case 30: PROCESS8_64;
+                    /* fallthrough */
+      case 22: PROCESS8_64;
+                    /* fallthrough */
+      case 14: PROCESS8_64;
+                    /* fallthrough */
+      case  6: PROCESS4_64;
+               PROCESS1_64;
+               PROCESS1_64;
+               return XXH64_avalanche(h64);
+
+      case 27: PROCESS8_64;
+                    /* fallthrough */
+      case 19: PROCESS8_64;
+                    /* fallthrough */
+      case 11: PROCESS8_64;
+               PROCESS1_64;
+               PROCESS1_64;
+               PROCESS1_64;
+               return XXH64_avalanche(h64);
+
+      case 31: PROCESS8_64;
+                    /* fallthrough */
+      case 23: PROCESS8_64;
+                    /* fallthrough */
+      case 15: PROCESS8_64;
+                    /* fallthrough */
+      case  7: PROCESS4_64;
+                    /* fallthrough */
+      case  3: PROCESS1_64;
+                    /* fallthrough */
+      case  2: PROCESS1_64;
+                    /* fallthrough */
+      case  1: PROCESS1_64;
+                    /* fallthrough */
+      case  0: return XXH64_avalanche(h64);
+    }
+
+    /* impossible to reach */
+    assert(0);
+    return 0;  /* unreachable, but some compilers complain without it */
+}
+
+FORCE_INLINE U64
+XXH64_endian_align(const void* input, size_t len, U64 seed,
+                XXH_endianess endian, XXH_alignment align)
+{
+    const BYTE* p = (const BYTE*)input;
+    const BYTE* bEnd = p + len;
+    U64 h64;
+
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+    if (p==NULL) {
+        len=0;
+        bEnd=p=(const BYTE*)(size_t)32;
+    }
+#endif
+
+    if (len>=32) {
+        const BYTE* const limit = bEnd - 32;
+        U64 v1 = seed + PRIME64_1 + PRIME64_2;
+        U64 v2 = seed + PRIME64_2;
+        U64 v3 = seed + 0;
+        U64 v4 = seed - PRIME64_1;
+
+        do {
+            v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
+            v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
+            v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
+            v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
+        } while (p<=limit);
+
+        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+        h64 = XXH64_mergeRound(h64, v1);
+        h64 = XXH64_mergeRound(h64, v2);
+        h64 = XXH64_mergeRound(h64, v3);
+        h64 = XXH64_mergeRound(h64, v4);
+
+    } else {
+        h64  = seed + PRIME64_5;
+    }
+
+    h64 += (U64) len;
+
+    return XXH64_finalize(h64, p, len, endian, align);
+}
+
+
+XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
+{
+#if 0
+    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+    XXH64_state_t state;
+    XXH64_reset(&state, seed);
+    XXH64_update(&state, input, len);
+    return XXH64_digest(&state);
+#else
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if (XXH_FORCE_ALIGN_CHECK) {
+        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */
+            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+                return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+            else
+                return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+    }   }
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+    else
+        return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+/*======   Hash Streaming   ======*/
+
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
+{
+    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
+{
+    XXH_free(statePtr);
+    return XXH_OK;
+}
+
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
+{
+    memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
+{
+    XXH64_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+    memset(&state, 0, sizeof(state));
+    state.v1 = seed + PRIME64_1 + PRIME64_2;
+    state.v2 = seed + PRIME64_2;
+    state.v3 = seed + 0;
+    state.v4 = seed - PRIME64_1;
+     /* do not write into reserved, planned to be removed in a future version */
+    memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));
+    return XXH_OK;
+}
+
+FORCE_INLINE XXH_errorcode
+XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
+{
+    if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+        return XXH_OK;
+#else
+        return XXH_ERROR;
+#endif
+
+    {   const BYTE* p = (const BYTE*)input;
+        const BYTE* const bEnd = p + len;
+
+        state->total_len += len;
+
+        if (state->memsize + len < 32) {  /* fill in tmp buffer */
+            XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
+            state->memsize += (U32)len;
+            return XXH_OK;
+        }
+
+        if (state->memsize) {   /* tmp buffer is full */
+            XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
+            state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));
+            state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));
+            state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));
+            state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));
+            p += 32-state->memsize;
+            state->memsize = 0;
+        }
+
+        if (p+32 <= bEnd) {
+            const BYTE* const limit = bEnd - 32;
+            U64 v1 = state->v1;
+            U64 v2 = state->v2;
+            U64 v3 = state->v3;
+            U64 v4 = state->v4;
+
+            do {
+                v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;
+                v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;
+                v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;
+                v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;
+            } while (p<=limit);
+
+            state->v1 = v1;
+            state->v2 = v2;
+            state->v3 = v3;
+            state->v4 = v4;
+        }
+
+        if (p < bEnd) {
+            XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
+            state->memsize = (unsigned)(bEnd-p);
+        }
+    }
+
+    return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
+    else
+        return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
+{
+    U64 h64;
+
+    if (state->total_len >= 32) {
+        U64 const v1 = state->v1;
+        U64 const v2 = state->v2;
+        U64 const v3 = state->v3;
+        U64 const v4 = state->v4;
+
+        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+        h64 = XXH64_mergeRound(h64, v1);
+        h64 = XXH64_mergeRound(h64, v2);
+        h64 = XXH64_mergeRound(h64, v3);
+        h64 = XXH64_mergeRound(h64, v4);
+    } else {
+        h64  = state->v3 /*seed*/ + PRIME64_5;
+    }
+
+    h64 += (U64) state->total_len;
+
+    return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned);
+}
+
+XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH64_digest_endian(state_in, XXH_littleEndian);
+    else
+        return XXH64_digest_endian(state_in, XXH_bigEndian);
+}
+
+
+/*====== Canonical representation   ======*/
+
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
+{
+    XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
+    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
+    memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
+{
+    return XXH_readBE64(src);
+}
+
+#endif  /* XXH_NO_LONG_LONG */
diff --git a/3rdparty/lz4/xxhash.h b/3rdparty/lz4/xxhash.h
new file mode 100644
index 00000000000..d6bad943358
--- /dev/null
+++ b/3rdparty/lz4/xxhash.h
@@ -0,0 +1,328 @@
+/*
+   xxHash - Extremely Fast Hash algorithm
+   Header File
+   Copyright (C) 2012-2016, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+/* Notice extracted from xxHash homepage :
+
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
+It also successfully passes all tests from the SMHasher suite.
+
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
+
+Name            Speed       Q.Score   Author
+xxHash          5.4 GB/s     10
+CrapWow         3.2 GB/s      2       Andrew
+MumurHash 3a    2.7 GB/s     10       Austin Appleby
+SpookyHash      2.0 GB/s     10       Bob Jenkins
+SBox            1.4 GB/s      9       Bret Mulvey
+Lookup3         1.2 GB/s      9       Bob Jenkins
+SuperFastHash   1.2 GB/s      1       Paul Hsieh
+CityHash64      1.05 GB/s    10       Pike & Alakuijala
+FNV             0.55 GB/s     5       Fowler, Noll, Vo
+CRC32           0.43 GB/s     9
+MD5-32          0.33 GB/s    10       Ronald L. Rivest
+SHA1-32         0.28 GB/s    10
+
+Q.Score is a measure of quality of the hash function.
+It depends on successfully passing SMHasher test set.
+10 is a perfect score.
+
+A 64-bit version, named XXH64, is available since r35.
+It offers much better speed, but for 64-bit applications only.
+Name     Speed on 64 bits    Speed on 32 bits
+XXH64       13.8 GB/s            1.9 GB/s
+XXH32        6.8 GB/s            6.0 GB/s
+*/
+
+#ifndef XXHASH_H_5627135585666179
+#define XXHASH_H_5627135585666179 1
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/* ****************************
+*  Definitions
+******************************/
+#include <stddef.h>   /* size_t */
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+
+/* ****************************
+ *  API modifier
+ ******************************/
+/** XXH_INLINE_ALL (and XXH_PRIVATE_API)
+ *  This is useful to include xxhash functions in `static` mode
+ *  in order to inline them, and remove their symbol from the public list.
+ *  Inlining can offer dramatic performance improvement on small keys.
+ *  Methodology :
+ *     #define XXH_INLINE_ALL
+ *     #include "xxhash.h"
+ * `xxhash.c` is automatically included.
+ *  It's not useful to compile and link it as a separate module.
+ */
+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
+#  ifndef XXH_STATIC_LINKING_ONLY
+#    define XXH_STATIC_LINKING_ONLY
+#  endif
+#  if defined(__GNUC__)
+#    define XXH_PUBLIC_API static __inline __attribute__((unused))
+#  elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+#    define XXH_PUBLIC_API static inline
+#  elif defined(_MSC_VER)
+#    define XXH_PUBLIC_API static __inline
+#  else
+     /* this version may generate warnings for unused static functions */
+#    define XXH_PUBLIC_API static
+#  endif
+#else
+#  define XXH_PUBLIC_API   /* do nothing */
+#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */
+
+/*! XXH_NAMESPACE, aka Namespace Emulation :
+ *
+ * If you want to include _and expose_ xxHash functions from within your own library,
+ * but also want to avoid symbol collisions with other libraries which may also include xxHash,
+ *
+ * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
+ * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).
+ *
+ * Note that no change is required within the calling program as long as it includes `xxhash.h` :
+ * regular symbol name will be automatically translated by this header.
+ */
+#ifdef XXH_NAMESPACE
+#  define XXH_CAT(A,B) A##B
+#  define XXH_NAME2(A,B) XXH_CAT(A,B)
+#  define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
+#  define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
+#  define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
+#  define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
+#  define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
+#  define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
+#  define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
+#  define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
+#  define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
+#  define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
+#  define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
+#  define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
+#  define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
+#  define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
+#  define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
+#  define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
+#  define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
+#  define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
+#  define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
+#endif
+
+
+/* *************************************
+*  Version
+***************************************/
+#define XXH_VERSION_MAJOR    0
+#define XXH_VERSION_MINOR    6
+#define XXH_VERSION_RELEASE  5
+#define XXH_VERSION_NUMBER  (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
+XXH_PUBLIC_API unsigned XXH_versionNumber (void);
+
+
+/*-**********************************************************************
+*  32-bit hash
+************************************************************************/
+typedef unsigned int XXH32_hash_t;
+
+/*! XXH32() :
+    Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input".
+    The memory between input & input+length must be valid (allocated and read-accessible).
+    "seed" can be used to alter the result predictably.
+    Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
+
+/*======   Streaming   ======*/
+typedef struct XXH32_state_s XXH32_state_t;   /* incomplete type */
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
+XXH_PUBLIC_API XXH_errorcode  XXH32_freeState(XXH32_state_t* statePtr);
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset  (XXH32_state_t* statePtr, unsigned int seed);
+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH32_hash_t  XXH32_digest (const XXH32_state_t* statePtr);
+
+/*
+ * Streaming functions generate the xxHash of an input provided in multiple segments.
+ * Note that, for small input, they are slower than single-call functions, due to state management.
+ * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.
+ *
+ * XXH state must first be allocated, using XXH*_createState() .
+ *
+ * Start a new hash by initializing state with a seed, using XXH*_reset().
+ *
+ * Then, feed the hash state by calling XXH*_update() as many times as necessary.
+ * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
+ *
+ * Finally, a hash value can be produced anytime, by using XXH*_digest().
+ * This function returns the nn-bits hash as an int or long long.
+ *
+ * It's still possible to continue inserting input into the hash state after a digest,
+ * and generate some new hashes later on, by calling again XXH*_digest().
+ *
+ * When done, free XXH state space if it was allocated dynamically.
+ */
+
+/*======   Canonical representation   ======*/
+
+typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
+
+/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
+ * The canonical representation uses human-readable write convention, aka big-endian (large digits first).
+ * These functions allow transformation of hash result into and from its canonical format.
+ * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
+ */
+
+
+#ifndef XXH_NO_LONG_LONG
+/*-**********************************************************************
+*  64-bit hash
+************************************************************************/
+typedef unsigned long long XXH64_hash_t;
+
+/*! XXH64() :
+    Calculate the 64-bit hash of sequence of length "len" stored at memory address "input".
+    "seed" can be used to alter the result predictably.
+    This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark).
+*/
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
+
+/*======   Streaming   ======*/
+typedef struct XXH64_state_s XXH64_state_t;   /* incomplete type */
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
+XXH_PUBLIC_API XXH_errorcode  XXH64_freeState(XXH64_state_t* statePtr);
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset  (XXH64_state_t* statePtr, unsigned long long seed);
+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t  XXH64_digest (const XXH64_state_t* statePtr);
+
+/*======   Canonical representation   ======*/
+typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
+#endif  /* XXH_NO_LONG_LONG */
+
+
+
+#ifdef XXH_STATIC_LINKING_ONLY
+
+/* ================================================================================================
+   This section contains declarations which are not guaranteed to remain stable.
+   They may change in future versions, becoming incompatible with a different version of the library.
+   These declarations should only be used with static linking.
+   Never use them in association with dynamic linking !
+=================================================================================================== */
+
+/* These definitions are only present to allow
+ * static allocation of XXH state, on stack or in a struct for example.
+ * Never **ever** use members directly. */
+
+#if !defined (__VMS) \
+  && (defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+#   include <stdint.h>
+
+struct XXH32_state_s {
+   uint32_t total_len_32;
+   uint32_t large_len;
+   uint32_t v1;
+   uint32_t v2;
+   uint32_t v3;
+   uint32_t v4;
+   uint32_t mem32[4];
+   uint32_t memsize;
+   uint32_t reserved;   /* never read nor write, might be removed in a future version */
+};   /* typedef'd to XXH32_state_t */
+
+struct XXH64_state_s {
+   uint64_t total_len;
+   uint64_t v1;
+   uint64_t v2;
+   uint64_t v3;
+   uint64_t v4;
+   uint64_t mem64[4];
+   uint32_t memsize;
+   uint32_t reserved[2];          /* never read nor write, might be removed in a future version */
+};   /* typedef'd to XXH64_state_t */
+
+# else
+
+struct XXH32_state_s {
+   unsigned total_len_32;
+   unsigned large_len;
+   unsigned v1;
+   unsigned v2;
+   unsigned v3;
+   unsigned v4;
+   unsigned mem32[4];
+   unsigned memsize;
+   unsigned reserved;   /* never read nor write, might be removed in a future version */
+};   /* typedef'd to XXH32_state_t */
+
+#   ifndef XXH_NO_LONG_LONG  /* remove 64-bit support */
+struct XXH64_state_s {
+   unsigned long long total_len;
+   unsigned long long v1;
+   unsigned long long v2;
+   unsigned long long v3;
+   unsigned long long v4;
+   unsigned long long mem64[4];
+   unsigned memsize;
+   unsigned reserved[2];     /* never read nor write, might be removed in a future version */
+};   /* typedef'd to XXH64_state_t */
+#    endif
+
+# endif
+
+
+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
+#  include "xxhash.c"   /* include xxhash function bodies as `static`, for inlining */
+#endif
+
+#endif /* XXH_STATIC_LINKING_ONLY */
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* XXHASH_H_5627135585666179 */
diff --git a/3rdparty/switchres/custom_video.cpp b/3rdparty/switchres/custom_video.cpp
new file mode 100644
index 00000000000..43ef46ea382
--- /dev/null
+++ b/3rdparty/switchres/custom_video.cpp
@@ -0,0 +1,178 @@
+/**************************************************************
+
+   custom_video.cpp - Custom video library
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+
+
+#include <stdio.h>
+#include "custom_video.h"
+#include "log.h"
+
+#if defined(_WIN32)
+#include "custom_video_ati.h"
+#include "custom_video_adl.h"
+#include "custom_video_pstrip.h"
+#elif defined(__linux__)
+#ifdef SR_WITH_XRANDR
+#include "custom_video_xrandr.h"
+#endif
+#ifdef SR_WITH_KMSDRM
+#include "custom_video_drmkms.h"
+#endif
+#endif
+
+
+extern bool ati_is_legacy(int vendor, int device);
+
+//============================================================
+//  custom_video::make
+//============================================================
+
+custom_video *custom_video::make(char *device_name, char *device_id, int method, custom_video_settings *vs)
+{
+#if defined(_WIN32)
+	if (method == CUSTOM_VIDEO_TIMING_POWERSTRIP)
+	{
+		m_custom_video = new pstrip_timing(device_name, vs);
+		if (m_custom_video)
+		{
+			m_custom_method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
+			return m_custom_video;
+		}
+	}
+	else
+	{
+		int vendor, device;
+		sscanf(device_id, "PCI\\VEN_%x&DEV_%x", &vendor, &device);
+
+		if (vendor == 0x1002) // ATI/AMD
+		{
+			if (ati_is_legacy(vendor, device))
+			{
+				m_custom_video = new ati_timing(device_name, vs);
+				if (m_custom_video)
+				{
+					m_custom_method = CUSTOM_VIDEO_TIMING_ATI_LEGACY;
+					return m_custom_video;
+				}
+			}
+			else
+			{
+				m_custom_video = new adl_timing(device_name, vs);
+				if (m_custom_video)
+				{
+					m_custom_method = CUSTOM_VIDEO_TIMING_ATI_ADL;
+					return m_custom_video;
+				}
+			}
+		}
+		else
+			log_info("Video chipset is not compatible.\n");
+	}
+#elif defined(__linux__)
+	if (device_id != NULL)
+		log_info("Device value is %s.\n", device_id);
+
+#ifdef SR_WITH_XRANDR
+	if (method == CUSTOM_VIDEO_TIMING_XRANDR || method == 0)
+	{
+		try
+		{
+			m_custom_video = new xrandr_timing(device_name, vs);
+		}
+		catch (...) {};
+		if (m_custom_video)
+		{
+			m_custom_method = CUSTOM_VIDEO_TIMING_XRANDR;
+			return m_custom_video;
+		}
+	}
+#endif /* SR_WITH_XRANDR */
+
+#ifdef SR_WITH_KMSDRM
+	if (method == CUSTOM_VIDEO_TIMING_DRMKMS || method == 0)
+	{
+		m_custom_video = new drmkms_timing(device_name, vs);
+		if (m_custom_video)
+		{
+			m_custom_method = CUSTOM_VIDEO_TIMING_DRMKMS;
+			return m_custom_video;
+		}
+	}
+#endif /* SR_WITH_KMSDRM */
+#endif
+
+	return this;
+}
+
+//============================================================
+//  custom_video::init
+//============================================================
+
+bool custom_video::init() { return false; }
+
+//============================================================
+//  custom_video::get_timing
+//============================================================
+
+bool custom_video::get_timing(modeline *mode)
+{
+	log_verbose("system mode\n");
+	mode->type |= CUSTOM_VIDEO_TIMING_SYSTEM;
+	return false;
+}
+
+//============================================================
+//  custom_video::set_timing
+//============================================================
+
+bool custom_video::set_timing(modeline *)
+{
+	return false;
+}
+
+//============================================================
+//  custom_video::add_mode
+//============================================================
+
+bool custom_video::add_mode(modeline *)
+{
+	return false;
+}
+
+//============================================================
+//  custom_video::delete_mode
+//============================================================
+
+bool custom_video::delete_mode(modeline *)
+{
+	return false;
+}
+
+//============================================================
+//  custom_video::update_mode
+//============================================================
+
+bool custom_video::update_mode(modeline *)
+{
+	return false;
+}
+
+//============================================================
+//  custom_video::process_modelist
+//============================================================
+
+bool custom_video::process_modelist(std::vector<modeline *>)
+{
+	return false;
+}
diff --git a/3rdparty/switchres/custom_video.h b/3rdparty/switchres/custom_video.h
new file mode 100644
index 00000000000..79ab327ec77
--- /dev/null
+++ b/3rdparty/switchres/custom_video.h
@@ -0,0 +1,107 @@
+/**************************************************************
+
+   custom_video.h - Custom video library header
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __CUSTOM_VIDEO__
+#define __CUSTOM_VIDEO__
+
+#include <vector>
+#include <cstring>
+#include "modeline.h"
+
+
+#define CUSTOM_VIDEO_TIMING_MASK        0x00000ff0
+#define CUSTOM_VIDEO_TIMING_AUTO        0x00000000
+#define CUSTOM_VIDEO_TIMING_SYSTEM      0x00000010
+#define CUSTOM_VIDEO_TIMING_XRANDR      0x00000020
+#define CUSTOM_VIDEO_TIMING_POWERSTRIP  0x00000040
+#define CUSTOM_VIDEO_TIMING_ATI_LEGACY  0x00000080
+#define CUSTOM_VIDEO_TIMING_ATI_ADL     0x00000100
+#define CUSTOM_VIDEO_TIMING_DRMKMS      0x00000200
+
+// Custom video caps
+#define CUSTOM_VIDEO_CAPS_UPDATE            0x001
+#define CUSTOM_VIDEO_CAPS_ADD               0x002
+#define CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE  0x004
+#define CUSTOM_VIDEO_CAPS_SCAN_EDITABLE     0x008
+
+// Timing creation commands
+#define TIMING_DELETE      0x001
+#define TIMING_CREATE      0x002
+#define TIMING_UPDATE      0x004
+#define TIMING_UPDATE_LIST 0x008
+
+typedef struct custom_video_settings
+{
+	bool screen_compositing;
+	bool screen_reordering;
+	bool allow_hardware_refresh;
+	char device_reg_key[128];
+	char custom_timing[256];
+} custom_video_settings;
+
+class custom_video
+{
+public:
+
+	custom_video() {};
+	virtual ~custom_video()
+	{
+		if (m_custom_video)
+		{
+			delete m_custom_video;
+			m_custom_video = nullptr;
+		}
+	};
+
+	custom_video *make(char *device_name, char *device_id, int method, custom_video_settings *vs);
+	virtual const char *api_name() { return "empty"; }
+	virtual bool init();
+	virtual int caps() { return 0; }
+
+	virtual bool add_mode(modeline *mode);
+	virtual bool delete_mode(modeline *mode);
+	virtual bool update_mode(modeline *mode);
+
+	virtual bool get_timing(modeline *mode);
+	virtual bool set_timing(modeline *mode);
+
+	virtual bool process_modelist(std::vector<modeline *>);
+
+	// getters
+	bool screen_compositing() { return m_vs.screen_compositing; }
+	bool screen_reordering() { return m_vs.screen_reordering; }
+	bool allow_hardware_refresh() { return m_vs.allow_hardware_refresh; }
+	const char *custom_timing() { return (const char*) &m_vs.custom_timing; }
+
+	// setters
+	void set_screen_compositing(bool value) { m_vs.screen_compositing = value; }
+	void set_screen_reordering(bool value) { m_vs.screen_reordering = value; }
+	void set_allow_hardware_refresh(bool value) { m_vs.allow_hardware_refresh = value; }
+	void set_custom_timing(const char *custom_timing) { strncpy(m_vs.custom_timing, custom_timing, sizeof(m_vs.custom_timing)-1); }
+
+	// options
+	custom_video_settings m_vs = {};
+
+	modeline m_user_mode = {};
+	modeline m_backup_mode = {};
+
+private:
+	char m_device_name[32];
+	char m_device_key[128];
+
+	custom_video *m_custom_video = 0;
+	int m_custom_method;
+};
+
+#endif
diff --git a/3rdparty/switchres/custom_video_adl.cpp b/3rdparty/switchres/custom_video_adl.cpp
new file mode 100644
index 00000000000..baaa3f9a490
--- /dev/null
+++ b/3rdparty/switchres/custom_video_adl.cpp
@@ -0,0 +1,532 @@
+/**************************************************************
+
+    custom_video_adl.cpp - ATI/AMD ADL library
+
+    ---------------------------------------------------------
+
+    Switchres   Modeline generation engine for emulation
+
+    License     GPL-2.0+
+    Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                          Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+//  Constants and structures ported from AMD ADL SDK files
+
+#include <windows.h>
+#include <stdio.h>
+#include "custom_video_adl.h"
+#include "log.h"
+
+
+//============================================================
+//  memory allocation callbacks
+//============================================================
+
+void* __stdcall ADL_Main_Memory_Alloc(int iSize)
+{
+	void* lpBuffer = malloc(iSize);
+	return lpBuffer;
+}
+
+void __stdcall ADL_Main_Memory_Free(void** lpBuffer)
+{
+	if (NULL != *lpBuffer)
+	{
+		free(*lpBuffer);
+		*lpBuffer = NULL;
+	}
+}
+
+//============================================================
+//  adl_timing::adl_timing
+//============================================================
+
+adl_timing::adl_timing(char *display_name, custom_video_settings *vs)
+{
+	m_vs = *vs;
+	strcpy (m_display_name, display_name);
+	strcpy (m_device_key, m_vs.device_reg_key);
+}
+
+//============================================================
+//  adl_timing::~adl_timing
+//============================================================
+
+adl_timing::~adl_timing()
+{
+	close();
+}
+
+//============================================================
+//  adl_timing::init
+//============================================================
+
+bool adl_timing::init()
+{
+	int ADL_Err = ADL_ERR;
+
+	log_verbose("ATI/AMD ADL init\n");
+
+	ADL_Err = open();
+	if (ADL_Err != ADL_OK)
+	{
+		log_verbose("ERROR: ADL Initialization error!\n");
+		return false;
+	}
+
+	ADL2_Adapter_NumberOfAdapters_Get = (ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_NumberOfAdapters_Get");
+	if (ADL2_Adapter_NumberOfAdapters_Get == NULL)
+	{
+		log_verbose("ERROR: ADL2_Adapter_NumberOfAdapters_Get not available!");
+		return false;
+	}
+	ADL2_Adapter_AdapterInfo_Get = (ADL2_ADAPTER_ADAPTERINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_AdapterInfo_Get");
+	if (ADL2_Adapter_AdapterInfo_Get == NULL)
+	{
+		log_verbose("ERROR: ADL2_Adapter_AdapterInfo_Get not available!");
+		return false;
+	}
+	ADL2_Display_DisplayInfo_Get = (ADL2_DISPLAY_DISPLAYINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_DisplayInfo_Get");
+	if (ADL2_Display_DisplayInfo_Get == NULL)
+	{
+		log_verbose("ERROR: ADL2_Display_DisplayInfo_Get not available!");
+		return false;
+	}
+	ADL2_Display_ModeTimingOverride_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Get");
+	if (ADL2_Display_ModeTimingOverride_Get == NULL)
+	{
+		log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Get not available!");
+		return false;
+	}
+	ADL2_Display_ModeTimingOverride_Set = (ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Set");
+	if (ADL2_Display_ModeTimingOverride_Set == NULL)
+	{
+		log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Set not available!");
+		return false;
+	}
+	ADL2_Display_ModeTimingOverrideList_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverrideList_Get");
+	if (ADL2_Display_ModeTimingOverrideList_Get == NULL)
+	{
+		log_verbose("ERROR: ADL2_Display_ModeTimingOverrideList_Get not available!");
+		return false;
+	}
+
+	ADL2_Flush_Driver_Data = (ADL2_FLUSH_DRIVER_DATA) (void *) GetProcAddress(hDLL,"ADL2_Flush_Driver_Data");
+	if (ADL2_Flush_Driver_Data == NULL)
+	{
+		log_verbose("ERROR: ADL2_Flush_Driver_Data not available!");
+		return false;
+	}
+
+	if (!enum_displays())
+	{
+		log_error("ADL error enumerating displays.\n");
+		return false;
+	}
+
+	if (!get_device_mapping_from_display_name())
+	{
+		log_error("ADL error mapping display.\n");
+		return false;
+	}
+
+	if (!get_driver_version(m_device_key))
+	{
+		log_error("ADL driver version unknown!.\n");
+	}
+
+	if (!get_timing_list())
+	{
+		log_error("ADL error getting list of timing overrides.\n");
+	}
+
+	log_verbose("ADL functions retrieved successfully.\n");
+	return true;
+}
+
+//============================================================
+//  adl_timing::adl_open
+//============================================================
+
+int adl_timing::open()
+{
+	ADL2_MAIN_CONTROL_CREATE ADL2_Main_Control_Create;
+	int ADL_Err = ADL_ERR;
+
+	hDLL = LoadLibraryA("atiadlxx.dll");
+	if (hDLL == NULL) hDLL = LoadLibraryA("atiadlxy.dll");
+
+	if (hDLL != NULL)
+	{
+		ADL2_Main_Control_Create = (ADL2_MAIN_CONTROL_CREATE) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Create");
+		if (ADL2_Main_Control_Create != NULL)
+				ADL_Err = ADL2_Main_Control_Create(ADL_Main_Memory_Alloc, 1, &m_adl);
+	}
+	else
+	{
+		log_verbose("ADL Library not found!\n");
+	}
+
+	return ADL_Err;
+}
+
+//============================================================
+//  adl_timing::close
+//============================================================
+
+void adl_timing::close()
+{
+	ADL2_MAIN_CONTROL_DESTROY ADL2_Main_Control_Destroy;
+
+	log_verbose("ATI/AMD ADL close\n");
+
+	for (int i = 0; i <= iNumberAdapters - 1; i++)
+		ADL_Main_Memory_Free((void **)&lpAdapter[i].m_display_list);
+
+	ADL_Main_Memory_Free((void **)&lpAdapterInfo);
+	ADL_Main_Memory_Free((void **)&lpAdapter);
+
+	ADL2_Main_Control_Destroy = (ADL2_MAIN_CONTROL_DESTROY) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Destroy");
+	if (ADL2_Main_Control_Destroy != NULL)
+		ADL2_Main_Control_Destroy(m_adl);
+
+	FreeLibrary(hDLL);
+}
+
+//============================================================
+//  adl_timing::get_driver_version
+//============================================================
+
+bool adl_timing::get_driver_version(char *device_key)
+{
+	HKEY hkey;
+	bool found = false;
+
+	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, device_key, 0, KEY_READ , &hkey) == ERROR_SUCCESS)
+	{
+		BYTE cat_ver[32];
+		DWORD length = sizeof(cat_ver);
+		if ((RegQueryValueExA(hkey, "Catalyst_Version", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
+			(RegQueryValueExA(hkey, "RadeonSoftwareVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
+			(RegQueryValueExA(hkey, "DriverVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS))
+		{
+			found = true;
+			is_patched = (RegQueryValueExA(hkey, "CalamityRelease", NULL, NULL, NULL, NULL) == ERROR_SUCCESS);
+			sscanf((char *)cat_ver, "%d.%d", &cat_version, &sub_version);
+			log_verbose("AMD driver version %d.%d%s\n", cat_version, sub_version, is_patched? "(patched)":"");
+		}
+		RegCloseKey(hkey);
+	}
+	return found;
+}
+
+//============================================================
+//  adl_timing::enum_displays
+//============================================================
+
+bool adl_timing::enum_displays()
+{
+	ADL2_Adapter_NumberOfAdapters_Get(m_adl, &iNumberAdapters);
+
+	lpAdapterInfo = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * iNumberAdapters);
+	memset(lpAdapterInfo, '\0', sizeof(AdapterInfo) * iNumberAdapters);
+	ADL2_Adapter_AdapterInfo_Get(m_adl, lpAdapterInfo, sizeof(AdapterInfo) * iNumberAdapters);
+
+	lpAdapter = (LPAdapterList)malloc(sizeof(AdapterList) * iNumberAdapters);
+	for (int i = 0; i <= iNumberAdapters - 1; i++)
+	{
+		lpAdapter[i].m_index = lpAdapterInfo[i].iAdapterIndex;
+		lpAdapter[i].m_bus   = lpAdapterInfo[i].iBusNumber;
+		memcpy(&lpAdapter[i].m_name, &lpAdapterInfo[i].strAdapterName, ADL_MAX_PATH);
+		memcpy(&lpAdapter[i].m_display_name, &lpAdapterInfo[i].strDisplayName, ADL_MAX_PATH);
+		lpAdapter[i].m_num_of_displays = 0;
+		lpAdapter[i].m_display_list = 0;
+
+		// Only get display info from target adapter (this api is very slow!)
+		if (!strcmp(lpAdapter[i].m_display_name, m_display_name))
+			ADL2_Display_DisplayInfo_Get(m_adl, lpAdapter[i].m_index, &lpAdapter[i].m_num_of_displays, &lpAdapter[i].m_display_list, 1);
+	}
+	return true;
+}
+
+//============================================================
+//  adl_timing::get_device_mapping_from_display_name
+//============================================================
+
+bool adl_timing::get_device_mapping_from_display_name()
+{
+	for (int i = 0; i <= iNumberAdapters -1; i++)
+	{
+		if (!strcmp(m_display_name, lpAdapter[i].m_display_name))
+		{
+			ADLDisplayInfo *display_list;
+			display_list = lpAdapter[i].m_display_list;
+
+			for (int j = 0; j <= lpAdapter[i].m_num_of_displays - 1; j++)
+			{
+				if (lpAdapter[i].m_index == display_list[j].displayID.iDisplayLogicalAdapterIndex)
+				{
+					m_adapter_index = lpAdapter[i].m_index;
+					m_display_index = display_list[j].displayID.iDisplayLogicalIndex;
+					return true;
+				}
+			}
+		}
+	}
+	return false;
+}
+
+//============================================================
+//  adl_timing::display_mode_info_to_modeline
+//============================================================
+
+bool adl_timing::display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m)
+{
+	if (dmi->sDetailedTiming.sHTotal == 0) return false;
+
+	ADLDetailedTiming dt;
+	memcpy(&dt, &dmi->sDetailedTiming, sizeof(ADLDetailedTiming));
+
+	if (dt.sHTotal == 0) return false;
+
+	m->htotal    = dt.sHTotal;
+	m->hactive   = dt.sHDisplay;
+	m->hbegin    = dt.sHSyncStart;
+	m->hend      = dt.sHSyncWidth + m->hbegin;
+	m->vtotal    = dt.sVTotal;
+	m->vactive   = dt.sVDisplay;
+	m->vbegin    = dt.sVSyncStart;
+	m->vend      = dt.sVSyncWidth + m->vbegin;
+	m->interlace = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED)? 1 : 0;
+	m->doublescan = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_DOUBLE_SCAN)? 1 : 0;
+	m->hsync     = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_H_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1);
+	m->vsync     = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_V_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1) ;
+	m->pclock    = dt.sPixelClock * 10000;
+
+	m->height  = m->height? m->height : dmi->iPelsHeight;
+	m->width   = m->width? m->width : dmi->iPelsWidth;
+	m->refresh = m->refresh? m->refresh : dmi->iRefreshRate / interlace_factor(m->interlace, 1);;
+	m->hfreq = float(m->pclock / m->htotal);
+	m->vfreq = float(m->hfreq / m->vtotal) * (m->interlace? 2 : 1);
+
+	return true;
+}
+
+//============================================================
+//  adl_timing::get_timing_list
+//============================================================
+
+bool adl_timing::get_timing_list()
+{
+	if (ADL2_Display_ModeTimingOverrideList_Get(m_adl, m_adapter_index, m_display_index, MAX_MODELINES, adl_mode, &m_num_of_adl_modes) != ADL_OK) return false;
+
+	return true;
+}
+
+//============================================================
+//  adl_timing::get_timing_from_cache
+//============================================================
+
+bool adl_timing::get_timing_from_cache(modeline *m)
+{
+	ADLDisplayModeInfo *mode = 0;
+
+	for (int i = 0; i < m_num_of_adl_modes; i++)
+	{
+		mode = &adl_mode[i];
+		if (mode->iPelsWidth == m->width && mode->iPelsHeight == m->height && mode->iRefreshRate == m->refresh)
+		{
+			if ((m->interlace) && !(mode->sDetailedTiming.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED))
+				continue;
+			goto found;
+		}
+	}
+
+	return false;
+
+	found:
+	if (display_mode_info_to_modeline(mode, m)) return true;
+
+	return false;
+}
+
+//============================================================
+//  adl_timing::get_timing
+//============================================================
+
+bool adl_timing::get_timing(modeline *m)
+{
+	ADLDisplayMode mode_in;
+	ADLDisplayModeInfo mode_info_out;
+	modeline m_temp = *m;
+
+	//modeline to ADLDisplayMode
+	mode_in.iPelsHeight       = m->height;
+	mode_in.iPelsWidth        = m->width;
+	mode_in.iBitsPerPel       = 32;
+	mode_in.iDisplayFrequency = m->refresh * interlace_factor(m->interlace, 1);
+
+	if (ADL2_Display_ModeTimingOverride_Get(m_adl, m_adapter_index, m_display_index, &mode_in, &mode_info_out) != ADL_OK) goto not_found;
+	if (display_mode_info_to_modeline(&mode_info_out, &m_temp))
+	{
+		if (m_temp.interlace == m->interlace)
+		{
+			memcpy(m, &m_temp, sizeof(modeline));
+			m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
+			return true;
+		}
+	}
+
+	not_found:
+
+	// Try to get timing from our cache (interlaced modes are not properly retrieved by ADL_Display_ModeTimingOverride_Get)
+	if (get_timing_from_cache(m))
+	{
+		m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
+		return true;
+	}
+
+	return false;
+}
+
+//============================================================
+//  adl_timing::set_timing
+//============================================================
+
+bool adl_timing::set_timing(modeline *m)
+{
+	return set_timing_override(m, TIMING_UPDATE);
+}
+
+//============================================================
+//  adl_timing::set_timing_override
+//============================================================
+
+bool adl_timing::set_timing_override(modeline *m, int update_mode)
+{
+	ADLDisplayModeInfo mode_info = {};
+	ADLDetailedTiming *dt;
+	modeline m_temp;
+
+	//modeline to ADLDisplayModeInfo
+	mode_info.iTimingStandard   = (update_mode & TIMING_DELETE)? ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT : ADL_DL_MODETIMING_STANDARD_CUSTOM;
+	mode_info.iPossibleStandard = 0;
+	mode_info.iRefreshRate      = m->refresh * interlace_factor(m->interlace, 0);
+	mode_info.iPelsWidth        = m->width;
+	mode_info.iPelsHeight       = m->height;
+
+	//modeline to ADLDetailedTiming
+	dt = &mode_info.sDetailedTiming;
+	dt->sTimingFlags     = (m->interlace? ADL_DL_TIMINGFLAG_INTERLACED : 0) |
+						   (m->doublescan? ADL_DL_TIMINGFLAG_DOUBLE_SCAN: 0) |
+						   (m->hsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_H_SYNC_POLARITY : 0) |
+						   (m->vsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_V_SYNC_POLARITY : 0);
+	dt->sHTotal          = m->htotal;
+	dt->sHDisplay        = m->hactive;
+	dt->sHSyncStart      = m->hbegin;
+	dt->sHSyncWidth      = m->hend - m->hbegin;
+	dt->sVTotal          = m->vtotal;
+	dt->sVDisplay        = m->vactive;
+	dt->sVSyncStart      = m->vbegin;
+	dt->sVSyncWidth      = m->vend - m->vbegin;
+	dt->sPixelClock      = m->pclock / 10000;
+	dt->sHOverscanRight  = 0;
+	dt->sHOverscanLeft   = 0;
+	dt->sVOverscanBottom = 0;
+	dt->sVOverscanTop    = 0;
+
+	if (ADL2_Display_ModeTimingOverride_Set(m_adl, m_adapter_index, m_display_index, &mode_info, (update_mode & TIMING_UPDATE_LIST)? 1 : 0) != ADL_OK) return false;
+
+	//ADL2_Flush_Driver_Data(m_adl, m_adapter_index);
+
+	// read modeline to trigger timing refresh on modded drivers
+	memcpy(&m_temp, m, sizeof(modeline));
+	if (update_mode & TIMING_UPDATE) get_timing(&m_temp);
+
+	return true;
+}
+
+//============================================================
+//  adl_timing::add_mode
+//============================================================
+
+bool adl_timing::add_mode(modeline *mode)
+{
+	if (!set_timing_override(mode, TIMING_UPDATE_LIST))
+	{
+		return false;
+	}
+
+	m_resync.wait();
+	mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
+
+	return true;
+}
+
+//============================================================
+//  adl_timing::delete_mode
+//============================================================
+
+bool adl_timing::delete_mode(modeline *mode)
+{
+	if (!set_timing_override(mode, TIMING_DELETE | TIMING_UPDATE_LIST))
+	{
+		return false;
+	}
+
+	m_resync.wait();
+
+	return true;
+}
+
+//============================================================
+//  adl_timing::update_mode
+//============================================================
+
+bool adl_timing::update_mode(modeline *mode)
+{
+	bool refresh_required = !is_patched || (mode->type & MODE_DESKTOP);
+
+	if (!set_timing_override(mode, refresh_required? TIMING_UPDATE_LIST : TIMING_UPDATE))
+	{
+		return false;
+	}
+
+	if (refresh_required) m_resync.wait();
+	mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
+	return true;
+}
+
+//============================================================
+//  adl_timing::process_modelist
+//============================================================
+
+bool adl_timing::process_modelist(std::vector<modeline *> modelist)
+{
+	bool refresh_required = false;
+	bool error = false;
+
+	for (auto &mode : modelist)
+	{
+		if (mode->type & MODE_DELETE || mode->type & MODE_ADD || (mode->type & MODE_UPDATE && (!is_patched || (mode->type & MODE_DESKTOP))))
+			refresh_required = true;
+
+		bool is_last = (mode == modelist.back());
+
+		if (!set_timing_override(mode, (mode->type & MODE_DELETE? TIMING_DELETE : TIMING_UPDATE) | (is_last && refresh_required? TIMING_UPDATE_LIST : 0)))
+		{
+			mode->type |= MODE_ERROR;
+			error = true;
+		}
+		else
+		{
+			mode->type &= ~MODE_ERROR;
+			mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
+		}
+	}
+
+	if (refresh_required) m_resync.wait();
+	return !error;
+}
diff --git a/3rdparty/switchres/custom_video_adl.h b/3rdparty/switchres/custom_video_adl.h
new file mode 100644
index 00000000000..f4513a3de15
--- /dev/null
+++ b/3rdparty/switchres/custom_video_adl.h
@@ -0,0 +1,201 @@
+/**************************************************************
+
+    custom_video_adl.h - ATI/AMD ADL library header
+
+    ---------------------------------------------------------
+
+    Switchres   Modeline generation engine for emulation
+
+    License     GPL-2.0+
+    Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                          Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <windows.h>
+#include "custom_video.h"
+#include "resync_windows.h"
+
+//  Constants and structures ported from AMD ADL SDK files
+#define ADL_MAX_PATH   256
+#define ADL_OK           0
+#define ADL_ERR         -1
+
+//ADL_DETAILED_TIMING.sTimingFlags
+#define ADL_DL_TIMINGFLAG_DOUBLE_SCAN               0x0001
+#define ADL_DL_TIMINGFLAG_INTERLACED                0x0002
+#define ADL_DL_TIMINGFLAG_H_SYNC_POLARITY           0x0004
+#define ADL_DL_TIMINGFLAG_V_SYNC_POLARITY           0x0008
+
+//ADL_DISPLAY_MODE_INFO.iTimingStandard
+#define ADL_DL_MODETIMING_STANDARD_CVT              0x00000001 // CVT Standard
+#define ADL_DL_MODETIMING_STANDARD_GTF              0x00000002 // GFT Standard
+#define ADL_DL_MODETIMING_STANDARD_DMT              0x00000004 // DMT Standard
+#define ADL_DL_MODETIMING_STANDARD_CUSTOM           0x00000008 // User-defined standard
+#define ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT   0x00000010 // Remove Mode from overriden list
+#define ADL_DL_MODETIMING_STANDARD_CVT_RB           0x00000020 // CVT-RB Standard
+
+typedef struct AdapterInfo
+{
+	int iSize;
+	int iAdapterIndex;
+	char strUDID[ADL_MAX_PATH];
+	int iBusNumber;
+	int iDeviceNumber;
+	int iFunctionNumber;
+	int iVendorID;
+	char strAdapterName[ADL_MAX_PATH];
+	char strDisplayName[ADL_MAX_PATH];
+	int iPresent;
+	int iExist;
+	char strDriverPath[ADL_MAX_PATH];
+	char strDriverPathExt[ADL_MAX_PATH];
+	char strPNPString[ADL_MAX_PATH];
+	int iOSDisplayIndex;
+} AdapterInfo, *LPAdapterInfo;
+
+typedef struct ADLDisplayID
+{
+	int iDisplayLogicalIndex;
+	int iDisplayPhysicalIndex;
+	int iDisplayLogicalAdapterIndex;
+	int iDisplayPhysicalAdapterIndex;
+} ADLDisplayID, *LPADLDisplayID;
+
+
+typedef struct ADLDisplayInfo
+{
+	ADLDisplayID displayID;
+	int iDisplayControllerIndex;
+	char strDisplayName[ADL_MAX_PATH];
+	char strDisplayManufacturerName[ADL_MAX_PATH];
+	int iDisplayType;
+	int iDisplayOutputType;
+	int iDisplayConnector;
+	int iDisplayInfoMask;
+	int iDisplayInfoValue;
+} ADLDisplayInfo, *LPADLDisplayInfo;
+
+typedef struct ADLDisplayMode
+{
+	int iPelsHeight;
+	int iPelsWidth;
+	int iBitsPerPel;
+	int iDisplayFrequency;
+} ADLDisplayMode;
+
+typedef struct ADLDetailedTiming
+{
+	int   iSize;
+	short sTimingFlags;
+	short sHTotal;
+	short sHDisplay;
+	short sHSyncStart;
+	short sHSyncWidth;
+	short sVTotal;
+	short sVDisplay;
+	short sVSyncStart;
+	short sVSyncWidth;
+	unsigned short sPixelClock;
+	short sHOverscanRight;
+	short sHOverscanLeft;
+	short sVOverscanBottom;
+	short sVOverscanTop;
+	short sOverscan8B;
+	short sOverscanGR;
+} ADLDetailedTiming;
+
+typedef struct ADLDisplayModeInfo
+{
+	int iTimingStandard;
+	int iPossibleStandard;
+	int iRefreshRate;
+	int iPelsWidth;
+	int iPelsHeight;
+	ADLDetailedTiming sDetailedTiming;
+} ADLDisplayModeInfo;
+
+typedef struct AdapterList
+{
+	int m_index;
+	int m_bus;
+	char m_name[ADL_MAX_PATH];
+	char m_display_name[ADL_MAX_PATH];
+	int m_num_of_displays;
+	ADLDisplayInfo *m_display_list;
+} AdapterList, *LPAdapterList;
+
+
+typedef void* ADL_CONTEXT_HANDLE;
+typedef void* (__stdcall *ADL_MAIN_MALLOC_CALLBACK)(int);
+typedef int (*ADL2_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int,  ADL_CONTEXT_HANDLE *);
+typedef int (*ADL2_MAIN_CONTROL_DESTROY)(ADL_CONTEXT_HANDLE);
+typedef int (*ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (ADL_CONTEXT_HANDLE, int*);
+typedef int (*ADL2_ADAPTER_ADAPTERINFO_GET) (ADL_CONTEXT_HANDLE, LPAdapterInfo, int);
+typedef int (*ADL2_DISPLAY_DISPLAYINFO_GET) (ADL_CONTEXT_HANDLE, int, int *, ADLDisplayInfo **, int);
+typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, ADLDisplayMode *lpModeIn, ADLDisplayModeInfo *lpModeInfoOut);
+typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, ADLDisplayModeInfo *lpMode, int iForceUpdate);
+typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, int iMaxNumOfOverrides, ADLDisplayModeInfo *lpModeInfoList, int *lpNumOfOverrides);
+typedef int (*ADL2_FLUSH_DRIVER_DATA) (ADL_CONTEXT_HANDLE, int iAdapterIndex);
+
+
+class adl_timing : public custom_video
+{
+	public:
+		adl_timing(char *display_name, custom_video_settings *vs);
+		~adl_timing();
+		const char *api_name() { return "AMD ADL"; }
+		bool init();
+		void close();
+		int caps() { return allow_hardware_refresh()? CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_ADD | CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE : is_patched? CUSTOM_VIDEO_CAPS_UPDATE : 0; }
+
+		bool add_mode(modeline *mode);
+		bool delete_mode(modeline *mode);
+		bool update_mode(modeline *mode);
+
+		bool get_timing(modeline *m);
+		bool set_timing(modeline *m);
+
+		bool process_modelist(std::vector<modeline *>);
+
+	private:
+		int open();
+		bool get_driver_version(char *device_key);
+		bool enum_displays();
+		bool get_device_mapping_from_display_name();
+		bool display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m);
+		bool get_timing_list();
+		bool get_timing_from_cache(modeline *m);
+		bool set_timing_override(modeline *m, int update_mode);
+
+		char m_display_name[32];
+		char m_device_key[128];
+
+		int m_adapter_index = 0;
+		int m_display_index = 0;
+
+		ADL2_ADAPTER_NUMBEROFADAPTERS_GET        ADL2_Adapter_NumberOfAdapters_Get;
+		ADL2_ADAPTER_ADAPTERINFO_GET             ADL2_Adapter_AdapterInfo_Get;
+		ADL2_DISPLAY_DISPLAYINFO_GET             ADL2_Display_DisplayInfo_Get;
+		ADL2_DISPLAY_MODETIMINGOVERRIDE_GET      ADL2_Display_ModeTimingOverride_Get;
+		ADL2_DISPLAY_MODETIMINGOVERRIDE_SET      ADL2_Display_ModeTimingOverride_Set;
+		ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET  ADL2_Display_ModeTimingOverrideList_Get;
+		ADL2_FLUSH_DRIVER_DATA                   ADL2_Flush_Driver_Data;
+
+		HINSTANCE hDLL;
+		LPAdapterInfo lpAdapterInfo = NULL;
+		LPAdapterList lpAdapter = NULL;;
+		int iNumberAdapters = 0;
+		int cat_version = 0;
+		int sub_version = 0;
+		bool is_patched = false;
+
+		ADL_CONTEXT_HANDLE m_adl = 0;
+		ADLDisplayModeInfo adl_mode[MAX_MODELINES];
+		int m_num_of_adl_modes = 0;
+
+		resync_handler m_resync;
+
+		int invert_pol(bool on_read) { return ((cat_version <= 12) || (cat_version >= 15 && on_read)); }
+		int interlace_factor(bool interlace, bool on_read) { return interlace && ((cat_version <= 12) || (cat_version >= 15 && on_read))? 2 : 1; }
+};
diff --git a/3rdparty/switchres/custom_video_ati.cpp b/3rdparty/switchres/custom_video_ati.cpp
new file mode 100644
index 00000000000..17aa9610acf
--- /dev/null
+++ b/3rdparty/switchres/custom_video_ati.cpp
@@ -0,0 +1,343 @@
+/**************************************************************
+
+   custom_video_ati.cpp - ATI legacy library
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <windows.h>
+#include <stdio.h>
+#include "custom_video_ati.h"
+#include "log.h"
+
+
+//============================================================
+//  ati_timing::ati_timing
+//============================================================
+
+ati_timing::ati_timing(char *device_name, custom_video_settings *vs)
+{
+	m_vs = *vs;
+	strcpy (m_device_name, device_name);
+	strcpy (m_device_key, m_vs.device_reg_key);
+}
+
+//============================================================
+//  ati_timing::ati_timing
+//============================================================
+
+bool ati_timing::init()
+{
+	log_verbose("ATI legacy init\n");
+
+	// Get Windows version
+	win_version = os_version();
+
+	if (win_version > 5 && !is_elevated())
+	{
+		log_error("ATI legacy error: the program needs administrator rights.\n");
+		return false;
+	}
+
+	return true;
+}
+
+//============================================================
+//  ati_timing::get_timing
+//============================================================
+
+bool ati_timing::get_timing(modeline *mode)
+{
+	HKEY hKey;
+	char lp_name[1024];
+	char lp_data[68];
+	DWORD length;
+	bool found = false;
+	int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
+	int vfreq_incr = 0;
+
+	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+	{
+		sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
+		length = sizeof(lp_data);
+
+		if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
+			found = true;
+		else if (win_version > 5 && mode->interlace)
+		{
+			vfreq_incr = 1;
+			sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
+			if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
+				found = true;
+		}
+		if (found)
+		{
+			mode->pclock  = get_DWORD_BCD(36, lp_data) * 10000;
+			mode->hactive = get_DWORD_BCD(8, lp_data);
+			mode->hbegin  = get_DWORD_BCD(12, lp_data);
+			mode->hend    = get_DWORD_BCD(16, lp_data) + mode->hbegin;
+			mode->htotal  = get_DWORD_BCD(4, lp_data);
+			mode->vactive = get_DWORD_BCD(24, lp_data);
+			mode->vbegin  = get_DWORD_BCD(28, lp_data);
+			mode->vend    = get_DWORD_BCD(32, lp_data) + mode->vbegin;
+			mode->vtotal  = get_DWORD_BCD(20, lp_data);
+			mode->interlace = (get_DWORD(0, lp_data) & CRTC_INTERLACED)?1:0;
+			mode->hsync     = (get_DWORD(0, lp_data) & CRTC_H_SYNC_POLARITY)?0:1;
+			mode->vsync     = (get_DWORD(0, lp_data) & CRTC_V_SYNC_POLARITY)?0:1;
+			mode->hfreq = mode->pclock / mode->htotal;
+			mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
+			mode->refresh_label = refresh_label;
+			mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
+
+			int checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
+						- mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
+			if (checksum != get_DWORD(64, lp_data))
+				log_verbose("bad checksum! ");
+		}
+		RegCloseKey(hKey);
+		return (found);
+	}
+	log_verbose("Failed opening registry entry for mode. ");
+	return false;
+}
+
+//============================================================
+//  ati_timing::set_timing
+//============================================================
+
+bool ati_timing::set_timing(modeline *mode)
+{
+	HKEY hKey;
+	char lp_name[1024];
+	char lp_data[68];
+	long checksum;
+	bool found = false;
+	int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
+	int vfreq_incr = 0;
+
+	memset(lp_data, 0, sizeof(lp_data));
+	set_DWORD_BCD(lp_data, (int)mode->pclock/10000, 36);
+	set_DWORD_BCD(lp_data, mode->hactive, 8);
+	set_DWORD_BCD(lp_data, mode->hbegin, 12);
+	set_DWORD_BCD(lp_data, mode->hend - mode->hbegin, 16);
+	set_DWORD_BCD(lp_data, mode->htotal, 4);
+	set_DWORD_BCD(lp_data, mode->vactive, 24);
+	set_DWORD_BCD(lp_data, mode->vbegin, 28);
+	set_DWORD_BCD(lp_data, mode->vend - mode->vbegin, 32);
+	set_DWORD_BCD(lp_data, mode->vtotal, 20);
+	set_DWORD(lp_data, (mode->interlace?CRTC_INTERLACED:0) | (mode->hsync?0:CRTC_H_SYNC_POLARITY) | (mode->vsync?0:CRTC_V_SYNC_POLARITY), 0);
+
+	checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
+			- mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
+	set_DWORD(lp_data, checksum, 64);
+
+	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+	{
+		sprintf (lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
+
+		if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+			found = true;
+		else if (win_version > 5 && mode->interlace)
+		{
+			vfreq_incr = 1;
+			sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
+			if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
+				found = true;
+		}
+
+		if (!(found && RegSetValueExA(hKey, lp_name, 0, REG_BINARY, (LPBYTE)lp_data, 68) == ERROR_SUCCESS))
+			log_info("Failed saving registry entry %s\n", lp_name);
+
+		RegCloseKey(hKey);
+		return (found);
+	}
+
+	log_info("Failed updating registry entry for mode.\n");
+	return 0;
+}
+
+//============================================================
+//  ati_timing::update_mode
+//============================================================
+
+bool ati_timing::update_mode(modeline *mode)
+{
+	if (!set_timing(mode))
+		return false;
+
+	mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
+
+	// ATI needs a call to EnumDisplaySettings to refresh timings
+	refresh_timings();
+
+	return true;
+}
+
+//============================================================
+//  ati_refresh_timings
+//============================================================
+
+void ati_timing::refresh_timings(void)
+{
+	int iModeNum = 0;
+	DEVMODEA lpDevMode;
+
+	memset(&lpDevMode, 0, sizeof(DEVMODEA));
+	lpDevMode.dmSize = sizeof(DEVMODEA);
+
+	while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, 0) != 0)
+		iModeNum++;
+}
+
+//============================================================
+//  adl_timing::process_modelist
+//============================================================
+
+bool ati_timing::process_modelist(std::vector<modeline *> modelist)
+{
+	bool error = false;
+
+	for (auto &mode : modelist)
+	{
+		if (!set_timing(mode))
+		{
+			mode->type |= MODE_ERROR;
+			error = true;
+		}
+		else
+		{
+			mode->type &= ~MODE_ERROR;
+			mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
+		}
+	}
+
+	refresh_timings();
+	return !error;
+}
+
+//============================================================
+// get_DWORD
+//============================================================
+
+int ati_timing::get_DWORD(int i, char *lp_data)
+{
+	char out[32] = "";
+	UINT32 x;
+
+	sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
+	sscanf(out, "%08X", &x);
+	return x;
+}
+
+//============================================================
+// get_DWORD_BCD
+//============================================================
+
+int ati_timing::get_DWORD_BCD(int i, char *lp_data)
+{
+	char out[32] = "";
+	UINT32 x;
+
+	sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
+	sscanf(out, "%d", &x);
+	return x;
+}
+
+//============================================================
+// set_DWORD
+//============================================================
+
+void ati_timing::set_DWORD(char *data_string, UINT32 data_dword, int offset)
+{
+	char *p_dword = (char*)&data_dword;
+
+	data_string[offset]   = p_dword[3]&0xFF;
+	data_string[offset+1] = p_dword[2]&0xFF;
+	data_string[offset+2] = p_dword[1]&0xFF;
+	data_string[offset+3] = p_dword[0]&0xFF;
+}
+
+//============================================================
+// set_DWORD_BCD
+//============================================================
+
+void ati_timing::set_DWORD_BCD(char *data_string, UINT32 data_dword, int offset)
+{
+	if (data_dword < 100000000)
+	{
+		int low_word, high_word;
+		int a, b, c, d;
+		char out[32] = "";
+
+		low_word = data_dword % 10000;
+		high_word = data_dword / 10000;
+
+		sprintf(out, "%d %d %d %d", high_word / 100, high_word % 100 , low_word / 100, low_word % 100);
+		sscanf(out, "%02X %02X %02X %02X", &a, &b, &c, &d);
+
+		data_string[offset]   = a;
+		data_string[offset+1] = b;
+		data_string[offset+2] = c;
+		data_string[offset+3] = d;
+	}
+}
+
+//============================================================
+// os_version
+//============================================================
+
+int ati_timing::os_version(void)
+{
+	OSVERSIONINFOA lpVersionInfo;
+
+	memset(&lpVersionInfo, 0, sizeof(OSVERSIONINFOA));
+	lpVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+	GetVersionExA (&lpVersionInfo);
+
+	return lpVersionInfo.dwMajorVersion;
+}
+
+//============================================================
+//  is_elevated
+//============================================================
+
+bool ati_timing::is_elevated()
+{
+	HANDLE htoken;
+	bool result = false;
+
+	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken))
+		return false;
+
+	TOKEN_ELEVATION te = {0};
+	DWORD dw_return_length;
+
+	if (GetTokenInformation(htoken, TokenElevation, &te, sizeof(te), &dw_return_length))
+	{
+		if (te.TokenIsElevated)
+		{
+			result = true;
+		}
+	}
+
+	CloseHandle(htoken);
+	return (result);
+}
+
+//============================================================
+// win_interlace_factor
+//============================================================
+
+int ati_timing::win_interlace_factor(modeline *mode)
+{
+	if (win_version > 5 && mode->interlace)
+		return 2;
+
+	return 1;
+}
diff --git a/3rdparty/switchres/custom_video_ati.h b/3rdparty/switchres/custom_video_ati.h
new file mode 100644
index 00000000000..17b5bc1fc46
--- /dev/null
+++ b/3rdparty/switchres/custom_video_ati.h
@@ -0,0 +1,53 @@
+/**************************************************************
+
+    custom_video_ati.h - ATI legacy library header
+
+    ---------------------------------------------------------
+
+    Switchres   Modeline generation engine for emulation
+
+    License     GPL-2.0+
+    Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                          Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <windows.h>
+#include "custom_video.h"
+
+#define CRTC_DOUBLE_SCAN                    0x0001
+#define CRTC_INTERLACED                     0x0002
+#define CRTC_H_SYNC_POLARITY                0x0004
+#define CRTC_V_SYNC_POLARITY                0x0008
+
+class ati_timing : public custom_video
+{
+	public:
+		ati_timing(char *device_name, custom_video_settings *vs);
+		~ati_timing() {};
+		const char *api_name() { return "ATI Legacy"; }
+		bool init();
+		int caps() { return CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_SCAN_EDITABLE; }
+
+		bool update_mode(modeline *mode);
+
+		bool get_timing(modeline *mode);
+		bool set_timing(modeline *mode);
+
+		bool process_modelist(std::vector<modeline *>);
+
+	private:
+		void refresh_timings(void);
+
+		int get_DWORD(int i, char *lp_data);
+		int get_DWORD_BCD(int i, char *lp_data);
+		void set_DWORD(char *data_string, UINT32 data_word, int offset);
+		void set_DWORD_BCD(char *data_string, UINT32 data_word, int offset);
+		int os_version(void);
+		bool is_elevated();
+		int win_interlace_factor(modeline *mode);
+
+		char m_device_name[32];
+		char m_device_key[256];
+		int win_version;
+};
diff --git a/3rdparty/switchres/custom_video_ati_family.cpp b/3rdparty/switchres/custom_video_ati_family.cpp
new file mode 100644
index 00000000000..220364bbbcb
--- /dev/null
+++ b/3rdparty/switchres/custom_video_ati_family.cpp
@@ -0,0 +1,848 @@
+/**************************************************************
+
+   custom_video_ati_family.cpp - ATI/AMD Radeon family
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+/*  Constants and structures ported from Linux open source drivers:
+    drivers\gpu\drm\radeon\radeon.h
+    drivers\gpu\drm\radeon\radeon_family.h
+    include\drm\drm_pciids.h
+*/
+
+#ifndef RADEON_FAMILY_H
+#define RADEON_FAMILY_H
+
+struct pci_device_id
+{
+	int vendor, device;
+	int subvendor, subdevice;
+	int _class, _class_mask;
+	int driver_data;
+};
+
+enum radeon_family
+{
+	CHIP_R100 = 0,
+	CHIP_RV100,
+	CHIP_RS100,
+	CHIP_RV200,
+	CHIP_RS200,
+	CHIP_R200,
+	CHIP_RV250,
+	CHIP_RS300,
+	CHIP_RV280,
+	CHIP_R300,
+	CHIP_R350,
+	CHIP_RV350,
+	CHIP_RV380,
+	CHIP_R420,
+	CHIP_R423,
+	CHIP_RV410,
+	CHIP_RS400,
+	CHIP_RS480,
+	CHIP_RS600,
+	CHIP_RS690,
+	CHIP_RS740,
+	CHIP_RV515,
+	CHIP_R520,
+	CHIP_RV530,
+	CHIP_RV560,
+	CHIP_RV570,
+	CHIP_R580,
+	CHIP_R600,
+	CHIP_RV610,
+	CHIP_RV630,
+	CHIP_RV670,
+	CHIP_RV620,
+	CHIP_RV635,
+	CHIP_RS780,
+	CHIP_RS880,
+	CHIP_RV770,
+	CHIP_RV730,
+	CHIP_RV710,
+	CHIP_RV740,
+	CHIP_CEDAR,
+	CHIP_REDWOOD,
+	CHIP_JUNIPER,
+	CHIP_CYPRESS,
+	CHIP_HEMLOCK,
+	CHIP_PALM,
+	CHIP_SUMO,
+	CHIP_SUMO2,
+	CHIP_BARTS,
+	CHIP_TURKS,
+	CHIP_CAICOS,
+	CHIP_CAYMAN,
+	CHIP_ARUBA,
+	CHIP_TAHITI,
+	CHIP_PITCAIRN,
+	CHIP_VERDE,
+	CHIP_OLAND,
+	CHIP_HAINAN,
+	CHIP_BONAIRE,
+	CHIP_KAVERI,
+	CHIP_KABINI,
+	CHIP_HAWAII,
+	CHIP_MULLINS,
+	CHIP_LAST,
+};
+
+enum radeon_chip_flags
+{
+	RADEON_FAMILY_MASK = 0x0000ffffUL,
+	RADEON_FLAGS_MASK  = 0xffff0000UL,
+	RADEON_IS_MOBILITY = 0x00010000UL,
+	RADEON_IS_IGP      = 0x00020000UL,
+	RADEON_SINGLE_CRTC = 0x00040000UL,
+	RADEON_IS_AGP      = 0x00080000UL,
+	RADEON_HAS_HIERZ   = 0x00100000UL,
+	RADEON_IS_PCIE     = 0x00200000UL,
+	RADEON_NEW_MEMMAP  = 0x00400000UL,
+	RADEON_IS_PCI      = 0x00800000UL,
+	RADEON_IS_IGPGART  = 0x01000000UL,
+	RADEON_IS_PX       = 0x02000000UL,
+};
+
+#define PCI_ANY_ID (~0)
+
+#define radeon_PCI_IDS \
+	{0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x3151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x3155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \
+	{0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
+	{0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x4149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x414A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x414B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x4150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+	{0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
+	{0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
+	{0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
+	{0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
+	{0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4B48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4C6E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4E47, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+	{0x1002, 0x4E48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+	{0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+	{0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+	{0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+	{0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+	{0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
+	{0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
+	{0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
+	{0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
+	{0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
+	{0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
+	{0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
+	{0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
+	{0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5657, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \
+	{0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5954, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5955, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5974, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+	{0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+	{0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+	{0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+	{0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+	{0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
+	{0x1002, 0x5a41, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5a42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5a61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5a62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
+	{0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
+	{0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6665, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6667, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x666F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6703, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6704, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6707, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6718, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6719, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x671c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x671d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x671f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6721, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6722, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6725, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6726, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6728, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6743, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6745, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6746, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6747, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6748, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x674A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6758, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6759, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x675B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x675D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x675F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6761, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6763, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6764, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6765, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6766, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6767, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6768, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6770, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6771, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6778, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6779, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x677B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6784, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x678A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6790, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6791, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6792, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6798, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6799, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x679A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6823, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6826, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6827, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x683B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x683D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x683F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6842, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6843, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6849, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x684C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6888, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6889, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x688A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x688C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68a0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68e1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68e5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68e8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68e9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68f1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68f8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68f9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68fa, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x68fe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x710A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x710B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x710C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x710E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x710F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7140, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7141, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7142, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7143, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x714A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x714B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x714C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x714D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x714E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x714F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x715E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x715F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7183, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7187, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7188, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x718A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x718B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x718C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x718D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x718F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7193, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x719B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x719F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71D2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71D5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71D6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x71DE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7244, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7245, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7247, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7248, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x724A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x724B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x724C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x724D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x724E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x724F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7280, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7283, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7284, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x728B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x728C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7290, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7291, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7293, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7297, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x791e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x791f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x793f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7941, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x7942, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x796c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x796d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x796e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x796f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
+	{0x1002, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9402, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9403, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x940A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x940B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x940F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94B3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94B5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9442, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9444, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x944A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x944B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x944C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x944E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9452, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9456, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x945A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x945B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x945E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x946A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x946B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x947A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x947B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9487, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9488, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9489, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x948A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x948F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9490, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9491, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9495, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9498, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x949C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x949E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x949F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94CB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x94CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9504, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9505, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9506, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9507, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9508, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9509, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x950F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9517, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9540, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9542, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x954E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x954F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9553, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9555, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x955f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9580, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9581, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9583, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9586, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9587, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9588, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9589, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x958A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x958B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x958C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x958D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x958E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x958F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9590, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9593, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9595, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9596, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9597, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9599, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x959B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x95CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
+	{0x1002, 0x9610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9616, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9642, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
+	{0x1002, 0x9648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
+	{0x1002, 0x9649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
+	{0x1002, 0x964a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x964b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x964c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x964e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
+	{0x1002, 0x964f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
+	{0x1002, 0x9710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9714, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9715, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9804, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9807, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x980A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9904, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9905, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9906, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9907, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9908, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9909, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x990A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x990B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x990C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x990D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x990E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x990F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9910, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9913, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9917, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9918, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9990, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9991, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9993, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9994, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9996, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9998, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x9999, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x999A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x999B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x999C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x999D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x99A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x99A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0x1002, 0x99A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+	{0, 0, 0, 0, 0, 0, 0}
+
+static struct pci_device_id pciidlist[] = {radeon_PCI_IDS};
+
+//============================================================
+// ati_family
+//============================================================
+
+int ati_family(int vendor, int device)
+{
+	int i = 0;
+	while (pciidlist[i].vendor)
+	{
+		if (pciidlist[i].vendor == vendor && pciidlist[i].device == device)
+			return (pciidlist[i].driver_data & RADEON_FAMILY_MASK);
+		i++;
+	}
+	// Not found, must be newer
+	if (vendor == 0x1002)
+		return CHIP_LAST;
+
+	return 0;
+}
+
+//============================================================
+// ati_is_legacy
+//============================================================
+
+bool ati_is_legacy(int vendor, int device)
+{
+	return (ati_family(vendor, device) < CHIP_CEDAR);
+}
+
+#endif
diff --git a/3rdparty/switchres/custom_video_drmkms.cpp b/3rdparty/switchres/custom_video_drmkms.cpp
new file mode 100755
index 00000000000..c7c2d0a3b16
--- /dev/null
+++ b/3rdparty/switchres/custom_video_drmkms.cpp
@@ -0,0 +1,1328 @@
+/**************************************************************
+
+   custom_video_drmkms.cpp - Linux DRM/KMS video management layer
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include "custom_video_drmkms.h"
+#include "log.h"
+
+#define drmGetVersion p_drmGetVersion
+#define drmFreeVersion p_drmFreeVersion
+#define drmModeGetResources p_drmModeGetResources
+#define drmModeGetConnector p_drmModeGetConnector
+#define drmModeGetConnectorCurrent p_drmModeGetConnectorCurrent
+#define drmModeFreeConnector p_drmModeFreeConnector
+#define drmModeFreeResources p_drmModeFreeResources
+#define drmModeGetEncoder p_drmModeGetEncoder
+#define drmModeFreeEncoder p_drmModeFreeEncoder
+#define drmModeGetCrtc p_drmModeGetCrtc
+#define drmModeSetCrtc p_drmModeSetCrtc
+#define drmModeFreeCrtc p_drmModeFreeCrtc
+#define drmModeAttachMode p_drmModeAttachMode
+#define drmModeDetachMode p_drmModeDetachMode
+#define drmModeAddFB p_drmModeAddFB
+#define drmModeRmFB p_drmModeRmFB
+#define drmModeGetFB p_drmModeGetFB
+#define drmModeFreeFB p_drmModeFreeFB
+#define drmPrimeHandleToFD p_drmPrimeHandleToFD
+#define drmModeGetPlaneResources p_drmModeGetPlaneResources
+#define drmModeFreePlaneResources p_drmModeFreePlaneResources
+#define drmIoctl p_drmIoctl
+#define drmGetCap p_drmGetCap
+#define drmGetDevices2 p_drmGetDevices2
+#define drmIsMaster p_drmIsMaster
+#define drmSetMaster p_drmSetMaster
+#define drmDropMaster p_drmDropMaster
+
+# define MAX_CARD_ID 10
+# define MAX_DRM_DEVICES 16
+
+// To enable libdrmhook: make SR_WITH_DRMHOOK=1
+#ifdef SR_WITH_DRMHOOK
+	#define hook_handle RTLD_DEFAULT
+	#define hook_log " (will attempt hook)"
+#else
+	#define hook_handle mp_drm_handle
+	#define hook_log ""
+#endif
+
+//============================================================
+//  shared the privileges of the master fd
+//============================================================
+
+// If 2 displays use the same GPU but a different connector, let's share the
+// FD indexed on the card ID
+
+static int s_shared_fd[MAX_CARD_ID] = {};
+
+// The active shares on a fd, per card id
+
+static int s_shared_count[MAX_CARD_ID] = {};
+
+// What we're missing here, is also a list of the connector ids associated with
+// the screen number, otherwise SR will try to use (again) the first connector
+// that has an monitor plugged to it
+
+static unsigned int s_shared_conn[MAX_CARD_ID] = {};
+
+//============================================================
+//  id for class object (static)
+//============================================================
+
+// This helps to trace counts of active displays accross vaious instances
+// ++'ed at constructor, --'ed at destructor
+// m_id will use the ++-ed value
+
+static int static_id = 0;
+
+//============================================================
+//  list connector types
+//============================================================
+
+const char *get_connector_name(int mode)
+{
+	switch (mode)
+	{
+		case DRM_MODE_CONNECTOR_Unknown:
+			return "Unknown";
+		case DRM_MODE_CONNECTOR_VGA:
+			return "VGA-";
+		case DRM_MODE_CONNECTOR_DVII:
+			return "DVI-I-";
+		case DRM_MODE_CONNECTOR_DVID:
+			return "DVI-D-";
+		case DRM_MODE_CONNECTOR_DVIA:
+			return "DVI-A-";
+		case DRM_MODE_CONNECTOR_Composite:
+			return "Composite-";
+		case DRM_MODE_CONNECTOR_SVIDEO:
+			return "SVIDEO-";
+		case DRM_MODE_CONNECTOR_LVDS:
+			return "LVDS-";
+		case DRM_MODE_CONNECTOR_Component:
+			return "Component-";
+		case DRM_MODE_CONNECTOR_9PinDIN:
+			return "9PinDIN-";
+		case DRM_MODE_CONNECTOR_DisplayPort:
+			return "DisplayPort-";
+		case DRM_MODE_CONNECTOR_HDMIA:
+			return "HDMI-A-";
+		case DRM_MODE_CONNECTOR_HDMIB:
+			return "HDMI-B-";
+		case DRM_MODE_CONNECTOR_TV:
+			return "TV-";
+		case DRM_MODE_CONNECTOR_eDP:
+			return "eDP-";
+		case DRM_MODE_CONNECTOR_VIRTUAL:
+			return "VIRTUAL-";
+		case DRM_MODE_CONNECTOR_DSI:
+			return "DSI-";
+		case DRM_MODE_CONNECTOR_DPI:
+			return "DPI-";
+		default:
+			return "not_defined-";
+	}
+}
+
+//============================================================
+//  Check if a connector is not used on a previous display
+//============================================================
+
+bool connector_already_used(unsigned int conn_id)
+{
+	// Don't remap to an already used connector
+	for (int c = 1 ; c < static_id ; c++)
+	{
+		if (s_shared_conn[c] == conn_id)
+			return true;
+	}
+	return false;
+}
+
+//============================================================
+//  Convert a SR modeline to a DRM modeline
+//============================================================
+
+void modeline_to_drm_modeline(int id, modeline *mode, drmModeModeInfo *drmmode)
+{
+	// Clear struct
+	memset(drmmode, 0, sizeof(drmModeModeInfo));
+
+	// Create specific mode name
+	snprintf(drmmode->name, 32, "SR-%d_%dx%d@%.02f%s", id, mode->hactive, mode->vactive, mode->vfreq, mode->interlace ? "i" : "");
+	drmmode->clock       = mode->pclock / 1000;
+	drmmode->hdisplay    = mode->hactive;
+	drmmode->hsync_start = mode->hbegin;
+	drmmode->hsync_end   = mode->hend;
+	drmmode->htotal      = mode->htotal;
+	drmmode->vdisplay    = mode->vactive;
+	drmmode->vsync_start = mode->vbegin;
+	drmmode->vsync_end   = mode->vend;
+	drmmode->vtotal      = mode->vtotal;
+	drmmode->flags       = (mode->interlace ? DRM_MODE_FLAG_INTERLACE : 0) | (mode->doublescan ? DRM_MODE_FLAG_DBLSCAN : 0) | (mode->hsync ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | (mode->vsync ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC);
+
+	drmmode->hskew       = 0;
+	drmmode->vscan       = 0;
+
+	drmmode->vrefresh    = mode->refresh;  // Used only for human readable output
+}
+
+//============================================================
+//  drmkms_timing::test_kernel_user_modes
+//============================================================
+
+bool drmkms_timing::test_kernel_user_modes()
+{
+	int ret = 0, first_modes_count = 0, second_modes_count = 0;
+	int fd;
+	drmModeModeInfo mode = {};
+	const char* my_name = "KMS Test mode";
+	drmModeConnector *conn;
+
+	// Make sure we are master, that is required for the IOCTL
+	fd = get_master_fd();
+	if (fd < 0)
+	{
+		log_verbose("DRM/KMS: <%d> (%s) Need master to test kernel user modes\n", m_id, __FUNCTION__);
+		return false;
+	}
+
+	// Create a dummy modeline with a pixel clock higher than 25MHz to avoid
+	// drivers checks rejecting the mode. Use a modeline that no one would
+	// ever use hopefully
+	strcpy(mode.name, my_name);
+	mode.clock       = 25212;
+	mode.hdisplay    = 1234;
+	mode.hsync_start = 1290;
+	mode.hsync_end   = 1408;
+	mode.htotal      = 1610;
+	mode.vdisplay    = 234;
+	mode.vsync_start = 238;
+	mode.vsync_end   = 241;
+	mode.vtotal      = 261;
+	mode.flags       = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC;
+
+	// Count the number of existing modes, so it should be +1 when attaching
+	// a new mode. Could also check the mode name, still better
+	conn = drmModeGetConnector(fd, m_desktop_output);
+	if (!conn)
+	{
+		log_verbose("DRM/KMS: <%d> (%s) Cannot get connector\n", m_id, __FUNCTION__);
+		m_kernel_user_modes = false;
+		return false;
+	}
+
+	first_modes_count = conn->count_modes;
+	ret = drmModeAttachMode(fd, m_desktop_output, &mode);
+	drmModeFreeConnector(conn);
+
+	// This case can only happen if we're not drmMaster. If the kernel doesn't
+	// support adding new modes, the IOCTL will still return 0, not an error
+	if (ret < 0)
+	{
+		// Let's fail, no need to go further
+		log_verbose("DRM/KMS: <%d> (%s) Cannot add new kernel user mode\n", m_id, __FUNCTION__);
+		m_kernel_user_modes = false;
+		return false;
+	}
+
+	// Not using drmModeGetConnectorCurrent here since we need to force a
+	// modelist connector refresh, so the kernel will probe the connector
+	conn = drmModeGetConnector(fd, m_desktop_output);
+	second_modes_count = conn->count_modes;
+	if (first_modes_count != second_modes_count)
+	{
+		log_verbose("DRM/KMS: <%d> (%s) Kernel supports user modes (%d vs %d)\n", m_id, __FUNCTION__, first_modes_count, second_modes_count);
+		m_kernel_user_modes = true;
+		drmModeDetachMode(fd, m_desktop_output, &mode);
+		if (fd != m_hook_fd)
+			drmDropMaster(fd);
+	}
+	else
+		log_verbose("DRM/KMS: <%d> (%s) Kernel doesn't supports user modes\n", m_id, __FUNCTION__);
+
+	drmModeFreeConnector(conn);
+	return m_kernel_user_modes;
+}
+
+//============================================================
+//  drmkms_timing::drmkms_timing
+//============================================================
+
+drmkms_timing::drmkms_timing(char *device_name, custom_video_settings *vs)
+{
+	m_vs = *vs;
+	m_id = ++static_id;
+
+	log_verbose("DRM/KMS: <%d> (drmkms_timing) creation (%s)\n", m_id, device_name);
+	// Copy screen device name and limit size
+	if ((strlen(device_name) + 1) > 32)
+	{
+		strncpy(m_device_name, device_name, 31);
+		log_error("DRM/KMS: <%d> (drmkms_timing) [ERROR] the devine name is too long it has been trucated to %s\n", m_id, m_device_name);
+	}
+	else
+		strcpy(m_device_name, device_name);
+}
+
+//============================================================
+//  drmkms_timing::~drmkms_timing
+//============================================================
+
+drmkms_timing::~drmkms_timing()
+{
+	// Remove kernel user modes
+	if (m_kernel_user_modes)
+	{
+		int i = 0, ret = 0;
+		int fd;
+		drmModeConnector *conn;
+
+		fd = get_master_fd();
+		if (fd >= 0)
+		{
+			conn = drmModeGetConnectorCurrent(fd, m_desktop_output);
+			drmSetMaster(fd);
+			for (i = 0; i < conn->count_modes; i++)
+			{
+				drmModeModeInfo *mode = &conn->modes[i];
+				log_verbose("DRM/KMS: <%d> (%s) Checking kernel mode: %s\n", m_id, __FUNCTION__, mode->name);
+				ret = strncmp(mode->name, "SR-", 3);
+				if (ret == 0)
+				{
+					log_verbose("DRM/KMS: <%d> (%s) Removing kernel user mode: %s\n", m_id, __FUNCTION__, mode->name);
+					drmModeDetachMode(fd, m_desktop_output, mode);
+				}
+			}
+			if (fd != m_hook_fd)
+				drmDropMaster(fd);
+
+			drmModeFreeConnector(conn);
+			if (fd != m_drm_fd and fd != m_hook_fd)
+				close(fd);
+		}
+	}
+
+	// Free the connector used
+	s_shared_conn[m_id] = -1;
+
+	// close DRM/KMS library
+	if (mp_drm_handle)
+		dlclose(mp_drm_handle);
+
+	if (m_drm_fd > 0)
+	{
+		if (!--s_shared_count[m_card_id])
+			close(m_drm_fd);
+	}
+
+	// Reset static data
+	static_id = 0;
+	memset(s_shared_fd, 0, sizeof(s_shared_fd));
+	memset(s_shared_count, 0, sizeof(s_shared_count));
+	memset(s_shared_conn, 0, sizeof(s_shared_conn));
+}
+
+//============================================================
+//  drmkms_timing::init
+//============================================================
+
+bool drmkms_timing::init()
+{
+	log_verbose("DRM/KMS: <%d> (init) loading DRM/KMS library%s\n", m_id, hook_log);
+	mp_drm_handle = dlopen("libdrm.so", RTLD_NOW);
+	if (mp_drm_handle)
+	{
+		p_drmGetVersion = (__typeof__(drmGetVersion)) dlsym(mp_drm_handle, "drmGetVersion");
+		if (p_drmGetVersion == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetVersion", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmFreeVersion = (__typeof__(drmFreeVersion)) dlsym(mp_drm_handle, "drmFreeVersion");
+		if (p_drmFreeVersion == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmFreeVersion", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetResources = (__typeof__(drmModeGetResources)) dlsym(mp_drm_handle, "drmModeGetResources");
+		if (p_drmModeGetResources == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetResources", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetConnector = (__typeof__(drmModeGetConnector)) dlsym(hook_handle, "drmModeGetConnector");
+		if (p_drmModeGetConnector == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetConnector", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetConnectorCurrent = (__typeof__(drmModeGetConnectorCurrent)) dlsym(hook_handle, "drmModeGetConnectorCurrent");
+		if (p_drmModeGetConnectorCurrent == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetConnectorCurrent", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeFreeConnector = (__typeof__(drmModeFreeConnector)) dlsym(hook_handle, "drmModeFreeConnector");
+		if (p_drmModeFreeConnector == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeConnector", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeFreeResources = (__typeof__(drmModeFreeResources)) dlsym(mp_drm_handle, "drmModeFreeResources");
+		if (p_drmModeFreeResources == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeResources", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetEncoder = (__typeof__(drmModeGetEncoder)) dlsym(mp_drm_handle, "drmModeGetEncoder");
+		if (p_drmModeGetEncoder == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetEncoder", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeFreeEncoder = (__typeof__(drmModeFreeEncoder)) dlsym(mp_drm_handle, "drmModeFreeEncoder");
+		if (p_drmModeFreeEncoder == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeEncoder", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetCrtc = (__typeof__(drmModeGetCrtc)) dlsym(mp_drm_handle, "drmModeGetCrtc");
+		if (p_drmModeGetCrtc == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetCrtc", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeSetCrtc = (__typeof__(drmModeSetCrtc)) dlsym(mp_drm_handle, "drmModeSetCrtc");
+		if (p_drmModeSetCrtc == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeSetCrtc", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeFreeCrtc = (__typeof__(drmModeFreeCrtc)) dlsym(mp_drm_handle, "drmModeFreeCrtc");
+		if (p_drmModeFreeCrtc == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeCrtc", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeAttachMode = (__typeof__(drmModeAttachMode)) dlsym(mp_drm_handle, "drmModeAttachMode");
+		if (p_drmModeAttachMode == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeAttachMode", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeDetachMode = (__typeof__(drmModeDetachMode)) dlsym(mp_drm_handle, "drmModeDetachMode");
+		if (p_drmModeDetachMode == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeDetachMode", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeAddFB = (__typeof__(drmModeAddFB)) dlsym(mp_drm_handle, "drmModeAddFB");
+		if (p_drmModeAddFB == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeAddFB", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeRmFB = (__typeof__(drmModeRmFB)) dlsym(mp_drm_handle, "drmModeRmFB");
+		if (p_drmModeRmFB == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeRmFB", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetFB = (__typeof__(drmModeGetFB)) dlsym(mp_drm_handle, "drmModeGetFB");
+		if (p_drmModeGetFB == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetFB", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeFreeFB = (__typeof__(drmModeFreeFB)) dlsym(mp_drm_handle, "drmModeFreeFB");
+		if (p_drmModeFreeFB == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeFB", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmPrimeHandleToFD = (__typeof__(drmPrimeHandleToFD)) dlsym(mp_drm_handle, "drmPrimeHandleToFD");
+		if (p_drmPrimeHandleToFD == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmPrimeHandleToFD", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeGetPlaneResources = (__typeof__(drmModeGetPlaneResources)) dlsym(mp_drm_handle, "drmModeGetPlaneResources");
+		if (p_drmModeGetPlaneResources == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetPlaneResources", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmModeFreePlaneResources = (__typeof__(drmModeFreePlaneResources)) dlsym(mp_drm_handle, "drmModeFreePlaneResources");
+		if (p_drmModeFreePlaneResources == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreePlaneResources", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmIoctl = (__typeof__(drmIoctl)) dlsym(mp_drm_handle, "drmIoctl");
+		if (p_drmIoctl == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmIoctl", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmGetCap = (__typeof__(drmGetCap)) dlsym(mp_drm_handle, "drmGetCap");
+		if (p_drmGetCap == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetCap", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmGetDevices2 = (__typeof__(drmGetDevices2)) dlsym(mp_drm_handle, "drmGetDevices2");
+		if (p_drmGetDevices2 == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetDevices2", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmIsMaster = (__typeof__(drmIsMaster)) dlsym(mp_drm_handle, "drmIsMaster");
+		if (p_drmIsMaster == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmIsMaster", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmSetMaster = (__typeof__(drmSetMaster)) dlsym(mp_drm_handle, "drmSetMaster");
+		if (p_drmSetMaster == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmSetMaster", "DRM_LIBRARY");
+			return false;
+		}
+
+		p_drmDropMaster = (__typeof__(drmDropMaster)) dlsym(mp_drm_handle, "drmDropMaster");
+		if (p_drmDropMaster == NULL)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmDropMaster", "DRM_LIBRARY");
+			return false;
+		}
+	}
+	else
+	{
+		log_error("DRM/KMS: <%d> (init) [ERROR] missing %s library\n", m_id, "DRM/KMS_LIBRARY");
+		return false;
+	}
+
+	int screen_pos = -1;
+
+	// Handle the screen name, "auto", "screen[0-9]" and device name
+	if (strlen(m_device_name) == 7 && !strncmp(m_device_name, "screen", 6) && m_device_name[6] >= '0' && m_device_name[6] <= '9')
+		screen_pos = m_device_name[6] - '0';
+	else if (strlen(m_device_name) == 1 && m_device_name[0] >= '0' && m_device_name[0] <= '9')
+		screen_pos = m_device_name[0] - '0';
+
+	// Get an array of drm devices to check
+	drmDevicePtr devices[MAX_DRM_DEVICES];
+	int num_devices = drmGetDevices2(0, NULL, 0);
+
+	if (num_devices > MAX_DRM_DEVICES)
+		num_devices = MAX_DRM_DEVICES;
+
+	int ret = drmGetDevices2(0, devices, num_devices);
+	if (ret < 0)
+	{
+		log_error("DRM/KMS: drmGetDevices2() returned an error %d\n", ret);
+		return false;
+	}
+
+	char *drm_name;
+	drmModeRes *p_res;
+	drmModeConnector *p_connector;
+
+	int output_position = 0;
+	for (int num = 0; num < num_devices; num++)
+	{
+		// Skip non-primary nodes
+		if (devices[num]->available_nodes & (1 << DRM_NODE_PRIMARY))
+			drm_name = devices[num]->nodes[DRM_NODE_PRIMARY];
+		else continue;
+
+		if (!access(drm_name, F_OK) == 0)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] cannot open device %s\n", m_id, drm_name);
+			break;
+		}
+		m_drm_fd = open(drm_name, O_RDWR | O_CLOEXEC);
+
+		drmVersion *version = drmGetVersion(m_drm_fd);
+		log_verbose("DRM/KMS: <%d> (init) version %d.%d.%d type %s\n", m_id, version->version_major, version->version_minor, version->version_patchlevel, version->name);
+		drmFreeVersion(version);
+
+		uint64_t check_dumb = 0;
+		if (drmGetCap(m_drm_fd, DRM_CAP_DUMB_BUFFER, &check_dumb) < 0)
+			log_error("DRM/KMS: <%d> (init) [ERROR] ioctl DRM_CAP_DUMB_BUFFER\n", m_id);
+
+		if (!check_dumb)
+		{
+			log_error("DRM/KMS: <%d> (init) [ERROR] dumb buffer not supported\n", m_id);
+			continue;
+		}
+
+		p_res = drmModeGetResources(m_drm_fd);
+
+		for (int i = 0; i < p_res->count_connectors; i++)
+		{
+			p_connector = drmModeGetConnectorCurrent(m_drm_fd, p_res->connectors[i]);
+			if (!p_connector)
+			{
+				log_error("DRM/KMS: <%d> (init) [ERROR] card %d connector %d - %d\n", m_id, num, i, p_res->connectors[i]);
+				continue;
+			}
+			char connector_name[32];
+			snprintf(connector_name, 32, "%s%d", get_connector_name(p_connector->connector_type), p_connector->connector_type_id);
+			log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s status %d - modes %d\n", m_id, num, i, p_connector->connector_id, connector_name, p_connector->connection, p_connector->count_modes);
+			// detect desktop connector
+			if (!m_desktop_output && p_connector->connection == DRM_MODE_CONNECTED)
+			{
+				if (!strcmp(m_device_name, "auto") || !strcmp(m_device_name, connector_name) || output_position == screen_pos)
+				{
+					// In a multihead setup, skip already used connectors
+					if (connector_already_used(p_connector->connector_id))
+					{
+						drmModeFreeConnector(p_connector);
+						continue;
+					}
+					m_desktop_output = p_connector->connector_id;
+					m_card_id = num;
+					strcpy(m_drm_name, drm_name);
+					log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s selected as primary output\n", m_id, num, i, m_desktop_output, connector_name);
+
+					drmModeEncoder *p_encoder = drmModeGetEncoder(m_drm_fd, p_connector->encoder_id);
+
+					if (p_encoder)
+					{
+						for (int e = 0; e < p_res->count_crtcs; e++)
+						{
+							mp_crtc_desktop = drmModeGetCrtc(m_drm_fd, p_res->crtcs[e]);
+
+							if (mp_crtc_desktop->crtc_id == p_encoder->crtc_id)
+							{
+								log_verbose("DRM/KMS: <%d> (init) desktop mode name %s crtc %d fb %d valid %d\n", m_id, mp_crtc_desktop->mode.name, mp_crtc_desktop->crtc_id, mp_crtc_desktop->buffer_id, mp_crtc_desktop->mode_valid);
+								break;
+							}
+							drmModeFreeCrtc(mp_crtc_desktop);
+						}
+					}
+					if (!mp_crtc_desktop)
+					{
+						m_desktop_output = 0;
+						log_error("DRM/KMS: <%d> (init) [ERROR] no crtc found\n", m_id);
+					}
+					drmModeFreeEncoder(p_encoder);
+				}
+				output_position++;
+			}
+			drmModeFreeConnector(p_connector);
+		}
+		drmModeFreeResources(p_res);
+		if (!m_desktop_output)
+			close(m_drm_fd);
+		else
+		{
+			if (drmIsMaster(m_drm_fd))
+			{
+				 // We've never called drmSetMaster before. This means we're the first app
+				 // opening the device, so the kernel sets us as master by default.
+				 // We drop master so other apps can become master
+				log_verbose("DRM/KMS: <%d> (%s) Already DRM master\n", m_id, __FUNCTION__);
+				s_shared_fd[m_card_id] = m_drm_fd;
+				s_shared_count[m_card_id] = 1;
+				drmDropMaster(m_drm_fd);
+			}
+			else
+			{
+				if (s_shared_count[m_card_id] > 0)
+				{
+					log_verbose("DRM/KMS: <%d> (%s : %d) The drm FD was substituted, expect the unexpected\n", m_id, __FUNCTION__, __LINE__);
+					close(m_drm_fd);
+					m_drm_fd = s_shared_fd[m_card_id];
+					s_shared_count[m_card_id]++;
+				}
+				else if (m_id == 1)
+				{
+					log_verbose("DRM/KMS: <%d> (%s) looking for the DRM master\n", m_id, __FUNCTION__);
+					int fd = get_master_fd();
+					if (fd >= 0)
+					{
+						close(m_drm_fd);
+						 // This statement is dangerous, as drmIsMaster can return 1
+						 // on m_drm_fd if there is no master left, but it doesn't
+						 // check if m_drm_fd is a valid fd
+
+						m_drm_fd = fd;
+						s_shared_fd[m_card_id] = m_drm_fd;
+						// start at 2 to disable closing the fd
+						s_shared_count[m_card_id] = 2;
+					}
+					if (!drmIsMaster(m_drm_fd))
+					{
+						m_desktop_output = 0;
+						log_error("DRM/KMS: <%d> (%s) [ERROR] limited DRM rights on this screen\n", m_id, __FUNCTION__);
+					}
+				}
+			}
+			// If we're here and we have a valid output, we're done.
+			if (m_desktop_output) break;
+		}
+	}
+
+	// Handle no screen detected case
+	if (!m_desktop_output)
+	{
+		log_error("DRM/KMS: <%d> (init) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+	else
+	{
+	}
+
+	// Check if we have a libdrm hook
+	if (drmModeGetConnectorCurrent(-1, 0) != NULL)
+	{
+		log_verbose("DRM/KMS: libdrm hook found!\n");
+		m_caps |= CUSTOM_VIDEO_CAPS_UPDATE;
+	}
+	// Check if the kernel handles user modes
+	else if (test_kernel_user_modes())
+		m_caps |= CUSTOM_VIDEO_CAPS_ADD;
+
+	if (drmIsMaster(m_drm_fd) and m_drm_fd != m_hook_fd)
+		drmDropMaster(m_drm_fd);
+
+	return true;
+}
+
+//============================================================
+//  drmkms_timing::get_master_fd
+//============================================================
+// BACKGROUND
+// This is written as of Linux 5.14, 5.15 is just out, not yet tested.
+// There are a few unexpected behaviours so far in DRM:
+//   - drmSetMaster seems to always return -1 on 5.4, but ok on 5.14
+//   - drmIsMaster doesn't care if the FD exists and will always return 1
+//     if the there is no master on the DRI device
+// That's why we can't trust drmIsMaster if we didn't make sure before that
+// the FD does exist.
+// get_master_fd will always return a valid master FD, or return -1 if it's
+// impossible
+
+int drmkms_timing::get_master_fd()
+{
+	const size_t path_length = 20;
+	char procpath[path_length];
+	char fullpath[512];
+	char* actualpath;
+	struct stat st;
+	int fd;
+
+	// CASE 1: m_drm_fd is a valid FD
+	if (fstat(m_drm_fd, &st) == 0)
+	{
+		if (drmIsMaster(m_drm_fd))
+			return m_drm_fd;
+		if (drmSetMaster(m_drm_fd) == 0)
+			return m_drm_fd;
+	}
+
+	// CASE 2: m_drm_fd can't be master, find the master FD
+	if (m_card_id > MAX_CARD_ID - 1 or m_card_id < 0)
+	{
+		log_error("DRM/KMS: <%d> (%s) [ERROR] card id (%d) out of bounds (0 to %d)\n", m_id, __FUNCTION__, m_card_id, MAX_CARD_ID - 1);
+		return -1;
+	}
+
+	if (!access(m_drm_name, F_OK) == 0)
+	{
+		log_error("DRM/KMS: <%d> (%s) [ERROR] Device %s doesn't exist\n", m_id, __FUNCTION__, m_drm_name);
+		return -1;
+	}
+
+	sprintf(procpath, "/proc/%d/fd", getpid());
+	auto dir = opendir(procpath);
+	if (!dir)
+		return -1;
+	while (auto f = readdir(dir))
+	{
+		// Skip everything that starts with a dot
+		if (f->d_name[0] == '.')
+			continue;
+		// Only symlinks matter
+		if (f-> d_type != DT_LNK)
+			continue;
+
+		//log_verbose("File: %s\n", f->d_name);
+		sprintf(fullpath, "%s/%s", procpath, f->d_name);
+		if (stat(fullpath, &st))
+			continue;
+		if (!S_ISCHR(st.st_mode))
+			continue;
+		actualpath = realpath(fullpath, NULL);
+		// Only check the device we expect
+		if (strncmp(m_drm_name, actualpath, path_length) != 0)
+		{
+			free(actualpath);
+			continue;
+		}
+		fd = atoi(f->d_name);
+		//log_verbose("File: %s -> %s %d\n", fullpath, actualpath, fd);
+		free(actualpath);
+
+		if (drmIsMaster(fd))
+		{
+			log_verbose("DRM/KMS: <%d> (%s) DRM hook created on FD %d\n", m_id, __FUNCTION__, fd);
+			closedir(dir);
+			m_hook_fd = fd;
+			return fd;
+		}
+	}
+	closedir(dir);
+
+	// CASE 3: m_drm_fd is not a master (and probably not even a valid FD), the currend pid doesn't have master rights
+	// Or master is owned by a 3rd party app (like a frontend ...)
+	log_verbose("DRM/KMS: <%d> (%s) Couldn't find a master FD, opening default %s\n", m_id, __FUNCTION__, m_drm_name);
+
+	// mark our former hook as invalid
+	m_hook_fd = -1;
+
+	fd = open(m_drm_name, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+	{
+		// Oh, we're totally screwed here, worst possible scenario
+		log_error("DRM/KMS: <%d> (%s) Can't open %s, can't get master rights\n", m_id, __FUNCTION__, m_drm_name);
+		return -1;
+	}
+
+	// Hardly any chance we reach here. I don't even know when to close the FD ...
+	if (drmIsMaster(fd) or drmSetMaster(fd) == 0)
+		return fd;
+
+	// There is definitely no way we get master ...
+	close(fd);
+	log_error("DRM/KMS: <%d> (%s) No way to get master rights!\n", m_id, __FUNCTION__);
+	return -1;
+}
+
+//============================================================
+//  drmkms_timing::update_mode
+//============================================================
+
+bool drmkms_timing::update_mode(modeline *mode)
+{
+	if (!mode)
+		return false;
+
+	if (!m_desktop_output)
+	{
+		log_error("DRM/KMS: <%d> (update_mode) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	// Without libdrm hook, the update method isn't natively supported, so we must delete
+	// the mode and readd it with updated timings.
+	if (!(m_caps & CUSTOM_VIDEO_CAPS_UPDATE))
+	{
+		if (!delete_mode(mode))
+		{
+			log_error("DRM/KMS: <%d> (update_mode) [ERROR] delete operation not successful", m_id);
+			return false;
+		}
+		if (!add_mode(mode))
+		{
+			log_error("DRM/KMS: <%d> (update_mode) [ERROR] add operation not successful", m_id);
+			return false;
+		}
+		return true;
+	}
+
+	// libdrn hook case, we can update timings directly in the connector's data
+	drmModeConnector *conn = drmModeGetConnectorCurrent(m_drm_fd, m_desktop_output);
+	if (conn)
+	{
+		for (int i = 0; i < conn->count_modes; i++)
+		{
+			drmModeModeInfo *drmmode = &conn->modes[i];
+			if ((int)mode->platform_data == i)
+			{
+				int m_type = drmmode->type;
+				modeline_to_drm_modeline(m_id, mode, drmmode);
+				drmmode->type = m_type;
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+//============================================================
+//  drmkms_timing::add_mode
+//============================================================
+
+bool drmkms_timing::add_mode(modeline *mode)
+{
+	if (!mode)
+		return false;
+
+	// Handle no screen detected case
+	if (!m_desktop_output)
+	{
+		log_error("DRM/KMS: <%d> (add_mode) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (!mp_crtc_desktop)
+	{
+		log_error("DRM/KMS: <%d> (add_mode) [ERROR] no desktop crtc\n", m_id);
+		return false;
+	}
+
+	if (!mode)
+		return false;
+
+	if (m_kernel_user_modes)
+	{
+		int ret = 0, fd = m_drm_fd;
+		drmModeModeInfo drmmode;
+
+		if (!drmIsMaster(fd))
+			fd = get_master_fd();
+
+		if (!drmIsMaster(fd))
+		{
+			log_error("DRM/KMS: <%d> (%s) Need master to add a kernel mode (%d)\n", m_id, __FUNCTION__, ret);
+			return false;
+		}
+
+		modeline_to_drm_modeline(m_id, mode, &drmmode);
+		drmmode.type = DRM_MODE_TYPE_USERDEF;
+
+		log_verbose("DRM/KMS: <%d> (add_mode) [DEBUG] Adding a mode to the kernel: %dx%d %s\n", m_id, drmmode.hdisplay, drmmode.vdisplay, drmmode.name);
+		// Calling drmModeGetConnector forces a refresh of the connector modes, which is slow, so don't do it
+		ret = drmModeAttachMode(fd, m_desktop_output, &drmmode);
+		if (ret != 0)
+		{
+			// This case hardly has any chance to happen, since at this point
+			// we are drmMaster, and we have already checked that the kernel
+			// supports user modes. If any error, it's on the kernel side
+			log_verbose("DRM/KMS: <%d> (%s) Couldn't add mode (ret=%d)\n", m_id, __FUNCTION__, ret);
+			if (fd != m_hook_fd)
+				drmDropMaster(fd);
+			return false;
+		}
+		log_verbose("DRM/KMS: <%d> (%s) Mode added\n", m_id, __FUNCTION__);
+		if (fd != m_hook_fd)
+			drmDropMaster(fd);
+	}
+
+	return true;
+}
+
+//============================================================
+//  drmkms_timing::set_timing
+//============================================================
+
+bool drmkms_timing::set_timing(modeline *mode)
+{
+	if (!mode)
+		return false;
+
+	// Handle no screen detected case
+	if (!m_desktop_output)
+	{
+		log_error("DRM/KMS: <%d> (set_timing) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (!kms_has_mode(mode))
+		add_mode(mode);
+
+	// If we can't be master, no need to go further
+	drmSetMaster(m_drm_fd);
+	if (!drmIsMaster(m_drm_fd))
+		return false;
+
+	// Setup the DRM mode structure
+	drmModeModeInfo dmode = {};
+
+	// Create specific mode name
+	snprintf(dmode.name, 32, "SR-%d_%dx%d@%.02f%s", m_id, mode->hactive, mode->vactive, mode->vfreq, mode->interlace ? "i" : "");
+	dmode.clock       = mode->pclock / 1000;
+	dmode.hdisplay    = mode->hactive;
+	dmode.hsync_start = mode->hbegin;
+	dmode.hsync_end   = mode->hend;
+	dmode.htotal      = mode->htotal;
+	dmode.vdisplay    = mode->vactive;
+	dmode.vsync_start = mode->vbegin;
+	dmode.vsync_end   = mode->vend;
+	dmode.vtotal      = mode->vtotal;
+	dmode.flags       = (mode->interlace ? DRM_MODE_FLAG_INTERLACE : 0) | (mode->doublescan ? DRM_MODE_FLAG_DBLSCAN : 0) | (mode->hsync ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | (mode->vsync ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC);
+
+	dmode.hskew       = 0;
+	dmode.vscan       = 0;
+
+	dmode.vrefresh    = mode->refresh;  // Used only for human readable output
+
+	dmode.type        = DRM_MODE_TYPE_USERDEF;  //DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	mode->type |= CUSTOM_VIDEO_TIMING_DRMKMS;
+
+	if (mode->type & MODE_DESKTOP)
+	{
+		log_verbose("DRM/KMS: <%d> (set_timing) <debug> restore desktop mode\n", m_id);
+		drmModeSetCrtc(m_drm_fd, mp_crtc_desktop->crtc_id, mp_crtc_desktop->buffer_id, mp_crtc_desktop->x, mp_crtc_desktop->y, &m_desktop_output, 1, &mp_crtc_desktop->mode);
+		if (m_dumb_handle)
+		{
+			int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &m_dumb_handle);
+			if (ret)
+				log_verbose("DRM/KMS: <%d> (set_timing) [ERROR] ioctl DRM_IOCTL_MODE_DESTROY_DUMB %d\n", m_id, ret);
+			m_dumb_handle = 0;
+		}
+		if (m_framebuffer_id && m_framebuffer_id != mp_crtc_desktop->buffer_id)
+		{
+			if (drmModeRmFB(m_drm_fd, m_framebuffer_id))
+				log_verbose("DRM/KMS: <%d> (set_timing) [ERROR] remove frame buffer\n", m_id);
+			m_framebuffer_id = 0;
+		}
+	}
+	else
+	{
+		unsigned int old_dumb_handle = m_dumb_handle;
+
+		drmModeFB *pframebuffer = drmModeGetFB(m_drm_fd, mp_crtc_desktop->buffer_id);
+		log_verbose("DRM/KMS: <%d> (set_timing) <debug> existing frame buffer id %d size %dx%d bpp %d\n", m_id, mp_crtc_desktop->buffer_id, pframebuffer->width, pframebuffer->height, pframebuffer->bpp);
+		//drmModePlaneRes *pplanes = drmModeGetPlaneResources(m_drm_fd);
+		//log_verbose("DRM/KMS: <%d> (add_mode) <debug> total planes %d\n", m_id, pplanes->count_planes);
+		//drmModeFreePlaneResources(pplanes);
+
+		unsigned int framebuffer_id = mp_crtc_desktop->buffer_id;
+
+		//if (pframebuffer->width < dmode.hdisplay || pframebuffer->height < dmode.vdisplay)
+		if (1)
+		{
+			log_verbose("DRM/KMS: <%d> (set_timing) <debug> creating new frame buffer with size %dx%d\n", m_id, dmode.hdisplay, dmode.vdisplay);
+
+			// create a new dumb fb (not driver specefic)
+			drm_mode_create_dumb create_dumb = {};
+			create_dumb.width = dmode.hdisplay;
+			create_dumb.height = dmode.vdisplay;
+			create_dumb.bpp = pframebuffer->bpp;
+
+			int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
+			if (ret)
+				log_verbose("DRM/KMS: <%d> (set_timing) [ERROR] ioctl DRM_IOCTL_MODE_CREATE_DUMB %d\n", m_id, ret);
+
+			if (drmModeAddFB(m_drm_fd, dmode.hdisplay, dmode.vdisplay, pframebuffer->depth, pframebuffer->bpp, create_dumb.pitch, create_dumb.handle, &framebuffer_id))
+				log_error("DRM/KMS: <%d> (set_timing) [ERROR] cannot add frame buffer\n", m_id);
+			else
+				m_dumb_handle = create_dumb.handle;
+
+			drm_mode_map_dumb map_dumb = {};
+			map_dumb.handle = create_dumb.handle;
+
+			ret = drmIoctl(m_drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
+			if (ret)
+				log_verbose("DRM/KMS: <%d> (set_timing) [ERROR] ioctl DRM_IOCTL_MODE_MAP_DUMB %d\n", m_id, ret);
+
+			void *map = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, m_drm_fd, map_dumb.offset);
+			if (map != MAP_FAILED)
+			{
+				// clear the frame buffer
+				memset(map, 0, create_dumb.size);
+			}
+			else
+				log_verbose("DRM/KMS: <%d> (set_timing) [ERROR] failed to map frame buffer %p\n", m_id, map);
+		}
+		else
+			log_verbose("DRM/KMS: <%d> (set_timing) <debug> use existing frame buffer\n", m_id);
+
+		drmModeFreeFB(pframebuffer);
+
+		pframebuffer = drmModeGetFB(m_drm_fd, framebuffer_id);
+		log_verbose("DRM/KMS: <%d> (set_timing) <debug> frame buffer id %d size %dx%d bpp %d\n", m_id, framebuffer_id, pframebuffer->width, pframebuffer->height, pframebuffer->bpp);
+		drmModeFreeFB(pframebuffer);
+
+		// set the mode on the crtc
+		if (drmModeSetCrtc(m_drm_fd, mp_crtc_desktop->crtc_id, framebuffer_id, 0, 0, &m_desktop_output, 1, &dmode))
+			log_error("DRM/KMS: <%d> (set_timing) [ERROR] cannot attach the mode to the crtc %d frame buffer %d\n", m_id, mp_crtc_desktop->crtc_id, framebuffer_id);
+		else
+		{
+			if (old_dumb_handle)
+			{
+				log_verbose("DRM/KMS: <%d> (set_timing) <debug> remove old dumb %d\n", m_id, old_dumb_handle);
+				int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &old_dumb_handle);
+				if (ret)
+					log_verbose("DRM/KMS: <%d> (set_timing) [ERROR] ioctl DRM_IOCTL_MODE_DESTROY_DUMB %d\n", m_id, ret);
+				old_dumb_handle = 0;
+			}
+			if (m_framebuffer_id && framebuffer_id != mp_crtc_desktop->buffer_id)
+			{
+				log_verbose("DRM/KMS: <%d> (set_timing) <debug> remove old frame buffer %d\n", m_id, m_framebuffer_id);
+				drmModeRmFB(m_drm_fd, m_framebuffer_id);
+				m_framebuffer_id = 0;
+			}
+			m_framebuffer_id = framebuffer_id;
+		}
+	}
+	if (can_drop_master)
+		drmDropMaster(m_drm_fd);
+
+	return true;
+}
+
+//============================================================
+//  drmkms_timing::delete_mode
+//============================================================
+
+bool drmkms_timing::delete_mode(modeline *mode)
+{
+	if (!mode)
+		return false;
+
+	// Handle no screen detected case
+	if (!m_desktop_output)
+	{
+		log_error("DRM/KMS: <%d> (delete_mode) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (m_kernel_user_modes)
+	{
+		int i = 0, ret = 0, fd = -1;
+		drmModeConnector *conn;
+
+		// If SR was initilized before SDL2 for instance, SR lost the DRM
+		fd = get_master_fd();
+		if (fd < 0)
+		{
+			log_verbose("DRM/KMS: <%d> (%s) Need master to remove kernel user modes\n", m_id, __FUNCTION__);
+			return false;
+		}
+
+		drmModeModeInfo srmode;
+		conn = drmModeGetConnectorCurrent(fd, m_desktop_output);
+		modeline_to_drm_modeline(m_id, mode, &srmode);
+		for (i = 0; i < conn->count_modes; i++)
+		{
+			drmModeModeInfo *drmmode = &conn->modes[i];
+			ret = strcmp(drmmode->name, srmode.name);
+			if (ret != 0)
+				continue;
+			ret = drmModeDetachMode(fd, m_desktop_output, drmmode);
+			if (fd != m_hook_fd)
+				drmDropMaster(m_drm_fd);
+			drmModeFreeConnector(conn);
+			if (ret != 0)
+			{
+				log_verbose("DRM/KMS: <%d> (%s) Failed removing kernel user mode: %s (ret=%d)\n", m_id, __FUNCTION__, drmmode->name, ret);
+				return false;
+			}
+			return true;
+		}
+	}
+
+	return true;
+}
+
+//============================================================
+//  drmkms_timing::get_timing
+//============================================================
+
+bool drmkms_timing::get_timing(modeline *mode)
+{
+	// Handle no screen detected case
+	if (!m_desktop_output)
+	{
+		log_error("DRM/KMS: <%d> (get_timing) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	// INFO: not used vrefresh, hskew, vscan
+	drmModeRes *p_res = drmModeGetResources(m_drm_fd);
+
+	for (int i = 0; i < p_res->count_connectors; i++)
+	{
+		drmModeConnector *p_connector = drmModeGetConnectorCurrent(m_drm_fd, p_res->connectors[i]);
+
+		// Cycle through the modelines and report them back to the display manager
+		if (p_connector && m_desktop_output == p_connector->connector_id)
+		{
+			if (m_video_modes_position < p_connector->count_modes)
+			{
+				drmModeModeInfo *pdmode = &p_connector->modes[m_video_modes_position++];
+
+				// Use mode position as index
+				mode->platform_data = m_video_modes_position - 1;
+
+				mode->pclock        = pdmode->clock * 1000;
+				mode->hactive       = pdmode->hdisplay;
+				mode->hbegin        = pdmode->hsync_start;
+				mode->hend          = pdmode->hsync_end;
+				mode->htotal        = pdmode->htotal;
+				mode->vactive       = pdmode->vdisplay;
+				mode->vbegin        = pdmode->vsync_start;
+				mode->vend          = pdmode->vsync_end;
+				mode->vtotal        = pdmode->vtotal;
+				mode->interlace     = (pdmode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
+				mode->doublescan    = (pdmode->flags & DRM_MODE_FLAG_DBLSCAN) ? 1 : 0;
+				mode->hsync         = (pdmode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
+				mode->vsync         = (pdmode->flags & DRM_MODE_FLAG_PVSYNC) ? 1 : 0;
+
+				mode->hfreq         = mode->pclock / mode->htotal;
+				mode->vfreq         = mode->hfreq / mode->vtotal * (mode->interlace ? 2 : 1);
+
+				// Store drm's integer refresh to make sure we use the same rounding
+				mode->refresh       = pdmode->vrefresh;
+
+				mode->width         = pdmode->hdisplay;
+				mode->height        = pdmode->vdisplay;
+
+				// Add the rotation flag from the plane (DRM_MODE_ROTATE_xxx)
+				// TODO: mode->type |= MODE_ROTATED;
+
+				mode->type |= CUSTOM_VIDEO_TIMING_DRMKMS;
+
+				// Check if this is a dummy mode
+				if (pdmode->type & (1<<7)) mode->type |= XYV_EDITABLE | SCAN_EDITABLE;
+
+				if (strncmp(pdmode->name, "SR-", 3) == 0)
+					log_verbose("DRM/KMS: <%d> (get_timing) [WARNING] modeline %s detected\n", m_id, pdmode->name);
+				else if (!strcmp(pdmode->name, mp_crtc_desktop->mode.name) && pdmode->clock == mp_crtc_desktop->mode.clock && pdmode->vrefresh == mp_crtc_desktop->mode.vrefresh)
+				{
+					// Add the desktop flag to desktop modeline
+					log_verbose("DRM/KMS: <%d> (get_timing) desktop mode name %s refresh %d found\n", m_id, mp_crtc_desktop->mode.name, mp_crtc_desktop->mode.vrefresh);
+					mode->type |= MODE_DESKTOP;
+				}
+			}
+			else
+			{
+				// Inititalise the position for the modeline list
+				m_video_modes_position = 0;
+			}
+		}
+		drmModeFreeConnector(p_connector);
+	}
+	drmModeFreeResources(p_res);
+
+	return true;
+}
+
+//============================================================
+//  drmkms_timing::process_modelist
+//============================================================
+
+bool drmkms_timing::process_modelist(std::vector<modeline *> modelist)
+{
+	bool error = false;
+	bool result = false;
+
+	for (auto &mode : modelist)
+	{
+		if (mode->type & MODE_DELETE)
+			result = delete_mode(mode);
+
+		else if (mode->type & MODE_ADD)
+			result = add_mode(mode);
+
+		else if (mode->type & MODE_UPDATE)
+			result = update_mode(mode);
+
+		if (!result)
+		{
+			mode->type |= MODE_ERROR;
+			error = true;
+		}
+		else
+			// succeed
+			mode->type &= ~MODE_ERROR;
+	}
+
+	return !error;
+}
+
+void drmkms_timing::list_drm_modes()
+{
+	int i = 0;
+	drmModeConnector *conn;
+	drmModeModeInfo *drmmode;
+
+	conn = drmModeGetConnectorCurrent(m_drm_fd, m_desktop_output);
+	for (i = 0; i < conn->count_modes; i++)
+	{
+		drmmode = &conn->modes[i];
+		log_verbose("DRM/KMS: <%d> (%s) DRM mode: %dx%d %s\n", m_id, __FUNCTION__, drmmode->hdisplay, drmmode->vdisplay, drmmode->name);
+	}
+	drmModeFreeConnector(conn);
+}
+
+//============================================================
+//  drmkms_timing::kms_has_mode
+//============================================================
+
+bool drmkms_timing::kms_has_mode(modeline* mode)
+{
+	int i = 0;
+	drmModeConnector *conn;
+	drmModeModeInfo drmmode;
+
+	// To avoid matching issues, we just compare the relevant timing fields
+	int size_to_compare = sizeof(drmModeModeInfo) - sizeof(drmModeModeInfo::type) - sizeof(drmModeModeInfo::name);
+
+	modeline_to_drm_modeline(m_id,  mode, &drmmode);
+
+	conn = drmModeGetConnectorCurrent(m_drm_fd, m_desktop_output);
+	for (i = 0; i < conn->count_modes; i++)
+	{
+		if (memcmp(&drmmode, &conn->modes[i], size_to_compare) == 0)
+		{
+			log_verbose("DRM/KMS: <%d> (%s) Found the mode in the connector\n", m_id, __FUNCTION__);
+			drmModeFreeConnector(conn);
+			return true;
+		}
+	}
+	log_verbose("DRM/KMS: <%d> (%s) Couldn't find the mode in the connector\n", m_id, __FUNCTION__);
+	drmModeFreeConnector(conn);
+	return false;
+}
diff --git a/3rdparty/switchres/custom_video_drmkms.h b/3rdparty/switchres/custom_video_drmkms.h
new file mode 100755
index 00000000000..c4b526afb1c
--- /dev/null
+++ b/3rdparty/switchres/custom_video_drmkms.h
@@ -0,0 +1,99 @@
+/**************************************************************
+
+   custom_video_drmkms.h - Linux DRM/KMS video management layer
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __CUSTOM_VIDEO_DRMKMS_
+#define __CUSTOM_VIDEO_DRMKMS_
+
+// DRM headers
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include "custom_video.h"
+
+class drmkms_timing : public custom_video
+{
+	public:
+		drmkms_timing(char *device_name, custom_video_settings *vs);
+		~drmkms_timing();
+		const char *api_name() { return "DRMKMS"; }
+		int caps() { return m_caps; }
+		bool init();
+
+		bool add_mode(modeline *mode);
+		bool delete_mode(modeline *mode);
+		bool update_mode(modeline *mode);
+
+		bool process_modelist(std::vector<modeline *>);
+
+		bool get_timing(modeline *mode);
+		bool set_timing(modeline *mode);
+
+	private:
+		/*
+		 * Consider m_id as the "display number": 1 for the 1st, 2 for the second etc...
+		 */
+		int m_id = 0;
+
+		int m_drm_fd = -1;
+		drmModeCrtc *mp_crtc_desktop = NULL;
+		int m_card_id = 0;
+		bool m_kernel_user_modes = false;
+		bool can_drop_master = true;
+		int m_hook_fd = -1;
+		int m_caps = 0;
+
+		char m_device_name[32];
+		char m_drm_name[32];
+		unsigned int m_desktop_output = 0;
+		int m_video_modes_position = 0;
+
+		void *mp_drm_handle = NULL;
+		unsigned int m_dumb_handle = 0;
+		unsigned int m_framebuffer_id = 0;
+
+		__typeof__(drmGetVersion) *p_drmGetVersion;
+		__typeof__(drmFreeVersion) *p_drmFreeVersion;
+		__typeof__(drmModeGetResources) *p_drmModeGetResources;
+		__typeof__(drmModeGetConnector) *p_drmModeGetConnector;
+		__typeof__(drmModeGetConnectorCurrent) *p_drmModeGetConnectorCurrent;
+		__typeof__(drmModeFreeConnector) *p_drmModeFreeConnector;
+		__typeof__(drmModeFreeResources) *p_drmModeFreeResources;
+		__typeof__(drmModeGetEncoder) *p_drmModeGetEncoder;
+		__typeof__(drmModeFreeEncoder) *p_drmModeFreeEncoder;
+		__typeof__(drmModeGetCrtc) *p_drmModeGetCrtc;
+		__typeof__(drmModeSetCrtc) *p_drmModeSetCrtc;
+		__typeof__(drmModeFreeCrtc) *p_drmModeFreeCrtc;
+		__typeof__(drmModeAttachMode) *p_drmModeAttachMode;
+		__typeof__(drmModeDetachMode) *p_drmModeDetachMode;
+		__typeof__(drmModeAddFB) *p_drmModeAddFB;
+		__typeof__(drmModeRmFB) *p_drmModeRmFB;
+		__typeof__(drmModeGetFB) *p_drmModeGetFB;
+		__typeof__(drmModeFreeFB) *p_drmModeFreeFB;
+		__typeof__(drmPrimeHandleToFD) *p_drmPrimeHandleToFD;
+		__typeof__(drmModeGetPlaneResources) *p_drmModeGetPlaneResources;
+		__typeof__(drmModeFreePlaneResources) *p_drmModeFreePlaneResources;
+		__typeof__(drmIoctl) *p_drmIoctl;
+		__typeof__(drmGetCap) *p_drmGetCap;
+		__typeof__(drmGetDevices2) *p_drmGetDevices2;
+		__typeof__(drmIsMaster) *p_drmIsMaster;
+		__typeof__(drmSetMaster) *p_drmSetMaster;
+		__typeof__(drmDropMaster) *p_drmDropMaster;
+
+		bool test_kernel_user_modes();
+		bool kms_has_mode(modeline*);
+		void list_drm_modes();
+		int get_master_fd();
+
+};
+
+#endif
diff --git a/3rdparty/switchres/custom_video_pstrip.cpp b/3rdparty/switchres/custom_video_pstrip.cpp
new file mode 100644
index 00000000000..15e80bb4ce1
--- /dev/null
+++ b/3rdparty/switchres/custom_video_pstrip.cpp
@@ -0,0 +1,628 @@
+/**************************************************************
+
+   custom_video_pstrip.cpp - PowerStrip interface routines
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+/*  http://forums.entechtaiwan.com/index.php?topic=5534.msg20902;topicseen#msg20902
+
+    UM_SETCUSTOMTIMING = WM_USER+200;
+    wparam = monitor number, zero-based
+    lparam = atom for string pointer
+    lresult = -1 for failure else current pixel clock (integer in Hz)
+    Note: pass full PowerStrip timing string*
+
+    UM_SETREFRESHRATE = WM_USER+201;
+    wparam = monitor number, zero-based
+    lparam = refresh rate (integer in Hz), or 0 for read-only
+    lresult = -1 for failure else current refresh rate (integer in Hz)
+
+    UM_SETPOLARITY = WM_USER+202;
+    wparam = monitor number, zero-based
+    lparam = polarity bits
+    lresult = -1 for failure else current polarity bits+1
+
+    UM_REMOTECONTROL = WM_USER+210;
+    wparam = 99
+    lparam =
+        0 to hide tray icon
+        1 to show tray icon,
+        2 to get build number
+       10 to show Performance profiles
+       11 to show Color profiles
+       12 to show Display profiles
+       13 to show Application profiles
+       14 to show Adapter information
+       15 to show Monitor information
+       16 to show Hotkey manager
+       17 to show Resource manager
+       18 to show Preferences
+       19 to show Online services
+       20 to show About screen
+       21 to show Tip-of-the-day
+       22 to show Setup wizard
+       23 to show Screen fonts
+       24 to show Advanced timing options
+       25 to show Custom resolutions
+       99 to close PS
+    lresult = -1 for failure else lparam+1 for success or build number (e.g., 335)
+    if lparam was 2
+
+    UM_SETGAMMARAMP = WM_USER+203;
+    wparam = monitor number, zero-based
+    lparam = atom for string pointer
+    lresult = -1 for failure, 1 for success
+
+    UM_CREATERESOLUTION = WM_USER+204;
+    wparam = monitor number, zero-based
+    lparam = atom for string pointer
+    lresult = -1 for failure, 1 for success
+    Note: pass full PowerStrip timing string*; reboot is usually necessary to see if
+    the resolution is accepted by the display driver
+
+    UM_GETTIMING = WM_USER+205;
+    wparam = monitor number, zero-based
+    lresult = -1 for failure else GlobalAtom number identifiying the timing string*
+    Note: be sure to call GlobalDeleteAtom after reading the string associated with
+    the atom
+
+    UM_GETSETCLOCKS = WM_USER+206;
+    wparam = monitor number, zero-based
+    lparam = atom for string pointer
+    lresult = -1 for failure else GlobalAtom number identifiying the performance
+    string**
+    Note: pass full PowerStrip performance string** to set the clocks, and ull to
+    get clocks; be sure to call GlobalDeleteAtom after reading the string associated
+    with the atom
+
+    NegativeHorizontalPolarity = 0x02;
+    NegativeVerticalPolarity = 0x04;
+
+    *Timing string parameter definition:
+     1 = horizontal active pixels
+     2 = horizontal front porch
+     3 = horizontal sync width
+     4 = horizontal back porch
+     5 = vertical active pixels
+     6 = vertical front porch
+     7 = vertical sync width
+     8 = vertical back porch
+     9 = pixel clock in hertz
+    10 = timing flags, where bit:
+         1 = negative horizontal porlarity
+         2 = negative vertical polarity
+         3 = interlaced
+         5 = composite sync
+         7 = sync-on-green
+         all other bits reserved
+
+    **Performance string parameter definition:
+     1 = memory clock in hertz
+     2 = engine clock in hertz
+     3 = reserved
+     4 = reserved
+     5 = reserved
+     6 = reserved
+     7 = reserved
+     8 = reserved
+     9 = 2D memory clock in hertz (if different from 3D)
+    10 = 2D engine clock in hertz (if different from 3D) */
+
+#include <windows.h>
+#include <stdio.h>
+
+#include "custom_video_pstrip.h"
+#include "log.h"
+
+//============================================================
+//  CONSTANTS
+//============================================================
+
+#define UM_SETCUSTOMTIMING      (WM_USER+200)
+#define UM_SETREFRESHRATE       (WM_USER+201)
+#define UM_SETPOLARITY          (WM_USER+202)
+#define UM_REMOTECONTROL        (WM_USER+210)
+#define UM_SETGAMMARAMP         (WM_USER+203)
+#define UM_CREATERESOLUTION     (WM_USER+204)
+#define UM_GETTIMING            (WM_USER+205)
+#define UM_GETSETCLOCKS         (WM_USER+206)
+#define UM_SETCUSTOMTIMINGFAST  (WM_USER+211) // glitches vertical sync with PS 3.65 build 568
+
+#define NegativeHorizontalPolarity      0x02
+#define NegativeVerticalPolarity        0x04
+#define Interlace                       0x08
+
+#define HideTrayIcon                    0x00
+#define ShowTrayIcon                    0x01
+#define ClosePowerStrip                 0x63
+
+//============================================================
+//  pstrip_timing::pstrip_timing
+//============================================================
+
+pstrip_timing::pstrip_timing(char *device_name, custom_video_settings *vs)
+{
+	m_vs = *vs;
+	strcpy (m_device_name, device_name);
+	strcpy (m_ps_timing, m_vs.custom_timing);
+}
+
+//============================================================
+//  pstrip_timing::~pstrip_timing()
+//============================================================
+
+pstrip_timing::~pstrip_timing()
+{
+	ps_reset();
+}
+
+//============================================================
+//  pstrip_timing::init
+//============================================================
+
+bool pstrip_timing::init()
+{
+	m_monitor_index = ps_monitor_index(m_device_name);
+
+	hPSWnd = FindWindowA("TPShidden", NULL);
+
+	if (hPSWnd)
+	{
+		log_verbose("PStrip: PowerStrip found!\n");
+
+		// Save current settings
+		ps_get_monitor_timing(&m_timing_backup);
+
+		// If we have a -ps_timing string defined, use it as user defined modeline
+		if (strcmp(m_ps_timing, "auto"))
+		{
+			MonitorTiming timing;
+			if (ps_read_timing_string(m_ps_timing, &timing))
+			{
+				ps_pstiming_to_modeline(&timing, &m_user_mode);
+				m_user_mode.type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
+
+				char modeline_txt[256]={'\x00'};
+				log_verbose("SwitchRes: ps_string: %s (%s)\n", m_ps_timing, modeline_print(&m_user_mode, modeline_txt, MS_PARAMS));
+			}
+			else
+				log_verbose("Switchres: ps_timing string with invalid format\n");
+		}
+	}
+	else
+	{
+		log_verbose("PStrip: Could not get PowerStrip API interface\n");
+		return false;
+	}
+
+	return true;
+}
+
+//============================================================
+//  pstrip_timing::get_timing
+//============================================================
+
+bool pstrip_timing::get_timing(modeline *mode)
+{
+	// If we have an user defined mode (ps_timing), lock any non matching mode
+	if (m_user_mode.hactive)
+	{
+		if (mode->width != m_user_mode.width || mode->height != m_user_mode.height)
+		{
+			mode->type |= MODE_DISABLED;
+			return false;
+		}
+	}
+
+	modeline m_temp = {};
+	if (ps_get_modeline(&m_temp))
+	{
+		// We can only get the timings of the current desktop mode, so filter out anything different
+		if (m_temp.width == mode->width && m_temp.height == mode->height && m_temp.refresh == mode->refresh)
+		{
+			*mode = m_temp;
+		}
+		mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
+		return true;
+	}
+
+	return false;
+}
+
+//============================================================
+//  pstrip_timing::set_timing
+//============================================================
+
+bool pstrip_timing::set_timing(modeline *mode)
+{
+	// In case -ps_timing is provided, pass it as raw string
+	if (m_user_mode.hactive)
+		ps_set_monitor_timing_string(m_ps_timing);
+
+	// Otherwise pass it as modeline
+	else
+		ps_set_modeline(mode);
+
+	Sleep(100);
+	return true;
+}
+
+
+//============================================================
+//  pstrip_timing::ps_reset
+//============================================================
+
+int pstrip_timing::ps_reset()
+{
+	return ps_set_monitor_timing(&m_timing_backup);
+}
+
+//============================================================
+//  ps_get_modeline
+//============================================================
+
+int pstrip_timing::ps_get_modeline(modeline *modeline)
+{
+	MonitorTiming timing = {};
+
+	if (ps_get_monitor_timing(&timing))
+	{
+		ps_pstiming_to_modeline(&timing, modeline);
+		return 1;
+	}
+	else return 0;
+}
+
+//============================================================
+//  pstrip_timing::ps_set_modeline
+//============================================================
+
+bool pstrip_timing::ps_set_modeline(modeline *modeline)
+{
+	MonitorTiming timing = {};
+
+	if (!ps_modeline_to_pstiming(modeline, &timing))
+		return false;
+
+	timing.PixelClockInKiloHertz = ps_best_pclock(&timing, timing.PixelClockInKiloHertz);
+
+	if (ps_set_monitor_timing(&timing))
+		return true;
+	else
+		return false;
+}
+
+//============================================================
+//  pstrip_timing::ps_get_monitor_timing
+//============================================================
+
+int pstrip_timing::ps_get_monitor_timing(MonitorTiming *timing)
+{
+	LRESULT lresult;
+	char in[256];
+
+	if (!hPSWnd) return 0;
+
+	lresult = SendMessage(hPSWnd, UM_GETTIMING, m_monitor_index, 0);
+
+	if (lresult == -1)
+	{
+		log_verbose("PStrip: Could not get PowerStrip timing string\n");
+		return 0;
+	}
+
+	if (!GlobalGetAtomNameA(lresult, in, sizeof(in)))
+	{
+		log_verbose("PStrip: GlobalGetAtomName failed\n");
+		return 0;
+	}
+
+	log_verbose("PStrip: ps_get_monitor_timing(%d): %s\n", m_monitor_index, in);
+
+	ps_read_timing_string(in, timing);
+
+	GlobalDeleteAtom(lresult); // delete atom created by PowerStrip
+
+	return 1;
+}
+
+//============================================================
+//  pstrip_timing::ps_set_monitor_timing
+//============================================================
+
+int pstrip_timing::ps_set_monitor_timing(MonitorTiming *timing)
+{
+	LRESULT lresult;
+	ATOM atom;
+	char out[256];
+
+	if (!hPSWnd) return 0;
+
+	ps_fill_timing_string(out, timing);
+	atom = GlobalAddAtomA(out);
+
+	if (atom)
+	{
+		lresult = SendMessage(hPSWnd, UM_SETCUSTOMTIMING, m_monitor_index, atom);
+
+		if (lresult < 0)
+		{
+			log_verbose("PStrip: SendMessage failed\n");
+			GlobalDeleteAtom(atom);
+		}
+		else
+		{
+			log_verbose("PStrip: ps_set_monitor_timing(%d): %s\n", m_monitor_index, out);
+			return 1;
+		}
+	}
+	else log_verbose("PStrip: ps_set_monitor_timing atom creation failed\n");
+
+	return 0;
+}
+
+//============================================================
+//  pstrip_timing::ps_set_monitor_timing_string
+//============================================================
+
+int pstrip_timing::ps_set_monitor_timing_string(char *in)
+{
+	MonitorTiming timing = {};
+
+	ps_read_timing_string(in, &timing);
+	return ps_set_monitor_timing(&timing);
+}
+
+//============================================================
+//  pstrip_timing::ps_set_refresh
+//============================================================
+
+int pstrip_timing::ps_set_refresh(double vfreq)
+{
+	MonitorTiming timing = {};
+	int hht, vvt, new_vvt;
+	int desired_pClock;
+	int best_pClock;
+
+	memcpy(&timing, &m_timing_backup, sizeof(MonitorTiming));
+
+	hht = timing.HorizontalActivePixels
+		+ timing.HorizontalFrontPorch
+		+ timing.HorizontalSyncWidth
+		+ timing.HorizontalBackPorch;
+
+	vvt = timing.VerticalActivePixels
+		+ timing.VerticalFrontPorch
+		+ timing.VerticalSyncWidth
+		+ timing.VerticalBackPorch;
+
+	desired_pClock = hht * vvt * vfreq / 1000;
+	best_pClock = ps_best_pclock(&timing, desired_pClock);
+
+	new_vvt = best_pClock * 1000 / (vfreq * hht);
+
+	timing.VerticalBackPorch += (new_vvt - vvt);
+	timing.PixelClockInKiloHertz = best_pClock;
+
+	ps_set_monitor_timing(&timing);
+	ps_get_monitor_timing(&timing);
+
+	return 1;
+}
+
+//============================================================
+//  pstrip_timing::ps_best_pclock
+//============================================================
+
+int pstrip_timing::ps_best_pclock(MonitorTiming *timing, int desired_pclock)
+{
+	MonitorTiming timing_read = {};
+	int best_pclock = 0;
+
+	log_verbose("PStrip: ps_best_pclock(%d), getting stable dotclocks for %d...\n", m_monitor_index, desired_pclock);
+
+	for (int i = -50; i <= 50; i += 25)
+	{
+		timing->PixelClockInKiloHertz = desired_pclock + i;
+
+		ps_set_monitor_timing(timing);
+		ps_get_monitor_timing(&timing_read);
+
+		if (abs(timing_read.PixelClockInKiloHertz - desired_pclock) < abs(desired_pclock - best_pclock))
+			best_pclock = timing_read.PixelClockInKiloHertz;
+	}
+
+	log_verbose("PStrip: ps_best_pclock(%d), new dotclock: %d\n", m_monitor_index, best_pclock);
+
+	return best_pclock;
+}
+
+//============================================================
+//  pstrip_timing::ps_create_resolution
+//============================================================
+
+int pstrip_timing::ps_create_resolution(modeline *modeline)
+{
+	LRESULT     lresult;
+	ATOM        atom;
+	char        out[256];
+	MonitorTiming timing = {};
+
+	if (!hPSWnd) return 0;
+
+	ps_modeline_to_pstiming(modeline, &timing);
+
+	ps_fill_timing_string(out, &timing);
+	atom = GlobalAddAtomA(out);
+
+	if (atom)
+	{
+		lresult = SendMessage(hPSWnd, UM_CREATERESOLUTION, m_monitor_index, atom);
+
+		if (lresult < 0)
+			{
+				log_verbose("PStrip: SendMessage failed\n");
+				GlobalDeleteAtom(atom);
+			}
+			else
+			{
+				log_verbose("PStrip: ps_create_resolution(%d): %dx%d succeded \n",
+					modeline->width, modeline->height, m_monitor_index);
+				return 1;
+			}
+		}
+		else log_verbose("PStrip: ps_create_resolution atom creation failed\n");
+
+	return 0;
+}
+
+//============================================================
+//  pstrip_timing::ps_read_timing_string
+//============================================================
+
+bool pstrip_timing::ps_read_timing_string(char *in, MonitorTiming *timing)
+{
+	if (sscanf(in,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+		&timing->HorizontalActivePixels,
+		&timing->HorizontalFrontPorch,
+		&timing->HorizontalSyncWidth,
+		&timing->HorizontalBackPorch,
+		&timing->VerticalActivePixels,
+		&timing->VerticalFrontPorch,
+		&timing->VerticalSyncWidth,
+		&timing->VerticalBackPorch,
+		&timing->PixelClockInKiloHertz,
+		&timing->TimingFlags.w) == 10) return true;
+
+	return false;
+}
+
+//============================================================
+//  pstrip_timing::ps_fill_timing_string
+//============================================================
+
+void pstrip_timing::ps_fill_timing_string(char *out, MonitorTiming *timing)
+{
+	sprintf(out, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+		timing->HorizontalActivePixels,
+		timing->HorizontalFrontPorch,
+		timing->HorizontalSyncWidth,
+		timing->HorizontalBackPorch,
+		timing->VerticalActivePixels,
+		timing->VerticalFrontPorch,
+		timing->VerticalSyncWidth,
+		timing->VerticalBackPorch,
+		timing->PixelClockInKiloHertz,
+		timing->TimingFlags.w);
+}
+
+//============================================================
+//  pstrip_timing::ps_modeline_to_pstiming
+//============================================================
+
+bool pstrip_timing::ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing)
+{
+	if (modeline->pclock == 0 || modeline->hactive == 0 || modeline->vactive == 0)
+	{
+		log_verbose("ps_modeline_to_pstiming error: invalid modeline\n");
+		return false;
+	}
+
+	timing->HorizontalActivePixels = modeline->hactive;
+	timing->HorizontalFrontPorch = modeline->hbegin - modeline->hactive;
+	timing->HorizontalSyncWidth = modeline->hend - modeline->hbegin;
+	timing->HorizontalBackPorch = modeline->htotal - modeline->hend;
+
+	timing->VerticalActivePixels = modeline->vactive;
+	timing->VerticalFrontPorch = modeline->vbegin - modeline->vactive;
+	timing->VerticalSyncWidth = modeline->vend - modeline->vbegin;
+	timing->VerticalBackPorch = modeline->vtotal - modeline->vend;
+
+	timing->PixelClockInKiloHertz = modeline->pclock / 1000;
+
+	if (modeline->hsync == 0)
+		timing->TimingFlags.w |= NegativeHorizontalPolarity;
+	if (modeline->vsync == 0)
+		timing->TimingFlags.w |= NegativeVerticalPolarity;
+	if (modeline->interlace)
+		timing->TimingFlags.w |= Interlace;
+
+	return true;
+}
+
+//============================================================
+//  pstrip_timing::ps_pstiming_to_modeline
+//============================================================
+
+int pstrip_timing::ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline)
+{
+	modeline->hactive = timing->HorizontalActivePixels;
+	modeline->hbegin = modeline->hactive + timing->HorizontalFrontPorch;
+	modeline->hend = modeline->hbegin + timing->HorizontalSyncWidth;
+	modeline->htotal = modeline->hend + timing->HorizontalBackPorch;
+
+	modeline->vactive = timing->VerticalActivePixels;
+	modeline->vbegin = modeline->vactive + timing->VerticalFrontPorch;
+	modeline->vend = modeline->vbegin + timing->VerticalSyncWidth;
+	modeline->vtotal = modeline->vend + timing->VerticalBackPorch;
+
+	modeline->width = modeline->hactive;
+	modeline->height = modeline->vactive;
+
+	modeline->pclock = timing->PixelClockInKiloHertz * 1000;
+
+	if (!(timing->TimingFlags.w & NegativeHorizontalPolarity))
+		modeline->hsync = 1;
+
+	if (!(timing->TimingFlags.w & NegativeVerticalPolarity))
+		modeline->vsync = 1;
+
+	if ((timing->TimingFlags.w & Interlace))
+		modeline->interlace = 1;
+
+	modeline->hfreq = modeline->pclock / modeline->htotal;
+	modeline->vfreq = modeline->hfreq / modeline->vtotal * (modeline->interlace?2:1);
+	modeline->refresh = int(modeline->vfreq);
+
+	return 0;
+}
+
+//============================================================
+//  pstrip_timing::ps_monitor_index
+//============================================================
+
+int pstrip_timing::ps_monitor_index (const char *display_name)
+{
+	int monitor_index = 0;
+	char sub_index[2];
+
+	sub_index[0] = display_name[strlen(display_name)-1];
+	sub_index[1] = 0;
+	if (sscanf(sub_index,"%d", &monitor_index) == 1)
+		monitor_index --;
+
+	return monitor_index;
+}
+
+//============================================================
+//  pstrip_timing::update_mode
+//============================================================
+
+bool pstrip_timing::update_mode(modeline *mode)
+{
+	if (!set_timing(mode))
+	{
+		return false;
+	}
+
+	mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
+	return true;
+}
diff --git a/3rdparty/switchres/custom_video_pstrip.h b/3rdparty/switchres/custom_video_pstrip.h
new file mode 100644
index 00000000000..d27a2b7326b
--- /dev/null
+++ b/3rdparty/switchres/custom_video_pstrip.h
@@ -0,0 +1,83 @@
+/**************************************************************
+
+     custom_video_powerstrip.h - PowerStrip interface routines
+
+     ---------------------------------------------------------
+
+     Switchres   Modeline generation engine for emulation
+
+     License     GPL-2.0+
+     Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                           Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include "custom_video.h"
+
+//============================================================
+//  TYPE DEFINITIONS
+//============================================================
+
+typedef struct
+{
+	int HorizontalActivePixels;
+	int HorizontalFrontPorch;
+	int HorizontalSyncWidth;
+	int HorizontalBackPorch;
+	int VerticalActivePixels;
+	int VerticalFrontPorch;
+	int VerticalSyncWidth;
+	int VerticalBackPorch;
+	int PixelClockInKiloHertz;
+	union
+	{
+		int w;
+		struct
+		{
+			unsigned :1;
+			unsigned HorizontalPolarityNegative:1;
+			unsigned VerticalPolarityNegative:1;
+			unsigned :29;
+		} b;
+	} TimingFlags;
+} MonitorTiming;
+
+
+class pstrip_timing : public custom_video
+{
+	public:
+		pstrip_timing(char *device_name, custom_video_settings *vs);
+		~pstrip_timing();
+		const char *api_name() { return "PowerStrip"; }
+		bool init();
+		int caps() { return CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_SCAN_EDITABLE | CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE; }
+
+		bool update_mode(modeline *mode);
+
+		bool get_timing(modeline *mode);
+		bool set_timing(modeline *m);
+
+	private:
+
+		int ps_reset();
+		int ps_get_modeline(modeline *modeline);
+		bool ps_set_modeline(modeline *modeline);
+		int ps_get_monitor_timing(MonitorTiming *timing);
+		int ps_set_monitor_timing(MonitorTiming *timing);
+		int ps_set_monitor_timing_string(char *in);
+		int ps_set_refresh(double vfreq);
+		int ps_best_pclock(MonitorTiming *timing, int desired_pclock);
+		int ps_create_resolution(modeline *modeline);
+		bool ps_read_timing_string(char *in, MonitorTiming *timing);
+		void ps_fill_timing_string(char *out, MonitorTiming *timing);
+		bool ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing);
+		int ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline);
+		int ps_monitor_index (const char *display_name);
+
+		char m_device_name[32];
+		char m_ps_timing[256];
+		int m_monitor_index = 0;
+		modeline m_user_mode = {};
+		MonitorTiming m_timing_backup = {};
+		HWND hPSWnd = 0;
+};
diff --git a/3rdparty/switchres/custom_video_xrandr.cpp b/3rdparty/switchres/custom_video_xrandr.cpp
new file mode 100644
index 00000000000..83b163e6811
--- /dev/null
+++ b/3rdparty/switchres/custom_video_xrandr.cpp
@@ -0,0 +1,1204 @@
+/**************************************************************
+
+   custom_video_xrandr.cpp - Linux XRANDR video management layer
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <exception>
+#include <dlfcn.h>
+#include <string.h>
+#include "custom_video_xrandr.h"
+#include "log.h"
+
+//============================================================
+//  library functions
+//============================================================
+
+#define XRRAddOutputMode p_XRRAddOutputMode
+#define XRRConfigCurrentConfiguration p_XRRConfigCurrentConfiguration
+#define XRRCreateMode p_XRRCreateMode
+#define XRRDeleteOutputMode p_XRRDeleteOutputMode
+#define XRRDestroyMode p_XRRDestroyMode
+#define XRRFreeCrtcInfo p_XRRFreeCrtcInfo
+#define XRRFreeOutputInfo p_XRRFreeOutputInfo
+#define XRRFreeScreenConfigInfo p_XRRFreeScreenConfigInfo
+#define XRRFreeScreenResources p_XRRFreeScreenResources
+#define XRRGetCrtcInfo p_XRRGetCrtcInfo
+#define XRRGetOutputInfo p_XRRGetOutputInfo
+#define XRRGetScreenInfo p_XRRGetScreenInfo
+#define XRRGetScreenResourcesCurrent p_XRRGetScreenResourcesCurrent
+#define XRRQueryVersion p_XRRQueryVersion
+#define XRRSetCrtcConfig p_XRRSetCrtcConfig
+#define XRRSetScreenSize p_XRRSetScreenSize
+#define XRRGetScreenSizeRange p_XRRGetScreenSizeRange
+
+#define XCloseDisplay p_XCloseDisplay
+#define XGrabServer p_XGrabServer
+#define XOpenDisplay p_XOpenDisplay
+#define XSync p_XSync
+#define XUngrabServer p_XUngrabServer
+#define XSetErrorHandler p_XSetErrorHandler
+#define XClearWindow p_XClearWindow
+#define XFillRectangle p_XFillRectangle
+#define XCreateGC p_XCreateGC
+
+//============================================================
+//  error_handler
+//  xorg error handler (static)
+//============================================================
+
+int xrandr_timing::ms_xerrors = 0;
+int xrandr_timing::ms_xerrors_flag = 0;
+static int (*old_error_handler)(Display *, XErrorEvent *);
+
+static __typeof__(XGetErrorText) *p_XGetErrorText;
+#define XGetErrorText p_XGetErrorText
+
+static int error_handler(Display *dpy, XErrorEvent *err)
+{
+	char buf[64];
+	XGetErrorText(dpy, err->error_code, buf, 64);
+	buf[0] = '\0';
+	xrandr_timing::ms_xerrors |= xrandr_timing::ms_xerrors_flag;
+	old_error_handler(dpy, err);
+	log_error("XRANDR: <-> (error_handler) [ERROR] %s error code %d flags %02x\n", buf, err->error_code, xrandr_timing::ms_xerrors);
+	return 0;
+}
+
+//============================================================
+//  id for class object (static)
+//============================================================
+
+static int s_id = 0;
+
+//============================================================
+//  screen management exclusivity array (static)
+//============================================================
+
+static int s_total_managed_screen = 0;
+static int *sp_shared_screen_manager = NULL;
+
+//============================================================
+//  desktop screen positions (static)
+//============================================================
+
+static XRRCrtcInfo *sp_desktop_crtc = NULL;
+
+//============================================================
+//  xrandr_timing::xrandr_timing
+//============================================================
+
+xrandr_timing::xrandr_timing(char *device_name, custom_video_settings *vs)
+{
+	m_vs = *vs;
+
+	// Increment id for each new screen
+	m_id = ++s_id;
+
+	log_verbose("XRANDR: <%d> (xrandr_timing) creation (%s)\n", m_id, device_name);
+	// Copy screen device name and limit size
+	if ((strlen(device_name) + 1) > 32)
+	{
+		strncpy(m_device_name, device_name, 31);
+		log_error("XRANDR: <%d> (xrandr_timing) [ERROR] the device name is too long it has been trucated to %s\n", m_id, m_device_name);
+	}
+	else
+		strcpy(m_device_name, device_name);
+
+	if (m_vs.screen_reordering)
+	{
+		if (m_id == 1)
+			m_enable_screen_reordering = 1;
+	}
+	else if (m_vs.screen_compositing)
+		m_enable_screen_compositing = 1;
+
+	log_verbose("XRANDR: <%d> (xrandr_timing) checking X availability (early stub)\n", m_id);
+
+	m_x11_handle = dlopen("libX11.so", RTLD_NOW);
+
+	if (m_x11_handle)
+	{
+		p_XOpenDisplay = (__typeof__(XOpenDisplay)) dlsym(m_x11_handle, "XOpenDisplay");
+		if (p_XOpenDisplay == NULL)
+		{
+			log_error("XRANDR: <%d> (xrandr_timing) [ERROR] missing func %s in %s\n", m_id, "XOpenDisplay", "X11_LIBRARY");
+			throw std::exception();
+		}
+		else
+		{
+			if (!XOpenDisplay(NULL))
+			{
+				log_verbose("XRANDR: <%d> (xrandr_timing) X server not found\n", m_id);
+				throw std::exception();
+			}
+		}
+	}
+	else
+	{
+		log_error("XRANDR: <%d> (xrandr_timing) [ERROR] missing %s library\n", m_id, "X11_LIBRARY");
+		throw std::exception();
+	}
+
+	s_total_managed_screen++;
+}
+
+//============================================================
+//  xrandr_timing::~xrandr_timing
+//============================================================
+
+xrandr_timing::~xrandr_timing()
+{
+	s_total_managed_screen--;
+	if (s_total_managed_screen == 0)
+	{
+		s_id = 0;
+
+		if (sp_desktop_crtc)
+			delete[]sp_desktop_crtc;
+
+		if (sp_shared_screen_manager)
+			delete[]sp_shared_screen_manager;
+
+		// Restore default desktop background
+		XClearWindow(m_pdisplay, m_root);
+	}
+
+	// Free the display
+	if (m_pdisplay != NULL)
+		XCloseDisplay(m_pdisplay);
+
+	// close Xrandr library
+	if (m_xrandr_handle)
+		dlclose(m_xrandr_handle);
+
+	// close X11 library
+	if (m_x11_handle)
+		dlclose(m_x11_handle);
+}
+
+//============================================================
+//  xrandr_timing::init
+//============================================================
+
+bool xrandr_timing::init()
+{
+	log_verbose("XRANDR: <%d> (init) loading Xrandr library\n", m_id);
+	if (!m_xrandr_handle)
+		m_xrandr_handle = dlopen("libXrandr.so", RTLD_NOW);
+	if (m_xrandr_handle)
+	{
+		p_XRRAddOutputMode = (__typeof__(XRRAddOutputMode)) dlsym(m_xrandr_handle, "XRRAddOutputMode");
+		if (p_XRRAddOutputMode == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRAddOutputMode", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRConfigCurrentConfiguration = (__typeof__(XRRConfigCurrentConfiguration)) dlsym(m_xrandr_handle, "XRRConfigCurrentConfiguration");
+		if (p_XRRConfigCurrentConfiguration == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRConfigCurrentConfiguration", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRCreateMode = (__typeof__(XRRCreateMode)) dlsym(m_xrandr_handle, "XRRCreateMode");
+		if (p_XRRCreateMode == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRCreateMode", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRDeleteOutputMode = (__typeof__(XRRDeleteOutputMode)) dlsym(m_xrandr_handle, "XRRDeleteOutputMode");
+		if (p_XRRDeleteOutputMode == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRDeleteOutputMode", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRDestroyMode = (__typeof__(XRRDestroyMode)) dlsym(m_xrandr_handle, "XRRDestroyMode");
+		if (p_XRRDestroyMode == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRDestroyMode", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRFreeCrtcInfo = (__typeof__(XRRFreeCrtcInfo)) dlsym(m_xrandr_handle, "XRRFreeCrtcInfo");
+		if (p_XRRFreeCrtcInfo == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeCrtcInfo", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRFreeOutputInfo = (__typeof__(XRRFreeOutputInfo)) dlsym(m_xrandr_handle, "XRRFreeOutputInfo");
+		if (p_XRRFreeOutputInfo == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeOutputInfo", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRFreeScreenConfigInfo = (__typeof__(XRRFreeScreenConfigInfo)) dlsym(m_xrandr_handle, "XRRFreeScreenConfigInfo");
+		if (p_XRRFreeScreenConfigInfo == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeScreenConfigInfo", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRFreeScreenResources = (__typeof__(XRRFreeScreenResources)) dlsym(m_xrandr_handle, "XRRFreeScreenResources");
+		if (p_XRRFreeScreenResources == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRFreeScreenResources", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRGetCrtcInfo = (__typeof__(XRRGetCrtcInfo)) dlsym(m_xrandr_handle, "XRRGetCrtcInfo");
+		if (p_XRRGetCrtcInfo == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetCrtcInfo", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRGetOutputInfo = (__typeof__(XRRGetOutputInfo)) dlsym(m_xrandr_handle, "XRRGetOutputInfo");
+		if (p_XRRGetOutputInfo == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetOutputInfo", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRGetScreenInfo = (__typeof__(XRRGetScreenInfo)) dlsym(m_xrandr_handle, "XRRGetScreenInfo");
+		if (p_XRRGetScreenInfo == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetScreenInfo", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRGetScreenResourcesCurrent = (__typeof__(XRRGetScreenResourcesCurrent)) dlsym(m_xrandr_handle, "XRRGetScreenResourcesCurrent");
+		if (p_XRRGetScreenResourcesCurrent == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRGetScreenResourcesCurrent", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRQueryVersion = (__typeof__(XRRQueryVersion)) dlsym(m_xrandr_handle, "XRRQueryVersion");
+		if (p_XRRQueryVersion == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRQueryVersion", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRSetCrtcConfig = (__typeof__(XRRSetCrtcConfig)) dlsym(m_xrandr_handle, "XRRSetCrtcConfig");
+		if (p_XRRSetCrtcConfig == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRSetCrtcConfig", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRSetScreenSize = (__typeof__(XRRSetScreenSize)) dlsym(m_xrandr_handle, "XRRSetScreenSize");
+		if (p_XRRSetScreenSize == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRSetScreenSize", "XRANDR_LIBRARY");
+			return false;
+		}
+
+		p_XRRGetScreenSizeRange = (__typeof__(XRRGetScreenSizeRange)) dlsym(m_xrandr_handle, "XRRGetScreenSizeRange");
+		if (p_XRRGetScreenSizeRange == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XRRSetScreenSize", "XRANDR_LIBRARY");
+			return false;
+		}
+	}
+	else
+	{
+		log_error("XRANDR: <%d> (init) [ERROR] missing %s library\n", m_id, "XRANDR_LIBRARY");
+		return false;
+	}
+
+	log_verbose("XRANDR: <%d> (init) loading X11 library\n", m_id);
+	if (!m_x11_handle)
+		m_x11_handle = dlopen("libX11.so", RTLD_NOW);
+	if (m_x11_handle)
+	{
+		p_XCloseDisplay = (__typeof__(XCloseDisplay)) dlsym(m_x11_handle, "XCloseDisplay");
+		if (p_XCloseDisplay == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XCloseDisplay", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XGrabServer = (__typeof__(XGrabServer)) dlsym(m_x11_handle, "XGrabServer");
+		if (p_XGrabServer == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XGrabServer", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XOpenDisplay = (__typeof__(XOpenDisplay)) dlsym(m_x11_handle, "XOpenDisplay");
+		if (p_XOpenDisplay == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XOpenDisplay", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XSync = (__typeof__(XSync)) dlsym(m_x11_handle, "XSync");
+		if (p_XSync == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XSync", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XUngrabServer = (__typeof__(XUngrabServer)) dlsym(m_x11_handle, "XUngrabServer");
+		if (p_XUngrabServer == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XUngrabServer", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XSetErrorHandler = (__typeof__(XSetErrorHandler)) dlsym(m_x11_handle, "XSetErrorHandler");
+		if (p_XSetErrorHandler == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XSetErrorHandler", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XGetErrorText = (__typeof__(XGetErrorText)) dlsym(m_x11_handle, "XGetErrorText");
+		if (p_XGetErrorText == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s\n", m_id, "XGetErrorText", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XClearWindow = (__typeof__(XClearWindow)) dlsym(m_x11_handle, "XClearWindow");
+		if (p_XClearWindow == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XClearWindow", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XFillRectangle = (__typeof__(XFillRectangle)) dlsym(m_x11_handle, "XFillRectangle");
+		if (p_XFillRectangle == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XFillRectangle", "X11_LIBRARY");
+			return false;
+		}
+
+		p_XCreateGC = (__typeof__(XCreateGC)) dlsym(m_x11_handle, "XCreateGC");
+		if (p_XCreateGC == NULL)
+		{
+			log_error("XRANDR: <%d> (init) [ERROR] missing func %s in %s", m_id, "XCreateGC", "X11_LIBRARY");
+			return false;
+		}
+	}
+	else
+	{
+		log_error("XRANDR: <%d> (init) [ERROR] missing %s library\n", m_id, "X11_LIBRARY");
+		return false;
+	}
+
+	// Select current display and root window
+	// m_pdisplay is global to reduce open/close calls, resource is freed when class is destroyed
+	if (!m_pdisplay)
+		m_pdisplay = XOpenDisplay(NULL);
+
+	if (!m_pdisplay)
+	{
+		log_error("XRANDR: <%d> (init) [ERROR] failed to connect to the X server\n", m_id);
+		return false;
+	}
+
+	// Display XRANDR version
+	int major_version, minor_version;
+	XRRQueryVersion(m_pdisplay, &major_version, &minor_version);
+	log_verbose("XRANDR: <%d> (init) version %d.%d\n", m_id, major_version, minor_version);
+
+	if (major_version < 1 || (major_version == 1 && minor_version < 2))
+	{
+		log_error("XRANDR: <%d> (init) [ERROR] Xrandr version 1.2 or above is required\n", m_id);
+		return false;
+	}
+
+	// screen_pos defines screen position, 0 is default first screen position and equivalent to 'auto'
+	int screen_pos = -1;
+	bool detected = false;
+
+	// Handle the screen name, "auto", "screen[0-9]" and XRANDR device name
+	if (strlen(m_device_name) == 7 && !strncmp(m_device_name, "screen", 6) && m_device_name[6] >= '0' && m_device_name[6] <= '9')
+		screen_pos = m_device_name[6] - '0';
+	else if (strlen(m_device_name) == 1 && m_device_name[0] >= '0' && m_device_name[0] <= '9')
+		screen_pos = m_device_name[0] - '0';
+
+	if (ScreenCount(m_pdisplay) > 1)
+		log_verbose("XRANDR: <%d> (init) [WARNING] screen count is %d, unpredictable behavior to be expected\n", m_id, ScreenCount(m_pdisplay));
+
+	for (int screen = 0; !detected && screen < ScreenCount(m_pdisplay); screen++)
+	{
+		log_verbose("XRANDR: <%d> (init) check screen number %d\n", m_id, screen);
+		m_screen = screen;
+		m_root = RootWindow(m_pdisplay, screen);
+
+		XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+
+		if (m_id == 1)
+		{
+			// Prepare the shared screen array
+			sp_shared_screen_manager = new int[resources->noutput];
+			for (int o = 0; o < resources->noutput; o++)
+				sp_shared_screen_manager[o] = 0;
+
+			// Save all active crtc positions
+			sp_desktop_crtc = new XRRCrtcInfo[resources->ncrtc];
+			for (int c = 0; c < resources->ncrtc; c++)
+				memcpy(&sp_desktop_crtc[c], XRRGetCrtcInfo(m_pdisplay, resources, resources->crtcs[c]), sizeof(XRRCrtcInfo));
+		}
+
+		// Get default screen rotation from screen configuration
+		XRRScreenConfiguration *sc = XRRGetScreenInfo(m_pdisplay, m_root);
+		XRRConfigCurrentConfiguration(sc, &m_desktop_rotation);
+		XRRFreeScreenConfigInfo(sc);
+
+		Rotation current_rotation = 0;
+		int output_position = 0;
+		for (int o = 0; o < resources->noutput; o++)
+		{
+			XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[o]);
+			if (!output_info)
+			{
+				log_error("XRANDR: <%d> (init) [ERROR] could not get output 0x%x information\n", m_id, (unsigned int)resources->outputs[o]);
+				continue;
+			}
+			// Check all connected output
+			if (m_desktop_output == -1 && output_info->connection == RR_Connected && output_info->crtc)
+			{
+
+				if (!strcmp(m_device_name, "auto") || !strcmp(m_device_name, output_info->name) || output_position == screen_pos)
+				{
+					// store the output connector
+					m_desktop_output = o;
+
+					// store screen minium and maximum resolutions
+					int min_width;
+					int max_width;
+					int min_height;
+					int max_height;
+					XRRGetScreenSizeRange (m_pdisplay, m_root, &min_width, &min_height, &max_width, &max_height);
+					m_min_width = min_width;
+					m_max_width = max_width;
+					m_min_height = min_height;
+					m_max_height = max_height;
+
+					if (sp_shared_screen_manager[m_desktop_output] == 0)
+					{
+						sp_shared_screen_manager[m_desktop_output] = m_id;
+						m_managed = 1;
+					}
+
+					// identify the current modeline and rotation
+					XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
+					current_rotation = crtc_info->rotation;
+					for (int m = 0; m < resources->nmode && m_desktop_mode.id == 0; m++)
+					{
+						// Get screen mode
+						if (crtc_info->mode == resources->modes[m].id)
+						{
+							m_desktop_mode = resources->modes[m];
+							m_last_crtc = *crtc_info;
+						}
+					}
+					XRRFreeCrtcInfo(crtc_info);
+
+					// check screen rotation (left or right)
+					if (current_rotation & 0xe)
+					{
+						m_crtc_flags = MODE_ROTATED;
+						log_verbose("XRANDR: <%d> (init) desktop rotation is %s\n", m_id, (current_rotation & 0x2) ? "left" : ((current_rotation & 0x8) ? "right" : "inverted"));
+					}
+				}
+				output_position++;
+			}
+			log_verbose("XRANDR: <%d> (init) check output connector '%s' active %d crtc %d %s\n", m_id, output_info->name, output_info->connection == RR_Connected ? 1 : 0, output_info->crtc ? 1 : 0, m_desktop_output == o ? (m_managed ? "[SELECTED]" : "[UNMANAGED]") : "");
+			XRRFreeOutputInfo(output_info);
+		}
+		XRRFreeScreenResources(resources);
+
+		// Check if screen has been detected
+		detected = m_desktop_output != -1;
+	}
+
+	if (!detected)
+		log_error("XRANDR: <%d> (init) [ERROR] no screen detected\n", m_id);
+
+	else if (m_enable_screen_reordering)
+	{
+		// Global screen placement
+		modeline mode = {};
+		mode.type = MODE_DESKTOP;
+		set_timing(&mode, XRANDR_ENABLE_SCREEN_REORDERING);
+	}
+
+	return detected;
+}
+
+//============================================================
+//  xrandr_timing::update_mode
+//============================================================
+
+bool xrandr_timing::update_mode(modeline *mode)
+{
+	if (!mode)
+		return false;
+
+	// Handle no screen detected case
+	if (m_desktop_output == -1)
+	{
+		log_error("XRANDR: <%d> (update_mode) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (!delete_mode(mode))
+	{
+		log_error("XRANDR: <%d> (update_mode) [ERROR] delete operation not successful", m_id);
+		return false;
+	}
+
+	if (!add_mode(mode))
+	{
+		log_error("XRANDR: <%d> (update_mode) [ERROR] add operation not successful", m_id);
+		return false;
+	}
+
+	return true;
+}
+
+//============================================================
+//  xrandr_timing::add_mode
+//============================================================
+
+bool xrandr_timing::add_mode(modeline *mode)
+{
+	if (!mode)
+		return false;
+
+	// Handle no screen detected case
+	if (m_desktop_output == -1)
+	{
+		log_error("XRANDR: <%d> (add_mode) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (!m_managed)
+	{
+		log_error("XRANDR: <%d> (add_mode) [WARNING] this screen is managed by <%d>\n", m_id, sp_shared_screen_manager[m_desktop_output]);
+		return false;
+	}
+
+	// Check if mode is available from the plaftform_data mode id
+	XRRModeInfo *pxmode = find_mode(mode);
+	if (pxmode != NULL)
+	{
+		log_error("XRANDR: <%d> (add_mode) [WARNING] mode already exist\n", m_id);
+		return true;
+	}
+
+	// Create specific mode name
+	char name[48];
+	sprintf(name, "SR-%d_%dx%d@%.02f%s", m_id, mode->hactive, mode->vactive, mode->vfreq, mode->interlace ? "i" : "");
+
+	// Check if mode is available from the SR name (should not be the case, otherwise it means that we recevied twice the same mode request)
+	pxmode = find_mode_by_name(name);
+	if (pxmode != NULL)
+	{
+		log_error("XRANDR: <%d> (add_mode) [WARNING] mode already exist (duplicate request)\n", m_id);
+		mode->platform_data = pxmode->id;
+		return true;
+	}
+
+	log_verbose("XRANDR: <%d> (add_mode) create mode %s\n", m_id, name);
+
+	// Setup the xrandr mode structure
+	XRRModeInfo xmode = {};
+
+	xmode.name       = name;
+	xmode.nameLength = strlen(name);
+	xmode.dotClock   = mode->pclock;
+	xmode.width      = mode->hactive;
+	xmode.hSyncStart = mode->hbegin;
+	xmode.hSyncEnd   = mode->hend;
+	xmode.hTotal     = mode->htotal;
+	xmode.height     = mode->vactive;
+	xmode.vSyncStart = mode->vbegin;
+	xmode.vSyncEnd   = mode->vend;
+	xmode.vTotal     = mode->vtotal;
+	xmode.modeFlags  = (mode->interlace ? RR_Interlace : 0) | (mode->doublescan ? RR_DoubleScan : 0) | (mode->hsync ? RR_HSyncPositive : RR_HSyncNegative) | (mode->vsync ? RR_VSyncPositive : RR_VSyncNegative);
+	xmode.hSkew      = 0;
+
+	mode->type |= CUSTOM_VIDEO_TIMING_XRANDR;
+
+	// Create the modeline
+	XSync(m_pdisplay, False);
+	ms_xerrors = 0;
+	ms_xerrors_flag = 0x01;
+	old_error_handler = XSetErrorHandler(error_handler);
+	RRMode gmid = XRRCreateMode(m_pdisplay, m_root, &xmode);
+	XSync(m_pdisplay, False);
+	XSetErrorHandler(old_error_handler);
+	if (ms_xerrors & ms_xerrors_flag)
+	{
+		log_error("XRANDR: <%d> (add_mode) [ERROR] in %s\n", m_id, "XRRCreateMode");
+		return false;
+	}
+
+	mode->platform_data = gmid;
+
+	// Add new modeline to primary output
+	XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+
+	XSync(m_pdisplay, False);
+	ms_xerrors_flag = 0x02;
+	old_error_handler = XSetErrorHandler(error_handler);
+	XRRAddOutputMode(m_pdisplay, resources->outputs[m_desktop_output], mode->platform_data);
+	XSync(m_pdisplay, False);
+	XSetErrorHandler(old_error_handler);
+
+	XRRFreeScreenResources(resources);
+
+	if (ms_xerrors & ms_xerrors_flag)
+	{
+		log_error("XRANDR: <%d> (add_mode) [ERROR] in %s\n", m_id, "XRRAddOutputMode");
+
+		// remove unlinked modeline
+		if (mode->platform_data)
+		{
+			log_error("XRANDR: <%d> (add_mode) [ERROR] remove mode [%04lx]\n", m_id, mode->platform_data);
+			XRRDestroyMode(m_pdisplay, mode->platform_data);
+			mode->platform_data = 0;
+		}
+	}
+	else
+		log_verbose("XRANDR: <%d> (add_mode) mode %04lx %dx%d refresh %.6f added\n", m_id, mode->platform_data, mode->hactive, mode->vactive, mode->vfreq);
+
+	return ms_xerrors == 0;
+}
+
+//============================================================
+//  xrandr_timing::find_mode_by_name
+//============================================================
+
+XRRModeInfo *xrandr_timing::find_mode_by_name(char *name)
+{
+	XRRModeInfo *pxmode = NULL;
+	XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+
+	// use SR name to return the mode
+	for (int m = 0; m < resources->nmode; m++)
+	{
+		if (strcmp(resources->modes[m].name, name) == 0)
+		{
+			pxmode = &resources->modes[m];
+			break;
+		}
+	}
+
+	XRRFreeScreenResources(resources);
+
+	return pxmode;
+}
+
+//============================================================
+//  xrandr_timing::find_mode
+//============================================================
+
+XRRModeInfo *xrandr_timing::find_mode(modeline *mode)
+{
+	XRRModeInfo *pxmode = NULL;
+	XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+
+	// use platform_data (mode id) to return the mode
+	for (int m = 0; m < resources->nmode; m++)
+	{
+		if (mode->platform_data == resources->modes[m].id)
+		{
+			pxmode = &resources->modes[m];
+			break;
+		}
+	}
+
+	XRRFreeScreenResources(resources);
+
+	return pxmode;
+}
+
+//============================================================
+//  xrandr_timing::set_timing
+//============================================================
+
+bool xrandr_timing::set_timing(modeline *mode)
+{
+	if (m_enable_screen_compositing)
+		return set_timing(mode, 0);
+
+	return set_timing(mode, XRANDR_DISABLE_CRTC_RELOCATION);
+}
+
+//============================================================
+//  xrandr_timing::set_timing
+//============================================================
+
+bool xrandr_timing::set_timing(modeline *mode, int flags)
+{
+	// Handle no screen detected case
+	if (m_desktop_output == -1)
+	{
+		log_error("XRANDR: <%d> (set_timing) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (!m_managed)
+	{
+		log_error("XRANDR: <%d> (set_timing) [WARNING] this screen is managed by <%d>\n", m_id, sp_shared_screen_manager[m_desktop_output]);
+		return false;
+	}
+
+	if (m_id != 1 && (flags & XRANDR_ENABLE_SCREEN_REORDERING))
+		flags = XRANDR_DISABLE_CRTC_RELOCATION; // only master can do global screen preparation
+
+	XRRModeInfo *pxmode = NULL;
+
+	if (mode->type & MODE_DESKTOP)
+		pxmode = &m_desktop_mode;
+	else
+		pxmode = find_mode(mode);
+
+	if (pxmode == NULL)
+	{
+		log_error("XRANDR: <%d> (set_timing) [ERROR] mode not found\n", m_id);
+		return false;
+	}
+
+	// Use xrandr to switch to new mode.
+	XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+	XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[m_desktop_output]);
+	XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
+
+	if (flags & XRANDR_DISABLE_CRTC_RELOCATION)
+		log_verbose("XRANDR: <%d> (set_timing) DISABLE crtc relocation\n", m_id);
+
+	if (flags & XRANDR_ENABLE_SCREEN_REORDERING)
+		log_verbose("XRANDR: <%d> (set_timing) GLOBAL desktop screen preparation\n", m_id);
+	else if (m_last_crtc.mode == crtc_info->mode && m_last_crtc.x == crtc_info->x && m_last_crtc.y == crtc_info->y && pxmode->id == crtc_info->mode)
+		log_verbose("XRANDR: <%d> (set_timing) requested mode is already active [%04lx] %ux%u+%d+%d\n", m_id, crtc_info->mode, crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
+	else if (m_last_crtc.mode != crtc_info->mode)
+	{
+		log_verbose("XRANDR: <%d> (set_timing) [WARNING] unexpected active modeline detected (last:[%04lx] now:[%04lx] %ux%u+%d+%d want:[%04lx])\n", m_id, m_last_crtc.mode, crtc_info->mode, crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y, pxmode->id);
+		*crtc_info = m_last_crtc;
+	}
+
+	// Grab X server to prevent unwanted interaction from the window manager
+	XGrabServer(m_pdisplay);
+
+	unsigned int width = m_min_width;
+	unsigned int height = m_min_height;
+
+	unsigned int active_crtc = 0;
+
+	unsigned int reordering_last_y = 0;
+
+	ms_xerrors = 0;
+
+	XRRCrtcInfo *global_crtc = new XRRCrtcInfo[resources->ncrtc];
+	XRRCrtcInfo *original_crtc = new XRRCrtcInfo[resources->ncrtc];
+
+	// caculate necessary screen size and of crtc neighborhood if they have at least one side aligned with the mode changed crtc
+	for (int c = 0; c < resources->ncrtc; c++)
+	{
+		// Prepare crtc references
+		memcpy(&original_crtc[c], XRRGetCrtcInfo(m_pdisplay, resources, resources->crtcs[c]), sizeof(XRRCrtcInfo));
+		memcpy(&global_crtc[c], XRRGetCrtcInfo(m_pdisplay, resources, resources->crtcs[c]), sizeof(XRRCrtcInfo));
+		// Original state
+		XRRCrtcInfo *crtc_info0 = &original_crtc[c];
+		// Modified state
+		XRRCrtcInfo *crtc_info1 = &global_crtc[c];
+		// clear timestamp
+		crtc_info1->timestamp = 0;
+
+		// Skip unused crtc
+		if (output_info->crtc != 0 && crtc_info0->mode != 0)
+		{
+			if (flags & XRANDR_ENABLE_SCREEN_REORDERING)
+			{
+				// Relocate all crtcs
+				// Super resolution placement, vertical stacking, reserved XRANDR_REORDERING_MAXIMUM_HEIGHT pixels
+				crtc_info1->x = 0;
+				crtc_info1->y = reordering_last_y;
+				if (crtc_info1->height > XRANDR_REORDERING_MAXIMUM_HEIGHT)
+					reordering_last_y += crtc_info1->height;
+				else
+					reordering_last_y += XRANDR_REORDERING_MAXIMUM_HEIGHT;
+				crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_REORDERING;
+				active_crtc++;
+			}
+			// Switchres selected desktop output
+			else if (resources->crtcs[c] == output_info->crtc)
+			{
+				crtc_info1->timestamp |= XRANDR_SETMODE_IS_DESKTOP;
+				crtc_info1->mode = pxmode->id;
+				crtc_info1->width = pxmode->width;
+				crtc_info1->height = pxmode->height;
+
+				if (mode->type & MODE_DESKTOP)
+				{
+					if (!m_enable_screen_compositing && (crtc_info1->x != sp_desktop_crtc[c].x || crtc_info1->y != sp_desktop_crtc[c].y))
+					{
+						// Restore original desktop position
+						crtc_info1->x = sp_desktop_crtc[c].x;
+						crtc_info1->y = sp_desktop_crtc[c].y;
+						crtc_info1->timestamp |= XRANDR_SETMODE_RESTORE_DESKTOP;
+					}
+				}
+				else
+				{
+					// Use curent position
+					crtc_info1->x = crtc_info->x;
+					crtc_info1->y = crtc_info->y;
+				}
+
+				if (crtc_info0->mode != crtc_info1->mode || crtc_info0->width != crtc_info1->width || crtc_info0->height != crtc_info1->height || crtc_info0->x != crtc_info1->x || crtc_info0->y != crtc_info1->y)
+					crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_DESKTOP_CRTC;
+			}
+			else if (mode->type & MODE_DESKTOP && m_enable_screen_reordering && (crtc_info1->x != sp_desktop_crtc[c].x || crtc_info1->y != sp_desktop_crtc[c].y))
+			{
+				crtc_info1->x = sp_desktop_crtc[c].x;
+				crtc_info1->y = sp_desktop_crtc[c].y;
+				crtc_info1->timestamp |= (XRANDR_SETMODE_RESTORE_DESKTOP | XRANDR_SETMODE_UPDATE_REORDERING);
+			}
+		}
+	}
+
+	for (int c = 0; c < resources->ncrtc; c++)
+	{
+		// Original state
+		XRRCrtcInfo *crtc_info0 = &original_crtc[c];
+		// Modified state
+		XRRCrtcInfo *crtc_info1 = &global_crtc[c];
+
+		// Skip unused crtc
+		if (output_info->crtc != 0 && crtc_info0->mode != 0)
+		{
+			if ((flags & XRANDR_DISABLE_CRTC_RELOCATION) == 0 && (crtc_info1->timestamp & XRANDR_SETMODE_IS_DESKTOP) == 0)
+			{
+				// relocate crtc impacted by new width
+				if (crtc_info1->x >= crtc_info->x + (int)crtc_info->width)
+				{
+					crtc_info1->x += pxmode->width - crtc_info->width;
+					crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_OTHER_CRTC;
+				}
+
+				// relocate crtc impacted by new height
+				if (crtc_info1->y >= crtc_info->y + (int)crtc_info->height)
+				{
+					crtc_info1->y += pxmode->height - crtc_info->height;
+					crtc_info1->timestamp |= XRANDR_SETMODE_UPDATE_OTHER_CRTC;
+				}
+			}
+
+			// Calculate overall screen size based on crtcs placement
+			if (crtc_info1->x + crtc_info1->width > width)
+				width = crtc_info1->x + crtc_info1->width;
+
+			if (crtc_info1->y + crtc_info1->height > height)
+				height = crtc_info1->y + crtc_info1->height;
+
+			if (width > m_max_width)
+			{
+				log_error("XRANDR: <%d> (set_timing) [ERROR] width is above allowed maximum (%d > %d)\n", m_id, width, m_max_width);
+				width = m_max_width;
+			}
+
+			if (height > m_max_height)
+			{
+				log_error("XRANDR: <%d> (set_timing) [ERROR] height is above allowed maximum (%d > %d)\n", m_id, height, m_max_height);
+				height = m_max_height;
+			}
+
+			if (crtc_info1->timestamp & XRANDR_SETMODE_UPDATE_MASK)
+				log_verbose("XRANDR: <%d> (set_timing) crtc %d%s [%04lx] %ux%u+%d+%d --> [%04lx] %ux%u+%d+%d flags [%02lx]\n", m_id, c, crtc_info1->timestamp & 1 ? "*" : " ", crtc_info0->mode, crtc_info0->width, crtc_info0->height, crtc_info0->x, crtc_info0->y, crtc_info1->mode, crtc_info1->width, crtc_info1->height, crtc_info1->x, crtc_info1->y, crtc_info1->timestamp);
+			else if (crtc_info1->timestamp & XRANDR_SETMODE_INFO_MASK)
+				log_verbose("XRANDR: <%d> (set_timing) crtc %d%s [%04lx] %ux%u+%d+%d flags [%02lx]\n", m_id, c, crtc_info1->timestamp & 1 ? "*" : " ", crtc_info1->mode, crtc_info1->width, crtc_info1->height, crtc_info1->x, crtc_info1->y, crtc_info1->timestamp);
+			else
+				log_verbose("XRANDR: <%d> (set_timing) crtc %d  [%04lx] %ux%u+%d+%d\n", m_id, c, crtc_info1->mode, crtc_info1->width, crtc_info1->height, crtc_info1->x, crtc_info1->y);
+		}
+	}
+
+	// Disable crtc with pending modification
+	for (int c = 0; c < resources->ncrtc; c++)
+	{
+		// Modified state
+		if (global_crtc[c].timestamp & XRANDR_SETMODE_UPDATE_MASK)
+		{
+			if (XRRSetCrtcConfig(m_pdisplay, resources, resources->crtcs[c], CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0) != RRSetConfigSuccess)
+			{
+				log_error("XRANDR: <%d> (set_timing) [ERROR] when disabling crtc %d\n", m_id, c);
+				ms_xerrors_flag = 0x01;
+				ms_xerrors |= ms_xerrors_flag;
+			}
+		}
+	}
+
+	// Set the framebuffer screen size to enable all crtc
+	if (ms_xerrors == 0)
+	{
+		log_verbose("XRANDR: <%d> (set_timing) setting screen size to %d x %d\n", m_id, width, height);
+		XSync(m_pdisplay, False);
+		ms_xerrors_flag = 0x02;
+		old_error_handler = XSetErrorHandler(error_handler);
+		XRRSetScreenSize(m_pdisplay, m_root, width, height, (int) ((25.4 * width) / 96.0), (int) ((25.4 * height) / 96.0));
+		XSync(m_pdisplay, False);
+		XSetErrorHandler(old_error_handler);
+		if (ms_xerrors & ms_xerrors_flag)
+			log_error("XRANDR: <%d> (set_timing) [ERROR] in %s\n", m_id, "XRRSetScreenSize");
+	}
+
+	// Refresh all crtc, switch modeline and set new placement
+	for (int c = 0; c < resources->ncrtc; c++)
+	{
+		// Modified state
+		XRRCrtcInfo *crtc_info1 = &global_crtc[c];
+		if (crtc_info1->timestamp & XRANDR_SETMODE_UPDATE_MASK)
+		{
+			if (crtc_info1->timestamp & XRANDR_SETMODE_IS_DESKTOP)
+				XFillRectangle(m_pdisplay, m_root, XCreateGC(m_pdisplay, m_root, 0, 0), crtc_info1->x, crtc_info1->y, crtc_info1->width, crtc_info1->height);
+			// enable crtc with updated parameters
+			XSync(m_pdisplay, False);
+			ms_xerrors_flag = 0x14;
+			old_error_handler = XSetErrorHandler(error_handler);
+			XRRSetCrtcConfig(m_pdisplay, resources, resources->crtcs[c], CurrentTime, crtc_info1->x, crtc_info1->y, crtc_info1->mode, crtc_info1->rotation, crtc_info1->outputs, crtc_info1->noutput);
+			XSync(m_pdisplay, False);
+			XSetErrorHandler(old_error_handler);
+			if (ms_xerrors & 0x10)
+			{
+				log_error("XRANDR: <%d> (set_timing) [ERROR] in %s crtc %d set modeline %04lx\n", m_id, "XRRSetCrtcConfig", c, crtc_info1->mode);
+				ms_xerrors &= 0xEF;
+			}
+		}
+	}
+	delete[]original_crtc;
+	delete[]global_crtc;
+
+	// Release X server, events can be processed now
+	XUngrabServer(m_pdisplay);
+
+	if (ms_xerrors & ms_xerrors_flag)
+		log_error("XRANDR: <%d> (set_timing) [ERROR] in %s\n", m_id, "XRRSetCrtcConfig");
+
+	// Recall the impacted crtc to settle parameters
+	XRRFreeCrtcInfo(crtc_info);
+	crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
+
+	// crtc config modeline change fail
+	if (crtc_info->mode == 0)
+		log_error("XRANDR: <%d> (set_timing) [ERROR] switching resolution failed, no modeline is set\n", m_id);
+	else
+		// save last crtc
+		m_last_crtc = *crtc_info;
+
+	XRRFreeCrtcInfo(crtc_info);
+	XRRFreeOutputInfo(output_info);
+	XRRFreeScreenResources(resources);
+
+	return (ms_xerrors == 0 && crtc_info->mode != 0);
+}
+
+//============================================================
+//  xrandr_timing::delete_mode
+//============================================================
+
+bool xrandr_timing::delete_mode(modeline *mode)
+{
+	// Handle no screen detected case
+	if (m_desktop_output == -1)
+	{
+		log_error("XRANDR: <%d> (delete_mode) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	if (!m_managed)
+	{
+		log_error("XRANDR: <%d> (delete_mode) [WARNING] this screen is managed by <%d>\n", m_id, sp_shared_screen_manager[m_desktop_output]);
+		return false;
+	}
+
+	if (!mode)
+		return false;
+
+	XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+
+	int total_xerrors = 0;
+	// Delete modeline
+	for (int m = 0; m < resources->nmode && mode->platform_data != 0; m++)
+	{
+		if (mode->platform_data == resources->modes[m].id)
+		{
+			XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[m_desktop_output]);
+			XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(m_pdisplay, resources, output_info->crtc);
+			if (resources->modes[m].id == crtc_info->mode)
+			{
+				log_verbose("XRANDR: <%d> (delete_mode) [WARNING] modeline [%04lx] is currently active, restoring desktop mode first\n", m_id, resources->modes[m].id);
+				modeline desktop_mode = {};
+				desktop_mode.type |= MODE_DESKTOP;
+				if (!set_timing(&desktop_mode, 0))
+				{
+					log_error("XRANDR: <%d> (delete_mode) [ERROR] Could not restore desktop mode\n", m_id);
+					return false;
+				}
+			}
+
+			XRRFreeCrtcInfo(crtc_info);
+			XRRFreeOutputInfo(output_info);
+
+			log_verbose("XRANDR: <%d> (delete_mode) remove mode %s\n", m_id, resources->modes[m].name);
+
+			XSync(m_pdisplay, False);
+			ms_xerrors = 0;
+			ms_xerrors_flag = 0x01;
+			old_error_handler = XSetErrorHandler(error_handler);
+			XRRDeleteOutputMode(m_pdisplay, resources->outputs[m_desktop_output], resources->modes[m].id);
+			if (ms_xerrors & ms_xerrors_flag)
+			{
+				log_error("XRANDR: <%d> (delete_mode) [ERROR] in %s\n", m_id, "XRRDeleteOutputMode");
+				total_xerrors++;
+			}
+
+			ms_xerrors_flag = 0x02;
+			XRRDestroyMode(m_pdisplay, resources->modes[m].id);
+			XSync(m_pdisplay, False);
+			XSetErrorHandler(old_error_handler);
+			if (ms_xerrors & ms_xerrors_flag)
+			{
+				log_error("XRANDR: <%d> (delete_mode) [ERROR] in %s\n", m_id, "XRRDestroyMode");
+				total_xerrors++;
+			}
+			mode->platform_data = 0;
+		}
+	}
+
+	XRRFreeScreenResources(resources);
+
+	return total_xerrors == 0;
+}
+
+//============================================================
+//  xrandr_timing::get_timing
+//============================================================
+
+bool xrandr_timing::get_timing(modeline *mode)
+{
+	// Handle no screen detected case
+	if (m_desktop_output == -1)
+	{
+		log_error("XRANDR: <%d> (get_timing) [ERROR] no screen detected\n", m_id);
+		return false;
+	}
+
+	XRRScreenResources *resources = XRRGetScreenResourcesCurrent(m_pdisplay, m_root);
+	XRROutputInfo *output_info = XRRGetOutputInfo(m_pdisplay, resources, resources->outputs[m_desktop_output]);
+
+	// Cycle through the modelines and report them back to the display manager
+	if (m_video_modes_position < output_info->nmode)
+	{
+		for (int m = 0; m < resources->nmode; m++)
+		{
+			XRRModeInfo *pxmode = &resources->modes[m];
+
+			if (pxmode->id == output_info->modes[m_video_modes_position])
+			{
+				mode->platform_data = pxmode->id;
+
+				mode->pclock     = pxmode->dotClock;
+				mode->hactive    = pxmode->width;
+				mode->hbegin     = pxmode->hSyncStart;
+				mode->hend       = pxmode->hSyncEnd;
+				mode->htotal     = pxmode->hTotal;
+				mode->vactive    = pxmode->height;
+				mode->vbegin     = pxmode->vSyncStart;
+				mode->vend       = pxmode->vSyncEnd;
+				mode->vtotal     = pxmode->vTotal;
+				mode->interlace  = (pxmode->modeFlags & RR_Interlace) ? 1 : 0;
+				mode->doublescan = (pxmode->modeFlags & RR_DoubleScan) ? 1 : 0;
+				mode->hsync      = (pxmode->modeFlags & RR_HSyncPositive) ? 1 : 0;
+				mode->vsync      = (pxmode->modeFlags & RR_VSyncPositive) ? 1 : 0;
+
+				mode->hfreq      = mode->pclock / mode->htotal;
+				mode->vfreq      = mode->hfreq / mode->vtotal * (mode->interlace ? 2 : 1);
+				mode->refresh    = mode->vfreq;
+
+				mode->width      = pxmode->width;
+				mode->height     = pxmode->height;
+
+				// Add the rotation flag from the crtc
+				mode->type |= m_crtc_flags;
+
+				mode->type |= CUSTOM_VIDEO_TIMING_XRANDR;
+
+				if (strncmp(pxmode->name, "SR-", 3) == 0)
+					log_verbose("XRANDR: <%d> (get_timing) [WARNING] modeline %s detected\n", m_id, pxmode->name);
+
+				// Add the desktop flag to desktop modeline
+				if (m_desktop_mode.id == pxmode->id)
+					mode->type |= MODE_DESKTOP;
+
+				log_verbose("XRANDR: <%d> (get_timing) mode %04lx %dx%d refresh %.6f added\n", m_id, pxmode->id, pxmode->width, pxmode->height, mode->vfreq);
+			}
+		}
+		m_video_modes_position++;
+	}
+	else
+	{
+		// Inititalise the position for the modeline list
+		m_video_modes_position = 0;
+	}
+
+	XRRFreeOutputInfo(output_info);
+	XRRFreeScreenResources(resources);
+
+	return true;
+}
+
+//============================================================
+//  xrandr_timing::process_modelist
+//============================================================
+
+bool xrandr_timing::process_modelist(std::vector<modeline *> modelist)
+{
+	bool error = false;
+	bool result = false;
+
+	for (auto &mode : modelist)
+	{
+		if (mode->type & MODE_DELETE)
+			result = delete_mode(mode);
+
+		else if (mode->type & MODE_ADD)
+			result = add_mode(mode);
+
+		else if (mode->type & MODE_UPDATE)
+			result = update_mode(mode);
+
+		if (!result)
+		{
+			mode->type |= MODE_ERROR;
+			error = true;
+		}
+		else
+			// succeed
+			mode->type &= ~MODE_ERROR;
+	}
+
+	return !error;
+}
diff --git a/3rdparty/switchres/custom_video_xrandr.h b/3rdparty/switchres/custom_video_xrandr.h
new file mode 100644
index 00000000000..d2cf795275b
--- /dev/null
+++ b/3rdparty/switchres/custom_video_xrandr.h
@@ -0,0 +1,123 @@
+/**************************************************************
+
+   custom_video_xrandr.h - Linux XRANDR video management layer
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __CUSTOM_VIDEO_XRANDR__
+#define __CUSTOM_VIDEO_XRANDR__
+
+// X11 Xrandr headers
+#include <X11/extensions/Xrandr.h>
+#include "custom_video.h"
+
+// Set timing option flags
+#define XRANDR_DISABLE_CRTC_RELOCATION  0x00000001
+#define XRANDR_ENABLE_SCREEN_REORDERING 0x00000002
+
+// Set timing internal flags
+#define XRANDR_SETMODE_IS_DESKTOP          0x00000001
+#define XRANDR_SETMODE_RESTORE_DESKTOP     0x00000002
+#define XRANDR_SETMODE_UPDATE_DESKTOP_CRTC 0x00000010
+#define XRANDR_SETMODE_UPDATE_OTHER_CRTC   0x00000020
+#define XRANDR_SETMODE_UPDATE_REORDERING   0x00000040
+
+#define XRANDR_SETMODE_INFO_MASK           0x0000000F
+#define XRANDR_SETMODE_UPDATE_MASK     0x000000F0
+
+// Super resolution placement, vertical stacking, reserved XRANDR_REORDERING_MAXIMUM_HEIGHT pixels
+//TODO confirm 1024 height is sufficient
+#define XRANDR_REORDERING_MAXIMUM_HEIGHT 1024
+
+class xrandr_timing : public custom_video
+{
+	public:
+		xrandr_timing(char *device_name, custom_video_settings *vs);
+		~xrandr_timing();
+		const char *api_name() { return "XRANDR"; }
+		int caps() { return CUSTOM_VIDEO_CAPS_ADD; }
+		bool init();
+
+		bool add_mode(modeline *mode);
+		bool delete_mode(modeline *mode);
+		bool update_mode(modeline *mode);
+
+		bool get_timing(modeline *mode);
+		bool set_timing(modeline *mode);
+
+		bool process_modelist(std::vector<modeline *>);
+
+		static int ms_xerrors;
+		static int ms_xerrors_flag;
+
+	private:
+		int m_id = 0;
+		int m_managed = 0;
+		int m_enable_screen_reordering = 0;
+		int m_enable_screen_compositing = 0;
+
+		XRRModeInfo *find_mode(modeline *mode);
+		XRRModeInfo *find_mode_by_name(char *name);
+
+		bool set_timing(modeline *mode, int flags);
+
+		int m_video_modes_position = 0;
+		char m_device_name[32];
+		Rotation m_desktop_rotation;
+		unsigned int m_min_width;
+		unsigned int m_max_width;
+		unsigned int m_min_height;
+		unsigned int m_max_height;
+
+		Display *m_pdisplay = NULL;
+		Window m_root;
+		int m_screen;
+
+		int m_desktop_output = -1;
+		XRRModeInfo m_desktop_mode = {};
+		int m_crtc_flags = 0;
+
+		XRRCrtcInfo m_last_crtc = {};
+
+		void *m_xrandr_handle = 0;
+
+		__typeof__(XRRAddOutputMode) *p_XRRAddOutputMode;
+		__typeof__(XRRConfigCurrentConfiguration) *p_XRRConfigCurrentConfiguration;
+		__typeof__(XRRCreateMode) *p_XRRCreateMode;
+		__typeof__(XRRDeleteOutputMode) *p_XRRDeleteOutputMode;
+		__typeof__(XRRDestroyMode) *p_XRRDestroyMode;
+		__typeof__(XRRFreeCrtcInfo) *p_XRRFreeCrtcInfo;
+		__typeof__(XRRFreeOutputInfo) *p_XRRFreeOutputInfo;
+		__typeof__(XRRFreeScreenConfigInfo) *p_XRRFreeScreenConfigInfo;
+		__typeof__(XRRFreeScreenResources) *p_XRRFreeScreenResources;
+		__typeof__(XRRGetCrtcInfo) *p_XRRGetCrtcInfo;
+		__typeof__(XRRGetOutputInfo) *p_XRRGetOutputInfo;
+		__typeof__(XRRGetScreenInfo) *p_XRRGetScreenInfo;
+		__typeof__(XRRGetScreenResourcesCurrent) *p_XRRGetScreenResourcesCurrent;
+		__typeof__(XRRQueryVersion) *p_XRRQueryVersion;
+		__typeof__(XRRSetCrtcConfig) *p_XRRSetCrtcConfig;
+		__typeof__(XRRSetScreenSize) *p_XRRSetScreenSize;
+		__typeof__(XRRGetScreenSizeRange) *p_XRRGetScreenSizeRange;
+
+		void *m_x11_handle = 0;
+
+		__typeof__(XCloseDisplay) *p_XCloseDisplay;
+		__typeof__(XGrabServer) *p_XGrabServer;
+		__typeof__(XOpenDisplay) *p_XOpenDisplay;
+		__typeof__(XSync) *p_XSync;
+		__typeof__(XUngrabServer) *p_XUngrabServer;
+		__typeof__(XSetErrorHandler) *p_XSetErrorHandler;
+		__typeof__(XClearWindow) *p_XClearWindow;
+		__typeof__(XFillRectangle) *p_XFillRectangle;
+		__typeof__(XCreateGC) *p_XCreateGC;
+};
+
+#endif
diff --git a/3rdparty/switchres/display.cpp b/3rdparty/switchres/display.cpp
new file mode 100644
index 00000000000..6f70997f81b
--- /dev/null
+++ b/3rdparty/switchres/display.cpp
@@ -0,0 +1,546 @@
+/**************************************************************
+
+   display.cpp - Display manager
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "display.h"
+#if defined(_WIN32)
+#include "display_windows.h"
+#elif defined(__linux__)
+#include "display_linux.h"
+#endif
+#ifdef SR_WITH_SDL2
+#include "display_sdl2.h"
+#endif
+
+#include "log.h"
+
+//============================================================
+//  display_manager::make
+//============================================================
+
+display_manager *display_manager::make(display_settings *ds)
+{
+	display_manager *display = nullptr;
+
+	if (!strcmp(ds->screen, "dummy"))
+	{
+		display = new dummy_display(ds);
+		return display;
+	}
+
+#ifdef SR_WITH_SDL2
+	try
+	{
+		display = new sdl2_display(ds);
+	}
+	catch (...) {};
+	if (!display)
+	{
+#endif
+
+#if defined(_WIN32)
+	display = new windows_display(ds);
+#elif defined(__linux__)
+	display = new linux_display(ds);
+#endif
+
+#ifdef SR_WITH_SDL2
+	}
+#endif
+
+	return display;
+}
+
+//============================================================
+//  display_manager::parse_options
+//============================================================
+
+void display_manager::parse_options()
+{
+	log_verbose("Switchres: display[%d] options: monitor[%s] generation[%s]\n",
+		m_index, m_ds.monitor, m_ds.modeline_generation?"on":"off");
+
+	// Get user_mode as <w>x<h>@<r>
+	set_user_mode(&m_ds.user_mode);
+
+	// Get user defined modeline (overrides user_mode)
+	modeline user_mode = {};
+	if (m_ds.modeline_generation)
+	{
+		if (modeline_parse(m_ds.user_modeline, &user_mode))
+		{
+			memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
+			user_mode.type |= MODE_USER_DEF;
+			set_user_mode(&user_mode);
+		}
+	}
+
+	// Get monitor specs
+	if (user_mode.hactive)
+	{
+		modeline_to_monitor_range(range, &user_mode);
+		monitor_show_range(range);
+	}
+	else
+		set_preset(m_ds.monitor);
+}
+
+//============================================================
+//  display_manager::set_preset
+//============================================================
+
+void display_manager::set_preset(const char *preset)
+{
+	for (size_t i = 0; i < strlen(m_ds.monitor); i++) m_ds.monitor[i] = tolower(m_ds.monitor[i]);
+
+	memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
+
+	if (!strcmp(preset, "custom"))
+		for (int i = 0; i < MAX_RANGES; i++) monitor_fill_range(&range[i], m_ds.crt_range[i]);
+
+	else if (!strcmp(preset, "lcd"))
+		monitor_fill_lcd_range(&range[0], m_ds.lcd_range);
+
+	else if (monitor_set_preset(preset, range) == 0)
+		monitor_set_preset("generic_15", range);
+}
+
+//============================================================
+//  display_manager::init
+//============================================================
+
+bool display_manager::init(void* pf_data)
+{
+	sprintf(m_ds.screen, "ram");
+	m_pf_data = pf_data;
+
+	return true;
+}
+
+//============================================================
+//  display_manager::caps
+//============================================================
+
+int display_manager::caps()
+{
+	if (video() != nullptr)
+		return video()->caps();
+	else
+		return CUSTOM_VIDEO_CAPS_ADD;
+}
+
+//============================================================
+//  display_manager::add_mode
+//============================================================
+
+bool display_manager::add_mode(modeline *mode)
+{
+	// Add new mode
+	if (video() != nullptr && !video()->add_mode(mode))
+	{
+		log_verbose("Switchres: error adding mode ");
+		log_mode(mode);
+		return false;
+	}
+
+	mode->type &= ~MODE_ADD;
+
+	log_verbose("Switchres: added ");
+	log_mode(mode);
+
+	return true;
+}
+
+//============================================================
+//  display_manager::delete_mode
+//============================================================
+
+bool display_manager::delete_mode(modeline *mode)
+{
+	if (video() != nullptr && !video()->delete_mode(mode))
+	{
+		log_verbose("Switchres: error deleting mode ");
+		log_mode(mode);
+		return false;
+	}
+
+	log_verbose("Switchres: deleted ");
+	log_mode(mode);
+
+	ptrdiff_t i = mode - &video_modes[0];
+	video_modes.erase(video_modes.begin() + i);
+
+	return true;
+}
+
+//============================================================
+//  display_manager::update_mode
+//============================================================
+
+bool display_manager::update_mode(modeline *mode)
+{
+	// Apply new timings
+	if (video() != nullptr && !video()->update_mode(mode))
+	{
+		log_verbose("Switchres: error updating mode ");
+		log_mode(mode);
+		return false;
+	}
+
+	mode->type &= ~MODE_UPDATE;
+
+	log_verbose("Switchres: updated ");
+	log_mode(mode);
+	return true;
+}
+
+//============================================================
+//  display_manager::set_mode
+//============================================================
+
+bool display_manager::set_mode(modeline *)
+{
+	return false;
+}
+
+//============================================================
+//  display_manager::log_mode
+//============================================================
+
+void display_manager::log_mode(modeline *mode)
+{
+	char modeline_txt[256];
+	log_verbose("%s timing %s\n", video() != nullptr? video()->api_name() : "dummy", modeline_print(mode, modeline_txt, MS_FULL));
+}
+
+//============================================================
+//  display_manager::restore_modes
+//============================================================
+
+bool display_manager::restore_modes()
+{
+	// Compare each mode in our table with its original state
+	for (unsigned i = video_modes.size(); i-- > 0; )
+	{
+		// First, delete all modes we've added
+		if (i + 1 > backup_modes.size())
+			video_modes[i].type |= MODE_DELETE;
+
+		// Now restore all modes which timings have been modified
+		else if (modeline_is_different(&video_modes[i], &backup_modes[i]))
+		{
+			video_modes[i] = backup_modes[i];
+			video_modes[i].type |= MODE_UPDATE;
+		}
+	}
+	// Finally, flush pending changes to driver
+	return flush_modes();
+}
+
+//============================================================
+//  display_manager::flush_modes
+//============================================================
+
+bool display_manager::flush_modes()
+{
+	bool error = false;
+	std::vector<modeline *> modified_modes = {};
+
+	// Loop through our mode table to collect all pending changes
+	for (auto &mode : video_modes)
+		if (mode.type & (MODE_UPDATE | MODE_ADD | MODE_DELETE))
+			modified_modes.push_back(&mode);
+
+	// Flush pending changes to driver
+	if (modified_modes.size() > 0)
+	{
+		if (video() != nullptr)
+			video()->process_modelist(modified_modes);
+
+		// Log error/success result for each mode
+		for (auto &mode : modified_modes)
+		{
+			log_verbose("Switchres: %s %s mode ", mode->type & MODE_ERROR? "error" : "success", mode->type & MODE_DELETE? "deleting" : mode->type & MODE_ADD? "adding" : "updating");
+			log_mode(mode);
+
+			if (mode->type & MODE_ERROR)
+				error = true;
+		}
+
+		// Update our internal mode table to reflect the changes
+		for (unsigned i = video_modes.size(); i-- > 0; )
+		{
+			if (video_modes[i].type & MODE_ERROR)
+				continue;
+
+			if (video_modes[i].type & MODE_DELETE)
+			{
+				video_modes.erase(video_modes.begin() + i);
+				m_selected_mode = 0;
+			}
+			else
+				video_modes[i].type &= ~(MODE_UPDATE | MODE_ADD);
+		}
+	}
+
+	return !error;
+}
+
+//============================================================
+//  display_manager::filter_modes
+//============================================================
+
+bool display_manager::filter_modes()
+{
+	for (auto &mode : video_modes)
+	{
+		// apply options to mode type
+		if (m_ds.refresh_dont_care)
+			mode.type |= V_FREQ_EDITABLE;
+
+		if ((caps() & CUSTOM_VIDEO_CAPS_UPDATE))
+			mode.type |= V_FREQ_EDITABLE;
+
+		if (caps() & CUSTOM_VIDEO_CAPS_SCAN_EDITABLE)
+			mode.type |= SCAN_EDITABLE;
+
+		if (!m_ds.modeline_generation)
+			mode.type &= ~(XYV_EDITABLE | SCAN_EDITABLE);
+
+		if ((mode.type & MODE_DESKTOP) && !(caps() & CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE))
+			mode.type &= ~V_FREQ_EDITABLE;
+
+		if (m_ds.lock_system_modes && (mode.type & CUSTOM_VIDEO_TIMING_SYSTEM))
+			mode.type |= MODE_DISABLED;
+
+		// Make sure to unlock the desktop mode as fallback
+		if (mode.type & MODE_DESKTOP)
+			mode.type &= ~MODE_DISABLED;
+
+		// Lock all modes that don't match the user's -resolution rules
+		if (m_user_mode.width != 0 || m_user_mode.height != 0 || m_user_mode.refresh == !0)
+		{
+			if (!( (mode.width == m_user_mode.width || (mode.type & X_RES_EDITABLE) || m_user_mode.width == 0)
+				&& (mode.height == m_user_mode.height || (mode.type & Y_RES_EDITABLE) || m_user_mode.height == 0)
+				&& (mode.refresh == m_user_mode.refresh || (mode.type & V_FREQ_EDITABLE) || m_user_mode.refresh == 0) ))
+				mode.type |= MODE_DISABLED;
+			else
+				mode.type &= ~MODE_DISABLED;
+		}
+	}
+
+	return true;
+}
+
+//============================================================
+//  display_manager::get_video_mode
+//============================================================
+
+modeline *display_manager::get_mode(int width, int height, float refresh, int flags)
+{
+	modeline s_mode = {};
+	modeline t_mode = {};
+	modeline best_mode = {};
+	char result[256]={'\x00'};
+
+	bool rotated = flags & SR_MODE_ROTATED;
+	bool interlaced = flags & SR_MODE_INTERLACED;
+
+	log_info("Switchres: Calculating best video mode for %dx%d@%.6f%s orientation: %s\n",
+						width, height, refresh, interlaced?"i":"", rotated?"rotated":"normal");
+
+	best_mode.result.weight |= R_OUT_OF_RANGE;
+
+	s_mode.interlace = interlaced;
+	s_mode.vfreq = refresh;
+
+	s_mode.hactive = width;
+	s_mode.vactive = height;
+
+	if (rotated)
+	{
+		std::swap(s_mode.hactive, s_mode.vactive);
+		s_mode.type |= MODE_ROTATED;
+	}
+
+	// Create a dummy mode entry if allowed
+	if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation)
+	{
+		modeline new_mode = {};
+		new_mode.type = XYV_EDITABLE | V_FREQ_EDITABLE | SCAN_EDITABLE | MODE_ADD | (desktop_is_rotated()? MODE_ROTATED : MODE_OK);
+		video_modes.push_back(new_mode);
+	}
+
+	// Run through our mode list and find the most suitable mode
+	for (auto &mode : video_modes)
+	{
+		log_verbose("\nSwitchres: %s%4d%sx%s%4d%s_%s%d=%.6fHz%s%s\n",
+			mode.type & X_RES_EDITABLE?"(":"[", mode.width, mode.type & X_RES_EDITABLE?")":"]",
+			mode.type & Y_RES_EDITABLE?"(":"[", mode.height, mode.type & Y_RES_EDITABLE?")":"]",
+			mode.type & V_FREQ_EDITABLE?"(":"[", mode.refresh, mode.vfreq, mode.type & V_FREQ_EDITABLE?")":"]",
+			mode.type & MODE_DISABLED?" - locked":"");
+
+		// now get the mode if allowed
+		if (mode.type & MODE_DISABLED)
+			continue;
+
+		for (int i = 0 ; i < MAX_RANGES ; i++)
+		{
+			if (range[i].hfreq_min == 0)
+				continue;
+
+			t_mode = mode;
+
+			// init all editable fields with source or user values
+			if (t_mode.type & X_RES_EDITABLE)
+				t_mode.hactive = m_user_mode.width? m_user_mode.width : s_mode.hactive;
+
+			if (t_mode.type & Y_RES_EDITABLE)
+				t_mode.vactive = m_user_mode.height? m_user_mode.height : s_mode.vactive;
+
+			if (t_mode.type & V_FREQ_EDITABLE)
+			{
+				// If user's vfreq is defined, it means we have an user modeline, so force it
+				if (m_user_mode.vfreq)
+					modeline_copy_timings(&t_mode, &m_user_mode);
+				else
+					t_mode.vfreq = s_mode.vfreq;
+			}
+
+			// lock resolution fields if required
+			if (m_user_mode.width) t_mode.type &= ~X_RES_EDITABLE;
+			if (m_user_mode.height) t_mode.type &= ~Y_RES_EDITABLE;
+			if (m_user_mode.vfreq) t_mode.type &= ~V_FREQ_EDITABLE;
+
+			modeline_create(&s_mode, &t_mode, &range[i], &m_ds.gs);
+			t_mode.range = i;
+
+			log_verbose("%s\n", modeline_result(&t_mode, result));
+
+			if (modeline_compare(&t_mode, &best_mode))
+			{
+				best_mode = t_mode;
+				m_selected_mode = &mode;
+			}
+		}
+	}
+
+	// If we didn't need to create a new mode, remove our dummy entry
+	if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation && m_selected_mode != &video_modes.back())
+		video_modes.pop_back();
+
+	// If we didn't find a suitable mode, exit now
+	if (best_mode.result.weight & R_OUT_OF_RANGE)
+	{
+		m_selected_mode = 0;
+		log_error("Switchres: could not find a video mode that meets your specs\n");
+		return nullptr;
+	}
+
+	if ((best_mode.type & V_FREQ_EDITABLE) && !(best_mode.result.weight & R_OUT_OF_RANGE))
+		modeline_adjust(&best_mode, range[best_mode.range].hfreq_max, &m_ds.gs);
+
+	log_verbose("\nSwitchres: %s (%dx%d@%.6f)->(%dx%d@%.6f)\n", rotated?"rotated":"normal",
+		width, height, refresh, best_mode.hactive, best_mode.vactive, best_mode.vfreq);
+
+	log_verbose("%s\n", modeline_result(&best_mode, result));
+
+	// Copy the new modeline to our mode list
+	if (m_ds.modeline_generation)
+	{
+		if (best_mode.type & MODE_ADD)
+		{
+			best_mode.width = best_mode.hactive;
+			best_mode.height = best_mode.vactive;
+			best_mode.refresh = int(best_mode.vfreq);
+			// lock new mode
+			best_mode.type &= ~(X_RES_EDITABLE | Y_RES_EDITABLE);
+		}
+		else if (modeline_is_different(&best_mode, m_selected_mode) != 0)
+			best_mode.type |= MODE_UPDATE;
+
+		char modeline[256]={'\x00'};
+		log_info("Switchres: Modeline %s\n", modeline_print(&best_mode, modeline, MS_FULL));
+	}
+
+	// Check if new best mode is different than previous one
+	m_switching_required = (m_current_mode != m_selected_mode || best_mode.type & MODE_UPDATE);
+
+	// Add id to mode
+	if (best_mode.id == 0)
+		best_mode.id = ++m_id_counter;
+
+	*m_selected_mode = best_mode;
+	return m_selected_mode;
+}
+
+//============================================================
+//  display_manager::auto_specs
+//============================================================
+
+bool display_manager::auto_specs()
+{
+	// Make sure we have a valid mode
+	if (desktop_mode.width == 0 || desktop_mode.height == 0 || desktop_mode.refresh == 0)
+	{
+		log_error("Switchres: Invalid desktop mode %dx%d@%d\n", desktop_mode.width, desktop_mode.height, desktop_mode.refresh);
+		return false;
+	}
+
+	log_verbose("Switchres: Creating automatic specs for LCD based on %s\n", (desktop_mode.type & CUSTOM_VIDEO_TIMING_SYSTEM)? "VESA GTF" : "current timings");
+
+	// Make sure our current refresh is within range if set to auto
+	if (!strcmp(m_ds.lcd_range, "auto"))
+	{
+		sprintf(m_ds.lcd_range, "%d-%d", desktop_mode.refresh - 1, desktop_mode.refresh + 1);
+		monitor_fill_lcd_range(range, m_ds.lcd_range);
+	}
+
+	// Create a working range with the best possible information
+	if (desktop_mode.type & CUSTOM_VIDEO_TIMING_SYSTEM) modeline_vesa_gtf(&desktop_mode);
+	modeline_to_monitor_range(range, &desktop_mode);
+	monitor_show_range(range);
+
+	// Force our resolution to LCD's native one
+	modeline user_mode = {};
+	user_mode.width = desktop_mode.width;
+	user_mode.height = desktop_mode.height;
+	user_mode.refresh = desktop_mode.refresh;
+	set_user_mode(&user_mode);
+
+	return true;
+}
+
+//============================================================
+//  display_manager::get_aspect
+//============================================================
+
+double display_manager::get_aspect(const char* aspect)
+{
+	int num, den;
+	if (sscanf(aspect, "%d:%d", &num, &den) == 2)
+	{
+		if (den == 0)
+		{
+			log_error("Error: denominator can't be zero\n");
+			return STANDARD_CRT_ASPECT;
+		}
+		return (double(num)/double(den));
+	}
+
+	log_error("Error: use format --aspect <num:den>\n");
+	return STANDARD_CRT_ASPECT;
+}
diff --git a/3rdparty/switchres/display.h b/3rdparty/switchres/display.h
new file mode 100644
index 00000000000..e3acf7f6ed6
--- /dev/null
+++ b/3rdparty/switchres/display.h
@@ -0,0 +1,227 @@
+/**************************************************************
+
+   display.h - Display manager
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __DISPLAY_H__
+#define __DISPLAY_H__
+
+#include <vector>
+#include "modeline.h"
+#include "custom_video.h"
+
+// Mode flags
+#define SR_MODE_INTERLACED    1<<0
+#define SR_MODE_ROTATED       1<<1
+
+typedef struct display_settings
+{
+	char   screen[32];
+	char   api[32];
+	bool   modeline_generation;
+	bool   lock_unsupported_modes;
+	bool   lock_system_modes;
+	bool   refresh_dont_care;
+	bool   keep_changes;
+	char   monitor[32];
+	char   crt_range[MAX_RANGES][256];
+	char   lcd_range[256];
+	char   user_modeline[256];
+	modeline user_mode;
+
+	generator_settings gs;
+	custom_video_settings vs;
+} display_settings;
+
+
+class display_manager
+{
+public:
+
+	display_manager() {};
+	virtual ~display_manager()
+	{
+		if (!m_ds.keep_changes) restore_modes();
+		if (m_factory) delete m_factory;
+	};
+
+	display_manager *make(display_settings *ds);
+	void parse_options();
+	virtual bool init(void* = nullptr);
+	virtual int caps();
+
+	// getters
+	int index() const { return m_index; }
+	custom_video *factory() const { return m_factory; }
+	custom_video *video() const { return m_video; }
+	bool has_ini() const { return m_has_ini; }
+
+	// getters (modes)
+	modeline user_mode() const { return m_user_mode; }
+	modeline *selected_mode() const { return m_selected_mode; }
+	modeline *current_mode() const { return m_current_mode; }
+
+	// getters (display manager)
+	const char *monitor() { return (const char*) &m_ds.monitor; }
+	const char *user_modeline() { return (const char*) &m_ds.user_modeline; }
+	const char *crt_range(int i) { return (const char*) &m_ds.crt_range[i]; }
+	const char *lcd_range() { return (const char*) &m_ds.lcd_range; }
+	const char *screen() { return (const char*) &m_ds.screen; }
+	const char *api() { return (const char*) &m_ds.api; }
+	bool modeline_generation() { return m_ds.modeline_generation; }
+	bool lock_unsupported_modes() { return m_ds.lock_unsupported_modes; }
+	bool lock_system_modes() { return m_ds.lock_system_modes; }
+	bool refresh_dont_care() { return m_ds.refresh_dont_care; }
+	bool keep_changes() { return m_ds.keep_changes; }
+	bool desktop_is_rotated() const { return m_desktop_is_rotated; }
+
+	// getters (modeline generator)
+	bool interlace() { return m_ds.gs.interlace; }
+	bool doublescan() { return m_ds.gs.doublescan; }
+	double dotclock_min() { return m_ds.gs.pclock_min; }
+	double refresh_tolerance() { return m_ds.gs.refresh_tolerance; }
+	int super_width() { return m_ds.gs.super_width; }
+	double monitor_aspect() { return m_ds.gs.monitor_aspect; }
+	double h_size() { return m_ds.gs.h_size; }
+	int h_shift() { return m_ds.gs.h_shift; }
+	int v_shift() { return m_ds.gs.v_shift; }
+	int v_shift_correct() { return m_ds.gs.v_shift_correct; }
+	int pixel_precision() { return m_ds.gs.pixel_precision; }
+	int interlace_force_even() { return m_ds.gs.interlace_force_even; }
+	int scale_proportional() { return m_ds.gs.scale_proportional; }
+
+	// getters (modeline result)
+	bool got_mode() { return (m_selected_mode != nullptr); }
+	int width() { return m_selected_mode != nullptr? m_selected_mode->width : 0; }
+	int height() { return m_selected_mode != nullptr? m_selected_mode->height : 0; }
+	int refresh() { return m_selected_mode != nullptr? m_selected_mode->refresh : 0; }
+	double v_freq() { return m_selected_mode != nullptr? m_selected_mode->vfreq : 0; }
+	double h_freq() { return m_selected_mode != nullptr? m_selected_mode->hfreq : 0; }
+	double x_scale() { return m_selected_mode != nullptr? m_selected_mode->result.x_scale : 0; }
+	double y_scale() { return m_selected_mode != nullptr? m_selected_mode->result.y_scale : 0; }
+	double v_scale() { return m_selected_mode != nullptr? m_selected_mode->result.v_scale : 0; }
+	bool is_interlaced() { return m_selected_mode != nullptr? m_selected_mode->interlace : false; }
+	bool is_doublescanned() { return m_selected_mode != nullptr? m_selected_mode->doublescan : false; }
+	bool is_stretched() { return m_selected_mode != nullptr? m_selected_mode->result.weight & R_RES_STRETCH : false; }
+	bool is_refresh_off() { return m_selected_mode != nullptr? m_selected_mode->result.weight & R_V_FREQ_OFF : false; }
+	bool is_switching_required() { return m_switching_required; }
+	bool is_mode_updated() { return m_selected_mode != nullptr? m_selected_mode->type & MODE_UPDATE : false; }
+	bool is_mode_new() { return m_selected_mode != nullptr? m_selected_mode->type & MODE_ADD : false; }
+
+	// getters (custom_video backend)
+	bool screen_compositing() { return m_ds.vs.screen_compositing; }
+	bool screen_reordering() { return m_ds.vs.screen_reordering; }
+	bool allow_hardware_refresh() { return m_ds.vs.allow_hardware_refresh; }
+	const char *custom_timing() { return (const char*) &m_ds.vs.custom_timing; }
+
+	// setters
+	void set_index(int index) { m_index = index; }
+	void set_factory(custom_video *factory) { m_factory = factory; }
+	void set_custom_video(custom_video *video) { m_video = video; }
+	void set_has_ini(bool value) { m_has_ini = value; }
+
+	// setters (modes)
+	void set_user_mode(modeline *mode) { m_ds.user_mode = m_user_mode = *mode; filter_modes(); }
+	void set_selected_mode(modeline *mode) { m_selected_mode = mode; }
+	void set_current_mode(modeline *mode) { m_current_mode = mode; }
+
+	// setters (display_manager)
+	void set_monitor(const char *preset) { strncpy(m_ds.monitor, preset, sizeof(m_ds.monitor)-1); set_preset(preset); }
+	void set_modeline(const char *modeline) { strncpy(m_ds.user_modeline, modeline, sizeof(m_ds.user_modeline)-1); }
+	void set_crt_range(int i, const char *range) { strncpy(m_ds.crt_range[i], range, sizeof(m_ds.crt_range[i])-1); }
+	void set_lcd_range(const char *range) { strncpy(m_ds.lcd_range, range, sizeof(m_ds.lcd_range)-1); }
+	void set_screen(const char *screen) { strncpy(m_ds.screen, screen, sizeof(m_ds.screen)-1); }
+	void set_api(const char *api) { strncpy(m_ds.api, api, sizeof(m_ds.api)-1); }
+	void set_modeline_generation(bool value) { m_ds.modeline_generation = value; }
+	void set_lock_unsupported_modes(bool value) { m_ds.lock_unsupported_modes = value; }
+	void set_lock_system_modes(bool value) { m_ds.lock_system_modes = value; }
+	void set_refresh_dont_care(bool value) { m_ds.refresh_dont_care = value; }
+	void set_keep_changes(bool value) { m_ds.keep_changes = value; }
+	void set_desktop_is_rotated(bool value) { m_desktop_is_rotated = value; }
+
+	// setters (modeline generator)
+	void set_interlace(bool value) { m_ds.gs.interlace = value; }
+	void set_doublescan(bool value) { m_ds.gs.doublescan = value; }
+	void set_dotclock_min(double value) { m_ds.gs.pclock_min = value * 1000000; }
+	void set_refresh_tolerance(double value) { m_ds.gs.refresh_tolerance = value; }
+	void set_super_width(int value) { m_ds.gs.super_width = value; }
+	void set_monitor_aspect(double value) { m_ds.gs.monitor_aspect = value; }
+	void set_monitor_aspect(const char* aspect) { set_monitor_aspect(get_aspect(aspect)); }
+	void set_h_size(double value) { m_ds.gs.h_size = value; }
+	void set_h_shift(int value) { m_ds.gs.h_shift = value; }
+	void set_v_shift(int value) { m_ds.gs.v_shift = value; }
+	void set_v_shift_correct(int value) { m_ds.gs.v_shift_correct = value; }
+	void set_pixel_precision(int value) { m_ds.gs.pixel_precision = value; }
+	void set_interlace_force_even(int value) { m_ds.gs.interlace_force_even = value; }
+	void set_scale_proportional(int value) { m_ds.gs.scale_proportional = value; }
+
+	// setters (custom_video backend)
+	void set_screen_compositing(bool value) { m_ds.vs.screen_compositing = value; }
+	void set_screen_reordering(bool value) { m_ds.vs.screen_reordering = value; }
+	void set_allow_hardware_refresh(bool value) { m_ds.vs.allow_hardware_refresh = value; }
+	void set_custom_timing(const char *custom_timing) { strncpy(m_ds.vs.custom_timing, custom_timing, sizeof(m_ds.vs.custom_timing)-1); }
+
+	// options
+	display_settings m_ds = {};
+
+	// mode setting interface
+	modeline *get_mode(int width, int height, float refresh, int flags);
+	bool add_mode(modeline *mode);
+	bool delete_mode(modeline *mode);
+	bool update_mode(modeline *mode);
+	virtual bool set_mode(modeline *);
+	void log_mode(modeline *mode);
+
+	// mode list handling
+	bool filter_modes();
+	bool restore_modes();
+	bool flush_modes();
+	bool auto_specs();
+
+	// mode list
+	std::vector<modeline> video_modes = {};
+	std::vector<modeline> backup_modes = {};
+	modeline desktop_mode = {};
+
+	// monitor preset
+	monitor_range range[MAX_RANGES];
+
+private:
+
+	// custom video backend
+	custom_video *m_factory = 0;
+	custom_video *m_video = 0;
+
+	modeline m_user_mode = {};
+	modeline *m_selected_mode = 0;
+	modeline *m_current_mode = 0;
+
+	int m_index = 0;
+	bool m_desktop_is_rotated = 0;
+	bool m_switching_required = 0;
+	bool m_has_ini = 0;
+	int m_id_counter = 0;
+
+	void set_preset(const char *preset);
+	double get_aspect(const char* aspect);
+
+protected:
+	void* m_pf_data = nullptr;
+};
+
+class dummy_display : public display_manager
+{
+	public:
+		dummy_display(display_settings *ds) { m_ds = *ds; };
+};
+
+#endif
diff --git a/3rdparty/switchres/display_linux.cpp b/3rdparty/switchres/display_linux.cpp
new file mode 100644
index 00000000000..470647f3e38
--- /dev/null
+++ b/3rdparty/switchres/display_linux.cpp
@@ -0,0 +1,171 @@
+/**************************************************************
+
+   display_linux.cpp - Display manager for Linux
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "display_linux.h"
+#include "log.h"
+
+//============================================================
+//  linux_display::linux_display
+//============================================================
+
+linux_display::linux_display(display_settings *ds)
+{
+	// Get display settings
+	m_ds = *ds;
+}
+
+//============================================================
+//  linux_display::~linux_display
+//============================================================
+
+linux_display::~linux_display()
+{
+	if (!m_ds.keep_changes)
+		restore_desktop_mode();
+}
+
+//============================================================
+//  linux_display::init
+//============================================================
+
+bool linux_display::init(void* pfdata)
+{
+	m_pf_data = pfdata;
+	// Initialize custom video
+	int method = CUSTOM_VIDEO_TIMING_AUTO;
+
+#ifdef SR_WITH_XRANDR
+	if (!strcmp(m_ds.api, "xrandr"))
+		method = CUSTOM_VIDEO_TIMING_XRANDR;
+#endif
+#ifdef SR_WITH_KMSDRM
+	if (!strcmp(m_ds.api, "drmkms"))
+		method = CUSTOM_VIDEO_TIMING_DRMKMS;
+#endif
+
+	set_factory(new custom_video);
+	set_custom_video(factory()->make(m_ds.screen, NULL, method, &m_ds.vs));
+	if (!video() or !video()->init())
+		return false;
+
+	// Build our display's mode list
+	video_modes.clear();
+	backup_modes.clear();
+	get_desktop_mode();
+	get_available_video_modes();
+
+	if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
+	filter_modes();
+
+	return true;
+}
+
+//============================================================
+//  linux_display::set_mode
+//============================================================
+
+bool linux_display::set_mode(modeline *mode)
+{
+	if (mode && set_desktop_mode(mode, 0))
+	{
+		set_current_mode(mode);
+		return true;
+	}
+	return false;
+}
+
+//============================================================
+//  linux_display::get_desktop_mode
+//============================================================
+
+bool linux_display::get_desktop_mode()
+{
+	if (video() == NULL)
+		return false;
+
+	return true;
+}
+
+//============================================================
+//  linux_display::set_desktop_mode
+//============================================================
+
+bool linux_display::set_desktop_mode(modeline *mode, int flags)
+{
+	if (!mode)
+		return false;
+
+	if (video() == NULL)
+		return false;
+
+	if (flags != 0)
+		log_info("Set desktop mode flags value is 0x%x.\n", flags);
+
+	return video()->set_timing(mode);
+}
+
+//============================================================
+//  linux_display::restore_desktop_mode
+//============================================================
+
+bool linux_display::restore_desktop_mode()
+{
+	if (video() == NULL)
+		return false;
+
+	return video()->set_timing(&desktop_mode);
+}
+
+//============================================================
+//  linux_display::get_available_video_modes
+//============================================================
+
+int linux_display::get_available_video_modes()
+{
+	if (video() == NULL)
+		return false;
+
+	// loop through all modes until NULL mode type is received
+	for (;;)
+	{
+		modeline mode;
+		memset(&mode, 0, sizeof(struct modeline));
+
+		// get next mode
+		video()->get_timing(&mode);
+		if (mode.type == 0)
+			break;
+
+		// set the desktop mode
+		if (mode.type & MODE_DESKTOP)
+		{
+			memcpy(&desktop_mode, &mode, sizeof(modeline));
+			if (current_mode() == nullptr)
+				set_current_mode(&mode);
+
+			if (mode.type & MODE_ROTATED) set_desktop_is_rotated(true);
+		}
+
+		video_modes.push_back(mode);
+		backup_modes.push_back(mode);
+
+		log_verbose("Switchres: [%3ld] %4dx%4d @%3d%s%s %s: ", video_modes.size(), mode.width, mode.height, mode.refresh, mode.interlace ? "i" : "p", mode.type & MODE_DESKTOP ? "*" : "", mode.type & MODE_ROTATED ? "rot" : "");
+		log_mode(&mode);
+	};
+
+	return true;
+}
diff --git a/3rdparty/switchres/display_linux.h b/3rdparty/switchres/display_linux.h
new file mode 100644
index 00000000000..f56faa84338
--- /dev/null
+++ b/3rdparty/switchres/display_linux.h
@@ -0,0 +1,30 @@
+/**************************************************************
+
+   display_linux.h - Display manager for Linux
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include "display.h"
+
+class linux_display : public display_manager
+{
+	public:
+		linux_display(display_settings *ds);
+		~linux_display();
+		bool init(void* = nullptr);
+		bool set_mode(modeline *mode);
+
+	private:
+		bool get_desktop_mode();
+		bool set_desktop_mode(modeline *mode, int flags);
+		bool restore_desktop_mode();
+		int get_available_video_modes();
+};
diff --git a/3rdparty/switchres/display_sdl2.cpp b/3rdparty/switchres/display_sdl2.cpp
new file mode 100644
index 00000000000..d8bf892aed9
--- /dev/null
+++ b/3rdparty/switchres/display_sdl2.cpp
@@ -0,0 +1,247 @@
+/**************************************************************
+
+   display_linux.cpp - Display manager for Linux
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <SDL.h>
+
+#include "display_sdl2.h"
+#include "log.h"
+
+
+//============================================================
+//  sdl2_display::sdl2_display
+//============================================================
+
+sdl2_display::sdl2_display(display_settings *ds)
+{
+	// First, we need to fin an active SDL2 window
+	if (SDL_WasInit(SDL_INIT_VIDEO) != 0) {
+		log_verbose("Switchres/SDL2: (%s): SDL2 video is initialized\n", __FUNCTION__);
+	}
+	else
+	{
+		log_verbose("Switchres/SDL2: (%s): SDL2 video wasn't initialized\n", __FUNCTION__);
+		throw std::exception();
+	}
+	// For now, only allow the SDL2 display manager for the KMSDRM backend
+	if ( strcmp("KMSDRM", SDL_GetCurrentVideoDriver()) != 0 )
+	{
+		log_info("Switchres/SDL2: (%s): SDL2 is only available for KMSDRM for now.\n", __FUNCTION__);
+		throw std::exception();
+	}
+
+	// Get display settings
+	m_ds = *ds;
+}
+
+//============================================================
+//  sdl2_display::~sdl2_display
+//============================================================
+
+sdl2_display::~sdl2_display()
+{
+}
+
+//============================================================
+//  sdl2_display::init
+//============================================================
+
+bool sdl2_display::init(void* pf_data)
+{
+	m_sdlwindow = (SDL_Window*) pf_data;
+
+	// Initialize custom video
+	int method = CUSTOM_VIDEO_TIMING_AUTO;
+
+#ifdef SR_WITH_XRANDR
+	if (!strcmp(m_ds.api, "xrandr"))
+		method = CUSTOM_VIDEO_TIMING_XRANDR;
+#endif
+#ifdef SR_WITH_KMSDRM
+	if (!strcmp(m_ds.api, "drmkms"))
+		method = CUSTOM_VIDEO_TIMING_DRMKMS;
+#endif
+	set_factory(new custom_video);
+	set_custom_video(factory()->make(m_ds.screen, NULL, method, &m_ds.vs));
+	if (!video() or !video()->init())
+		return false;
+	// Build our display's mode list
+	video_modes.clear();
+	backup_modes.clear();
+	//No need to call get_desktop_mode() SDL2 will restore the desktop mode itself
+	get_available_video_modes();
+
+	if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
+	filter_modes();
+
+	//SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
+
+	// Get SDL version information
+	SDL_version version;
+	SDL_GetVersion(&version);
+	log_info("Switchres/SDL2: Detected SDL version %d.%d.%d\n",	(int)version.major, (int)version.minor, (int)version.patch);
+
+	SDL_Window* window = NULL;
+	Uint32 id = 0;
+
+	/*
+	// Alternative method, only when the render has been created
+	if( SDL_GL_GetCurrentWindow() !=  NULL)
+	    log_verbose("Swithres/SDL2: (%s) SDL_GL_GetCurrentWindow(); OK !\n", __FUNCTION__);
+	*/
+	if (pf_data == nullptr or pf_data == NULL)
+	{
+		int screen = atoi(m_ds.screen);
+
+		while( (window = SDL_GetWindowFromID(++id)) )
+		{
+			log_verbose("Switchres/SDL2: (%s:%d) SDL display id vs SR display id: %d vs %d (window id: %d)\n", __FUNCTION__, __LINE__, SDL_GetWindowDisplayIndex(window), screen, id);
+			if (SDL_GetWindowDisplayIndex(window) == screen)
+			{
+				log_verbose("Switchres/SDL2: (%s:%d) Found a display-matching SDL window\n ", __FUNCTION__, __LINE__);
+				m_sdlwindow = window;
+				return true;
+			}
+		}
+
+		log_verbose("Switchres/SDL2: (%s:%d) No SDL window matching the display found\n ", __FUNCTION__, __LINE__);
+
+	}
+	else
+	{
+		id = SDL_GetWindowID((SDL_Window*)pf_data);
+		if( id )
+		{
+			log_verbose("Switchres/SDL2: (%s:%d) got a valid SDL_Window pointer (window id: %d)\n", __FUNCTION__, __LINE__, id);
+			m_sdlwindow = (SDL_Window*)pf_data;
+		}
+		else
+			log_verbose("Switchres/SDL2: (%s:%d) No SDL2 window found, don't expect things to work good\n", __FUNCTION__, __LINE__);
+	}
+
+	// Need a check to see if SDL2 can refresh the modelist
+	return true;
+}
+
+//============================================================
+//  sdl2_display::set_mode
+//============================================================
+
+bool sdl2_display::set_mode(modeline *mode)
+{
+	// Call SDL2
+	SDL_DisplayMode target, closest;
+	target.w = mode->width;
+	target.h = mode->height;
+	target.format = 0;  // don't care
+	target.refresh_rate = mode->refresh;
+
+	/*
+	 * Circumventing an annoying choice of SDL2: fullscreen modes are mutually exclusive
+	 * which means you can't switch from one to another. If required, need to remove the flag, then
+	 * set the new one. This will sadly trigger a window resizing
+	 */
+	if ( (SDL_GetWindowFlags(m_sdlwindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
+	{
+		if ( SDL_SetWindowFullscreen(m_sdlwindow, 0) != 0 )
+		{
+			log_error("Swithres/SDL2: (%s) Couldn't reset the fullscreen mode. %s\n", __FUNCTION__, SDL_GetError());
+			return false;
+		}
+	}
+
+	if ( (SDL_GetWindowFlags(m_sdlwindow) & SDL_WINDOW_FULLSCREEN) != SDL_WINDOW_FULLSCREEN )
+	{
+		// Now we set the right mode that allows SDL2 modeswitches
+		if ( SDL_SetWindowFullscreen(m_sdlwindow, SDL_WINDOW_FULLSCREEN) != 0 )
+		{
+			log_error("Swithres/SDL2: (%s) Couldn't set the window to FULLSCREEN. %s\n", __FUNCTION__, SDL_GetError());
+			return false;
+		}
+	}
+
+	// We may first check if the mode was already added, so we don't force a probe of all modes
+	if ( SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(m_sdlwindow), &target, &closest) == NULL )
+	{
+		// If the returned pointer is null, no match was found.
+		log_error("Swithres/SDL2: (%s) No suitable display mode was found! %s\n\n", __FUNCTION__, SDL_GetError());
+		return false;
+	}
+	log_verbose("  Received: \t%dx%dpx @ %dhz \n", closest.w, closest.h, closest.refresh_rate);
+
+	if ( SDL_SetWindowDisplayMode(m_sdlwindow, &closest) != 0 )
+	{
+		log_error("Swithres/SDL2: (%s) Failed to switch mode: %s\n", __FUNCTION__, SDL_GetError());
+		return false;
+	}
+
+	log_verbose("Swithres/SDL2: (%s) SDL2 display mode changed for window/display %d/%d!\n", __FUNCTION__, SDL_GetWindowID(m_sdlwindow), SDL_GetWindowDisplayIndex(m_sdlwindow));
+	log_verbose("               to %dx%d@%d\n",closest.w, closest.h, closest.refresh_rate);
+
+	set_current_mode(mode);
+	return true;
+}
+
+//============================================================
+//  sdl2_display::get_desktop_mode
+//============================================================
+
+bool sdl2_display::get_desktop_mode()
+{
+	if (video() == NULL)
+		return false;
+
+	return true;
+}
+
+//============================================================
+//  sdl2_display::get_available_video_modes
+//============================================================
+
+int sdl2_display::get_available_video_modes()
+{
+	if (video() == NULL)
+		return false;
+
+	// loop through all modes until NULL mode type is received
+	for (;;)
+	{
+		modeline mode;
+		memset(&mode, 0, sizeof(struct modeline));
+
+		// get next mode
+		video()->get_timing(&mode);
+		if (mode.type == 0)
+			break;
+
+		// set the desktop mode
+		if (mode.type & MODE_DESKTOP)
+		{
+			memcpy(&desktop_mode, &mode, sizeof(modeline));
+			if (current_mode() == nullptr)
+				set_current_mode(&mode);
+
+			if (mode.type & MODE_ROTATED) set_desktop_is_rotated(true);
+		}
+
+		video_modes.push_back(mode);
+		backup_modes.push_back(mode);
+
+		log_verbose("Switchres/SDL2: [%3ld] %4dx%4d @%3d%s%s %s: ", video_modes.size(), mode.width, mode.height, mode.refresh, mode.interlace ? "i" : "p", mode.type & MODE_DESKTOP ? "*" : "", mode.type & MODE_ROTATED ? "rot" : "");
+		log_mode(&mode);
+	};
+
+	return true;
+}
diff --git a/3rdparty/switchres/display_sdl2.h b/3rdparty/switchres/display_sdl2.h
new file mode 100644
index 00000000000..1051841d898
--- /dev/null
+++ b/3rdparty/switchres/display_sdl2.h
@@ -0,0 +1,32 @@
+/**************************************************************
+
+   display_linux.h - Display manager for Linux
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include "display.h"
+#include "SDL.h"
+#include "SDL_syswm.h"
+
+class sdl2_display : public display_manager
+{
+	public:
+		sdl2_display(display_settings *ds);
+		~sdl2_display();
+		bool init(void* pf_data);
+		bool set_mode(modeline *mode);
+
+	private:
+		SDL_Window* m_sdlwindow = NULL;
+
+		bool get_desktop_mode();
+		int get_available_video_modes();
+};
diff --git a/3rdparty/switchres/display_windows.cpp b/3rdparty/switchres/display_windows.cpp
new file mode 100644
index 00000000000..7d23f2a64cd
--- /dev/null
+++ b/3rdparty/switchres/display_windows.cpp
@@ -0,0 +1,311 @@
+/**************************************************************
+
+   display_windows.cpp - Display manager for Windows
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include "display_windows.h"
+#include "log.h"
+
+typedef struct ENUM_INFO
+{
+	int index;
+	HMONITOR h_monitor;
+} ENUM_INFO;
+
+//============================================================
+//  windows_display::windows_display
+//============================================================
+
+windows_display::windows_display(display_settings *ds)
+{
+	// Get display settings
+	m_ds = *ds;
+}
+
+//============================================================
+//  windows_display::~windows_display
+//============================================================
+
+windows_display::~windows_display()
+{
+	// Restore previous settings
+	if (!m_ds.keep_changes) ChangeDisplaySettingsExA(m_device_name, NULL, NULL, 0, 0);
+}
+
+//============================================================
+//  windows_display::init
+//============================================================
+
+int CALLBACK monitor_by_index(HMONITOR h_monitor, HDC, LPRECT, LPARAM data)
+{
+	ENUM_INFO *mon_info = (ENUM_INFO*) data;
+	if (--mon_info->index < 0)
+	{
+		mon_info->h_monitor = h_monitor;
+		return false;
+	}
+	return true;
+}
+
+bool windows_display::init(void*)
+{
+	char display[32] = {};
+
+	// If monitor is passed by index, find the matching device
+	if (strlen(m_ds.screen) == 1)
+	{
+		int monitor_index = m_ds.screen[0] - '0';
+		if (monitor_index < 0 || monitor_index > 9)
+		{
+			log_error("Switchres: bad monitor index %d\n", monitor_index);
+			return false;
+		}
+
+		ENUM_INFO mon_info;
+		mon_info.index = monitor_index;
+		mon_info.h_monitor = NULL;
+
+		EnumDisplayMonitors(NULL, NULL, monitor_by_index, (LPARAM)&mon_info);
+		if (mon_info.h_monitor != NULL)
+		{
+			MONITORINFOEXA info = {};
+			info.cbSize = sizeof(info);
+			GetMonitorInfoA(mon_info.h_monitor, &info);
+			snprintf(display, sizeof(display) -1, "%s", info.szDevice);
+			log_info("display %s\n", display);
+		}
+		else
+		{
+			log_error("Swichres: couldn't find handle for monitor index %d\n", monitor_index);
+			return false;
+		}
+	}
+	else
+		strncpy(display, m_ds.screen, sizeof(display)-1);
+
+	// Find the display by device name, or "auto" for primary display
+	DISPLAY_DEVICEA lpDisplayDevice[DISPLAY_MAX];
+	int idev = 0;
+	int found = -1;
+
+	while (idev < DISPLAY_MAX)
+	{
+		memset(&lpDisplayDevice[idev], 0, sizeof(DISPLAY_DEVICEA));
+		lpDisplayDevice[idev].cb = sizeof(DISPLAY_DEVICEA);
+
+		if (EnumDisplayDevicesA(NULL, idev, &lpDisplayDevice[idev], 0) == FALSE)
+			break;
+
+		if ((!strcmp(display, "auto") && (lpDisplayDevice[idev].StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
+			|| !strcmp(display, lpDisplayDevice[idev].DeviceName))
+			found = idev;
+
+		idev++;
+	}
+	if (found != -1)
+	{
+		strncpy(m_device_name, lpDisplayDevice[found].DeviceName, sizeof(m_device_name) -1);
+		strncpy(m_device_id, lpDisplayDevice[found].DeviceID, sizeof(m_device_id) -1);
+		log_verbose("Switchres: %s: %s (%s)\n", m_device_name, lpDisplayDevice[found].DeviceString, m_device_id);
+
+		char *pch;
+		int i;
+		for (i = 0; i < idev; i++)
+		{
+			pch = strstr(lpDisplayDevice[i].DeviceString, lpDisplayDevice[found].DeviceString);
+			if (pch)
+			{
+				found = i;
+				break;
+			}
+		}
+
+		char *chsrc, *chdst;
+		chdst = m_device_key;
+
+		for (chsrc = lpDisplayDevice[i].DeviceKey + 18; *chsrc != 0; chsrc++)
+			*chdst++ = *chsrc;
+
+		*chdst = 0;
+	}
+	else
+	{
+		log_verbose("Switchres: Failed obtaining default video registry key\n");
+		return false;
+	}
+
+	log_verbose("Switchres: Device key: %s\n", m_device_key);
+
+	// Initialize custom video
+	int method = CUSTOM_VIDEO_TIMING_AUTO;
+	if(!strcmp(m_ds.api, "powerstrip")) method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
+	strcpy(m_ds.vs.device_reg_key, m_device_key);
+
+	// Create custom video backend
+	set_factory(new custom_video);
+	set_custom_video(factory()->make(m_device_name, m_device_id, method, &m_ds.vs));
+	if (video()) video()->init();
+
+	// Build our display's mode list
+	video_modes.clear();
+	backup_modes.clear();
+	get_desktop_mode();
+	get_available_video_modes();
+	if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
+	filter_modes();
+
+	return true;
+}
+
+//============================================================
+//  windows_display::set_mode
+//============================================================
+
+bool windows_display::set_mode(modeline *mode)
+{
+	if (mode && set_desktop_mode(mode, (m_ds.keep_changes? CDS_UPDATEREGISTRY : CDS_FULLSCREEN) | CDS_RESET))
+	{
+		set_current_mode(mode);
+		return true;
+	}
+
+	return false;
+}
+
+//============================================================
+//  windows_display::get_desktop_mode
+//============================================================
+
+bool windows_display::get_desktop_mode()
+{
+	memset(&m_devmode, 0, sizeof(DEVMODEA));
+	m_devmode.dmSize = sizeof(DEVMODEA);
+
+	if (EnumDisplaySettingsExA(!strcmp(m_device_name, "auto")?NULL:m_device_name, ENUM_CURRENT_SETTINGS, &m_devmode, 0))
+	{
+		desktop_mode.width = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsWidth:m_devmode.dmPelsHeight;
+		desktop_mode.height = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsHeight:m_devmode.dmPelsWidth;
+		desktop_mode.refresh = m_devmode.dmDisplayFrequency;
+		desktop_mode.interlace = (m_devmode.dmDisplayFlags & DM_INTERLACED)?1:0;
+		return true;
+	}
+	return false;
+}
+
+//============================================================
+//  windows_display::set_desktop_mode
+//============================================================
+
+bool windows_display::set_desktop_mode(modeline *mode, int flags)
+{
+	if (mode)
+	{
+		DEVMODEA lpDevMode;
+		memset(&lpDevMode, 0, sizeof(DEVMODEA));
+		lpDevMode.dmSize = sizeof(DEVMODEA);
+		lpDevMode.dmPelsWidth = mode->type & MODE_ROTATED? mode->height : mode->width;
+		lpDevMode.dmPelsHeight = mode->type & MODE_ROTATED? mode->width : mode->height;
+		lpDevMode.dmDisplayFrequency = (int)mode->refresh;
+		lpDevMode.dmDisplayFlags = mode->interlace? DM_INTERLACED : 0;
+		lpDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
+
+		log_info("set_desktop_mode: %s (%dx%d@%d) flags(%x)\n", m_device_name, (int)lpDevMode.dmPelsWidth, (int)lpDevMode.dmPelsHeight, (int)lpDevMode.dmDisplayFrequency, (int)lpDevMode.dmDisplayFlags);
+
+		int result = ChangeDisplaySettingsExA(m_device_name, &lpDevMode, NULL, flags, 0);
+		if (result == DISP_CHANGE_SUCCESSFUL)
+			return true;
+
+		log_error("ChangeDisplaySettingsExA error(%x)\n", (int)result);
+	}
+	return false;
+}
+
+//============================================================
+//  windows_display::restore_desktop_mode
+//============================================================
+
+bool windows_display::restore_desktop_mode()
+{
+	if (ChangeDisplaySettingsExA(m_device_name, &m_devmode, NULL, 0, 0) == DISP_CHANGE_SUCCESSFUL)
+		return true;
+
+	return false;
+}
+
+//============================================================
+//  windows_display::get_available_video_modes
+//============================================================
+
+int windows_display::get_available_video_modes()
+{
+	int iModeNum = 0, j = 0, k = 0;
+	DEVMODEA lpDevMode;
+
+	memset(&lpDevMode, 0, sizeof(DEVMODEA));
+	lpDevMode.dmSize = sizeof(DEVMODEA);
+
+	log_verbose("Switchres: Searching for custom video modes...\n");
+
+	while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, m_ds.lock_unsupported_modes?0:EDS_RAWMODE) != 0)
+	{
+		if (lpDevMode.dmBitsPerPel == 32 && lpDevMode.dmDisplayFixedOutput == DMDFO_DEFAULT)
+		{
+			modeline m;
+			memset(&m, 0, sizeof(struct modeline));
+			m.interlace = (lpDevMode.dmDisplayFlags & DM_INTERLACED)?1:0;
+			m.width = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsWidth:lpDevMode.dmPelsHeight;
+			m.height = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsHeight:lpDevMode.dmPelsWidth;
+			m.refresh = lpDevMode.dmDisplayFrequency;
+			m.hactive = m.width;
+			m.vactive = m.height;
+			m.vfreq = m.refresh;
+			m.type |= lpDevMode.dmDisplayOrientation == DMDO_90 || lpDevMode.dmDisplayOrientation == DMDO_270? MODE_ROTATED : MODE_OK;
+
+			for (auto &mode : video_modes) if (mode.width == m.width && mode.height == m.height && mode.refresh == m.refresh && m.interlace == mode.interlace) goto found;
+
+			if (m.width == desktop_mode.width && m.height == desktop_mode.height && m.refresh == desktop_mode.refresh && m.interlace == desktop_mode.interlace)
+			{
+				m.type |= MODE_DESKTOP;
+				if (m.type & MODE_ROTATED) set_desktop_is_rotated(true);
+				if (current_mode() == nullptr)
+				set_current_mode(&m);
+			}
+
+			log_verbose("Switchres: [%3d] %4dx%4d @%3d%s%s %s: ", k, m.width, m.height, m.refresh, m.interlace?"i":"p", m.type & MODE_DESKTOP?"*":"",  m.type & MODE_ROTATED?"rot":"");
+
+			if (video() && video()->get_timing(&m))
+			{
+				j++;
+				log_mode(&m);
+			}
+			else
+			{
+				m.type |= CUSTOM_VIDEO_TIMING_SYSTEM;
+				log_verbose("system mode\n");
+			}
+
+			// Save our desktop mode now that we queried detailed timings
+			if (m.type & MODE_DESKTOP) desktop_mode = m;
+
+			video_modes.push_back(m);
+			backup_modes.push_back(m);
+			k++;
+		}
+		found:
+		iModeNum++;
+	}
+	k--;
+	log_verbose("Switchres: Found %d custom of %d active video modes\n", j, k);
+	return k;
+}
+
diff --git a/3rdparty/switchres/display_windows.h b/3rdparty/switchres/display_windows.h
new file mode 100644
index 00000000000..760381f3c0d
--- /dev/null
+++ b/3rdparty/switchres/display_windows.h
@@ -0,0 +1,45 @@
+/**************************************************************
+
+   display_windows.h - Display manager for Windows
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <windows.h>
+#include "display.h"
+
+//============================================================
+//  PARAMETERS
+//============================================================
+
+// display modes
+#define DM_INTERLACED 0x00000002
+#define DISPLAY_MAX 16
+
+
+class windows_display : public display_manager
+{
+	public:
+		windows_display(display_settings *ds);
+		~windows_display();
+		bool init(void* = nullptr);
+		bool set_mode(modeline *mode);
+
+	private:
+		bool get_desktop_mode();
+		bool set_desktop_mode(modeline *mode, int flags);
+		bool restore_desktop_mode();
+		int get_available_video_modes();
+
+		char m_device_name[32];
+		char m_device_id[128];
+		char m_device_key[128];
+		DEVMODEA m_devmode;
+};
diff --git a/3rdparty/switchres/drm_hook.cpp b/3rdparty/switchres/drm_hook.cpp
new file mode 100644
index 00000000000..ca63571bbe5
--- /dev/null
+++ b/3rdparty/switchres/drm_hook.cpp
@@ -0,0 +1,211 @@
+/**************************************************************
+
+   drm_hook.cpp - Linux DRM/KMS library hook
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2022 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <dlfcn.h>
+#include <cstring>
+
+// DRM headers
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#define MAX_CONNECTORS 10
+
+bool hook_connector(drmModeConnectorPtr conn);
+drmModeConnectorPtr get_connector(uint32_t connector_id);
+
+typedef struct connector_hook
+{
+	drmModeConnector conn;
+	drmModeModeInfo modes[128];
+	uint32_t props[128];
+	uint64_t prop_values[128];
+	uint32_t encoders[128];
+} connector_hook;
+
+connector_hook connector[MAX_CONNECTORS];
+int m_num_connectors = 0;
+
+//============================================================
+//  drmModeGetConnector
+//============================================================
+
+drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
+{
+	static void* (*my_get_connector)(int, uint32_t) = NULL;
+
+	if (!my_get_connector)
+		my_get_connector = (void*(*)(int, uint32_t))dlsym(RTLD_NEXT, "drmModeGetConnector");
+
+	// Allow hook detection (original func would return NULL)
+	if (fd == -1)
+		return &connector[0].conn;
+
+	drmModeConnectorPtr conn = get_connector(connector_id);
+
+	if (conn != NULL)
+		// already hooked
+		return conn;
+
+	else
+	{
+		// attempt connector hook
+		conn = (drmModeConnectorPtr)my_get_connector(fd, connector_id);
+		if (!conn) return NULL;
+
+		if (hook_connector(conn))
+		{
+			conn = get_connector(connector_id);
+			printf("Switchres: returning hooked connector %d\n", conn->connector_id);
+		}
+	}
+
+	return conn;
+}
+
+//============================================================
+//  drmModeGetConnectorCurrent
+//============================================================
+
+drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, uint32_t connector_id)
+{
+	static void* (*my_get_connector_current)(int, uint32_t) = NULL;
+
+	if (!my_get_connector_current)
+		my_get_connector_current = (void*(*)(int, uint32_t))dlsym(RTLD_NEXT, "drmModeGetConnectorCurrent");
+
+	// Allow hook detection (original func would return NULL)
+	if (fd == -1)
+		return &connector[0].conn;
+
+	drmModeConnectorPtr conn = get_connector(connector_id);
+
+	if (conn != NULL)
+		// already hooked
+		return conn;
+
+	else
+	{
+		// attempt connector hook
+		conn = (drmModeConnectorPtr)my_get_connector_current(fd, connector_id);
+		if (!conn) return NULL;
+
+		if (hook_connector(conn))
+		{
+			conn = get_connector(connector_id);
+			printf("Switchres: returning hooked connector %d\n", conn->connector_id);
+		}
+	}
+
+	return conn;
+}
+
+//============================================================
+//  drmModeFreeConnector
+//============================================================
+
+void drmModeFreeConnector(drmModeConnectorPtr ptr)
+{
+	static void (*my_free_connector)(drmModeConnectorPtr) = NULL;
+
+	if (!my_free_connector)
+		my_free_connector = (void (*)(drmModeConnectorPtr)) dlsym(RTLD_NEXT, "drmModeFreeConnector");
+
+	// Skip our hooked connector
+	for (int i = 0; i < m_num_connectors; i++)
+		if (ptr == &connector[i].conn)
+			return;
+
+	my_free_connector(ptr);
+}
+
+//============================================================
+//  hook_connector
+//============================================================
+
+bool hook_connector(drmModeConnectorPtr conn)
+{
+	if (conn == NULL)
+		return false;
+
+	if (m_num_connectors >= MAX_CONNECTORS)
+		return false;
+
+	if (conn->count_modes == 0)
+		return false;
+
+	connector_hook *conn_hook = &connector[m_num_connectors++];
+	drmModeConnectorPtr my_conn = &conn_hook->conn;
+
+	printf("Switchres: hooking connector %d\n", conn->connector_id);
+	*my_conn = *conn;
+
+	drmModeModeInfo *my_modes = conn_hook->modes;
+	memcpy(my_modes, conn->modes, sizeof(drmModeModeInfo) * conn->count_modes);
+	my_conn->modes = my_modes;
+
+	uint32_t *my_encoders = conn_hook->encoders;
+	memcpy(my_encoders, conn->encoders, sizeof(uint32_t) * conn->count_encoders);
+	my_conn->encoders = my_encoders;
+
+	uint32_t *my_props = conn_hook->props;
+	memcpy(my_props, conn->props, sizeof(uint32_t) * conn->count_props);
+	my_conn->props = my_props;
+
+	uint64_t *my_prop_values = conn_hook->prop_values;
+	memcpy(my_prop_values, conn->prop_values, sizeof(uint64_t) * conn->count_props);
+	my_conn->prop_values = my_prop_values;
+
+	drmModeFreeConnector(conn);
+
+	bool found = false;
+	drmModeModeInfo *mode = NULL;
+	for (int i = 0; i < my_conn->count_modes; i++)
+	{
+		mode = &my_modes[i];
+		if (mode->type & DRM_MODE_TYPE_PREFERRED)
+		{
+			found = true;
+			break;
+		}
+	}
+
+	// If preferred mode not found, default to first entry
+	if (!found)
+		mode = &my_modes[0];
+
+	// Add dummy mode to mode list (preferred mode with hdisplay +1, vfresh +1)
+	drmModeModeInfo *dummy_mode = &my_modes[my_conn->count_modes];
+	*dummy_mode = *mode;
+	dummy_mode->vrefresh++;
+	dummy_mode->hdisplay++;
+	dummy_mode->type |= (1<<7);
+	my_conn->count_modes++;
+
+	return true;
+}
+
+//============================================================
+//  get_connector
+//============================================================
+
+drmModeConnectorPtr get_connector(uint32_t connector_id)
+{
+	for (int i = 0; i < m_num_connectors; i++)
+		if (connector[i].conn.connector_id == connector_id)
+			return &connector[i].conn;
+
+	return NULL;
+}
diff --git a/3rdparty/switchres/edid.cpp b/3rdparty/switchres/edid.cpp
new file mode 100644
index 00000000000..3b27a6cfa9c
--- /dev/null
+++ b/3rdparty/switchres/edid.cpp
@@ -0,0 +1,244 @@
+/**************************************************************
+
+   edid.c - Basic EDID generation
+   (based on edid.S: EDID data template by Carsten Emde)
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "switchres.h"
+#include "edid.h"
+
+//============================================================
+//  edid_from_modeline
+//============================================================
+
+int edid_from_modeline(modeline *mode, monitor_range *range, const char *name, edid_block *edid)
+{
+	if (!edid) return 0;
+
+	// header
+	edid->b[0] = 0x00;
+	edid->b[1] = 0xff;
+	edid->b[2] = 0xff;
+	edid->b[3] = 0xff;
+	edid->b[4] = 0xff;
+	edid->b[5] = 0xff;
+	edid->b[6] = 0xff;
+	edid->b[7] = 0x00;
+
+	// Manufacturer ID = "SWR"
+	edid->b[8] = 0x4e;
+	edid->b[9] = 0xf2;
+
+	// Manufacturer product code
+	edid->b[10] = 0x00;
+	edid->b[11] = 0x00;
+
+	// Serial number
+	edid->b[12] = 0x00;
+	edid->b[13] = 0x00;
+	edid->b[14] = 0x00;
+	edid->b[15] = 0x00;
+
+	// Week of manufacture
+	edid->b[16] = 5;
+
+	// Year of manufacture
+	edid->b[17] = 2021 - 1990;
+
+	// EDID version and revision
+	edid->b[18] = 1;
+	edid->b[19] = 3;
+
+	// video params
+	edid->b[20] = 0x6d;
+
+	// Maximum H & V size in cm
+	edid->b[21] = 48;
+	edid->b[22] = 36;
+
+	// Gamma
+	edid->b[23] = 120;
+
+	// Display features
+	edid->b[24] = 0x0A;
+
+	// Chromacity coordinates;
+	edid->b[25] = 0x5e;
+	edid->b[26] = 0xc0;
+	edid->b[27] = 0xa4;
+	edid->b[28] = 0x59;
+	edid->b[29] = 0x4a;
+	edid->b[30] = 0x98;
+	edid->b[31] = 0x25;
+	edid->b[32] = 0x20;
+	edid->b[33] = 0x50;
+	edid->b[34] = 0x54;
+
+	// Established timings
+	edid->b[35] = 0x00;
+	edid->b[36] = 0x00;
+	edid->b[37] = 0x00;
+
+	// Standard timing information
+	edid->b[38] = 0x01;
+	edid->b[39] = 0x01;
+	edid->b[40] = 0x01;
+	edid->b[41] = 0x01;
+	edid->b[42] = 0x01;
+	edid->b[43] = 0x01;
+	edid->b[44] = 0x01;
+	edid->b[45] = 0x01;
+	edid->b[46] = 0x01;
+	edid->b[47] = 0x01;
+	edid->b[48] = 0x01;
+	edid->b[49] = 0x01;
+	edid->b[50] = 0x01;
+	edid->b[51] = 0x01;
+	edid->b[52] = 0x01;
+	edid->b[53] = 0x01;
+
+	// Pixel clock in 10 kHz units. (0.-655.35 MHz, little-endian)
+	edid->b[54] = (mode->pclock / 10000) & 0xff;
+	edid->b[55] = (mode->pclock / 10000) >> 8;
+
+	int h_active = mode->hactive;
+	int h_blank = mode->htotal - mode->hactive;
+	int h_offset = mode->hbegin - mode->hactive;
+	int h_pulse = mode->hend - mode->hbegin;
+
+	int v_active = mode->vactive;
+	int v_blank = (int)mode->vtotal - mode->vactive;
+	int v_offset = mode->vbegin - mode->vactive;
+	int v_pulse = mode->vend - mode->vbegin;
+
+	// Horizontal active pixels 8 lsbits (0-4095)
+	edid->b[56] = h_active & 0xff;
+
+	// Horizontal blanking pixels 8 lsbits (0-4095)
+	edid->b[57] = h_blank & 0xff;
+
+	// Bits 7-4 Horizontal active pixels 4 msbits
+	// Bits 3-0 Horizontal blanking pixels 4 msbits
+	edid->b[58] = (((h_active >> 8) & 0x0f) << 4) + ((h_blank >> 8) & 0x0f);
+
+	// Vertical active lines 8 lsbits (0-4095)
+	edid->b[59] = v_active & 0xff;
+
+	// Vertical blanking lines 8 lsbits (0-4095)
+	edid->b[60] = v_blank & 0xff;
+
+	// Bits 7-4 Vertical active lines 4 msbits
+	// Bits 3-0 Vertical blanking lines 4 msbits
+	edid->b[61] = (((v_active >> 8) & 0x0f) << 4) + ((v_blank >> 8) & 0x0f);
+
+	// Horizontal sync offset pixels 8 lsbits (0-1023) From blanking start
+	edid->b[62] = h_offset & 0xff;
+
+	// Horizontal sync pulse width pixels 8 lsbits (0-1023)
+	edid->b[63] = h_pulse & 0xff;
+
+	// Bits 7-4 Vertical sync offset lines 4 lsbits 0-63)
+	// Bits 3-0 Vertical sync pulse width lines 4 lsbits 0-63)
+	edid->b[64] = ((v_offset & 0x0f) << 4) + (v_pulse & 0x0f);
+
+	// Bits 7-6     Horizontal sync offset pixels 2 msbits
+	// Bits 5-4     Horizontal sync pulse width pixels 2 msbits
+	// Bits 3-2     Vertical sync offset lines 2 msbits
+	// Bits 1-0     Vertical sync pulse width lines 2 msbits
+	edid->b[65] = (((h_offset >> 8) & 0x03) << 6) +
+			   (((h_pulse >> 8) & 0x03) << 4) +
+			   (((v_offset >> 8) & 0x03) << 2) +
+			   ((v_pulse >> 8) & 0x03);
+
+	// Horizontal display size, mm, 8 lsbits (0-4095 mm, 161 in)
+	edid->b[66] = 485 & 0xff;
+
+	// Vertical display size, mm, 8 lsbits (0-4095 mm, 161 in)
+	edid->b[67] = 364 & 0xff;
+
+	// Bits 7-4 Horizontal display size, mm, 4 msbits
+	// Bits 3-0 Vertical display size, mm, 4 msbits
+	edid->b[68] = (((485 >> 8) & 0x0f) << 4) + ((364 >> 8) & 0x0f);
+
+	// Horizontal border pixels (each side; total is twice this)
+	edid->b[69] = 0;
+
+	// Vertical border lines (each side; total is twice this)
+	edid->b[70] = 0;
+
+	// Features bitmap
+	edid->b[71] = ((mode->interlace & 0x01) << 7) + 0x18 + (mode->vsync << 2) + (mode->hsync << 2);
+
+
+	// Descriptor: monitor serial number
+	edid->b[72] = 0;
+	edid->b[73] = 0;
+	edid->b[74] = 0;
+	edid->b[75] = 0xff;
+	edid->b[76] = 0;
+	edid->b[77] = 'S';
+	edid->b[78] = 'w';
+	edid->b[79] = 'i';
+	edid->b[80] = 't';
+	edid->b[81] = 'c';
+	edid->b[82] = 'h';
+	edid->b[83] = 'r';
+	edid->b[84] = 'e';
+	edid->b[85] = 's';
+	edid->b[86] = '2';
+	edid->b[87] = '0';
+	edid->b[88] = '0';
+	edid->b[89] = 0x0a;
+
+	// Descriptor: monitor range limits
+	edid->b[90] = 0;
+	edid->b[91] = 0;
+	edid->b[92] = 0;
+	edid->b[93] = 0xfd;
+	edid->b[94] = 0;
+	edid->b[95] = ((int)range->vfreq_min) & 0xff;
+	edid->b[96] = ((int)range->vfreq_max) & 0xff;
+	edid->b[97] = ((int)range->hfreq_min / 1000) & 0xff;
+	edid->b[98] = ((int)range->hfreq_max / 1000) & 0xff;
+	edid->b[99] = 0xff;
+	edid->b[100] = 0;
+	edid->b[101] = 0x0a;
+	edid->b[102] = 0x20;
+	edid->b[103] = 0x20;
+	edid->b[104] = 0x20;
+	edid->b[105] = 0x20;
+	edid->b[106] = 0x20;
+	edid->b[107] = 0x20;
+
+	// Descriptor: text
+	edid->b[108] = 0;
+	edid->b[109] = 0;
+	edid->b[110] = 0;
+	edid->b[111] = 0xfc;
+	edid->b[112] = 0;
+	snprintf(&edid->b[113], 13, "%s", name);
+	edid->b[125] = 0x0a;
+
+	// Extensions to follow
+	edid->b[126] = 0;
+
+	// Compute checksum
+	char checksum = 0;
+	int i;
+	for (i = 0; i <= 126; i++)
+		checksum += edid->b[i];
+	edid->b[127] = 256 - checksum;
+
+	return 1;
+}
diff --git a/3rdparty/switchres/edid.h b/3rdparty/switchres/edid.h
new file mode 100644
index 00000000000..66bffa1184c
--- /dev/null
+++ b/3rdparty/switchres/edid.h
@@ -0,0 +1,37 @@
+/**************************************************************
+
+   edid.h - Basic EDID generation
+   (based on edid.S: EDID data template by Carsten Emde)
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __EDID_H__
+#define __EDID_H__
+
+//============================================================
+//  TYPE DEFINITIONS
+//============================================================
+
+typedef struct edid_block
+{
+	char b[128];
+/*  char ext1[128];
+    char ext2[128];
+    char ext3[128];*/
+} edid_block;
+
+//============================================================
+//  PROTOTYPES
+//============================================================
+
+int edid_from_modeline(modeline *mode, monitor_range *range, const char *name, edid_block *edid);
+
+#endif
diff --git a/3rdparty/switchres/font.h b/3rdparty/switchres/font.h
new file mode 100644
index 00000000000..3a24c6dbe7b
--- /dev/null
+++ b/3rdparty/switchres/font.h
@@ -0,0 +1,3776 @@
+#ifndef ATTRACTPLUS_TTF_H
+#define ATTRACTPLUS_TTF_H
+
+static const unsigned char ATTRACTPLUS_TTF[] = {
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x80, 0x00, 0x03, 0x00, 0x60,
+	0x46, 0x46, 0x54, 0x4d, 0x6f, 0x57, 0x60, 0x3a, 0x00, 0x00, 0xb0, 0x8c,
+	0x00, 0x00, 0x00, 0x1c, 0x47, 0x44, 0x45, 0x46, 0x01, 0xaf, 0x00, 0x24,
+	0x00, 0x00, 0xb0, 0x64, 0x00, 0x00, 0x00, 0x28, 0x4f, 0x53, 0x2f, 0x32,
+	0x68, 0xca, 0x32, 0x45, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x00, 0x56,
+	0x63, 0x6d, 0x61, 0x70, 0xdb, 0x66, 0xe8, 0x57, 0x00, 0x00, 0x07, 0xc0,
+	0x00, 0x00, 0x03, 0x76, 0x63, 0x76, 0x74, 0x20, 0x00, 0x22, 0x02, 0x88,
+	0x00, 0x00, 0x0b, 0x38, 0x00, 0x00, 0x00, 0x04, 0x67, 0x61, 0x73, 0x70,
+	0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0xb0, 0x5c, 0x00, 0x00, 0x00, 0x08,
+	0x67, 0x6c, 0x79, 0x66, 0xec, 0xb7, 0xc3, 0x0d, 0x00, 0x00, 0x0e, 0x40,
+	0x00, 0x00, 0x97, 0x78, 0x68, 0x65, 0x61, 0x64, 0x22, 0x08, 0x4c, 0x17,
+	0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x36, 0x68, 0x68, 0x65, 0x61,
+	0x09, 0x73, 0x06, 0x93, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x24,
+	0x68, 0x6d, 0x74, 0x78, 0x43, 0xc7, 0x01, 0x45, 0x00, 0x00, 0x01, 0xc0,
+	0x00, 0x00, 0x06, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0xa3, 0x69, 0x7c, 0x14,
+	0x00, 0x00, 0x0b, 0x3c, 0x00, 0x00, 0x03, 0x02, 0x6d, 0x61, 0x78, 0x70,
+	0x01, 0xd4, 0x00, 0xa5, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x20,
+	0x6e, 0x61, 0x6d, 0x65, 0x36, 0xa7, 0x1a, 0x86, 0x00, 0x00, 0xa5, 0xb8,
+	0x00, 0x00, 0x01, 0xda, 0x70, 0x6f, 0x73, 0x74, 0xc8, 0xb1, 0x50, 0x66,
+	0x00, 0x00, 0xa7, 0x94, 0x00, 0x00, 0x08, 0xc7, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0xe1, 0x5c, 0xf2, 0x43, 0x5f, 0x0f, 0x3c, 0xf5,
+	0x00, 0x1f, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xe0, 0x84, 0x1a,
+	0x00, 0x00, 0x00, 0x00, 0xde, 0xe0, 0x84, 0x1a, 0x00, 0x00, 0xfe, 0xe9,
+	0x05, 0x14, 0x03, 0xfd, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x46, 0xff, 0x46,
+	0x01, 0x17, 0x05, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x05, 0x14, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0x00, 0x74,
+	0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x01, 0x97, 0x01, 0xf4, 0x00, 0x05, 0x00, 0x00, 0x02, 0x99,
+	0x02, 0xcc, 0x00, 0x00, 0x00, 0x8f, 0x02, 0x99, 0x02, 0xcc, 0x00, 0x00,
+	0x01, 0xeb, 0x00, 0x33, 0x01, 0x09, 0x00, 0x00, 0x02, 0x00, 0x06, 0x03,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x07, 0x00, 0x01,
+	0xe0, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x74,
+	0x74, 0x66, 0x00, 0x40, 0x00, 0x20, 0x30, 0x00, 0x03, 0x46, 0xff, 0x46,
+	0x01, 0x17, 0x03, 0x46, 0x01, 0xd1, 0x40, 0x00, 0x00, 0x93, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x02, 0x1e, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0xee, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x00, 0xb9, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x5c, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x00, 0xb9, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0xe6, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0x16, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00,
+	0x01, 0x16, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x00, 0xb9, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00,
+	0x01, 0x16, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x00, 0xb9, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0xe6, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0xe6, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0x16, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x89, 0x00, 0x00,
+	0x02, 0x89, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x00, 0xb9, 0x00, 0x00, 0x03, 0x43, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0x16, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0x73, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0xe6, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0x16, 0x00, 0x00, 0x01, 0x16, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00,
+	0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00,
+	0x01, 0x73, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0x89, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0xe6, 0x00, 0x00, 0x03, 0x43, 0x00, 0x5c, 0x02, 0xe6, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x5c, 0x02, 0xe6, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x03, 0xfc, 0x00, 0x00, 0x04, 0xb6, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x05, 0x13, 0x00, 0x00, 0x04, 0xb6, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x03, 0xa0, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x03, 0xa0, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00,
+	0x02, 0xe6, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00,
+	0x03, 0xfc, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x05, 0x70, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00, 0x05, 0x70, 0x00, 0x00,
+	0x03, 0xa0, 0x00, 0x00, 0x05, 0x13, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0x00, 0x02, 0xe6, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00,
+	0x04, 0x59, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00,
+	0x03, 0x43, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00,
+	0x05, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0x00, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6c,
+	0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x01, 0x50,
+	0x00, 0x00, 0x00, 0x50, 0x00, 0x40, 0x00, 0x05, 0x00, 0x10, 0x00, 0x7e,
+	0x00, 0xac, 0x01, 0x7e, 0x20, 0x19, 0x20, 0x1d, 0x20, 0x22, 0x20, 0x26,
+	0x20, 0x33, 0x20, 0x3d, 0x20, 0xac, 0x21, 0x22, 0x21, 0x93, 0x22, 0x1e,
+	0x25, 0xb2, 0x25, 0xba, 0x25, 0xbc, 0x25, 0xc4, 0x26, 0x05, 0x26, 0x0e,
+	0x26, 0x20, 0x26, 0x2f, 0x26, 0x3c, 0x26, 0x40, 0x26, 0x42, 0x26, 0x53,
+	0x26, 0x61, 0x26, 0x63, 0x26, 0x66, 0x26, 0x6b, 0x27, 0x02, 0x27, 0x04,
+	0x27, 0x09, 0x27, 0x0c, 0x27, 0x0e, 0x27, 0x10, 0x27, 0x1e, 0x27, 0x21,
+	0x27, 0x3f, 0x30, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x20, 0x00, 0xa0,
+	0x00, 0xae, 0x20, 0x18, 0x20, 0x1c, 0x20, 0x22, 0x20, 0x26, 0x20, 0x32,
+	0x20, 0x3d, 0x20, 0xac, 0x21, 0x22, 0x21, 0x90, 0x22, 0x1e, 0x25, 0xb2,
+	0x25, 0xba, 0x25, 0xbc, 0x25, 0xc4, 0x26, 0x02, 0x26, 0x0e, 0x26, 0x20,
+	0x26, 0x2e, 0x26, 0x39, 0x26, 0x40, 0x26, 0x42, 0x26, 0x48, 0x26, 0x60,
+	0x26, 0x63, 0x26, 0x65, 0x26, 0x6a, 0x27, 0x02, 0x27, 0x04, 0x27, 0x08,
+	0x27, 0x0c, 0x27, 0x0e, 0x27, 0x10, 0x27, 0x1e, 0x27, 0x21, 0x27, 0x3f,
+	0x30, 0x00, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xc2, 0xff, 0xc1, 0xe1, 0x28,
+	0xe1, 0x26, 0xe1, 0x22, 0xe1, 0x1f, 0xe1, 0x14, 0xe1, 0x0b, 0xe0, 0x9d,
+	0xe0, 0x28, 0xdf, 0xbb, 0xdf, 0x31, 0xdb, 0x9e, 0xdb, 0x97, 0xdb, 0x96,
+	0xdb, 0x8f, 0xdb, 0x52, 0xdb, 0x4a, 0xdb, 0x39, 0xdb, 0x2c, 0xdb, 0x23,
+	0xdb, 0x20, 0xdb, 0x1f, 0xdb, 0x1a, 0xdb, 0x0e, 0xdb, 0x0d, 0xdb, 0x0c,
+	0xdb, 0x09, 0xda, 0x73, 0xda, 0x72, 0xda, 0x6f, 0xda, 0x6d, 0xda, 0x6c,
+	0xda, 0x6b, 0xda, 0x5e, 0xda, 0x5c, 0xda, 0x3f, 0xd1, 0x7f, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x0a,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09,
+	0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f,
+	0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15,
+	0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b,
+	0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x21,
+	0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27,
+	0x00, 0x28, 0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d,
+	0x00, 0x2e, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33,
+	0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39,
+	0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f,
+	0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45,
+	0x00, 0x46, 0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b,
+	0x00, 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x50, 0x00, 0x51,
+	0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57,
+	0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d,
+	0x00, 0x5e, 0x00, 0x5f, 0x00, 0x60, 0x00, 0x61, 0x00, 0x00, 0x00, 0x85,
+	0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9d,
+	0x00, 0xa2, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa6,
+	0x00, 0xa8, 0x00, 0xaa, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xac, 0x00, 0xae,
+	0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb3,
+	0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb6, 0x00, 0xbb, 0x00, 0xba, 0x00, 0xbc,
+	0x00, 0xbd, 0x00, 0x00, 0x00, 0x71, 0x00, 0x64, 0x00, 0x65, 0x00, 0x69,
+	0x01, 0x44, 0x00, 0x77, 0x00, 0xa0, 0x00, 0x6f, 0x00, 0x6b, 0x01, 0x4a,
+	0x00, 0x75, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x87, 0x00, 0x99, 0x01, 0x4f,
+	0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x76, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x7b,
+	0x00, 0x00, 0x00, 0xa7, 0x00, 0xb9, 0x00, 0x80, 0x00, 0x63, 0x00, 0x6e,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x7c,
+	0x01, 0x45, 0x00, 0x62, 0x00, 0x81, 0x00, 0x84, 0x00, 0x96, 0x01, 0x13,
+	0x01, 0x14, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x43, 0x01, 0x40,
+	0x01, 0x41, 0x00, 0xb8, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x39, 0x00, 0x00,
+	0x01, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x8b,
+	0x00, 0x82, 0x00, 0x8c, 0x00, 0x89, 0x00, 0x8e, 0x00, 0x8f, 0x00, 0x90,
+	0x00, 0x8d, 0x00, 0x94, 0x00, 0x95, 0x00, 0x00, 0x00, 0x93, 0x00, 0x9b,
+	0x00, 0x9c, 0x00, 0x9a, 0x00, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x02, 0x88, 0x00, 0x00, 0x00, 0x2c,
+	0x00, 0x2c, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x90,
+	0x00, 0xd4, 0x01, 0x0e, 0x01, 0x5a, 0x01, 0x6c, 0x01, 0x96, 0x01, 0xbe,
+	0x01, 0xde, 0x01, 0xfa, 0x02, 0x0c, 0x02, 0x1e, 0x02, 0x2a, 0x02, 0x4a,
+	0x02, 0x82, 0x02, 0x9e, 0x02, 0xd8, 0x03, 0x12, 0x03, 0x3c, 0x03, 0x6e,
+	0x03, 0xa8, 0x03, 0xd0, 0x04, 0x12, 0x04, 0x4a, 0x04, 0x5c, 0x04, 0x76,
+	0x04, 0xa2, 0x04, 0xc2, 0x04, 0xee, 0x05, 0x1a, 0x05, 0x72, 0x05, 0xa0,
+	0x05, 0xd4, 0x05, 0xfc, 0x06, 0x2a, 0x06, 0x4e, 0x06, 0x6c, 0x06, 0x9e,
+	0x06, 0xc4, 0x06, 0xe2, 0x07, 0x04, 0x07, 0x3c, 0x07, 0x56, 0x07, 0x86,
+	0x07, 0xb2, 0x07, 0xe2, 0x08, 0x06, 0x08, 0x44, 0x08, 0x74, 0x08, 0xa8,
+	0x08, 0xc4, 0x08, 0xee, 0x09, 0x1e, 0x09, 0x52, 0x09, 0x94, 0x09, 0xc2,
+	0x09, 0xf0, 0x0a, 0x0c, 0x0a, 0x30, 0x0a, 0x4c, 0x0a, 0x74, 0x0a, 0x86,
+	0x0a, 0x9e, 0x0a, 0xc8, 0x0a, 0xf0, 0x0b, 0x14, 0x0b, 0x3c, 0x0b, 0x66,
+	0x0b, 0x88, 0x0b, 0xb4, 0x0b, 0xd8, 0x0b, 0xf2, 0x0c, 0x12, 0x0c, 0x40,
+	0x0c, 0x58, 0x0c, 0x88, 0x0c, 0xa8, 0x0c, 0xd2, 0x0c, 0xf8, 0x0d, 0x20,
+	0x0d, 0x38, 0x0d, 0x64, 0x0d, 0x84, 0x0d, 0xa6, 0x0d, 0xd0, 0x0e, 0x00,
+	0x0e, 0x2e, 0x0e, 0x58, 0x0e, 0x82, 0x0e, 0xaa, 0x0e, 0xc0, 0x0e, 0xe6,
+	0x0f, 0x06, 0x0f, 0x06, 0x0f, 0x1e, 0x0f, 0x50, 0x0f, 0x88, 0x0f, 0xc4,
+	0x0f, 0xfe, 0x10, 0x18, 0x10, 0x66, 0x10, 0x7a, 0x10, 0xc0, 0x10, 0xe8,
+	0x11, 0x28, 0x11, 0x3c, 0x11, 0x86, 0x11, 0x9c, 0x11, 0xb8, 0x11, 0xe0,
+	0x12, 0x00, 0x12, 0x1a, 0x12, 0x30, 0x12, 0x52, 0x12, 0x7e, 0x12, 0x8e,
+	0x12, 0xa8, 0x12, 0xc0, 0x12, 0xe4, 0x13, 0x24, 0x13, 0x66, 0x13, 0xb4,
+	0x13, 0xfc, 0x14, 0x2a, 0x14, 0x62, 0x14, 0x9a, 0x14, 0xd8, 0x15, 0x1a,
+	0x15, 0x52, 0x15, 0x92, 0x15, 0xcc, 0x15, 0xfe, 0x16, 0x2e, 0x16, 0x5e,
+	0x16, 0x96, 0x16, 0xc6, 0x16, 0xf0, 0x17, 0x1a, 0x17, 0x4a, 0x17, 0x74,
+	0x17, 0xaa, 0x17, 0xec, 0x18, 0x28, 0x18, 0x64, 0x18, 0xa6, 0x18, 0xec,
+	0x19, 0x28, 0x19, 0x60, 0x19, 0x92, 0x19, 0xc6, 0x19, 0xfa, 0x1a, 0x36,
+	0x1a, 0x6c, 0x1a, 0xa6, 0x1a, 0xca, 0x1b, 0x02, 0x1b, 0x38, 0x1b, 0x6e,
+	0x1b, 0xac, 0x1b, 0xec, 0x1c, 0x22, 0x1c, 0x5e, 0x1c, 0xa2, 0x1c, 0xcc,
+	0x1d, 0x00, 0x1d, 0x36, 0x1d, 0x72, 0x1d, 0xa8, 0x1d, 0xc8, 0x1d, 0xe8,
+	0x1e, 0x0c, 0x1e, 0x2c, 0x1e, 0x64, 0x1e, 0x9a, 0x1e, 0xd0, 0x1f, 0x04,
+	0x1f, 0x40, 0x1f, 0x80, 0x1f, 0xb6, 0x1f, 0xd4, 0x1f, 0xf8, 0x20, 0x26,
+	0x20, 0x54, 0x20, 0x8a, 0x20, 0xb8, 0x20, 0xee, 0x21, 0x16, 0x21, 0x4e,
+	0x21, 0x86, 0x21, 0xba, 0x21, 0xf8, 0x22, 0x34, 0x22, 0x6c, 0x22, 0x9e,
+	0x22, 0xd2, 0x23, 0x00, 0x23, 0x3a, 0x23, 0x70, 0x23, 0x9e, 0x23, 0xc8,
+	0x24, 0x00, 0x24, 0x34, 0x24, 0x74, 0x24, 0xa6, 0x24, 0xdc, 0x25, 0x0a,
+	0x25, 0x3a, 0x25, 0x6e, 0x25, 0xa4, 0x25, 0xe0, 0x26, 0x0c, 0x26, 0x3e,
+	0x26, 0x6a, 0x26, 0x9c, 0x26, 0xd2, 0x27, 0x0e, 0x27, 0x52, 0x27, 0x94,
+	0x27, 0xd8, 0x28, 0x18, 0x28, 0x52, 0x28, 0x86, 0x28, 0xc0, 0x28, 0xf8,
+	0x29, 0x30, 0x29, 0x54, 0x29, 0x86, 0x29, 0xb0, 0x29, 0xe4, 0x2a, 0x0e,
+	0x2a, 0x34, 0x2a, 0x52, 0x2a, 0x80, 0x2a, 0xa4, 0x2a, 0xca, 0x2a, 0xf0,
+	0x2b, 0x14, 0x2b, 0x26, 0x2b, 0x60, 0x2b, 0x96, 0x2b, 0xca, 0x2b, 0xf6,
+	0x2c, 0x38, 0x2c, 0x6e, 0x2c, 0x9a, 0x2c, 0xc0, 0x2c, 0xde, 0x2d, 0x00,
+	0x2d, 0x1e, 0x2d, 0x40, 0x2d, 0x5c, 0x2d, 0x7c, 0x2d, 0x96, 0x2d, 0xbc,
+	0x2d, 0xd6, 0x2e, 0x0e, 0x2e, 0x38, 0x2e, 0x70, 0x2e, 0x96, 0x2e, 0xd2,
+	0x2f, 0x04, 0x2f, 0x2a, 0x2f, 0x5a, 0x2f, 0x7e, 0x2f, 0xba, 0x2f, 0xee,
+	0x30, 0x32, 0x30, 0x6e, 0x30, 0xb6, 0x30, 0xf6, 0x31, 0x30, 0x31, 0x78,
+	0x31, 0xb2, 0x31, 0xd4, 0x32, 0x0e, 0x32, 0x32, 0x32, 0x72, 0x32, 0x9a,
+	0x32, 0xda, 0x33, 0x12, 0x33, 0x58, 0x33, 0x98, 0x33, 0xd4, 0x34, 0x06,
+	0x34, 0x4a, 0x34, 0x88, 0x34, 0xaa, 0x34, 0xd0, 0x34, 0xfc, 0x35, 0x2c,
+	0x35, 0x4e, 0x35, 0x7c, 0x35, 0xbe, 0x35, 0xf8, 0x36, 0x2e, 0x36, 0x5c,
+	0x36, 0x9a, 0x36, 0xce, 0x37, 0x0c, 0x37, 0x48, 0x37, 0x8a, 0x37, 0xc4,
+	0x37, 0xfa, 0x38, 0x24, 0x38, 0x6a, 0x38, 0xac, 0x38, 0xea, 0x39, 0x2a,
+	0x39, 0x62, 0x39, 0xa0, 0x39, 0xd6, 0x3a, 0x0e, 0x3a, 0x3e, 0x3a, 0x82,
+	0x3a, 0xbe, 0x3a, 0xd2, 0x3a, 0xe4, 0x3b, 0x04, 0x3b, 0x24, 0x3b, 0x40,
+	0x3b, 0x58, 0x3b, 0x6e, 0x3b, 0x8e, 0x3b, 0xbe, 0x3b, 0xfe, 0x3c, 0x30,
+	0x3c, 0x52, 0x3c, 0x72, 0x3c, 0x94, 0x3c, 0xb4, 0x3c, 0xf2, 0x3d, 0x14,
+	0x3d, 0x36, 0x3d, 0x58, 0x3d, 0x7a, 0x3d, 0xb6, 0x3e, 0x2a, 0x3e, 0x8e,
+	0x3e, 0xc0, 0x3f, 0x28, 0x3f, 0xbc, 0x40, 0x1e, 0x40, 0x6e, 0x40, 0xd6,
+	0x41, 0x42, 0x41, 0x86, 0x42, 0x08, 0x42, 0x42, 0x42, 0x7c, 0x42, 0xc0,
+	0x43, 0x20, 0x43, 0x6c, 0x43, 0xe4, 0x44, 0x44, 0x44, 0xb4, 0x45, 0x10,
+	0x45, 0x6e, 0x45, 0xac, 0x46, 0x2a, 0x46, 0x86, 0x46, 0xde, 0x47, 0x16,
+	0x47, 0x6c, 0x47, 0x9c, 0x47, 0xc6, 0x47, 0xf2, 0x48, 0x16, 0x48, 0x44,
+	0x48, 0xc2, 0x49, 0x30, 0x49, 0x6a, 0x49, 0xb4, 0x4a, 0x1e, 0x4a, 0x7e,
+	0x4a, 0xde, 0x4b, 0x20, 0x4b, 0x82, 0x4b, 0xbc, 0x4b, 0xbc, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x01, 0xbc, 0x03, 0xdd, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x2e, 0xb1, 0x01, 0x00, 0x2f, 0x3c, 0xb2, 0x07, 0x04,
+	0x00, 0xed, 0x32, 0xb1, 0x06, 0x05, 0xdc, 0x3c, 0xb2, 0x03, 0x02, 0x00,
+	0xed, 0x32, 0x00, 0xb1, 0x03, 0x00, 0x2f, 0x3c, 0xb2, 0x05, 0x04, 0x00,
+	0xed, 0x32, 0xb2, 0x07, 0x06, 0x01, 0xfc, 0x3c, 0xb2, 0x01, 0x02, 0x00,
+	0xed, 0x32, 0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x31, 0x01,
+	0x8b, 0xfe, 0xa7, 0x01, 0x27, 0xfe, 0xd9, 0x03, 0xdd, 0xfc, 0x23, 0x31,
+	0x03, 0x7b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x31, 0x35, 0x33, 0x15,
+	0x27, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x00, 0x02, 0x00, 0x00, 0x02, 0x2c, 0x01, 0x17, 0x03, 0x44, 0x00, 0x07,
+	0x00, 0x0f, 0x00, 0x00, 0x13, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x3d,
+	0x02, 0x33, 0x1d, 0x02, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x02, 0x2d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x25,
+	0x00, 0x2b, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x23, 0x1d, 0x01, 0x23, 0x3d,
+	0x01, 0x23, 0x35, 0x33, 0x3d, 0x01, 0x23, 0x35, 0x33, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x23, 0x1d,
+	0x01, 0x33, 0x15, 0x23, 0x1d, 0x01, 0x03, 0x3d, 0x01, 0x23, 0x1d, 0x01,
+	0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0xff, 0xa3, 0x01, 0xd0, 0x03, 0x44, 0x00, 0x2d,
+	0x00, 0x33, 0x00, 0x39, 0x00, 0x00, 0x17, 0x35, 0x23, 0x35, 0x33, 0x3d,
+	0x02, 0x23, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x35, 0x33, 0x15,
+	0x33, 0x15, 0x33, 0x15, 0x23, 0x35, 0x23, 0x1d, 0x01, 0x33, 0x15, 0x33,
+	0x1d, 0x02, 0x23, 0x3d, 0x02, 0x23, 0x1d, 0x02, 0x33, 0x15, 0x23, 0x15,
+	0x27, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x13, 0x3d, 0x01, 0x23, 0x1d, 0x01,
+	0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5c, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x17, 0x00, 0x1d,
+	0x00, 0x25, 0x00, 0x29, 0x00, 0x00, 0x31, 0x35, 0x33, 0x15, 0x21, 0x23,
+	0x3d, 0x01, 0x3b, 0x01, 0x1d, 0x01, 0x25, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x25, 0x23,
+	0x3d, 0x01, 0x3b, 0x01, 0x1d, 0x01, 0x37, 0x35, 0x33, 0x15, 0x5d, 0x01,
+	0x16, 0x5d, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x02, 0x2d, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x09, 0x00, 0x11, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b,
+	0x00, 0x31, 0x00, 0x37, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x25, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x21, 0x2b, 0x01, 0x3d, 0x01, 0x3b, 0x01, 0x15,
+	0x05, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x25, 0x35, 0x33, 0x15, 0x21,
+	0x23, 0x35, 0x23, 0x35, 0x3b, 0x01, 0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01,
+	0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x37, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x01, 0xd0, 0x5d, 0xfe, 0x8d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0xfe, 0xe9, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0xba, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00,
+	0x11, 0x35, 0x33, 0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x5d, 0x5d, 0x02,
+	0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x00, 0x05, 0x00, 0x00,
+	0xff, 0xa3, 0x01, 0x17, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x13,
+	0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15, 0x27, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0x27, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x11,
+	0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x17,
+	0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0xff, 0xa3, 0x01, 0x17, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x13,
+	0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x15, 0x35, 0x33, 0x15, 0x3d, 0x02,
+	0x33, 0x1d, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x03, 0x23, 0x3d, 0x01,
+	0x33, 0x15, 0x27, 0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x73, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0xd0, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x13, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x23, 0x35, 0x23, 0x35, 0x33,
+	0x35, 0x33, 0x15, 0x33, 0x15, 0x23, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x5c, 0x01, 0xd0, 0x02, 0x2d, 0x00, 0x13, 0x00, 0x00, 0x37, 0x3d,
+	0x01, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3b,
+	0x01, 0x15, 0x2b, 0x01, 0x1d, 0x01, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0x46, 0x00, 0xba,
+	0x00, 0x5d, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x15, 0x35, 0x33, 0x15,
+	0x3d, 0x02, 0x33, 0x1d, 0x01, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x16, 0x01, 0x74,
+	0x01, 0x74, 0x00, 0x09, 0x00, 0x00, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35,
+	0x3b, 0x01, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x01,
+	0x73, 0x5d, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d,
+	0x00, 0x5d, 0x00, 0x03, 0x00, 0x00, 0x31, 0x35, 0x33, 0x15, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x17, 0x00, 0x00,
+	0x31, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d,
+	0x02, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5c, 0x5d, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x07, 0x00, 0x2b, 0x00, 0x33,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x03,
+	0x23, 0x15, 0x23, 0x15, 0x23, 0x1d, 0x01, 0x23, 0x3d, 0x05, 0x33, 0x1d,
+	0x02, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x1d, 0x05, 0x03, 0x2b,
+	0x02, 0x35, 0x3b, 0x02, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x2d, 0x5c,
+	0x00, 0x01, 0x00, 0x5c, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x17,
+	0x00, 0x00, 0x21, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x04, 0x23, 0x35, 0x33,
+	0x35, 0x33, 0x1d, 0x06, 0x33, 0x15, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x11, 0x00, 0x15,
+	0x00, 0x19, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x2b, 0x00, 0x00, 0x11, 0x35,
+	0x33, 0x15, 0x01, 0x33, 0x15, 0x2b, 0x04, 0x3d, 0x01, 0x33, 0x15, 0x3b,
+	0x01, 0x27, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33,
+	0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x27, 0x2b, 0x02, 0x35, 0x3b, 0x02,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x02, 0x2d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xba, 0x5c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1d,
+	0x00, 0x23, 0x00, 0x2b, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21,
+	0x23, 0x35, 0x33, 0x25, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x31, 0x3d, 0x01,
+	0x33, 0x1d, 0x01, 0x27, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x02, 0x2d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x19,
+	0x00, 0x21, 0x00, 0x00, 0x21, 0x3d, 0x02, 0x2b, 0x02, 0x3d, 0x01, 0x33,
+	0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x1d, 0x03, 0x33, 0x15, 0x23, 0x1d,
+	0x02, 0x03, 0x33, 0x3d, 0x01, 0x23, 0x15, 0x23, 0x15, 0x01, 0x16, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x07,
+	0x00, 0x0f, 0x00, 0x13, 0x00, 0x2b, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b,
+	0x02, 0x35, 0x3b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x35,
+	0x33, 0x25, 0x2b, 0x03, 0x3d, 0x03, 0x3b, 0x04, 0x15, 0x2b, 0x03, 0x1d,
+	0x01, 0x3b, 0x02, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x13, 0x00, 0x27, 0x00, 0x2f,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x03, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x3b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x05, 0x33,
+	0x1d, 0x01, 0x3b, 0x02, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x01, 0x2b, 0x02,
+	0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x2d, 0x5d, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x0b, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x00, 0x33, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01,
+	0x3d, 0x01, 0x2b, 0x03, 0x35, 0x3b, 0x04, 0x1d, 0x01, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xba, 0x5d, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x17,
+	0x00, 0x1f, 0x00, 0x25, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x00, 0x25, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21,
+	0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x25, 0x2b, 0x02, 0x35, 0x3b, 0x02,
+	0x31, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15,
+	0x25, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x07, 0x00, 0x1b, 0x00, 0x1f,
+	0x00, 0x27, 0x00, 0x2f, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x3b, 0x01, 0x3d, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x02, 0x33,
+	0x1d, 0x05, 0x21, 0x23, 0x35, 0x33, 0x35, 0x23, 0x3d, 0x02, 0x33, 0x1d,
+	0x01, 0x25, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x16, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x5d, 0x02, 0x2d, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x00, 0x3d, 0x01, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0xff, 0x46, 0x00, 0xba, 0x02, 0x2d, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x15, 0x35, 0x33, 0x15, 0x11, 0x35,
+	0x33, 0x15, 0x03, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xba, 0x5d, 0x5d, 0x02, 0x8a, 0x5d, 0x5d, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x15,
+	0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x27, 0x23,
+	0x35, 0x33, 0x2b, 0x01, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x01, 0x33, 0x15,
+	0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0xb9, 0x01, 0xd0, 0x02, 0x2d, 0x00, 0x0b, 0x00, 0x17, 0x00, 0x00,
+	0x01, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0x13, 0x33, 0x15, 0x2b,
+	0x04, 0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0x16, 0x5c, 0x5c, 0x01, 0x17, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x15, 0x00, 0x19, 0x00, 0x1d,
+	0x00, 0x00, 0x31, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01,
+	0x33, 0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x27, 0x23, 0x35, 0x33, 0x2b,
+	0x01, 0x35, 0x33, 0x2b, 0x01, 0x35, 0x33, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x11, 0x00, 0x17,
+	0x00, 0x1d, 0x00, 0x00, 0x33, 0x35, 0x33, 0x15, 0x27, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x03, 0x35, 0x33, 0x15, 0x17, 0x35, 0x33, 0x15, 0x3d, 0x02,
+	0x33, 0x1d, 0x01, 0x27, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x01, 0x73, 0x5d, 0x5d, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x00, 0x09, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x8a, 0x02, 0xe7, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d,
+	0x00, 0x27, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x45,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x2b, 0x01,
+	0x35, 0x33, 0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x3b,
+	0x01, 0x1d, 0x02, 0x33, 0x15, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02,
+	0x21, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x3d, 0x01, 0x23, 0x1d, 0x01,
+	0x01, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x25, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9,
+	0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xfe,
+	0x30, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0xfe, 0xe9, 0x5d, 0x01, 0x17, 0x5d,
+	0x5d, 0xfe, 0x30, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x17, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x23, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b,
+	0x02, 0x1d, 0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b, 0x02, 0x3d,
+	0x01, 0x33, 0x1d, 0x06, 0x03, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x73,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x0b,
+	0x00, 0x31, 0x00, 0x00, 0x25, 0x33, 0x3d, 0x02, 0x2b, 0x02, 0x1d, 0x02,
+	0x33, 0x17, 0x2b, 0x02, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x33, 0x1d, 0x01,
+	0x23, 0x3d, 0x01, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x02, 0x15, 0x33, 0x1d,
+	0x02, 0x23, 0x15, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x09, 0x00, 0x17, 0x00, 0x21,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x2b, 0x01,
+	0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b,
+	0x01, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0x2c, 0x5c, 0x5c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x11, 0x00, 0x2b, 0x00, 0x00, 0x37, 0x33, 0x35, 0x33,
+	0x3d, 0x03, 0x23, 0x35, 0x2b, 0x01, 0x1d, 0x05, 0x17, 0x2b, 0x01, 0x3d,
+	0x07, 0x3b, 0x02, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x03, 0x23, 0x15, 0x23,
+	0x15, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x21,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15,
+	0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x1b,
+	0x00, 0x00, 0x31, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01,
+	0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x03, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x15,
+	0x00, 0x23, 0x00, 0x2d, 0x00, 0x00, 0x21, 0x2b, 0x02, 0x35, 0x3b, 0x02,
+	0x3d, 0x02, 0x2b, 0x01, 0x35, 0x3b, 0x02, 0x1d, 0x04, 0x25, 0x23, 0x3d,
+	0x05, 0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01,
+	0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0x2c, 0x5c, 0x5c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x27, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d,
+	0x03, 0x23, 0x3d, 0x07, 0x33, 0x1d, 0x02, 0x3b, 0x02, 0x3d, 0x02, 0x33,
+	0x1d, 0x07, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x02, 0xe7, 0x00, 0x19, 0x00, 0x00, 0x33, 0x2b,
+	0x01, 0x35, 0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d,
+	0x05, 0x33, 0x15, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x05, 0x00, 0x15, 0x00, 0x1b,
+	0x00, 0x00, 0x37, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x06, 0x33,
+	0x1d, 0x06, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d,
+	0x00, 0x00, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d, 0x07, 0x33,
+	0x1d, 0x02, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x23, 0x35, 0x23, 0x35,
+	0x23, 0x1d, 0x03, 0x13, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d,
+	0x01, 0x33, 0x15, 0x01, 0x73, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x17, 0x00, 0x00, 0x25, 0x33,
+	0x15, 0x2b, 0x03, 0x3d, 0x07, 0x33, 0x1d, 0x06, 0x33, 0x01, 0x16, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x19,
+	0x00, 0x2d, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x23, 0x1d, 0x01, 0x23, 0x3d,
+	0x01, 0x33, 0x3d, 0x01, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x07, 0x21, 0x3d,
+	0x07, 0x33, 0x1d, 0x01, 0x33, 0x1d, 0x01, 0x23, 0x1d, 0x03, 0x01, 0x73,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x13,
+	0x00, 0x2b, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x23, 0x35, 0x33, 0x3d, 0x02,
+	0x33, 0x1d, 0x07, 0x21, 0x3d, 0x07, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15,
+	0x23, 0x35, 0x23, 0x1d, 0x05, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0xfe, 0x30,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x07, 0x00, 0x15, 0x00, 0x23, 0x00, 0x2b, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d,
+	0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0xd0, 0x5c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x23, 0x00, 0x00, 0x31, 0x3d, 0x07, 0x3b, 0x03, 0x15,
+	0x33, 0x1d, 0x01, 0x23, 0x3d, 0x01, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x02,
+	0x15, 0x2b, 0x02, 0x1d, 0x03, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x1b, 0x00, 0x27,
+	0x00, 0x2b, 0x00, 0x33, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x25, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x21, 0x23, 0x35, 0x33, 0x05, 0x23, 0x3d, 0x05,
+	0x33, 0x1d, 0x04, 0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x2b, 0x01, 0x35,
+	0x33, 0x13, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d, 0xfe, 0xea,
+	0x5c, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x01,
+	0x16, 0x5d, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x01, 0x74, 0x5c, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x09, 0x00, 0x2d, 0x00, 0x00,
+	0x21, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x21, 0x3d, 0x07, 0x3b, 0x03, 0x15,
+	0x33, 0x1d, 0x01, 0x23, 0x3d, 0x01, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x02,
+	0x15, 0x2b, 0x02, 0x1d, 0x03, 0x01, 0x73, 0x5d, 0xfe, 0x30, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x09, 0x00, 0x11, 0x00, 0x19,
+	0x00, 0x1f, 0x00, 0x29, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x35,
+	0x3b, 0x02, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x03, 0x2b, 0x02, 0x35, 0x3b,
+	0x02, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x25, 0x33, 0x15, 0x2b, 0x03,
+	0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x19, 0x00, 0x00, 0x33, 0x3d, 0x06, 0x2b, 0x01, 0x35,
+	0x3b, 0x04, 0x15, 0x2b, 0x01, 0x1d, 0x06, 0xba, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x07,
+	0x00, 0x17, 0x00, 0x27, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x06, 0x33,
+	0x1d, 0x05, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x0d, 0x00, 0x15, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x00, 0x33, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x2b, 0x01, 0x3d,
+	0x02, 0x33, 0x1d, 0x01, 0x37, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23,
+	0x3d, 0x02, 0x33, 0x1d, 0x01, 0xba, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0xb9,
+	0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x0b, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x00, 0x21, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x37, 0x3d,
+	0x05, 0x33, 0x1d, 0x05, 0x2b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x07, 0x23,
+	0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0xb9,
+	0x5d, 0xba, 0x5c, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x09, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x13,
+	0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x29, 0x00, 0x2f,
+	0x00, 0x00, 0x21, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x3d, 0x02, 0x33,
+	0x1d, 0x02, 0x01, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x37, 0x23,
+	0x35, 0x33, 0x31, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x01, 0x73,
+	0x5d, 0xfe, 0x30, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x09, 0x00, 0x0f, 0x00, 0x15,
+	0x00, 0x1b, 0x00, 0x21, 0x00, 0x00, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x11, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x2b, 0x01, 0x3d, 0x01, 0x33, 0x15,
+	0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15,
+	0xba, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x01, 0x33, 0x15, 0x33, 0x27, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x01, 0x2b,
+	0x02, 0x35, 0x3b, 0x03, 0x1d, 0x01, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0xff, 0xa3, 0x00, 0xba, 0x03, 0x44, 0x00, 0x19,
+	0x00, 0x00, 0x17, 0x23, 0x3d, 0x09, 0x3b, 0x01, 0x15, 0x23, 0x1d, 0x07,
+	0x33, 0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x11,
+	0x00, 0x17, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x27, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0x27, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x27, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x01, 0x00, 0x00,
+	0xff, 0xa3, 0x00, 0xba, 0x03, 0x44, 0x00, 0x19, 0x00, 0x00, 0x17, 0x23,
+	0x35, 0x33, 0x3d, 0x07, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x09, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xd0, 0x01, 0xd0,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x17,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x25, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0x05, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x37, 0x23,
+	0x35, 0x33, 0x01, 0x73, 0x5d, 0xfe, 0x30, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5c, 0x5c, 0x5c, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0xff, 0xa3, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x0b,
+	0x00, 0x00, 0x21, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0x01, 0x73,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x02, 0x2c, 0x01, 0x17, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x27, 0x23,
+	0x35, 0x33, 0x2b, 0x01, 0x35, 0x33, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x11,
+	0x00, 0x19, 0x00, 0x1f, 0x00, 0x00, 0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d,
+	0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d,
+	0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01,
+	0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x0b,
+	0x00, 0x23, 0x00, 0x00, 0x37, 0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03,
+	0x17, 0x2b, 0x01, 0x3d, 0x07, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x33,
+	0x1d, 0x03, 0x23, 0x15, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x07, 0x00, 0x11, 0x00, 0x19,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d,
+	0x03, 0x33, 0x1d, 0x02, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x01,
+	0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0x73, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00,
+	0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x03, 0x33, 0x35, 0x3b, 0x01, 0x3d,
+	0x01, 0x33, 0x1d, 0x07, 0x27, 0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03,
+	0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0x2d, 0x00, 0x07, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x00, 0x25, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33, 0x15, 0x3b,
+	0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01,
+	0x16, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x13, 0x00, 0x19, 0x00, 0x00, 0x33, 0x3d, 0x03, 0x23,
+	0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x23, 0x1d, 0x03,
+	0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0xe6, 0x5c, 0x5c, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x05,
+	0x00, 0x1b, 0x00, 0x27, 0x00, 0x00, 0x17, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x35, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x03, 0x33, 0x35, 0x3b, 0x02,
+	0x1d, 0x06, 0x27, 0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0xba, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x0b, 0x00, 0x21, 0x00, 0x00, 0x21, 0x3d, 0x04, 0x33,
+	0x1d, 0x04, 0x21, 0x3d, 0x07, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b,
+	0x01, 0x1d, 0x04, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba,
+	0x02, 0xe7, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x33, 0x3d, 0x04, 0x23,
+	0x35, 0x3b, 0x01, 0x1d, 0x05, 0x03, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0xff, 0x46, 0x00, 0xba, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19,
+	0x00, 0x00, 0x15, 0x35, 0x33, 0x15, 0x11, 0x35, 0x33, 0x15, 0x03, 0x3d,
+	0x05, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x06, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5d, 0x5d, 0x03, 0x44, 0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x21, 0x3d, 0x07, 0x33, 0x1d, 0x03, 0x33, 0x15, 0x33, 0x15,
+	0x23, 0x35, 0x23, 0x1d, 0x02, 0x13, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33,
+	0x15, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xba, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x13, 0x00, 0x00,
+	0x33, 0x35, 0x33, 0x15, 0x27, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0x2d, 0x00, 0x0b, 0x00, 0x17, 0x00, 0x27,
+	0x00, 0x2b, 0x00, 0x00, 0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x21, 0x3d,
+	0x04, 0x33, 0x1d, 0x04, 0x21, 0x3d, 0x05, 0x3b, 0x01, 0x15, 0x23, 0x1d,
+	0x04, 0x01, 0x23, 0x35, 0x33, 0x01, 0x73, 0x5d, 0xfe, 0xea, 0x5c, 0xfe,
+	0xea, 0x5d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x0b, 0x00, 0x1d, 0x00, 0x00,
+	0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x21, 0x3d, 0x05, 0x3b, 0x02, 0x15,
+	0x2b, 0x01, 0x1d, 0x04, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x05,
+	0x00, 0x0f, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x00, 0x37, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03,
+	0x33, 0x1d, 0x02, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0xba, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x23,
+	0x00, 0x00, 0x15, 0x3d, 0x07, 0x3b, 0x02, 0x15, 0x33, 0x1d, 0x03, 0x23,
+	0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d,
+	0x01, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x02, 0x2d, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x05, 0x3d, 0x01, 0x2b,
+	0x01, 0x35, 0x23, 0x3d, 0x03, 0x33, 0x35, 0x3b, 0x02, 0x1d, 0x07, 0x03,
+	0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0x01, 0x16, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x01, 0x17, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x02, 0x2d, 0x00, 0x0b,
+	0x00, 0x11, 0x00, 0x00, 0x31, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x13, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x07,
+	0x00, 0x0d, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x00, 0x37, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x27,
+	0x2b, 0x01, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x35, 0x33, 0x37, 0x33, 0x15,
+	0x2b, 0x02, 0x35, 0x33, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x33, 0x35, 0x33, 0x15,
+	0x27, 0x23, 0x3d, 0x03, 0x23, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x33, 0x15, 0x23, 0x1d, 0x02, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x00,
+	0x21, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25,
+	0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0x2d, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x11, 0x00, 0x17, 0x00, 0x1d,
+	0x00, 0x00, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d,
+	0x01, 0x2b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x37, 0x3d, 0x01, 0x33, 0x1d,
+	0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0x5d, 0xb9, 0x5d,
+	0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0x2d, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1b, 0x00, 0x25,
+	0x00, 0x00, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x37, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x2b, 0x01, 0x3d, 0x01,
+	0x33, 0x15, 0x07, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x01, 0x16, 0x5d,
+	0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0xba, 0x5c, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0x2d, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1b, 0x00, 0x21,
+	0x00, 0x00, 0x21, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x3d, 0x02, 0x33,
+	0x1d, 0x02, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x31, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x01, 0x16, 0x5d, 0xfe,
+	0x8d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x05,
+	0x00, 0x19, 0x00, 0x25, 0x00, 0x00, 0x17, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x06,
+	0x25, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0xba, 0x5c, 0x5c, 0x5d, 0xb9,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x0b,
+	0x00, 0x0f, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b,
+	0x03, 0x3d, 0x01, 0x33, 0x15, 0x33, 0x27, 0x35, 0x33, 0x15, 0x3d, 0x01,
+	0x33, 0x15, 0x3d, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x03, 0x1d, 0x01, 0x01,
+	0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x0b, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x00, 0x33, 0x35,
+	0x33, 0x15, 0x27, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x27, 0x23, 0x35,
+	0x33, 0x31, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x11, 0x35, 0x33, 0x15, 0xba,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0xff, 0xa3, 0x00, 0x5d, 0x03, 0x44, 0x00, 0x15, 0x00, 0x00, 0x15, 0x3d,
+	0x09, 0x33, 0x1d, 0x09, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1b,
+	0x00, 0x00, 0x31, 0x35, 0x33, 0x15, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x11,
+	0x35, 0x33, 0x15, 0x27, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x27, 0x23,
+	0x35, 0x33, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x00, 0x04, 0x00, 0x00,
+	0x01, 0x16, 0x02, 0x2d, 0x01, 0xd0, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0d,
+	0x00, 0x13, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x25, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0xfe, 0xe9, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x02, 0xe7, 0x00, 0x0d,
+	0x00, 0x11, 0x00, 0x00, 0x31, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x03, 0x35,
+	0x33, 0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x1d,
+	0x00, 0x21, 0x00, 0x2b, 0x00, 0x00, 0x33, 0x35, 0x23, 0x35, 0x23, 0x3d,
+	0x03, 0x33, 0x35, 0x33, 0x35, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x23,
+	0x35, 0x23, 0x1d, 0x03, 0x33, 0x15, 0x23, 0x15, 0x37, 0x35, 0x33, 0x15,
+	0x21, 0x3d, 0x03, 0x23, 0x1d, 0x03, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x11, 0x00, 0x23, 0x00, 0x29, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x03, 0x33, 0x15, 0x2b, 0x04, 0x3d, 0x01, 0x33,
+	0x15, 0x3b, 0x01, 0x27, 0x3d, 0x01, 0x23, 0x35, 0x33, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0xb9, 0x5d, 0x5c, 0x5c, 0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+	0x00, 0xb9, 0x02, 0x2d, 0x02, 0x2d, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d,
+	0x00, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00,
+	0x25, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x05, 0x23, 0x3d, 0x01,
+	0x33, 0x15, 0x25, 0x35, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x05,
+	0x23, 0x35, 0x33, 0x01, 0xd0, 0x5d, 0xfd, 0xd3, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5c, 0x01, 0x16, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x01, 0x16, 0x5d,
+	0xba, 0x5d, 0x5c, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xba, 0x5c, 0x5c,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2d,
+	0x00, 0x00, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x2b, 0x01,
+	0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x33, 0x3d, 0x01, 0x33, 0x1d,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x15, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x15,
+	0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15,
+	0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0xa3, 0x00, 0x5d,
+	0x02, 0xe7, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x15, 0x3d, 0x03, 0x33,
+	0x1d, 0x03, 0x03, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f,
+	0x00, 0x15, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2f,
+	0x00, 0x37, 0x00, 0x00, 0x37, 0x35, 0x33, 0x15, 0x13, 0x35, 0x33, 0x15,
+	0x03, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d,
+	0x01, 0x21, 0x23, 0x35, 0x33, 0x25, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x07,
+	0x23, 0x3d, 0x01, 0x33, 0x15, 0x37, 0x23, 0x35, 0x33, 0x07, 0x23, 0x3d,
+	0x01, 0x33, 0x15, 0x25, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0xba, 0x5c, 0x5d,
+	0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0xb9,
+	0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xba, 0x5c,
+	0x5c, 0x01, 0x73, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x02, 0x89, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x33,
+	0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0xe6, 0x5c, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a, 0x02, 0xe7, 0x00, 0x0b,
+	0x00, 0x19, 0x00, 0x27, 0x00, 0x39, 0x00, 0x45, 0x00, 0x00, 0x25, 0x33,
+	0x15, 0x2b, 0x04, 0x35, 0x3b, 0x03, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x21,
+	0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x3d,
+	0x03, 0x3b, 0x02, 0x15, 0x2b, 0x01, 0x1d, 0x01, 0x01, 0x2b, 0x04, 0x35,
+	0x3b, 0x04, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xba, 0x5d, 0xfd, 0xd3, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x17, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x74, 0x5c, 0x00, 0x03, 0x00, 0x00,
+	0x01, 0x73, 0x01, 0x74, 0x03, 0x44, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1b,
+	0x00, 0x00, 0x01, 0x2b, 0x01, 0x35, 0x23, 0x35, 0x33, 0x35, 0x3b, 0x01,
+	0x35, 0x33, 0x1d, 0x03, 0x27, 0x33, 0x35, 0x2b, 0x01, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x01, 0x73,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01,
+	0x16, 0x5d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xb9, 0x02, 0x2d,
+	0x02, 0x8a, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13,
+	0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00,
+	0x25, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x23, 0x35, 0x33,
+	0x05, 0x23, 0x35, 0x3b, 0x01, 0x23, 0x35, 0x33, 0x05, 0x23, 0x35, 0x33,
+	0x21, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x25, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x01, 0xd0, 0x5d, 0xfe, 0x8d, 0x5c, 0xba, 0x5d,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5c,
+	0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x01, 0x16, 0x01, 0xd0, 0x01, 0xd0, 0x00, 0x0d,
+	0x00, 0x00, 0x01, 0x35, 0x2b, 0x03, 0x35, 0x3b, 0x04, 0x1d, 0x01, 0x01,
+	0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x16,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x3b,
+	0x00, 0x47, 0x00, 0x00, 0x25, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d,
+	0x03, 0x3b, 0x02, 0x15, 0x23, 0x15, 0x23, 0x1d, 0x01, 0x17, 0x33, 0x15,
+	0x2b, 0x04, 0x35, 0x3b, 0x03, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x21, 0x23,
+	0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b, 0x04, 0x35, 0x3b, 0x04, 0x01,
+	0x73, 0x5d, 0xfe, 0xea, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xba, 0x5d, 0xfd, 0xd3, 0x5d,
+	0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x01, 0x73, 0x02, 0x8a, 0x01, 0xd0, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x33,
+	0x15, 0x2b, 0x06, 0x35, 0x3b, 0x04, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0xd0, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x2c, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x00,
+	0x13, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x3b,
+	0x01, 0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0x8a, 0x00, 0x13,
+	0x00, 0x1f, 0x00, 0x00, 0x37, 0x3d, 0x01, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x01,
+	0x17, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x01, 0xd0, 0x01, 0x17, 0x03, 0x44, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x13,
+	0x00, 0x00, 0x13, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x33, 0x15,
+	0x27, 0x35, 0x33, 0x15, 0x27, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x01,
+	0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x01, 0xd0, 0x01, 0x17, 0x03, 0x44, 0x00, 0x11,
+	0x00, 0x00, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x23, 0x35, 0x23,
+	0x35, 0x3b, 0x02, 0x1d, 0x03, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x2c, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x11, 0x35,
+	0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d,
+	0x5c, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x21,
+	0x00, 0x00, 0x15, 0x3d, 0x07, 0x33, 0x1d, 0x04, 0x3b, 0x01, 0x3d, 0x04,
+	0x33, 0x1d, 0x05, 0x2b, 0x02, 0x1d, 0x01, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x1f, 0x00, 0x27, 0x00, 0x00,
+	0x21, 0x3d, 0x02, 0x23, 0x1d, 0x02, 0x23, 0x3d, 0x02, 0x23, 0x35, 0x23,
+	0x3d, 0x02, 0x33, 0x35, 0x3b, 0x03, 0x1d, 0x07, 0x03, 0x3d, 0x02, 0x23,
+	0x1d, 0x02, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x01, 0x16, 0x00, 0xba, 0x01, 0xd0, 0x00, 0x07,
+	0x00, 0x00, 0x13, 0x23, 0x3d, 0x01, 0x3b, 0x01, 0x1d, 0x01, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x03, 0x00, 0x00,
+	0x01, 0x16, 0x01, 0x17, 0x02, 0x2d, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d,
+	0x00, 0x00, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15,
+	0x27, 0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x01, 0xd0, 0x01, 0x17, 0x03, 0x44, 0x00, 0x0f,
+	0x00, 0x00, 0x13, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x23, 0x35, 0x33, 0x35,
+	0x33, 0x1d, 0x02, 0x33, 0x15, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0xd0, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0x01, 0xd0, 0x01, 0x74, 0x03, 0x44, 0x00, 0x05,
+	0x00, 0x0b, 0x00, 0x11, 0x00, 0x17, 0x00, 0x00, 0x13, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01,
+	0x33, 0x15, 0x37, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0xba, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x02,
+	0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d,
+	0x00, 0x0a, 0x00, 0x00, 0x00, 0xb9, 0x02, 0x2d, 0x02, 0x8a, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b,
+	0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x25, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x25, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x25, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x23, 0x35, 0x33,
+	0x05, 0x23, 0x35, 0x3b, 0x01, 0x23, 0x35, 0x33, 0x05, 0x23, 0x35, 0x33,
+	0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d,
+	0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5c, 0xba, 0x5d, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0xb9, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0xe7, 0x02, 0xe7, 0x00, 0x0d, 0x00, 0x11, 0x00, 0x1d,
+	0x00, 0x23, 0x00, 0x29, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x21, 0x35,
+	0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x23, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x03, 0x3d, 0x01, 0x23, 0x35, 0x33, 0x35, 0x33,
+	0x1d, 0x03, 0x11, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d,
+	0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x02, 0x2d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x00, 0x00, 0x09, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0xe7, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x19,
+	0x00, 0x1f, 0x00, 0x23, 0x00, 0x29, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x39,
+	0x00, 0x00, 0x33, 0x35, 0x33, 0x15, 0x03, 0x3d, 0x01, 0x23, 0x35, 0x33,
+	0x35, 0x33, 0x1d, 0x03, 0x01, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15,
+	0x33, 0x15, 0x25, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x25, 0x35, 0x33, 0x15,
+	0x27, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x05, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xfd, 0xd4,
+	0x5c, 0x01, 0x74, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x8c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0x44, 0x02, 0xe7, 0x00, 0x0d, 0x00, 0x11, 0x00, 0x17,
+	0x00, 0x1d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x39, 0x00, 0x00, 0x21, 0x35,
+	0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x23, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x02, 0x33,
+	0x1d, 0x01, 0x25, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x23, 0x35, 0x23,
+	0x35, 0x3b, 0x02, 0x1d, 0x03, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3d,
+	0x01, 0x33, 0x15, 0x02, 0x8a, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfd, 0xd4,
+	0x5c, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0f,
+	0x00, 0x15, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x03, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x02,
+	0x33, 0x1d, 0x01, 0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00,
+	0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d, 0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d,
+	0x01, 0x3b, 0x02, 0x3d, 0x01, 0x33, 0x1d, 0x06, 0x01, 0x35, 0x33, 0x15,
+	0x17, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x27, 0x23, 0x35, 0x33, 0x01, 0x73,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfe, 0xea, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x03, 0x43, 0x5d, 0x5d, 0xb9, 0x5c, 0xba, 0x5d, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d, 0x03, 0x23,
+	0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b, 0x02, 0x3d, 0x01, 0x33, 0x1d, 0x06,
+	0x01, 0x35, 0x33, 0x15, 0x17, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x27, 0x35,
+	0x33, 0x15, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xfe, 0xea, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xb9, 0x5c, 0xba, 0x5d, 0x5d,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x23,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x21, 0x3d,
+	0x03, 0x2b, 0x02, 0x1d, 0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b,
+	0x02, 0x3d, 0x01, 0x33, 0x1d, 0x06, 0x03, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x17, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x27, 0x23, 0x35, 0x33,
+	0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba,
+	0x5d, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c,
+	0xba, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x37,
+	0x00, 0x3b, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d, 0x03, 0x23,
+	0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b, 0x02, 0x3d, 0x01, 0x33, 0x1d, 0x06,
+	0x03, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x17, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x01, 0x73,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba, 0x5d, 0xfe,
+	0xea, 0x5d, 0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0xba,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xa0, 0x00, 0x23, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x00,
+	0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d, 0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d,
+	0x01, 0x3b, 0x02, 0x3d, 0x01, 0x33, 0x1d, 0x06, 0x03, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x17, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x73,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba, 0x5d, 0xfe,
+	0xea, 0x5d, 0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03,
+	0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x31,
+	0x00, 0x35, 0x00, 0x39, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d,
+	0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b, 0x02, 0x3d, 0x01, 0x33,
+	0x1d, 0x06, 0x03, 0x2b, 0x02, 0x35, 0x33, 0x35, 0x33, 0x15, 0x33, 0x27,
+	0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x23, 0x35, 0x33, 0x01,
+	0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a,
+	0x02, 0xe7, 0x00, 0x07, 0x00, 0x2d, 0x00, 0x33, 0x00, 0x00, 0x31, 0x3d,
+	0x02, 0x33, 0x1d, 0x02, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x02, 0x2b,
+	0x01, 0x3d, 0x02, 0x33, 0x3d, 0x01, 0x3b, 0x04, 0x15, 0x2b, 0x02, 0x1d,
+	0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x03, 0x3d, 0x01,
+	0x23, 0x1d, 0x01, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x11, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x00, 0x17, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x03, 0x15, 0x23, 0x15,
+	0x25, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03,
+	0x35, 0x3b, 0x01, 0xba, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02,
+	0x2c, 0x5c, 0x5c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x25, 0x00, 0x29, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15,
+	0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33,
+	0x11, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03,
+	0x43, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x25, 0x00, 0x29, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15,
+	0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33,
+	0x11, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03,
+	0x43, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x29, 0x00, 0x2f, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b,
+	0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01,
+	0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd,
+	0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43, 0x5d, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0xa0, 0x00, 0x03, 0x00, 0x07, 0x00, 0x29,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01,
+	0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x01, 0x16, 0x5d, 0xfe,
+	0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x21,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x2b, 0x01, 0x35, 0x33, 0x3d,
+	0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d, 0x05, 0x33, 0x15, 0x03,
+	0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0xb9, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x03, 0xa0, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x21,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x2b, 0x01, 0x35, 0x33, 0x3d,
+	0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d, 0x05, 0x33, 0x15, 0x03,
+	0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x03, 0xa0, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x21,
+	0x00, 0x25, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02, 0x15,
+	0x23, 0x1d, 0x05, 0x33, 0x15, 0x03, 0x23, 0x35, 0x33, 0xba, 0x5c, 0xfe,
+	0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x03, 0xa0, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x03, 0xa0, 0x00, 0x03, 0x00, 0x07, 0x00, 0x21,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d,
+	0x05, 0x33, 0x15, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xfc, 0xbd, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x2d, 0x02, 0xe7, 0x00, 0x13, 0x00, 0x2f, 0x00, 0x00,
+	0x25, 0x33, 0x35, 0x33, 0x3d, 0x03, 0x23, 0x35, 0x2b, 0x01, 0x1d, 0x01,
+	0x33, 0x15, 0x23, 0x1d, 0x02, 0x17, 0x2b, 0x01, 0x3d, 0x03, 0x23, 0x35,
+	0x33, 0x3d, 0x02, 0x3b, 0x02, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x03, 0x23,
+	0x15, 0x23, 0x15, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x13, 0x00, 0x2b, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x23,
+	0x35, 0x33, 0x3d, 0x02, 0x33, 0x1d, 0x07, 0x21, 0x3d, 0x07, 0x33, 0x15,
+	0x33, 0x15, 0x33, 0x15, 0x23, 0x35, 0x23, 0x1d, 0x05, 0x13, 0x35, 0x33,
+	0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0xba, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x19,
+	0x00, 0x27, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x11, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d,
+	0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x27, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0xd0, 0x5c, 0xba, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x19,
+	0x00, 0x27, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x11, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d,
+	0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x27, 0x35, 0x33, 0x15, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x01, 0xd0, 0x5c, 0xba, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f,
+	0x00, 0x1d, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x01, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x21, 0x23, 0x3d, 0x05, 0x33,
+	0x1d, 0x04, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x27, 0x23, 0x35, 0x33,
+	0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd,
+	0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0xba, 0x5d, 0x00, 0x00, 0x08, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f,
+	0x00, 0x1d, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b,
+	0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x21, 0x23, 0x3d,
+	0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x01,
+	0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba, 0x5c, 0x5c,
+	0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c,
+	0xba, 0x5d, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xa0, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x2b,
+	0x00, 0x33, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d,
+	0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x1a,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0xd0, 0x5c, 0x00, 0x09, 0x00, 0x00, 0x00, 0xb9, 0x01, 0xd0,
+	0x02, 0x8a, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13,
+	0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x00, 0x25, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x25, 0x23, 0x35, 0x33, 0x05, 0x35,
+	0x33, 0x15, 0x37, 0x23, 0x35, 0x33, 0x31, 0x35, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01, 0x73,
+	0x5d, 0xfe, 0x30, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x15, 0x00, 0x31, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x06, 0x33, 0x1d, 0x03, 0x33, 0x1d,
+	0x01, 0x33, 0x3d, 0x03, 0x23, 0x1d, 0x01, 0x23, 0x3d, 0x01, 0x33, 0x3d,
+	0x01, 0x2b, 0x01, 0x35, 0x3b, 0x03, 0x1d, 0x06, 0x01, 0x16, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x0b, 0x00, 0x1b, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x06,
+	0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x13, 0x23,
+	0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0xe6, 0x5d, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x1b,
+	0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21,
+	0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x13, 0x35, 0x33, 0x15, 0xba, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0xb9,
+	0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0xe6, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d,
+	0x06, 0x21, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x13, 0x23, 0x35, 0x33,
+	0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x03, 0x43, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0xe6,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xa0, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2f,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21,
+	0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1f,
+	0x00, 0x25, 0x00, 0x29, 0x00, 0x00, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x03, 0x35, 0x33, 0x15, 0x11, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x2b, 0x01,
+	0x3d, 0x01, 0x33, 0x15, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0x13, 0x35, 0x33, 0x15, 0xba, 0x5c, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x01, 0xd0, 0x02, 0x8a, 0x00, 0x1f,
+	0x00, 0x00, 0x3d, 0x06, 0x33, 0x15, 0x3b, 0x02, 0x15, 0x33, 0x1d, 0x01,
+	0x23, 0x3d, 0x01, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x02, 0x15, 0x2b, 0x02,
+	0x15, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x0f,
+	0x00, 0x15, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x29, 0x00, 0x31, 0x00, 0x00,
+	0x31, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x37, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x03, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x31, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x27, 0x2b, 0x02, 0x35, 0x3b, 0x02,
+	0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x15, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x27,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x2b, 0x01, 0x35, 0x23, 0x3d,
+	0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d,
+	0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x27,
+	0x23, 0x35, 0x33, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d,
+	0xb9, 0x5d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x15, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x27,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x23, 0x3d,
+	0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d,
+	0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x27,
+	0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d,
+	0xb9, 0x5d, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19, 0x00, 0x21, 0x00, 0x27,
+	0x00, 0x2d, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x35,
+	0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d, 0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13,
+	0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01,
+	0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x01, 0x73, 0x5d, 0xb9, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19,
+	0x00, 0x21, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x23, 0x3d,
+	0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d,
+	0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0xba, 0x5c, 0xfe, 0xea, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x02, 0x8a, 0x5c,
+	0x5c, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0xb9,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19, 0x00, 0x21, 0x00, 0x27,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d,
+	0x04, 0x27, 0x33, 0x3d, 0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x01, 0x73, 0x5d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x11, 0x00, 0x19, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x01, 0x33,
+	0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d, 0x01, 0x2b,
+	0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x3d,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x23, 0x35, 0x33, 0x01,
+	0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a,
+	0x02, 0x2d, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x29, 0x00, 0x2f, 0x00, 0x35,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x21, 0x33, 0x15,
+	0x2b, 0x01, 0x35, 0x21, 0x23, 0x3d, 0x01, 0x2b, 0x01, 0x1d, 0x01, 0x23,
+	0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x3b, 0x01, 0x35,
+	0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xfe, 0xea, 0x5c, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xfe, 0xe9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x00, 0x04, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17,
+	0x00, 0x1f, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x23, 0x35,
+	0x3b, 0x02, 0x15, 0x23, 0x15, 0x27, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02,
+	0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x01, 0x73, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x1d,
+	0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33, 0x15, 0x3b,
+	0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x27, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01,
+	0x16, 0x5d, 0xb9, 0x5d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x27,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33, 0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d,
+	0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x27, 0x35,
+	0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x21, 0x00, 0x27,
+	0x00, 0x2d, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33,
+	0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16,
+	0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0xd3,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01,
+	0x16, 0x5d, 0xb9, 0x5d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x21, 0x00, 0x27,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33, 0x15, 0x3b,
+	0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0xd3,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01,
+	0x16, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba,
+	0x03, 0x44, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x00, 0x33, 0x3d,
+	0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x05, 0x03, 0x35, 0x33, 0x15, 0x27,
+	0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x03, 0x44, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17,
+	0x00, 0x00, 0x33, 0x3d, 0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x05, 0x03,
+	0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x03, 0x44, 0x00, 0x0f,
+	0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x00, 0x33, 0x3d, 0x04, 0x23,
+	0x35, 0x3b, 0x01, 0x1d, 0x05, 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33,
+	0x15, 0x37, 0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xfe, 0xea,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x02, 0xe7, 0x00, 0x0f,
+	0x00, 0x13, 0x00, 0x17, 0x00, 0x00, 0x33, 0x3d, 0x04, 0x23, 0x35, 0x3b,
+	0x01, 0x1d, 0x05, 0x11, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0xa0, 0x00, 0x05, 0x00, 0x21, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00,
+	0x37, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d,
+	0x03, 0x23, 0x3d, 0x03, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x05,
+	0x03, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x3b, 0x01, 0x15, 0x23, 0x27, 0x23,
+	0x35, 0x33, 0xba, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02,
+	0x2d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x0b, 0x00, 0x1d, 0x00, 0x21,
+	0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x00, 0x21, 0x3d, 0x04, 0x33,
+	0x1d, 0x04, 0x21, 0x3d, 0x05, 0x3b, 0x02, 0x15, 0x2b, 0x01, 0x1d, 0x04,
+	0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x35, 0x33, 0x15,
+	0x2b, 0x01, 0x35, 0x33, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x13,
+	0x00, 0x1d, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x03, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x2b, 0x01, 0x35, 0x3b,
+	0x01, 0x27, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x01, 0x16, 0x5d, 0xb9, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x13,
+	0x00, 0x1d, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x11, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x2b, 0x01, 0x35, 0x3b,
+	0x01, 0x27, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x02,
+	0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16,
+	0x5d, 0xb9, 0x5d, 0x5d, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x2d, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x03, 0x33,
+	0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a,
+	0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01,
+	0x16, 0x5d, 0xb9, 0x5d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d,
+	0x03, 0x33, 0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13,
+	0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x21, 0x00, 0x27, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03,
+	0x33, 0x1d, 0x02, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c,
+	0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0xb9, 0x01, 0xd0, 0x02, 0x8a, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x13, 0x00, 0x00, 0x37, 0x35, 0x33, 0x15, 0x03, 0x35,
+	0x33, 0x15, 0x17, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0xba, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0xba, 0x5c, 0x5c, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x0f,
+	0x00, 0x1f, 0x00, 0x00, 0x33, 0x2b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x01,
+	0x33, 0x1d, 0x01, 0x33, 0x15, 0x3d, 0x02, 0x23, 0x3d, 0x01, 0x23, 0x35,
+	0x3b, 0x02, 0x1d, 0x04, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x15, 0x00, 0x21, 0x00, 0x25, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x11, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25,
+	0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x13, 0x23, 0x35, 0x33, 0xba, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x15, 0x00, 0x21,
+	0x00, 0x25, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d, 0x04, 0x33,
+	0x1d, 0x03, 0x13, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c,
+	0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0x2c, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19, 0x00, 0x25, 0x00, 0x2b,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d,
+	0x04, 0x33, 0x1d, 0x03, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16,
+	0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea,
+	0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c,
+	0x5c, 0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x02, 0x2c, 0x5d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19, 0x00, 0x25, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d, 0x04, 0x33,
+	0x1d, 0x03, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c,
+	0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x00, 0x05, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x09, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x06, 0x25, 0x23, 0x3d, 0x04,
+	0x33, 0x1d, 0x03, 0x13, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5d, 0x5d,
+	0x00, 0x01, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x27,
+	0x00, 0x00, 0x15, 0x3d, 0x09, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x33,
+	0x1d, 0x03, 0x23, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0x3b, 0x01, 0x15,
+	0x2b, 0x01, 0x1d, 0x01, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d,
+	0x00, 0x21, 0x00, 0x2d, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x06, 0x25, 0x23, 0x3d, 0x04,
+	0x33, 0x1d, 0x03, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x02,
+	0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xa0, 0x00, 0x23,
+	0x00, 0x2b, 0x00, 0x37, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b, 0x02, 0x1d,
+	0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b, 0x02, 0x3d, 0x01, 0x33,
+	0x1d, 0x06, 0x03, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x35, 0x33, 0x15, 0x2b,
+	0x04, 0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c,
+	0xba, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x11, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x00,
+	0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x35,
+	0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d, 0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13,
+	0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b,
+	0x01, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0xb9, 0x5c, 0x5c, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x23,
+	0x00, 0x27, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x21, 0x3d,
+	0x03, 0x2b, 0x02, 0x1d, 0x03, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b,
+	0x02, 0x3d, 0x01, 0x33, 0x1d, 0x06, 0x01, 0x35, 0x33, 0x15, 0x17, 0x2b,
+	0x02, 0x35, 0x3b, 0x02, 0x27, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33,
+	0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfe,
+	0xea, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xb9, 0x5c, 0xba, 0x5d,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x11, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x01, 0x33,
+	0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04, 0x27, 0x33, 0x3d, 0x01, 0x2b,
+	0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x27, 0x33, 0x15,
+	0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01,
+	0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0xb9, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x03, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x00, 0x05, 0x35,
+	0x33, 0x15, 0x25, 0x3d, 0x06, 0x33, 0x1d, 0x01, 0x3b, 0x02, 0x3d, 0x01,
+	0x33, 0x1d, 0x06, 0x23, 0x15, 0x23, 0x35, 0x33, 0x3d, 0x03, 0x2b, 0x02,
+	0x1d, 0x03, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d, 0xfe,
+	0x30, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x03, 0x00, 0x17, 0x00, 0x1f,
+	0x00, 0x25, 0x00, 0x00, 0x05, 0x35, 0x33, 0x15, 0x27, 0x23, 0x35, 0x23,
+	0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x04,
+	0x23, 0x27, 0x33, 0x3d, 0x01, 0x2b, 0x01, 0x1d, 0x01, 0x13, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x0d, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x29, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x2b, 0x01,
+	0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b,
+	0x01, 0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02,
+	0x2c, 0x5c, 0x5c, 0xba, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x15,
+	0x00, 0x1d, 0x00, 0x21, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33, 0x1d, 0x02,
+	0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x15, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x73, 0x5d, 0x5d,
+	0xb9, 0x5d, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x3d, 0x05,
+	0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x35,
+	0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5c, 0x5c, 0xba, 0x5d, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x21, 0x00, 0x27, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b,
+	0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x33, 0x37, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01,
+	0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x73, 0x5d, 0x5d, 0xb9, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xa0, 0x00, 0x03,
+	0x00, 0x0d, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x3d, 0x05,
+	0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x03, 0x43, 0x5d,
+	0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5c, 0x5c, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x07, 0x00, 0x11, 0x00, 0x19,
+	0x00, 0x1f, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b,
+	0x01, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x33, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x01, 0x16, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x01, 0x73, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x1b,
+	0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x3d, 0x05,
+	0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x3d,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x03, 0x43, 0x5d,
+	0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5c, 0x5c, 0xba, 0x5d, 0x5d, 0x5d,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x07,
+	0x00, 0x11, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33,
+	0x1d, 0x02, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x35, 0x33, 0x15,
+	0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01,
+	0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x73,
+	0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x11, 0x00, 0x2b, 0x00, 0x31,
+	0x00, 0x35, 0x00, 0x39, 0x00, 0x00, 0x37, 0x33, 0x35, 0x33, 0x3d, 0x03,
+	0x23, 0x35, 0x2b, 0x01, 0x1d, 0x05, 0x17, 0x2b, 0x01, 0x3d, 0x07, 0x3b,
+	0x02, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x03, 0x23, 0x15, 0x23, 0x15, 0x03,
+	0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35,
+	0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0xa0, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x2d,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x00, 0x01, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x01, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x03, 0x33,
+	0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d, 0x07, 0x27, 0x33, 0x3d, 0x03,
+	0x2b, 0x01, 0x1d, 0x03, 0x01, 0xd0, 0x5d, 0xfe, 0xe9, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x02, 0x2d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xfd, 0xd3, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x2d, 0x02, 0xe7, 0x00, 0x13, 0x00, 0x2f, 0x00, 0x00,
+	0x25, 0x33, 0x35, 0x33, 0x3d, 0x03, 0x23, 0x35, 0x2b, 0x01, 0x1d, 0x01,
+	0x33, 0x15, 0x23, 0x1d, 0x02, 0x17, 0x2b, 0x01, 0x3d, 0x03, 0x23, 0x35,
+	0x33, 0x3d, 0x02, 0x3b, 0x02, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x03, 0x23,
+	0x15, 0x23, 0x15, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0x44, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x00,
+	0x21, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x03, 0x33, 0x35, 0x3b, 0x01, 0x35,
+	0x23, 0x35, 0x33, 0x35, 0x33, 0x15, 0x33, 0x15, 0x23, 0x1d, 0x06, 0x27,
+	0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0x01, 0x16, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0xa0, 0x00, 0x21, 0x00, 0x2b, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b,
+	0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01,
+	0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x13, 0x33, 0x15, 0x2b, 0x03, 0x35,
+	0x3b, 0x01, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x07, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33,
+	0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5c, 0x5c, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0xfd, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b,
+	0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01,
+	0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x11, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x07,
+	0x00, 0x19, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33,
+	0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x27, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35,
+	0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0xa0, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x07, 0x3b,
+	0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d,
+	0x02, 0x33, 0x11, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x01, 0x16, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03,
+	0x43, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x07, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b, 0x01, 0x3d, 0x03, 0x33,
+	0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15, 0x13, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x27, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x01, 0x16,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01,
+	0x16, 0x5d, 0xb9, 0x5c, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00,
+	0x05, 0x35, 0x33, 0x15, 0x27, 0x23, 0x35, 0x2b, 0x01, 0x3d, 0x07, 0x3b,
+	0x03, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d,
+	0x02, 0x3b, 0x02, 0x15, 0x23, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x03,
+	0x00, 0x0d, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x27, 0x23, 0x3d, 0x01, 0x3b, 0x02, 0x15, 0x2b, 0x01, 0x27, 0x23, 0x3d,
+	0x03, 0x33, 0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02, 0x15,
+	0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0xfd, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x2b, 0x02,
+	0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x11, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x07, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x2b,
+	0x01, 0x3d, 0x03, 0x33, 0x15, 0x3b, 0x01, 0x35, 0x33, 0x1d, 0x01, 0x2b,
+	0x02, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x27, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01, 0x16,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x1d, 0x00, 0x2b, 0x00, 0x35,
+	0x00, 0x39, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x02, 0x2b, 0x01, 0x35, 0x3b,
+	0x02, 0x1d, 0x04, 0x25, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x33,
+	0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x35, 0x23, 0x35, 0x33, 0x01, 0x16,
+	0x5d, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xfc, 0xbd, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x02, 0x2c, 0x5c, 0x5c, 0xba, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d,
+	0x00, 0x23, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35,
+	0x2b, 0x01, 0x35, 0x23, 0x3d, 0x03, 0x33, 0x35, 0x3b, 0x02, 0x1d, 0x06,
+	0x27, 0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd,
+	0x19, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x02, 0x89, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x19, 0x00, 0x27,
+	0x00, 0x31, 0x00, 0x35, 0x00, 0x39, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x13, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x02, 0x2b, 0x01, 0x35, 0x3b,
+	0x02, 0x1d, 0x04, 0x25, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x33,
+	0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x03, 0x43, 0x5d,
+	0x5d, 0xfc, 0xbd, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x02, 0x2c, 0x5c, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74, 0x03, 0x44, 0x00, 0x05,
+	0x00, 0x1b, 0x00, 0x27, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x35, 0x00, 0x00,
+	0x17, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x23,
+	0x3d, 0x03, 0x33, 0x35, 0x3b, 0x02, 0x1d, 0x06, 0x27, 0x33, 0x3d, 0x03,
+	0x2b, 0x01, 0x1d, 0x03, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35,
+	0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x89,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xa0, 0x00, 0x03, 0x00, 0x19, 0x00, 0x27,
+	0x00, 0x31, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x02, 0x35,
+	0x3b, 0x02, 0x3d, 0x02, 0x2b, 0x01, 0x35, 0x3b, 0x02, 0x1d, 0x04, 0x25,
+	0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35,
+	0x3b, 0x01, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5c,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x2d, 0x00, 0x00,
+	0x17, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x23,
+	0x3d, 0x03, 0x33, 0x35, 0x3b, 0x02, 0x1d, 0x06, 0x27, 0x33, 0x3d, 0x03,
+	0x2b, 0x01, 0x1d, 0x03, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0xba, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x89, 0x5c, 0x5c,
+	0x00, 0x04, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x1b, 0x00, 0x29, 0x00, 0x33, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x3d, 0x01, 0x23, 0x35, 0x3b, 0x02, 0x3d, 0x02, 0x2b, 0x01, 0x35, 0x3b,
+	0x02, 0x1d, 0x04, 0x2b, 0x01, 0x15, 0x27, 0x23, 0x3d, 0x05, 0x33, 0x1d,
+	0x04, 0x01, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x02, 0x2c, 0x5c, 0x5c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x1f,
+	0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x03,
+	0x33, 0x35, 0x3b, 0x02, 0x1d, 0x06, 0x27, 0x33, 0x3d, 0x03, 0x2b, 0x01,
+	0x1d, 0x03, 0x13, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x89, 0x5d, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x27,
+	0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b,
+	0x02, 0x1d, 0x03, 0x23, 0x3d, 0x07, 0x33, 0x1d, 0x02, 0x3b, 0x02, 0x3d,
+	0x02, 0x33, 0x1d, 0x07, 0x03, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x37, 0x23, 0x35, 0x33, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0xba, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x0b, 0x00, 0x21, 0x00, 0x00, 0x21, 0x3d, 0x04, 0x33,
+	0x1d, 0x04, 0x21, 0x3d, 0x07, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b,
+	0x01, 0x1d, 0x04, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a,
+	0x02, 0xe7, 0x00, 0x27, 0x00, 0x2f, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x2b,
+	0x02, 0x1d, 0x03, 0x23, 0x3d, 0x05, 0x23, 0x35, 0x33, 0x35, 0x33, 0x15,
+	0x3b, 0x02, 0x35, 0x33, 0x15, 0x33, 0x15, 0x23, 0x1d, 0x05, 0x03, 0x33,
+	0x35, 0x2b, 0x02, 0x15, 0x33, 0x01, 0xd0, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0x44, 0x00, 0x0b, 0x00, 0x27, 0x00, 0x00,
+	0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x21, 0x3d, 0x06, 0x23, 0x35, 0x33,
+	0x35, 0x33, 0x15, 0x33, 0x15, 0x23, 0x15, 0x3b, 0x01, 0x15, 0x2b, 0x01,
+	0x1d, 0x04, 0x01, 0x73, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x21,
+	0x00, 0x25, 0x00, 0x29, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b,
+	0x02, 0x15, 0x23, 0x1d, 0x05, 0x33, 0x15, 0x11, 0x35, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0xa0,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f,
+	0x00, 0x00, 0x33, 0x3d, 0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x05, 0x03,
+	0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x37, 0x35, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0xba, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0xfe, 0xea, 0x5d,
+	0xb9, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x03, 0xa0, 0x00, 0x19, 0x00, 0x21, 0x00, 0x00, 0x33, 0x2b, 0x01, 0x35,
+	0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d, 0x05, 0x33,
+	0x15, 0x03, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0xa0, 0x5d, 0x5d, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x02, 0xe7, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x00,
+	0x33, 0x3d, 0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x05, 0x11, 0x33, 0x15,
+	0x2b, 0x02, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x02, 0xe6, 0x5c, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x21,
+	0x00, 0x25, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x2b, 0x01, 0x35,
+	0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d, 0x05, 0x33,
+	0x15, 0x03, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0xb9, 0x5d,
+	0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03,
+	0xa0, 0x5d, 0x5d, 0x5d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x00,
+	0x33, 0x3d, 0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x05, 0x03, 0x35, 0x33,
+	0x15, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0x46, 0x01, 0x17,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x1f, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x27, 0x23, 0x35, 0x23, 0x35, 0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02,
+	0x15, 0x23, 0x1d, 0x05, 0x33, 0x15, 0x23, 0xba, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0xff, 0x46, 0x00, 0xba, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b,
+	0x00, 0x1b, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15,
+	0x03, 0x23, 0x35, 0x33, 0x31, 0x3d, 0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d,
+	0x05, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x03, 0x44, 0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x03, 0xa0, 0x00, 0x03,
+	0x00, 0x1d, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x2b, 0x01, 0x35,
+	0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x1d, 0x05, 0x33,
+	0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x03, 0x43, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x02, 0x2d, 0x00, 0x0d,
+	0x00, 0x00, 0x31, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe7, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x1f, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x05, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x05, 0x23, 0x35, 0x3b,
+	0x02, 0x15, 0x23, 0x1d, 0x05, 0x33, 0x15, 0x25, 0x3d, 0x06, 0x33, 0x1d,
+	0x06, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x02, 0x2d, 0x5d, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x01,
+	0x74, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x05, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x13, 0x00, 0x17,
+	0x00, 0x1b, 0x00, 0x2d, 0x00, 0x00, 0x05, 0x35, 0x33, 0x15, 0x25, 0x3d,
+	0x04, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x05, 0x13, 0x35, 0x33, 0x15, 0x21,
+	0x35, 0x33, 0x15, 0x13, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x06,
+	0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d,
+	0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a, 0x5c,
+	0x5c, 0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d,
+	0x00, 0x1d, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d,
+	0x06, 0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x13, 0x23,
+	0x35, 0x33, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x02,
+	0xe6, 0x5d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0xff, 0x46, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x1d, 0x00, 0x21,
+	0x00, 0x00, 0x15, 0x35, 0x33, 0x15, 0x13, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x11, 0x3d, 0x05, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x06, 0x11,
+	0x23, 0x35, 0x33, 0x5d, 0x5d, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x03, 0x44, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd,
+	0x19, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x09, 0x00, 0x25,
+	0x00, 0x29, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x35, 0x00, 0x00, 0x17, 0x35,
+	0x33, 0x15, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d, 0x07, 0x33,
+	0x1d, 0x02, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x23, 0x35, 0x23, 0x35,
+	0x23, 0x1d, 0x03, 0x17, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15, 0x3d,
+	0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe,
+	0x30, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x02, 0x2d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03, 0x00, 0x25, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15, 0x27, 0x3d, 0x07, 0x33,
+	0x1d, 0x03, 0x33, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x01, 0x23, 0x15, 0x23,
+	0x35, 0x33, 0x3d, 0x01, 0x23, 0x35, 0x23, 0x1d, 0x02, 0x13, 0x35, 0x33,
+	0x15, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0x2d, 0x00, 0x05, 0x00, 0x19, 0x00, 0x1d,
+	0x00, 0x21, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d,
+	0x05, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x33, 0x15, 0x23, 0x35, 0x23, 0x1d,
+	0x02, 0x13, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x01, 0x16, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x1b, 0x00, 0x1f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x03, 0x3d, 0x07, 0x33, 0x1d, 0x06, 0x33, 0x11, 0x35, 0x33,
+	0x15, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba,
+	0x03, 0xfd, 0x00, 0x11, 0x00, 0x15, 0x00, 0x19, 0x00, 0x00, 0x31, 0x3d,
+	0x07, 0x33, 0x1d, 0x07, 0x03, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x3d, 0x01, 0x2b, 0x01, 0x3d, 0x07, 0x33, 0x1d, 0x06, 0x3b, 0x02, 0x15,
+	0x23, 0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0xff, 0x46, 0x00, 0xba, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x19, 0x00, 0x00, 0x15, 0x35, 0x33, 0x15, 0x3d, 0x01,
+	0x33, 0x15, 0x27, 0x23, 0x3d, 0x07, 0x33, 0x1d, 0x06, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x1d, 0x00, 0x00, 0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x11, 0x33,
+	0x15, 0x2b, 0x03, 0x3d, 0x07, 0x33, 0x1d, 0x06, 0x33, 0xba, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x02, 0xe7, 0x00, 0x11, 0x00, 0x17, 0x00, 0x00,
+	0x31, 0x3d, 0x07, 0x33, 0x1d, 0x07, 0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x1b, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x11, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x07, 0x33, 0x1d, 0x06, 0x33, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x17, 0x02, 0xe7, 0x00, 0x11, 0x00, 0x15, 0x00, 0x00,
+	0x31, 0x3d, 0x07, 0x33, 0x1d, 0x07, 0x13, 0x35, 0x33, 0x15, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x1b,
+	0x00, 0x1f, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x02, 0x23,
+	0x35, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x33, 0x15, 0x23, 0x1d, 0x02,
+	0x33, 0x11, 0x35, 0x33, 0x15, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x01, 0x73, 0x5d, 0x5d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x02, 0xe7, 0x00, 0x15, 0x00, 0x00, 0x33, 0x3d, 0x02, 0x23, 0x35, 0x33,
+	0x3d, 0x03, 0x33, 0x1d, 0x02, 0x33, 0x15, 0x23, 0x1d, 0x03, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x13, 0x00, 0x2b, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x23, 0x35, 0x33, 0x3d, 0x02,
+	0x33, 0x1d, 0x07, 0x21, 0x3d, 0x07, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15,
+	0x23, 0x35, 0x23, 0x1d, 0x05, 0x13, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33,
+	0x15, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x09,
+	0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x33,
+	0x1d, 0x03, 0x21, 0x3d, 0x04, 0x3b, 0x02, 0x15, 0x2b, 0x01, 0x1d, 0x03,
+	0x11, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x01, 0x16, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x17, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x37, 0x3d, 0x03, 0x23, 0x35, 0x33, 0x3d, 0x02, 0x33, 0x1d, 0x07, 0x21,
+	0x3d, 0x07, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x23, 0x35, 0x23, 0x1d,
+	0x05, 0x17, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0xfe,
+	0x30, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x01, 0xd0, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x27, 0x3d, 0x04, 0x3b, 0x02, 0x15, 0x33, 0x1d, 0x03, 0x23, 0x15, 0x23,
+	0x35, 0x33, 0x3d, 0x03, 0x2b, 0x01, 0x1d, 0x03, 0x5d, 0x5d, 0xba, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x13, 0x00, 0x2b, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x23, 0x35, 0x33,
+	0x3d, 0x02, 0x33, 0x1d, 0x07, 0x21, 0x3d, 0x07, 0x33, 0x15, 0x33, 0x15,
+	0x33, 0x15, 0x23, 0x35, 0x23, 0x1d, 0x05, 0x13, 0x35, 0x33, 0x15, 0x3d,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x01, 0x73, 0x5d, 0x5d, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03,
+	0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x09, 0x00, 0x19, 0x00, 0x1f,
+	0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x21, 0x3d, 0x04, 0x3b, 0x02, 0x15, 0x2b, 0x01, 0x1d, 0x03, 0x13, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33,
+	0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x02, 0x8a, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x09,
+	0x00, 0x19, 0x00, 0x1f, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x21, 0x3d, 0x04, 0x3b, 0x02, 0x15, 0x2b, 0x01, 0x1d, 0x03, 0x03, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x01, 0x73, 0x5d, 0xfe, 0x8d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x2d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x00, 0x01, 0x00, 0x00, 0xff, 0xa3, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x31, 0x00, 0x00, 0x31, 0x3d, 0x07, 0x33, 0x15, 0x33,
+	0x15, 0x33, 0x15, 0x33, 0x3d, 0x02, 0x33, 0x1d, 0x07, 0x23, 0x15, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x3d, 0x03, 0x23, 0x35, 0x23, 0x35, 0x23, 0x1d,
+	0x05, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0xa3, 0x01, 0x74,
+	0x01, 0xd0, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x25, 0x3d, 0x04, 0x3b, 0x02, 0x15, 0x33, 0x1d, 0x03, 0x23, 0x3d, 0x03,
+	0x2b, 0x01, 0x1d, 0x03, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xa0, 0x00, 0x07, 0x00, 0x15, 0x00, 0x23, 0x00, 0x2b, 0x00, 0x37,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05,
+	0x33, 0x1d, 0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b,
+	0x02, 0x35, 0x3b, 0x02, 0x35, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0xba,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x29,
+	0x00, 0x00, 0x37, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x03, 0x33,
+	0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0xba,
+	0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5c, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x07,
+	0x00, 0x15, 0x00, 0x23, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b,
+	0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x05,
+	0x33, 0x1d, 0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x01, 0x2b,
+	0x02, 0x35, 0x3b, 0x02, 0x27, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01,
+	0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0xba, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x05, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x25,
+	0x00, 0x29, 0x00, 0x2d, 0x00, 0x00, 0x37, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d,
+	0x02, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x27, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x2b,
+	0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01,
+	0x3d, 0x05, 0x33, 0x1d, 0x05, 0x21, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04,
+	0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x01, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x03, 0x43, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d,
+	0x03, 0x33, 0x1d, 0x03, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13,
+	0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x01, 0x16, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe7, 0x02, 0xe7, 0x00, 0x29,
+	0x00, 0x3b, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x06, 0x35, 0x23, 0x3d,
+	0x05, 0x33, 0x35, 0x3b, 0x06, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01,
+	0x15, 0x2b, 0x01, 0x1d, 0x02, 0x33, 0x21, 0x33, 0x3d, 0x05, 0x2b, 0x02,
+	0x1d, 0x05, 0x33, 0x02, 0x8a, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x8a, 0x02, 0x2d, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x0f,
+	0x00, 0x21, 0x00, 0x2b, 0x00, 0x31, 0x00, 0x37, 0x00, 0x00, 0x25, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x23, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x21, 0x35,
+	0x33, 0x15, 0x21, 0x23, 0x3d, 0x03, 0x33, 0x15, 0x3b, 0x01, 0x35, 0x33,
+	0x1d, 0x01, 0x2b, 0x02, 0x15, 0x05, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02,
+	0x01, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x01, 0xd0, 0x5d,
+	0xfe, 0xe9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xea,
+	0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x09, 0x00, 0x2d, 0x00, 0x31,
+	0x00, 0x35, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x21, 0x3d,
+	0x07, 0x3b, 0x03, 0x15, 0x33, 0x1d, 0x01, 0x23, 0x3d, 0x01, 0x2b, 0x02,
+	0x1d, 0x01, 0x3b, 0x02, 0x15, 0x2b, 0x02, 0x1d, 0x03, 0x13, 0x35, 0x33,
+	0x15, 0x3d, 0x01, 0x33, 0x15, 0x01, 0x73, 0x5d, 0xfe, 0x30, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x19, 0x00, 0x00,
+	0x31, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x11, 0x35, 0x33, 0x1d, 0x01, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x37, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x00,
+	0x00, 0x04, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x0d, 0x00, 0x31, 0x00, 0x35, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x37, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x21, 0x3d, 0x07, 0x3b, 0x03, 0x15,
+	0x33, 0x1d, 0x01, 0x23, 0x3d, 0x01, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x02,
+	0x15, 0x2b, 0x02, 0x1d, 0x03, 0x17, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0xb9,
+	0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0xff, 0x46, 0x01, 0x17, 0x02, 0x2d, 0x00, 0x03, 0x00, 0x07, 0x00, 0x13,
+	0x00, 0x19, 0x00, 0x00, 0x15, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15,
+	0x27, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x13, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x09, 0x00, 0x2d, 0x00, 0x31,
+	0x00, 0x35, 0x00, 0x39, 0x00, 0x00, 0x21, 0x3d, 0x03, 0x33, 0x1d, 0x03,
+	0x21, 0x3d, 0x07, 0x3b, 0x03, 0x15, 0x33, 0x1d, 0x01, 0x23, 0x3d, 0x01,
+	0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x02, 0x15, 0x2b, 0x02, 0x1d, 0x03, 0x13,
+	0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x01,
+	0x73, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17,
+	0x03, 0x44, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x19, 0x00, 0x1d,
+	0x00, 0x00, 0x31, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x11, 0x35, 0x33, 0x1d,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x37, 0x35, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x33, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x02, 0x8a,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x0d, 0x00, 0x15, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x31,
+	0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x03, 0x35,
+	0x3b, 0x02, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x03, 0x2b, 0x02, 0x35, 0x3b,
+	0x02, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x25, 0x33, 0x15, 0x2b, 0x03,
+	0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9,
+	0x5c, 0x5c, 0xba, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x11,
+	0x00, 0x17, 0x00, 0x1b, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x01,
+	0x33, 0x1d, 0x01, 0x27, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x35,
+	0x33, 0x37, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x15,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x11, 0x00, 0x19, 0x00, 0x21, 0x00, 0x27, 0x00, 0x31,
+	0x00, 0x35, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x02, 0x3d, 0x02, 0x33, 0x1d,
+	0x02, 0x03, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x21, 0x23, 0x3d, 0x01, 0x33,
+	0x15, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x35, 0x23, 0x35,
+	0x33, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5c, 0xba, 0x5d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f,
+	0x00, 0x15, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x27, 0x00, 0x2d, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b,
+	0x02, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x27, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x2b, 0x01, 0x35, 0x33, 0x37, 0x33, 0x15, 0x2b, 0x02,
+	0x35, 0x33, 0x37, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd,
+	0xd3, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xb9, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x25,
+	0x00, 0x2f, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x2b, 0x01,
+	0x35, 0x3b, 0x03, 0x15, 0x23, 0x15, 0x37, 0x3d, 0x02, 0x33, 0x1d, 0x02,
+	0x03, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15,
+	0x25, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x01, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xb9, 0x5c, 0x5c, 0x00, 0x00, 0x06, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x02, 0x2d, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1d,
+	0x00, 0x25, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x2b, 0x01,
+	0x35, 0x3b, 0x02, 0x1d, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x27, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x35, 0x33, 0x37, 0x33, 0x15, 0x2b,
+	0x02, 0x35, 0x33, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x08, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x15,
+	0x00, 0x1d, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x35, 0x00, 0x00,
+	0x13, 0x35, 0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x02,
+	0x3d, 0x02, 0x33, 0x1d, 0x02, 0x03, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x21,
+	0x23, 0x3d, 0x01, 0x33, 0x15, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b,
+	0x01, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0xba, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d,
+	0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x00,
+	0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x07,
+	0x00, 0x0d, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x37, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01,
+	0x3d, 0x01, 0x33, 0x1d, 0x01, 0x27, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x2b,
+	0x01, 0x35, 0x33, 0x37, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x35, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33,
+	0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0x46, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x03, 0x00, 0x1f, 0x00, 0x00, 0x17, 0x35, 0x33, 0x15,
+	0x3d, 0x08, 0x2b, 0x01, 0x35, 0x3b, 0x04, 0x15, 0x2b, 0x01, 0x1d, 0x07,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0xff, 0xa3, 0x01, 0x17, 0x02, 0xe7, 0x00, 0x05,
+	0x00, 0x09, 0x00, 0x1d, 0x00, 0x00, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x35, 0x33, 0x15, 0x27, 0x23, 0x3d, 0x03, 0x23, 0x35, 0x33, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x23, 0x1d, 0x02, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, 0x00,
+	0x33, 0x3d, 0x06, 0x2b, 0x01, 0x35, 0x3b, 0x04, 0x15, 0x2b, 0x01, 0x1d,
+	0x06, 0x03, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x5c, 0x02, 0x8a, 0x03, 0xfd, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x29,
+	0x00, 0x00, 0x25, 0x3d, 0x05, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x01,
+	0x33, 0x1d, 0x02, 0x3b, 0x02, 0x15, 0x2b, 0x02, 0x1d, 0x04, 0x13, 0x35,
+	0x33, 0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x01, 0x16, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x89, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x1d, 0x00, 0x00, 0x33, 0x3d,
+	0x04, 0x23, 0x35, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x04, 0x15, 0x2b,
+	0x01, 0x15, 0x33, 0x15, 0x23, 0x1d, 0x04, 0xba, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x02, 0x8a,
+	0x03, 0xa0, 0x00, 0x27, 0x00, 0x00, 0x25, 0x3d, 0x02, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x3d, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x3d, 0x01, 0x33,
+	0x1d, 0x02, 0x3b, 0x02, 0x15, 0x2b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15,
+	0x2b, 0x01, 0x1d, 0x01, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x39,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21,
+	0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x01, 0x35, 0x33, 0x15, 0x2b, 0x02,
+	0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d,
+	0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd,
+	0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0xe6, 0x5d, 0x5d, 0x5d, 0x00,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x19, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x00,
+	0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d, 0x04, 0x33,
+	0x1d, 0x03, 0x13, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0xba, 0x5c,
+	0xfe, 0xea, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0xb9, 0x5d, 0xb9, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c,
+	0xfd, 0x76, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0x2c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xa0, 0x00, 0x07, 0x00, 0x17, 0x00, 0x27,
+	0x00, 0x33, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01,
+	0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05,
+	0x01, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0x01, 0x16, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x02, 0xe6, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x27,
+	0x00, 0x00, 0x21, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d,
+	0x05, 0x25, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x13, 0x33, 0x15, 0x2b,
+	0x03, 0x35, 0x3b, 0x01, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5c,
+	0x5c, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x07, 0x00, 0x17, 0x00, 0x27, 0x00, 0x2f, 0x00, 0x33,
+	0x00, 0x37, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01,
+	0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05,
+	0x13, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x21,
+	0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0xe6,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x23,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03,
+	0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23,
+	0x35, 0x33, 0x01, 0x16, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x1b, 0x00, 0x2b, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x11, 0x33,
+	0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21,
+	0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x13, 0x35, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x23, 0x35, 0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0xb9, 0x5d, 0xb9, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x02, 0xe6, 0x5d, 0x5d, 0xfd, 0x77, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x02, 0x89, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0xa0, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x23,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x31, 0x00, 0x00, 0x21, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d, 0x04, 0x33,
+	0x1d, 0x03, 0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15,
+	0x21, 0x23, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f,
+	0x00, 0x1f, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x01, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b, 0x02, 0x35,
+	0x3b, 0x01, 0x3d, 0x06, 0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x06, 0x33,
+	0x1d, 0x05, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x01, 0x16,
+	0x5d, 0xfe, 0x8d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0xfe, 0x8d, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02,
+	0xe6, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x19,
+	0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d, 0x04,
+	0x33, 0x1d, 0x05, 0x25, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x13, 0x35,
+	0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0xb9,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0xb9, 0x5d, 0xb9,
+	0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfd, 0x76, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0xfe, 0xe9, 0x01, 0xd0,
+	0x02, 0xe7, 0x00, 0x05, 0x00, 0x09, 0x00, 0x13, 0x00, 0x23, 0x00, 0x33,
+	0x00, 0x00, 0x05, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x31, 0x23, 0x35, 0x33,
+	0x31, 0x35, 0x23, 0x35, 0x3b, 0x02, 0x15, 0x23, 0x15, 0x37, 0x3d, 0x06,
+	0x33, 0x1d, 0x06, 0x21, 0x23, 0x3d, 0x06, 0x33, 0x1d, 0x05, 0x01, 0x16,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xba, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x03, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x02, 0x2d, 0x00, 0x03, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x05, 0x35,
+	0x33, 0x15, 0x27, 0x23, 0x35, 0x23, 0x35, 0x3b, 0x01, 0x3d, 0x04, 0x33,
+	0x1d, 0x05, 0x23, 0x27, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x01, 0x16,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0xba,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x35, 0x00, 0x39, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x13, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x2b, 0x01,
+	0x3d, 0x01, 0x33, 0x15, 0x07, 0x23, 0x3d, 0x05, 0x33, 0x1d, 0x04, 0x13,
+	0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0xfe,
+	0xea, 0x5d, 0xb9, 0x5d, 0xba, 0x5c, 0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x03, 0x43, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xfd, 0x77, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x02, 0x8a, 0x5d, 0x00,
+	0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0x44, 0x00, 0x05,
+	0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x2d,
+	0x00, 0x31, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x2b, 0x01, 0x3d, 0x01, 0x33, 0x15,
+	0x07, 0x23, 0x3d, 0x03, 0x33, 0x1d, 0x02, 0x13, 0x23, 0x35, 0x33, 0x01,
+	0x16, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0xb9, 0x5d,
+	0xba, 0x5c, 0x5c, 0xb9, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x02, 0x8a, 0x5c, 0x5c, 0x5c, 0x5c, 0xfe,
+	0x30, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x00,
+	0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x09,
+	0x00, 0x0d, 0x00, 0x11, 0x00, 0x17, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x29,
+	0x00, 0x2d, 0x00, 0x00, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x11, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x2b, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x13, 0x23, 0x35, 0x33, 0xba, 0x5c,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x01, 0x16, 0x5d, 0x00, 0x06, 0x00, 0x00, 0xff, 0x46, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x21, 0x00, 0x2d,
+	0x00, 0x33, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x2b, 0x01, 0x35, 0x3b,
+	0x01, 0x3d, 0x04, 0x33, 0x1d, 0x06, 0x25, 0x23, 0x3d, 0x04, 0x33, 0x1d,
+	0x03, 0x13, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x16, 0x5d, 0xfe, 0x8d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x02, 0x8a, 0x5c, 0x5c,
+	0x5c, 0x5c, 0xfd, 0x19, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x02, 0x2c, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xa0, 0x00, 0x09,
+	0x00, 0x0d, 0x00, 0x11, 0x00, 0x17, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x29,
+	0x00, 0x00, 0x33, 0x3d, 0x03, 0x33, 0x1d, 0x03, 0x11, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x2b, 0x01,
+	0x3d, 0x01, 0x33, 0x15, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0x5d, 0xfe, 0xea, 0x5d, 0x5c, 0x5d,
+	0xb9, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe,
+	0x30, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0xfd, 0x00, 0x03, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f,
+	0x00, 0x2d, 0x00, 0x31, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x33,
+	0x15, 0x2b, 0x04, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x27, 0x35,
+	0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01,
+	0x2b, 0x03, 0x35, 0x3b, 0x04, 0x1d, 0x01, 0x03, 0x35, 0x33, 0x15, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xb9,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0xba, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x13,
+	0x00, 0x17, 0x00, 0x23, 0x00, 0x27, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x01, 0x33, 0x15, 0x33, 0x27, 0x35,
+	0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x2b, 0x02, 0x35, 0x3b,
+	0x03, 0x1d, 0x01, 0x03, 0x35, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0xa0, 0x00, 0x03, 0x00, 0x13, 0x00, 0x17,
+	0x00, 0x1b, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x13, 0x33, 0x15, 0x2b, 0x04, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3b, 0x01,
+	0x27, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15,
+	0x3d, 0x01, 0x2b, 0x03, 0x35, 0x3b, 0x04, 0x1d, 0x01, 0xba, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x13, 0x35,
+	0x33, 0x15, 0x11, 0x33, 0x15, 0x2b, 0x03, 0x3d, 0x01, 0x33, 0x15, 0x33,
+	0x27, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x2b, 0x02,
+	0x35, 0x3b, 0x03, 0x1d, 0x01, 0xba, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x02, 0x8a, 0x5c, 0x5c, 0xfd, 0xd3, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x03, 0xfd, 0x00, 0x03,
+	0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x31,
+	0x00, 0x35, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15, 0x13, 0x33, 0x15, 0x2b,
+	0x04, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x27, 0x35, 0x33, 0x15,
+	0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x2b, 0x03,
+	0x35, 0x3b, 0x04, 0x1d, 0x01, 0x03, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0xb9, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xba, 0x5d, 0xb9, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0xfd,
+	0x1a, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x73, 0x5d,
+	0x5d, 0x5d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74,
+	0x03, 0x44, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x25,
+	0x00, 0x29, 0x00, 0x2d, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x3d,
+	0x01, 0x33, 0x15, 0x33, 0x27, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15,
+	0x3d, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x03, 0x1d, 0x01, 0x03, 0x33, 0x15,
+	0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01,
+	0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x2c, 0x00, 0xba,
+	0x03, 0x44, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x11, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x02, 0x2c, 0x00, 0xba, 0x03, 0x44, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00,
+	0x11, 0x35, 0x33, 0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x5d, 0x5d, 0x02,
+	0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x00, 0x04, 0x00, 0x00,
+	0x02, 0x2c, 0x01, 0x74, 0x03, 0x44, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x0f,
+	0x00, 0x13, 0x00, 0x00, 0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x37, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33,
+	0xba, 0x5c, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0xb9, 0x5d, 0x5d, 0x02, 0x2d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d,
+	0x00, 0x04, 0x00, 0x00, 0x02, 0x2c, 0x01, 0x74, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x00, 0x13, 0x35, 0x33, 0x15,
+	0x21, 0x35, 0x33, 0x15, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x2b, 0x01,
+	0x3d, 0x01, 0x33, 0x15, 0xba, 0x5c, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0xb9,
+	0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb9, 0x01, 0xd0,
+	0x02, 0x8a, 0x00, 0x13, 0x00, 0x00, 0x25, 0x2b, 0x01, 0x35, 0x23, 0x3d,
+	0x02, 0x33, 0x35, 0x3b, 0x02, 0x15, 0x33, 0x1d, 0x02, 0x23, 0x15, 0x01,
+	0x16, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a, 0x00, 0x5d, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x02, 0x2d, 0x5d, 0xfe, 0x8c, 0x5d,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x02, 0x2c, 0x01, 0x17, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x11, 0x35, 0x33, 0x15, 0x3d, 0x01,
+	0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x5d, 0x5d, 0x5c, 0x02, 0x2d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x02, 0x2c, 0x01, 0x74, 0x03, 0x44, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x0f,
+	0x00, 0x13, 0x00, 0x00, 0x13, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x37, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33,
+	0xba, 0x5c, 0xfe, 0xea, 0x5d, 0xb9, 0x5d, 0xb9, 0x5d, 0x5d, 0x02, 0x2d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x0f, 0x00, 0x13, 0x00, 0x19, 0x00, 0x21, 0x00, 0x00, 0x33, 0x35,
+	0x33, 0x15, 0x27, 0x3d, 0x03, 0x33, 0x15, 0x33, 0x15, 0x23, 0x1d, 0x01,
+	0x01, 0x35, 0x33, 0x15, 0x05, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x27, 0x2b,
+	0x02, 0x35, 0x3b, 0x02, 0xba, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0xea,
+	0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0x73,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x2d, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x0b, 0x00, 0x0f, 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x01, 0x35,
+	0x33, 0x15, 0x03, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x35, 0x33,
+	0x15, 0x21, 0x23, 0x35, 0x23, 0x35, 0x33, 0x35, 0x23, 0x35, 0x33, 0x3d,
+	0x01, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x15, 0x3b, 0x01,
+	0x15, 0x2b, 0x01, 0x01, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0xd0, 0x5d,
+	0xba, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0xba, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0x00, 0x02, 0x00, 0x00,
+	0x01, 0xd0, 0x03, 0xa0, 0x03, 0x44, 0x00, 0x0f, 0x00, 0x2b, 0x00, 0x00,
+	0x01, 0x3d, 0x01, 0x23, 0x15, 0x23, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33,
+	0x1d, 0x03, 0x21, 0x3d, 0x02, 0x2b, 0x01, 0x1d, 0x02, 0x23, 0x3d, 0x02,
+	0x2b, 0x01, 0x35, 0x3b, 0x05, 0x15, 0x33, 0x15, 0x23, 0x1d, 0x01, 0x03,
+	0x43, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0xb9, 0x03, 0xa0, 0x02, 0x8a, 0x00, 0x1d,
+	0x00, 0x00, 0x37, 0x35, 0x23, 0x35, 0x23, 0x35, 0x33, 0x35, 0x33, 0x35,
+	0x33, 0x1d, 0x01, 0x3b, 0x06, 0x15, 0x2b, 0x06, 0x1d, 0x01, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0x44, 0x00, 0x1b, 0x00, 0x00, 0x33, 0x3d,
+	0x05, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x15, 0x33, 0x15,
+	0x33, 0x15, 0x2b, 0x01, 0x1d, 0x05, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0xb9, 0x03, 0xa0, 0x02, 0x8a, 0x00, 0x1d,
+	0x00, 0x00, 0x25, 0x3d, 0x01, 0x2b, 0x06, 0x35, 0x3b, 0x06, 0x3d, 0x01,
+	0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x23, 0x15, 0x23, 0x15, 0x02, 0x8a,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x03, 0x44, 0x00, 0x1b, 0x00, 0x00, 0x33, 0x35,
+	0x23, 0x35, 0x23, 0x35, 0x3b, 0x01, 0x3d, 0x05, 0x33, 0x1d, 0x05, 0x3b,
+	0x01, 0x15, 0x23, 0x15, 0x23, 0x15, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0x00, 0x00, 0xb9, 0x02, 0x8a, 0x02, 0x2d, 0x00, 0x05,
+	0x00, 0x0b, 0x00, 0x11, 0x00, 0x17, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x29,
+	0x00, 0x00, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x23, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01,
+	0x33, 0x15, 0x05, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x25, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0xd0, 0x5d, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x01, 0xd0, 0x5d, 0xfe, 0xe9, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5c, 0x00, 0x5c, 0x02, 0xe7,
+	0x02, 0xe7, 0x00, 0x1b, 0x00, 0x00, 0x25, 0x2b, 0x05, 0x3d, 0x01, 0x33,
+	0x3d, 0x01, 0x33, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x15, 0x33, 0x1d, 0x01,
+	0x33, 0x1d, 0x01, 0x33, 0x1d, 0x01, 0x02, 0x8a, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x02, 0x8a, 0x02, 0xe7, 0x00, 0x1b,
+	0x00, 0x00, 0x37, 0x23, 0x3d, 0x06, 0x3b, 0x01, 0x15, 0x3b, 0x01, 0x15,
+	0x3b, 0x01, 0x15, 0x33, 0x15, 0x23, 0x15, 0x2b, 0x01, 0x15, 0x2b, 0x01,
+	0x15, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5c,
+	0x00, 0x5c, 0x02, 0xe7, 0x02, 0xe7, 0x00, 0x1b, 0x00, 0x00, 0x25, 0x35,
+	0x23, 0x3d, 0x01, 0x23, 0x3d, 0x01, 0x23, 0x3d, 0x01, 0x3b, 0x06, 0x1d,
+	0x01, 0x23, 0x1d, 0x01, 0x23, 0x1d, 0x01, 0x23, 0x15, 0x01, 0x73, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x02, 0x8a,
+	0x02, 0xe7, 0x00, 0x1b, 0x00, 0x00, 0x25, 0x23, 0x35, 0x2b, 0x01, 0x35,
+	0x2b, 0x01, 0x35, 0x23, 0x35, 0x33, 0x35, 0x3b, 0x01, 0x35, 0x3b, 0x01,
+	0x35, 0x3b, 0x01, 0x1d, 0x06, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x00,
+	0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x2d, 0x00, 0x31, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x13, 0x35,
+	0x23, 0x15, 0x23, 0x35, 0x23, 0x1d, 0x03, 0x23, 0x3d, 0x03, 0x23, 0x15,
+	0x23, 0x35, 0x23, 0x15, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x35, 0x3b,
+	0x01, 0x35, 0x33, 0x15, 0x3b, 0x01, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x01,
+	0x01, 0x35, 0x33, 0x15, 0x01, 0xd0, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x03, 0xfd, 0x00, 0x33,
+	0x00, 0x37, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x63, 0x00, 0x67, 0x00, 0x00,
+	0x01, 0x35, 0x23, 0x35, 0x23, 0x15, 0x23, 0x1d, 0x01, 0x33, 0x15, 0x23,
+	0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x35, 0x3b, 0x03, 0x15, 0x33,
+	0x15, 0x33, 0x1d, 0x01, 0x23, 0x15, 0x23, 0x15, 0x2b, 0x03, 0x35, 0x3b,
+	0x03, 0x35, 0x33, 0x3d, 0x01, 0x23, 0x35, 0x23, 0x1d, 0x01, 0x37, 0x35,
+	0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x21, 0x35, 0x33, 0x15, 0x2b, 0x01,
+	0x35, 0x23, 0x35, 0x33, 0x35, 0x2b, 0x03, 0x15, 0x33, 0x15, 0x23, 0x15,
+	0x23, 0x3d, 0x02, 0x23, 0x35, 0x3b, 0x01, 0x35, 0x3b, 0x03, 0x15, 0x3b,
+	0x01, 0x15, 0x23, 0x1d, 0x01, 0x05, 0x23, 0x35, 0x33, 0x01, 0xd0, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0xb9, 0x5d, 0xfd, 0x77, 0x5d, 0x5d, 0x02, 0x89, 0x5d, 0xba,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfd, 0x77, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5a,
+	0x03, 0x44, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x23, 0x00, 0x29,
+	0x00, 0x31, 0x00, 0x37, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49,
+	0x00, 0x4d, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x02, 0x35, 0x3b, 0x01,
+	0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01,
+	0x25, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02, 0x21, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x05, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x25, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x07, 0x23, 0x35, 0x33, 0x25, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x05, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15, 0x3d, 0x01, 0x33, 0x15,
+	0x01, 0x16, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d,
+	0x5d, 0x03, 0xa0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0x01, 0xd0,
+	0x5d, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x2b,
+	0x00, 0x00, 0x21, 0x23, 0x35, 0x23, 0x35, 0x23, 0x15, 0x23, 0x15, 0x2b,
+	0x01, 0x35, 0x33, 0x3d, 0x02, 0x23, 0x35, 0x23, 0x35, 0x3b, 0x02, 0x35,
+	0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x3b, 0x02, 0x15, 0x23,
+	0x15, 0x23, 0x1d, 0x02, 0x33, 0x15, 0x02, 0x8a, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0xb7,
+	0x02, 0xe7, 0x00, 0x37, 0x00, 0x43, 0x00, 0x51, 0x00, 0x59, 0x00, 0x5d,
+	0x00, 0x61, 0x00, 0x00, 0x21, 0x2b, 0x07, 0x35, 0x23, 0x3d, 0x01, 0x33,
+	0x35, 0x33, 0x35, 0x2b, 0x01, 0x3d, 0x01, 0x33, 0x35, 0x3b, 0x0a, 0x15,
+	0x33, 0x1d, 0x01, 0x2b, 0x01, 0x35, 0x23, 0x35, 0x23, 0x15, 0x33, 0x15,
+	0x33, 0x15, 0x33, 0x1d, 0x02, 0x2b, 0x01, 0x15, 0x25, 0x33, 0x35, 0x2b,
+	0x04, 0x15, 0x3b, 0x02, 0x37, 0x33, 0x35, 0x23, 0x35, 0x2b, 0x01, 0x15,
+	0x2b, 0x01, 0x15, 0x3b, 0x02, 0x13, 0x33, 0x35, 0x2b, 0x02, 0x15, 0x33,
+	0x05, 0x35, 0x23, 0x15, 0x37, 0x35, 0x23, 0x15, 0x03, 0x43, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0xfe, 0xe9, 0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x17, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x12, 0x00, 0x00,
+	0x00, 0x00, 0x04, 0x5a, 0x03, 0xfd, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x13,
+	0x00, 0x17, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x35, 0x00, 0x39,
+	0x00, 0x41, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x55, 0x00, 0x5f,
+	0x00, 0x63, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x00, 0x21, 0x3d, 0x01, 0x3b,
+	0x01, 0x15, 0x23, 0x15, 0x21, 0x35, 0x23, 0x35, 0x3b, 0x01, 0x1d, 0x01,
+	0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x13, 0x2b, 0x02, 0x3d,
+	0x02, 0x33, 0x1d, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d, 0x02, 0x37,
+	0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x25, 0x23, 0x35, 0x33, 0x05,
+	0x23, 0x35, 0x33, 0x21, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d,
+	0x02, 0x33, 0x1d, 0x01, 0x25, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15,
+	0x25, 0x35, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x03, 0x05, 0x23, 0x35,
+	0x33, 0x21, 0x3d, 0x01, 0x33, 0x15, 0x33, 0x15, 0x23, 0x21, 0x2b, 0x01,
+	0x35, 0x33, 0x35, 0x33, 0x15, 0x03, 0xa0, 0x5d, 0x5d, 0x5d, 0xfc, 0x60,
+	0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5c, 0xfe, 0x8d, 0x5d, 0xba, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0xfd, 0x1a, 0x5c, 0x02,
+	0x2d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0xfd, 0x76,
+	0x5c, 0x5c, 0x02, 0x2d, 0x5d, 0x5d, 0xfd, 0xd3, 0x5d, 0x01, 0xd0, 0x5d,
+	0xba, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xfe, 0x30, 0x5c,
+	0x5c, 0x02, 0x8a, 0x5d, 0x5d, 0x5d, 0xfc, 0xbd, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x03, 0x44, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x23,
+	0x00, 0x27, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b, 0x00, 0x57, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x03, 0x35, 0x33, 0x15, 0x21,
+	0x23, 0x35, 0x33, 0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x2b, 0x01, 0x35,
+	0x33, 0x05, 0x35, 0x33, 0x15, 0x2b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x03,
+	0x21, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x37, 0x23, 0x3d, 0x03,
+	0x2b, 0x01, 0x15, 0x23, 0x35, 0x33, 0x35, 0x3b, 0x04, 0x15, 0x33, 0x15,
+	0x23, 0x35, 0x2b, 0x01, 0x1d, 0x02, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xba, 0x5c, 0xfd, 0xd4, 0x5d, 0x5d, 0x02,
+	0x2c, 0x5d, 0xb9, 0x5d, 0x5d, 0xfe, 0x30, 0x5c, 0xb9, 0x5d, 0x5d, 0x01,
+	0xd0, 0x5d, 0x5d, 0xfe, 0xe9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x37, 0x00, 0x43, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15,
+	0x11, 0x2b, 0x03, 0x35, 0x23, 0x35, 0x23, 0x3d, 0x04, 0x33, 0x1d, 0x01,
+	0x33, 0x35, 0x3b, 0x01, 0x15, 0x33, 0x15, 0x3b, 0x01, 0x35, 0x33, 0x3d,
+	0x01, 0x33, 0x1d, 0x04, 0x23, 0x15, 0x23, 0x15, 0x25, 0x35, 0x23, 0x15,
+	0x01, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x25, 0x2b, 0x04, 0x35,
+	0x3b, 0x04, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xfe, 0xe9,
+	0x5d, 0x01, 0xd0, 0x5c, 0x5c, 0xfd, 0x77, 0x5d, 0x01, 0xd0, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d,
+	0xfd, 0xd3, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0xba, 0x5c, 0x5c, 0x01, 0xd0, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x00,
+	0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x2b,
+	0x00, 0x2f, 0x00, 0x3b, 0x00, 0x43, 0x00, 0x47, 0x00, 0x4b, 0x00, 0x57,
+	0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x01, 0x33,
+	0x15, 0x2b, 0x04, 0x35, 0x3b, 0x03, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35,
+	0x33, 0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x2b, 0x01, 0x35, 0x33, 0x05,
+	0x35, 0x33, 0x15, 0x2b, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x21, 0x2b,
+	0x02, 0x35, 0x3b, 0x02, 0x13, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15,
+	0x25, 0x2b, 0x04, 0x35, 0x3b, 0x04, 0x02, 0x2d, 0x5d, 0xfe, 0x30, 0x5c,
+	0x01, 0x17, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xba,
+	0x5c, 0xfd, 0xd4, 0x5d, 0x5d, 0x02, 0x2c, 0x5d, 0xb9, 0x5d, 0x5d, 0xfe,
+	0x30, 0x5c, 0xb9, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5c, 0xfd, 0x77, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x17, 0x5c, 0x5c,
+	0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b,
+	0x00, 0x27, 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x3f, 0x00, 0x43, 0x00, 0x47,
+	0x00, 0x4b, 0x00, 0x57, 0x00, 0x00, 0x01, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x01, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x03, 0x35, 0x33,
+	0x15, 0x21, 0x23, 0x35, 0x33, 0x21, 0x3d, 0x04, 0x33, 0x1d, 0x04, 0x25,
+	0x33, 0x15, 0x2b, 0x02, 0x35, 0x33, 0x05, 0x23, 0x3d, 0x04, 0x33, 0x1d,
+	0x03, 0x21, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x01, 0x23, 0x35,
+	0x33, 0x05, 0x35, 0x33, 0x15, 0x25, 0x2b, 0x04, 0x35, 0x3b, 0x04, 0x02,
+	0x2d, 0x5d, 0xfe, 0x30, 0x5c, 0x01, 0x17, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0xba, 0x5c, 0xfd, 0xd4, 0x5d, 0x5d, 0x02, 0x2c,
+	0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d,
+	0x01, 0xd0, 0x5d, 0xfe, 0x8c, 0x5c, 0x5c, 0x01, 0xd0, 0x5c, 0x5c, 0xfd,
+	0x77, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x17, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x03, 0x44, 0x00, 0x23, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3b,
+	0x00, 0x00, 0x21, 0x2b, 0x03, 0x35, 0x23, 0x35, 0x23, 0x3d, 0x04, 0x33,
+	0x35, 0x33, 0x35, 0x3b, 0x04, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x04, 0x23,
+	0x15, 0x23, 0x15, 0x27, 0x33, 0x35, 0x2b, 0x02, 0x35, 0x23, 0x15, 0x33,
+	0x15, 0x33, 0x25, 0x35, 0x23, 0x15, 0x37, 0x35, 0x23, 0x15, 0x21, 0x35,
+	0x23, 0x15, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x01, 0x17, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xba, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x10, 0x00, 0x00,
+	0xff, 0xa3, 0x03, 0xfd, 0x03, 0xa0, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13,
+	0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x2f, 0x00, 0x3b,
+	0x00, 0x3f, 0x00, 0x43, 0x00, 0x47, 0x00, 0x53, 0x00, 0x57, 0x00, 0x5b,
+	0x00, 0x5f, 0x00, 0x00, 0x05, 0x3d, 0x01, 0x23, 0x35, 0x3b, 0x02, 0x15,
+	0x23, 0x1d, 0x01, 0x25, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15, 0x25,
+	0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x25, 0x23, 0x35, 0x33, 0x05,
+	0x23, 0x35, 0x33, 0x21, 0x3d, 0x02, 0x33, 0x15, 0x3b, 0x01, 0x15, 0x2b,
+	0x01, 0x15, 0x21, 0x23, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x33,
+	0x1d, 0x01, 0x25, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x25, 0x35,
+	0x33, 0x15, 0x2b, 0x03, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x33,
+	0x05, 0x23, 0x35, 0x33, 0x21, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33,
+	0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0xfc, 0xbd,
+	0x5d, 0x02, 0x89, 0x5d, 0x5d, 0xfd, 0x77, 0x5c, 0x01, 0xd0, 0x5c, 0x5c,
+	0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd,
+	0xd3, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0x5c, 0xfe,
+	0x30, 0x5d, 0x01, 0x73, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xfe, 0x8c, 0x5c, 0x5c, 0x02, 0x2d, 0x5d, 0xfd, 0x1a, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0,
+	0x03, 0x44, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x27, 0x00, 0x2f, 0x00, 0x00,
+	0x33, 0x3d, 0x01, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x23, 0x35, 0x3b,
+	0x02, 0x15, 0x23, 0x15, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x1d, 0x01, 0x13,
+	0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01,
+	0x25, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x8d,
+	0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8a, 0x03, 0x44, 0x00, 0x35,
+	0x00, 0x00, 0x01, 0x3d, 0x01, 0x23, 0x1d, 0x01, 0x23, 0x15, 0x33, 0x1d,
+	0x02, 0x23, 0x3d, 0x02, 0x2b, 0x02, 0x1d, 0x02, 0x3b, 0x02, 0x15, 0x2b,
+	0x02, 0x35, 0x23, 0x3d, 0x02, 0x33, 0x35, 0x3b, 0x02, 0x3d, 0x01, 0x33,
+	0x35, 0x2b, 0x01, 0x35, 0x3b, 0x03, 0x1d, 0x03, 0x02, 0x2d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01,
+	0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x11, 0x00, 0x15, 0x00, 0x19,
+	0x00, 0x21, 0x00, 0x29, 0x00, 0x31, 0x00, 0x39, 0x00, 0x00, 0x21, 0x3d,
+	0x07, 0x33, 0x1d, 0x07, 0x13, 0x35, 0x33, 0x15, 0x21, 0x35, 0x33, 0x15,
+	0x25, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02, 0x33, 0x1d,
+	0x01, 0x25, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x05, 0x2b, 0x02, 0x35, 0x3b,
+	0x02, 0x01, 0x73, 0x5d, 0xba, 0x5c, 0xfd, 0x77, 0x5d, 0x02, 0x2c, 0x5d,
+	0xfd, 0x1a, 0x5d, 0x5d, 0x02, 0x89, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0xfe, 0x8d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x03, 0x44, 0x00, 0x05,
+	0x00, 0x09, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21,
+	0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x37, 0x00, 0x3d,
+	0x00, 0x43, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x35,
+	0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01,
+	0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x25, 0x23, 0x35, 0x33, 0x05, 0x35,
+	0x33, 0x15, 0x37, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x31, 0x35, 0x33, 0x15,
+	0x21, 0x23, 0x35, 0x33, 0x21, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33,
+	0x25, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0xfe, 0xe9, 0x5d, 0x5d, 0x01,
+	0x17, 0x5c, 0xfe, 0x30, 0x5c, 0x5c, 0x01, 0x74, 0x5d, 0x5d, 0xfe, 0x8c,
+	0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9, 0x5d, 0x5d, 0x01,
+	0x17, 0x5c, 0xfe, 0x30, 0x5c, 0x5c, 0x02, 0x2d, 0x5d, 0x5d, 0x5d, 0xfd,
+	0xd4, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x03, 0x44, 0x00, 0x05,
+	0x00, 0x0b, 0x00, 0x29, 0x00, 0x37, 0x00, 0x3d, 0x00, 0x43, 0x00, 0x00,
+	0x25, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x21, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x21, 0x2b, 0x05, 0x35, 0x33, 0x3d, 0x04, 0x23, 0x35, 0x3b, 0x05, 0x15,
+	0x23, 0x1d, 0x04, 0x33, 0x21, 0x33, 0x3d, 0x04, 0x2b, 0x01, 0x1d, 0x04,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x03, 0x43, 0x5d, 0x5d, 0x5d, 0xfd, 0x77, 0x5d, 0x5d, 0x5d, 0x02, 0xe6,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5c, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d,
+	0x5d, 0x5d, 0xfd, 0xd4, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x02, 0x89, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0xfd, 0x03, 0x44, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x15,
+	0x00, 0x1b, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x31, 0x00, 0x37,
+	0x00, 0x3d, 0x00, 0x43, 0x00, 0x49, 0x00, 0x4f, 0x00, 0x5b, 0x00, 0x00,
+	0x01, 0x35, 0x33, 0x15, 0x01, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x3b, 0x02,
+	0x25, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15,
+	0x05, 0x23, 0x35, 0x33, 0x25, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x21, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23,
+	0x3d, 0x01, 0x33, 0x15, 0x25, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x05, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x21, 0x2b, 0x04, 0x35, 0x3b, 0x04, 0x03, 0xa0,
+	0x5d, 0xfe, 0x8d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x01, 0x16, 0x5d, 0x5d, 0x5d, 0xfe, 0x30, 0x5c, 0x5d, 0x5d, 0x5c, 0x02,
+	0x8a, 0x5d, 0xfe, 0xe9, 0x5c, 0x5c, 0xfd, 0x77, 0x5d, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x1a, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0x5d, 0xfd, 0x76, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x02, 0x2d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00,
+	0xff, 0x46, 0x03, 0x44, 0x03, 0x44, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x15,
+	0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x43,
+	0x00, 0x47, 0x00, 0x4d, 0x00, 0x00, 0x05, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x31, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x25, 0x33, 0x15, 0x2b, 0x02,
+	0x35, 0x3b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02,
+	0x33, 0x1d, 0x01, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x3d, 0x03, 0x33,
+	0x1d, 0x02, 0x21, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x33, 0x1d,
+	0x01, 0x25, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x37, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x02, 0xe6, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xfe, 0x8c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0xb9, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x02,
+	0x2d, 0x5c, 0x5d, 0xfe, 0x30, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x01,
+	0x73, 0x5c, 0x5c, 0xfe, 0x8d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5c, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x0a, 0x00, 0x00,
+	0xff, 0x46, 0x05, 0x14, 0x03, 0x44, 0x00, 0x1b, 0x00, 0x2d, 0x00, 0x3f,
+	0x00, 0x43, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, 0x63, 0x00, 0x6b,
+	0x00, 0x6f, 0x00, 0x00, 0x05, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x3d,
+	0x07, 0x33, 0x1d, 0x06, 0x33, 0x15, 0x23, 0x1d, 0x01, 0x25, 0x3d, 0x07,
+	0x33, 0x1d, 0x07, 0x21, 0x3d, 0x07, 0x33, 0x1d, 0x07, 0x25, 0x35, 0x33,
+	0x15, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x3d, 0x04, 0x33, 0x1d, 0x03, 0x03,
+	0x2b, 0x02, 0x35, 0x3b, 0x02, 0x05, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x05,
+	0x2b, 0x02, 0x35, 0x3b, 0x02, 0x05, 0x23, 0x35, 0x33, 0x03, 0x43, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0xfe, 0x30, 0x5d,
+	0x03, 0x43, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0xfe, 0x8d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xb9, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfd,
+	0x03, 0x44, 0x00, 0x17, 0x00, 0x25, 0x00, 0x33, 0x00, 0x3b, 0x00, 0x43,
+	0x00, 0x47, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b,
+	0x0a, 0x35, 0x3b, 0x08, 0x37, 0x33, 0x15, 0x2b, 0x04, 0x35, 0x33, 0x35,
+	0x33, 0x15, 0x33, 0x05, 0x2b, 0x03, 0x35, 0x3b, 0x02, 0x35, 0x33, 0x15,
+	0x33, 0x15, 0x25, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02,
+	0x33, 0x1d, 0x01, 0x25, 0x23, 0x35, 0x33, 0x05, 0x35, 0x33, 0x15, 0x25,
+	0x2b, 0x02, 0x35, 0x3b, 0x02, 0x03, 0xa0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0xfe, 0x30, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x01, 0x16, 0x5d, 0xfd, 0xd3, 0x5c, 0x5c, 0x01, 0xd0, 0x5c, 0x5c,
+	0xfe, 0x30, 0x5d, 0x01, 0x17, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0xb9, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0xba, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x08, 0x00, 0x00,
+	0xff, 0xa3, 0x05, 0x14, 0x03, 0x44, 0x00, 0x11, 0x00, 0x23, 0x00, 0x29,
+	0x00, 0x37, 0x00, 0x49, 0x00, 0x51, 0x00, 0x59, 0x00, 0x5d, 0x00, 0x00,
+	0x21, 0x3d, 0x07, 0x33, 0x1d, 0x07, 0x21, 0x3d, 0x07, 0x33, 0x1d, 0x07,
+	0x21, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x33, 0x3d, 0x01, 0x23, 0x35, 0x33,
+	0x35, 0x33, 0x15, 0x33, 0x15, 0x23, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x07,
+	0x33, 0x1d, 0x06, 0x03, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x05, 0x2b, 0x02,
+	0x35, 0x3b, 0x02, 0x05, 0x23, 0x35, 0x33, 0x01, 0xd0, 0x5d, 0xfe, 0x30,
+	0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe,
+	0x8d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x02, 0x89, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x03, 0x44, 0x00, 0x03, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x00,
+	0x31, 0x35, 0x33, 0x15, 0x01, 0x35, 0x33, 0x15, 0x37, 0x3d, 0x01, 0x23,
+	0x15, 0x23, 0x15, 0x23, 0x15, 0x23, 0x15, 0x23, 0x15, 0x23, 0x15, 0x23,
+	0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33,
+	0x35, 0x2b, 0x01, 0x35, 0x3b, 0x03, 0x1d, 0x03, 0x21, 0x23, 0x35, 0x33,
+	0x5d, 0x01, 0x73, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0xba, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x0f, 0x00, 0x00,
+	0xff, 0x46, 0x04, 0xb7, 0x03, 0x44, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x11,
+	0x00, 0x15, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2f, 0x00, 0x37,
+	0x00, 0x3f, 0x00, 0x47, 0x00, 0x4f, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x5f,
+	0x00, 0x00, 0x21, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x05, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x25, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x07, 0x35, 0x33, 0x15,
+	0x25, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d, 0x01, 0x33, 0x15,
+	0x37, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x05, 0x23, 0x3d, 0x02, 0x33, 0x1d,
+	0x01, 0x05, 0x3d, 0x02, 0x33, 0x1d, 0x02, 0x2b, 0x01, 0x3d, 0x02, 0x33,
+	0x1d, 0x01, 0x25, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x05, 0x3d, 0x02,
+	0x33, 0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x25, 0x23,
+	0x35, 0x33, 0x05, 0x23, 0x35, 0x33, 0x01, 0x16, 0x5d, 0x01, 0x17, 0x5c,
+	0x5c, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x01, 0x17, 0x5c,
+	0xfe, 0xea, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0xe9, 0x5d,
+	0x5d, 0xfe, 0x30, 0x5d, 0xba, 0x5c, 0x5c, 0x01, 0xd0, 0x5c, 0x5c, 0xfe,
+	0xea, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0xfd, 0xd3,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
+	0x00, 0x5c, 0x03, 0xa0, 0x02, 0x8a, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0d,
+	0x00, 0x13, 0x00, 0x17, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x29, 0x00, 0x2f,
+	0x00, 0x33, 0x00, 0x39, 0x00, 0x3f, 0x00, 0x00, 0x3d, 0x01, 0x33, 0x15,
+	0x03, 0x35, 0x33, 0x15, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x21, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x21, 0x35, 0x33, 0x15, 0x21, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x01, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x21, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x21, 0x35, 0x33, 0x15,
+	0x21, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x05, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x5d, 0x5d, 0x5d, 0x02, 0x89, 0x5d, 0x5d, 0x5c, 0xfe, 0xe9, 0x5d, 0x5d,
+	0x5d, 0x02, 0x2d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe, 0x8c,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x01, 0xd0, 0x5d, 0x5d, 0x5c, 0xfe, 0xe9, 0x5d,
+	0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0xfe,
+	0x8c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x01, 0x73, 0x5d, 0x5d,
+	0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x01,
+	0x17, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe7, 0x03, 0x44, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, 0x2d, 0x00, 0x33, 0x00, 0x39,
+	0x00, 0x3d, 0x00, 0x41, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x21, 0x35,
+	0x33, 0x15, 0x25, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x05, 0x3d, 0x01, 0x33,
+	0x1d, 0x01, 0x25, 0x23, 0x35, 0x2b, 0x01, 0x15, 0x23, 0x35, 0x2b, 0x01,
+	0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x3b,
+	0x01, 0x15, 0x2b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x21, 0x23, 0x3d,
+	0x01, 0x33, 0x15, 0x25, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x02,
+	0x8a, 0x5c, 0xfd, 0x1a, 0x5d, 0x02, 0x2d, 0x5d, 0x5d, 0xfd, 0xd3, 0x5d,
+	0x01, 0x73, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0xfe, 0x30, 0x5d, 0x5d, 0x01,
+	0xd0, 0x5c, 0xfd, 0x77, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xb9, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x2f, 0x00, 0x00, 0x25, 0x33,
+	0x15, 0x2b, 0x04, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x23, 0x15, 0x2b, 0x01,
+	0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35,
+	0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x01, 0x23,
+	0x15, 0x2b, 0x01, 0x35, 0x23, 0x1d, 0x01, 0x02, 0x2d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x00,
+	0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44, 0x02, 0xe7, 0x00, 0x03,
+	0x00, 0x07, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b,
+	0x00, 0x1f, 0x00, 0x27, 0x00, 0x2f, 0x00, 0x37, 0x00, 0x3f, 0x00, 0x00,
+	0x21, 0x35, 0x33, 0x15, 0x03, 0x35, 0x33, 0x15, 0x11, 0x35, 0x33, 0x15,
+	0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33,
+	0x21, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x21, 0x3d, 0x02, 0x33,
+	0x1d, 0x02, 0x21, 0x23, 0x3d, 0x02, 0x33, 0x1d, 0x01, 0x25, 0x2b, 0x02,
+	0x35, 0x3b, 0x02, 0x05, 0x2b, 0x02, 0x35, 0x3b, 0x02, 0x01, 0x73, 0x5d,
+	0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0xba, 0x5d, 0xfe, 0x8c, 0x5c, 0x5c,
+	0x01, 0x74, 0x5c, 0xfd, 0xd4, 0x5d, 0x5d, 0x02, 0x2c, 0x5d, 0xfd, 0x1a,
+	0x5d, 0x5d, 0x02, 0x89, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xfe, 0x8d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x02, 0x2d, 0x5d, 0x5d,
+	0xfe, 0x30, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5c,
+	0x5c, 0x5c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe7,
+	0x03, 0x44, 0x00, 0x29, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x05, 0x35,
+	0x3b, 0x01, 0x35, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x33,
+	0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x15, 0x33, 0x1d, 0x01, 0x33, 0x15,
+	0x33, 0x1d, 0x01, 0x23, 0x15, 0x2b, 0x01, 0x15, 0x02, 0x2d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x02, 0xe7, 0x00, 0x23, 0x00, 0x00, 0x21, 0x35, 0x23, 0x35, 0x23, 0x35,
+	0x23, 0x35, 0x23, 0x3d, 0x02, 0x33, 0x35, 0x3b, 0x02, 0x15, 0x33, 0x35,
+	0x3b, 0x02, 0x15, 0x33, 0x1d, 0x02, 0x23, 0x15, 0x23, 0x15, 0x23, 0x15,
+	0x23, 0x15, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x03, 0x44, 0x00, 0x23, 0x00, 0x00, 0x21, 0x35, 0x23, 0x35, 0x23, 0x35,
+	0x23, 0x35, 0x23, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35, 0x33, 0x35,
+	0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x33, 0x15, 0x23, 0x15,
+	0x23, 0x15, 0x23, 0x15, 0x23, 0x15, 0x01, 0x73, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xd0, 0x02, 0xe7, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x3d,
+	0x01, 0x23, 0x1d, 0x03, 0x23, 0x15, 0x2b, 0x01, 0x3d, 0x01, 0x3b, 0x01,
+	0x3d, 0x05, 0x33, 0x15, 0x33, 0x15, 0x33, 0x1d, 0x02, 0x01, 0x73, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x8a, 0x02, 0xe7, 0x00, 0x2d, 0x00, 0x00, 0x21, 0x23,
+	0x3d, 0x01, 0x3b, 0x01, 0x3d, 0x03, 0x2b, 0x02, 0x1d, 0x04, 0x23, 0x15,
+	0x2b, 0x01, 0x3d, 0x01, 0x3b, 0x01, 0x3d, 0x05, 0x3b, 0x04, 0x1d, 0x06,
+	0x23, 0x15, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfd,
+	0x03, 0x44, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x19,
+	0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x4b, 0x00, 0x51, 0x00, 0x55,
+	0x00, 0x59, 0x00, 0x5f, 0x00, 0x00, 0x37, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x25, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x05, 0x35, 0x33, 0x15, 0x21, 0x23,
+	0x35, 0x33, 0x21, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x05, 0x2b, 0x01, 0x35,
+	0x33, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35, 0x23, 0x35, 0x3b, 0x01,
+	0x15, 0x33, 0x15, 0x33, 0x35, 0x3b, 0x01, 0x15, 0x33, 0x15, 0x23, 0x15,
+	0x2b, 0x01, 0x35, 0x23, 0x15, 0x23, 0x25, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x05, 0x35, 0x23, 0x15, 0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x25, 0x33,
+	0x15, 0x2b, 0x01, 0x35, 0x05, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x3b,
+	0x01, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0xba, 0x5c, 0x5c, 0x5d, 0x03, 0x43,
+	0x5d, 0x5d, 0x5d, 0xfd, 0xd3, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x02, 0xe6,
+	0x5d, 0x5c, 0x5c, 0x5d, 0xfd, 0xd3, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x02, 0x8a, 0x5d, 0x5d, 0x5d, 0xfd, 0xd3, 0x5c, 0x02, 0x2c,
+	0x5d, 0x5d, 0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0xfd, 0xd3, 0x5d, 0xfe,
+	0xea, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0xba, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x01, 0x17, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+	0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfd,
+	0x03, 0x44, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x19,
+	0x00, 0x39, 0x00, 0x3f, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x53,
+	0x00, 0x00, 0x37, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x25, 0x33, 0x15, 0x2b,
+	0x01, 0x35, 0x05, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x21, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x05, 0x2b, 0x01, 0x35, 0x33, 0x35, 0x33, 0x35,
+	0x23, 0x35, 0x23, 0x35, 0x3b, 0x01, 0x15, 0x33, 0x15, 0x33, 0x35, 0x3b,
+	0x01, 0x15, 0x23, 0x15, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x23, 0x15, 0x23,
+	0x01, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x25, 0x33, 0x15, 0x2b, 0x01, 0x35,
+	0x05, 0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x3b, 0x01, 0x2b, 0x01, 0x35,
+	0x3b, 0x01, 0xba, 0x5c, 0x5c, 0x5d, 0x03, 0x43, 0x5d, 0x5d, 0x5d, 0xfd,
+	0xd3, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x02, 0xe6, 0x5d, 0x5c, 0x5c, 0x5d,
+	0xfd, 0xd3, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d,
+	0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0xfd, 0xd3, 0x5d, 0xfe, 0xea, 0x5d,
+	0x5d, 0xb9, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x74, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0xa0, 0x03, 0x44, 0x00, 0x33, 0x00, 0x00, 0x21, 0x23,
+	0x35, 0x33, 0x3d, 0x01, 0x33, 0x35, 0x2b, 0x02, 0x15, 0x2b, 0x01, 0x35,
+	0x33, 0x35, 0x23, 0x35, 0x3b, 0x01, 0x15, 0x3b, 0x02, 0x35, 0x23, 0x3d,
+	0x01, 0x23, 0x35, 0x3b, 0x01, 0x15, 0x33, 0x1d, 0x01, 0x33, 0x15, 0x3b,
+	0x02, 0x15, 0x2b, 0x02, 0x15, 0x23, 0x1d, 0x01, 0x23, 0x15, 0x01, 0x73,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x5c, 0x03, 0xa0,
+	0x02, 0xe7, 0x00, 0x21, 0x00, 0x51, 0x00, 0x00, 0x25, 0x2b, 0x08, 0x3d,
+	0x06, 0x3b, 0x09, 0x1d, 0x06, 0x27, 0x33, 0x3d, 0x03, 0x23, 0x15, 0x23,
+	0x15, 0x23, 0x35, 0x33, 0x35, 0x33, 0x35, 0x2b, 0x05, 0x15, 0x33, 0x15,
+	0x33, 0x15, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x35, 0x23, 0x35, 0x23, 0x35,
+	0x23, 0x1d, 0x03, 0x3b, 0x05, 0x03, 0x43, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x5d, 0xba, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0xe7, 0x03, 0x44, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x11,
+	0x00, 0x33, 0x00, 0x39, 0x00, 0x45, 0x00, 0x4b, 0x00, 0x51, 0x00, 0x55,
+	0x00, 0x59, 0x00, 0x00, 0x25, 0x33, 0x15, 0x2b, 0x03, 0x35, 0x3b, 0x02,
+	0x35, 0x33, 0x15, 0x21, 0x23, 0x35, 0x33, 0x21, 0x3d, 0x01, 0x2b, 0x01,
+	0x15, 0x33, 0x15, 0x2b, 0x01, 0x35, 0x23, 0x15, 0x23, 0x35, 0x33, 0x35,
+	0x33, 0x3d, 0x01, 0x33, 0x15, 0x3b, 0x01, 0x3d, 0x02, 0x33, 0x1d, 0x05,
+	0x21, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x37, 0x2b, 0x01, 0x3d, 0x03, 0x33,
+	0x1d, 0x02, 0x33, 0x37, 0x3d, 0x01, 0x33, 0x1d, 0x01, 0x2b, 0x01, 0x3d,
+	0x01, 0x33, 0x15, 0x25, 0x23, 0x35, 0x33, 0x05, 0x23, 0x35, 0x33, 0x01,
+	0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0xba, 0x5d, 0xfe, 0x30,
+	0x5d, 0x5d, 0x01, 0xd0, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0xfd, 0x77, 0x5d, 0x5d, 0xb9, 0x5c, 0x5d,
+	0x5d, 0x5c, 0xba, 0x5d, 0xba, 0x5d, 0x5d, 0x01, 0x17, 0x5d, 0x5d, 0xfe,
+	0x8c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5c, 0x03, 0xfd,
+	0x03, 0x44, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x27, 0x00, 0x4f,
+	0x00, 0x00, 0x25, 0x2b, 0x02, 0x35, 0x3b, 0x01, 0x35, 0x33, 0x35, 0x33,
+	0x1d, 0x02, 0x25, 0x2b, 0x01, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x35,
+	0x3b, 0x01, 0x15, 0x23, 0x15, 0x33, 0x25, 0x23, 0x35, 0x33, 0x05, 0x2b,
+	0x01, 0x35, 0x3b, 0x01, 0x21, 0x2b, 0x01, 0x35, 0x2b, 0x01, 0x35, 0x2b,
+	0x01, 0x35, 0x23, 0x15, 0x33, 0x15, 0x3b, 0x01, 0x15, 0x2b, 0x01, 0x35,
+	0x2b, 0x01, 0x15, 0x23, 0x3d, 0x01, 0x33, 0x35, 0x33, 0x35, 0x3b, 0x01,
+	0x15, 0x3b, 0x01, 0x15, 0x3b, 0x01, 0x15, 0x33, 0x03, 0xa0, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0xfd, 0x76, 0x5c,
+	0x5d, 0x5d, 0x5c, 0x02, 0x2d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5c, 0x03, 0xfd,
+	0x03, 0x44, 0x00, 0x15, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x41,
+	0x00, 0x4f, 0x00, 0x00, 0x25, 0x23, 0x35, 0x33, 0x35, 0x3b, 0x01, 0x35,
+	0x3b, 0x01, 0x35, 0x3b, 0x01, 0x15, 0x23, 0x15, 0x2b, 0x01, 0x15, 0x2b,
+	0x01, 0x15, 0x27, 0x23, 0x35, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x3b, 0x01,
+	0x15, 0x23, 0x25, 0x2b, 0x01, 0x35, 0x3b, 0x01, 0x21, 0x35, 0x33, 0x15,
+	0x21, 0x2b, 0x01, 0x35, 0x2b, 0x01, 0x15, 0x2b, 0x01, 0x35, 0x3b, 0x01,
+	0x35, 0x3b, 0x01, 0x35, 0x3b, 0x01, 0x15, 0x23, 0x15, 0x33, 0x21, 0x35,
+	0x23, 0x35, 0x2b, 0x01, 0x35, 0x3b, 0x03, 0x1d, 0x02, 0x01, 0x16, 0x5c,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0xb9, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x01, 0x16, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x01, 0x73, 0x5d, 0xfe, 0xea, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff, 0xa3, 0x02, 0xe7,
+	0x03, 0x44, 0x00, 0x23, 0x00, 0x3b, 0x00, 0x00, 0x05, 0x2b, 0x01, 0x35,
+	0x23, 0x3d, 0x02, 0x23, 0x35, 0x23, 0x3d, 0x02, 0x3b, 0x01, 0x3d, 0x01,
+	0x3b, 0x02, 0x1d, 0x01, 0x3b, 0x01, 0x15, 0x33, 0x1d, 0x02, 0x2b, 0x01,
+	0x1d, 0x03, 0x27, 0x3d, 0x03, 0x3b, 0x01, 0x35, 0x2b, 0x01, 0x3d, 0x01,
+	0x23, 0x1d, 0x01, 0x2b, 0x01, 0x15, 0x3b, 0x01, 0x1d, 0x03, 0x01, 0xd0,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5c, 0x5c, 0x5d, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0xba, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c,
+	0x5d, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44,
+	0x03, 0xa0, 0x00, 0x03, 0x00, 0x23, 0x00, 0x29, 0x00, 0x2f, 0x00, 0x4f,
+	0x00, 0x53, 0x00, 0x00, 0x21, 0x35, 0x33, 0x15, 0x3d, 0x01, 0x23, 0x15,
+	0x23, 0x35, 0x2b, 0x02, 0x3d, 0x01, 0x33, 0x15, 0x33, 0x35, 0x33, 0x15,
+	0x3b, 0x02, 0x35, 0x33, 0x15, 0x33, 0x35, 0x33, 0x1d, 0x01, 0x2b, 0x02,
+	0x15, 0x13, 0x23, 0x3d, 0x01, 0x33, 0x15, 0x05, 0x23, 0x3d, 0x01, 0x33,
+	0x15, 0x25, 0x35, 0x23, 0x15, 0x23, 0x35, 0x2b, 0x02, 0x15, 0x23, 0x35,
+	0x23, 0x15, 0x23, 0x3d, 0x01, 0x3b, 0x02, 0x35, 0x33, 0x15, 0x33, 0x35,
+	0x33, 0x15, 0x3b, 0x02, 0x1d, 0x01, 0x01, 0x23, 0x35, 0x33, 0x01, 0x73,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0x5d, 0xb9, 0x5c, 0x5c, 0xfd, 0xd4, 0x5d,
+	0x5d, 0x02, 0x2c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0xfe, 0x8d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x01, 0x16, 0x5d, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0x44, 0x03, 0x44, 0x00, 0x25, 0x00, 0x31, 0x00, 0x00,
+	0x21, 0x23, 0x35, 0x23, 0x15, 0x2b, 0x01, 0x35, 0x23, 0x3d, 0x02, 0x23,
+	0x3d, 0x01, 0x33, 0x35, 0x3b, 0x01, 0x3d, 0x01, 0x3b, 0x02, 0x1d, 0x01,
+	0x3b, 0x01, 0x15, 0x33, 0x1d, 0x01, 0x23, 0x1d, 0x02, 0x23, 0x15, 0x03,
+	0x35, 0x33, 0x35, 0x23, 0x35, 0x23, 0x15, 0x23, 0x15, 0x33, 0x15, 0x02,
+	0x2d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5c, 0xba, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d,
+	0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5d, 0x5d, 0x01, 0x16, 0x5d, 0x5d,
+	0x5d, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xae, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x32, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x50, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x15, 0x00, 0x83, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0f, 0x00, 0xb9, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0xeb, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x01, 0x1c, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x01, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x42, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x03, 0x00, 0x2a, 0x00, 0x57, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x99, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x05, 0x00, 0x20, 0x00, 0xc9, 0x00, 0x03,
+	0x00, 0x01, 0x04, 0x09, 0x00, 0x06, 0x00, 0x1e, 0x00, 0xfc, 0x00, 0x4f,
+	0x00, 0x6f, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6b, 0x00, 0x00, 0x4f, 0x6f,
+	0x6d, 0x65, 0x6b, 0x00, 0x00, 0x41, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72,
+	0x00, 0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x4d, 0x00, 0x6f, 0x00, 0x64,
+	0x00, 0x65, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x73, 0x00, 0x00,
+	0x41, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x50,
+	0x6c, 0x75, 0x73, 0x00, 0x00, 0x4d, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69,
+	0x00, 0x75, 0x00, 0x6d, 0x00, 0x00, 0x4d, 0x65, 0x64, 0x69, 0x75, 0x6d,
+	0x00, 0x00, 0x4f, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6b, 0x00,
+	0x3a, 0x00, 0x41, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00,
+	0x63, 0x00, 0x74, 0x00, 0x4d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00,
+	0x50, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x73, 0x00, 0x00, 0x4f, 0x6f, 0x6d,
+	0x65, 0x6b, 0x3a, 0x41, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x4d, 0x6f,
+	0x64, 0x65, 0x50, 0x6c, 0x75, 0x73, 0x00, 0x00, 0x41, 0x00, 0x74, 0x00,
+	0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x4d, 0x00,
+	0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x75, 0x00,
+	0x73, 0x00, 0x00, 0x41, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x4d, 0x6f,
+	0x64, 0x65, 0x50, 0x6c, 0x75, 0x73, 0x00, 0x00, 0x56, 0x00, 0x65, 0x00,
+	0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
+	0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x30, 0x00,
+	0x30, 0x00, 0x20, 0x00, 0x00, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x20, 0x30, 0x30, 0x31, 0x2e, 0x30, 0x30, 0x30, 0x20, 0x00, 0x00, 0x41,
+	0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x63, 0x00, 0x74,
+	0x00, 0x4d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x50, 0x00, 0x6c,
+	0x00, 0x75, 0x00, 0x73, 0x00, 0x00, 0x41, 0x74, 0x74, 0x72, 0x61, 0x63,
+	0x74, 0x4d, 0x6f, 0x64, 0x65, 0x50, 0x6c, 0x75, 0x73, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x33,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
+	0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c,
+	0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12,
+	0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18,
+	0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e,
+	0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24,
+	0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, 0x28, 0x00, 0x29, 0x00, 0x2a,
+	0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00, 0x30,
+	0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36,
+	0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c,
+	0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42,
+	0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x48,
+	0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00, 0x4c, 0x00, 0x4d, 0x00, 0x4e,
+	0x00, 0x4f, 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54,
+	0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a,
+	0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x60,
+	0x00, 0x61, 0x01, 0x02, 0x00, 0xa3, 0x00, 0x84, 0x00, 0x85, 0x00, 0xbd,
+	0x00, 0x96, 0x00, 0xe8, 0x00, 0x86, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x9d,
+	0x00, 0xa9, 0x00, 0xa4, 0x00, 0x8a, 0x00, 0xda, 0x00, 0x83, 0x00, 0x93,
+	0x01, 0x03, 0x01, 0x04, 0x00, 0x8d, 0x01, 0x05, 0x00, 0x88, 0x00, 0xc3,
+	0x00, 0xde, 0x01, 0x06, 0x00, 0x9e, 0x00, 0xaa, 0x00, 0xf5, 0x00, 0xf4,
+	0x00, 0xf6, 0x00, 0xa2, 0x00, 0xad, 0x00, 0xc9, 0x00, 0xc7, 0x00, 0xae,
+	0x00, 0x62, 0x00, 0x63, 0x00, 0x90, 0x00, 0x64, 0x00, 0xcb, 0x00, 0x65,
+	0x00, 0xc8, 0x00, 0xca, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce,
+	0x00, 0xe9, 0x00, 0x66, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xaf,
+	0x00, 0x67, 0x00, 0xf0, 0x00, 0x91, 0x00, 0xd6, 0x00, 0xd4, 0x00, 0xd5,
+	0x00, 0x68, 0x00, 0xeb, 0x00, 0xed, 0x00, 0x89, 0x00, 0x6a, 0x00, 0x69,
+	0x00, 0x6b, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x6e, 0x00, 0xa0, 0x00, 0x6f,
+	0x00, 0x71, 0x00, 0x70, 0x00, 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x74,
+	0x00, 0x76, 0x00, 0x77, 0x00, 0xea, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x79,
+	0x00, 0x7b, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0xb8, 0x00, 0xa1, 0x00, 0x7f,
+	0x00, 0x7e, 0x00, 0x80, 0x00, 0x81, 0x00, 0xec, 0x00, 0xee, 0x00, 0xba,
+	0x01, 0x07, 0x01, 0x08, 0x01, 0x09, 0x01, 0x0a, 0x01, 0x0b, 0x01, 0x0c,
+	0x00, 0xfd, 0x00, 0xfe, 0x01, 0x0d, 0x01, 0x0e, 0x01, 0x0f, 0x01, 0x10,
+	0x00, 0xff, 0x01, 0x00, 0x01, 0x11, 0x01, 0x12, 0x01, 0x13, 0x01, 0x01,
+	0x01, 0x14, 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x01, 0x18, 0x01, 0x19,
+	0x01, 0x1a, 0x01, 0x1b, 0x01, 0x1c, 0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1f,
+	0x00, 0xf8, 0x00, 0xf9, 0x01, 0x20, 0x01, 0x21, 0x01, 0x22, 0x01, 0x23,
+	0x01, 0x24, 0x01, 0x25, 0x01, 0x26, 0x01, 0x27, 0x01, 0x28, 0x01, 0x29,
+	0x01, 0x2a, 0x01, 0x2b, 0x01, 0x2c, 0x01, 0x2d, 0x01, 0x2e, 0x01, 0x2f,
+	0x00, 0xfa, 0x00, 0xd7, 0x01, 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x33,
+	0x01, 0x34, 0x01, 0x35, 0x01, 0x36, 0x01, 0x37, 0x01, 0x38, 0x01, 0x39,
+	0x01, 0x3a, 0x01, 0x3b, 0x01, 0x3c, 0x01, 0x3d, 0x01, 0x3e, 0x00, 0xe2,
+	0x00, 0xe3, 0x01, 0x3f, 0x01, 0x40, 0x01, 0x41, 0x01, 0x42, 0x01, 0x43,
+	0x01, 0x44, 0x01, 0x45, 0x01, 0x46, 0x01, 0x47, 0x01, 0x48, 0x01, 0x49,
+	0x01, 0x4a, 0x01, 0x4b, 0x01, 0x4c, 0x01, 0x4d, 0x00, 0xb0, 0x00, 0xb1,
+	0x01, 0x4e, 0x01, 0x4f, 0x01, 0x50, 0x01, 0x51, 0x01, 0x52, 0x01, 0x53,
+	0x01, 0x54, 0x01, 0x55, 0x01, 0x56, 0x01, 0x57, 0x00, 0xfb, 0x00, 0xfc,
+	0x00, 0xe4, 0x00, 0xe5, 0x01, 0x58, 0x01, 0x59, 0x01, 0x5a, 0x01, 0x5b,
+	0x01, 0x5c, 0x01, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x61,
+	0x01, 0x62, 0x01, 0x63, 0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67,
+	0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x6c, 0x01, 0x6d,
+	0x00, 0xbb, 0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x00, 0xe6,
+	0x00, 0xe7, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0x87,
+	0x00, 0xab, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, 0x00, 0x8c,
+	0x01, 0x76, 0x01, 0x77, 0x01, 0x78, 0x01, 0x79, 0x00, 0x92, 0x01, 0x7a,
+	0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80,
+	0x01, 0x81, 0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86,
+	0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, 0x01, 0x8c,
+	0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92,
+	0x01, 0x93, 0x01, 0x94, 0x01, 0x95, 0x01, 0x96, 0x01, 0x97, 0x01, 0x98,
+	0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x9d, 0x01, 0x9e,
+	0x01, 0x9f, 0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4,
+	0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0xa8, 0x01, 0xa9, 0x07, 0x75,
+	0x6e, 0x69, 0x30, 0x30, 0x41, 0x30, 0x07, 0x75, 0x6e, 0x69, 0x30, 0x30,
+	0x42, 0x32, 0x07, 0x75, 0x6e, 0x69, 0x30, 0x30, 0x42, 0x33, 0x07, 0x75,
+	0x6e, 0x69, 0x30, 0x30, 0x42, 0x35, 0x07, 0x75, 0x6e, 0x69, 0x30, 0x30,
+	0x42, 0x39, 0x07, 0x41, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x61,
+	0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06, 0x41, 0x62, 0x72, 0x65, 0x76,
+	0x65, 0x06, 0x61, 0x62, 0x72, 0x65, 0x76, 0x65, 0x07, 0x41, 0x6f, 0x67,
+	0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x61, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b,
+	0x0b, 0x43, 0x63, 0x69, 0x72, 0x63, 0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78,
+	0x0b, 0x63, 0x63, 0x69, 0x72, 0x63, 0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78,
+	0x0a, 0x43, 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0a,
+	0x63, 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x44,
+	0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x64, 0x63, 0x61, 0x72, 0x6f, 0x6e,
+	0x06, 0x44, 0x63, 0x72, 0x6f, 0x61, 0x74, 0x07, 0x45, 0x6d, 0x61, 0x63,
+	0x72, 0x6f, 0x6e, 0x07, 0x65, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06,
+	0x45, 0x62, 0x72, 0x65, 0x76, 0x65, 0x06, 0x65, 0x62, 0x72, 0x65, 0x76,
+	0x65, 0x0a, 0x45, 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74,
+	0x0a, 0x65, 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07,
+	0x45, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x65, 0x6f, 0x67, 0x6f,
+	0x6e, 0x65, 0x6b, 0x06, 0x45, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x65,
+	0x63, 0x61, 0x72, 0x6f, 0x6e, 0x0b, 0x47, 0x63, 0x69, 0x72, 0x63, 0x75,
+	0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0b, 0x67, 0x63, 0x69, 0x72, 0x63, 0x75,
+	0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0a, 0x47, 0x64, 0x6f, 0x74, 0x61, 0x63,
+	0x63, 0x65, 0x6e, 0x74, 0x0a, 0x67, 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63,
+	0x65, 0x6e, 0x74, 0x0c, 0x47, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63,
+	0x63, 0x65, 0x6e, 0x74, 0x0c, 0x67, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61,
+	0x63, 0x63, 0x65, 0x6e, 0x74, 0x0b, 0x48, 0x63, 0x69, 0x72, 0x63, 0x75,
+	0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0b, 0x68, 0x63, 0x69, 0x72, 0x63, 0x75,
+	0x6d, 0x66, 0x6c, 0x65, 0x78, 0x04, 0x48, 0x62, 0x61, 0x72, 0x04, 0x68,
+	0x62, 0x61, 0x72, 0x06, 0x49, 0x74, 0x69, 0x6c, 0x64, 0x65, 0x06, 0x69,
+	0x74, 0x69, 0x6c, 0x64, 0x65, 0x07, 0x49, 0x6d, 0x61, 0x63, 0x72, 0x6f,
+	0x6e, 0x07, 0x69, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06, 0x49, 0x62,
+	0x72, 0x65, 0x76, 0x65, 0x06, 0x69, 0x62, 0x72, 0x65, 0x76, 0x65, 0x07,
+	0x49, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x69, 0x6f, 0x67, 0x6f,
+	0x6e, 0x65, 0x6b, 0x02, 0x49, 0x4a, 0x02, 0x69, 0x6a, 0x0b, 0x4a, 0x63,
+	0x69, 0x72, 0x63, 0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0b, 0x6a, 0x63,
+	0x69, 0x72, 0x63, 0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0c, 0x4b, 0x63,
+	0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x6b,
+	0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c,
+	0x6b, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x6c, 0x61, 0x6e, 0x64, 0x69, 0x63,
+	0x06, 0x4c, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06, 0x6c, 0x61, 0x63, 0x75,
+	0x74, 0x65, 0x0c, 0x4c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63,
+	0x65, 0x6e, 0x74, 0x0c, 0x6c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63,
+	0x63, 0x65, 0x6e, 0x74, 0x06, 0x4c, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06,
+	0x6c, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x04, 0x4c, 0x64, 0x6f, 0x74, 0x04,
+	0x6c, 0x64, 0x6f, 0x74, 0x06, 0x4e, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06,
+	0x6e, 0x61, 0x63, 0x75, 0x74, 0x65, 0x0c, 0x4e, 0x63, 0x6f, 0x6d, 0x6d,
+	0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x6e, 0x63, 0x6f, 0x6d,
+	0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x4e, 0x63, 0x61,
+	0x72, 0x6f, 0x6e, 0x06, 0x6e, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x0b, 0x6e,
+	0x61, 0x70, 0x6f, 0x73, 0x74, 0x72, 0x6f, 0x70, 0x68, 0x65, 0x03, 0x45,
+	0x6e, 0x67, 0x03, 0x65, 0x6e, 0x67, 0x07, 0x4f, 0x6d, 0x61, 0x63, 0x72,
+	0x6f, 0x6e, 0x07, 0x6f, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06, 0x4f,
+	0x62, 0x72, 0x65, 0x76, 0x65, 0x06, 0x6f, 0x62, 0x72, 0x65, 0x76, 0x65,
+	0x0d, 0x4f, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, 0x6c, 0x61,
+	0x75, 0x74, 0x0d, 0x6f, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d,
+	0x6c, 0x61, 0x75, 0x74, 0x06, 0x52, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06,
+	0x72, 0x61, 0x63, 0x75, 0x74, 0x65, 0x0c, 0x52, 0x63, 0x6f, 0x6d, 0x6d,
+	0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x72, 0x63, 0x6f, 0x6d,
+	0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x52, 0x63, 0x61,
+	0x72, 0x6f, 0x6e, 0x06, 0x72, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x53,
+	0x61, 0x63, 0x75, 0x74, 0x65, 0x06, 0x73, 0x61, 0x63, 0x75, 0x74, 0x65,
+	0x0b, 0x53, 0x63, 0x69, 0x72, 0x63, 0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78,
+	0x0b, 0x73, 0x63, 0x69, 0x72, 0x63, 0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78,
+	0x0c, 0x54, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e,
+	0x74, 0x0c, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65,
+	0x6e, 0x74, 0x06, 0x54, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x74, 0x63,
+	0x61, 0x72, 0x6f, 0x6e, 0x04, 0x54, 0x62, 0x61, 0x72, 0x04, 0x74, 0x62,
+	0x61, 0x72, 0x06, 0x55, 0x74, 0x69, 0x6c, 0x64, 0x65, 0x06, 0x75, 0x74,
+	0x69, 0x6c, 0x64, 0x65, 0x07, 0x55, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e,
+	0x07, 0x75, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06, 0x55, 0x62, 0x72,
+	0x65, 0x76, 0x65, 0x06, 0x75, 0x62, 0x72, 0x65, 0x76, 0x65, 0x05, 0x55,
+	0x72, 0x69, 0x6e, 0x67, 0x05, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x0d, 0x55,
+	0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, 0x6c, 0x61, 0x75, 0x74,
+	0x0d, 0x75, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, 0x6c, 0x61,
+	0x75, 0x74, 0x07, 0x55, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x75,
+	0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x0b, 0x57, 0x63, 0x69, 0x72, 0x63,
+	0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0b, 0x77, 0x63, 0x69, 0x72, 0x63,
+	0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0b, 0x59, 0x63, 0x69, 0x72, 0x63,
+	0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78, 0x0b, 0x79, 0x63, 0x69, 0x72, 0x63,
+	0x75, 0x6d, 0x66, 0x6c, 0x65, 0x78, 0x06, 0x5a, 0x61, 0x63, 0x75, 0x74,
+	0x65, 0x06, 0x7a, 0x61, 0x63, 0x75, 0x74, 0x65, 0x0a, 0x5a, 0x64, 0x6f,
+	0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0a, 0x7a, 0x64, 0x6f, 0x74,
+	0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x6d, 0x69, 0x6e, 0x75, 0x74,
+	0x65, 0x06, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x07, 0x75, 0x6e, 0x69,
+	0x32, 0x30, 0x33, 0x44, 0x04, 0x45, 0x75, 0x72, 0x6f, 0x09, 0x61, 0x72,
+	0x72, 0x6f, 0x77, 0x6c, 0x65, 0x66, 0x74, 0x07, 0x61, 0x72, 0x72, 0x6f,
+	0x77, 0x75, 0x70, 0x0a, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x72, 0x69, 0x67,
+	0x68, 0x74, 0x09, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x64, 0x6f, 0x77, 0x6e,
+	0x07, 0x74, 0x72, 0x69, 0x61, 0x67, 0x75, 0x70, 0x07, 0x74, 0x72, 0x69,
+	0x61, 0x67, 0x72, 0x74, 0x07, 0x74, 0x72, 0x69, 0x61, 0x67, 0x64, 0x6e,
+	0x07, 0x74, 0x72, 0x69, 0x61, 0x67, 0x6c, 0x66, 0x07, 0x75, 0x6e, 0x69,
+	0x32, 0x36, 0x30, 0x32, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x30, 0x33,
+	0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x30, 0x34, 0x07, 0x75, 0x6e, 0x69,
+	0x32, 0x36, 0x30, 0x35, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x30, 0x45,
+	0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x32, 0x30, 0x07, 0x75, 0x6e, 0x69,
+	0x32, 0x36, 0x32, 0x45, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x32, 0x46,
+	0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x33, 0x39, 0x09, 0x73, 0x6d, 0x69,
+	0x6c, 0x65, 0x66, 0x61, 0x63, 0x65, 0x0c, 0x69, 0x6e, 0x76, 0x73, 0x6d,
+	0x69, 0x6c, 0x65, 0x66, 0x61, 0x63, 0x65, 0x03, 0x73, 0x75, 0x6e, 0x06,
+	0x66, 0x65, 0x6d, 0x61, 0x6c, 0x65, 0x04, 0x6d, 0x61, 0x6c, 0x65, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x36, 0x34, 0x38, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x36, 0x34, 0x39, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x34, 0x41, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x36, 0x34, 0x42, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x36, 0x34, 0x43, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x34, 0x44, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x36, 0x34, 0x45, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x36, 0x34, 0x46, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x35, 0x30, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x36, 0x35, 0x31, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x36, 0x35, 0x32, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x35, 0x33, 0x05,
+	0x73, 0x70, 0x61, 0x64, 0x65, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x36, 0x36,
+	0x31, 0x04, 0x63, 0x6c, 0x75, 0x62, 0x05, 0x68, 0x65, 0x61, 0x72, 0x74,
+	0x07, 0x64, 0x69, 0x61, 0x6d, 0x6f, 0x6e, 0x64, 0x0b, 0x6d, 0x75, 0x73,
+	0x69, 0x63, 0x61, 0x6c, 0x6e, 0x6f, 0x74, 0x65, 0x0e, 0x6d, 0x75, 0x73,
+	0x69, 0x63, 0x61, 0x6c, 0x6e, 0x6f, 0x74, 0x65, 0x64, 0x62, 0x6c, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x37, 0x30, 0x32, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x37, 0x30, 0x34, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x37, 0x30, 0x38, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x37, 0x30, 0x39, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x37, 0x30, 0x43, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x37, 0x30, 0x45, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x37, 0x31, 0x30, 0x07, 0x75, 0x6e, 0x69, 0x32,
+	0x37, 0x31, 0x45, 0x07, 0x75, 0x6e, 0x69, 0x32, 0x37, 0x32, 0x31, 0x07,
+	0x75, 0x6e, 0x69, 0x32, 0x37, 0x33, 0x46, 0x07, 0x75, 0x6e, 0x69, 0x33,
+	0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x20,
+	0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x7f, 0x00, 0x01,
+	0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0xc7, 0xfe, 0xb0, 0xdf, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x78, 0x2b, 0x41,
+	0x00, 0x00, 0x00, 0x00, 0xde, 0xe0, 0x84, 0x19
+};
+
+#endif /* ATTRACTPLUS_TTF_H */
diff --git a/3rdparty/switchres/grid.cpp b/3rdparty/switchres/grid.cpp
new file mode 100644
index 00000000000..5f637fbe041
--- /dev/null
+++ b/3rdparty/switchres/grid.cpp
@@ -0,0 +1,410 @@
+/**************************************************************
+
+   grid.cpp - Simple test grid
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2022 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse,
+                         Radek Dutkiewicz
+
+ **************************************************************/
+
+// To add additional text lines:
+// export GRID_TEXT="$(echo -e "\nArrows - screen position, Page Up/Down - H size\n\nH size: 1.0\nH position: 0\nV position: 0")"
+
+// To remove additional lines:
+// unset GRID_TEXT
+
+
+#define SDL_MAIN_HANDLED
+#define NUM_GRIDS 2
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_ttf.h>
+#include "font.h"
+#include <string.h>
+#include <iostream>
+#include <vector>
+#include <algorithm>
+#include <math.h>
+
+#define FONT_SIZE 11
+
+enum GRID_ADJUST
+{
+	LEFT = 64,
+	RIGHT,
+	UP,
+	DOWN,
+	H_SIZE_INC,
+	H_SIZE_DEC
+};
+
+typedef struct grid_display
+{
+	int index;
+	int width;
+	int height;
+
+	SDL_Window *window;
+	SDL_Renderer *renderer;
+	std::vector<SDL_Texture*> textures;
+} GRID_DISPLAY;
+
+SDL_Surface *surface;
+TTF_Font *font;
+std::vector<std::string> grid_texts;
+
+
+//============================================================
+//  draw_grid
+//============================================================
+
+void draw_grid(int num_grid, int width, int height, SDL_Renderer *renderer, std::vector<SDL_Texture*> textures)
+{
+	// Clean the surface
+	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+	SDL_RenderClear(renderer);
+
+	SDL_Rect rect {0, 0, width, height};
+
+	switch (num_grid)
+	{
+		case 0:
+			// 16 x 12 squares
+			{
+				// Fill the screen with red
+				rect = {0, 0, width, height};
+				SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+				SDL_RenderFillRect(renderer, &rect);
+
+				// Draw white rectangle
+				rect = {width / 32, height / 24 , width - width / 16, height - height / 12};
+				SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+				SDL_RenderFillRect(renderer, &rect);
+
+				// Draw grid using black rectangles
+				SDL_Rect rects[16 * 12];
+
+				// Set the thickness of horizontal and vertical lines based on the screen resolution
+				int line_w = round(float(width) / 320.0);
+				int line_h = round(float(height) / 240.0);
+				if ( line_w < 1 ) line_w = 1;
+				if ( line_h < 1 ) line_h = 1;
+
+				float rect_w = (width - line_w * 17) / 16.0;
+				float rect_h = (height - line_h * 13) / 12.0;
+
+				for (int i = 0; i < 16; i++)
+				{
+					int x_pos1 = ceil(i * rect_w);
+					int x_pos2 = ceil((i+1) * rect_w);
+					for (int j = 0; j < 12; j++)
+					{
+						int y_pos1 = ceil(j * rect_h);
+						int y_pos2 = ceil((j+1) * rect_h);
+						rects[i + j * 16] = {x_pos1 + (i+1) * line_w , y_pos1 + (j+1) * line_h, x_pos2 - x_pos1, y_pos2 - y_pos1};
+					}
+				}
+
+				SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+				SDL_RenderFillRects(renderer, rects, 16 * 12);
+			}
+			break;
+
+		case 1:
+			// cps2 grid
+
+			// Draw outer rectangle
+			SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+			SDL_RenderDrawRect(renderer, &rect);
+
+			for (int i = 0;  i < width / 16; i++)
+			{
+				for (int j = 0; j < height / 16; j++)
+				{
+					if (i == 0 || j == 0 || i == (width / 16) - 1 || j == (height / 16) - 1)
+						SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+					else
+						SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+
+					rect = {i * 16, j * 16, 16, 16};
+					SDL_RenderDrawRect(renderer, &rect);
+
+					rect = {i * 16 + 7, j * 16 + 7, 2, 2};
+					SDL_RenderDrawRect(renderer, &rect);
+				}
+			}
+			break;
+	}
+
+	// Compute text scaling factors
+	int text_scale_w = std::max(1, (int)floor(width / 320 + 0.5));
+	int text_scale_h = std::max(1, (int)floor(height / 240 + 0.5));
+
+	SDL_Rect text_frame = {0, 0, 0, 0};
+
+	// Compute text frame size
+	for (SDL_Texture *t : textures)
+	{
+		int texture_width = 0;
+		int texture_height = 0;
+
+		SDL_QueryTexture(t, NULL, NULL, &texture_width, &texture_height);
+
+		text_frame.w = std::max(text_frame.w, texture_width);
+		text_frame.h += texture_height;
+	}
+
+	text_frame.w *= text_scale_w;
+	text_frame.h *= text_scale_h;
+
+	text_frame.w += FONT_SIZE * text_scale_w; // margin x
+	text_frame.h += FONT_SIZE * text_scale_h; // margin y
+
+	text_frame.x = ceil((width - text_frame.w ) / 2);
+	text_frame.y = ceil((height - text_frame.h ) / 2);
+
+	// Draw Text background
+	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+	SDL_RenderFillRect(renderer, &text_frame);
+
+	// Draw Text frame
+	SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+	SDL_RenderDrawRect(renderer, &text_frame);
+
+	// Draw text lines
+	SDL_Rect text_rect = {0, text_frame.y + FONT_SIZE * text_scale_h / 2, 0, 0};
+
+	for (SDL_Texture *t : textures)
+	{
+		int texture_width = 0;
+		int texture_height = 0;
+
+		SDL_QueryTexture(t, NULL, NULL, &texture_width, &texture_height);
+
+		text_rect.w = texture_width *= text_scale_w;
+		text_rect.h = texture_height *= text_scale_h;
+		text_rect.x = ceil((width - text_rect.w ) / 2);
+
+		SDL_RenderCopy(renderer, t, NULL, &text_rect);
+
+		text_rect.y += text_rect.h;
+	}
+
+	SDL_RenderPresent(renderer);
+}
+
+//============================================================
+//  main
+//============================================================
+
+int main(int argc, char **argv)
+{
+	SDL_Window* win_array[10] = {};
+	GRID_DISPLAY display_array[10] = {};
+	int display_total = 0;
+
+	// Initialize SDL
+	if (SDL_Init(SDL_INIT_VIDEO) != 0)
+	{
+		printf("error initializing SDL: %s\n", SDL_GetError());
+		return 1;
+	}
+
+	// Get target displays
+	if (argc > 1)
+	{
+		// Parse command line for display indexes
+		int display_index = 0;
+		int num_displays = SDL_GetNumVideoDisplays();
+
+		for (int arg = 1; arg < argc; arg++)
+		{
+			sscanf(argv[arg], "%d", &display_index);
+
+			if (display_index < 0 || display_index > num_displays - 1)
+			{
+				printf("error, bad display_index: %d\n", display_index);
+				return 1;
+			}
+
+			display_array[display_total].index = display_index;
+			display_total++;
+		}
+	}
+	else
+	{
+		// No display specified, use default
+		display_array[0].index = 0;
+		display_total = 1;
+	}
+
+	// Initialize text
+	TTF_Init();
+	SDL_RWops* font_resource = SDL_RWFromConstMem(ATTRACTPLUS_TTF, (sizeof(ATTRACTPLUS_TTF)) / (sizeof(ATTRACTPLUS_TTF[0])));
+	font = TTF_OpenFontRW(font_resource, 1, FONT_SIZE);
+
+	grid_texts.push_back(" "); // empty line for screen resolution
+
+	char *grid_text = getenv("GRID_TEXT");
+
+	if ( grid_text != NULL )
+	{
+		char* p = strtok(grid_text, "\n");
+		while(p)
+		{
+			grid_texts.push_back(p);
+			p = strtok(NULL, "\n");
+		}
+	}
+
+	// Create windows
+	for (int disp = 0; disp < display_total; disp++)
+	{
+		// Get target display size
+		SDL_DisplayMode dm;
+		SDL_GetCurrentDisplayMode(display_array[disp].index, &dm);
+
+		SDL_ShowCursor(SDL_DISABLE);
+
+		display_array[disp].width = dm.w;
+		display_array[disp].height = dm.h;
+
+		// Create window
+		display_array[disp].window = SDL_CreateWindow("Switchres test grid", SDL_WINDOWPOS_CENTERED_DISPLAY(display_array[disp].index), SDL_WINDOWPOS_CENTERED, dm.w, dm.h, SDL_WINDOW_FULLSCREEN_DESKTOP);
+
+		// Required by Window multi-monitor
+		SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
+
+		// Create renderer
+		display_array[disp].renderer = SDL_CreateRenderer(display_array[disp].window, -1, SDL_RENDERER_ACCELERATED);
+		SDL_RenderPresent(display_array[disp].renderer);
+
+		// Render first text
+		grid_texts[0] = "Mode: " + std::to_string(dm.w) + " x " + std::to_string(dm.h) + " @ " + std::to_string(dm.refresh_rate);
+
+		for ( int i = 0; i < grid_texts.size(); i++ )
+		{
+			surface = TTF_RenderText_Solid(font, grid_texts[i].c_str(), {255, 255, 255});
+			display_array[disp].textures.push_back(SDL_CreateTextureFromSurface(display_array[disp].renderer, surface));
+		}
+
+		// Draw grid
+		draw_grid(0, display_array[disp].width, display_array[disp].height, display_array[disp].renderer, display_array[disp].textures);
+	}
+
+
+
+
+	// Wait for escape key
+	bool close = false;
+	int  num_grid = 0;
+	int return_code = 0;
+	int CTRL_modifier = 0;
+
+	while (!close)
+	{
+		SDL_Event event;
+
+		while (SDL_PollEvent(&event))
+		{
+			switch (event.type)
+			{
+				case SDL_QUIT:
+					close = true;
+					break;
+
+				case SDL_KEYDOWN:
+					if (event.key.keysym.mod & KMOD_LCTRL || event.key.keysym.mod & KMOD_RCTRL)
+						CTRL_modifier = 1<<7;
+
+					switch (event.key.keysym.scancode)
+					{
+						case SDL_SCANCODE_ESCAPE:
+						case SDL_SCANCODE_Q:
+							close = true;
+							return_code = 1;
+							break;
+
+						case SDL_SCANCODE_BACKSPACE:
+						case SDL_SCANCODE_DELETE:
+							close = true;
+							return_code = 2;
+							break;
+
+						case SDL_SCANCODE_R:
+							close = true;
+							return_code = 3;
+							break;
+
+						case SDL_SCANCODE_RETURN:
+						case SDL_SCANCODE_KP_ENTER:
+							close = true;
+							break;
+
+						case SDL_SCANCODE_TAB:
+							num_grid ++;
+							for (int disp = 0; disp < display_total; disp++)
+								draw_grid(num_grid % NUM_GRIDS, display_array[disp].width, display_array[disp].height, display_array[disp].renderer, display_array[disp].textures);
+							break;
+
+						case SDL_SCANCODE_LEFT:
+							close = true;
+							return_code = GRID_ADJUST::LEFT;
+							break;
+
+						case SDL_SCANCODE_RIGHT:
+							close = true;
+							return_code = GRID_ADJUST::RIGHT;
+							break;
+
+						case SDL_SCANCODE_UP:
+							close = true;
+							return_code = GRID_ADJUST::UP;
+							break;
+
+						case SDL_SCANCODE_DOWN:
+							close = true;
+							return_code = GRID_ADJUST::DOWN;
+							break;
+
+						case SDL_SCANCODE_PAGEUP:
+							close = true;
+							return_code = GRID_ADJUST::H_SIZE_INC;
+							break;
+
+						case SDL_SCANCODE_PAGEDOWN:
+							close = true;
+							return_code = GRID_ADJUST::H_SIZE_DEC;
+							break;
+
+						default:
+							break;
+					}
+
+			}
+		}
+	}
+
+	// Destroy font
+	SDL_FreeSurface(surface);
+	TTF_CloseFont(font);
+	TTF_Quit();
+
+	// Destroy all windows
+	for (int disp = 0; disp < display_total; disp++)
+	{
+		for (SDL_Texture *t : display_array[disp].textures)
+			SDL_DestroyTexture(t);
+		SDL_DestroyWindow(display_array[disp].window);
+	}
+
+	SDL_Quit();
+
+	return return_code | CTRL_modifier;
+}
diff --git a/3rdparty/switchres/log.cpp b/3rdparty/switchres/log.cpp
new file mode 100644
index 00000000000..19bc7143404
--- /dev/null
+++ b/3rdparty/switchres/log.cpp
@@ -0,0 +1,78 @@
+/**************************************************************
+
+   log.cpp - Simple logging for Switchres
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include "log.h"
+
+enum log_verbosity { NONE, SR_ERROR, SR_INFO, SR_DEBUG };
+static log_verbosity log_level = SR_INFO;
+
+void log_dummy(const char *, ...) {}
+
+LOG_VERBOSE log_verbose = &log_dummy;
+LOG_INFO log_info = &log_dummy;
+LOG_ERROR log_error = &log_dummy;
+
+/*
+ * These bakup pointers are here to let the user modify the log level at runtime
+ * We can't sadly unify a log function and test the log level to test if it should
+ * output a log, because it would imply frewriting log_ functions with va_args
+ * and wouldn't work with emulators log functions anymore
+ */
+LOG_VERBOSE log_verbose_bak = &log_dummy;
+LOG_INFO log_info_bak = &log_dummy;
+LOG_ERROR log_error_bak = &log_dummy;
+
+
+void set_log_verbose(void *func_ptr)
+{
+	if (log_level >= SR_DEBUG)
+		log_verbose = (LOG_VERBOSE)func_ptr;
+	log_verbose_bak = (LOG_VERBOSE)func_ptr;
+}
+
+void set_log_info(void *func_ptr)
+{
+	if (log_level >= SR_INFO)
+		log_info = (LOG_INFO)func_ptr;
+	log_info_bak = (LOG_INFO)func_ptr;
+}
+
+void set_log_error(void *func_ptr)
+{
+	if (log_level >= SR_ERROR)
+		log_error = (LOG_ERROR)func_ptr;
+	log_error_bak = (LOG_ERROR)func_ptr;
+}
+
+void set_log_verbosity(int level)
+{
+	// Keep the log in the enum bounds
+	if (level < NONE)
+		level = NONE;
+	if(level > SR_DEBUG)
+		level = SR_DEBUG;
+
+	log_error = &log_dummy;
+	log_info = &log_dummy;
+	log_verbose = &log_dummy;
+
+	if (level >= SR_ERROR)
+		log_error = log_error_bak;
+
+	if (level >= SR_INFO)
+		log_info = log_info_bak;
+
+	if (level >= SR_DEBUG)
+		log_verbose = log_verbose_bak;
+}
diff --git a/3rdparty/switchres/log.h b/3rdparty/switchres/log.h
new file mode 100644
index 00000000000..8ac475a02d7
--- /dev/null
+++ b/3rdparty/switchres/log.h
@@ -0,0 +1,38 @@
+/**************************************************************
+
+   log.h - Simple logging for Switchres
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __LOG__
+#define __LOG__
+
+#if defined(__GNUC__)
+#define ATTR_PRINTF(x,y)        __attribute__((format(printf, x, y)))
+#else
+#define ATTR_PRINTF(x,y)
+#endif
+
+typedef void (*LOG_VERBOSE)(const char *format, ...) ATTR_PRINTF(1,2);
+extern LOG_VERBOSE log_verbose;
+
+typedef void (*LOG_INFO)(const char *format, ...) ATTR_PRINTF(1,2);
+extern LOG_INFO log_info;
+
+typedef void (*LOG_ERROR)(const char *format, ...) ATTR_PRINTF(1,2);
+extern LOG_ERROR log_error;
+
+void set_log_verbosity(int);
+void set_log_verbose(void *func_ptr);
+void set_log_info(void *func_ptr);
+void set_log_error(void *func_ptr);
+
+#endif
diff --git a/3rdparty/switchres/makefile b/3rdparty/switchres/makefile
new file mode 100644
index 00000000000..6811ca43fdc
--- /dev/null
+++ b/3rdparty/switchres/makefile
@@ -0,0 +1,162 @@
+PLATFORM := $(shell uname)
+
+MAIN = switchres_main
+STANDALONE = switchres
+TARGET_LIB = libswitchres
+DRMHOOK_LIB = libdrmhook
+GRID = grid
+SRC = monitor.cpp modeline.cpp switchres.cpp display.cpp custom_video.cpp log.cpp switchres_wrapper.cpp edid.cpp
+OBJS = $(SRC:.cpp=.o)
+
+CROSS_COMPILE ?=
+CXX ?= g++
+AR ?= ar
+LDFLAGS = -shared
+FINAL_CXX=$(CROSS_COMPILE)$(CXX)
+FINAL_AR=$(CROSS_COMPILE)$(AR)
+CPPFLAGS = -O3 -Wall -Wextra
+
+PKG_CONFIG=pkg-config
+INSTALL=install
+LN=ln
+
+DESTDIR ?=
+PREFIX ?= /usr
+INCDIR = $(DESTDIR)$(PREFIX)/include
+LIBDIR = $(DESTDIR)$(PREFIX)/lib
+BINDIR = $(DESTDIR)$(PREFIX)/bin
+PKGDIR = $(LIBDIR)/pkgconfig
+
+ifneq ($(DEBUG),)
+    CPPFLAGS += -g
+endif
+
+# If the version is not set at make, read it from switchres.h
+ifeq ($(VERSION),)
+	VERSION:=$(shell grep -E "^\#define SWITCHRES_VERSION" switchres.h | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" )
+else
+    CPPFLAGS += -DSWITCHRES_VERSION="\"$(VERSION)\""
+endif
+VERSION_MAJOR := $(firstword $(subst ., ,$(VERSION)))
+VERSION_MINOR := $(word 2,$(subst ., ,$(VERSION)))
+VERSION_PATCH := $(word 3,$(subst ., ,$(VERSION)))
+
+$(info Switchres $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH))
+
+# Linux
+ifeq  ($(PLATFORM),Linux)
+SRC += display_linux.cpp
+
+HAS_VALID_XRANDR := $(shell $(PKG_CONFIG) --silence-errors --libs xrandr; echo $$?)
+ifeq ($(HAS_VALID_XRANDR),1)
+    $(info Switchres needs xrandr. X support is disabled)
+else
+    $(info X support enabled)
+    CPPFLAGS += -DSR_WITH_XRANDR
+    SRC += custom_video_xrandr.cpp
+endif
+
+HAS_VALID_DRMKMS := $(shell $(PKG_CONFIG) --silence-errors --libs "libdrm >= 2.4.98"; echo $$?)
+ifeq ($(HAS_VALID_DRMKMS),1)
+    $(info Switchres needs libdrm >= 2.4.98. KMS support is disabled)
+else
+    $(info KMS support enabled)
+    CPPFLAGS += -DSR_WITH_KMSDRM
+    EXTRA_LIBS = libdrm
+    SRC += custom_video_drmkms.cpp
+    ifeq ($(SR_WITH_DRMHOOK),1)
+        CPPFLAGS += -DSR_WITH_DRMHOOK
+    endif
+endif
+
+# SDL2 misses a test for drm as drm.h is required
+HAS_VALID_SDL2 := $(shell $(PKG_CONFIG) --silence-errors --libs "sdl2 >= 2.0.16"; echo $$?)
+ifeq ($(HAS_VALID_SDL2),1)
+    $(info Switchres needs SDL2 >= 2.0.16. SDL2 support is disabled)
+else
+    $(info SDL2 support enabled)
+    CPPFLAGS += -DSR_WITH_SDL2 $(pkg-config --cflags sdl2)
+    EXTRA_LIBS += sdl2
+    SRC += display_sdl2.cpp
+endif
+
+ifneq (,$(EXTRA_LIBS))
+CPPFLAGS += $(shell $(PKG_CONFIG) --cflags $(EXTRA_LIBS))
+LIBS += $(shell $(PKG_CONFIG) --libs $(EXTRA_LIBS))
+endif
+
+CPPFLAGS += -fPIC
+LIBS += -ldl
+
+REMOVE = rm -f
+STATIC_LIB_EXT = a
+DYNAMIC_LIB_EXT = so.$(VERSION)
+LINKER_NAME := $(TARGET_LIB).so
+REAL_SO_NAME := $(LINKER_NAME).$(VERSION)
+SO_NAME := $(LINKER_NAME).$(VERSION_MAJOR)
+LIB_CPPFLAGS := -Wl,-soname,$(SO_NAME)
+# Windows
+else ifneq (,$(findstring NT,$(PLATFORM)))
+SRC += display_windows.cpp custom_video_ati_family.cpp custom_video_ati.cpp custom_video_adl.cpp custom_video_pstrip.cpp resync_windows.cpp
+WIN_ONLY_FLAGS = -static-libgcc -static-libstdc++
+CPPFLAGS += -static $(WIN_ONLY_FLAGS)
+LIBS =
+#REMOVE = del /f
+REMOVE = rm -f
+STATIC_LIB_EXT = lib
+DYNAMIC_LIB_EXT = dll
+endif
+
+define SR_PKG_CONFIG
+prefix=$(PREFIX)
+exec_prefix=$${prefix}
+includedir=$${prefix}/include
+libdir=$${exec_prefix}/lib
+
+Name: libswitchres
+Description: A modeline generator for CRT monitors
+Version: $(VERSION)
+Cflags: -I$${includedir}/switchres
+Libs: -L$${libdir} -ldl -lswitchres
+endef
+
+
+%.o : %.cpp
+	$(FINAL_CXX) -c $(CPPFLAGS) $< -o $@
+
+all: $(SRC:.cpp=.o) $(MAIN).cpp $(TARGET_LIB) prepare_pkg_config
+	@echo $(OSFLAG)
+	$(FINAL_CXX) $(CPPFLAGS) $(CXXFLAGS) $(SRC:.cpp=.o) $(MAIN).cpp $(LIBS) -o $(STANDALONE)
+
+$(TARGET_LIB): $(OBJS)
+	$(FINAL_CXX) $(LDFLAGS) $(CPPFLAGS) $(LIB_CPPFLAGS) -o $@.$(DYNAMIC_LIB_EXT) $^
+	$(FINAL_CXX) -c $(CPPFLAGS) -DSR_WIN32_STATIC switchres_wrapper.cpp -o switchres_wrapper.o
+	$(FINAL_AR) rcs $@.$(STATIC_LIB_EXT) $(^)
+
+$(DRMHOOK_LIB):
+	$(FINAL_CXX) drm_hook.cpp -shared -ldl -fPIC -I/usr/include/libdrm  -o libdrmhook.so
+
+$(GRID):
+	$(FINAL_CXX) grid.cpp $(WIN_ONLY_FLAGS) -lSDL2 -lSDL2_ttf -o grid
+
+clean:
+	$(REMOVE) $(OBJS) $(STANDALONE) $(TARGET_LIB).*
+	$(REMOVE) switchres.pc
+
+prepare_pkg_config:
+	$(file > switchres.pc,$(SR_PKG_CONFIG))
+
+install:
+	$(INSTALL) -Dm644 $(TARGET_LIB).$(DYNAMIC_LIB_EXT) $(LIBDIR)/$(TARGET_LIB).$(DYNAMIC_LIB_EXT)
+	$(INSTALL) -Dm644 switchres_defines.h $(INCDIR)/switchres/switchres_defines.h
+	$(INSTALL) -Dm644 switchres_wrapper.h $(INCDIR)/switchres/switchres_wrapper.h
+	$(INSTALL) -Dm644 switchres.h $(INCDIR)/switchres/switchres.h
+	$(INSTALL) -Dm644 switchres.pc $(PKGDIR)/switchres.pc
+ifneq ($(SO_NAME),)
+	$(LN) -s -f $(REAL_SO_NAME) $(LIBDIR)/$(SO_NAME)
+	$(LN) -s -f $(SO_NAME) $(LIBDIR)/$(LINKER_NAME)
+endif
+
+uninstall:
+	$(REMOVE) $(LIBDIR)/$(TARGET_LIB).*
+	$(REMOVE) $(PKGDIR)/switchres.pc
diff --git a/3rdparty/switchres/modeline.cpp b/3rdparty/switchres/modeline.cpp
new file mode 100644
index 00000000000..7479b91435a
--- /dev/null
+++ b/3rdparty/switchres/modeline.cpp
@@ -0,0 +1,917 @@
+/**************************************************************
+
+   modeline.cpp - Modeline generation and scoring routines
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <cstddef>
+#include "modeline.h"
+#include "log.h"
+
+#define max(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a > _b ? _a : _b; })
+#define min(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a < _b ? _a : _b; })
+
+
+//============================================================
+//  PROTOTYPES
+//============================================================
+
+int get_line_params(modeline *mode, monitor_range *range, int char_size);
+int scale_into_range (int value, int lower_limit, int higher_limit);
+int scale_into_range (double value, double lower_limit, double higher_limit);
+int scale_into_aspect (int source_res, int tot_res, double original_monitor_aspect, double users_monitor_aspect, double *best_diff);
+int stretch_into_range(double vfreq, monitor_range *range, double borders, bool interlace_allowed, double *interlace);
+int total_lines_for_yres(int yres, double vfreq, monitor_range *range, double borders, double interlace);
+double max_vfreq_for_yres (int yres, monitor_range *range, double borders, double interlace);
+
+//============================================================
+//  modeline_create
+//============================================================
+
+int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, generator_settings *cs)
+{
+	double vfreq_real = 0;
+	double interlace = 1;
+	double doublescan = 1;
+	double scan_factor = 1;
+	int x_scale = 0;
+	int y_scale = 0;
+	int v_scale = 0;
+	double x_fscale = 0;
+	double y_fscale = 0;
+	double v_fscale = 0;
+	double x_diff = 0;
+	double y_diff = 0;
+	double v_diff = 0;
+	double y_ratio = 0;
+	double borders = 0;
+	int rotation = s_mode->type & MODE_ROTATED;
+	double source_aspect = rotation? 1.0 / (STANDARD_CRT_ASPECT) : (STANDARD_CRT_ASPECT);
+	t_mode->result.weight = 0;
+
+	// ≈≈≈ Vertical refresh ≈≈≈
+	// try to fit vertical frequency into current range
+	v_scale = scale_into_range(t_mode->vfreq, range->vfreq_min, range->vfreq_max);
+
+	if (!v_scale && (t_mode->type & V_FREQ_EDITABLE))
+	{
+		t_mode->vfreq = t_mode->vfreq < range->vfreq_min? range->vfreq_min : range->vfreq_max;
+		v_scale = 1;
+	}
+	else if (v_scale != 1 && !(t_mode->type & V_FREQ_EDITABLE))
+	{
+		t_mode->result.weight |= R_OUT_OF_RANGE;
+		return -1;
+	}
+
+	// ≈≈≈ Vertical resolution ≈≈≈
+	// try to fit active lines in the progressive range first
+	if (range->progressive_lines_min && (!t_mode->interlace || (t_mode->type & SCAN_EDITABLE)))
+		y_scale = scale_into_range(t_mode->vactive, range->progressive_lines_min, range->progressive_lines_max);
+
+	// if not possible, try to fit in the interlaced range, if any
+	if (!y_scale && range->interlaced_lines_min && cs->interlace && (t_mode->interlace || (t_mode->type & SCAN_EDITABLE)))
+	{
+		y_scale = scale_into_range(t_mode->vactive, range->interlaced_lines_min, range->interlaced_lines_max);
+		interlace = 2;
+	}
+
+	// if we succeeded, let's see if we can apply integer scaling
+	if (y_scale == 1 || (y_scale > 1 && (t_mode->type & Y_RES_EDITABLE)))
+	{
+		// check if we should apply doublescan
+		if (cs->doublescan && y_scale % 2 == 0)
+		{
+			y_scale /= 2;
+			doublescan = 0.5;
+		}
+		scan_factor = interlace * doublescan;
+
+		// Calculate top border in case of multi-standard consumer TVs
+		if (cs->v_shift_correct)
+			borders = (range->progressive_lines_max - t_mode->vactive * y_scale / interlace) * (1.0 / range->hfreq_min) / 2;
+
+		// calculate expected achievable refresh for this height
+		vfreq_real = min(t_mode->vfreq * v_scale, max_vfreq_for_yres(t_mode->vactive * y_scale, range, borders, scan_factor));
+		if (vfreq_real != t_mode->vfreq * v_scale && !(t_mode->type & V_FREQ_EDITABLE))
+		{
+			t_mode->result.weight |= R_OUT_OF_RANGE;
+			return -1;
+		}
+
+		// calculate the ratio that our scaled yres represents with respect to the original height
+		y_ratio = double(t_mode->vactive) * y_scale / s_mode->vactive;
+		int y_source_scaled = s_mode->vactive * floor(y_ratio);
+
+		// if our original height doesn't fit the target height, we're forced to stretch
+		if (!y_source_scaled)
+			t_mode->result.weight |= R_RES_STRETCH;
+
+		// otherwise we try to perform integer scaling
+		else
+		{
+			// exclude lcd ranges from raw border computation
+			if (t_mode->type & V_FREQ_EDITABLE && range->progressive_lines_max - range->progressive_lines_min > 0)
+			{
+				// calculate y borders considering physical lines (instead of logical resolution)
+				int tot_yres = total_lines_for_yres(t_mode->vactive * y_scale, vfreq_real, range, borders, scan_factor);
+				int tot_source = total_lines_for_yres(y_source_scaled, t_mode->vfreq * v_scale, range, borders, scan_factor);
+				y_diff = tot_yres > tot_source?double(tot_yres % tot_source) / tot_yres * 100:0;
+
+				// we penalize for the logical lines we need to add in order to meet the user's lower active lines limit
+				int y_min = interlace == 2?range->interlaced_lines_min:range->progressive_lines_min;
+				int tot_rest = (y_min >= y_source_scaled / doublescan)? y_min % int(y_source_scaled / doublescan):0;
+				y_diff += double(tot_rest) / tot_yres * 100;
+			}
+			else
+				y_diff = double((t_mode->vactive * y_scale) % y_source_scaled) / (t_mode->vactive * y_scale) * 100;
+
+			// we save the integer ratio between source and target resolutions, this will be used for prescaling
+			y_scale = floor(y_ratio);
+
+			// now if the borders obtained are low enough (< 10%) we'll finally apply integer scaling
+			// otherwise we'll stretch the original resolution over the target one
+			if (!(y_ratio >= 1.0 && y_ratio < 16.0 && y_diff < 10.0))
+				t_mode->result.weight |= R_RES_STRETCH;
+		}
+	}
+
+	// otherwise, check if we're allowed to apply fractional scaling
+	else if (t_mode->type & Y_RES_EDITABLE)
+		t_mode->result.weight |= R_RES_STRETCH;
+
+	// if there's nothing we can do, we're out of range
+	else
+	{
+		t_mode->result.weight |= R_OUT_OF_RANGE;
+		return -1;
+	}
+
+	// ≈≈≈ Horizontal resolution ≈≈≈
+	// make the best possible adjustment of xres depending on what happened in the previous steps
+	// let's start with the SCALED case
+	if (!(t_mode->result.weight & R_RES_STRETCH))
+	{
+		// apply integer scaling to yres
+		if (t_mode->type & Y_RES_EDITABLE) t_mode->vactive *= y_scale;
+
+		// if we can, let's apply the same scaling to both directions
+		if (t_mode->type & X_RES_EDITABLE)
+		{
+			x_scale = cs->scale_proportional? y_scale : 1;
+			double aspect_corrector = max(1.0f, cs->monitor_aspect / source_aspect);
+			t_mode->hactive = normalize(double(t_mode->hactive) * double(x_scale) * aspect_corrector, cs->pixel_precision? 1 : 8);
+		}
+
+		// otherwise, try to get the best out of our current xres
+		else
+		{
+			x_scale = t_mode->hactive / s_mode->hactive;
+			// if the source width fits our xres, try applying integer scaling
+			if (x_scale)
+			{
+				x_scale = scale_into_aspect(s_mode->hactive, t_mode->hactive, source_aspect, cs->monitor_aspect, &x_diff);
+				if (x_diff > 15.0 && t_mode->hactive < cs->super_width)
+						t_mode->result.weight |= R_RES_STRETCH;
+			}
+			// otherwise apply fractional scaling
+			else
+				t_mode->result.weight |= R_RES_STRETCH;
+		}
+	}
+
+	// if the result was fractional scaling in any of the previous steps, deal with it
+	if (t_mode->result.weight & R_RES_STRETCH)
+	{
+		if (t_mode->type & Y_RES_EDITABLE)
+		{
+			// always try to use the interlaced range first if it exists, for better resolution
+			t_mode->vactive = stretch_into_range(t_mode->vfreq * v_scale, range, borders, cs->interlace, &interlace);
+
+			// check in case we couldn't achieve the desired refresh
+			vfreq_real = min(t_mode->vfreq * v_scale, max_vfreq_for_yres(t_mode->vactive, range, borders, interlace));
+		}
+
+		// check if we can create a normal aspect resolution
+		if (t_mode->type & X_RES_EDITABLE)
+			t_mode->hactive = max(t_mode->hactive, normalize(STANDARD_CRT_ASPECT * t_mode->vactive, cs->pixel_precision? 1 : 8));
+
+		// calculate integer scale for prescaling
+		x_scale = max(1, scale_into_aspect(s_mode->hactive, t_mode->hactive, source_aspect, cs->monitor_aspect, &x_diff));
+		y_scale = max(1, floor(double(t_mode->vactive) / s_mode->vactive));
+
+		scan_factor = interlace;
+		doublescan = 1;
+	}
+
+	x_fscale = double(t_mode->hactive) / s_mode->hactive * source_aspect / cs->monitor_aspect;
+	y_fscale = double(t_mode->vactive) / s_mode->vactive;
+	v_fscale = vfreq_real / s_mode->vfreq;
+	v_diff = (vfreq_real / v_scale) -  s_mode->vfreq;
+	if (fabs(v_diff) > cs->refresh_tolerance)
+		t_mode->result.weight |= R_V_FREQ_OFF;
+
+	// ≈≈≈ Modeline generation ≈≈≈
+	// compute new modeline if we are allowed to
+	if (t_mode->type & V_FREQ_EDITABLE)
+	{
+		double margin = 0;
+		double vblank_lines = 0;
+		double vvt_ini = 0;
+		double interlace_incr = !cs->interlace_force_even && interlace == 2? 0.5 : 0;
+
+		// Get resulting refresh
+		t_mode->vfreq = vfreq_real;
+
+		// Get total vertical lines
+		vvt_ini = total_lines_for_yres(t_mode->vactive, t_mode->vfreq, range, borders, scan_factor) + interlace_incr;
+
+		// Calculate horizontal frequency
+		t_mode->hfreq = t_mode->vfreq * vvt_ini;
+
+		horizontal_values:
+
+		// Fill horizontal part of modeline
+		get_line_params(t_mode, range, cs->pixel_precision? 1 : 8);
+
+		// Calculate pixel clock
+		t_mode->pclock = t_mode->htotal * t_mode->hfreq;
+		if (t_mode->pclock <= cs->pclock_min)
+		{
+			if (t_mode->type & X_RES_EDITABLE)
+			{
+				x_scale *= 2;
+				x_fscale *= 2;
+				t_mode->hactive *= 2;
+				goto horizontal_values;
+			}
+			else
+			{
+				t_mode->result.weight |= R_OUT_OF_RANGE;
+				return -1;
+			}
+		}
+
+		// Vertical blanking
+		t_mode->vtotal = vvt_ini * scan_factor;
+		vblank_lines = round_near(t_mode->hfreq * (range->vertical_blank + borders)) + interlace_incr;
+		margin = (t_mode->vtotal - t_mode->vactive - vblank_lines * scan_factor) / (cs->v_shift_correct? 1 : 2);
+
+		double v_front_porch = margin + t_mode->hfreq * range->vfront_porch * scan_factor + interlace_incr;
+		int (*pf_round)(double) = interlace == 2? (cs->interlace_force_even? round_near_even : round_near_odd) : round_near;
+
+		t_mode->vbegin = t_mode->vactive + max(pf_round(v_front_porch), 1);
+		t_mode->vend = t_mode->vbegin + max(round_near(t_mode->hfreq * range->vsync_pulse * scan_factor), 1);
+
+		// Recalculate final vfreq
+		t_mode->vfreq = (t_mode->hfreq / t_mode->vtotal) * scan_factor;
+
+		t_mode->hsync = range->hsync_polarity;
+		t_mode->vsync = range->vsync_polarity;
+		t_mode->interlace = interlace == 2? 1 : 0;
+		t_mode->doublescan = doublescan == 1? 0 : 1;
+	}
+
+	// finally, store result
+	t_mode->result.scan_penalty = (s_mode->interlace != t_mode->interlace? 1 : 0) + (s_mode->doublescan != t_mode->doublescan? 1 : 0);
+	t_mode->result.x_scale = t_mode->result.weight & R_RES_STRETCH || t_mode->hactive >= cs->super_width ? x_fscale : (double)x_scale;
+	t_mode->result.y_scale = t_mode->result.weight & R_RES_STRETCH? y_fscale : (double)y_scale;
+	t_mode->result.v_scale = v_fscale;
+	t_mode->result.x_diff = x_diff;
+	t_mode->result.y_diff = y_diff;
+	t_mode->result.v_diff = v_diff;
+
+	return 0;
+}
+
+//============================================================
+//  get_line_params
+//============================================================
+
+int get_line_params(modeline *mode, monitor_range *range, int char_size)
+{
+	int hhi, hhf, hht;
+	int hh, hs, he, ht;
+	double line_time, char_time, new_char_time;
+	double hfront_porch_min, hsync_pulse_min, hback_porch_min;
+
+	hfront_porch_min = range->hfront_porch * .90;
+	hsync_pulse_min  = range->hsync_pulse  * .90;
+	hback_porch_min  = range->hback_porch  * .90;
+
+	line_time = 1 / mode->hfreq * 1000000;
+
+	hh = round(mode->hactive / char_size);
+	hs = he = ht = 1;
+
+	do {
+		char_time = line_time / (hh + hs + he + ht);
+		if (hs * char_time < hfront_porch_min ||
+			fabs((hs + 1) * char_time - range->hfront_porch) < fabs(hs * char_time - range->hfront_porch))
+			hs++;
+
+		if (he * char_time < hsync_pulse_min ||
+			fabs((he + 1) * char_time - range->hsync_pulse) < fabs(he * char_time - range->hsync_pulse))
+			he++;
+
+		if (ht * char_time < hback_porch_min ||
+			fabs((ht + 1) * char_time - range->hback_porch) < fabs(ht * char_time - range->hback_porch))
+			ht++;
+
+		new_char_time = line_time / (hh + hs + he + ht);
+	} while (new_char_time != char_time);
+
+	hhi = (hh + hs) * char_size;
+	hhf = (hh + hs + he) * char_size;
+	hht = (hh + hs + he + ht) * char_size;
+
+	mode->hbegin  = hhi;
+	mode->hend    = hhf;
+	mode->htotal  = hht;
+
+	return 0;
+}
+
+//============================================================
+//  scale_into_range
+//============================================================
+
+int scale_into_range (int value, int lower_limit, int higher_limit)
+{
+	int scale = 1;
+	while (value * scale < lower_limit) scale ++;
+	if (value * scale <= higher_limit)
+		return scale;
+	else
+		return 0;
+}
+
+//============================================================
+//  scale_into_range
+//============================================================
+
+int scale_into_range (double value, double lower_limit, double higher_limit)
+{
+	int scale = 1;
+	while (value * scale < lower_limit) scale ++;
+	if (value * scale <= higher_limit)
+		return scale;
+	else
+		return 0;
+}
+
+//============================================================
+//  scale_into_aspect
+//============================================================
+
+int scale_into_aspect (int source_res, int tot_res, double original_monitor_aspect, double users_monitor_aspect, double *best_diff)
+{
+	int scale = 1, best_scale = 1;
+	double diff = 0;
+	*best_diff = 0;
+
+	while (source_res * scale <= tot_res)
+	{
+		diff = fabs(1.0 - (users_monitor_aspect / (double(tot_res) / double(source_res * scale) * original_monitor_aspect))) * 100.0;
+		if (diff < *best_diff || *best_diff == 0)
+		{
+			*best_diff = diff;
+			best_scale = scale;
+		}
+		scale ++;
+	}
+	return best_scale;
+}
+
+//============================================================
+//  stretch_into_range
+//============================================================
+
+int stretch_into_range(double vfreq, monitor_range *range, double borders, bool interlace_allowed, double *interlace)
+{
+	int yres, lower_limit;
+
+	if (range->interlaced_lines_min && interlace_allowed)
+	{
+		yres = range->interlaced_lines_max;
+		lower_limit = range->interlaced_lines_min;
+		*interlace = 2;
+	}
+	else
+	{
+		yres = range->progressive_lines_max;
+		lower_limit = range->progressive_lines_min;
+	}
+
+	while (yres > lower_limit && max_vfreq_for_yres(yres, range, borders, *interlace) < vfreq)
+		yres -= 8;
+
+	return yres;
+}
+
+
+//============================================================
+//  total_lines_for_yres
+//============================================================
+
+int total_lines_for_yres(int yres, double vfreq, monitor_range *range, double borders, double interlace)
+{
+	int vvt = max(yres / interlace + round_near(vfreq * yres / (interlace * (1.0 - vfreq * (range->vertical_blank + borders))) * (range->vertical_blank + borders)), 1);
+	while ((vfreq * vvt < range->hfreq_min) && (vfreq * (vvt + 1) < range->hfreq_max)) vvt++;
+	return vvt;
+}
+
+//============================================================
+//  max_vfreq_for_yres
+//============================================================
+
+double max_vfreq_for_yres (int yres, monitor_range *range, double borders, double interlace)
+{
+	return range->hfreq_max / (yres / interlace + round_near(range->hfreq_max * (range->vertical_blank + borders)));
+}
+
+//============================================================
+//  modeline_print
+//============================================================
+
+char * modeline_print(modeline *mode, char *modeline, int flags)
+{
+	char label[48]={'\x00'};
+	char params[192]={'\x00'};
+
+	if (flags & MS_LABEL)
+		sprintf(label, "\"%dx%d_%d%s %.6fKHz %.6fHz\"", mode->hactive, mode->vactive, mode->refresh, mode->interlace?"i":"", mode->hfreq/1000, mode->vfreq);
+
+	if (flags & MS_LABEL_SDL)
+		sprintf(label, "\"%dx%d_%.6f\"", mode->hactive, mode->vactive, mode->vfreq);
+
+	if (flags & MS_PARAMS)
+		sprintf(params, " %.6f %d %d %d %d %d %d %d %d %s %s %s %s", double(mode->pclock)/1000000.0, mode->hactive, mode->hbegin, mode->hend, mode->htotal, mode->vactive, mode->vbegin, mode->vend, mode->vtotal,
+			mode->interlace?"interlace":"", mode->doublescan?"doublescan":"", mode->hsync?"+hsync":"-hsync", mode->vsync?"+vsync":"-vsync");
+
+	sprintf(modeline, "%s%s", label, params);
+
+	return modeline;
+}
+
+//============================================================
+//  modeline_result
+//============================================================
+
+char * modeline_result(modeline *mode, char *result)
+{
+	log_verbose("   rng(%d): ", mode->range);
+
+	if (mode->result.weight & R_OUT_OF_RANGE)
+		sprintf(result, " out of range");
+
+	else
+		sprintf(result, "%4d x%4d_%3.6f%s%s %3.6f [%s] scale(%.3f, %.3f, %.3f) diff(%.3f, %.3f, %.3f)",
+			mode->hactive, mode->vactive, mode->vfreq, mode->interlace?"i":"p", mode->doublescan?"d":"", mode->hfreq/1000, mode->result.weight & R_RES_STRETCH?"fract":"integ",
+			mode->result.x_scale, mode->result.y_scale, mode->result.v_scale, mode->result.x_diff, mode->result.y_diff, mode->result.v_diff);
+	return result;
+}
+
+//============================================================
+//  modeline_compare
+//============================================================
+
+int modeline_compare(modeline *t, modeline *best)
+{
+	bool vector = (t->hactive == (int)t->result.x_scale);
+
+	if (t->result.weight < best->result.weight)
+		return 1;
+
+	else if (t->result.weight <= best->result.weight)
+	{
+		double t_v_diff = fabs(t->result.v_diff);
+		double b_v_diff = fabs(best->result.v_diff);
+
+		if (t->result.weight & R_RES_STRETCH || vector)
+		{
+			double t_y_score = t->result.y_scale * (t->interlace?(2.0/3.0):1.0);
+			double b_y_score = best->result.y_scale * (best->interlace?(2.0/3.0):1.0);
+
+			if  ((t_v_diff <  b_v_diff) ||
+				((t_v_diff == b_v_diff) && (t_y_score > b_y_score)) ||
+				((t_v_diff == b_v_diff) && (t_y_score == b_y_score) && (t->result.x_scale > best->result.x_scale)))
+					return 1;
+		}
+		else
+		{
+			int t_y_score = t->result.y_scale + t->result.scan_penalty;
+			int b_y_score = best->result.y_scale + best->result.scan_penalty;
+			double xy_diff = roundf((t->result.x_diff + t->result.y_diff) * 100) / 100;
+			double best_xy_diff = roundf((best->result.x_diff + best->result.y_diff) * 100) / 100;
+
+			if  ((t_y_score < b_y_score) ||
+				((t_y_score == b_y_score) && (xy_diff < best_xy_diff)) ||
+				((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale < best->result.x_scale)) ||
+				((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale == best->result.x_scale) && (t_v_diff <  b_v_diff)))
+					return 1;
+		}
+	}
+	return 0;
+}
+
+//============================================================
+//  modeline_vesa_gtf
+//  Based on the VESA GTF spreadsheet by Andy Morrish 1/5/97
+//============================================================
+
+int modeline_vesa_gtf(modeline *m)
+{
+	int C, M;
+	int v_sync_lines, v_porch_lines_min, v_front_porch_lines, v_back_porch_lines, v_sync_v_back_porch_lines, v_total_lines;
+	int h_sync_width_percent, h_sync_width_pixels, h_blanking_pixels, h_front_porch_pixels, h_total_pixels;
+	double v_freq, v_freq_est, v_freq_real, v_sync_v_back_porch;
+	double h_freq, h_period, h_period_real, h_ideal_blanking;
+	double pixel_freq, interlace;
+
+	// Check if there's a value defined for vfreq. We're assuming input vfreq is the total field vfreq regardless interlace
+	v_freq = m->vfreq? m->vfreq:double(m->refresh);
+
+	// These values are GTF defined defaults
+	v_sync_lines = 3;
+	v_porch_lines_min = 1;
+	v_front_porch_lines = v_porch_lines_min;
+	v_sync_v_back_porch = 550;
+	h_sync_width_percent = 8;
+	M = 128.0 / 256 * 600;
+	C = ((40 - 20) * 128.0 / 256) + 20;
+
+	// GTF calculation
+	interlace = m->interlace?0.5:0;
+	h_period = ((1.0 / v_freq) - (v_sync_v_back_porch / 1000000)) / ((double)m->height + v_front_porch_lines + interlace) * 1000000;
+	v_sync_v_back_porch_lines = round_near(v_sync_v_back_porch / h_period);
+	v_back_porch_lines = v_sync_v_back_porch_lines - v_sync_lines;
+	v_total_lines = m->height + v_front_porch_lines + v_sync_lines + v_back_porch_lines;
+	v_freq_est = (1.0 / h_period) / v_total_lines * 1000000;
+	h_period_real = h_period / (v_freq / v_freq_est);
+	v_freq_real = (1.0 / h_period_real) / v_total_lines * 1000000;
+	h_ideal_blanking = double(C - (M * h_period_real / 1000));
+	h_blanking_pixels = round_near(m->width * h_ideal_blanking /(100 - h_ideal_blanking) / (2 * 8)) * (2 * 8);
+	h_total_pixels = m->width + h_blanking_pixels;
+	pixel_freq = h_total_pixels / h_period_real * 1000000;
+	h_freq = 1000000 / h_period_real;
+	h_sync_width_pixels = round_near(h_sync_width_percent * h_total_pixels / 100 / 8) * 8;
+	h_front_porch_pixels = (h_blanking_pixels / 2) - h_sync_width_pixels;
+
+	// Results
+	m->hactive = m->width;
+	m->hbegin = m->hactive + h_front_porch_pixels;
+	m->hend = m->hbegin + h_sync_width_pixels;
+	m->htotal = h_total_pixels;
+	m->vactive = m->height;
+	m->vbegin = m->vactive + v_front_porch_lines;
+	m->vend = m->vbegin + v_sync_lines;
+	m->vtotal = v_total_lines;
+	m->hfreq = h_freq;
+	m->vfreq = v_freq_real;
+	m->pclock = pixel_freq;
+	m->hsync = 0;
+	m->vsync = 1;
+
+	return true;
+}
+
+//============================================================
+//  modeline_parse
+//============================================================
+
+int modeline_parse(const char *user_modeline, modeline *mode)
+{
+	char modeline_txt[256]={'\x00'};
+
+	if (!strcmp(user_modeline, "auto"))
+		return false;
+
+	// Remove quotes
+	char *quote_start, *quote_end;
+	quote_start = strstr((char*)user_modeline, "\"");
+	if (quote_start)
+	{
+		quote_start++;
+		quote_end = strstr(quote_start, "\"");
+		if (!quote_end || *quote_end++ == 0)
+			return false;
+		user_modeline = quote_end;
+	}
+
+	// Get timing flags
+	mode->interlace = strstr(user_modeline, "interlace")?1:0;
+	mode->doublescan = strstr(user_modeline, "doublescan")?1:0;
+	mode->hsync = strstr(user_modeline, "+hsync")?1:0;
+	mode->vsync = strstr(user_modeline, "+vsync")?1:0;
+
+	// Get timing values
+	double pclock;
+	int e = sscanf(user_modeline, " %lf %d %d %d %d %d %d %d %d",
+		&pclock,
+		&mode->hactive, &mode->hbegin, &mode->hend, &mode->htotal,
+		&mode->vactive, &mode->vbegin, &mode->vend, &mode->vtotal);
+
+	if (e != 9)
+	{
+		log_error("Switchres: missing parameter in user modeline\n  %s\n", user_modeline);
+		memset(mode, 0, sizeof(struct modeline));
+		return false;
+	}
+
+	// Calculate timings
+	mode->pclock = pclock * 1000000.0;
+	mode->hfreq = mode->pclock / mode->htotal;
+	mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
+	mode->refresh = mode->vfreq;
+	mode->width = mode->hactive;
+	mode->height = mode->vactive;
+	log_verbose("Switchres: user modeline %s\n", modeline_print(mode, modeline_txt, MS_FULL));
+
+	return true;
+}
+
+//============================================================
+//  modeline_to_monitor_range
+//============================================================
+
+int modeline_to_monitor_range(monitor_range *range, modeline *mode)
+{
+	// If Vfreq range is empty, create it around the provided vfreq
+	if (range->vfreq_min == 0.0f) range->vfreq_min = mode->vfreq - 0.2;
+	if (range->vfreq_max == 0.0f) range->vfreq_max = mode->vfreq + 0.2;
+
+	// Make sure the range includes the target vfreq
+	if (mode->vfreq < range->vfreq_min || mode->vfreq > range->vfreq_max)
+		return 0;
+
+	double line_time = 1 / mode->hfreq;
+	double pixel_time = line_time / mode->htotal * 1000000;
+	double interlace_factor = mode->interlace? 0.5 : 1.0;
+
+	range->hfront_porch = pixel_time * (mode->hbegin - mode->hactive);
+	range->hsync_pulse = pixel_time * (mode->hend - mode->hbegin);
+	range->hback_porch = pixel_time * (mode->htotal - mode->hend);
+
+	// We floor the vertical fields to remove the half line from interlaced modes, because
+	// the modeline generator will add it automatically. Otherwise it would be added twice.
+	range->vfront_porch = line_time * floor((mode->vbegin - mode->vactive) * interlace_factor);
+	range->vsync_pulse = line_time * floor((mode->vend - mode->vbegin) * interlace_factor);
+	range->vback_porch = line_time * floor((mode->vtotal - mode->vend) * interlace_factor);
+	range->vertical_blank = range->vfront_porch + range->vsync_pulse + range->vback_porch;
+
+	range->hsync_polarity = mode->hsync;
+	range->vsync_polarity = mode->vsync;
+
+	range->progressive_lines_min = mode->interlace? 0 : mode->vactive;
+	range->progressive_lines_max = mode->interlace? 0 : mode->vactive;
+	range->interlaced_lines_min = mode->interlace? mode->vactive : 0;
+	range->interlaced_lines_max= mode->interlace? mode->vactive : 0;
+
+	range->hfreq_min = range->vfreq_min * mode->vtotal * interlace_factor;
+	range->hfreq_max = range->vfreq_max * mode->vtotal * interlace_factor;
+
+	return 1;
+}
+
+//============================================================
+//  modeline_adjust
+//============================================================
+
+int modeline_adjust(modeline *mode, double hfreq_max, generator_settings *cs)
+{
+	// If input values are out of range, they are fixed within range and returned in the cs struct.
+
+	// H size ajdustment, valid values 0.5-2.0
+	if (cs->h_size != 1.0f)
+	{
+		if (cs->h_size > 2.0f)
+			cs->h_size = 2.0f;
+		else if (cs->h_size < 0.5f)
+			cs->h_size = 0.5f;
+
+		monitor_range range;
+		memset(&range, 0, sizeof(monitor_range));
+
+		modeline_to_monitor_range(&range, mode);
+
+		range.hfront_porch /= cs->h_size;
+		range.hback_porch /= cs->h_size;
+
+		modeline_create(mode, mode, &range, cs);
+	}
+
+	// H shift adjustment, positive or negative value
+	if (cs->h_shift != 0)
+	{
+		if (cs->h_shift >= mode->hbegin - mode->hactive)
+			cs->h_shift = mode->hbegin - mode->hactive - 1;
+
+		else if (cs->h_shift <= mode->hend - mode->htotal)
+			cs->h_shift = mode->hend - mode->htotal + 1;
+
+		mode->hbegin -= cs->h_shift;
+		mode->hend -= cs->h_shift;
+	}
+
+	// V shift adjustment, positive or negative value
+	if (cs->v_shift != 0)
+	{
+		int vactive = mode->vactive;
+		int vbegin = mode->vbegin;
+		int vend = mode->vend;
+		int vtotal = mode->vtotal;
+
+		if (mode->interlace)
+		{
+			vactive >>= 1;
+			vbegin  >>= 1;
+			vend    >>= 1;
+			vtotal  >>= 1;
+		}
+
+		int v_front_porch = vbegin - vactive;
+		int v_back_porch =  vend - vtotal;
+		int max_vtotal = hfreq_max / mode->vfreq;
+		int border = max_vtotal - vtotal;
+		int padding = 0;
+
+		// v_shift positive
+		if (cs->v_shift >= v_front_porch)
+		{
+			int v_front_porch_ex = v_front_porch + border;
+			if (cs->v_shift >= v_front_porch_ex)
+				cs->v_shift = v_front_porch_ex - 1;
+
+			padding = cs->v_shift - v_front_porch + 1;
+			vbegin += padding;
+			vend += padding;
+			vtotal += padding;
+		}
+
+		// v_shift negative
+		else if (cs->v_shift <= v_back_porch + 1)
+		{
+			int v_back_porch_ex = v_back_porch - border;
+			if (cs->v_shift <= v_back_porch_ex + 1)
+				cs->v_shift = v_back_porch_ex + 2;
+
+			padding = -(cs->v_shift - v_back_porch - 2);
+			vtotal += padding;
+		}
+
+		vbegin -= cs->v_shift;
+		vend -= cs->v_shift;
+
+		if (mode->interlace)
+		{
+			vbegin =  (vbegin << 1)  | (mode->vbegin & 1);
+			vend =    (vend << 1)    | (mode->vend & 1);
+			vtotal =  (vtotal << 1)  | (mode->vtotal & 1);
+		}
+
+		mode->vbegin = vbegin;
+		mode->vend = vend;
+		mode->vtotal = vtotal;
+
+		if (padding != 0)
+		{
+			mode->hfreq = mode->vfreq * mode->vtotal / (mode->interlace? 2.0 : 1.0);
+
+			monitor_range range;
+			memset(&range, 0, sizeof(monitor_range));
+			modeline_to_monitor_range(&range, mode);
+			monitor_show_range(&range);
+			modeline_create(mode, mode, &range, cs);
+		}
+	}
+
+	return 0;
+}
+
+//============================================================
+//  modeline_is_different
+//============================================================
+
+int modeline_is_different(modeline *n, modeline *p)
+{
+	// Remove on last fields in modeline comparison
+	return memcmp(n, p, offsetof(struct modeline, vfreq));
+}
+
+//============================================================
+//  modeline_copy_timings
+//============================================================
+
+void modeline_copy_timings(modeline *n, modeline *p)
+{
+	// Only copy relevant timing fields
+	memcpy(n, p, offsetof(struct modeline, width));
+}
+
+//============================================================
+//  monitor_fill_vesa_gtf
+//============================================================
+
+int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines)
+{
+	int lines = 0;
+	sscanf(max_lines, "vesa_%d", &lines);
+
+	if (!lines)
+		return 0;
+
+	int i = 0;
+	if (lines >= 480)
+		i += monitor_fill_vesa_range(&range[i], 384, 480);
+	if (lines >= 600)
+		i += monitor_fill_vesa_range(&range[i], 480, 600);
+	if (lines >= 768)
+		i += monitor_fill_vesa_range(&range[i], 600, 768);
+	if (lines >= 1024)
+		i += monitor_fill_vesa_range(&range[i], 768, 1024);
+
+	return i;
+}
+
+//============================================================
+//  monitor_fill_vesa_range
+//============================================================
+
+int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max)
+{
+	modeline mode;
+	memset(&mode, 0, sizeof(modeline));
+
+	mode.width = real_res(STANDARD_CRT_ASPECT * lines_max);
+	mode.height = lines_max;
+	mode.refresh = 60;
+	range->vfreq_min = 50;
+	range->vfreq_max = 65;
+
+	modeline_vesa_gtf(&mode);
+	modeline_to_monitor_range(range, &mode);
+
+	range->progressive_lines_min = lines_min;
+	range->hfreq_min = mode.hfreq - 500;
+	range->hfreq_max = mode.hfreq + 500;
+	monitor_show_range(range);
+
+	return 1;
+}
+
+//============================================================
+//  round_near
+//============================================================
+
+int round_near(double number)
+{
+	return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5);
+}
+
+//============================================================
+//  round_near_odd
+//============================================================
+
+int round_near_odd(double number)
+{
+	return int(ceil(number)) % 2 == 0? floor(number) : ceil(number);
+}
+
+//============================================================
+//  round_near_even
+//============================================================
+
+int round_near_even(double number)
+{
+	return int(ceil(number)) % 2 == 1? floor(number) : ceil(number);
+}
+
+//============================================================
+//  normalize
+//============================================================
+
+int normalize(int a, int b)
+{
+	int c, d;
+	c = a % b;
+	d = a / b;
+	if (c) d++;
+	return d * b;
+}
+
+//============================================================
+//  real_res
+//============================================================
+
+int real_res(int x) {return (int) (x / 8) * 8;}
diff --git a/3rdparty/switchres/modeline.h b/3rdparty/switchres/modeline.h
new file mode 100644
index 00000000000..f71f465ade9
--- /dev/null
+++ b/3rdparty/switchres/modeline.h
@@ -0,0 +1,145 @@
+/**************************************************************
+
+   modeline.h - Modeline generation header
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __MODELINE_H__
+#define __MODELINE_H__
+
+#include <stdint.h>
+#include <math.h>
+#include <cstddef>
+#include "monitor.h"
+
+
+//============================================================
+//  CONSTANTS
+//============================================================
+
+// Modeline print flags
+#define MS_LABEL      0x00000001
+#define MS_LABEL_SDL  0x00000002
+#define MS_PARAMS     0x00000004
+#define MS_FULL       MS_LABEL | MS_PARAMS
+
+// Modeline result
+#define R_V_FREQ_OFF    0x00000001
+#define R_RES_STRETCH   0x00000002
+#define R_OUT_OF_RANGE  0x00000004
+
+// Mode types
+#define MODE_OK         0x00000000
+#define MODE_DESKTOP    0x01000000
+#define MODE_ROTATED    0x02000000
+#define MODE_DISABLED   0x04000000
+#define MODE_USER_DEF   0x08000000
+#define MODE_UPDATE     0x10000000
+#define MODE_ADD        0x20000000
+#define MODE_DELETE     0x40000000
+#define MODE_ERROR      0x80000000
+#define V_FREQ_EDITABLE 0x00000001
+#define X_RES_EDITABLE  0x00000002
+#define Y_RES_EDITABLE  0x00000004
+#define SCAN_EDITABLE   0x00000008
+#define XYV_EDITABLE   (X_RES_EDITABLE | Y_RES_EDITABLE | V_FREQ_EDITABLE )
+
+#define DUMMY_WIDTH 1234
+#define MAX_MODELINES 256
+
+//============================================================
+//  TYPE DEFINITIONS
+//============================================================
+
+typedef struct mode_result
+{
+	int     weight;
+	int     scan_penalty;
+	double  x_scale;
+	double  y_scale;
+	double  v_scale;
+	double  x_diff;
+	double  y_diff;
+	double  v_diff;
+} mode_result;
+
+typedef struct modeline
+{
+	uint64_t    pclock;
+	int    hactive;
+	int    hbegin;
+	int    hend;
+	int    htotal;
+	int    vactive;
+	int    vbegin;
+	int    vend;
+	int    vtotal;
+	int    interlace;
+	int    doublescan;
+	int    hsync;
+	int    vsync;
+	//
+	double vfreq;
+	double hfreq;
+	//
+	int    width;
+	int    height;
+	int    refresh;
+	int    refresh_label;
+	//
+	int    id;
+	int    type;
+	int    range;
+	uint64_t platform_data;
+	//
+	mode_result result;
+} modeline;
+
+typedef struct generator_settings
+{
+	int      interlace;
+	int      doublescan;
+	uint64_t pclock_min;
+	double   monitor_aspect;
+	double   refresh_tolerance;
+	int      super_width;
+	double   h_size;
+	int      h_shift;
+	int      v_shift;
+	int      v_shift_correct;
+	int      pixel_precision;
+	int      interlace_force_even;
+	int      scale_proportional;
+} generator_settings;
+
+//============================================================
+//  PROTOTYPES
+//============================================================
+
+int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, generator_settings *cs);
+int modeline_compare(modeline *t_mode, modeline *best_mode);
+char * modeline_print(modeline *mode, char *modeline, int flags);
+char * modeline_result(modeline *mode, char *result);
+int modeline_vesa_gtf(modeline *m);
+int modeline_parse(const char *user_modeline, modeline *mode);
+int modeline_to_monitor_range(monitor_range *range, modeline *mode);
+int modeline_adjust(modeline *mode, double hfreq_max, generator_settings *cs);
+int modeline_is_different(modeline *n, modeline *p);
+void modeline_copy_timings(modeline *n, modeline *p);
+
+int round_near(double number);
+int round_near_odd(double number);
+int round_near_even(double number);
+int normalize(int a, int b);
+int real_res(int x);
+
+
+#endif
diff --git a/3rdparty/switchres/monitor.cpp b/3rdparty/switchres/monitor.cpp
new file mode 100644
index 00000000000..c2a473e2025
--- /dev/null
+++ b/3rdparty/switchres/monitor.cpp
@@ -0,0 +1,437 @@
+/**************************************************************
+
+   monitor.cpp - Monitor presets and custom monitor definition
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "monitor.h"
+#include "log.h"
+
+//============================================================
+//  CONSTANTS
+//============================================================
+
+#define HFREQ_MIN  14000
+#define HFREQ_MAX  540672 // 8192 * 1.1 * 60
+#define VFREQ_MIN  40
+#define VFREQ_MAX  200
+#define PROGRESSIVE_LINES_MIN 128
+
+//============================================================
+//  monitor_fill_range
+//============================================================
+
+int monitor_fill_range(monitor_range *range, const char *specs_line)
+{
+	monitor_range new_range;
+
+	if (strlen(specs_line) == 0)
+		return 0;
+
+	if (strcmp(specs_line, "auto")) {
+		int e = sscanf(specs_line, "%lf-%lf,%lf-%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d",
+			&new_range.hfreq_min, &new_range.hfreq_max,
+			&new_range.vfreq_min, &new_range.vfreq_max,
+			&new_range.hfront_porch, &new_range.hsync_pulse, &new_range.hback_porch,
+			&new_range.vfront_porch, &new_range.vsync_pulse, &new_range.vback_porch,
+			&new_range.hsync_polarity, &new_range.vsync_polarity,
+			&new_range.progressive_lines_min, &new_range.progressive_lines_max,
+			&new_range.interlaced_lines_min, &new_range.interlaced_lines_max);
+
+		if (e != 16) {
+			log_error("Switchres: Error trying to fill monitor range with\n  %s\n", specs_line);
+			return -1;
+		}
+
+		new_range.vfront_porch /= 1000;
+		new_range.vsync_pulse /= 1000;
+		new_range.vback_porch /= 1000;
+		new_range.vertical_blank = (new_range.vfront_porch + new_range.vsync_pulse + new_range.vback_porch);
+
+		if (monitor_evaluate_range(&new_range))
+		{
+			log_error("Switchres: Error in monitor range (ignoring): %s\n", specs_line);
+			return -1;
+		}
+		else
+		{
+			memcpy(range, &new_range, sizeof(struct monitor_range));
+			monitor_show_range(range);
+		}
+	}
+	return 0;
+}
+
+//============================================================
+//  monitor_fill_lcd_range
+//============================================================
+
+int monitor_fill_lcd_range(monitor_range *range, const char *specs_line)
+{
+	if (strlen(specs_line) == 0)
+		return 0;
+
+	if (strcmp(specs_line, "auto"))
+	{
+		if (sscanf(specs_line, "%lf-%lf", &range->vfreq_min, &range->vfreq_max) == 2)
+		{
+			log_verbose("Switchres: LCD vfreq range set by user as %f-%f\n", range->vfreq_min, range->vfreq_max);
+			return true;
+		}
+		else
+			log_error("Switchres: Error trying to fill LCD range with\n  %s\n", specs_line);
+	}
+	// Use default values
+	range->vfreq_min = 59;
+	range->vfreq_max = 61;
+	log_verbose("Switchres: Using default vfreq range for LCD %f-%f\n", range->vfreq_min, range->vfreq_max);
+
+	return 0;
+}
+
+//============================================================
+//  monitor_show_range
+//============================================================
+
+int monitor_show_range(monitor_range *range)
+{
+	log_verbose("Switchres: Monitor range %.2f-%.2f,%.2f-%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%d,%d,%d,%d,%d,%d\n",
+		range->hfreq_min, range->hfreq_max,
+		range->vfreq_min, range->vfreq_max,
+		range->hfront_porch, range->hsync_pulse, range->hback_porch,
+		range->vfront_porch * 1000, range->vsync_pulse * 1000, range->vback_porch * 1000,
+		range->hsync_polarity, range->vsync_polarity,
+		range->progressive_lines_min, range->progressive_lines_max,
+		range->interlaced_lines_min, range->interlaced_lines_max);
+
+	return 0;
+}
+
+//============================================================
+//  monitor_set_preset
+//============================================================
+
+int monitor_set_preset(const char *type, monitor_range *range)
+{
+	// PAL TV - 50 Hz/625
+	if (!strcmp(type, "pal"))
+	{
+		monitor_fill_range(&range[0], "15625.00-15625.00, 50.00-50.00, 1.500, 4.700, 5.800, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// NTSC TV - 60 Hz/525
+	else if (!strcmp(type, "ntsc"))
+	{
+		monitor_fill_range(&range[0], "15734.26-15734.26, 59.94-59.94, 1.500, 4.700, 4.700, 0.191, 0.191, 0.953, 0, 0, 192, 240, 448, 480");
+		return 1;
+	}
+	// Generic 15.7 kHz
+	else if (!strcmp(type, "generic_15"))
+	{
+		monitor_fill_range(&range[0], "15625-15750, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// Arcade 15.7 kHz - standard resolution
+	else if (!strcmp(type, "arcade_15"))
+	{
+		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// Arcade 15.7-16.5 kHz - extended resolution
+	else if (!strcmp(type, "arcade_15ex"))
+	{
+		monitor_fill_range(&range[0], "15625-16500, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// Arcade 25.0 kHz - medium resolution
+	else if (!strcmp(type, "arcade_25"))
+	{
+		monitor_fill_range(&range[0], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
+		return 1;
+	}
+	// Arcade 31.5 kHz - medium resolution
+	else if (!strcmp(type, "arcade_31"))
+	{
+		monitor_fill_range(&range[0], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
+		return 1;
+	}
+	// Arcade 15.7/25.0 kHz - dual-sync
+	else if (!strcmp(type, "arcade_15_25"))
+	{
+		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
+		return 2;
+	}
+	// Arcade 15.7/31.5 kHz - dual-sync
+	else if (!strcmp(type, "arcade_15_31"))
+	{
+		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
+		return 2;
+	}
+	// Arcade 15.7/25.0/31.5 kHz - tri-sync
+	else if (!strcmp(type, "arcade_15_25_31"))
+	{
+		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
+		monitor_fill_range(&range[2], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
+		return 3;
+	}
+	// Makvision 2929D
+	else if (!strcmp(type, "m2929"))
+	{
+		monitor_fill_range(&range[0], "30000-40000, 47.00-90.00, 0.600, 2.500, 2.800, 0.032, 0.096, 0.448, 0, 0, 384, 640, 0, 0");
+		return 1;
+	}
+	// Wells Gardner D9800, D9400
+	else if (!strcmp(type, "d9800") || !strcmp(type, "d9400"))
+	{
+		monitor_fill_range(&range[0], "15250-18000, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
+		monitor_fill_range(&range[1], "18001-19000, 40-80, 2.187, 4.688, 6.719, 0.140, 0.191, 0.950, 0, 0, 288, 320, 0, 0");
+		monitor_fill_range(&range[2], "20501-29000, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 320, 384, 0, 0");
+		monitor_fill_range(&range[3], "29001-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 384, 480, 0, 0");
+		monitor_fill_range(&range[4], "32001-34000, 40-80, 0.636, 3.813, 1.906, 0.020, 0.106, 0.607, 0, 0, 480, 576, 0, 0");
+		monitor_fill_range(&range[5], "34001-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 576, 600, 0, 0");
+		return 6;
+	}
+	// Wells Gardner D9200
+	else if (!strcmp(type, "d9200"))
+	{
+		monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
+		monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
+		monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 400, 512, 0, 0");
+		monitor_fill_range(&range[3], "37000-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 512, 600, 0, 0");
+		return 4;
+	}
+	// Wells Gardner K7000
+	else if (!strcmp(type, "k7000"))
+	{
+		monitor_fill_range(&range[0], "15625-15800, 49.50-63.00, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// Wells Gardner 25K7131
+	else if (!strcmp(type, "k7131"))
+	{
+		monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// Wei-Ya M3129
+	else if (!strcmp(type, "m3129"))
+	{
+		monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 1, 1, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 1, 1, 384, 400, 0, 0");
+		monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 1, 1, 400, 512, 0, 0");
+		return 3;
+	}
+	// Hantarex MTC 9110
+	else if (!strcmp(type, "h9110") || !strcmp(type, "polo"))
+	{
+		monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
+		return 1;
+	}
+	// Hantarex Polostar 25
+	else if (!strcmp(type, "pstar"))
+	{
+		monitor_fill_range(&range[0], "15700-15800, 50-65, 1.800, 0.400, 7.400, 0.064, 0.160, 1.056, 0, 0, 192, 256, 0, 0");
+		monitor_fill_range(&range[1], "16200-16300, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 256, 264, 512, 528");
+		monitor_fill_range(&range[2], "25300-25400, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 384, 400, 768, 800");
+		monitor_fill_range(&range[3], "31500-31600, 50-65, 0.170, 0.350, 5.500, 0.040, 0.040, 0.640, 0, 0, 400, 512, 0, 0");
+		return 4;
+	}
+	// Nanao MS-2930, MS-2931
+	else if (!strcmp(type, "ms2930"))
+	{
+		monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
+		monitor_fill_range(&range[2], "31000-32000, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 480, 512, 0, 0");
+		return 3;
+	}
+	// Nanao MS9-29
+	else if (!strcmp(type, "ms929"))
+	{
+		monitor_fill_range(&range[0], "15450-16050, 50-65, 3.910, 4.700, 6.850, 0.190, 0.191, 1.018, 0, 0, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "23900-24900, 50-65, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 384, 400, 0, 0");
+		return 2;
+	}
+	// Rodotron 666B-29
+	else if (!strcmp(type, "r666b"))
+	{
+		monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
+		monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
+		monitor_fill_range(&range[2], "31000-32500, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 400, 512, 0, 0");
+		return 3;
+	}
+	// PC CRT 70kHz/120Hz
+	else if (!strcmp(type, "pc_31_120"))
+	{
+		monitor_fill_range(&range[0], "31400-31600, 100-130, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 200, 256, 0, 0");
+		monitor_fill_range(&range[1], "31400-31600, 50-65, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 400, 512, 0, 0");
+		return 2;
+	}
+	// PC CRT 70kHz/120Hz
+	else if (!strcmp(type, "pc_70_120"))
+	{
+		monitor_fill_range(&range[0], "30000-70000, 100-130, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 192, 320, 0, 0");
+		monitor_fill_range(&range[1], "30000-70000, 50-65, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 400, 1024, 0, 0");
+		return 2;
+	}
+	// VESA GTF
+	else if (!strcmp(type, "vesa_480") || !strcmp(type, "vesa_600") || !strcmp(type, "vesa_768") || !strcmp(type, "vesa_1024"))
+	{
+		return monitor_fill_vesa_gtf(&range[0], type);
+	}
+
+	log_error("Switchres: Monitor type unknown: %s\n", type);
+	return 0;
+}
+
+//============================================================
+//  monitor_evaluate_range
+//============================================================
+
+int monitor_evaluate_range(monitor_range *range)
+{
+	// First we check that all frequency ranges are reasonable
+	if (range->hfreq_min < HFREQ_MIN || range->hfreq_min > HFREQ_MAX)
+	{
+		log_error("Switchres: hfreq_min %.2f out of range\n", range->hfreq_min);
+		return 1;
+	}
+	if (range->hfreq_max < HFREQ_MIN || range->hfreq_max < range->hfreq_min || range->hfreq_max > HFREQ_MAX)
+	{
+		log_error("Switchres: hfreq_max %.2f out of range\n", range->hfreq_max);
+		return 1;
+	}
+	if (range->vfreq_min < VFREQ_MIN || range->vfreq_min > VFREQ_MAX)
+	{
+		log_error("Switchres: vfreq_min %.2f out of range\n", range->vfreq_min);
+		return 1;
+	}
+	if (range->vfreq_max < VFREQ_MIN || range->vfreq_max < range->vfreq_min || range->vfreq_max > VFREQ_MAX)
+	{
+		log_error("Switchres: vfreq_max %.2f out of range\n", range->vfreq_max);
+		return 1;
+	}
+
+	// line_time in μs. We check that no horizontal value is longer than a whole line
+	double line_time = 1 / range->hfreq_max * 1000000;
+
+	if (range->hfront_porch <= 0 || range->hfront_porch > line_time)
+	{
+		log_error("Switchres: hfront_porch %.3f out of range\n", range->hfront_porch);
+		return 1;
+	}
+	if (range->hsync_pulse <= 0 || range->hsync_pulse > line_time)
+	{
+		log_error("Switchres: hsync_pulse %.3f out of range\n", range->hsync_pulse);
+		return 1;
+	}
+	if (range->hback_porch <= 0 || range->hback_porch > line_time)
+	{
+		log_error("Switchres: hback_porch %.3f out of range\n", range->hback_porch);
+		return 1;
+	}
+
+	// frame_time in ms. We check that no vertical value is longer than a whole frame
+	double frame_time = 1 / range->vfreq_max * 1000;
+
+	if (range->vfront_porch <= 0 || range->vfront_porch > frame_time)
+	{
+		log_error("Switchres: vfront_porch %.3f out of range\n", range->vfront_porch);
+		return 1;
+	}
+	if (range->vsync_pulse <= 0 || range->vsync_pulse > frame_time)
+	{
+		log_error("Switchres: vsync_pulse %.3f out of range\n", range->vsync_pulse);
+		return 1;
+	}
+	if (range->vback_porch <= 0 || range->vback_porch > frame_time)
+	{
+		log_error("Switchres: vback_porch %.3f out of range\n", range->vback_porch);
+		return 1;
+	}
+
+	// Now we check sync polarities
+	if (range->hsync_polarity != 0 && range->hsync_polarity != 1)
+	{
+		log_error("Switchres: Hsync polarity can be only 0 or 1\n");
+		return 1;
+	}
+	if (range->vsync_polarity != 0 && range->vsync_polarity != 1)
+	{
+		log_error("Switchres: Vsync polarity can be only 0 or 1\n");
+		return 1;
+	}
+
+	// Finally we check that the line limiters are reasonable
+	// Progressive range:
+	if (range->progressive_lines_min > 0 && range->progressive_lines_min < PROGRESSIVE_LINES_MIN)
+	{
+		log_error("Switchres: progressive_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN);
+		return 1;
+	}
+	if ((range->progressive_lines_min + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
+	{
+		log_error("Switchres: progressive_lines_min %d out of range\n", range->progressive_lines_min);
+		return 1;
+	}
+	if (range->progressive_lines_max < range->progressive_lines_min)
+	{
+		log_error("Switchres: progressive_lines_max must greater than progressive_lines_min\n");
+		return 1;
+	}
+	if ((range->progressive_lines_max + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
+	{
+		log_error("Switchres: progressive_lines_max %d out of range\n", range->progressive_lines_max);
+		return 1;
+	}
+
+	// Interlaced range:
+	if (range->interlaced_lines_min != 0)
+	{
+		if (range->interlaced_lines_min < range->progressive_lines_max)
+		{
+			log_error("Switchres: interlaced_lines_min must greater than progressive_lines_max\n");
+			return 1;
+		}
+		if (range->interlaced_lines_min < PROGRESSIVE_LINES_MIN * 2)
+		{
+			log_error("Switchres: interlaced_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN * 2);
+			return 1;
+		}
+		if ((range->interlaced_lines_min / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
+		{
+			log_error("Switchres: interlaced_lines_min %d out of range\n", range->interlaced_lines_min);
+			return 1;
+		}
+		if (range->interlaced_lines_max < range->interlaced_lines_min)
+		{
+			log_error("Switchres: interlaced_lines_max must greater than interlaced_lines_min\n");
+			return 1;
+		}
+		if ((range->interlaced_lines_max / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
+		{
+			log_error("Switchres: interlaced_lines_max %d out of range\n", range->interlaced_lines_max);
+			return 1;
+		}
+	}
+	else
+	{
+		if (range->interlaced_lines_max != 0)
+		{
+			log_error("Switchres: interlaced_lines_max must be zero if interlaced_lines_min is not defined\n");
+			return 1;
+		}
+	}
+	return 0;
+}
diff --git a/3rdparty/switchres/monitor.h b/3rdparty/switchres/monitor.h
new file mode 100644
index 00000000000..552cef314ea
--- /dev/null
+++ b/3rdparty/switchres/monitor.h
@@ -0,0 +1,64 @@
+/**************************************************************
+
+   monitor.h - Monitor presets header
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __MONITOR_H__
+#define __MONITOR_H__
+
+//============================================================
+//  CONSTANTS
+//============================================================
+
+#define MAX_RANGES 10
+#define MONITOR_CRT 0
+#define MONITOR_LCD 1
+#define STANDARD_CRT_ASPECT 4.0/3.0
+
+//============================================================
+//  TYPE DEFINITIONS
+//============================================================
+
+typedef struct monitor_range
+{
+	double hfreq_min;
+	double hfreq_max;
+	double vfreq_min;
+	double vfreq_max;
+	double hfront_porch;
+	double hsync_pulse;
+	double hback_porch;
+	double vfront_porch;
+	double vsync_pulse;
+	double vback_porch;
+	int    hsync_polarity;
+	int    vsync_polarity;
+	int    progressive_lines_min;
+	int    progressive_lines_max;
+	int    interlaced_lines_min;
+	int    interlaced_lines_max;
+	double vertical_blank;
+} monitor_range;
+
+//============================================================
+//  PROTOTYPES
+//============================================================
+
+int monitor_fill_range(monitor_range *range, const char *specs_line);
+int monitor_show_range(monitor_range *range);
+int monitor_set_preset(const char *type, monitor_range *range);
+int monitor_fill_lcd_range(monitor_range *range, const char *specs_line);
+int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines);
+int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max);
+int monitor_evaluate_range(monitor_range *range);
+
+#endif
diff --git a/3rdparty/switchres/resync_windows.cpp b/3rdparty/switchres/resync_windows.cpp
new file mode 100644
index 00000000000..ab98ac7d14a
--- /dev/null
+++ b/3rdparty/switchres/resync_windows.cpp
@@ -0,0 +1,171 @@
+/**************************************************************
+
+    resync_windows.cpp - Windows device change notifying helper
+
+    ---------------------------------------------------------
+
+    Switchres   Modeline generation engine for emulation
+
+    License     GPL-2.0+
+    Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                          Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <functional>
+#include "resync_windows.h"
+#include "log.h"
+
+GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 };
+
+//============================================================
+//  resync_handler::resync_handler
+//============================================================
+
+resync_handler::resync_handler()
+{
+	m_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+	CreateThread(NULL, 0, handler_thread, (LPVOID)this, 0, &my_thread);
+}
+
+//============================================================
+//  resync_handler::~resync_handler
+//============================================================
+
+resync_handler::~resync_handler()
+{
+	SendMessage(m_hwnd, WM_CLOSE, 0, 0);
+	if (m_event) CloseHandle(m_event);
+}
+
+//============================================================
+//  resync_handler::handler_thread
+//============================================================
+
+DWORD WINAPI resync_handler::handler_thread(LPVOID lpParameter)
+{
+	return ((resync_handler *)lpParameter)->handler_thread_wt();
+}
+
+DWORD resync_handler::handler_thread_wt()
+{
+	WNDCLASSEX wc;
+	MSG msg;
+	HINSTANCE hinst = GetModuleHandle(NULL);
+
+	wc.cbSize = sizeof(wc);
+	wc.lpfnWndProc = this->resync_wnd_proc;
+	wc.style = CS_HREDRAW | CS_VREDRAW;
+	wc.cbWndExtra = 0;
+	wc.cbClsExtra = 0;
+	wc.hInstance = hinst;
+	wc.hbrBackground = 0;
+	wc.lpszMenuName = NULL;
+	wc.lpszClassName = "resync_handler";
+	wc.hIcon = NULL;
+	wc.hIconSm = wc.hIcon;
+	wc.hCursor = LoadCursor(NULL, IDC_HAND);
+
+	RegisterClassEx(&wc);
+
+	m_hwnd = CreateWindowEx(0, "resync_handler", NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hinst, NULL);
+	SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
+
+	// Register notifications of display monitor events
+	DEV_BROADCAST_DEVICEINTERFACE filter;
+	ZeroMemory(&filter, sizeof(filter));
+	filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+	filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+	filter.dbcc_classguid = GUID_DEVINTERFACE_MONITOR;
+	HDEVNOTIFY hDeviceNotify = RegisterDeviceNotification(m_hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE);
+	if (hDeviceNotify == NULL)
+		log_error("Error registering notification\n");
+
+	while (GetMessage(&msg, NULL, 0, 0))
+	{
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	}
+
+	return -1;
+}
+
+//============================================================
+//  resync_handler::wait
+//============================================================
+
+void resync_handler::wait()
+{
+	m_is_notified_1 = false;
+	m_is_notified_2 = false;
+
+	auto start = std::chrono::steady_clock::now();
+
+	while (!m_is_notified_1 || !m_is_notified_2)
+		WaitForSingleObject(m_event, 10);
+
+	auto end = std::chrono::steady_clock::now();
+	log_verbose("resync time elapsed %I64d ms\n", std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count());
+}
+
+//============================================================
+//  resync_handler::resync_wnd_proc
+//============================================================
+
+LRESULT CALLBACK resync_handler::resync_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+	resync_handler *me = reinterpret_cast<resync_handler*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+	if (me) return me->my_wnd_proc(hwnd, msg, wparam, lparam);
+
+	return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+LRESULT CALLBACK resync_handler::my_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+	switch (msg)
+	{
+		case WM_DEVICECHANGE:
+		{
+			switch (wparam)
+			{
+				case DBT_DEVICEARRIVAL:
+				{
+					log_verbose("Message: DBT_DEVICEARRIVAL\n");
+					PDEV_BROADCAST_DEVICEINTERFACE db = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
+					if (db != nullptr)
+					{
+						if (db->dbcc_classguid == GUID_DEVINTERFACE_MONITOR)
+						{
+							m_is_notified_1 = true;
+							SetEvent(m_event);
+						}
+					}
+					break;
+				}
+				case DBT_DEVICEREMOVECOMPLETE:
+					log_verbose("Message: DBT_DEVICEREMOVECOMPLETE\n");
+					break;
+				case DBT_DEVNODES_CHANGED:
+					log_verbose("Message: DBT_DEVNODES_CHANGED\n");
+					m_is_notified_2 = true;
+					SetEvent(m_event);
+					break;
+				default:
+					log_verbose("Message: WM_DEVICECHANGE message received, value %x unhandled.\n", (int)wparam);
+					break;
+			}
+			return 0;
+		}
+		break;
+
+		case WM_CLOSE:
+		{
+			PostQuitMessage(0);
+			return 0;
+		}
+
+		default:
+			return DefWindowProc(hwnd, msg, wparam, lparam);
+	}
+	return 0;
+}
diff --git a/3rdparty/switchres/resync_windows.h b/3rdparty/switchres/resync_windows.h
new file mode 100644
index 00000000000..75918dc9d20
--- /dev/null
+++ b/3rdparty/switchres/resync_windows.h
@@ -0,0 +1,43 @@
+/**************************************************************
+
+    resync_windows.h - Windows device change notifying helper
+
+    ---------------------------------------------------------
+
+    Switchres   Modeline generation engine for emulation
+
+    License     GPL-2.0+
+    Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __RESYNC_WINDOWS__
+#define __RESYNC_WINDOWS__
+
+#include <chrono>
+#include <windows.h>
+#include <dbt.h>
+
+class resync_handler
+{
+	public:
+		resync_handler();
+		~resync_handler();
+
+		void wait();
+
+	private:
+		static LRESULT CALLBACK resync_wnd_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+		LRESULT CALLBACK my_wnd_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+		static DWORD WINAPI handler_thread(LPVOID lpParameter);
+		DWORD handler_thread_wt();
+
+		HWND m_hwnd;
+		DWORD my_thread;
+		bool m_is_notified_1;
+		bool m_is_notified_2;
+		HANDLE m_event;
+};
+
+#endif
diff --git a/3rdparty/switchres/switchres.cpp b/3rdparty/switchres/switchres.cpp
new file mode 100644
index 00000000000..76dbdd6982c
--- /dev/null
+++ b/3rdparty/switchres/switchres.cpp
@@ -0,0 +1,416 @@
+/**************************************************************
+
+   switchres.cpp - Swichres manager
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <fstream>
+#include <string.h>
+#include <algorithm>
+#include "switchres.h"
+#include "log.h"
+
+using namespace std;
+const string WHITESPACE = " \n\r\t\f\v";
+
+#if defined(_WIN32)
+	#define SR_CONFIG_PATHS ";.\\;.\\ini\\;"
+#else
+	#define SR_CONFIG_PATHS ";./;./ini/;/etc/;"
+#endif
+
+//============================================================
+//  logging
+//============================================================
+
+void switchres_manager::set_log_level(int log_level) { set_log_verbosity(log_level); }
+void switchres_manager::set_log_verbose_fn(void *func_ptr) { set_log_verbose((void *)func_ptr); }
+void switchres_manager::set_log_info_fn(void *func_ptr) { set_log_info((void *)func_ptr); }
+void switchres_manager::set_log_error_fn(void *func_ptr) { set_log_error((void *)func_ptr); }
+
+//============================================================
+//  File parsing helpers
+//============================================================
+
+string ltrim(const string& s)
+{
+	size_t start = s.find_first_not_of(WHITESPACE);
+	return (start == string::npos) ? "" : s.substr(start);
+}
+
+string rtrim(const string& s)
+{
+	size_t end = s.find_last_not_of(WHITESPACE);
+	return (end == string::npos) ? "" : s.substr(0, end + 1);
+}
+
+string trim(const string& s)
+{
+	return rtrim(ltrim(s));
+}
+
+bool get_value(const string& line, string& key, string& value)
+{
+	size_t key_end = line.find_first_of(WHITESPACE);
+
+	key = line.substr(0, key_end);
+	value = ltrim(line.substr(key_end + 1));
+
+	if (key.length() > 0 && value.length() > 0)
+		return true;
+
+	return false;
+}
+
+constexpr unsigned int s2i(const char* str, int h = 0)
+{
+	return !str[h] ? 5381 : (s2i(str, h+1)*33) ^ str[h];
+}
+
+//============================================================
+//  switchres_manager::switchres_manager
+//============================================================
+
+switchres_manager::switchres_manager()
+{
+	// Create our display manager
+	m_display_factory = new display_manager();
+	m_current_display = m_display_factory;
+
+	// Set display manager default options
+	display()->set_monitor("generic_15");
+	display()->set_modeline("auto");
+	display()->set_lcd_range("auto");
+	for (int i = 0; i < MAX_RANGES; i++) display()->set_crt_range(i, "auto");
+	display()->set_screen("auto");
+	display()->set_modeline_generation(true);
+	display()->set_lock_unsupported_modes(true);
+	display()->set_lock_system_modes(true);
+	display()->set_refresh_dont_care(false);
+
+	// Set modeline generator default options
+	display()->set_interlace(true);
+	display()->set_doublescan(true);
+	display()->set_dotclock_min(0.0f);
+	display()->set_monitor_aspect(STANDARD_CRT_ASPECT);
+	display()->set_refresh_tolerance(2.0f);
+	display()->set_super_width(2560);
+	display()->set_h_shift(0);
+	display()->set_v_shift(0);
+	display()->set_h_size(1.0f);
+	display()->set_v_shift_correct(0);
+	display()->set_pixel_precision(1);
+	display()->set_interlace_force_even(0);
+	display()->set_scale_proportional(1);
+
+	// Set logger properties
+	set_log_info_fn((void*)printf);
+	set_log_error_fn((void*)printf);
+	set_log_verbose_fn((void*)printf);
+	set_log_level(2);
+}
+
+//============================================================
+//  switchres_manager::~switchres_manager
+//============================================================
+
+switchres_manager::~switchres_manager()
+{
+	if (m_display_factory) delete m_display_factory;
+
+	for (auto &display : displays)
+		delete display;
+};
+
+//============================================================
+//  switchres_manager::add_display
+//============================================================
+
+display_manager* switchres_manager::add_display(bool parse_options)
+{
+	// Parse display specific ini, if it exists
+	char file_name[32] = {0};
+	sprintf(file_name, "display%d.ini", (int)displays.size());
+	bool has_ini = parse_config(file_name);
+
+	// Create new display
+	display_manager *display = m_display_factory->make(&m_display_factory->m_ds);
+	if (display == nullptr)
+	{
+		log_error("Switchres: error adding display\n");
+		return nullptr;
+	}
+
+	m_current_display = display;
+	display->set_index(displays.size());
+	display->set_has_ini(has_ini);
+	displays.push_back(display);
+
+	log_verbose("Switchres(v%s) add display[%d]\n", SWITCHRES_VERSION, display->index());
+
+	if (parse_options)
+		display->parse_options();
+
+	return display;
+}
+
+//============================================================
+//  switchres_manager::parse_config
+//============================================================
+
+bool switchres_manager::parse_config(const char *file_name)
+{
+	ifstream config_file;
+
+	// Search for ini file in our config paths
+	auto start = 0U;
+	while (true)
+	{
+		char full_path[256] = "";
+		string paths = SR_CONFIG_PATHS;
+
+		auto end = paths.find(";", start);
+		if (end == string::npos) return false;
+
+		snprintf(full_path, sizeof(full_path), "%s%s", paths.substr(start, end - start).c_str(), file_name);
+		config_file.open(full_path);
+
+		if (config_file.is_open())
+		{
+			log_verbose("parsing %s\n", full_path);
+			break;
+		}
+		start = end + 1;
+	}
+
+	// Ini file found, parse it
+	string line;
+	while (getline(config_file, line))
+	{
+		line = trim(line);
+		if (line.length() == 0 || line.at(0) == '#')
+			continue;
+
+		string key, value;
+		if(get_value(line, key, value))
+			set_option(key.c_str(), value.c_str());
+	}
+	config_file.close();
+	return true;
+}
+
+//============================================================
+//  switchres_manager::set_option
+//============================================================
+
+void switchres_manager::set_option(const char* key, const char* value)
+{
+	switch (s2i(key))
+	{
+		// Switchres options
+		case s2i("verbose"):
+			if (atoi(value)) set_log_verbose_fn((void*)printf);
+			break;
+		case s2i("monitor"):
+			display()->set_monitor(value);
+			break;
+		case s2i("crt_range0"):
+			display()->set_crt_range(0, value);
+			break;
+		case s2i("crt_range1"):
+			display()->set_crt_range(1, value);
+			break;
+		case s2i("crt_range2"):
+			display()->set_crt_range(2, value);
+			break;
+		case s2i("crt_range3"):
+			display()->set_crt_range(3, value);
+			break;
+		case s2i("crt_range4"):
+			display()->set_crt_range(4, value);
+			break;
+		case s2i("crt_range5"):
+			display()->set_crt_range(5, value);
+			break;
+		case s2i("crt_range6"):
+			display()->set_crt_range(6, value);
+			break;
+		case s2i("crt_range7"):
+			display()->set_crt_range(7, value);
+			break;
+		case s2i("crt_range8"):
+			display()->set_crt_range(8, value);
+			break;
+		case s2i("crt_range9"):
+			display()->set_crt_range(9, value);
+			break;
+		case s2i("lcd_range"):
+			display()->set_lcd_range(value);
+			break;
+		case s2i("modeline"):
+			display()->set_modeline(value);
+			break;
+		case s2i("user_mode"):
+		{
+			modeline user_mode = {};
+			if (strcmp(value, "auto"))
+			{
+				if (sscanf(value, "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
+				{
+					log_error("Error: use format resolution <w>x<h>@<r>\n");
+					break;
+				}
+			}
+			display()->set_user_mode(&user_mode);
+			break;
+		}
+
+		// Display options
+		case s2i("display"):
+			display()->set_screen(value);
+			break;
+		case s2i("api"):
+			display()->set_api(value);
+			break;
+		case s2i("modeline_generation"):
+			display()->set_modeline_generation(atoi(value));
+			break;
+		case s2i("lock_unsupported_modes"):
+			display()->set_lock_unsupported_modes(atoi(value));
+			break;
+		case s2i("lock_system_modes"):
+			display()->set_lock_system_modes(atoi(value));
+			break;
+		case s2i("refresh_dont_care"):
+			display()->set_refresh_dont_care(atoi(value));
+			break;
+		case s2i("keep_changes"):
+			display()->set_keep_changes(atoi(value));
+			break;
+
+		// Modeline generation options
+		case s2i("interlace"):
+			display()->set_interlace(atoi(value));
+			break;
+		case s2i("doublescan"):
+			display()->set_doublescan(atoi(value));
+			break;
+		case s2i("dotclock_min"):
+		{
+			double pclock_min = 0.0f;
+			sscanf(value, "%lf", &pclock_min);
+			display()->set_dotclock_min(pclock_min);
+			break;
+		}
+		case s2i("sync_refresh_tolerance"):
+		{
+			double refresh_tolerance = 0.0f;
+			sscanf(value, "%lf", &refresh_tolerance);
+			display()->set_refresh_tolerance(refresh_tolerance);
+			break;
+		}
+		case s2i("super_width"):
+		{
+			int super_width = 0;
+			sscanf(value, "%d", &super_width);
+			display()->set_super_width(super_width);
+			break;
+		}
+		case s2i("aspect"):
+			display()->set_monitor_aspect(value);
+			break;
+		case s2i("h_size"):
+		{
+			double h_size = 1.0f;
+			sscanf(value, "%lf", &h_size);
+			display()->set_h_size(h_size);
+			break;
+		}
+		case s2i("h_shift"):
+		{
+			int h_shift = 0;
+			sscanf(value, "%d", &h_shift);
+			display()->set_h_shift(h_shift);
+			break;
+		}
+		case s2i("v_shift"):
+		{
+			int v_shift = 0;
+			sscanf(value, "%d", &v_shift);
+			display()->set_v_shift(v_shift);
+			break;
+		}
+		case s2i("v_shift_correct"):
+			display()->set_v_shift_correct(atoi(value));
+			break;
+
+		case s2i("pixel_precision"):
+			display()->set_pixel_precision(atoi(value));
+			break;
+
+		case s2i("interlace_force_even"):
+			display()->set_interlace_force_even(atoi(value));
+			break;
+
+		case s2i("scale_proportional"):
+			display()->set_scale_proportional(atoi(value));
+			break;
+
+		// Custom video backend options
+		case s2i("screen_compositing"):
+			display()->set_screen_compositing(atoi(value));
+			break;
+		case s2i("screen_reordering"):
+			display()->set_screen_reordering(atoi(value));
+			break;
+		case s2i("allow_hardware_refresh"):
+			display()->set_allow_hardware_refresh(atoi(value));
+			break;
+		case s2i("custom_timing"):
+			display()->set_custom_timing(value);
+			break;
+
+		// Various
+		case s2i("verbosity"):
+		{
+			int verbosity_level = 1;
+			sscanf(value, "%d", &verbosity_level);
+			set_log_level(verbosity_level);
+			break;
+		}
+
+		default:
+			log_error("Invalid option %s\n", key);
+			break;
+	}
+}
+
+//============================================================
+//  switchres_manager::set_current_display
+//============================================================
+
+void switchres_manager::set_current_display(int index)
+{
+	int disp_index;
+
+	if (index == -1)
+	{
+		m_current_display = m_display_factory;
+		return;
+	}
+	else if (index < 0 || index >= (int)displays.size())
+		disp_index = 0;
+
+	else
+		disp_index = index;
+
+	m_current_display = displays[disp_index];
+}
diff --git a/3rdparty/switchres/switchres.h b/3rdparty/switchres/switchres.h
new file mode 100644
index 00000000000..c072cc12aa4
--- /dev/null
+++ b/3rdparty/switchres/switchres.h
@@ -0,0 +1,70 @@
+/**************************************************************
+
+   switchres.h - SwichRes general header
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef __SWITCHRES_H__
+#define __SWITCHRES_H__
+
+#include <cstring>
+#include <vector>
+#include "monitor.h"
+#include "modeline.h"
+#include "display.h"
+#include "edid.h"
+
+//============================================================
+//  CONSTANTS
+//============================================================
+
+#ifndef SWITCHRES_VERSION
+#define SWITCHRES_VERSION "2.2.1"
+#endif
+
+
+class switchres_manager
+{
+public:
+
+	switchres_manager();
+	~switchres_manager();
+
+	// getters
+	display_manager *display() const { return m_current_display; }
+	display_manager *display(int i) const { return i < (int)displays.size()? displays[i] : nullptr; }
+	display_manager *display_factory() const { return m_display_factory; }
+	static char* get_version() { return (char*) SWITCHRES_VERSION; };
+
+	// setters (log manager)
+	void set_log_level(int log_level);
+	void set_log_verbose_fn(void *func_ptr);
+	void set_log_info_fn(void *func_ptr);
+	void set_log_error_fn(void *func_ptr);
+
+	void set_current_display(int index);
+	void set_option(const char* key, const char* value);
+
+	// interface
+	display_manager* add_display(bool parse_options = true);
+	bool parse_config(const char *file_name);
+
+	// display list
+	std::vector<display_manager *> displays;
+
+private:
+
+	display_manager *m_display_factory = 0;
+	display_manager *m_current_display = 0;
+};
+
+
+#endif
diff --git a/3rdparty/switchres/switchres_defines.h b/3rdparty/switchres/switchres_defines.h
new file mode 100644
index 00000000000..42470eda9bf
--- /dev/null
+++ b/3rdparty/switchres/switchres_defines.h
@@ -0,0 +1,42 @@
+#define SR_DEFINES
+
+#define  SR_OPT_MONITOR                 "monitor"
+#define  SR_OPT_CRT_RANGE0              "crt_range0"
+#define  SR_OPT_CRT_RANGE1              "crt_range1"
+#define  SR_OPT_CRT_RANGE2              "crt_range2"
+#define  SR_OPT_CRT_RANGE3              "crt_range3"
+#define  SR_OPT_CRT_RANGE4              "crt_range4"
+#define  SR_OPT_CRT_RANGE5              "crt_range5"
+#define  SR_OPT_CRT_RANGE6              "crt_range6"
+#define  SR_OPT_CRT_RANGE7              "crt_range7"
+#define  SR_OPT_CRT_RANGE8              "crt_range8"
+#define  SR_OPT_CRT_RANGE9              "crt_range9"
+#define  SR_OPT_LCD_RANGE               "lcd_range"
+#define  SR_OPT_MODELINE                "modeline"
+#define  SR_OPT_USER_MODE               "user_mode"
+#define  SR_OPT_DISPLAY                 "display"
+#define  SR_OPT_API                     "api"
+#define  SR_OPT_LOCK_UNSUPPORTED_MODES  "lock_unsupported_modes"
+#define  SR_OPT_LOCK_SYSTEM_MODES       "lock_system_modes"
+#define  SR_OPT_REFRESH_DONT_CARE       "refresh_dont_care"
+#define  SR_OPT_KEEP_CHANGES            "keep_changes"
+#define  SR_OPT_MODELINE_GENERATION     "modeline_generation"
+#define  SR_OPT_INTERLACE               "interlace"
+#define  SR_OPT_DOUBLESCAN              "doublescan"
+#define  SR_OPT_DOTCLOCK_MIN            "dotclock_min"
+#define  SR_OPT_SYNC_REFRESH_TOLERANCE  "sync_refresh_tolerance"
+#define  SR_OPT_SUPER_WIDTH             "super_width"
+#define  SR_OPT_ASPECT                  "aspect"
+#define  SR_OPT_V_SHIFT_CORRECT         "v_shift_correct"
+#define  SR_OPT_H_SIZE                  "h_size"
+#define  SR_OPT_H_SHIFT                 "h_shift"
+#define  SR_OPT_V_SHIFT                 "v_shift"
+#define  SR_OPT_PIXEL_PRECISION         "pixel_precision"
+#define  SR_OPT_INTERLACE_FORCE_EVEN    "interlace_force_even"
+#define  SR_OPT_SCALE_PROPORTIONAL      "scale_proportional"
+#define  SR_OPT_SCREEN_COMPOSITING      "screen_compositing"
+#define  SR_OPT_SCREEN_REORDERING       "screen_reordering"
+#define  SR_OPT_ALLOW_HARDWARE_REFRESH  "allow_hardware_refresh"
+#define  SR_OPT_CUSTOM_TIMING           "custom_timing"
+#define  SR_OPT_VERBOSE                 "verbose"
+#define  SR_OPT_VERBOSITY               "verbosity"
\ No newline at end of file
diff --git a/3rdparty/switchres/switchres_main.cpp b/3rdparty/switchres/switchres_main.cpp
new file mode 100644
index 00000000000..5514fde0122
--- /dev/null
+++ b/3rdparty/switchres/switchres_main.cpp
@@ -0,0 +1,466 @@
+/**************************************************************
+
+   switchres_main.cpp - Swichres standalone launcher
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <iostream>
+#include <cstring>
+#include <getopt.h>
+#include "switchres.h"
+#include "switchres_defines.h"
+#include "log.h"
+
+using namespace std;
+
+int show_version();
+int show_usage();
+
+enum
+ {
+	OPT_CRT_RANGE0 = 128,
+	OPT_CRT_RANGE1,
+	OPT_CRT_RANGE2,
+	OPT_CRT_RANGE3,
+	OPT_CRT_RANGE4,
+	OPT_CRT_RANGE5,
+	OPT_CRT_RANGE6,
+	OPT_CRT_RANGE7,
+	OPT_CRT_RANGE8,
+	OPT_CRT_RANGE9,
+	OPT_LCD_RANGE,
+	OPT_MODELINE,
+	OPT_USER_MODE,
+	OPT_API,
+	OPT_LOCK_UNSUPPORTED_MODES,
+	OPT_LOCK_SYSTEM_MODES,
+	OPT_REFRESH_DONT_CARE,
+	OPT_KEEP_CHANGES,
+	OPT_MODELINE_GENERATION,
+	OPT_INTERLACE,
+	OPT_DOUBLESCAN,
+	OPT_DOTCLOCK_MIN,
+	OPT_SYNC_REFRESH_TOLERANCE,
+	OPT_SUPER_WIDTH,
+	OPT_V_SHIFT_CORRECT,
+	OPT_H_SIZE,
+	OPT_H_SHIFT,
+	OPT_V_SHIFT,
+	OPT_PIXEL_PRECISION,
+	OPT_INTERLACE_FORCE_EVEN,
+	OPT_SCALE_PROPORTIONAL,
+	OPT_SCREEN_COMPOSITING,
+	OPT_SCREEN_REORDERING,
+	OPT_ALLOW_HARDWARE_REFRESH,
+	OPT_CUSTOM_TIMING,
+	OPT_VERBOSITY
+ };
+
+//============================================================
+//  main
+//============================================================
+
+int main(int argc, char **argv)
+{
+
+	switchres_manager switchres;
+	display_manager* df = switchres.display_factory();
+
+	switchres.parse_config("switchres.ini");
+
+	int width = 0;
+	int height = 0;
+	float refresh = 0.0;
+	modeline user_mode = {};
+	int index = 0;
+
+	int version_flag = false;
+	bool help_flag = false;
+	bool resolution_flag = false;
+	bool calculate_flag = false;
+	bool edid_flag = false;
+	bool switch_flag = false;
+	bool launch_flag = false;
+	bool force_flag = false;
+	bool interlaced_flag = false;
+	bool rotated_flag = false;
+	bool user_ini_flag = false;
+	bool keep_changes_flag = false;
+	bool geometry_flag = false;
+	int status_code = 0;
+
+	string ini_file;
+	string launch_command;
+
+	while (1)
+	{
+		static struct option long_options[] =
+		{
+			// Options unique to standalone Switchres
+			{"version",     no_argument,       &version_flag, '1'},
+			{"help",        no_argument,       0, 'h'},
+			{"calc",        no_argument,       0, 'c'},
+			{"switch",      no_argument,       0, 's'},
+			{"launch",      required_argument, 0, 'l'},
+			{"edid",        no_argument,       0, 'e'},
+			{"rotated",     no_argument,       0, 'r'},
+			{"force",       required_argument, 0, 'f'}, // equ. --user_mode
+			{"ini",         required_argument, 0, 'i'},
+			{"keep",        no_argument,       0, 'k'}, // equ. --keep_changes
+			{"geometry",    required_argument, 0, 'g'},
+			// Options available in short and long forms
+			{SR_OPT_VERBOSE,                no_argument,       0, 'v'},
+			{SR_OPT_DISPLAY,                required_argument, 0, 'd'},
+			{SR_OPT_MONITOR,                required_argument, 0, 'm'},
+			{SR_OPT_ASPECT,                 required_argument, 0, 'a'},
+			// Long options, from switchres.ini
+			{SR_OPT_CRT_RANGE0,             required_argument, 0, OPT_CRT_RANGE0},
+			{SR_OPT_CRT_RANGE1,             required_argument, 0, OPT_CRT_RANGE1},
+			{SR_OPT_CRT_RANGE2,             required_argument, 0, OPT_CRT_RANGE2},
+			{SR_OPT_CRT_RANGE3,             required_argument, 0, OPT_CRT_RANGE3},
+			{SR_OPT_CRT_RANGE4,             required_argument, 0, OPT_CRT_RANGE4},
+			{SR_OPT_CRT_RANGE5,             required_argument, 0, OPT_CRT_RANGE5},
+			{SR_OPT_CRT_RANGE6,             required_argument, 0, OPT_CRT_RANGE6},
+			{SR_OPT_CRT_RANGE7,             required_argument, 0, OPT_CRT_RANGE7},
+			{SR_OPT_CRT_RANGE8,             required_argument, 0, OPT_CRT_RANGE8},
+			{SR_OPT_CRT_RANGE9,             required_argument, 0, OPT_CRT_RANGE9},
+			{SR_OPT_LCD_RANGE,              required_argument, 0, OPT_LCD_RANGE},
+			{SR_OPT_MODELINE,               required_argument, 0, OPT_MODELINE},
+			{SR_OPT_USER_MODE,              required_argument, 0, OPT_USER_MODE},
+			{SR_OPT_API,                    required_argument, 0, OPT_API},
+			{SR_OPT_LOCK_UNSUPPORTED_MODES, required_argument, 0, OPT_LOCK_UNSUPPORTED_MODES},
+			{SR_OPT_LOCK_SYSTEM_MODES,      required_argument, 0, OPT_LOCK_SYSTEM_MODES},
+			{SR_OPT_REFRESH_DONT_CARE,      required_argument, 0, OPT_REFRESH_DONT_CARE},
+			{SR_OPT_KEEP_CHANGES,           required_argument, 0, OPT_KEEP_CHANGES},
+			{SR_OPT_MODELINE_GENERATION,    required_argument, 0, OPT_MODELINE_GENERATION},
+			{SR_OPT_INTERLACE,              required_argument, 0, OPT_INTERLACE},
+			{SR_OPT_DOUBLESCAN,             required_argument, 0, OPT_DOUBLESCAN},
+			{SR_OPT_DOTCLOCK_MIN,           required_argument, 0, OPT_DOTCLOCK_MIN},
+			{SR_OPT_SYNC_REFRESH_TOLERANCE, required_argument, 0, OPT_SYNC_REFRESH_TOLERANCE},
+			{SR_OPT_SUPER_WIDTH,            required_argument, 0, OPT_SUPER_WIDTH},
+			{SR_OPT_V_SHIFT_CORRECT,        required_argument, 0, OPT_V_SHIFT_CORRECT},
+			{SR_OPT_H_SIZE,                 required_argument, 0, OPT_H_SIZE},
+			{SR_OPT_H_SHIFT,                required_argument, 0, OPT_H_SHIFT},
+			{SR_OPT_V_SHIFT,                required_argument, 0, OPT_V_SHIFT},
+			{SR_OPT_PIXEL_PRECISION,        required_argument, 0, OPT_PIXEL_PRECISION},
+			{SR_OPT_INTERLACE_FORCE_EVEN,   required_argument, 0, OPT_INTERLACE_FORCE_EVEN},
+			{SR_OPT_SCALE_PROPORTIONAL,     required_argument, 0, OPT_SCALE_PROPORTIONAL},
+			{SR_OPT_SCREEN_COMPOSITING,     required_argument, 0, OPT_SCREEN_COMPOSITING},
+			{SR_OPT_SCREEN_REORDERING,      required_argument, 0, OPT_SCREEN_REORDERING},
+			{SR_OPT_ALLOW_HARDWARE_REFRESH, required_argument, 0, OPT_ALLOW_HARDWARE_REFRESH},
+			{SR_OPT_CUSTOM_TIMING,          required_argument, 0, OPT_CUSTOM_TIMING},
+			{SR_OPT_VERBOSITY,              required_argument, 0, OPT_VERBOSITY},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:kg:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		if (version_flag)
+		{
+			show_version();
+			return 0;
+		}
+
+		switch (c)
+		{
+			case 'v':
+				switchres.set_log_level(3);
+				switchres.set_log_error_fn((void*)printf);
+				switchres.set_log_info_fn((void*)printf);
+				switchres.set_log_verbose_fn((void*)printf);
+				break;
+
+			case 'h':
+				help_flag = true;
+				break;
+
+			case 'c':
+				calculate_flag = true;
+				break;
+
+			case 's':
+				switch_flag = true;
+				break;
+
+			case 'l':
+				launch_flag = true;
+				launch_command = optarg;
+				break;
+
+			case 'm':
+				df->set_monitor(optarg);
+				break;
+
+			case 'r':
+				rotated_flag = true;
+				break;
+
+			case 'd':
+				// Add new display in multi-monitor case
+				if (index > 0) switchres.add_display();
+				index ++;
+				switchres.set_current_display(-1);
+				switchres.set_option(SR_OPT_DISPLAY, optarg);
+				break;
+
+			case 'a':
+				df->set_monitor_aspect(optarg);
+				break;
+
+			case 'e':
+				edid_flag = true;
+				break;
+
+			case 'f':
+			case OPT_USER_MODE:
+				force_flag = true;
+				if (sscanf(optarg, "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
+					log_error("Error: use format <w>x<h>@<r>\n");
+				break;
+
+			case 'i':
+				user_ini_flag = true;
+				ini_file = optarg;
+				break;
+
+			case 'b':
+				df->set_api(optarg);
+				break;
+
+			case 'k':
+				keep_changes_flag = true;
+				df->set_keep_changes(true);
+				break;
+
+			case 'g':
+				double h_size; int h_shift, v_shift;
+				if (sscanf(optarg, "%lf:%d:%d", &h_size, &h_shift, &v_shift) < 3)
+					log_error("Error: use format --geometry <h_size>:<h_shift>:<v_shift>\n");
+				geometry_flag = true;
+				df->set_h_size(h_size);
+				df->set_h_shift(h_shift);
+				df->set_v_shift(v_shift);
+				break;
+
+			// Long options
+			case OPT_CRT_RANGE0:
+			case OPT_CRT_RANGE1:
+			case OPT_CRT_RANGE2:
+			case OPT_CRT_RANGE3:
+			case OPT_CRT_RANGE4:
+			case OPT_CRT_RANGE5:
+			case OPT_CRT_RANGE6:
+			case OPT_CRT_RANGE7:
+			case OPT_CRT_RANGE8:
+			case OPT_CRT_RANGE9:
+			case OPT_LCD_RANGE:
+			case OPT_MODELINE:
+			case OPT_API:
+			case OPT_LOCK_UNSUPPORTED_MODES:
+			case OPT_LOCK_SYSTEM_MODES:
+			case OPT_REFRESH_DONT_CARE:
+			case OPT_KEEP_CHANGES:
+			case OPT_MODELINE_GENERATION:
+			case OPT_INTERLACE:
+			case OPT_DOUBLESCAN:
+			case OPT_DOTCLOCK_MIN:
+			case OPT_SYNC_REFRESH_TOLERANCE:
+			case OPT_SUPER_WIDTH:
+			case OPT_V_SHIFT_CORRECT:
+			case OPT_H_SIZE:
+			case OPT_H_SHIFT:
+			case OPT_V_SHIFT:
+			case OPT_PIXEL_PRECISION:
+			case OPT_INTERLACE_FORCE_EVEN:
+			case OPT_SCALE_PROPORTIONAL:
+			case OPT_SCREEN_COMPOSITING:
+			case OPT_SCREEN_REORDERING:
+			case OPT_ALLOW_HARDWARE_REFRESH:
+			case OPT_CUSTOM_TIMING:
+			case OPT_VERBOSITY:
+				switchres.set_option(long_options[option_index].name, optarg);
+				break;
+
+			default:
+				return 0;
+		}
+	}
+
+	if (help_flag)
+		goto usage;
+
+	// Get user video mode information from command line
+	if ((argc - optind) < 3)
+	{
+		log_error("Error: missing argument\n");
+		goto usage;
+	}
+	else if ((argc - optind) > 3)
+	{
+		log_error("Error: too many arguments\n");
+		goto usage;
+	}
+	else
+	{
+		resolution_flag = true;
+		width = atoi(argv[optind]);
+		height = atoi(argv[optind + 1]);
+		refresh = atof(argv[optind + 2]);
+
+		if (width <= 0 || height <= 0 || refresh <= 0.0f)
+		{
+			log_error("Error: wrong video mode request: %sx%s@%s\n", argv[optind], argv[optind + 1], argv[optind + 2]);
+			goto usage;
+		}
+
+		char scan_mode = argv[optind + 2][strlen(argv[optind + 2]) -1];
+		if (scan_mode == 'i')
+			interlaced_flag = true;
+	}
+
+	if (user_ini_flag)
+		switchres.parse_config(ini_file.c_str());
+
+	if (calculate_flag)
+		switchres.display()->set_screen("dummy");
+
+	switchres.add_display();
+
+	if (force_flag)
+		switchres.display()->set_user_mode(&user_mode);
+
+	if (!calculate_flag && !edid_flag)
+	{
+		for (auto &display : switchres.displays)
+			display->init();
+	}
+
+	if (resolution_flag)
+	{
+		for (auto &display : switchres.displays)
+		{
+			int flags = (interlaced_flag? SR_MODE_INTERLACED : 0) | (rotated_flag? SR_MODE_ROTATED : 0);
+			modeline *mode = display->get_mode(width, height, refresh, flags);
+			if (mode) display->flush_modes();
+
+			if (mode && geometry_flag)
+			{
+				monitor_range range = {};
+				modeline_to_monitor_range(&range, mode);
+				log_info("Adjusted geometry (%.3f:%d:%d) H: %.3f, %.3f, %.3f V: %.3f, %.3f, %.3f\n",
+						display->h_size(), display->h_shift(), display->v_shift(),
+						range.hfront_porch, range.hsync_pulse, range.hback_porch,
+						range.vfront_porch * 1000, range.vsync_pulse * 1000, range.vback_porch * 1000);
+			}
+		}
+
+		if (edid_flag)
+		{
+			edid_block edid = {};
+			modeline *mode = switchres.display()->selected_mode();
+			if (mode)
+			{
+				monitor_range *range = &switchres.display()->range[mode->range];
+				edid_from_modeline(mode, range, switchres.display()->monitor(), &edid);
+
+				char file_name[strlen(switchres.display()->monitor()) + 5];
+				sprintf(file_name, "%s.bin", switchres.display()->monitor());
+
+				FILE *file = fopen(file_name, "wb");
+				if (file)
+				{
+					fwrite(&edid, sizeof(edid), 1, file);
+					fclose (file);
+					log_info("EDID saved as %s\n", file_name);
+				}
+			}
+		}
+
+		if (switch_flag) for (auto &display : switchres.displays) display->set_mode(display->selected_mode());
+
+		if (switch_flag && !launch_flag && !keep_changes_flag)
+		{
+			log_info("Press ENTER to exit...\n");
+			cin.get();
+		}
+
+		if (launch_flag)
+		{
+			status_code = system(launch_command.c_str());
+			#ifdef __linux__
+			status_code = WEXITSTATUS(status_code);
+			#endif
+			log_info("Process exited with value %d\n", status_code);
+		}
+	}
+
+	return (status_code);
+
+usage:
+	show_usage();
+	return 0;
+}
+
+//============================================================
+//  show_version
+//============================================================
+
+int show_version()
+{
+	char version[]
+	{
+		"Switchres " SWITCHRES_VERSION "\n"
+		"Modeline generation engine for emulation\n"
+		"Copyright (C) 2010-2024 - Chris Kennedy, Antonio Giner, Alexandre Wodarczyk, Gil Delescluse\n"
+		"License GPL-2.0+\n"
+		"This is free software: you are free to change and redistribute it.\n"
+		"There is NO WARRANTY, to the extent permitted by law.\n"
+	};
+
+	log_info("%s", version);
+	return 0;
+}
+
+//============================================================
+//  show_usage
+//============================================================
+
+int show_usage()
+{
+	char usage[] =
+	{
+		"Usage: switchres <width> <height> <refresh> [options]\n"
+		"Options:\n"
+		"  -c, --calc                        Calculate video mode and exit\n"
+		"  -s, --switch                      Switch to video mode\n"
+		"  -l, --launch <command>            Launch <command>\n"
+		"  -m, --monitor <preset>            Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)\n"
+		"  -a, --aspect <num:den>            Monitor aspect ratio\n"
+		"  -r, --rotated                     Rotate axes, preserving aspect ratio.\n"
+		"  -d, --display <display_index>     Use target display (index = 0, 1, 2...)\n"
+		"  -f, --force <w>x<h>@<r>           Force a specific video mode from display mode list\n"
+		"  -i, --ini <file.ini>              Specify an ini file\n"
+		"  -e, --edid                        Create an EDID binary with calculated video modes\n"
+		"  -k, --keep                        Keep changes on exit (warning: this disables cleanup)\n"
+		"  -g, --geometry <adjustment>       Adjust geometry of generated modeline\n"
+		"                                    adjustment = <h_size>:<h_shift>:<v_shift>\n"
+		"                                    e.g. switchres 640 480 60 -c -g 1.1:-1:2\n\n"
+		"For more options, refer to switchres.ini. All options in switchres.ini can be applied in\n"
+		"command line as long options, e.g.: switchres 256 224 57.55 -c --dotclock_min 8.0\n\n"
+	};
+
+	log_info("%s", usage);
+	return 0;
+}
diff --git a/3rdparty/switchres/switchres_wrapper.cpp b/3rdparty/switchres/switchres_wrapper.cpp
new file mode 100644
index 00000000000..11fef0eb49d
--- /dev/null
+++ b/3rdparty/switchres/switchres_wrapper.cpp
@@ -0,0 +1,435 @@
+/**************************************************************
+
+   switchres_wrapper.cpp - Switchres C wrapper API
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2022 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#define MODULE_API_EXPORTS
+#include "switchres.h"
+#include "switchres_wrapper.h"
+#include "log.h"
+#include <stdio.h>
+#include <locale>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SR_ACTION_ADD         1<<0
+#define SR_ACTION_GET_FROM_ID 1<<1
+#define SR_ACTION_FLUSH       1<<2
+#define SR_ACTION_SWITCH      1<<3
+
+
+//============================================================
+//  PROTOTYPES
+//============================================================
+
+int sr_mode_internal(int width, int height, double refresh, int flags, sr_mode *srm, int action, const char *caller);
+void modeline_to_sr_mode(modeline* m, sr_mode* srm);
+
+
+//============================================================
+//  GLOBALS
+//============================================================
+
+// Switchres manager object
+switchres_manager* swr;
+
+
+//============================================================
+// Start of Switchres API
+//============================================================
+
+//============================================================
+//  sr_init
+//============================================================
+
+MODULE_API void sr_init()
+{
+	setlocale(LC_NUMERIC, "C");
+	swr = new switchres_manager;
+	swr->parse_config("switchres.ini");
+}
+
+
+//============================================================
+//  sr_get_version
+//============================================================
+
+MODULE_API char* sr_get_version() {
+	return switchres_manager::get_version();
+}
+
+//============================================================
+//  sr_deinit
+//============================================================
+
+MODULE_API void sr_deinit()
+{
+	delete swr;
+}
+
+
+//============================================================
+//  sr_init_disp
+//============================================================
+
+MODULE_API int sr_init_disp(const char* screen, void* pfdata)
+{
+	if (screen)
+		swr->display_factory()->set_screen(screen);
+
+	display_manager *disp = swr->add_display();
+	if (disp == nullptr)
+	{
+		log_error("%s: error, couldn't add a display\n", __FUNCTION__);
+		return -1;
+	}
+
+	if (!disp->init(pfdata))
+	{
+		log_error("%s: error, couldn't init the display\n", __FUNCTION__);
+		return -1;
+	}
+
+	return disp->index();
+}
+
+
+//============================================================
+//  sr_init_disp
+//============================================================
+
+MODULE_API void sr_set_disp(int index)
+{
+	swr->set_current_display(index);
+}
+
+
+//============================================================
+//  sr_load_ini
+//============================================================
+
+MODULE_API void sr_load_ini(char* config)
+{
+	swr->parse_config(config);
+	swr->display()->parse_options();
+}
+
+
+//============================================================
+//  sr_set_option
+//============================================================
+
+MODULE_API void sr_set_option(const char* key, const char* value)
+{
+	swr->set_option(key, value);
+}
+
+
+//============================================================
+//  sr_get_state
+//============================================================
+
+MODULE_API void sr_get_state(sr_state *state)
+{
+	if (state == nullptr)
+		return;
+
+	*state = {};
+
+	sprintf(state->monitor, "%s", swr->display()->monitor());
+	state->modeline_generation = swr->display()->modeline_generation();
+	state->desktop_is_rotated =  swr->display()->desktop_is_rotated();
+	state->interlace =           swr->display()->interlace();
+	state->doublescan =          swr->display()->doublescan();
+	state->dotclock_min =        swr->display()->dotclock_min();
+	state->refresh_tolerance =   swr->display()->refresh_tolerance();
+	state->super_width =         swr->display()->super_width();
+	state->monitor_aspect =      swr->display()->monitor_aspect();
+	state->h_size =              swr->display()->h_size();
+	state->h_shift =             swr->display()->h_shift();
+	state->v_shift =             swr->display()->v_shift();
+	state->pixel_precision =     swr->display()->pixel_precision();
+	state->selected_mode =       swr->display()->selected_mode() == nullptr? -1 : swr->display()->selected_mode()->id;
+	state->current_mode =        swr->display()->current_mode() == nullptr? -1 : swr->display()->current_mode()->id;
+}
+
+
+//============================================================
+//  sr_set_monitor
+//============================================================
+
+MODULE_API void sr_set_monitor(const char *preset)
+{
+	swr->display()->set_monitor(preset);
+}
+
+
+//============================================================
+//  sr_set_user_mode
+//============================================================
+
+MODULE_API void sr_set_user_mode(int width, int height, int refresh)
+{
+	modeline user_mode = {};
+	user_mode.width = width;
+	user_mode.height = height;
+	user_mode.refresh = refresh;
+	swr->display()->set_user_mode(&user_mode);
+}
+
+
+//============================================================
+//  sr_get_mode
+//============================================================
+
+MODULE_API int sr_get_mode(int id, sr_mode *srm)
+{
+	if (srm == nullptr)
+		return 0;
+
+	*srm = {};
+	srm->id = id;
+
+	return sr_mode_internal(0, 0, 0, 0, srm, SR_ACTION_GET_FROM_ID, __FUNCTION__);
+}
+
+
+//============================================================
+//  sr_add_mode
+//============================================================
+
+MODULE_API int sr_add_mode(int width, int height, double refresh, int flags, sr_mode *srm)
+{
+	bool flush = !(flags & SR_MODE_DONT_FLUSH);
+	return sr_mode_internal(width, height, refresh, flags, srm, SR_ACTION_ADD | (flush? SR_ACTION_FLUSH: 0), __FUNCTION__);
+}
+
+
+//============================================================
+//  sr_flush
+//============================================================
+
+MODULE_API int sr_flush()
+{
+	return sr_mode_internal(0, 0, 0, 0, 0, SR_ACTION_FLUSH, __FUNCTION__);
+}
+
+
+//============================================================
+//  sr_switch_to_mode
+//============================================================
+
+MODULE_API int sr_switch_to_mode(int width, int height, double refresh, int flags, sr_mode *srm)
+{
+	return sr_mode_internal(width, height, refresh, flags, srm, SR_ACTION_ADD | SR_ACTION_FLUSH | SR_ACTION_SWITCH, __FUNCTION__);
+}
+
+
+//============================================================
+//  sr_set_mode
+//============================================================
+
+MODULE_API int sr_set_mode(int id)
+{
+	sr_mode srm = {};
+	srm.id = id;
+
+	return sr_mode_internal(0, 0, 0, 0, &srm, SR_ACTION_GET_FROM_ID | SR_ACTION_SWITCH, __FUNCTION__);
+}
+
+
+//============================================================
+//  sr_set_log_level
+//============================================================
+
+MODULE_API void sr_set_log_level(int l)
+{
+	swr->set_log_level(l);
+}
+
+
+//============================================================
+//  sr_set_log_callbacks
+//============================================================
+
+MODULE_API void sr_set_log_callback_info(void * f)
+{
+	swr->set_log_info_fn((void *)f);
+}
+
+MODULE_API void sr_set_log_callback_debug(void * f)
+{
+	swr->set_log_verbose_fn((void *)f);
+}
+
+MODULE_API void sr_set_log_callback_error(void * f)
+{
+	swr->set_log_error_fn((void *)f);
+}
+
+
+//============================================================
+//  srlib
+//============================================================
+
+MODULE_API srAPI srlib =
+{
+	sr_init,
+	sr_load_ini,
+	sr_get_version,
+	sr_deinit,
+	sr_init_disp,
+	sr_set_disp,
+	sr_get_mode,
+	sr_add_mode,
+	sr_switch_to_mode,
+	sr_flush,
+	sr_set_mode,
+	sr_set_monitor,
+	sr_set_user_mode,
+	sr_set_option,
+	sr_get_state,
+	sr_set_log_level,
+	sr_set_log_callback_error,
+	sr_set_log_callback_info,
+	sr_set_log_callback_debug,
+};
+
+
+//============================================================
+// End of Switchres API
+//============================================================
+
+//============================================================
+//  sr_mode_internal
+//============================================================
+
+int sr_mode_internal(int width, int height, double refresh, int flags, sr_mode *srm, int action, const char *caller)
+{
+	display_manager *disp = swr->display();
+	if (disp == nullptr)
+	{
+		log_error("%s: error, didn't get a display\n", caller);
+		return 0;
+	}
+
+	if (action & SR_ACTION_ADD)
+	{
+		if (srm == nullptr)
+		{
+			log_error("%s: error, invalid sr_mode pointer\n", caller);
+			return 0;
+		}
+
+		disp->get_mode(width, height, refresh, flags);
+		if (disp->got_mode())
+		{
+			log_verbose("%s: got mode %dx%d@%f type(%x)\n", caller, disp->width(), disp->height(), disp->v_freq(), disp->selected_mode()->type);
+			modeline_to_sr_mode(disp->selected_mode(), srm);
+		}
+		else
+		{
+			log_error("%s: error getting mode\n", caller);
+			return 0;
+		}
+	}
+
+	else if (action & SR_ACTION_GET_FROM_ID)
+	{
+		bool found = false;
+
+		for (auto mode : disp->video_modes)
+		{
+			if (mode.id == srm->id)
+			{
+				found = true;
+				disp->set_selected_mode(&mode);
+				log_verbose("%s: got mode %dx%d@%f type(%x)\n", caller, disp->width(), disp->height(), disp->v_freq(), disp->selected_mode()->type);
+				modeline_to_sr_mode(disp->selected_mode(), srm);
+				break;
+			}
+		}
+
+		if (!found)
+		{
+			log_error("%s: mode ID %d not found\n", caller, srm->id);
+			return 0;
+		}
+	}
+
+	if (action & SR_ACTION_FLUSH)
+	{
+		if (!disp->flush_modes())
+		{
+			log_error("%s: error flushing display\n", caller);
+			return 0;
+		}
+	}
+
+	if (action & SR_ACTION_SWITCH)
+	{
+		if (disp->is_switching_required() || disp->current_mode() != disp->selected_mode())
+		{
+			if (disp->set_mode(disp->selected_mode()))
+				log_info("%s: successfully switched to %dx%d@%f\n", caller, disp->width(), disp->height(), disp->v_freq());
+			else
+			{
+				log_error("%s: error switching to %dx%d@%f\n", caller, disp->width(), disp->height(), disp->v_freq());
+				return 0;
+			}
+		}
+		else
+			log_info("%s: switching not required\n", caller);
+	}
+
+	return 1;
+}
+
+
+//============================================================
+//  modeline_to_sr_mode
+//============================================================
+
+void modeline_to_sr_mode(modeline* m, sr_mode* srm)
+{
+	srm->width          = m->hactive;
+	srm->height         = m->vactive;
+	srm->refresh        = m->refresh;
+	//
+	srm->vfreq          = m->vfreq;
+	srm->hfreq          = m->hfreq;
+	//
+	srm->pclock         = m->pclock;
+	srm->hbegin         = m->hbegin;
+	srm->hend           = m->hend;
+	srm->htotal         = m->htotal;
+	srm->vbegin         = m->vbegin;
+	srm->vend           = m->vend;
+	srm->vtotal         = m->vtotal;
+	srm->interlace      = m->interlace;
+	srm->doublescan     = m->doublescan;
+	srm->hsync          = m->hsync;
+	srm->vsync          = m->vsync;
+	//
+	srm->is_refresh_off = m->result.weight & R_V_FREQ_OFF ? 1 : 0;
+	srm->is_stretched   = m->result.weight & R_RES_STRETCH ? 1 : 0;
+	srm->x_scale        = m->result.x_scale;
+	srm->y_scale        = m->result.y_scale;
+	srm->v_scale        = m->result.v_scale;
+	srm->id             = m->id;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/3rdparty/switchres/switchres_wrapper.h b/3rdparty/switchres/switchres_wrapper.h
new file mode 100644
index 00000000000..fe3bc40e782
--- /dev/null
+++ b/3rdparty/switchres/switchres_wrapper.h
@@ -0,0 +1,190 @@
+/**************************************************************
+
+   switchres_wrapper.h - Switchres C wrapper API header file
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2021 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#include <stdint.h>
+#ifndef SR_DEFINES
+#include "switchres_defines.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __linux__
+#include <dlfcn.h>
+#define LIBTYPE void*
+#define OPENLIB(libname) dlopen((libname), RTLD_LAZY)
+#define LIBFUNC(libh, fn) dlsym((libh), (fn))
+#define LIBERROR dlerror
+#define CLOSELIB(libh) dlclose((libh))
+
+#elif defined _WIN32
+#include <windows.h>
+#define LIBTYPE HINSTANCE
+#define OPENLIB(libname) LoadLibrary(TEXT((libname)))
+#define LIBFUNC(lib, fn) GetProcAddress((lib), (fn))
+
+#define CLOSELIB(libp) FreeLibrary((libp))
+#endif
+
+#ifdef _WIN32
+/*
+ * This is a trick to avoid exporting some functions thus having the binary
+ * flagged as a virus. If switchres_wrapper.cpp is included in the compilation
+ * LIBERROR() is just declared and not compiled. If switchres_wrapper.cpp is
+ * not compiled, LIBERROR is defined here
+ */
+#ifndef SR_WIN32_STATIC
+char* LIBERROR()
+{
+	DWORD errorMessageID = GetLastError();
+	if(errorMessageID == 0)
+		return NULL;
+
+	LPSTR messageBuffer;
+	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+			 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
+
+	SetLastError(0);
+
+	static char error_msg[256] = {0};
+	strncpy(error_msg, messageBuffer, sizeof(error_msg)-1);
+	LocalFree(messageBuffer);
+	return error_msg;
+}
+#endif /* SR_WIN32_STATIC */
+	#ifndef SR_WIN32_STATIC
+		#define MODULE_API __declspec(dllexport)
+	#else
+		#define MODULE_API
+	#endif
+#else
+	#define MODULE_API
+#endif /* _WIN32 */
+
+#ifdef __linux__
+#define LIBSWR "libswitchres.so"
+#elif _WIN32
+#define LIBSWR "libswitchres.dll"
+#endif
+
+/* Mode flags */
+#define SR_MODE_INTERLACED    1<<0
+#define SR_MODE_ROTATED       1<<1
+#define SR_MODE_DONT_FLUSH    1<<16
+
+/* That's all the exposed data from Switchres calculation */
+typedef struct MODULE_API
+{
+	int      width;
+	int      height;
+	int      refresh;
+	//
+	double   vfreq;
+	double   hfreq;
+	//
+	uint64_t pclock;
+	int      hbegin;
+	int      hend;
+	int      htotal;
+	int      vbegin;
+	int      vend;
+	int      vtotal;
+	int      interlace;
+	int      doublescan;
+	int      hsync;
+	int      vsync;
+	//
+	int      is_refresh_off;
+	int      is_stretched;
+	double   x_scale;
+	double   y_scale;
+	double   v_scale;
+	int      id;
+} sr_mode;
+
+/* Used to retrieve SR settings and state */
+typedef struct MODULE_API
+{
+	char     monitor[32];
+	int      modeline_generation;
+	int      desktop_is_rotated;
+	int      interlace;
+	int      doublescan;
+	double   dotclock_min;
+	double   refresh_tolerance;
+	int      super_width;
+	double   monitor_aspect;
+	double   h_size;
+	double   h_shift;
+	double   v_shift;
+	int      pixel_precision;
+	int      selected_mode;
+	int      current_mode;
+} sr_state;
+
+/* Declaration of the wrapper functions */
+MODULE_API void sr_init();
+MODULE_API char* sr_get_version();
+MODULE_API void sr_load_ini(char* config);
+MODULE_API void sr_deinit();
+MODULE_API int sr_init_disp(const char*, void*);
+MODULE_API void sr_set_disp(int);
+MODULE_API int sr_get_mode(int, sr_mode*);
+MODULE_API int sr_add_mode(int, int, double, int, sr_mode*);
+MODULE_API int sr_switch_to_mode(int, int, double, int, sr_mode*);
+MODULE_API int sr_flush();
+MODULE_API int sr_set_mode(int);
+MODULE_API void sr_set_monitor(const char*);
+MODULE_API void sr_set_user_mode(int, int, int);
+MODULE_API void sr_set_option(const char* key, const char* value);
+MODULE_API void sr_get_state(sr_state *state);
+
+/* Logging related functions */
+MODULE_API void sr_set_log_level(int);
+MODULE_API void sr_set_log_callback_error(void *);
+MODULE_API void sr_set_log_callback_info(void *);
+MODULE_API void sr_set_log_callback_debug(void *);
+
+/* Others */
+MODULE_API void sr_set_sdl_window(void *);
+
+/* Inspired by https://stackoverflow.com/a/1067684 */
+typedef struct MODULE_API
+{
+	void (*init)(void);
+	void (*load_ini)(char*);
+	char* (*sr_get_version)(void);
+	void (*deinit)(void);
+	int (*init_disp)(const char*, void*);
+	void (*set_disp)(int);
+	int (*get_mode)(int, sr_mode*);
+	int (*add_mode)(int, int, double, int, sr_mode*);
+	int (*switch_to_mode)(int, int, double, int, sr_mode*);
+	int (*flush)(void);
+	int (*set_mode)(int);
+	void (*set_monitor)(const char*);
+	void (*set_user_mode)(int, int, int);
+	void (*set_option)(const char*, const char*);
+	void (*get_state)(sr_state*);
+	void (*set_log_level) (int);
+	void (*set_log_callback_error)(void *);
+	void (*set_log_callback_info)(void *);
+	void (*set_log_callback_debug)(void *);
+} srAPI;
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/dist.mak b/dist.mak
index f348cf39d84..39bb21e4b9c 100644
--- a/dist.mak
+++ b/dist.mak
@@ -106,7 +106,7 @@ GEN_FOLDERS := $(addprefix $(STAGEDIR)/,$(CREATED_DIRS))
 COPY_BINARIES := $(addprefix $(STAGEDIR)/,$(addsuffix $(EXE),$(BINARIES)) $(SYMFILE))
 COPY_FILES := $(addprefix $(STAGEDIR)/,$(COPIED_FILES))
 
-all: $(COPY_BINARIES) $(COPY_FILES) $(STAGEDIR)/docs/MAME.pdf
+all: $(COPY_BINARIES) $(COPY_FILES)
 
 clean:
 	$(SILENT) rm -rf $(STAGEDIR)
@@ -124,10 +124,4 @@ $(STAGEDIR)/%.sym: $(BINDIR)/%.sym | $(GEN_FOLDERS)
 $(STAGEDIR)/%: % | $(GEN_FOLDERS)
 	$(call COPY,$<,$@)
 
-$(STAGEDIR)/docs/MAME.pdf: docs/build/latex/MAME.pdf | $(GEN_FOLDERS)
-	$(call COPY,$<,$@)
-
-docs/build/latex/MAME.pdf:
-	$(MAKE) -C docs PAPER=a4 latexpdf
-
 .PHONY: all clean
diff --git a/scripts/extlib.lua b/scripts/extlib.lua
index 6d80717eb95..02af12249f1 100644
--- a/scripts/extlib.lua
+++ b/scripts/extlib.lua
@@ -20,6 +20,7 @@ local extlibs = {
 	glm        = { "glm",       "3rdparty/glm" },
 	rapidjson  = { "rapidjson", "3rdparty/rapidjson/include" },
 	pugixml    = { "pugixml",   "3rdparty/pugixml/src" },
+	switchres  = { "switchres", "3rdparty/switchres" },
 }
 
 -- system lib options
diff --git a/scripts/src/3rdparty.lua b/scripts/src/3rdparty.lua
index 1482c3d71a4..96e6dc50bc3 100755
--- a/scripts/src/3rdparty.lua
+++ b/scripts/src/3rdparty.lua
@@ -878,6 +878,27 @@ links {
 end
 
 
+--------------------------------------------------
+-- lz4 library objects
+--------------------------------------------------
+
+project "lz4"
+	uuid "88b89ceb-8382-4d44-860f-75b8fa6823ad"
+	kind "StaticLib"
+
+files {
+		MAME_DIR .. "3rdparty/lz4/lz4.c",
+		MAME_DIR .. "3rdparty/lz4/lz4file.c",
+		MAME_DIR .. "3rdparty/lz4/lz4frame.c",
+		MAME_DIR .. "3rdparty/lz4/lz4hc.c",
+		MAME_DIR .. "3rdparty/lz4/xxhash.c"
+	}
+
+buildoptions_c {
+		"-Wno-bad-function-cast"
+	}
+
+
 --------------------------------------------------
 -- lib7z library objects
 --------------------------------------------------
@@ -2174,3 +2195,81 @@ project "asmjit"
 		MAME_DIR .. "3rdparty/asmjit/src/asmjit/x86/x86rapass.cpp",
 	}
 end
+
+
+--------------------------------------------------
+-- switchres library
+--------------------------------------------------
+
+project "switchres"
+	uuid "556720c2-c830-4c5f-bb6c-ec89eface072"
+	kind "StaticLib"
+
+files {
+	MAME_DIR .. "3rdparty/switchres/switchres.cpp",
+	MAME_DIR .. "3rdparty/switchres/switchres.h",
+	MAME_DIR .. "3rdparty/switchres/modeline.cpp",
+	MAME_DIR .. "3rdparty/switchres/modeline.h",
+	MAME_DIR .. "3rdparty/switchres/monitor.cpp",
+	MAME_DIR .. "3rdparty/switchres/monitor.h",
+	MAME_DIR .. "3rdparty/switchres/display.cpp",
+	MAME_DIR .. "3rdparty/switchres/display.h",
+	MAME_DIR .. "3rdparty/switchres/custom_video.cpp",
+	MAME_DIR .. "3rdparty/switchres/custom_video.h",
+	MAME_DIR .. "3rdparty/switchres/log.cpp",
+	MAME_DIR .. "3rdparty/switchres/log.h",
+}
+
+if _OPTIONS["targetos"]=="windows" then
+	files {
+		MAME_DIR .. "3rdparty/switchres/display_windows.cpp",
+		MAME_DIR .. "3rdparty/switchres/display_windows.h",
+		MAME_DIR .. "3rdparty/switchres/resync_windows.cpp",
+		MAME_DIR .. "3rdparty/switchres/resync_windows.h",
+		MAME_DIR .. "3rdparty/switchres/custom_video_adl.cpp",
+		MAME_DIR .. "3rdparty/switchres/custom_video_adl.h",
+		MAME_DIR .. "3rdparty/switchres/custom_video_ati.cpp",
+		MAME_DIR .. "3rdparty/switchres/custom_video_ati.h",
+		MAME_DIR .. "3rdparty/switchres/custom_video_ati_family.cpp",
+		MAME_DIR .. "3rdparty/switchres/custom_video_pstrip.cpp",
+		MAME_DIR .. "3rdparty/switchres/custom_video_pstrip.h",
+	}
+end
+
+if _OPTIONS["targetos"]=="linux" then
+	files {
+		MAME_DIR .. "3rdparty/switchres/display_linux.cpp",
+		MAME_DIR .. "3rdparty/switchres/display_linux.h",
+		MAME_DIR .. "3rdparty/switchres/display_sdl2.cpp",
+		MAME_DIR .. "3rdparty/switchres/display_sdl2.h",
+		MAME_DIR .. "3rdparty/switchres/custom_video_xrandr.cpp",
+		MAME_DIR .. "3rdparty/switchres/custom_video_xrandr.h",
+		MAME_DIR .. "3rdparty/switchres/custom_video_drmkms.cpp",
+		MAME_DIR .. "3rdparty/switchres/custom_video_drmkms.h",
+	}
+
+	defines {
+		"SR_WITH_XRANDR",
+		"SR_WITH_KMSDRM",
+		"SR_WITH_SDL2",
+	}
+
+	buildoptions {
+		backtick("pkg-config --cflags libdrm"),
+		backtick("pkg-config --cflags sdl2"),
+	}
+
+	links {
+		"Xrandr",
+	}
+
+	local str = backtick("pkg-config --libs libdrm")
+	addlibfromstring(str)
+	addoptionsfromstring(str)
+end
+
+if _OPTIONS["gcc"]~=nil and string.find(_OPTIONS["gcc"], "clang") then
+	buildoptions {
+		"-Wno-unused-private-field",
+	}
+end
diff --git a/scripts/src/main.lua b/scripts/src/main.lua
index 3d6b6dbf472..3984903eec6 100644
--- a/scripts/src/main.lua
+++ b/scripts/src/main.lua
@@ -207,6 +207,7 @@ end
 		"bgfx",
 		"bimg",
 		"bx",
+		"switchres",
 		"ocore_" .. _OPTIONS["osd"],
 	}
 
diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua
index 4b796a1f5b4..e401b1c2151 100644
--- a/scripts/src/osd/modules.lua
+++ b/scripts/src/osd/modules.lua
@@ -87,6 +87,7 @@ function osdmodulesbuild()
 		MAME_DIR .. "src/osd/modules/input/input_dinput.cpp",
 		MAME_DIR .. "src/osd/modules/input/input_dinput.h",
 		MAME_DIR .. "src/osd/modules/input/input_mac.cpp",
+		MAME_DIR .. "src/osd/modules/input/input_mister.cpp",
 		MAME_DIR .. "src/osd/modules/input/input_module.h",
 		MAME_DIR .. "src/osd/modules/input/input_none.cpp",
 		MAME_DIR .. "src/osd/modules/input/input_rawinput.cpp",
@@ -129,6 +130,7 @@ function osdmodulesbuild()
 		MAME_DIR .. "src/osd/modules/render/drawnone.cpp",
 		MAME_DIR .. "src/osd/modules/render/drawogl.cpp",
 		MAME_DIR .. "src/osd/modules/render/drawsdl.cpp",
+		MAME_DIR .. "src/osd/modules/render/drawnogpu.cpp",
 		MAME_DIR .. "src/osd/modules/render/render_module.h",
 		MAME_DIR .. "src/osd/modules/sound/coreaudio_sound.cpp",
 		MAME_DIR .. "src/osd/modules/sound/js_sound.cpp",
@@ -143,6 +145,7 @@ function osdmodulesbuild()
 		MAME_DIR .. "src/osd/modules/sound/sound_module.h",
 		MAME_DIR .. "src/osd/modules/sound/wasapi_sound.cpp",
 		MAME_DIR .. "src/osd/modules/sound/xaudio2_sound.cpp",
+		MAME_DIR .. "src/osd/modules/switchres/switchres_module.cpp",
 	}
 	includedirs {
 		MAME_DIR .. "src/osd",
@@ -565,6 +568,10 @@ function osdmodulestargetconf()
 		end
 	end
 
+	links {
+			"lz4",
+		}
+
 	if _OPTIONS["targetos"]=="windows" then
 		links {
 			"gdi32",
@@ -572,6 +579,7 @@ function osdmodulestargetconf()
 			"dxguid",
 			"oleaut32",
 			"winmm",
+			"hid",
 		}
 	elseif _OPTIONS["targetos"]=="macosx" then
 		links {
diff --git a/scripts/src/osd/sdl.lua b/scripts/src/osd/sdl.lua
index 86a727213a9..851257497ce 100644
--- a/scripts/src/osd/sdl.lua
+++ b/scripts/src/osd/sdl.lua
@@ -53,6 +53,12 @@ function maintargetosdoptions(_target,_subtarget)
 		addoptionsfromstring(str)
 	end
 
+	if BASE_TARGETOS=="unix" and _OPTIONS["targetos"]=="linux" then
+		local str = backtick("pkg-config --libs libdrm")
+		addlibfromstring(str)
+		addoptionsfromstring(str)
+	end
+
 	if _OPTIONS["targetos"]=="windows" then
 		if _OPTIONS["USE_LIBSDL"]~="1" then
 			configuration { "mingw*"}
diff --git a/scripts/src/osd/sdl_cfg.lua b/scripts/src/osd/sdl_cfg.lua
index f3a970c4ad4..12ed3572e6e 100644
--- a/scripts/src/osd/sdl_cfg.lua
+++ b/scripts/src/osd/sdl_cfg.lua
@@ -136,6 +136,9 @@ if _OPTIONS["targetos"]=="windows" then
 	configuration { }
 
 elseif _OPTIONS["targetos"]=="linux" then
+	buildoptions {
+		backtick("pkg-config --cflags libdrm"),
+	}
 	if _OPTIONS["QT_HOME"]~=nil then
 		buildoptions {
 			"-I" .. backtick(_OPTIONS["QT_HOME"] .. "/bin/qmake -query QT_INSTALL_HEADERS"),
diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp
index fa75c885c6d..4f73eed6767 100644
--- a/src/emu/emuopts.cpp
+++ b/src/emu/emuopts.cpp
@@ -94,7 +94,12 @@ const options_entry emu_options::s_option_entries[] =
 	{ OPTION_SLEEP,                                      "1",         core_options::option_type::BOOLEAN,    "enable sleeping, which gives time back to other applications when idle" },
 	{ OPTION_SPEED "(0.1-100)",                          "1.0",       core_options::option_type::FLOAT,      "controls the speed of gameplay, relative to realtime; smaller numbers are slower" },
 	{ OPTION_REFRESHSPEED ";rs",                         "0",         core_options::option_type::BOOLEAN,    "automatically adjust emulation speed to keep the emulated refresh rate slower than the host screen" },
-	{ OPTION_LOWLATENCY ";lolat",                        "0",         core_options::option_type::BOOLEAN,    "draws new frame before throttling to reduce input latency" },
+	{ OPTION_SYNCREFRESH ";srf",                         "0",         core_options::option_type::BOOLEAN,    "enable using the start of VBLANK for throttling instead of the game time" },
+	{ OPTION_SYNCAUDIO ";sau",                           "1",         core_options::option_type::BOOLEAN,    "enable audio resampling to stay synchronized with video" },
+	{ OPTION_LOWLATENCY ";lolat",                        "1",         core_options::option_type::BOOLEAN,    "draws new frame before throttling to reduce input latency" },
+	{ OPTION_FRAMEDELAY ";fd",                           "0",         core_options::option_type::INTEGER,    "delays the start of each frame to minimize input lag (0-9)"},
+	{ OPTION_VSYNC_OFFSET,                               "0",         core_options::option_type::INTEGER,    "offset vsync position by this many lines to prevent tearing with frame_delay and high-resolution displays" },
+	{ OPTION_BLACK_FRAME_INSERTION ";bfi",               "0",         core_options::option_type::INTEGER,    "number of black frames to insert after each normal frame, intended to reduce motion blur on 120+ Hz monitors" },
 
 	// render options
 	{ nullptr,                                           nullptr,     core_options::option_type::HEADER,     "CORE RENDER OPTIONS" },
diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h
index 2d72580a759..1c5427cf98e 100644
--- a/src/emu/emuopts.h
+++ b/src/emu/emuopts.h
@@ -75,7 +75,12 @@
 #define OPTION_SLEEP                "sleep"
 #define OPTION_SPEED                "speed"
 #define OPTION_REFRESHSPEED         "refreshspeed"
+#define OPTION_SYNCREFRESH          "syncrefresh"
+#define OPTION_SYNCAUDIO            "syncaudio"
 #define OPTION_LOWLATENCY           "lowlatency"
+#define OPTION_FRAMEDELAY           "framedelay"
+#define OPTION_VSYNC_OFFSET         "vsync_offset"
+#define OPTION_BLACK_FRAME_INSERTION "black_frame_insertion"
 
 // core render options
 #define OPTION_KEEPASPECT           "keepaspect"
@@ -360,7 +365,12 @@ public:
 	bool sleep() const { return m_sleep; }
 	float speed() const { return float_value(OPTION_SPEED); }
 	bool refresh_speed() const { return m_refresh_speed; }
+	bool sync_refresh() const { return bool_value(OPTION_SYNCREFRESH); }
+	bool sync_audio() const { return bool_value(OPTION_SYNCAUDIO); }
 	bool low_latency() const { return bool_value(OPTION_LOWLATENCY); }
+	int frame_delay() const { return int_value(OPTION_FRAMEDELAY); }
+	int vsync_offset() const { return int_value(OPTION_VSYNC_OFFSET); }
+	int black_frame_insertion() const { return int_value(OPTION_BLACK_FRAME_INSERTION); }
 
 	// core render options
 	bool keep_aspect() const { return bool_value(OPTION_KEEPASPECT); }
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 37862f03ea1..b60aefe57a4 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -414,6 +414,7 @@ void render_texture::set_bitmap(bitmap_t &bitmap, const rectangle &sbounds, text
 	m_bitmap = &bitmap;
 	m_sbounds = sbounds;
 	m_format = format;
+	m_curseq++;
 
 	// invalidate all scaled versions
 	for (auto & elem : m_scaled)
@@ -471,7 +472,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
 		texinfo.width_margin = m_sbounds.left();
 		texinfo.height = sheight;
 		// palette will be set later
-		texinfo.seqid = ++m_curseq;
+		texinfo.seqid = m_curseq;
 	}
 	else
 	{
@@ -526,7 +527,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
 		texinfo.width = dwidth;
 		texinfo.height = dheight;
 		// palette will be set later
-		texinfo.seqid = scaled->seqid;
+		texinfo.seqid = m_curseq;
 	}
 }
 
@@ -942,6 +943,7 @@ render_target::render_target(render_manager &manager, render_container *ui, T &&
 	, m_keepaspect(false)
 	, m_int_overscan(false)
 	, m_pixel_aspect(0.0f)
+	, m_integer_aspect(1.0f)
 	, m_int_scale_x(0)
 	, m_int_scale_y(0)
 	, m_max_refresh(0)
@@ -1032,6 +1034,18 @@ bool render_target::is_ui_target() const
 	return (this == &m_manager.ui_target());
 }
 
+//-------------------------------------------------
+//  is_ui_rotated - return true if UI is rotated
+//-------------------------------------------------
+bool render_target::is_ui_rotated() const
+{
+	int orient = orientation_add(orientation(), manager().ui_container().orientation());
+
+	if (orient & ORIENTATION_SWAP_XY)
+		return true;
+	else
+		return false;
+}
 
 //-------------------------------------------------
 //  index - return the index of this target
@@ -1042,6 +1056,44 @@ int render_target::index() const
 	return m_manager.m_targetlist.indexof(*this);
 }
 
+//-------------------------------------------------
+//  texel_width - width of UI pixel after scaling
+//  in 0-1 coordinate range
+//-------------------------------------------------
+
+float render_target::texel_width() const
+{
+		float tw;
+
+		if (is_ui_rotated())
+			tw  = 1.0f / height();
+		else
+		{
+			tw  = 1.0f / width();
+			tw = tw / integer_aspect();
+		}
+		return tw;
+}
+
+//-------------------------------------------------
+//  texel_height - height of UI pixel after scaling
+//  in 0-1 coordinate range
+//-------------------------------------------------
+
+float render_target::texel_height() const
+{
+		float th;
+
+		if (is_ui_rotated())
+		{
+			th = 1.0f / width();
+			th = th * integer_aspect();
+		}
+		else
+			th = 1.0f / height();
+
+		return th;
+}
 
 //-------------------------------------------------
 //  set_bounds - set the bounds and pixel aspect
@@ -1550,7 +1602,7 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
 						continue;
 
 					// score the result
-					float new_diff = fabsf(aspect_ratio * (a / b) - (ab / bb));
+					float new_diff = fabsf(aspect_ratio - (a / b) / (ab / bb));
 
 					if (new_diff <= diff)
 					{
@@ -3483,9 +3535,16 @@ float render_manager::ui_aspect(render_container *rc)
 		{
 			float pixel_aspect = target->pixel_aspect();
 
+			if (pixel_aspect < 1.0f)
+				pixel_aspect = 1.0f / roundf(1.0f / pixel_aspect);
+			else
+				pixel_aspect = roundf(pixel_aspect);
+
 			if (orient & ORIENTATION_SWAP_XY)
 				pixel_aspect = 1.0f / pixel_aspect;
 
+			m_ui_target->set_integer_aspect(pixel_aspect);
+
 			return aspect /= pixel_aspect;
 		}
 	}
diff --git a/src/emu/render.h b/src/emu/render.h
index c4c0f504edd..4f35a99c6dc 100644
--- a/src/emu/render.h
+++ b/src/emu/render.h
@@ -506,6 +506,7 @@ public:
 	u32 width() const { return m_width; }
 	u32 height() const { return m_height; }
 	float pixel_aspect() const { return m_pixel_aspect; }
+	float integer_aspect() const { return m_integer_aspect; }
 	bool keepaspect() const { return m_keepaspect; }
 	int scale_mode() const { return m_scale_mode; }
 	float max_update_rate() const { return m_max_refresh; }
@@ -517,6 +518,9 @@ public:
 	bool hidden() const { return ((m_flags & RENDER_CREATE_HIDDEN) != 0); }
 	bool is_ui_target() const;
 	int index() const;
+	float texel_width() const;
+	float texel_height() const;
+	bool is_ui_rotated() const;
 
 	// setters
 	void set_bounds(s32 width, s32 height, float pixel_aspect = 0);
@@ -527,6 +531,7 @@ public:
 	void set_transform_container(bool transform_container) { m_transform_container = transform_container; }
 	void set_keepaspect(bool keepaspect) { m_keepaspect = keepaspect; }
 	void set_scale_mode(int scale_mode) { m_scale_mode = scale_mode; }
+	void set_integer_aspect(float aspect) { m_integer_aspect = aspect; }
 
 	// pointer input handling
 	void pointer_updated(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks);
@@ -628,6 +633,7 @@ private:
 	bool                    m_keepaspect;               // constrain aspect ratio
 	bool                    m_int_overscan;             // allow overscan on integer scaled targets
 	float                   m_pixel_aspect;             // aspect ratio of individual pixels
+	float                   m_integer_aspect;           // aspect ratio of individual pixels rounded to integers
 	int                     m_scale_mode;               // type of scale to apply
 	int                     m_int_scale_x;              // horizontal integer scale factor
 	int                     m_int_scale_y;              // vertical integer scale factor
diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp
index 5d09061ab70..2476d31567d 100644
--- a/src/emu/rendutil.cpp
+++ b/src/emu/rendutil.cpp
@@ -137,6 +137,7 @@ struct jpeg_setjmp_error_mgr : public jpeg_error_mgr
 ***************************************************************************/
 
 /* utilities */
+static void resample_argb_bitmap_integer(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept;
 static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept;
 static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept;
 static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) noexcept;
@@ -172,7 +173,68 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source,
 	if (dx > 0x1000 || dy > 0x1000 || force)
 		resample_argb_bitmap_average(&dest.pix(0), dest.rowpixels(), dwidth, dheight, sbase, source.rowpixels(), swidth, sheight, color, dx, dy);
 	else
-		resample_argb_bitmap_bilinear(&dest.pix(0), dest.rowpixels(), dwidth, dheight, sbase, source.rowpixels(), swidth, sheight, color, dx, dy);
+	{
+		dx = ceil(float(swidth << 12) / dwidth);
+		dy = ceil(float(sheight << 12) / dheight);
+
+		if (dwidth % swidth == 0 && dheight % sheight == 0)
+			resample_argb_bitmap_integer(&dest.pix(0), dest.rowpixels(), dwidth, dheight, sbase, source.rowpixels(), swidth, sheight, color, dx, dy);
+		else
+			resample_argb_bitmap_bilinear(&dest.pix(0), dest.rowpixels(), dwidth, dheight, sbase, source.rowpixels(), swidth, sheight, color, dx, dy);
+	}
+}
+
+
+/*-------------------------------------------------
+    resample_argb_bitmap_integer - resample a texture
+    by performing simple blitting
+-------------------------------------------------*/
+
+static void resample_argb_bitmap_integer(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept
+{
+	u32 r, g, b, a;
+	u32 x, y;
+
+	/* precompute premultiplied R/G/B/A factors */
+	r = color.r * color.a * 256.0f;
+	g = color.g * color.a * 256.0f;
+	b = color.b * color.a * 256.0f;
+	a = color.a * 256.0f;
+
+	/* loop over the target vertically */
+	for (y = 0; y < dheight; y++)
+	{
+		u32 cury = y * dy;
+
+		/* loop over the target horizontally */
+		for (x = 0; x < dwidth; x++)
+		{
+			u32 sumr, sumg, sumb, suma;
+			u32 curx = x * dx;
+
+			/* fetch the source pixel */
+			rgb_t pix = source[(cury >> 12) * srowpixels + (curx >> 12)];
+
+			/* apply scaling */
+			suma = pix.a() * a / 256;
+			sumr = pix.r() * r / 256;
+			sumg = pix.g() * g / 256;
+			sumb = pix.b() * b / 256;
+
+			/* if we're translucent, add in the destination pixel contribution */
+			if (a < 256)
+			{
+				rgb_t dpix = dest[y * drowpixels + x];
+				suma += dpix.a() * (256 - a);
+				sumr += dpix.r() * (256 - a);
+				sumg += dpix.g() * (256 - a);
+				sumb += dpix.b() * (256 - a);
+			}
+
+			/* store the target pixel, dividing the RGBA values by the overall scale factor */
+			dest[y * drowpixels + x] = rgb_t(suma, sumr, sumg, sumb);
+		}
+	}
 }
 
 
diff --git a/src/emu/video.cpp b/src/emu/video.cpp
index 75ba128df91..6879432e20a 100644
--- a/src/emu/video.cpp
+++ b/src/emu/video.cpp
@@ -26,6 +26,7 @@
 #include "xmlfile.h"
 
 #include "osdepend.h"
+#include <switchres/switchres.h>
 
 #include "rendersw.hxx"
 
@@ -59,7 +60,7 @@ const bool video_manager::s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] =
 	{ false, true , true , true , true , true , true , true , true , true , true , true  }
 };
 
-
+int video_manager::s_fd_speeds[FD_BINS] = { 0,0,0,0,0,0,0,0,0,0 };
 
 //**************************************************************************
 //  VIDEO MANAGER
@@ -94,6 +95,10 @@ video_manager::video_manager(running_machine &machine)
 	, m_overall_valid_counter(0)
 	, m_throttled(true)
 	, m_throttle_rate(1.0f)
+	, m_syncrefresh(machine.options().sync_refresh())
+	, m_syncaudio(machine.options().sync_audio())
+	, m_framedelay(machine.options().frame_delay())
+	, m_vsync_offset(machine.options().vsync_offset())
 	, m_fastforward(false)
 	, m_seconds_to_run(machine.options().seconds_to_run())
 	, m_auto_frameskip(machine.options().auto_frameskip())
@@ -228,7 +233,7 @@ void video_manager::frame_update(bool from_debugger)
 	// if none of the screens changed and we haven't skipped too many frames in a row,
 	// mark this frame as skipped to prevent throttling; this helps for games that
 	// don't update their screen at the monitor refresh rate
-	if (!anything_changed && !m_auto_frameskip && (m_frameskip_level == 0) && (m_empty_skip_count++ < 3))
+	if (!m_syncrefresh && !anything_changed && !m_auto_frameskip && (m_frameskip_level == 0) && (m_empty_skip_count++ < 3))
 		skipped_it = true;
 	else
 		m_empty_skip_count = 0;
@@ -244,6 +249,21 @@ void video_manager::frame_update(bool from_debugger)
 		machine().osd().update(!from_debugger && skipped_it);
 	}
 
+	// manage black frame insertion
+	if (machine().options().black_frame_insertion() && machine().options().sync_refresh())
+	{
+		if (phase == machine_phase::RUNNING && (!machine().paused() || machine().options().update_in_pause()))
+		{
+			render_container *container = &machine().render().ui_container();
+			container->add_rect(0, 0, 1, 1, 0xff000000, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+			for (int i = 0; i < machine().options().black_frame_insertion(); i++)
+			{
+				update_throttle(current_time);
+				machine().osd().update(!from_debugger && skipped_it);
+			}
+		}
+	}
+
 	// we synchronize after rendering instead of before, if low latency mode is enabled
 	if (!from_debugger && phase > machine_phase::INIT && m_low_latency && effective_throttle())
 		update_throttle(current_time);
@@ -508,6 +528,23 @@ void video_manager::exit()
 		osd_ticks_t tps = osd_ticks_per_second();
 		double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps;
 		double final_emu_time = m_overall_emutime.as_double();
+
+		if (!m_throttled)
+		{
+			int i;
+			float sum = 0;
+
+			osd_printf_info("Frame delay/percentage:");
+
+			for (i = 0; i < FD_BINS; i++)
+                sum += s_fd_speeds[i];
+
+			for (i = 0; i < FD_BINS; i++)
+				if (s_fd_speeds[i])
+					osd_printf_info(" %d/%.2f%%", i, (float) s_fd_speeds[i] / sum * 100.f);
+			osd_printf_info("\n");
+		}
+
 		osd_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, (m_overall_emutime + attotime(0, ATTOSECONDS_PER_SECOND / 2)).seconds());
 	}
 }
@@ -697,6 +734,26 @@ void video_manager::update_throttle(attotime emutime)
 
 */
 
+	// if we're only syncing to the refresh, bail now
+	if (m_syncrefresh)
+	{
+		if (m_framedelay == 0 || m_framedelay > 9)
+			return;
+
+		screen_device *const screen = screen_device_enumerator(machine().root_device()).first();
+		if (screen)
+		{
+			osd_ticks_t now = osd_ticks();
+			osd_ticks_t ticks_per_second = osd_ticks_per_second();
+			attoseconds_t attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second * m_throttle_rate;
+
+			attoseconds_t period = screen->frame_period().attoseconds();
+			int bfi = machine().options().black_frame_insertion();
+			throttle_until_ticks(now + period / attoseconds_per_tick * m_framedelay / 10 / (bfi + 1));
+			return;
+		}
+	}
+
 	// outer scope so we can break out in case of a resync
 	while (1)
 	{
@@ -963,6 +1020,21 @@ void video_manager::recompute_speed(const attotime &emutime)
 		osd_ticks_t tps = osd_ticks_per_second();
 		m_speed_percent = delta_emutime.as_double() * (double)tps / (double)delta_realtime;
 
+		// adjust speed for audio resampling
+		if (m_syncaudio && m_syncrefresh && m_throttled)
+		{
+			if (m_speed_percent >= 0.8 && m_speed_percent <= 1.2)
+				m_speed = m_speed_percent * 1000;
+		}
+
+		// log speed for frame delay statistic
+		if (!m_throttled)
+		{
+			int bin = (float) FD_BINS - 10.f/m_speed_percent;
+			bin = bin > (FD_BINS - 1) ? (FD_BINS - 1) : bin < 0 ? 0 : bin;
+			s_fd_speeds[bin]++;
+		}
+
 		// remember the last times
 		m_speed_last_realtime = realtime;
 		m_speed_last_emutime = emutime;
diff --git a/src/emu/video.h b/src/emu/video.h
index 6517c90d170..44c67140881 100644
--- a/src/emu/video.h
+++ b/src/emu/video.h
@@ -30,6 +30,7 @@
 constexpr int FRAMESKIP_LEVELS = 12;
 constexpr int MAX_FRAMESKIP = FRAMESKIP_LEVELS - 2;
 
+constexpr int FD_BINS = 10;
 
 //**************************************************************************
 //  TYPE DEFINITIONS
@@ -52,6 +53,10 @@ public:
 	int frameskip() const { return m_auto_frameskip ? -1 : m_frameskip_level; }
 	bool throttled() const { return m_throttled; }
 	float throttle_rate() const { return m_throttle_rate; }
+	bool sync_refresh() const { return m_syncrefresh; }
+	bool sync_audio() const { return m_syncaudio; }
+	int32_t framedelay() const { return m_framedelay; }
+	int32_t vsync_offset() const { return m_vsync_offset; }
 	bool fastforward() const { return m_fastforward; }
 
 	// setters
@@ -61,6 +66,10 @@ public:
 	void set_fastforward(bool ffwd) { m_fastforward = ffwd; }
 	void set_output_changed() { m_output_changed = true; }
 	void set_speed_factor(int speed) { m_speed = speed; }
+	void set_sync_refresh(bool syncrefresh) { m_syncrefresh = syncrefresh; }
+	void set_sync_audio(bool syncaudio) { m_syncaudio = syncaudio; }
+	void set_framedelay(int framedelay) { m_framedelay = framedelay; }
+	void set_vsync_offset(int vsync_offset) { m_vsync_offset = vsync_offset; }
 
 	// misc
 	void toggle_record_movie(movie_recording::format format);
@@ -141,6 +150,10 @@ private:
 	// configuration
 	bool                m_throttled;                // flag: true if we're currently throttled
 	float               m_throttle_rate;            // target rate for throttling
+	bool                m_syncrefresh;              // flag: TRUE if we're currently refresh-synced
+	bool                m_syncaudio;                // flag: TRUE if audio resampling is enabled
+	int32_t             m_framedelay;               // tenths of frame to delay emulation start
+	int32_t             m_vsync_offset;             // offset vsync position by this many lines
 	bool                m_fastforward;              // flag: true if we're currently fast-forwarding
 	u32                 m_seconds_to_run;           // number of seconds to run before quitting
 	bool                m_auto_frameskip;           // flag: true if we're automatically frameskipping
@@ -166,6 +179,9 @@ private:
 	// movie recordings
 	std::vector<movie_recording::ptr> m_movie_recordings;
 
+	// frame delay statistics
+	static int          s_fd_speeds[FD_BINS];
+
 	static const bool   s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS];
 
 	static const attoseconds_t ATTOSECONDS_PER_SPEED_UPDATE = ATTOSECONDS_PER_SECOND / 4;
diff --git a/src/frontend/mame/ui/confswitch.cpp b/src/frontend/mame/ui/confswitch.cpp
index 27340eda2ba..916fe5c82a1 100644
--- a/src/frontend/mame/ui/confswitch.cpp
+++ b/src/frontend/mame/ui/confswitch.cpp
@@ -13,6 +13,8 @@
 
 #include "uiinput.h"
 
+#include "rendutil.h"
+
 #include <algorithm>
 #include <cstring>
 
@@ -416,13 +418,21 @@ void menu_settings_dip_switches::custom_render(uint32_t flags, void *selectedref
 			unsigned const cnt(group.switch_count());
 			ui().draw_outlined_box(
 					container(),
-					switchleft, liney, switchleft + (m_single_width * cnt), switchbottom,
+					switchleft,
+					liney,
+					switchleft + (m_single_width * cnt),
+					switchbottom,
 					ui().colors().background_color());
+
 			for (unsigned i = 1; cnt > i; ++i)
 			{
-				container().add_line(
-						switchleft + (m_single_width * i), liney, switchleft + (m_single_width * i), switchbottom,
-						UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+				container().add_rect(
+						switchleft + (m_single_width * i) + machine().render().ui_target().texel_width() / 2.0F,
+						liney + machine().render().ui_target().texel_height(),
+						switchleft + (m_single_width * i) - machine().render().ui_target().texel_width() / 2.0F,
+						switchbottom - machine().render().ui_target().texel_height(),
+						ui().colors().border_color(),
+						PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 			}
 
 			// compute top and bottom for on and off positions
diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp
index fc715ec8507..25a784e7e9b 100644
--- a/src/frontend/mame/ui/info.cpp
+++ b/src/frontend/mame/ui/info.cpp
@@ -22,6 +22,7 @@
 #include "screen.h"
 #include "softlist.h"
 #include "speaker.h"
+#include "osdepend.h"
 
 #include "util/unicode.h"
 
@@ -595,6 +596,7 @@ std::string machine_info::game_info_string() const
 		buf << _("None\n");
 	else
 	{
+		int i = 0;
 		for (screen_device &screen : scriter)
 		{
 			std::string detail;
@@ -623,6 +625,9 @@ std::string machine_info::game_info_string() const
 			util::stream_format(buf,
 					(scrcount > 1) ? _("%1$s: %2$s\n") : _("%2$s\n"),
 					get_screen_desc(screen), detail);
+
+			// display Switchres information
+			buf << _(m_machine.osd().switchres_mode(i++));
 		}
 	}
 
diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp
index 6ae5ef6a3d0..e926153ade8 100644
--- a/src/frontend/mame/ui/menu.cpp
+++ b/src/frontend/mame/ui/menu.cpp
@@ -815,16 +815,42 @@ void menu::draw(uint32_t flags)
 			else if (pitem.type() == menu_item_type::SEPARATOR)
 			{
 				// if we're just a divider, draw a line
-				container().add_line(visible_left, line_y0 + 0.5F * line_height(), visible_left + visible_width, line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+				rgb_t color = ui().colors().border_color();
+				color.set_a(color.a() / 2);
+				container().add_rect(
+						x1 + machine().render().ui_target().texel_width() * 3.0F,
+						line_y0 + 0.5F * line_height() + machine().render().ui_target().texel_height() / 2.0F,
+						x2 - machine().render().ui_target().texel_width() * 3.0F,
+						line_y0 + 0.5F * line_height() - machine().render().ui_target().texel_height() / 2.0F,
+						color,
+						PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 			}
 			else if (pitem.subtext().empty())
 			{
 				// if we don't have a subitem, just draw the string centered
 				if (pitem.flags() & FLAG_UI_HEADING)
 				{
+
 					float heading_width = get_string_width(itemtext);
-					container().add_line(visible_left, line_y0 + 0.5F * line_height(), visible_left + ((visible_width - heading_width) / 2) - lr_border(), line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-					container().add_line(visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border(), line_y0 + 0.5F * line_height(), visible_left + visible_width, line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+					// draw ui heading lines with 50% alpha
+					rgb_t color = ui().colors().border_color();
+					color.set_a(color.a() / 2);
+					container().add_rect(
+							x1 + machine().render().ui_target().texel_width() * 3.0F,
+							line_y0 + 0.5F * line_height() + machine().render().ui_target().texel_height() / 2.0F,
+							visible_left + ((visible_width - heading_width) / 2) - lr_border(),
+							line_y0 + 0.5F * line_height() - machine().render().ui_target().texel_height() / 2.0F,
+							color,
+							PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+					container().add_rect(
+							visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border(),
+							line_y0 + 0.5F * line_height() + machine().render().ui_target().texel_height() / 2.0F,
+							x2 - machine().render().ui_target().texel_width() * 3.0F,
+							line_y0 + 0.5F * line_height() - machine().render().ui_target().texel_height() / 2.0F,
+							color,
+							PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 				}
 				ui().draw_text_full(
 						container(),
@@ -967,7 +993,7 @@ void menu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
 	float const ui_line_height = ui().get_line_height();
 
 	// force whole pixels for line height, gutters and borders
-	m_line_height = std::floor(ui_line_height * float(height)) / float(height);
+	m_line_height = std::round(ui_line_height * float(height)) / float(height);
 	m_gutter_width = std::floor(0.5F * ui_line_height * aspect * float(width)) / float(width);
 	m_tb_border = std::floor(ui().box_tb_border() * float(height)) / float(height);
 	m_lr_border = std::floor(ui().box_lr_border() * aspect * float(width)) / float(width);
@@ -1931,7 +1957,11 @@ bool menu::check_metrics()
 {
 	render_manager &render(machine().render());
 	render_target &target(render.ui_target());
-	std::pair<uint32_t, uint32_t> const uisize(target.width(), target.height());
+	std::pair<uint32_t, uint32_t> uisize(target.width(), target.height());
+
+	if (render.ui_target().is_ui_rotated())
+		std::swap(uisize.first, uisize.second);
+
 	float const aspect = render.ui_aspect(&container());
 	if ((uisize == m_last_size) && (std::fabs(1.0F - (aspect / m_last_aspect)) < 1e-6F))
 		return false;
@@ -2014,6 +2044,7 @@ bool menu::do_handle()
 	machine().osd().check_osd_inputs();
 
 	// recompute metrics if necessary
+
 	if (check_metrics())
 		need_update = true;
 
diff --git a/src/frontend/mame/ui/moptions.cpp b/src/frontend/mame/ui/moptions.cpp
index d6eba821947..10df239ad2c 100644
--- a/src/frontend/mame/ui/moptions.cpp
+++ b/src/frontend/mame/ui/moptions.cpp
@@ -51,7 +51,7 @@ const options_entry ui_options::s_option_entries[] =
 	// UI options
 	{ nullptr,                              nullptr,        option_type::HEADER,      "UI OPTIONS" },
 	{ OPTION_INFOS_SIZE "(0.20-1.00)",      "0.75",         option_type::FLOAT,       "UI right panel infos text size (0.20 - 1.00)" },
-	{ OPTION_FONT_ROWS "(25-40)",           "30",           option_type::INTEGER,     "UI font lines per screen (25 - 40)" },
+	{ OPTION_FONT_ROWS "(20-40)",           "30",           option_type::INTEGER,     "UI font lines per screen (20 - 40)" },
 	{ OPTION_UI_BORDER_COLOR,               "ffffffff",     option_type::STRING,      "UI border color (ARGB)" },
 	{ OPTION_UI_BACKGROUND_COLOR,           "ef101030",     option_type::STRING,      "UI background color (ARGB)" },
 	{ OPTION_UI_CLONE_COLOR,                "ff808080",     option_type::STRING,      "UI clone color (ARGB)" },
diff --git a/src/frontend/mame/ui/sliders.cpp b/src/frontend/mame/ui/sliders.cpp
index d59757914ef..f21c79a37b5 100644
--- a/src/frontend/mame/ui/sliders.cpp
+++ b/src/frontend/mame/ui/sliders.cpp
@@ -13,6 +13,7 @@
 
 #include "ui/slider.h"
 #include "ui/ui.h"
+#include "rendutil.h"
 
 #include "osdepend.h"
 
@@ -317,16 +318,59 @@ void menu_sliders::custom_render(uint32_t flags, void *selectedref, float top, f
 		default_x = bar_left + bar_width * default_percentage;
 		current_x = bar_left + bar_width * percentage;
 
+		if (machine().render().ui_target().is_ui_rotated())
+		{
+			default_x = std::floor(default_x * machine().render().ui_target().height()) / machine().render().ui_target().height();
+			current_x = std::floor(current_x * machine().render().ui_target().height()) / machine().render().ui_target().height();
+		}
+		else
+		{
+			default_x = std::floor(default_x * machine().render().ui_target().width()) / machine().render().ui_target().width();
+			current_x = std::floor(current_x * machine().render().ui_target().width()) / machine().render().ui_target().width();
+		}
+
 		// fill in the percentage
-		container().add_rect(bar_left, bar_top, current_x, bar_bottom, ui().colors().slider_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+		container().add_rect(
+				bar_left,
+				bar_top,
+				current_x,
+				bar_bottom,
+				ui().colors().slider_color(),
+				PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 
 		// draw the top and bottom lines
-		container().add_line(bar_left, bar_top, bar_left + bar_width, bar_top, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-		container().add_line(bar_left, bar_bottom, bar_left + bar_width, bar_bottom, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+		container().add_rect(
+				bar_left,
+				bar_top,
+				bar_left + bar_width,
+				bar_top + machine().render().ui_target().texel_height(),
+				ui().colors().border_color(),
+				PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+		container().add_rect(
+				bar_left,
+				bar_bottom,
+				bar_left + bar_width,
+				bar_bottom - machine().render().ui_target().texel_height(),
+				ui().colors().border_color(),
+				PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 
 		// draw default marker
-		container().add_line(default_x, bar_area_top, default_x, bar_top, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-		container().add_line(default_x, bar_bottom, default_x, bar_area_top + bar_area_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+		container().add_rect(
+				default_x,
+				bar_area_top,
+				default_x - machine().render().ui_target().texel_width(),
+				bar_top,
+				ui().colors().border_color(),
+				PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+		container().add_rect(
+				default_x,
+				bar_bottom,
+				default_x - machine().render().ui_target().texel_width(),
+				bar_area_top + bar_area_height,
+				ui().colors().border_color(),
+				PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 
 		// draw the actual text
 		draw_text_normal(
diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp
index 9f75e853ff0..a7c108982e7 100644
--- a/src/frontend/mame/ui/submenu.cpp
+++ b/src/frontend/mame/ui/submenu.cpp
@@ -124,7 +124,6 @@ std::vector<submenu::option> submenu::video_options()
 			{ option_type::OSD,  N_("Video Mode"),                              OSDOPTION_VIDEO },
 			{ option_type::OSD,  N_("Number Of Screens"),                       OSDOPTION_NUMSCREENS },
 #if defined(UI_WINDOWS) && !defined(UI_SDL)
-			{ option_type::OSD,  N_("Triple Buffering"),                        WINOPTION_TRIPLEBUFFER },
 			{ option_type::OSD,  N_("HLSL"),                                    WINOPTION_HLSL_ENABLE },
 #endif
 			{ option_type::OSD,  N_("GLSL"),                                    OSDOPTION_GL_GLSL },
@@ -133,7 +132,7 @@ std::vector<submenu::option> submenu::video_options()
 			{ option_type::OSD,  N_("Window Mode"),                             OSDOPTION_WINDOW },
 			{ option_type::EMU,  N_("Enforce Aspect Ratio"),                    OPTION_KEEPASPECT },
 			{ option_type::OSD,  N_("Start Out Maximized"),                     OSDOPTION_MAXIMIZE },
-			{ option_type::OSD,  N_("Synchronized Refresh"),                    OSDOPTION_SYNCREFRESH },
+			{ option_type::EMU,  N_("Synchronized Refresh"),                    OPTION_SYNCREFRESH },
 			{ option_type::OSD,  N_("Wait Vertical Sync"),                      OSDOPTION_WAITVSYNC } };
 }
 
@@ -269,7 +268,7 @@ bool submenu::handle(event const *ev)
 				if (ev->iptkey == IPT_UI_CLEAR)
 					sm_option.options->set_value(sm_option.name, sm_option.entry->default_value(), OPTION_PRIORITY_CMDLINE);
 				else
-					sm_option.options->set_value(sm_option.name, sm_option.entry->bool_value() ? 0 : 1, OPTION_PRIORITY_CMDLINE);
+					sm_option.options->set_value(sm_option.name, sm_option.entry->bool_value() ? false : true, OPTION_PRIORITY_CMDLINE);
 				break;
 			case core_options::option_type::INTEGER:
 				if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
diff --git a/src/frontend/mame/ui/textbox.cpp b/src/frontend/mame/ui/textbox.cpp
index 223af5e67cf..6a20a8c5eef 100644
--- a/src/frontend/mame/ui/textbox.cpp
+++ b/src/frontend/mame/ui/textbox.cpp
@@ -15,6 +15,8 @@
 
 #include "uiinput.h"
 
+#include "rendutil.h"
+
 #include <string_view>
 #include <utility>
 
@@ -407,11 +409,16 @@ void menu_textbox::draw(uint32_t flags)
 			m_top_line ? (m_top_line + 1) : 0, text_lines,
 			effective_left, m_visible_top + (m_top_line ? line_height() : 0.0F));
 
-	// add visual separator before the "return to prevous menu" item
-	container().add_line(
-			x1, separator + (0.5F * line_height()),
-			x2, separator + (0.5F * line_height()),
-			UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+	// draw separator with 50% alpha
+	rgb_t color = ui().colors().border_color();
+	color.set_a(color.a() / 2);
+	container().add_rect(
+			x1 + machine().render().ui_target().texel_width() * 3.0F,
+			separator + 0.5F * line_height() + machine().render().ui_target().texel_height() / 2.0F,
+			x2 - machine().render().ui_target().texel_width() * 3.0F,
+			separator + 0.5F * line_height() - machine().render().ui_target().texel_height() / 2.0F,
+			color,
+			PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 
 	float const line_y0 = m_visible_top + float(m_window_lines + 1) * line_height();
 	float const line_y1 = line_y0 + line_height();
diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp
index cb6a66b24a2..386fac83514 100644
--- a/src/frontend/mame/ui/ui.cpp
+++ b/src/frontend/mame/ui/ui.cpp
@@ -36,6 +36,7 @@
 #include "fileio.h"
 #include "natkeyboard.h"
 #include "render.h"
+#include "rendutil.h"
 #include "cheat.h"
 #include "rendfont.h"
 #include "rendlay.h"
@@ -47,6 +48,7 @@
 // FIXME: allow OSD module headers to be included in a less ugly way
 #include "../osd/modules/lib/osdlib.h"
 #include "../osd/modules/lib/osdobj_common.h"
+#include "config.h"
 
 #include <functional>
 #include <type_traits>
@@ -320,6 +322,9 @@ void mame_ui_manager::init()
 			configuration_manager::load_delegate(&mame_ui_manager::config_load_pointers, this),
 			configuration_manager::save_delegate(&mame_ui_manager::config_save_pointers, this));
 
+	// register callbacks
+	machine().configuration().config_register("sliders", configuration_manager::load_delegate(&mame_ui_manager::sliders_load, this), configuration_manager::save_delegate(&mame_ui_manager::sliders_save, this));
+
 	// create mouse bitmap
 	uint32_t *dst = &m_mouse_bitmap.pix(0);
 	memcpy(dst,mouse_bitmap,32*32*sizeof(uint32_t));
@@ -717,7 +722,7 @@ void mame_ui_manager::display_startup_screens(bool first_time)
 				warning_text = machine_info().game_info_string();
 			if (!warning_text.empty())
 			{
-				warning_text.append(_("\n\nPress any key to continue"));
+				warning_text.append(_("\nPress any key to continue"));
 				set_handler(ui_callback_type::MODAL, handler_callback_func(handler_messagebox_anykey));
 			}
 			break;
@@ -981,12 +986,17 @@ float mame_ui_manager::get_line_height(float scale)
 {
 	int32_t const raw_font_pixel_height = get_font()->pixel_height();
 	float target_pixel_height = machine().render().ui_target().height();
+	float target_pixel_width = machine().render().ui_target().width();
+
+	if (machine().render().ui_target().is_ui_rotated())
+		std::swap(target_pixel_height, target_pixel_width);
 
 	// compute the font pixel height at the nominal size
 	float const one_to_one_line_height = float(raw_font_pixel_height) / target_pixel_height;
 
 	// determine the scale factor
-	float scale_factor = target_font_height() * scale / one_to_one_line_height;
+	float scale_factor = std::min(target_pixel_height / float(raw_font_pixel_height) / options().font_rows(),
+								target_pixel_width / float(raw_font_pixel_height) / options().font_rows());
 
 	// if our font is small-ish, do integral scaling
 	if (raw_font_pixel_height < 24)
@@ -1002,6 +1012,10 @@ float mame_ui_manager::get_line_height(float scale)
 			// otherwise, just ensure an integral scale factor
 			scale_factor = floorf(scale_factor);
 		}
+
+		// correct scale by pixel_aspect for integer factors in both axes
+		float pixel_aspect = std::max(machine().render().ui_target().integer_aspect(), 1.0f);
+		scale_factor *= pixel_aspect;
 	}
 	else
 	{
@@ -1062,10 +1076,13 @@ void mame_ui_manager::draw_outlined_box(render_container &container, float x0, f
 void mame_ui_manager::draw_outlined_box(render_container &container, float x0, float y0, float x1, float y1, rgb_t fgcolor, rgb_t bgcolor)
 {
 	container.add_rect(x0, y0, x1, y1, bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-	container.add_line(x0, y0, x1, y0, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-	container.add_line(x1, y0, x1, y1, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-	container.add_line(x1, y1, x0, y1, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-	container.add_line(x0, y1, x0, y0, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+	float tw = machine().render().ui_target().texel_width();
+	float th = machine().render().ui_target().texel_height();
+	container.add_rect(x0, y0, x1, y0 + th, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+	container.add_rect(x1, y0 + th, x1 - tw, y1 - th, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+	container.add_rect(x1, y1, x0, y1 - th, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+	container.add_rect(x0, y1 - th, x0 + tw, y0 + th, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
 }
 
 
@@ -1904,6 +1921,14 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
 				slider_alloc(util::string_format(_("%1$s Channel %d Volume"), snd.device().tag(), channel), -960, 0, 120, 10, std::bind(&mame_ui_manager::slider_devvol_chan, this, &snd, channel, _1, _2));
 	}
 
+	// add frame delay
+	slider_alloc(_("Frame Delay"), 0, machine.options().frame_delay(), 9, 1, std::bind(&mame_ui_manager::slider_framedelay, this, _1, _2));
+
+#ifdef _WIN32
+	// add vsync offset
+	slider_alloc(_("V-Sync Offset"), 0, machine.options().vsync_offset(), 1024, 1, std::bind(&mame_ui_manager::slider_vsync_offset, this, _1, _2));
+#endif
+
 	// add analog adjusters
 	for (auto &port : machine.ioport().ports())
 	{
@@ -1924,7 +1949,7 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
 		for (device_execute_interface &exec : execute_interface_enumerator(machine.root_device()))
 		{
 			std::string str = string_format(_("Overclock CPU %1$s"), exec.device().tag());
-			slider_alloc(std::move(str), 100, 1000, 4000, 10, std::bind(&mame_ui_manager::slider_overclock, this, std::ref(exec.device()), _1, _2));
+			slider_alloc(std::move(str), 100, 1000, 4000, 1, std::bind(&mame_ui_manager::slider_overclock, this, std::ref(exec.device()), _1, _2));
 		}
 		for (device_sound_interface &snd : sound_interface_enumerator(machine.root_device()))
 		{
@@ -1932,7 +1957,7 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
 			if (!snd.device().interface(exec) && snd.device().unscaled_clock() != 0)
 			{
 				std::string str = string_format(_("Overclock %1$s sound"), snd.device().tag());
-				slider_alloc(std::move(str), 100, 1000, 4000, 10, std::bind(&mame_ui_manager::slider_overclock, this, std::ref(snd.device()), _1, _2));
+				slider_alloc(std::move(str), 100, 1000, 4000, 1, std::bind(&mame_ui_manager::slider_overclock, this, std::ref(snd.device()), _1, _2));
 			}
 		}
 	}
@@ -2010,6 +2035,11 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
 		}
 	}
 
+	// add geometry sliders
+	slider_alloc(_("CRT H size"), 500, floor(downcast<osd_options &>(machine.options()).h_size() * 1000.0 + 0.5), 2000, 10, std::bind(&mame_ui_manager::slider_h_size, this, _1, _2));
+	slider_alloc(_("CRT H shift"), -64, downcast<osd_options &>(machine.options()).h_shift(), 64, 1, std::bind(&mame_ui_manager::slider_h_shift, this, _1, _2));
+	slider_alloc(_("CRT V shift"), -64, downcast<osd_options &>(machine.options()).v_shift(), 64, 1, std::bind(&mame_ui_manager::slider_v_shift, this, _1, _2));
+
 #ifdef MAME_DEBUG
 	// add crosshair adjusters
 	for (auto &port : machine.ioport().ports())
@@ -2027,6 +2057,8 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
 	}
 #endif
 
+	sliders_apply();
+
 	std::vector<ui::menu_item> items;
 	for (auto &slider : m_sliders)
 	{
@@ -2113,6 +2145,84 @@ int32_t mame_ui_manager::slider_devvol_chan(device_sound_interface *snd, int cha
 }
 
 
+//-------------------------------------------------
+//  slider_framedelay - global frame delay slider
+//  callback
+//-------------------------------------------------
+
+int32_t mame_ui_manager::slider_framedelay(std::string *str, int32_t newval)
+{
+	if (newval != SLIDER_NOCHANGE)
+		machine().video().set_framedelay(newval);
+	if (str)
+		*str = string_format(_("%1$3d"), machine().video().framedelay());
+	return machine().video().framedelay();
+}
+
+
+//--------------------------------------------------
+//  slider_vsync_offset - global vsync_offset slider
+//  callback
+//--------------------------------------------------
+
+int32_t mame_ui_manager::slider_vsync_offset(std::string *str, int32_t newval)
+{
+	if (newval != SLIDER_NOCHANGE)
+		machine().video().set_vsync_offset(newval);
+	if (str)
+		*str = string_format(_("%1$3d"), machine().video().vsync_offset());
+	return machine().video().vsync_offset();
+}
+
+
+//--------------------------------------------------
+//  slider_h_size - global h_size slider
+//  callback
+//--------------------------------------------------
+
+int32_t mame_ui_manager::slider_h_size(std::string *str, int32_t newval)
+{
+	if (newval != SLIDER_NOCHANGE)
+	{
+		float fval = (float)newval * 0.001f;
+		machine().options().set_value(OSDOPTION_H_SIZE, fval, OPTION_PRIORITY_CMDLINE);
+	}
+
+	if (str)
+		*str = string_format(_("%1.2f"), downcast<osd_options &>(machine().options()).h_size());
+	return floor(downcast<osd_options &>(machine().options()).h_size() * 1000.0 + 0.5);
+}
+
+
+//--------------------------------------------------
+//  slider_h_shift - global h_shift slider
+//  callback
+//--------------------------------------------------
+
+int32_t mame_ui_manager::slider_h_shift(std::string *str, int32_t newval)
+{
+	if (newval != SLIDER_NOCHANGE)
+		machine().options().set_value(OSDOPTION_H_SHIFT, newval, OPTION_PRIORITY_CMDLINE);
+	if (str)
+		*str = string_format(_("%1$3d"), downcast<osd_options &>(machine().options()).h_shift());
+	return downcast<osd_options &>(machine().options()).h_shift();
+}
+
+
+//--------------------------------------------------
+//  slider_v_shift - global v_shift slider
+//  callback
+//--------------------------------------------------
+
+int32_t mame_ui_manager::slider_v_shift(std::string *str, int32_t newval)
+{
+	if (newval != SLIDER_NOCHANGE)
+		machine().options().set_value(OSDOPTION_V_SHIFT, newval, OPTION_PRIORITY_CMDLINE);
+	if (str)
+		*str = string_format(_("%1$3d"), downcast<osd_options &>(machine().options()).v_shift());
+	return downcast<osd_options &>(machine().options()).v_shift();
+}
+
 
 //-------------------------------------------------
 //  slider_adjuster - analog adjuster slider
@@ -2169,6 +2279,7 @@ int32_t mame_ui_manager::slider_overclock(device_t &device, std::string *str, in
 
 	int32_t curval = floor(device.clock_scale() * 1000.0 + 0.5);
 	if (str)
+
 	{
 		if (curval % 10)
 			*str = string_format(_("%1$.1f%%"), float(curval) * 0.1f);
@@ -2750,3 +2861,90 @@ void ui_colors::refresh(const ui_options &options)
 	m_dipsw_color = options.dipsw_color();
 	m_slider_color = options.slider_color();
 }
+
+//-------------------------------------------------
+//  sliders_load - read data from the
+//  configuration file
+//-------------------------------------------------
+
+void mame_ui_manager::sliders_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode)
+{
+	// we only care about system files
+	if (cfg_type != config_type::SYSTEM)
+		return;
+
+	// might not have any data
+	if (parentnode == nullptr)
+		return;
+
+	// iterate over slider nodes
+	for (util::xml::data_node const *slider_node = parentnode->get_child("slider"); slider_node; slider_node = slider_node->get_next_sibling("slider"))
+	{
+		std::string desc = slider_node->get_attribute_string("desc", "");
+		int32_t saved_val = slider_node->get_attribute_int("value", 0);
+
+		// create a dummy slider to store the saved value
+		slider_saved_alloc(std::move(desc), 0, saved_val, 0, 0, nullptr);
+	}
+}
+
+
+//-------------------------------------------------
+//  sliders_appy - apply data from the conf. file
+//  This currently needs to be done on a separate
+//  step because sliders are not created yet when
+//  configuration file is loaded
+//-------------------------------------------------
+
+void mame_ui_manager::sliders_apply(void)
+{
+	// iterate over sliders and restore saved values
+	for (auto &slider : m_sliders)
+	{
+		for (auto &slider_saved : m_sliders_saved)
+		{
+			if (!strcmp(slider->description.c_str(), slider_saved->description.c_str()))
+			{
+				std::string tempstring;
+				slider->update(&tempstring, slider_saved->defval);
+				break;
+
+			}
+		}
+	}
+}
+
+
+//-------------------------------------------------
+//  sliders_save - save data to the configuration
+//  file
+//-------------------------------------------------
+
+void mame_ui_manager::sliders_save(config_type cfg_type, util::xml::data_node *parentnode)
+{
+	// we only care about system files
+	if (cfg_type != config_type::SYSTEM)
+		return;
+
+	std::string tempstring;
+	util::xml::data_node *slider_node;
+
+	// save UI sliders
+	for (auto &slider : m_sliders)
+	{
+		if (slider->description.find("Frame Delay") == std::string::npos &&
+			slider->description.find("V-Sync Offset") == std::string::npos &&
+			slider->description.find("Overclock") == std::string::npos &&
+			slider->description.find("Screen Refresh Rate") == std::string::npos &&
+			slider->description.find("CRT") == std::string::npos)
+			continue;
+
+		int32_t curval = slider->update(&tempstring, SLIDER_NOCHANGE);
+		if (curval != slider->defval)
+		{
+			slider_node = parentnode->add_child("slider", nullptr);
+			slider_node->set_attribute("desc", slider->description.c_str());
+			slider_node->set_attribute_int("value", curval);
+		}
+	}
+}
diff --git a/src/frontend/mame/ui/ui.h b/src/frontend/mame/ui/ui.h
index f64f43f0df3..f9c730662e5 100644
--- a/src/frontend/mame/ui/ui.h
+++ b/src/frontend/mame/ui/ui.h
@@ -337,18 +337,25 @@ private:
 	void set_handler(ui_callback_type callback_type, handler_callback_func &&callback);
 	void frame_update();
 	void exit();
+
 	void increase_frameskip();
 	void decrease_frameskip();
 	void config_load_warnings(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
 	void config_save_warnings(config_type cfg_type, util::xml::data_node *parentnode);
 	void config_load_pointers(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
 	void config_save_pointers(config_type cfg_type, util::xml::data_node *parentnode);
+	void sliders_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
+	void sliders_save(config_type cfg_type, util::xml::data_node *parentnode);
+	void sliders_apply(void);
 	template <typename... Params> void slider_alloc(Params &&...args) { m_sliders.push_back(std::make_unique<slider_state>(std::forward<Params>(args)...)); }
+	template <typename... Params> void slider_saved_alloc(Params &&...args) { m_sliders_saved.push_back(std::make_unique<slider_state>(std::forward<Params>(args)...)); }
 
 	// slider controls
 	int32_t slider_volume(std::string *str, int32_t newval);
 	int32_t slider_devvol(device_sound_interface *snd, std::string *str, int32_t newval);
 	int32_t slider_devvol_chan(device_sound_interface *snd, int channel, std::string *str, int32_t newval);
+	int32_t slider_framedelay(std::string *str, int32_t newval);
+	int32_t slider_vsync_offset(std::string *str, int32_t newval);
 	int32_t slider_adjuster(ioport_field &field, std::string *str, int32_t newval);
 	int32_t slider_speed(std::string *str, int32_t newval);
 	int32_t slider_overclock(device_t &device, std::string *str, int32_t newval);
@@ -370,12 +377,16 @@ private:
 	int32_t slider_beam_dot_size(screen_device &screen, std::string *str, int32_t newval);
 	int32_t slider_beam_intensity_weight(screen_device &screen, std::string *str, int32_t newval);
 	std::string slider_get_screen_desc(screen_device &screen);
+	int32_t slider_h_size(std::string *str, int32_t newval);
+	int32_t slider_h_shift(std::string *str, int32_t newval);
+	int32_t slider_v_shift(std::string *str, int32_t newval);
 #ifdef MAME_DEBUG
 	int32_t slider_crossscale(ioport_field &field, std::string *str, int32_t newval);
 	int32_t slider_crossoffset(ioport_field &field, std::string *str, int32_t newval);
 #endif
 
 	std::vector<std::unique_ptr<slider_state>> m_sliders;
+	std::vector<std::unique_ptr<slider_state>> m_sliders_saved;
 };
 
 
diff --git a/src/osd/modules/input/input_common.cpp b/src/osd/modules/input/input_common.cpp
index ddcd3805640..c33b929cc1a 100644
--- a/src/osd/modules/input/input_common.cpp
+++ b/src/osd/modules/input/input_common.cpp
@@ -27,7 +27,7 @@
 #define KEY_TRANS_WIN32(disc, virtual)
 #endif
 
-#if defined(OSD_SDL) || defined(SDLMAME_WIN32)
+#if defined(OSD_SDL) || defined(SDLMAME_WIN32) || defined(MISTER_INPUT)
 #include <SDL2/SDL.h>
 #define KEY_TRANS_SDL(sdlsc) SDL_SCANCODE_##sdlsc,
 #else
diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h
index 73ddd2bf936..863e4490a8b 100644
--- a/src/osd/modules/input/input_common.h
+++ b/src/osd/modules/input/input_common.h
@@ -28,6 +28,7 @@
 #include <mutex>
 #include <queue>
 
+#define MISTER_INPUT 1
 
 //============================================================
 //  PARAMETERS
@@ -195,7 +196,7 @@ struct key_trans_entry
 {
 	input_item_id   mame_key;
 
-#if defined(OSD_SDL) || defined(SDLMAME_WIN32)
+#if defined(OSD_SDL) || defined(SDLMAME_WIN32) || defined(MISTER_INPUT)
 	int             sdl_scancode;
 #endif
 #if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
diff --git a/src/osd/modules/input/input_mister.cpp b/src/osd/modules/input/input_mister.cpp
new file mode 100644
index 00000000000..bcca1e128ab
--- /dev/null
+++ b/src/osd/modules/input/input_mister.cpp
@@ -0,0 +1,774 @@
+// license:BSD-3-Clause
+// copyright-holders:Antonio Giner, Sergi Clara
+//============================================================
+//
+//  input_mister.cpp - nogpu input module
+//
+//============================================================
+
+#include "assignmenthelper.h"
+#include "modules/lib/osdlib.h"
+#include "modules/lib/osdobj_common.h"
+#include "input_common.h"
+
+
+// emu
+#include "emu.h"
+#include "strconv.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock2.h>
+#define socklen_t int
+#else
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+// nogpu UDP input server
+#define UDP_INPUT_PORT 32101
+#define MISTER_MAX_BUTTONS 10
+
+//joystick map
+#define MISTER_JOY_RIGHT (1 << 0)
+#define MISTER_JOY_LEFT  (1 << 1)
+#define MISTER_JOY_DOWN  (1 << 2)
+#define MISTER_JOY_UP    (1 << 3)
+#define MISTER_JOY_B1    (1 << 4)
+#define MISTER_JOY_B2    (1 << 5)
+#define MISTER_JOY_B3    (1 << 6)
+#define MISTER_JOY_B4    (1 << 7)
+#define MISTER_JOY_B5    (1 << 8)
+#define MISTER_JOY_B6    (1 << 9)
+#define MISTER_JOY_B7    (1 << 10)
+#define MISTER_JOY_B8    (1 << 11)
+#define MISTER_JOY_B9    (1 << 12)
+#define MISTER_JOY_B10   (1 << 13)
+
+//devices
+#define MISTER_JOYSTICK_DEVICE (1 << 0)
+#define MISTER_KEYBOARD_DEVICE (1 << 1)
+#define MISTER_MOUSE_DEVICE    (1 << 2)
+
+#pragma pack(1)
+
+typedef struct nogpu_joy_dig_inputs
+{
+	uint32_t frame;
+	uint8_t  order;
+	uint16_t joy1;
+	uint16_t joy2;
+} nogpu_joy_dig_inputs;
+
+typedef struct nogpu_joy_inputs
+{
+	uint32_t frame;
+	uint8_t  order;
+	uint16_t joy1;
+	uint16_t joy2;
+	int8_t   joy1_LX_analog;
+	int8_t   joy1_LY_analog;
+	int8_t   joy1_RX_analog;
+	int8_t   joy1_RY_analog;
+	int8_t   joy2_LX_analog;
+	int8_t   joy2_LY_analog;
+	int8_t   joy2_RX_analog;
+	int8_t   joy2_RY_analog;
+} nogpu_joy_inputs;
+
+typedef struct nogpu_keyboard_inputs
+{
+	uint32_t frame;
+	uint8_t  order;
+	uint8_t  keys[32];
+} nogpu_keyboard_inputs;
+
+typedef struct nogpu_ps2_inputs
+{
+	uint32_t frame;
+	uint8_t  order;
+	uint8_t  keys[32];
+	uint8_t  mouse_bits;
+	uint8_t  mouse_X;
+	uint8_t  mouse_Y;
+	uint8_t  mouse_Z;
+} nogpu_ps2_inputs;
+
+#pragma pack(0)
+
+namespace osd {
+
+namespace {
+
+
+//============================================================
+//  mister_init
+//============================================================
+
+class mister_input
+{
+public:
+	static int init_socket(int device, const osd_options &options);
+	static void deinit_socket();
+	static void poll_server();
+
+protected:
+	inline static int m_initialized;
+	inline static int m_sockfd;
+	inline static sockaddr_in m_server_addr;
+
+	inline static nogpu_joy_inputs m_joy_inputs;
+	inline static nogpu_ps2_inputs m_ps2_inputs;
+
+	inline static char recv_buff[std::max(sizeof(nogpu_joy_inputs), sizeof(nogpu_ps2_inputs))];
+	inline static bool joy_update_pending;
+	inline static bool keyboard_update_pending;
+	inline static bool mouse_update_pending;
+};
+
+
+//============================================================
+//  mister_input::init_socket
+//============================================================
+
+int mister_input::init_socket(int device, const osd_options &options)
+{
+	m_joy_inputs = {0};
+	m_ps2_inputs = {0};
+
+	if (m_initialized != 0)
+	{
+		if (m_initialized & device)
+			deinit_socket();
+		else
+		{
+			// already initialized, skip initialization
+			m_initialized |= device;
+			return 0;
+		}
+	}
+
+	#ifdef _WIN32
+		osd_printf_verbose("nogpu_input: Initializing Winsock...");
+		WSADATA wsa;
+		int result = WSAStartup(MAKEWORD(2, 2), &wsa);
+		if (result != NO_ERROR)
+		{
+			osd_printf_verbose("Failed. Error code : %d", WSAGetLastError());
+			return -1;
+		}
+		osd_printf_verbose("done.\n");
+	#endif
+
+	osd_printf_verbose("nogpu_input: Initializing socket... ");
+	m_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+	if (m_sockfd < 0)
+	{
+		osd_printf_verbose("Could not create socket\n");
+		return -1;
+	}
+	else
+		osd_printf_verbose(" done.\n");
+
+	short port = UDP_INPUT_PORT;
+	const char* local_host = options.mister_ip();
+
+	m_server_addr = {};
+	m_server_addr.sin_family = AF_INET;
+	m_server_addr.sin_port = htons(port);
+	m_server_addr.sin_addr.s_addr = inet_addr(local_host);
+
+	osd_printf_verbose("nogpu_input: Setting socket async... ");
+
+	#ifdef _WIN32
+		u_long opt = 1;
+		if (ioctlsocket(m_sockfd, FIONBIO, &opt) < 0)
+			osd_printf_verbose("Could not set nonblocking.\n");
+	#else
+		int flags;
+		flags = fcntl(m_sockfd, F_GETFD, 0);
+		if (flags < 0)
+			osd_printf_verbose("Could not get socket flags.\n");
+		else
+		{
+			flags |= O_NONBLOCK;
+			if (fcntl(m_sockfd, F_SETFL, flags) < 0)
+				osd_printf_verbose("Could not set nonblocking.\n");
+		}
+	#endif
+	osd_printf_verbose(" done.\n");
+
+	// Signal server for input
+	char buffer[1];
+	sendto(m_sockfd, buffer, 1, 0, (sockaddr *)&m_server_addr, sizeof(m_server_addr));
+
+	m_initialized |= device;
+	return 0;
+}
+
+
+//============================================================
+//  mister_input::deinit_socket
+//============================================================
+
+void mister_input::deinit_socket()
+{
+	osd_printf_verbose("nogpu_input: closing input socket.\n");
+#ifdef WIN32
+	closesocket(m_sockfd);
+	WSACleanup();
+#else
+	close(m_sockfd);
+#endif
+	m_initialized = 0;
+}
+
+
+//============================================================
+//  mister_input::poll_server
+//============================================================
+
+// Note: We have no way to tell whether the joysticks are analogue or digital during initialization.
+// With regards to PS2 inputs, we neither know if we have both keyboard and mouse or only one of them.
+// It's all determined later here by the size of the packes we receive.
+
+void mister_input::poll_server()
+{
+	socklen_t server_addr_size = sizeof(m_server_addr);
+	int bytes_recv = 0;
+	memset(recv_buff, 0, sizeof(recv_buff));
+
+	// Poll MiSTer server
+	do
+	{
+		bytes_recv = recvfrom(m_sockfd, recv_buff, sizeof(recv_buff), 0, (sockaddr*)&m_server_addr, &server_addr_size);
+		if (bytes_recv < 0)
+			continue;
+
+		switch (bytes_recv)
+		{
+			case sizeof(nogpu_joy_dig_inputs):
+			case sizeof(nogpu_joy_inputs):
+			{
+				nogpu_joy_inputs &new_inputs = (nogpu_joy_inputs&) recv_buff;
+				if (new_inputs.frame > m_joy_inputs.frame || (new_inputs.frame == m_joy_inputs.frame && new_inputs.order > m_joy_inputs.order))
+				{
+					m_joy_inputs = new_inputs;
+					joy_update_pending = true;
+				}
+				break;
+			}
+			case sizeof(nogpu_keyboard_inputs):
+			{
+				nogpu_keyboard_inputs &new_inputs = (nogpu_keyboard_inputs&) recv_buff;
+				if (new_inputs.frame > m_ps2_inputs.frame || (new_inputs.frame == m_ps2_inputs.frame && new_inputs.order > m_ps2_inputs.order))
+				{
+					nogpu_keyboard_inputs &inputs = (nogpu_keyboard_inputs&) m_ps2_inputs;
+					inputs = new_inputs;
+					keyboard_update_pending = true;
+				}
+				break;
+			}
+			case sizeof(nogpu_ps2_inputs):
+			{
+				nogpu_ps2_inputs &new_inputs = (nogpu_ps2_inputs&) recv_buff;
+				if (new_inputs.frame > m_ps2_inputs.frame || (new_inputs.frame == m_ps2_inputs.frame && new_inputs.order > m_ps2_inputs.order))
+				{
+					m_ps2_inputs = new_inputs;
+					keyboard_update_pending = true;
+					mouse_update_pending = true;
+				}
+				break;
+			}
+		}
+	}
+	while (bytes_recv > 0);
+}
+
+//============================================================
+//  mister_joystick_device
+//============================================================
+
+class mister_joystick_device : public device_info, protected joystick_assignment_helper
+{
+public:
+	mister_joystick_device(
+			std::string &&name,
+			std::string &&id,
+			input_module &module,
+			u32 player) : device_info(std::move(name), std::move(id), module) {};
+
+	virtual void poll(bool relative_reset) override {};
+	virtual void reset() override;
+	virtual void configure(osd::input_device &device) override;
+	void update(uint16_t state, int8_t lx, int8_t ly, int8_t rx, int8_t ry);
+
+protected:
+	u8 m_hats[4];
+	u8 m_buttons[MAX_BUTTONS];
+	s32 m_axes[4];
+};
+
+//============================================================
+//  mister_joystick_device::configure
+//============================================================
+
+void mister_joystick_device::configure(osd::input_device &device)
+ {
+	input_device::assignment_vector assignments;
+	char tempname[32];
+
+	input_item_id hatactual[4];
+
+	for (int i = 0 ; i < 4; i++)
+	{
+		input_item_id itemid;
+		m_hats[i] = 0;
+
+		switch (i)
+		{
+			case 0:
+				snprintf(tempname, sizeof(tempname), "Hat Up");
+				break;
+			case 1:
+				snprintf(tempname, sizeof(tempname), "Hat Down");
+				break;
+			case 2:
+				snprintf(tempname, sizeof(tempname), "Hat Left");
+				break;
+			case 3:
+				snprintf(tempname, sizeof(tempname), "Hat Right");
+				break;
+		}
+
+		itemid = input_item_id(ITEM_ID_HAT1UP + i);
+
+		hatactual[i] = device.add_item(
+				tempname,
+				std::string_view(),
+				itemid,
+				generic_button_get_state<u8>,
+				&m_hats[i]);
+	}
+
+	// add axes
+	input_item_id axisactual[4];
+	for (int i = 0; i < 4; i++)
+	{
+		char tempname[3];
+		snprintf(tempname, sizeof(tempname), "A%d", i);
+		axisactual[i] = device.add_item(tempname, std::string_view(), input_item_id(ITEM_ID_XAXIS + i), generic_axis_get_state<s32>, &m_axes[i]);
+	}
+
+	add_directional_assignments(assignments, axisactual[0], axisactual[1], hatactual[2], hatactual[3], hatactual[0], hatactual[1]);
+
+	// loop over all buttons
+	for (int button = 0; button < MISTER_MAX_BUTTONS; button++)
+	{
+		input_item_id itemid;
+		m_buttons[button] = 0;
+
+		itemid = input_item_id(ITEM_ID_BUTTON1 + button);
+
+		input_item_id const actual = device.add_item(
+				default_button_name(button),
+				std::string_view(),
+				itemid,
+				generic_button_get_state<u8>,
+				&m_buttons[button]);
+
+		input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual));
+		assignments.emplace_back(ioport_type(IPT_BUTTON1 + button), SEQ_TYPE_STANDARD, seq);
+
+		// assign the first few buttons to UI actions and pedals
+		switch (button)
+		{
+		case 0:
+			assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq);
+			assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq);
+			break;
+		case 1:
+			assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq);
+			assignments.emplace_back(IPT_UI_BACK, SEQ_TYPE_STANDARD, seq);
+			break;
+		case 2:
+			assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq);
+			assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq);
+			break;
+		case 3:
+			assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq);
+			break;
+		}
+	}
+
+	// set default assignments
+	device.set_default_assignments(std::move(assignments));
+ }
+
+//============================================================
+//  mister_joystick_device::update
+//============================================================
+
+void mister_joystick_device::update(uint16_t state, int8_t lx, int8_t ly, int8_t rx, int8_t ry)
+{
+	m_hats[0] = (state & MISTER_JOY_UP)    ? 0xff : 0x00;
+	m_hats[1] = (state & MISTER_JOY_DOWN)  ? 0xff : 0x00;
+	m_hats[2] = (state & MISTER_JOY_LEFT)  ? 0xff : 0x00;
+	m_hats[3] = (state & MISTER_JOY_RIGHT) ? 0xff : 0x00;
+	m_buttons[0] = (state & MISTER_JOY_B1)? 0xff : 0x00;
+	m_buttons[1] = (state & MISTER_JOY_B2)? 0xff : 0x00;
+	m_buttons[2] = (state & MISTER_JOY_B3)? 0xff : 0x00;
+	m_buttons[3] = (state & MISTER_JOY_B4)? 0xff : 0x00;
+	m_buttons[4] = (state & MISTER_JOY_B5)? 0xff : 0x00;
+	m_buttons[5] = (state & MISTER_JOY_B6)? 0xff : 0x00;
+	m_buttons[6] = (state & MISTER_JOY_B7)? 0xff : 0x00;
+	m_buttons[7] = (state & MISTER_JOY_B8)? 0xff : 0x00;
+	m_buttons[8] = (state & MISTER_JOY_B9)? 0xff : 0x00;
+	m_buttons[9] = (state & MISTER_JOY_B10)? 0xff : 0x00;
+	m_axes[0] = lx * 512;
+	m_axes[1] = ly * 512;
+	m_axes[2] = rx * 512;
+	m_axes[3] = ry * 512;
+}
+
+//============================================================
+//  mister_joystick_device::reset
+//============================================================
+
+void mister_joystick_device::reset()
+{
+	std::fill(std::begin(m_buttons), std::end(m_buttons), 0);
+	std::fill(std::begin(m_hats), std::end(m_hats), 0);
+	std::fill(std::begin(m_axes), std::end(m_axes), 0);
+}
+
+
+//============================================================
+//  joystick_input_mister
+//============================================================
+
+class joystick_input_mister : public osd_module, public input_module, public mister_input
+{
+public:
+	joystick_input_mister() :
+		osd_module(OSD_JOYSTICKINPUT_PROVIDER, "mister"),
+		joystick1("MiSTer", "joy1", *this, 1),
+		joystick2("MiSTer", "joy2", *this, 2) {};
+
+	~joystick_input_mister() { deinit_socket(); };
+	virtual int init(osd_interface &osd, const osd_options &options) override { return init_socket(MISTER_JOYSTICK_DEVICE, options); };
+	virtual void input_init(running_machine &machine) override;
+	virtual void poll_if_necessary(bool relative_reset) override;
+
+private:
+	mister_joystick_device joystick1;
+	mister_joystick_device joystick2;
+};
+
+//============================================================
+//  joystick_input_mister::input_init
+//============================================================
+
+void joystick_input_mister::input_init(running_machine &machine)
+{
+	osd::input_device &osddev1 = machine.input().add_device(DEVICE_CLASS_JOYSTICK, "MiSTer", "joy1", (void *)&joystick1);
+	joystick1.configure(osddev1);
+	joystick1.reset();
+
+	osd::input_device &osddev2 = machine.input().add_device(DEVICE_CLASS_JOYSTICK, "MiSTer", "joy2", (void *)&joystick2);
+	joystick2.configure(osddev2);
+	joystick2.reset();
+}
+
+//============================================================
+//  joystick_input_mister::poll_if_necessary
+//============================================================
+
+void joystick_input_mister::poll_if_necessary(bool relative_reset)
+{
+	poll_server();
+
+	// Update joysticks button state
+	if (joy_update_pending)
+	{
+		joystick1.update(m_joy_inputs.joy1, m_joy_inputs.joy1_LX_analog, m_joy_inputs.joy1_LY_analog, m_joy_inputs.joy1_RX_analog, m_joy_inputs.joy1_RY_analog);
+		joystick2.update(m_joy_inputs.joy2, m_joy_inputs.joy2_LX_analog, m_joy_inputs.joy2_LY_analog, m_joy_inputs.joy2_RX_analog, m_joy_inputs.joy2_RY_analog);
+		joy_update_pending = false;
+	}
+}
+
+
+// KEYBOARD
+
+//============================================================
+//  mister_keyboard_device
+//============================================================
+
+class mister_keyboard_device : public device_info
+{
+public:
+	mister_keyboard_device(
+			std::string &&name,
+			std::string &&id,
+			input_module &module,
+			u32 player) : device_info(std::move(name), std::move(id), module) {};
+
+	virtual void poll(bool relative_reset) override {};
+	virtual void reset() override;
+	virtual void configure(osd::input_device &device) override;
+	void update(uint8_t *keys);
+
+protected:
+	uint8_t m_state[256];
+};
+
+
+//============================================================
+//  mister_keyboard_device::configure
+//============================================================
+
+void mister_keyboard_device::configure(osd::input_device &device)
+{
+	keyboard_trans_table const &table = keyboard_trans_table::instance();
+
+	// populate it
+	for (int keynum = 0; table[keynum].mame_key != ITEM_ID_INVALID; keynum++)
+	{
+		input_item_id itemid = table[keynum].mame_key;
+		device.add_item(
+				table[keynum].ui_name,
+				std::string_view(),
+				itemid,
+				generic_button_get_state<std::uint8_t>,
+				&m_state[table[keynum].sdl_scancode]);
+	}
+}
+
+//============================================================
+//  mister_keyboard_device::update
+//============================================================
+
+void mister_keyboard_device::update(uint8_t *keys)
+{
+	for (int i = 0; i < 256; i++)
+		m_state[i] = (1 & (keys[i / 8] >> (i % 8))) ? 0x80 : 0x00;
+}
+
+//============================================================
+//  mister_keyboard_device::reset
+//============================================================
+
+void mister_keyboard_device::reset()
+{
+	std::fill(std::begin(m_state), std::end(m_state), 0);
+}
+
+//============================================================
+//  keyboard_input_mister
+//============================================================
+
+class keyboard_input_mister : public osd_module, public input_module, public mister_input
+{
+public:
+	keyboard_input_mister() :
+		osd_module(OSD_KEYBOARDINPUT_PROVIDER, "mister"),
+		keyboard1("MiSTer", "keyboard", *this, 1) {};
+
+	~keyboard_input_mister() { deinit_socket(); };
+	virtual int init(osd_interface &osd, const osd_options &options) override { return init_socket(MISTER_KEYBOARD_DEVICE, options); };
+	virtual void input_init(running_machine &machine) override;
+	virtual void poll_if_necessary(bool relative_reset) override;
+
+private:
+	mister_keyboard_device keyboard1;
+};
+
+//============================================================
+//  keyboard_input_mister::input_init
+//============================================================
+
+void keyboard_input_mister::input_init(running_machine &machine)
+{
+	osd::input_device &osddev1 = machine.input().add_device(DEVICE_CLASS_KEYBOARD, "MiSTer", "keyboard1", (void *)&keyboard1);
+	keyboard1.configure(osddev1);
+	keyboard1.reset();
+}
+
+//============================================================
+//  keyboard_input_mister::poll_if_necessary
+//============================================================
+
+void keyboard_input_mister::poll_if_necessary(bool relative_reset)
+{
+	poll_server();
+
+	// Update keyboard state
+	if (keyboard_update_pending)
+	{
+		keyboard1.update(m_ps2_inputs.keys);
+		keyboard_update_pending = false;
+	}
+}
+
+
+// MOUSE
+
+//============================================================
+//  mister_mouse_device
+//============================================================
+
+class mister_mouse_device : public device_info
+{
+public:
+	mister_mouse_device(
+			std::string &&name,
+			std::string &&id,
+			input_module &module,
+			u32 player) : device_info(std::move(name), std::move(id), module) {};
+
+	virtual void poll(bool relative_reset) override;
+	virtual void reset() override;
+	virtual void configure(osd::input_device &device) override;
+	void update(uint8_t mouse_bits, uint8_t mouse_X, uint8_t mouse_Y, uint8_t mouse_Z);
+
+
+protected:
+	s32 m_lX, m_lY;
+	s32 m_x, m_y;
+	uint8_t m_buttons[3];
+};
+
+//============================================================
+//  mister_mouse_device::configure
+//============================================================
+
+void mister_mouse_device::configure(osd::input_device &device)
+{
+	// add the m_axes
+	device.add_item(
+			"X",
+			std::string_view(),
+			ITEM_ID_XAXIS,
+			generic_axis_get_state<s32>,
+			&m_lX);
+	device.add_item(
+			"Y",
+			std::string_view(),
+			ITEM_ID_YAXIS,
+			generic_axis_get_state<s32>,
+			&m_lY);
+
+	// populate the buttons
+	for (int butnum = 0; butnum < 3; butnum++)
+	{
+		device.add_item(
+				default_button_name(butnum),
+				std::string_view(),
+				input_item_id(ITEM_ID_BUTTON1 + butnum),
+				generic_button_get_state<std::uint8_t>,
+				&m_buttons[butnum]);
+	}
+}
+
+//============================================================
+//  mister_mouse_device::poll
+//============================================================
+
+void mister_mouse_device::poll(bool relative_reset)
+{
+	if (relative_reset)
+	{
+		m_lX = std::exchange(m_x, 0);
+		m_lY = std::exchange(m_y, 0);
+	}
+}
+
+//============================================================
+//  mister_mouse_device::update
+//============================================================
+
+void mister_mouse_device::update(uint8_t mouse_bits, uint8_t mouse_X, uint8_t mouse_Y, uint8_t mouse_Z)
+{
+	s32 rel_x = mouse_X - ((mouse_bits << 4) & 0x100);
+	s32 rel_y = mouse_Y - ((mouse_bits << 3) & 0x100);
+
+	m_x += rel_x * input_device::RELATIVE_PER_PIXEL;
+	m_y -= rel_y * input_device::RELATIVE_PER_PIXEL;
+
+	for (int i = 0; i < 3; i++)
+		m_buttons[i] = mouse_bits & (1 << i) ? 0x80 : 0x00;
+}
+
+//============================================================
+//  mister_mouse_device::reset
+//============================================================
+
+void mister_mouse_device::reset()
+{
+	std::fill(std::begin(m_buttons), std::end(m_buttons), 0);
+	m_lX = m_lY = 0;
+	m_x = m_y = 0;
+}
+
+//============================================================
+//  mouse_input_mister
+//============================================================
+
+class mouse_input_mister : public osd_module, public input_module, public mister_input
+{
+public:
+	mouse_input_mister() :
+		osd_module(OSD_MOUSEINPUT_PROVIDER, "mister"),
+		mouse1("MiSTer", "mouse", *this, 1) {};
+
+	~mouse_input_mister() { deinit_socket(); };
+	virtual int init(osd_interface &osd, const osd_options &options) override { return init_socket(MISTER_MOUSE_DEVICE, options); };
+	virtual void input_init(running_machine &machine) override;
+	virtual void poll_if_necessary(bool relative_reset) override;
+
+private:
+	mister_mouse_device mouse1;
+};
+
+//============================================================
+//  mouse_input_mister::input_init
+//============================================================
+
+void mouse_input_mister::input_init(running_machine &machine)
+{
+	osd::input_device &osddev1 = machine.input().add_device(DEVICE_CLASS_MOUSE, "MiSTer", "mouse1", (void *)&mouse1);
+	mouse1.configure(osddev1);
+	mouse1.reset();
+}
+
+//============================================================
+//  mouse_input_mister::poll_if_necessary
+//============================================================
+
+void mouse_input_mister::poll_if_necessary(bool relative_reset)
+{
+	poll_server();
+
+	// Update keyboard state
+	if (mouse_update_pending)
+	{
+		mouse1.update(m_ps2_inputs.mouse_bits, m_ps2_inputs.mouse_X, m_ps2_inputs.mouse_Y, m_ps2_inputs.mouse_Z);
+		mouse_update_pending = false;
+	}
+
+	mouse1.poll(relative_reset);
+}
+
+
+} // anonymous namesapce
+
+} // namespace osd
+
+
+MODULE_DEFINITION(JOYSTICKINPUT_MISTER, osd::joystick_input_mister)
+MODULE_DEFINITION(KEYBOARDINPUT_MISTER, osd::keyboard_input_mister)
+MODULE_DEFINITION(MOUSEINPUT_MISTER, osd::mouse_input_mister)
diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp
index 5b6813dcd59..2eae2cb7031 100644
--- a/src/osd/modules/input/input_rawinput.cpp
+++ b/src/osd/modules/input/input_rawinput.cpp
@@ -1,5 +1,5 @@
 // license:BSD-3-Clause
-// copyright-holders:Aaron Giles, Brad Hughes
+// copyright-holders:Aaron Giles, Brad Hughes, Chester C. Rumpled
 //============================================================
 //
 //  input_rawinput.cpp - Windows RawInput input implementation
@@ -34,7 +34,10 @@
 // standard windows headers
 #include <windows.h>
 #include <tchar.h>
-
+extern "C"
+{
+	#include <hidsdi.h>
+}
 
 namespace osd {
 
@@ -421,6 +424,280 @@ private:
 };
 
 
+//============================================================
+//  rawinput_joystick_device
+//============================================================
+
+class rawinput_joystick_device : public rawinput_device
+{
+public:
+	rawinput_joystick_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) :
+		rawinput_device(std::move(name), std::move(id), module, handle),
+		m_joystick({ { 0 } })
+	{
+	}
+
+	virtual void reset() override
+	{
+		m_joystick = {};
+	}
+
+	virtual void process_event(RAWINPUT const &rawinput) override
+	{
+		for (size_t button_index = 0; button_index != MAX_BUTTONS; ++button_index)
+			m_joystick.buttons[button_index] = 0x00;
+
+		for (size_t axis_index = 0; axis_index != 9; ++axis_index)
+			m_joystick.axes[axis_index] = 0;
+
+		for (size_t hat_index = 0; hat_index != 4; ++hat_index)
+			m_joystick.hats[hat_index] = 0x00;
+
+		UINT preparsed_data_buf_size = 0;
+		if (GetRawInputDeviceInfo(rawinput.header.hDevice, RIDI_PREPARSEDDATA, NULL, &preparsed_data_buf_size) != 0)
+			return;
+
+		std::unique_ptr<uint8_t[]> preparsed_data_buf = std::make_unique<uint8_t[]>(preparsed_data_buf_size);
+		PHIDP_PREPARSED_DATA preparsed_data_buf_ptr = reinterpret_cast<PHIDP_PREPARSED_DATA>(preparsed_data_buf.get());
+
+		if (GetRawInputDeviceInfo(rawinput.header.hDevice, RIDI_PREPARSEDDATA, preparsed_data_buf_ptr, &preparsed_data_buf_size) < 0)
+			return;
+
+		HIDP_CAPS joystick_caps;
+		if (HidP_GetCaps(preparsed_data_buf_ptr, &joystick_caps) != HIDP_STATUS_SUCCESS)
+			return;
+
+		set_button_caps(rawinput, preparsed_data_buf_ptr, joystick_caps.NumberInputButtonCaps);
+		set_value_caps(rawinput, preparsed_data_buf_ptr, joystick_caps.NumberInputValueCaps);
+	}
+
+	virtual void configure(input_device &device) override
+	{
+		RID_DEVICE_INFO rdi = {};
+		rdi.cbSize = sizeof(RID_DEVICE_INFO);
+		UINT rdi_size = rdi.cbSize;
+		GetRawInputDeviceInfoW(device_handle(), RIDI_DEVICEINFO, &rdi, &rdi_size);
+
+		// populate it
+		const char *const rawinput_pov_names[] = {"DPAD Up", "DPAD Down", "DPAD Left", "DPAD Right"};
+
+		for (size_t pov_index = 0; pov_index != 4; ++pov_index)
+			device.add_item(
+					rawinput_pov_names[pov_index],
+					std::string_view(),
+					ITEM_ID_OTHER_SWITCH,
+					generic_button_get_state<BYTE>,
+					&m_joystick.hats[pov_index]);
+
+		// loop over all axes
+		for (int axis = 0; axis != 9; ++axis)
+		{
+			char temp_name[512];
+			input_item_id itemid;
+
+			if (axis < INPUT_MAX_AXIS)
+				itemid = (input_item_id)(ITEM_ID_XAXIS + axis);
+			else if (axis < INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE)
+				itemid = (input_item_id)(ITEM_ID_ADD_ABSOLUTE1 - INPUT_MAX_AXIS + axis);
+			else
+				itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE;
+
+			snprintf(temp_name, sizeof(temp_name), "A%d", axis + 1);
+			device.add_item(
+					temp_name,
+					std::string_view(),
+					itemid,
+					generic_axis_get_state<int32_t>,
+					&m_joystick.axes[axis]);
+		}
+
+		// add the item to the device
+		for (size_t button_index = 0; button_index != MAX_BUTTONS; ++button_index)
+			device.add_item(default_button_name(button_index),
+					std::string_view(),
+					static_cast<input_item_id>(ITEM_ID_BUTTON1 + button_index),
+					generic_button_get_state<BYTE>,
+					&m_joystick.buttons[button_index]);
+	}
+
+private:
+	joystick_state m_joystick;
+
+	int32_t sign_extend(uint32_t value, size_t bits)
+	{
+		const int32_t mask = 1U << (bits - 1);
+		return (value ^ mask) - mask;
+	}
+
+	const unsigned long get_bitmask(const unsigned short bits)
+	{
+		return (1 << bits) - 1;
+	}
+
+	void set_axis_value(RAWINPUT const &rawinput, const PHIDP_PREPARSED_DATA& preparsed_data_buf_ptr, const HIDP_VALUE_CAPS& value_cap, const size_t axis_index)
+	{
+		if (value_cap.PhysicalMin < value_cap.PhysicalMax)
+		{
+			LONG scaled_axis_value = 0;
+			if (HidP_GetScaledUsageValue(HidP_Input, value_cap.UsagePage, 0, value_cap.Range.UsageMin, &scaled_axis_value, preparsed_data_buf_ptr,
+				(PCHAR)rawinput.data.hid.bRawData, rawinput.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS)
+			{
+				m_joystick.axes[axis_index] = normalize_absolute_axis(static_cast<double>(scaled_axis_value), static_cast<double>(value_cap.PhysicalMin), static_cast<double>(value_cap.PhysicalMax));
+				return;
+			}
+		}
+
+		ULONG axis_value = 0;
+		if (HidP_GetUsageValue(HidP_Input, value_cap.UsagePage, 0, value_cap.Range.UsageMin, &axis_value, preparsed_data_buf_ptr,
+			(PCHAR)rawinput.data.hid.bRawData, rawinput.data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS)
+		{
+			return;
+		}
+
+		const unsigned long bitmask = get_bitmask(value_cap.BitSize);
+		const ULONG logical_min = value_cap.LogicalMin & bitmask;
+		const ULONG logical_max = value_cap.LogicalMax & bitmask;
+
+		if (logical_min < logical_max)
+		{
+			m_joystick.axes[axis_index] = normalize_absolute_axis(static_cast<double>(axis_value & bitmask), static_cast<double>(logical_min), static_cast<double>(logical_max));
+		}
+		else
+		{
+			m_joystick.axes[axis_index] = normalize_absolute_axis(
+				static_cast<double>(sign_extend(axis_value & bitmask, value_cap.BitSize)),
+				static_cast<double>(sign_extend(logical_min, value_cap.BitSize)),
+					static_cast<double>(sign_extend(logical_max, value_cap.BitSize)));
+		}
+	}
+
+	void set_value_caps(RAWINPUT const &rawinput, const PHIDP_PREPARSED_DATA& preparsed_data_buf_ptr, USHORT number_input_value_caps)
+	{
+		if (number_input_value_caps < 1)
+			return;
+
+		std::vector<HIDP_VALUE_CAPS> value_caps(number_input_value_caps);
+		if (HidP_GetValueCaps(HidP_Input, value_caps.data(), &number_input_value_caps, preparsed_data_buf_ptr) != HIDP_STATUS_SUCCESS)
+		return;
+
+		for (HIDP_VALUE_CAPS& value_cap : value_caps)
+		{
+			switch (value_cap.Range.UsageMin)
+			{
+				case HID_USAGE_GENERIC_HATSWITCH:
+				{
+					m_joystick.hats[0] = 0x00;
+					m_joystick.hats[1] = 0x00;
+					m_joystick.hats[2] = 0x00;
+					m_joystick.hats[3] = 0x00;
+
+					ULONG usage_value = 0;
+					if (HidP_GetUsageValue(HidP_Input, value_cap.UsagePage, 0, value_cap.Range.UsageMin, &usage_value, preparsed_data_buf_ptr,
+						(PCHAR)rawinput.data.hid.bRawData, rawinput.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS)
+					{
+						const LONG hat_value = usage_value - value_cap.LogicalMin;
+
+						switch (hat_value)
+						{
+							case 0:
+							{
+								m_joystick.hats[0] = 0x80;
+								break;
+							}
+							case 1:
+							{
+								m_joystick.hats[0] = 0x80;
+								m_joystick.hats[3] = 0x80;
+								break;
+							}
+							case 2:
+							{
+								m_joystick.hats[3] = 0x80;
+								break;
+							}
+							case 3:
+							{
+								m_joystick.hats[1] = 0x80;
+								m_joystick.hats[3] = 0x80;
+								break;
+							}
+							case 4:
+							{
+								m_joystick.hats[1] = 0x80;
+								break;
+							}
+							case 5:
+							{
+								m_joystick.hats[1] = 0x80;
+								m_joystick.hats[2] = 0x80;
+								break;
+							}
+							case 6:
+							{
+								m_joystick.hats[2] = 0x80;
+								break;
+							}
+							case 7:
+							{
+								m_joystick.hats[0] = 0x80;
+								m_joystick.hats[2] = 0x80;
+								break;
+							}
+							default:
+							{
+								break;
+							}
+						}
+					}
+					break;
+				}
+				default:
+				{
+					const size_t axis_index = static_cast<size_t>(value_cap.Range.UsageMin) - static_cast<size_t>(HID_USAGE_GENERIC_X);
+					
+					if (axis_index >= 0 && axis_index <= 8)
+					{
+						set_axis_value(rawinput, preparsed_data_buf_ptr, value_cap, axis_index);
+					}
+
+					break;
+				}
+			}
+		}
+	}
+
+	void set_button_caps(RAWINPUT const &rawinput, const PHIDP_PREPARSED_DATA& preparsed_data_buf_ptr, USHORT number_input_button_caps)
+	{
+		if (number_input_button_caps < 1)
+			return;
+
+		ULONG buttons_length = 0;
+		if (HidP_GetUsagesEx(HidP_Input, 0, nullptr, &buttons_length, preparsed_data_buf_ptr, (PCHAR)rawinput.data.hid.bRawData, rawinput.data.hid.dwSizeHid) != HIDP_STATUS_BUFFER_TOO_SMALL)
+		{
+			return;
+		}
+		
+		std::unique_ptr<USAGE_AND_PAGE[]> usages = std::make_unique<USAGE_AND_PAGE[]>(buttons_length);
+		if (HidP_GetUsagesEx(HidP_Input, 0, usages.get(), &buttons_length, preparsed_data_buf_ptr, (PCHAR)rawinput.data.hid.bRawData, rawinput.data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS)
+		{
+			for (size_t usage_page_index = 0; usage_page_index < buttons_length; usage_page_index++)
+			{
+				const uint16_t usage_page = usages[usage_page_index].UsagePage;
+				const uint16_t usage = usages[usage_page_index].Usage;
+				if (usage_page == HID_USAGE_PAGE_BUTTON && usage > 0)
+				{
+					const size_t button_index = static_cast<size_t>(usage - 1);
+					if (button_index < MAX_BUTTONS)
+					{
+						m_joystick.buttons[button_index] = 0x80;
+					}
+				}
+			}
+		}
+	}
+};
+
+
 //============================================================
 //  rawinput_mouse_device
 //============================================================
@@ -700,18 +977,27 @@ public:
 
 		// finally, register to receive raw input WM_INPUT messages if we found devices
 		RAWINPUTDEVICE registration;
-		registration.usUsagePage = usagepage();
+		registration.usUsagePage = HID_USAGE_PAGE_GENERIC;
 		registration.usUsage = usage();
 		registration.dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
 		registration.hwndTarget = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window();
 
-		// register the device
-		RegisterRawInputDevices(&registration, 1, sizeof(registration));
+		// some joysticks are reported as gamepads and viceversa, so we register both
+		std::vector<RAWINPUTDEVICE> registrations;
+		registrations.push_back(registration);
+
+		if (registration.usUsage == HID_USAGE_GENERIC_JOYSTICK)
+		{
+			registration.usUsage = HID_USAGE_GENERIC_GAMEPAD;
+			registrations.push_back(registration);
+		}
+
+		if (!RegisterRawInputDevices(registrations.data(), registrations.size(), sizeof(registration)))
+			osd_printf_error("Error registering RawInput devices.\n");
 	}
 
 protected:
 	virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) = 0;
-	virtual USHORT usagepage() = 0;
 	virtual USHORT usage() = 0;
 
 	template<class TDevice>
@@ -726,17 +1012,53 @@ protected:
 		if (name_length > 1 && GetRawInputDeviceInfoW(rawinputdevice.hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == UINT(-1))
 			return nullptr;
 
+		const std::wstring tname_basic_string = tname.get();
+		tname.reset();
+
 		// if this is an RDP name, skip it
-		if (wcsstr(tname.get(), L"Root#RDP_") != nullptr)
+		if (tname_basic_string.find(L"Root#RDP_") != std::string::npos)
 			return nullptr;
 
-		// improve the name
-		std::string utf8_name = text::from_wstring(rawinput_device_improve_name(tname.get()));
+		// this is for duplicate devices in a collection such as extra mouse buttons
+		if (tname_basic_string.find(L"&Col01") != std::string::npos)
+			return nullptr;
 
 		// set device ID to raw input name
-		std::string utf8_id = text::from_wstring(tname.get());
+		std::string utf8_id = osd::text::from_wstring(tname_basic_string);
+		std::string utf8_name;
 
-		tname.reset();
+		HANDLE hid_handle = CreateFileW(tname_basic_string.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+
+		if (hid_handle == INVALID_HANDLE_VALUE)
+		{
+			GetLastError();
+
+			// improve the name
+			utf8_name = osd::text::from_wstring(rawinput_device_improve_name(tname_basic_string));
+		}
+		else
+		{
+			std::wstring buffer;
+			buffer.resize(256);
+
+			if (HidD_GetProductString(hid_handle, buffer.data(), static_cast<ULONG>(buffer.size())))
+			{
+				buffer.erase(buffer.find(L'\0'));
+				utf8_name = osd::text::from_wstring(buffer);
+			}
+
+			buffer.resize(256);
+
+			if (HidD_GetManufacturerString(hid_handle, buffer.data(), static_cast<ULONG>(buffer.size())))
+			{
+				buffer.erase(buffer.find(L'\0'));
+
+				if (buffer != L"")
+					utf8_name += " (" + osd::text::from_wstring(buffer) + ")";
+			}
+		}
+
+		CloseHandle(hid_handle);
 
 		// allocate a device
 		return &create_device<TDevice>(
@@ -883,8 +1205,7 @@ public:
 	}
 
 protected:
-	virtual USHORT usagepage() override { return 1; }
-	virtual USHORT usage() override { return 6; }
+	virtual USHORT usage() override { return HID_USAGE_GENERIC_KEYBOARD; }
 
 	virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
 	{
@@ -898,6 +1219,98 @@ protected:
 };
 
 
+//============================================================
+//  joystick_input_rawinput - RawInput joystick module
+//============================================================
+
+class joystick_input_rawinput : public rawinput_module
+{
+public:
+	joystick_input_rawinput() : rawinput_module(OSD_JOYSTICKINPUT_PROVIDER, "rawinput")
+	{
+	}
+
+protected:
+	virtual USHORT usage() override { return HID_USAGE_GENERIC_JOYSTICK; }
+
+	void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
+	{
+		// first make sure this is not a keyboard or a mouse
+		if (device.dwType != RIM_TYPEHID)
+			return;
+
+		// also check if it's a valid joystick or gamepad
+		if (!is_valid_joystick(device))
+			return;
+
+		// allocate and link in a new device
+		create_rawinput_device<rawinput_joystick_device>(DEVICE_CLASS_JOYSTICK, device);
+
+	}
+
+	bool is_valid_joystick(RAWINPUTDEVICELIST const &device)
+	{
+		RID_DEVICE_INFO rdi = {};
+		rdi.cbSize = sizeof(RID_DEVICE_INFO);
+
+		UINT rdi_size = rdi.cbSize;
+		if (GetRawInputDeviceInfoW(device.hDevice, RIDI_DEVICEINFO, &rdi, &rdi_size) < 1)
+			return false;
+
+		if (rdi.hid.usUsage != HID_USAGE_GENERIC_JOYSTICK && rdi.hid.usUsage != HID_USAGE_GENERIC_GAMEPAD)
+			return false;
+
+		// get the device information
+		UINT preparsed_data_buffer_size;
+		GetRawInputDeviceInfoW(device.hDevice, RIDI_PREPARSEDDATA, NULL, &preparsed_data_buffer_size);
+
+		std::unique_ptr<uint8_t[]> preparsed_data_buffer = std::make_unique<uint8_t[]>(preparsed_data_buffer_size);
+		PHIDP_PREPARSED_DATA preparsed_data_ptr = reinterpret_cast<PHIDP_PREPARSED_DATA>(preparsed_data_buffer.get());
+		if (GetRawInputDeviceInfoW(device.hDevice, RIDI_PREPARSEDDATA, preparsed_data_ptr, &preparsed_data_buffer_size) < 0)
+			return false;
+
+		HIDP_CAPS joystick_capabilities;
+		if (HidP_GetCaps(preparsed_data_ptr, &joystick_capabilities) != HIDP_STATUS_SUCCESS)
+			return false;
+
+		if (joystick_capabilities.NumberInputButtonCaps < 1 && joystick_capabilities.NumberInputValueCaps < 1)
+			return false;
+
+		std::vector<HIDP_BUTTON_CAPS> button_capabilities(joystick_capabilities.NumberInputButtonCaps);
+		if (HidP_GetButtonCaps(HidP_Input, button_capabilities.data(), &joystick_capabilities.NumberInputButtonCaps, preparsed_data_ptr) != HIDP_STATUS_SUCCESS)
+			return false;
+
+		// Populate joystick device buttons
+		constexpr uint32_t button_usage_page = HID_USAGE_PAGE_BUTTON;
+		constexpr size_t buttons_length_cap = 32;
+		size_t button_count = 0;
+
+		for (const auto& button_capability : button_capabilities)
+		{
+			uint16_t usage_min = button_capability.Range.UsageMin;
+			uint16_t usage_max = button_capability.Range.UsageMax;
+
+			if (usage_min == 0 || usage_max == 0)
+				continue;
+
+			size_t button_index_min = static_cast<size_t>(usage_min - 1);
+			size_t button_index_max = static_cast<size_t>(usage_max - 1);
+
+			if (button_capability.UsagePage == button_usage_page && button_index_min < buttons_length_cap)
+			{
+				button_index_max = std::min(buttons_length_cap - 1, button_index_max);
+				button_count = std::max(button_count, button_index_max + 1);
+			}
+		}
+
+		// should we even allow a joystick that has no buttons?
+		if (button_count < 1)
+			return false;
+
+		return true;
+	}
+};
+
 //============================================================
 //  mouse_input_rawinput - RawInput mouse module
 //============================================================
@@ -910,8 +1323,7 @@ public:
 	}
 
 protected:
-	virtual USHORT usagepage() override { return 1; }
-	virtual USHORT usage() override { return 2; }
+	virtual USHORT usage() override { return HID_USAGE_GENERIC_MOUSE; }
 
 	virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
 	{
@@ -937,8 +1349,7 @@ public:
 	}
 
 protected:
-	virtual USHORT usagepage() override { return 1; }
-	virtual USHORT usage() override { return 2; }
+	virtual USHORT usage() override { return HID_USAGE_GENERIC_MOUSE; }
 
 	virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override
 	{
@@ -964,6 +1375,7 @@ namespace osd {
 namespace {
 
 MODULE_NOT_SUPPORTED(keyboard_input_rawinput, OSD_KEYBOARDINPUT_PROVIDER, "rawinput")
+MODULE_NOT_SUPPORTED(joystick_input_rawinput, OSD_JOYSTICKINPUT_PROVIDER, "rawinput")
 MODULE_NOT_SUPPORTED(mouse_input_rawinput, OSD_MOUSEINPUT_PROVIDER, "rawinput")
 MODULE_NOT_SUPPORTED(lightgun_input_rawinput, OSD_LIGHTGUNINPUT_PROVIDER, "rawinput")
 
@@ -974,5 +1386,6 @@ MODULE_NOT_SUPPORTED(lightgun_input_rawinput, OSD_LIGHTGUNINPUT_PROVIDER, "rawin
 #endif // defined(OSD_WINDOWS)
 
 MODULE_DEFINITION(KEYBOARDINPUT_RAWINPUT, osd::keyboard_input_rawinput)
+MODULE_DEFINITION(JOYSTICKINPUT_RAWINPUT, osd::joystick_input_rawinput)
 MODULE_DEFINITION(MOUSEINPUT_RAWINPUT, osd::mouse_input_rawinput)
 MODULE_DEFINITION(LIGHTGUNINPUT_RAWINPUT, osd::lightgun_input_rawinput)
diff --git a/src/osd/modules/input/input_wincommon.h b/src/osd/modules/input/input_wincommon.h
index 6b34d60abca..536a58b3efa 100644
--- a/src/osd/modules/input/input_wincommon.h
+++ b/src/osd/modules/input/input_wincommon.h
@@ -35,6 +35,14 @@ struct mouse_state
 	BYTE    rgbButtons[8];
 };
 
+// state information for a joystick
+struct joystick_state
+{
+	int32_t axes[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+	BYTE	buttons[MAX_BUTTONS];
+	BYTE	hats[4] = { 0x00, 0x00, 0x00, 0x00 };
+};
+
 } // namespace osd
 
 #endif // MAME_OSD_INPUT_INPUT_WINCOMMON_H
diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp
index 82fabfdc0ba..2025327e87d 100644
--- a/src/osd/modules/lib/osdobj_common.cpp
+++ b/src/osd/modules/lib/osdobj_common.cpp
@@ -74,7 +74,7 @@ const options_entry osd_options::s_option_entries[] =
 	{ OSDOPTION_WINDOW ";w",                     "0",              core_options::option_type::BOOLEAN,   "enable window mode; otherwise, full screen mode is assumed" },
 	{ OSDOPTION_MAXIMIZE ";max",                 "1",              core_options::option_type::BOOLEAN,   "default to maximized windows" },
 	{ OSDOPTION_WAITVSYNC ";vs",                 "0",              core_options::option_type::BOOLEAN,   "enable waiting for the start of VBLANK before flipping screens (reduces tearing effects)" },
-	{ OSDOPTION_SYNCREFRESH ";srf",              "0",              core_options::option_type::BOOLEAN,   "enable using the start of VBLANK for throttling instead of the game time" },
+	{ OSDOPTION_SYNC_MODE "(0-4)",               "2",              core_options::option_type::INTEGER,   "sync mode"},
 	{ OSD_MONITOR_PROVIDER,                      OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "monitor discovery method: " },
 
 	// per-window options
@@ -106,10 +106,56 @@ const options_entry osd_options::s_option_entries[] =
 
 	// full screen options
 	{ nullptr,                                   nullptr,          core_options::option_type::HEADER,    "OSD FULL SCREEN OPTIONS" },
-	{ OSDOPTION_SWITCHRES,                       "0",              core_options::option_type::BOOLEAN,   "enable resolution switching" },
+	{ OSDOPTION_SWITCHRES,                       "1",              core_options::option_type::BOOLEAN,   "enable resolution switching" },
+	{ OSDOPTION_SWITCHRES_INI,                   "0",              core_options::option_type::BOOLEAN,   "read configuration from switchres.ini" },
+	{ OSDOPTION_SWITCHRES_BACKEND,               OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Switchres backend to use (adl, ati, powerstrip, xrandr, drmkms)" },
+	{ OSDOPTION_MODE_SETTING,                    "0",              core_options::option_type::BOOLEAN,   "force resolution switching through Switchres backend" },
+	{ OSDOPTION_MODELINE_GENERATION ";ml",       "1",              core_options::option_type::BOOLEAN,   "Automatic generation of modelines based on the specified monitor type" },
+	{ OSDOPTION_MONITOR ";m",                    "generic_15",     core_options::option_type::STRING,    "Monitor type, e.g.: generic_15, arcade_15, lcd, custom, etc." },
+	{ OSDOPTION_INTERLACE ";in",                 "1",              core_options::option_type::BOOLEAN,   "Enable interlaced scanning when necessary" },
+	{ OSDOPTION_DOUBLESCAN ";ds",                "0",              core_options::option_type::BOOLEAN,   "Enable double scanning when necessary (unsupported by some backends/gpus)" },
+	{ OSDOPTION_SUPER_WIDTH ";cs",               "2560",           core_options::option_type::INTEGER,   "Automatically apply -unevenstretchx if resolution width is equal or greater than this value" },
+	{ OSDOPTION_CHANGERES ";cr",                 "1",              core_options::option_type::BOOLEAN,   "Enable dynamic in-game video mode switching" },
+	{ OSDOPTION_LOCK_SYSTEM_MODES ";lsm",        "1",              core_options::option_type::BOOLEAN,   "Lock system (non-custom) video modes, only use modes created by us" },
+	{ OSDOPTION_LOCK_UNSUPPORTED_MODES ";lum",   "1",              core_options::option_type::BOOLEAN,   "Lock video modes reported as unsupported by your monitor's EDID" },
+	{ OSDOPTION_REFRESH_DONT_CARE ";rdc",        "0",              core_options::option_type::BOOLEAN,   "Ignore video mode's refresh reported by OS when checking ranges" },
+	{ OSDOPTION_DOTCLOCK_MIN ";dcm",             "0",              core_options::option_type::FLOAT,     "Lowest pixel clock supported by video card, in MHz, default is 0" },
+	{ OSDOPTION_H_SIZE,                          "1",              core_options::option_type::FLOAT,     "Apply horizontal size correction to calculated mode"},
+	{ OSDOPTION_H_SHIFT,                         "0",              core_options::option_type::INTEGER,   "Apply horizontal shift correction to calculated mode"},
+	{ OSDOPTION_V_SHIFT,                         "0",              core_options::option_type::INTEGER,   "Apply vertical shift correction to calculated mode"},
+	{ OSDOPTION_V_SHIFT_CORRECT,                 "0",              core_options::option_type::INTEGER,   "Apply vertical shift correction for multi-standard consumer CRT TVs"},
+	{ OSDOPTION_PIXEL_PRECISION,                 "1",              core_options::option_type::BOOLEAN,   "Calculate horizontal values with 1-pixel precision to improve horizontal centering" },
+	{ OSDOPTION_SYNC_REFRESH_TOLERANCE ";srt",   "2.0",            core_options::option_type::FLOAT,     "Maximum refresh difference, in Hz, allowed in order to synchronize" },
+	{ OSDOPTION_INTERLACE_FORCE_EVEN,            "0",              core_options::option_type::BOOLEAN,   "Calculate all vertical values of interlaced modes as even numbers. Required by AMD APU hardware on Linux" },
+	{ OSDOPTION_AUTOSYNC,                        "1",              core_options::option_type::BOOLEAN,   "automatically enable syncrefresh if refresh difference is below syncrefresh_tolerance" },
+	{ OSDOPTION_AUTOFILTER,                      "1",              core_options::option_type::BOOLEAN,   "automatically set bilinear filtering with fractional stretching or interlaced " },
+	{ OSDOPTION_AUTOSTRETCH,                     "1",              core_options::option_type::BOOLEAN,   "automatically set scaling mode (integer or fractional) based on the selected video mode " },
+	{ OSDOPTION_SCALE_PROPORTIONAL,              "1",              core_options::option_type::BOOLEAN,   "Scale both axes by the same factor, when integer scaling is applied" },
+	{ OSDOPTION_DRI_DEVICE,                      OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Force drm video card for vsync (card0, card1, etc.) or auto for automatic." },
+	{ OSDOPTION_SCREEN_COMPOSITING,              "0",              core_options::option_type::BOOLEAN,   "Readjust relative screen positions of a multi-display setup after mode switching (Linux)" },
+	{ OSDOPTION_SCREEN_REORDERING,               "0",              core_options::option_type::BOOLEAN,   "Reallocates desktop multiple screens stacked vertically, so super-resolutions fit (Linux)" },
+	{ OSDOPTION_ALLOW_HW_REFRESH,                "0",              core_options::option_type::BOOLEAN,   "Allow on-the-fly mode addition (Windows)" },
+	{ OSDOPTION_MODELINE ";mode",                OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Use custom defined modeline" },
+	{ OSDOPTION_PS_TIMING ";pst",                OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Use custom Powertrip timing string" },
+	{ OSDOPTION_LCD_RANGE ";lcd",                OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom LCD range, VfreqMin-VfreqMax, in Hz, e.g.: 55.50-61.00" },
+	{ OSDOPTION_CRT_RANGE "0",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range, see documentation for details." },
+	{ OSDOPTION_CRT_RANGE "1",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "2",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "3",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "4",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "5",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "6",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "7",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "8",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_CRT_RANGE "9",                   OSDOPTVAL_AUTO,   core_options::option_type::STRING,    "Add custom CRT range" },
+	{ OSDOPTION_MISTER_IP,                       "0.0.0.0",        core_options::option_type::STRING,    "nogpu device ip address" },
+	{ OSDOPTION_MISTER_COMPRESSION,              OSDOPTVAL_NONE,   core_options::option_type::STRING,    "nogpu device frame compression algorithm (none, lz4)" },
+	{ OSDOPTION_MISTER_WINDOW,                   "0",              core_options::option_type::BOOLEAN,   "nogpu show client window (affects performace)" },
+	{ OSDOPTION_MISTER_FD_MARGIN,                "1.5",            core_options::option_type::FLOAT,     "nogpu security margin for automatic frame delay, in ms."},
+	{ OSDOPTION_MISTER_INTERLACED_FB,            "1",              core_options::option_type::BOOLEAN,   "nogpu blit odd/even lines separately."},
 
 	{ nullptr,                                   nullptr,          core_options::option_type::HEADER,    "OSD ACCELERATED VIDEO OPTIONS" },
-	{ OSDOPTION_FILTER ";glfilter;flt",          "1",              core_options::option_type::BOOLEAN,   "use bilinear filtering when scaling emulated video" },
+	{ OSDOPTION_FILTER ";glfilter;flt",          "0",              core_options::option_type::BOOLEAN,   "use bilinear filtering when scaling emulated video" },
 	{ OSDOPTION_PRESCALE "(1-20)",               "1",              core_options::option_type::INTEGER,   "scale emulated video by this factor before applying filters/shaders" },
 
 #if USE_OPENGL
@@ -242,6 +288,7 @@ void osd_common_t::register_options()
 	REGISTER_MODULE(m_mod_man, RENDERER_SDL1);
 #endif
 	REGISTER_MODULE(m_mod_man, RENDERER_NONE);
+	REGISTER_MODULE(m_mod_man, RENDERER_MISTER);
 
 	REGISTER_MODULE(m_mod_man, SOUND_WASAPI);
 	REGISTER_MODULE(m_mod_man, SOUND_XAUDIO2);
@@ -288,12 +335,14 @@ void osd_common_t::register_options()
 	REGISTER_MODULE(m_mod_man, KEYBOARDINPUT_RAWINPUT);
 	REGISTER_MODULE(m_mod_man, KEYBOARDINPUT_DINPUT);
 	REGISTER_MODULE(m_mod_man, KEYBOARDINPUT_WIN32);
+	REGISTER_MODULE(m_mod_man, KEYBOARDINPUT_MISTER);
 	REGISTER_MODULE(m_mod_man, KEYBOARD_NONE);
 
 	REGISTER_MODULE(m_mod_man, MOUSEINPUT_SDL);
 	REGISTER_MODULE(m_mod_man, MOUSEINPUT_RAWINPUT);
 	REGISTER_MODULE(m_mod_man, MOUSEINPUT_DINPUT);
 	REGISTER_MODULE(m_mod_man, MOUSEINPUT_WIN32);
+	REGISTER_MODULE(m_mod_man, MOUSEINPUT_MISTER);
 	REGISTER_MODULE(m_mod_man, MOUSE_NONE);
 
 	REGISTER_MODULE(m_mod_man, LIGHTGUNINPUT_SDL);
@@ -307,6 +356,8 @@ void osd_common_t::register_options()
 	REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_WINHYBRID);
 	REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_DINPUT);
 	REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_XINPUT);
+	REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_RAWINPUT);
+	REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_MISTER);
 	REGISTER_MODULE(m_mod_man, JOYSTICK_NONE);
 
 	REGISTER_MODULE(m_mod_man, OUTPUT_NONE);
@@ -693,6 +744,9 @@ void osd_common_t::init_subsystems()
 
 	// various modules depend on having a window handle
 	m_render = &select_module_options<render_module>(OSD_RENDERER_PROVIDER);
+
+	m_switchres.init(machine());
+
 	if (!video_init())
 	{
 		video_exit();
@@ -757,6 +811,7 @@ void osd_common_t::poll_input_modules(bool relative_reset)
 void osd_common_t::exit_subsystems()
 {
 	video_exit();
+	m_switchres.exit();
 }
 
 void osd_common_t::video_exit()
diff --git a/src/osd/modules/lib/osdobj_common.h b/src/osd/modules/lib/osdobj_common.h
index 7103e7e7fdd..d2be9191a80 100644
--- a/src/osd/modules/lib/osdobj_common.h
+++ b/src/osd/modules/lib/osdobj_common.h
@@ -17,6 +17,7 @@
 
 #include "modules/osdmodule.h"
 #include "modules/output/output_module.h"
+#include "modules/switchres/switchres_module.h"
 
 #include "emuopts.h"
 
@@ -57,7 +58,7 @@
 #define OSDOPTION_WINDOW                "window"
 #define OSDOPTION_MAXIMIZE              "maximize"
 #define OSDOPTION_WAITVSYNC             "waitvsync"
-#define OSDOPTION_SYNCREFRESH           "syncrefresh"
+#define OSDOPTION_SYNC_MODE             "sync_mode"
 
 #define OSDOPTION_SCREEN                "screen"
 #define OSDOPTION_ASPECT                "aspect"
@@ -96,6 +97,44 @@
 #define OSDOPTION_BGFX_LUT              "bgfx_lut"
 #define OSDOPTION_BGFX_AVI_NAME         "bgfx_avi_name"
 
+#define OSDOPTION_SWITCHRES_INI         "switchres_ini"
+#define OSDOPTION_SWITCHRES_BACKEND     "switchres_backend"
+#define OSDOPTION_MODE_SETTING          "modesetting"
+#define OSDOPTION_MODELINE_GENERATION   "modeline_generation"
+#define OSDOPTION_MONITOR               "monitor"
+#define OSDOPTION_INTERLACE             "interlace"
+#define OSDOPTION_DOUBLESCAN            "doublescan"
+#define OSDOPTION_SUPER_WIDTH           "super_width"
+#define OSDOPTION_CHANGERES             "changeres"
+#define OSDOPTION_LOCK_SYSTEM_MODES     "lock_system_modes"
+#define OSDOPTION_LOCK_UNSUPPORTED_MODES "lock_unsupported_modes"
+#define OSDOPTION_REFRESH_DONT_CARE     "refresh_dont_care"
+#define OSDOPTION_DOTCLOCK_MIN          "dotclock_min"
+#define OSDOPTION_H_SIZE                "h_size"
+#define OSDOPTION_H_SHIFT               "h_shift"
+#define OSDOPTION_V_SHIFT               "v_shift"
+#define OSDOPTION_V_SHIFT_CORRECT       "v_shift_correct"
+#define OSDOPTION_PIXEL_PRECISION       "pixel_precision"
+#define OSDOPTION_SYNC_REFRESH_TOLERANCE "sync_refresh_tolerance"
+#define OSDOPTION_INTERLACE_FORCE_EVEN  "interlace_force_even"
+#define OSDOPTION_AUTOSYNC              "autosync"
+#define OSDOPTION_AUTOFILTER            "autofilter"
+#define OSDOPTION_AUTOSTRETCH           "autostretch"
+#define OSDOPTION_SCALE_PROPORTIONAL    "scale_proportional"
+#define OSDOPTION_DRI_DEVICE            "dri_device"
+#define OSDOPTION_SCREEN_COMPOSITING    "screen_compositing"
+#define OSDOPTION_SCREEN_REORDERING     "screen_reordering"
+#define OSDOPTION_ALLOW_HW_REFRESH      "allow_hw_refresh"
+#define OSDOPTION_MODELINE              "modeline"
+#define OSDOPTION_PS_TIMING             "ps_timing"
+#define OSDOPTION_LCD_RANGE             "lcd_range"
+#define OSDOPTION_CRT_RANGE             "crt_range"
+#define OSDOPTION_MISTER_IP             "mister_ip"
+#define OSDOPTION_MISTER_COMPRESSION    "mister_compression"
+#define OSDOPTION_MISTER_WINDOW         "mister_window"
+#define OSDOPTION_MISTER_FD_MARGIN      "mister_fd_margin"
+#define OSDOPTION_MISTER_INTERLACED_FB  "mister_interlaced_fb"
+
 #define OSDOPTVAL_AUTO                  "auto"
 #define OSDOPTVAL_NONE                  "none"
 
@@ -132,7 +171,7 @@ public:
 	bool window() const { return bool_value(OSDOPTION_WINDOW); }
 	bool maximize() const { return bool_value(OSDOPTION_MAXIMIZE); }
 	bool wait_vsync() const { return bool_value(OSDOPTION_WAITVSYNC); }
-	bool sync_refresh() const { return bool_value(OSDOPTION_SYNCREFRESH); }
+	int sync_mode() const { return int_value(OSDOPTION_SYNC_MODE); }
 
 	// per-window options
 	const char *screen() const { return value(OSDOPTION_SCREEN); }
@@ -146,6 +185,43 @@ public:
 
 	// full screen options
 	bool switch_res() const { return bool_value(OSDOPTION_SWITCHRES); }
+	bool switchres_ini() const { return bool_value(OSDOPTION_SWITCHRES_INI); }
+	const char *switchres_backend() const { return value(OSDOPTION_SWITCHRES_BACKEND); }
+	bool mode_setting() const { return bool_value(OSDOPTION_MODE_SETTING); }
+	bool modeline_generation() const { return bool_value(OSDOPTION_MODELINE_GENERATION); }
+	const char *monitor() const { return value(OSDOPTION_MONITOR); }
+	bool doublescan() const { return bool_value(OSDOPTION_DOUBLESCAN); }
+	bool interlace() const { return bool_value(OSDOPTION_INTERLACE); }
+	int super_width() const { return int_value(OSDOPTION_SUPER_WIDTH); }
+	bool changeres() const { return int_value(OSDOPTION_CHANGERES); }
+	bool lock_system_modes() const { return bool_value(OSDOPTION_LOCK_SYSTEM_MODES); }
+	bool lock_unsupported_modes() const { return bool_value(OSDOPTION_LOCK_UNSUPPORTED_MODES); }
+	bool refresh_dont_care() const { return bool_value(OSDOPTION_REFRESH_DONT_CARE); }
+	float dotclock_min() const { return float_value(OSDOPTION_DOTCLOCK_MIN); }
+	float h_size() const { return float_value(OSDOPTION_H_SIZE); }
+	int h_shift() const { return int_value(OSDOPTION_H_SHIFT); }
+	int v_shift() const { return int_value(OSDOPTION_V_SHIFT); }
+	int v_shift_correct() const { return int_value(OSDOPTION_V_SHIFT_CORRECT); }
+	bool pixel_precision() const { return bool_value(OSDOPTION_PIXEL_PRECISION); }
+	float sync_refresh_tolerance() const { return float_value(OSDOPTION_SYNC_REFRESH_TOLERANCE); }
+	bool interlace_force_even() const { return bool_value(OSDOPTION_INTERLACE_FORCE_EVEN); }
+	bool autosync() const { return bool_value(OSDOPTION_AUTOSYNC); }
+	bool autofilter() const { return bool_value(OSDOPTION_AUTOFILTER); }
+	bool autostretch() const { return bool_value(OSDOPTION_AUTOSTRETCH); }
+	bool scale_proportional() const { return bool_value(OSDOPTION_SCALE_PROPORTIONAL); }
+	const char *dri_device() const { return value(OSDOPTION_DRI_DEVICE); }
+	bool screen_compositing() const { return bool_value(OSDOPTION_SCREEN_COMPOSITING); }
+	bool screen_reordering() const { return bool_value(OSDOPTION_SCREEN_REORDERING); }
+	bool allow_hw_refresh() const { return bool_value(OSDOPTION_ALLOW_HW_REFRESH); }
+	const char *modeline() const { return value(OSDOPTION_MODELINE); }
+	const char *ps_timing() const { return value(OSDOPTION_PS_TIMING); }
+	const char *lcd_range() const { return value(OSDOPTION_LCD_RANGE); }
+	const char *crt_range(int index) const { return value(util::string_format("%s%d", OSDOPTION_CRT_RANGE, index)); }
+	const char *mister_ip() const { return value(OSDOPTION_MISTER_IP); }
+	const char *mister_compression() const { return value(OSDOPTION_MISTER_COMPRESSION); }
+	bool mister_window() const { return bool_value(OSDOPTION_MISTER_WINDOW); }
+	float mister_fd_margin() const { return float_value(OSDOPTION_MISTER_FD_MARGIN); }
+	bool mister_interlaced_fb() const { return bool_value(OSDOPTION_MISTER_INTERLACED_FB); }
 
 	// accelerated video options
 	bool filter() const { return bool_value(OSDOPTION_FILTER); }
@@ -252,11 +328,14 @@ public:
 	virtual std::unique_ptr<osd::network_device> open_network_device(int id, osd::network_handler &handler) override;
 	virtual std::vector<osd::network_device_info> list_network_devices() override;
 
+	virtual const char *switchres_mode(int i) override { return m_switchres.display_mode_to_txt(i); }
+
 	// FIXME: everything below seems to be osd specific and not part of
 	//        this INTERFACE but part of the osd IMPLEMENTATION
 
 	// getters
 	running_machine &machine() const { assert(m_machine != nullptr); return *m_machine; }
+	switchres_module *switchres() { return &m_switchres; }
 
 	virtual void debugger_update();
 
@@ -334,6 +413,7 @@ protected:
 	monitor_module* m_monitor_module;
 	std::unique_ptr<osd_watchdog> m_watchdog;
 	std::vector<ui::menu_item> m_sliders;
+	switchres_module m_switchres;
 
 private:
 	std::unordered_map<std::string, std::string> m_option_descs;
diff --git a/src/osd/modules/osdwindow.h b/src/osd/modules/osdwindow.h
index 9167a775682..4b915714160 100644
--- a/src/osd/modules/osdwindow.h
+++ b/src/osd/modules/osdwindow.h
@@ -375,6 +375,7 @@ public:
 	virtual void record() { }
 	virtual void toggle_fsfx() { }
 	virtual bool sliders_dirty() { return m_sliders_dirty; }
+	virtual int restart() { return 0; }
 
 protected:
 	virtual void build_slider_list() { }
@@ -412,14 +413,13 @@ struct osd_video_config
 	// hardware options
 	int                 waitvsync;                  // spin until vsync
 	int                 syncrefresh;                // sync only to refresh rate
+	int                 sync_mode;                  // sync refresh mode (0-3)
 	int                 switchres;                  // switch resolutions
+	int                 framedelay;					// frame delay
 
 	// d3d, accel, opengl
 	int                 filter;                     // enable filtering
 
-	// d3d
-	int                 triplebuf;                  // triple buffer
-
 	//============================================================
 	// SDL - options
 	//============================================================
diff --git a/src/osd/modules/render/draw13.cpp b/src/osd/modules/render/draw13.cpp
index a36ee8a8f52..e28c375de09 100644
--- a/src/osd/modules/render/draw13.cpp
+++ b/src/osd/modules/render/draw13.cpp
@@ -19,6 +19,7 @@
 // OSD headers
 #include "sdlopts.h"
 #include "window.h"
+#include "modules/monitor/monitor_common.h"
 
 // lib/util
 #include "options.h"
@@ -37,6 +38,14 @@
 #include <iterator>
 #include <list>
 
+#ifdef SDLMAME_X11
+#include <unistd.h>
+// DRM
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <fcntl.h>
+#endif
+
 
 namespace osd {
 
@@ -234,6 +243,18 @@ static inline bool is_transparent(const float &a)
 	return (a <  0.0001f);
 }
 
+
+//============================================================
+//  DRM
+//============================================================
+
+#ifdef SDLMAME_X11
+static int drm_open(const char *dri_device);
+static void drm_waitvblank(int crtc);
+static int fd = 0;
+static const char* dri_device = nullptr;
+#endif
+
 //============================================================
 //  CONSTRUCTOR & DESTRUCTOR
 //============================================================
@@ -458,8 +479,19 @@ int renderer_sdl2::create()
 		SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
 	}
 
+	bool renderer_vsync = true;
+#ifdef SDLMAME_X11
+	if (window().index() == 0 && video_config.syncrefresh && video_config.sync_mode != 0)
+	{
+		// Try to open DRM device
+		fd = drm_open(dri_device);
+		if (fd != 0)
+			renderer_vsync = (video_config.sync_mode == 2 || video_config.sync_mode == 4)? true : false;
+	}
+#endif
+
 	if (video_config.waitvsync)
-		m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
+		m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, renderer_vsync? SDL_RENDERER_PRESENTVSYNC : 0 | SDL_RENDERER_ACCELERATED);
 	else
 		m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_ACCELERATED);
 
@@ -484,6 +516,146 @@ int renderer_sdl2::create()
 }
 
 
+#ifdef SDLMAME_X11
+//============================================================
+//  drm_open
+//============================================================
+
+static int drm_open(const char *dri_device)
+{
+	int fd = 0;
+	char dri_path[16];
+	char *node = dri_path;
+
+	// Dri device forced by user
+	if (strcmp(dri_device, "auto") != 0)
+	{
+		osd_printf_verbose("drm_open: %s for by user\n", dri_device);
+		snprintf(node, sizeof(dri_path), "/dev/dri/%s", dri_device);
+	}
+
+	// Automatic selection
+	else
+	{
+		// Get an array of drm devices to check
+		int num_devices = drmGetDevices2(0, NULL, 0);
+		if (num_devices <= 0)
+		{
+			osd_printf_error("drm_open: couldn't find any drm device\n");
+			return 0;
+		}
+
+		drmDevicePtr *devices = (drmDevicePtr*)calloc(num_devices, sizeof(drmDevicePtr));
+		if (drmGetDevices2(0, devices, num_devices) < 0)
+		{
+			osd_printf_error("drm_open: drmGetDevices2() failed\n");
+			return 0;
+		}
+
+		// Parse device list to find the first one with a valid connector
+		bool found = false;
+
+		for (int i = 0; i < num_devices; i++)
+		{
+			// Skip non-primary nodes
+			if (devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY))
+				node = devices[i]->nodes[DRM_NODE_PRIMARY];
+
+			else continue;
+
+			fd = open(node, O_RDWR | O_CLOEXEC);
+			if (fd < 0)
+			{
+				osd_printf_error("drm_open: couldn't open %s\n", node);
+				continue;
+			}
+			drmModeRes *resources = drmModeGetResources(fd);
+			if (resources && resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0)
+			{
+				for (int j = 0; j < resources->count_connectors; j++)
+				{
+					drmModeConnector *conn = drmModeGetConnector(fd, resources->connectors[j]);
+					if (!conn) continue;
+
+					// We found a valid connector, use it
+					if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0)
+						found = true;
+
+					drmModeFreeConnector(conn);
+					if (found) break;
+				}
+			}
+			drmModeFreeResources(resources);
+			close(fd);
+
+			if (found) break;
+		}
+
+		drmFreeDevices(devices, num_devices);
+		free(devices);
+
+		if (!found)
+		{
+			osd_printf_error("drm_open: couldn't find any device with a valid connector\n");
+			return 0;
+		}
+	}
+
+	fd = open(node, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+	{
+		osd_printf_error("drm_open: cannot open %s\n", node);
+		return 0;
+	}
+
+	osd_printf_verbose("drm_open: %s successfully opened\n", node);
+	return fd;
+}
+
+//============================================================
+//  drm_waitvblank
+//============================================================
+
+static void drm_waitvblank(int crtc)
+{
+
+	drmVBlank vbl;
+	memset(&vbl, 0, sizeof(vbl));
+	vbl.request.sequence = 1;
+
+	// handle vblank for all SR managed crtc
+	// this is a hack based on SDL reported screen index
+	// it won't work on multi-gpu
+	// TO DO: find a correct way to map screen to crtc
+
+	// single screen (default)
+	vbl.request.type = DRM_VBLANK_RELATIVE;
+
+	// two screens
+	if (crtc == 1) vbl.request.type = drmVBlankSeqType(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY);
+
+	// multi-screen
+	else if (crtc > 1)
+	{
+		static uint64_t caps;
+		static bool caps_checked = false;
+
+		if (!caps_checked)
+		{
+			caps_checked = true;
+			if (drmGetCap(fd, DRM_CAP_VBLANK_HIGH_CRTC, &caps))
+				osd_printf_error("A newer kernel is needed for vblank syncing on multi screen\n");
+		}
+		if (caps)
+			vbl.request.type = drmVBlankSeqType(DRM_VBLANK_RELATIVE | ((crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) & DRM_VBLANK_HIGH_CRTC_MASK));
+	}
+
+	if (drmWaitVBlank(fd, &vbl) != 0)
+		osd_printf_verbose("drmWaitVBlank failed\n");
+}
+#endif
+
+
 //============================================================
 //  drawsdl_xy_to_render_target
 //============================================================
@@ -607,11 +779,23 @@ int renderer_sdl2::draw(int update)
 
 	window().m_primlist->release_lock();
 
+#ifdef SDLMAME_X11
+	// wait for vertical retrace
+	if ((video_config.sync_mode == 3 || video_config.sync_mode == 4) && video_config.syncrefresh && fd)
+		drm_waitvblank(window().monitor()->oshandle());
+#endif
+
 	m_last_blit_pixels = blit_pixels;
 	m_last_blit_time = -osd_ticks();
 	SDL_RenderPresent(m_sdl_renderer);
 	m_last_blit_time += osd_ticks();
 
+#ifdef SDLMAME_X11
+	// wait for vertical retrace
+	if ((video_config.sync_mode == 1 || video_config.sync_mode == 2) && video_config.syncrefresh && fd)
+		drm_waitvblank(window().monitor()->oshandle());
+#endif
+
 	return 0;
 }
 
@@ -1019,6 +1203,10 @@ int video_sdl2::init(osd_interface &osd, osd_options const &options)
 		}
 	}
 
+#ifdef SDLMAME_X11
+	dri_device = dynamic_cast<sdl_options const &>(options).dri_device();
+#endif
+
 	return 0;
 }
 
diff --git a/src/osd/modules/render/drawd3d.cpp b/src/osd/modules/render/drawd3d.cpp
index 542e2f8955d..b3881b163b3 100644
--- a/src/osd/modules/render/drawd3d.cpp
+++ b/src/osd/modules/render/drawd3d.cpp
@@ -31,6 +31,10 @@
 #include "aviio.h"
 
 #include <utility>
+#include <switchres/switchres.h>
+
+#define D3DPRESENT_DONOTFLIP      0x00000004L
+#define D3DPRESENT_FORCEIMMEDIATE 0x00000100L
 
 
 //============================================================
@@ -60,10 +64,10 @@ protected:
 	virtual unsigned flags() const override { return FLAG_INTERACTIVE; }
 
 private:
-	using d3d9_create_fn = IDirect3D9 *(WINAPI *)(UINT);
+	using d3d9_create_fn = IDirect3D9Ex *(WINAPI *)(UINT, IDirect3D9Ex **);
 
 	dynamic_module::ptr m_d3d9_dll;
-	Microsoft::WRL::ComPtr<IDirect3D9> m_d3dobj;
+	Microsoft::WRL::ComPtr<IDirect3D9Ex> m_d3dobj;
 	osd_options const *m_options;
 };
 
@@ -75,7 +79,7 @@ private:
 bool video_d3d::probe()
 {
 	// do a dry run of loading the Direct3D 9 DLL
-	return dynamic_module::open({ "d3d9.dll" })->bind<d3d9_create_fn>("Direct3DCreate9") != nullptr;
+	return dynamic_module::open({ "d3d9.dll" })->bind<d3d9_create_fn>("Direct3DCreate9Ex") != nullptr;
 }
 
 
@@ -88,25 +92,26 @@ int video_d3d::init(osd_interface &osd, osd_options const &options)
 	m_options = &options;
 
 	m_d3d9_dll = dynamic_module::open({ "d3d9.dll" });
-	auto const d3d9_create_ptr = m_d3d9_dll->bind<d3d9_create_fn>("Direct3DCreate9");
+	auto const d3d9_create_ptr = m_d3d9_dll->bind<d3d9_create_fn>("Direct3DCreate9Ex");
 	if (!d3d9_create_ptr)
 	{
-		osd_printf_warning("Direct3D: Could not find Direct3DCreate9 function in d3d9.dll\n");
+		osd_printf_warning("Direct3D: Could not find Direct3DCreate9Ex function in d3d9.dll\n");
 		m_d3d9_dll.reset();
 		m_options = nullptr;
 		return -1;
 	}
 
-	m_d3dobj.Attach((d3d9_create_ptr)(D3D_SDK_VERSION));
+	//m_d3dobj.Attach((d3d9_create_ptr)(D3D_SDK_VERSION, (IDirect3D9Ex**) m_d3dobj));
+	(*d3d9_create_ptr)(D3D_SDK_VERSION, (IDirect3D9Ex**) &m_d3dobj);
 	if (!m_d3dobj)
 	{
-		osd_printf_warning("Direct3D: Unable to initialize Direct3D 9\n");
+		osd_printf_warning("Direct3D: Unable to initialize Direct3D 9Ex\n");
 		m_d3d9_dll.reset();
 		m_options = nullptr;
 		return -1;
 	}
 
-	osd_printf_verbose("Direct3D: Using Direct3D 9\n");
+	osd_printf_verbose("Direct3D: Using Direct3D 9Ex\n");
 
 	return 0;
 }
@@ -583,10 +588,17 @@ renderer_d3d9::renderer_d3d9(osd_window &window, const IDirect3D9Ptr &d3dobj)
 	, m_width(0)
 	, m_height(0)
 	, m_refresh(0)
+	, m_frame_delay(0)
 	, m_create_error_count(0)
 	, m_post_fx_available(true)
 	, m_gamma_supported(0)
 	, m_pixformat()
+	, m_query(nullptr)
+	, m_swap9(nullptr)
+	, m_swap(nullptr)
+	, m_sync_count(0)
+	, m_enter_line(0)
+	, m_exit_line(0)
 	, m_lockedbuf(nullptr)
 	, m_numverts(0)
 	, m_vectorbatch(nullptr)
@@ -817,33 +829,145 @@ void renderer_d3d9::end_frame()
 	if (FAILED(result))
 		osd_printf_verbose("Direct3D: Error %08lX during device end_scene call\n", result);
 
+	if ((m_frame_delay != video_config.framedelay) || (m_vsync_offset != window().machine().video().vsync_offset()))
+	{
+		m_frame_delay = video_config.framedelay;
+		m_vsync_offset = window().machine().video().vsync_offset();
+		update_break_scanlines();
+	}
+
+	// sync to VBLANK-BEGIN
+	if (video_config.syncrefresh)
+	{
+		m_device->GetRasterStatus(0, &m_raster_status);
+		m_enter_line = m_raster_status.ScanLine;
+
+		do
+		{
+			if (m_device->GetRasterStatus(0, &m_raster_status) != D3D_OK)
+				break;
+		} while (m_frame_delay?
+			// with frame delay, wait for break scanline, or just exit if we're already in vblank
+			!m_raster_status.InVBlank && m_raster_status.ScanLine < m_break_scanline :
+			// with syncrefresh only, just wait for vblank
+			!m_raster_status.InVBlank);
+	}
+
 	// present the current buffers
-	result = m_device->Present(nullptr, nullptr, nullptr, nullptr);
-	if (FAILED(result))
+	result = m_device->PresentEx(nullptr, nullptr, nullptr, nullptr, D3DPRESENT_INTERVAL_ONE);
+	if (FAILED(result) && (result != D3DERR_WASSTILLDRAWING))
 		osd_printf_verbose("Direct3D: Error %08lX during device present call\n", result);
+
+	// sync to VBLANK-END
+	if (video_config.syncrefresh)
+	{
+		do
+		{
+			if (m_device->GetRasterStatus(0, &m_raster_status) != D3D_OK)
+				break;
+		} while (m_raster_status.InVBlank);
+
+		m_exit_line = m_raster_status.ScanLine;
+
+		// check if retrace has been missed
+		if (m_swap != nullptr)
+		{
+			m_swap->GetPresentStats(&m_stats);
+
+			if (m_stats.PresentRefreshCount - m_sync_count > 1 && m_enter_line != 0)
+			{
+				static const double tps = (double)osd_ticks_per_second();
+				static const double time_start = (double)osd_ticks() / tps;
+				osd_printf_verbose("Missed retrace, realtime is %f\n", (double)osd_ticks() / tps - time_start);
+			}
+			m_sync_count = m_stats.PresentRefreshCount;
+		}
+		//osd_printf_verbose("frame %d enter_line %d exit_line %d\n", m_sync_count, m_enter_line, m_exit_line);
+	}
+}
+
+void renderer_d3d9::device_flush()
+{
+	HRESULT result;
+
+	if(m_device)
+	{
+		if(m_query != nullptr)
+		{
+			m_query->Issue(D3DISSUE_END);
+			do
+			{
+				result = m_query->GetData(NULL, 0, D3DGETDATA_FLUSH);
+				if (result == D3DERR_DEVICELOST)
+					return;
+			} while(result == S_FALSE);
+		}
+	}
+}
+
+void renderer_d3d9::update_break_scanlines()
+{
+	switchres_manager *m_switchres = &downcast<windows_osd_interface&>(window().machine().osd()).switchres()->switchres();
+	if (m_switchres->display(window().index()) == nullptr)
+		return;
+
+	modeline *m_switchres_mode = m_switchres->display(window().index())->selected_mode();
+	if (m_switchres_mode == nullptr)
+		return;
+
+	switch (m_vendor_id)
+	{
+		case 0x1002: // ATI
+			m_first_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
+				(m_switchres_mode->vtotal - m_switchres_mode->vbegin - 1) / (m_switchres_mode->interlace ? 2 : 1) :
+				1;
+
+			m_last_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
+				(m_switchres_mode->vactive - 1) + (m_switchres_mode->vtotal - m_switchres_mode->vbegin - 1) / (m_switchres_mode->interlace ? 2 : 1) :
+				m_height;
+			break;
+
+		case 0x8086: // Intel
+			m_first_scanline = 1;
+
+			m_last_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
+				m_switchres_mode->vactive / (m_switchres_mode->interlace ? 2 : 1) :
+				m_height;
+			break;
+
+		default: // NVIDIA (0x10DE) + others (?)
+			m_first_scanline = 0;
+
+			m_last_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
+				(m_switchres_mode->vactive - 1) / (m_switchres_mode->interlace ? 2 : 1) :
+				m_height - 1;
+			break;
+	}
+
+	m_break_scanline = m_last_scanline - m_vsync_offset;
+	m_break_scanline = m_break_scanline > m_first_scanline ? m_break_scanline : m_last_scanline;
+	m_delay_scanline = m_first_scanline + m_height * (float)video_config.framedelay / (10 * m_switchres_mode->result.v_scale);
+
+	osd_printf_verbose("Direct3D: Frame delay: %d, First scanline: %d, Last scanline: %d, Break scanline: %d, Delay scanline: %d\n", video_config.framedelay, m_first_scanline, m_last_scanline, m_break_scanline, m_delay_scanline);
 }
 
+
 void renderer_d3d9::update_presentation_parameters()
 {
 	memset(&m_presentation, 0, sizeof(m_presentation));
 	m_presentation.BackBufferWidth = m_width;
 	m_presentation.BackBufferHeight = m_height;
 	m_presentation.BackBufferFormat = m_pixformat;
-	m_presentation.BackBufferCount = video_config.triplebuf ? 2 : 1;
+	m_presentation.BackBufferCount = 1;
 	m_presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
-	m_presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;
+	m_presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;;
 	m_presentation.hDeviceWindow = dynamic_cast<win_window_info &>(window()).platform_window();
 	m_presentation.Windowed = !window().fullscreen();
 	m_presentation.EnableAutoDepthStencil = FALSE;
 	m_presentation.AutoDepthStencilFormat = D3DFMT_D16;
-	m_presentation.Flags = 0;
-	m_presentation.FullScreen_RefreshRateInHz = m_refresh;
-	m_presentation.PresentationInterval = (
-		(video_config.triplebuf && window().fullscreen())
-		|| video_config.waitvsync
-		|| video_config.syncrefresh)
-			? D3DPRESENT_INTERVAL_ONE
-			: D3DPRESENT_INTERVAL_IMMEDIATE;
+	m_presentation.Flags = D3DPRESENTFLAG_UNPRUNEDMODE;
+	m_presentation.FullScreen_RefreshRateInHz = window().fullscreen()?m_refresh : 0;
+	m_presentation.PresentationInterval = (video_config.waitvsync && !video_config.syncrefresh)? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
 }
 
 
@@ -915,14 +1039,11 @@ int renderer_d3d9::device_create(HWND hwnd)
 	// initialize the D3D presentation parameters
 	update_presentation_parameters();
 
+	D3DDISPLAYMODEEX *display_mode = window().fullscreen()? &m_display_mode : nullptr;
+
 	// create the D3D device
-	result = m_d3dobj->CreateDevice(
-			m_adapter,
-			D3DDEVTYPE_HAL,
-			device_hwnd,
-			D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
-			&m_presentation,
-			&m_device);
+	result = m_d3dobj->CreateDeviceEx(
+		m_adapter, D3DDEVTYPE_HAL, device_hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE | D3DCREATE_ENABLE_PRESENTSTATS, &m_presentation, display_mode, &m_device);
 	if (FAILED(result))
 	{
 		// if we got a "DEVICELOST" error, it may be transitory; count it and only fail if
@@ -943,6 +1064,22 @@ int renderer_d3d9::device_create(HWND hwnd)
 	m_create_error_count = 0;
 	osd_printf_verbose("Direct3D: Device created at %dx%d\n", m_width, m_height);
 
+	result = m_device->SetMaximumFrameLatency(1);
+	if (FAILED(result))
+		osd_printf_error("Unable to set Direct3DEx device maximum frame latency\n");
+
+	result = m_device->CreateQuery(D3DQUERYTYPE_EVENT, &m_query);
+	if (FAILED(result))
+		osd_printf_error("Unable to create Query\n");
+
+	result = m_device->GetSwapChain(0, &m_swap9);
+	if (FAILED(result))
+		osd_printf_error("Unable get swap chain\n");
+	else
+		m_swap9->QueryInterface(__uuidof(IDirect3DSwapChain9Ex), (void**)&m_swap);
+
+	update_break_scanlines();
+
 	update_gamma_ramp();
 
 	return device_create_resources();
@@ -1071,6 +1208,24 @@ void renderer_d3d9::device_delete_resources()
 
 	// free the vertex buffer
 	m_vertexbuf.Reset();
+
+	if (m_query != nullptr)
+	{
+		m_query->Release();
+		m_query = nullptr;
+	}
+
+	if (m_swap != nullptr)
+	{
+		m_swap->Release();
+		m_swap = nullptr;
+	}
+
+	if (m_swap9 != nullptr)
+	{
+		m_swap9->Release();
+		m_swap9 = nullptr;
+	}
 }
 
 
@@ -1213,6 +1368,40 @@ int renderer_d3d9::device_test_cooperative()
 }
 
 
+//============================================================
+//  restart
+//============================================================
+
+int renderer_d3d9::restart()
+{
+	// free all existing resources
+	if (m_shaders->enabled()) device_delete_resources();
+
+	// configure new video mode
+	if (video_config.switchres)
+		pick_best_mode();
+	update_presentation_parameters();
+
+	if (video_config.syncrefresh)
+		update_break_scanlines();
+
+	D3DDISPLAYMODEEX *display_mode = window().fullscreen()? &m_display_mode : nullptr;
+
+	// reset the device
+	HRESULT result = m_device->ResetEx(&m_presentation, display_mode);
+	if (FAILED(result))
+	{
+		osd_printf_error("Unable to reset, result %08lX\n", result);
+		return 1;
+	}
+
+	// create the resources again
+	if (m_shaders->enabled()) device_create_resources();
+
+	return 0;
+}
+
+
 //============================================================
 //  config_adapter_mode
 //============================================================
@@ -1232,9 +1421,14 @@ int renderer_d3d9::config_adapter_mode()
 	}
 
 	osd_printf_verbose("Direct3D: Configuring adapter #%d = %s\n", m_adapter, id.Description);
+	osd_printf_verbose("Direct3D: Adapter has Vendor ID: %lX and Device ID: %lX\n", id.VendorId, id.DeviceId);
+
+	m_vendor_id = id.VendorId;
 
 	// get the current display mode
-	result = m_d3dobj->GetAdapterDisplayMode(m_adapter, &m_origmode);
+	m_origmode.Size = sizeof(D3DDISPLAYMODEEX);
+	result = m_d3dobj->GetAdapterDisplayModeEx(m_adapter, &m_origmode, 0);
+
 	if (FAILED(result))
 	{
 		osd_printf_error("Error getting mode for adapter #%d\n", m_adapter);
@@ -1245,6 +1439,9 @@ int renderer_d3d9::config_adapter_mode()
 	{
 		// choose a resolution: window mode case
 
+		// Use current desktop mode
+		m_display_mode = m_origmode;
+
 		// bounds are from the window client rect
 		RECT client;
 		GetClientRectExceptMenu(dynamic_cast<win_window_info &>(window()).platform_window(), &client, window().fullscreen());
@@ -1253,7 +1450,7 @@ int renderer_d3d9::config_adapter_mode()
 
 		// pix format is from the current mode
 		m_pixformat = m_origmode.Format;
-		m_refresh = 0;
+		m_refresh = m_origmode.RefreshRate;
 
 		// make sure it's a pixel format we can get behind
 		if (m_pixformat != D3DFMT_X1R5G5B5 && m_pixformat != D3DFMT_R5G6B5 && m_pixformat != D3DFMT_X8R8G8B8)
@@ -1325,6 +1522,27 @@ void renderer_d3d9::pick_best_mode()
 	int32_t minwidth, minheight;
 	float best_score = 0.0f;
 
+	switchres_manager *m_switchres = &downcast<windows_osd_interface&>(window().machine().osd()).switchres()->switchres();
+	if (m_switchres->display(window().index()) != nullptr)
+	{
+		modeline *m_switchres_mode = m_switchres->display(window().index())->selected_mode();
+		if (m_switchres_mode != nullptr)
+		{
+			m_width = m_switchres_mode->type & MODE_ROTATED? m_switchres_mode->height : m_switchres_mode->width;
+			m_height = m_switchres_mode->type & MODE_ROTATED? m_switchres_mode->width : m_switchres_mode->height;
+			m_refresh = (int)m_switchres_mode->refresh;
+			m_interlace = m_switchres_mode->interlace;
+
+			m_display_mode.Size = sizeof(D3DDISPLAYMODEEX);
+			m_display_mode.Width = m_width;
+			m_display_mode.Height = m_height;
+			m_display_mode.RefreshRate = m_refresh;
+			m_display_mode.Format = m_pixformat;
+			m_display_mode.ScanLineOrdering = m_interlace? D3DSCANLINEORDERING_INTERLACED : D3DSCANLINEORDERING_PROGRESSIVE;
+			return;
+		}
+	}
+
 	// determine the refresh rate of the primary screen
 	const screen_device *primary_screen = screen_device_enumerator(window().machine().root_device()).first();
 	if (primary_screen != nullptr)
@@ -1349,9 +1567,16 @@ void renderer_d3d9::pick_best_mode()
 	osd_printf_verbose("Direct3D: Selecting video mode...\n");
 	for (int modenum = 0; modenum < maxmodes; modenum++)
 	{
+		// allow all modes
+		D3DDISPLAYMODEFILTER filter;
+		memset (&filter, 0, sizeof(filter));
+		filter.Size = sizeof(D3DDISPLAYMODEFILTER);
+		filter.Format = D3DFMT_X8R8G8B8;
+
 		// check this mode
-		D3DDISPLAYMODE mode;
-		HRESULT result = m_d3dobj->EnumAdapterModes(m_adapter, D3DFMT_X8R8G8B8, modenum, &mode);
+		D3DDISPLAYMODEEX mode;
+		mode.Size = sizeof(mode);
+		HRESULT result = m_d3dobj->EnumAdapterModesEx(m_adapter, &filter, modenum, &mode);
 		if (FAILED(result))
 			break;
 
@@ -1397,6 +1622,7 @@ void renderer_d3d9::pick_best_mode()
 			m_height = mode.Height;
 			m_pixformat = mode.Format;
 			m_refresh = mode.RefreshRate;
+			m_display_mode = mode;
 		}
 	}
 	osd_printf_verbose("Direct3D: Mode selected = %4dx%4d@%3dHz\n", m_width, m_height, m_refresh);
@@ -1980,7 +2206,8 @@ texture_info::texture_info(d3d_texture_manager &manager, const render_texinfo* t
 	{
 		// non-screen textures are easy
 		assert(PRIMFLAG_TEXFORMAT(flags) != TEXFORMAT_YUY16);
-		const HRESULT result = m_renderer.get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &m_d3dtex, nullptr);
+		const HRESULT result = m_renderer.get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_d3dtex, nullptr);
+
 		if (FAILED(result))
 			goto error;
 
diff --git a/src/osd/modules/render/drawd3d.h b/src/osd/modules/render/drawd3d.h
index 3ab7651d87c..de0697855ef 100644
--- a/src/osd/modules/render/drawd3d.h
+++ b/src/osd/modules/render/drawd3d.h
@@ -47,7 +47,7 @@ struct hlsl_options;
 class renderer_d3d9 : public osd_renderer, public slider_dirty_notifier
 {
 public:
-	using IDirect3D9Ptr = Microsoft::WRL::ComPtr<IDirect3D9>;
+	using IDirect3D9Ptr = Microsoft::WRL::ComPtr<IDirect3D9Ex>;
 
 	renderer_d3d9(osd_window &window, const IDirect3D9Ptr &d3dobj);
 	virtual ~renderer_d3d9();
@@ -61,6 +61,7 @@ public:
 	virtual void add_audio_to_recording(const int16_t *buffer, int samples_this_frame) override;
 	virtual std::vector<ui::menu_item> get_slider_list() override;
 	virtual void set_sliders_dirty() override;
+	virtual int restart() override;
 
 	int                     initialize();
 
@@ -68,6 +69,8 @@ public:
 	int                     device_create_resources();
 	void                    device_delete();
 	void                    device_delete_resources();
+	void                    device_flush();
+	void                    update_break_scanlines();
 	void                    update_presentation_parameters();
 	void                    update_gamma_ramp();
 
@@ -110,7 +113,7 @@ public:
 	bool                    post_fx_available() const { return m_post_fx_available; }
 	void                    set_post_fx_unavailable() { m_post_fx_available = false; }
 
-	IDirect3DDevice9 *      get_device() const { return m_device.Get(); }
+	IDirect3DDevice9Ex *    get_device() const { return m_device.Get(); }
 	D3DPRESENT_PARAMETERS * get_presentation() { return &m_presentation; }
 
 	IDirect3DVertexBuffer9 *get_vertex_buffer() const { return m_vertexbuf.Get(); }
@@ -119,7 +122,7 @@ public:
 
 	D3DFORMAT               get_screen_format() const { return m_screen_format; }
 	D3DFORMAT               get_pixel_format() const { return m_pixformat; }
-	D3DDISPLAYMODE          get_origmode() const { return m_origmode; }
+	D3DDISPLAYMODEEX        get_origmode() const { return m_origmode; }
 
 	uint32_t                get_last_texture_flags() const { return m_last_texture_flags; }
 
@@ -129,22 +132,39 @@ public:
 	shaders *               get_shaders() const { return m_shaders.get(); }
 
 private:
-	using IDirect3DDevice9Ptr = Microsoft::WRL::ComPtr<IDirect3DDevice9>;
+	using IDirect3DDevice9Ptr = Microsoft::WRL::ComPtr<IDirect3DDevice9Ex>;
 	using IDirect3DVertexBuffer9Ptr = Microsoft::WRL::ComPtr<IDirect3DVertexBuffer9>;
 
 	const IDirect3D9Ptr     m_d3dobj;                   // Direct3D 9 API object
 	int                     m_adapter;                  // ordinal adapter number
+	int                     m_vendor_id;                // adapter vendor id
 	int                     m_width;                    // current width
 	int                     m_height;                   // current height
 	int                     m_refresh;                  // current refresh rate
+	bool                    m_interlace;                // current interlace
+	int                     m_frame_delay;              // current frame delay value
+	int                     m_vsync_offset;             // current vsync_offset value
+	int                     m_first_scanline;           // first scanline number (visible)
+	int                     m_last_scanline;            // last scanline number (visible)
+	int                     m_delay_scanline;           // scanline number supposed to be after frame delay
+	int                     m_break_scanline;           // break scanline number, for vsync offset
 	int                     m_create_error_count;       // number of consecutive create errors
 	bool                    m_post_fx_available;
 
 	IDirect3DDevice9Ptr     m_device;                   // pointer to the Direct3DDevice object
 	int                     m_gamma_supported;          // is full screen gamma supported?
 	D3DPRESENT_PARAMETERS   m_presentation;             // set of presentation parameters
-	D3DDISPLAYMODE          m_origmode;                 // original display mode for the adapter
+	D3DDISPLAYMODEEX        m_origmode;                 // original display mode for the adapter
+	D3DDISPLAYMODEEX        m_display_mode;             // full screen display mode
 	D3DFORMAT               m_pixformat;                // pixel format we are using
+	IDirect3DQuery9 *		m_query;
+	IDirect3DSwapChain9 *   m_swap9;
+	IDirect3DSwapChain9Ex * m_swap;
+	D3DPRESENTSTATS         m_stats;
+	D3DRASTER_STATUS        m_raster_status;
+	int                     m_sync_count;
+	int                     m_enter_line;
+	int                     m_exit_line;
 
 	IDirect3DVertexBuffer9Ptr m_vertexbuf;              // pointer to the vertex buffer object
 	vertex *                m_lockedbuf;                // pointer to the locked vertex buffer
diff --git a/src/osd/modules/render/drawnogpu.cpp b/src/osd/modules/render/drawnogpu.cpp
new file mode 100644
index 00000000000..9c502944489
--- /dev/null
+++ b/src/osd/modules/render/drawnogpu.cpp
@@ -0,0 +1,1001 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Antonio Giner, Sergi Clara
+//============================================================
+//
+//  drawgnopu.cpp - nogpu drawing
+//
+//============================================================
+
+#include "render_module.h"
+
+#include "modules/osdmodule.h"
+
+#include "window.h"
+
+// emu
+#include "emu.h"
+#include "rendersw.hxx"
+
+
+#include "render_module.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock2.h>
+#define socklen_t int
+#else
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+// MAMEOS headers
+#if defined(OSD_WINDOWS)
+#include "winmain.h"
+#elif defined(OSD_SDL)
+#include "osdsdl.h"
+#endif
+
+#include <lz4/lz4.h>
+#include <lz4/lz4hc.h>
+
+#include <switchres/switchres.h>
+
+#define MAX_BUFFER_WIDTH 768
+#define MAX_BUFFER_HEIGHT 576
+#define VRAM_BUFFER_SIZE 65536
+#define SEND_BIFFER_SIZE 2097152 //2 * 1024 * 1024
+#define MAX_LZ4_BLOCK   61440
+#define MAX_SAMPLE_RATE 48000
+#define STREAMS_UPDATE_FREQUENCY 50 // sound.h
+
+// nogpu UDP server
+#define UDP_PORT 32100
+
+// Server commands
+#define CMD_CLOSE 1
+#define CMD_INIT 2
+#define CMD_SWITCHRES 3
+#define CMD_AUDIO 4
+#define CMD_GET_STATUS 5
+#define CMD_BLIT_VSYNC 6
+
+// Status bits
+#define VRAM_READY       1 << 0
+#define VRAM_END_FRAME   1 << 1
+#define VRAM_SYNCED      1 << 2
+#define VRAM_FRAMESKIP   1 << 3
+#define VGA_VBLANK       1 << 4
+#define VGA_FIELD        1 << 5
+#define FPGA_AUDIO       1 << 6
+#define VGA_QUEUE        1 << 7
+
+#pragma pack(1)
+
+typedef struct nogpu_modeline
+{
+	double    pclock;
+	uint16_t  hactive;
+	uint16_t  hbegin;
+	uint16_t  hend;
+	uint16_t  htotal;
+	uint16_t  vactive;
+	uint16_t  vbegin;
+	uint16_t  vend;
+	uint16_t  vtotal;
+	uint8_t  interlace;
+} nogpu_modeline;
+
+typedef struct nogpu_status
+{
+	uint16_t vcount;
+	uint32_t frame_num;
+} nogpu_status;
+
+typedef struct nogpu_blit_status
+{
+	uint32_t frame_req;
+	uint16_t vcount_req;
+	uint32_t frame_gpu;
+	uint16_t vcount_gpu;
+	uint8_t bits;
+} nogpu_blit_status;
+
+typedef struct cmd_init
+{
+	const uint8_t cmd = CMD_INIT;
+	uint8_t	compression;
+	uint8_t sound_rate;
+	uint8_t sound_channels;
+} cmd_init;
+
+typedef struct cmd_close
+{
+	const uint8_t cmd = CMD_CLOSE;
+} cmd_close;
+
+typedef struct cmd_switchres
+{
+	const uint8_t cmd = CMD_SWITCHRES;
+	nogpu_modeline mode;
+} cmd_switchres;
+
+typedef struct cmd_audio
+{
+	const uint8_t cmd = CMD_AUDIO;
+	uint16_t sample_bytes;
+} cmd_blit;
+
+typedef struct cmd_blit_vsync
+{
+	const uint8_t cmd = CMD_BLIT_VSYNC;
+	uint32_t frame;
+	uint16_t vsync;
+	uint32_t compressed_data_size;
+} cmd_blit_vsync;
+
+
+typedef struct cmd_get_status
+{
+	const uint8_t cmd = CMD_GET_STATUS;
+} cmd_get_status;
+
+#pragma pack(0)
+
+
+namespace osd {
+
+namespace {
+
+// renderer_nogpu is the information for the current screen
+class renderer_nogpu : public osd_renderer
+{
+public:
+	renderer_nogpu(osd_window &window)
+		: osd_renderer(window)
+		, m_bmdata(nullptr)
+		, m_bmsize(0)
+	{
+	}
+
+	virtual ~renderer_nogpu();
+	virtual int create() override;
+	virtual render_primitive_list *get_primitives() override;
+	virtual int draw(const int update) override;
+	virtual void save() override {}
+	virtual void record() override {}
+	virtual void toggle_fsfx() override {}
+	virtual void add_audio_to_recording(const int16_t *buffer, int samples_this_frame) override;
+
+private:
+#if defined(OSD_WINDOWS)
+	BITMAPINFO                  m_bminfo;
+#endif
+	std::unique_ptr<uint8_t []> m_bmdata;
+	size_t                      m_bmsize;
+
+	// npgpu private members
+	bool m_initialized = false;
+	bool m_first_blit = true;
+	int m_compression = 0;
+	bool m_show_window = false;
+	bool m_is_internal_fe = false;
+	bool m_autofilter = false;
+	bool m_bilinear = false;
+	bool m_interlaced_fb = false;
+	int m_frame = 0;
+	int m_field = 0;
+	int m_width = 0;
+	int m_height = 0;
+	int m_vtotal = 0;
+	int m_vsync_scanline = 0;
+	bool m_sleep_allowed = false;
+	double m_period = 16.666667;
+	double m_line_period = 0.064;
+	double m_frame_delay = 0.0;
+	double m_fd_margin = 1.5;
+	float m_aspect = 4.0 / 3.0;
+	float m_pixel_aspect = 1.0;
+	int m_channels = 2;
+	int m_sample_rate = MAX_SAMPLE_RATE;
+	nogpu_status m_status;
+	nogpu_blit_status m_blit_status;
+	modeline m_current_mode;
+
+	osd_ticks_t time_start = 0;
+	osd_ticks_t time_entry = 0;
+	osd_ticks_t time_blit = 0;
+	osd_ticks_t time_exit = 0;
+	osd_ticks_t time_frame[16];
+	osd_ticks_t time_frame_avg = 0;
+	osd_ticks_t time_frame_dm = 0;
+	osd_ticks_t time_sleep = 1 * osd_ticks_per_second() / 1000.0; // 1 ms
+
+	int m_sockfd = -1; //INVALID_SOCKET;
+	sockaddr_in m_server_addr;
+
+	char m_fb[MAX_BUFFER_HEIGHT * MAX_BUFFER_WIDTH * 3];
+	char m_fb_compressed[LZ4_COMPRESSBOUND(MAX_BUFFER_HEIGHT * MAX_BUFFER_WIDTH * 3)];
+	char inp_buf[2][MAX_LZ4_BLOCK + 1];
+	uint16_t m_ab[MAX_SAMPLE_RATE / STREAMS_UPDATE_FREQUENCY * 2];
+
+	bool nogpu_init();
+	bool nogpu_send_command(void *command, int command_size);
+	bool nogpu_switch_video_mode();
+	void nogpu_blit(uint32_t frame, uint16_t vsync, uint16_t line_width);
+	void nogpu_send_mtu(char *buffer, int bytes_to_send, int chunk_max_size);
+	//void nogpu_send_lz4(char *buffer, int bytes_to_send, int block_size);
+	int nogpu_compress(int id_compress, char *buffer_comp, const char *buffer_rgb, uint32_t buffer_size);
+	bool nogpu_wait_ack(double timeout);
+	bool nogpu_wait_status(nogpu_blit_status *status, double timeout);
+	void nogpu_register_frametime(osd_ticks_t frametime);
+};
+
+inline double get_ms(osd_ticks_t ticks) { return (double) ticks / osd_ticks_per_second() * 1000; };
+
+//============================================================
+//  renderer_nogpu::create
+//============================================================
+
+int renderer_nogpu::create()
+{
+	#if defined(OSD_WINDOWS)
+		// fill in the bitmap info header
+		m_bminfo.bmiHeader.biSize            = sizeof(m_bminfo.bmiHeader);
+		m_bminfo.bmiHeader.biPlanes          = 1;
+		m_bminfo.bmiHeader.biBitCount        = 32;
+		m_bminfo.bmiHeader.biCompression     = BI_RGB;
+		m_bminfo.bmiHeader.biSizeImage       = 0;
+		m_bminfo.bmiHeader.biXPelsPerMeter   = 0;
+		m_bminfo.bmiHeader.biYPelsPerMeter   = 0;
+		m_bminfo.bmiHeader.biClrUsed         = 0;
+		m_bminfo.bmiHeader.biClrImportant    = 0;
+	#endif
+
+	return 0;
+}
+
+//============================================================
+//  renderer_nogpu::~renderer_nogpu
+//============================================================
+
+renderer_nogpu::~renderer_nogpu()
+{
+	// Wait for fpga to flush last blit
+	osd_sleep(m_period * osd_ticks_per_second() / 1000.0);
+
+	osd_printf_verbose("nogpu: Sending CMD_CLOSE...");
+	cmd_close command;
+
+	nogpu_send_command(&command, sizeof(command));
+	osd_printf_verbose("done.\n");
+
+#ifdef WIN32
+	closesocket(m_sockfd);
+	WSACleanup();
+#else
+	close(m_sockfd);
+#endif
+}
+
+//============================================================
+//  renderer_nogpu::get_primitives
+//============================================================
+
+render_primitive_list *renderer_nogpu::get_primitives()
+{
+	if (m_width == 0 || m_height == 0)
+	{
+		osd_dim const dimensions = window().get_size();
+		if ((dimensions.width() <= 0) || (dimensions.height() <= 0))
+			return nullptr;
+
+		m_width = std::min(dimensions.width(), MAX_BUFFER_WIDTH);
+		m_height = std::min(dimensions.height(), MAX_BUFFER_HEIGHT);
+	}
+
+	window().target()->set_bounds(m_width, m_height, m_pixel_aspect);
+	return &window().target()->get_primitives();
+}
+
+//============================================================
+//  renderer_nogpu::draw
+//============================================================
+
+int renderer_nogpu::draw(const int update)
+{
+	#if defined(OSD_WINDOWS)
+		auto &win = dynamic_cast<win_window_info &>(window());
+	#elif defined(OSD_SDL)
+		auto &win = dynamic_cast<sdl_window_info &>(window());
+	#endif
+
+	#if defined(OSD_WINDOWS)
+	// we don't have any special resize behaviors
+	if (win.m_resize_state == win_window_info::RESIZE_STATE_PENDING)
+		win.m_resize_state = win_window_info::RESIZE_STATE_NORMAL;
+	#endif
+
+	// resize window if required
+	static int old_width = 0;
+	static int old_height = 0;
+	if (old_width != m_width || old_height != m_height)
+	{
+		old_width = m_width;
+		old_height = m_height;
+
+		#if defined(OSD_WINDOWS)
+		if (m_show_window)
+		{
+			RECT client_rect;
+			RECT window_rect;
+			GetClientRect(win.platform_window(), &client_rect);
+			GetWindowRect(win.platform_window(), &window_rect);
+
+			int extra_width = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
+			int extra_height = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
+
+			SetWindowPos(win.platform_window(), nullptr, 0, 0, m_width + extra_width, m_height + extra_height, SWP_NOMOVE | SWP_NOZORDER);
+		}
+		#endif
+	}
+
+	// compute pitch of target
+	int const pitch = (m_width + 3) & ~3;
+
+	// make sure our temporary bitmap is big enough
+	if ((pitch * m_height * 4) > m_bmsize)
+	{
+		m_bmsize = pitch * m_height * 4 * 2;
+		m_bmdata.reset();
+		m_bmdata = std::make_unique<uint8_t []>(m_bmsize);
+	}
+
+	// draw the primitives to the bitmap
+	win.m_primlist->acquire_lock();
+	if (m_bilinear)
+		software_renderer<uint32_t, 0,0,0, 16,8,0,0, 1>::draw_primitives(*win.m_primlist, m_bmdata.get(), m_width, m_height, pitch);
+	else
+		software_renderer<uint32_t, 0,0,0, 16,8,0,0, 0>::draw_primitives(*win.m_primlist, m_bmdata.get(), m_width, m_height, pitch);
+	win.m_primlist->release_lock();
+
+	// blit to the screen
+	#if defined(OSD_WINDOWS)
+	if (m_show_window)
+	{
+		// fill in bitmap-specific info
+		m_bminfo.bmiHeader.biWidth = pitch;
+		m_bminfo.bmiHeader.biHeight = -m_height;
+
+		StretchDIBits(
+				win.m_dc, 0, 0, m_width, m_height,
+				0, 0, m_width, m_height,
+				m_bmdata.get(), &m_bminfo, DIB_RGB_COLORS, SRCCOPY);
+	}
+	#endif
+
+	// initialize nogpu right before first blit
+	if (m_first_blit && !m_initialized)
+	{
+		m_initialized = nogpu_init();
+		if (m_initialized)
+			osd_printf_verbose("done.\n");
+		else
+		{
+			osd_printf_verbose("failed.\n");
+			window().machine().video().set_throttled(true);
+			window().machine().video().set_sync_refresh(false);
+			m_first_blit = false;
+		}
+	}
+
+	// only send frame if nogpu is initialized
+	if (!m_initialized)
+		return 0;
+
+	// get current field for interlaced mode
+	if (m_interlaced_fb && m_current_mode.interlace)
+		m_field = (m_blit_status.bits & VGA_FIELD? 1 : 0) ^ ((m_frame - m_blit_status.frame_gpu) % 2);
+
+	// convert RGBA buffer to RGB
+	int interlace_factor = m_interlaced_fb && m_current_mode.interlace ? 2 : 1;
+	int i = 0, j = 0, k = 0;
+	int lstart = pitch * 4 * m_field;
+	int lend = (m_height - 1) * pitch * 4;
+	int lstep = pitch * 4 * interlace_factor;
+
+	for (i = lstart; i <= lend ; i += lstep)
+	{
+		for (j = 0; j < m_width * 4; j += 4)
+		{
+			m_fb[k] = (char)m_bmdata[i+j];
+			m_fb[k+1] = (char)m_bmdata[i+j+1];
+			m_fb[k+2] = (char)m_bmdata[i+j+2];
+			k += 3;
+		}
+	}
+
+	// change video mode right before the blit
+	if (m_initialized) nogpu_switch_video_mode();
+
+	bool valid_status = false;
+
+	time_entry = osd_ticks();
+
+	if (video_config.syncrefresh && m_first_blit)
+	{
+		time_start = time_entry;
+		time_blit = time_entry;
+		time_exit = time_entry;
+
+		m_first_blit = false;
+		m_frame = 1;
+
+		// Skip blitting first frame, so we avoid glitches while MAME loads roms
+		return 0;
+	}
+
+	// Blit now
+	nogpu_blit(m_frame, m_width, m_height / interlace_factor);
+
+	time_blit = osd_ticks();
+	osd_printf_verbose("[%.3f] frame: %d emulation_time: %.3f blit_time: %.3f \n",
+			get_ms(time_entry - time_start), m_frame,get_ms(time_entry - time_exit), get_ms(time_blit - time_entry));
+
+	nogpu_register_frametime(time_entry - time_exit);
+
+	// Wait raster position
+	if (video_config.syncrefresh)
+		valid_status = nogpu_wait_status(&m_blit_status, std::max(0.0, m_period - get_ms(time_blit - time_exit)));
+
+	if (video_config.syncrefresh && valid_status)
+		m_frame = m_blit_status.frame_req + 1;
+	else
+		m_frame ++;
+
+	time_exit = osd_ticks();
+	osd_printf_verbose("[%.3f] ft_avg %.3f Dm: %.3f fd: %.3f wait_time: %.3f\n\n",
+		get_ms(time_exit - time_start), get_ms(time_frame_avg), get_ms(time_frame_dm), m_frame_delay * 10.0, get_ms(time_exit - time_blit));
+
+	return 0;
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_init
+//============================================================
+
+bool renderer_nogpu::nogpu_init()
+{
+	int result;
+
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(window().machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(window().machine().options());
+	#endif
+
+	#ifdef _WIN32
+		osd_printf_verbose("nogpu: Initializing Winsock...");
+		WSADATA wsa;
+		result = WSAStartup(MAKEWORD(2, 2), &wsa);
+		if (result != NO_ERROR)
+		{
+			osd_printf_verbose("Failed. Error code : %d", WSAGetLastError());
+			return false;
+		}
+		osd_printf_verbose("done.\n");
+	#endif
+
+	osd_printf_verbose("nogpu: Initializing socket... ");
+	m_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+	if (m_sockfd < 0)
+	{
+		osd_printf_verbose("Could not create socket\n");
+		return false;
+	}
+	else
+		osd_printf_verbose(" done.\n");
+
+	short port = UDP_PORT;
+	const char* local_host = options.mister_ip();
+
+	m_server_addr = {};
+	m_server_addr.sin_family = AF_INET;
+	m_server_addr.sin_port = htons(port);
+	m_server_addr.sin_addr.s_addr = inet_addr(local_host);
+
+	osd_printf_verbose("nogpu: Setting socket async...\n");
+
+	#ifdef _WIN32
+		u_long opt = 1;
+		if (ioctlsocket(m_sockfd, FIONBIO, &opt) < 0)
+			osd_printf_verbose("Could not set nonblocking.\n");
+	#else
+		int flags;
+		flags = fcntl(m_sockfd, F_GETFD, 0);
+		if (flags < 0)
+			osd_printf_verbose("Could not get socket flags.\n");
+		else
+		{
+			flags |= O_NONBLOCK;
+			if (fcntl(m_sockfd, F_SETFL, flags) < 0)
+				osd_printf_verbose("Could not set nonblocking.\n");
+		}
+	#endif
+
+	osd_printf_verbose("nogpu: Setting send buffer to %d bytes...\n", SEND_BIFFER_SIZE);
+	int opt_val = SEND_BIFFER_SIZE;
+	result = setsockopt(m_sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&opt_val, sizeof(opt_val));
+	if (result < 0)
+	{
+		osd_printf_verbose("Unable to set send buffer: %d\n", result);
+		return false;
+	}
+
+	m_compression = 0; // raw
+	const char* compression = options.mister_compression();
+	if (!strcmp(compression, "lz4"))
+	{
+		m_compression = 0x01;
+		osd_printf_verbose("nogpu: compression algorithm %s\n", compression);
+	}
+	else if (!strcmp(compression, "lz4hc"))
+	{
+		m_compression = 0x02;
+		osd_printf_verbose("nogpu: compression algorithm %s\n", compression);
+	}
+	else if (strcmp(compression, "none"))
+		osd_printf_verbose("nogpu: compression algorithm %s not supported\n", compression);
+
+	switch(options.sample_rate())
+	{
+		case 22050:
+			m_sample_rate = 1;
+			break;
+		case 44100:
+			m_sample_rate = 2;
+			break;
+		case 48000:
+			m_sample_rate = 3;
+			break;
+		default:
+			m_sample_rate = 0;
+			osd_printf_verbose("nogpu: not valid sample rate %d\n", options.sample_rate());
+	}
+
+	osd_printf_verbose("nogpu: Sending CMD_INIT...");
+	cmd_init command;
+	command.compression = m_compression ? 1 : 0;
+	command.sound_rate = m_sample_rate;
+	command.sound_channels = m_channels;
+
+	// Reset current mode
+	m_current_mode = {};
+
+	// Hide window optionally
+	m_show_window = options.mister_window();
+	#if defined(OSD_WINDOWS)
+		if (!m_show_window)
+		{
+			auto &win = dynamic_cast<win_window_info &>(window());
+			SetWindowLong(win.platform_window(), GWL_EXSTYLE, WS_EX_LAYERED);
+			SetLayeredWindowAttributes(win.platform_window(), 0, 0, LWA_ALPHA);
+		}
+	#endif
+
+	m_fd_margin = (double)options.mister_fd_margin();
+	m_sleep_allowed = options.sleep();
+	m_autofilter = options.autofilter();
+	m_interlaced_fb = options.mister_interlaced_fb();
+	m_is_internal_fe = strcmp(window().machine().system().name, "___empty") == 0;
+
+	if (nogpu_send_command(&command, sizeof(command)))
+		return nogpu_wait_ack(1000);
+
+	return false;
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_switch_video_mode()
+//============================================================
+
+bool renderer_nogpu::nogpu_switch_video_mode()
+{
+	#if defined(OSD_WINDOWS)
+		switchres_manager *m_switchres = &downcast<windows_osd_interface&>(window().machine().osd()).switchres()->switchres();
+	#elif defined(OSD_SDL)
+		switchres_manager *m_switchres = &downcast<sdl_osd_interface&>(window().machine().osd()).switchres()->switchres();
+	#endif
+	display_manager *display = m_switchres->display(window().index());
+
+	// Check if we have a pending mode change
+	if (display == nullptr)
+		return false;
+
+	modeline *mode = display->selected_mode();
+	if (mode == nullptr)
+		return false;
+
+	// Make sure SR's mode isn't bigger than our buffer
+	if (mode->hactive > MAX_BUFFER_WIDTH || mode->vactive > MAX_BUFFER_HEIGHT)
+	{
+		// Force 640x480 otherwise
+		display->get_mode(640, 480, mode->vfreq, 0);
+		if (!display->got_mode())
+			return false;
+
+		mode = display->selected_mode();
+	}
+
+	// If not, we're done
+	if (!modeline_is_different(mode, &m_current_mode))
+		return true;
+
+	m_current_mode = *mode;
+
+	// Send new modeline to nogpu
+	osd_printf_verbose("nogpu: Sending CMD_SWITCHRES...\n");
+
+	cmd_switchres command;
+	nogpu_modeline *m = &command.mode;
+
+	m->pclock    = double(mode->pclock) / 1000000.0;
+	m->hactive   = mode->hactive;
+	m->hbegin    = mode->hbegin;
+	m->hend      = mode->hend;
+	m->htotal    = mode->htotal;
+	m->vactive   = mode->vactive;
+	m->vbegin    = mode->vbegin;
+	m->vend      = mode->vend;
+	m->vtotal    = mode->vtotal;
+	m->interlace = mode->interlace ? (m_interlaced_fb ? 1 : 2) : 0;
+
+	m_width = mode->hactive;
+	m_height = mode->vactive;
+	m_vtotal = mode->vtotal;
+	m_period = 1000.0 / (double(mode->pclock) / (mode->htotal * mode->vtotal)) / (mode->interlace? 2: 1);
+	m_line_period = 1000.0 / mode->hfreq;
+	m_field = 0;
+	m_bilinear = m_autofilter & display->is_stretched();
+
+	m_aspect = display->monitor_aspect();
+	m_pixel_aspect = m_aspect / ((float)m_width / m_height);
+
+	return nogpu_send_command(&command, sizeof(command));
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_wait_ack
+//============================================================
+
+bool renderer_nogpu::nogpu_wait_ack(double timeout)
+{
+	osd_ticks_t time_1 = osd_ticks();
+	socklen_t server_addr_size = sizeof(m_server_addr);
+
+	// Poll server for ack
+	do
+	{
+		int bytes_recv = recvfrom(m_sockfd, (char *)&m_status, sizeof(nogpu_blit_status), 0, (sockaddr*)&m_server_addr, &server_addr_size);
+
+		if (bytes_recv == sizeof(nogpu_blit_status))
+			break;
+
+		osd_ticks_t time_2 = osd_ticks();
+		if (get_ms(time_2 - time_1) > timeout)
+		{
+			osd_printf_verbose("timeout: %.3f ms\n", timeout);
+			return false;
+		}
+
+		osd_sleep(time_sleep);
+
+	} while (true);
+
+	return true;
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_wait_status
+//============================================================
+
+bool renderer_nogpu::nogpu_wait_status(nogpu_blit_status *status, double timeout)
+{
+	int retries = 0;
+	osd_ticks_t time_1 = 0;
+	osd_ticks_t time_2 = 0;
+	socklen_t server_addr_size = sizeof(m_server_addr);
+	int bytes_recv = 0;
+
+	osd_printf_verbose("[%.3f] wait_status[%.3f]: ", get_ms(osd_ticks() - time_start), timeout);
+	time_1 = osd_ticks();
+
+	// Poll server for blit line timestamp
+	do
+	{
+		retries++;
+		bytes_recv = recvfrom(m_sockfd, (char *)status, sizeof(nogpu_blit_status), 0, (sockaddr*)&m_server_addr, &server_addr_size);
+
+		if (bytes_recv > 0 && m_frame == status->frame_req)
+			break;
+
+		time_2 = osd_ticks();
+		if (get_ms(time_2 - time_1) > timeout)
+		{
+			osd_printf_verbose("timeout[%d]: %.3f\n", retries, timeout);
+			return false;
+		}
+
+		if (m_sleep_allowed) osd_sleep(time_sleep);
+
+	} while (true);
+
+
+	// We have a valid timestamp
+	osd_printf_verbose("success[%d] ack[%d][%d]->[%d][%d] ", retries, status->frame_req, status->vcount_req, status->frame_gpu, status->vcount_gpu);
+
+	// Compute line target for next blit, relative to last blit line timestamp
+	int lines_to_wait = (status->frame_req - status->frame_gpu) * m_current_mode.vtotal + status->vcount_req - status->vcount_gpu;
+	if (m_current_mode.interlace)
+		lines_to_wait /= 2;
+
+	// Compute time target for emulation of next frame, so that blit after it happens at desired line target
+	osd_ticks_t time_target = time_entry + (osd_ticks_t)((double)lines_to_wait * m_line_period * osd_ticks_per_second() / 1000.0) - time_frame_avg;
+
+	// Wait for target time
+	if ((int)(time_target - osd_ticks()) > 0)
+	{
+		osd_printf_verbose("to wait[%d|%.3f]\n", lines_to_wait, get_ms(time_target - osd_ticks()));
+
+		do
+		{
+			time_2 = osd_ticks();
+			if (time_2 >= time_target)
+				break;
+
+			if (m_sleep_allowed && get_ms(time_target - time_2) > 2.0)
+				osd_sleep(time_sleep);
+
+		} while (true);
+	}
+	else
+		osd_printf_verbose("delayed, exiting\n");
+
+	// Make sure our frame counter hasn't fallen behind gpu's
+	if (status->frame_gpu > status->frame_req)
+		status->frame_req = status->frame_gpu + 1;
+
+	return true;
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_register_frametime
+//============================================================
+
+void renderer_nogpu::nogpu_register_frametime(osd_ticks_t frametime)
+{
+	static int i = 0;
+	static int regs = 0;
+	const int max_regs = sizeof(time_frame) / sizeof(time_frame[0]);
+	osd_ticks_t acum = 0;
+	int diff = 0;
+
+	// Discard invalid values
+	if (frametime <= 0 || get_ms(frametime) > m_period)
+		return;
+
+	// Register value and compute current average
+	time_frame[i] = frametime;
+	i++;
+
+	if (i > max_regs)
+		i = 0;
+
+	if (regs < max_regs)
+		regs++;
+
+	for (int k = 0; k < regs; k++)
+		acum += time_frame[k];
+
+	time_frame_avg = acum / regs;
+
+	// Compute current max deviation
+	osd_ticks_t max_diff = 0;
+
+	for (int k = 1; k <= regs; k++)
+	{
+		diff = time_frame[k] - time_frame[k-1];
+
+		if (diff > 0 && diff > max_diff)
+			max_diff = diff;
+	}
+
+	time_frame_dm = max_diff;
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_send_mtu
+//============================================================
+
+void renderer_nogpu::nogpu_send_mtu(char *buffer, int bytes_to_send, int chunk_max_size)
+{
+	int bytes_this_chunk = 0;
+	int chunk_size = 0;
+	uint32_t offset = 0;
+
+	do
+	{
+		chunk_size = bytes_to_send > chunk_max_size? chunk_max_size : bytes_to_send;
+		bytes_to_send -= chunk_size;
+		bytes_this_chunk = chunk_size;
+
+		nogpu_send_command(buffer + offset, bytes_this_chunk);
+		offset += chunk_size;
+
+	} while (bytes_to_send > 0);
+}
+/*
+//============================================================
+//  renderer_nogpu::nogpu_send_lz4
+//============================================================
+
+void renderer_nogpu::nogpu_send_lz4(char *buffer, int bytes_to_send, int block_size)
+{
+	LZ4_stream_t lz4_stream_body;
+	LZ4_stream_t* lz4_stream = &lz4_stream_body;
+	LZ4_initStream(lz4_stream, sizeof(*lz4_stream));
+
+	int inp_buf_index = 0;
+	int bytes_this_chunk = 0;
+	int chunk_size = 0;
+	uint32_t offset = 0;
+
+	do
+	{
+		chunk_size = bytes_to_send > block_size? block_size : bytes_to_send;
+		bytes_to_send -= chunk_size;
+		bytes_this_chunk = chunk_size;
+
+		char* const inp_ptr = inp_buf[inp_buf_index];
+		memcpy((char *)&inp_ptr[0], buffer + offset, chunk_size);
+
+		const uint16_t c_size = LZ4_compress_fast_continue(lz4_stream, inp_ptr, (char *)&m_fb_compressed[2], bytes_this_chunk, MAX_LZ4_BLOCK, 1);
+		uint16_t *c_size_ptr = (uint16_t *)&m_fb_compressed[0];
+		*c_size_ptr = c_size;
+
+		nogpu_send_mtu((char *) &m_fb_compressed[0], c_size + 2, 1472);
+		offset += chunk_size;
+		inp_buf_index ^= 1;
+
+	} while (bytes_to_send > 0);
+}
+*/
+//============================================================
+//  renderer_nogpu::nogpu_blit
+//============================================================
+
+void renderer_nogpu::nogpu_blit(uint32_t frame, uint16_t width, uint16_t height)
+{
+	uint32_t compressed_data_size = 0;
+	int vsync_offset = 0;
+
+	// Calculate frame delay factor
+	if (m_is_internal_fe)
+		// Internal frontend needs fd > 0
+		m_frame_delay = .5;
+
+	else if (video_config.framedelay == 0)
+		// automatic
+		m_frame_delay = std::max((double)(m_period - std::max(m_fd_margin, get_ms(time_frame_dm))) / m_period, 0.0);
+	else
+	{
+		// user defined
+		m_frame_delay = (double)(video_config.framedelay) / 10.0;
+		vsync_offset = window().machine().video().vsync_offset();
+	}
+
+	// Update vsync scanline
+	m_vsync_scanline = std::min<int>((m_current_mode.vtotal) * m_frame_delay + vsync_offset + 1, m_current_mode.vtotal);
+
+	if (m_compression == 1)
+		compressed_data_size = LZ4_compress_default((char *)&m_fb[0], (char *)&m_fb_compressed[0], width * height * 3, LZ4_COMPRESSBOUND(MAX_BUFFER_HEIGHT * MAX_BUFFER_WIDTH * 3));
+	else if(m_compression == 2)
+		compressed_data_size = LZ4_compress_HC((char *)&m_fb[0], (char *)&m_fb_compressed[0], width * height * 3, LZ4_COMPRESSBOUND(MAX_BUFFER_HEIGHT * MAX_BUFFER_WIDTH * 3), LZ4HC_CLEVEL_DEFAULT);
+
+	// Send CMD_BLIT
+	cmd_blit_vsync command;
+	command.frame = frame;
+	command.vsync = video_config.syncrefresh? m_vsync_scanline : 0;
+	command.compressed_data_size = compressed_data_size;
+	nogpu_send_command(&command, sizeof(command));
+
+	if (m_compression == 0)
+		nogpu_send_mtu(&m_fb[0], width * height * 3, 1472);
+
+	else
+		nogpu_send_mtu(&m_fb_compressed[0], (int)compressed_data_size, 1472);
+}
+
+//============================================================
+//  renderer_nogpu::add_audio_to_recording
+//============================================================
+
+void renderer_nogpu::add_audio_to_recording(const int16_t *buffer, int samples_this_frame)
+{
+	if (m_blit_status.bits & FPGA_AUDIO && m_sample_rate)
+	{
+		osd_printf_verbose("audio samples sent: %d\n", samples_this_frame);
+
+		char *buff = (char*)buffer;
+
+		// FIXME: If audio is mono, copy the samples to both channels
+		// We could send mono audio to the MiSTer and save some bandwidth
+		// but unfortunately the channel number isn't known at initialization
+		// time, and we can't reset it later either, yet.
+		uint32_t channels = window().machine().sound().outputs_count();
+		if (channels == 1)
+		{
+			for (int i = samples_this_frame - 1; i >= 0; i--)
+			{
+				uint16_t sample = buffer[i];
+				m_ab[i * 2] = sample;
+				m_ab[i * 2 + 1] = sample;
+				buff = (char *)m_ab;
+			}
+		}
+
+		// Send CMD_AUDIO
+		cmd_audio command;
+		command.sample_bytes = samples_this_frame * m_channels * 2;
+		nogpu_send_command(&command, sizeof(command));
+		nogpu_send_mtu(buff, command.sample_bytes, 1472);
+	}
+}
+
+//============================================================
+//  renderer_nogpu::nogpu_send_command
+//============================================================
+
+bool renderer_nogpu::nogpu_send_command(void *command, int command_size)
+{
+	int rc = sendto(m_sockfd, (char *)command, command_size, 0, (sockaddr*)&m_server_addr, sizeof(m_server_addr));
+
+	if (rc < 0)
+	{
+		osd_printf_verbose("nogpu_send_command failed.\n");
+		return false;
+	}
+
+	return true;
+}
+
+//
+
+class video_nogpu : public osd_module, public render_module
+{
+public:
+	video_nogpu() : osd_module(OSD_RENDERER_PROVIDER, "mister") { }
+
+	virtual int init(osd_interface &osd, osd_options const &options) override { return 0; }
+	virtual void exit() override { }
+
+	virtual std::unique_ptr<osd_renderer> create(osd_window &window) override;
+
+protected:
+	virtual unsigned flags() const override { return FLAG_INTERACTIVE; }
+};
+
+std::unique_ptr<osd_renderer> video_nogpu::create(osd_window &window)
+{
+	return std::make_unique<renderer_nogpu>(window);
+}
+
+} // anonymous namespace
+
+} // namespace osd
+
+MODULE_DEFINITION(RENDERER_MISTER, osd::video_nogpu)
+
diff --git a/src/osd/modules/render/drawogl.cpp b/src/osd/modules/render/drawogl.cpp
index f9f76946bf4..8ddbf0cf70e 100644
--- a/src/osd/modules/render/drawogl.cpp
+++ b/src/osd/modules/render/drawogl.cpp
@@ -24,6 +24,7 @@
 // OSD common headers
 #include "modules/lib/osdlib.h"
 #include "modules/lib/osdobj_common.h"
+#include "modules/monitor/monitor_common.h"
 #include "modules/opengl/gl_shader_mgr.h"
 #include "osdcomm.h"
 
@@ -61,6 +62,14 @@ typedef uint64_t HashT;
 #include <utility>
 
 
+#ifdef SDLMAME_X11
+#include <unistd.h>
+// DRM
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <fcntl.h>
+#endif
+
 #if defined(SDLMAME_MACOSX) || defined(OSD_MAC)
 
 #include <cstring>
@@ -554,6 +563,13 @@ static int glsl_shader_feature = glsl_shader_info::FEAT_PLAIN; // FIXME: why is
 
 bool renderer_ogl::s_shown_video_info = false;
 
+#ifdef SDLMAME_X11
+static int drm_open(const char *dri_device);
+static void drm_waitvblank(int crtc);
+static int fd = 0;
+static const char* dri_device = nullptr;
+#endif
+
 //============================================================
 // Load the OGL function addresses
 //============================================================
@@ -762,7 +778,17 @@ int renderer_ogl::create()
 		osd_printf_error("Creating OpenGL context failed: %s\n", msg ? msg : "unknown error");
 		return 1;
 	}
-	m_gl_context->set_swap_interval(video_config.waitvsync ? 1 : 0);
+#ifdef SDLMAME_X11
+	if (window().index() == 0 && video_config.syncrefresh && video_config.sync_mode != 0)
+	{
+		// Try to open DRM device
+		fd = drm_open(dri_device);
+		if (fd != 0)
+			m_gl_context->set_swap_interval((video_config.sync_mode == 2 || video_config.sync_mode == 4)? 1 : 0);
+	}
+	else
+#endif
+	m_gl_context->set_swap_interval((video_config.waitvsync) ? 1 : 0);
 
 	m_blittimer = 0;
 	m_surf_w = 0;
@@ -787,6 +813,144 @@ int renderer_ogl::create()
 	return 0;
 }
 
+#ifdef SDLMAME_X11
+//============================================================
+//  drm_open
+//============================================================
+
+static int drm_open(const char *dri_device)
+{
+	int fd = 0;
+	char dri_path[16];
+	char *node = dri_path;
+
+	// Dri device forced by user
+	if (strcmp(dri_device, "auto") != 0)
+	{
+		osd_printf_verbose("drm_open: %s for by user\n", dri_device);
+		snprintf(node, sizeof(dri_path), "/dev/dri/%s", dri_device);
+	}
+
+	// Automatic selection
+	else
+	{
+		// Get an array of drm devices to check
+		int num_devices = drmGetDevices2(0, NULL, 0);
+		if (num_devices <= 0)
+		{
+			osd_printf_error("drm_open: couldn't find any drm device\n");
+			return 0;
+		}
+
+		drmDevicePtr *devices = (drmDevicePtr*)calloc(num_devices, sizeof(drmDevicePtr));
+		if (drmGetDevices2(0, devices, num_devices) < 0)
+		{
+			osd_printf_error("drm_open: drmGetDevices2() failed\n");
+			return 0;
+		}
+
+		// Parse device list to find the first one with a valid connector
+		bool found = false;
+
+		for (int i = 0; i < num_devices; i++)
+		{
+			// Skip non-primary nodes
+			if (devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY))
+				node = devices[i]->nodes[DRM_NODE_PRIMARY];
+
+			else continue;
+
+			fd = open(node, O_RDWR | O_CLOEXEC);
+			if (fd < 0)
+			{
+				osd_printf_error("drm_open: couldn't open %s\n", node);
+				continue;
+			}
+			drmModeRes *resources = drmModeGetResources(fd);
+			if (resources && resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0)
+			{
+				for (int j = 0; j < resources->count_connectors; j++)
+				{
+					drmModeConnector *conn = drmModeGetConnector(fd, resources->connectors[j]);
+					if (!conn) continue;
+
+					// We found a valid connector, use it
+					if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0)
+						found = true;
+
+					drmModeFreeConnector(conn);
+					if (found) break;
+				}
+			}
+			drmModeFreeResources(resources);
+			close(fd);
+
+			if (found) break;
+		}
+
+		drmFreeDevices(devices, num_devices);
+		free(devices);
+
+		if (!found)
+		{
+			osd_printf_error("drm_open: couldn't find any device with a valid connector\n");
+			return 0;
+		}
+	}
+
+	fd = open(node, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+	{
+		osd_printf_error("drm_open: cannot open %s\n", node);
+		return 0;
+	}
+
+	osd_printf_verbose("drm_open: %s successfully opened\n", node);
+	return fd;
+}
+
+//============================================================
+//  drm_waitvblank
+//============================================================
+
+static void drm_waitvblank(int crtc)
+{
+
+	drmVBlank vbl;
+	memset(&vbl, 0, sizeof(vbl));
+	vbl.request.sequence = 1;
+
+	// handle vblank for all SR managed crtc
+	// this is a hack based on SDL reported screen index
+	// it won't work on multi-gpu
+	// TO DO: find a correct way to map screen to crtc
+
+	// single screen (default)
+	vbl.request.type = DRM_VBLANK_RELATIVE;
+
+	// two screens
+	if (crtc == 1) vbl.request.type = drmVBlankSeqType(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY);
+
+	// multi-screen
+	else if (crtc > 1)
+	{
+		static uint64_t caps;
+		static bool caps_checked = false;
+
+		if (!caps_checked)
+		{
+			caps_checked = true;
+			if (drmGetCap(fd, DRM_CAP_VBLANK_HIGH_CRTC, &caps))
+				osd_printf_error("A newer kernel is needed for vblank syncing on multi screen\n");
+		}
+		if (caps)
+			vbl.request.type = drmVBlankSeqType(DRM_VBLANK_RELATIVE | ((crtc << DRM_VBLANK_HIGH_CRTC_SHIFT) & DRM_VBLANK_HIGH_CRTC_MASK));
+	}
+
+	if (drmWaitVBlank(fd, &vbl) != 0)
+		osd_printf_verbose("drmWaitVBlank failed\n");
+}
+#endif
 
 //============================================================
 //  drawsdl_xy_to_render_target
@@ -1531,8 +1695,23 @@ int renderer_ogl::draw(const int update)
 	window().m_primlist->release_lock();
 	m_init_context = 0;
 
+#ifdef SDLMAME_X11
+	// wait for vertical retrace
+	if ((video_config.sync_mode == 3 || video_config.sync_mode == 4) && video_config.syncrefresh && fd)
+		drm_waitvblank(window().monitor()->oshandle());
+#endif
+
 	m_gl_context->swap_buffer();
 
+#ifdef SDLMAME_X11
+	// wait for vertical retrace
+	if ((video_config.sync_mode == 1 || video_config.sync_mode == 2) && video_config.syncrefresh && fd)
+		drm_waitvblank(window().monitor()->oshandle());
+#endif
+
+	// Finish GL to minimize latency
+	glFinish();
+
 	return 0;
 }
 
@@ -3005,6 +3184,10 @@ int video_opengl::init(osd_interface &osd, osd_options const &options)
 	osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n");
 #endif // defined(OSD_WINDOWS)
 
+#ifdef SDLMAME_X11
+	dri_device = dynamic_cast<sdl_options const &>(options).dri_device();
+#endif
+
 	return 0;
 }
 
diff --git a/src/osd/modules/switchres/switchres_module.cpp b/src/osd/modules/switchres/switchres_module.cpp
new file mode 100644
index 00000000000..920773f2247
--- /dev/null
+++ b/src/osd/modules/switchres/switchres_module.cpp
@@ -0,0 +1,538 @@
+/**************************************************************
+
+   switchres_module.cpp - Switchres MAME module
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2020 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+// MAME headers
+#include "emu.h"
+#include "rendlay.h"
+#include "render.h"
+#include "../frontend/mame/mameopts.h"
+
+// MAMEOS headers
+#if defined(OSD_WINDOWS)
+#include "winmain.h"
+#elif defined(OSD_SDL)
+#include "osdsdl.h"
+#endif
+
+#include "modules/osdwindow.h"
+#include "modules/monitor/monitor_module.h"
+#include <switchres/switchres.h>
+#include "switchres_module.h"
+
+#include <cstdarg>
+#include <locale>
+
+#define OPTION_PRIORITY_SWITCHRES OPTION_PRIORITY_MAME_INI + 1
+
+//============================================================
+//  logging wrappers
+//============================================================
+
+static void sr_printf_verbose(const char *format, ...)
+{
+	char buffer[1024];
+	va_list args;
+	va_start(args, format);
+	vsprintf(buffer, format, args);
+	osd_vprintf_verbose(util::make_format_argument_pack(std::forward<char*>(buffer)));
+	va_end(args);
+}
+
+static void sr_printf_info(const char *format, ...)
+{
+	char buffer[1024];
+	va_list args;
+	va_start(args, format);
+	vsprintf(buffer, format, args);
+	osd_vprintf_info(util::make_format_argument_pack(std::forward<char*>(buffer)));
+	va_end(args);
+}
+
+static void sr_printf_error(const char *format, ...)
+{
+	char buffer[1024];
+	va_list args;
+	va_start(args, format);
+	vsprintf(buffer, format, args);
+	osd_vprintf_error(util::make_format_argument_pack(std::forward<char*>(buffer)));
+	va_end(args);
+}
+
+//============================================================
+//  switchres_module::init
+//============================================================
+
+void switchres_module::init(running_machine &machine)
+{
+	setlocale(LC_NUMERIC, "C");
+
+	m_machine = &machine;
+	m_switchres = new switchres_manager;
+
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine.options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine.options());
+	#endif
+
+	// Set logging functions
+	if (strcmp(options.video(), "mister") == 0)
+		// Disable info logs for nogpu to avoid glitches
+		switchres().set_log_info_fn((void *)sr_printf_verbose);
+	else
+		switchres().set_log_info_fn((void *)sr_printf_info);
+
+	switchres().set_log_verbose_fn((void *)sr_printf_verbose);
+	switchres().set_log_error_fn((void *)sr_printf_error);
+
+	if (machine.options().verbose()) switchres().set_log_level(3);
+}
+
+//============================================================
+//  switchres_module::exit
+//============================================================
+
+void switchres_module::exit()
+{
+	osd_printf_verbose("Switchres: exit\n");
+	if (m_switchres) delete m_switchres;
+	m_switchres = 0;
+}
+
+//============================================================
+//  switchres_module::add_display
+//============================================================
+
+display_manager* switchres_module::add_display(int index, osd_monitor_info *monitor, osd_window_config *config)
+{
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine().options());
+	#endif
+
+	m_priority = OPTION_PRIORITY_DEFAULT;
+
+	display_manager* df = switchres().display_factory();
+
+	// Fill in SR's settings with MAME's options
+
+	df->set_monitor(options.monitor());
+	df->set_modeline(options.modeline());
+	for (int i = 0; i < MAX_RANGES; i++) df->set_crt_range(i, options.crt_range(i));
+	df->set_lcd_range(options.lcd_range());
+	df->set_modeline_generation(options.modeline_generation());
+	df->set_lock_unsupported_modes(options.lock_unsupported_modes());
+	df->set_lock_system_modes(options.lock_system_modes());
+	df->set_refresh_dont_care(options.refresh_dont_care());
+
+	df->set_interlace(options.interlace());
+	df->set_doublescan(options.doublescan());
+	df->set_dotclock_min(options.dotclock_min());
+	df->set_refresh_tolerance(options.sync_refresh_tolerance());
+	df->set_super_width(options.super_width());
+	df->set_h_size(options.h_size());
+	df->set_h_shift(options.h_shift());
+	df->set_v_shift(options.v_shift());
+	df->set_v_shift_correct(options.v_shift_correct());
+	df->set_pixel_precision(options.pixel_precision());
+	df->set_interlace_force_even(options.interlace_force_even());
+	df->set_scale_proportional(options.scale_proportional());
+
+	df->set_api(options.switchres_backend());
+	df->set_screen_compositing(options.screen_compositing());
+	df->set_screen_reordering(options.screen_reordering());
+	df->set_allow_hardware_refresh(options.allow_hw_refresh());
+
+	modeline user_mode = {};
+	user_mode.width = config->width;
+	user_mode.height = config->height;
+	user_mode.refresh = config->refresh;
+	df->set_user_mode(&user_mode);
+
+	// If allowed, try to parse switchres.ini, and raise our priority if found
+	if (options.switchres_ini() && m_switchres->parse_config("switchres.ini"))
+		m_priority = OPTION_PRIORITY_SWITCHRES;
+
+	// Use dummy display for nogpu device
+	if (strcmp(options.video(), "mister") == 0)
+		df->set_screen("dummy");
+
+	// Add a new display manager. This also parses its display#.ini
+	display_manager *display = switchres().add_display(0);
+
+	// If we found a display#.ini, raise our priority
+	if (display->has_ini())
+		m_priority = OPTION_PRIORITY_SWITCHRES;
+
+	// Always override SR's display option with MAME's -screen option (except for -video mister)
+	if (strcmp(options.video(), "mister") != 0)
+		display->set_screen(std::string(monitor->devicename()).c_str());
+
+	// Finally, override SR's settings with MAME's options of higher priority
+	if (options.get_entry(OSDOPTION_MONITOR)->priority() > m_priority) display->set_monitor(options.monitor());
+	if (options.get_entry(OSDOPTION_MODELINE)->priority() > m_priority) display->set_modeline(options.modeline());
+	for (int i = 0; i < MAX_RANGES; i++) if (options.get_entry(string_format("%s%d", OSDOPTION_CRT_RANGE, i).c_str())->priority() > m_priority) display->set_crt_range(i, options.crt_range(i));
+	if (options.get_entry(OSDOPTION_LCD_RANGE)->priority() > m_priority) display->set_lcd_range(options.lcd_range());
+	if (options.get_entry(OSDOPTION_MODELINE_GENERATION)->priority() > m_priority) display->set_modeline_generation(options.modeline_generation());
+	if (options.get_entry(OSDOPTION_LOCK_UNSUPPORTED_MODES)->priority() > m_priority) display->set_lock_unsupported_modes(options.lock_unsupported_modes());
+	if (options.get_entry(OSDOPTION_LOCK_SYSTEM_MODES)->priority() > m_priority) display->set_lock_system_modes(options.lock_system_modes());
+	if (options.get_entry(OSDOPTION_REFRESH_DONT_CARE)->priority() > m_priority) display->set_refresh_dont_care(options.refresh_dont_care());
+
+	if (options.get_entry(OSDOPTION_INTERLACE)->priority() > m_priority) display->set_interlace(options.interlace());
+	if (options.get_entry(OSDOPTION_DOUBLESCAN)->priority() > m_priority) display->set_doublescan(options.doublescan());
+	if (options.get_entry(OSDOPTION_DOTCLOCK_MIN)->priority() > m_priority) display->set_dotclock_min(options.dotclock_min());
+	if (options.get_entry(OSDOPTION_SYNC_REFRESH_TOLERANCE)->priority() > m_priority) display->set_refresh_tolerance(options.sync_refresh_tolerance());
+	if (options.get_entry(OSDOPTION_SUPER_WIDTH)->priority() > m_priority) display->set_super_width(options.super_width());
+	if (options.get_entry(OSDOPTION_H_SIZE)->priority() > m_priority) display->set_h_size(options.h_size());
+	if (options.get_entry(OSDOPTION_H_SHIFT)->priority() > m_priority) display->set_h_shift(options.h_shift());
+	if (options.get_entry(OSDOPTION_V_SHIFT)->priority() > m_priority) display->set_v_shift(options.v_shift());
+	if (options.get_entry(OSDOPTION_V_SHIFT_CORRECT)->priority() > m_priority) display->set_v_shift_correct(options.v_shift_correct());
+	if (options.get_entry(OSDOPTION_PIXEL_PRECISION)->priority() > m_priority) display->set_pixel_precision(options.pixel_precision());
+	if (options.get_entry(OSDOPTION_INTERLACE_FORCE_EVEN)->priority() > m_priority) display->set_interlace_force_even(options.interlace_force_even());
+	if (options.get_entry(OSDOPTION_SCALE_PROPORTIONAL)->priority() > m_priority) display->set_scale_proportional(options.scale_proportional());
+
+	if (options.get_entry(OSDOPTION_SWITCHRES_BACKEND)->priority() > m_priority) display->set_api(options.switchres_backend());
+	if (options.get_entry(OSDOPTION_SCREEN_COMPOSITING)->priority() > m_priority) display->set_screen_compositing(options.screen_compositing());
+	if (options.get_entry(OSDOPTION_SCREEN_REORDERING)->priority() > m_priority) display->set_screen_reordering(options.screen_reordering());
+	if (options.get_entry(OSDOPTION_ALLOW_HW_REFRESH)->priority() > m_priority) display->set_allow_hardware_refresh(options.allow_hw_refresh());
+
+	if ((options.get_entry(OSDOPTION_RESOLUTION)->priority() > m_priority) ||
+		(options.get_entry(string_format("%s%d", OSDOPTION_RESOLUTION, index).c_str())->priority() > m_priority))
+	{
+		user_mode.width = config->width;
+		user_mode.height = config->height;
+		user_mode.refresh = config->refresh;
+		display->set_user_mode(&user_mode);
+	}
+
+	// Parse options now
+	display->parse_options();
+
+	m_num_screens ++;
+	return display;
+}
+
+//============================================================
+//  switchres_module::init_display
+//============================================================
+
+bool switchres_module::init_display(int index, osd_monitor_info *monitor, osd_window_config *config, render_target *target, void* pf_data)
+{
+	display_manager *display = switchres().display(index);
+
+	if (!display)
+		return false;
+
+	// Initialize the display manager
+	if (!display->init(pf_data))
+		return false;
+
+	display->set_monitor_aspect(display->desktop_is_rotated()? 1.0f / monitor->aspect() : monitor->aspect());
+	get_game_info(display, target);
+
+	osd_printf_verbose("Switchres: get_mode(%d) %d %d %f %f\n", index, width(index), height(index), refresh(index), display->monitor_aspect());
+	display->get_mode(width(index), height(index), refresh(index), rotation(index)? SR_MODE_ROTATED : 0);
+	if (display->got_mode()) set_mode(index, monitor, target, config);
+
+	return true;
+}
+
+//============================================================
+//  switchres_module::delete_display
+//============================================================
+
+void switchres_module::delete_display(int index)
+{
+	if (switchres().displays[index])
+	{
+		delete switchres().displays[index];
+		switchres().displays[index] = nullptr;
+	}
+}
+
+//============================================================
+//  switchres_module::get_game_info
+//============================================================
+
+void switchres_module::get_game_info(display_manager* display, render_target *target)
+{
+	bool rotation = effective_orientation(display, target);
+	set_rotation(display->index(), rotation);
+
+	int minwidth, minheight;
+	target->compute_minimum_size(minwidth, minheight);
+
+	if (rotation ^ display->desktop_is_rotated()) std::swap(minwidth, minheight);
+	set_width(display->index(), minwidth);
+	set_height(display->index(), minheight);
+
+	// determine the refresh rate of the primary screen
+	const screen_device *primary_screen = screen_device_enumerator(machine().root_device()).first();
+	if (primary_screen != nullptr) set_refresh(display->index(), primary_screen->frame_number() == 0? ATTOSECONDS_TO_HZ(primary_screen->refresh_attoseconds()) : primary_screen->frame_period().as_hz());
+}
+
+//============================================================
+//  switchres_module::effective_orientation
+//============================================================
+
+bool switchres_module::effective_orientation(display_manager* display, render_target *target)
+{
+
+	bool target_is_rotated = (target->orientation() & machine_flags::MASK_ORIENTATION) & ORIENTATION_SWAP_XY? true:false;
+	bool game_is_rotated = (machine().system().flags & machine_flags::MASK_ORIENTATION) & ORIENTATION_SWAP_XY;
+
+	return target_is_rotated ^ game_is_rotated ^ display->desktop_is_rotated();
+}
+
+//============================================================
+//  switchres_module::check_resolution_change
+//============================================================
+
+bool switchres_module::check_resolution_change(int i, osd_monitor_info *monitor, render_target *target, osd_window_config *config)
+{
+	display_manager *display = switchres().display(i);
+
+	int old_width = width(i);
+	int old_height = height(i);
+	double old_refresh = refresh(i);
+	bool old_rotation = rotation(i);
+
+	get_game_info(display, target);
+
+	if (old_width != width(i) || old_height != height(i) || old_refresh != refresh(i) || old_rotation != rotation(i))
+	{
+		osd_printf_verbose("Switchres: Resolution change from %dx%d@%f %s to %dx%d@%f %s\n",
+			old_width, old_height, old_refresh, old_rotation?"rotated":"normal", width(i), height(i), refresh(i), rotation(i)?"rotated":"normal");
+
+		display->get_mode(width(i), height(i), refresh(i), rotation(i)? SR_MODE_ROTATED : 0);
+
+		if (display->got_mode())
+		{
+			if (display->is_switching_required())
+			{
+				set_mode(i, monitor, target, config);
+				return true;
+			}
+
+			set_options(display, target);
+		}
+	}
+
+	return false;
+}
+
+//============================================================
+//  switchres_module::set_mode
+//============================================================
+
+bool switchres_module::set_mode(int i, osd_monitor_info *monitor, render_target *target, osd_window_config *config)
+{
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine().options());
+	#endif
+
+	display_manager *display = switchres().display(i);
+
+	if (display->got_mode())
+	{
+		if (display->is_mode_updated()) display->update_mode(display->selected_mode());
+
+		else if (display->is_mode_new()) display->add_mode(display->selected_mode());
+
+		config->width = display->width();
+		config->height = display->height();
+		config->refresh = display->refresh();
+
+		if (options.mode_setting())
+		{
+			display->set_mode(display->selected_mode());
+			monitor->refresh();
+			monitor->update_resolution(display->width(), display->height());
+		}
+
+		set_options(display, target);
+
+		return true;
+	}
+
+	return false;
+}
+
+//============================================================
+//  switchres_module::check_geometry_change
+//============================================================
+
+bool switchres_module::check_geometry_change(int i)
+{
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine().options());
+	#endif
+
+	display_manager *display = switchres().display(i);
+
+	if (options.h_size() != display->h_size() || options.h_shift() != display->h_shift() || options.v_shift() != display->v_shift())
+		return true;
+
+	return false;
+}
+
+//============================================================
+//  switchres_module::adjust_mode
+//============================================================
+
+bool switchres_module::adjust_mode(int i)
+{
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine().options());
+	#endif
+
+	display_manager *display = switchres().display(i);
+
+	display->set_h_size(options.h_size());
+	display->set_h_shift(options.h_shift());
+	display->set_v_shift(options.v_shift());
+
+	display->get_mode(width(i), height(i), refresh(i), rotation(i)? SR_MODE_ROTATED : 0);
+	if (display->got_mode())
+	{
+		if (display->is_mode_updated()) display->update_mode(display->selected_mode());
+
+		else if (display->is_mode_new()) display->add_mode(display->selected_mode());
+
+		if (options.mode_setting())
+			display->set_mode(display->selected_mode());
+
+		options.set_value(OSDOPTION_H_SIZE, (float)display->h_size(), OPTION_PRIORITY_CMDLINE);
+		options.set_value(OSDOPTION_H_SHIFT, display->h_shift(), OPTION_PRIORITY_CMDLINE);
+		options.set_value(OSDOPTION_V_SHIFT, display->v_shift(), OPTION_PRIORITY_CMDLINE);
+	}
+
+	return true;
+}
+
+//============================================================
+//  switchres_module::set_options
+//============================================================
+
+void switchres_module::set_options(display_manager* display, render_target *target)
+{
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine().options());
+	#endif
+
+	// Set scaling/stretching options
+
+	if (options.autostretch())
+	{
+		bool is_super_resolution = !(display->is_stretched()) && (display->width() >= display->super_width());
+
+		bool target_is_rotated = (target->orientation() & machine_flags::MASK_ORIENTATION) & ORIENTATION_SWAP_XY? true : false;
+		float target_aspect = target->current_view().effective_aspect();
+		if (target_is_rotated) target_aspect = 1.0f / target_aspect;
+		bool force_aspect = (target_aspect != display->monitor_aspect());
+
+		set_option(OPTION_KEEPASPECT, force_aspect);
+		set_option(OPTION_UNEVENSTRETCH, display->is_stretched());
+		set_option(OPTION_UNEVENSTRETCHX, is_super_resolution);
+
+		// Update target if it's already initialized
+		if (target)
+		{
+			target->set_keepaspect(options.keep_aspect());
+
+			if (options.uneven_stretch())
+				target->set_scale_mode(SCALE_FRACTIONAL);
+			else if(options.uneven_stretch_x())
+				target->set_scale_mode(SCALE_FRACTIONAL_X);
+			else if(options.uneven_stretch_y())
+				target->set_scale_mode(SCALE_FRACTIONAL_Y);
+			else
+				target->set_scale_mode(SCALE_INTEGER);
+		}
+	}
+
+	// Set MAME OSD specific options
+
+	// Vertical synchronization management (autosync)
+	// Disable -syncrefresh if our vfreq is scaled or out of syncrefresh_tolerance
+	if (options.autosync())
+	{
+		bool sync_refresh_effective = (options.black_frame_insertion() > 0) || !((display->is_refresh_off()) || floorf(display->v_scale()) > 1.0f);
+	#if defined(OSD_WINDOWS)
+		set_option(OSDOPTION_WAITVSYNC, true);
+	#elif defined(OSD_SDL)
+		set_option(OSDOPTION_WAITVSYNC, sync_refresh_effective);
+	#endif
+		set_option(OPTION_SYNCREFRESH, sync_refresh_effective);
+	}
+
+	// Set filter options
+	if (options.autofilter())
+		set_option(OSDOPTION_FILTER, (display->is_stretched() || display->is_interlaced()));
+
+	#if defined(OSD_WINDOWS)
+		downcast<windows_osd_interface &>(machine().osd()).extract_video_config();
+	#elif defined(OSD_SDL)
+		downcast<sdl_osd_interface &>(machine().osd()).extract_video_config();
+	#endif
+}
+
+//============================================================
+//  switchres_module::set_option - option setting wrapper
+//============================================================
+
+void switchres_module::set_option(const char *option_ID, bool state)
+{
+	#if defined(OSD_WINDOWS)
+		windows_options &options = downcast<windows_options &>(machine().options());
+	#elif defined(OSD_SDL)
+		sdl_options &options = downcast<sdl_options &>(machine().options());
+	#endif
+
+	//options.set_value(option_ID, state, OPTION_PRIORITY_SWITCHRES);
+	options.set_value(option_ID, state, OPTION_PRIORITY_SWITCHRES);
+	osd_printf_verbose("SwitchRes: Setting option -%s%s\n", options.bool_value(option_ID)?"":"no", option_ID);
+}
+
+//============================================================
+//  switchres_module::mode_to_txt
+//============================================================
+
+const char *switchres_module::display_mode_to_txt(int i)
+{
+	if (!downcast<osd_options &>(machine().options()).switch_res())
+		return "Switchres is disabled\n";
+
+	display_manager *display = switchres().display(i);
+
+	if (display == nullptr)
+		sprintf(m_mode_txt, "SR(%d): no physical display\n", i);
+
+	else if (display->got_mode())
+		sprintf(m_mode_txt, "SR(%d): %d x %d%s%s %2.3f Hz %2.3f kHz\n",
+				i, display->width(), display->height(), display->is_interlaced()?"i":"p", display->is_doublescanned()?"d":"", display->v_freq(), display->h_freq()/1000);
+	else
+		sprintf(m_mode_txt, "SR(%d): could not find a video mode\n", i);
+
+	return m_mode_txt;
+}
diff --git a/src/osd/modules/switchres/switchres_module.h b/src/osd/modules/switchres/switchres_module.h
new file mode 100644
index 00000000000..4d9862d045e
--- /dev/null
+++ b/src/osd/modules/switchres/switchres_module.h
@@ -0,0 +1,75 @@
+/**************************************************************
+
+   switchres_module.h - Switchres MAME module
+
+   ---------------------------------------------------------
+
+   Switchres   Modeline generation engine for emulation
+
+   License     GPL-2.0+
+   Copyright   2010-2020 Chris Kennedy, Antonio Giner,
+                         Alexandre Wodarczyk, Gil Delescluse
+
+ **************************************************************/
+
+#ifndef SWITCHRES_MODULE_H_
+#define SWITCHRES_MODULE_H_
+
+#define MAX_WINDOWS 4
+
+class osd_monitor_info;
+class osd_window_config;
+class switchres_manager;
+class display_manager;
+
+class switchres_module
+{
+public:
+	switchres_module() {};
+	~switchres_module() {};
+
+	// getters
+	running_machine &machine() const { assert(m_machine != nullptr); return *m_machine; }
+	switchres_manager &switchres() const { assert(m_switchres != nullptr); return *m_switchres; }
+	int width(int i) { return m_width[i]; }
+	int height(int i) { return m_height[i]; }
+	double refresh(int i) { return m_refresh[i]; }
+	bool rotation(int i)  { return m_rotation[i]; }
+
+	// setters
+	void set_width(int i, int width) { m_width[i] = width; }
+	void set_height(int i, double height) { m_height[i] = height; }
+	void set_refresh(int i, double refresh) { m_refresh[i] = refresh; }
+	void set_rotation(int i, bool rotation) { m_rotation[i] = rotation; }
+
+	// interface
+	void init(running_machine &machine);
+	void exit();
+	display_manager* add_display(int index, osd_monitor_info *monitor, osd_window_config *config);
+	bool init_display(int index, osd_monitor_info *monitor, osd_window_config *config, render_target *target, void* pf_data);
+	void delete_display(int index);
+	void get_game_info(display_manager* display, render_target *target);
+	bool effective_orientation(display_manager* display, render_target *target);
+	bool check_resolution_change(int i, osd_monitor_info *monitor, render_target *target, osd_window_config *config);
+	bool check_geometry_change(int i);
+	bool set_mode(int i, osd_monitor_info *monitor, render_target *target, osd_window_config *config);
+	bool adjust_mode(int i);
+	void set_options(display_manager* display, render_target *target);
+	void set_option(const char *option_ID, bool state);
+	const char* display_mode_to_txt(int i);
+
+private:
+	switchres_manager* m_switchres;
+	running_machine*   m_machine;
+
+	int    m_num_screens = 0;
+
+	int    m_width[MAX_WINDOWS];
+	int    m_height[MAX_WINDOWS];
+	double m_refresh[MAX_WINDOWS];
+	bool   m_rotation[MAX_WINDOWS];
+	char   m_mode_txt[256] = {};
+	int    m_priority = 0;
+};
+
+#endif
diff --git a/src/osd/osdepend.h b/src/osd/osdepend.h
index a81632911f5..a91f0975bfd 100644
--- a/src/osd/osdepend.h
+++ b/src/osd/osdepend.h
@@ -115,6 +115,9 @@ public:
 	virtual std::unique_ptr<osd::network_device> open_network_device(int id, osd::network_handler &handler) = 0;
 	virtual std::vector<osd::network_device_info> list_network_devices() = 0;
 
+	// switchres interface
+	virtual const char *switchres_mode(int i) = 0;
+
 protected:
 	virtual ~osd_interface() { }
 };
diff --git a/src/osd/sdl/osdsdl.cpp b/src/osd/sdl/osdsdl.cpp
index d9268869729..e776e4ccf45 100644
--- a/src/osd/sdl/osdsdl.cpp
+++ b/src/osd/sdl/osdsdl.cpp
@@ -792,9 +792,6 @@ void sdl_osd_interface::check_osd_inputs()
 	// check for toggling fullscreen mode (don't do this in debug mode)
 	if (machine().ui_input().pressed(IPT_OSD_1) && !(machine().debug_flags & DEBUG_FLAG_OSD_ENABLED))
 	{
-		// destroy the renderers first so that the render module can bounce if it depends on having a window handle
-		for (auto it = osd_common_t::window_list().rbegin(); osd_common_t::window_list().rend() != it; ++it)
-			(*it)->renderer_reset();
 		for (auto const &curwin : osd_common_t::window_list())
 			dynamic_cast<sdl_window_info &>(*curwin).toggle_full_screen();
 	}
diff --git a/src/osd/sdl/osdsdl.h b/src/osd/sdl/osdsdl.h
index 74730f69df5..7fe2ab38c3a 100644
--- a/src/osd/sdl/osdsdl.h
+++ b/src/osd/sdl/osdsdl.h
@@ -152,12 +152,17 @@ public:
 	// input overridables
 	virtual void customize_input_type_list(std::vector<input_type_entry> &typelist) override;
 
+	// video overridables
+	virtual void add_audio_to_recording(const int16_t *buffer, int samples_this_frame) override;
+
 	virtual bool video_init() override;
 	virtual bool window_init() override;
 
 	virtual void video_exit() override;
 	virtual void window_exit() override;
 
+	void extract_video_config();
+
 	// SDL-specific
 	virtual bool has_focus() const override { return bool(m_focus_window); }
 	void release_keys();
@@ -182,7 +187,6 @@ private:
 
 	virtual void osd_exit() override;
 
-	void extract_video_config();
 	void output_oslog(const char *buffer);
 
 	void process_window_event(SDL_Event const &event);
diff --git a/src/osd/sdl/video.cpp b/src/osd/sdl/video.cpp
index 8fe836aabf2..b1f5285dbb8 100644
--- a/src/osd/sdl/video.cpp
+++ b/src/osd/sdl/video.cpp
@@ -135,11 +135,8 @@ void sdl_osd_interface::extract_video_config()
 	video_config.centerv       = options().centerv();
 	video_config.waitvsync     = options().wait_vsync();
 	video_config.syncrefresh   = options().sync_refresh();
-	if (!video_config.waitvsync && video_config.syncrefresh)
-	{
-		osd_printf_warning("-syncrefresh specified without -waitvsync. Reverting to -nosyncrefresh\n");
-		video_config.syncrefresh = 0;
-	}
+	video_config.sync_mode     = options().sync_mode();
+	video_config.framedelay    = options().frame_delay();
 
 	if (video_config.prescale < 1 || video_config.prescale > 20)
 	{
diff --git a/src/osd/sdl/window.cpp b/src/osd/sdl/window.cpp
index 59be82238d0..2a2bcd0ddd6 100644
--- a/src/osd/sdl/window.cpp
+++ b/src/osd/sdl/window.cpp
@@ -111,6 +111,14 @@ bool sdl_osd_interface::window_init()
 }
 
 
+void sdl_osd_interface::add_audio_to_recording(const int16_t *buffer, int samples_this_frame)
+{
+	auto const &window = osd_common_t::window_list().front(); // We only record on the first window
+	if (window)
+		window->renderer().add_audio_to_recording(buffer, samples_this_frame);
+}
+
+
 //============================================================
 //  sdlwindow_exit
 //  (main thread)
@@ -213,27 +221,24 @@ void sdl_window_info::toggle_full_screen()
 		m_windowed_dim = get_size();
 	}
 
-	// kill off the drawers
-	renderer_reset();
-	bool is_osx = false;
-#ifdef SDLMAME_MACOSX
-	// FIXME: This is weird behaviour and certainly a bug in SDL
-	is_osx = true;
-#endif
-	if (fullscreen() && (video_config.switchres || is_osx))
-	{
-		SDL_SetWindowFullscreen(platform_window(), 0);
-		SDL_SetWindowDisplayMode(platform_window(), &m_original_mode);
-		SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN);
-	}
-	SDL_DestroyWindow(platform_window());
-	set_platform_window(nullptr);
-	downcast<sdl_osd_interface &>(machine().osd()).release_keys();
-
 	// toggle the window mode
 	set_fullscreen(!fullscreen());
 
-	complete_create();
+	SDL_SetWindowFullscreen(platform_window(), fullscreen() ? SDL_WINDOW_FULLSCREEN : 0);
+	SDL_SetWindowBordered(platform_window(), fullscreen() ? SDL_FALSE : SDL_TRUE);
+	SDL_SetWindowResizable(platform_window(), fullscreen() ? SDL_FALSE : SDL_TRUE);
+
+	if (fullscreen() && downcast<sdl_options &>(machine().options()).mode_setting())
+	{
+		if (m_display_manager)
+			downcast<sdl_osd_interface&>(machine().osd()).switchres()->set_mode(index(), monitor(), target(), &m_win_config);
+	}
+
+	if (!fullscreen())
+	{
+		osd_dim temp = m_windowed_dim.width() > 0 ? m_windowed_dim : get_min_bounds(keepaspect());
+		resize(temp.width(), temp.height());
+	}
 }
 
 void sdl_window_info::modify_prescale(int dir)
@@ -638,6 +643,13 @@ void sdl_window_info::complete_destroy()
 	}
 
 	renderer_reset();
+
+	if (m_display_manager)
+	{
+		downcast<sdl_osd_interface&>(machine().osd()).switchres()->delete_display(index());
+		m_display_manager = nullptr;
+	}
+
 	SDL_DestroyWindow(platform_window());
 	set_platform_window(nullptr);
 	downcast<sdl_osd_interface &>(machine().osd()).release_keys();
@@ -730,6 +742,14 @@ void sdl_window_info::update()
 	// if we're visible and running and not in the middle of a resize, draw
 	if (target() != nullptr)
 	{
+		// check if geometry has changed
+		if (this->m_fullscreen && video_config.switchres && downcast<sdl_osd_interface&>(machine().osd()).switchres()->check_geometry_change(index()))
+		{
+			toggle_full_screen();
+			downcast<sdl_osd_interface&>(machine().osd()).switchres()->adjust_mode(index());
+			toggle_full_screen();
+		}
+
 		int tempwidth, tempheight;
 
 		// see if the games video mode has changed
@@ -745,13 +765,23 @@ void sdl_window_info::update()
 			}
 			else if (video_config.switchres)
 			{
-				osd_dim tmp = this->pick_best_mode();
-				resize(tmp.width(), tmp.height());
+				// check if we need to change the video mode
+				if (downcast<sdl_options &>(machine().options()).changeres())
+					if (m_display_manager) downcast<sdl_osd_interface&>(machine().osd()).switchres()->check_resolution_change(index(), monitor(), target(), &m_win_config);
+
+				if (!downcast<sdl_options &>(machine().options()).mode_setting())
+				{
+					SDL_DisplayMode mode = {};
+					mode.w = m_win_config.width;
+					mode.h = m_win_config.height;
+					mode.refresh_rate = m_win_config.refresh;
+					SDL_SetWindowDisplayMode(platform_window(), &mode);
+				}
 			}
 		}
 
 		osd_ticks_t event_wait_ticks;
-		if (video_config.waitvsync && video_config.syncrefresh)
+		if (video_config.syncrefresh)
 			event_wait_ticks = osd_ticks_per_second(); // block at most a second
 		else
 			event_wait_ticks = 0;
@@ -805,16 +835,13 @@ void sdl_window_info::update()
 int sdl_window_info::complete_create()
 {
 	osd_dim temp(0,0);
+	bool mode_setting = downcast<sdl_options &>(machine().options()).mode_setting();
 
 	// clear out original mode. Needed on OSX
 	if (fullscreen())
 	{
 		// default to the current mode exactly
 		temp = monitor()->position_size().dim();
-
-		// if we're allowed to switch resolutions, override with something better
-		if (video_config.switchres)
-			temp = pick_best_mode();
 	}
 	else if (m_windowed_dim.width() > 0)
 	{
@@ -854,9 +881,9 @@ int sdl_window_info::complete_create()
 	m_extra_flags |= (fullscreen() ?
 			SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE);
 
-#if defined(SDLMAME_WIN32)
+//#if defined(SDLMAME_WIN32)
 	SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
-#endif
+//#endif
 
 	// get monitor work area for centering
 	osd_rect work = monitor()->usuable_position_size();
@@ -944,12 +971,21 @@ int sdl_window_info::complete_create()
 		//SDL_GetCurrentDisplayMode(window().monitor()->handle, &mode);
 		SDL_GetWindowDisplayMode(platform_window(), &mode);
 		m_original_mode = mode;
-		mode.w = temp.width();
-		mode.h = temp.height();
-		if (m_win_config.refresh)
-			mode.refresh_rate = m_win_config.refresh;
 
-		SDL_SetWindowDisplayMode(platform_window(), &mode);    // Try to set mode
+		// add the switchres display manager
+		m_display_manager = downcast<sdl_osd_interface&>(machine().osd()).switchres()->add_display(index(), monitor(), &m_win_config);
+		downcast <sdl_osd_interface&>(machine().osd()).switchres()->init_display(index(), monitor(), &m_win_config, target(), sdlwindow);
+		temp = osd_dim(m_win_config.width, m_win_config.height);
+
+		if (!mode_setting)
+		{
+			mode.w = temp.width();
+			mode.h = temp.height();
+			if (m_win_config.refresh)
+				mode.refresh_rate = m_win_config.refresh;
+
+			SDL_SetWindowDisplayMode(platform_window(), &mode);    // Try to set mode
+		}
 #ifndef SDLMAME_WIN32
 		/* FIXME: Warp the mouse to 0,0 in case a virtual desktop resolution
 		 * is in place after the mode switch - which will most likely be the case
diff --git a/src/osd/sdl/window.h b/src/osd/sdl/window.h
index 49fe452192f..d30e4124cb5 100644
--- a/src/osd/sdl/window.h
+++ b/src/osd/sdl/window.h
@@ -32,6 +32,8 @@ typedef uintptr_t HashT;
 
 #define OSDWORK_CALLBACK(name)  void *name(void *param, int threadid)
 
+class display_manager;
+
 class sdl_window_info : public osd_window_t<SDL_Window*>
 {
 public:
@@ -129,6 +131,8 @@ private:
 	bool m_mouse_inside;
 	std::vector<pointer_dev_info> m_ptrdev_info;
 	std::vector<sdl_pointer_info> m_active_pointers;
+
+	display_manager *   m_display_manager = 0;
 };
 
 #endif // MAME_OSD_SDL_WINDOW_H
diff --git a/src/osd/windows/video.cpp b/src/osd/windows/video.cpp
index 439f0019f69..f74c3e9beb9 100644
--- a/src/osd/windows/video.cpp
+++ b/src/osd/windows/video.cpp
@@ -171,8 +171,8 @@ void windows_osd_interface::extract_video_config()
 
 	video_config.waitvsync     = options().wait_vsync();
 	video_config.syncrefresh   = options().sync_refresh();
-	video_config.triplebuf     = options().triple_buffer();
 	video_config.switchres     = options().switch_res();
+	video_config.framedelay    = options().frame_delay();
 
 	if (video_config.prescale < 1 || video_config.prescale > 20)
 	{
diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp
index 2759b32227f..1cb5bcd087a 100644
--- a/src/osd/windows/window.cpp
+++ b/src/osd/windows/window.cpp
@@ -118,7 +118,7 @@ static HANDLE ui_pause_event;
 
 static bool s_aggressive_focus;
 
-
+static bool is_mister_renderer = false;
 
 //============================================================
 //  PROTOTYPES
@@ -727,6 +727,15 @@ std::unique_ptr<win_window_info> win_window_info::create(
 	window->m_targetlayerconfig = window->target()->layer_config();
 	window->m_targetvismask = window->target()->visibility_mask();
 
+	// add they switchres display manager
+	if (window->m_fullscreen_safe && downcast<windows_options &>(machine.options()).switch_res())
+	{
+		window->m_display_manager = WINOSD(machine)->switchres()->add_display(index, monitor.get(), &window->m_win_config);
+		if (window->m_display_manager) WINOSD(machine)->switchres()->init_display(index, monitor.get(), &window->m_win_config, window->target(), nullptr);
+	}
+
+	is_mister_renderer = (strcmp(downcast<windows_options &>(machine.options()).video(), "mister") == 0);
+
 	// set the initial maximized state
 	window->m_startmaximized = downcast<windows_options &>(machine.options()).maximize();
 
@@ -791,6 +800,35 @@ void win_window_info::update()
 		}
 	}
 
+	bool reset_required = false;
+
+	// check if we need to change the video mode
+	auto &options = downcast<windows_options &>(machine().options());
+	if (options.switch_res() && options.changeres())
+		reset_required = WINOSD(machine())->switchres()->check_resolution_change(index(), monitor(), target(), &m_win_config);
+
+	// check if frame delay has changed
+	int new_frame_delay = machine().video().framedelay();
+	if (new_frame_delay != video_config.framedelay)
+	{
+		reset_required |= ((bool)video_config.framedelay != (bool)new_frame_delay);
+		video_config.framedelay = new_frame_delay;
+	}
+
+	// check if geometry has changed
+	if (fullscreen() && options.switch_res() && WINOSD(machine())->switchres()->check_geometry_change(index()))
+	{
+		if (!is_mister_renderer) winwindow_toggle_full_screen();
+		WINOSD(machine())->switchres()->adjust_mode(index());
+		if (!is_mister_renderer) winwindow_toggle_full_screen();
+	}
+
+	if (reset_required)
+	{
+		reset_fullscreen_renderer();
+		return;
+	}
+
 	// if we're visible and running and not in the middle of a resize, draw
 	if (platform_window() != nullptr && target() != nullptr && has_renderer())
 	{
@@ -1288,6 +1326,9 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
 				{
 					for (const auto &w : osd_common_t::window_list())
 						ShowWindow(dynamic_cast<win_window_info &>(*w).platform_window(), SW_RESTORE);
+
+					// Set the first window as foreground
+					SetForegroundWindow(dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window());
 				}
 				else if ((wparam == WA_INACTIVE) && !is_mame_window(HWND(lparam)))
 				{
@@ -1341,6 +1382,12 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
 		window->maximize_window();
 		break;
 
+	// make sure the taskbar doesn't get on top of us
+	case WM_DEVICECHANGE:
+	case WM_WININICHANGE:
+		SetWindowPos(wnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+		break;
+
 	// maximum size set
 	case WM_DISPLAYCHANGE:
 		/* FIXME: The current codebase has an issue with setting aspect
@@ -1350,7 +1397,6 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
 		 * should be used.
 		 */
 		window->monitor()->refresh();
-		window->monitor()->update_resolution(LOWORD(lparam), HIWORD(lparam));
 		break;
 
 	// set focus: if we're not the primary window, switch back
@@ -1861,6 +1907,27 @@ void win_window_info::set_fullscreen(int fullscreen)
 }
 
 
+//============================================================
+//  reset_fullscreen_renderer
+//============================================================
+
+void win_window_info::reset_fullscreen_renderer()
+{
+	// if we're in the right state, punt
+	if (!m_fullscreen)
+		return;
+
+	// D3D renderer needs a reset
+	{
+		renderer().restart();
+		return;
+	}
+
+	// Resize our window if required
+	adjust_window_position_after_major_change();
+}
+
+
 //============================================================
 //  focus
 //  (main or window thread)
diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h
index ada71f1cba7..3aab092132c 100644
--- a/src/osd/windows/window.h
+++ b/src/osd/windows/window.h
@@ -48,6 +48,8 @@ enum class win_window_focus
 };
 
 
+class display_manager;
+
 class win_window_info  : public osd_window_t<HWND>
 {
 public:
@@ -155,6 +157,7 @@ private:
 	void maximize_window();
 	void adjust_window_position_after_major_change();
 	void set_fullscreen(int fullscreen);
+	void reset_fullscreen_renderer();
 
 	// pointer handling helpers
 	void pointer_entered(WPARAM wparam, LPARAM lparam);
@@ -189,6 +192,8 @@ private:
 	std::vector<win_pointer_info> m_active_pointers;
 
 	static POINT        s_saved_cursor_pos;
+
+	display_manager *   m_display_manager = 0;
 };
 
 
diff --git a/src/osd/windows/winopts.cpp b/src/osd/windows/winopts.cpp
index 0408593dda3..24aeba24f56 100644
--- a/src/osd/windows/winopts.cpp
+++ b/src/osd/windows/winopts.cpp
@@ -121,7 +121,6 @@ options_entry const f_win_option_entries[] =
 
 	// full screen options
 	{ nullptr,                                        nullptr,    core_options::option_type::HEADER,     "FULL SCREEN OPTIONS" },
-	{ WINOPTION_TRIPLEBUFFER ";tb",                   "0",        core_options::option_type::BOOLEAN,    "enable triple buffering" },
 	{ WINOPTION_FULLSCREENBRIGHTNESS ";fsb(0.1-2.0)", "1.0",      core_options::option_type::FLOAT,      "brightness value in full screen mode" },
 	{ WINOPTION_FULLSCREENCONTRAST ";fsc(0.1-2.0)",   "1.0",      core_options::option_type::FLOAT,      "contrast value in full screen mode" },
 	{ WINOPTION_FULLSCREENGAMMA ";fsg(0.1-3.0)",      "1.0",      core_options::option_type::FLOAT,      "gamma value in full screen mode" },
diff --git a/src/osd/windows/winopts.h b/src/osd/windows/winopts.h
index 44936f2d192..765ba47cf26 100644
--- a/src/osd/windows/winopts.h
+++ b/src/osd/windows/winopts.h
@@ -109,7 +109,6 @@
 #define WINOPTION_UI_LUT_ENABLE "ui_lut_enable"
 
 // full screen options
-#define WINOPTION_TRIPLEBUFFER          "triplebuffer"
 #define WINOPTION_FULLSCREENBRIGHTNESS  "full_screen_brightness"
 #define WINOPTION_FULLSCREENCONTRAST    "full_screen_contrast"
 #define WINOPTION_FULLSCREENGAMMA       "full_screen_gamma"
@@ -220,7 +219,6 @@ public:
 	bool ui_lut_enable() const { return bool_value(WINOPTION_UI_LUT_ENABLE); }
 
 	// full screen options
-	bool triple_buffer() const { return bool_value(WINOPTION_TRIPLEBUFFER); }
 	float full_screen_brightness() const { return float_value(WINOPTION_FULLSCREENBRIGHTNESS); }
 	float full_screen_contrast() const { return float_value(WINOPTION_FULLSCREENCONTRAST); }
 	float full_screen_gamma() const { return float_value(WINOPTION_FULLSCREENGAMMA); }
