diff -pruN 1:4.7.0+dfsg1-2/azure-pipelines.yml 1:4.7.4+dfsg1-2/azure-pipelines.yml
--- 1:4.7.0+dfsg1-2/azure-pipelines.yml	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.7.4+dfsg1-2/azure-pipelines.yml	2022-07-09 05:14:42.000000000 +0000
@@ -0,0 +1,306 @@
+
+trigger:
+- github
+
+jobs:
+- job: windows_tests
+  pool:
+    vmImage: 'windows-latest'
+  steps:
+  - script: |
+      git config --global core.autocrlf false
+    displayName: git config
+  - checkout: self
+    fetchDepth: 1
+  - script: |
+      pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
+      for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x
+      popd
+      call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
+      mkdir tcltk & cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tcl.git
+      cd tcl & cd win
+      nmake -f makefile.vc INSTALLDIR=$(Build.SourcesDirectory)\tcldir
+      nmake -f makefile.vc install INSTALLDIR=$(Build.SourcesDirectory)\tcldir
+    displayName: Generate tcl library
+  - task: CMake@1
+    inputs:
+      workingDirectory: 'build'
+      cmakeArgs: '-DTCL_INCLUDE_PATH=$(Build.SourcesDirectory)\tcldir\include $(Build.SourcesDirectory) -DGTEST=ON'
+  - task: MSBuild@1
+    inputs:
+      solution: 'build/ALL_BUILD.vcxproj'
+      maximumCpuCount: true
+      platform: 'x64'
+      configuration: 'Debug'
+    displayName: Generate SCID
+  - script: build\gtest\Debug\scid_tests.exe
+    displayName: run scid_tests.exe
+
+- job: windows_latest
+  pool:
+    vmImage: 'windows-latest'
+  steps:
+  - script: |
+      pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
+      for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x
+      popd
+      call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
+      mkdir tcltk & cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tcl.git
+      cd tcl & cd win
+      nmake -f makefile.vc INSTALLDIR=$(Build.SourcesDirectory)\tcldir
+      nmake -f makefile.vc install INSTALLDIR=$(Build.SourcesDirectory)\tcldir
+    displayName: Generate tcl library
+  - script: |
+      pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
+      for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x
+      popd
+      call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
+      cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tk.git
+      cd tk & cd win
+      nmake -f makefile.vc INSTALLDIR=$(Build.SourcesDirectory)\tcldir
+      nmake -f makefile.vc install INSTALLDIR=$(Build.SourcesDirectory)\tcldir
+    displayName: Generate tk library
+  - task: CMake@1
+    inputs:
+      workingDirectory: 'build'
+      cmakeArgs: '-DCMAKE_INSTALL_PREFIX=$(Build.SourcesDirectory)/install -DTCL_INCLUDE_PATH=$(Build.SourcesDirectory)\tcldir\include $(Build.SourcesDirectory) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DWITH_MD_LIBRARY=OFF'
+  - task: MSBuild@1
+    inputs:
+      solution: 'build/INSTALL.vcxproj'
+      maximumCpuCount: true
+      platform: 'x64'
+      configuration: 'Release'
+      msbuildArguments: "/p:SpectreMitigation=false /p:RuntimeTypeInfo=false /p:EnableCOMDATFolding=true /p:LinkTimeCodeGeneration=UseLinkTimeCodeGeneration"
+    displayName: Generate SCID
+  - script: |
+      pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
+      for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x
+      popd
+      call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
+      git clone --depth=1 https://github.com/official-stockfish/Stockfish.git stockfish
+      cd stockfish\src
+      cl /std:c++17 /DNDEBUG /DUSE_POPCNT /DUSE_AVX2 /DUSE_SSE41 /DUSE_SSSE3 /DUSE_SSE2  /GS- /MT /O2 /Oi /Ot /Oy /GL /EHsc *.cpp nnue/*.cpp nnue/features/*.cpp syzygy/*.cpp /link /LTCG /STACK:reserve=8388608 /OUT:stockfish.exe advapi32.lib
+      copy stockfish.exe $(Build.SourcesDirectory)\install\bin
+    displayName: Generate stockfish
+  - bash: |
+      nnue=$(egrep -o "nn-[a-z0-9]+.nnue" stockfish/src/evaluate.h)
+      echo "https://data.stockfishchess.org/nn/$nnue ==> install/bin/$nnue"
+      curl -o install/bin/$nnue https://data.stockfishchess.org/nn/$nnue
+    displayName: Download stockfish nnue
+  - script: |
+      move bin engines
+      move scid bin
+      copy $(Build.SourcesDirectory)\tcldir\bin\*.dll bin
+      xcopy /e $(Build.SourcesDirectory)\tcldir\lib\tcl8 lib\tcl8\
+      xcopy /e $(Build.SourcesDirectory)\tcldir\lib\tcl8.6 lib\tcl8.6\
+      xcopy /e $(Build.SourcesDirectory)\tcldir\lib\tk8.6 lib\tk8.6\
+    workingDirectory: '$(Build.SourcesDirectory)\install'
+    displayName: Add the tcl/tk library to the package
+  - task: CopyFiles@2
+    inputs:
+      SourceFolder: '$(Build.SourcesDirectory)\install'
+      TargetFolder: '$(build.artifactstagingdirectory)'
+  - task: PublishBuildArtifacts@1
+    displayName: 'Publish Artifact: windows x64'
+    inputs:
+      artifactName: scid_windows_x64
+      PathtoPublish: '$(build.artifactstagingdirectory)'
+
+- job: ubuntu_latest
+  pool:
+    vmImage: 'ubuntu-latest'
+  steps:
+  - script: |
+      sudo apt-get update &&
+      sudo apt-get -y install tcl-dev clang clang-tidy clang-format cloc
+    displayName: download tclk
+  - script: |
+      cloc src --exclude-dir=egtb,mtbgen,polyglot
+      cloc tcl --exclude-dir=lang
+    displayName: count line of code
+  - script: |
+      clang-format -version
+      clang-format -i src/codec*
+      clang-format -i src/movegen*
+      clang-format -i src/pgn*
+      clang-format -i src/bytebuf.h
+      clang-format -i src/gameview.h
+      clang-format -i src/namebase.h
+      if [[ -n $(git diff) ]]; then
+        git diff
+        exit -1
+      fi
+    displayName: clang-format
+  - script: |
+      mkdir build_tidy && cd build_tidy
+      cmake -DCMAKE_CXX_FLAGS="-g -Wall -Wextra -Werror -pedantic" -DSPELLCHKVALIDATE=ON \
+             $(Build.SourcesDirectory)
+      make VERBOSE=1
+      run-clang-tidy -header-filter=.* -checks=performance-*,clang-*,-clang-analyzer-security.insecureAPI.strcpy ../src 2> /dev/null
+    displayName: clang-tidy
+  - script: |
+      mkdir build && cd build
+      cmake -DCPACK_GENERATOR=DEB \
+            -DCPACK_DEBIAN_PACKAGE_MAINTAINER="Fulvio Benini" \
+            -DCPACK_DEBIAN_PACKAGE_DEPENDS=tk \
+            -DCPACK_DEBIAN_PACKAGE_LICENSE=GPL2 \
+            -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_CXX_FLAGS="-fno-exceptions -fno-rtti" \
+            -DCMAKE_EXE_LINKER_FLAGS="-static-libstdc++ -static-libgcc" \
+             $(Build.SourcesDirectory)
+      make package
+    displayName: Generate SCID
+  - task: CopyFiles@2
+    inputs:
+      SourceFolder: '$(Build.SourcesDirectory)/build'
+      Contents: '*.deb'
+      TargetFolder: '$(build.artifactstagingdirectory)'
+  - task: PublishBuildArtifacts@1
+    displayName: 'Publish Artifact: debian x64'
+    inputs:
+      artifactName: scid_ubuntu_latest_x64_deb_package
+      PathtoPublish: '$(build.artifactstagingdirectory)'
+
+- job: linux_static
+  pool:
+    vmImage: 'ubuntu-latest'
+  steps:
+  - script: |
+      mkdir tcltk && cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tcl.git
+      cd tcl && cd unix
+      ./configure --prefix=$(Build.SourcesDirectory)/tcltk --disable-shared --enable-threads
+      make && make install
+    displayName: Generate tcl library
+  - script: |
+      cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tk.git
+      cd tk && cd unix
+      ./configure --prefix=$(Build.SourcesDirectory)/tcltk --enable-threads
+      make && make install
+    displayName: Generate tk library
+  - script: |
+      mkdir build && cd build
+      cmake -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_CXX_FLAGS="-fno-exceptions -fno-rtti" \
+            -DCMAKE_INSTALL_PREFIX=$(Build.SourcesDirectory)/scid_linux \
+            -DTCL_INCLUDE_PATH=$(Build.SourcesDirectory)/tcltk/include \
+            -DTCL_LIBRARY=$(Build.SourcesDirectory)/tcltk/lib/libtcl8.6.a \
+            -DCMAKE_CXX_STANDARD_LIBRARIES="-lz -ldl" \
+             $(Build.SourcesDirectory)
+      make VERBOSE=1
+      make install
+    displayName: Generate SCID
+  - script: |
+      cd scid_linux
+      mv bin engines
+      mkdir lib
+      cp $(Build.SourcesDirectory)/tcltk/lib/*.so lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tcl8 lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tcl8.6 lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tk8.6 lib/
+      cd ..
+      tar -zcvf '$(build.artifactstagingdirectory)/scid_linux.tar.gz' --owner=0 --group=0 scid_linux
+    displayName: Add the tcl/tk library to the package
+  - task: PublishBuildArtifacts@1
+    displayName: 'Publish Artifact: linux static x64'
+    inputs:
+      artifactName: scid_linux_x64_static_tcl
+      PathtoPublish: '$(build.artifactstagingdirectory)'
+
+- job: macOS_static
+  pool:
+    vmImage: 'macOS-latest'
+  steps:
+  - script: |
+      mkdir tcltk && cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tcl.git
+      cd tcl && cd unix
+      ./configure --prefix=$(Build.SourcesDirectory)/tcltk --disable-shared --enable-threads
+      make && make install
+    displayName: Generate tcl library
+  - script: |
+      cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tk.git
+      cd tk && cd unix
+      ./configure --prefix=$(Build.SourcesDirectory)/tcltk --enable-threads --enable-aqua
+      make && make install
+    displayName: Generate tk library
+  - script: |
+      mkdir build && cd build
+      export LDFLAGS="-lz -framework CoreFoundation"
+      cmake -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_CXX_FLAGS="-fno-exceptions -fno-rtti" \
+            -DCMAKE_INSTALL_PREFIX=$(Build.SourcesDirectory)/scid_macos \
+            -DTCL_INCLUDE_PATH=$(Build.SourcesDirectory)/tcltk/include \
+            -DTCL_LIBRARY=$(Build.SourcesDirectory)/tcltk/lib/libtcl8.6.a \
+             $(Build.SourcesDirectory)
+      make VERBOSE=1
+      make install
+    displayName: Generate SCID
+  - script: |
+      cd scid_macos
+      mv bin engines
+      mkdir lib
+      cp $(Build.SourcesDirectory)/tcltk/lib/*.dylib lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tcl8 lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tcl8.6 lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tk8.6 lib/
+      cd ..
+      tar -zcvf '$(build.artifactstagingdirectory)/scid_macOS.tar.gz' scid_macos
+    displayName: Add the tcl/tk library to the package
+  - task: PublishBuildArtifacts@1
+    displayName: 'Publish Artifact: macOS static x64'
+    inputs:
+      artifactName: scid_macOS_latest_64_static_tcl
+      PathtoPublish: '$(build.artifactstagingdirectory)'
+
+- job: macOS_catalina_static
+  pool:
+    vmImage: 'macOS-10.15'
+  steps:
+  - script: |
+      mkdir tcltk && cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tcl.git
+      cd tcl && cd unix
+      ./configure --prefix=$(Build.SourcesDirectory)/tcltk --disable-shared --enable-threads
+      make && make install
+    displayName: Generate tcl library
+  - script: |
+      cd tcltk
+      git clone --depth=1 --branch core-8-6-branch https://github.com/tcltk/tk.git
+      cd tk && cd unix
+      ./configure --prefix=$(Build.SourcesDirectory)/tcltk --enable-threads --enable-aqua
+      make && make install
+    displayName: Generate tk library
+  - script: |
+      mkdir build && cd build
+      export LDFLAGS="-lz -framework CoreFoundation"
+      cmake -DCMAKE_BUILD_TYPE=Release \
+            -DCMAKE_CXX_FLAGS="-fno-exceptions -fno-rtti" \
+            -DCMAKE_INSTALL_PREFIX=$(Build.SourcesDirectory)/scid_macos \
+            -DTCL_INCLUDE_PATH=$(Build.SourcesDirectory)/tcltk/include \
+            -DTCL_LIBRARY=$(Build.SourcesDirectory)/tcltk/lib/libtcl8.6.a \
+             $(Build.SourcesDirectory)
+      make VERBOSE=1
+      make install
+    displayName: Generate SCID
+  - script: |
+      cd scid_macos
+      mv bin engines
+      mkdir lib
+      cp $(Build.SourcesDirectory)/tcltk/lib/*.dylib lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tcl8 lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tcl8.6 lib/
+      cp -R $(Build.SourcesDirectory)/tcltk/lib/tk8.6 lib/
+      cd ..
+      tar -zcvf '$(build.artifactstagingdirectory)/scid_macOS-10.15.tar.gz' scid_macos
+    displayName: Add the tcl/tk library to the package
+  - task: PublishBuildArtifacts@1
+    displayName: 'Publish Artifact: macOS-10.15 static x64'
+    inputs:
+      artifactName: scid_macOS_10_15_x64_static_tcl
+      PathtoPublish: '$(build.artifactstagingdirectory)'
diff -pruN 1:4.7.0+dfsg1-2/CMakeLists.txt 1:4.7.4+dfsg1-2/CMakeLists.txt
--- 1:4.7.0+dfsg1-2/CMakeLists.txt	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/CMakeLists.txt	2022-07-09 05:14:42.000000000 +0000
@@ -13,17 +13,19 @@
 # You should have received a copy of the GNU General Public License
 # along with Scid. If not, see <http://www.gnu.org/licenses/>.
 
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.15)
 project(scid)
-set(CPACK_PACKAGE_VERSION 4.7.0)
+set(CPACK_PACKAGE_VERSION 4.7.4)
 set(
   CPACK_PACKAGE_DESCRIPTION_SUMMARY
   "chess database application with play and training functionality"
 )
+set(CPACK_DEBIAN_PACKAGE_DEPENDS "tk8.6 (>= 8.6.0)")
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
 include(CPack)
 
 set(CMAKE_CXX_EXTENSIONS OFF)
-set(CMAKE_CXX_STANDARD 14 CACHE STRING "")
+set(CMAKE_CXX_STANDARD 17 CACHE STRING "")
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
 if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS)
@@ -32,24 +34,29 @@ endif()
 
 if(MSVC)
   add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_WARNINGS)
+  # To run/debug using Visual Studio set "scid" as startup project and add:
+  # Command Arguments: ../tcl/start.tcl
+  # Environment:       PATH=C:\tcl\bin
 endif()
 
+find_package(Threads)
+find_package(TCL)
 
 # polyglot
 file(GLOB POLYGLOT_SRC src/polyglot/*.cpp)
 add_library(polyglot ${POLYGLOT_SRC})
 
 # scid
-file(GLOB SCID_SRC src/*.cpp)
-add_executable(scid ${SCID_SRC})
-
-option(SCID_MULTITHREADING "Enable/disable multihreading" ON)
-if(SCID_MULTITHREADING)
-  find_package(Threads REQUIRED)
-  target_link_libraries(scid ${CMAKE_THREAD_LIBS_INIT})
+file(GLOB SCID_SRC src/*.h src/*.cpp)
+if(MSVC)
+  add_executable(scid WIN32 ${SCID_SRC} resources/win/scid.rc resources/win/scid.manifest)
+  target_link_options(scid PRIVATE /ENTRY:mainCRTStartup)
 else()
-  target_compile_definitions(scid PRIVATE -DMULTITHREADING_OFF)
+  add_executable(scid ${SCID_SRC})
 endif()
+set_property(TARGET scid PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
+target_include_directories(scid PRIVATE ${TCL_INCLUDE_PATH})
+target_link_libraries(scid PRIVATE polyglot ${CMAKE_THREAD_LIBS_INIT} ${TCL_LIBRARY})
 
 option(SCID_USE_TB "Enable Nalimov tablebases" OFF)
 if(SCID_USE_TB)
@@ -61,11 +68,9 @@ if(SPELLCHKVALIDATE)
   target_compile_definitions(scid PRIVATE -DSPELLCHKVALIDATE)
 endif()
 
-find_package(TCL)
-target_include_directories(scid PRIVATE ${TCL_INCLUDE_PATH})
-target_link_libraries(scid polyglot ${TCL_LIBRARY})
-
 install(TARGETS scid DESTINATION scid)
+file(GLOB ECO_FILES *.eco)
+install(FILES ${ECO_FILES} DESTINATION scid)
 install(PROGRAMS shell_scid DESTINATION bin RENAME scid)
 install(DIRECTORY bitmaps DESTINATION scid)
 install(DIRECTORY bitmaps2 DESTINATION scid)
@@ -84,17 +89,6 @@ set_target_properties(phalanx-scid PROPE
 install(TARGETS phalanx-scid DESTINATION bin)
 
 
-# engine scidlet
-file(GLOB SCIDLET_SRC engines/scidlet/*.cpp)
-set(SCIDLET_EXTRA
-  src/engine.cpp
-  src/recog.cpp
-  src/misc.cpp
-  src/position.cpp
-)
-add_executable(scidlet ${SCIDLET_SRC} ${SCIDLET_EXTRA})
-install(TARGETS scidlet DESTINATION bin)
-
 option(GTEST "Build unit tests" OFF)
 if(GTEST)
   add_subdirectory(gtest)
diff -pruN 1:4.7.0+dfsg1-2/configure 1:4.7.4+dfsg1-2/configure
--- 1:4.7.0+dfsg1-2/configure	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/configure	2022-07-09 05:14:42.000000000 +0000
@@ -18,7 +18,7 @@ set var(COMPILE) g++
 set var(DEBUG) {-DNDEBUG}
 set var(OBJS) {$(SCIDOBJS)}
 set var(THREADS) {-pthread}
-set var(OPTIMIZE) {-std=c++14 -O3 -march=native -fno-rtti -fno-exceptions}
+set var(OPTIMIZE) {-std=c++17 -O3 -march=native -fno-rtti -fno-exceptions}
 set var(PROFILE) {}
 set var(SCIDFLAGS) {}
 set var(SHAREDIR) /usr/local/share/scid
@@ -280,7 +280,6 @@ proc usage {} {
     puts {  TCL_LIBRARY  Compiler directives for linking Tcl.}
     puts {  TCL_VERSION  Your Tcl/Tk version. Example: TCL_VERSION="8.3".}
     puts {  THREADS      Flags for c++11 threads. Default: "-pthread".}
-    puts {               Use THREADS="-DMULTITHREADING_OFF" to disable multi-threading.}
     puts {  WARNINGS     C++ compiler warnings. Default: "-Wall".}
     puts {  MAKEFILE     Makefile configuration file. Default: "Makefile.conf".}
     puts {}
diff -pruN 1:4.7.0+dfsg1-2/debian/changelog 1:4.7.4+dfsg1-2/debian/changelog
--- 1:4.7.0+dfsg1-2/debian/changelog	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/changelog	2022-07-30 16:46:08.000000000 +0000
@@ -1,3 +1,26 @@
+scid (1:4.7.4+dfsg1-2) unstable; urgency=medium
+
+  * debian/rules:
+    - Pass OPTIMIZE variable without '-march=native' value
+      to avoid build failures. Removed by mistake.
+
+ -- Jose G. López <josgalo@jglopez.name>  Sat, 30 Jul 2022 18:46:08 +0200
+
+scid (1:4.7.4+dfsg1-1) unstable; urgency=medium
+
+  * New upstream version.
+  * debian/control:
+    - Bump to Standards-Version 4.6.1. No changes required.
+  * debain/copyright:
+    - Update upstream and debian package copyright years.
+  * debian/patches:
+    - 02_uninitialized-memory-access.patch. Remove patch applied upstream.
+    - 01_Makefile.conf.diff. Refresh patch.
+  * debian/rules:
+    - Remove 'OPTIMIZE' option as it gets the same value from default.
+
+ -- Jose G. López <josgalo@jglopez.name>  Tue, 12 Jul 2022 20:22:12 +0200
+
 scid (1:4.7.0+dfsg1-2) unstable; urgency=medium
 
   * Use dh_python3 to calculate Python 3 dependencies
diff -pruN 1:4.7.0+dfsg1-2/debian/control 1:4.7.4+dfsg1-2/debian/control
--- 1:4.7.0+dfsg1-2/debian/control	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/control	2022-07-30 16:46:08.000000000 +0000
@@ -3,7 +3,7 @@ Section: games
 Priority: optional
 Maintainer: Jose G. López <josgalo@jglopez.name>
 Build-Depends: debhelper-compat (= 13), tcl-dev, tk-dev, zlib1g-dev, libx11-dev, dpkg-dev (>= 1.16.0), dh-python, python3
-Standards-Version: 4.5.1
+Standards-Version: 4.6.1
 Homepage: http://scid.sf.net
 Vcs-Git: https://salsa.debian.org/josgalo-guest/scid.git
 Vcs-Browser: https://salsa.debian.org/josgalo-guest/scid
diff -pruN 1:4.7.0+dfsg1-2/debian/copyright 1:4.7.4+dfsg1-2/debian/copyright
--- 1:4.7.0+dfsg1-2/debian/copyright	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/copyright	2022-07-30 16:46:08.000000000 +0000
@@ -10,7 +10,7 @@ Files-Excluded: books
 Files: *
 Copyright: Copyright 1999-2004 Shane Hudson
            Copyright 2006-2011 Pascal Georges
-	   Copyright 2009-2019 Fulvio Benini
+	   Copyright 2009-2022 Fulvio Benini
 License: GPL-2.0
 
 Files: scripts/sc_remote.tk
@@ -34,7 +34,7 @@ Comment: Derived from original TrueType
 
 Files: src/sortcache.h src/sortcache.cpp
 Copyright: Copyright (C) 2011  Gerd Lorscheid
-           Copyright (C) 2011-2015  Fulvio Benini
+           Copyright (C) 2011-2017  Fulvio Benini
 License: GPL-2.0
 
 Files: img/buttons/*
@@ -120,7 +120,7 @@ License: GPL-2.0
 Files: debian/*
 Copyright: 2001-2008, Peter van Rossum <petervr@debian.org>
            2009-2014, Oliver Korff <ok@xynyx.de>
-           2016-2019, Jose G. López <josgalo@jglopez.name>
+           2016-2022, Jose G. López <josgalo@jglopez.name>
 License: GPL-2.0
 
 License: GPL-2.0
diff -pruN 1:4.7.0+dfsg1-2/debian/patches/01_Makefile.conf.diff 1:4.7.4+dfsg1-2/debian/patches/01_Makefile.conf.diff
--- 1:4.7.0+dfsg1-2/debian/patches/01_Makefile.conf.diff	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/patches/01_Makefile.conf.diff	2022-07-30 16:46:08.000000000 +0000
@@ -2,7 +2,7 @@ Description: patching Makefile.conf
  Makefile.conf has to be adjusted for the debian distribution
 Author: Oliver Korff <ok@xynyx.de>
 Reviewed-by: Jose G. López <josgalo@jglopez.name>
-Last-Update: 2019-07-27
+Last-Update: 2022-07-12
 --- a/Makefile.conf
 +++ b/Makefile.conf
 @@ -49,11 +49,11 @@
@@ -50,7 +50,7 @@ Last-Update: 2019-07-27
  
  
  ifeq ($(INSTALL),install_mac)
-@@ -148,15 +148,17 @@
+@@ -145,15 +145,17 @@
  install_scid: all_scid
  	install -m 755 -d "$(DESTDIR)$(SHAREDIR)"
  	install -m 755 -d "$(DESTDIR)$(BINDIR)"
@@ -76,7 +76,7 @@ Last-Update: 2019-07-27
  	install -m 755 -d "$(DESTDIR)$(SHAREDIR)/bases"
  	if test -d ./bases; then install -m 666 ./bases/*.* "$(DESTDIR)$(SHAREDIR)/bases/" ; fi
  	install -m 755 -d "$(DESTDIR)$(SHAREDIR)/html"
-@@ -242,7 +244,7 @@
+@@ -239,7 +241,7 @@
  clean:
  	rm -f scidlet scidlet.d src/*.o src/polyglot/*.o $(EXECS) scid $(SCRIPTS)
  	rm -f src/*.d src/polyglot/*.d
@@ -85,7 +85,7 @@ Last-Update: 2019-07-27
  	rm -Rf dist
  
  ### To make the executable files smaller: type "make strip".
-@@ -315,7 +317,7 @@
+@@ -312,7 +314,7 @@
  
  
  tkscid: $(OBJS) $(XOBJS)
diff -pruN 1:4.7.0+dfsg1-2/debian/patches/02_uninitialized-memory-access.patch 1:4.7.4+dfsg1-2/debian/patches/02_uninitialized-memory-access.patch
--- 1:4.7.0+dfsg1-2/debian/patches/02_uninitialized-memory-access.patch	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/patches/02_uninitialized-memory-access.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,42 +0,0 @@
-Author: Bernhard Übelacker <bernhardu@mailbox.org>
-Author: Fulvio Benini <fulvioscid@users.sf.net>
-Date: Tue, 8 Jan 2019 18:09:02 +0100
-Subject: Avoid crash because of uninitialized memory access
-Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=918696
-Reviewed-by: Jose G. López <josgalo@gmail.com>
-Last-Update: Mon, 22 Jul 2019 20:04:10 +0200
-Bug: https://sourceforge.net/p/scid/bugs/124/
-Applied-Upstream: https://sourceforge.net/p/scid/code/ci/771d117df70b35f32b93d437b612359680a9a5cc/
----
- src/engine.cpp | 2 ++
- src/engine.h   | 1 +
- 2 files changed, 3 insertions(+)
-
---- a/src/engine.cpp
-+++ b/src/engine.cpp
-@@ -1095,6 +1095,7 @@
-       if (TranTable != NULL) { delete[] TranTable; }
-       TranTable = new transTableEntryT [TranTableSize];
- #endif
-+      memset(TranTable, 0, sizeof(*TranTable)*TranTableSize);
-     }
-     ClearHashTable();
- }
-@@ -1117,6 +1118,7 @@
-       if (PawnTable != NULL) { delete[] PawnTable; }
-       PawnTable = new pawnTableEntryT [PawnTableSize];
- #endif
-+      memset(PawnTable, 0, sizeof(*PawnTable)*PawnTableSize);
-     }
-     ClearPawnTable();
- }
---- a/src/engine.h
-+++ b/src/engine.h
-@@ -315,6 +315,7 @@
- inline void
- Engine::ClearKillerMoves (void)
- {
-+    memset(KillerMove, 0, sizeof(KillerMove));
-     for (uint i=0; i < ENGINE_MAX_PLY; i++) {
-         KillerMove[i][0].from = NULL_SQUARE;
-         KillerMove[i][1].from = NULL_SQUARE;
diff -pruN 1:4.7.0+dfsg1-2/debian/patches/series 1:4.7.4+dfsg1-2/debian/patches/series
--- 1:4.7.0+dfsg1-2/debian/patches/series	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/patches/series	2022-07-30 16:46:08.000000000 +0000
@@ -1,5 +1,4 @@
 03_pngfix-python3.patch
-02_uninitialized-memory-access.patch
 01_Makefile.conf.diff
 01_tcl_syntax.diff
 01_datafolder.diff
diff -pruN 1:4.7.0+dfsg1-2/debian/rules 1:4.7.4+dfsg1-2/debian/rules
--- 1:4.7.0+dfsg1-2/debian/rules	2020-12-11 17:27:27.000000000 +0000
+++ 1:4.7.4+dfsg1-2/debian/rules	2022-07-30 16:46:08.000000000 +0000
@@ -11,7 +11,8 @@ export DEB_LDFLAGS_MAINT_APPEND = -Wl,--
 override_dh_auto_configure:
 	./configure BINDIR="$(CURDIR)/debian/tmp/scid/usr/games" \
 		SHAREDIR="$(CURDIR)/debian/tmp/scid/usr/share/scid" \
-		OPTIMIZE="-O3 -fno-rtti -fno-exceptions" TB=""
+		OPTIMIZE="-std=c++17 -O3 -fno-rtti -fno-exceptions" \
+		TB=""
 
 override_dh_auto_install:
 	$(MAKE) install_scid
diff -pruN 1:4.7.0+dfsg1-2/Doxyfile 1:4.7.4+dfsg1-2/Doxyfile
--- 1:4.7.0+dfsg1-2/Doxyfile	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/Doxyfile	2022-07-09 05:14:42.000000000 +0000
@@ -38,7 +38,7 @@ PROJECT_NAME           = Scid
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 4.7.0
+PROJECT_NUMBER         = 4.7.4
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff -pruN 1:4.7.0+dfsg1-2/.gitignore 1:4.7.4+dfsg1-2/.gitignore
--- 1:4.7.0+dfsg1-2/.gitignore	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/.gitignore	2022-07-09 05:14:42.000000000 +0000
@@ -15,6 +15,10 @@ Makefile.bak
 .\#*
 *.patch
 
+.cache
+.vs
+.vscode
+
 engines/phalanx-scid/phalanx-scid
 engines/togaII1.2.1a/src/.depend
 engines/togaII1.2.1a/src/togaII
@@ -36,6 +40,7 @@ spf2spi
 spliteco
 tcscid
 tkscid
+build/
 Debug
 Release
 Testing
diff -pruN 1:4.7.0+dfsg1-2/gtest/CMakeLists.txt 1:4.7.4+dfsg1-2/gtest/CMakeLists.txt
--- 1:4.7.0+dfsg1-2/gtest/CMakeLists.txt	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/CMakeLists.txt	2022-07-09 05:14:42.000000000 +0000
@@ -14,7 +14,7 @@
 # along with Scid. If not, see <http://www.gnu.org/licenses/>.
 
 cmake_minimum_required(VERSION 3.2)
-set(CMAKE_CXX_STANDARD 14 CACHE STRING "")
+set(CMAKE_CXX_STANDARD 17 CACHE STRING "")
 
 # googletest
 if(NOT IS_DIRECTORY "${CMAKE_BINARY_DIR}/googletest")
@@ -27,20 +27,15 @@ add_subdirectory(${CMAKE_BINARY_DIR}/goo
 # scid_sources
 set(SCID_BASE
   ../src/codec_scid4.cpp
-  ../src/index.cpp
   ../src/scidbase.cpp
   ../src/sortcache.cpp
   ../src/stored.cpp
-  ../src/game.cpp ../src/position.cpp ../src/bytebuf.cpp ../src/textbuf.cpp ../src/misc.cpp
+  ../src/game.cpp ../src/position.cpp ../src/textbuf.cpp ../src/misc.cpp
 )
 add_library(scid_base ${SCID_BASE})
 target_include_directories(scid_base PUBLIC ../src)
-if(SCID_MULTITHREADING)
-  find_package(Threads REQUIRED)
-  target_link_libraries(scid_base PUBLIC ${CMAKE_THREAD_LIBS_INIT})
-else()
-  target_compile_definitions(scid_base PUBLIC -DMULTITHREADING_OFF)
-endif()
+find_package(Threads REQUIRED)
+target_link_libraries(scid_base PUBLIC ${CMAKE_THREAD_LIBS_INIT})
 
 # scid_tests
 file(GLOB GTEST_SRC *.cpp)
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_codec.cpp 1:4.7.4+dfsg1-2/gtest/test_codec.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_codec.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_codec.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -30,32 +30,26 @@
 
 namespace {
 
-fileModeT fmodes[] = {
-	FMODE_Memory, FMODE_Create, FMODE_ReadOnly, FMODE_WriteOnly, FMODE_Both
-};
+fileModeT fmodes[] = {FMODE_Create, FMODE_ReadOnly, FMODE_WriteOnly,
+                      FMODE_Both};
 const char* filename = "codecbase";
 
-ICodecDatabase::Codec codecs[] = {
-	ICodecDatabase::MEMORY, ICodecDatabase::SCID4, ICodecDatabase::PGN
-};
+ICodecDatabase::Codec codecs[] = {ICodecDatabase::MEMORY, ICodecDatabase::SCID4,
+                                  ICodecDatabase::PGN};
 
-std::vector<std::pair<ICodecDatabase::Codec, std::string> > unsupportedVec = {
-	{ ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_None) },
-	{ ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_Create) },
-	{ ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_ReadOnly) },
-	{ ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_WriteOnly) },
-	{ ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_Both) },
-
-	{ ICodecDatabase::SCID4, "FMODE" + std::to_string(FMODE_None) },
-	{ ICodecDatabase::SCID4, "FMODE" + std::to_string(FMODE_Memory) },
-	{ ICodecDatabase::SCID4, "FMODE" + std::to_string(FMODE_WriteOnly) },
-	{ ICodecDatabase::SCID4, "empty_filename" },
-
-	{ ICodecDatabase::PGN, "FMODE" + std::to_string(FMODE_None) },
-	{ ICodecDatabase::PGN, "FMODE" + std::to_string(FMODE_Memory) },
-	{ ICodecDatabase::PGN, "saveGame_game" },
-	{ ICodecDatabase::PGN, "empty_filename" }
-};
+std::vector<std::pair<ICodecDatabase::Codec, std::string>> unsupportedVec = {
+    {ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_None)},
+    {ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_ReadOnly)},
+    {ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_WriteOnly)},
+    {ICodecDatabase::MEMORY, "FMODE" + std::to_string(FMODE_Both)},
+
+    {ICodecDatabase::SCID4, "FMODE" + std::to_string(FMODE_None)},
+    {ICodecDatabase::SCID4, "FMODE" + std::to_string(FMODE_WriteOnly)},
+    {ICodecDatabase::SCID4, "empty_filename"},
+
+    {ICodecDatabase::PGN, "FMODE" + std::to_string(FMODE_None)},
+    {ICodecDatabase::PGN, "saveGame_game"},
+    {ICodecDatabase::PGN, "empty_filename"}};
 
 class Supports {
 	ICodecDatabase::Codec dbtype_;
@@ -70,11 +64,10 @@ public:
 	}
 };
 
-template <int nGames, int maxMoves, int maxCommentLen>
-class GameGenerator {
-	typedef std::vector<std::unique_ptr<Game> > Vec;
+template <int nGames, int maxMoves, int maxCommentLen> class GameGenerator {
+	typedef std::vector<std::unique_ptr<Game>> Vec;
 	Vec v_;
-	std::vector<std::vector<byte> > encoded_;
+	std::vector<std::vector<byte>> encoded_;
 	std::mt19937 mt_;
 
 public:
@@ -88,7 +81,7 @@ public:
 		return v_;
 	}
 
-	const std::vector<std::vector<byte> >& getNative() {
+	const std::vector<std::vector<byte>>& getNative() {
 		if (encoded_.empty())
 			get();
 
@@ -101,22 +94,19 @@ public:
 		int g = 0;
 		for (auto& game : encoded) {
 			auto entry = idx.GetEntry(g++);
-			auto data = codec->getGameData(entry->GetOffset(), entry->GetLength());
-			ASSERT_NE(nullptr, data);
+			auto data = codec->getGameData(entry->GetOffset(),
+			                               entry->GetLength());
+			ASSERT_TRUE(data);
 			ASSERT_EQ(game.size(), entry->GetLength());
-			EXPECT_TRUE(std::equal(data, data + entry->GetLength(), game.data()));
+			EXPECT_TRUE(std::equal(
+			    data.data(), data.data() + entry->GetLength(), game.data()));
 		}
 	}
 
 private:
 	void encodeGames() {
-		static ByteBuffer buf(128 * 1024);
 		for (auto& game : v_) {
-			ASSERT_EQ(OK, game->Encode(&buf, NULL));
-			encoded_.emplace_back();
-			auto& v = encoded_.back();
-			v.insert(v.end(), buf.getData(),
-			         buf.getData() + buf.GetByteCount());
+			game->Encode(encoded_.emplace_back());
 		}
 	}
 
@@ -125,10 +115,14 @@ private:
 		res->GetCurrentPos()->StdStart();
 		MoveList mlist;
 		for (auto i = rand(0, maxMoves); i > 0; --i) {
-			res->GetCurrentPos()->GenerateMoves(&mlist, EMPTY, GEN_ALL_MOVES, true);
+			res->GetCurrentPos()->GenerateMoves(&mlist, EMPTY, GEN_ALL_MOVES,
+			                                    true);
 			if (mlist.Size() == 0)
 				break;
-			res->AddMove(mlist.Get(rand(0, mlist.Size() - 1)));
+
+			auto move = *mlist.Get(rand(0, mlist.Size() - 1));
+			res->GetCurrentPos()->fillMove(move);
+			res->AddMove(move);
 
 			if (rand(0, 6) == 0)
 				res->SetMoveComment(rand_comment().c_str());
@@ -152,9 +146,8 @@ private:
 		size_t len = rand(0, maxCommentLen);
 		std::string res(len, ' ');
 		std::uniform_int_distribution<int> dist{33, 122};
-		std::generate_n(res.begin(), res.size(), [&] () {
-			return static_cast<char>(dist(mt_));
-		});
+		std::generate_n(res.begin(), res.size(),
+		                [&]() { return static_cast<char>(dist(mt_)); });
 		return res;
 	}
 };
@@ -167,10 +160,6 @@ void makeDatabase(ICodecDatabase::Codec
 		return;
 
 	fileModeT fMode = FMODE_Create;
-	if (!supports("FMODE" + std::to_string(fMode))) {
-		fMode = FMODE_Memory;
-		ASSERT_TRUE(supports("FMODE" + std::to_string(fMode)));
-	}
 
 	struct Cleanup {
 		std::vector<std::string> filenames;
@@ -217,56 +206,31 @@ void makeDatabase(ICodecDatabase::Codec
 
 class Test_Codec : public ::testing::TestWithParam<ICodecDatabase::Codec> {};
 
-TEST_P(Test_Codec, addGame_game) {
-	makeDatabase(GetParam(), "addGame_game",
-	             [](ICodecDatabase* codec, Index&, NameBase&) {
-		for (auto& game : gameGenerator.get()) {
-			ASSERT_EQ(OK, codec->addGame(game.get()));
-		}
-	});
-}
-
 TEST_P(Test_Codec, addGame_native) {
 	makeDatabase(GetParam(), "addGame_native",
 	             [](ICodecDatabase* codec, Index&, NameBase& nb) {
-		for (const auto& game : gameGenerator.getNative()) {
-			std::pair<errorT, idNumberT> names[] = {
-			    nb.addName(NAME_PLAYER, "Dummy White", 255, 1000),
-			    nb.addName(NAME_PLAYER, "Dummy Black", 255, 1000),
-			    nb.addName(NAME_EVENT, "Dummy Event", 255, 1000),
-			    nb.addName(NAME_SITE, "Dummy Site", 255, 1000),
-			    nb.addName(NAME_ROUND, "Dummy Round", 255, 1000)};
-			for (auto& e : names)
-				ASSERT_EQ(OK, e.first);
-
-			IndexEntry ie;
-			std::memset(&ie, 0, sizeof(IndexEntry));
-			ie.SetWhite(names[0].second);
-			ie.SetBlack(names[1].second);
-			ie.SetEvent(names[2].second);
-			ie.SetSite(names[3].second);
-			ie.SetRound(names[4].second);
-			ASSERT_EQ(OK, codec->addGame(&ie, &nb, game.data(), game.size()));
-		}
-	});
-}
-
-TEST_P(Test_Codec, saveGame_game) {
-	makeDatabase(GetParam(), "saveGame_game",
-	             [](ICodecDatabase* codec, Index&, NameBase&) {
-		const auto& games = gameGenerator.get();
-		for (size_t i = 0, n = games.size(); i < n; ++i) {
-			ASSERT_EQ(OK, codec->addGame(games[0].get()));
-		}
-		codec->flush();
-
-		std::vector<int> randIdx(games.size());
-		std::iota(randIdx.begin(), randIdx.end(), 0);
-		std::shuffle(randIdx.begin(), randIdx.end(), std::mt19937());
-		for (auto idx : randIdx) {
-			ASSERT_EQ(OK, codec->saveGame(games[idx].get(), idx));
-		}
-	});
+		             for (const auto& game : gameGenerator.getNative()) {
+			             std::pair<errorT, idNumberT> names[] = {
+			                 nb.addName(NAME_PLAYER, "Dummy White", 255, 1000),
+			                 nb.addName(NAME_PLAYER, "Dummy Black", 255, 1000),
+			                 nb.addName(NAME_EVENT, "Dummy Event", 255, 1000),
+			                 nb.addName(NAME_SITE, "Dummy Site", 255, 1000),
+			                 nb.addName(NAME_ROUND, "Dummy Round", 255, 1000)};
+			             for (auto& e : names)
+				             ASSERT_EQ(OK, e.first);
+
+			             IndexEntry ie;
+			             std::memset(&ie, 0, sizeof(IndexEntry));
+			             ie.SetWhite(names[0].second);
+			             ie.SetBlack(names[1].second);
+			             ie.SetEvent(names[2].second);
+			             ie.SetSite(names[3].second);
+			             ie.SetRound(names[4].second);
+			             ASSERT_EQ(OK,
+			                       codec->addGame(ie, TagRoster::make(ie, nb),
+			                                      {game.data(), game.size()}));
+		             }
+	             });
 }
 
 // Try to get a ICodecDatabase pointer for each supported file mode, then test
@@ -316,8 +280,8 @@ TEST_P(Test_Codec, create_emptyfilename)
 
 	Index idx;
 	NameBase nb;
-	auto err =
-	    ICodecDatabase::open(dbtype, FMODE_Create, "", Progress(), &idx, &nb);
+	auto err = ICodecDatabase::open(dbtype, FMODE_Create, "", Progress(), &idx,
+	                                &nb);
 	auto codec = std::unique_ptr<ICodecDatabase>(err.first);
 
 	if (!supports("empty_filename")) {
@@ -330,8 +294,9 @@ TEST_P(Test_Codec, create_emptyfilename)
 	}
 }
 
-// Creates two databases; remove the first one and rename the second to the first.
-// This test mimic the process perfomed to finalize the compaction of a database.
+// Creates two databases; remove the first one and rename the second to the
+// first. This test mimic the process perfomed to finalize the compaction of a
+// database.
 TEST_P(Test_Codec, rename) {
 	ICodecDatabase::Codec dbtype = GetParam();
 	Supports supports(dbtype);
@@ -398,4 +363,5 @@ TEST_P(Test_Codec, rename) {
 	}
 }
 
-INSTANTIATE_TEST_CASE_P(CodecDatabase, Test_Codec, ::testing::ValuesIn(codecs));
+INSTANTIATE_TEST_SUITE_P(CodecDatabase, Test_Codec,
+                         ::testing::ValuesIn(codecs));
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_containers.cpp 1:4.7.4+dfsg1-2/gtest/test_containers.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_containers.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_containers.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -14,10 +14,13 @@
  * along with Scid. If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "bytebuf.h"
 #include "containers.h"
 #include <algorithm>
 #include <cmath>
+#include <cstring>
 #include <gtest/gtest.h>
+#include <vector>
 
 namespace {
 
@@ -34,6 +37,8 @@ public:
 		++nObjects;
 	}
 
+	RefCounted& operator=(const RefCounted&) = default;
+
 	RefCounted* clone() { return new RefCounted(*this); }
 
 	~RefCounted() { --nObjects; }
@@ -81,7 +86,7 @@ TEST(Test_Containers, VectorChunked) {
 		auto contiguous = v.contiguous(i);
 		RefCounted* it = &v[i];
 		for (size_t j = 0; j < contiguous; j++) {
-			(*it++).ch[0] = (char) i;
+			(*it++).ch[0] = (char)i;
 			++i;
 		}
 	}
@@ -169,4 +174,19 @@ TEST(Test_Containers, UndoRedo) {
 	EXPECT_EQ('a', cur->ch[0]);
 
 	delete cur;
-}
\ No newline at end of file
+}
+
+TEST(Test_Containers, ByteBuffer_GetTerminatedString) {
+	const char* test_data[] = {"abcd", "", "efg"};
+	auto v = [&] {
+		std::vector<char> res;
+		for (auto str : test_data) {
+			res.insert(res.end(), str, str + std::strlen(str) + 1);
+		}
+		return res;
+	}();
+	ByteBuffer buf(reinterpret_cast<unsigned char*>(v.data()), v.size());
+	for (auto str : test_data) {
+		EXPECT_STREQ(str, buf.GetTerminatedString());
+	}
+}
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_decodemove.cpp 1:4.7.4+dfsg1-2/gtest/test_decodemove.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_decodemove.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_decodemove.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Fulvio Benini
+ * Copyright (C) 2020 Fulvio Benini
  *
  * Scid is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,461 +15,155 @@
  */
 
 #include "bytebuf.h"
-#include "fastgame.h"
-#include "movelist.h"
 #include <gtest/gtest.h>
 
-namespace testdecode_legacy {
-
-inline errorT decodeKing(byte val, simpleMoveT* sm) {
-	static const int sqdiff[] = {0, -9, -8, -7, -1, 1, 7, 8, 9, -2, 2};
-
-	if (val == 0) {
-		sm->to = sm->from; // Null move
-		return OK;
-	}
-
-	if (val < 1 || val > 10) {
-		return ERROR_Decode;
-	}
-	sm->to = sm->from + sqdiff[val];
-	return OK;
-}
-
-inline errorT decodeKnight(byte val, simpleMoveT* sm) {
-	static const int sqdiff[] = {0, -17, -15, -10, -6, 6, 10, 15, 17};
-	if (val < 1 || val > 8) {
-		return ERROR_Decode;
-	}
-	sm->to = sm->from + sqdiff[val];
-	return OK;
-}
-
-inline errorT decodeRook(byte val, simpleMoveT* sm) {
-	if (val >= 8) {
-		// This is a move along a Fyle, to a different rank:
-		sm->to = square_Make(square_Fyle(sm->from), (val - 8));
-	} else {
-		sm->to = square_Make(val, square_Rank(sm->from));
-	}
-	return OK;
-}
-
-inline errorT decodeBishop(byte val, simpleMoveT* sm) {
-	byte fyle = (val & 7);
-	int fylediff = (int)fyle - (int)square_Fyle(sm->from);
-	if (val >= 8) {
-		// It is an up-left/down-right direction move.
-		sm->to = sm->from - 7 * fylediff;
-	} else {
-		sm->to = sm->from + 9 * fylediff;
-	}
-	if (sm->to > H8) {
-		return ERROR_Decode;
-	}
-	return OK;
-}
-
-inline errorT decodeQueen(ByteBuffer* buf, byte val, simpleMoveT* sm) {
-	if (val >= 8) {
-		// Rook-vertical move:
-		sm->to = square_Make(square_Fyle(sm->from), (val - 8));
-
-	} else if (val != square_Fyle(sm->from)) {
-		// Rook-horizontal move:
-		sm->to = square_Make(val, square_Rank(sm->from));
-
-	} else {
-		// Diagonal move: coded in TWO bytes.
-		val = buf->GetByte();
-		if (val < 64 || val > 127) {
-			return ERROR_Decode;
-		}
-		sm->to = val - 64;
-	}
-	return OK;
-}
-
-inline errorT decodePawn(byte val, simpleMoveT* sm, colorT toMove) {
-	static const int toSquareDiff[16] = {7, 8, 9, 7, 8, 9, 7, 8,
-	                                     9, 7, 8, 9, 7, 8, 9, 16};
-
-	static const pieceT promoPieceFromVal[16] = {
-	    EMPTY, EMPTY,  EMPTY,  QUEEN,  QUEEN,  QUEEN,  ROOK,   ROOK,
-	    ROOK,  BISHOP, BISHOP, BISHOP, KNIGHT, KNIGHT, KNIGHT, EMPTY};
-
-	if (toMove == WHITE) {
-		sm->to = sm->from + toSquareDiff[val];
-	} else {
-		sm->to = sm->from - toSquareDiff[val];
+TEST(Test_decodeMove, pawn_white) {
+	const squareT from = C2;
+	std::tuple<unsigned char, int, pieceT> data[] = {
+	    {0, from + 7, INVALID_PIECE}, {1, from + 8, INVALID_PIECE},
+	    {2, from + 9, INVALID_PIECE}, {3, from + 7, QUEEN},
+	    {4, from + 8, QUEEN},         {5, from + 9, QUEEN},
+	    {6, from + 7, ROOK},          {7, from + 8, ROOK},
+	    {8, from + 9, ROOK},          {9, from + 7, BISHOP},
+	    {10, from + 8, BISHOP},       {11, from + 9, BISHOP},
+	    {12, from + 7, KNIGHT},       {13, from + 8, KNIGHT},
+	    {14, from + 9, KNIGHT},       {15, from + 16, INVALID_PIECE},
+	};
+	ByteBuffer bbuf(nullptr, 0);
+	for (auto [moveCode, expTo, expPromo] : data) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, PAWN, from, moveCode);
+		EXPECT_EQ(to, expTo);
+		EXPECT_EQ(promo, expPromo);
 	}
-
-	sm->promote = promoPieceFromVal[val];
-
-	return OK;
 }
 
-} // namespace testdecode_legacy
-
-namespace testdecode_generic {
-
-inline int decodePieceIdx(byte v) { return v >> 4; }
-
-/**
- * Decode a SCID4's move
- * @from: start square of the moving piece
- * @val: index of the target square
- *
- * Excluding queens, the other chess pieces cannot reach more than 16 target
- * squares from any given position. This allow to store the target square of
- * a move into 4 bits, as an index of all the possible target squares.
- * Return:
- * - the target square
- * Error handling:
- * - Debug code will check if the decoded value is a valid [0-63] square.
- * - Release code will force the returned valid to be a valid [0-63] square
- *   but, for performance reasons, do not report invalid encoded moves.
- */
-template <typename TOpSimple, typename TOp2Bytes, typename TOpPawn,
-          typename TOpCastle, typename TOpNull>
-auto decodeMove(pieceT piece_type, squareT from, byte move,
-                TOpSimple move_simple, TOp2Bytes move_2bytes, TOpPawn move_pawn,
-                TOpCastle move_castle, TOpNull move_null) {
-	move &= 0x0F;
-	switch (piece_type) {
-	case BISHOP: {
-		auto offset = square_Fyle(move) - square_Fyle(from);
-		offset *= (move >= 8) ? -7 : 9;
-		return move_simple(from + offset);
-	}
-	case KING: {
-		if (move == 0) { // null move
-			return move_null();
-		}
-		if (move == 9) { // castle queen side
-			return move_castle(false);
-		}
-		if (move == 10) { // castle king side
-			return move_castle(true);
-		}
-		static constexpr int8_t king_mask[] = {0, -9, -8, -7, -1, 1, 7, 8,
-		                                       9, 0,  0,  0,  0,  0, 0, 0};
-		return move_simple(from + king_mask[move]);
-	}
-	case KNIGHT: {
-		static constexpr int8_t knight_mask[] = {
-		    0, -17, -15, -10, -6, 6, 10, 15, 17, 0, 0, 0, 0, 0, 0, 0};
-		return move_simple(from + knight_mask[move]);
-	}
-	case QUEEN:
-		if (move == square_Fyle(from)) {
-			return move_2bytes();
-		}
-		/* FALLTHRU */
-	case ROOK:
-		if (move >= 8) { // This is a move along a Fyle, to a different rank:
-			return move_simple(square_Make(square_Fyle(from), (move - 8)));
-		} else {
-			return move_simple(square_Make(move, square_Rank(from)));
-		}
+TEST(Test_decodeMove, pawn_black) {
+	const squareT from = D7;
+	std::tuple<unsigned char, int, pieceT> data[] = {
+	    {0, from - 7, INVALID_PIECE}, {1, from - 8, INVALID_PIECE},
+	    {2, from - 9, INVALID_PIECE}, {3, from - 7, QUEEN},
+	    {4, from - 8, QUEEN},         {5, from - 9, QUEEN},
+	    {6, from - 7, ROOK},          {7, from - 8, ROOK},
+	    {8, from - 9, ROOK},          {9, from - 7, BISHOP},
+	    {10, from - 8, BISHOP},       {11, from - 9, BISHOP},
+	    {12, from - 7, KNIGHT},       {13, from - 8, KNIGHT},
+	    {14, from - 9, KNIGHT},       {15, from - 16, INVALID_PIECE},
 	};
-
-	ASSERT(piece_type == PAWN);
-	static const int8_t pawn_mask[] = {7, 8, 9, 7, 8, 9, 7, 8,
-	                                   9, 7, 8, 9, 7, 8, 9, 16};
-	static const pieceT pawn_promo[] = {
-	    INVALID_PIECE, INVALID_PIECE, INVALID_PIECE, QUEEN,
-	    QUEEN,         QUEEN,         ROOK,          ROOK,
-	    ROOK,          BISHOP,        BISHOP,        BISHOP,
-	    KNIGHT,        KNIGHT,        KNIGHT,        INVALID_PIECE};
-	bool maybe_enpassant = (move == 0 || move == 2);
-	return move_pawn(pawn_mask[move], pawn_promo[move], maybe_enpassant);
-}
-
-inline int decode2ndByte(byte val) { return val - 64; }
-
-} // namespace testdecode_generic
-
-namespace testdecode_fastgame {
-
-static inline int decodeKing(squareT from, byte val) {
-	ASSERT(val <= 8);
-	static const int8_t sqdiff[] = {0, -9, -8, -7, -1, 1, 7, 8, 9};
-	return from + sqdiff[val];
-}
-static inline int decodeQueen2byte(byte val) { return val - 64; }
-static inline int decodeBishop(squareT from, byte val) {
-	int fylediff = square_Fyle(val) - square_Fyle(from);
-	return (val >= 8) ? from - 7 * fylediff //
-	                  : from + 9 * fylediff;
-}
-static inline int decodeKnight(squareT from, byte val) {
-	ASSERT(val <= 16);
-	static const int8_t sqdiff[] = {0,  -17, -15, -10, -6, 6, 10, 15,
-	                                17, 0,   0,   0,   0,  0, 0,  0};
-	return from + sqdiff[val];
-}
-static inline int decodeRook(squareT from, byte val) {
-	ASSERT(val <= 16);
-	if (val >= 8) // vertical move
-		return square_Make(square_Fyle(from), (val - 8));
-	else // horizontal move
-		return square_Make(val, square_Rank(from));
-}
-static inline int decodePawn(colorT color, squareT from, byte val,
-                             pieceT& promo, bool& enPassant) {
-	ASSERT(val <= 16);
-	static const int8_t sqdiff[] = {7, 8, 9, 7, 8, 9, 7, 8,
-	                                9, 7, 8, 9, 7, 8, 9, 16};
-	static const pieceT promoPieceFromVal[] = {
-	    0,    0,      0,      QUEEN,  QUEEN,  QUEEN,  ROOK,   ROOK,
-	    ROOK, BISHOP, BISHOP, BISHOP, KNIGHT, KNIGHT, KNIGHT, 0};
-	promo = promoPieceFromVal[val];
-	enPassant = (val == 0 || val == 2);
-	return (color == WHITE) ? from + sqdiff[val] //
-	                        : from - sqdiff[val];
-}
-
-class make_simpleMoveT {
-	simpleMoveT sm_;
-
-public:
-	make_simpleMoveT() { sm_.clear(); }
-
-	make_simpleMoveT(colorT c, squareT from, squareT to, pieceT pt) {
-		sm_.clear();
-		sm_.movingPiece = piece_Make(c, pt);
-		sm_.from = from;
-		sm_.to = to;
-		sm_.promote = EMPTY;
-	}
-
-	void setPromo(pieceT promo) { sm_.promote = promo; }
-
-	operator bool() const { return sm_.promote != INVALID_PIECE; }
-	const simpleMoveT& data() const { return sm_; }
-};
-
-template <typename TResult, typename TBuf>
-TResult decodeMove(colorT toMove, pieceT moving_piece, squareT from, byte move,
-                   TBuf& buf) {
-	move &= 0x0F;
-
-	int to;
-	pieceT promo = INVALID_PIECE;
-	bool enPassant = false;
-	switch (moving_piece) {
-	case PAWN:
-		to = decodePawn(toMove, from, move, promo, enPassant);
-		break;
-	case BISHOP:
-		to = decodeBishop(from, move);
-		break;
-	case KNIGHT:
-		to = decodeKnight(from, move);
-		if (from == to)
-			return {}; // decode error
-
-		break;
-	case QUEEN:
-		if (move == square_Fyle(from)) { // 2 BYTES MOVE
-			to = decodeQueen2byte(buf.GetByte());
-			break;
-		}
-		/* FALLTHRU */
-	case ROOK:
-		to = decodeRook(from, move);
-		break;
-	case KING:
-		if (move == 0) { // NULL MOVE
-			to = from;
-			break;
-		}
-		if (move <= 8) {
-			to = decodeKing(from, move);
-			break;
-		}
-		if (move <= 10) { // CASTLE
-			to = from + (move == 9 ? -2 : 2);
-			break;
-		}
-		return {}; // decode error
-
-	default:
-		return {}; // decode error
+	ByteBuffer bbuf(nullptr, 0);
+	for (auto [moveCode, expTo, expPromo] : data) {
+		auto [to, promo] = bbuf.decodeMove(BLACK, PAWN, from, moveCode);
+		EXPECT_EQ(to, expTo);
+		EXPECT_EQ(promo, expPromo);
 	}
-
-	if (to < 0 || to > 63)
-		return {}; // decode error
-
-	TResult res(toMove, from, to, moving_piece);
-	if (promo != INVALID_PIECE)
-		res.setPromo(promo);
-	return res;
 }
 
-} // namespace testdecode_fastgame
-
-TEST(Test_decodemove, legacy_generic) {
-	ByteBuffer buf(1024);
-	ByteBuffer buf2(1024);
-	for (unsigned i = 0; i < 1024; ++i) {
-		buf.PutByte(static_cast<byte>(i));
-		buf2.PutByte(static_cast<byte>(i));
-	}
-	buf.BackToStart();
-	buf2.BackToStart();
-
-	for (const auto piece : {WP, BP, WN, WR, WB, WK, WQ}) {
-		const colorT toMove = piece_Color(piece);
-		for (squareT from = A1; from <= H8; ++from) {
-			for (byte val = 0; val <= 16; ++val) {
-				simpleMoveT sm_old{};
-				sm_old.movingPiece = piece;
-				sm_old.from = from;
-				sm_old.promote = EMPTY;
-				errorT err = ERROR_Decode;
-				switch (piece_Type(sm_old.movingPiece)) {
-				case PAWN:
-					err = testdecode_legacy::decodePawn(val & 15, &sm_old,
-					                                    toMove);
-					break;
-				case KNIGHT:
-					err = testdecode_legacy::decodeKnight(val & 15, &sm_old);
-					break;
-				case ROOK:
-					err = testdecode_legacy::decodeRook(val & 15, &sm_old);
-					break;
-				case BISHOP:
-					err = testdecode_legacy::decodeBishop(val & 15, &sm_old);
-					break;
-				case KING:
-					err = testdecode_legacy::decodeKing(val & 15, &sm_old);
-					break;
-				case QUEEN:
-					err = testdecode_legacy::decodeQueen(&buf2, val & 15,
-					                                     &sm_old);
-				}
-
-				simpleMoveT sm{};
-				sm.movingPiece = piece;
-				sm.from = from;
-				sm.promote = EMPTY;
-				auto res = testdecode_generic::decodeMove( //
-				    piece_Type(sm.movingPiece), sm.from, val,
-				    [&](int dest_sq) {
-					    if (dest_sq == sm.from) {
-						    if (sm.movingPiece == BISHOP ||
-						        sm.movingPiece == QUEEN ||
-						        sm.movingPiece == ROOK) {
-							    // The legacy code allows equal "from" and
-							    // "to" squares for BISHOPs, QUEEN and ROOKs.
-						    } else {
-							    return ERROR_Decode;
-						    }
-					    }
-
-					    sm.to = dest_sq;
-
-					    if (dest_sq < 0 || dest_sq > 63) {
-						    if (sm.movingPiece == KING ||
-						        sm.movingPiece == KNIGHT) {
-							    // The legacy code allows invalid squares for
-							    // KINGs and KNIGHTs
-						    } else {
-							    return ERROR_Decode;
-						    }
-					    }
-
-					    return OK;
-				    },
-				    [&]() { // 2 BYTES
-					    int dest_sq = buf.GetByte() - 64;
-					    if (dest_sq < 0 || dest_sq > 63)
-						    return ERROR_Decode;
-
-					    sm.to = dest_sq;
-					    return OK;
-				    },
-				    [&](int offset, pieceT promo, bool) { // PAWN
-					    if (toMove == WHITE) {
-						    sm.to = sm.from + offset;
-					    } else {
-						    sm.to = sm.from - offset;
-					    }
-					    if (promo != INVALID_PIECE) {
-						    sm.promote = promo;
-					    }
-					    return OK;
-				    },
-				    [&](bool king_side) { // CASTLE
-					    sm.to = sm.from + (king_side ? 2 : -2);
-					    return OK;
-				    },
-				    [&]() { // NULL MOVE
-					    sm.to = sm.from;
-					    return OK;
-				    });
-
-				EXPECT_EQ(res, err);
-				EXPECT_EQ(std::memcmp(&sm, &sm_old, sizeof sm), 0);
-			}
-		}
+TEST(Test_decodeMove, bishop) {
+	const squareT from = G7;
+	ByteBuffer bbuf(nullptr, 0);
+	for (unsigned char moveCode = 0; moveCode < 16; ++moveCode) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, BISHOP, from, moveCode);
+		EXPECT_EQ(promo, INVALID_PIECE);
+		EXPECT_EQ(square_Fyle(to), square_Fyle(moveCode));
+		int distRank = (to + 64) / 8 - (from + 64) / 8;
+		int distFyle = (to + 64) % 8 - (from + 64) % 8;
+		if (moveCode < 8)
+			EXPECT_EQ(distRank, distFyle);
+		else
+			EXPECT_EQ(distRank, -distFyle);
 	}
 }
 
-TEST(Test_decodemove, legacy_fastgame) {
-	ByteBuffer buf(1024);
-	ByteBuffer buf2(1024);
-	for (unsigned i = 0; i < 1024; ++i) {
-		buf.PutByte(static_cast<byte>(i));
-		buf2.PutByte(static_cast<byte>(i));
-	}
-	buf.BackToStart();
-	buf2.BackToStart();
-
-	for (const auto piece : {WP, BP, WN, WR, WB, WK, WQ}) {
-		const colorT toMove = piece_Color(piece);
-		for (squareT from = A1; from <= H8; ++from) {
-			for (byte val = 0; val <= 16; ++val) {
-				simpleMoveT sm_old{};
-				sm_old.movingPiece = piece;
-				sm_old.from = from;
-				sm_old.promote = EMPTY;
-				errorT err = ERROR_Decode;
-				switch (piece_Type(sm_old.movingPiece)) {
-				case PAWN:
-					err = testdecode_legacy::decodePawn(val & 15, &sm_old,
-					                                    toMove);
-					break;
-				case KNIGHT:
-					err = testdecode_legacy::decodeKnight(val & 15, &sm_old);
-					break;
-				case ROOK:
-					err = testdecode_legacy::decodeRook(val & 15, &sm_old);
-					break;
-				case BISHOP:
-					err = testdecode_legacy::decodeBishop(val & 15, &sm_old);
-					break;
-				case KING:
-					err = testdecode_legacy::decodeKing(val & 15, &sm_old);
-					break;
-				case QUEEN:
-					err = testdecode_legacy::decodeQueen(&buf2, val & 15,
-					                                     &sm_old);
-				}
-				if (sm_old.to > 63)
-					err = ERROR_Decode;
-
-				auto sm = testdecode_fastgame::decodeMove<
-				    testdecode_fastgame::make_simpleMoveT>(
-				    toMove, piece_Type(piece), from, val, buf);
-				errorT res = sm ? OK : ERROR_Decode;
-
-				ASSERT_EQ(res, err);
-				ASSERT_TRUE(res != OK || std::memcmp(&sm.data(), &sm_old,
-				                                     sizeof sm_old) == 0);
-			}
-		}
+TEST(Test_decodeMove, knight) {
+	const squareT from = B3;
+	std::tuple<unsigned char, int> data[] = {
+	    {1, from - 17}, {2, from - 15}, {3, from - 10}, {4, from - 6},
+	    {5, from + 6},  {6, from + 10}, {7, from + 15}, {8, from + 17},
+	};
+	ByteBuffer bbuf(nullptr, 0);
+	for (auto [moveCode, expTo] : data) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, KNIGHT, from, moveCode);
+		EXPECT_EQ(to, expTo);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+	for (auto moveCode : {0, 9, 10, 11, 12, 13, 14, 15}) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, KNIGHT, from, moveCode);
+		EXPECT_EQ(to, from);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+}
+
+TEST(Test_decodeMove, rook) {
+	const squareT from = B3;
+	ByteBuffer bbuf(nullptr, 0);
+	for (unsigned char moveCode = 0; moveCode < 8; ++moveCode) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, ROOK, from, moveCode);
+		EXPECT_EQ(square_Rank(to), square_Rank(from));
+		EXPECT_EQ(square_Fyle(to), moveCode);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+	for (unsigned char moveCode = 8; moveCode < 16; ++moveCode) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, ROOK, from, moveCode);
+		EXPECT_EQ(square_Fyle(to), square_Fyle(from));
+		EXPECT_EQ(square_Rank(to), moveCode - 8);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+}
+
+TEST(Test_decodeMove, queen) {
+	const squareT from = A1;
+	unsigned char data[] = {C3 + 64};
+	ByteBuffer bbuf(data, sizeof data);
+	{
+		ASSERT_TRUE(bbuf);
+		auto [to, promo] = bbuf.decodeMove(WHITE, QUEEN, from, 0);
+		EXPECT_EQ(to, C3);
+		EXPECT_EQ(promo, INVALID_PIECE);
+		EXPECT_FALSE(bbuf);
+	}
+	for (unsigned char moveCode = 1; moveCode < 8; ++moveCode) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, QUEEN, from, moveCode);
+		EXPECT_EQ(square_Rank(to), square_Rank(from));
+		EXPECT_EQ(square_Fyle(to), moveCode);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+	for (unsigned char moveCode = 8; moveCode < 16; ++moveCode) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, QUEEN, from, moveCode);
+		EXPECT_EQ(square_Fyle(to), square_Fyle(from));
+		EXPECT_EQ(square_Rank(to), moveCode - 8);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+}
+
+TEST(Test_decodeMove, king) {
+	const squareT from = B3;
+	ByteBuffer bbuf(nullptr, 0);
+	{
+		auto [to, promo] = bbuf.decodeMove(WHITE, KING, from, 0);
+		EXPECT_EQ(to, from);
+		EXPECT_EQ(promo, PAWN);
+	}
+	std::tuple<unsigned char, int> data[] = {
+	    {1, from - 9}, {2, from - 8}, {3, from - 7}, {4, from - 1},
+	    {5, from + 1}, {6, from + 7}, {7, from + 8}, {8, from + 9},
+	};
+	for (auto [moveCode, expTo] : data) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, KING, from, moveCode);
+		EXPECT_EQ(to, expTo);
+		EXPECT_EQ(promo, INVALID_PIECE);
+	}
+	{
+		auto [to, promo] = bbuf.decodeMove(WHITE, KING, from, 9);
+		EXPECT_EQ(to, from);
+		EXPECT_EQ(promo, QUEEN);
+	}
+	{
+		auto [to, promo] = bbuf.decodeMove(WHITE, KING, from, 10);
+		EXPECT_EQ(to, from);
+		EXPECT_EQ(promo, KING);
+	}
+	for (unsigned char moveCode = 11; moveCode < 16; ++moveCode) {
+		auto [to, promo] = bbuf.decodeMove(WHITE, KING, from, moveCode);
+		EXPECT_EQ(to, from);
+		EXPECT_EQ(promo, INVALID_PIECE);
 	}
 }
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_filebuf.cpp 1:4.7.4+dfsg1-2/gtest/test_filebuf.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_filebuf.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_filebuf.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -247,10 +247,6 @@ TEST_F(Test_Filebuf, Filebuf_open) {
 	}
 	{
 		Filebuf file;
-		ASSERT_NE(OK, file.Open(fname, FMODE_Memory));
-	}
-	{
-		Filebuf file;
 		ASSERT_EQ(OK, file.Open(fname, FMODE_Create));
 		ASSERT_EQ(1, file.WriteOneByte(0x01));
 		ASSERT_EQ(2, file.WriteTwoBytes(0x0202));
@@ -403,5 +399,5 @@ TEST_P(Test_FilebufGetline, filesize) {
 	}
 }
 
-INSTANTIATE_TEST_CASE_P(smallbuf, Test_FilebufGetline,
-                        ::testing::Values(1000, 100, 10));
+INSTANTIATE_TEST_SUITE_P(smallbuf, Test_FilebufGetline,
+                         ::testing::Values(1000, 100, 10));
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_game.cpp 1:4.7.4+dfsg1-2/gtest/test_game.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_game.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_game.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -15,6 +15,7 @@
  */
 
 #include "game.h"
+#include "pgnparse.h"
 #include "scidbase.h"
 #include <algorithm>
 #include <bytebuf.h>
@@ -35,13 +36,11 @@ TEST(Test_Game, clone) {
 	for (auto filename : {gameUTF8, gameLatin1, gameLatin1Conv}) {
 
 		scidBaseT dbase;
-		ASSERT_EQ(OK, dbase.Open(ICodecDatabase::PGN, FMODE_Both, filename));
+		ASSERT_EQ(OK, dbase.open("PGN", FMODE_Both, filename));
 		ASSERT_NE(nullptr, dbase.getIndexEntry_bounds(0));
 
 		Game game;
-		ByteBuffer bufGame(BBUF_SIZE);
-		ASSERT_EQ(OK, dbase.getGame(dbase.getIndexEntry(0), &bufGame));
-		ASSERT_EQ(OK, game.Decode(&bufGame, GAME_DECODE_ALL));
+		ASSERT_EQ(OK, dbase.getGame(*dbase.getIndexEntry(0), game));
 
 		std::mt19937 re(std::random_device{}());
 		game.MoveToLocationInPGN(std::uniform_int_distribution<>{0, 500}(re));
@@ -72,13 +71,6 @@ TEST(Test_Game, clone) {
 
 		ASSERT_TRUE(std::equal(pgnClone.first, pgnClone.first + pgnClone.second,
 		                       pgnGame.first, pgnGame.first + pgnGame.second));
-
-		ByteBuffer bufClone(BBUF_SIZE);
-		clone->Encode(&bufClone, NULL);
-
-		ASSERT_TRUE(std::equal(
-		    bufClone.getData(), bufClone.getData() + bufClone.GetByteCount(),
-		    bufGame.getData(), bufGame.getData() + bufGame.GetByteCount()));
 	}
 }
 
@@ -86,13 +78,13 @@ TEST(Test_Game, locationInPGN) {
 	for (auto filename : {gameUTF8, gameLatin1, gameLatin1Conv}) {
 
 		scidBaseT dbase;
-		ASSERT_EQ(OK, dbase.Open(ICodecDatabase::PGN, FMODE_Both, filename));
+		ASSERT_EQ(OK, dbase.open("PGN", FMODE_Both, filename));
 		ASSERT_NE(nullptr, dbase.getIndexEntry_bounds(0));
 
 		Game game;
-		ByteBuffer bufGame(BBUF_SIZE);
-		ASSERT_EQ(OK, dbase.getGame(dbase.getIndexEntry(0), &bufGame));
-		ASSERT_EQ(OK, game.Decode(&bufGame, GAME_DECODE_ALL));
+		auto bufGame = dbase.getGame(*dbase.getIndexEntry(0));
+		ASSERT_TRUE(bufGame);
+		ASSERT_EQ(OK, game.DecodeMovesOnly(bufGame));
 
 		unsigned location = 1;
 		game.MoveToStart();
@@ -120,14 +112,44 @@ TEST(Test_Game, locationInPGN) {
 	}
 }
 
+TEST(Test_Game, MoveToStart_MoveToEnd) {
+	scidBaseT dbase;
+	ASSERT_EQ(OK, dbase.open("PGN", FMODE_Both, gameUTF8));
+	auto ie = dbase.getIndexEntry_bounds(0);
+	ASSERT_NE(nullptr, ie);
+
+	auto randomEngine = std::mt19937(std::random_device{}());
+	auto distribution = std::uniform_int_distribution<>{2, 500};
+	Game game;
+	ASSERT_EQ(OK, dbase.getGame(*ie, game));
+
+	for (int i = 0; i < 10; i++) {
+		game.MoveToLocationInPGN(distribution(randomEngine));
+		ASSERT_NE(0, game.GetCurrentPly());
+		game.MoveToStart(); // Move to start from any position
+		EXPECT_EQ(0, game.GetCurrentPly());
+	}
+	game.MoveToStart(); // Move to start from start
+	EXPECT_EQ(0, game.GetCurrentPly());
+	game.MoveToEnd(); // Move to end from start
+	EXPECT_EQ(149, game.GetCurrentPly());
+	game.MoveToEnd(); // Move to end from end
+	EXPECT_EQ(149, game.GetCurrentPly());
+	for (int i = 0; i < 10; i++) {
+		game.MoveToLocationInPGN(distribution(randomEngine));
+		game.MoveToEnd(); // Move to end from any position
+		EXPECT_EQ(149, game.GetCurrentPly());
+	}
+}
+
 TEST(Test_Game, gamevisit) {
 	Game game;
 
 	// Expect to visit the STR even for an empty game
-	std::vector<std::pair<std::string, std::string> > expected_STR = {
+	std::vector<std::pair<std::string, std::string>> expected_STR = {
 	    {"Event", ""}, {"Site", ""},  {"Date", "????.??.??"}, {"Round", ""},
 	    {"White", ""}, {"Black", ""}, {"Result", "*"}};
-	std::vector<std::pair<std::string, std::string> > result_STR;
+	std::vector<std::pair<std::string, std::string>> result_STR;
 	gamevisit::tags_STR(game, [&](const char* tag, const char* value) {
 		result_STR.emplace_back(tag, value);
 	});
@@ -135,8 +157,8 @@ TEST(Test_Game, gamevisit) {
 	                       result_STR.begin(), result_STR.end()));
 
 	// Expect no extra tags for an empty game
-	std::vector<std::pair<std::string, std::string> > expected_extra;
-	std::vector<std::pair<std::string, std::string> > result_extra;
+	std::vector<std::pair<std::string, std::string>> expected_extra;
+	std::vector<std::pair<std::string, std::string>> result_extra;
 	gamevisit::tags_extra(game, [&](const char* tag, const char* value) {
 		result_extra.emplace_back(tag, value);
 	});
@@ -198,3 +220,264 @@ TEST(Test_Game, gamevisit) {
 		EXPECT_EQ(exp, *it++);
 	}
 }
+
+TEST(Test_Game, empty_tag_name) {
+	std::vector<unsigned char> encodedGame;
+	{
+		Game game;
+		game.AddPgnTag("Normal tag ", "normal  value");
+		game.AddPgnTag("", "empty tag name");
+		game.AddPgnTag("Annotator", "common tag");
+		EXPECT_EQ(game.GetExtraTags().size(), 3);
+
+		game.Encode(encodedGame);
+	}
+
+	ByteBuffer bbuf(encodedGame.data(), encodedGame.size());
+	int i = 0;
+	bbuf.decodeTags([&i](auto tag_name, auto tag_value) {
+		if (i++ == 0) {
+			EXPECT_EQ(tag_name, "Normal tag ");
+			EXPECT_EQ(tag_value, "normal  value");
+		} else {
+			EXPECT_EQ(tag_name, "Annotator");
+			EXPECT_EQ(tag_value, "common tag");
+		}
+	});
+	EXPECT_EQ(i, 2);
+}
+
+TEST(Test_Game, encodeFEN) {
+	std::vector<unsigned char> encodedGame;
+	const char* kiwipete =
+	    "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
+	{
+		Game game;
+		game.SetStartFen(kiwipete);
+		game.Encode(encodedGame);
+	}
+	{
+		ByteBuffer bbuf(encodedGame.data(), encodedGame.size());
+		Game game;
+		game.DecodeMovesOnly(bbuf);
+		game.MoveToStart();
+		char str[1024];
+		game.currentPos()->PrintFEN(str, FEN_ALL_FIELDS);
+		EXPECT_STREQ(kiwipete, str);
+	}
+}
+
+TEST(Test_Game, currentPosUCI_startpos) {
+	std::string_view pgn = "1.d4 (1.e4 e5 ( 1...c5)) (1.c4) 1...d5 2.c4";
+	Game game;
+	pgn::parse_game({pgn.data(), pgn.data() + pgn.size()}, PgnVisitor{game});
+
+	const std::pair<unsigned, const char*> expected[] = {
+	    {0, "position startpos moves"},
+	    {1, "position startpos moves"},
+	    {2, "position startpos moves d2d4"},
+	    {3, "position startpos moves"},
+	    {4, "position startpos moves e2e4"},
+	    {5, "position startpos moves e2e4 e7e5"},
+	    {6, "position startpos moves e2e4"},
+	    {7, "position startpos moves e2e4 c7c5"},
+	    {8, "position startpos moves"},
+	    {9, "position startpos moves c2c4"},
+	    {10, "position startpos moves d2d4 d7d5"},
+	    {11, "position startpos moves d2d4 d7d5 c2c4"}};
+	for (auto [pos, str] : expected) {
+		game.MoveToLocationInPGN(pos);
+		EXPECT_EQ(str, game.currentPosUCI());
+	}
+}
+
+TEST(Test_Game, currentPosUCI_fen) {
+	std::string_view pgn =
+	    "[FEN 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198]\n"
+	    "198.Kd2 ( 198.Nxd3 a1=R+ 199.Kd2 cxd3 )198...a1=Q 199.Ke3 Qe1+ 0-1";
+	Game game;
+	pgn::parse_game({pgn.data(), pgn.data() + pgn.size()}, PgnVisitor{game});
+
+	const std::pair<unsigned, const char*> expected[] = {
+	    // clang-format off
+	    {0, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves"},
+	    {1, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves"},
+	    {2, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves c1d2"},
+	    {3, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves"},
+	    {4, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves f2d3"},
+	    {5, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves f2d3 a2a1r"},
+	    {6, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves f2d3 a2a1r c1d2"},
+	    {7, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves f2d3 a2a1r c1d2 c4d3"},
+	    {8, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves c1d2 a2a1q"},
+	    {9, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves c1d2 a2a1q d2e3"},
+	    {10, "position fen 8/8/8/8/2p5/1k1p4/p4N2/2K5 w - - 0 198 moves c1d2 a2a1q d2e3 a1e1"}
+	    // clang-format on
+	};
+	for (auto [pos, str] : expected) {
+		game.MoveToLocationInPGN(pos);
+		EXPECT_EQ(str, game.currentPosUCI());
+	}
+}
+
+TEST(Test_Game, illegalPGN_Castling) {
+	std::string_view pgn = "1.e4 e5 2.Nf3 Nf6 3.Be2 Be7 4.O-O O-O 5.O-O";
+	Game game;
+	PgnParseLog pgnLog;
+	EXPECT_FALSE(pgnParseGame(pgn.data(), pgn.size(), game, pgnLog));
+	EXPECT_FALSE(pgnLog.log.empty());
+	char fen[256];
+	game.MoveToEnd();
+	game.currentPos()->PrintFEN(fen, FEN_ALL_FIELDS);
+	EXPECT_STREQ(
+	    fen, "rnbq1rk1/ppppbppp/5n2/4p3/4P3/5N2/PPPPBPPP/RNBQ1RK1 w - - 6 5");
+	game.MoveBackup();
+	game.currentPos()->PrintFEN(fen, FEN_ALL_FIELDS);
+	EXPECT_STREQ(
+	    fen, "rnbqk2r/ppppbppp/5n2/4p3/4P3/5N2/PPPPBPPP/RNBQ1RK1 b kq - 5 4");
+}
+
+TEST(Test_Game, illegalPGN_KingCapture) {
+	std::string_view pgn = "1.d4 e6 2.e4 Bb4+ 3.-- Be1";
+	Game game;
+	PgnParseLog pgnLog;
+	EXPECT_FALSE(pgnParseGame(pgn.data(), pgn.size(), game, pgnLog));
+	EXPECT_FALSE(pgnLog.log.empty());
+	char fen[256];
+	game.MoveToEnd();
+	game.currentPos()->PrintFEN(fen, FEN_ALL_FIELDS);
+	EXPECT_STREQ(
+	    fen, "rnbqk1nr/pppp1ppp/4p3/8/1b1PP3/8/PPP2PPP/RNBQKBNR b KQkq - 2 3");
+}
+
+namespace {
+
+/// Replace the move after the first comment with @e movecode
+auto make_invalid(unsigned char movecode, std::string_view pgn) {
+	std::vector<unsigned char> data;
+	Game g;
+	pgn::parse_game({pgn.data(), pgn.data() + pgn.size()}, PgnVisitor{g});
+	g.Encode(data);
+	auto comment_tag = std::find(data.begin(), data.end(), 12);
+	if (comment_tag != data.end())
+		*++comment_tag = movecode;
+	return data;
+}
+
+template <typename DataT> std::string decode_gameview(DataT const& data) {
+	auto bbuf = ByteBuffer{data.data() + 1, data.size()};
+	auto fen = bbuf.decodeStartBoard().second;
+	if (fen) {
+		Position startPos;
+		startPos.ReadFromFEN(fen);
+		return GameView(bbuf, startPos).getMoveSAN(0, 99);
+	}
+	return GameView(bbuf).getMoveSAN(0, 99);
+}
+
+template <typename DataT> std::string decode_game(DataT const& data) {
+	auto bbuf = ByteBuffer{data.data(), data.size()};
+	Game game;
+	game.DecodeMovesOnly(bbuf);
+	game.MoveToStart();
+	std::string moves;
+	do {
+		moves += ' ';
+		moves.append(game.GetNextSAN());
+	} while (game.MoveForward() == OK);
+	moves.erase(0, 1);
+	return moves;
+}
+} // namespace
+
+TEST(Test_Game, illegalSCID4_Castling) {
+	// The castling rook's index may change when another piece is captured.
+	auto change_idx = make_invalid(
+	    0, // unchanged, valid sequence
+	    "[FEN kr6/8/8/8/8/8/8/1NB1K2R b K - 0 1]\n 1...Rxb1 2.O-O Kb7 3.Rf7+");
+	EXPECT_EQ(decode_game(change_idx), "Rxb1 O-O Kb7 Rf7+ ");
+	EXPECT_EQ(decode_gameview(change_idx), "1...Rxb1  2.O-O Kb7  3.Rf7+");
+
+	// Chess960. Allowed by gameview.
+	auto chess960 = make_invalid(
+	    9, // replace null-move with long castle
+	    "[FEN bnrbkrqn/pppppppp/8/8/8/8/PPPPPPPP/BNRBKRQN w KQkq - 0 1]\n"
+	    "1.b3 Ng6 2.e4 e5 3.Ng3 Nc6 4.f3 Bg5 5.Be2 a6 6.Nc3 d6 7.Nd5 {_} -- "
+	    "8.Nf5");
+	EXPECT_EQ(decode_game(chess960),
+	          "b3 Ng6 e4 e5 Ng3 Nc6 f3 Bg5 Be2 a6 Nc3 d6 Nd5 O-O-O Nf5 ");
+	EXPECT_EQ(decode_gameview(chess960),
+	          "1.b3 Ng6  2.e4 e5  3.Ng3 Nc6  4.f3 Bg5  5.Be2 a6  6.Nc3 d6  "
+	          "7.Nd5 O-O-O  8.Nf5");
+
+	// Illegal castling. Allowed by both gameview and game.
+	// The chess rules for castling (king not in check, empty squares between
+	// the rook and the king final positions) are not enforced.
+	auto obstacles = make_invalid(0, // unchanged,
+	                              "1.d4 d5 2.Qd3 Nf6 3.Bg5 Nc6 4.O-O-O");
+	EXPECT_EQ(decode_game(obstacles), "d4 d5 Qd3 Nf6 Bg5 Nc6 O-O-O ");
+	EXPECT_EQ(decode_gameview(obstacles),
+	          "1.d4 d5  2.Qd3 Nf6  3.Bg5 Nc6  4.O-O-O");
+
+	auto check = make_invalid(0, // unchanged,
+	                          "1.d4 d5 2.Nf3 e6 3.e3 Nf6 4.Nc3 Be7 5.Bb5+ O-O");
+	EXPECT_EQ(decode_game(check), "d4 d5 Nf3 e6 e3 Nf6 Nc3 Be7 Bb5+ O-O ");
+	EXPECT_EQ(decode_gameview(check),
+	          "1.d4 d5  2.Nf3 e6  3.e3 Nf6  4.Nc3 Be7  5.Bb5+ O-O");
+
+	// Castle twice. Allowed by gameview: no changes to the board; the notations
+	// is wrongly reported as O-O-O because the rook is to the left of the king.
+	auto twice = make_invalid(
+	    10, // replace 5.a4 with O-O
+	    "1.e4 e5 2.Nf3 Nf6 3.Be2 Be7 4.O-O O-O {_} 5.a4 a5 6.Kh1");
+	EXPECT_EQ(decode_game(twice), "e4 e5 Nf3 Nf6 Be2 Be7 O-O O-O ");
+	EXPECT_EQ(decode_gameview(twice),
+	          "1.e4 e5  2.Nf3 Nf6  3.Be2 Be7  4.O-O O-O  5.O-O-O a5  6.Kh1");
+
+	// Moved rook. Allowed by gameview: the rook is moved to D1
+	auto moved_rook = make_invalid(
+	    9, // replace 2.c4 with O-O-O
+	    "[FEN r3k2r/2p2p2/2pq1p2/p2pp2p/P2PP2P/2PQ1P2/2P2P2/R3K2R w KQkq]\n"
+	    "1.Ra3 Rh6 {_} 2.c4 O-O-O 3. Ra2");
+	EXPECT_EQ(decode_game(moved_rook), "Ra3 Rh6 ");
+	EXPECT_EQ(decode_gameview(moved_rook), "1.Ra3 Rh6  2.O-O O-O-O  3.Rd2");
+
+	// No rook
+	auto no_rook = make_invalid(
+	    9, // replace 2.c4 with O-O-O
+	    "[FEN 2k2r2/ppp5/8/8/8/2P1N2R/5PP1/4K3 b - - 0 1]\n"
+	    "1...a5 {_} 2.c4 a4 3.Rh5");
+	EXPECT_EQ(decode_game(no_rook), "a5 ");
+	EXPECT_EQ(decode_gameview(no_rook), "1...a5");
+
+	// Captured rook
+	auto captured_rook = make_invalid(
+	    9, // replace 4..f5 with O-O-O
+	    "[FEN r3k2r/2p2p2/2pq1p2/p2pp2p/P2PP2P/2PQ1P2/2P2P2/R3K2R w KQkq]\n"
+	    "1.Qa6 c5 2.Qxa8+ Qd8 3.Qc6+ Qd7 4.O-O {_} f5 5.Kh1 Kd8");
+	EXPECT_EQ(decode_game(captured_rook), "Qa6 c5 Qxa8+ Qd8 Qc6+ Qd7 O-O ");
+	EXPECT_EQ(decode_gameview(captured_rook),
+	          "1.Qa6 c5  2.Qxa8+ Qd8  3.Qc6+ Qd7  4.O-O");
+
+	// occupied squares. Allowed by gameview: both pieces will be present on the
+	// same square. The previous piece can be moved.
+	auto occ_king = make_invalid(
+	    0, // replace 2.Nf3 with a null move
+	    "1.e4 e5 {_} 2.Nf3 Nf6 3.Be2 Be7 4.O-O O-O 5.Kh1 a5 6.Nxe5");
+	EXPECT_EQ(decode_game(occ_king), "e4 e5 -- Nf6 Be2 Be7 ");
+	EXPECT_EQ(decode_gameview(occ_king),
+	          "1.e4 e5  2.-- Nf6  3.Be2 Be7  4.O-O O-O  5.Kh1 a5  6.Nf3");
+
+	auto occ_rook = make_invalid(
+	    0, // replace 3...Be7 with a null move
+	    "1.e4 e5 2.Nf3 Nf6 3.Be2 {_} Be7 4.O-O O-O 5.Kh1");
+	EXPECT_EQ(decode_game(occ_rook), "e4 e5 Nf3 Nf6 Be2 -- O-O ");
+	EXPECT_EQ(decode_gameview(occ_rook),
+	          "1.e4 e5  2.Nf3 Nf6  3.Be2 --  4.O-O O-O  5.Kh1");
+}
+
+TEST(Test_Game, illegalSCID4_KingCapture) {
+	auto data = make_invalid(0, // null move
+	                         "1.d4 e6 2.e4 Bb4+ {_} 3.Ke2 Be1 4.Ke1");
+	EXPECT_EQ(decode_gameview(data), "1.d4 e6  2.e4 Bb4+  3.-- Bxe1  4.a4");
+	EXPECT_EQ(decode_game(data), "d4 e6 e4 Bb4+ -- ");
+}
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_hfilter.cpp 1:4.7.4+dfsg1-2/gtest/test_hfilter.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_hfilter.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_hfilter.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -384,4 +384,4 @@ TEST_P(Test_HFilter, hfilter_nonconstFun
 	}
 }
 
-INSTANTIATE_TEST_CASE_P(HFilter, Test_HFilter, ::testing::ValuesIn(test_cases));
+INSTANTIATE_TEST_SUITE_P(HFilter, Test_HFilter, ::testing::ValuesIn(test_cases));
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_indexentry.cpp 1:4.7.4+dfsg1-2/gtest/test_indexentry.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_indexentry.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_indexentry.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -23,8 +23,6 @@
 #include <stdint.h>
 #include <vector>
 
-void decodeIndexEntry(const char* buf_it, versionT version, IndexEntry* ie);
-
 namespace v4_6 {
 #include "test_indexentry_v4_6.h"
 }
@@ -42,7 +40,7 @@ public:
 		return std::equal(buf_, buf_ + count, b.buf_);
 	}
 
-	const char* getBuffer() {
+	char* getBuffer() {
 		return buf_;
 	}
 
@@ -93,163 +91,6 @@ private:
 
 } // namespace
 
-TEST(Test_IndexEntry, Limits_SCID4) {
-	const std::vector<uint64_t> limits_SCID4 = {
-	    uint64_t(1) << 32,           // bits for gamefile offset
-	    uint64_t(1) << 17,           // bits for game length
-	    uint64_t(1) << 20,           // bits for white name
-	    uint64_t(1) << 20,           // bits for black name
-	    uint64_t(1) << 19,           // bits for event name
-	    uint64_t(1) << 19,           // bits for site name
-	    uint64_t(1) << 18,           // bits for round name
-	    DATE_MAKE(YEAR_MAX, 12, 31), // max date (20 bits)
-	    DATE_MAKE(YEAR_MAX, 12, 31), // max event date (12 bits)
-	    uint64_t(1) << 4,            // bits for result
-	    MAX_ELO,                     // max white elo (12 bits)
-	    MAX_ELO,                     // max black elo (12 bits)
-	    uint64_t(1) << 4,            // bits for white elo type
-	    uint64_t(1) << 4,            // bits for black elo type
-	    uint64_t(1) << 16,           // bits for eco code
-	    uint64_t(1) << 10,           // bits for numHalfMoves
-	    uint64_t(1) << 22,           // bits for flags
-	    50,                          // max variation count (4 bits)
-	    50,                          // max comment count (4 bits)
-	    50,                          // max nag count (4 bits)
-	    uint64_t(1) << 24,           // bits for FinalMatSig
-	    uint64_t(1) << 8,            // bits for StoredLineCode
-	    uint64_t(1) << 6,            // bits for HomePawnData[0]
-	    uint64_t(1) << 8,            // bits for HomePawnData[1]
-	    uint64_t(1) << 8,            // bits for HomePawnData[2]
-	    uint64_t(1) << 8,            // bits for HomePawnData[3]
-	    uint64_t(1) << 8,            // bits for HomePawnData[4]
-	    uint64_t(1) << 8,            // bits for HomePawnData[5]
-	    uint64_t(1) << 8,            // bits for HomePawnData[6]
-	    uint64_t(1) << 8,            // bits for HomePawnData[7]
-	    uint64_t(1) << 8             // bits for HomePawnData[8]
-	}; // In total a SCID4 IndexEntry uses 375 bits (47 bytes)
-
-	auto setEntry = [](auto& entry, auto it) {
-		entry.SetOffset(*it++);
-		entry.SetLength(static_cast<size_t>(*it++));
-		entry.SetWhite(static_cast<idNumberT>(*it++));
-		entry.SetBlack(static_cast<idNumberT>(*it++));
-		entry.SetEvent(static_cast<idNumberT>(*it++));
-		entry.SetSite(static_cast<idNumberT>(*it++));
-		entry.SetRound(static_cast<idNumberT>(*it++));
-		entry.SetDate(static_cast<dateT>(*it++));
-		entry.SetEventDate(static_cast<dateT>(*it++));
-		entry.SetResult(static_cast<resultT>(*it++));
-		entry.SetWhiteElo(static_cast<eloT>(*it++));
-		entry.SetBlackElo(static_cast<eloT>(*it++));
-		entry.SetWhiteRatingType(static_cast<byte>(*it++));
-		entry.SetBlackRatingType(static_cast<byte>(*it++));
-		entry.SetEcoCode(static_cast<ecoT>(*it++));
-		entry.SetNumHalfMoves(static_cast<ushort>(*it++));
-		entry.clearFlags();
-		entry.SetFlag(static_cast<uint32_t>(*it++), true);
-		entry.SetVariationCount(static_cast<unsigned>(*it++));
-		entry.SetCommentCount(static_cast<unsigned>(*it++));
-		entry.SetNagCount(static_cast<unsigned>(*it++));
-		entry.SetFinalMatSig(static_cast<matSigT>(*it++));
-		entry.SetStoredLineCode(static_cast<byte>(*it++));
-		for (int i = 0; i < 9; i++) {
-			entry.GetHomePawnData()[i] = (static_cast<byte>(*it++));
-		}
-	};
-
-	auto chkEntry = [](auto& entry, auto it, bool fullEDate = false) {
-		ASSERT_EQ(*it++, entry.GetOffset());
-		ASSERT_EQ(*it++, entry.GetLength());
-		ASSERT_EQ(*it++, entry.GetWhite());
-		ASSERT_EQ(*it++, entry.GetBlack());
-		ASSERT_EQ(*it++, entry.GetEvent());
-		ASSERT_EQ(*it++, entry.GetSite());
-		ASSERT_EQ(*it++, entry.GetRound());
-		int64_t year = date_GetYear(static_cast<dateT>(*it));
-		ASSERT_EQ(*it++, entry.GetDate());
-		dateT eventDate = static_cast<dateT>(*it++);
-		// Due to a compact encoding format, the EventDate
-		// must be within a few years of the Date.
-		if (!fullEDate && std::abs(year - date_GetYear(eventDate)) > 3)
-			eventDate = 0;
-		ASSERT_EQ(eventDate, entry.GetEventDate());
-		ASSERT_EQ(*it++, entry.GetResult());
-		ASSERT_EQ(*it++, entry.GetWhiteElo());
-		ASSERT_EQ(*it++, entry.GetBlackElo());
-		ASSERT_EQ(*it++, entry.GetWhiteRatingType());
-		ASSERT_EQ(*it++, entry.GetBlackRatingType());
-		ASSERT_EQ(*it++, entry.GetEcoCode());
-		ASSERT_EQ(*it++, entry.GetNumHalfMoves());
-		ASSERT_TRUE(entry.GetFlag(static_cast<uint32_t>(*it++)));
-		ASSERT_TRUE(std::abs(int64_t(*it++) - entry.GetVariationCount()) <= 5);
-		ASSERT_TRUE(std::abs(int64_t(*it++) - entry.GetCommentCount()) <= 5);
-		ASSERT_TRUE(std::abs(int64_t(*it++) - entry.GetNagCount()) <= 5);
-		ASSERT_EQ(*it++, entry.GetFinalMatSig());
-		ASSERT_EQ(*it++, entry.GetStoredLineCode());
-		for (int i = 0; i < 9; i++) {
-			ASSERT_EQ(*it++, entry.GetHomePawnData()[i]);
-		}
-	};
-
-	std::mt19937 re(std::random_device{}());
-	for (int i = 0; i < 100000; ++i) {
-		auto v = limits_SCID4;
-		for (auto& e : v) {
-			if (i == 0) // First loop with max values
-				e -= 1;
-			else
-				e = std::uniform_int_distribution<uint64_t>{0, e - 1}(re);
-		}
-
-		Buffer ie_buf;
-		{ // Build the current IndexEntry and serialize it to ie_buf
-			IndexEntry ie;
-			setEntry(ie, v.cbegin());
-			chkEntry(ie, v.cbegin(), true);
-			ie.Write(&ie_buf, 400);
-			ie_buf.ToStart();
-		}
-
-		Buffer ie_buf_v4_6;
-		{ // Build the v4_6::IndexEntry and serialize it to ie_buf_v4_6
-			v4_6::IndexEntry ie4_6;
-			ie4_6.Init();
-			setEntry(ie4_6, v.cbegin());
-			chkEntry(ie4_6, v.cbegin());
-			ie4_6.Write(&ie_buf_v4_6, 400);
-			ie_buf_v4_6.ToStart();
-		}
-
-		ASSERT_TRUE(ie_buf.equal(ie_buf_v4_6, v4_6::INDEX_ENTRY_SIZE));
-
-		{ // Read a current IndexEntry from ie_buf and check it
-			IndexEntry ie;
-			decodeIndexEntry(ie_buf.getBuffer(), 400, &ie);
-			chkEntry(ie, v.cbegin());
-		}
-
-		{ // Read a current IndexEntry from ie_buf_v4_6 and check it
-			IndexEntry ie;
-			decodeIndexEntry(ie_buf_v4_6.getBuffer(), 400, &ie);
-			chkEntry(ie, v.cbegin());
-		}
-
-		{ // Read a v4_6::IndexEntry from ie_buf and check it
-			v4_6::IndexEntry ie4_6;
-			ie4_6.Read(&ie_buf, 400);
-			chkEntry(ie4_6, v.cbegin());
-			ie_buf.ToStart();
-		}
-
-		{ // Read a v4_6::IndexEntry from ie_buf_v4_6 and check it
-			v4_6::IndexEntry ie4_6;
-			ie4_6.Read(&ie_buf_v4_6, 400);
-			chkEntry(ie4_6, v.cbegin());
-			ie_buf_v4_6.ToStart();
-		}
-	}
-}
-
 TEST(Test_IndexEntry, Flags_dedicatedGetSet) {
 	IndexEntry ie;
 	std::mt19937 re(std::random_device{}());
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_indexentry_v4_6.h 1:4.7.4+dfsg1-2/gtest/test_indexentry_v4_6.h
--- 1:4.7.0+dfsg1-2/gtest/test_indexentry_v4_6.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_indexentry_v4_6.h	2022-07-09 05:14:42.000000000 +0000
@@ -26,9 +26,6 @@
 #include "matsig.h"
 #include "namebase.h"
 
-// Length is encoded as 17bits uint
-#define MAX_GAME_LENGTH 131072
-
 // HPSIG_SIZE = size of HomePawnData array in an IndexEntry.
 // It is nine bytes: the first byte contains the number of valid entries
 // in the array, and the next 8 bytes contain up to 16 half-byte entries.
@@ -91,7 +88,6 @@ public:
         return Length_Low + (uint32_t(Length_High & 0x80) << 9);
     }
     void SetLength (size_t length) {
-        ASSERT(length < MAX_GAME_LENGTH);
         Length_Low = static_cast<uint16_t>(length & 0xFFFF);
         // preserve the last 7 bits
         Length_High = ( Length_High & 0x7F ) | static_cast<byte>( (length >> 16) << 7 );
@@ -213,17 +209,7 @@ public:
     }
     resultT GetResult () const { return (VarCounts >> 12); }
     eloT GetWhiteElo () const { return u16_low_12(WhiteElo); }
-    eloT GetWhiteElo (const NameBase* nb)  const {
-        eloT r = GetWhiteElo();
-        if (r == 0 && nb != 0) return nb->GetElo (GetWhite());
-        return r;
-    }
     eloT GetBlackElo () const { return u16_low_12(BlackElo); }
-    eloT GetBlackElo (const NameBase* nb) const {
-        eloT r = GetBlackElo();
-        if (r == 0 && nb != 0) return nb->GetElo (GetBlack());
-        return r;
-    }
     eloT GetElo(colorT col) const {
         if (col == BLACK) return GetBlackElo();
         return GetWhiteElo();
@@ -600,38 +586,6 @@ IndexEntry::Write (T* file, versionT ver
     return OK;
 }
 
-inline byte IndexEntry::GetRating(const NameBase* nb) const {
-    eloT welo = GetWhiteElo();
-    eloT belo = GetBlackElo();
-    if (welo == 0) { welo = nb->GetElo (GetWhite()); }
-    if (belo == 0) { belo = nb->GetElo (GetBlack()); }
-    int rating = static_cast<int>(welo + belo) / 140;
-
-    // Bonus for comments or Nags
-    if (GetCommentCount() > 2 || GetNagCount() > 2) {
-        if (rating < 21) { // Missing elo
-            rating = 40;
-        } else {
-            rating += 6;
-        }
-    }
-
-    // Early draw penalty
-    if (GetResult() == RESULT_Draw) {
-        uint moves = GetNumHalfMoves();
-        if (moves < 80) {
-            rating -= 3;
-            if (moves < 60) {
-                rating -= 2;
-                if (moves < 40) rating -= 2;
-            }
-        }
-    }
-
-    if (rating < 0) return 0;
-    else return static_cast<byte> (rating);
-}
-
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // IndexEntry::CharToFlag():
 //    Returns the flag number corresponding to the given character.
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_namebase.cpp 1:4.7.4+dfsg1-2/gtest/test_namebase.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_namebase.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_namebase.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -121,8 +121,15 @@ TEST(Test_Namebase, sort_order) {
 	EXPECT_EQ(OK, nb.addName(nt, "ÜA", 255, 1000).first);
 	EXPECT_EQ(OK, nb.addName(nt, "ÜÜ", 255, 1000).first);
 	auto names = nb.getFirstMatches(nt, "", 4);
-	EXPECT_STREQ(nb.GetName(nt, names[0]), "AÜ");
-	EXPECT_STREQ(nb.GetName(nt, names[1]), "AA");
-	EXPECT_STREQ(nb.GetName(nt, names[2]), "ÜÜ");
-	EXPECT_STREQ(nb.GetName(nt, names[3]), "ÜA");
+	if (std::is_signed_v<char>) {
+		EXPECT_STREQ(nb.GetName(nt, names[0]), "AÜ");
+		EXPECT_STREQ(nb.GetName(nt, names[1]), "AA");
+		EXPECT_STREQ(nb.GetName(nt, names[2]), "ÜÜ");
+		EXPECT_STREQ(nb.GetName(nt, names[3]), "ÜA");
+	} else {
+		EXPECT_STREQ(nb.GetName(nt, names[1]), "AA");
+		EXPECT_STREQ(nb.GetName(nt, names[0]), "AÜ");
+		EXPECT_STREQ(nb.GetName(nt, names[3]), "ÜA");
+		EXPECT_STREQ(nb.GetName(nt, names[2]), "ÜÜ");
+	}
 }
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_position.cpp 1:4.7.4+dfsg1-2/gtest/test_position.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_position.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_position.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -14,7 +14,7 @@
  * along with Scid. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "fastgame.h"
+#include "gameview.h"
 #include "position.h"
 #include "searchpos.h"
 #include <cstring>
@@ -32,20 +32,21 @@ TEST(Test_movegen, attack) {
 	board[33] = !empty;
 	board[52] = !empty;
 
-	EXPECT_TRUE(movegen::attack(31, 29, WHITE, QUEEN, board, empty));
-	EXPECT_FALSE(movegen::attack(31, 27, WHITE, QUEEN, board, empty));
-	EXPECT_TRUE(movegen::attack(52, 28, BLACK, ROOK, board, empty));
-	EXPECT_FALSE(movegen::attack(52, 20, BLACK, ROOK, board, empty));
-	EXPECT_TRUE(movegen::attack(1, 11, WHITE, KNIGHT, board, empty));
-	EXPECT_FALSE(movegen::attack(1, 6, WHITE, KNIGHT, board, empty));
-	EXPECT_TRUE(movegen::attack(33, 19, BLACK, BISHOP, board, empty));
-	EXPECT_FALSE(movegen::attack(33, 12, BLACK, BISHOP, board, empty));
-	EXPECT_TRUE(movegen::attack(12, 19, WHITE, PAWN, board, empty));
-	EXPECT_FALSE(movegen::attack(12, 20, WHITE, PAWN, board, empty));
-	EXPECT_TRUE(movegen::attack(19, 12, BLACK, PAWN, board, empty));
-	EXPECT_FALSE(movegen::attack(12, 19, BLACK, PAWN, board, empty));
-	EXPECT_TRUE(movegen::attack(12, 3, WHITE, KING, board, empty));
-	EXPECT_FALSE(movegen::attack(12, 14, WHITE, KING, board, empty));
+	auto isOccupied = [&](auto square) { return board[square] != empty; };
+	EXPECT_TRUE(movegen::attack(31, 29, WHITE, QUEEN, isOccupied));
+	EXPECT_FALSE(movegen::attack(31, 27, WHITE, QUEEN, isOccupied));
+	EXPECT_TRUE(movegen::attack(52, 28, BLACK, ROOK, isOccupied));
+	EXPECT_FALSE(movegen::attack(52, 20, BLACK, ROOK, isOccupied));
+	EXPECT_TRUE(movegen::attack(1, 11, WHITE, KNIGHT, isOccupied));
+	EXPECT_FALSE(movegen::attack(1, 6, WHITE, KNIGHT, isOccupied));
+	EXPECT_TRUE(movegen::attack(33, 19, BLACK, BISHOP, isOccupied));
+	EXPECT_FALSE(movegen::attack(33, 12, BLACK, BISHOP, isOccupied));
+	EXPECT_TRUE(movegen::attack(12, 19, WHITE, PAWN, isOccupied));
+	EXPECT_FALSE(movegen::attack(12, 20, WHITE, PAWN, isOccupied));
+	EXPECT_TRUE(movegen::attack(19, 12, BLACK, PAWN, isOccupied));
+	EXPECT_FALSE(movegen::attack(12, 19, BLACK, PAWN, isOccupied));
+	EXPECT_TRUE(movegen::attack(12, 3, WHITE, KING, isOccupied));
+	EXPECT_FALSE(movegen::attack(12, 14, WHITE, KING, isOccupied));
 }
 
 TEST(Test_movegen, opens_ray) {
@@ -60,16 +61,17 @@ TEST(Test_movegen, opens_ray) {
 	board[33] = !empty;
 	board[52] = !empty;
 
-	auto test = movegen::opens_ray(19, 27, 12, board, empty);
+	auto isOccupied = [&](auto square) { return board[square] != empty; };
+	auto test = movegen::opens_ray(19, 27, 12, isOccupied);
 	EXPECT_TRUE(test.first == BISHOP && test.second == 33);
 
-	test = movegen::opens_ray(21, 29, 12, board, empty);
+	test = movegen::opens_ray(21, 29, 12, isOccupied);
 	EXPECT_TRUE(test.first == INVALID_PIECE);
 
-	test = movegen::opens_ray(28, 20, 12, board, empty);
+	test = movegen::opens_ray(28, 20, 12, isOccupied);
 	EXPECT_TRUE(test.first == INVALID_PIECE);
 
-	test = movegen::opens_ray(28, 27, 12, board, empty);
+	test = movegen::opens_ray(28, 27, 12, isOccupied);
 	EXPECT_TRUE(test.first == ROOK && test.second == 52);
 }
 
@@ -123,6 +125,7 @@ TEST(Test_movegen, UCItoSAN) {
 		pos.MakeSANString(&sm, buf, SAN_MATETEST);
 		EXPECT_STREQ(*it, buf);
 
+		pos.DoSimpleMove(sm);
 		FullMove fullmove;
 		colorT col = piece_Color(sm.movingPiece);
 		pieceT pt = piece_Type(sm.movingPiece);
@@ -138,11 +141,10 @@ TEST(Test_movegen, UCItoSAN) {
 			}
 		} else {
 			if (col == WHITE)
-				fullmove = FullMove(WHITE, E1, (castle == 1) ? H1 : A1);
+				fullmove = FullMove(WHITE, E1, (castle > 0) ? H1 : A1);
 			else
-				fullmove = FullMove(BLACK, E8, (castle == 1) ? H8 : A8);
+				fullmove = FullMove(BLACK, E8, (castle > 0) ? H8 : A8);
 		}
-		pos.DoSimpleMove(&sm);
 		FastBoard fastboard(pos);
 		pos.UndoSimpleMove(&sm);
 		fastboard.fillSANInfo(fullmove);
@@ -264,3 +266,450 @@ TEST(Test_MaterialCount, less_mat) {
 	EXPECT_TRUE(less_mat(mt_count, matSig, false, true));
 	EXPECT_TRUE(less_mat(mt_count, matSig, false, false));
 }
+
+TEST(Test_ReadFromFen, invalid_FEN) {
+	Position pos;
+	EXPECT_EQ(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2/Q1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p4/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4a3/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4 3/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p3/4q3/8/PKP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1k5p/p7/4p3/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1q2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 b"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 a"));
+	EXPECT_NE(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 b z"));
+	EXPECT_NE(OK, // extra piece on rank
+	          pos.ReadFromFEN(
+	              "rnbqkbn1/ppppppppr/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"));
+	EXPECT_NE(OK, // white 18 pieces
+	          pos.ReadFromFEN(
+	              "nbqkbnr/ppppNNpp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"));
+	EXPECT_NE(OK, // black 17 pieces
+	          pos.ReadFromFEN(
+	              "nbqkbnr/pppppppp/8/8/8/8/PPPPPnPP/RNBQKBNR w KQkq - 0 1"));
+	EXPECT_NE(OK, // colour
+	          pos.ReadFromFEN(
+	              "nbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR K KQkq - 0 1"));
+	EXPECT_NE(OK, // king in check
+	          pos.ReadFromFEN(
+	              "1B6/prpb2p1/2KPp3/qp1p4/Q1k5/nRP3p1/BRPP2Pp/BN6 w - -"));
+}
+
+TEST(Test_ReadFromFen, castling_flags) {
+	const char* valid_fens[] = {
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR    w    KQkq",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQk",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQ",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w K",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w Q",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w Qk",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w -",
+	    "rnbbnqkr/pppppppp/8/8/8/8/PPPPPPPP/RNBBNQKR w HAha - 0 1",
+	    "nbqnrkbr/pppppppp/8/8/8/8/PPPPPPPP/NBQNRKBR w HEhe - 0 1"};
+	const char* invalid_fens[] = {
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w z",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w 1",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w T",
+	    "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w Ki"};
+	Position pos;
+	for (auto fen : valid_fens) {
+		EXPECT_EQ(OK, pos.ReadFromFEN(fen));
+	}
+	for (auto fen : invalid_fens) {
+		EXPECT_NE(OK, pos.ReadFromFEN(fen));
+	}
+}
+
+TEST(Test_ReadFromFen, EP_target) {
+	Position pos;
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 1 1"));
+	EXPECT_EQ(NULL_SQUARE, pos.GetEPTarget());
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - a3 1 1"));
+	EXPECT_EQ(A3, pos.GetEPTarget());
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - b6 1 1"));
+	EXPECT_EQ(B6, pos.GetEPTarget());
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - f3 1 1"));
+	EXPECT_EQ(F3, pos.GetEPTarget());
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - h6 1 1"));
+	EXPECT_EQ(H6, pos.GetEPTarget());
+
+	EXPECT_NE(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - i6 1 1"));
+	EXPECT_NE(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - a2 1 1"));
+	EXPECT_NE(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - z3 1 1"));
+	EXPECT_NE(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - a7 1 1"));
+	EXPECT_NE(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - a 3 1 1"));
+}
+
+TEST(Test_ReadFromFen, halfmove_clock) {
+	const char* valid_fens[] = {"8/K7/8/8/7k/8/8/8 w - - 0 1",
+	                            "8/K7/8/8/7k/8/8/8 w - - 5 1",
+	                            "8/K7/8/8/7k/8/8/8 w - - 45 1"};
+	const char* invalid_fens[] = {
+	    "8/K7/8/8/7k/8/8/8 w - - -1 1", "8/K7/8/8/7k/8/8/8 w - - - 1 1",
+	    "8/K7/8/8/7k/8/8/8 w - - - 1",  "8/K7/8/8/7k/8/8/8 w - - a 1",
+	    "8/K7/8/8/7k/8/8/8 w - - a5 1", "8/K7/8/8/7k/8/8/8 w - - 0x5 1"};
+	char buf[1024];
+	Position pos;
+	for (auto fen : valid_fens) {
+		EXPECT_EQ(OK, pos.ReadFromFEN(fen));
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(buf, fen);
+	}
+	for (auto fen : invalid_fens) {
+		EXPECT_EQ(OK, pos.ReadFromFEN(fen));
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(buf, "8/K7/8/8/7k/8/8/8 w - - 0 1");
+	}
+}
+
+TEST(Test_ReadFromFen, fullmove_number) {
+	Position pos;
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 0 0"));
+	EXPECT_EQ(pos.GetPlyCounter(), 0);
+	EXPECT_EQ(pos.GetPlyCounter() / 2 + 1, 1);
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 0 -1"));
+	EXPECT_EQ(pos.GetPlyCounter(), 0);
+	EXPECT_EQ(pos.GetPlyCounter() / 2 + 1, 1);
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 0 a"));
+	EXPECT_EQ(pos.GetPlyCounter(), 0);
+	EXPECT_EQ(pos.GetPlyCounter() / 2 + 1, 1);
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 0 1"));
+	EXPECT_EQ(pos.GetPlyCounter() / 2 + 1, 1);
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 0 25"));
+	EXPECT_EQ(pos.GetPlyCounter() / 2 + 1, 25);
+	EXPECT_EQ(OK, pos.ReadFromFEN("8/K7/8/8/7k/8/8/8 w - - 0 115"));
+	EXPECT_EQ(pos.GetPlyCounter() / 2 + 1, 115);
+}
+
+TEST(Test_ReadFromFen, GetList) {
+	Position pos;
+	auto getPiece = [&](auto sq) { return std::pair(sq, pos.GetPiece(sq)); };
+
+	ASSERT_EQ(OK, pos.ReadFromFEN("rnb1k2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 b"));
+	const auto wh_list = pos.GetList(WHITE);
+	ASSERT_TRUE(7 == pos.GetCount(WHITE));
+	EXPECT_EQ(std::pair(C1, WK), getPiece(wh_list[0]));
+	EXPECT_EQ(std::pair(A2, WP), getPiece(wh_list[1]));
+	EXPECT_EQ(std::pair(B2, WP), getPiece(wh_list[2]));
+	EXPECT_EQ(std::pair(C2, WP), getPiece(wh_list[3]));
+	EXPECT_EQ(std::pair(F2, WR), getPiece(wh_list[4]));
+	EXPECT_EQ(std::pair(H2, WP), getPiece(wh_list[5]));
+	EXPECT_EQ(std::pair(H8, WQ), getPiece(wh_list[6]));
+	const auto bl_list = pos.GetList(BLACK);
+	ASSERT_TRUE(9 == pos.GetCount(BLACK));
+	EXPECT_EQ(std::pair(E8, BK), getPiece(bl_list[0]));
+	EXPECT_EQ(std::pair(B8, BN), getPiece(bl_list[1]));
+	EXPECT_EQ(std::pair(C8, BB), getPiece(bl_list[2]));
+	EXPECT_EQ(std::pair(A8, BR), getPiece(bl_list[3]));
+	EXPECT_EQ(std::pair(B7, BP), getPiece(bl_list[4]));
+	EXPECT_EQ(std::pair(H7, BP), getPiece(bl_list[5]));
+	EXPECT_EQ(std::pair(A6, BP), getPiece(bl_list[6]));
+	EXPECT_EQ(std::pair(E5, BP), getPiece(bl_list[7]));
+	EXPECT_EQ(std::pair(E4, BQ), getPiece(bl_list[8]));
+}
+
+TEST(Test_PositionReadCoordMoves, ReadFromFENorUCI) {
+	char buf[1024];
+	Position pos;
+	EXPECT_EQ(OK, pos.ReadFromFENorUCI("position startpos"));
+	EXPECT_TRUE(pos.IsStdStart());
+
+	EXPECT_EQ(OK, pos.ReadFromFENorUCI("   position startpos    "));
+	EXPECT_TRUE(pos.IsStdStart());
+
+	EXPECT_EQ(OK, pos.ReadFromFENorUCI("position startpos moves"));
+	EXPECT_TRUE(pos.IsStdStart());
+
+	EXPECT_EQ(OK, pos.ReadFromFENorUCI("   position startpos moves   "));
+	EXPECT_TRUE(pos.IsStdStart());
+
+	EXPECT_NE(OK, pos.ReadFromFENorUCI(""));
+
+	EXPECT_EQ(OK,
+	          pos.ReadFromFENorUCI("   position startpos moves e2e4 c7c5 "));
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(
+	    buf, "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2");
+
+	EXPECT_EQ(OK, pos.ReadFromFENorUCI("rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/"
+	                                   "RNBQKBNR w KQkq - 0 2 moves g1f3 "));
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(
+	    buf, "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2");
+}
+
+TEST(Test_PositionReadCoordMoves, ReadCoordMoves) {
+	std::tuple<const char*, const char*, const char*, const char*> expectOK[] =
+	    {{"position startpos", "1.e4 c5",
+	      "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2",
+	      "e2e4 c7c5"},
+	     {"position startpos", "1.e4 c5",
+	      "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2",
+	      " e2e4 c7c5 "}};
+
+	for (auto [startpos, sanMoves, endpos, coordMoves] : expectOK) {
+		Position pos;
+		EXPECT_EQ(OK, pos.ReadFromFENorUCI(startpos));
+		std::string san;
+		EXPECT_EQ(
+		    OK, pos.MakeCoordMoves(coordMoves, std::strlen(coordMoves), &san));
+		char buf[1024];
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(endpos, buf);
+		EXPECT_STREQ(sanMoves, san.c_str());
+	}
+}
+
+TEST(Test_MoveGeneration, GetCastling) {
+	{
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/8/8/5k2/8/4K2R w K -"));
+		EXPECT_TRUE(pos.IsLegalMove(E1, G1, EMPTY));
+	}
+	{ // Adjacent enemy king
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/8/8/8/6k1/4K2R w K -"));
+		EXPECT_FALSE(pos.IsLegalMove(E1, G1, EMPTY));
+	}
+	{ // King in check
+		Position pos;
+		ASSERT_EQ(
+		    OK, pos.ReadFromFEN(
+		            "r3k2r/pppp1ppp/5n1b/4p3/4P3/5N2/PP3PPP/r3KB1R w KQkq -"));
+		EXPECT_FALSE(pos.IsLegalMove(E1, G1, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(E1, C1, EMPTY));
+	}
+	{ // Obstacles
+		Position pos;
+		ASSERT_EQ(OK,
+		          pos.ReadFromFEN(
+		              "rn2k2r/pppp1ppp/5n2/4p3/4P3/5N2/PP3PPP/R3KB1R b KQkq"));
+		EXPECT_TRUE(pos.IsLegalMove(E8, G8, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(E8, C8, EMPTY));
+
+		pos.SetToMove(WHITE);
+		EXPECT_FALSE(pos.IsLegalMove(E1, G1, EMPTY));
+		EXPECT_TRUE(pos.IsLegalMove(E1, C1, EMPTY));
+	}
+	{ // Destination in check
+		Position pos;
+		ASSERT_EQ(
+		    OK, pos.ReadFromFEN(
+		            "r3k2r/pppp1ppp/5n1b/4p2r/4P3/5N2/PP3PP1/R3K2R b KQkq -"));
+		EXPECT_TRUE(pos.IsLegalMove(E8, G8, EMPTY));
+		EXPECT_TRUE(pos.IsLegalMove(E8, C8, EMPTY));
+
+		pos.SetToMove(WHITE);
+		EXPECT_TRUE(pos.IsLegalMove(E1, G1, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(E1, C1, EMPTY));
+	}
+	{ // Wrong rank
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/1k6/8/8/8/8/4K2R w K - 0 1"));
+		EXPECT_TRUE(pos.IsLegalMove(E1, G1, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(E1, G2, EMPTY));
+	}
+}
+
+TEST(Test_PositionDoSimpleMove, castling_flags) {
+	std::vector<simpleMoveT> sm;
+	char buf[1024];
+	Position pos;
+	ASSERT_EQ(OK, pos.ReadFromFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1"));
+
+	pos.ParseMove(&sm.emplace_back(), "e1g1");
+	pos.DoSimpleMove(sm.back());
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k2r/8/8/8/8/8/8/R4RK1 b kq - 1 1");
+
+	pos.ParseMove(&sm.emplace_back(), "h8g8");
+	pos.DoSimpleMove(sm.back());
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k1r1/8/8/8/8/8/8/R4RK1 w q - 2 2");
+
+	pos.ParseMove(&sm.emplace_back(), "g1h2");
+	pos.DoSimpleMove(sm.back());
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k1r1/8/8/8/8/8/7K/R4R2 b q - 3 2");
+
+	pos.ParseMove(&sm.emplace_back(), "e8c8");
+	pos.DoSimpleMove(sm.back());
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "2kr2r1/8/8/8/8/8/7K/R4R2 w - - 4 3");
+
+	// UndoSimpleMove
+	auto it = sm.crbegin();
+	pos.UndoSimpleMove(&(*it++));
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k1r1/8/8/8/8/8/7K/R4R2 b q - 3 2");
+
+	pos.UndoSimpleMove(&(*it++));
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k1r1/8/8/8/8/8/8/R4RK1 w q - 2 2");
+
+	pos.UndoSimpleMove(&(*it++));
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k2r/8/8/8/8/8/8/R4RK1 b kq - 1 1");
+
+	pos.UndoSimpleMove(&(*it++));
+	pos.PrintFEN(buf, FEN_ALL_FIELDS);
+	EXPECT_STREQ(buf, "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1");
+}
+
+TEST(Test_PositionDoSimpleMove, castling_flags_capture) {
+	char buf[1024];
+	simpleMoveT sm;
+	Position pos;
+	{
+		ASSERT_EQ(OK, pos.ReadFromFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1"));
+		pos.ParseMove(&sm, "h1h8");
+		pos.DoSimpleMove(sm);
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(buf, "r3k2R/8/8/8/8/8/8/R3K3 b Qq - 0 1");
+	}
+	{
+		ASSERT_EQ(OK, pos.ReadFromFEN("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1"));
+		pos.ParseMove(&sm, "a1a8");
+		pos.DoSimpleMove(sm);
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(buf, "R3k2r/8/8/8/8/8/8/4K2R b Kk - 0 1");
+	}
+	{
+		ASSERT_EQ(OK, pos.ReadFromFEN("r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1"));
+		pos.ParseMove(&sm, "h8h1");
+		pos.DoSimpleMove(sm);
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(buf, "r3k3/8/8/8/8/8/8/R3K2r w Qq - 0 2");
+	}
+	{
+		ASSERT_EQ(OK, pos.ReadFromFEN("r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1"));
+		pos.ParseMove(&sm, "a8a1");
+		pos.DoSimpleMove(sm);
+		pos.PrintFEN(buf, FEN_ALL_FIELDS);
+		EXPECT_STREQ(buf, "4k2r/8/8/8/8/8/8/r3K2R w Kk - 0 2");
+	}
+}
+
+TEST(Test_PositionIsLegalMove, normal) {
+	{
+		Position pos = Position::getStdStart();
+		EXPECT_TRUE(pos.IsLegalMove(B1, C3, EMPTY));
+		EXPECT_TRUE(pos.IsLegalMove(E2, E4, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(E2, E4, QUEEN));
+		EXPECT_FALSE(pos.IsLegalMove(E3, E4, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(C1, F4, EMPTY));
+	}
+}
+
+TEST(Test_PositionIsLegalMove, king_in_check) {
+	Position pos;
+	ASSERT_EQ(
+	    OK,
+	    pos.ReadFromFEN(
+	        "r1b1kb1r/ppp2ppp/4p3/3pP3/3q4/1BN5/PPPP2PP/R1BQR1K1 w kq - 0 11"));
+	EXPECT_TRUE(pos.IsLegalMove(G1, H1, EMPTY));
+	EXPECT_TRUE(pos.IsLegalMove(E1, E3, EMPTY));
+	EXPECT_FALSE(pos.IsLegalMove(E1, E4, EMPTY));
+	EXPECT_FALSE(pos.IsLegalMove(C3, B5, EMPTY));
+
+	{ // Capture the attacker
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/1kR5/8/8/8/8/4K3/8 b - - 0 1"));
+		EXPECT_TRUE(pos.IsLegalMove(B7, C7, EMPTY));
+	}
+	{ // Capture a defended piece
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/1b6/1k6/8/4p3/4K3/8/8 w - - 0 1"));
+		EXPECT_FALSE(pos.IsLegalMove(E3, E4, EMPTY));
+	}
+	{ // Adjacent enemy king
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/3k4/2p5/4K3/8/8 w - - 0 1"));
+		EXPECT_FALSE(pos.IsLegalMove(E3, E4, EMPTY));
+		EXPECT_TRUE(pos.IsLegalMove(E3, E2, EMPTY));
+	}
+	{ // Evade check
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/3k4/8/8/8/1r3K2/8 w - - 0 1"));
+		EXPECT_TRUE(pos.IsLegalMove(F2, E3, EMPTY));
+		EXPECT_FALSE(pos.IsLegalMove(F2, G2, EMPTY));
+	}
+}
+
+TEST(Test_PositionIsLegalMove, en_passant) {
+	{ // En passant capture
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/8/4pP2/8/7k/3K4 b - f3 0 1"));
+		EXPECT_TRUE(pos.IsLegalMove(E4, F3, EMPTY));
+	}
+	{ // En passant capture, the pawn checks the king
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/6k1/4pP2/8/8/3K4 b - f3 0 1"));
+		EXPECT_TRUE(pos.IsLegalMove(E4, F3, EMPTY));
+	}
+	{ // Hidden attacker
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/2B5/8/8/4pP2/8/7k/3K4 b - f3 0 1"));
+		EXPECT_FALSE(pos.IsLegalMove(E4, F3, EMPTY));
+	}
+}
+
+TEST(Test_PositionIsKingInCheck, last_move_optimization) {
+	simpleMoveT sm;
+
+	{ // No Check
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/6k1/5pp1/8/2KR4/2B5 w - -"));
+		pos.ParseMove(&sm, "d2g2");
+		pos.DoSimpleMove(sm);
+		EXPECT_FALSE(pos.IsKingInCheck(sm));
+	}
+	{ // Direct attack
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/6k1/5p2/8/2KR4/2B5 w - -"));
+		pos.ParseMove(&sm, "d2g2");
+		pos.DoSimpleMove(sm);
+		EXPECT_TRUE(pos.IsKingInCheck(sm));
+	}
+	{ // Discovered check
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/6k1/6p1/8/2KR4/2B5 w - -"));
+		pos.ParseMove(&sm, "d2g2");
+		pos.DoSimpleMove(sm);
+		EXPECT_TRUE(pos.IsKingInCheck(sm));
+	}
+	{ // Double check
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/6k1/8/8/2KR4/2B5 w - -"));
+		pos.ParseMove(&sm, "d2g2");
+		pos.DoSimpleMove(sm);
+		EXPECT_TRUE(pos.IsKingInCheck(sm));
+	}
+	{ // Castling
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("4k2r/6pp/8/8/8/6P1/4P1PP/5K2 b k -"));
+		pos.ParseMove(&sm, "e8h8");
+		pos.DoSimpleMove(sm);
+		EXPECT_TRUE(pos.IsKingInCheck(sm));
+	}
+	{ // En passant capture, the pawn checks the king
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/8/8/6k1/4p3/8/5P2/3K4 w - -"));
+		pos.ParseMove(&sm, "f2f4");
+		pos.DoSimpleMove(sm);
+		EXPECT_TRUE(pos.IsKingInCheck(sm));
+		pos.ParseMove(&sm, "e4f3");
+		pos.DoSimpleMove(sm);
+		EXPECT_FALSE(pos.IsKingInCheck(sm));
+	}
+	{ // En passant capture, discovered check
+		Position pos;
+		ASSERT_EQ(OK, pos.ReadFromFEN("8/4r3/8/6k1/4pP2/8/8/4K3 b - f3 0 1"));
+		pos.ParseMove(&sm, "e4f3");
+		pos.DoSimpleMove(sm);
+		EXPECT_TRUE(pos.IsKingInCheck(sm));
+	}
+}
\ No newline at end of file
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_scidbase.cpp 1:4.7.4+dfsg1-2/gtest/test_scidbase.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_scidbase.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_scidbase.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -300,7 +300,7 @@ TEST_F(Test_Scidbase, getGamePos1) {
 	ASSERT_STREQ(parseLog.log.c_str(), "");
 
 	scidBaseT dbase;
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, dbase.open("MEMORY", FMODE_Create, "Memory"));
 	ASSERT_EQ(OK, dbase.saveGame(&game));
 	ASSERT_NE(nullptr, dbase.getIndexEntry_bounds(0));
 
@@ -327,7 +327,7 @@ TEST_F(Test_Scidbase, getGamePos2) {
 	ASSERT_STREQ(parseLog.log.c_str(), "");
 
 	scidBaseT dbase;
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, dbase.open("MEMORY", FMODE_Create, "Memory"));
 	ASSERT_EQ(OK, dbase.saveGame(&game));
 	ASSERT_NE(nullptr, dbase.getIndexEntry_bounds(0));
 
@@ -343,7 +343,7 @@ TEST_F(Test_Scidbase, getGamePos3) {
 	ASSERT_STREQ(parseLog.log.c_str(), "");
 
 	scidBaseT dbase;
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, dbase.open("MEMORY", FMODE_Create, "Memory"));
 	ASSERT_EQ(OK, dbase.saveGame(&game));
 	ASSERT_NE(nullptr, dbase.getIndexEntry_bounds(0));
 
@@ -362,7 +362,7 @@ TEST_F(Test_Scidbase, saveGame) {
 	ASSERT_STREQ(parseLog.log.c_str(), "");
 
 	scidBaseT dbase;
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, dbase.open("MEMORY", FMODE_Create, "Memory"));
 	EXPECT_EQ(nullptr, dbase.getIndexEntry_bounds(0));
 	HFilter dbfilter = dbase.getFilter("dbfilter");
 	EXPECT_EQ(0U, dbfilter->size());
@@ -421,7 +421,7 @@ TEST_F(Test_Scidbase, importGames) {
 
 
 	scidBaseT srcBase;
-	ASSERT_EQ(OK, srcBase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, srcBase.open("MEMORY", FMODE_Create, "Memory"));
 	ASSERT_EQ(OK, srcBase.saveGame(&game0));
 	ASSERT_EQ(OK, srcBase.saveGame(&game1));
 	ASSERT_EQ(OK, srcBase.saveGame(&game1));
@@ -430,55 +430,38 @@ TEST_F(Test_Scidbase, importGames) {
 	srcFilter->erase(1);
 
 	scidBaseT dbase;
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, dbase.open("MEMORY", FMODE_Create, "Memory"));
 	EXPECT_EQ(nullptr, dbase.getIndexEntry_bounds(0));
 	HFilter dbfilter = dbase.getFilter("dbfilter");
 
-	EXPECT_EQ(ERROR_BadArg, dbase.importGame(&dbase, 0));
 	EXPECT_EQ(ERROR_BadArg, dbase.importGames(&dbase, dbfilter, Progress()));
-	EXPECT_EQ(ERROR_BadArg, dbase.importGame(&srcBase, 3));
 	EXPECT_EQ(0U, dbase.numGames());
 	EXPECT_EQ(0U, dbfilter->size());
 
 	{
-		EXPECT_EQ(OK, dbase.importGame(&srcBase, 1));
-		EXPECT_EQ(1U, dbase.numGames());
-		EXPECT_EQ(1U, dbfilter->size());
-		auto ie0 = dbase.getIndexEntry_bounds(0);
-		auto ie1 = dbase.getIndexEntry_bounds(1);
-		EXPECT_NE(nullptr, ie0);
-		EXPECT_EQ(nullptr, ie1);
-		EXPECT_NE(ie0, ie1);
-		auto gamepos = collectPositions(dbase, 0);
-		EXPECT_EQ(test_pgnLong, encodePgn(gamepos));
-	}
-	{
 		EXPECT_EQ(OK, dbase.importGames(&srcBase, srcFilter, Progress()));
-		EXPECT_EQ(3U, dbase.numGames());
-		EXPECT_EQ(3U, dbfilter->size());
+		EXPECT_EQ(2U, dbase.numGames());
+		EXPECT_EQ(2U, dbfilter->size());
 		auto ie0 = dbase.getIndexEntry_bounds(0);
 		auto ie1 = dbase.getIndexEntry_bounds(1);
 		auto ie2 = dbase.getIndexEntry_bounds(2);
 		EXPECT_NE(nullptr, ie0);
 		EXPECT_NE(nullptr, ie1);
-		EXPECT_NE(nullptr, ie2);
+		EXPECT_EQ(nullptr, ie2);
 		EXPECT_NE(ie0, ie1);
-		EXPECT_NE(ie0, ie2);
 		auto gamepos = collectPositions(dbase, 0);
-		EXPECT_EQ(test_pgnLong, encodePgn(gamepos));
-		gamepos = collectPositions(dbase, 1);
 		EXPECT_EQ(test_pgnShort, encodePgn(gamepos));
-		gamepos = collectPositions(dbase, 2);
+		gamepos = collectPositions(dbase, 1);
 		EXPECT_EQ(test_pgnLong, encodePgn(gamepos));
 	}
 }
 
 TEST_F(Test_Scidbase, new_compose_delete_get_Filter) {
 	scidBaseT dbase;
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::MEMORY, FMODE_Memory));
+	ASSERT_EQ(OK, dbase.open("MEMORY", FMODE_Create, "Memory"));
 
 	std::vector<std::pair<std::string, bool>> tests = {
-	    {" dbfilter", false},   {"dbfilter ", false}, {"+dbfilter+", true},
+	    {" dbfilter", false},   {"dbfilter ", false}, {"dbfilter", true},
 	    {"tree", true},         {"+dbfilter", false}, {"++dbfilter", false},
 	    {"++dbfilter+", false}, {"", false},          {"+", false},
 	    {"++", false},          {"+++", false},       {" +", false},
@@ -494,10 +477,12 @@ TEST_F(Test_Scidbase, new_compose_delete
 			auto composed = dbase.composeFilter(e.first, mask);
 			EXPECT_EQ(valid, dbase.getFilter(composed) != nullptr);
 			if (valid) {
+				auto [f1, f2] = dbase.getFilterComponents(composed);
+				EXPECT_EQ(f1, e.first);
+				EXPECT_EQ(f2, mask);
+				EXPECT_EQ(e.first, dbase.composeFilter(composed, ""));
+
 				mask = e.first;
-				if (e.first[0] != '+') {
-					EXPECT_EQ(e.first, dbase.composeFilter(composed, ""));
-				}
 			}
 		}
 	};
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_sortcache.cpp 1:4.7.4+dfsg1-2/gtest/test_sortcache.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_sortcache.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_sortcache.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -28,7 +28,7 @@ TEST_F(Test_SortCache, select_sortedPosi
 	// Open the test database
 	scidBaseT dbase;
 	static const char* database = SCID_TESTDIR "res_database";
-	ASSERT_EQ(OK, dbase.Open(ICodecDatabase::SCID4, FMODE_ReadOnly, database));
+	ASSERT_EQ(OK, dbase.open("SCID4", FMODE_ReadOnly, database));
 	ASSERT_NE(0U, dbase.numGames());
 	const NameBase* nb = dbase.getNameBase();
 	std::uniform_int_distribution<> rndID(0, dbase.numGames() - 1);
@@ -74,19 +74,19 @@ TEST_F(Test_SortCache, select_sortedPosi
 		rec[8] = ie->GetEcoCode();
 		rec[9] = RESULT_SORT[ie->GetResult()];
 		rec[10] = ie->GetNumHalfMoves();
-		rec[11] = ie->GetWhiteElo(nb) + ie->GetBlackElo(nb);
+		rec[11] = ie->GetWhiteElo() + ie->GetBlackElo();
 		rec[12] = ie->GetSite(); // NAME COUNTRY
 		rec[13] = ie->GetDeleteFlag() ? 1 : 0;
 		rec[14] = ie->GetEventDate();
-		rec[15] = ie->GetWhiteElo(nb);
-		rec[16] = ie->GetBlackElo(nb);
+		rec[15] = ie->GetWhiteElo();
+		rec[16] = ie->GetBlackElo();
 		rec[17] = ie->GetCommentCount();
 		rec[18] = ie->GetVariationCount();
 		rec[19] = ie->GetNagCount();
 		rec[20] = ie->GetResult() == RESULT_White ? 1 : 0;
 		rec[21] = ie->GetResult() == RESULT_Draw ? 1 : 0;
 		rec[22] = ie->GetResult() == RESULT_Black ? 1 : 0;
-		rec[23] = ie->GetRating(nb);
+		rec[23] = ie->GetRating();
 	}
 
 	auto sort_vIndex = [&vIndex, nb, &getFieldIdx](std::string criteria) {
@@ -212,7 +212,7 @@ TEST_F(Test_SortCache, select_sortedPosi
 			// Test scidBaseT::createSortCache()
 			if (!pass) {
 				auto sc = dbase.createSortCache(crit);
-				EXPECT_EQ(validCriteria, sc != nullptr);
+				EXPECT_EQ(validCriteria, sc);
 			}
 
 			// Test scidBaseT::releaseSortCache()
@@ -222,6 +222,6 @@ TEST_F(Test_SortCache, select_sortedPosi
 	}
 
 	// Test scidBaseT::Close() when background threads are running.
-	EXPECT_NE(nullptr, dbase.createSortCache("w+s-"));
-	EXPECT_NE(nullptr, dbase.createSortCache("i-d-n+"));
+	EXPECT_TRUE(dbase.createSortCache("w+s-"));
+	EXPECT_TRUE(dbase.createSortCache("i-d-n+"));
 }
diff -pruN 1:4.7.0+dfsg1-2/gtest/test_stored.cpp 1:4.7.4+dfsg1-2/gtest/test_stored.cpp
--- 1:4.7.0+dfsg1-2/gtest/test_stored.cpp	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.7.4+dfsg1-2/gtest/test_stored.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021  Fulvio Benini.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+ * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "board_def.h"
+#include "position.h"
+#include "stored.h"
+#include <gtest/gtest.h>
+
+// 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4 4.Qc2 O-O
+std::tuple<squareT, squareT, bool> line63[] = {
+    {D2, D4, false}, {G8, F6, false}, {C2, C4, false}, {E7, E6, false},
+    {B1, C3, false}, {F8, B4, false}, {D1, C2, false}, {NS, NS, true}};
+const auto line63_fen =
+    "rnbq1rk1/pppp1ppp/4pn2/8/1bPP4/2N5/PPQ1PPPP/R1B1KBNR w KQ - 0 5";
+
+auto cmp_moves = [&](auto move, auto line) {
+	if (line.isCastle())
+		return std::get<2>(move) && line.getFrom() < line.getTo();
+
+	return std::get<0>(move) == line.getFrom() &&
+	       std::get<1>(move) == line.getTo();
+};
+
+TEST(Test_StoredLine, classify) {
+	auto code = StoredLine::classify([&](auto begin, auto end) {
+		return std::equal(std::begin(line63), std::end(line63), begin, end,
+		                  cmp_moves);
+	});
+	EXPECT_EQ(63, code);
+}
+
+TEST(Test_StoredLine, getMove) {
+	auto it = std::begin(line63);
+	unsigned i = 0;
+	while (auto move = StoredLine::getMove(63, i++)) {
+		EXPECT_TRUE(cmp_moves(*it++, move));
+	}
+}
+
+TEST(Test_StoredLine, match) {
+	Position pos;
+	pos.ReadFromFEN(line63_fen);
+	const auto stored = StoredLine(pos.GetBoard(), pos.GetToMove());
+	EXPECT_EQ(-1, stored.match(62));
+	EXPECT_EQ(8, stored.match(63));
+	EXPECT_EQ(8, stored.match(64));
+	EXPECT_EQ(-2, stored.match(65));
+}
\ No newline at end of file
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_BD_BackStart.png and 1:4.7.4+dfsg1-2/img/buttons/tb_BD_BackStart.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_BD_exitvar.png and 1:4.7.4+dfsg1-2/img/buttons/tb_BD_exitvar.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_BD_ForwardEnd.png and 1:4.7.4+dfsg1-2/img/buttons/tb_BD_ForwardEnd.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_BD_Fullscreen.png and 1:4.7.4+dfsg1-2/img/buttons/tb_BD_Fullscreen.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_BD_Scorebar.png and 1:4.7.4+dfsg1-2/img/buttons/tb_BD_Scorebar.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_addbestline.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_addbestline.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_addbestmove.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_addbestmove.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_addlines.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_addlines.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_add.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_add.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_clone.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_clone.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_config.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_config.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_delete.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_delete.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_lock.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_lock.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_network.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_network.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_off.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_off.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_on.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_on.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_eng_reload.png and 1:4.7.4+dfsg1-2/img/buttons/tb_eng_reload.png differ
Binary files 1:4.7.0+dfsg1-2/img/buttons/tb_tabmenu.gif and 1:4.7.4+dfsg1-2/img/buttons/tb_tabmenu.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_bfa.gif and 1:4.7.4+dfsg1-2/img/flags/flag_bfa.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_bhu.gif and 1:4.7.4+dfsg1-2/img/flags/flag_bhu.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_caf.gif and 1:4.7.4+dfsg1-2/img/flags/flag_caf.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cam.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cam.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cay.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cay.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cgo.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cgo.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cha.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cha.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_civ.gif and 1:4.7.4+dfsg1-2/img/flags/flag_civ.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cod.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cod.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cok.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cok.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cpv.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cpv.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_cta.gif and 1:4.7.4+dfsg1-2/img/flags/flag_cta.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_dji.gif and 1:4.7.4+dfsg1-2/img/flags/flag_dji.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_dma.gif and 1:4.7.4+dfsg1-2/img/flags/flag_dma.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_eqg.gif and 1:4.7.4+dfsg1-2/img/flags/flag_eqg.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_gab.gif and 1:4.7.4+dfsg1-2/img/flags/flag_gab.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_gci.gif and 1:4.7.4+dfsg1-2/img/flags/flag_gci.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_gnb.gif and 1:4.7.4+dfsg1-2/img/flags/flag_gnb.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_gui.gif and 1:4.7.4+dfsg1-2/img/flags/flag_gui.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_gum.gif and 1:4.7.4+dfsg1-2/img/flags/flag_gum.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_isv.gif and 1:4.7.4+dfsg1-2/img/flags/flag_isv.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_jci.gif and 1:4.7.4+dfsg1-2/img/flags/flag_jci.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_kos.gif and 1:4.7.4+dfsg1-2/img/flags/flag_kos.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_ksa.gif and 1:4.7.4+dfsg1-2/img/flags/flag_ksa.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_kvx.gif and 1:4.7.4+dfsg1-2/img/flags/flag_kvx.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_lbn.gif and 1:4.7.4+dfsg1-2/img/flags/flag_lbn.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_lby.gif and 1:4.7.4+dfsg1-2/img/flags/flag_lby.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_lca.gif and 1:4.7.4+dfsg1-2/img/flags/flag_lca.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_les.gif and 1:4.7.4+dfsg1-2/img/flags/flag_les.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_lva.gif and 1:4.7.4+dfsg1-2/img/flags/flag_lva.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_mac.gif and 1:4.7.4+dfsg1-2/img/flags/flag_mac.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_maw.gif and 1:4.7.4+dfsg1-2/img/flags/flag_maw.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_mdv.gif and 1:4.7.4+dfsg1-2/img/flags/flag_mdv.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_mtn.gif and 1:4.7.4+dfsg1-2/img/flags/flag_mtn.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_mwi.gif and 1:4.7.4+dfsg1-2/img/flags/flag_mwi.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_nam.gif and 1:4.7.4+dfsg1-2/img/flags/flag_nam.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_nga.gif and 1:4.7.4+dfsg1-2/img/flags/flag_nga.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_nig.gif and 1:4.7.4+dfsg1-2/img/flags/flag_nig.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_nru.gif and 1:4.7.4+dfsg1-2/img/flags/flag_nru.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_oma.gif and 1:4.7.4+dfsg1-2/img/flags/flag_oma.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_ple.gif and 1:4.7.4+dfsg1-2/img/flags/flag_ple.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_plw.gif and 1:4.7.4+dfsg1-2/img/flags/flag_plw.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_rou.gif and 1:4.7.4+dfsg1-2/img/flags/flag_rou.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_rwa.gif and 1:4.7.4+dfsg1-2/img/flags/flag_rwa.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_sam.gif and 1:4.7.4+dfsg1-2/img/flags/flag_sam.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_sgp.gif and 1:4.7.4+dfsg1-2/img/flags/flag_sgp.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_skn.gif and 1:4.7.4+dfsg1-2/img/flags/flag_skn.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_sol.gif and 1:4.7.4+dfsg1-2/img/flags/flag_sol.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_som.gif and 1:4.7.4+dfsg1-2/img/flags/flag_som.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_ssd.gif and 1:4.7.4+dfsg1-2/img/flags/flag_ssd.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_stp.gif and 1:4.7.4+dfsg1-2/img/flags/flag_stp.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_swz.gif and 1:4.7.4+dfsg1-2/img/flags/flag_swz.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_tan.gif and 1:4.7.4+dfsg1-2/img/flags/flag_tan.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_tga.gif and 1:4.7.4+dfsg1-2/img/flags/flag_tga.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_tls.gif and 1:4.7.4+dfsg1-2/img/flags/flag_tls.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_tog.gif and 1:4.7.4+dfsg1-2/img/flags/flag_tog.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_tpe.gif and 1:4.7.4+dfsg1-2/img/flags/flag_tpe.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_tto.gif and 1:4.7.4+dfsg1-2/img/flags/flag_tto.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_van.gif and 1:4.7.4+dfsg1-2/img/flags/flag_van.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_vin.gif and 1:4.7.4+dfsg1-2/img/flags/flag_vin.gif differ
Binary files 1:4.7.0+dfsg1-2/img/flags/flag_wls.gif and 1:4.7.4+dfsg1-2/img/flags/flag_wls.gif differ
diff -pruN 1:4.7.0+dfsg1-2/InnoSetup.iss 1:4.7.4+dfsg1-2/InnoSetup.iss
--- 1:4.7.0+dfsg1-2/InnoSetup.iss	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/InnoSetup.iss	2022-07-09 05:14:42.000000000 +0000
@@ -1,5 +1,5 @@
 
-#define AppVersion '4.7.0'
+#define AppVersion '4.7.4'
 #define AppName    'Scid'
 #define TCLDIR     'C:\Tcl'
 
@@ -121,7 +121,7 @@ begin
       lines[8]  := '  Time 0'
       lines[9]  := '  URL  {}'
       lines[10] := '  UCI 1'
-      lines[11] := '  UCIoptions {}'
+      lines[11] := '  UCIoptions {{{Analysis Contempt} Off}}'
       lines[12] := '}'
       lines[13]  := ''
       lines[14]  := 'engine {'
@@ -133,7 +133,7 @@ begin
       lines[20]  := '  Time 0'
       lines[21]  := '  URL  {}'
       lines[22] := '  UCI 1'
-      lines[23] := '  UCIoptions {{Threads 2}}'
+      lines[23] := '  UCIoptions {{{Analysis Contempt} Off} {Threads 2}}'
       lines[24] := '}'
       lines[25]  := ''
       lines[26]  := 'engine {'
@@ -145,7 +145,7 @@ begin
       lines[32]  := '  Time 0'
       lines[33]  := '  URL  {}'
       lines[34] := '  UCI 1'
-      lines[35] := '  UCIoptions {{Threads 4}}'
+      lines[35] := '  UCIoptions {{{Analysis Contempt} Off} {Threads 4}}'
       lines[36] := '}'
       SaveStringsToFile(filename,lines,true);
   end
diff -pruN 1:4.7.0+dfsg1-2/Makefile.conf 1:4.7.4+dfsg1-2/Makefile.conf
--- 1:4.7.0+dfsg1-2/Makefile.conf	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/Makefile.conf	2022-07-09 05:14:42.000000000 +0000
@@ -130,12 +130,9 @@ all: all_scid all_engines
 
 all_scid: scid $(SCRIPTS) $(EXECS)
 
-all_engines: scidlet phalanx-scid
+all_engines: phalanx-scid
 
 
-scidlet: src/engine.o src/recog.o src/misc.o src/position.o
-	$(COMPILE) $(CPP_FLAGS) -o scidlet engines/scidlet/scidlet.cpp src/engine.o src/recog.o src/misc.o src/position.o
-
 phalanx-scid:
 	cd engines/phalanx-scid/ && $(MAKE) && cd ../../
 
diff -pruN 1:4.7.0+dfsg1-2/Makefile.vc 1:4.7.4+dfsg1-2/Makefile.vc
--- 1:4.7.0+dfsg1-2/Makefile.vc	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/Makefile.vc	2022-07-09 05:14:42.000000000 +0000
@@ -171,10 +171,8 @@ SCID_EXECS = \
 #	files that most of the Scid programs use.
 #
 SCID_OBJS = \
-	$(TMP_DIR)\bytebuf.obj \
 	$(TMP_DIR)\codec_scid4.obj \
 	$(TMP_DIR)\game.obj \
-	$(TMP_DIR)\index.obj \
 	$(TMP_DIR)\matsig.obj \
 	$(TMP_DIR)\misc.obj \
 	$(TMP_DIR)\position.obj \
@@ -224,7 +222,7 @@ SCID_XOBJS = \
 OPTIMIZE	= -ZI -Fd$(TMP_DIR)\ -Od
 !message *** Build 'Debug' Version	= yes
 !else
-OPTIMIZE	= -Ox /EHsc
+OPTIMIZE	= -O2 /EHsc
 !message *** Build 'Release' Version	= yes
 !if "$(GLOBAL_OPT)" == "1"
 OPTIMIZE	= $(OPTIMIZE) -GL
@@ -258,7 +256,7 @@ PROFILE		= $(PROFILE) -DWIN32 -DWIN32_LE
 #	-c	:Compile Without linking
 #
 CFLAGS		= $(PROFILE) $(OPTIMIZE) $(WARNINGS) \
-			$(SCID_INCLUDES) -c -nologo
+			$(SCID_INCLUDES) -c -nologo -std:c++17
 !message *** CFLAGS: $(CFLAGS)
 
 
diff -pruN 1:4.7.0+dfsg1-2/README.md 1:4.7.4+dfsg1-2/README.md
--- 1:4.7.0+dfsg1-2/README.md	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/README.md	2022-07-09 05:14:42.000000000 +0000
@@ -1,6 +1,7 @@
 [![Build Status](https://travis-ci.org/benini/scid.svg?branch=github)](https://travis-ci.org/benini/scid)
 [![codecov](https://codecov.io/gh/benini/scid/branch/github/graph/badge.svg)](https://codecov.io/gh/benini/scid)
 [![coverity](https://scan.coverity.com/projects/14455/badge.svg)](https://scan.coverity.com/projects/benini-scid)
+[![Build Status](https://dev.azure.com/beninifulvio/beninifulvio/_apis/build/status/benini.scid?branchName=github)](https://dev.azure.com/beninifulvio/beninifulvio/_build/latest?definitionId=1&branchName=github)
 [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://scid.sourceforge.net/doxygen/html/files.html)
 [![GitHub license](https://img.shields.io/badge/license-GPL-blue.svg)](https://sourceforge.net/p/scid/code/ci/master/tree/COPYING)
 
diff -pruN 1:4.7.0+dfsg1-2/resources/win/scid.manifest 1:4.7.4+dfsg1-2/resources/win/scid.manifest
--- 1:4.7.0+dfsg1-2/resources/win/scid.manifest	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.7.4+dfsg1-2/resources/win/scid.manifest	2022-07-09 05:14:42.000000000 +0000
@@ -0,0 +1,2 @@
+﻿<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security></trustInfo><application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings><dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware></windowsSettings></application></assembly>
\ No newline at end of file
diff -pruN 1:4.7.0+dfsg1-2/resources/win/scid.rc 1:4.7.4+dfsg1-2/resources/win/scid.rc
--- 1:4.7.0+dfsg1-2/resources/win/scid.rc	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/resources/win/scid.rc	2022-07-09 05:14:42.000000000 +0000
@@ -16,8 +16,8 @@ SCID                    ICON
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,7,0,0
- PRODUCTVERSION 4,7,0,0
+ FILEVERSION 4,7,4,0
+ PRODUCTVERSION 4,7,4,0
 BEGIN
     BLOCK "StringFileInfo"
     BEGIN
@@ -25,11 +25,11 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "http://sourceforge.net/projects/scid/"
             VALUE "FileDescription", "Chess Database"
-            VALUE "FileVersion", "4.7.0.0"
-            VALUE "LegalCopyright", "Copyright (C) 1999-2019"
+            VALUE "FileVersion", "4.7.4.0"
+            VALUE "LegalCopyright", "Copyright (C) 1999-2022"
             VALUE "OriginalFilename", "scid.exe"
             VALUE "ProductName", "Scid"
-            VALUE "ProductVersion", "4.7.0.0"
+            VALUE "ProductVersion", "4.7.4.0"
         END
     END
     BLOCK "VarFileInfo"
diff -pruN 1:4.7.0+dfsg1-2/src/attacks.h 1:4.7.4+dfsg1-2/src/attacks.h
--- 1:4.7.0+dfsg1-2/src/attacks.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/attacks.h	2022-07-09 05:14:42.000000000 +0000
@@ -15,9 +15,7 @@
 #ifndef SCID_ATTACKS_H
 #define SCID_ATTACKS_H
 
-#ifndef SCID_COMMON_H
-#include "common.h"
-#endif
+#include "board_def.h"
 
 
 // NOTE: These arrays have been automatically generated. Don't change
diff -pruN 1:4.7.0+dfsg1-2/src/board_def.h 1:4.7.4+dfsg1-2/src/board_def.h
--- 1:4.7.0+dfsg1-2/src/board_def.h	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/board_def.h	2022-07-09 05:14:42.000000000 +0000
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2000-2004  Shane Hudson..
+ *
+ * This file is part of Scid (Shane's Chess Information Database).
+ *
+ * Scid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Scid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ * Constants and definitions of the chess board.
+ */
+
+#pragma once
+
+typedef unsigned char pieceT;  // e.g ROOK or WK
+typedef unsigned char colorT;  // WHITE or BLACK
+typedef unsigned char squareT; // e.g. A3
+typedef unsigned char rankT;   // Chess board rank
+typedef unsigned char fyleT;   // Chess board file
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// PIECES COLORS
+const unsigned NUM_COLOR_TYPES = 2;
+const colorT WHITE = 0, BLACK = 1, NOCOLOR = 2;
+const char COLOR_CHAR[3] = {'W', 'B', '_'};
+
+inline colorT color_Flip(colorT c) { return 1 - c; }
+
+inline char color_Char(colorT c) { return COLOR_CHAR[c]; }
+
+// PIECE TYPES (without color; same value as a white piece)
+const pieceT INVALID_PIECE = 0, KING = 1, QUEEN = 2, ROOK = 3, BISHOP = 4,
+             KNIGHT = 5, PAWN = 6;
+
+// PIECES:
+//   Note that color(x) == ((x & 0x8) >> 3)  and  type(x) == (x & 0x7)
+//   EMPTY is deliberately nonzero, and END_OF_BOARD is zero, so that
+//   a board can be used as a regular 0-terminated string, provided
+//   that board[NULL_SQUARE] == END_OF_BOARD, as it always should be.
+const pieceT EMPTY = 7;
+const pieceT END_OF_BOARD = 0;
+const pieceT WK = 1, WQ = 2, WR = 3, WB = 4, WN = 5, WP = 6;
+const pieceT BK = 9, BQ = 10, BR = 11, BB = 12, BN = 13, BP = 14;
+
+inline colorT piece_Color(pieceT p) {
+	return (p == EMPTY) ? NOCOLOR : ((p & 8) >> 3);
+}
+// Slightly faster piece_Color when we are sure the piece is not empty:
+inline colorT piece_Color_NotEmpty(pieceT p) { return (p & 8) >> 3; }
+
+inline pieceT piece_Type(pieceT p) { return (p & 7); }
+
+inline pieceT piece_Make(colorT c, pieceT p) { return ((c << 3) | (p & 7)); }
+
+// PIECE_CHAR[]: array of piece characters, capitals for White pieces.
+const char PIECE_CHAR[] = "xKQRBNP.xkqrbnpxMm";
+
+inline char piece_Char(pieceT p) { return PIECE_CHAR[piece_Type(p)]; }
+
+class PieceFromByte {
+    pieceT pieceFromByte_[256] = {};
+
+public:
+    constexpr PieceFromByte() {
+        for (auto& e : pieceFromByte_) {
+            e = EMPTY;
+        }
+        pieceFromByte_[(int)'K'] = WK;
+        pieceFromByte_[(int)'k'] = BK;
+        pieceFromByte_[(int)'Q'] = WQ;
+        pieceFromByte_[(int)'q'] = BQ;
+        pieceFromByte_[(int)'R'] = WR;
+        pieceFromByte_[(int)'r'] = BR;
+        pieceFromByte_[(int)'B'] = WB;
+        pieceFromByte_[(int)'b'] = BB;
+        pieceFromByte_[(int)'N'] = WN;
+        pieceFromByte_[(int)'n'] = BN;
+        pieceFromByte_[(int)'P'] = WP;
+        pieceFromByte_[(int)'p'] = BP;
+    };
+
+    pieceT operator[](unsigned char idx) const { return pieceFromByte_[idx]; }
+};
+constexpr inline auto pieceFromByte = PieceFromByte();
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// SQUARES
+const squareT A1 = 0, B1 = 1, C1 = 2, D1 = 3, E1 = 4, F1 = 5, G1 = 6, H1 = 7,
+              A2 = 8, B2 = 9, C2 = 10, D2 = 11, E2 = 12, F2 = 13, G2 = 14,
+              H2 = 15, A3 = 16, B3 = 17, C3 = 18, D3 = 19, E3 = 20, F3 = 21,
+              G3 = 22, H3 = 23, A4 = 24, B4 = 25, C4 = 26, D4 = 27, E4 = 28,
+              F4 = 29, G4 = 30, H4 = 31, A5 = 32, B5 = 33, C5 = 34, D5 = 35,
+              E5 = 36, F5 = 37, G5 = 38, H5 = 39, A6 = 40, B6 = 41, C6 = 42,
+              D6 = 43, E6 = 44, F6 = 45, G6 = 46, H6 = 47, A7 = 48, B7 = 49,
+              C7 = 50, D7 = 51, E7 = 52, F7 = 53, G7 = 54, H7 = 55, A8 = 56,
+              B8 = 57, C8 = 58, D8 = 59, E8 = 60, F8 = 61, G8 = 62, H8 = 63,
+              COLOR_SQUARE = 64, NULL_SQUARE = 65,
+              NS = 65; // NS is abbreviation for NULL_SQUARE.
+
+const rankT RANK_1 = 0, RANK_2 = 1, RANK_3 = 2, RANK_4 = 3, RANK_5 = 4,
+            RANK_6 = 5, RANK_7 = 6, RANK_8 = 7, NO_RANK = 64;
+
+const fyleT
+    // we use "fyle" instead of "file" to avoid confusion with disk files.
+    A_FYLE = 0,
+    B_FYLE = 1, C_FYLE = 2, D_FYLE = 3, E_FYLE = 4, F_FYLE = 5, G_FYLE = 6,
+    H_FYLE = 7, NO_FYLE = 64;
+
+inline rankT rank_FromChar(char c) {
+	if (c < '1' || c > '8') {
+		return NO_RANK;
+	} else
+		return (c - '1');
+}
+
+inline fyleT fyle_FromChar(char c) {
+	if (c < 'a' || c > 'h') {
+		return NO_FYLE;
+	} else
+		return (c - 'a');
+}
+
+inline squareT square_Make(fyleT f, rankT r) { return ((r << 3) | f); }
+
+inline fyleT square_Fyle(squareT sq) { return (sq & 0x7); }
+
+inline rankT square_Rank(squareT sq) { return ((sq >> 3) & 0x7); }
+
+constexpr squareT square_Relative(colorT c, squareT sq) {
+	return static_cast<squareT>(sq ^ (c * 56));
+}
+
+constexpr rankT rank_Relative(colorT c, rankT r) {
+	return static_cast<rankT>(r ^ (c * 7));
+}
diff -pruN 1:4.7.0+dfsg1-2/src/bytebuf.cpp 1:4.7.4+dfsg1-2/src/bytebuf.cpp
--- 1:4.7.0+dfsg1-2/src/bytebuf.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/bytebuf.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,149 +0,0 @@
-//////////////////////////////////////////////////////////////////////
-//
-//  FILE:       bytebuf.cpp
-//              ByteBuffer class for Scid.
-//
-//  Part of:    Scid (Shane's Chess Information Database)
-//  Version:    0.3
-//
-//  Notice:     Copyright (c) 1999  Shane Hudson.  All rights reserved.
-//
-//  Author:     Shane Hudson (sgh@users.sourceforge.net)
-//
-//////////////////////////////////////////////////////////////////////
-
-
-#include "bytebuf.h"
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::Empty():
-//      Empties the ByteBuffer.
-//
-void
-ByteBuffer::Empty()
-{
-    ReadPos = ByteCount = 0;
-    ExternalBuffer = NULL;
-    Buffer = AllocatedBuffer;
-    Current = Buffer;
-    Err = OK;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::BackToStart():
-//      Sets the ByteBuffer's read position back to the buffer start.
-//
-void
-ByteBuffer::BackToStart()
-{
-    ReadPos = 0;
-    Current = Buffer;
-    Err = OK;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::ProvideExternal():
-//      Provides an external buffer to use instead of the allocated
-//      buffer. This is used when the buffer is only going to be read
-//      and it would waste time (and degrade performance) to copy the
-//      data to the buffer's allocated space first.
-//
-void
-ByteBuffer::ProvideExternal (byte * data, size_t length)
-{
-    ExternalBuffer = data;
-    ByteCount = length;
-    ReadPos = 0;
-    Current = Buffer = ExternalBuffer;
-    Err = OK;
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::Skip():
-//      Skips over a specified number of bytes.
-void
-ByteBuffer::Skip (size_t length)
-{
-    ASSERT (Current != NULL);
-
-    if (ReadPos + length > ByteCount) { Err = ERROR_BufferRead; return; }
-    ReadPos += length;
-    Current += length;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::GetFixedString():
-//      Reads a fixed-length string from the buffer. A terminating
-//      null character is not added.
-void
-ByteBuffer::GetFixedString (char * str, size_t length)
-{
-    ASSERT(Current != NULL  &&  str != NULL);
-
-    if (Err != OK) { return; }
-    if (ReadPos + length > ByteCount) { Err = ERROR_BufferRead; return; }
-    ReadPos += length;
-    while (length > 0) {
-        *str = *Current; Current++; str++;
-        length--;
-    }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::PutFixedString():
-//      Writes a fixed-length string to the buffer. A terminating null
-//      character is not written, unless it was part of the string.
-void
-ByteBuffer::PutFixedString (const char * str, size_t length)
-{
-    ASSERT(Current != NULL  &&  str != NULL);
-    if (ByteCount + length > BufferSize) { Err = ERROR_BufferFull; return; }
-    ByteCount += length;
-    while (length > 0) {
-        *Current = *str; Current++; str++;
-        length--;
-    }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::GetTerminatedString():
-//    Get a null-terminated string.
-//    Just sets str to point to current, and then moves current
-//    to the end of the string, so the calling function can to
-//    duplicate the string itself if it needs to.
-//    The length returned does not include the trailing '\0'.
-void
-ByteBuffer::GetTerminatedString (char ** str)
-{
-    ASSERT(Current != NULL  &&  str != NULL);
-
-    *str = (char *) Current;
-    while (*Current) {
-        Current++;
-        ReadPos++;
-    }
-    Current++;
-    ReadPos++;
-    if (ReadPos > ByteCount) { Err = ERROR_BufferRead; }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ByteBuffer::PutTerminatedString():
-//      Writes a null-terminated string to the buffer, including
-//      the null character.
-void
-ByteBuffer::PutTerminatedString (const char * str)
-{
-    ASSERT(Current != NULL  &&  str != NULL);
-    while (*str) {
-        if (ByteCount >= BufferSize) { Err = ERROR_BufferFull; return; }
-        *Current = *str; Current++; str++;
-        ByteCount++;
-    }
-    *Current = 0; Current++; ByteCount++;
-}
-
-//////////////////////////////////////////////////////////////////////
-//  EOF: bytebuf.cpp
-//////////////////////////////////////////////////////////////////////
diff -pruN 1:4.7.0+dfsg1-2/src/bytebuf.h 1:4.7.4+dfsg1-2/src/bytebuf.h
--- 1:4.7.0+dfsg1-2/src/bytebuf.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/bytebuf.h	2022-07-09 05:14:42.000000000 +0000
@@ -1,102 +1,372 @@
-//////////////////////////////////////////////////////////////////////
-//
-//  FILE:       bytebuf.h
-//              ByteBuffer class.
-//
-//  Part of:    Scid (Shane's Chess Information Database)
-//  Version:    0.2
-//
-//  Notice:     Copyright (c) 1999  Shane Hudson.  All rights reserved.
-//
-//  Author:     Shane Hudson (sgh@users.sourceforge.net)
-//
-//////////////////////////////////////////////////////////////////////
-
-// The ByteBuffer class is used to read and write binary buffers.
-// It is primarily used in Scid for storing in-memory the contents of
-// a game as it is represented on-disk in the gamedata file (gfile).
-
-
-#ifndef SCID_BYTEBUF_H
-#define SCID_BYTEBUF_H
-
-#include "common.h"
-
-class ByteBuffer
-{
- private:
-    //----------------------------------
-    //  TextBuffer:  Data Structures
-    //----------------------------------
-
-    size_t ReadPos;
-    size_t ByteCount;
-    size_t BufferSize;
-    byte * Buffer;
-    byte * Current;
-    byte * AllocatedBuffer;
-    byte * ExternalBuffer;
-
-    errorT Err;
-    
-    //----------------------------------
-    //  TextBuffer:  Public Functions
-    //----------------------------------
- public:
-    ByteBuffer(size_t length) : BufferSize(length), AllocatedBuffer(new byte[length]) { Empty(); }
-    ~ByteBuffer() { delete[] AllocatedBuffer; }
-
 /*
- * ProvideExternal is super dangerous:
- * - GFile::ReadGame will change "data" when reading another game
- * - At that point reading or __writing__ to the bytebuffer will create havoc
- * For example calling GFile::ReadGame(&bytebuffer, ...) followed by GFile::AddGame(&bytebuffer)
- * is __very__ bad
+ * Copyright (C) 2019  Fulvio Benini.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+ * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
-    void ProvideExternal (byte * data, size_t length);
 
-    errorT Status ()      { return Err; }
-    size_t GetByteCount() { return ByteCount; }
-    void   BackToStart ();
-    void   Skip (size_t value);
-    byte   GetByte () {
-        ASSERT(Current != NULL);
-        if (ReadPos >= ByteCount) { Err = ERROR_BufferRead; return 0; }
-        byte b = *Current;
-        Current++; ReadPos++;
-        return b;
-    }
-    void   GetFixedString (char *str, size_t length);
-    void   GetTerminatedString (char **str);
-    const byte* getData() { return Buffer; }
+/** @file
+ * Defines a ByteBuffer class which will act like a std::string_view.
+ * The caller must ensure that a ByteBuffer object does not outlive the
+ * pointed-to data.
+ */
 
-/*
- * Writing to a bytebuffer is very error-prone
- * - Empty() must be called to be sure that Buffer points to the internal buffer
- *   and to clear the Error Flag;
- * - Status() must be called after every Put
+#pragma once
+
+#include "board_def.h"
+#include "error.h"
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <string_view>
+
+/**
+ * The Tag section is a list of <tag,value> variable length records:
+ * tag_length: 1-byte
+ * tag name: [0:240] bytes
+ * value_length: 1-byte
+ * value: [0:255] bytes
+ * The section is terminated with 1-byte with value 0, meaning that un empty
+ * tag name cannot be stored.
+ * Some common tags are encoded in one byte, using a tag_length value over 240
+ * and a tag name of 0 bytes.
  */
-    void Empty ();
-    void PutByte (byte value) {
-        ASSERT(Current != NULL);
-        ASSERT(Buffer == AllocatedBuffer);
-        if (Buffer == AllocatedBuffer  && ByteCount >= BufferSize) {
-            Err = ERROR_BufferFull; return;
-        }
-        *Current = value;
-        Current++; ByteCount++;
-    }
-    void PutFixedString (const char *str, size_t length);
-    void PutTerminatedString (const char *str);
-
-
-private:
-	ByteBuffer(const ByteBuffer&);
-	ByteBuffer& operator=(const ByteBuffer&);
+inline constexpr size_t MAX_TAG_LEN = 240;
+inline constexpr std::string_view commonTags[] = {
+    // 241, 242: Country
+    "WhiteCountry", "BlackCountry",
+    // 243: Annotator
+    "Annotator",
+    // 244: PlyCount
+    "PlyCount",
+    // 245: EventDate (plain text encoding)
+    "EventDate",
+    // 246, 247: Opening, Variation
+    "Opening", "Variation",
+    // 248-250: Setup and Source
+    "Setup", "Source", "SetUp"
+    // 252-254: spare for future use
+    // 255: Reserved for compact EventDate encoding
 };
 
-#endif  // #ifndef SCID_BYTEBUF_H
+template <typename SourceT, typename DestT>
+void encodeTags(const SourceT& tagList, DestT& dest) {
+	for (auto& tag : tagList) {
+		if (tag.first.length() == 0)
+			continue; // Cannot store empty tag names
+
+		const auto it = std::find(commonTags, std::end(commonTags), tag.first);
+		if (it != std::end(commonTags)) {
+			// Common tags are stored with just their 1-byte value [241:250]
+			const auto tagnum = std::distance(commonTags, it) + MAX_TAG_LEN + 1;
+			dest.emplace_back(static_cast<unsigned char>(tagnum));
+		} else {
+			// Other tags are stored as 1-byte length [1:240] + the tag name.
+			const auto tag_name = tag.first.data();
+			const auto length = std::min(tag.first.length(), MAX_TAG_LEN);
+			dest.emplace_back(static_cast<unsigned char>(length));
+			dest.insert(dest.end(), tag_name, tag_name + length);
+		}
+
+		// The value is stored as 1-byte length [0:255] + the data.
+		const auto value = tag.second.data();
+		const auto valueLen = std::min<size_t>(tag.second.length(), 255);
+		dest.emplace_back(static_cast<unsigned char>(valueLen));
+		dest.insert(dest.end(), value, value + valueLen);
+	}
+	dest.emplace_back(0);
+}
+
+/**
+ * The StartBoard section starts with a byte containing three flags:
+ * 0-bit: true if the game doesn't start with the standard board
+ * 1-bit: true if there are pawn to queen promotions
+ * 2-bit: true if there are pawn to non-queen promotions.
+ * If the 0-bit is true, the byte is followed by a null terminated string
+ * containing the FEN of the start position.
+ */
+template <typename DestT>
+void encodeStartBoard(bool promoFlag, bool underpromoFlag, const char* FEN,
+                      DestT& dest) {
+	char flags = 0;
+	if (FEN) {
+		flags += 1;
+	}
+	if (promoFlag) {
+		flags += 2;
+	}
+	if (underpromoFlag) {
+		flags += 4;
+	}
+	dest.emplace_back(flags);
+	if (FEN) {
+		const auto len = std::strlen(FEN) + 1; // Include the null char
+		dest.insert(dest.end(), FEN, FEN + len);
+	}
+}
+
+class ByteBuffer {
+	const unsigned char* data_;
+	const unsigned char* const end_;
+
+public:
+	ByteBuffer(const unsigned char* data, size_t length)
+	    : data_(data), end_(data + length) {
+		assert(data_ || data_ == end_);
+	}
+
+	explicit operator bool() const { return data_ != end_; }
+
+	const unsigned char* data() const { return data_; }
+	size_t size() const { return std::distance(data_, end_); }
+
+	/// Decodes the tag pairs not stored into the index.
+	/// @param fn: a function that should accept 2 parameters
+	///            (string_view tag_name, string_view tag_value)
+	///            and that will be called for each tag pair.
+	template <typename FuncT> errorT decodeTags(FuncT fn) {
+		if (data_ == end_)
+			return ERROR_BufferRead;
+
+		auto it = data_;
+		for (;;) {
+			const auto tagLen = *it++;
+			if (tagLen == 0) {
+				data_ = it;
+				return OK; // Reached the end of the tags section
+			}
+
+			const char* tag = nullptr;
+			size_t tagID = 0;
+			if (tagLen > MAX_TAG_LEN) {
+				// A common tag name, not explicitly stored
+				tagID = tagLen - MAX_TAG_LEN - 1;
+			} else {
+				tag = reinterpret_cast<const char*>(it);
+				it += tagLen;
+			}
+
+			if (it >= end_)
+				return ERROR_Decode;
+
+			// 255 was a special 3-bytes encoding of EventDate used in SCID2
+			const auto valueLen = (tagLen != 255) ? *it++ : 3;
+			const char* value = reinterpret_cast<const char*>(it);
+			it += valueLen;
+			if (it >= end_)
+				return ERROR_Decode;
+
+			if (tag) {
+				fn(std::string_view(tag, tagLen),
+				   std::string_view(value, valueLen));
+			} else if (tagID < 10) {
+				fn(commonTags[tagID], std::string_view(value, valueLen));
+			}
+		}
+	}
+
+	/// Decodes the start position.
+	/// @returns OK on success and the FEN of the start position (nullptr for
+	///          the standard starting position).
+	/// To decode the moves the correct index should be assigned to each piece:
+	/// they are assigned from left to right, but the king should always have
+	/// index 0. It is therefore swapped with the piece occupying the index 0.
+	/// For example with "rnb1k2Q/1p5p/p7/4p3/4q3/8/PPP2R1P/2K5 b" the black
+	/// rook on A8 gets index 3, the black night on B8 gets index 1 .... and the
+	/// white queen on H8 gets index 6, the pawn on A2 gets index 1 ...
+	std::pair<errorT, const char*> decodeStartBoard() {
+		if (data_ == end_)
+			return {ERROR_Decode, nullptr};
+
+		const auto flags = *data_++;
+		if ((flags & 1) == 0)
+			return {OK, nullptr};
+
+		if (const auto FEN = GetTerminatedString())
+			return {OK, FEN};
+
+		return {ERROR_Decode, nullptr};
+	}
+
+	/// Reads a null-terminated string from the buffer.
+	const char* GetTerminatedString() {
+		const char* res = reinterpret_cast<const char*>(data_);
+		data_ = std::find(data_, end_, 0);
+		if (data_ == end_)
+			return nullptr;
+
+		++data_; // skip the null char
+		return res;
+	}
+
+	/// Extract the next move.
+	/// @returns a std::pair containing OK and the move value.
+	///          Returns ERROR_EndOfMoveList when the end of the game is
+	///          reached, an error otherwise.
+	template <typename MoveFn, typename CommentFn, typename VariationFn,
+	          typename NagFn>
+	std::pair<errorT, unsigned char>
+	nextMove(int varDepth, MoveFn acceptMove, CommentFn commentMarker,
+	         VariationFn changeVar, NagFn addNag) {
+		// The king has always index 0 and only 11 possible moves (8 destination
+		// squares, 2 castle moves and the null move). The unused codes 11-15
+		// represent special markers for nags, comments and variations.
+		// Note: 2-bytes queen moves use the second byte as (64 + destination
+		// square) to avoid interferences with these markers. However the byte
+		// immediatly after the ENCODE_NAG can have any possible value.
+		enum {
+			ENCODE_NAG = 11,
+			ENCODE_COMMENT,
+			ENCODE_START_MARKER,
+			ENCODE_END_MARKER,
+			ENCODE_END_GAME
+		};
+
+		auto it = data_;
+		for (; it != end_; ++it) {
+			switch (*it) {
+			case ENCODE_NAG:
+				if (++it == end_) {
+					return {ERROR_Decode, 0}; // ERROR: missing nag
+				}
+				if (!addNag(*it)) {
+					return {ERROR_Decode, 0}; // ERROR: marker decoding
+				}
+				continue;
+
+			case ENCODE_COMMENT:
+				commentMarker();
+				continue;
+
+			case ENCODE_START_MARKER:
+				++varDepth;
+				if (!changeVar(true)) {
+					return {ERROR_Decode, 0}; // ERROR: variation
+				}
+				continue;
+
+			case ENCODE_END_MARKER:
+				if (varDepth == 0) {
+					return {ERROR_Decode, 0}; // ERROR: end marker in main line
+				}
+				--varDepth;
+				if (!changeVar(false)) {
+					return {ERROR_Decode, 0}; // ERROR: variation
+				}
+				continue;
+
+			case ENCODE_END_GAME:
+				if (varDepth != 0) {
+					return {ERROR_Decode, 0}; // ERROR: unexpected end of game
+				}
+				data_ = ++it;
+				return {ERROR_EndOfMoveList, 0}; // SUCCESS: end of game
+
+			default:
+				if (acceptMove(varDepth)) {
+					data_ = it;
+					return {OK, *data_++}; // SUCCESS
+				}
+			}
+		}
+		return {ERROR_Decode, 0}; // ERROR: missing ENCODE_END_GAME
+	}
+
+	/// Find the next move in the current line.
+	/// Ignore variations, comments and nags.
+	std::pair<errorT, unsigned char> nextLineMove() {
+		return nextMove(
+		    0, [](auto varDepth) { return varDepth == 0; },
+		    [] {},                     // Ignore comments
+		    [](auto) { return true; }, // Ignore variations
+		    [](auto) { return true; }  // Ignore nags
+		);
+	}
+
+	/// Decode a move encoded in SCID4 format.
+	/// Excluding queens, the other chess pieces cannot reach more than 16
+	/// target squares from any given position. This allow to store the target
+	/// square of a move into 4 bits, as an index of all the reachable squares.
+	/// @param movingPiece: the type (PAWN, BISHOP, etc.) of the piece to move.
+	/// @param from: the square where is the piece to move.
+	/// @param moveCode: the SCID4 encoding of the move (a 0-15 value).
+	/// @returns a pair containing the destination square and the new type of
+	///          the piece for promotions (INVALID_PIECE for normal moves).
+	///          Special moves are returned as:
+	///          - castle kingside: {from, KING}
+	///          - castle queenside: {from, QUEEN}
+	///          - null move: {from, PAWN}
+	/// On error returns an invalid square (<0 or >63) or {from, INVALID_PIECE}.
+	std::pair<int, pieceT> decodeMove(colorT toMove, pieceT movingPiece,
+	                                  squareT from, unsigned char moveCode) {
+		moveCode &= 0x0F;
+		switch (movingPiece) {
+		case PAWN: {
+			static const pieceT promoPiece[] = {
+			    INVALID_PIECE, INVALID_PIECE, INVALID_PIECE, QUEEN,
+			    QUEEN,         QUEEN,         ROOK,          ROOK,
+			    ROOK,          BISHOP,        BISHOP,        BISHOP,
+			    KNIGHT,        KNIGHT,        KNIGHT,        INVALID_PIECE};
+			static const int8_t sqdiff[] = {7, 8, 9, 7, 8, 9, 7, 8,
+			                                9, 7, 8, 9, 7, 8, 9, 16};
+			int to = (toMove == WHITE) ? from + sqdiff[moveCode]
+			                           : from - sqdiff[moveCode];
+			return {to, promoPiece[moveCode]};
+		}
+		case BISHOP: {
+			int fylediff = square_Fyle(moveCode) - square_Fyle(from);
+			int to = (moveCode >= 8) ? from - 7 * fylediff
+			                         : from + 9 * fylediff;
+			return {to, INVALID_PIECE};
+		}
+		case KNIGHT: {
+			static const int8_t sqdiff[] = {0,  -17, -15, -10, -6, 6, 10, 15,
+			                                17, 0,   0,   0,   0,  0, 0,  0};
+			return {from + sqdiff[moveCode], INVALID_PIECE};
+		}
+		case QUEEN:
+			if (moveCode == square_Fyle(from)) { // 2 BYTES MOVE
+				int to = (data_ != end_) ? *data_++ : 0;
+				return {to - 64, INVALID_PIECE};
+			}
+			[[fallthrough]];
+		case ROOK: {
+			int to = (moveCode >= 8) // a vertical move
+			             ? square_Make(square_Fyle(from), (moveCode - 8))
+			             : square_Make(moveCode, square_Rank(from));
+			return {to, INVALID_PIECE};
+		}
+		case KING:
+			if (moveCode == 0) // NULL MOVE
+				return {from, PAWN};
+
+			if (moveCode <= 8) {
+				static const int8_t sqdiff[] = {0, -9, -8, -7, -1, 1, 7, 8, 9};
+				return {from + sqdiff[moveCode], INVALID_PIECE};
+			}
+
+			if (moveCode == 9) // CASTLE QUEENSIDE
+				return {from, QUEEN};
+
+			if (moveCode == 10) // CASTLE KINGSIDE
+				return {from, KING};
+		}
 
-//////////////////////////////////////////////////////////////////////
-//  EOF: bytebuf.h
-//////////////////////////////////////////////////////////////////////
+		return {from, INVALID_PIECE}; // decode error
+	}
+};
diff -pruN 1:4.7.0+dfsg1-2/src/codec.h 1:4.7.4+dfsg1-2/src/codec.h
--- 1:4.7.0+dfsg1-2/src/codec.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec.h	2022-07-09 05:14:42.000000000 +0000
@@ -25,14 +25,12 @@
 #ifndef CODEC_H
 #define CODEC_H
 
+#include "bytebuf.h"
 #include "common.h"
+#include "namebase.h"
 #include <string>
 #include <vector>
 
-class Game;
-class Index;
-class IndexEntry;
-class NameBase;
 class Progress;
 
 /**
@@ -82,42 +80,44 @@ public:
 	getExtraInfo() const = 0;
 
 	/**
-	 * Fetches the data of a game (excluding index info), encoded in native
-	 * format.
+	 * Store an extra information about the database (type, description, etc..)
+	 */
+	virtual errorT setExtraInfo(const char* tagname, const char* new_value) = 0;
+
+	/**
+	 * Fetches the data (encoded in native format) of a game.
+	 * Invoking another function of the codec object invalidates the data.
+	 * @e getGamesMoves() returns only the moves' data, without extra tags and
+	 * comments, and it might be faster with some codecs.
 	 * @param offset: offset of the requested game.
 	 * @param length: length of the game data (in bytes).
 	 * @returns
 	 * - a pointer to the game data.
 	 * - 0 (nullptr) on error.
 	 */
-	virtual const byte* getGameData(uint64_t offset, uint32_t length) = 0;
-
-	/**
-	 * Add a game to the database.
-	 * @param srcIe:   valid pointer to the header data.
-	 * @param srcNb:   valid pointer to the NameBase containing srcIe's names.
-	 * @param srcData: valid pointer to a buffer containing the game data
-	 *                 (encoded in native format).
-	 * @param dataLen: length of the game data (in bytes).
-	 * @returns OK if successful or an error code.
-	 */
-	virtual errorT addGame(const IndexEntry* srcIe, const NameBase* srcNb,
-	                       const byte* srcData, size_t dataLen) = 0;
+	virtual ByteBuffer getGameData(uint64_t offset, uint32_t length) = 0;
+	virtual ByteBuffer getGameMoves(IndexEntry const& ie) = 0;
 
 	/**
 	 * Add a game to the database.
-	 * @param game: valid pointer to a Game object with the data of the game.
+	 * @param ie:   the header data of the source game.
+	 * @param tags: contains 5 of the Seven Tag Roster.
+	 * @param data: the data (encoded in native format) of the game.
 	 * @returns OK if successful or an error code.
 	 */
-	virtual errorT addGame(Game* game) = 0;
+	virtual errorT addGame(IndexEntry const& ie, TagRoster const& tags,
+	                       ByteBuffer const& data) = 0;
 
 	/**
 	 * Replaces a game in the database.
-	 * @param game:     valid pointer to a Game object with the new data.
+	 * @param ie:   the header data of the source game.
+	 * @param tags: contains 5 of the Seven Tag Roster.
+	 * @param data: the data (encoded in native format) of the game.
 	 * @param replaced: valid gamenumT of the game to be replaced
 	 * @returns OK if successful or an error code.
 	 */
-	virtual errorT saveGame(Game* game, gamenumT replaced) = 0;
+	virtual errorT saveGame(IndexEntry const& ie, TagRoster const& tags,
+	                        ByteBuffer const& data, gamenumT replaced) = 0;
 
 	/**
 	 * Replaces a game's IndexEntry (which contains the header data of a game).
diff -pruN 1:4.7.0+dfsg1-2/src/codec_memory.h 1:4.7.4+dfsg1-2/src/codec_memory.h
--- 1:4.7.0+dfsg1-2/src/codec_memory.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec_memory.h	2022-07-09 05:14:42.000000000 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017  Fulvio Benini
+ * Copyright (C) 2016-2021  Fulvio Benini
 
  * This file is part of Scid (Shane's Chess Information Database).
  *
@@ -24,15 +24,20 @@
 #ifndef CODEC_MEMORY_H
 #define CODEC_MEMORY_H
 
-#include "codec_native.h"
+#include "codec.h"
+#include "index.h"
+#include "namebase.h"
 
 /**
  * Manages memory databases that do not have associated files.
  * Every open database should have a native representation in memory: to satisfy
  * this requirement non-native codecs should be derived from this class.
  */
-class CodecMemory : public CodecNative<CodecMemory> {
+class CodecMemory : public ICodecDatabase {
+	Index* idx_ = nullptr;
+	NameBase* nb_ = nullptr;
 	VectorChunked<byte, 24> v_;
+	unsigned baseType_ = 0;
 
 	enum : uint64_t {
 		LIMIT_GAMEOFFSET = 1ULL << 46,
@@ -52,15 +57,58 @@ public: // ICodecDatabase interface
 	std::vector<std::pair<const char*, std::string>>
 	getExtraInfo() const override {
 		std::vector<std::pair<const char*, std::string>> res;
-		res.emplace_back("type", std::to_string(idx_->GetType()));
+		res.emplace_back("type", std::to_string(baseType_));
 		return res;
 	}
 
-	const byte* getGameData(uint64_t offset, uint32_t length) override {
+	errorT setExtraInfo(const char* tagname, const char* new_value) override {
+		if (std::strcmp(tagname, "type") == 0) {
+			baseType_ = strGetUnsigned(new_value);
+			return OK;
+		}
+		return ERROR_CodecUnsupFeat;
+	}
+
+	ByteBuffer getGameData(uint64_t offset, uint32_t length) final {
 		ASSERT(offset < v_.size());
 		ASSERT(length <= v_.size() - offset);
 		ASSERT(v_.contiguous(static_cast<size_t>(offset)) >= length);
-		return &v_[offset];
+
+		return {&v_[offset], length};
+	}
+
+	ByteBuffer getGameMoves(IndexEntry const& ie) final {
+		auto data = getGameData(ie.GetOffset(), ie.GetLength());
+		if (data && OK == data.decodeTags([](auto, auto) {}))
+			return data;
+
+		return {nullptr, 0};
+	}
+
+	errorT addGame(IndexEntry const& ie_src, TagRoster const& tags,
+	               ByteBuffer const& data) override {
+		IndexEntry ie = ie_src;
+		if (auto err = addGameNamesAndData(ie, tags, data.data(), data.size()))
+			return err;
+
+		return dyn_addIndexEntry(ie);
+	}
+
+	errorT saveGame(IndexEntry const& ie_src, TagRoster const& tags,
+	                ByteBuffer const& data, gamenumT replaced) override {
+		IndexEntry ie = ie_src;
+		if (auto err = addGameNamesAndData(ie, tags, data.data(), data.size()))
+			return err;
+
+		return dyn_saveIndexEntry(ie, replaced);
+	}
+
+	errorT saveIndexEntry(const IndexEntry& ie, gamenumT replaced) override {
+		return dyn_saveIndexEntry(ie, replaced);
+	}
+
+	std::pair<errorT, idNumberT> addName(nameT nt, const char* name) override {
+		return dyn_addName(nt, name);
 	}
 
 	errorT flush() override { return OK; }
@@ -69,24 +117,22 @@ public: // ICodecDatabase interface
 	                NameBase* nb) override {
 		if (idx == 0 || nb == 0)
 			return ERROR;
-		if (fMode != FMODE_Memory)
+		if (fMode != FMODE_Create)
 			return ERROR;
 		idx_ = idx;
 		nb_ = nb;
 		return OK;
 	}
 
-public: // CodecNative CRTP
-	/**
-	 * Stores the data of a game into memory.
-	 * @param src:    valid pointer to a buffer that contains the game data
-	 *                (encoded in native format).
-	 * @param length: the length of the buffer @p src (in bytes).
-	 * @returns
-	 * - on success, a @e std::pair containing OK and the offset of the stored
-	 * data (usable to retrieve the data with getGameData()).
-	 * - on failure, a @e std::pair containing an error code and 0.
-	 */
+private:
+	/// Stores the data of a game into memory.
+	/// @param src:    valid pointer to a buffer that contains the game data
+	///                (encoded in native format).
+	/// @param length: the length of the buffer @p src (in bytes).
+	/// @returns
+	/// - on success, a @e std::pair containing OK and the offset of the stored
+	/// data (usable to retrieve the data with getGameData()).
+	/// - on failure, a @e std::pair containing an error code and 0.
 	std::pair<errorT, uint64_t> dyn_addGameData(const byte* src,
 	                                            size_t length) {
 		ASSERT(src != 0);
@@ -94,16 +140,11 @@ public: // CodecNative CRTP
 		if (length >= LIMIT_GAMELEN)
 			return std::make_pair(ERROR_GameLengthLimit, 0);
 
-		auto offset = v_.size();
-		auto capacity = v_.capacity();
-		if (capacity - offset < length) // Do not fit in the current chunk
-			offset = capacity;
+		const auto offset = v_.next_contiguous(length);
 		if (offset >= LIMIT_GAMEOFFSET)
 			return std::make_pair(ERROR_OffsetLimit, 0);
 
-		v_.resize(offset + length);
-		ASSERT(v_.contiguous(offset) >= length);
-		std::copy_n(src, length, &v_[offset]);
+		v_.append(src, length, offset);
 		return {OK, offset};
 	}
 
@@ -126,11 +167,12 @@ public: // CodecNative CRTP
 	 * @returns OK if successful or an error code.
 	 */
 	errorT dyn_addIndexEntry(const IndexEntry& ie) {
-		auto nGames = idx_->GetNumGames();
+		const auto nGames = idx_->GetNumGames();
 		if (nGames >= LIMIT_NUMGAMES)
 			return ERROR_NumGamesLimit;
 
-		return idx_->WriteEntry(&ie, nGames);
+		idx_->addEntry(ie);
+		return OK;
 	}
 
 	/**
@@ -140,7 +182,31 @@ public: // CodecNative CRTP
 	 * @returns OK if successful or an error code.
 	 */
 	errorT dyn_saveIndexEntry(const IndexEntry& ie, gamenumT replaced) {
-		return idx_->WriteEntry(&ie, replaced);
+		idx_->replaceEntry(ie, replaced);
+		return OK;
+	}
+
+protected:
+	bool equalExceptFlags(IndexEntry const& ie, gamenumT gnum) {
+		return idx_->GetEntry(gnum)->equalExceptFlags(ie);
+	}
+
+private:
+	/// Add the game's roster tags and gamedata to the database.
+	/// Set the references to the new data in @e ie.
+	errorT addGameNamesAndData(IndexEntry& ie, TagRoster const& tags,
+	                           const byte* srcData, size_t dataLen) {
+		auto errNames = tags.map(
+		    ie, [&](auto nt, auto name) { return dyn_addName(nt, name); });
+		if (errNames)
+			return errNames;
+
+		auto [err, offset] = dyn_addGameData(srcData, dataLen);
+		if (!err) {
+			ie.SetOffset(offset);
+			ie.SetLength(dataLen);
+		}
+		return err;
 	}
 };
 
diff -pruN 1:4.7.0+dfsg1-2/src/codec_native.h 1:4.7.4+dfsg1-2/src/codec_native.h
--- 1:4.7.0+dfsg1-2/src/codec_native.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec_native.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017  Fulvio Benini
-
- * This file is part of Scid (Shane's Chess Information Database).
- *
- * Scid is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation.
- *
- * Scid is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-/** @file
- * Implements @e CodecNative, which is used as base class by native codecs.
- */
-
-#ifndef CODEC_NATIVE_H
-#define CODEC_NATIVE_H
-
-#include "bytebuf.h"
-#include "codec.h"
-#include "game.h"
-#include "index.h"
-#include "namebase.h"
-
-/**
- * This class stores the pointers to the @e Index and @e NameBase objects used
- * by a native codec. It also implements the addGame() and saveGame() functions
- * of the ICodecDatabase interface, splitting them into 2 function calls to a
- * derived class (CRTP technique):
- * - dyn_addName() should return the ID corresponding to a name, eventually
- *   adding the name to the @e nb_ if necessary;
- * - dyn_addGameData() should stores the data of the game, encoded in native
- *   format, and returns the offset that can be used to retrieve the data.
- */
-template <typename Derived> class CodecNative : public ICodecDatabase {
-protected:
-	Index* idx_;
-	NameBase* nb_;
-	ByteBuffer bbuf_;
-
-protected:
-	CodecNative() : idx_(0), nb_(0), bbuf_(BBUF_SIZE) {}
-
-public: // ICodecDatabase interface
-	errorT addGame(const IndexEntry* srcIe, const NameBase* srcNb,
-	               const byte* srcData, size_t dataLen) override {
-		IndexEntry ie = *srcIe;
-		errorT err = addGameHelper(
-		    &ie, srcData, dataLen,
-		    srcNb->GetName(NAME_PLAYER, srcIe->GetWhite()),
-		    srcNb->GetName(NAME_PLAYER, srcIe->GetBlack()),
-		    srcNb->GetName(NAME_EVENT, srcIe->GetEvent()),
-		    srcNb->GetName(NAME_SITE, srcIe->GetSite()),
-		    srcNb->GetName(NAME_ROUND, srcIe->GetRound()));
-		if (err != OK)
-			return err;
-
-		return derived()->dyn_addIndexEntry(ie);
-	}
-
-	errorT addGame(Game* game) override {
-		std::pair<errorT, IndexEntry> ie = addGameHelper(game);
-		if (ie.first != OK)
-			return ie.first;
-
-		return derived()->dyn_addIndexEntry(ie.second);
-	}
-
-	errorT saveGame(Game* game, gamenumT replaced) override {
-		if (replaced >= idx_->GetNumGames())
-			return ERROR_BadArg;
-
-		std::pair<errorT, IndexEntry> ie = addGameHelper(game);
-		if (ie.first != OK)
-			return ie.first;
-
-		return derived()->dyn_saveIndexEntry(ie.second, replaced);
-	}
-
-	errorT saveIndexEntry(const IndexEntry& ie, gamenumT replaced) override {
-		return derived()->dyn_saveIndexEntry(ie, replaced);
-	}
-
-	std::pair<errorT, idNumberT> addName(nameT nt, const char* name) override {
-		return derived()->dyn_addName(nt, name);
-	}
-
-private:
-	std::pair<errorT, IndexEntry> addGameHelper(Game* game) {
-		std::pair<errorT, IndexEntry> res;
-		res.second.Init();
-		res.first = game->Encode(&bbuf_, &res.second);
-		if (res.first == OK) {
-			res.first = addGameHelper(&res.second, bbuf_.getData(),
-			                          bbuf_.GetByteCount(), game->GetWhiteStr(),
-			                          game->GetBlackStr(), game->GetEventStr(),
-			                          game->GetSiteStr(), game->GetRoundStr());
-		}
-		return res;
-	}
-
-	errorT addGameHelper(IndexEntry* ie, const byte* srcData, size_t dataLen,
-	                     const char* white, const char* black,
-	                     const char* event, const char* site,
-	                     const char* round) {
-		auto id = derived()->dyn_addName(NAME_PLAYER, white);
-		if (id.first != OK)
-			return id.first;
-		ie->SetWhite(id.second);
-		nb_->AddElo(id.second, ie->GetWhiteElo());
-
-		id = derived()->dyn_addName(NAME_PLAYER, black);
-		if (id.first != OK)
-			return id.first;
-		ie->SetBlack(id.second);
-		nb_->AddElo(id.second, ie->GetBlackElo());
-
-		id = derived()->dyn_addName(NAME_EVENT, event);
-		if (id.first != OK)
-			return id.first;
-		ie->SetEvent(id.second);
-
-		id = derived()->dyn_addName(NAME_SITE, site);
-		if (id.first != OK)
-			return id.first;
-		ie->SetSite(id.second);
-
-		id = derived()->dyn_addName(NAME_ROUND, round);
-		if (id.first != OK)
-			return id.first;
-		ie->SetRound(id.second);
-
-		auto offset = derived()->dyn_addGameData(srcData, dataLen);
-		if (offset.first == OK) {
-			ie->SetOffset(offset.second);
-			ie->SetLength(dataLen);
-		}
-		return offset.first;
-	}
-
-	Derived* derived() { return static_cast<Derived*>(this); }
-};
-
-#endif
diff -pruN 1:4.7.0+dfsg1-2/src/codec_pgn.h 1:4.7.4+dfsg1-2/src/codec_pgn.h
--- 1:4.7.0+dfsg1-2/src/codec_pgn.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec_pgn.h	2022-07-09 05:14:42.000000000 +0000
@@ -26,7 +26,6 @@
 #define CODEC_PGN_H
 
 #include "codec_proxy.h"
-#include "common.h"
 #include "filebuf.h"
 #include "pgnparse.h"
 #include <algorithm>
diff -pruN 1:4.7.0+dfsg1-2/src/codec_proxy.h 1:4.7.4+dfsg1-2/src/codec_proxy.h
--- 1:4.7.0+dfsg1-2/src/codec_proxy.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec_proxy.h	2022-07-09 05:14:42.000000000 +0000
@@ -27,13 +27,10 @@
 
 #include "codec.h"
 #include "codec_memory.h"
-#include "common.h"
 #include "game.h"
 
-#ifndef MULTITHREADING_OFF
 #include <atomic>
 #include <thread>
-#endif
 
 /**
  * Base class for non-native databases.
@@ -98,41 +95,34 @@ public:
 	errorT gameSave(Game*, gamenumT) { return ERROR_CodecUnsupFeat; }
 
 private:
-	errorT addGame(Game* game) final {
-		errorT err = getDerived()->gameAdd(game);
-		if (err != OK)
+	errorT saveGame(IndexEntry const& ie, TagRoster const& tags,
+	                ByteBuffer const& data, gamenumT replaced) final {
+		Game game;
+		if (errorT err = game.Decode(ie, tags, data))
 			return err;
 
-		return CodecMemory::addGame(game);
-	}
-
-	errorT saveGame(Game* game, gamenumT replaced) final {
-		errorT err = getDerived()->gameSave(game, replaced);
-		if (err != OK)
+		if (errorT err = getDerived()->gameSave(&game, replaced))
 			return err;
 
-		return CodecMemory::saveGame(game, replaced);
+		return CodecMemory::saveGame(ie, tags, data, replaced);
 	}
 
-	errorT addGame(const IndexEntry* srcIe, const NameBase* srcNb,
-	               const byte* srcData, size_t dataLen) final {
-		ByteBuffer buf(0);
-		buf.ProvideExternal(const_cast<byte*>(srcData), dataLen);
+	errorT addGame(IndexEntry const& ie, TagRoster const& tags,
+	               ByteBuffer const& data) final {
 		Game game;
-		errorT err = game.Decode(&buf, GAME_DECODE_ALL);
-		if (err == OK)
-			err = game.LoadStandardTags(srcIe, srcNb);
-		if (err != OK)
+		if (errorT err = game.Decode(ie, tags, data))
 			return err;
 
-		err = getDerived()->gameAdd(&game);
-		if (err != OK)
+		if (errorT err = getDerived()->gameAdd(&game))
 			return err;
 
-		return CodecMemory::addGame(srcIe, srcNb, srcData, dataLen);
+		return CodecMemory::addGame(ie, tags, data);
 	}
 
-	errorT saveIndexEntry(const IndexEntry&, gamenumT) final {
+	errorT saveIndexEntry(const IndexEntry& ie, gamenumT replaced) final {
+		if (CodecMemory::equalExceptFlags(ie, replaced))
+			return CodecMemory::saveIndexEntry(ie, replaced);
+
 		return ERROR_CodecUnsupFeat;
 	}
 
@@ -149,7 +139,7 @@ private:
 		if (filename == 0)
 			return ERROR;
 
-		errorT err = CodecMemory::dyn_open(FMODE_Memory, filename, progress,
+		errorT err = CodecMemory::dyn_open(FMODE_Create, filename, progress,
 		                                   idx, nb);
 		if (err != OK)
 			return err;
@@ -158,8 +148,11 @@ private:
 		if (err != OK)
 			return err;
 
+		std::vector<byte> buf;
 		return parseGames(progress, *getDerived(), [&](Game& game) {
-			return this->CodecMemory::addGame(&game);
+			buf.clear();
+			auto [ie, tags] = game.Encode(buf);
+			return CodecMemory::addGame(ie, tags, {buf.data(), buf.size()});
 		});
 	}
 
@@ -171,13 +164,12 @@ public:
 	template <typename TProgress, typename TSource, typename TDestFn>
 	static errorT parseGames(const TProgress& progress, TSource& src,
 	                         TDestFn destFn) {
-#ifndef MULTITHREADING_OFF
 		auto workTotal = src.parseProgress().second;
 
 		Game game[4];
 		std::atomic<size_t> workDone{};
 		std::atomic<int8_t> sync[4] = {};
-		enum {sy_free, sy_used, sy_stop};
+		enum { sy_free, sy_used, sy_stop };
 
 		std::thread producer([&]() {
 			uint64_t slot;
@@ -244,27 +236,6 @@ public:
 		producer.join();
 		progress(1, 1, src.parseErrors());
 		return err;
-
-#else
-		Game g;
-		errorT err = OK;
-		uint64_t nImported = 0;
-		while (src.parseNext(g) != ERROR_NotFound) {
-			err = destFn(g);
-			if (err != OK)
-				break;
-
-			if (++nImported % 1024 == 0) {
-				std::pair<size_t, size_t> count = src.parseProgress();
-				if (!progress.report(count.first, count.second)) {
-					err = ERROR_UserCancel;
-					break;
-				}
-			}
-		}
-		progress(1, 1, src.parseErrors());
-		return err;
-#endif
 	}
 };
 
diff -pruN 1:4.7.0+dfsg1-2/src/codec_scid4.cpp 1:4.7.4+dfsg1-2/src/codec_scid4.cpp
--- 1:4.7.0+dfsg1-2/src/codec_scid4.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec_scid4.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -47,19 +47,12 @@ namespace {
  */
 const char* NAMEBASE_MAGIC = "Scid.sn";
 
-/**
- * Read a SCIDv4 NameBase file into memory.
- * @param filename: the full path of the file to open.
- * @param fMode:    a valid file mode.
- * @param nb:       reference to the object where the names will be stored.
- * @returns OK if successful or an error code.
- */
+/// Read a SCIDv4 NameBase file into memory.
+/// @param filename: the full path of the file to open.
+/// @param fMode:    a valid file mode.
+/// @param nb:       reference to the object where the names will be stored.
+/// @returns OK if successful or an error code.
 errorT namefileRead(const char* filename, fileModeT fmode, NameBase& nb) {
-	auto nb_data = nb.getData();
-	auto& map = std::get<0>(nb_data);
-	auto& names = std::get<1>(nb_data);
-	auto& eloV = std::get<2>(nb_data);
-
 	Filebuf file;
 	if (file.Open(filename, fmode) != OK)
 		return ERROR_FileOpen;
@@ -89,9 +82,7 @@ errorT namefileRead(const char* filename
 	obsolete_maxFreq[NAME_ROUND] = file.ReadThreeBytes();
 	// ***
 
-	eloV.resize(Header_numNames[NAME_PLAYER], 0);
 	for (nameT nt : {NAME_PLAYER, NAME_EVENT, NAME_SITE, NAME_ROUND}) {
-		names[nt].resize(Header_numNames[nt]);
 		idNumberT id;
 		std::string prevName;
 		for (idNumberT i = 0; i < Header_numNames[nt]; i++) {
@@ -120,56 +111,24 @@ errorT namefileRead(const char* filename
 			if (prefix > length)
 				return ERROR_Corrupt;
 
-			char* name = new char[length + 1];
-			std::copy_n(prevName.c_str(), prefix, name);
-			std::streamsize extra_chars = length - prefix;
-			if (extra_chars != file.sgetn(name + prefix, extra_chars)) {
-				delete[] name;
+			prevName.resize(length);
+			const auto new_chars = length - prefix;
+			if (new_chars != file.sgetn(prevName.data() + prefix, new_chars))
 				return ERROR_FileRead;
-			}
-			name[length] = 0;
-			prevName.assign(name, length);
 
-			if (id < Header_numNames[nt] && names[nt][id] == 0) {
-				names[nt][id].reset(name);
-				map[nt].insert(map[nt].end(), std::make_pair(name, id));
-			} else {
-				delete[] name;
+			if (id >= Header_numNames[nt] ||
+			    !nb.insert(prevName.c_str(), length, nt, id))
 				return ERROR_Corrupt;
-			}
 		}
-
-		if (map[nt].size() != names[nt].size())
-			return ERROR_Corrupt;
 	}
 
 	return OK;
 }
 
-bool assert_sorted(const char* str1, const char* str2) {
-	// *** Compatibility ***
-	// Older code used a custom StrTree class with a peculiar sorting:
-	// - the first char was interpreted as an unsigned char;
-	// - the remaining part was compared with the function
-	// strComapare(),
-	//   which converts the chars to ints, and is not consistent with
-	//   the standard function strcmp().
-	// The old StrTree class did also have unpredictable behaviors when
-	// fed with names not sorted according to that criteria, for example
-	// it could create Namebase objects with duplicate entries.
-	// ***
-	if (*str1 == *str2)
-		return strCompare(str1, str2) < 0;
-
-	return static_cast<uint>(*str1) < static_cast<uint>(*str2);
-}
-
-/**
- * Write a SCIDv4 NameBase file.
- * @param filename: the full path of the file to open.
- * @param nb:       reference to the object where the names will be stored.
- * @returns OK if successful or an error code.
- */
+/// Write a SCIDv4 NameBase file.
+/// @param filename: the full path of the file to open.
+/// @param nb:       reference to the object where the names will be stored.
+/// @returns OK if successful or an error code.
 template <typename TCont, typename TFreq>
 errorT namefileWrite(const char* filename, const TCont& names_ids,
                      const TFreq& freq) {
@@ -210,7 +169,20 @@ errorT namefileWrite(const char* filenam
 			const char* name = it.first;
 			idNumberT id = it.second;
 
-			ASSERT(prevName == nullptr || assert_sorted(prevName, name));
+			// *** Compatibility ***
+			// Older code used a custom StrTree class with a peculiar sorting:
+			// - the first char was interpreted as an unsigned char;
+			// - the remaining part was compared with strComapare(),
+			//   which converts the chars to ints, and is not consistent with
+			//   the standard function strcmp().
+			// The old StrTree class did also have unpredictable behaviors when
+			// fed with names not sorted according to that criteria, for example
+			// it could create Namebase objects with duplicate entries.
+			// ***
+			ASSERT(prevName == nullptr ||
+			       static_cast<uint>(*prevName) < static_cast<uint>(*name) ||
+			       (static_cast<uint>(*prevName) == static_cast<uint>(*name) &&
+			        strCompare(prevName, name) < 0));
 
 			// write idNumber in 2 bytes if possible, otherwise 3.
 			if (numNames >= 65536) {
@@ -245,16 +217,108 @@ errorT namefileWrite(const char* filenam
 	return OK;
 }
 
-} // namespace
-
 /**
- * Decode SCID4 (or SCID3) data into an IndexEntry object.
- * @param buf_it:  pointer to the buffer containing the data
- *                 (should contain INDEX_ENTRY_SIZE chars)
- * @param version: 400 for SCID4 or 300 for SCID3.
- * @param ie:      pointer to the IndexEntry object where the data will be
- *                 stored.
+ * An Index file starts with an header of 182 bytes containing:
+ * - index_magic (8 bytes): identifies the file format.
+ * - version (2 bytes): 300 or 400
+ * - baseType (4 bytes): e.g. tournament, theory, etc.
+ * - numGames (3 bytes): total number of games contained into the index.
+ * - autoLoad (3 bytes): number of the game to load at start
+ *                       (0=none, 1=1st, >numGames=last).
+ * - description (108 bytes): a null-terminated string describing the database.
+ * - flag1 description (9 bytes): a null-terminated string describing flag1.
+ * - flag2 description (9 bytes): a null-terminated string describing flag2.
+ * - flag3 description (9 bytes): a null-terminated string describing flag3.
+ * - flag4 description (9 bytes): a null-terminated string describing flag4.
+ * - flag5 description (9 bytes): a null-terminated string describing flag5.
+ * - flag6 description (9 bytes): a null-terminated string describing flag6.
  */
+constexpr char INDEX_MAGIC[8] = "Scid.si";
+constexpr size_t SCID_DESC_LENGTH = 107;
+constexpr size_t CUSTOM_FLAG_MAX = 6;
+constexpr size_t CUSTOM_FLAG_DESC_LENGTH = 8;
+constexpr int INDEX_ENTRY_SIZE = 47;
+constexpr int OLD_INDEX_ENTRY_SIZE = 46;
+// Header on-disk size: magic=8, version=2, numGames=3, baseType=4, autoLoad=3
+// Description length = 111 bytes including trailing '\0'.
+// Custom flag desc length = 9 bytes including trailing '\0'.
+// So total is 128 bytes + 9*6 = 182 bytes for the whole header.
+constexpr size_t INDEX_HEADER_SIZE = 8 + 2 + 3 + 4 + 3 + SCID_DESC_LENGTH + 1 +
+                                     (CUSTOM_FLAG_DESC_LENGTH + 1) *
+                                         CUSTOM_FLAG_MAX;
+
+/// Read the header section of a SCIDv4 Index file into memory.
+/// @param indexFile: file handle positioned at the start of the Index file.
+/// @param fMode:     a valid file mode.
+/// @param header:    reference to the object where the data will be stored.
+/// @returns OK if successful or an error code.
+template <typename FileT, typename HeaderT>
+std::pair<errorT, gamenumT> readIndexHeader(FileT& indexFile, HeaderT& header) {
+	char magic[8];
+	indexFile.sgetn(magic, 8);
+	if (!std::equal(std::begin(magic), std::end(magic), std::begin(INDEX_MAGIC),
+	                std::end(INDEX_MAGIC))) {
+		return {ERROR_BadMagic, {}};
+	}
+
+	header.version = indexFile.ReadTwoBytes();
+	header.baseType = indexFile.ReadFourBytes();
+	auto numGames = indexFile.ReadThreeBytes();
+	header.autoLoad = indexFile.ReadThreeBytes();
+	char desc[SCID_DESC_LENGTH + 1];
+	indexFile.sgetn(desc, SCID_DESC_LENGTH + 1);
+	header.description.assign(desc,
+	                          std::find(desc, desc + SCID_DESC_LENGTH, '\0'));
+	if (header.version >= 400) {
+		for (size_t i = 0; i < CUSTOM_FLAG_MAX; i++) {
+			char buf[CUSTOM_FLAG_DESC_LENGTH + 1];
+			indexFile.sgetn(buf, CUSTOM_FLAG_DESC_LENGTH + 1);
+			header.flagDesc[i].assign(
+			    buf, std::find(buf, buf + CUSTOM_FLAG_DESC_LENGTH, '\0'));
+		}
+	}
+	return {OK, numGames};
+}
+
+/// Write the header section of a SCIDv4 Index file.
+/// @param indexFile: file handle of the Index file.
+/// @param header:    reference to the object containing the header data.
+/// @returns OK if successful or an error code.
+template <typename FileT, typename HeaderT>
+errorT writeIndexHeader(FileT& indexFile, HeaderT const& Header,
+                        gamenumT nGames) {
+	if (indexFile.pubseekpos(0) != std::streampos(0))
+		return ERROR_FileWrite;
+
+	std::streamsize n = 0;
+	n += indexFile.sputn(INDEX_MAGIC, 8);
+	n += indexFile.WriteTwoBytes(Header.version);
+	n += indexFile.WriteFourBytes(Header.baseType);
+	n += indexFile.WriteThreeBytes(nGames);
+	n += indexFile.WriteThreeBytes(Header.autoLoad);
+	char desc[SCID_DESC_LENGTH + 1] = {};
+	std::copy_n(Header.description.data(),
+	            std::min(Header.description.size(), SCID_DESC_LENGTH), desc);
+	n += indexFile.sputn(desc, SCID_DESC_LENGTH + 1);
+	for (auto const& flagDesc : Header.flagDesc) {
+		char buf[CUSTOM_FLAG_DESC_LENGTH + 1] = {};
+		std::copy_n(flagDesc.data(),
+		            std::min(flagDesc.size(), CUSTOM_FLAG_DESC_LENGTH), buf);
+
+		n += indexFile.sputn(buf, CUSTOM_FLAG_DESC_LENGTH + 1);
+	}
+	if (n != INDEX_HEADER_SIZE || indexFile.pubsync() == -1)
+		return ERROR_FileWrite;
+
+	return OK;
+}
+
+/// Decode SCID4 (or SCID3) data into an IndexEntry object.
+/// @param buf_it:  pointer to the buffer containing the data
+///                 (should contain INDEX_ENTRY_SIZE chars)
+/// @param version: 400 for SCID4 or 300 for SCID3.
+/// @param ie:      pointer to the IndexEntry object where the data will be
+///                 stored.
 void decodeIndexEntry(const char* buf_it, versionT version, IndexEntry* ie) {
 	auto ReadOneByte = [&buf_it]() {
 		uint8_t res = *buf_it++;
@@ -271,6 +335,8 @@ void decodeIndexEntry(const char* buf_it
 		return res;
 	};
 
+	ie->setChessStd();
+
 	// Offset of the gamefile record (32 bits).
 	ie->SetOffset(ReadFourBytes());
 
@@ -353,20 +419,123 @@ void decodeIndexEntry(const char* buf_it
 	uint16_t NumHalfMoves = ReadOneByte();
 	uint16_t pawnData0 = ReadOneByte();
 	ie->SetNumHalfMoves(((pawnData0 & 0xC0) << 2) | NumHalfMoves);
-	byte* pb = ie->GetHomePawnData();
-	*pb++ = pawnData0 & 0x3F;
-	std::copy_n(buf_it, HPSIG_SIZE - 1, pb);
+	ie->SetHomePawnData(pawnData0 & 0x3F,
+	                    reinterpret_cast<const unsigned char*>(buf_it));
+}
+
+/// Enccode an IndexEntry to SCID4 format.
+/// @param buf_it:  pointer to the buffer where the data would be stored
+///                 (should be able to contain at least INDEX_ENTRY_SIZE chars)
+void encodeIndexEntry(const IndexEntry* ie, char* buf_it) {
+	auto WriteOneByte = [&buf_it](uint8_t v) { *buf_it++ = v; };
+	auto WriteTwoBytes = [&WriteOneByte](uint16_t v) {
+		WriteOneByte(static_cast<uint8_t>(v >> 8));
+		WriteOneByte(static_cast<uint8_t>(v));
+	};
+	auto WriteFourBytes = [&WriteTwoBytes](uint32_t v) {
+		WriteTwoBytes(static_cast<uint16_t>(v >> 16));
+		WriteTwoBytes(static_cast<uint16_t>(v));
+	};
+
+	ASSERT(ie->GetOffset() < (1ULL << 32));
+	WriteFourBytes(static_cast<uint32_t>(ie->GetOffset()));
+
+	ASSERT(ie->GetLength() < (1ULL << 17));
+	WriteTwoBytes(static_cast<uint16_t>(ie->GetLength()));
+	uint8_t len_flags = static_cast<uint8_t>(ie->GetLength() >> 9) & 0x80;
+	len_flags |= static_cast<uint8_t>(ie->GetRawFlags() >> 16) & 0x3F;
+	WriteOneByte(len_flags);
+	WriteTwoBytes(static_cast<uint16_t>(ie->GetRawFlags()));
+
+	// WhiteID and BlackID are 20-bit values, EventID and SiteID are
+	// 19-bit values, and RoundID is an 18-bit value.
+	// WhiteID high 4 bits = bits 4-7 of WhiteBlack_High.
+	// BlackID high 4 bits = bits 0-3 of WhiteBlack_High.
+	// EventID high 3 bits = bits 5-7 of EventSiteRnd_high.
+	// SiteID  high 3 bits = bits 2-4 of EventSiteRnd_high.
+	// RoundID high 2 bits = bits 0-1 of EventSiteRnd_high.
+	ASSERT(std::max(ie->GetWhite(), ie->GetBlack()) < (1ULL << 20));
+	uint32_t WhiteID_Low = ie->GetWhite();
+	uint32_t BlackID_Low = ie->GetBlack();
+	uint32_t WhiteBlack_High = (WhiteID_Low & 0x0F0000) >> 12;
+	WhiteBlack_High |= (BlackID_Low & 0x0F0000) >> 16;
+	WriteOneByte(static_cast<uint8_t>(WhiteBlack_High));
+	WriteTwoBytes(static_cast<uint16_t>(WhiteID_Low));
+	WriteTwoBytes(static_cast<uint16_t>(BlackID_Low));
+
+	ASSERT(std::max(ie->GetEvent(), ie->GetSite()) < (1ULL << 19));
+	ASSERT(ie->GetRound() < (1ULL << 18));
+	uint32_t EventID_Low = ie->GetEvent();
+	uint32_t SiteID_Low = ie->GetSite();
+	uint32_t RoundID_Low = ie->GetRound();
+	uint32_t EventSiteRnd_High = (EventID_Low & 0x070000) >> 11;
+	EventSiteRnd_High |= (SiteID_Low & 0x070000) >> 14;
+	EventSiteRnd_High |= (RoundID_Low & 0x030000) >> 16;
+	WriteOneByte(static_cast<uint8_t>(EventSiteRnd_High));
+	WriteTwoBytes(static_cast<uint16_t>(EventID_Low));
+	WriteTwoBytes(static_cast<uint16_t>(SiteID_Low));
+	WriteTwoBytes(static_cast<uint16_t>(RoundID_Low));
+
+	uint16_t varCounts = ie->GetRaw4bitsCounts();
+	varCounts |= static_cast<uint16_t>(ie->GetResult() & 0x0F) << 12;
+	WriteTwoBytes(varCounts);
+
+	WriteTwoBytes(ie->GetEcoCode());
+
+	// Due to a compact encoding format, the EventDate
+	// must be within a few years of the Date.
+	uint32_t date = ie->GetDate() & 0xFFFFF;
+	uint32_t edate = ie->GetEventDate();
+	uint32_t eyear = date_GetYear(edate);
+	uint32_t dyear = date_GetYear(date);
+	if ((eyear + 3) < dyear || eyear > (dyear + 3)) {
+		edate = ZERO_DATE;
+	} else {
+		eyear = (eyear + 4 - dyear) & 7;
+		edate = (eyear << 9) | (date_GetMonth(edate) << 5) | date_GetDay(edate);
+	}
+	WriteFourBytes((edate << 20) | date);
+
+	// Elo ratings and rating types: 2 bytes each.
+	uint16_t wElo = std::min(MAX_ELO, ie->GetWhiteElo());
+	wElo |= static_cast<uint16_t>(ie->GetWhiteRatingType()) << 12;
+	uint16_t bElo = std::min(MAX_ELO, ie->GetBlackElo());
+	bElo |= static_cast<uint16_t>(ie->GetBlackRatingType()) << 12;
+	WriteTwoBytes(wElo);
+	WriteTwoBytes(bElo);
+
+	ASSERT(ie->GetFinalMatSig() < (1ULL << 24));
+	ASSERT(ie->GetStoredLineCode() < (1ULL << 8));
+	uint32_t FinalMatSig = ie->GetFinalMatSig();
+	FinalMatSig |= static_cast<uint32_t>(ie->GetStoredLineCode()) << 24;
+	WriteFourBytes(FinalMatSig);
+
+	// The first byte of HomePawnData has high bits of the NumHalfMoves
+	// counter in its top two bits:
+	uint16_t nMoves = ie->GetNumHalfMoves();
+	ASSERT(nMoves < (1ULL << 10));
+	WriteOneByte(static_cast<uint8_t>(nMoves));
+	uint8_t pawnData0 = static_cast<uint8_t>(nMoves >> 8) << 6;
+
+	// Write the 9-byte homePawnData array:
+	const byte* pb = ie->GetHomePawnData();
+	pawnData0 |= *pb & 0x3F;
+	WriteOneByte(pawnData0);
+	std::copy(pb + 1, pb + HPSIG_SIZE, buf_it);
 }
 
+} // namespace
+
 errorT CodecSCID4::dyn_open(fileModeT fMode, const char* filename,
                             const Progress& progress, Index* idx,
                             NameBase* nb) {
-	if (filename == nullptr || idx == nullptr || nb == nullptr)
+	if (fMode == FMODE_WriteOnly || !filename || !idx || !nb)
 		return ERROR;
 	if (*filename == '\0')
 		return ERROR_FileOpen;
 
 	idx_ = idx;
+	idx_->Init();
 	nb_ = nb;
 	filenames_.resize(3);
 	filenames_[0] = std::string(filename) + ".si4";
@@ -377,32 +546,65 @@ errorT CodecSCID4::dyn_open(fileModeT fM
 	if (err != OK)
 		return err;
 
+	const char* indexFilename = filenames_[0].c_str();
 	if (fMode == FMODE_Create) {
-		err = idx->Create(filename);
+		// Check that the file does not exists
+		if (idxfile_.Open(indexFilename, FMODE_ReadOnly) == OK)
+			err = ERROR_FileOpen;
+
+		if (err == OK)
+			err = idxfile_.Open(indexFilename, FMODE_Create);
+
+		if (err == OK)
+			err = writeIndexHeader(idxfile_, header_, idx_->GetNumGames());
+
 		if (err == OK) {
 			err = namefileWrite(filenames_[1].c_str(), nb_->getNames(),
-			                    idx_->calcNameFreq(*nb_));
+			                    nb->calcNameFreq(*idx_));
 		}
 	} else {
-		err = idx->Open(filename, fMode);
-		if (err == OK)
-			err = namefileRead(filenames_[1].c_str(), fMode, *nb_);
-		if (err == OK)
-			err = readIndex(progress);
+		if (auto err = namefileRead(filenames_[1].c_str(), fMode, *nb_))
+			return err;
+
+		if (auto err = idxfile_.Open(indexFilename, fMode))
+			return err;
+
+		auto [errHeader, nGames] = readIndexHeader(idxfile_, header_);
+		if (errHeader)
+			return errHeader;
+
+		constexpr versionT SCID_OLDEST_VERSION = 300; // Oldest readable version
+		if (header_.version < SCID_OLDEST_VERSION ||
+		    header_.version > SCID_VERSION)
+			return ERROR_FileVersion;
+
+		if (header_.version != SCID_VERSION && fMode != FMODE_ReadOnly)
+			return ERROR_FileMode; // Old versions must be opened readonly
+
+		err = readIndex(nGames, progress);
 	}
 
 	return err;
 }
 
 errorT CodecSCID4::flush() {
-	errorT err = idx_->flush();
+	seqWrite_ = 0;
+	errorT errHeader = OK;
+	if (header_.dirty) {
+		errHeader = writeIndexHeader(idxfile_, header_, idx_->GetNumGames());
+		if (errHeader == OK)
+			header_.dirty = false;
+	}
+	errorT errSync = (idxfile_.pubsync() != 0) ? ERROR_FileWrite : OK;
+	errorT err = (errHeader == OK) ? errSync : errHeader;
+
 	if (err == OK) {
 		// *** Compatibility ***
 		// Even if name's frequency is no longer used, it's necessary to
 		// keep the compatibility with older Scid versions, forcing a
 		// recalculation.
 		err = namefileWrite(filenames_[1].c_str(), nb_->getNames(),
-		                    idx_->calcNameFreq(*nb_));
+		                    nb_->calcNameFreq(*idx_));
 	}
 	errorT errGfile = (gfile_.pubsync() == 0) ? OK : ERROR_FileWrite;
 
@@ -415,7 +617,7 @@ errorT CodecSCID4::flush() {
  * @param progress: a Progress object used for GUI communications.
  * @returns OK if successful or an error code.
  */
-inline errorT CodecSCID4::readIndex(const Progress& progress) {
+errorT CodecSCID4::readIndex(gamenumT nGames, Progress const& progress) {
 	gamenumT nUnknowIDs = 0;
 	idNumberT maxID[NUM_NAME_TYPES];
 	for (nameT nt = NAME_PLAYER; nt < NUM_NAME_TYPES; nt++) {
@@ -460,13 +662,10 @@ inline errorT CodecSCID4::readIndex(cons
 		return true;
 	};
 
-	auto idxFile = idx_->FilePtr;
-	auto version = idx_->Header.version;
-	auto nGames = idx_->GetNumGames();
 	idx_->entries_.resize(nGames);
-
-	auto nBytes = (version < 400) ? OLD_INDEX_ENTRY_SIZE : INDEX_ENTRY_SIZE;
-	for (gamenumT gNum = 0; idxFile->sgetc() != EOF; ++gNum) {
+	const auto nBytes = (header_.version < 400) ? OLD_INDEX_ENTRY_SIZE
+	                                            : INDEX_ENTRY_SIZE;
+	for (gamenumT gNum = 0; idxfile_.sgetc() != EOF; ++gNum) {
 		if (gNum == nGames)
 			return ERROR_CorruptData;
 
@@ -476,23 +675,68 @@ inline errorT CodecSCID4::readIndex(cons
 		}
 
 		char buf[INDEX_ENTRY_SIZE];
-		if (idxFile->sgetn(buf, nBytes) != nBytes)
+		if (idxfile_.sgetn(buf, nBytes) != nBytes)
 			return ERROR_FileRead;
 
-		IndexEntry* ie = idx_->FetchEntry(gNum);
-		decodeIndexEntry(buf, version, ie);
+		IndexEntry& ie = idx_->entries_[gNum];
+		decodeIndexEntry(buf, header_.version, &ie);
 
-		if (!validateNameIDs(ie))
+		if (!validateNameIDs(&ie))
 			return ERROR_CorruptData;
-
-		nb_->AddElo(ie->GetWhite(), ie->GetWhiteElo());
-		nb_->AddElo(ie->GetBlack(), ie->GetBlackElo());
 	}
 	progress.report(1, 1);
 
-	if (nGames != idx_->GetNumGames())
-		return ERROR_FileRead;
-
 	idx_->nInvalidNameId_ = nUnknowIDs;
 	return (nUnknowIDs == 0) ? OK : ERROR_NameDataLoss;
 }
+
+errorT CodecSCID4::writeEntry(const IndexEntry& ie, gamenumT gnum) {
+	if (seqWrite_ == 0 || (gnum != seqWrite_ + 1)) {
+		std::streampos pos = gnum;
+		pos = pos * INDEX_ENTRY_SIZE + INDEX_HEADER_SIZE;
+		if (idxfile_.pubseekpos(pos) != pos) {
+			seqWrite_ = 0;
+			return ERROR_FileWrite;
+		}
+	}
+	char buf[INDEX_ENTRY_SIZE];
+	encodeIndexEntry(&ie, buf);
+	errorT res = idxfile_.sputn(buf, INDEX_ENTRY_SIZE) == INDEX_ENTRY_SIZE
+	                 ? OK
+	                 : ERROR_FileWrite;
+
+	seqWrite_ = (res == OK) ? gnum : 0;
+	return res;
+}
+
+errorT CodecSCID4::setExtraInfo(const char* tagname, const char* new_value) {
+	if (std::strcmp(tagname, "type") == 0) {
+		header_.baseType = strGetUnsigned(new_value);
+
+	} else if (std::strcmp(tagname, "description") == 0) {
+		header_.description = new_value;
+		if (header_.description.size() > SCID_DESC_LENGTH)
+			header_.description.resize(SCID_DESC_LENGTH);
+
+	} else if (std::strcmp(tagname, "autoload") == 0) {
+		header_.autoLoad = strGetUnsigned(new_value);
+
+	} else {
+		auto len = std::strlen(tagname);
+		if (len != 5 || !std::equal(tagname, tagname + 4, "flag"))
+			return ERROR_CodecUnsupFeat;
+
+		uint flag = IndexEntry::CharToFlag(tagname[4]);
+		if (flag < IndexEntry::IDX_FLAG_CUSTOM1 ||
+		    flag > IndexEntry::IDX_FLAG_CUSTOM6)
+			return ERROR_CodecUnsupFeat;
+
+		const auto idx = flag - IndexEntry::IDX_FLAG_CUSTOM1;
+		header_.flagDesc[idx] = new_value;
+		if (header_.flagDesc[idx].size() > CUSTOM_FLAG_DESC_LENGTH)
+			header_.flagDesc[idx].resize(CUSTOM_FLAG_DESC_LENGTH);
+	}
+
+	header_.dirty = true;
+	return OK;
+}
diff -pruN 1:4.7.0+dfsg1-2/src/codec_scid4.h 1:4.7.4+dfsg1-2/src/codec_scid4.h
--- 1:4.7.0+dfsg1-2/src/codec_scid4.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/codec_scid4.h	2022-07-09 05:14:42.000000000 +0000
@@ -25,17 +25,33 @@
 #ifndef CODEC_SCID4_H
 #define CODEC_SCID4_H
 
-#include "codec_native.h"
+#include "codec.h"
 #include "filebuf.h"
+#include "index.h"
+#include "namebase.h"
 #include <limits>
 
 /**
  * This class manages databases encoded in SCID format v4.
  */
-class CodecSCID4 : public CodecNative<CodecSCID4> {
+class CodecSCID4 : public ICodecDatabase {
+	Index* idx_ = nullptr;
+	NameBase* nb_ = nullptr;
 	std::vector<std::string> filenames_;
+	Filebuf idxfile_;
 	FilebufAppend gfile_;
 	char gamecache_[1ULL << 17];
+	gamenumT seqWrite_ = 0;
+
+	struct {
+		std::string description; // a string describing the database.
+		std::string flagDesc[6]; // short description (8 chars) for CUSTOM flags
+		gamenumT autoLoad = 1;   // game number to autoload:
+		                         // 0=none, 1=1st, >numGames=last
+		uint32_t baseType = 0;   // Type, e.g. tournament, theory, etc.
+		versionT version = SCID_VERSION; // version number. 2 bytes.
+		bool dirty = false;
+	} header_;
 
 	enum : uint64_t {
 		LIMIT_GAMEOFFSET = 1ULL << 32,
@@ -57,36 +73,67 @@ public: // ICodecDatabase interface
 	std::vector<std::pair<const char*, std::string>>
 	getExtraInfo() const final {
 		std::vector<std::pair<const char*, std::string>> res;
-		res.emplace_back("type", std::to_string(idx_->GetType()));
-		res.emplace_back("description", idx_->GetDescription());
-		res.emplace_back("autoload", std::to_string(idx_->GetAutoLoad()));
-		res.emplace_back("flag1",
-		                 idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM1));
-		res.emplace_back("flag2",
-		                 idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM2));
-		res.emplace_back("flag3",
-		                 idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM3));
-		res.emplace_back("flag4",
-		                 idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM4));
-		res.emplace_back("flag5",
-		                 idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM5));
-		res.emplace_back("flag6",
-		                 idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM6));
+		res.emplace_back("type", std::to_string(header_.baseType));
+		res.emplace_back("description", header_.description);
+		const auto autoload = std::min(header_.autoLoad, idx_->GetNumGames());
+		res.emplace_back("autoload", std::to_string(autoload));
+		res.emplace_back("flag1", header_.flagDesc[0]);
+		res.emplace_back("flag2", header_.flagDesc[1]);
+		res.emplace_back("flag3", header_.flagDesc[2]);
+		res.emplace_back("flag4", header_.flagDesc[3]);
+		res.emplace_back("flag5", header_.flagDesc[4]);
+		res.emplace_back("flag6", header_.flagDesc[5]);
 		return res;
 	}
 
-	const byte* getGameData(uint64_t offset, uint32_t length) final {
+	errorT setExtraInfo(const char* tagname, const char* new_value) override;
+
+	ByteBuffer getGameData(uint64_t offset, uint32_t length) final {
 		if (offset >= gfile_.size())
-			return NULL;
+			return {nullptr, 0};
 		if (length >= LIMIT_GAMELEN)
-			return NULL;
+			return {nullptr, 0};
 
 		if (gfile_.pubseekpos(offset) == -1)
-			return NULL;
+			return {nullptr, 0};
 		if (gfile_.sgetn(gamecache_, length) != std::streamsize(length))
-			return NULL;
+			return {nullptr, 0};
+
+		return {reinterpret_cast<const byte*>(gamecache_), length};
+	}
+
+	ByteBuffer getGameMoves(IndexEntry const& ie) final {
+		auto data = getGameData(ie.GetOffset(), ie.GetLength());
+		if (data && OK == data.decodeTags([](auto, auto) {}))
+			return data;
+
+		return {nullptr, 0};
+	}
+
+	errorT addGame(IndexEntry const& ie_src, TagRoster const& tags,
+	               ByteBuffer const& data) final {
+		IndexEntry ie = ie_src;
+		if (auto err = addGameNamesAndData(ie, tags, data.data(), data.size()))
+			return err;
+
+		return dyn_addIndexEntry(ie);
+	}
+
+	errorT saveGame(IndexEntry const& ie_src, TagRoster const& tags,
+	                ByteBuffer const& data, gamenumT replaced) final {
+		IndexEntry ie = ie_src;
+		if (auto err = addGameNamesAndData(ie, tags, data.data(), data.size()))
+			return err;
 
-		return reinterpret_cast<const byte*>(gamecache_);
+		return dyn_saveIndexEntry(ie, replaced);
+	}
+
+	errorT saveIndexEntry(const IndexEntry& ie, gamenumT replaced) final {
+		return dyn_saveIndexEntry(ie, replaced);
+	}
+
+	std::pair<errorT, idNumberT> addName(nameT nt, const char* name) final {
+		return dyn_addName(nt, name);
 	}
 
 	errorT flush() final;
@@ -94,7 +141,7 @@ public: // ICodecDatabase interface
 	errorT dyn_open(fileModeT, const char*, const Progress&, Index*,
 	                NameBase*) final;
 
-public: // CodecNative interface
+private:
 	/**
 	 * Stores the data into the .sg4 file.
 	 * @param src:    valid pointer to a buffer that contains the game data
@@ -158,11 +205,13 @@ public: // CodecNative interface
 	 * @returns OK if successful or an error code.
 	 */
 	errorT dyn_addIndexEntry(const IndexEntry& ie) {
-		auto nGames = idx_->GetNumGames();
+		const auto nGames = idx_->GetNumGames();
 		if (nGames >= LIMIT_NUMGAMES)
 			return ERROR_NumGamesLimit;
 
-		return idx_->WriteEntry(&ie, nGames);
+		idx_->addEntry(ie);
+		header_.dirty = true;
+		return writeEntry(ie, nGames);
 	}
 
 	/**
@@ -172,11 +221,33 @@ public: // CodecNative interface
 	 * @returns OK if successful or an error code.
 	 */
 	errorT dyn_saveIndexEntry(const IndexEntry& ie, gamenumT replaced) {
-		return idx_->WriteEntry(&ie, replaced);
+		idx_->replaceEntry(ie, replaced);
+		return writeEntry(ie, replaced);
 	}
 
-private:
-	errorT readIndex(const Progress& progress);
+	/// Add the game's roster tags and gamedata to the database.
+	/// Set the references to the new data in @e ie.
+	errorT addGameNamesAndData(IndexEntry& ie, TagRoster const& tags,
+	                           const byte* srcData, size_t dataLen) {
+		if (!ie.isChessStd())
+			return ERROR_CodecChess960;
+
+		auto errNames = tags.map(
+		    ie, [&](auto nt, auto name) { return dyn_addName(nt, name); });
+		if (errNames)
+			return errNames;
+
+		auto [err, offset] = dyn_addGameData(srcData, dataLen);
+		if (!err) {
+			ie.SetOffset(offset);
+			ie.SetLength(dataLen);
+		}
+		return err;
+	}
+
+	errorT readIndex(gamenumT nGames, Progress const& progress);
+
+	errorT writeEntry(const IndexEntry& ie, gamenumT gnum);
 };
 
 #endif
diff -pruN 1:4.7.0+dfsg1-2/src/common.h 1:4.7.4+dfsg1-2/src/common.h
--- 1:4.7.0+dfsg1-2/src/common.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/common.h	2022-07-09 05:14:42.000000000 +0000
@@ -16,145 +16,46 @@
 #ifndef SCID_COMMON_H
 #define SCID_COMMON_H
 
+#include "board_def.h"
+#include "error.h"
+#include <assert.h>
 #include <cstddef>
-#if defined(_MSC_VER) && _MSC_VER <= 1600
-typedef unsigned __int8   uint8_t;
-typedef unsigned __int16  uint16_t;
-typedef unsigned __int32  uint32_t;
-typedef unsigned __int64  uint64_t;
-typedef __int32  int32_t;
-#else
 #include <stdint.h>
-#endif // _MSC_VER <= 1600
-
-#include "error.h"
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CONSTANTS:
-
-// Buffer sizes
-#define BBUF_SIZE 256000 //120000
+#define ASSERT(f) assert(f)
 
-typedef unsigned short versionT;
 
 // Version: div by 100 for major number, modulo 100 for minor number
 // so 109 = 1.9, 110 = 1.10, etc.
-
+typedef unsigned short versionT;
 const versionT SCID_VERSION = 400;     // Current file format version = 4.0
-const versionT SCID_OLDEST_VERSION = 300; // Oldest readable file format: 3.0
-
-const char SCID_VERSION_STRING[] = "4.7.0";     // Current Scid version
-const char SCID_WEBSITE[] = "http://scid.sourceforge.net/";
-
-const char PGN_SUFFIX[] = ".pgn";
 
+const char SCID_VERSION_STRING[] = "4.7.4";     // Current Scid version
 
-//////////////////////////////////////////////////////////////////////
-// ASSERT macro: asserts an expression. Differs from the standard
-//    assert in that it does NOT print the expression (this is a waste,
-//    if an assert fails you can go to the code to see why) and that
-//    it MUST be a statement, not part of a larger expression.
-//    Adapted from the book "Writing Solid Code".
-#include <assert.h>
-#define ASSERT(f) assert(f)
-
-
-// Bit Manipulations
-
-#define BIT_7(x)            ((x) & 128)
-#define BIT_6(x)            ((x) &  64)
-#define BIT_5(x)            ((x) &  32)
-#define BIT_4(x)            ((x) &  16)
-#define BIT_3(x)            ((x) &   8)
-#define BIT_2(x)            ((x) &   4)
-#define BIT_1(x)            ((x) &   2)
-#define BIT_0(x)            ((x) &   1)
-
-// Upper or lower 4 bits of a byte, in the range 0..15
-
-#define UPPER_4_BITS(x)     (((x) & 240) >> 4)      // 240 is (15 * 16)
-#define LOWER_4_BITS(x)     ((x) &  15)
-
-//  Upper or lower 12 bits of an integer, in the range 0..4095
-//
-#define UPPER_12_BITS(x)    (((x) & (4096 * 4095)) >> 12)
-#define LOWER_12_BITS(x)    ((x) &  4095)
 
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // TYPE DEFINITIONS
 
 //  General types
-
 typedef unsigned char byte;      //  8 bit unsigned
 typedef uint16_t ushort;         // 16 bit unsigned
 typedef uint32_t uint;           // 32 bit unsigned
 typedef int32_t  sint;           // 32 bit signed
 
-
-//  compareT: comparison type
-
-typedef signed int    compareT;
-const compareT
-    LESS_THAN = -1,   EQUAL_TO = 0,   GREATER_THAN = 1;
-
 //  Chess Types
-
-typedef byte                    pieceT;      // e.g ROOK or WK
-typedef byte                    colorT;      // WHITE or BLACK
-typedef byte                    squareT;     // e.g. A3
 typedef byte                    directionT;  // e.g. UP_LEFT
-typedef byte                    rankT;       // Chess board rank
-typedef byte                    fyleT;       // Chess board file
 typedef byte                    leftDiagT;   // Up-left diagonals
 typedef byte                    rightDiagT;  // Up-right diagonals
-
-// boardT: 64 squares plus two extra squares: one for storing the side
-//   to move as a byte and one for the string terminator, so boards can
-//   be compared using regular string functions:
-typedef pieceT                  boardT [66];
-
-typedef byte                    smallBoardT [32];
-                                    // A more densely packed board, 2 squares
-                                    // per byte.
-
 typedef byte                    castleDirT;  // LEFT or RIGHT
 
-//  Other Small Types
-
-typedef ushort                  statusT;
-
-//  Fixed String Types
-
 typedef char    sanStringT [ 10];   // SAN Move Notation
 
-//  File-related Types
-
-typedef char    fileNameT [512];
-typedef uint    fileLengthT;
-
 enum fileModeT {
     FMODE_None = 0,
     FMODE_ReadOnly,
     FMODE_WriteOnly,
     FMODE_Both,
-    FMODE_Create,
-    FMODE_Memory
-};
-
-//  Date type: see date.h and date.cpp
-
-typedef uint    dateT;
-
-
-// There are four name types: PLAYER, EVENT, SITE and ROUND tags.
-// Names are accessed through IDs.
-typedef uint32_t idNumberT; // Should be idNameT
-typedef unsigned nameT;
-enum {
-    NAME_PLAYER, NAME_EVENT, NAME_SITE, NAME_ROUND,
-    NUM_NAME_TYPES,
-    NAME_INVALID = 99
+    FMODE_Create
 };
 
 
@@ -177,7 +78,7 @@ const byte RATING_USCF = 4;
 const byte RATING_DWZ = 5;
 const byte RATING_BCF = 6;
 
-extern const char * ratingTypeNames [17];   // Defined in game.cpp
+extern const char * ratingTypeNames [8];   // Defined in game.cpp
 
 
 
@@ -199,58 +100,16 @@ const resultT RESULT_OPPOSITE [4] = {
     RESULT_None, RESULT_Black, RESULT_White, RESULT_Draw
 };
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHESS PIECES, COLORS AND THEIR MACROS
-
-const uint NUM_COLOR_TYPES = 2;
-const colorT
-    WHITE = 0,
-    BLACK = 1,
-    NOCOLOR = 2;
-
-const char COLOR_CHAR [3] = {'W', 'B', '_' };
-
-  inline colorT
-color_Flip (colorT c) { return 1 - c; }
-
-  inline char
-color_Char(colorT c)  { return COLOR_CHAR[c]; }
 
 const castleDirT  QSIDE = 0,  KSIDE = 1;
 
 
-// PIECE TYPES (without color; same value as a white piece)
-
-const pieceT
-    INVALID_PIECE = 0,
-    KING = 1,
-    QUEEN = 2,
-    ROOK = 3,
-    BISHOP = 4,
-    KNIGHT = 5,
-    PAWN = 6;
-
-// PIECES:
-//   Note that color(x) == ((x & 0x8) >> 3)  and  type(x) == (x & 0x7)
-//   EMPTY is deliberately nonzero, and END_OF_BOARD is zero, so that
-//   a board can be used as a regular 0-terminated string, provided
-//   that board[NULL_SQUARE] == END_OF_BOARD, as it always should be.
-
-const pieceT  EMPTY = 7;
-const pieceT  END_OF_BOARD = 0;
-const pieceT  WK =  1, WQ =  2, WR =  3, WB =  4, WN =  5, WP =  6;
-const pieceT  BK =  9, BQ = 10, BR = 11, BB = 12, BN = 13, BP = 14;
-
 // Minor piece definitions, used for searching by material only:
 const pieceT  WM = 16, BM = 17;
 
 const uint MAX_PIECE_TYPES = 18;
 
 
-// PIECE_CHAR[]: array of piece characters, capitals for White pieces.
-
-const char PIECE_CHAR [] = "xKQRBNP.xkqrbnpxMm";
-
 // PIECE_FLIP[]: array of pieces, with colors reversed.
 
 const pieceT PIECE_FLIP [MAX_PIECE_TYPES] = {
@@ -267,62 +126,11 @@ const bool PIECE_IS_SLIDER [8] = {
     false,
 };
 
-// PIECE_VALUE: Piece values, K=1000, Q=9, R=5, B=N=3, P=1
-
-const int PIECE_VALUE [MAX_PIECE_TYPES] = {
-    0,
-    100, 9, 5, 3, 3, 1,
-    0, 0,
-    -100, -9, -5, -3, -3, -1,
-    0, 3, -3
-};
-
-//
-// INLINE FUNCTIONS for pieces
-//
-
-  inline colorT
-piece_Color(pieceT p)  { return (p == EMPTY) ? NOCOLOR : ((p & 8) >> 3); }
-
-// Slightly faster piece_Color when we are sure the piece is not empty:
-  inline colorT
-piece_Color_NotEmpty(pieceT p)  { return (p & 8) >> 3; }
-
-  inline pieceT
-piece_Type(pieceT p)  { return (p & 7); }
-
-  inline pieceT
-piece_Make(colorT c, pieceT p)  { return ((c << 3) | (p & 7)); }
-
-  inline bool
-piece_IsWhite(pieceT p)  { return (p>=WK && p<=WP); }
-
-  inline bool
-piece_IsBlack(pieceT p)  { return (p>=BK && p<=BP); }
-
   inline bool
 piece_IsKing(pieceT p)  { return (piece_Type(p) == KING); }
-
-  inline bool
-piece_IsQueen(pieceT p)  { return (piece_Type(p) == QUEEN); }
-
-  inline bool
-piece_IsRook(pieceT p)  { return (piece_Type(p) == ROOK); }
-
-  inline bool
-piece_IsBishop(pieceT p)  { return (piece_Type(p) == BISHOP); }
-
-  inline bool
-piece_IsKnight(pieceT p)  { return (piece_Type(p) == KNIGHT); }
-
-  inline bool
-piece_IsPawn(pieceT p)  { return (piece_Type(p) == PAWN); }
-
   inline bool
 piece_IsSlider(pieceT p) { return PIECE_IS_SLIDER[piece_Type(p)]; }
 
-  inline char
-piece_Char(pieceT p)  { return PIECE_CHAR[piece_Type(p)]; }
 
   inline pieceT
 piece_FromChar(int x)
@@ -337,61 +145,10 @@ piece_FromChar(int x)
     }
 }
 
-inline int
-piece_Value (pieceT p)  { return PIECE_VALUE[p]; }
-
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // SQUARES AND SQUARE MACROS
 
-const squareT
-    A1 = 0, B1 = 1, C1 = 2, D1 = 3, E1 = 4, F1 = 5, G1 = 6, H1 = 7,
-    A2 = 8, B2 = 9, C2 =10, D2 =11, E2 =12, F2 =13, G2 =14, H2 =15,
-    A3 =16, B3 =17, C3 =18, D3 =19, E3 =20, F3 =21, G3 =22, H3 =23,
-    A4 =24, B4 =25, C4 =26, D4 =27, E4 =28, F4 =29, G4 =30, H4 =31,
-    A5 =32, B5 =33, C5 =34, D5 =35, E5 =36, F5 =37, G5 =38, H5 =39,
-    A6 =40, B6 =41, C6 =42, D6 =43, E6 =44, F6 =45, G6 =46, H6 =47,
-    A7 =48, B7 =49, C7 =50, D7 =51, E7 =52, F7 =53, G7 =54, H7 =55,
-    A8 =56, B8 =57, C8 =58, D8 =59, E8 =60, F8 =61, G8 =62, H8 =63,
-    COLOR_SQUARE = 64,
-    NULL_SQUARE = 65, NS = 65;    // NS is abbreviation for NULL_SQUARE.
-
-const rankT
-    RANK_1 = 0, RANK_2 = 1, RANK_3 = 2, RANK_4 = 3, RANK_5 = 4, RANK_6 = 5,
-    RANK_7 = 6, RANK_8 = 7, NO_RANK = 64;
-
-const fyleT
-    // we use "fyle" instead of "file" to avoid confusion with disk files.
-    A_FYLE = 0, B_FYLE = 1, C_FYLE = 2, D_FYLE = 3, E_FYLE = 4, F_FYLE = 5,
-    G_FYLE = 6, H_FYLE = 7, NO_FYLE = 64;
-
-  inline rankT
-rank_FromChar(char c)
-{   if (c < '1'  ||  c > '8') { return NO_RANK; } else return (c - '1');  }
-
-  inline fyleT
-fyle_FromChar(char c)
-{   if (c < 'a'  ||  c > 'h') { return NO_FYLE; } else return (c - 'a');  }
-
-  inline squareT
-square_Make(fyleT f, rankT r)
-{
-    ASSERT (f <= H_FYLE  &&  r <= RANK_8);
-    return ((r << 3) | f);
-}
-
-  inline fyleT
-square_Fyle(squareT sq)
-{
-    return (sq & 0x7);
-}
-
-  inline rankT
-square_Rank(squareT sq)
-{
-    return ((sq >> 3) & 0x7);
-}
-
   inline leftDiagT
 square_LeftDiag (squareT sq)
 {
@@ -528,21 +285,6 @@ square_RankChar (squareT sq)
     return square_Rank(sq) + '1';
 }
 
-  inline void
-square_Print (squareT sq, char * str)
-{
-    if (sq <= H8) {
-        str[0] = square_FyleChar(sq);
-        str[1] = square_RankChar(sq);
-        str[2] = 0;
-    } else if (sq == NULL_SQUARE) {
-        str[0] = 'N'; str[1] = 'S'; str[2] = 0;
-    } else {
-        str[0] = 'X'; str[1] = 'X'; str[2] = 0;
-    }
-    return;
-}
-
 // Directions:
 // Up = 1, Down = 2, Left = 4, Right = 8, UpLeft = 5, UpRight = 9,
 // DownLeft = 6, DownRight = 10
@@ -628,8 +370,8 @@ direction_Delta (directionT dir)
 
 // The starting Board
 //
-  const boardT
-START_BOARD =
+  const pieceT
+START_BOARD[66] =
 {
     WR, WN, WB, WQ, WK, WB, WN, WR,    // A1--H1
     WP, WP, WP, WP, WP, WP, WP, WP,    // A2--H2
@@ -643,7 +385,6 @@ START_BOARD =
     END_OF_BOARD  // NULL_SQUARE
 };
 
-
 // Square colors for the standard chess board:
 //
   const colorT
@@ -659,17 +400,6 @@ BOARD_SQUARECOLOR[66] = {
     NOCOLOR, NOCOLOR  // Color square and Null square
 };
 
-  inline int
-board_Compare (const pieceT * b1, const pieceT * b2)
-{
-    for (uint i=0; i < 64; i++) {
-        if (*b1 < *b2) { return -1; }
-        if (*b1 > *b2) { return 1; }
-        b1++; b2++;
-    }
-    return 0;
-}
-
 // square_Adjacent: returns 1 if the two squares are adjacent. Note that
 //    diagonal adjacency is included: a1 and b2 are adjacent.
 //    Also note that a square is adjacent to itself.
diff -pruN 1:4.7.0+dfsg1-2/src/containers.h 1:4.7.4+dfsg1-2/src/containers.h
--- 1:4.7.0+dfsg1-2/src/containers.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/containers.h	2022-07-09 05:14:42.000000000 +0000
@@ -19,7 +19,8 @@
 #ifndef CONTAINERS_H
 #define CONTAINERS_H
 
-#include "common.h" // for ASSERT
+#include <algorithm>
+#include <cassert>
 #include <vector>
 
 /**
@@ -121,11 +122,11 @@ public:
 	}
 
 	const T& operator[](size_t idx) const {
-		ASSERT(idx < size_);
+		assert(idx < size_);
 		return chunks_[idx >> CHUNKSHIFT][idx & low_mask];
 	}
 	T& operator[](size_t idx) {
-		ASSERT(idx < size_);
+		assert(idx < size_);
 		return chunks_[idx >> CHUNKSHIFT][idx & low_mask];
 	}
 
@@ -136,10 +137,42 @@ public:
 	 * the count of contiguously allocated objects starting at @e pos (included)
 	 */
 	size_t contiguous(size_t pos) const {
-		ASSERT(pos < size());
+		assert(pos < size());
 		return 1 + (~pos & low_mask);
 	}
 
+	/// Returns the next offset in the container where at least @e nElements
+	/// can be inserted contiguously
+	size_t next_contiguous(size_t nElements) const {
+		const auto offset = size();
+		const auto capacity = this->capacity();
+		return capacity - offset < nElements // Doesn't fit in the current chunk
+		           ? capacity
+		           : offset;
+	}
+
+	/// Append elements to the container
+	/// @param src: an array of T objects
+	/// @param srcSize: the number of elements in the array
+	/// @param offset: a value >= size() where the new elements can be inserted
+	///                contiguously
+	void append(const T* src, size_t srcSize, size_t offset) {
+		assert(offset >= size());
+		resize(offset + srcSize);
+		assert(contiguous(offset) >= srcSize);
+		std::copy_n(src, srcSize, &operator[](offset));
+	}
+
+	/// Append elements to the container
+	/// @param src: an array of T objects
+	/// @param srcSize: the number of elements in the array
+	/// @return: a pointer to the first inserted element
+	const T* append(const T* src, size_t srcSize) {
+		const auto offset = next_contiguous(srcSize);
+		append(src, srcSize, offset);
+		return &operator[](offset);
+	}
+
 	void push_back(const T& e) {
 		size_t idx = size_;
 		resize(size_ + 1);
diff -pruN 1:4.7.0+dfsg1-2/src/crosstab.cpp 1:4.7.4+dfsg1-2/src/crosstab.cpp
--- 1:4.7.0+dfsg1-2/src/crosstab.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/crosstab.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -140,7 +140,7 @@ Crosstable::Init ()
     MaxRound = 0;
     FirstDate = ZERO_DATE;
     for (resultT r = 0; r < NUM_RESULT_TYPES; r++) { ResultCount[r] = 0; }
-    ShowTitles = ShowElos = ShowCountries = ShowTallies = SwissColors = ShowAges = true;
+    ShowTitles = ShowElos = ShowFlags = ShowCountries = ShowTallies = SwissColors = ShowAges = true;
     ShowTiebreaks = false;
     SortOption = CROSSTABLE_SortScore;
     OutputFormat = CROSSTABLE_Plain;
@@ -481,6 +481,7 @@ Crosstable::PrintTable (DString * dstr,
     PrintRatings = false;
     PrintTitles = false;
     PrintCountries = false;
+    PrintFlags = false;
     PrintAges = false;
     PrintTiebreaks = true;
     PrintTallies = true;
@@ -488,7 +489,7 @@ Crosstable::PrintTable (DString * dstr,
         playerDataT * pd = PlayerData[player];
         if (pd->elo > 0) { PrintRatings = true; }
         if (pd->title[0] != 0) { PrintTitles = true; }
-        if (pd->country[0] != 0) { PrintCountries = true; }
+        if (pd->country[0] != 0) { PrintCountries = true; PrintFlags = true; }
         if (pd->birthdate != ZERO_DATE) {
             PrintAges = true;
             int age = (int) date_GetYear(FirstDate);
@@ -502,6 +503,7 @@ Crosstable::PrintTable (DString * dstr,
     if (! ShowElos) { PrintRatings = false; }
     if (! ShowTitles) { PrintTitles = false; }
     if (! ShowCountries) { PrintCountries = false; }
+    if (! ShowFlags) { PrintFlags = false; }
     if (! ShowTallies) { PrintTallies = false; }
     if (! ShowAges) { PrintAges = false; }
     if (! ShowTiebreaks) { PrintTiebreaks = false; }
@@ -558,14 +560,15 @@ Crosstable::PrintTable (DString * dstr,
     if (PrintRatings) { LineWidth += 16; }
     if (PrintTitles) { LineWidth += 4; }
     if (PrintCountries) { LineWidth += 4; }
+    if (PrintFlags && OutputFormat == CROSSTABLE_Hypertext) { LineWidth += 3; }
     if (PrintAges) { LineWidth += 4; }
 
     if (mode == CROSSTABLE_Swiss) {
-        LineWidth += 16 + PlayerNumWidth;
+        LineWidth += 28 + PlayerNumWidth;
         LineWidth += (PlayerNumWidth + (SwissColors ? 3 : 2)) * MaxRound;
         if (PrintTiebreaks) { LineWidth += 5; }
     } else if (mode == CROSSTABLE_AllPlayAll) {
-        LineWidth += 16 + PlayerNumWidth;
+        LineWidth += 28 + PlayerNumWidth;
         if (playerLimit == 2) {
             LineWidth += (MaxClashes + 1);
         } else {
@@ -577,6 +580,7 @@ Crosstable::PrintTable (DString * dstr,
         if (PrintRatings) { LineWidth += 10; }
         if (PrintTitles) { LineWidth += 8; }
         if (PrintCountries) { LineWidth += 8; }
+        if (PrintFlags) { LineWidth += 4; }
         if (PrintAges) { LineWidth += 8; }
     }
 
@@ -647,11 +651,11 @@ Crosstable::PrintPlayer (DString * dstr,
         if (pdata->elo) {
             sprintf (stemp, "%4u ", pdata->elo);
         } else {
-	    if (OutputFormat == CROSSTABLE_Html) {
-	      strcpy (stemp, "  -  ");
-	    } else {
-	      strcpy (stemp, "     ");
-	    }
+            if (OutputFormat == CROSSTABLE_Html) {
+                strcpy (stemp, "  -  ");
+            } else {
+                strcpy (stemp, "     ");
+            }
         }
         dstr->Append (StartRightCol, stemp, EndRightCol);
     }
@@ -660,31 +664,35 @@ Crosstable::PrintPlayer (DString * dstr,
     //   as firefox doesn't make a grid box for blanks S.A.
 
     if (PrintTitles) {
-	if (OutputFormat == CROSSTABLE_Html && !strCompare(pdata->title,"")) {
-	  sprintf (stemp, " -  ");
-	} else {
-	  sprintf (stemp, "%3s ", pdata->title);
-	}
+        if (OutputFormat == CROSSTABLE_Html && !strCompare(pdata->title,"")) {
+            sprintf (stemp, " -  ");
+        } else {
+            sprintf (stemp, "%3s ", pdata->title);
+        }
         dstr->Append (StartCol, stemp, EndCol);
     }
     if (PrintAges) {
         if (pdata->ageInYears == 0) {
-	    if (OutputFormat == CROSSTABLE_Html) {
-	      sprintf (stemp, " -  ");
-	    } else {
-	      strCopy (stemp, "    ");
-	    }
+            if (OutputFormat == CROSSTABLE_Html) {
+                sprintf (stemp, " -  ");
+            } else {
+                strCopy (stemp, "    ");
+            }
         } else {
             sprintf (stemp, "%3d ", pdata->ageInYears);
         }
         dstr->Append (StartCol, stemp, EndCol);
     }
     if (PrintCountries) {
-	if (OutputFormat == CROSSTABLE_Html && !strCompare(pdata->country,"")) {
-	  sprintf (stemp, " -  ");
-	} else {
-	  sprintf (stemp, "%-3s ", pdata->country);
-	}
+        if (OutputFormat == CROSSTABLE_Html && !strCompare(pdata->country,"")) {
+            sprintf (stemp, " -  ");
+        } else {
+            sprintf (stemp, "%-3s ", pdata->country);
+        }
+        dstr->Append (StartCol, stemp, EndCol);
+    }
+    if (PrintFlags && OutputFormat == CROSSTABLE_Hypertext) {
+        sprintf (stemp, "<img flag_%s>",  pdata->country[0] ? pdata->country : "unkown");
         dstr->Append (StartCol, stemp, EndCol);
     }
     if (OutputFormat == CROSSTABLE_Hypertext) { dstr->Append ("</pi>"); }
@@ -768,14 +776,19 @@ Crosstable::PrintAllPlayAll (DString * d
     if (PrintCountries) {
         dstr->Append (StartBoldCol, " Nat", EndBoldCol);
     }
+    if (PrintFlags && OutputFormat == CROSSTABLE_Hypertext) {
+        dstr->Append (StartBoldCol, " Nat ", EndBoldCol);
+    }
     if (OutputFormat == CROSSTABLE_LaTeX) {
         // Todo : fix LateX Score column alignment with 3 points for win.
         dstr->Append (" \\multicolumn{2}{c}{\\bf Score} & ");
     } else {
         if (ThreeWin) 
-	  dstr->Append ("  ", StartBoldCol, "Score", EndBoldCol, " ");
+            dstr->Append ("  ", StartBoldCol, "Score", EndBoldCol, " ");
+        else if (PrintFlags && OutputFormat == CROSSTABLE_Hypertext)
+            dstr->Append (" ", StartBoldCol, "Score  ", EndBoldCol, "   ");
         else
-	  dstr->Append ("   ", StartBoldCol, " Score ", EndBoldCol, "   ");
+            dstr->Append ("  ", StartBoldCol, " Score  ", EndBoldCol, "   ");
     }
     if (PrintTiebreaks) {
         dstr->Append (StartBoldCol, " (Tie) ", EndBoldCol);
@@ -783,7 +796,7 @@ Crosstable::PrintAllPlayAll (DString * d
 
     for (player = 0; player < playerLimit; player++) {
         strPad (stemp, PlayerData[SortedIndex[player]]->name, MaxClashes, ' ');
-        if (APAColumnNums) {
+        if (APAColumnNums && MaxClashes > 0) {
             // Print numbers instead of names over columns:
             strPad (stemp, "", MaxClashes, ' ');
             uint pnum = player + 1;
@@ -809,10 +822,15 @@ Crosstable::PrintAllPlayAll (DString * d
         }
     }
     if (PrintRatings) {
-        dstr->Append ("   ", StartBoldCol, "Perf Chg Percnt", EndBoldCol);
+        if ( OutputFormat == CROSSTABLE_Html) {
+            dstr->Append ("   ", StartBoldCol, "Perf Chg", EndBoldCol);
+        } else {
+            dstr->Append ("   ", StartBoldCol, "Perf Chg Percnt", EndBoldCol);
+        }
     } else
         dstr->Append (" ", StartBoldCol, "Percnt", EndBoldCol);
     if (PrintTallies && OutputFormat == CROSSTABLE_Html) {
+        dstr->Append ("   ", StartBoldCol, "Perc", EndBoldCol);
         dstr->Append ("   ", StartBoldCol, "+/-/=", EndBoldCol);
     }
     dstr->Append (EndRow, NewLine);
@@ -974,13 +992,18 @@ Crosstable::PrintSwiss (DString * dstr,
     if (PrintCountries) {
         dstr->Append (StartBoldCol, " Nat", EndBoldCol);
     }
+    if (PrintFlags && OutputFormat == CROSSTABLE_Hypertext) {
+        dstr->Append (StartBoldCol, " Nat ", EndBoldCol);
+    }
     if (OutputFormat == CROSSTABLE_LaTeX) {
         dstr->Append (" \\multicolumn{2}{c}{\\bf Score} & ");
     } else {
         if (ThreeWin) 
-	  dstr->Append ("  ", StartBoldCol, "Score", EndBoldCol, " ");
+            dstr->Append ("  ", StartBoldCol, "Score", EndBoldCol, " ");
+        else if (PrintFlags && OutputFormat == CROSSTABLE_Hypertext)
+            dstr->Append (" ", StartBoldCol, "Score  ", EndBoldCol, "   ");
         else
-	  dstr->Append ("   ", StartBoldCol, " Score ", EndBoldCol, "   ");
+            dstr->Append ("  ", StartBoldCol, " Score  ", EndBoldCol, "   ");
     }
     if (PrintTiebreaks) {
         dstr->Append (StartBoldCol, "(Tie)", EndBoldCol);
@@ -996,10 +1019,15 @@ Crosstable::PrintSwiss (DString * dstr,
         }
     }
     if (PrintRatings) {
-        dstr->Append ("   ", StartBoldCol, "Perf Chg Percnt", EndBoldCol);
+        if ( OutputFormat == CROSSTABLE_Html) {
+            dstr->Append ("   ", StartBoldCol, "Perf Chg", EndBoldCol);
+        } else {
+            dstr->Append ("   ", StartBoldCol, "Perf Chg Percnt", EndBoldCol);
+        }
     } else
         dstr->Append (" ", StartBoldCol, "Percnt", EndBoldCol);
     if (PrintTallies && OutputFormat == CROSSTABLE_Html) {
+        dstr->Append ("   ", StartBoldCol, "Perc", EndBoldCol);
         dstr->Append ("   ", StartBoldCol, "+/-/=", EndBoldCol);
     }
     dstr->Append (EndRow, NewLine);
diff -pruN 1:4.7.0+dfsg1-2/src/crosstab.h 1:4.7.4+dfsg1-2/src/crosstab.h
--- 1:4.7.0+dfsg1-2/src/crosstab.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/crosstab.h	2022-07-09 05:14:42.000000000 +0000
@@ -90,6 +90,7 @@ class Crosstable
     bool         ShowTitles;
     bool         ShowElos;
     bool         ShowCountries;
+    bool         ShowFlags;
     bool         ShowTallies;
     bool         ShowAges;
     bool         ShowTiebreaks;
@@ -110,6 +111,7 @@ class Crosstable
     bool         PrintTitles;
     bool         PrintRatings;
     bool         PrintCountries;
+    bool         PrintFlags;
     bool         PrintTallies;
     bool         PrintAges;
     bool         PrintTiebreaks;
@@ -167,6 +169,7 @@ class Crosstable
     void   SetTitles (bool b) { ShowTitles = b; }
     void   SetElos (bool b) { ShowElos = b; }
     void   SetCountries (bool b) { ShowCountries = b; }
+    void   SetFlags (bool b) { ShowFlags = b; }
     void   SetTallies (bool b) { ShowTallies = b; }
     void   SetTiebreaks (bool b) { ShowTiebreaks = b; }
     void   SetSwissColors (bool b) { SwissColors = b; }
diff -pruN 1:4.7.0+dfsg1-2/src/date.h 1:4.7.4+dfsg1-2/src/date.h
--- 1:4.7.0+dfsg1-2/src/date.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/date.h	2022-07-09 05:14:42.000000000 +0000
@@ -16,9 +16,10 @@
 #ifndef SCID_DATE_H
 #define SCID_DATE_H
 
-#include "common.h"
 #include <algorithm>
+#include <cstddef>
 #include <cstdlib>
+#include <stdint.h>
 
 // DATE STORAGE FORMAT:
 // In memory, dates are stored in a 32-bit (4-byte) uint, of which only
@@ -32,15 +33,17 @@
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 //  CONSTANTS and MACROS:
 
+typedef uint32_t    dateT;
+
 const dateT ZERO_DATE = 0;
 
-const uint  YEAR_SHIFT  = 9;
-const uint  MONTH_SHIFT = 5;
-const uint  DAY_SHIFT   = 0;
+const uint32_t  YEAR_SHIFT  = 9;
+const uint32_t  MONTH_SHIFT = 5;
+const uint32_t  DAY_SHIFT   = 0;
 
 // DAY (31 days) 5 bits (32) , MONTH (12 months) 4 bits (16)
 
-const uint YEAR_MAX = 2047;  // 2^11 - 1
+const uint32_t YEAR_MAX = 2047;  // 2^11 - 1
 
 #define DATE_MAKE(y,m,d) (((y) << YEAR_SHIFT) | ((m) << MONTH_SHIFT) | (d))
 
@@ -48,37 +51,37 @@ const uint YEAR_MAX = 2047;  // 2^11 - 1
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // date_GetYear():
 //      Get the year from a date.
-inline uint
+inline uint32_t
 date_GetYear (dateT date)
 {
-    return (uint) (date >> YEAR_SHIFT);
+    return (uint32_t) (date >> YEAR_SHIFT);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // date_GetMonth():
 //      Get the month from a date.
-inline uint
+inline uint32_t
 date_GetMonth (dateT date)
 {
-    return (uint) ((date >> MONTH_SHIFT) & 15);
+    return (uint32_t) ((date >> MONTH_SHIFT) & 15);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 //### date_GetDay():
 //      Get the day of the month from a date.
-inline uint
+inline uint32_t
 date_GetDay (dateT date)
 {
-    return (uint) (date & 31);
+    return (uint32_t) (date & 31);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 //### date_GetMonthDay():
 //      Get the month and day; used to check for year-only dates. S.A
-inline uint
+inline uint32_t
 date_GetMonthDay (dateT date)
 {
-    return (uint) (date & 511);
+    return (uint32_t) (date & 511);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -87,7 +90,7 @@ inline void
 date_DecodeToString (dateT date, char * str)
 {
     ASSERT(str != NULL);
-    uint year, month, day;
+    uint32_t year, month, day;
 
     year = date_GetYear (date);
     month = date_GetMonth (date);
@@ -130,7 +133,7 @@ date_EncodeFromString (const char * str)
     ASSERT(str != NULL);
 
     dateT date;
-    uint year, month, day;
+    uint32_t year, month, day;
 
     // convert year:
     year = std::strtoul(str, NULL, 10);
diff -pruN 1:4.7.0+dfsg1-2/src/dbasepool.cpp 1:4.7.4+dfsg1-2/src/dbasepool.cpp
--- 1:4.7.0+dfsg1-2/src/dbasepool.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/dbasepool.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -36,11 +36,13 @@ scidBaseT* dbList = NULL;      // array
 
 void DBasePool::init() {
 	dbList = new scidBaseT[MAX_BASES];
+	DBasePool::clearClipBase();
+	DBasePool::switchCurrent(&(dbList[CLIPBASE_NUM]));
+}
 
-	dbList[CLIPBASE_NUM].Open(ICodecDatabase::MEMORY, FMODE_Memory, "<clipbase>");
+void DBasePool::clearClipBase() {
+	dbList[CLIPBASE_NUM].open("MEMORY", FMODE_Create, "<clipbase>");
 	dbList[CLIPBASE_NUM].setExtraInfo("type", "2");
-
-	DBasePool::switchCurrent(&(dbList[CLIPBASE_NUM]));
 }
 
 void DBasePool::closeAll() {
diff -pruN 1:4.7.0+dfsg1-2/src/dbasepool.h 1:4.7.4+dfsg1-2/src/dbasepool.h
--- 1:4.7.0+dfsg1-2/src/dbasepool.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/dbasepool.h	2022-07-09 05:14:42.000000000 +0000
@@ -34,6 +34,10 @@ namespace DBasePool {
 void init();
 
 
+ /// Reset the ClipBase to empty.
+void clearClipBase();
+
+
 /**
  * closeAll() - close all the databases in the pool.
  *
diff -pruN 1:4.7.0+dfsg1-2/src/dstring.h 1:4.7.4+dfsg1-2/src/dstring.h
--- 1:4.7.0+dfsg1-2/src/dstring.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/dstring.h	2022-07-09 05:14:42.000000000 +0000
@@ -15,7 +15,6 @@
 #ifndef SCID_DSTRING_H
 #define SCID_DSTRING_H
 
-#include "common.h"
 #include <stdio.h>
 #include <string>
 
diff -pruN 1:4.7.0+dfsg1-2/src/engine.cpp 1:4.7.4+dfsg1-2/src/engine.cpp
--- 1:4.7.0+dfsg1-2/src/engine.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/engine.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -772,13 +772,10 @@ Engine::ScorePawnStructure (pawnTableEnt
         pawnFiles[BLACK][f+1] = Pos.FyleCount (BP, f);
     }
 
-    for (colorT c = WHITE; c <= BLACK; c++) {
-        pieceT pawn = piece_Make (c, PAWN);
-        uint npawns = Pos.PieceCount(pawn);
-        SquareList sqlist;
-        Pos.GetSquares (pawn, &sqlist);
-        for (uint i = 0; i < npawns; i++) {
-            squareT sq = sqlist.Get(i);
+    for (squareT sq = A1; sq <= H8; ++sq) {
+        pieceT piece = Pos.GetPiece(sq);
+        if (piece_Type(piece) == PAWN) {
+            colorT c = piece_Color_NotEmpty(piece);
             squareT wsq = (c == WHITE) ? sq : square_FlipRank(sq);
             squareT bonusSq = square_FlipRank(wsq);
             pawnScore[c] += PawnSquare[bonusSq];
@@ -924,49 +921,10 @@ Engine::IsGettingMatedScore (int score)
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Engine::PlayMove
-//   Play the specified move, not in a search.
-void
-Engine::PlayMove (simpleMoveT * sm) {
-    PushRepeat(&RootPos);
-    RootPos.DoSimpleMove(sm);
-    Pos.DoSimpleMove(sm);
-#ifdef WINCE
-    simpleMoveT * newMove = (simpleMoveT *) my_Tcl_Alloc(sizeof(simpleMoveT));
-#else
-    simpleMoveT * newMove = new simpleMoveT;
-#endif
-    *newMove = *sm;
-    GameMoves[NumGameMoves] = newMove;
-    NumGameMoves++;
-    // Change the transposition table sequence number:
-    TranTableSequence++;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Engine::RetractMove
-//    Take back a move played in the game.
-void
-Engine::RetractMove (void)
-{
-    if (NumGameMoves == 0) { return; }
-    PopRepeat();
-    NumGameMoves--;
-    RootPos.UndoSimpleMove(GameMoves[NumGameMoves]);
-    Pos.UndoSimpleMove(GameMoves[NumGameMoves]);
-#ifdef WINCE
-    my_Tcl_Free((char *)GameMoves);
-#else
-    delete GameMoves[NumGameMoves];
-#endif
-    TranTableSequence--;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Engine::DoMove
 //   Make the specified move in a search.
 inline void
-Engine::DoMove (simpleMoveT * sm) {
+Engine::DoMove (ScoredMove * sm) {
     PushRepeat(&Pos);
     Pos.DoSimpleMove(sm);
     Ply++;
@@ -976,7 +934,7 @@ Engine::DoMove (simpleMoveT * sm) {
 // Engine::UndoMove
 //    Take back the specified move in a search.
 inline void
-Engine::UndoMove (simpleMoveT * sm) {
+Engine::UndoMove (ScoredMove * sm) {
     PopRepeat();
     Pos.UndoSimpleMove(sm);
     Ply--;
@@ -1164,7 +1122,7 @@ inline byte tte_Castling (transTableEntr
 inline bool tte_IsOnlyMove (transTableEntryT * tte)
 {  return (((tte->flags >> 2) & 1) == 1); }
 
-inline void tte_SetBestMove (transTableEntryT * tte, simpleMoveT * bestMove)
+inline void tte_SetBestMove (transTableEntryT * tte, ScoredMove * bestMove)
 {
     ASSERT (bestMove->from <= H8  &&  bestMove->to <= H8);
     ushort bm = bestMove->from;
@@ -1173,7 +1131,7 @@ inline void tte_SetBestMove (transTableE
     tte->bestMove = bm;
 }
 
-inline void tte_GetBestMove (transTableEntryT * tte, simpleMoveT * bestMove)
+inline void tte_GetBestMove (transTableEntryT * tte, ScoredMove * bestMove)
 {
     ushort bm = tte->bestMove;
     bestMove->promote = bm & 15; bm >>= 4;
@@ -1186,7 +1144,7 @@ inline void tte_GetBestMove (transTableE
 //   Store the score for the current position in the transposition table.
 void
 Engine::StoreHash (int depth, scoreFlagT ttFlag, int score,
-                   simpleMoveT * bestMove, bool isOnlyMove)
+                   ScoredMove * bestMove, bool isOnlyMove)
 {
     if (TranTableSize == 0) { return; }
     ASSERT (ttFlag <= SCORE_UPPER);
@@ -1279,7 +1237,7 @@ Engine::StoreHash (int depth, scoreFlagT
 //    Probe the transposition table for the current position.
 //
 scoreFlagT
-Engine::ProbeHash (int depth, int * score, simpleMoveT * bestMove, bool * isOnlyMove)
+Engine::ProbeHash (int depth, int * score, ScoredMove * bestMove, bool * isOnlyMove)
 {
     // Clear the best move:
     if (bestMove != NULL) { bestMove->from = bestMove->to = NULL_SQUARE; }
@@ -1335,16 +1293,6 @@ static uint nFailHighFirstMove = 0;
 void
 Engine::SetPosition (Position * newpos)
 {
-    // Delete old game moves:
-    for (uint i=0; i < NumGameMoves; i++) {
-#ifdef WINCE
-        my_Tcl_Free((char *) GameMoves[i]);
-#else
-        delete GameMoves[i];
-#endif
-    }
-    NumGameMoves = 0;
-
     // Set the position:
     if (newpos == NULL) {
         RootPos.StdStart();
@@ -1406,7 +1354,7 @@ Engine::Think (MoveList * mlist)
     // Sort the root move list by quiescent evaluation to get a
     // reasonably good initial move order:
     for (uint i=0; i < mlist->Size(); i++) {
-        simpleMoveT * sm = mlist->Get(i);
+        auto sm = mlist->Get(i);
         DoMove(sm);
         sm->score = -Quiesce (-Infinity, Infinity);
         UndoMove(sm);
@@ -1518,11 +1466,11 @@ Engine::SearchRoot (int depth, int alpha
     int bestScore = -Infinity - 1;
 
     for (uint movenum=0; movenum < mlist->Size(); movenum++) {
-        simpleMoveT * sm = mlist->Get(movenum);
+        auto sm = mlist->Get(movenum);
         uint oldNodeCount = NodeCount;
         // Make this move and search it:
         DoMove (sm);
-        InCheck[Ply] = Pos.IsKingInCheck (sm);
+        InCheck[Ply] = Pos.IsKingInCheck (*sm);
 #define PVS_SEARCH
 #ifdef PVS_SEARCH
         int score = alpha;
@@ -1567,6 +1515,11 @@ Engine::SearchRoot (int depth, int alpha
     return bestScore;
 }
 
+static bool isLegalMove(Position const& pos, simpleMoveT const& sm) {
+    return pos.IsLegalMove(sm.from, sm.to, sm.promote) &&
+           sm.movingPiece == pos.GetPiece(sm.from);
+}
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Engine::Search
 //   Internal Search routine, used at every depth except
@@ -1613,7 +1566,7 @@ Engine::Search (int depth, int alpha, in
 
     // Probe the hash table:
     int hashscore = alpha;
-    simpleMoveT hashmove;
+    auto hashmove = ScoredMove();
     bool isOnlyMove = 0;
     scoreFlagT hashflag = ProbeHash (depth, &hashscore, &hashmove, &isOnlyMove);
 
@@ -1692,7 +1645,7 @@ Engine::Search (int depth, int alpha, in
 
     MoveList mlist;
     bool gotHashMove;
-    if (Pos.IsLegalMove (&hashmove)) {
+    if (isLegalMove(Pos, hashmove)) {
         gotHashMove = true;
         // For now, we only add the hash move to the move list.
         mlist.push_back(hashmove);
@@ -1735,7 +1688,7 @@ Engine::Search (int depth, int alpha, in
 
         // Make this move and remember if it gives check:
         DoMove (sm);
-        InCheck[Ply] = Pos.IsKingInCheck (sm);
+        InCheck[Ply] = Pos.IsKingInCheck (*sm);
 
         // Simple futility pruning. Note that pruning with depth of two
         // remaining is risky, but seems to work well enough in practise.
@@ -1819,7 +1772,12 @@ Engine::Search (int depth, int alpha, in
             mlist.Clear();
             Pos.GenerateMoves (&mlist, EMPTY, GEN_ALL_MOVES, InCheck[Ply]);
             ScoreMoves (&mlist);
-            MoveList::iterator hm = std::find(mlist.begin(), mlist.end(), cmpMove(hashmove));
+            MoveList::iterator hm = std::find_if(
+                mlist.begin(), mlist.end(), [&](auto const& move) {
+                    return move.from == hashmove.from &&
+                           move.to == hashmove.to &&
+                           move.promote == hashmove.promote;
+                });
             if (hm != mlist.end()) {
                 std::iter_swap(mlist.begin(), hm);
             } else {
@@ -1845,7 +1803,7 @@ Engine::Search (int depth, int alpha, in
     } else {
         // Update the transposition table with the best move:
         ASSERT (bestMoveIndex >= 0);
-        simpleMoveT * bestMove = mlist.Get(bestMoveIndex);
+        auto bestMove = mlist.Get(bestMoveIndex);
         IncHistoryValue (bestMove, depth * depth);
         // Should we also add this as a killer move? Possibly not,
         // since it was not good enough to cause a beta cutoff.
@@ -1905,7 +1863,7 @@ Engine::Quiesce (int alpha, int beta)
     MoveList mlist;
     Pos.GenerateMoves (&mlist, GEN_CAPTURES);
     for (uint m=0; m < mlist.Size(); m++) {
-        simpleMoveT * sm = mlist.Get(m);
+        auto sm = mlist.Get(m);
         sm->score = SEE (sm->from, sm->to);
     }
 
@@ -2233,7 +2191,7 @@ void
 Engine::ScoreMoves (MoveList * mlist)
 {
     for (uint i = 0; i < mlist->Size(); i++) {
-        simpleMoveT * sm = mlist->Get(i);
+        auto sm = mlist->Get(i);
         if (sm->capturedPiece != EMPTY  ||  sm->promote != EMPTY) {
             int see = SEE (sm->from, sm->to);
             if (see >= 0) {
@@ -2291,11 +2249,11 @@ Engine::PrintPV (uint depth, int score,
 
     // Make and print each PV move:
     for (i = 0; i < pv->length; i++) {
-        simpleMoveT * sm = &(pv->move[i]);
+        auto sm = &(pv->move[i]);
 
         // Check for legality, to protect against hash table
         // false hits and bugs in PV updating:
-        if (! Pos.IsLegalMove (sm)) {
+        if (! isLegalMove(Pos, *sm)) {
             Output (" <illegal>");
             break;
         }
@@ -2355,8 +2313,8 @@ Engine::PerfTest (uint depth)
     Pos.GenerateMoves (&mlist);
     uint nmoves = 0;
     for (uint i = 0; i < mlist.Size(); i++) {
-        simpleMoveT * sm = mlist.Get(i);
-        Pos.DoSimpleMove (sm);
+        auto sm = mlist.Get(i);
+        Pos.DoSimpleMove (*sm);
         nmoves += PerfTest (depth-1);
         Pos.UndoSimpleMove (sm);
     }
diff -pruN 1:4.7.0+dfsg1-2/src/engine.h 1:4.7.4+dfsg1-2/src/engine.h
--- 1:4.7.0+dfsg1-2/src/engine.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/engine.h	2022-07-09 05:14:42.000000000 +0000
@@ -39,7 +39,7 @@ const uint ENGINE_PAWN_KB =            1
 //
 struct principalVarT {
     uint length;
-    simpleMoveT move [ENGINE_MAX_PLY];
+    ScoredMove move [ENGINE_MAX_PLY];
 };
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -132,7 +132,7 @@ private:
     repeatT  RepStack [1024];      // Repetition stack.
     bool     InCheck [ENGINE_MAX_PLY];   // In-check at each ply.
     principalVarT PV [ENGINE_MAX_PLY];   // Principal variation at each ply.
-    simpleMoveT KillerMove [ENGINE_MAX_PLY][2];  // Two killer moves per ply.
+    ScoredMove KillerMove [ENGINE_MAX_PLY][2];  // Two killer moves per ply.
     int History[16][64];    // Success history of piece-to-square moves.
     byte     TranTableSequence;    // Transposition table sequence number.
     uint     TranTableSize;        // Number of Transposition table entries.
@@ -141,8 +141,6 @@ private:
     pawnTableEntryT * PawnTable;   // Pawn structure score hash table.
     bool (*CallbackFunction)(Engine *, void *);  // Periodic callback.
     void *   CallbackData;
-    simpleMoveT * GameMoves [1024];
-    uint      NumGameMoves;
 
 private:
     int PieceValue (pieceT piece);
@@ -151,27 +149,27 @@ private:
     int Quiesce (int alpha, int beta);
     int SEE (squareT from, squareT to);
     void ScoreMoves (MoveList * mlist);
-    inline void DoMove (simpleMoveT * sm);
-    inline void UndoMove (simpleMoveT * sm);
+    inline void DoMove (ScoredMove * sm);
+    inline void UndoMove (ScoredMove * sm);
     inline void SetPVLength (void);
-    inline void UpdatePV (simpleMoveT * sm);
+    inline void UpdatePV (ScoredMove * sm);
     void Output (const char * format, ...);
     void PrintPV (uint depth, int score) { PrintPV (depth, score, ""); }
     void PrintPV (uint depth, int score, const char * annotation);
     inline void PushRepeat (Position * pos);
     inline void PopRepeat (void);
     void StoreHash (int depth, scoreFlagT flag, int score,
-                    simpleMoveT * bestmove, bool isOnlyMove);
-    scoreFlagT ProbeHash (int depth, int * score, simpleMoveT * bestMove, bool * isOnlyMove);
+                    ScoredMove * bestmove, bool isOnlyMove);
+    scoreFlagT ProbeHash (int depth, int * score, ScoredMove * bestMove, bool * isOnlyMove);
 
     inline void ClearKillerMoves (void);
-    inline void AddKillerMove (simpleMoveT * sm);
-    inline bool IsKillerMove (simpleMoveT * sm);
+    inline void AddKillerMove (ScoredMove * sm);
+    inline bool IsKillerMove (ScoredMove * sm);
 
     inline void ClearHistoryValues (void);
     inline void HalveHistoryValues (void);
-    inline void IncHistoryValue (simpleMoveT * sm, int increment);
-    inline int GetHistoryValue (simpleMoveT * sm);
+    inline void IncHistoryValue (ScoredMove * sm, int increment);
+    inline int GetHistoryValue (ScoredMove * sm);
 
     int Score (int alpha, int beta);
     inline int ScoreWhiteMaterial (void);
@@ -205,7 +203,6 @@ public:
         SetHashTableKilobytes (ENGINE_HASH_KB);
         SetPawnTableKilobytes (ENGINE_PAWN_KB);
         CallbackFunction = NULL;
-        NumGameMoves = 0;
         RootPos.StdStart();
         Pos.StdStart();
         for (auto& e : PV) { e.length = 0; }
@@ -264,8 +261,6 @@ public:
 
     void SetPosition (Position * pos);
     Position * GetPosition (void) { return &RootPos; }
-    void PlayMove (simpleMoveT * move);
-    void RetractMove (void);
     int Score (void);
     int ScoreMaterial (void);
     principalVarT * GetPV (void) { return &(PV[0]); }
@@ -286,7 +281,7 @@ Engine::SetPVLength (void)
 //   Updates the principal variation at the current Ply to
 //   include the specified move.
 inline void
-Engine::UpdatePV (simpleMoveT * sm)
+Engine::UpdatePV (ScoredMove * sm)
 {
     if (Ply >= ENGINE_MAX_PLY - 1) { return; }
     if (InNullMove > 0) { return; }
@@ -322,12 +317,12 @@ Engine::ClearKillerMoves (void)
 }
 
 inline void
-Engine::AddKillerMove (simpleMoveT * sm)
+Engine::AddKillerMove (ScoredMove* sm)
 {
     if (sm->capturedPiece != EMPTY  &&  sm->score >= 0) { return; }
     if (sm->promote != EMPTY  &&  sm->score >= 0) { return; }
-    simpleMoveT * killer0 = &(KillerMove[Ply][0]);
-    simpleMoveT * killer1 = &(KillerMove[Ply][1]);
+    auto killer0 = &(KillerMove[Ply][0]);
+    auto killer1 = &(KillerMove[Ply][1]);
     if (killer0->from == sm->from  &&  killer0->to == sm->to
           &&  killer0->movingPiece == sm->movingPiece) {
         return;
@@ -337,14 +332,14 @@ Engine::AddKillerMove (simpleMoveT * sm)
 }
 
 inline bool
-Engine::IsKillerMove (simpleMoveT * sm)
+Engine::IsKillerMove (ScoredMove* sm)
 {
-    simpleMoveT * killer0 = &(KillerMove[Ply][0]);
+    auto killer0 = &(KillerMove[Ply][0]);
     if (killer0->from == sm->from  &&  killer0->to == sm->to
           &&  killer0->movingPiece == sm->movingPiece) {
         return true;        
     }
-    simpleMoveT * killer1 = &(KillerMove[Ply][1]);
+    auto killer1 = &(KillerMove[Ply][1]);
     if (killer1->from == sm->from  &&  killer1->to == sm->to
           &&  killer1->movingPiece == sm->movingPiece) {
         return true;        
@@ -381,7 +376,7 @@ Engine::HalveHistoryValues (void)
 }
 
 inline void
-Engine::IncHistoryValue (simpleMoveT * sm, int increment)
+Engine::IncHistoryValue (ScoredMove * sm, int increment)
 {
     if (sm->capturedPiece != EMPTY  &&  sm->score >= 0) { return; }
     if (sm->promote != EMPTY  &&  sm->score >= 0) { return; }
@@ -397,7 +392,7 @@ Engine::IncHistoryValue (simpleMoveT * s
 }
 
 inline int
-Engine::GetHistoryValue (simpleMoveT * sm)
+Engine::GetHistoryValue (ScoredMove * sm)
 {
     pieceT p = sm->movingPiece;
     squareT to = sm->to;
diff -pruN 1:4.7.0+dfsg1-2/src/error.h 1:4.7.4+dfsg1-2/src/error.h
--- 1:4.7.0+dfsg1-2/src/error.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/error.h	2022-07-09 05:14:42.000000000 +0000
@@ -79,7 +79,11 @@ const errorT
     ERROR_BufferRead = 602,
 
     // Codec errors
-    ERROR_CodecUnsupFeat = 701;
+    ERROR_CodecUnsupFeat = 701,
+    ERROR_CodecChess960 = 702;
+
+
+static_assert(OK == false);
 
 #endif   // #ifdef SCID_ERROR_H
 
diff -pruN 1:4.7.0+dfsg1-2/src/fastgame.h 1:4.7.4+dfsg1-2/src/fastgame.h
--- 1:4.7.0+dfsg1-2/src/fastgame.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/fastgame.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,672 +0,0 @@
-/*
-* Copyright (C) 2013-2015  Fulvio Benini
-
-* This file is part of Scid (Shane's Chess Information Database).
-*
-* Scid is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation.
-*
-* Scid is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Scid.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef FASTGAME_H
-#define FASTGAME_H
-
-#include "common.h"
-#include "fullmove.h"
-#include "movegen.h"
-#include "position.h"
-#include <algorithm>
-#include <cstdlib>
-#include <cstring>
-#include <sstream>
-#include <string>
-
-/// Store the number of pieces for each type and color.
-class MaterialCount {
-	int8_t n_[2][8] = {};
-
-public:
-	/// Add one piece.
-	void incr(colorT color, pieceT piece_type) {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(piece_type > 0 && piece_type < 8);
-
-		++n_[color][0];
-		++n_[color][piece_type];
-	}
-
-	/// Subtract one piece.
-	void decr(colorT color, pieceT piece_type) {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(piece_type > 0 && piece_type < 8);
-
-		--n_[color][0];
-		--n_[color][piece_type];
-	}
-
-	/// Return the total number of pieces of the specified color.
-	int8_t count(colorT color) const {
-		ASSERT(color == 0 || color == 1);
-
-		return n_[color][0];
-	}
-
-	/// Return the number of pieces of the specified color and type.
-	int8_t count(colorT color, pieceT piece_type) const {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(piece_type > 0 && piece_type < 8);
-
-		return n_[color][piece_type];
-	}
-
-	bool operator==(const MaterialCount& b) const {
-		const int8_t* a = n_[0];
-		const int8_t* b_ptr = b.n_[0];
-		return std::equal(a, a + 16, b_ptr);
-	}
-
-	bool operator!=(const MaterialCount& b) const {
-		return !operator==(b);
-	}
-};
-
-/// Store the type and position of the pieces compatibly with the SCID4 coding.
-class PieceList {
-	struct {
-		squareT sq;
-		pieceT piece_type;
-	} pieces_[2][16];
-
-public:
-	/// SCID4 encoded games must use index 0 for kings.
-	int8_t getKingIdx() const { return 0; }
-
-	/// Return the type of the piece with index @e idx
-	pieceT getPieceType(colorT color, int idx) const {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(idx >= 0 && idx < 16);
-
-		return pieces_[color][idx].piece_type;
-	}
-
-	/// Return the square position of the piece with index @e idx
-	squareT getSquare(colorT color, int idx) const {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(idx >= 0 && idx < 16);
-
-		return pieces_[color][idx].sq;
-	}
-
-	/// Change the square position of the piece with index @e idx
-	void move(colorT color, int idx, squareT to) {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(idx >= 0 && idx < 16);
-
-		pieces_[color][idx].sq = to;
-	}
-
-	/// Change the type of the piece with index @e idx
-	void promote(colorT color, int idx, pieceT piece_type) {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(idx >= 0 && idx < 16);
-
-		pieces_[color][idx].piece_type = piece_type;
-	}
-
-	/// Remove the piece with index @e removed_idx.
-	/// Piece's indexes are important for decoding SCID4 moves:  when a piece is
-	/// removed it's index is used by the last valid index @e lastvalid_idx.
-	/// Return the square of the new piece with index @e removed_idx.
-	squareT remove(colorT color, int removed_idx, int lastvalid_idx) {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(removed_idx >= 0 && removed_idx < 16);
-		ASSERT(lastvalid_idx >= 0 && lastvalid_idx < 16);
-
-		pieces_[color][removed_idx] = pieces_[color][lastvalid_idx];
-		return pieces_[color][lastvalid_idx].sq;
-	}
-
-	/// Set the type and square of the piece with index @e idx
-	void set(colorT color, int idx, squareT sq, pieceT piece_type) {
-		ASSERT(color == 0 || color == 1);
-		ASSERT(idx >= 0 && idx < 16);
-		ASSERT(piece_type != KING || idx == getKingIdx());
-
-		pieces_[color][idx].sq = sq;
-		pieces_[color][idx].piece_type = piece_type;
-	}
-};
-
-class FastBoard {
-	uint8_t board_[64];
-	MaterialCount mt_;
-	PieceList pieces_;
-
-	enum { EMPTY_SQ_ = 0xFF };
-
-public:
-	FastBoard() {}
-	FastBoard(Position& pos) { Init(pos); }
-
-	void Init() {
-		static Position StdStartPos(Position::getStdStart());
-		static FastBoard StdStart(StdStartPos);
-		*this = StdStart;
-	}
-
-	void Init(Position& pos) {
-		std::fill_n(board_, 64, EMPTY_SQ_);
-
-		for (auto color : {WHITE, BLACK}) {
-			const auto pos_count = pos.GetCount(color);
-			const auto pos_list = pos.GetList(color);
-			for (uint8_t idx = 0; idx < 16; ++idx) {
-				if (idx < pos_count) {
-					const squareT sq = pos_list[idx];
-					const pieceT piece_type = piece_Type(pos.GetPiece(sq));
-					pieces_.set(color, idx, sq, piece_type);
-					board_[sq] = idx;
-					mt_.incr(color, piece_type);
-				} else {
-					pieces_.set(color, idx, 0, INVALID_PIECE);
-				}
-			}
-		}
-	}
-
-	bool isEqual(const pieceT* board, const MaterialCount& mt_count) const {
-		if (mt_ != mt_count)
-			return false;
-
-		for (int idx = 0, n = mt_.count(WHITE); idx < n; ++idx) {
-			const auto sq = pieces_.getSquare(WHITE, idx);
-			const auto pt = pieces_.getPieceType(WHITE, idx);
-			if (board[sq] != piece_Make(WHITE, pt))
-				return false;
-		}
-		for (int idx = 0, n = mt_.count(BLACK); idx < n; ++idx) {
-			const auto sq = pieces_.getSquare(BLACK, idx);
-			const auto pt = pieces_.getPieceType(BLACK, idx);
-			if (board[sq] != piece_Make(BLACK, pt))
-				return false;
-		}
-		return true;
-	}
-
-	const MaterialCount& materialCount() const {
-		return mt_;
-	}
-
-	squareT getSquare(colorT color, int idx) const {
-		return pieces_.getSquare(color, idx);
-	}
-
-	pieceT getPiece(colorT color, int idx) const {
-		return pieces_.getPieceType(color, idx);
-	}
-
-	// TODO: error detection
-	template <colorT color> squareT castle(bool king_side) {
-		const squareT black = (color == WHITE) ? 0 : 56;
-		squareT king_to, rook_from, rook_to;
-		if (king_side) { // King Side
-			king_to = black + G1;
-			rook_from = black + H1;
-			rook_to = black + F1;
-		} else { // Queen Side
-			king_to = black + C1;
-			rook_from = black + A1;
-			rook_to = black + D1;
-		}
-		const uint8_t rook_idx = board_[rook_from];
-		const auto king_idx = pieces_.getKingIdx();
-		const squareT king_from = pieces_.getSquare(color, king_idx);
-		pieces_.move(color, rook_idx, rook_to);
-		pieces_.move(color, king_idx, king_to);
-		board_[rook_to] = rook_idx;
-		board_[king_to] = king_idx;
-		board_[rook_from] = EMPTY_SQ_;
-		board_[king_from] = EMPTY_SQ_;
-		return rook_from;
-	}
-
-	template <colorT color>
-	pieceT move(uint8_t idx, squareT to, pieceT promo) {
-		if (promo != INVALID_PIECE) {
-			pieces_.promote(color, idx, promo);
-			mt_.incr(color, promo);
-			mt_.decr(color, PAWN);
-		}
-		const auto from = pieces_.getSquare(color, idx);
-		board_[from] = EMPTY_SQ_;
-		pieces_.move(color, idx, to);
-		return remove<1 - color>(to, idx);
-	}
-
-	template <colorT color>
-	pieceT remove(squareT sq, uint8_t newIdx = EMPTY_SQ_) {
-		const uint8_t oldIdx = board_[sq];
-		board_[sq] = newIdx;
-		if (oldIdx == EMPTY_SQ_)
-			return INVALID_PIECE;
-
-		pieceT removed_pt =  pieces_.getPieceType(color, oldIdx);
-		mt_.decr(color, removed_pt);
-		int lastvalid_idx = mt_.count(color);
-		if (oldIdx != lastvalid_idx) {
-			squareT moved_sq = pieces_.remove(color, oldIdx, lastvalid_idx);
-			board_[moved_sq] = oldIdx;
-		}
-		return removed_pt;
-	}
-
-	/**
-	 * Given the actual board position, find if the last move needs to be made
-	 * unambiguous and if it gives check (or TODO mate), and then sets the
-	 * appropriate bits in @e lastmove.
-	 * @param lastmove: the last move played.
-	 */
-	void fillSANInfo(FullMove& lastmove) {
-		squareT lastFrom = lastmove.getFrom();
-		squareT lastTo = lastmove.getTo();
-		colorT lastCol = lastmove.getColor();
-		pieceT lastPt = lastmove.getPiece();
-
-		if (lastPt == PAWN) {
-			if (lastmove.isPromo())
-				lastPt = lastmove.getPromo();
-		} else if (mt_.count(lastCol, lastPt) > 1) {
-			int ambiguity = ambiguousMove(lastFrom, lastTo, lastCol, lastPt);
-			if (ambiguity)
-				lastmove.setAmbiguity(ambiguity != 5, ambiguity >= 5);
-		}
-
-		// Look for checks
-		ASSERT(mt_.count(WHITE) >= 1 && mt_.count(BLACK) >= 1);
-
-		const squareT enemyKingSq = getKingSquare(color_Flip(lastCol));
-		bool direct_check = lastPt != KING && movegen::attack<uint8_t>(
-		                                          lastTo, enemyKingSq, lastCol,
-		                                          lastPt, board_, EMPTY_SQ_);
-		if (direct_check || // Look for a discovered check
-		    find_attacker_slider(enemyKingSq, lastCol) >= 0) {
-			lastmove.setCheck();
-
-			// TODO: Find if it's mate:
-			// - it's not mate if the king can move to a safe square
-			// - it's mate if it's double check or the attacker cannot be
-			//   captured or blocked.
-		}
-	}
-
-private:
-	squareT getKingSquare(colorT color) {
-		return pieces_.getSquare(color, pieces_.getKingIdx());
-	}
-
-	int ambiguousMove(squareT lastFrom, squareT lastTo, colorT lastCol,
-	                  pieceT lastPt) {
-		int ambiguity = 0;
-
-		const squareT kingSq = getKingSquare(lastCol);
-		const colorT enemyCol = color_Flip(lastCol);
-		for (int i = 1, n = mt_.count(lastCol); i < n; i++) {
-			if (getPiece(lastCol, i) != lastPt)
-				continue; // Skip: different type
-
-			const squareT sq = getSquare(lastCol, i);
-			if (sq == lastTo)
-				continue; // Skip: this is the analyzed piece
-
-			board_[lastFrom] = board_[sq];
-			board_[sq] = EMPTY_SQ_;
-
-			bool pseudoLegal = movegen::pseudo<uint8_t>(
-			    sq, lastTo, lastCol, lastPt, board_, EMPTY_SQ_);
-
-			std::pair<pieceT, squareT> pin;
-			if (pseudoLegal)
-				pin = movegen::opens_ray<uint8_t>(sq, lastTo, kingSq, board_,
-				                                  EMPTY_SQ_);
-			board_[sq] = board_[lastFrom];
-			board_[lastFrom] = EMPTY_SQ_;
-
-			if (!pseudoLegal)
-				continue; // Skip: illegal move
-
-			if (pin.first != INVALID_PIECE) {
-				uint8_t idx = board_[pin.second];
-				if (idx != EMPTY_SQ_ && idx < mt_.count(enemyCol) &&
-				    getSquare(enemyCol, idx) == pin.second) {
-					const pieceT pt = getPiece(enemyCol, idx);
-					if (pt == QUEEN || pt == pin.first)
-						continue; // Skip: pinned piece
-				}
-			}
-
-			// Ambiguity:
-			// 1 (0001) --> need from-file (preferred) or from-rank
-			// 3 (0011) --> need from-file
-			// 5 (0101) --> need from-rank
-			// 7 (0111) --> need both from-file and from-rank
-			ambiguity |= 1;
-			if (square_Rank(lastFrom) == square_Rank(sq)) {
-				ambiguity |= 2; // 0b0010
-			} else if (square_Fyle(lastFrom) == square_Fyle(sq)) {
-				ambiguity |= 4; // 0b0100
-			}
-		}
-
-		return ambiguity;
-	}
-
-	int find_attacker_slider(squareT destSq, colorT color) {
-		for (int idx = 0, n = mt_.count(color); idx < n; ++idx) {
-			const pieceT pt = getPiece(color, idx);
-			if (pt != QUEEN && pt != ROOK && pt != BISHOP)
-				continue;
-
-			const squareT sq = getSquare(color, idx);
-			if (movegen::attack_slider<uint8_t>(sq, destSq, pt, board_,
-			                                    EMPTY_SQ_)) {
-				return idx;
-			}
-		}
-		return -1;
-	}
-};
-
-class FastGame {
-	FastBoard board_;
-	const byte* v_it_;
-	const byte* v_end_;
-	colorT cToMove_;
-
-public:
-	static FastGame Create(const byte* v_begin, const byte* v_end) {
-		const byte* v_it = v_begin;
-		while (v_it < v_end) {
-			byte b = *v_it++;
-			if (b == 0) {
-				if (v_it >= v_end) break; // Error
-				byte haveFEN = *v_it++ & 1;
-				if (haveFEN == 0) {
-					return FastGame(v_it, v_end);
-				} else {
-					const char* FENstring = (char*) v_it;
-					while (v_it < v_end) {
-						if (*v_it++ == 0) return FastGame(FENstring, v_it, v_end);
-					}
-					break; // FEN error
-				}
-			} else if (b == 255) { // Skip special 3-byte binary encoding of EventDate
-				v_it += 3;
-			} else { // Skip tags
-				enum { MAX_TAG_LEN = 240 };
-				if (b <= MAX_TAG_LEN) v_it += b;
-				if (v_it < v_end) v_it += *v_it +1;
-			}
-		}
-
-		return FastGame(0,0); // Error default to StdStart and empty buffer
-	}
-
-	FullMove getMove(int ply_to_skip) {
-		for (int ply=0; ply <= ply_to_skip; ply++, cToMove_ = 1 - cToMove_) {
-			auto move = (cToMove_ == WHITE)
-			                ? DecodeNextMove<FullMove, WHITE>()
-			                : DecodeNextMove<FullMove, BLACK>();
-			if (!move)
-				break;
-
-			if (ply == ply_to_skip) {
-				board_.fillSANInfo(move);
-				cToMove_ = 1 - cToMove_;
-				return move;
-			}
-		}
-		return {};
-	}
-
-	std::string getMoveSAN(int ply_to_skip, int count) {
-		std::stringstream res;
-		for (int ply=0; ply < ply_to_skip + count; ply++, cToMove_ = 1 - cToMove_) {
-			FullMove move;
-			if (cToMove_ == WHITE) {
-				move = DecodeNextMove <FullMove, WHITE>();
-				if (!move)
-					break;
-				if (ply < ply_to_skip) continue;
-				if (ply > ply_to_skip) res << "  ";
-				res << (1 + ply/2) << ".";
-			} else {
-				move = DecodeNextMove <FullMove, BLACK>();
-				if (!move)
-					break;
-				if (ply < ply_to_skip) continue;
-				if (ply == ply_to_skip) res << (1 + ply/2) << "...";
-				else res << " ";
-			}
-			board_.fillSANInfo(move);
-			res << move.getSAN();
-		}
-		return res.str();
-	}
-
-	template <colorT toMove>
-	int search(const byte* board, const MaterialCount& mt_count) {
-		int ply = 1;
-		auto less_material = [](const MaterialCount& a, const MaterialCount& b,
-		                        const colorT color, const auto move) {
-			if (!move)
-				return true;
-
-			const auto captured_pt = move.getCaptured();
-			if (captured_pt == INVALID_PIECE)
-				return false;
-
-			if (a.count(color) < b.count(color))
-				return true;
-
-			return a.count(color, PAWN) + a.count(color, captured_pt) <
-			       b.count(color, PAWN) + b.count(color, captured_pt);
-		};
-
-		if (cToMove_ != toMove) {
-			const auto move = DecodeNextMove<FullMove, 1 - toMove>();
-			if (!move)
-				return 0;
-			ply += 1;
-		}
-		for (;;) {
-			if (board_.isEqual(board, mt_count))
-				return ply;
-
-			{
-				const auto move = DecodeNextMove<FullMove, toMove>();
-				if (less_material(board_.materialCount(), mt_count, 1 - toMove,
-				                  move))
-					return 0;
-			}
-			{
-				const auto move = DecodeNextMove<FullMove, 1 - toMove>();
-				if (less_material(board_.materialCount(), mt_count, toMove,
-				                  move))
-					return 0;
-			}
-
-			ply += 2;
-		}
-		return 0;
-	}
-
-private:
-	FastGame(const byte* v_it, const byte* v_end)
-	: v_it_ (v_it), v_end_(v_end), cToMove_(WHITE) {
-		board_.Init();
-	}
-
-	FastGame(const char* FEN, const byte* v_it, const byte* v_end)
-	: v_it_ (v_it), v_end_(v_end) {
-		Position StartPos;
-		if (FEN == 0 || StartPos.ReadFromFEN(FEN) != OK) StartPos.StdStart();
-		board_.Init(StartPos);
-		cToMove_ = StartPos.GetToMove();
-	}
-
-	template <typename TResult, colorT toMove>
-	TResult DecodeNextMove() {
-		enum { ENCODE_NAG = 11, ENCODE_COMMENT, ENCODE_START_MARKER, ENCODE_END_MARKER, ENCODE_END_GAME };
-		enum { ENCODE_FIRST = 11, ENCODE_LAST = 15 };
-
-		while (v_it_ < v_end_) {
-			byte b = *v_it_++;
-			if (b < ENCODE_FIRST || b > ENCODE_LAST) return doPly<TResult, toMove>(b);
-			if (b == ENCODE_END_GAME || b == ENCODE_END_MARKER) return {};
-			if (b == ENCODE_NAG) {v_it_++; continue; }
-			if (b == ENCODE_START_MARKER) {
-				int nestCount = 1;
-				do {
-					if (v_it_ >= v_end_) return {};
-					switch (*v_it_++) {
-					case ENCODE_NAG: v_it_++; break;
-					case ENCODE_START_MARKER: nestCount++; break;
-					case ENCODE_END_MARKER: nestCount--; break;
-					case ENCODE_END_GAME: return {};
-					}
-				} while (nestCount > 0);
-			}
-		}
-		return {};
-	}
-
-	template <typename TResult, colorT toMove> TResult doPly(byte v) {
-		byte idx_piece_moving = v >> 4;
-		byte move = v & 0x0F;
-		pieceT moving_piece = board_.getPiece(toMove, idx_piece_moving);
-		squareT from = board_.getSquare(toMove, idx_piece_moving);
-		int to;
-		pieceT promo = INVALID_PIECE;
-		bool enPassant = false;
-		switch (moving_piece) {
-		case PAWN:
-			to = decodePawn<toMove>(from, move, promo, enPassant);
-			break;
-		case BISHOP:
-			to = decodeBishop(from, move);
-			break;
-		case KNIGHT:
-			to = decodeKnight(from, move);
-			break;
-		case QUEEN:
-			if (move == square_Fyle(from)) { // 2 BYTES MOVE
-				if (v_it_ >= v_end_)
-					return {}; // decode error
-
-				to = decodeQueen2byte(*v_it_++);
-				break;
-			}
-			/* FALLTHRU */
-		case ROOK:
-			to = decodeRook(from, move);
-			break;
-		case KING:
-			if (move == 0) { // NULL MOVE
-				return TResult(toMove, 0, 0, KING);
-			}
-			if (move <= 8) {
-				to = decodeKing(from, move);
-				break;
-			}
-			if (move <= 10) { // CASTLE
-				const squareT rook_from = board_.castle<toMove>(move == 10);
-				return TResult(toMove, from, rook_from);
-			}
-			return {}; // decode error
-
-		default:
-			return {}; // decode error
-		}
-
-		if (to < 0 || to > 63)
-			return {}; // decode error
-
-		pieceT captured = board_.move<toMove>(idx_piece_moving, to, promo);
-		TResult res(toMove, from, to, moving_piece);
-		if (promo != INVALID_PIECE)
-			res.setPromo(promo);
-		if (captured != INVALID_PIECE) {
-			res.setCapture(captured, false);
-		} else if (enPassant) {
-			squareT sq = (toMove == WHITE) ? to - 8 : to + 8;
-			captured = board_.remove<1 - toMove>(0x3F & sq);
-			res.setCapture(captured, true);
-		}
-		return res;
-	}
-
-	/**
-	 * decode*() - decode a move from Scid format
-	 * @from: start square of the moving piece
-	 * @val: index of the target square
-	 *
-	 * Excluding queens, the other chess pieces cannot reach more than 16 target
-	 * squares from any given position. This allow to store the target square of
-	 * a move into 4 bits, as an index of all the possible target squares.
-	 * Return:
-	 * - the target square
-	 * Error handling:
-	 * - Debug code will check if the decoded value is a valid [0-63] square.
-	 * - Release code will force the returned valid to be a valid [0-63] square
-	 *   but, for performance reasons, do not report invalid encoded moves.
-	 */
-	static inline int decodeKing(squareT from, byte val) {
-		ASSERT(val <= 8);
-		static const int8_t sqdiff[] = {0, -9, -8, -7, -1, 1, 7, 8, 9};
-		return from + sqdiff[val];
-	}
-	static inline int decodeQueen2byte(byte val) {
-		return val - 64;
-	}
-	static inline int decodeBishop(squareT from, byte val) {
-		int fylediff = square_Fyle(val) - square_Fyle(from);
-		return (val >= 8) ? from - 7 * fylediff //
-		                  : from + 9 * fylediff;
-	}
-	static inline int decodeKnight(squareT from, byte val) {
-		ASSERT(val <= 16);
-		static const int8_t sqdiff[] = {0, -17, -15, -10, -6, 6, 10, 15, 17, 0, 0, 0, 0, 0, 0, 0};
-		return from + sqdiff[val];
-	}
-	static inline int decodeRook(squareT from, byte val) {
-		ASSERT(val <= 16);
-		if (val >= 8) // vertical move
-			return square_Make(square_Fyle(from), (val - 8));
-		else // horizontal move
-			return square_Make(val, square_Rank(from));
-	}
-	template <colorT color>
-	static inline int decodePawn(squareT from, byte val, pieceT& promo,
-	                             bool& enPassant) {
-		ASSERT(val <= 16);
-		static const int8_t sqdiff [] = { 7,8,9, 7,8,9, 7,8,9, 7,8,9, 7,8,9, 16 };
-		static const pieceT promoPieceFromVal [] = {
-			0,0,0,QUEEN,QUEEN,QUEEN, ROOK,ROOK,ROOK, BISHOP,BISHOP,BISHOP,KNIGHT,KNIGHT,KNIGHT,0
-		};
-		promo = promoPieceFromVal[val];
-		enPassant = (val == 0 || val == 2);
-		return (color == WHITE) ? from + sqdiff[val] //
-		                        : from - sqdiff[val];
-	}
-};
-
-
-#endif
diff -pruN 1:4.7.0+dfsg1-2/src/filebuf.h 1:4.7.4+dfsg1-2/src/filebuf.h
--- 1:4.7.0+dfsg1-2/src/filebuf.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/filebuf.h	2022-07-09 05:14:42.000000000 +0000
@@ -169,11 +169,11 @@ private:
 	template <int nBytes> uint32_t read() {
 		uint32_t res = 0;
 		if (nBytes > 3)
-			res += ReadOneByte() << 24;
+			res += static_cast<uint32_t>(ReadOneByte()) << 24;
 		if (nBytes > 2)
-			res += ReadOneByte() << 16;
+			res += static_cast<uint32_t>(ReadOneByte()) << 16;
 		if (nBytes > 1)
-			res += ReadOneByte() << 8;
+			res += static_cast<uint32_t>(ReadOneByte()) << 8;
 		return res + ReadOneByte();
 	}
 };
diff -pruN 1:4.7.0+dfsg1-2/src/filter.cpp 1:4.7.4+dfsg1-2/src/filter.cpp
--- 1:4.7.0+dfsg1-2/src/filter.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/filter.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -12,8 +12,7 @@
 //
 //////////////////////////////////////////////////////////////////////
 
-#include "hfilter.h"
-#include "filter.h"
+#include "tree.h"
 #include <cstring>
 
 
@@ -76,10 +75,9 @@ CompressedFilter::Verify (Filter * filte
 void
 CompressedFilter::CompressFrom (Filter * filter)
 {
-    Clear();
+    delete[] CompressedData;
 
     CFilterSize = filter->Size();
-    CFilterCount = filter->Count();
     if(filter->data() == NULL) {
         CompressedLength = 0;
         CompressedData = NULL;
diff -pruN 1:4.7.0+dfsg1-2/src/filter.h 1:4.7.4+dfsg1-2/src/filter.h
--- 1:4.7.0+dfsg1-2/src/filter.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/filter.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,82 +0,0 @@
-//////////////////////////////////////////////////////////////////////
-//
-//  FILE:       filter.h
-//              Filter and CompressedFilter Classes
-//
-//  Part of:    Scid (Shane's Chess Information Database)
-//  Version:    1.4
-//
-//  Notice:     Copyright (c) 2000  Shane Hudson.  All rights reserved.
-//
-//  Author:     Shane Hudson (sgh@users.sourceforge.net)
-//
-//////////////////////////////////////////////////////////////////////
-
-
-#ifndef SCID_FILTER_H
-#define SCID_FILTER_H
-
-#include "common.h"
-
-
-//////////////////////////////////////////////////////////////////////
-//
-// CompressedFilter class:
-//    Holds the same data as a filter, in compressed format.
-//    Random access to individual values is not possible.
-//    A CompressedFilter is created from, or restored to, a regular
-//    filter with the methods CompressFrom() and UncompressTo().
-class CompressedFilter
-{
-  private:
-
-    uint    CFilterSize;
-    uint    CFilterCount;
-    uint    CompressedLength;
-    byte *  CompressedData;
-
-  public:
-    CompressedFilter (void)     { Init(); }
-    ~CompressedFilter (void) {
-        if (CompressedData != NULL) delete[] CompressedData;
-    }
-
-    inline void Clear();
-
-    uint Size() const { return CFilterSize; }
-    uint Count() { return CFilterCount; }
-
-    errorT Verify (Filter * filter);
-
-    void CompressFrom (Filter * filter);
-    errorT UncompressTo(Filter * filter) const;
-
-  private:
-    CompressedFilter(const CompressedFilter&);
-    void operator=(const CompressedFilter&);
-
-    inline void Init();
-};
-
-inline void
-CompressedFilter::Init ()
-{
-    CFilterSize = 0;
-    CFilterCount = 0;
-    CompressedLength = 0;
-    CompressedData = NULL;
-}
-
-inline void
-CompressedFilter::Clear ()
-{
-    if (CompressedData != NULL) { delete[] CompressedData; }
-    Init();
-}
-
-
-#endif  // #ifndef SCID_FILTER_H
-
-//////////////////////////////////////////////////////////////////////
-//  EOF: filter.h
-//////////////////////////////////////////////////////////////////////
diff -pruN 1:4.7.0+dfsg1-2/src/fullmove.h 1:4.7.4+dfsg1-2/src/fullmove.h
--- 1:4.7.0+dfsg1-2/src/fullmove.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/fullmove.h	2022-07-09 05:14:42.000000000 +0000
@@ -58,22 +58,32 @@ public:
 		m_ = to | (from << 6) | (pt << 24) | (c << 27);
 	}
 
-	operator bool() const { return m_ != 0; }
-	bool operator!=(const FullMove& f) const { return m_ != f.m_; }
+	bool operator==(FullMove const& f) const { return m_ == f.m_; }
+
+	// Special moves:
+	// NONE: encoded as 0 (from 0 to 0);
+	// NULL: encoded as 65 (from 1 to 1);
+	// PROMO: encoded setting the special move flag to 1
+	// ENPASSANT: encoded setting the special move flag to 2
+	// CASTLING: encoded setting the special move flag to 3, from is the square
+	//           of the king and to is the square of the rook.
+	//           If from < to it is castling king side.
+	explicit operator bool() const { return m_ != 0; }
+	bool    isNull()      const { return m_ == 0b01000001; }
 	bool    isPromo()     const { return (m_ & (3 << 14)) == (1 << 14); }
 	bool    isEnpassant() const { return (m_ & (3 << 14)) == (2 << 14); }
 	bool    isCastle()    const { return (m_ & (3 << 14)) == (3 << 14); }
+
 	squareT getTo()       const { return m_ & 0x3F; }
 	squareT getFrom()     const { return (m_ >> 6) & 0x3F; }
 	pieceT  getPiece()    const { return (m_ >> 24) & 0x07; }
 	colorT  getColor()    const { return (m_ >> 27 & 1) ? BLACK : WHITE; }
 	pieceT  getPromo()    const { return ((m_ >> 12) & 0x03) +2; }
 	pieceT  getCaptured() const { return (m_ >> 21) & 0x07; }
-	std::string getSAN(colorT* toMove = 0) const {
+	std::string getSAN() const {
 		std::string res;
-		if (toMove) *toMove = getColor();
-		squareT to = getTo();
-		squareT from = getFrom();
+		const auto to = getTo();
+		const auto from = getFrom();
 		if (to == 0 && from == 0) return "--";
 		if (isCastle()) {
 			res = (to > from) ? "O-O" : "O-O-O";
diff -pruN 1:4.7.0+dfsg1-2/src/game.cpp 1:4.7.4+dfsg1-2/src/game.cpp
--- 1:4.7.0+dfsg1-2/src/game.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/game.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -68,24 +68,12 @@ char transPiecesChar(char c) {
   return ret;
 }
 
-const char * ratingTypeNames [17] = {
+const char * ratingTypeNames [8] = {
     "Elo", "Rating", "Rapid", "ICCF", "USCF", "DWZ", "ECF",
-    // Reserved for future use:
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     // End of array marker:
     NULL
 };
 
-uint
-strGetRatingType (const char * name) {
-    uint i = 0;
-    while (ratingTypeNames[i] != NULL) {
-        if (strEqual (name, ratingTypeNames[i])) { return i; }
-        i++;
-    }
-    return 0;
-}
-
 typedef Game * GamePtr;
 
 
@@ -471,10 +459,6 @@ Game::Game(const Game& obj) {
 		StartPos = std::make_unique<Position>(*obj.StartPos);
 
 	NumHalfMoves = obj.NumHalfMoves;
-	PromotionsFlag = obj.PromotionsFlag;
-	KeepDecodedMoves = obj.KeepDecodedMoves;
-	WhiteEstimateElo = obj.WhiteEstimateElo;
-	BlackEstimateElo = obj.BlackEstimateElo;
 	NumMovesPrinted = obj.NumMovesPrinted;
 	PgnStyle = obj.PgnStyle;
 	PgnFormat = obj.PgnFormat;
@@ -549,7 +533,6 @@ void Game::Clear() {
 	EventDate = ZERO_DATE;
 	EcoCode = 0;
 	WhiteElo = BlackElo = 0;
-	WhiteEstimateElo = BlackEstimateElo = 0;
 	WhiteRatingType = BlackRatingType = RATING_Elo;
 	Result = RESULT_None;
 	ScidFlags[0] = 0;
@@ -560,8 +543,6 @@ void Game::Clear() {
 	HtmlStyle = 0;
 
 	ClearMoves();
-	KeepDecodedMoves = true;
-	PromotionsFlag = false;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -599,20 +580,6 @@ Game::SetPgnFormatFromString (const char
     return PgnFormatFromString (str, &PgnFormat);
 }
 
-errorT
-Game::SetStartFen (const char * fenStr)
-{
-    auto pos = std::make_unique<Position>();
-    errorT err = pos->ReadFromFEN (fenStr);
-    if (err != OK)
-        return err;
-
-    ClearMoves();
-    StartPos = std::move(pos);
-    *CurrentPos = *StartPos;
-    return OK;
-}
-
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Game::AddPgnTag(): Add a PGN Tag.
 //
@@ -639,21 +606,6 @@ const char* Game::FindExtraTag(const cha
     return NULL;
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Game::RemoveExtraTag():
-//   Remove an extra PGN tag if it exists.
-bool Game::RemoveExtraTag(const char* tag) {
-    auto it = std::remove_if(extraTags_.begin(), extraTags_.end(),
-                             [&](const std::pair<std::string, std::string>& e) {
-                                 return e.first == tag;
-                             });
-    if (it != extraTags_.end()) {
-        extraTags_.erase(it, extraTags_.end());
-        return true;
-    }
-    return false;
-}
-
 std::string& Game::accessTagValue(const char* tag, size_t tagLen) {
 	if (tagLen == 5) {
 		if (std::equal(tag, tag + 5, "Event"))
@@ -743,7 +695,7 @@ errorT Game::MoveForward(void) {
 	if (CurrentMove->endMarker())
 		return ERROR_EndOfMoveList;
 
-	CurrentPos->DoSimpleMove(&CurrentMove->moveData);
+	CurrentPos->DoSimpleMove(CurrentMove->moveData);
 	CurrentMove = CurrentMove->next;
 
 	// Invariants
@@ -826,6 +778,12 @@ void Game::MoveToStart() {
 	ASSERT(!CurrentMove->startMarker());
 }
 
+void Game::MoveToEnd() {
+	MoveToStart();
+	while (MoveForward() == OK) {
+	}
+}
+
 errorT Game::MoveForwardInPGN() {
 	if (CurrentMove->prev->varChild && MoveBackup() == OK)
 		return MoveIntoVariation(0);
@@ -845,7 +803,7 @@ errorT Game::MoveForwardInPGN() {
 }
 
 errorT Game::MoveToLocationInPGN(unsigned stopLocation) {
-	MoveToPly(0);
+	MoveToStart();
 	for (unsigned loc = 1; loc < stopLocation; ++loc) {
 		errorT err = MoveForwardInPGN();
 		if (err != OK)
@@ -878,6 +836,40 @@ unsigned Game::GetPgnOffset() const {
 	return res;
 }
 
+std::string Game::currentPosUCI() const {
+	std::string res = "position startpos moves";
+	char FEN[256] = {};
+
+	std::vector<const moveT*> moves;
+	const moveT* move = CurrentMove;
+	while ((move = move->getPrevMove())) {
+		if (move->moveData.isNullMove()) {
+			Position lastValidPos = *currentPos();
+			for (const moveT* m : moves) {
+				lastValidPos.UndoSimpleMove(&m->moveData);
+			}
+			lastValidPos.PrintFEN(FEN, FEN_ALL_FIELDS);
+			break;
+		}
+		moves.emplace_back(move);
+	}
+
+	if (*FEN || HasNonStandardStart(FEN)) {
+		res.replace(9, 4, "fen ");
+		res.replace(13, 4, FEN);
+	}
+
+	const auto allocSpeedup = res.size();
+	res.resize(allocSpeedup + moves.size() * 6);
+	auto it = res.data() + allocSpeedup;
+	for (auto m = moves.crbegin(), end = moves.crend(); m != end; ++m) {
+		*it++ = ' ';
+		it = (*m)->moveData.toLongNotation(it);
+	}
+	res.resize(std::distance(res.data(), it)); // shrink
+	return res;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // The following functions modify the moves graph in order to add or delete
 // moves. Promoting variations also modifies the moves graph.
@@ -886,19 +878,14 @@ unsigned Game::GetPgnOffset() const {
 // Game::AddMove():
 //      Add a move at current position and do it.
 //
-errorT Game::AddMove(const simpleMoveT* sm) {
-	ASSERT(sm != NULL);
-
+errorT Game::AddMove(simpleMoveT const& sm) {
 	// We must be at the end of a game/variation to add a move:
 	if (!CurrentMove->endMarker())
 		Truncate();
 
 	CurrentMove->setNext(NewMove(END_MARKER));
 	CurrentMove->marker = NO_MARKER;
-	CurrentMove->moveData.from = sm->from;
-	CurrentMove->moveData.to = sm->to;
-	CurrentMove->moveData.promote = sm->promote;
-	CurrentMove->moveData.movingPiece = sm->movingPiece;
+	CurrentMove->moveData = sm;
 	if (VarDepth == 0)
 		++NumHalfMoves;
 
@@ -962,16 +949,18 @@ errorT Game::MainVariation() {
 	root->swapLine(*parent.second->next);
 
 	ASSERT(VarDepth);
-	--VarDepth;
-
-	// Now, the information about the material at the end of the
-	// game, pawn promotions, will be wrong if the variation was
-	// promoted to an actual game move, so call MakeHomePawnList()
-	// to go through the game moves and ensure it is correct.
-	auto location = currentLocation();
-	byte tempPawnList[9];
-	MakeHomePawnList(tempPawnList);
-	restoreLocation(location);
+	if (--VarDepth == 0) { // Recalculate NumHalfMoves
+		const auto count_moves = [](auto move) {
+			int res = 0;
+			while (!move->endMarker()) {
+				++res;
+				move = move->next;
+			}
+			return res;
+		};
+		ASSERT(FirstMove->startMarker() && FirstMove->next);
+		NumHalfMoves = count_moves(FirstMove->next);
+	}
 
 	return OK;
 }
@@ -1033,70 +1022,15 @@ void Game::TruncateStart() {
     NumHalfMoves -= GetCurrentPly();
     StartPos = std::move(pos);
     *CurrentPos = *StartPos;
-	FirstMove->setNext(CurrentMove);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Game::MakeHomePawnList():
-//    Is passed an array of 9 bytes and fills it with the game's
-//    home pawn delta information.
-//    This function also ensures that other information about the
-//    game that will be stored in the index file and used to speed
-//    up searches (material at end of game, etc) is up to date.
-
-bool Game::MakeHomePawnList(byte* pbPawnList) {
-    ASSERT(pbPawnList != nullptr);
-    // We zero out the list first:
-    std::fill_n(pbPawnList, 9, 0);
-
-    uint count = 0;
-    uint halfByte = 0;
-    errorT err = OK;
-    uint hpOld = HPSIG_StdStart;    // All 16 pawns are on their home squares.
-    byte* pbList = pbPawnList +1;
-
-    NumHalfMoves = 0;
-    PromotionsFlag = false;
-    bool UnderPromosFlag = false;
-    MoveToPly(0);
-
-    while (err == OK) {
-        uint hpNew = CurrentPos->GetHPSig();
-        uint changed = hpOld - hpNew;
-        if (changed != 0 && !HasNonStandardStart()) {
-            // Find the idx of the moved pawn
-            uint changeValue = 0;
-            while (1) {
-                changed = changed >> 1;
-                if (changed == 0) break;
-                changeValue++;
-            }
+    FirstMove->setNext(CurrentMove);
 
-            // There are only 16 pawns, so we can store two pawn moves
-            // in every byte
-            if (halfByte == 0) {
-                *pbList = (changeValue << 4);  halfByte = 1;
-            } else {
-                *pbList |= (changeValue & 15);  pbList++;  halfByte = 0;
-            }
-            hpOld = hpNew;
-            count++;
+    // Do all the moves to update moveData.pieceNum to the new StartPos
+    do {
+        if (!CurrentMove->startMarker() && !CurrentMove->endMarker()) {
+            CurrentPos->fillMove(CurrentMove->moveData);
         }
-        if (CurrentMove->marker != END_MARKER) {
-            if (CurrentMove->moveData.promote != EMPTY) {
-                PromotionsFlag = true;
-                if (piece_Type(CurrentMove->moveData.promote) != QUEEN) {
-                    UnderPromosFlag = true;
-                }
-            }
-        }
-        err = MoveForward();
-        if (err == OK) { NumHalfMoves++; }
-    }
-    // First byte in pawnlist array stores the count:
-    pbPawnList[0] = (byte) count;
-
-    return UnderPromosFlag;
+    } while (MoveForwardInPGN() == OK);
+    MoveToStart();
 }
 
 namespace {
@@ -1125,9 +1059,9 @@ int calcHomePawnMask (pieceT pawn, const
 //      Used by Game::MaterialMatch() to test patterns.
 //      Returns 1 if all the patterns in the list match, 0 otherwise.
 //
-int patternsMatch(const Position* pos, patternT* ptn) {
+int patternsMatch(const Position* pos, patternT* ptn, size_t ptn_size) {
     const pieceT* board = pos->GetBoard();
-    while (ptn != NULL) {
+    for (auto ptn_end = ptn + ptn_size; ptn != ptn_end; ++ptn) {
         if (ptn->rankMatch == NO_RANK) {
 
             if (ptn->fyleMatch == NO_FYLE) { // Nothing to test!
@@ -1156,9 +1090,6 @@ int patternsMatch(const Position* pos, p
                 if (found != ptn->flag) { return 0; }
             }
         }
-
-        // If we get this far, this pattern matched. Try the next one:
-        ptn = ptn->next;
     }
 
     // If we reach here, all patterns matched:
@@ -1172,29 +1103,17 @@ int patternsMatch(const Position* pos, p
 //      counts, to specify the maximum and minimum number of counts
 //      of each type of piece.
 //
-bool
-Game::MaterialMatch (ByteBuffer * buf, byte * min, byte * max,
-                     patternT * patterns, int minPly, int maxPly,
-                     int matchLength, bool oppBishops, bool sameBishops,
-                     int minDiff, int maxDiff)
-{
-    // If buf is NULL, the game is in memory. Otherwise, Decode only
-    // the necessary moves:
-    errorT err = OK;
-
-    if (buf == NULL) {
-        MoveToPly(0);
-    } else {
-        Clear();
-        err = DecodeStart (buf);
-        KeepDecodedMoves = false;
-    }
-
+bool Game::MaterialMatch(bool PromotionsFlag, ByteBuffer& buf, byte* min,
+                         byte* max, patternT* patterns, size_t ptn_size,
+                         int minPly, int maxPly, int matchLength,
+                         bool oppBishops, bool sameBishops, int minDiff,
+                         int maxDiff) {
     ASSERT (matchLength >= 1);
 
     int matchesNeeded = matchLength;
     int matDiff;
     uint plyCount = 0;
+    errorT err = DecodeSkipTags(&buf);
     while (err == OK) {
         bool foundMatch = false;
         byte wMinor, bMinor;
@@ -1276,7 +1195,7 @@ Game::MaterialMatch (ByteBuffer * buf, b
         if (matDiff < minDiff  ||  matDiff > maxDiff) { goto Next_Move; }
 
         // At this point, the Material matches; do the patterns match?
-        if (patterns == NULL || patternsMatch(currentPos(), patterns)) {
+        if (ptn_size == 0 || patternsMatch(currentPos(), patterns, ptn_size)) {
             foundMatch = true;
             matchesNeeded--;
             if (matchesNeeded <= 0) { return true; }
@@ -1289,13 +1208,12 @@ Game::MaterialMatch (ByteBuffer * buf, b
         if (! PromotionsFlag) { return false; }
 
       Next_Move:
-        if (buf == NULL) {
-            MoveForward();
-            if (CurrentMove->marker == END_MARKER) {
-                err = ERROR_EndOfMoveList;
+        {
+            simpleMoveT sm;
+            err = DecodeNextMove(&buf, sm);
+            if (err == OK) {
+                CurrentPos->DoSimpleMove(sm);
             }
-        } else {
-            err = DecodeNextMove (buf, NULL);
         }
         plyCount++;
         if (! foundMatch) { matchesNeeded = matchLength; }
@@ -1314,26 +1232,21 @@ Game::MaterialMatch (ByteBuffer * buf, b
 //      true if the game could never match even with extra moves.
 //
 bool
-Game::ExactMatch (Position * searchPos, ByteBuffer * buf, simpleMoveT * sm,
-                  gameExactMatchT searchType, bool * neverMatch)
+Game::ExactMatch (Position * searchPos, ByteBuffer * buf,
+                  gameExactMatchT searchType)
 {
     // If buf is NULL, the game is in memory. Otherwise, Decode only
     // the necessary moves:
     errorT err = OK;
 
     if (buf == NULL) {
-        MoveToPly(0);
+        MoveToStart();
     } else {
-        Clear ();
-        err = DecodeStart (buf);
-        KeepDecodedMoves = false;
+        err = DecodeSkipTags(buf);
     }
 
-    uint plyCount = 0;
-    //uint skip = 0;    // Just for statistics on number of moves skipped.
     uint search_whiteHPawns = 0;
     uint search_blackHPawns = 0;
-    uint current_whiteHPawns, current_blackHPawns;
     bool check_pawnMaskWhite, check_pawnMaskBlack;
     bool doHomePawnChecks = false;
 
@@ -1353,11 +1266,6 @@ Game::ExactMatch (Position * searchPos,
         }
     }
 
-    // If neverMatch is null, point it at a dummy value
-    bool dummy;
-    if (neverMatch == NULL) { neverMatch = &dummy; }
-    *neverMatch = false;
-
     if (searchType == GAME_EXACT_MATCH_Exact  ||
         searchType == GAME_EXACT_MATCH_Pawns) {
         doHomePawnChecks = true;
@@ -1371,7 +1279,6 @@ Game::ExactMatch (Position * searchPos,
         const pieceT* board = searchPos->GetBoard();
         const pieceT* b1 = currentBoard;
         const pieceT* b2 = board;
-        bool found = true;
 
         // If NO_SPEEDUPS is defined, a slower search is done without
         // optimisations that detect insufficient material.
@@ -1379,13 +1286,11 @@ Game::ExactMatch (Position * searchPos,
         // Insufficient material optimisation:
         if (searchPos->GetCount(WHITE) > CurrentPos->GetCount(WHITE)  ||
             searchPos->GetCount(BLACK) > CurrentPos->GetCount(BLACK)) {
-            *neverMatch = true;
             return false;
         }
         // Insufficient pawns optimisation:
         if (searchPos->PieceCount(WP) > CurrentPos->PieceCount(WP)  ||
             searchPos->PieceCount(BP) > CurrentPos->PieceCount(BP)) {
-            *neverMatch = true;
             return false;
         }
 
@@ -1396,26 +1301,24 @@ Game::ExactMatch (Position * searchPos,
         // not equal to search_xxHPawns.
         // We do not do this optimisation for a pawn files search,
         // because the exact pawn squares are not important there.
-
-        if (searchType != GAME_EXACT_MATCH_Fyles) {
             if (check_pawnMaskWhite) {
-                current_whiteHPawns = calcHomePawnMask (WP, currentBoard);
+                auto current_whiteHPawns = calcHomePawnMask (WP, currentBoard);
                 if ((current_whiteHPawns & search_whiteHPawns)
                         != search_whiteHPawns) {
-                    *neverMatch = true;
                     return false;
                 }
+                check_pawnMaskWhite = false;
             }
             if (check_pawnMaskBlack) {
-                current_blackHPawns = calcHomePawnMask (BP, currentBoard);
+                auto current_blackHPawns = calcHomePawnMask (BP, currentBoard);
                 if ((current_blackHPawns & search_blackHPawns)
                         != search_blackHPawns) {
-                    *neverMatch = true;
                     return false;
                 }
+                check_pawnMaskBlack = false;
             }
-        }
 #endif  // #ifndef NO_SPEEDUPS
+        bool found = true;
 
         // Not correct color: skip to next move
         if (searchPos->GetToMove() != CurrentPos->GetToMove()) {
@@ -1477,62 +1380,31 @@ Game::ExactMatch (Position * searchPos,
         }
 
         if (found) {
-            // Found a match! Set the returned next-move:
-            if (sm) {  // We need to decode the next move.
-                if (buf == NULL) {
-                    MoveForward();
-                    if (CurrentMove->marker == END_MARKER) {
-                        // Position matched at last move in the game.
-                        sm->from = sm->to = NULL_SQUARE;
-                        sm->promote = EMPTY;
-                    } else {
-                        *sm = CurrentMove->prev->moveData;
-                        MoveBackup();
-                    }
-                } else {
-                    err = DecodeNextMove (buf, sm);
-                    if (err != OK) {
-                        // Position matched at last move in the game.
-                        sm->from = sm->to = NULL_SQUARE;
-                        sm->promote = EMPTY;
-                    } else {
-                        // Backup to the matching position:
-                        CurrentPos->UndoSimpleMove (sm);
-                    }
-                }
-            }
             return true;
         }
 
     Move_Forward:
-#ifndef NO_SPEEDUPS
-        if (doHomePawnChecks) {
-            check_pawnMaskWhite = false;
-            check_pawnMaskBlack = false;
-            rankT rTo = square_Rank (CurrentMove->moveData.to);
-            rankT rFrom = square_Rank (CurrentMove->moveData.from);
-            // We only re-check the home pawn masks when something moves
-            // to or from the 2nd/7th rank:
-            if (rTo == RANK_2  ||  rFrom == RANK_2) {
-                check_pawnMaskWhite = true;
-            }
-            if (rTo == RANK_7  ||  rFrom == RANK_7) {
-                check_pawnMaskBlack = true;
-            }
-        }
-#endif
         if (buf == NULL) {
-            MoveForward ();
-            if (CurrentMove->marker == END_MARKER) {
-                err = ERROR_EndOfMoveList;
-            }
+            err = MoveForward();
         } else {
-            err = DecodeNextMove (buf, NULL);
-            if (err != OK  &&  err != ERROR_EndOfMoveList) {
-                return false;
+            simpleMoveT nextMove;
+            err = DecodeNextMove(buf, nextMove);
+            if (err == OK) {
+                CurrentPos->DoSimpleMove(nextMove);
+                if (doHomePawnChecks) {
+                    rankT rTo = square_Rank (nextMove.to);
+                    rankT rFrom = square_Rank (nextMove.from);
+                    // We only re-check the home pawn masks when something moves
+                    // to or from the 2nd/7th rank:
+                    if (rTo == RANK_2  ||  rFrom == RANK_2) {
+                        check_pawnMaskWhite = true;
+                    }
+                    if (rTo == RANK_7  ||  rFrom == RANK_7) {
+                        check_pawnMaskBlack = true;
+                    }
+                }
             }
         }
-        plyCount++;
     }
     return false;
 }
@@ -1642,7 +1514,7 @@ Game::GetPartialMoveList (DString * outS
     // unaltered:
     auto location = currentLocation();
 
-    MoveToPly(0);
+    MoveToStart();
     char temp [80];
     for (uint i=0; i < plyCount; i++) {
         if (CurrentMove->marker == END_MARKER) {
@@ -1695,14 +1567,14 @@ void Game::GetSAN(char* str) {
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Game::GetPrevSAN():
-//      Print the SAN representation of the current move to a string.
+//      Print the SAN representation of the previous move to a string.
 //      Prints an empty string ("") if not at a move.
 void
 Game::GetPrevSAN (char * str)
 {
     ASSERT (str != NULL);
     moveT * m = CurrentMove->prev;
-    if (m->marker == START_MARKER  ||  m->marker == END_MARKER) {
+    if (m->startMarker()  ||  m->endMarker()) {
         str[0] = 0;
         return;
     }
@@ -1720,14 +1592,11 @@ Game::GetPrevSAN (char * str)
 //      Prints an empty string ("") if not at a move.
 void Game::GetPrevMoveUCI(char* str) const {
     ASSERT(str != NULL);
-    moveT* m = CurrentMove->prev;
-    if (m->marker == START_MARKER) {
-        str[0] = 0;
-    } else {
-        Position pos(*CurrentPos);
-        pos.UndoSimpleMove(&m->moveData);
-        pos.MakeUCIString(&m->moveData, str);
-    }
+    const auto m = CurrentMove->prev;
+    if (!m->startMarker())
+        str = m->moveData.toLongNotation(str);
+
+    *str = '\0';
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1738,50 +1607,12 @@ void
 Game::GetNextMoveUCI (char * str)
 {
     ASSERT (str != NULL);
-    moveT * m = CurrentMove;
-    if (m->marker == START_MARKER  ||  m->marker == END_MARKER) {
-      str[0] = 0;
-      return;
-    }
-    CurrentPos->MakeUCIString (&(m->moveData), str);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// commentEmpty:
-//    Called by WriteMoveList to check there is really
-//    something to print given display options.
-//    comment is supposed to be non null
-bool
-Game::CommentEmpty ( const char * comment)
-{
-    char * s = NULL;
-    bool ret = false;
+    if (!CurrentMove->endMarker())
+        str = CurrentMove->moveData.toLongNotation(str);
 
-    if (comment == NULL)
-      return true;
-
-    if (comment[0] == '\0')
-      return true;
-
-    if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
-      s = strDuplicate (comment);
-      strTrimMarkCodes (s);
-      char * tmp = s;
-      bool empty = true;
-      while (tmp[0] != 0) {
-        if (tmp[0] != ' ') {
-          empty = false;
-          break;
-        }
-        tmp++;
-      }
-      ret = empty;
-
-      delete[] s;
-    }
-
-    return ret;
+    *str = '\0';
 }
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // writeComment:
 //    Called by WriteMoveList to write a single comment.
@@ -1789,16 +1620,7 @@ void
 Game::WriteComment (TextBuffer * tb, const char * preStr,
               const char * comment, const char * postStr)
 {
-
-    char * s = NULL;
-
-    if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
-        s = strDuplicate (comment);
-        strTrimMarkCodes (s);
-    } else {
-      s = (char *) comment;
-    }
-
+    const char* s = comment;
     if (s[0] != '\0') {
 
         if (IsColorFormat()) {
@@ -1824,10 +1646,6 @@ Game::WriteComment (TextBuffer * tb, con
 
         if (IsColorFormat()) { tb->PrintString ("</c>"); }
     }
-
-    if (PgnStyle & PGN_STYLE_STRIP_MARKS) {
-        delete[] s;
-    }
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1892,12 +1710,18 @@ errorT Game::WriteMoveList(TextBuffer* t
         postCommentStr = "";
     }
 
+    std::string strippedComment;
     // If this is a variation and it starts with a comment, print it:
-    if ((VarDepth > 0 || CurrentMove->prev == FirstMove) && 
-            ! CurrentMove->prev->comment.empty()) {
-        if (PgnStyle & PGN_STYLE_COMMENTS) {
-            WriteComment (tb, preCommentStr, CurrentMove->prev->comment.c_str(),
-                          postCommentStr);
+    if ((VarDepth > 0 || CurrentMove->prev == FirstMove) &&
+        (PgnStyle & PGN_STYLE_COMMENTS)) {
+        const char* comment = CurrentMove->prev->comment.c_str();
+        if (*comment && (PgnStyle & PGN_STYLE_STRIP_MARKS)) {
+            strippedComment = m->comment;
+            strTrimMarkCodes(strippedComment.data());
+            comment = strippedComment.data();
+        }
+        if (*comment) {
+            WriteComment(tb, preCommentStr, comment, postCommentStr);
             tb->PrintSpace();
             if (!VarDepth) {
                 tb->ClearTranslation ('\n');
@@ -2109,7 +1933,13 @@ errorT Game::WriteMoveList(TextBuffer* t
                 printMoveNum = true;
             }
 
-            if (!m->comment.empty() && ! CommentEmpty(m->comment.c_str()) ) {
+            const char* comment = m->comment.c_str();
+            if (*comment && (PgnStyle & PGN_STYLE_STRIP_MARKS)) {
+                strippedComment = m->comment;
+                strTrimMarkCodes(strippedComment.data());
+                comment = strippedComment.data();
+            }
+            if (*comment) {
                 if (!inComment && IsPlainFormat()  &&
                     (PgnStyle & PGN_STYLE_NO_NULL_MOVES)) {
                     // If this move has no variations, but the next move
@@ -2149,7 +1979,7 @@ errorT Game::WriteMoveList(TextBuffer* t
                     }
                 }
 
-                WriteComment (tb, preCommentStr, m->comment.c_str(), postCommentStr);
+                WriteComment(tb, preCommentStr, comment, postCommentStr);
 
                 if ((PgnStyle & PGN_STYLE_INDENT_COMMENTS) && VarDepth == 0) {
                     if (IsColorFormat()) {
@@ -2161,7 +1991,7 @@ errorT Game::WriteMoveList(TextBuffer* t
                 } else {
                     tb->PrintSpace();
                 }
-                if (printDiagrams  &&  strIsPrefix ("#", m->comment.c_str())) {
+                if (printDiagrams  &&  strIsPrefix ("#", comment)) {
                     if (IsLatexFormat()) {
                         tb->PrintString ("\n\\begin{diagram}\n");
                     }
@@ -2215,11 +2045,10 @@ errorT Game::WriteMoveList(TextBuffer* t
             for (uint i=0; i < m->numVariations; i++) {
                 if (PgnStyle & PGN_STYLE_INDENT_VARS) {
                     if (IsColorFormat()) {
-                        switch (VarDepth) {
-                            case 0: tb->PrintString ("<ip1>"); break;
-                            case 1: tb->PrintString ("<ip2>"); break;
-                            case 2: tb->PrintString ("<ip3>"); break;
-                            case 3: tb->PrintString ("<ip4>"); break;
+                        if (VarDepth < 19) {
+                            char tmp_str[16];
+                            sprintf(tmp_str, "<ip%u>", VarDepth + 1);
+                            tb->PrintString(tmp_str);
                         }
                     } else {
                         tb->SetIndent (tb->GetIndent() + 4); tb->Indent();
@@ -2267,11 +2096,10 @@ errorT Game::WriteMoveList(TextBuffer* t
                 }
                 if (PgnStyle & PGN_STYLE_INDENT_VARS) {
                     if (IsColorFormat()) {
-                        switch (VarDepth) {
-                            case 0: tb->PrintString ("</ip1><br>"); break;
-                            case 1: tb->PrintString ("</ip2><br>"); break;
-                            case 2: tb->PrintString ("</ip3><br>"); break;
-                            case 3: tb->PrintString ("</ip4><br>"); break;
+                        if (VarDepth < 19) {
+                            char tmp_str[16];
+                            sprintf(tmp_str, "</ip%u><br>", VarDepth + 1);
+                            tb->PrintString(tmp_str);
                         }
                     } else {
                         tb->SetIndent (tb->GetIndent() - 4); tb->Indent();
@@ -2536,7 +2364,7 @@ errorT Game::WritePGN(TextBuffer* tb) {
         tb->PrintString (newline);
     }
 
-    MoveToPly(0);
+    MoveToStart();
 
     if (IsHtmlFormat()) { tb->PrintString ("<p>"); }
     NumMovesPrinted = 1;
@@ -2580,113 +2408,67 @@ Game::WriteToPGN(uint lineWidth, bool Ne
 //      index file entry and a namebase that stores the
 //      player/site/event/round names.
 //
-errorT
-Game::LoadStandardTags (const IndexEntry* ie, const NameBase* nb)
-{
-    ASSERT (ie != NULL  &&  nb != NULL);
-    SetEventStr (ie->GetEventName (nb));
-    SetSiteStr (ie->GetSiteName (nb));
-    SetWhiteStr (ie->GetWhiteName (nb));
-    SetBlackStr (ie->GetBlackName (nb));
-    SetRoundStr (ie->GetRoundName (nb));
-    SetDate (ie->GetDate());
-    SetEventDate (ie->GetEventDate());
-    SetWhiteElo (ie->GetWhiteElo());
-    SetBlackElo (ie->GetBlackElo());
-    WhiteEstimateElo = nb->GetElo (ie->GetWhite());
-    BlackEstimateElo = nb->GetElo (ie->GetBlack());
-    SetWhiteRatingType (ie->GetWhiteRatingType());
-    SetBlackRatingType (ie->GetBlackRatingType());
-    SetResult (ie->GetResult());
-    SetEco (ie->GetEcoCode());
-    ie->GetFlagStr (ScidFlags, NULL);
-    return OK;
+void Game::LoadStandardTags(IndexEntry const& ie, TagRoster const& tags) {
+    SetEventStr(tags.event);
+    SetSiteStr(tags.site);
+    SetWhiteStr(tags.white);
+    SetBlackStr(tags.black);
+    SetRoundStr(tags.round);
+    SetDate(ie.GetDate());
+    SetEventDate(ie.GetEventDate());
+    SetWhiteElo(ie.GetWhiteElo());
+    SetBlackElo(ie.GetBlackElo());
+    SetWhiteRatingType(ie.GetWhiteRatingType());
+    SetBlackRatingType(ie.GetBlackRatingType());
+    SetResult(ie.GetResult());
+    SetEco(ie.GetEcoCode());
+    ie.GetFlagStr(ScidFlags, NULL);
 }
 
 eloT
-Game::GetAverageElo ()
-{
-    eloT white = WhiteElo;
-    eloT black = BlackElo;
-    if (white == 0) { white = WhiteEstimateElo; }
-    if (black == 0) { black = BlackEstimateElo; }
-    return (white + black) / 2;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// makeMoveByte(): inline routine used for encoding most moves.
-//
-static inline byte
-makeMoveByte (byte pieceNum, byte value)
-{
-    ASSERT (pieceNum <= 15  &&  value <= 15);
-    return (byte)((pieceNum & 15) << 4)  |  (byte)(value & 15);
+Game::GetAverageElo () {
+	auto white = WhiteElo;
+	auto black = BlackElo;
+	return (white == 0 || black == 0) ? 0 : (white + black) / 2;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // encodeKing(): encoding of King moves.
 //
-static inline void
-encodeKing (ByteBuffer * buf, simpleMoveT * sm)
-{
+static byte encodeKing(squareT from, int to) {
     // Valid King difference-from-old-square values are:
     // -9, -8, -7, -1, 1, 7, 8, 9, and -2 and 2 for castling.
     // To convert this to a val in the range [1-10], we add 9 and
     // then look up the val[] table.
     // Coded values 1-8 are one-square moves; 9 and 10 are Castling.
-
-    ASSERT(sm->pieceNum == 0);  // Kings MUST be piece Number zero.
-    int diff = (int) sm->to - (int) sm->from;
     static const byte val[] = {
     /* -9 -8 -7 -6 -5 -4 -3 -2 -1  0  1   2  3  4  5  6  7  8  9 */
         1, 2, 3, 0, 0, 0, 0, 9, 4, 0, 5, 10, 0, 0, 0, 0, 6, 7, 8
     };
+    auto diff = to - from;
+    static_assert(std::is_same_v<decltype(diff), int>);
 
     // If target square is the from square, it is the null move, which
     // is represented as a king move to its own square and is encoded
     // as the byte value zero.
-    if (sm->to == sm->from) {
-        buf->PutByte (makeMoveByte (0, 0));
-        return;
-    }
+    ASSERT((to == from && val[diff+9] == 0) || val[diff+9] != 0);
 
     // Verify we have a valid King move:
-    ASSERT(diff >= -9  &&  diff <= 9  &&  val[diff+9] != 0);
-    buf->PutByte (makeMoveByte (0, val [diff + 9]));
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodeKing(): decoding of King moves.
-//
-static inline errorT
-decodeKing (byte val, simpleMoveT * sm)
-{
-    static const int sqdiff[] = {
-        0, -9, -8, -7, -1, 1, 7, 8, 9, -2, 2
-    };
-
-    if (val == 0) {
-      sm->to = sm->from;  // Null move
-        return OK;
-    }
-
-    if (val < 1  ||  val > 10) { return ERROR_Decode; }
-    sm->to = sm->from + sqdiff[val];
-    return OK;
+    ASSERT(diff >= -9 && diff <= 9);
+    return val[diff + 9];
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // encodeKnight(): encoding Knight moves.
 //
-static inline void
-encodeKnight (ByteBuffer * buf, simpleMoveT * sm)
-{
+static byte encodeKnight(squareT from, squareT to) {
     // Valid Knight difference-from-old-square values are:
     // -17, -15, -10, -6, 6, 10, 15, 17.
     // To convert this to a value in the range [1-8], we add 17 to
     // the difference and then look up the val[] table.
 
-    int diff = (int) sm->to - (int) sm->from;
+    auto diff = to - from;
+    static_assert(std::is_same_v<decltype(diff), int>);
     static const byte val[] = {
     /* -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1  0 */
         1,  0,  2,  0,  0,  0,  0,  3,  0, 0, 0, 4, 0, 0, 0, 0, 0, 0,
@@ -2697,175 +2479,74 @@ encodeKnight (ByteBuffer * buf, simpleMo
 
     // Verify we have a valid knight move:
     ASSERT (diff >= -17  &&  diff <= 17  &&  val[diff + 17] != 0);
-    buf->PutByte (makeMoveByte (sm->pieceNum, val [diff + 17]));
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodeKnight(): decoding Knight moves.
-//
-static inline errorT
-decodeKnight (byte val, simpleMoveT * sm)
-{
-    static const int sqdiff[] = {
-        0, -17, -15, -10, -6, 6, 10, 15, 17
-    };
-    if (val < 1  ||  val > 8) { return ERROR_Decode; }
-    sm->to = sm->from + sqdiff[val];
-    return OK;
+    return val[diff + 17];
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // encodeRook(): encoding rook moves.
 //
-static inline void
-encodeRook (ByteBuffer * buf, simpleMoveT * sm)
-{
+static byte encodeRook(squareT from, squareT to) {
     // Valid Rook moves are to same rank, OR to same fyle.
     // We encode the 8 squares on the same rank 0-8, and the 8
     // squares on the same fyle 9-15. This means that for any particular
     // rook move, two of the values in the range [0-15] will be
     // meaningless, as they will represent the from-square.
 
-    ASSERT (sm->from <= H8  &&  sm->to <= H8);
-    byte val;
-
     // Check if the two squares share the same rank:
-    if (square_Rank(sm->from) == square_Rank(sm->to)) {
-        val = square_Fyle(sm->to);
-    } else {
-        val = 8 + square_Rank(sm->to);
+    if (square_Rank(from) == square_Rank(to)) {
+        return square_Fyle(to);
     }
-    buf->PutByte (makeMoveByte (sm->pieceNum, val));
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodeRook(): decoding Rook moves.
-//
-static inline errorT
-decodeRook (byte val, simpleMoveT * sm)
-{
-    if (val >= 8) {
-        // This is a move along a Fyle, to a different rank:
-        sm->to = square_Make (square_Fyle(sm->from), (val - 8));
-    } else {
-        sm->to = square_Make (val, square_Rank(sm->from));
-    }
-    return OK;
+    return 8 + square_Rank(to);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // encodeBishop(): encoding Bishop moves.
 //
-static inline void
-encodeBishop (ByteBuffer * buf, simpleMoveT * sm)
-{
+static byte encodeBishop(squareT from, squareT to) {
     // We encode a Bishop move as the Fyle moved to, plus
     // a one-bit flag to indicate if the direction was
     // up-right/down-left or vice versa.
 
-    ASSERT (sm->to <= H8  &&  sm->from <= H8);
-    byte val;
-    val = square_Fyle(sm->to);
-    int rankdiff = (int)square_Rank(sm->to) - (int)square_Rank(sm->from);
-    int fylediff = (int)square_Fyle(sm->to) - (int)square_Fyle(sm->from);
+    auto rankdiff = square_Rank(to) - square_Rank(from);
+    auto fylediff = square_Fyle(to) - square_Fyle(from);
+    static_assert(std::is_same_v<decltype(rankdiff), int>);
+    static_assert(std::is_same_v<decltype(fylediff), int>);
 
     // If (rankdiff * fylediff) is negative, it's up-left/down-right:
-    if (rankdiff * fylediff < 0) { val += 8; }
+    if (rankdiff * fylediff < 0)
+        return square_Fyle(to) + 8;
 
-    buf->PutByte (makeMoveByte (sm->pieceNum, val));
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodeBishop(): decoding Bishop moves.
-//
-static inline errorT
-decodeBishop (byte val, simpleMoveT * sm)
-{
-    byte fyle = (val & 7);
-    int fylediff = (int)fyle - (int)square_Fyle(sm->from);
-    if (val >= 8) {
-        // It is an up-left/down-right direction move.
-        sm->to = sm->from - 7 * fylediff;
-    } else {
-        sm->to = sm->from + 9 * fylediff;
-    }
-    if (sm->to > H8) { return ERROR_Decode;}
-    return OK;
+    return square_Fyle(to);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // encodeQueen(): encoding Queen moves.
 //
-static inline void
-encodeQueen (ByteBuffer * buf, simpleMoveT * sm)
-{
+static byte encodeQueen(squareT from, squareT to, byte& multibyte) {
     // We cannot fit all Queen moves in one byte, so Rooklike moves
     // are in one byte (encoded the same way as Rook moves),
     // while diagonal moves are in two bytes.
+    if (square_Rank(from) == square_Rank(to)) // Rook-horizontal move:
+        return square_Fyle(to);
 
-    ASSERT (sm->to <= H8  &&  sm->from <= H8);
-    byte val;
-
-    if (square_Rank(sm->from) == square_Rank(sm->to)) {
-        // Rook-horizontal move:
-
-        val = square_Fyle(sm->to);
-        buf->PutByte (makeMoveByte (sm->pieceNum, val));
+    if (square_Fyle(from) == square_Fyle(to)) // Rook-vertical move:
+        return 8 + square_Rank(to);
 
-    } else if (square_Fyle(sm->from) == square_Fyle(sm->to)) {
-        // Rook-vertical move:
-
-        val = 8 + square_Rank(sm->to);
-        buf->PutByte (makeMoveByte (sm->pieceNum, val));
-
-    } else {
-        // Diagonal move:
-        ASSERT(std::abs(sm->to / 8 - sm->from / 8) ==
-               std::abs(sm->to % 8 - sm->from % 8));
-
-        // First, we put a rook-horizontal move to the from square (which
-        // is illegal of course) to indicate it is NOT a rooklike move:
-
-        val = square_Fyle(sm->from);
-        buf->PutByte (makeMoveByte (sm->pieceNum, val));
-
-        // Now we put the to-square in the next byte. We add a 64 to it
-        // to make sure that it cannot clash with the Special tokens (which
-        // are in the range 0 to 15, since they are special King moves).
-
-        buf->PutByte (sm->to + 64);
-    }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodeQueen(): decoding Queen moves.
-//
-static inline errorT
-decodeQueen (ByteBuffer * buf, byte val, simpleMoveT * sm)
-{
-    if (val >= 8) {
-        // Rook-vertical move:
-        sm->to = square_Make (square_Fyle(sm->from), (val - 8));
-
-    } else if (val != square_Fyle(sm->from)) {
-        // Rook-horizontal move:
-        sm->to = square_Make (val, square_Rank(sm->from));
-
-    } else {
-        // Diagonal move: coded in TWO bytes.
-        val = buf->GetByte();
-        if (val < 64  ||  val > 127) { return ERROR_Decode; }
-        sm->to = val - 64;
-    }
-    return OK;
+    // Diagonal move:
+    ASSERT(std::abs(to / 8 - from / 8) == std::abs(to % 8 - from % 8));
+    // First, we put a rook-horizontal move to the from square (which
+    // is illegal of course) to indicate it is NOT a rooklike move:
+    // Now we put the to-square in the next byte. We add a 64 to it
+    // to make sure that it cannot clash with the Special tokens (which
+    // are in the range 0 to 15, since they are special King moves).
+    multibyte = to + 64;
+    return square_Fyle(from);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // encodePawn(): encoding Pawn moves.
 //
-static inline void
-encodePawn (ByteBuffer * buf, simpleMoveT * sm)
-{
+static byte encodePawn(squareT from, squareT to, pieceT promo) {
     // Pawn moves require a promotion encoding.
     // The pawn moves are:
     // 0 = capture-left,
@@ -2878,12 +2559,13 @@ encodePawn (ByteBuffer * buf, simpleMove
     // 15 = forward TWO squares.
 
     byte val;
-    int diff = (int)(sm->to) - (int)(sm->from);
+    auto diff = to - from;
+    static_assert(std::is_same_v<decltype(diff), int>);
 
     if (diff < 0) { diff = -diff; }
     if (diff == 16) { // Move forward two squares
         val = 15;
-        ASSERT (sm->promote == EMPTY);
+        ASSERT (promo == EMPTY);
 
     } else {
         if (diff == 7) { val = 0; }
@@ -2892,48 +2574,16 @@ encodePawn (ByteBuffer * buf, simpleMove
             ASSERT (diff == 9);
             val = 2;
         }
-        if (sm->promote != EMPTY) {
+        if (promo != EMPTY) {
             // Handle promotions.
             // sm->promote must be Queen=2,Rook=3, Bishop=4 or Knight=5.
             // We add 3 for Queen, 6 for Rook, 9 for Bishop, 12 for Knight.
 
-            ASSERT (sm->promote >= QUEEN  &&  sm->promote <= KNIGHT);
-            val += 3 * ((sm->promote) - 1);
+            ASSERT (promo >= QUEEN  &&  promo <= KNIGHT);
+            val += 3 * ((promo) - 1);
         }
     }
-    buf->PutByte (makeMoveByte (sm->pieceNum, val));
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodePawn(): decoding Pawn moves.
-//
-static inline errorT
-decodePawn (byte val, simpleMoveT * sm, colorT toMove)
-{
-    static const int
-    toSquareDiff [16] = {
-        7,8,9, 7,8,9, 7,8,9, 7,8,9, 7,8,9, 16
-    };
-
-    static const pieceT
-    promoPieceFromVal [16] = {
-        EMPTY,EMPTY,EMPTY,
-        QUEEN,QUEEN,QUEEN,
-        ROOK,ROOK,ROOK,
-        BISHOP,BISHOP,BISHOP,
-        KNIGHT,KNIGHT,KNIGHT,
-        EMPTY
-    };
-
-    if (toMove == WHITE) {
-        sm->to = sm->from + toSquareDiff[val];
-    } else {
-        sm->to = sm->from - toSquareDiff[val];
-    }
-
-    sm->promote = promoPieceFromVal[val];
-
-    return OK;
+    return val;
 }
 
 
@@ -2955,15 +2605,6 @@ decodePawn (byte val, simpleMoveT * sm,
 // a game must end with the end-game token.
 
 
-// The inline routine  isSpecialMoveCode() returns true is a byte value
-// has the value of a special non-move token:
-inline bool
-isSpecialMoveCode (byte val)
-{
-    return (val <= ENCODE_LAST  &&  val >= ENCODE_FIRST);
-}
-
-
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // decodeMove():
 //      Decode a move from a bytebuffer. Assumes the byte val is an
@@ -2974,344 +2615,278 @@ isSpecialMoveCode (byte val)
 //
 static errorT decodeMove(ByteBuffer* buf, simpleMoveT* sm, byte val,
                          const Position* pos) {
-    const squareT* sqList = pos->GetList(pos->GetToMove());
-    sm->from = sqList[val >> 4];
-    if (sm->from > H8) { return ERROR_Decode; }
-
-    const pieceT* board = pos->GetBoard();
-    sm->movingPiece = board[sm->from];
-    sm->promote = EMPTY;
+	const colorT toMove = pos->GetToMove();
+	const squareT from = pos->GetList(toMove)[val >> 4];
+	if (from > H8)
+		return ERROR_Decode;
+
+	const auto ptype = piece_Type(pos->GetPiece(from));
+	const auto [to, promo] = buf->decodeMove(toMove, ptype, from, val);
+	if (to < 0 || to > 63)
+		return ERROR_Decode;
+
+	if (to == from) {
+		if (promo == INVALID_PIECE)
+			return ERROR_Decode;
 
-    errorT err = OK;
-    pieceT pt = piece_Type (sm->movingPiece);
-    switch (pt) {
-    case PAWN:
-        err = decodePawn (val & 15, sm, pos->GetToMove());
-        break;
-    case KNIGHT:
-        err = decodeKnight (val & 15, sm);
-        break;
-    case ROOK:
-        err = decodeRook (val & 15, sm);
-        break;
-    case BISHOP:
-        err = decodeBishop (val & 15, sm);
-        break;
-    case KING:
-        err = decodeKing (val & 15, sm);
-        break;
-    // For queen moves: Rook-like moves are in 1 byte, diagonals are in 2.
-    case QUEEN:
-        err = decodeQueen (buf, val & 15, sm);
-        break;
-    default:
-        err = ERROR_Decode;
-    }
-    return err;
+		if (promo != PAWN && !pos->canCastle<false>(promo == KING))
+			return ERROR_Decode;
+	} else {
+		if (to == pos->GetKingSquare(WHITE) || to == pos->GetKingSquare(BLACK))
+			return ERROR_Decode;
+	}
+	pos->makeMove(from, to, promo, *sm);
+	return OK;
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Game::EncodeMove():
-//  Encode one move and output it to the bytebuffer.
-//
-static void encodeMove(ByteBuffer* buf, moveT* m) {
-    simpleMoveT* sm = &(m->moveData);
-    pieceT pt = piece_Type(sm->movingPiece);
-
-    typedef void encodeFnType (ByteBuffer *, simpleMoveT *);
-    static encodeFnType * encodeFn[] = {
-        NULL         /* 0 */,
-        encodeKing   /*1=KING*/,
-        encodeQueen  /*2=QUEEN*/,
-        encodeRook   /*3=ROOK*/,
-        encodeBishop /*4=BISHOP*/,
-        encodeKnight /*5=KNIGHT*/,
-        encodePawn   /*6=PAWN*/
-    };
-    ASSERT (pt >= KING  &&  pt <= PAWN);
-    (encodeFn[pt]) (buf, sm);
+template <typename DestT>
+void encodeMove(const simpleMoveT& sm, DestT& dest) {
+	byte multibyte = 0;
+	byte val;
+	switch (piece_Type(sm.movingPiece)) {
+	case KING:
+		ASSERT(sm.pieceNum == 0); // Kings MUST be piece Number zero.
+		val = encodeKing(sm.from,
+		                 sm.isCastle() ? sm.from + sm.isCastle() : sm.to);
+		break;
+	case QUEEN:
+		val = encodeQueen(sm.from, sm.to, multibyte);
+		break;
+	case ROOK:
+		val = encodeRook(sm.from, sm.to);
+		break;
+	case BISHOP:
+		val = encodeBishop(sm.from, sm.to);
+		break;
+	case KNIGHT:
+		val = encodeKnight(sm.from, sm.to);
+		break;
+	default:
+		ASSERT(PAWN == piece_Type(sm.movingPiece));
+		val = encodePawn(sm.from, sm.to, sm.promote);
+	}
+	ASSERT(sm.pieceNum <= 15 && val <= 15);
+	const auto encoded = static_cast<byte>(val | (sm.pieceNum << 4));
+	dest.emplace_back(encoded);
+	if (multibyte) { // Diagonal Queen moves are stored using two bytes.
+		dest.emplace_back(multibyte);
+	}
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// encodeVariation(): Used by Encode() to encode the game's moves.
-//      Recursive; calls itself to encode subvariations.
-//
-static errorT encodeVariation(ByteBuffer* buf, moveT* m, uint* subVarCount,
-                              uint* nagCount, uint depth) {
-    ASSERT(m != NULL);
-
-    // Check if there is a pre-game or start-of-variation comment:
-    if (! m->prev->comment.empty()) {
-        buf->PutByte (ENCODE_COMMENT);
-    }
-
-    while (m->marker != END_MARKER) {
-        encodeMove (buf, m);
-        for (uint i=0; i < (uint) m->nagCount; i++) {
-            buf->PutByte (ENCODE_NAG);
-            buf->PutByte (m->nags[i]);
-            *nagCount += 1;
-        }
-        if (! m->comment.empty()) {
-            buf->PutByte (ENCODE_COMMENT);
-        }
-        if (m->numVariations > 0) {
-            moveT * subVar = m->varChild;
-            for (uint i=0; i < m->numVariations; i++) {
-                *subVarCount += 1;
-                buf->PutByte (ENCODE_START_MARKER);
-                encodeVariation (buf, subVar->next, subVarCount, nagCount, depth+1);
-                subVar = subVar->varChild;
-            }
-        }
-        m = m->next;
-    }
-    // At end, we output the end-variation or end-game token.
-    if (depth == 0) {
-        buf->PutByte (ENCODE_END_GAME);
-    } else {
-        buf->PutByte (ENCODE_END_MARKER);
-    }
-    return buf->Status();
+/// Encode the moves, the nags, the comment mark and the variations.
+template <typename MoveT, typename DestT>
+std::pair<unsigned, unsigned> encodeMovelist(bool mark_comments, const MoveT* m,
+                                             DestT& dest) {
+	ASSERT(m && m->startMarker());
+
+	// Check if there is a pre-game comment
+	if (mark_comments && !m->comment.empty())
+		dest.emplace_back(ENCODE_COMMENT);
+
+	unsigned n_vars = 0;
+	unsigned n_nags = 0;
+	while ((m = m->nextMoveInPGN())) {
+		if (m->startMarker()) {
+			++n_vars;
+			dest.emplace_back(ENCODE_START_MARKER);
+			if (mark_comments && !m->comment.empty())
+				dest.emplace_back(ENCODE_COMMENT);
+
+		} else if (m->endMarker()) {
+			if (m->nextMoveInPGN())
+				dest.emplace_back(ENCODE_END_MARKER);
+
+		} else {
+			encodeMove(m->moveData, dest);
+
+			for (int i = 0, n = m->nagCount; i < n; ++i) {
+				dest.emplace_back(ENCODE_NAG);
+				dest.emplace_back(m->nags[i]);
+				++n_nags;
+			}
+			if (mark_comments && !m->comment.empty())
+				dest.emplace_back(ENCODE_COMMENT);
+		}
+	}
+	dest.emplace_back(ENCODE_END_GAME);
+	return {n_vars, n_nags};
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Game::DecodeVariation():
-//      Decodes the game moves. Recursively decodes subvariations.
-//
-errorT
-Game::DecodeVariation (ByteBuffer * buf, byte flags, uint level)
-{
-    simpleMoveT sm;
-    errorT err;
-    byte b = buf->GetByte ();
-    while (b != ENCODE_END_GAME  &&  b != ENCODE_END_MARKER) {
-        switch (b) {
-        case ENCODE_START_MARKER:
-            err = AddVariation();
-            if (err != OK) { return err; }
-            err = DecodeVariation (buf, flags, level + 1);
-            if (err != OK) { return err; }
-            err = MoveExitVariation();
-            if (err != OK) { return err; }
-            err = MoveForward();
-            if (err != OK) { return err; }
-            break;
-
-        case ENCODE_NAG:
-            AddNag (buf->GetByte ());
-            break;
-
-        case ENCODE_COMMENT:
-            if (flags & GAME_DECODE_COMMENTS) {
-                // Mark this comment as needing to be read
-                CurrentMove->prev->comment = "*";
-            }
-            break;
-
-        default:  // It is a regular move
-            err = decodeMove(buf, &sm, b, currentPos());
-            if (err != OK)  { return err; }
-            AddMove(&sm);
-        }
-
-        b = buf->GetByte ();
-        if (buf->Status() != OK) { return buf->Status(); }
-    }
-
-    if (level == 0  &&  b != ENCODE_END_GAME) { return ERROR_Decode; }
-    if (level > 0  &&  b != ENCODE_END_MARKER) { return ERROR_Decode; }
-    return buf->Status();
+/// Decodes the game moves
+errorT Game::DecodeVariation(ByteBuffer& buf,
+                             std::vector<moveT*>& comment_marks) {
+	simpleMoveT sm;
+	for (;;) {
+		auto [err, val] = buf.nextMove(
+		    this->VarDepth, [&](auto) { return true; },
+		    [&] {
+			    // Mark this comment as needing to be read
+			    comment_marks.push_back(this->CurrentMove->prev);
+		    },
+		    [&](auto newVariation) {
+			    if (newVariation)
+				    return AddVariation() == OK;
+
+			    return (MoveExitVariation() == OK && MoveForward() == OK);
+		    },
+		    [&](auto nag) {
+			    return this->AddNag(nag) == OK;
+		    });
+		if (err)
+			return (err == ERROR_EndOfMoveList) ? OK : err;
+
+		auto errMove = decodeMove(&buf, &sm, val, currentPos());
+		if (!errMove)
+			errMove = AddMove(sm);
+		if (errMove)
+			return errMove;
+	}
 }
 
+// Return the number of comments and true if comment marks are useful
+template <typename MoveT> auto countComments(const MoveT* m) {
+	unsigned n_comments = 0;
+	unsigned n_empty = 0;
+	for (; m; m = m->nextMoveInPGN()) {
+		if (m->endMarker())
+			continue;
+
+		if (m->comment.empty()) {
+			++n_empty;
+		} else {
+			++n_comments;
+		}
+	}
+	return std::make_pair(n_comments, n_comments < n_empty);
+}
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Common tags are encoded in one byte, as a value over 240.
-//       This means that the maximum length of a non-common tag is 240
-//       bytes, and the maximum number of common tags is 15.
-//
-constexpr size_t MAX_TAG_LEN = 240;
-
-static const char* commonTags[255 - MAX_TAG_LEN] = {
-    // 241, 242: Country
-    "WhiteCountry", "BlackCountry",
-    // 243: Annotator
-    "Annotator",
-    // 244: PlyCount
-    "PlyCount",
-    // 245: EventDate (plain text encoding)
-    "EventDate",
-    // 246, 247: Opening, Variation
-    "Opening", "Variation",
-    // 248-250: Setup and Source
-    "Setup", "Source", "SetUp",
-    // 252-254: spare for future use
-    NULL, NULL, NULL, NULL,
-    // 255: Reserved for compact EventDate encoding
-    NULL
-};
+/**
+ * The Comments section is composed by null-terminated strings. The comments are
+ * stored in the order in which they will appear in the PGN notation:
+ * {C1} 1.d4 {C2} (1.b4 {C3} 1...e5 {C4} (1...Na6 {C5}) 2.e4 {C6})
+ * ({C7} 1.g4 {C8}) 1...d5 {C9}
+ */
+template <typename MoveT, typename DestT>
+void encodeComments(bool mark_comments, const MoveT* m, DestT& dest) {
+	for (; m; m = m->nextMoveInPGN()) {
+		if (m->endMarker())
+			continue;
+
+		if (!m->comment.empty() || !mark_comments) {
+			const auto len = m->comment.size() + 1; // Include the null char
+			const auto data = m->comment.c_str();
+			dest.insert(dest.end(), data, data + len);
+		}
+	}
+}
 
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// encodeTags():
-//      Encodes the non-standard tags.
-//
-template <typename TCont>
-errorT encodeTags(ByteBuffer* buf, const TCont& tagList) {
-    for (auto& tag : tagList) {
-        uint tagnum = 1;
-        const char ** common = commonTags;
-        while (*common != NULL) {
-            if (tag.first == *common) {
-                buf->PutByte ((byte) MAX_TAG_LEN + tagnum);
-                break;
-            } else {
-                common++;
-                tagnum++;
-            }
-        }
-        if (*common == NULL) {   // This is not a common tag.
-            auto length = std::min(tag.first.length(), MAX_TAG_LEN);
-            buf->PutByte ((byte) length);
-            buf->PutFixedString(tag.first.c_str(), length);
-        }
+// Decodes the comments from @e buf and stores them into the marked moves.
+// If the number of comments is greater than the number of marked moves, they
+// are added sequentially after the last move that has a comment.
+// This way it is possible to not add encode marks for games that are almost
+// fully commented.
+template <typename SourceT, typename MoveT>
+static errorT decodeComments(SourceT& buf, MoveT* first_move,
+                             std::vector<MoveT*>& comment_marks) {
+    if (!comment_marks.empty()) {
+        for (auto m : comment_marks) {
+            if (auto str = buf.GetTerminatedString())
+                m->comment = str;
+            else
+                return ERROR_Decode;
+        }
+        first_move = comment_marks.back()->nextMoveInPGN();
+    }
+    while (auto str = buf.GetTerminatedString()) {
+        if (!first_move)
+            return ERROR_Decode;
 
-        auto valueLen = std::min<size_t>(tag.second.length(), 255);
-        buf->PutByte ((byte) valueLen);
-        buf->PutFixedString(tag.second.c_str(), valueLen);
+        first_move->comment = str;
+        first_move = first_move->nextMoveInPGN();
     }
-    buf->PutByte (0);
-    return buf->Status();
+    return OK;
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Game::DecodeTags():
-//      Decodes the non-standard tags of the game.
-//
-errorT
-Game::DecodeTags (ByteBuffer * buf, bool storeTags)
-{
-    byte b;
-    char tag [255];
-    char value [255];
-
-    b = buf->GetByte ();
-    while (b != 0  &&  buf->Status() == OK) {
-        if (b == 255) {
-            // Special binary 3-byte encoding of EventDate:
-            dateT date = 0;
-            b = buf->GetByte(); date = (date << 8) | b;
-            b = buf->GetByte(); date = (date << 8) | b;
-            b = buf->GetByte(); date = (date << 8) | b;
-            SetEventDate (date);
-            //char dateStr[20];
-            //date_DecodeToString (date, dateStr);
-            //if (storeTags) { AddPgnTag ("EventDate", dateStr); }
-        } else if (b > MAX_TAG_LEN) {
-            // A common tag name, not explicitly stored:
-            char * ctag = (char *) commonTags[b - MAX_TAG_LEN - 1];
-            b = buf->GetByte ();
-            buf->GetFixedString (value, b);
-            value[b] = '\0';
-            if (storeTags) { AddPgnTag (ctag, value); }
-        } else {
-            buf->GetFixedString (tag, b);
-            tag[b] = '\0';
-            b = buf->GetByte ();
-            buf->GetFixedString (value, b);
-            value[b] = '\0';
-            if (storeTags) { AddPgnTag (tag, value); }
-        }
-        b = buf->GetByte();
-    }
-   return buf->Status();
-}
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// skipTags():
-//      Called instead of DecodeTags() to skip over the tags of the
-//      game when decoding it. Called from DecodeStart() since the
-//      nonstandard tags are not needed for searches.
-//
-static errorT
-skipTags (ByteBuffer * buf)
-{
-    byte b;
-    b = buf->GetByte ();
-    while (b != 0  &&  buf->Status() == OK) {
-        if (b == 255) {
-            // Special 3-byte binary encoding of EventDate:
-            buf->Skip (3);
-        } else {
-            if (b > MAX_TAG_LEN) {
-                // Do nothing.
-            } else {
-                buf->Skip (b);
-            }
-            b = buf->GetByte ();
-            buf->Skip (b);
-        }
-        b = buf->GetByte();
-    }
-    return buf->Status ();
-}
+/// Calculate the game's main line information:
+/// - home pawn delta information
+/// - promotions flags
+/// - number of half moves
+/// - final material signature
+/// - stored line code
+template <typename MoveT>
+std::pair<bool, bool> mainlineInfo(const Position* customStart,
+                                   const MoveT* firstMove, IndexEntry& dest) {
+	ushort nHalfMoves = 0;
+	bool PromoFlag = false;
+	bool UnderPromosFlag = false;
+	unsigned hpCount = 0;
+	byte hpVal[8] = {};
+	Position pos = customStart ? *customStart : Position::getStdStart();
+
+	auto hpOld = HPSIG_StdStart; // All 16 pawns are on their home squares.
+	for (auto move = firstMove; !move->endMarker(); move = move->next) {
+		++nHalfMoves;
+
+		if (move->moveData.promote != EMPTY) {
+			PromoFlag = true;
+			if (piece_Type(move->moveData.promote) != QUEEN) {
+				UnderPromosFlag = true;
+			}
+		}
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// encodeComments():
-//      Encode the comments of the game. Recurses the moves of the game
-//      and writes the comment whenever a move with a comment is found.
-//
-static errorT encodeComments(ByteBuffer* buf, moveT* m, uint* commentCounter) {
-    ASSERT(buf != NULL && m != NULL);
+		pos.DoSimpleMove(move->moveData);
+		if (!customStart) {
+			const auto hpNew = pos.GetHPSig();
+			if (unsigned changed = hpOld - hpNew) {
+				hpOld = hpNew;
+				byte idxMovedPawn = 0; // __builtin_ctz(changed)
+				while (changed >>= 1) {
+					++idxMovedPawn;
+				}
+				assert(idxMovedPawn <= 0x0F);
+				if ((hpCount & 1) == 0) // There are only 16 pawns, so we can
+					idxMovedPawn <<= 4; // store two pawn moves in every byte
+				hpVal[hpCount++ / 2] |= idxMovedPawn;
+			}
+		}
+	}
 
-    while (m->marker != END_MARKER) {
-        if (! m->comment.empty()) {
-            buf->PutTerminatedString (m->comment.c_str());
-            *commentCounter += 1;
-        }
-        if (m->numVariations) {
-           moveT * subVar = m->varChild;
-            for (uint i=0; i < m->numVariations; i++) {
-                encodeComments (buf, subVar, commentCounter);
-                subVar = subVar->varChild;
-            }
-        }
-        m = m->next;
-    }
-    return buf->Status();
-}
+	byte storedCode = 0;
+	if (!customStart) {
+		storedCode = StoredLine::classify([&](auto begin, auto end) {
+			if (std::distance(begin, end) > nHalfMoves)
+				return false;
+
+			const moveT* gameMove = firstMove;
+			for (; begin != end; ++begin) {
+				if (begin->isCastle()) {
+					auto side = begin->getTo() > begin->getFrom() ? 2 : -2;
+					if (gameMove->moveData.isCastle() != side)
+						return false;
+
+				} else if (gameMove->moveData.from != begin->getFrom() ||
+				           gameMove->moveData.to != begin->getTo()) {
+					return false;
+				}
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// decodeComments():
-//      Decodes the comments of the game. When decoding the moves, the
-//      comment field of each move that has a comment is marked (made
-//      not empty), so this function recurses the movelist and subvariations
-//      and allocates each comment to its move.
-//
-static errorT decodeComments(ByteBuffer* buf, moveT* m) {
-    ASSERT (buf != NULL  &&  m != NULL);
+				gameMove = gameMove->next;
+			}
+			return true;
+		});
+	}
 
-    while (m->marker != END_MARKER) {
-        if (! m->comment.empty()) {
-            ASSERT (m->comment == "*");
-            char * str;
-            buf->GetTerminatedString(&str);
-            m->comment = str;
-        }
+	dest.SetHomePawnData(static_cast<byte>(hpCount), hpVal);
+	dest.SetPromotionsFlag(PromoFlag);
+	dest.SetUnderPromoFlag(UnderPromosFlag);
+	dest.SetNumHalfMoves(nHalfMoves);
+	dest.SetFinalMatSig(matsig_Make(pos.GetMaterial()));
+	dest.SetStoredLineCode(storedCode);
 
-        if (m->numVariations) {
-           moveT * subVar = m->varChild;
-            for (uint i=0; i < m->numVariations; i++) {
-                decodeComments (buf, subVar);
-                subVar = subVar->varChild;
-            }
-        }
-        m = m->next;
-    }
-    return buf->Status();
+	return {PromoFlag, UnderPromosFlag};
 }
 
-
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Game::Encode(): Encode the game to a buffer for disk storage.
 //      If passed a NON-null IndexEntry pointer, it will fill in the
@@ -3323,105 +2898,62 @@ static errorT decodeComments(ByteBuffer*
 //       -  finalMatSig: the material signature of the final position.
 //       -  homePawnData: the home pawn change list.
 //
-errorT
-Game::Encode (ByteBuffer * buf, IndexEntry * ie)
-{
-    ASSERT (buf != NULL);
+std::pair<IndexEntry, TagRoster> Game::Encode(std::vector<byte>& dest) const {
+    auto tags = TagRoster();
+    tags.event = GetEventStr();
+    tags.site = GetSiteStr();
+    tags.white = GetWhiteStr();
+    tags.black = GetBlackStr();
+    tags.round = GetRoundStr();
 
-    // Make the home pawn change list and update PromotionFlag:
-    byte homePawnList[9];
-    auto UnderPromosFlag = MakeHomePawnList(homePawnList);
+    auto ie = IndexEntry();
+    // Set the fields in the IndexEntry:
+    ie.SetDate(Date);
+    ie.SetEventDate(EventDate);
+    ie.SetResult(Result);
+    ie.SetEcoCode(EcoCode);
+    ie.SetWhiteElo(WhiteElo);
+    ie.SetBlackElo(BlackElo);
+    ie.SetWhiteRatingType(WhiteRatingType);
+    ie.SetBlackRatingType(BlackRatingType);
+    if (HasNonStandardStart()) {
+        ie.SetStartFlag(true);
+        if (StartPos->isChess960()) {
+            ie.setChess960();
+        }
+    } else {
+        ie.SetStartFlag(false);
+    }
+    ie.SetFlag(IndexEntry::StrToFlagMask(ScidFlags), true);
+
+    const auto [promo, underPromo] = mainlineInfo(StartPos.get(),
+                                                  FirstMove->next, ie);
 
-    buf->Empty();
     // First, encode info not already stored in the index
     // This will be the non-STR (non-"seven tag roster") PGN tags.
-    errorT err = encodeTags(buf, GetExtraTags());
-    if (err != OK) { return err; }
-    // Now the game flags:
-    byte flags = 0;
-    if (HasNonStandardStart()) { flags += 1; }
-    if (PromotionsFlag)   { flags += 2; }
-    if (UnderPromosFlag)  { flags += 4; }
-    buf->PutByte (flags);
-    // Now encode the startBoard, if there is one.
-    if (StartPos) {
-        char tempStr [256];
-        StartPos->PrintFEN (tempStr, FEN_ALL_FIELDS);
-        buf->PutTerminatedString (tempStr);
-    }
+    encodeTags(GetExtraTags(), dest);
+
+    // Encode the promotion flags and the start position
+    char FEN[256];
+    encodeStartBoard(promo, underPromo,
+                     HasNonStandardStart(FEN) ? FEN : nullptr, dest);
+
+    auto [commentCount, markComments] = countComments(FirstMove);
+
+    // Compatibility: SCID4 requires the markers
+    markComments = true;
 
     // Now the movelist:
-    uint varCount = 0;
-    uint nagCount = 0;
-    err = encodeVariation (buf, FirstMove->next, &varCount, &nagCount, 0);
-    if (err != OK) { return err; }
+    auto [varCount, nagCount] = encodeMovelist(markComments, FirstMove, dest);
 
     // Now do the comments
-    uint commentCount = 0;
-    
-    err = encodeComments (buf, FirstMove, &commentCount);
+    encodeComments(markComments, FirstMove, dest);
 
-    // Set the fields in the IndexEntry:
-    if (ie != NULL) {
-        ie->SetDate (Date);
-        ie->SetEventDate (EventDate);
-        ie->SetResult (Result);
-        ie->SetEcoCode (EcoCode);
-        ie->SetWhiteElo (WhiteElo);
-        ie->SetBlackElo (BlackElo);
-        ie->SetWhiteRatingType (WhiteRatingType);
-        ie->SetBlackRatingType (BlackRatingType);
-
-        ie->clearFlags();
-        ie->SetStartFlag (HasNonStandardStart());
-        ie->SetCommentCount (commentCount);
-        ie->SetVariationCount (varCount);
-        ie->SetNagCount (nagCount);
-        ie->SetFlag(IndexEntry::StrToFlagMask(ScidFlags), true);
-
-        std::copy_n(homePawnList, sizeof(homePawnList), ie->GetHomePawnData());
-        // Set other data updated by MakeHomePawnList():
-        ie->SetPromotionsFlag (PromotionsFlag);
-        ie->SetUnderPromoFlag (UnderPromosFlag);
-        ie->SetNumHalfMoves (NumHalfMoves);
-        ASSERT(AtEnd());
-        ie->SetFinalMatSig(matsig_Make(CurrentPos->GetMaterial()));
-
-        // Find the longest matching stored line for this game:
-        uint storedLineCode = 0;
-        if (!HasNonStandardStart()) {
-            uint longestMatch = 0;
-            for (uint i = 1; i < StoredLine::count(); i++) {
-                moveT* gameMove = FirstMove->next;
-                uint matchLength = 0;
-                FullMove m = StoredLine::getMove(i, matchLength);
-                while (m) {
-                    if (gameMove->marker == END_MARKER
-                        ||  gameMove->moveData.from != m.getFrom()
-                        ||  gameMove->moveData.to != m.getTo())
-                    {
-                        matchLength = 0; break;
-                    }
-                    gameMove = gameMove->next;
-                    m = StoredLine::getMove(i, ++matchLength);
-                }
-                if (matchLength > longestMatch) {
-                    longestMatch = matchLength;
-                    storedLineCode = i;
-                }
-            }
-        }
-        ASSERT(storedLineCode == static_cast<byte>(storedLineCode));
-        ie->SetStoredLineCode(static_cast<byte>(storedLineCode));
-    }
-    
-    // as each game entry length is coded on 17 bits, and game must fit in a block
-    // return an error if there is an overflow
-    if (buf->GetByteCount() >= MAX_GAME_LENGTH) {
-      err = ERROR_GameFull;
-    }
+    ie.SetCommentCount(commentCount);
+    ie.SetVariationCount(varCount);
+    ie.SetNagCount(nagCount);
 
-    return err;
+    return {ie, tags};
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3438,64 +2970,14 @@ Game::Encode (ByteBuffer * buf, IndexEnt
 //      moves have been decoded. Returns ERROR_Game if some corruption was
 //      detected.
 //
-errorT
-Game::DecodeNextMove (ByteBuffer * buf, simpleMoveT * sm)
-{
-    ASSERT (buf != NULL);
-    errorT err;
-    byte b;
-    while (1) {
-        b = buf->GetByte ();
-        if (buf->Status() != OK) { return ERROR_Game; }
-        switch (b) {
-        case ENCODE_NAG:
-            // We ignore NAGS but have to read it from the buffer
-            buf->GetByte();
-            break;
+errorT Game::DecodeNextMove(ByteBuffer* buf, simpleMoveT& sm) {
+	ASSERT(buf != NULL);
 
-        case ENCODE_COMMENT:  // We also ignore comments
-            break;
-
-        case ENCODE_START_MARKER:
-            // Find the end of this variation and its children
-            uint nestCount;
-            nestCount= 1;
-            while (nestCount > 0) {
-                b = buf->GetByte();
-                if (buf->Status() != OK) { return ERROR_Game; }
-                if (b == ENCODE_NAG) { buf->GetByte(); }
-                else if (b == ENCODE_START_MARKER) { nestCount++; }
-                else if (b == ENCODE_END_MARKER) { nestCount--; }
-                else if (b == ENCODE_END_GAME) {
-                    // Open var at end of game: should never happen!
-                    return ERROR_Game;
-                }
-            }
-            break;
-
-        case ENCODE_END_MARKER:  // End marker in main game: error!
-            return ERROR_Game;
-
-        case ENCODE_END_GAME:  // We reached the end of the game:
-            return ERROR_EndOfMoveList;
-
-        default:  // It's a move in the game; decode it:
-            simpleMoveT tempMove;
-            if (!sm) { sm = &tempMove; }
-            err = decodeMove (buf, sm, b, currentPos());
-            if (err != OK)  { return err; }
-            if (KeepDecodedMoves) {
-                AddMove(sm);
-            } else {
-                CurrentPos->DoSimpleMove (sm);
-            }
-            return OK;
-        }
-    }
+	auto [err, val] = buf->nextLineMove();
+	if (err)
+		return err;
 
-    // We never reach here:
-    ASSERT(0);
-    return ERROR_Game;
+	return decodeMove(buf, &sm, val, currentPos());
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3505,60 +2987,63 @@ Game::DecodeNextMove (ByteBuffer * buf,
 //      DecodeNextMove() can be called to decode each successive
 //      mainline move.
 //
-errorT Game::DecodeStart(ByteBuffer* buf, bool decodeTags) {
+errorT Game::DecodeSkipTags(ByteBuffer* buf) {
     ASSERT(buf != NULL);
-    errorT err = buf->Status();
-    if (err != OK)
-        return err;
 
-    err = decodeTags ? DecodeTags(buf, true) : skipTags(buf);
+    Clear();
+    errorT err = buf->decodeTags([](auto, auto) {});
     if (err != OK)
         return err;
 
-    // Now the flags:
-    byte flags = buf->GetByte();
-    if (flags & 2) { PromotionsFlag = true; }
-
-    // Now decode the startBoard, if there is one.
-    if (flags & 1) {
-        char * tempStr;
-        buf->GetTerminatedString (&tempStr);
-        if ((err = buf->Status()) != OK)
-            return err;
-
-        StartPos = std::make_unique<Position>();
-        err = StartPos->ReadFromFEN (tempStr);
-        if (err != OK) {
-            StartPos = nullptr;
-            return err;
-        }
-        *CurrentPos = *StartPos;
-    }
+    const auto [err_startpos, fen] = buf->decodeStartBoard();
+    if (err_startpos)
+        return err_startpos;
 
-    return err;
+    if (fen)
+        return SetStartFen(fen);
+
+    return OK;
+}
+
+errorT Game::DecodeMovesOnly(ByteBuffer& buf) {
+	if (errorT err = DecodeSkipTags(&buf))
+		return err;
+
+	std::vector<moveT*> comment_marks;
+	return DecodeVariation(buf, comment_marks);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Game::Decode():
 //      Decodes a game from its on-disk representation in a bytebuffer.
 //      Decodes all the information: comments, variations, non-standard
-//      tags, etc, or selectively can ignore comments and/or tags for
-//      speed if the argument "flags" indicates.
+//      tags, etc.
 //
-errorT
-Game::Decode (ByteBuffer * buf, byte flags)
-{
-    ASSERT (buf != NULL);
-
+errorT Game::Decode(IndexEntry const& ie, TagRoster const& tags, ByteBuffer buf) {
     Clear();
-    errorT err = DecodeStart(buf, flags & GAME_DECODE_TAGS);
+    LoadStandardTags(ie, tags);
+
+    errorT err = buf.decodeTags([&](const auto& tag, const auto& value) {
+        accessTagValue(tag.data(), tag.size()).assign(value);
+    });
+    if (err)
+        return err;
+
+    const auto [err_startpos, fen] = buf.decodeStartBoard();
+    if (err_startpos)
+        return err_startpos;
+
+    if (fen)
+        err = SetStartFen(fen);
+
+    std::vector<moveT*> comment_marks;
     if (err == OK)
-        err = DecodeVariation (buf, flags, 0);
+        err = DecodeVariation(buf, comment_marks);
 
-    if (err == OK && (flags & GAME_DECODE_COMMENTS))
-        err = decodeComments (buf, FirstMove);
+    if (err == OK)
+        err = decodeComments(buf, FirstMove, comment_marks);
 
-    return (err != OK) ? err : buf->Status();
+    return err;
 }
 
 //////////////////////////////////////////////////////////////////////
diff -pruN 1:4.7.0+dfsg1-2/src/game.h 1:4.7.4+dfsg1-2/src/game.h
--- 1:4.7.0+dfsg1-2/src/game.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/game.h	2022-07-09 05:14:42.000000000 +0000
@@ -119,14 +119,8 @@ struct patternT
     rankT      rankMatch;   // RANK_1 .. RANK_8 or NO_RANK
     fyleT      fyleMatch;   // A_FYLE .. H_FYLE or NO_FYLE
     byte       flag;        // 0 means this pattern must NOT occur.
-    patternT * next;
 };
 
-#define GAME_DECODE_NONE 0
-#define GAME_DECODE_TAGS 1
-#define GAME_DECODE_COMMENTS 2
-#define GAME_DECODE_ALL 3
-
 enum gameExactMatchT {
     GAME_EXACT_MATCH_Exact = 0,
     GAME_EXACT_MATCH_Pawns,
@@ -159,8 +153,6 @@ enum gameFormatT {
 void  game_printNag (byte nag, char * str, bool asSymbol, gameFormatT format);
 byte game_parseNag(std::pair<const char*, const char*> strview);
 
-uint strGetRatingType (const char * name);
-
 //////////////////////////////////////////////////////////////////////
 //  Game:  Class Definition
 
@@ -193,12 +185,6 @@ class Game {
     ushort      NumHalfMoves; // Total half moves in the main line.
 
     // TODO: The following variables should not be part of this class.
-    bool        PromotionsFlag;   // Used by MaterialMatch
-    bool        KeepDecodedMoves; // Used by MaterialMatch end ExactMatch
-
-    eloT        WhiteEstimateElo;
-    eloT        BlackEstimateElo;
-
     uint        NumMovesPrinted; // Used in recursive WriteMoveList method.
     uint        PgnStyle;        // see PGN_STYLE macros above.
     gameFormatT PgnFormat;       // see PGN_FORMAT macros above.
@@ -209,8 +195,7 @@ private:
     moveT* allocMove();
     moveT* NewMove(markerT marker);
     void ClearMoves();
-    bool MakeHomePawnList(byte* pbPawnList);
-    errorT DecodeVariation(ByteBuffer* buf, byte flags, uint level);
+    errorT DecodeVariation(ByteBuffer& buf, std::vector<moveT*>& comment_marks);
     errorT WritePGN(TextBuffer* tb);
 
     /**
@@ -238,7 +223,24 @@ public:
 
     /// Setup the start position from a FEN string and remove all the moves.
     /// If the FEN is invalid the game is not changed.
-    errorT SetStartFen(const char* fenStr);
+    errorT SetStartFen(const char* fenStr) {
+        auto pos = std::make_unique<Position>();
+        if (auto err = pos->ReadFromFEN(fenStr))
+            return err;
+
+        SetStartPos(std::move(pos));
+        return OK;
+    }
+
+    /// Set a new start position and remove all the moves.
+    void SetStartPos(Position const& pos) {
+        return SetStartPos(std::make_unique<Position>(pos));
+    }
+    void SetStartPos(std::unique_ptr<Position> pos) {
+        ClearMoves();
+        StartPos = std::move(pos);
+        *CurrentPos = *StartPos;
+    }
 
     void SetScidFlags(const char* s, size_t len) {
         constexpr size_t size = sizeof(ScidFlags) / sizeof(*ScidFlags);
@@ -252,7 +254,7 @@ public:
     //////////////////////////////////////////////////////////////
     // Functions to add or delete moves:
     //
-    errorT AddMove(const simpleMoveT* sm);
+    errorT AddMove(simpleMoveT const& sm);
     errorT AddVariation();
     errorT DeleteVariation();
     errorT FirstVariation();
@@ -271,6 +273,8 @@ public:
     errorT MoveForwardInPGN();
     errorT MoveToLocationInPGN(unsigned stopLocation);
     void MoveToStart();
+    /// Move to the end of the main line.
+    void MoveToEnd();
     void MoveToPly(int hmNumber) { // Move to a specified
         MoveToStart();             // mainline ply in the game.
         for (int i = 0; i < hmNumber; ++i)
@@ -292,6 +296,8 @@ public:
     Position* GetCurrentPos() { // Deprecated, use the const version
         return CurrentPos.get();
     }
+    /// @return an "UCI position" string that leads to the current position
+    std::string currentPosUCI() const;
     simpleMoveT* GetCurrentMove() { // Deprecated
         return CurrentMove->endMarker() ? nullptr : &CurrentMove->moveData;
     }
@@ -328,6 +334,7 @@ public:
     bool AtVarEnd() const { return CurrentMove->endMarker(); }
     bool AtStart() const { return (VarDepth == 0 && AtVarStart()); }
     bool AtEnd() const { return (VarDepth == 0 && AtVarEnd()); }
+    bool AtEmptyVar() const { return VarDepth != 0 && AtVarStart() && AtVarEnd(); }
 
     //////////////////////////////////////////////////////////////
     // Functions that get/set information about the last/next move.
@@ -370,13 +377,12 @@ public:
     // Functions that get/set the tag pairs:
     //
     void AddPgnTag(const char* tag, const char* value);
-    bool RemoveExtraTag(const char* tag);
     const char* FindExtraTag(const char* tag) const;
     std::string& accessTagValue(const char* tag, size_t tagLen);
     const decltype(extraTags_) & GetExtraTags() const { return extraTags_; }
     void ClearExtraTags() { extraTags_.clear(); }
 
-    errorT LoadStandardTags(const IndexEntry* ie, const NameBase* nb);
+    void LoadStandardTags(IndexEntry const& ie, TagRoster const& tags);
 
     void     SetEventStr (const char * str) { EventStr = str; }
     void     SetSiteStr  (const char * str) { SiteStr  = str; }
@@ -388,8 +394,8 @@ public:
     void     SetResult (resultT res) { Result = res; }
     void     SetWhiteElo (eloT elo)  { WhiteElo = elo; }
     void     SetBlackElo (eloT elo)  { BlackElo = elo; }
-    void     SetWhiteRatingType (byte b) { WhiteRatingType = b; }
-    void     SetBlackRatingType (byte b) { BlackRatingType = b; }
+    void     SetWhiteRatingType (byte b) { WhiteRatingType = b > 7 ? 0 : b; }
+    void     SetBlackRatingType (byte b) { BlackRatingType = b > 7 ? 0 : b; }
     int setRating(colorT col, const char* ratingType, size_t ratingTypeLen,
                   std::pair<const char*, const char*> rating);
     void     SetEco (ecoT eco)       { EcoCode = eco; }
@@ -403,15 +409,12 @@ public:
     resultT  GetResult ()      const { return Result; }
     eloT     GetWhiteElo ()    const { return WhiteElo; }
     eloT     GetBlackElo ()    const { return BlackElo; }
-    eloT     GetWhiteEstimateElo() const { return WhiteEstimateElo; }
-    eloT     GetBlackEstimateElo() const { return BlackEstimateElo; }
     byte     GetWhiteRatingType () const { return WhiteRatingType; }
     byte     GetBlackRatingType () const { return BlackRatingType; }
     ecoT     GetEco ()         const { return EcoCode; }
     eloT     GetAverageElo ();
 
     // PGN conversion
-    bool      CommentEmpty ( const char * comment);
     void      WriteComment (TextBuffer * tb, const char * preStr,
                             const char * comment, const char * postStr);
     errorT    WriteMoveList(TextBuffer* tb, moveT* oldCurrentMove,
@@ -443,28 +446,19 @@ public:
 
     errorT    GetPartialMoveList (DString * str, uint plyCount);
 
-    bool      MaterialMatch (ByteBuffer * buf, byte * min, byte * max,
-                             patternT * pattern, int minPly, int maxPly,
-                             int matchLength,
-                             bool oppBishops, bool sameBishops,
-                             int minDiff, int maxDiff);
-    bool      ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm,
-                          gameExactMatchT searchType, bool * neverMatch);
+    bool MaterialMatch(bool PromotionsFlag, ByteBuffer& buf, byte* min,
+                       byte* max, patternT* ptn, size_t ptn_size, int minPly,
+                       int maxPly, int matchLength, bool oppBishops,
+                       bool sameBishops, int minDiff, int maxDiff);
+    bool      ExactMatch (Position * pos, ByteBuffer * buf,
+                          gameExactMatchT searchType);
     bool      VarExactMatch (Position * searchPos, gameExactMatchT searchType);
-    inline bool ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm)
-      { return ExactMatch (pos, buf, sm, GAME_EXACT_MATCH_Exact, NULL); }
-    inline bool ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm,
-                            bool * neverMatch)
-      { return ExactMatch (pos, buf, sm, GAME_EXACT_MATCH_Exact, neverMatch); }
-    inline bool ExactMatch (Position * pos, ByteBuffer * buf, simpleMoveT * sm,
-                            gameExactMatchT searchType)
-      { return ExactMatch (pos, buf, sm, searchType, NULL); }
-
-    errorT    Encode (ByteBuffer * buf, IndexEntry * ie);
-    errorT    DecodeStart(ByteBuffer* buf, bool decodeTags = false);
-    errorT    DecodeNextMove (ByteBuffer * buf, simpleMoveT * sm);
-    errorT    Decode (ByteBuffer * buf, byte flags);
-    errorT    DecodeTags (ByteBuffer * buf, bool storeTags);
+
+    std::pair<IndexEntry, TagRoster> Encode(std::vector<byte>& dest) const;
+    errorT    DecodeSkipTags(ByteBuffer* buf);
+    errorT    DecodeNextMove (ByteBuffer * buf, simpleMoveT& sm);
+    errorT    Decode(IndexEntry const& ie, TagRoster const& tags, ByteBuffer buf);
+    errorT    DecodeMovesOnly(ByteBuffer& buf);
 
     Game* clone();
 };
diff -pruN 1:4.7.0+dfsg1-2/src/gameview.h 1:4.7.4+dfsg1-2/src/gameview.h
--- 1:4.7.0+dfsg1-2/src/gameview.h	1970-01-01 00:00:00.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/gameview.h	2022-07-09 05:14:42.000000000 +0000
@@ -0,0 +1,562 @@
+/*
+* Copyright (C) 2013-2015  Fulvio Benini
+
+* This file is part of Scid (Shane's Chess Information Database).
+*
+* Scid is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation.
+*
+* Scid is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with Scid.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "bytebuf.h"
+#include "common.h"
+#include "fullmove.h"
+#include "movegen.h"
+#include "position.h"
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+#include <string>
+
+/// Store the number of pieces for each type and color.
+class MaterialCount {
+	int8_t n_[2][8] = {};
+
+public:
+	/// Add one piece.
+	void incr(colorT color, pieceT piece_type) {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(piece_type > 0 && piece_type < 8);
+
+		++n_[color][0];
+		++n_[color][piece_type];
+	}
+
+	/// Subtract one piece.
+	void decr(colorT color, pieceT piece_type) {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(piece_type > 0 && piece_type < 8);
+
+		--n_[color][0];
+		--n_[color][piece_type];
+	}
+
+	/// Return the total number of pieces of the specified color.
+	int8_t count(colorT color) const {
+		ASSERT(color == 0 || color == 1);
+
+		return n_[color][0];
+	}
+
+	/// Return the number of pieces of the specified color and type.
+	int8_t count(colorT color, pieceT piece_type) const {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(piece_type > 0 && piece_type < 8);
+
+		return n_[color][piece_type];
+	}
+
+	bool operator==(const MaterialCount& b) const {
+		const int8_t* a = n_[0];
+		const int8_t* b_ptr = b.n_[0];
+		return std::equal(a, a + 16, b_ptr);
+	}
+
+	bool operator!=(const MaterialCount& b) const { return !operator==(b); }
+};
+
+/// Store the type and position of the pieces compatibly with the SCID4 coding.
+class PieceList {
+	struct {
+		squareT sq;
+		pieceT piece_type;
+	} pieces_[2][16];
+
+public:
+	/// SCID4 encoded games must use index 0 for kings.
+	int8_t getKingIdx() const { return 0; }
+
+	/// Return the type of the piece with index @e idx
+	pieceT getPieceType(colorT color, int idx) const {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(idx >= 0 && idx < 16);
+
+		return pieces_[color][idx].piece_type;
+	}
+
+	/// Return the square position of the piece with index @e idx
+	squareT getSquare(colorT color, int idx) const {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(idx >= 0 && idx < 16);
+
+		return pieces_[color][idx].sq;
+	}
+
+	/// Change the square position of the piece with index @e idx
+	void changeSq(colorT color, int idx, squareT to) {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(idx >= 0 && idx < 16);
+
+		pieces_[color][idx].sq = to;
+	}
+
+	/// Change the type of the piece with index @e idx
+	void promote(colorT color, int idx, pieceT piece_type) {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(idx >= 0 && idx < 16);
+
+		pieces_[color][idx].piece_type = piece_type;
+	}
+
+	/// Remove the piece with index @e removed_idx.
+	/// Piece's indexes are important for decoding SCID4 moves:  when a piece is
+	/// removed it's index is used by the last valid index @e lastvalid_idx.
+	/// Return the square of the new piece with index @e removed_idx.
+	squareT remove(colorT color, int removed_idx, int lastvalid_idx) {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(removed_idx >= 0 && removed_idx < 16);
+		ASSERT(lastvalid_idx >= 0 && lastvalid_idx < 16);
+
+		pieces_[color][removed_idx] = pieces_[color][lastvalid_idx];
+		return pieces_[color][lastvalid_idx].sq;
+	}
+
+	/// Set the type and square of the piece with index @e idx
+	void set(colorT color, int idx, squareT sq, pieceT piece_type) {
+		ASSERT(color == 0 || color == 1);
+		ASSERT(idx >= 0 && idx < 16);
+		ASSERT(piece_type != KING || idx == getKingIdx());
+
+		pieces_[color][idx].sq = sq;
+		pieces_[color][idx].piece_type = piece_type;
+	}
+};
+
+class FastBoard {
+	uint8_t board_[64];
+	MaterialCount mt_;
+	PieceList pieces_;
+	uint8_t castlingRook_[2][2]; // [WHITE|BLACK][long|short] the idx of the
+	                             // rooks that can castle. 0 if none
+
+	enum { EMPTY_SQ_ = 0xFF };
+
+public:
+	explicit FastBoard(const Position& pos) { Init(pos); }
+
+	static FastBoard stdStart() {
+		static const auto std_start = FastBoard(Position::getStdStart());
+		return std_start;
+	}
+
+	void Init(const Position& pos) {
+		std::fill_n(board_, 64, EMPTY_SQ_);
+		std::fill_n(*castlingRook_, 4, 0);
+
+		for (auto color : {WHITE, BLACK}) {
+			const auto pos_count = pos.GetCount(color);
+			const auto pos_list = pos.GetList(color);
+			for (uint8_t idx = 0; idx < 16; ++idx) {
+				if (idx < pos_count) {
+					const squareT sq = pos_list[idx];
+					const pieceT piece_type = piece_Type(pos.GetPiece(sq));
+					pieces_.set(color, idx, sq, piece_type);
+					board_[sq] = idx;
+					mt_.incr(color, piece_type);
+
+					if (piece_type == ROOK &&
+					    square_Rank(sq) == rank_Relative(color, RANK_1)) {
+						auto oldIdx = castlingRook_[color][0];
+						if (!oldIdx || sq < pieces_.getSquare(color, oldIdx))
+							castlingRook_[color][0] = idx;
+
+						oldIdx = castlingRook_[color][1];
+						if (!oldIdx || sq > pieces_.getSquare(color, oldIdx))
+							castlingRook_[color][1] = idx;
+					}
+				} else {
+					pieces_.set(color, idx, 0, INVALID_PIECE);
+				}
+			}
+		}
+	}
+
+	bool isEqual(const pieceT* board, const MaterialCount& mt_count) const {
+		if (mt_ != mt_count)
+			return false;
+
+		for (int idx = 0, n = mt_.count(WHITE); idx < n; ++idx) {
+			const auto sq = pieces_.getSquare(WHITE, idx);
+			const auto pt = pieces_.getPieceType(WHITE, idx);
+			if (board[sq] != piece_Make(WHITE, pt))
+				return false;
+		}
+		for (int idx = 0, n = mt_.count(BLACK); idx < n; ++idx) {
+			const auto sq = pieces_.getSquare(BLACK, idx);
+			const auto pt = pieces_.getPieceType(BLACK, idx);
+			if (board[sq] != piece_Make(BLACK, pt))
+				return false;
+		}
+		return true;
+	}
+
+	const MaterialCount& materialCount() const { return mt_; }
+
+	squareT getSquare(colorT color, int idx) const {
+		return pieces_.getSquare(color, idx);
+	}
+
+	pieceT getPiece(colorT color, int idx) const {
+		return pieces_.getPieceType(color, idx);
+	}
+
+	/// The king and the rook are moved to the castle squares (even if they were
+	/// no longer in their starting squares).
+	/// @returns the previous position of the rook on success.
+	///          On error returns the king's square (no piece is moved).
+	template <colorT color> squareT castle(bool king_side) {
+		const squareT king_to = king_side ? square_Relative(color, G1)
+		                                  : square_Relative(color, C1);
+		const squareT rook_to = king_side ? square_Relative(color, F1)
+		                                  : square_Relative(color, D1);
+		const auto king_idx = pieces_.getKingIdx();
+		const squareT king_from = pieces_.getSquare(color, king_idx);
+		const auto rook_idx = castlingRook_[color][king_side ? 1 : 0];
+		const squareT rook_from = pieces_.getSquare(color, rook_idx);
+
+		if (pieces_.getPieceType(color, rook_idx) != ROOK)
+			return king_from; // No rook or captured
+
+		pieces_.changeSq(color, rook_idx, rook_to);
+		pieces_.changeSq(color, king_idx, king_to);
+		board_[rook_from] = EMPTY_SQ_;
+		board_[king_from] = EMPTY_SQ_;
+		board_[rook_to] = rook_idx;
+		board_[king_to] = king_idx;
+		return rook_from;
+	}
+
+	template <colorT color> pieceT move(int idx, squareT to, pieceT promo) {
+		if (promo != INVALID_PIECE) {
+			pieces_.promote(color, idx, promo);
+			mt_.incr(color, promo);
+			mt_.decr(color, PAWN);
+		}
+		const auto from = pieces_.getSquare(color, idx);
+		board_[from] = EMPTY_SQ_;
+		pieces_.changeSq(color, idx, to);
+		return remove<1 - color>(to, idx);
+	}
+
+	template <colorT color> pieceT remove(squareT sq, int newIdx = EMPTY_SQ_) {
+		ASSERT(static_cast<uint8_t>(newIdx) == newIdx);
+		const auto oldIdx = board_[sq];
+		board_[sq] = static_cast<uint8_t>(newIdx);
+		if (oldIdx == EMPTY_SQ_)
+			return INVALID_PIECE;
+
+		pieceT removed_pt = pieces_.getPieceType(color, oldIdx);
+		mt_.decr(color, removed_pt);
+		int lastvalid_idx = mt_.count(color);
+		if (oldIdx != lastvalid_idx) {
+			squareT moved_sq = pieces_.remove(color, oldIdx, lastvalid_idx);
+			board_[moved_sq] = oldIdx;
+
+			for (auto& cRook : castlingRook_[color]) {
+				if (cRook == lastvalid_idx)
+					cRook = oldIdx;
+			}
+		}
+		return removed_pt;
+	}
+
+	/**
+	 * Given the actual board position, find if the last move needs to be made
+	 * unambiguous and if it gives check (or TODO mate), and then sets the
+	 * appropriate bits in @e lastmove.
+	 * @param lastmove: the last move played.
+	 */
+	void fillSANInfo(FullMove& lastmove) const {
+		const auto lastFrom = lastmove.getFrom();
+		const auto lastTo = lastmove.getTo();
+		const auto lastCol = lastmove.getColor();
+		auto lastPt = lastmove.getPiece();
+
+		if (lastPt == PAWN) {
+			if (lastmove.isPromo())
+				lastPt = lastmove.getPromo();
+		} else if (mt_.count(lastCol, lastPt) > 1) {
+			int ambiguity = ambiguousMove(lastFrom, lastTo, lastCol, lastPt);
+			if (ambiguity)
+				lastmove.setAmbiguity(ambiguity != 5, ambiguity >= 5);
+		}
+
+		// Look for checks
+		ASSERT(mt_.count(WHITE) >= 1 && mt_.count(BLACK) >= 1);
+
+		auto isOccupied = [this](auto sq) { return board_[sq] != EMPTY_SQ_; };
+		const auto enemyKingSq = getKingSquare(color_Flip(lastCol));
+		bool direct_check = (lastPt != KING) &&
+		                    movegen::attack(lastTo, enemyKingSq, lastCol,
+		                                    lastPt, isOccupied);
+		if (direct_check || // Look for a discovered check
+		    find_attacker_slider(enemyKingSq, lastCol) >= 0) {
+			lastmove.setCheck();
+
+			// TODO: Find if it's mate:
+			// - it's not mate if the king can move to a safe square
+			// - it's mate if it's double check or the attacker cannot be
+			//   captured or blocked.
+		}
+	}
+
+private:
+	squareT getKingSquare(colorT color) const {
+		return pieces_.getSquare(color, pieces_.getKingIdx());
+	}
+
+	int ambiguousMove(squareT lastFrom, squareT lastTo, colorT lastCol,
+	                  pieceT lastPt) const {
+		int ambiguity = 0;
+
+		const squareT kingSq = getKingSquare(lastCol);
+		const colorT enemyCol = color_Flip(lastCol);
+		for (int i = 1, n = mt_.count(lastCol); i < n; i++) {
+			if (getPiece(lastCol, i) != lastPt)
+				continue; // Skip: different type
+
+			const squareT sq = getSquare(lastCol, i);
+			if (sq == lastTo)
+				continue; // Skip: this is the analyzed piece
+
+			auto isOccupied = [sq, lastFrom, this](auto square) {
+				if (square == sq)
+					return false;
+				if (square == lastFrom)
+					return true;
+				return board_[square] != EMPTY_SQ_;
+			};
+			if (!movegen::pseudo(sq, lastTo, lastCol, lastPt, isOccupied))
+				continue; // Skip: illegal move
+
+			const auto pin = movegen::opens_ray(sq, lastTo, kingSq, isOccupied);
+			if (pin.first != INVALID_PIECE) {
+				uint8_t idx = board_[pin.second];
+				if (idx != EMPTY_SQ_ && idx < mt_.count(enemyCol) &&
+				    getSquare(enemyCol, idx) == pin.second) {
+					const pieceT pt = getPiece(enemyCol, idx);
+					if (pt == QUEEN || pt == pin.first)
+						continue; // Skip: pinned piece
+				}
+			}
+
+			// Ambiguity:
+			// 1 (0001) --> need from-file (preferred) or from-rank
+			// 3 (0011) --> need from-file
+			// 5 (0101) --> need from-rank
+			// 7 (0111) --> need both from-file and from-rank
+			ambiguity |= 1;
+			if (square_Rank(lastFrom) == square_Rank(sq)) {
+				ambiguity |= 2; // 0b0010
+			} else if (square_Fyle(lastFrom) == square_Fyle(sq)) {
+				ambiguity |= 4; // 0b0100
+			}
+		}
+
+		return ambiguity;
+	}
+
+	int find_attacker_slider(squareT destSq, colorT color) const {
+		for (int idx = 0, n = mt_.count(color); idx < n; ++idx) {
+			const pieceT pt = getPiece(color, idx);
+			if (pt != QUEEN && pt != ROOK && pt != BISHOP)
+				continue;
+
+			auto isOccupied = [this](auto square) {
+				return board_[square] != EMPTY_SQ_;
+			};
+			const squareT sq = getSquare(color, idx);
+			if (movegen::attack_slider(sq, destSq, pt, isOccupied)) {
+				return idx;
+			}
+		}
+		return -1;
+	}
+};
+
+class GameView {
+	FastBoard board_;
+	ByteBuffer bbuf_;
+	colorT cToMove_;
+
+public:
+	explicit GameView(const ByteBuffer& bbuf)
+	    : board_(FastBoard::stdStart()), bbuf_(bbuf), cToMove_(WHITE) {}
+
+	GameView(const ByteBuffer& bbuf, const Position& startPos)
+	    : board_(startPos), bbuf_(bbuf), cToMove_(startPos.GetToMove()) {}
+
+	template <typename FuncT> void mainLine(FuncT fn) {
+		while (const auto move = (cToMove_ == WHITE)
+		                             ? DecodeNextMove<FullMove, WHITE>()
+		                             : DecodeNextMove<FullMove, BLACK>()) {
+			cToMove_ = 1 - cToMove_;
+			if (!fn(move))
+				return;
+		}
+	}
+
+	FullMove getMove(int ply_to_skip) {
+		for (int ply = 0; ply <= ply_to_skip; ply++, cToMove_ = 1 - cToMove_) {
+			auto move = (cToMove_ == WHITE) ? DecodeNextMove<FullMove, WHITE>()
+			                                : DecodeNextMove<FullMove, BLACK>();
+			if (!move)
+				break;
+
+			if (ply == ply_to_skip) {
+				board_.fillSANInfo(move);
+				cToMove_ = 1 - cToMove_;
+				return move;
+			}
+		}
+		return {};
+	}
+
+	std::string getMoveSAN(int ply_to_skip, int count) {
+		std::stringstream res;
+		const auto ply_num = (cToMove_ == WHITE) ? 2 : 3;
+		for (int ply = 0; ply < ply_to_skip + count;
+		     ply++, cToMove_ = 1 - cToMove_) {
+			FullMove move;
+			if (cToMove_ == WHITE) {
+				move = DecodeNextMove<FullMove, WHITE>();
+				if (!move)
+					break;
+				if (ply < ply_to_skip)
+					continue;
+				if (ply > ply_to_skip)
+					res << "  ";
+				res << (ply_num + ply) / 2 << ".";
+			} else {
+				move = DecodeNextMove<FullMove, BLACK>();
+				if (!move)
+					break;
+				if (ply < ply_to_skip)
+					continue;
+				if (ply == ply_to_skip)
+					res << (ply_num + ply) / 2 << "...";
+				else
+					res << " ";
+			}
+			board_.fillSANInfo(move);
+			res << move.getSAN();
+		}
+		return res.str();
+	}
+
+	template <colorT toMove>
+	int search(const byte* board, const MaterialCount& mt_count) {
+		int ply = 1;
+		auto less_material = [](const MaterialCount& a, const MaterialCount& b,
+		                        const colorT color, const auto move) {
+			if (!move)
+				return true;
+
+			const auto captured_pt = move.getCaptured();
+			if (captured_pt == INVALID_PIECE)
+				return false;
+
+			if (a.count(color) < b.count(color))
+				return true;
+
+			return a.count(color, PAWN) + a.count(color, captured_pt) <
+			       b.count(color, PAWN) + b.count(color, captured_pt);
+		};
+
+		if (cToMove_ != toMove) {
+			const auto move = DecodeNextMove<FullMove, 1 - toMove>();
+			if (!move)
+				return 0;
+			ply += 1;
+		}
+		for (;;) {
+			if (board_.isEqual(board, mt_count))
+				return ply;
+
+			{
+				const auto move = DecodeNextMove<FullMove, toMove>();
+				if (less_material(board_.materialCount(), mt_count, 1 - toMove,
+				                  move))
+					return 0;
+			}
+			{
+				const auto move = DecodeNextMove<FullMove, 1 - toMove>();
+				if (less_material(board_.materialCount(), mt_count, toMove,
+				                  move))
+					return 0;
+			}
+
+			ply += 2;
+		}
+		return 0;
+	}
+
+private:
+	template <typename TResult, colorT toMove> TResult DecodeNextMove() {
+		auto [err, val] = bbuf_.nextLineMove();
+		if (err)
+			return {};
+
+		return doPly<TResult, toMove>(val);
+	}
+
+	template <typename TResult, colorT toMove> TResult doPly(byte v) {
+		const auto idx_piece_moving = v >> 4;
+		const auto moving_piece = board_.getPiece(toMove, idx_piece_moving);
+		const auto from = board_.getSquare(toMove, idx_piece_moving);
+
+		auto [destSq, promo] = bbuf_.decodeMove(toMove, moving_piece, from, v);
+		if (destSq < 0 || destSq > 63)
+			return {}; // decode error
+
+		const auto to = static_cast<squareT>(destSq);
+		if (to == from) {
+			if (promo == INVALID_PIECE)
+				return {}; // decode error
+
+			if (promo == PAWN) // NULL MOVE
+				return TResult(toMove, 0, 0, KING);
+
+			// CASTLE
+			const auto rook_from = board_.castle<toMove>(promo == KING);
+			return (rook_from == from) ? TResult() // decode error
+			                           : TResult(toMove, from, rook_from);
+		}
+
+		bool enPassant = moving_piece == PAWN &&
+		                 square_Fyle(from) != square_Fyle(to);
+		pieceT captured = board_.move<toMove>(idx_piece_moving, to, promo);
+		TResult res(toMove, from, to, moving_piece);
+		if (promo != INVALID_PIECE)
+			res.setPromo(promo);
+		if (captured != INVALID_PIECE) {
+			res.setCapture(captured, false);
+		} else if (enPassant) {
+			squareT sq = (toMove == WHITE) ? to - 8 : to + 8;
+			captured = board_.remove<1 - toMove>(0x3F & sq);
+			res.setCapture(captured, true);
+		}
+		return res;
+	}
+};
diff -pruN 1:4.7.0+dfsg1-2/src/hash.h 1:4.7.4+dfsg1-2/src/hash.h
--- 1:4.7.0+dfsg1-2/src/hash.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/hash.h	2022-07-09 05:14:42.000000000 +0000
@@ -12,10 +12,12 @@
 //
 //////////////////////////////////////////////////////////////////////
 
+#pragma once
 
-#ifndef SCID_HASH_H
-#define SCID_HASH_H
-
+class HashVal {
+    unsigned hashVal_[16][64] = {};
+public:
+    constexpr HashVal() {
 // goodHashValues
 //   This is a table of 12 (pieces) * 64 (squares) = 768 pre-generated
 //   "good" 32-bit hash values, to be used for Zobrist hashing.
@@ -24,7 +26,7 @@
 //      (b) every value differs from every other value by at least 10
 //          bits and at most 32-10=22 bits.
 //
-const uint
+constexpr unsigned
 goodHashValues [12 * 64] = {
     0x039B11BFu,0x4890D6A4u,0x37539B8Au,0xA7E3A104u,0x8B263019u,0xEB71AE0Bu,
     0x87099341u,0x32EF9CD4u,0x698B8BC1u,0x823FEACEu,0x8E607A5Au,0x7241E921u,
@@ -156,8 +158,45 @@ goodHashValues [12 * 64] = {
     0x8873E4B5u,0x3221744Au,0xEB3B4FBDu,0xB17E5F84u,0xEFD0E469u,0xD08C2EC3u
 };
 
-#endif // SCID_HASH_H
+        // Fill in the hash values for each valid [piece][square] index,
+        // using a table of pre-generated good values:
+        const unsigned int * hash = goodHashValues;
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[WK][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[WQ][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[WR][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[WB][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[WN][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[WP][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[BK][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[BQ][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[BR][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[BB][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[BN][sq] = *hash; hash++; }
+        for (auto sq=A1; sq <= H8; sq++) { hashVal_[BP][sq] = *hash; hash++; }
 
-//////////////////////////////////////////////////////////////////////
-//  EOF: hash.h
-//////////////////////////////////////////////////////////////////////
+    }
+
+    constexpr void operator()(unsigned& hash, unsigned char p, unsigned char sq) const {
+        hash ^= hashVal_[p][sq];
+    };
+};
+constexpr inline auto HASH = HashVal();
+inline auto const& UNHASH = HASH;
+
+constexpr inline unsigned stdStartPawnHash = [] {
+    unsigned h = 0;
+    HASH (h,WP,A2);  HASH (h,WP,B2);  HASH (h,WP,C2);  HASH (h,WP,D2);
+    HASH (h,WP,E2);  HASH (h,WP,F2);  HASH (h,WP,G2);  HASH (h,WP,H2);
+    HASH (h,BP,A7);  HASH (h,BP,B7);  HASH (h,BP,C7);  HASH (h,BP,D7);
+    HASH (h,BP,E7);  HASH (h,BP,F7);  HASH (h,BP,G7);  HASH (h,BP,H7);
+    return h;
+}();
+
+constexpr inline unsigned stdStartHash = [] {
+    auto h = stdStartPawnHash;
+    HASH (h,WR,A1);  HASH (h,WN,B1);  HASH (h,WB,C1);  HASH (h,WQ,D1);
+    HASH (h,WK,E1);  HASH (h,WB,F1);  HASH (h,WN,G1);  HASH (h,WR,H1);
+    HASH (h,BR,A8);  HASH (h,BN,B8);  HASH (h,BB,C8);  HASH (h,BQ,D8);
+    HASH (h,BK,E8);  HASH (h,BB,F8);  HASH (h,BN,G8);  HASH (h,BR,H8);
+    return h;
+}();
diff -pruN 1:4.7.0+dfsg1-2/src/hfilter.h 1:4.7.4+dfsg1-2/src/hfilter.h
--- 1:4.7.0+dfsg1-2/src/hfilter.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/hfilter.h	2022-07-09 05:14:42.000000000 +0000
@@ -229,7 +229,7 @@ public: // Pointer interface
 	const HFilter& operator*() const { return *this; }
 
 public:
-	explicit HFilter(Filter* main = 0, const Filter* mask = 0)
+	explicit HFilter(Filter* main, const Filter* mask = 0)
 	    : main_(main), mask_(mask) {}
 
 	void clear() { return main_->Fill(0); }
@@ -237,15 +237,17 @@ public:
 	void insert_or_assign(gamenumT gnum, uint8_t ply) {
 		return main_->Set(gnum, ply + 1);
 	}
-	size_t size() const {
-		if (mask_ == 0)
+	gamenumT size() const {
+		if (!mask_ || mask_->Count() == mask_->Size())
 			return main_->Count();
 		if (main_->Count() == main_->Size())
 			return mask_->Count();
-		const_iterator::difference_type res = std::distance(begin(), end());
-		return static_cast<size_t>(res);
+		return static_cast<gamenumT>(std::distance(begin(), end()));
 	}
 
+	/// Returns the number of games included in the main filter
+	size_t mainSize() const { return main_->Count(); }
+
 	/* Convenience function, behave like:
 	 * for (gamenumT gnum = 0; gnum < scidBaseT::numGames(); gnum++)
 	 *		std:map::insert_or_assign(gnum, 0);
diff -pruN 1:4.7.0+dfsg1-2/src/index.cpp 1:4.7.4+dfsg1-2/src/index.cpp
--- 1:4.7.0+dfsg1-2/src/index.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/index.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,190 +0,0 @@
-/*
-* Copyright (c) 1999-2002  Shane Hudson
-* Copyright (c) 2006-2009  Pascal Georges
-* Copyright (C) 2014-2016  Fulvio Benini
-
-* This file is part of Scid (Shane's Chess Information Database).
-*
-* Scid is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation.
-*
-* Scid is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Scid.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "index.h"
-#include "namebase.h"
-#include <cstring>
-
-static constexpr char INDEX_MAGIC[8] = "Scid.si";
-
-void Index::Init ()
-{
-    Header.numGames  = 0;
-    Header.version   = SCID_VERSION;
-    Header.baseType = 0;
-    Header.autoLoad = 1;
-    Header.description[0] = 0;
-    std::memset(Header.customFlagDesc, 0, sizeof(Header.customFlagDesc));
-    Header.dirty_ = false;
-    FilePtr = NULL;
-    fileMode_ = FMODE_Memory;
-    nInvalidNameId_ = 0;
-    seqWrite_ = 0;
-    entries_.resize(0);
-}
-
-errorT Index::Clear ()
-{
-    errorT res = flush();
-    delete FilePtr;
-    Init();
-    return res;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Index::CreateIndexFile():
-//      Creates and opens a new empty index file.
-//
-errorT
-Index::Create(const char* filename)
-{
-    ASSERT(filename != 0);
-
-    Clear();
-    FilePtr = new Filebuf;
-    std::string fname = filename;
-    fname += INDEX_SUFFIX;
-    //Check that the file does not exists and then create it
-    if (FilePtr->Open(fname.c_str(), FMODE_ReadOnly) == OK ||
-        FilePtr->Open(fname.c_str(), FMODE_Create) != OK) {
-        delete FilePtr;
-        FilePtr = NULL;
-        return ERROR_FileOpen;
-    }
-    fileMode_ = FMODE_Both;
-    return WriteHeader();
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Index::Open():
-//      Open an index file and read the header.
-//
-errorT
-Index::Open (const char* filename, fileModeT fmode)
-{
-    ASSERT(filename != 0);
-
-    Clear();
-    if (fmode == FMODE_WriteOnly) return ERROR_FileMode;
-    FilePtr = new Filebuf;
-    std::string fname = filename;
-    fname += INDEX_SUFFIX;
-
-    if (FilePtr->Open (fname.c_str(), fmode) != OK) {
-        delete FilePtr;
-        FilePtr = NULL;
-        return ERROR_FileOpen;
-    }
-
-    char magic[8];
-    FilePtr->sgetn(magic, 8);
-    if (!std::equal(std::begin(magic), std::end(magic), std::begin(INDEX_MAGIC),
-                    std::end(INDEX_MAGIC))) {
-        delete FilePtr;
-        FilePtr = NULL;
-        return ERROR_BadMagic;
-    }
-
-    Header.version = FilePtr->ReadTwoBytes ();
-    if (Header.version < SCID_OLDEST_VERSION || Header.version > SCID_VERSION) {
-        delete FilePtr;
-        FilePtr = NULL;
-        return ERROR_FileVersion;
-    }
-    if (Header.version != SCID_VERSION && fmode != FMODE_ReadOnly) {
-        //Old versions must be opened readonly
-        delete FilePtr;
-        FilePtr = NULL;
-        return ERROR_FileMode;
-    }
-
-    Header.baseType = FilePtr->ReadFourBytes ();
-    Header.numGames = FilePtr->ReadThreeBytes ();
-    Header.autoLoad = FilePtr->ReadThreeBytes ();
-    FilePtr->sgetn(Header.description, SCID_DESC_LENGTH + 1);
-    Header.description[SCID_DESC_LENGTH] = 0;
-    if (Header.version >= 400) {
-        for (uint i = 0 ; i < CUSTOM_FLAG_MAX ; i++ ) {
-            FilePtr->sgetn(Header.customFlagDesc[i], CUSTOM_FLAG_DESC_LENGTH + 1);
-            Header.customFlagDesc[i][CUSTOM_FLAG_DESC_LENGTH] = 0;
-        }
-    } 
-
-    fileMode_ = fmode;
-    return OK;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Index::WriteHeader():
-//      Write the header to the open index file.
-//
-errorT
-Index::WriteHeader ()
-{
-    ASSERT(FilePtr != NULL);
-    if (FilePtr->pubseekpos(0) != std::streampos(0)) return ERROR_FileWrite;
-
-    seqWrite_ = 0;
-    std::streamsize n = 0;
-    n += FilePtr->sputn(INDEX_MAGIC, 8);
-    n += FilePtr->WriteTwoBytes (Header.version);
-    n += FilePtr->WriteFourBytes (Header.baseType);
-    n += FilePtr->WriteThreeBytes (Header.numGames);
-    n += FilePtr->WriteThreeBytes (Header.autoLoad);
-    n += FilePtr->sputn(Header.description, SCID_DESC_LENGTH + 1);
-    for (size_t i = 0 ; i < CUSTOM_FLAG_MAX ; i++ ) {
-        n += FilePtr->sputn(Header.customFlagDesc[i], CUSTOM_FLAG_DESC_LENGTH + 1);
-    }
-    if (n != INDEX_HEADER_SIZE || FilePtr->pubsync() == -1) return ERROR_FileWrite;
-    Header.dirty_ = false;
-    return OK;
-}
-
-errorT Index::WriteEntry(const IndexEntry* ie, gamenumT idx)
-{
-    if (idx > Header.numGames) return ERROR_BadArg;
-    if (fileMode_ == FMODE_ReadOnly) { return ERROR_FileMode; }
-
-    if (idx == Header.numGames) {
-        entries_.push_back(*ie);
-        Header.numGames++;
-        Header.dirty_ = true;
-    } else {
-        IndexEntry* copyToMemory = &(entries_[idx]);
-        *copyToMemory = *ie;
-    }
-    if (FilePtr == NULL) return OK;
-
-    if ((seqWrite_ == 0) || (idx != seqWrite_ + 1)) {
-        std::streampos pos = INDEX_ENTRY_SIZE * idx + INDEX_HEADER_SIZE;
-        if (FilePtr->pubseekpos(pos) != pos) {
-            seqWrite_ = 0;
-            return ERROR_FileWrite;
-        }
-    }
-    errorT res = ie->Write (FilePtr, Header.version);
-    seqWrite_ = (res == OK) ? idx : 0;
-    return res;
-}
-
-//////////////////////////////////////////////////////////////////////
-//  EOF: index.cpp
-//////////////////////////////////////////////////////////////////////
-
diff -pruN 1:4.7.0+dfsg1-2/src/indexentry.h 1:4.7.4+dfsg1-2/src/indexentry.h
--- 1:4.7.0+dfsg1-2/src/indexentry.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/indexentry.h	2022-07-09 05:14:42.000000000 +0000
@@ -24,10 +24,9 @@
 #include "common.h"
 #include "date.h"
 #include "matsig.h"
-#include "namebase.h"
+#include <cstring> //memcmp
 
-// Length is encoded as 17bits uint
-#define MAX_GAME_LENGTH 131072
+using idNumberT = uint32_t; // Should be idNameT
 
 // HPSIG_SIZE = size of HomePawnData array in an IndexEntry.
 // It is nine bytes: the first byte contains the number of valid entries
@@ -38,10 +37,6 @@ const eloT MAX_ELO = 4000; // Since we s
 
 const byte CUSTOM_FLAG_MASK[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5 };
 
-// Total on-disk size per index entry: currently 47 bytes.
-const uint  INDEX_ENTRY_SIZE = 47;
-const uint  OLD_INDEX_ENTRY_SIZE = 46;
-
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class IndexEntry: one of these per game in the index file.
@@ -55,18 +50,19 @@ class IndexEntry {
     uint64_t offset_         : 46; // Start of gamefile record for this game.
     uint64_t gameDataSize_   : 18; // Length of gamefile record for this game.
 
-    uint64_t storedLineCode_ :  8;
-    uint64_t whiteID_        : 28;
-    uint64_t blackID_        : 28;
+    uint32_t nComments_      :  4;
+    uint32_t whiteID_        : 28;
+
+    uint32_t nVariations_    :  4;
+    uint32_t blackID_        : 28;
 
+    uint32_t nNags_          :  4;
     uint32_t eventID_        : 28;
-    uint32_t whiteEloType_   :  4;
 
-    uint32_t siteID_         : 28;
-    uint32_t blackEloType_   :  4;
+    uint32_t siteID_;
 
+    uint32_t variant_        :  4;
     uint32_t roundID_        : 28;
-    uint32_t result_         :  4;
 
     uint32_t whiteElo_       : 12;
     uint32_t date_           : 20;
@@ -77,22 +73,18 @@ class IndexEntry {
     uint32_t numHalfMoves_   : 10;
     uint32_t flags_          : 22;
 
+    uint32_t result_         :  2;
+    uint32_t whiteEloType_   :  3;
+    uint32_t blackEloType_   :  3;
     uint32_t finalMatSig_    : 24; // material of the final position in the game
-    uint32_t nVariations_    :  4;
-    uint32_t nComments_      :  4;
 
     uint16_t ECOcode_;
 
-    uint8_t  nNags_          :  4;
+    uint8_t  storedLineCode_;
 
     byte     HomePawnData [HPSIG_SIZE];  // homePawnSig data.
 
 public:
-    void Init() { std::memset(this, 0, sizeof(IndexEntry)); }
-
-    template <class T> errorT Write (T* file, versionT version) const;
-
-    // get functions
     uint64_t  GetOffset() const { return offset_; }
     uint32_t  GetLength() const { return gameDataSize_; }
     idNumberT GetWhite() const { return whiteID_; }
@@ -115,8 +107,23 @@ public:
     byte      GetStoredLineCode() const { return storedLineCode_; }
     ecoT      GetEcoCode() const { return ECOcode_; }
     bool      GetFlag(uint32_t mask) const { return (flags_ & mask) == mask; }
+    uint32_t  GetRawFlags() const { return flags_; }
+    uint16_t  GetRaw4bitsCounts() const {
+        uint16_t res = nVariations_ & 0x0F;
+        res |= static_cast<uint16_t>(nComments_ & 0x0F) << 4;
+        res |= static_cast<uint16_t>(nNags_ & 0x0F) << 8;
+        return res;
+    }
+
+    void setChessStd() { variant_ = 0; }
+    void setChess960() { variant_ = 1; }
+    bool isChessStd() const { return variant_ == 0; }
+
     const byte* GetHomePawnData() const { return HomePawnData; }
-    byte* GetHomePawnData() { return HomePawnData; }
+    void SetHomePawnData(byte hpCount, const byte hpVal[8]) {
+        HomePawnData[0] = hpCount; // First byte stores the count
+        std::copy_n(hpVal, 8, HomePawnData + 1);
+    }
 
     // set functions, assert that the value is not truncated.
     void SetOffset(uint64_t offset) {
@@ -222,32 +229,7 @@ public:
         return (col == BLACK) ? SetBlack(id) : SetWhite(id);
     }
 
-    const char* GetWhiteName (const NameBase* nb) const {
-        return nb->GetName (NAME_PLAYER, GetWhite()); 
-    }
-    const char* GetBlackName (const NameBase* nb) const {
-        return nb->GetName (NAME_PLAYER, GetBlack());
-    }
-    const char* GetEventName (const NameBase* nb) const {
-        return nb->GetName (NAME_EVENT, GetEvent());
-    }
-    const char* GetSiteName (const NameBase* nb) const {
-        return nb->GetName (NAME_SITE, GetSite());
-    }
-    const char* GetRoundName (const NameBase* nb) const {
-        return nb->GetName (NAME_ROUND, GetRound());
-    }
-    eloT GetWhiteElo (const NameBase* nb)  const {
-        eloT r = GetWhiteElo();
-        if (r == 0 && nb != 0) return nb->GetElo (GetWhite());
-        return r;
-    }
-    eloT GetBlackElo (const NameBase* nb) const {
-        eloT r = GetBlackElo();
-        if (r == 0 && nb != 0) return nb->GetElo (GetBlack());
-        return r;
-    }
-    byte   GetRating(const NameBase* nb) const;
+    byte   GetRating() const;
 
     bool GetStartFlag () const      { return GetFlag(1 << IDX_FLAG_START); }
     bool GetPromotionsFlag () const { return GetFlag(1 << IDX_FLAG_PROMO); }
@@ -267,6 +249,11 @@ public:
     void SetUnderPromoFlag (bool b) { SetFlag(1 << IDX_FLAG_UPROMO, b); }
     void SetDeleteFlag (bool b)     { SetFlag(1 << IDX_FLAG_DELETE, b); }
     void clearFlags() { return SetFlag(IDX_MASK_ALLFLAGS, false); }
+    bool equalExceptFlags(IndexEntry ie) const {
+        ie.flags_ = flags_;
+        static_assert(std::has_unique_object_representations_v<IndexEntry>);
+        return memcmp(this, &ie, sizeof(IndexEntry)) == 0;
+    }
 
     enum {
         // IndexEntry Flag types:
@@ -313,132 +300,16 @@ private:
 };
 
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// IndexEntry::Write():
-//      Writes a single index entry to an open index file.
-//      INDEX_ENTRY_SIZE must be updated
-template <class T> errorT IndexEntry::Write(T* file, versionT version) const {
-	if (version < 400) // Cannot write old-version index files:
-		return ERROR_FileVersion;
-
-	char buf[INDEX_ENTRY_SIZE];
-	char* buf_it = buf;
-	auto WriteOneByte = [&buf_it](uint8_t v) { *buf_it++ = v; };
-	auto WriteTwoBytes = [&WriteOneByte](uint16_t v) {
-		WriteOneByte(static_cast<uint8_t>(v >> 8));
-		WriteOneByte(static_cast<uint8_t>(v));
-	};
-	auto WriteFourBytes = [&WriteTwoBytes](uint32_t v) {
-		WriteTwoBytes(static_cast<uint16_t>(v >> 16));
-		WriteTwoBytes(static_cast<uint16_t>(v));
-	};
-
-	const IndexEntry* ie = this;
-
-	ASSERT(ie->GetOffset() < (1ULL << 32));
-	WriteFourBytes(static_cast<uint32_t>(ie->GetOffset()));
-
-	ASSERT(ie->GetLength() < (1ULL << 17));
-	WriteTwoBytes(static_cast<uint16_t>(ie->GetLength()));
-	uint8_t len_flags = static_cast<uint8_t>(ie->GetLength() >> 9) & 0x80;
-	len_flags |= static_cast<uint8_t>(ie->flags_ >> 16) & 0x3F;
-	WriteOneByte(len_flags);
-	WriteTwoBytes(static_cast<uint16_t>(ie->flags_));
-
-	// WhiteID and BlackID are 20-bit values, EventID and SiteID are
-	// 19-bit values, and RoundID is an 18-bit value.
-	// WhiteID high 4 bits = bits 4-7 of WhiteBlack_High.
-	// BlackID high 4 bits = bits 0-3 of WhiteBlack_High.
-	// EventID high 3 bits = bits 5-7 of EventSiteRnd_high.
-	// SiteID  high 3 bits = bits 2-4 of EventSiteRnd_high.
-	// RoundID high 2 bits = bits 0-1 of EventSiteRnd_high.
-	ASSERT(std::max(ie->GetWhite(), ie->GetBlack()) < (1ULL << 20));
-	uint32_t WhiteID_Low = ie->GetWhite();
-	uint32_t BlackID_Low = ie->GetBlack();
-	uint32_t WhiteBlack_High = (WhiteID_Low & 0x0F0000) >> 12;
-	WhiteBlack_High |= (BlackID_Low & 0x0F0000) >> 16;
-	WriteOneByte(static_cast<uint8_t>(WhiteBlack_High));
-	WriteTwoBytes(static_cast<uint16_t>(WhiteID_Low));
-	WriteTwoBytes(static_cast<uint16_t>(BlackID_Low));
-
-	ASSERT(std::max(ie->GetEvent(), ie->GetSite()) < (1ULL << 19));
-	ASSERT(ie->GetRound() < (1ULL << 18));
-	uint32_t EventID_Low = ie->GetEvent();
-	uint32_t SiteID_Low = ie->GetSite();
-	uint32_t RoundID_Low = ie->GetRound();
-	uint32_t EventSiteRnd_High = (EventID_Low & 0x070000) >> 11;
-	EventSiteRnd_High |= (SiteID_Low & 0x070000) >> 14;
-	EventSiteRnd_High |= (RoundID_Low & 0x030000) >> 16;
-	WriteOneByte(static_cast<uint8_t>(EventSiteRnd_High));
-	WriteTwoBytes(static_cast<uint16_t>(EventID_Low));
-	WriteTwoBytes(static_cast<uint16_t>(SiteID_Low));
-	WriteTwoBytes(static_cast<uint16_t>(RoundID_Low));
-
-	uint16_t varCounts = ie->nVariations_ & 0x0F;
-	varCounts |= static_cast<uint16_t>(ie->nComments_ & 0x0F) << 4;
-	varCounts |= static_cast<uint16_t>(ie->nNags_ & 0x0F) << 8;
-	varCounts |= static_cast<uint16_t>(ie->GetResult() & 0x0F) << 12;
-	WriteTwoBytes(varCounts);
-
-	WriteTwoBytes(ie->GetEcoCode());
-
-	// Due to a compact encoding format, the EventDate
-	// must be within a few years of the Date.
-	uint32_t date = ie->GetDate() & 0xFFFFF;
-	uint32_t edate = ie->GetEventDate();
-	uint32_t eyear = date_GetYear(edate);
-	uint32_t dyear = date_GetYear(date);
-	if ((eyear + 3) < dyear || eyear > (dyear + 3)) {
-		edate = ZERO_DATE;
-	} else {
-		eyear = (eyear + 4 - dyear) & 7;
-		edate = (eyear << 9) | (date_GetMonth(edate) << 5) | date_GetDay(edate);
-	}
-	WriteFourBytes((edate << 20) | date);
-
-	// Elo ratings and rating types: 2 bytes each.
-	uint16_t wElo = std::min(MAX_ELO, ie->GetWhiteElo());
-	wElo |= static_cast<uint16_t>(ie->GetWhiteRatingType()) << 12;
-	uint16_t bElo = std::min(MAX_ELO, ie->GetBlackElo());
-	bElo |= static_cast<uint16_t>(ie->GetBlackRatingType()) << 12;
-	WriteTwoBytes(wElo);
-	WriteTwoBytes(bElo);
-
-	ASSERT(ie->GetFinalMatSig() < (1ULL << 24));
-	ASSERT(ie->GetStoredLineCode() < (1ULL << 8));
-	uint32_t FinalMatSig = ie->GetFinalMatSig();
-	FinalMatSig |= static_cast<uint32_t>(ie->GetStoredLineCode()) << 24;
-	WriteFourBytes(FinalMatSig);
-
-	// The first byte of HomePawnData has high bits of the NumHalfMoves
-	// counter in its top two bits:
-	uint16_t nMoves = ie->GetNumHalfMoves();
-	ASSERT(nMoves < (1ULL << 10));
-	WriteOneByte(static_cast<uint8_t>(nMoves));
-	uint8_t pawnData0 = static_cast<uint8_t>(nMoves >> 8) << 6;
-
-	// Write the 9-byte homePawnData array:
-	const byte* pb = ie->GetHomePawnData();
-	pawnData0 |= *pb & 0x3F;
-	WriteOneByte(pawnData0);
-	std::copy(pb + 1, pb + HPSIG_SIZE, buf_it);
-
-	return file->sputn(buf, INDEX_ENTRY_SIZE) == INDEX_ENTRY_SIZE
-	           ? OK
-	           : ERROR_FileWrite;
-}
-
-inline byte IndexEntry::GetRating(const NameBase* nb) const {
+inline byte IndexEntry::GetRating() const {
     eloT welo = GetWhiteElo();
     eloT belo = GetBlackElo();
-    if (welo == 0) { welo = nb->GetElo (GetWhite()); }
-    if (belo == 0) { belo = nb->GetElo (GetBlack()); }
-    int rating = static_cast<int>(welo + belo) / 140;
+    auto rating = (welo != 0 && belo != 0) ? (welo + belo) / 140 : 0;
+    static_assert(std::is_signed_v<decltype(rating)>);
 
     // Bonus for comments or Nags
     if (GetCommentCount() > 2 || GetNagCount() > 2) {
         if (rating < 21) { // Missing elo
-            rating = 40;
+            rating = 38;
         } else {
             rating += 6;
         }
diff -pruN 1:4.7.0+dfsg1-2/src/index.h 1:4.7.4+dfsg1-2/src/index.h
--- 1:4.7.0+dfsg1-2/src/index.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/index.h	2022-07-09 05:14:42.000000000 +0000
@@ -23,34 +23,10 @@
 
 #include "common.h"
 #include "containers.h"
-#include "date.h"
 #include "indexentry.h"
-#include "filebuf.h"
-#include "hfilter.h"
-#include <array>
 #include <string>
 #include <vector>
-#include <cstring>
 
-class NameBase;
-
-//////////////////////////////////////////////////////////////////////
-//  Index:  Constants
-
-const char         INDEX_SUFFIX[]     = ".si4";
-const char         OLD_INDEX_SUFFIX[] = ".si3";
-
-// Descriptions can be up to 107 bytes long.
-const uint  SCID_DESC_LENGTH = 107;
-const uint  CUSTOM_FLAG_DESC_LENGTH = 8;
-const uint  CUSTOM_FLAG_MAX = 6;
-
-// Header on-disk size: magic=8, version=2, numGames=3, baseType=4, autoLoad=3
-// Description length = 111 bytes including trailing '\0'.
-// Custom flag desc length = 9 bytes including trailing '\0'.
-// So total is 128 bytes + 9*6 = 182 bytes for the whole header.
-const uint  INDEX_HEADER_SIZE = 8 + 2 + 3 + 4 + 3 + SCID_DESC_LENGTH + 1 + (CUSTOM_FLAG_DESC_LENGTH+1) * CUSTOM_FLAG_MAX;
-const uint  OLD_INDEX_HEADER_SIZE = INDEX_HEADER_SIZE - (CUSTOM_FLAG_DESC_LENGTH+1) * CUSTOM_FLAG_MAX;
 
 //////////////////////////////////////////////////////////////////////
 //  Index:  Class Definition
@@ -63,34 +39,14 @@ private:
     // CHUNKSHIFT is the base-2 logarithm of the number of index entries allocated as one chunk.
     // i.e 16 = 2^16 = 65536 (total size of one chunk: 65536*48 = 3MB)
     VectorChunked<IndexEntry, 16> entries_; // A two-level array of the entire index.
-    Filebuf*     FilePtr;       // filehandle for opened index file.
-    fileModeT    fileMode_;     // Mode: e.g. FILE_WRITEONLY
     int nInvalidNameId_;
-    gamenumT seqWrite_;
-
-    struct { // one at the start of the index file.
-        versionT    version;     // version number. 2 bytes.
-        uint        baseType;    // Type, e.g. tournament, theory, etc.
-        gamenumT    numGames;    // number of games in file.
-        gamenumT    autoLoad;    // game number to autoload: 0=none, 1=1st, >numGames=last
-        // description is a fixed-length string describing the database.
-        char        description [SCID_DESC_LENGTH + 1];
-        // short description (8 chars) for the CUSTOM_FLAG_MAX bits for CUSTOM flags
-        char        customFlagDesc [CUSTOM_FLAG_MAX][CUSTOM_FLAG_DESC_LENGTH+1] ;
-        bool        dirty_;      // If true, Header needs rewriting to disk.
-    } Header;
 
     friend class CodecSCID4;
 
 public:
     Index()  { Init(); }
-    Index(const Index&);
-    Index& operator=(const Index&);
-    ~Index() { Clear(); }
-
-    errorT Open(const char* filename, fileModeT fmode);
-    errorT Create(const char* filename);
-    errorT Close() { return Clear(); }
+
+    void Close() { Init(); }
 
     const IndexEntry* GetEntry (gamenumT g) const {
         ASSERT(g < GetNumGames());
@@ -109,122 +65,27 @@ public:
     int GetBadNameIdCount() const { return nInvalidNameId_; }
 
     /**
-     * Counts how many times every names contained in @e nb is used.
-     * @param nb: the NameBase linked to this Index
-     * @returns an array of std::vectors containing the count of each name.
-     */
-    std::array<std::vector<int>, NUM_NAME_TYPES>
-    calcNameFreq(const NameBase& nb) const {
-        std::array<std::vector<int>, NUM_NAME_TYPES> resVec;
-        for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
-            resVec[n].resize(nb.GetNumNames(n), 0);
-        }
-        for (gamenumT i = 0, n = GetNumGames(); i < n; i++) {
-            const IndexEntry* ie = GetEntry(i);
-            resVec[NAME_PLAYER][ie->GetWhite()] += 1;
-            resVec[NAME_PLAYER][ie->GetBlack()] += 1;
-            resVec[NAME_EVENT][ie->GetEvent()] += 1;
-            resVec[NAME_SITE][ie->GetSite()] += 1;
-            resVec[NAME_ROUND][ie->GetRound()] += 1;
-        }
-        return resVec;
-    }
-
-    /**
      * Header getter functions
      */
-    gamenumT    GetNumGames ()    const { return Header.numGames; }
-    uint        GetType ()        const { return Header.baseType; }
-    versionT    GetVersion ()     const { return Header.version; }
-    const char* GetDescription () const { return Header.description; }
-    const char* GetCustomFlagDesc (byte c) const {
-        if (c < IndexEntry::IDX_FLAG_CUSTOM1 || c > IndexEntry::IDX_FLAG_CUSTOM6) return 0;
-        return Header.customFlagDesc[c - IndexEntry::IDX_FLAG_CUSTOM1];
-    }
-    gamenumT GetAutoLoad () const {
-        return (Header.autoLoad <= Header.numGames) ? Header.autoLoad : Header.numGames;
-    }
-
-    /**
-     * Header setter functions
-     */
-    errorT copyHeaderInfo(const Index& src) {
-        if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
-        Header.baseType = src.Header.baseType;
-        Header.autoLoad = src.Header.autoLoad;
-        std::memcpy(Header.description, src.Header.description, sizeof Header.description);
-        std::memcpy(Header.customFlagDesc, src.Header.customFlagDesc, sizeof Header.customFlagDesc);
-        Header.dirty_ = true;
-        return flush();
-    }
-    errorT SetType (uint t) {
-        if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
-        Header.baseType = t;
-        Header.dirty_ = true;
-        return flush();
-    }
-    errorT SetDescription (const char* str) {
-        if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
-        strncpy(Header.description, str, SCID_DESC_LENGTH);
-        Header.description[SCID_DESC_LENGTH] = 0;
-        Header.dirty_ = true;
-        return flush();
-    }
-    errorT SetCustomFlagDesc (byte c, const char* str) {
-        if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
-        if (c < IndexEntry::IDX_FLAG_CUSTOM1 || c > IndexEntry::IDX_FLAG_CUSTOM6) return ERROR_BadArg;
-        char* flagDesc = Header.customFlagDesc[c - IndexEntry::IDX_FLAG_CUSTOM1];
-        strncpy(flagDesc, str, CUSTOM_FLAG_DESC_LENGTH);
-        flagDesc[CUSTOM_FLAG_DESC_LENGTH] = 0;
-        Header.dirty_ = true;
-        return flush();
-    }
-    errorT SetAutoLoad (gamenumT gnum) {
-        if (fileMode_ == FMODE_ReadOnly) return ERROR_FileMode;
-        Header.autoLoad = gnum;
-        Header.dirty_ = true;
-        return flush();
+    gamenumT GetNumGames() const {
+        return static_cast<gamenumT>(entries_.size());
     }
 
-    /**
-     * FetchEntry() - return a modifiable pointer to a game's IndexEntry
-     *
-     * The pointer returned by this function allow to modify the IndexEntry
-     * informations of a game. If modified, the IndexEntry object must be
-     * passed to WriteEntry() to write the changes to the disks.
-     * This functions is very error prone. For example:
-     * IndexEntry* ie = FetchEntry(0);
-     * ie->SetWhiteName(nb, "New player with white");
-     * oops(); // the function oops() may call GetEntry(0) and get a messy object.
-     * ie->SetBlackName(nb, "New player with black");
-     *
-     * A safer alternative is to create a temporary copy of the IndexEntry object
-     * returned by GetEntry() and then write all the changes in a single step
-     */
-    IndexEntry* FetchEntry (gamenumT g) {
-        ASSERT(g < GetNumGames());
-        return &(entries_[g]);
+    void addEntry(const IndexEntry& ie) {
+        entries_.push_back(ie);
     }
 
-    /**
-     * WriteEntry() - modify a game in the Index
-     */
-    errorT WriteEntry(const IndexEntry* ie, gamenumT idx);
+    void replaceEntry(const IndexEntry& ie, gamenumT replaced) {
+        ASSERT(replaced < this->GetNumGames());
 
-    /**
-     * flush() - writes all cached data to the file
-     */
-    errorT flush() {
-        if (FilePtr == 0) return OK;
-        errorT errHeader = (Header.dirty_) ? WriteHeader() : OK;
-        errorT errSync = (FilePtr->pubsync() != 0) ? ERROR_FileWrite : OK;
-        return (errHeader == OK) ? errSync : errHeader;
+        entries_[replaced] = ie;
     }
 
 private:
-    void Init ();
-    errorT Clear ();
-    errorT WriteHeader ();
+    void Init() {
+        nInvalidNameId_ = 0;
+        entries_.resize(0);
+    }
 };
 
 
diff -pruN 1:4.7.0+dfsg1-2/src/misc.cpp 1:4.7.4+dfsg1-2/src/misc.cpp
--- 1:4.7.0+dfsg1-2/src/misc.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/misc.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -157,30 +157,6 @@ eco_LastSubCode (ecoT eco)
 //   String Routines
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// strCopyExclude(): copies original to target, filtering out any
-//      characters in excludeChars.
-void
-strCopyExclude (char * target, const char * original,
-                const char * excludeChars)
-{
-    while (*original != 0) {
-        int exclude = 0;
-        for (char * s = (char *) excludeChars; *s; s++) {
-            if (*original == *s) {
-                exclude = 1;
-                break;
-            }
-        }
-        if (!exclude) {
-            *target = *original;
-            target++;
-        }
-        original++;
-    }
-    *target = 0;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // strAppend():
 //      Appends extra to the end of target, and returns a pointer
 //      to the new END of the string target.
@@ -381,7 +357,7 @@ strTrimMarkCodes (char * str)
             // If we see end-of-string or code-starting '[', there is some
             // error so go back to the start of this code and treat it
             // normally.
-            if (ch == 0  ||  ch == '[') {
+            if (ch == '\0'  ||  ch == '[') {
                 *out++ = *startLocation;
                 inCode = false;
                 in = startLocation;
@@ -392,7 +368,7 @@ strTrimMarkCodes (char * str)
             // For all other characters in a code, just ignore it.
         } else {
             // Stop at end-of-string:
-            if (ch == 0) { break; }
+            if (ch == '\0') { break; }
             // Look for the start of a code that is to be stripped:
             if (ch == '['  &&  in[1] == '%') {
                 inCode = true;
@@ -404,7 +380,11 @@ strTrimMarkCodes (char * str)
         in++;
     }
     // Terminate the modified string:
-    *out = 0;
+    *out = char();
+
+    // If there are only spaces left, remove everything
+    if (in != out && std::count(str, out, ' ') == std::distance(str, out))
+        *str = char();
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff -pruN 1:4.7.0+dfsg1-2/src/misc.h 1:4.7.4+dfsg1-2/src/misc.h
--- 1:4.7.0+dfsg1-2/src/misc.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/misc.h	2022-07-09 05:14:42.000000000 +0000
@@ -68,8 +68,7 @@ public:
 	};
 
 	Progress(Impl* f = NULL) : f_(f) {}
-	Progress(const Progress&);
-	Progress& operator=(const Progress&);
+	Progress(const Progress&) = delete;
 	~Progress() { delete f_; }
 
 	bool report(size_t done, size_t total) const {
@@ -143,8 +142,6 @@ inline uint32_t strStartHash(const char*
 
 char * strDuplicate (const char * str);
 
-void   strCopyExclude (char * target, const char * original,
-                       const char * excludeChars);
 char * strAppend (char * target, const char * extra);
 uint   strPad (char * target, const char * orig, int length, char pad);
 const char * strFirstChar (const char * target, char matchChar);
diff -pruN 1:4.7.0+dfsg1-2/src/movegen.h 1:4.7.4+dfsg1-2/src/movegen.h
--- 1:4.7.0+dfsg1-2/src/movegen.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/movegen.h	2022-07-09 05:14:42.000000000 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2017  Fulvio Benini
+ * Copyright (C) 2011-2021  Fulvio Benini
  *
  * This file is part of Scid (Shane's Chess Information Database).
  *
@@ -20,6 +20,8 @@
  * Implements functions for the validation of chess moves.
  */
 
+#pragma once
+
 #include <utility>
 
 // These functions use the following move classification:
@@ -41,7 +43,9 @@
 
 namespace movegen {
 
-const int NSQUARES = 8;
+constexpr int NSQUARES = 8;
+constexpr int kWPHomeRank = 1;
+constexpr int kBPHomeRank = NSQUARES - 2;
 
 inline bool valid_king(squareT sqFrom, squareT sqTo) {
 	unsigned distRank = 1 + (sqTo / NSQUARES) - (sqFrom / NSQUARES);
@@ -97,9 +101,9 @@ inline bool attack_pawn(squareT sqFrom,
 	return (distFyle == 1 || distFyle == -1);
 }
 
-template <typename TBoard>
+template <typename TFunc>
 bool attack_slider(squareT sqFrom, squareT sqTo, pieceT pieceType,
-                   const TBoard* board, const TBoard EMPTY_SQUARE) {
+                   TFunc isOccupied) {
 	int sqStep = valid_slider(sqFrom, sqTo, pieceType);
 	if (sqStep == 0)
 		return false;
@@ -109,7 +113,7 @@ bool attack_slider(squareT sqFrom, squar
 		sqStep = -sqStep;
 
 	for (int sq = sqFrom + sqStep; sq != sqTo; sq += sqStep) {
-		if (board[sq] != EMPTY_SQUARE)
+		if (isOccupied(sq))
 			return false;
 	}
 
@@ -123,14 +127,15 @@ bool attack_slider(squareT sqFrom, squar
  * @param sqTo:         square of the piece to be captured.
  * @param pieceCol:     color of the moving piece.
  * @param pieceType:    type of the moving piece.
- * @param board:        pointer to the board position; it's is irrelevant if the
- *                      position is the one before or after the move was made.
- * @param EMPTY_SQUARE: value of an empty square in the board position.
+ * @param isOccupied:   callable object which should returns true if a square is
+ *                      occupied by a piece. Since it is not invoked with @e
+ *                      sqFrom or @e sqTo, it's is irrelevant if the position is
+ *                      the one before or after the move was made.
  * @returns true if the move is a valid ATTACK move.
  */
-template <typename TBoard>
+template <typename TFunc>
 bool attack(squareT sqFrom, squareT sqTo, pieceT pieceCol, pieceT pieceType,
-            const TBoard* board, const TBoard EMPTY_SQUARE) {
+            TFunc isOccupied) {
 	switch (pieceType) {
 	case KING:
 		return valid_king(sqFrom, sqTo);
@@ -141,22 +146,35 @@ bool attack(squareT sqFrom, squareT sqTo
 	default:
 		break;
 	}
-	return attack_slider(sqFrom, sqTo, pieceType, board, EMPTY_SQUARE);
+	return attack_slider(sqFrom, sqTo, pieceType, isOccupied);
 }
 
-template <typename TBoard>
-bool pseudo(squareT sqFrom, squareT sqTo, colorT /*pieceCol*/, pieceT pieceType,
-            const TBoard* board, const TBoard EMPTY_SQUARE) {
-	// TODO: pawn and king moves
-	ASSERT(pieceType != PAWN && pieceType != KING);
+template <typename TFunc>
+inline bool pseudo_advance_pawn(squareT sqFrom, squareT sqTo, colorT pieceCol,
+                                TFunc isOccupied) {
+	if ((sqTo % NSQUARES) != (sqFrom % NSQUARES) // Different file
+	    || isOccupied(sqTo)) // Pawns can only capture diagonally
+		return false;
 
-	switch (pieceType) {
-	case KNIGHT:
-		return valid_knight(sqFrom, sqTo);
-	default:
-		break;
-	}
-	return attack_slider(sqFrom, sqTo, pieceType, board, EMPTY_SQUARE);
+	int fromRank = sqFrom / NSQUARES;
+	int distRank = (sqTo / NSQUARES) - fromRank;
+	if (pieceCol == WHITE)
+		return distRank == 1 || (distRank == 2 && fromRank == kWPHomeRank &&
+		                         !isOccupied(sqFrom + NSQUARES));
+
+	return distRank == -1 || (distRank == -2 && fromRank == kBPHomeRank &&
+	                          !isOccupied(sqFrom - NSQUARES));
+}
+
+template <typename TFunc>
+bool pseudo(squareT sqFrom, squareT sqTo, colorT pieceCol, pieceT pieceType,
+            TFunc isOccupied) {
+	// TODO: castle moves
+	if (pieceType == PAWN &&
+	    pseudo_advance_pawn(sqFrom, sqTo, pieceCol, isOccupied))
+		return true;
+
+	return attack(sqFrom, sqTo, pieceCol, pieceType, isOccupied);
 }
 
 /**
@@ -166,19 +184,17 @@ bool pseudo(squareT sqFrom, squareT sqTo
  * @param sqTo:         destination square of the pseudo-legal move.
  * @param sqRay:        the projected ray starts from @e sqRay and goes through
  *                      @e sqFrom; it is usually the square where the king is.
- * @param board:        pointer to the board position where the pseudo-legal
- *                      move has to be made.
- * @param EMPTY_SQUARE: value of an empty square in the board position.
+ * @param isOccupied:   callable object which should returns true if a square is
+ *                      occupied by a piece.
  * @returns a std::pair with the type (INVALID_PIECE, BISHOP, ROOK) and the
  * square of the candidate pinning piece. If the type is INVALID_PIECE there is
  * no pin and the move is legal, otherwise it's necessary to test the board
  * position and if the returned square is occupied by an enemy QUEEN, or an
  * enemy piece matching the returned type, the move is not legal.
  */
-template <typename TBoard>
+template <typename TFunc>
 inline std::pair<pieceT, squareT> opens_ray(squareT sqFrom, squareT sqTo,
-                                            squareT sqRay, const TBoard* board,
-                                            const TBoard EMPTY_SQUARE) {
+                                            squareT sqRay, TFunc isOccupied) {
 	ASSERT(sqRay != sqFrom);
 
 	int fyleFrom = sqFrom % NSQUARES;
@@ -195,7 +211,7 @@ inline std::pair<pieceT, squareT> opens_
 		pt = ROOK;
 	} else {
 		if (fyleFrom == 0 || fyleFrom == (NSQUARES - 1))
-			return std::pair<pieceT, squareT>(INVALID_PIECE, 0);
+			return {INVALID_PIECE, 0};
 
 		if (distRank == 0) {
 			sqStep = 1; // horizontal
@@ -210,7 +226,7 @@ inline std::pair<pieceT, squareT> opens_
 			fyleEdge = NSQUARES - 1;
 			pt = BISHOP;
 		} else {
-			return std::pair<pieceT, squareT>(INVALID_PIECE, 0);
+			return {INVALID_PIECE, 0};
 		}
 	}
 	if (sqFrom > sqRay) {
@@ -219,22 +235,32 @@ inline std::pair<pieceT, squareT> opens_
 	}
 
 	for (int sq = sqFrom + sqStep; sq != sqRay; sq += sqStep) {
-		if (sq == sqTo || board[sq] != EMPTY_SQUARE)
-			return std::pair<pieceT, squareT>(INVALID_PIECE, 0);
+		if (sq == sqTo || isOccupied(sq))
+			return {INVALID_PIECE, 0};
 	}
 
 	for (int sq = sqFrom - sqStep; sq < NSQUARES * NSQUARES; sq -= sqStep) {
 		if (sq < 0 || sq == sqTo)
 			break;
 
-		if (board[sq] != EMPTY_SQUARE)
-			return std::make_pair(pt, static_cast<squareT>(sq));
+		if (isOccupied(sq))
+			return {pt, static_cast<squareT>(sq)};
 
 		if ((sq % NSQUARES) == fyleEdge)
 			break;
 	}
-	return std::pair<pieceT, squareT>(INVALID_PIECE, 0);
+	return {INVALID_PIECE, 0};
 }
 
+/// Checks if there is a valid ray from @e sqFrom to @e sqTo and if a piece on
+/// @e sqBlock would block that ray.
+/// @param sqFrom:       start square of the ray.
+/// @param sqTo:         end square of the ray.
+/// @param sqBlock:      the square that may block the ray.
+/// @returns true if a piece on @e sqBlock would block the ray.
+inline bool blocks_ray(squareT sqFrom, squareT sqTo, squareT sqBlock) {
+	return !movegen::attack_slider(sqFrom, sqTo, QUEEN,
+	                               [&](auto sq) { return sq == sqBlock; });
+}
 
 } // end of namespace movegen
diff -pruN 1:4.7.0+dfsg1-2/src/movelist.h 1:4.7.4+dfsg1-2/src/movelist.h
--- 1:4.7.0+dfsg1-2/src/movelist.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/movelist.h	2022-07-09 05:14:42.000000000 +0000
@@ -18,8 +18,6 @@
 #define SCID_MOVELIST_H
 
 #include "common.h"
-#include <cstring>
-#include <iterator>
 
 //////////////////////////////////////////////////////////////////////
 //  MoveList:  Constants
@@ -37,8 +35,9 @@ struct simpleMoveT
 {
     squareT  from;
     squareT  to;
-    pieceT   promote;
-    pieceT   movingPiece;
+    pieceT   promote; // EMPTY if not a promotion, type (no color) otherwise
+    pieceT   movingPiece : 7;
+    pieceT   castling : 1;
     byte     pieceNum;
     byte     capturedNum;
     pieceT   capturedPiece;
@@ -47,73 +46,81 @@ struct simpleMoveT
     byte     castleFlags;    // pre-move information
     squareT  epSquare;       // pre-move information
     ushort   oldHalfMoveClock;
-    int32_t  score;          // used for alpha/beta ordering.
 
 	bool isNullMove() const {
 		return from == to && from != NULL_SQUARE &&
 		       piece_Type(movingPiece) == KING;
 	}
 
+	/// Returns:
+	///  +2 for king side castle
+	///  -2 for queen side castle
+	///  0 (false) if it is not a castle moves.
 	int isCastle() const {
-		ASSERT(piece_Type(movingPiece) == KING);
-		if (square_Fyle(from) == E_FYLE) {
-			squareT toFyle = square_Fyle(to);
-			if (toFyle == G_FYLE)
-				return 1;
-			if (toFyle == C_FYLE)
-				return 2;
-		}
-		return 0;
-	}
+		if (castling)
+			return to > from ? 2 : -2;
 
-	bool operator<(const simpleMoveT& b) const {
-		// Highest score first
-		return score > b.score;
+		return 0;
 	}
 
-	void clear() {
-		std::memset(this, 0, sizeof *this);
+	/// Converts the move to long algebraic notation.
+	/// @return a pointer one past the last char written.
+	template <typename OutputIt> OutputIt toLongNotation(OutputIt dest) const {
+		if (from == to) {
+			// UCI standard for null move
+			*dest++ = '0';
+			*dest++ = '0';
+			*dest++ = '0';
+			*dest++ = '0';
+		} else {
+			*dest++ = square_FyleChar(from);
+			*dest++ = square_RankChar(from);
+			*dest++ = square_FyleChar(to);
+			*dest++ = square_RankChar(to);
+			if (promote != EMPTY) {
+				constexpr const char promoChars[] = "  qrbn ";
+				*dest++ = promoChars[piece_Type(promote)];
+			}
+		}
+		return dest;
 	}
 };
 
-struct cmpMove {
-	const simpleMoveT& m;
-	explicit cmpMove(const simpleMoveT& sm) : m(sm) {}
+struct ScoredMove : public simpleMoveT {
+	int32_t score; // used for alpha/beta ordering.
+
+	bool operator<(const ScoredMove& b) const {
+		// Highest score first
+		return score > b.score;
+	}
 };
-inline bool operator==(const simpleMoveT& a, const cmpMove& b) {
-	return a.from == b.m.from && a.to == b.m.to && a.promote == b.m.promote;
-}
 
 // typedef std::vector<simpleMoveT> MoveList;
 class MoveList {
 	uint ListSize = 0;
-	simpleMoveT Moves[MAX_LEGAL_MOVES];
+	ScoredMove Moves[MAX_LEGAL_MOVES];
 
 public:
-	typedef simpleMoveT* iterator;
+	typedef ScoredMove* iterator;
 	iterator begin() { return Moves; };
 	iterator end() { return Moves + ListSize; }
 	uint Size() { return ListSize; }
 	void Clear() { ListSize = 0; }
-	void emplace_back(squareT from, squareT to, pieceT promote,
-	                  pieceT movingPiece, pieceT capturedPiece) {
+	ScoredMove& emplace_back() {
 		ASSERT(ListSize < MAX_LEGAL_MOVES);
-		simpleMoveT& sm = Moves[ListSize++];
-		sm.from = from;
-		sm.to = to;
-		sm.promote = promote;
-		sm.movingPiece = movingPiece;
-		sm.capturedPiece = capturedPiece;
+		ScoredMove& sm = Moves[ListSize++];
+		sm = ScoredMove();
+		return sm;
 	}
 	void resize(size_t count) {
 		ASSERT(count <= MAX_LEGAL_MOVES);
 		ListSize = static_cast<uint>(count);
 	}
-	void push_back(const simpleMoveT& sm) {
+	void push_back(const ScoredMove& sm) {
 		ASSERT(ListSize < MAX_LEGAL_MOVES);
 		Moves[ListSize++] = sm;
 	}
-	simpleMoveT* Get(size_t index) {
+	ScoredMove* Get(size_t index) {
 		ASSERT(index < ListSize);
 		return &(Moves[index]);
 	}
diff -pruN 1:4.7.0+dfsg1-2/src/movetree.h 1:4.7.4+dfsg1-2/src/movetree.h
--- 1:4.7.0+dfsg1-2/src/movetree.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/movetree.h	2022-07-09 05:14:42.000000000 +0000
@@ -60,10 +60,10 @@ enum markerT : byte { NO_MARKER = 0, STA
 //   to specify a location in the game.
 //
 struct moveT {
-	simpleMoveT moveData; // piece moving, target square etc
 	moveT* prev;
 	moveT* next;
 	moveT* varChild;
+	simpleMoveT moveData; // piece moving, target square etc
 	char san[10];   // SAN representation of move
 	markerT marker; // can be NO_MARKER, START_MARKER or END_MARKER
 	byte numVariations;
@@ -72,7 +72,7 @@ struct moveT {
 	std::string comment;
 
 	void clear() {
-		moveData.clear();
+		moveData = simpleMoveT();
 		prev = nullptr;
 		next = nullptr;
 		varChild = nullptr;
@@ -158,7 +158,7 @@ struct moveT {
 		return {varStart->prev, varStart};
 	}
 
-	const moveT* nextMoveInPGN() const {
+	moveT* nextMoveInPGN() const {
 		if (endMarker()) {
 			auto parent = getParent();
 			auto nextVar = parent.second->varChild;
diff -pruN 1:4.7.0+dfsg1-2/src/mtbdata.h 1:4.7.4+dfsg1-2/src/mtbdata.h
--- 1:4.7.0+dfsg1-2/src/mtbdata.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/mtbdata.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,3802 +0,0 @@
-//////////////////////////////////////////////////////////////////////
-//
-//  FILE:       mtbdata.h
-//              Memory tablebases
-//
-//  Part of:    Scid (Shane's Chess Information Database)
-//  Version:    3.5
-//
-//  Notice:     Copyright (c) 2003 Shane Hudson.  All rights reserved.
-//
-//  Author:     Shane Hudson (sgh@users.sourceforge.net)
-//
-//////////////////////////////////////////////////////////////////////
-
-
-// This file was automatically generated by the program mtbgen.cpp.
-// It contains selected compressed tablebase data used by the endgame
-// recognition code in the Scidlet chess engine.
-
-#ifndef SCID_MTBDATA_H
-#define SCID_MTBDATA_H
-
-
-//////////////////////////////////////////////////////////////////////
-//
-// KQK
-
-static const byte mtbdata_KQK[5954] = {
-    1,72,0,159,0,46,16,17,15,13,34,34,0,34,36,34,50,51,35,34,51,76,35,51,
-    192,1,50,35,208,0,151,1,51,44,51,67,68,5,52,207,0,1,0,85,11,85,69,68,
-    84,85,0,69,111,1,5,0,101,102,86,101,102,1,86,133,31,1,2,103,102,118,
-    119,0,103,133,31,1,15,51,23,17,33,18,191,32,10,68,216,226,29,143,32,
-    5,0,52,227,32,63,5,68,68,98,69,68,146,32,63,5,85,85,86,209,32,62,102,
-    118,87,15,8,114,15,7,191,56,14,159,0,5,143,33,218,1,34,34,18,144,26,
-    205,0,98,60,160,29,50,51,79,79,143,64,10,79,12,160,17,66,145,52,148,
-    21,34,50,147,1,35,34,50,221,162,20,244,2,143,4,2,35,72,128,28,79,13,
-    51,254,128,32,207,0,0,15,115,132,16,159,0,7,136,20,141,117,125,255,
-    52,144,1,127,11,143,8,14,143,32,98,143,12,5,150,0,28,191,201,145,31,
-    2,69,208,1,15,11,143,8,14,15,30,143,32,46,255,60,158,0,143,12,1,242,
-    2,128,11,79,6,177,178,198,9,251,143,20,14,63,78,143,32,14,63,5,150,
-    0,31,37,103,226,206,192,31,9,143,8,14,31,81,1,80,0,159,0,79,51,15,13,
-    67,68,68,216,52,209,0,5,51,183,1,3,85,85,48,69,85,85,224,0,15,2,101,
-    102,102,22,102,118,119,87,130,1,119,192,0,52,67,118,103,241,2,102,103,
-    102,119,34,233,0,161,1,228,1,35,120,18,135,136,63,15,83,34,34,161,29,
-    209,0,29,5,143,32,9,71,229,32,102,199,32,101,86,102,177,30,192,2,114,
-    98,86,149,1,79,9,127,10,24,17,64,1,18,34,17,18,33,33,34,18,37,80,18,
-    18,128,1,34,17,192,0,18,215,161,3,5,221,1,33,154,2,34,15,11,143,32,
-    49,79,200,64,101,160,30,101,85,16,6,31,31,128,195,32,19,40,34,34,68,
-    51,50,1,50,51,50,35,35,34,50,34,58,128,1,50,35,241,1,194,0,81,35,131,
-    4,191,34,84,34,80,48,160,2,95,12,1,248,39,143,32,22,184,97,95,13,153,
-    0,95,43,56,51,14,51,85,68,68,84,174,14,226,14,0,222,53,137,20,15,1,
-    52,143,4,33,225,13,144,12,127,4,255,69,176,0,196,0,247,31,137,32,68,
-    111,11,129,128,142,128,129,15,17,72,68,68,193,73,137,74,136,140,63,
-    102,102,70,131,4,159,0,7,240,20,127,2,198,2,247,47,2,241,23,213,2,143,
-    8,3,176,60,86,192,0,47,7,126,144,8,86,199,32,143,32,1,225,31,86,241,
-    29,95,7,63,88,85,85,209,167,152,168,165,8,134,140,206,3,254,241,3,143,
-    4,13,143,8,14,150,14,31,7,148,8,4,143,20,0,240,118,15,13,139,32,158,
-    0,5,104,102,102,255,136,240,1,193,0,92,133,200,95,36,21,95,6,192,149,
-    2,143,12,38,31,49,1,64,0,144,0,68,68,52,68,84,64,69,68,2,85,69,84,85,
-    69,131,68,209,1,85,84,84,69,85,211,2,4,15,104,84,85,85,85,102,179,1,
-    100,128,102,225,1,68,68,86,101,68,84,0,100,85,68,100,69,86,68,100,48,
-    86,100,102,0,244,3,102,84,101,24,102,102,100,70,192,0,162,0,70,118,
-    12,100,118,102,116,119,64,192,25,85,17,85,117,119,119,0,102,101,86,
-    4,129,1,117,136,101,134,86,16,103,64,133,136,50,50,51,67,68,52,32,67,
-    68,36,144,33,36,52,67,52,3,35,52,68,67,36,52,68,192,1,55,223,28,83,
-    34,34,129,16,240,13,66,127,0,131,4,211,231,33,193,25,135,32,85,34,51,
-    51,240,55,169,193,3,193,32,69,129,33,100,192,0,70,119,234,128,34,39,
-    208,31,40,119,32,135,37,137,34,144,0,66,36,34,32,34,50,110,176,0,35,
-    160,0,0,50,144,16,192,0,112,128,17,98,33,33,18,33,17,18,1,33,34,17,
-    33,34,34,33,17,111,80,18,0,177,7,34,131,4,160,4,153,1,205,224,2,192,
-    5,152,0,33,50,163,6,161,0,18,95,161,7,33,192,0,18,161,3,210,8,192,29,
-    66,22,112,34,66,66,0,36,129,2,162,32,20,34,34,51,51,144,36,85,193,36,
-    67,65,52,85,128,1,99,85,67,100,52,45,0,69,83,160,27,85,128,32,193,35,
-    101,229,44,129,66,132,68,47,12,51,51,128,48,52,4,160,12,36,51,51,53,
-    35,192,13,35,221,51,64,208,42,37,145,11,129,16,240,19,52,7,130,3,51,
-    35,34,35,50,129,1,198,0,179,144,0,129,4,50,241,1,128,21,35,35,240,39,
-    254,129,5,36,78,242,43,129,1,19,35,130,28,117,68,35,224,29,176,47,64,
-    67,80,52,220,129,2,177,30,132,64,84,209,113,99,144,63,68,186,100,192,
-    1,100,225,62,134,32,13,103,79,22,123,68,68,145,72,193,127,133,77,194,
-    0,84,160,14,31,132,12,51,51,83,136,4,242,0,92,146,21,254,170,119,227,
-    21,145,0,95,2,226,25,193,4,79,2,130,16,248,69,96,147,14,134,28,192,
-    0,192,146,85,84,7,85,100,85,84,101,69,64,176,1,235,144,0,133,128,65,
-    20,103,16,103,16,231,135,32,27,106,161,11,101,86,144,141,192,0,253,
-    176,0,158,0,242,11,140,4,137,5,149,149,89,101,255,216,4,127,2,194,4,
-    79,10,209,3,148,4,79,4,193,0,236,226,12,194,66,129,60,194,60,86,129,
-    61,160,0,118,28,86,103,101,118,178,62,128,32,208,0,118,76,119,119,193,
-    0,118,103,64,1,135,6,103,135,118,135,119,134,33,160,7,7,85,117,103,
-    102,102,119,64,176,0,126,147,0,118,20,48,242,13,241,1,214,3,143,4,20,
-    127,118,119,31,9,197,8,143,8,7,208,1,148,2,143,16,8,254,147,8,143,20,
-    16,131,32,176,0,41,192,0,37,242,3,59,119,135,120,225,165,159,0,0,143,
-    4,99,135,151,2,192,47,8,143,8,14,47,16,1,64,0,144,0,68,68,68,67,85,
-    40,85,68,84,192,0,68,177,0,69,85,39,68,85,84,16,85,102,16,0,224,132,
-    4,2,128,1,177,5,68,69,68,84,178,69,224,4,69,227,2,95,75,85,67,178,19,
-    101,85,85,209,13,208,14,85,84,130,2,69,190,193,2,144,1,85,128,4,163,
-    19,193,3,128,19,192,0,168,69,112,86,208,19,86,136,20,102,102,3,70,85,
-    102,102,69,102,100,0,5,161,0,86,100,69,102,103,132,4,36,148,178,8,192,
-    30,68,86,128,1,70,242,9,68,32,84,84,102,145,13,85,85,51,51,66,35,51,
-    160,2,51,67,68,66,240,0,0,66,51,68,51,66,67,52,68,112,66,67,128,2,179,
-    15,159,0,77,35,34,34,154,51,145,16,68,34,144,49,194,0,67,32,205,52,
-    193,1,130,16,35,34,129,19,228,51,68,209,224,22,208,29,224,24,100,227,
-    25,68,68,52,212,160,23,131,36,128,32,85,96,85,96,85,15,86,116,69,85,
-    102,2,129,28,208,26,0,208,4,52,51,86,51,52,68,54,149,68,192,11,85,52,
-    160,9,52,2,34,168,144,0,208,15,50,192,0,34,176,0,51,36,7,51,34,67,50,
-    68,34,112,144,17,65,0,17,0,34,34,17,18,33,0,48,18,34,18,17,33,34,18,
-    61,18,34,34,0,34,160,3,101,18,126,132,1,33,144,1,128,5,34,68,208,4,
-    224,5,127,33,34,225,0,224,6,128,9,210,8,24,224,52,136,192,0,193,29,
-    35,34,35,32,52,66,64,35,34,177,35,34,34,35,34,50,144,51,144,24,84,53,
-    49,68,83,102,0,52,68,52,86,52,68,70,83,21,52,68,86,52,144,3,51,143,
-    32,5,100,238,21,129,64,133,92,52,84,145,97,59,177,12,0,50,51,85,51,
-    50,51,53,51,67,50,67,192,1,67,67,85,50,34,193,193,16,128,40,144,0,50,
-    51,50,34,50,153,208,53,128,1,51,50,208,18,129,19,51,51,254,130,4,146,
-    1,225,2,32,176,3,112,179,4,38,63,68,67,52,17,129,8,161,8,224,4,113,
-    128,145,0,132,28,68,52,34,68,84,51,48,34,83,85,128,3,192,0,53,83,51,
-    247,34,176,19,130,4,129,64,130,100,84,176,0,177,24,124,32,70,192,1,
-    95,20,160,4,134,128,160,123,85,255,70,160,128,224,104,80,192,0,18,130,
-    4,130,100,223,241,36,128,108,226,0,68,244,7,193,16,195,7,164,80,255,
-    197,4,162,118,131,4,222,90,177,135,127,10,193,3,38,222,133,24,177,126,
-    165,0,69,192,52,131,28,41,163,23,67,69,87,16,84,69,85,87,139,128,172,
-    129,32,83,118,80,116,5,163,35,85,68,85,101,192,0,102,85,86,160,30,86,
-    0,101,85,102,86,101,101,119,86,121,101,102,144,0,139,4,195,4,161,0,
-    85,119,247,193,5,163,71,146,1,90,192,3,102,111,9,81,250,111,10,143,
-    8,14,2,225,17,163,51,209,1,86,145,2,149,102,224,20,102,85,128,24,87,
-    192,0,103,193,34,193,162,35,119,119,85,102,117,65,16,85,16,87,133,85,
-    102,120,140,179,17,162,3,102,102,118,16,176,0,102,207,103,160,0,225,
-    0,102,136,129,1,211,12,146,1,189,154,0,143,4,49,119,31,10,0,31,11,209,
-    21,135,123,31,9,104,16,192,0,18,241,56,119,224,9,118,224,28,119,64,
-    162,0,17,120,55,158,0,29,120,119,119,153,192,0,16,127,7,136,234,79,
-    13,34,143,8,41,79,15,136,127,41,121,0,0,121,119,1,73,0,159,0,79,51,
-    68,2,52,51,0,5,68,51,52,68,67,68,52,10,67,52,68,52,52,161,3,85,2,26,
-    53,68,68,51,176,1,194,5,84,144,1,0,69,69,84,85,85,84,68,68,34,100,85,
-    102,2,86,102,102,130,5,216,101,192,1,177,2,101,113,208,2,118,118,141,
-    119,2,103,102,119,162,5,241,1,102,164,145,2,65,103,192,0,135,136,98,
-    120,100,136,136,130,5,0,119,119,49,135,109,136,119,160,0,79,79,56,151,
-    31,31,3,67,118,243,35,68,176,33,164,37,27,84,129,29,139,32,219,85,144,
-    29,145,1,85,145,33,31,143,67,166,29,87,31,12,67,31,46,118,15,35,56,
-    133,84,144,17,191,147,18,129,21,52,208,84,65,254,21,239,57,13,148,92,
-    253,217,5,183,25,143,28,3,168,29,128,4,131,96,143,32,8,102,104,31,13,
-    118,31,32,132,112,69,192,15,51,84,12,84,85,84,69,69,64,128,1,84,255,
-    69,192,0,130,115,159,24,11,178,120,143,4,21,138,27,161,127,63,247,127,
-    85,69,193,127,128,14,128,17,128,127,145,1,27,160,2,85,84,85,130,125,
-    143,128,3,118,124,109,143,64,36,88,133,12,145,42,86,176,0,144,14,86,
-    231,162,0,242,11,128,3,149,0,101,85,208,3,65,254,143,4,47,163,3,132,
-    15,143,16,2,144,1,131,8,130,123,146,0,136,87,132,31,102,102,119,226,
-    28,102,103,255,103,128,29,212,31,131,32,176,3,243,30,231,2,240,28,239,
-    15,0,242,126,130,3,208,2,88,135,136,192,10,192,11,119,144,42,102,224,
-    1,177,39,193,0,87,143,4,4,194,3,255,15,42,244,26,15,8,128,3,197,8,6,
-    136,28,198,28,185,192,24,145,0,104,131,63,209,59,226,28,119,120,254,
-    241,59,212,31,133,32,129,31,200,0,130,3,167,37,192,9,255,135,177,0,
-    192,5,161,0,194,6,143,4,75,143,16,15,208,20,255,193,0,131,5,193,26,
-    176,0,144,55,138,12,134,9,145,90,42,159,0,1,137,153,98,121,131,63,153,
-    18,8,136,152,153,136,137,144,1,153,136,1,9,0,0,101,85,150,0,53,83,79,
-    242,0,86,244,1,101,86,200,3,15,81,244,15,62,10,102,102,176,0,0,135,
-    4,193,1,144,19,0,101,101,102,86,101,86,102,119,40,119,118,119,1,102,
-    160,1,87,101,2,102,102,86,117,119,119,103,32,43,103,118,118,192,0,103,
-    177,3,136,65,112,145,1,87,192,3,19,129,2,135,136,119,4,135,119,136,
-    51,51,85,228,14,83,40,85,53,51,224,1,53,177,0,53,83,63,83,53,85,32,
-    178,2,79,75,129,15,49,255,161,1,210,13,240,1,176,16,192,29,128,18,208,
-    16,146,30,236,138,32,162,52,144,51,97,85,160,29,211,26,85,233,85,128,
-    28,177,3,240,1,87,193,1,117,117,81,192,0,87,31,17,84,228,10,67,68,52,
-    0,19,84,53,51,52,67,83,53,231,84,112,176,1,159,0,77,68,68,21,131,48,
-    66,31,26,85,31,49,68,68,85,84,195,10,1,84,85,69,52,67,68,85,69,6,177,
-    0,69,84,84,69,85,144,1,176,1,224,68,176,20,146,0,128,1,52,67,67,52,
-    157,67,208,35,68,67,128,2,195,0,201,25,67,124,144,40,67,214,1,209,27,
-    163,28,226,7,241,7,51,27,67,69,67,52,225,6,161,0,52,161,8,193,176,0,
-    243,15,128,17,85,53,67,84,85,180,241,1,176,16,85,160,16,192,0,69,137,
-    32,101,166,54,143,64,16,117,111,30,102,102,144,105,130,12,1,84,85,101,
-    70,84,102,102,70,94,240,126,69,160,127,70,240,2,192,0,160,19,132,36,
-    107,68,84,208,33,176,0,85,128,1,69,208,1,239,192,19,96,176,27,143,4,
-    0,69,161,1,208,39,15,6,118,241,0,68,209,43,1,199,125,69,193,31,197,
-    0,243,100,131,1,128,29,146,124,145,1,70,84,193,62,95,225,21,86,209,
-    0,118,1,143,128,46,210,43,163,44,249,208,7,145,8,245,11,157,0,143,4,
-    57,4,118,103,254,31,1,12,135,28,162,6,192,26,197,24,43,161,5,169,103,
-    59,135,163,29,135,133,32,135,104,162,193,3,240,1,120,224,2,120,135,
-    135,192,0,253,120,128,3,149,6,212,7,177,38,25,143,4,9,120,61,127,13,
-    135,104,15,9,145,0,143,8,6,149,24,120,191,15,1,40,135,65,36,164,2,128,
-    1,21,62,6,152,153,0,243,9,193,30,195,2,238,4,254,136,210,34,192,0,176,
-    1,230,10,143,4,133,159,0,3,99,31,152,137,136,136,176,0,118,193,31,33,
-    128,184,33,42,1,4,80,85,101,87,85,144,0,102,0,102,86,101,85,85,51,85,
-    101,0,102,85,101,119,85,101,85,87,2,102,101,86,101,119,101,103,128,
-    3,234,86,129,4,3,34,85,176,0,85,208,2,190,86,144,1,102,15,78,128,13,
-    130,1,244,15,161,21,68,118,85,224,16,86,85,86,192,0,102,150,101,160,
-    4,117,119,0,86,161,19,130,4,0,119,119,101,85,117,119,103,85,155,86,
-    96,103,117,144,24,241,3,119,211,8,130,49,193,1,134,136,103,102,103,
-    48,99,119,134,192,9,130,28,102,102,70,130,32,160,162,8,16,87,192,32,
-    85,119,101,86,17,54,51,83,53,131,16,53,85,51,0,144,0,83,85,51,83,86,
-    51,83,1,51,53,85,83,53,83,85,83,204,183,35,31,71,242,15,102,53,161,
-    15,18,85,18,85,83,51,83,192,29,53,101,240,21,141,83,133,32,69,68,100,
-    20,49,117,5,192,32,85,119,102,85,87,16,86,233,18,130,36,227,27,116,
-    87,192,0,119,117,184,179,57,145,0,69,149,44,100,146,1,86,53,37,51,67,
-    53,227,14,53,68,100,67,168,176,0,16,67,16,67,239,31,79,69,68,141,52,
-    192,60,102,102,52,176,16,21,67,93,224,16,69,160,16,69,0,208,23,4,84,
-    223,129,34,6,225,23,86,63,19,135,64,149,40,41,49,128,11,69,68,144,0,
-    129,36,102,68,51,0,128,1,68,84,102,68,84,68,70,0,85,84,69,84,102,84,
-    85,68,49,68,52,67,129,4,3,51,52,67,7,176,0,52,68,52,68,67,192,0,192,
-    2,234,194,38,154,0,144,25,114,67,144,1,68,209,1,81,67,84,178,91,51,
-    241,7,51,84,53,167,162,7,161,0,68,128,9,68,52,128,15,130,32,74,128,
-    17,85,240,15,102,70,160,16,84,193,61,3,68,69,102,86,68,86,100,140,64,
-    239,211,25,127,25,128,3,136,128,68,62,130,125,134,68,136,208,15,192,
-    30,119,85,84,128,4,84,86,62,101,102,84,129,116,165,53,240,3,16,192,
-    3,207,84,128,36,130,1,85,84,194,27,131,57,177,39,252,242,39,224,2,128,
-    4,176,3,0,104,176,1,68,115,68,86,18,129,8,130,29,85,84,210,31,234,130,
-    32,193,30,224,20,192,0,102,35,118,130,1,62,102,102,118,133,28,130,2,
-    128,132,177,2,160,1,173,119,192,124,118,143,128,4,118,193,3,192,0,119,
-    119,192,7,103,176,4,162,141,144,5,85,128,2,224,131,22,64,104,119,103,
-    208,3,103,160,7,134,4,231,101,112,162,11,177,0,103,119,209,7,193,11,
-    255,244,3,5,160,1,193,14,195,3,193,16,166,153,144,20,253,195,153,97,
-    164,0,241,5,104,147,0,20,118,255,192,0,208,15,242,27,131,24,176,10,
-    192,22,194,15,224,23,142,225,19,128,28,118,135,136,144,0,139,4,161,
-    23,71,103,135,113,120,119,119,18,130,9,144,179,18,96,135,136,208,0,
-    136,120,119,227,120,192,3,160,4,82,103,119,136,224,2,239,161,3,209,
-    3,209,4,144,1,135,229,3,129,4,165,7,191,210,7,192,0,120,8,133,45,147,
-    7,192,3,195,5,255,177,1,79,7,20,47,33,160,23,227,16,194,27,134,28,238,
-    194,26,208,1,201,0,70,134,81,67,152,0,16,153,153,137,137,163,1,120,
-    135,153,12,153,119,136,152,153,192,2,176,0,137,255,153,176,3,192,0,
-    212,3,98,146,10,80,16,190,98,160,0,152,167,9,36,144,0,33,50,191,136,
-    47,11,152,162,4,31,11,231,15,19,99,107,19,137,143,8,1,29,137,192,1,
-    152,128,25,204,160,25,229,29,193,0,153,152,96,161,0,153,0,136,1,32,
-    96,102,153,0,84,102,102,70,16,100,102,102,86,184,2,70,84,70,10,68,68,
-    84,84,85,0,68,4,33,85,69,68,209,2,68,84,69,84,138,225,0,143,4,11,84,
-    85,101,176,1,101,100,192,84,176,3,2,85,70,68,69,84,0,84,69,85,68,100,
-    102,118,103,60,102,102,118,178,13,160,3,224,5,140,16,102,152,102,1,
-    118,119,144,0,34,102,102,195,119,160,6,144,6,118,103,118,119,240,2,
-    249,224,0,176,2,2,226,5,96,209,5,118,103,12,129,1,118,119,135,136,119,
-    129,5,103,135,134,152,2,136,136,153,73,143,24,13,79,78,207,192,12,130,
-    35,135,16,84,85,225,1,192,35,128,1,239,224,39,130,32,147,0,1,69,240,
-    35,28,128,31,249,50,3,196,32,193,29,241,38,127,13,136,72,255,63,109,
-    241,13,59,178,32,192,1,192,0,240,18,48,249,230,26,63,70,160,45,147,
-    16,161,17,192,83,102,86,239,208,2,224,83,163,49,159,0,79,101,192,0,
-    132,60,128,100,63,182,14,101,102,133,3,163,26,131,96,143,32,13,209,
-    105,216,89,202,29,95,2,104,129,110,137,128,86,101,63,102,119,87,208,
-    78,193,112,192,0,129,65,161,81,159,228,35,145,20,69,85,192,83,101,129,
-    122,232,89,248,240,39,128,39,192,40,146,25,69,143,41,0,86,101,179,86,
-    144,28,86,128,28,128,14,102,102,227,60,255,241,29,242,1,155,0,209,131,
-    98,140,4,210,134,163,31,230,243,60,225,1,240,129,148,0,135,120,208,
-    0,141,32,120,136,104,111,3,131,140,130,40,144,12,118,87,1,102,119,103,
-    86,103,118,118,87,255,176,2,130,4,200,36,145,16,128,35,133,36,79,6,
-    194,0,254,128,41,31,3,130,1,208,13,17,136,32,218,30,144,57,111,119,
-    103,145,54,193,31,118,135,28,192,0,181,28,1,161,28,135,120,135,135,
-    119,119,120,156,135,60,210,3,136,136,128,3,96,134,32,152,254,153,144,
-    0,241,36,240,2,91,210,43,133,72,144,1,255,103,160,167,192,167,197,0,
-    214,143,227,18,177,175,160,4,255,148,13,112,143,4,39,192,26,130,15,
-    146,22,214,54,149,24,102,193,55,135,162,56,128,2,136,136,131,89,195,
-    30,190,135,209,3,120,192,3,211,25,152,26,252,61,147,31,56,137,136,152,
-    129,189,131,32,246,190,153,152,71,137,152,192,0,136,153,137,146,66,
-    216,66,255,194,10,129,1,176,7,97,143,4,8,35,31,42,146,48,188,31,5,137,
-    2,136,216,54,136,30,128,24,98,153,235,153,131,28,228,2,209,0,137,49,
-    153,197,0,224,148,0,242,64,40,1,1,1,0,102,102,119,103,102,102,128,176,
-    0,145,0,70,101,102,102,85,102,101,118,119,208,2,224,1,103,119,128,3,
-    85,21,65,86,86,85,18,69,48,85,0,192,4,86,85,119,85,86,102,87,46,102,
-    86,102,32,101,212,3,130,4,97,173,101,224,2,119,128,1,87,209,1,144,8,
-    86,161,180,4,128,7,101,52,101,103,85,101,49,160,8,101,86,131,15,147,
-    0,69,68,100,250,224,0,146,14,111,3,51,27,128,21,118,208,23,219,119,
-    17,39,119,241,2,128,27,103,224,2,48,129,28,118,136,97,197,3,103,119,
-    136,22,136,103,119,134,192,0,103,193,2,140,32,200,68,133,11,210,2,119,
-    68,129,30,70,69,130,68,68,85,85,68,84,101,160,17,15,119,68,69,85,71,
-    52,79,75,128,18,241,208,15,194,18,163,16,224,17,176,36,69,102,100,92,
-    192,0,69,133,32,118,118,178,5,8,102,125,102,134,128,4,8,145,0,193,1,
-    0,102,224,3,180,29,15,3,144,14,101,71,68,68,184,69,135,16,68,224,12,
-    224,16,128,1,70,85,222,68,134,35,159,0,70,85,240,13,139,32,195,32,97,
-    236,69,240,18,142,64,63,57,104,225,81,136,92,119,99,119,71,224,0,145,
-    66,85,85,103,160,1,231,135,95,159,0,75,193,102,132,60,68,102,240,15,
-    162,105,253,240,2,177,108,177,117,143,32,6,130,28,127,18,135,96,86,
-    49,143,128,0,102,85,129,12,148,0,86,101,119,254,128,4,225,11,213,38,
-    133,123,226,3,131,22,209,124,144,123,125,85,102,128,37,130,4,128,3,
-    114,132,26,86,127,224,2,101,193,40,160,2,128,128,130,126,130,28,242,
-    4,191,241,2,155,0,118,146,26,134,4,129,22,131,60,149,29,122,128,17,
-    135,129,28,84,128,2,134,32,135,95,9,173,153,48,152,129,11,120,226,11,
-    145,0,103,3,128,17,102,119,135,136,119,119,176,0,255,132,3,240,62,119,
-    161,11,192,4,241,21,196,0,130,34,252,129,7,116,213,22,128,9,193,9,105,
-    236,26,119,241,118,192,0,146,26,160,146,43,118,103,118,248,241,12,66,
-    231,15,195,30,243,10,22,119,119,239,135,16,132,28,133,6,136,240,23,
-    225,24,147,1,56,128,25,152,153,144,0,128,161,136,4,151,153,22,120,119,
-    120,153,227,1,120,130,8,130,30,253,136,193,9,241,41,195,0,116,130,44,
-    100,120,255,144,14,128,2,129,5,242,36,6,226,17,4,192,5,223,225,7,195,
-    2,159,0,0,135,129,13,208,4,114,0,159,135,8,147,26,135,120,192,18,4,
-    194,9,106,239,226,1,159,0,0,246,18,123,152,192,0,241,31,192,29,56,85,
-    153,153,240,2,32,129,3,119,137,58,153,153,137,130,36,128,1,161,3,137,
-    144,6,55,152,137,153,193,3,241,1,119,208,0,131,4,227,226,9,8,162,0,
-    0,137,136,120,7,127,148,14,137,160,5,160,12,34,133,44,30,225,9,142,
-    31,19,159,26,0,137,136,137,20,195,5,139,16,250,153,246,23,194,0,148,
-    0,193,30,136,3,170,129,1,1,0,112,136,119,135,119,119,119,24,136,120,
-    119,119,176,0,145,0,103,118,0,119,119,102,119,135,136,118,119,245,136,
-    147,2,18,212,3,16,87,128,4,85,198,110,23,99,119,119,118,200,6,31,9,
-    92,136,120,129,16,104,208,2,176,0,160,15,102,8,102,118,102,102,85,208,
-    0,118,119,55,104,119,103,16,128,20,136,177,5,241,4,169,17,65,102,177,
-    1,135,133,8,136,136,248,129,24,240,0,144,0,83,128,2,192,1,120,136,149,
-    135,192,0,120,119,64,152,0,153,192,49,195,16,193,3,153,153,120,136,
-    151,126,153,121,128,12,128,25,177,25,244,21,135,20,33,235,104,130,18,
-    194,0,36,86,64,85,162,1,0,211,21,85,117,85,85,85,118,86,96,85,85,176,
-    0,39,101,102,85,85,132,119,129,1,119,101,118,85,128,6,86,112,85,101,
-    130,4,129,8,84,101,103,85,47,101,85,117,227,25,103,181,12,128,5,130,
-    32,246,145,0,208,2,209,12,163,0,131,27,119,128,4,226,2,189,103,208,
-    6,102,194,20,128,32,195,33,82,86,255,161,10,242,35,224,30,176,27,80,
-    133,36,79,4,149,43,246,143,52,1,226,1,128,28,128,25,192,18,86,47,3,
-    159,0,79,63,102,86,117,128,34,128,31,65,160,19,211,2,191,224,40,192,
-    33,102,225,39,146,0,193,24,146,64,135,32,235,129,75,176,0,35,143,64,
-    1,152,32,135,47,17,188,128,16,128,14,103,0,21,128,18,208,48,86,49,85,
-    103,85,193,65,159,0,76,86,102,86,175,192,13,129,76,101,60,118,48,195,
-    2,52,227,132,68,53,85,128,96,119,119,151,117,34,135,29,136,152,50,102,
-    102,134,144,6,99,103,120,130,127,5,102,103,134,144,78,191,129,112,145,
-    83,119,144,32,178,84,145,79,160,34,226,112,250,130,84,195,2,159,0,75,
-    128,49,225,14,135,96,85,130,2,254,103,129,3,160,0,129,1,193,29,135,
-    4,131,32,0,255,135,224,23,142,132,135,64,4,128,5,145,6,224,1,255,132,
-    35,194,142,210,39,193,38,128,128,130,126,131,163,130,148,222,176,111,
-    145,44,241,1,118,224,34,208,120,164,50,132,120,213,87,0,192,3,101,144,
-    4,101,143,4,2,86,143,128,130,192,8,101,118,118,2,131,12,128,1,255,226,
-    15,210,175,213,13,159,0,20,130,184,248,1,241,153,130,30,191,197,62,
-    16,152,149,1,130,33,243,2,196,2,134,32,255,132,4,146,70,160,1,129,38,
-    193,0,161,17,129,26,128,35,255,195,34,145,37,161,41,132,43,139,4,132,
-    169,194,179,160,17,255,31,2,153,23,128,80,129,60,130,44,133,32,145,
-    6,34,255,82,142,116,243,213,208,194,131,25,196,0,230,90,210,60,189,
-    40,128,125,136,192,157,129,1,145,0,133,2,137,114,2,136,0,131,12,135,
-    32,135,121,96,21,153,137,153,135,144,6,135,143,4,11,136,167,31,4,129,
-    40,137,192,3,152,152,19,154,0,95,53,120,128,7,151,129,9,143,8,4,196,
-    47,101,254,129,2,131,4,73,144,56,32,204,2,176,28,240,28,240,137,227,
-    29,116,132,30,111,2
-};
-
-static MTB * mtb_KQK = NULL;
-
-void initMTB_KQK()
-{
-    mtb_KQK = new MTB ("KQK", 4, 10);
-    mtb_KQK->SetPackedData (mtbdata_KQK);
-    mtb_KQK->Add (A1, WHITE, 277);
-    mtb_KQK->Add (B1, WHITE, 381);
-    mtb_KQK->Add (C1, WHITE, 682);
-    mtb_KQK->Add (D1, WHITE, 787);
-    mtb_KQK->Add (B2, WHITE, 474);
-    mtb_KQK->Add (C2, WHITE, 561);
-    mtb_KQK->Add (D2, WHITE, 816);
-    mtb_KQK->Add (C3, WHITE, 591);
-    mtb_KQK->Add (D3, WHITE, 686);
-    mtb_KQK->Add (D4, WHITE, 699);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KRK
-
-static const byte mtbdata_KRK[7112] = {
-    1,75,0,159,0,46,32,34,2,2,159,4,7,73,208,3,36,195,4,18,17,15,2,65,68,
-    89,1,36,192,0,52,127,1,16,102,102,33,208,0,100,85,16,102,102,102,86,
-    130,12,19,101,102,134,103,102,2,224,118,12,17,129,1,134,136,119,136,
-    215,152,79,1,15,50,24,224,24,1,191,36,16,143,32,5,18,161,32,52,51,35,
-    111,8,68,84,6,224,101,192,30,203,0,147,0,85,85,133,103,3,102,119,135,
-    103,102,136,120,60,211,65,128,1,1,135,128,30,151,153,61,123,64,41,193,
-    20,161,53,47,1,49,16,136,1,218,45,130,28,144,27,33,223,27,6,112,33,
-    143,32,15,142,51,111,13,85,85,101,99,31,0,80,163,101,2,118,0,118,120,
-    119,29,134,33,193,35,136,119,119,151,15,6,144,19,255,65,196,24,209,
-    20,89,130,24,133,28,132,1,73,201,137,4,15,2,145,0,49,50,96,52,51,13,
-    48,34,34,50,35,11,49,66,131,129,32,208,60,52,84,85,85,53,44,238,50,
-    128,1,3,32,103,160,1,41,86,207,119,16,193,29,119,121,10,18,192,35,154,
-    19,1,167,154,7,34,73,128,20,251,67,196,45,159,0,3,140,4,141,52,105,
-    52,144,1,184,15,7,192,21,53,208,0,142,28,128,32,67,86,59,85,85,69,1,
-    204,0,101,117,194,31,106,5,117,7,144,0,117,192,2,133,240,28,248,135,
-    210,29,82,129,31,53,193,34,119,151,166,137,48,153,54,167,154,51,130,
-    32,219,105,193,15,176,105,70,63,1,145,0,86,137,4,252,147,46,95,7,176,
-    1,82,130,1,69,18,119,107,119,87,134,3,28,87,16,118,16,181,193,9,163,
-    26,119,176,2,150,0,117,208,1,117,60,208,27,87,152,192,0,240,25,121,
-    131,28,85,86,85,149,32,135,128,4,119,32,20,255,121,18,148,0,140,32,
-    197,32,19,128,67,178,71,225,202,170,8,140,4,194,4,135,136,136,88,183,
-    192,0,113,104,13,193,19,133,0,245,19,143,9,198,23,153,153,89,213,19,
-    0,244,52,101,200,27,151,7,16,119,137,128,21,152,247,195,6,196,4,54,
-    131,26,147,0,121,204,0,83,174,55,95,2,121,128,11,121,130,25,96,132,
-    17,255,103,132,2,130,168,15,2,145,15,128,3,143,8,6,148,19,159,15,3,
-    151,0,170,154,201,22,129,28,150,54,98,222,176,52,137,26,94,119,82,202,
-    25,143,32,3,32,46,187,171,154,198,0,170,128,15,33,16,1,82,0,159,0,79,
-    51,10,85,53,203,0,10,51,51,67,68,85,224,0,68,0,93,85,69,35,102,40,241,
-    2,1,119,6,33,86,85,85,136,87,27,0,66,117,102,129,3,119,118,119,153,
-    17,192,121,27,3,102,119,153,137,119,48,119,170,138,31,0,63,82,51,51,
-    34,62,34,17,35,15,4,17,141,32,9,147,32,0,68,68,119,85,101,102,119,86,
-    30,101,102,136,87,11,18,66,192,28,14,119,102,136,153,121,91,1,16,170,
-    102,16,135,128,30,151,61,153,64,4,102,33,100,102,102,17,31,9,1,103,
-    0,0,15,6,17,17,33,192,25,1,230,31,5,130,32,207,32,5,33,68,67,132,29,
-    15,11,190,102,0,102,192,30,203,0,3,192,33,193,28,61,119,102,104,96,
-    122,241,3,4,136,107,128,30,152,77,101,85,128,24,34,15,2,223,129,20,
-    133,28,0,33,12,141,4,61,2,119,193,31,37,207,32,4,133,32,193,28,85,144,
-    30,29,56,145,0,102,86,80,176,61,240,24,102,136,117,103,134,10,35,128,
-    3,120,192,28,135,54,129,29,102,105,89,103,135,48,34,160,184,41,187,
-    144,0,119,69,100,119,127,85,84,160,105,47,2,112,129,113,143,4,23,33,
-    127,77,70,192,24,177,85,192,56,133,22,198,0,81,54,96,119,118,193,27,
-    53,104,54,131,6,59,68,68,136,81,129,28,2,150,6,150,149,0,133,32,170,
-    137,2,105,5,194,3,191,136,113,136,192,31,6,64,3,49,47,193,103,136,101,
-    128,101,101,208,117,93,144,0,179,193,75,201,2,136,143,4,1,137,8,136,
-    104,208,110,231,128,2,66,146,112,7,136,103,49,64,229,129,1,1,145,0,
-    192,23,136,118,116,135,119,17,134,139,4,129,24,128,3,153,160,1,129,
-    25,109,195,0,152,66,117,170,129,28,32,187,187,128,1,68,139,33,102,128,
-    31,187,33,187,160,2,36,184,2,129,32,193,11,151,224,10,254,128,1,130,
-    26,193,16,5,145,0,143,4,1,129,18,47,11,179,137,160,0,135,161,21,19,
-    153,152,35,227,194,3,225,3,196,0,5,170,152,154,2,142,193,2,193,1,170,
-    136,152,48,96,148,0,16,187,169,154,137,193,8,170,153,154,138,137,113,
-    187,169,170,0,186,135,4,189,171,132,3,170,101,82,132,32,3,204,105,16,
-    221,20,192,1,153,16,201,204,64,144,0,170,192,5,170,169,170,170,170,
-    47,152,170,170,128,25,153,128,13,71,0,69,143,4,6,187,111,14,155,187,
-    171,224,2,170,46,111,2,170,153,128,2,153,31,1,197,3,33,96,153,185,19,
-    193,26,186,187,171,187,248,168,128,6,64,194,1,196,0,101,204,187,31,
-    187,171,204,169,195,2,33,240,58,50,15,70,188,187,171,221,1,80,1,128,
-    130,6,144,4,1,81,0,144,0,119,1,167,137,136,144,207,0,2,15,80,72,68,
-    1,85,69,68,54,68,102,70,47,0,0,102,194,1,0,6,118,103,102,102,134,104,
-    43,3,1,101,102,135,120,102,102,151,137,96,16,153,27,1,119,119,102,102,
-    5,152,153,120,136,184,155,16,187,128,27,1,104,118,119,119,122,23,12,
-    136,136,122,23,135,30,15,82,68,0,68,52,51,21,65,68,68,21,60,65,85,85,
-    29,2,193,35,192,28,118,114,103,101,192,30,27,35,85,85,128,32,189,119,
-    32,136,112,10,17,36,135,104,32,167,64,90,170,96,102,86,1,136,120,135,
-    24,136,136,85,21,192,192,13,45,48,102,22,83,102,102,64,22,17,63,7,32,
-    34,0,0,16,176,17,32,18,15,0,49,19,49,51,48,51,17,17,1,47,4,84,69,51,
-    3,51,21,81,69,68,21,81,176,14,247,12,131,32,192,16,192,28,112,85,192,
-    30,106,161,130,20,34,118,32,118,120,151,153,138,75,194,91,119,102,119,
-    32,119,47,10,160,136,32,88,47,6,54,83,102,85,3,21,51,51,34,18,34,34,
-    14,51,144,0,84,53,128,24,128,28,18,17,176,2,247,192,2,105,134,4,192,
-    32,82,33,75,82,114,129,32,97,128,64,47,6,144,0,119,103,0,181,119,129,
-    29,133,10,81,118,130,3,119,141,64,0,153,119,122,9,51,118,2,52,121,119,
-    184,187,121,183,9,144,187,16,134,104,48,88,136,136,124,102,86,176,73,
-    13,130,111,176,44,132,2,119,167,87,143,4,3,85,15,14,119,103,224,48,
-    1,114,145,0,87,200,0,226,19,131,24,135,104,80,123,136,120,10,58,160,
-    89,3,151,6,10,147,0,85,85,151,121,176,152,154,6,202,122,5,148,4,119,
-    153,128,32,185,48,92,185,187,2,183,5,49,224,124,120,94,153,105,192,
-    157,104,209,129,92,130,104,128,8,51,105,137,120,128,2,50,153,105,131,
-    1,231,49,134,12,143,4,17,133,3,153,153,195,4,17,39,53,169,122,51,153,
-    137,1,192,1,193,81,96,6,138,137,120,169,154,153,129,3,64,169,153,70,
-    29,185,171,100,6,123,1,22,170,153,192,2,153,34,120,201,188,0,204,153,
-    136,0,224,184,1,129,32,193,10,185,155,170,154,60,153,137,153,176,1,
-    12,146,0,143,4,32,185,131,139,111,13,187,170,154,185,139,192,2,49,104,
-    169,154,195,4,193,1,185,155,169,178,64,24,171,19,1,201,172,69,229,202,
-    23,129,10,192,4,201,188,10,156,37,21,201,204,195,6,217,205,6,170,32,
-    0,202,204,144,0,169,170,170,154,0,203,188,187,170,203,156,187,170,95,
-    187,155,7,186,0,17,95,6,1,47,95,10,219,157,95,13,221,15,8,130,2,48,
-    106,10,172,203,3,10,204,128,10,173,195,2,87,219,205,54,171,74,189,132,
-    3,128,15,0,21,1,64,0,144,0,102,102,120,119,136,5,153,137,136,136,170,
-    139,206,0,102,6,6,136,119,102,102,137,30,15,81,204,85,1,241,0,119,136,
-    31,0,0,117,193,102,193,3,1,153,120,119,119,170,136,60,2,103,119,136,
-    192,1,136,187,194,122,62,226,3,102,119,119,121,128,32,50,122,170,135,
-    62,33,85,85,224,31,24,102,113,119,153,31,1,159,32,84,68,68,1,102,17,
-    101,102,102,17,117,119,144,29,33,102,102,1,103,119,119,61,136,104,136,
-    29,33,161,28,0,151,84,128,30,167,29,170,32,119,0,119,7,153,153,137,
-    122,170,151,30,3,234,209,11,193,29,31,2,129,20,65,240,48,17,159,0,7,
-    0,0,34,2,0,0,17,1,0,96,0,33,31,1,0,68,17,68,68,51,17,17,65,16,15,4,
-    101,102,129,32,44,176,80,17,118,16,134,27,34,101,67,102,135,192,28,
-    135,136,104,104,13,161,225,3,84,136,128,30,168,136,187,123,197,10,82,
-    194,71,153,137,120,15,10,136,64,15,10,68,0,65,68,34,34,33,148,34,127,
-    0,34,34,129,56,85,128,28,34,181,17,128,2,33,92,134,4,68,35,17,237,144,
-    2,25,144,0,131,32,103,128,30,176,61,17,205,176,114,89,100,135,136,114,
-    49,106,178,57,195,3,104,80,144,25,187,138,48,88,139,184,57,187,144,
-    0,193,24,151,170,2,154,137,151,187,151,137,152,192,30,208,154,56,17,
-    101,144,40,135,153,119,95,119,135,160,73,119,0,129,1,5,145,0,95,160,
-    83,102,128,4,103,128,2,78,227,51,193,16,117,31,14,120,32,63,0,27,121,
-    0,153,129,10,230,147,153,136,103,136,136,137,154,1,208,94,137,106,5,
-    142,32,204,128,155,240,204,192,31,52,1,192,11,119,152,170,0,154,153,
-    152,187,152,153,152,170,46,152,153,153,48,154,52,145,0,193,15,75,136,
-    170,144,132,170,152,54,168,52,183,129,36,134,4,153,128,2,49,136,132,
-    1,194,1,251,111,1,9,63,3,201,9,57,1,155,20,173,4,53,187,129,2,154,52,
-    2,139,212,2,147,0,51,187,16,204,192,3,204,92,155,153,96,185,65,129,
-    32,129,9,170,0,204,171,153,170,204,170,153,170,174,187,67,169,0,171,
-    0,145,0,130,4,235,187,64,196,3,5,186,79,10,170,131,1,190,15,7,193,13,
-    171,143,12,10,131,2,11,116,194,21,165,187,112,203,47,0,204,172,21,154,
-    118,16,156,38,194,6,16,221,192,3,23,96,201,204,144,0,193,21,186,204,
-    188,187,46,186,221,187,26,187,17,32,143,4,6,185,170,127,13,186,243,
-    3,15,8,143,8,14,221,189,95,15,9,189,80,204,12,85,193,3,83,192,47,0,
-    7,17,1,68,0,159,0,77,153,136,68,3,52,0,51,51,85,69,68,68,136,72,34,
-    68,68,153,201,0,104,86,85,1,193,102,129,4,0,136,88,85,85,153,32,41,
-    121,119,3,103,102,102,136,14,118,119,119,153,120,32,192,2,5,32,187,
-    155,136,51,120,118,119,153,14,137,136,136,187,154,4,128,3,1,49,204,
-    188,153,52,128,2,187,153,153,241,1,34,193,1,32,127,78,85,84,51,230,
-    86,193,32,130,33,40,136,102,242,31,134,32,209,102,240,33,17,119,20,
-    153,121,134,79,34,102,144,0,136,118,192,28,129,29,88,115,16,138,143,
-    32,3,52,192,29,185,187,0,39,95,102,102,100,131,28,68,68,192,31,15,1,
-    102,31,16,135,242,27,135,64,135,136,201,0,16,219,170,65,24,170,9,28,
-    169,129,2,69,2,187,3,153,84,152,227,67,84,99,208,12,67,144,18,12,85,
-    67,84,131,24,211,193,1,183,57,150,0,102,200,25,102,70,129,6,111,75,
-    136,145,55,1,100,209,0,193,27,240,0,71,56,153,162,59,102,102,119,129,
-    4,193,31,200,84,241,2,17,153,121,243,91,119,103,89,119,119,131,32,136,
-    128,31,119,187,155,207,132,24,129,34,18,152,154,57,192,29,241,31,170,
-    97,144,1,153,81,187,114,187,83,76,153,86,39,102,83,144,29,92,136,94,
-    86,102,131,80,84,208,17,143,4,18,145,0,72,110,153,103,67,129,28,119,
-    193,30,81,136,32,150,170,244,119,136,102,192,0,134,213,1,39,56,170,
-    138,169,132,60,128,30,176,65,170,136,228,153,96,192,2,84,187,171,131,
-    4,153,134,121,129,25,168,136,187,170,64,88,105,204,188,242,63,128,32,
-    152,48,169,171,136,129,2,16,219,204,187,18,203,153,134,135,148,85,103,
-    152,153,119,128,44,109,127,153,134,149,89,192,48,80,131,113,136,4,134,
-    8,204,115,33,193,21,136,118,35,29,136,201,119,209,57,39,170,152,242,
-    23,170,170,195,193,6,128,28,240,52,153,170,136,168,192,27,17,209,30,
-    170,170,204,244,3,170,138,137,227,195,5,241,29,96,197,0,204,187,186,
-    132,4,4,144,3,154,186,170,204,186,34,171,140,187,36,204,204,186,130,
-    32,145,7,187,112,187,186,192,31,32,192,2,204,203,186,136,219,32,203,
-    187,152,242,3,187,187,62,152,170,187,193,21,194,48,177,110,36,136,4,
-    168,170,41,170,47,14,138,192,18,135,170,223,170,46,196,28,153,192,27,
-    129,1,176,80,147,0,53,240,58,170,151,195,2,114,137,208,27,137,127,129,
-    32,204,131,4,176,2,193,9,193,3,240,24,242,1,48,241,30,221,203,20,224,
-    1,204,171,203,3,187,204,186,204,187,204,202,192,2,140,113,192,0,221,
-    220,204,148,0,128,35,204,54,203,204,221,160,1,66,203,208,0,1,223,185,
-    244,3,224,129,187,176,69,240,68,81,6,68,143,4,10,187,31,14,171,187,
-    187,31,6,187,241,169,193,30,31,4,194,3,241,86,169,218,205,198,128,26,
-    244,23,128,27,186,203,204,133,32,196,0,11,205,221,204,221,205,164,4,
-    187,33,164,130,6,33,202,67,238,220,136,4,220,166,220,114,221,192,2,
-    221,219,128,3,128,5,1,8,224,206,187,170,145,0,154,153,2,153,153,201,
-    204,187,170,233,128,2,112,217,205,195,0,17,15,78,183,171,90,128,85,
-    5,119,119,85,85,167,170,16,85,85,183,171,39,184,171,119,128,136,1,119,
-    87,85,85,120,119,1,119,119,168,170,135,136,184,171,48,39,185,187,242,
-    3,161,4,119,169,138,28,136,136,185,171,0,192,2,5,218,130,221,131,13,
-    169,170,136,136,186,240,25,114,218,189,0,64,5,157,201,195,28,166,169,
-    144,32,155,192,1,155,185,11,159,0,78,39,119,87,88,134,32,102,86,192,
-    32,128,29,57,105,168,138,144,27,113,5,138,120,230,58,192,28,224,30,
-    111,11,202,204,80,42,176,205,119,173,209,2,129,64,169,170,170,3,172,
-    74,187,170,171,74,185,90,37,63,78,135,104,225,26,119,119,193,31,102,
-    49,16,119,103,208,1,8,168,138,135,250,123,128,2,105,192,28,130,9,132,
-    32,153,64,196,170,193,2,21,218,221,171,76,171,36,202,218,205,23,187,
-    123,196,60,122,1,170,170,186,75,187,170,119,71,136,208,12,24,170,74,
-    151,242,3,153,73,9,151,153,170,74,68,158,0,118,87,75,242,22,102,129,
-    56,118,71,208,0,69,75,231,211,55,1,136,1,40,153,121,228,55,192,29,100,
-    129,28,120,240,93,105,169,154,131,4,135,226,104,129,33,195,60,57,185,
-    187,168,132,32,174,135,128,2,152,0,168,128,31,119,0,214,187,145,0,0,
-    170,4,203,48,70,83,219,125,242,3,187,1,170,74,240,32,35,12,186,123,
-    4,89,151,153,205,35,38,33,153,121,131,52,151,89,192,23,143,4,1,226,
-    169,132,56,66,207,32,1,185,155,137,67,12,121,120,119,120,135,192,4,
-    240,5,120,140,151,199,0,185,155,169,160,147,144,0,121,4,152,153,153,
-    137,153,153,129,117,169,88,154,169,54,186,160,27,130,20,153,152,145,
-    153,209,88,186,171,48,187,169,172,18,53,219,221,172,132,32,168,170,
-    224,28,0,171,219,189,172,187,219,205,172,146,219,54,141,203,130,4,186,
-    139,195,64,98,136,120,208,17,56,186,139,186,131,68,112,138,153,198,
-    3,194,54,12,169,138,153,249,153,15,14,129,24,2,130,26,195,0,202,156,
-    146,135,144,1,170,138,128,81,155,185,64,152,186,2,203,172,36,160,28,
-    170,154,14,170,170,187,171,187,194,2,129,1,194,1,128,204,131,56,185,
-    187,169,153,187,155,10,186,187,203,188,187,5,188,1,120,235,222,208,
-    1,129,28,160,32,225,0,219,189,10,188,187,235,206,188,192,32,188,128,
-    32,8,188,219,220,173,203,241,99,204,202,15,172,203,170,187,139,160,
-    36,128,16,201,0,172,128,4,146,8,186,129,14,139,160,72,132,3,204,128,
-    140,79,45,204,202,188,204,140,202,224,188,26,128,24,193,3,187,171,186,
-    187,17,187,171,202,188,129,1,220,189,204,153,147,24,176,19,203,156,
-    224,0,240,89,204,188,66,161,0,187,192,0,203,188,236,206,135,4,37,204,
-    172,203,162,3,220,205,0,204,16,51,237,222,221,69,187,204,204,0,203,
-    188,221,205,220,188,237,206,144,221,128,1,221,204,48,220,237,190,97,
-    221,205,146,32,128,7,221,189,221,205,194,129,12,41,9,220,189,221,204,
-    201,3,68,221,173,127,45,221,220,205,31,6,221,7,189,220,205,221,189,
-    219,31,3,3,31,224,23,189,204,205,192,25,19,160,4,193,26,200,225,30,
-    128,1,166,0,253,239,119,237,238,224,221,130,6,192,34,195,0,206,221,
-    221,1,8,160,221,190,170,145,0,136,136,3,170,153,171,187,187,170,171,
-    128,2,10,201,0,136,221,173,153,1,136,1,56,187,171,153,17,41,15,78,136,
-    204,64,187,119,3,118,119,136,153,121,17,119,136,204,123,42,153,204,
-    156,28,131,5,153,118,119,146,1,81,24,186,70,221,172,198,9,186,204,170,
-    66,25,43,188,204,189,134,32,187,16,187,64,21,9,201,136,171,22,153,192,
-    4,153,200,49,9,31,78,153,166,226,31,102,102,24,103,102,136,136,193,
-    32,11,153,204,84,154,154,26,187,240,1,204,7,186,27,221,173,201,204,
-    144,0,20,169,192,30,68,64,173,54,172,204,189,240,30,187,66,187,155,
-    131,64,187,172,204,185,58,193,203,197,28,16,170,136,166,153,187,176,
-    60,31,80,167,130,1,160,32,119,136,119,7,135,136,136,119,151,153,41,
-    0,186,131,56,23,170,193,2,101,128,30,202,18,140,170,192,33,204,170,
-    186,129,2,57,187,85,204,188,18,170,128,29,185,128,1,188,140,192,30,
-    9,170,170,169,130,12,144,17,153,45,170,170,166,144,64,150,208,12,89,
-    134,83,244,35,134,160,4,102,159,0,0,136,104,135,1,38,224,56,102,135,
-    43,136,119,162,107,128,27,49,135,136,119,2,91,153,170,152,17,132,12,
-    135,136,153,192,5,153,119,168,162,208,33,7,170,134,32,151,153,169,128,
-    5,253,170,16,194,2,4,192,60,130,28,16,170,88,0,187,192,30,203,64,86,
-    187,204,130,204,3,170,171,170,187,187,192,1,99,204,201,128,31,71,170,
-    204,185,19,167,193,11,16,170,29,187,137,166,53,192,33,143,144,25,11,
-    153,187,138,228,3,193,23,192,2,125,1,135,7,139,4,131,33,194,34,68,170,
-    144,160,19,130,28,170,152,96,137,136,170,222,119,208,1,25,221,67,129,
-    37,194,53,129,6,36,204,170,172,101,187,221,131,92,170,66,187,155,128,
-    122,186,187,187,221,128,62,38,221,189,219,53,203,221,132,32,176,0,130,
-    203,192,33,203,221,201,187,203,128,31,136,204,116,187,204,202,130,4,
-    169,187,216,186,128,132,131,66,187,194,66,67,186,204,122,186,171,129,
-    16,224,1,35,193,2,187,143,4,26,225,187,104,147,0,224,20,171,186,187,
-    186,241,195,0,108,194,3,80,37,202,221,203,131,50,67,186,171,202,204,
-    203,33,136,208,94,19,203,238,205,132,28,171,170,56,203,221,202,64,192,
-    28,192,2,203,221,131,205,192,31,205,219,219,238,220,147,36,0,144,0,
-    203,204,220,188,219,221,218,10,189,203,204,202,188,49,204,129,1,253,
-    238,147,100,129,15,133,3,49,128,35,195,1,221,235,135,4,193,37,201,4,
-    63,15,188,63,10,204,195,16,42,56,203,204,241,85,203,128,19,204,199,
-    0,76,238,205,22,204,221,204,3,64,220,88,238,221,241,127,204,208,128,
-    81,204,220,36,221,220,204,193,2,220,221,66,220,49,221,255,221,146,0,
-    192,33,188,221,238,126,144,1,238,224,2,240,2,70,49,132,4,224,1,223,
-    205,202,3,67,205,129,12,129,22,193,10,48,250,193,4,199,0,127,43,128,
-    14,111,7,193,17,204,0,249,237,2,194,29,6,193,3,196,0,255,238,208,6,
-    193,5,115,238,37,221,1,1,208,205,221,221,253,223,221,14,146,0,220,221,
-    205,188,146,1,192,2,194,0,129,189,49,204,204,237,190,205,204,1,2,188,
-    204,188,221,189,205,204,128,49,192,1,204,187,203,187,187,236,48,190,
-    204,188,129,1,83,170,170,220,48,189,204,188,49,165,0,219,221,170,16,
-    170,203,188,170,18,168,170,102,9,102,219,205,170,170,33,203,204,0,35,
-    218,205,156,153,218,205,155,134,153,17,152,153,102,102,193,1,33,64,
-    202,204,19,218,205,187,153,234,224,206,139,4,129,2,133,5,219,205,172,
-    170,34,235,222,172,130,12,169,170,153,67,96,206,172,192,12,19,236,206,
-    204,204,100,252,239,243,23,129,14,220,221,67,236,224,222,16,192,3,128,
-    29,188,235,206,221,159,188,148,24,203,220,64,162,29,226,29,192,27,0,
-    159,0,77,200,188,108,102,200,188,107,200,102,85,193,28,200,188,199,
-    0,201,188,16,153,153,217,189,242,31,153,152,121,123,102,102,129,1,193,
-    2,5,192,26,172,129,42,96,133,32,203,176,33,201,44,220,205,205,220,112,
-    236,222,131,56,117,129,2,236,206,205,195,204,192,3,144,28,204,204,190,
-    187,208,2,153,129,4,224,29,188,171,144,31,196,0,187,171,3,239,31,78,
-    187,187,152,121,121,119,49,224,147,0,128,32,176,1,57,169,170,154,153,
-    33,201,172,154,122,185,171,153,153,187,193,29,130,30,188,34,146,64,
-    38,189,195,78,109,17,220,240,28,192,61,220,241,90,87,204,129,192,31,
-    19,187,139,187,187,205,172,156,83,193,30,170,106,144,9,121,159,0,77,
-    153,161,137,128,25,138,131,28,136,120,119,119,196,129,2,57,160,4,153,
-    202,172,133,64,119,189,119,161,5,186,128,32,129,3,1,192,28,204,223,
-    64,130,24,4,187,0,192,57,99,132,64,48,12,205,220,129,3,81,170,138,170,
-    36,170,221,157,35,204,156,192,32,107,24,187,187,153,105,208,12,20,136,
-    104,8,136,136,204,108,201,242,3,187,107,24,185,187,204,108,182,85,146,
-    0,136,120,25,136,136,153,121,131,20,161,56,153,105,127,208,0,103,87,
-    131,28,131,4,193,5,193,20,130,6,1,68,170,154,170,170,186,155,186,128,
-    198,33,240,61,153,186,155,185,170,170,98,154,169,35,144,0,203,188,186,
-    194,69,8,169,138,169,170,171,128,8,187,171,197,186,160,141,99,203,188,
-    202,128,32,202,139,130,20,161,64,203,172,186,48,202,192,31,190,195,
-    0,192,28,221,128,30,242,154,96,144,65,66,227,221,129,3,1,65,237,158,
-    237,130,4,89,17,187,16,155,24,193,18,220,157,51,20,123,185,202,35,37,
-    202,156,128,22,95,145,0,122,32,106,27,129,12,25,197,37,2,133,32,186,
-    155,187,170,202,172,19,98,186,139,208,0,192,30,186,171,186,192,0,203,
-    185,82,129,31,219,189,212,101,155,128,33,20,204,114,204,188,203,192,
-    28,203,131,20,187,188,186,131,2,189,80,128,1,193,0,129,185,237,133,
-    222,131,188,219,221,202,204,224,28,205,129,82,196,188,187,155,187,187,
-    237,174,60,131,32,220,173,195,64,193,50,197,0,129,4,220,201,173,161,
-    101,144,0,172,202,198,3,204,156,241,42,131,136,192,47,47,23,224,145,
-    204,156,203,158,194,140,29,204,172,80,192,13,130,1,160,177,127,220,
-    189,20,225,177,128,6,228,179,194,0,193,212,15,135,28,187,187,221,189,
-    176,93,130,215,84,248,144,0,135,220,128,34,224,0,137,32,193,8,254,207,
-    64,253,238,49,236,206,237,205,204,230,172,129,29,200,0,128,4,237,206,
-    163,12,192,195,55,204,221,173,160,72,196,3,221,129,38,67,253,131,12,
-    32,201,7,47,2,138,8,192,17,47,6,221,255,177,118,193,0,130,28,20,240,
-    21,17,195,3,129,24,252,176,8,130,248,83,177,88,130,32,53,192,0,254,
-    4,223,238,222,255,223,254,164,36,221,5,221,238,222,237,222,254,128,
-    2,238,128,160,0,33,1,0,224,238,221,205,237,255,223,64,205,204,144,0,
-    170,204,204,171,237,98,238,222,128,2,197,0,221,205,221,128,4,199,221,
-    192,1,51,188,171,221,48,17,128,130,1,192,3,204,204,189,187,204,238,
-    64,189,187,145,0,169,187,187,171,204,225,221,115,194,0,192,1,185,221,
-    189,171,67,65,170,32,154,153,153,153,53,24,134,3,221,173,186,65,130,
-    8,170,138,192,136,5,37,203,238,238,171,203,99,238,206,50,129,4,203,
-    221,205,2,4,39,222,189,203,255,222,132,16,139,14,136,203,238,221,187,
-    1,128,3,18,142,220,17,255,223,203,129,24,193,4,48,32,203,220,255,87,
-    205,238,220,220,8,205,238,222,220,221,144,0,170,170,140,204,112,221,
-    220,204,16,35,221,1,220,218,221,204,171,170,238,205,252,130,16,50,112,
-    82,97,193,1,15,77,169,3,221,221,136,169,238,221,136,5,16,193,28,169,
-    221,141,54,187,221,189,79,188,187,134,48,136,136,146,1,129,2,192,47,
-    1,1,220,238,205,220,220,255,206,9,241,31,204,187,204,188,48,204,204,
-    254,192,2,86,241,29,128,57,130,4,129,32,114,24,14,203,170,205,171,219,
-    128,3,241,10,129,18,227,187,193,1,120,95,79,187,200,153,65,1,145,0,
-    136,136,137,136,169,170,152,198,128,2,71,192,26,188,187,221,16,89,224,
-    221,240,1,192,29,130,30,221,205,219,220,51,238,206,219,129,60,131,64,
-    204,203,128,31,5,51,221,205,203,205,221,224,0,238,25,225,3,221,221,
-    189,128,26,130,2,221,219,12,54,204,187,202,187,144,39,242,10,187,156,
-    170,128,92,187,200,224,1,7,159,0,78,170,91,187,185,0,201,194,1,160,
-    32,153,225,96,128,195,2,83,203,204,188,204,203,221,223,188,130,56,129,
-    34,203,128,5,129,2,130,3,64,127,224,25,220,128,32,130,92,132,64,0,192,
-    2,17,176,32,192,60,221,96,146,0,204,187,205,113,187,219,113,128,31,
-    37,204,204,202,98,160,11,219,131,72,80,204,204,200,128,1,195,184,160,
-    1,3,170,170,168,170,195,1,234,129,68,113,182,85,132,86,169,128,53,169,
-    130,56,33,153,153,154,16,168,153,169,170,252,199,0,144,74,209,74,229,
-    61,243,0,197,2,112,187,183,187,176,131,204,242,131,224,1,169,128,2,
-    176,0,255,65,84,146,19,243,87,128,148,128,34,193,20,132,2,243,145,0,
-    225,57,128,32,130,28,2,238,204,32,217,227,60,193,0,83,237,51,144,7,
-    221,221,173,193,1,60,221,59,204,8,130,17,221,181,225,14,145,0,185,136,
-    3,136,4,204,99,170,253,160,156,111,0,209,10,25,129,27,129,2,131,28,
-    203,127,192,3,203,160,51,18,160,116,129,1,229,13,131,18,191,135,32,
-    129,37,204,224,62,105,193,59,192,186,131,28,28,240,181,237,238,220,
-    128,2,131,62,145,0,237,13,238,236,221,237,255,20,176,0,237,30,192,33,
-    237,238,235,194,0,134,33,192,4,130,72,46,221,220,187,195,34,203,176,
-    12,177,170,193,13,191,219,192,44,220,130,196,208,15,131,16,201,0,131,
-    4,183,132,20,28,188,31,10,145,0,203,194,3,21,211,224,2,20,136,28,221,
-    128,216,238,237,130,16,118,131,2,220,209,58,198,0,130,26,255,147,30,
-    130,32,224,255,128,30,194,28,195,30,238,238,237,238,14,238,255,254,
-    239,238,144,0,208,1,176,0,170,254,128,2,252,16,236,160,2,236,224,3,
-    127,237,222,128,8,146,40,160,69,192,10,81,193,70,255,129,101,193,41,
-    193,9,134,4,129,16,163,1,196,0,111,44,239,129,52,47,6,163,26,131,28,
-    239,39,130,19,129,6,1,99,254,255,254,238,254,255,255,135,241,27,161,
-    29,205,238,255,237,128,2,192,28,0,148,0,1,1,224,238,237,238,254,255,
-    254,46,193,0,239,238,144,0,237,0,194,1,132,2,3,221,238,236,238,237,
-    255,252,225,4,138,241,1,64,222,221,237,128,6,237,162,0,4,236,238,221,
-    221,236,221,39,221,11,221,219,221,221,205,192,8,221,224,3,69,133,3,
-    205,35,220,205,204,144,0,203,211,32,128,4,192,4,237,194,2,221,221,129,
-    1,248,195,1,31,3,194,9,208,14,160,13,145,0,204,221,174,205,97,222,48,
-    222,146,2,193,1,128,18,99,238,255,242,19,129,24,205,238,255,211,1,162,
-    193,21,129,1,255,192,25,255,255,255,133,28,207,221,193,1,195,2,254,
-    239,177,8,161,25,160,32,69,194,6,204,128,8,254,255,253,164,33,237,126,
-    224,14,220,192,12,192,32,130,2,128,4,130,28,165,0,125,236,238,193,22,
-    224,18,192,0,128,5,144,0,203,163,194,26,193,1,220,224,2,204,204,219,
-    128,2,194,1,129,27,33,188,187,187,187,202,2,40,221,205,220,115,204,
-    146,8,203,204,118,172,170,44,128,10,48,238,144,31,130,36,124,172,170,
-    193,1,134,3,148,0,131,46,129,16,173,239,170,37,53,128,25,221,128,36,
-    198,2,192,59,221,51,130,5,160,54,221,193,41,131,42,224,47,205,165,131,
-    31,145,0,220,160,0,219,205,224,0,205,253,49,180,24,209,29,131,2,196,
-    54,79,74,192,36,170,17,194,0,221,204,170,69,203,221,172,212,192,2,99,
-    193,4,220,161,0,238,132,72,221,125,170,170,161,47,227,47,177,21,193,
-    32,241,64,222,238,200,66,193,55,165,74,129,32,236,192,31,193,0,81,48,
-    236,205,238,194,1,163,8,187,187,188,13,187,221,204,219,205,114,130,
-    4,203,226,240,50,131,2,227,85,159,0,76,187,187,186,160,70,8,186,187,
-    203,204,202,128,1,187,187,62,170,170,171,128,31,195,2,193,0,131,32,
-    134,30,252,188,101,183,53,2,179,102,1,50,221,223,237,193,2,15,2,221,
-    12,197,28,133,92,176,82,87,128,31,202,160,2,202,96,218,198,74,159,0,
-    70,117,193,30,203,224,32,193,0,3,170,192,1,203,205,213,1,141,32,128,
-    64,186,187,127,1,12,221,95,8,220,144,33,236,208,3,131,96,241,34,129,
-    2,253,193,0,195,30,129,36,194,128,164,18,193,2,129,31,218,103,224,12,
-    202,130,52,130,34,221,218,164,14,193,35,118,182,85,170,130,53,193,55,
-    136,56,187,8,131,28,254,203,200,26,193,2,43,147,25,168,98,156,0,130,
-    94,159,238,180,113,238,219,128,2,135,115,135,32,133,164,253,129,33,
-    88,128,31,209,67,193,192,147,0,161,161,254,248,130,172,128,156,192,
-    32,128,32,193,31,200,138,221,238,185,234,8,203,131,14,132,148,17,220,
-    221,251,131,68,195,0,130,4,211,164,134,36,20,188,47,6,199,197,28,131,
-    1,33,220,238,222,148,26,193,2,255,151,60,224,49,128,194,192,54,179,
-    91,192,19,128,58,128,2,190,128,3,128,151,221,240,185,131,185,192,158,
-    243,27,177,195,239,255,195,2,193,28,192,189,255,192,31,162,0,145,0,
-    191,129,5,227,1,253,144,198,227,188,131,8,193,194,162,39,127,208,41,
-    253,216,169,193,15,195,40,131,2,241,0,197,2,255,192,74,134,4,133,214,
-    247,40,11,137,8,195,3,131,223,255,194,27,11,192,8,197,2,150,91,201,
-    222,196,225,193,3,184,133,221,133,224,238,240,0,37,241,1
-};
-
-static MTB * mtb_KRK = NULL;
-
-void initMTB_KRK()
-{
-    mtb_KRK = new MTB ("KRK", 4, 10);
-    mtb_KRK->SetPackedData (mtbdata_KRK);
-    mtb_KRK->Add (A1, WHITE, 554);
-    mtb_KRK->Add (B1, WHITE, 621);
-    mtb_KRK->Add (C1, WHITE, 635);
-    mtb_KRK->Add (D1, WHITE, 606);
-    mtb_KRK->Add (B2, WHITE, 711);
-    mtb_KRK->Add (C2, WHITE, 782);
-    mtb_KRK->Add (D2, WHITE, 718);
-    mtb_KRK->Add (C3, WHITE, 879);
-    mtb_KRK->Add (D3, WHITE, 839);
-    mtb_KRK->Add (D4, WHITE, 767);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KPK
-
-static const byte mtbdata_KPK[6552] = {
-    1,65,255,159,0,109,0,3,3,3,0,130,0,134,2,15,14,15,15,15,144,2,134,31,
-    1,7,0,0,63,1,160,2,148,127,1,31,31,142,8,1,148,8,1,97,7,7,224,5,128,
-    1,14,14,14,2,209,49,241,7,96,1,82,15,0,1,201,82,143,8,1,164,16,1,0,
-    147,8,1,0,223,48,130,1,131,16,1,33,17,33,15,3,255,51,80,50,64,50,64,
-    48,2,255,17,129,10,17,143,8,2,18,1,17,1,239,17,1,17,0,15,16,130,18,
-    16,255,143,8,3,16,67,36,16,131,10,36,16,128,131,26,15,0,1,65,255,159,
-    0,117,0,7,7,7,0,144,0,133,2,3,31,1,0,0,15,132,63,1,15,0,31,127,1,31,
-    88,31,63,226,3,63,243,0,129,7,14,15,84,15,15,145,7,2,148,8,3,131,2,
-    3,219,7,240,7,144,2,15,33,160,2,31,33,233,176,2,162,7,16,134,8,15,4,
-    7,5,34,3,15,11,130,2,3,31,23,132,16,173,47,4,95,4,191,15,0,145,6,2,
-    252,224,0,97,1,128,2,18,96,18,63,254,63,19,32,164,7,135,8,242,4,146,
-    20,145,22,242,7,133,2,131,32,160,1,242,7,63,63,99,123,127,127,150,7,
-    148,0,244,3,245,4,7,140,2,208,128,14,19,116,127,141,47,1,73,255,159,
-    0,109,60,63,1,0,0,6,133,2,0,14,14,14,0,0,37,145,7,132,4,31,127,1,31,
-    0,63,16,226,2,63,63,127,130,1,127,127,60,161,62,240,3,62,144,6,28,31,
-    31,31,82,145,7,4,148,8,7,131,2,7,15,128,4,237,15,128,8,65,160,2,63,
-    1,176,2,127,153,1,48,60,63,1,144,5,31,29,36,147,6,14,10,35,31,23,130,
-    2,7,87,63,47,132,16,95,52,191,52,19,239,50,145,4,49,145,6,4,224,0,97,
-    17,231,128,2,18,128,4,18,127,127,19,64,255,18,48,131,32,144,4,226,0,
-    146,20,144,0,48,231,132,2,99,160,1,242,7,127,127,115,144,7,239,133,
-    1,244,2,36,245,4,14,140,2,133,4,245,6,0,150,0,1,81,255,159,0,109,254,
-    227,0,0,120,127,32,1,0,0,133,3,0,28,28,28,212,0,0,133,2,15,132,4,63,
-    226,1,63,69,0,127,130,1,127,127,254,145,8,254,80,128,8,124,240,3,124,
-    144,6,56,62,62,169,62,145,7,8,20,14,131,2,15,31,110,128,4,31,32,97,
-    63,16,33,176,2,209,254,162,16,129,16,125,130,12,0,62,58,36,147,6,28,
-    20,35,62,46,130,2,15,95,127,95,116,191,68,35,66,240,21,239,66,145,4,
-    17,145,6,8,224,0,97,17,254,128,2,18,128,4,18,128,6,18,96,243,7,127,
-    255,255,131,32,144,4,226,0,144,6,130,1,96,158,132,2,3,127,127,243,7,
-    144,7,255,41,4,245,2,176,62,68,28,108,254,6,1,64,255,159,0,117,0,7,
-    7,7,3,130,0,0,14,15,15,7,7,128,1,32,31,31,31,160,1,0,0,63,63,134,63,
-    49,0,127,127,127,49,142,7,48,0,3,3,97,135,2,15,14,14,247,2,145,9,144,
-    8,18,32,15,128,8,33,166,176,2,95,0,1,35,0,1,160,16,130,1,63,14,14,14,
-    50,17,49,18,48,221,18,143,8,1,164,16,1,177,24,2,19,1,255,49,2,17,129,
-    10,17,143,8,2,19,0,241,18,0,18,0,18,1,0,15,255,16,130,18,16,143,8,3,
-    16,35,116,0,240,131,10,36,0,131,26,47,0,1,72,255,159,0,109,0,15,0,0,
-    0,161,0,137,2,7,32,7,31,31,31,8,145,3,7,63,63,63,160,1,0,31,46,127,
-    127,127,81,63,192,4,81,142,9,54,0,7,7,129,7,54,3,144,8,144,2,111,7,
-    15,64,144,9,31,65,176,2,162,7,164,16,135,7,14,147,16,0,2,3,0,175,3,
-    131,2,3,161,16,7,128,8,98,16,210,35,179,7,25,15,36,7,5,35,70,15,11,
-    130,2,3,31,23,192,6,129,16,219,47,1,240,7,95,1,160,7,191,1,191,59,145,
-    6,2,161,31,0,17,128,2,18,251,160,16,18,49,17,33,187,31,0,161,19,255,
-    145,36,131,1,128,8,132,2,129,32,18,242,7,16,128,82,174,39,1,72,255,
-    159,0,109,62,63,1,0,0,14,0,31,31,31,30,240,0,133,3,129,2,164,15,64,
-    15,224,3,31,31,128,1,127,77,127,127,160,1,0,63,192,3,1,127,10,226,4,
-    127,127,0,60,144,4,62,136,7,22,0,14,14,14,137,9,7,128,4,144,9,109,15,
-    31,144,8,0,63,17,176,2,127,153,17,64,0,60,161,16,16,0,28,146,160,14,
-    240,0,0,4,3,0,7,131,2,191,7,113,15,128,8,2,96,51,208,1,210,133,16,17,
-    145,24,29,147,6,14,10,19,70,31,23,130,2,7,63,47,129,4,48,223,95,65,
-    240,7,191,49,64,50,35,223,49,1,145,6,4,224,0,17,1,128,2,255,2,160,16,
-    2,161,31,1,178,23,131,28,129,32,255,130,8,242,0,0,130,1,0,132,2,113,
-    2,224,242,7,32,131,35,4,255,1,80,255,159,0,109,252,226,0,254,0,124,
-    0,127,127,127,126,126,0,0,0,29,62,62,62,60,240,0,133,4,129,2,30,78,
-    64,31,48,63,63,128,1,192,2,160,1,66,0,127,226,3,127,127,252,254,241,
-    7,20,254,0,0,120,144,4,124,136,7,0,42,28,28,28,137,9,15,128,4,31,128,
-    8,217,63,208,1,144,9,127,145,8,176,2,252,254,230,50,0,161,16,48,0,56,
-    160,14,240,0,74,0,8,3,0,14,131,2,15,113,252,31,96,2,96,51,128,16,19,
-    120,105,127,125,192,2,145,24,58,147,6,28,20,35,19,62,46,130,2,15,127,
-    95,129,4,127,32,191,49,240,7,50,35,128,20,34,223,49,1,145,6,8,224,0,
-    17,1,128,2,255,2,160,16,2,178,23,131,40,243,7,128,8,242,3,255,96,242,
-    0,0,130,1,0,132,2,129,32,2,128,131,6,244,7,255,1,64,255,159,0,181,0,
-    0,7,7,7,65,3,0,0,14,15,15,7,7,16,128,1,31,31,31,160,1,0,0,63,99,63,
-    63,48,143,7,8,0,3,3,80,15,136,2,15,14,14,2,145,9,144,8,2,94,16,15,143,
-    8,10,1,18,128,1,160,16,98,63,14,14,14,2,49,1,33,143,8,10,203,115,0,
-    35,1,0,3,1,97,127,49,15,0,95,11,0,51,84,16,224,131,10,4,16,15,6,1,68,
-    255,159,0,173,0,0,15,0,0,128,0,138,2,7,0,0,0,7,31,66,31,31,145,3,7,
-    63,63,63,161,1,24,31,127,127,127,64,143,9,8,0,7,217,7,128,7,39,3,144,
-    8,144,2,7,15,173,16,192,10,31,17,31,142,8,112,14,29,146,16,0,0,2,18,
-    144,7,131,2,3,125,161,16,7,96,19,208,1,17,95,1,15,36,4,7,5,3,15,11,
-    130,2,3,109,31,23,192,6,129,16,47,1,240,7,95,223,32,111,5,129,8,2,164,
-    31,33,49,33,240,49,33,1,17,14,1,68,255,159,0,173,0,62,63,1,0,1,0,0,
-    31,31,31,30,0,0,169,134,3,129,2,15,240,2,15,224,3,31,31,23,128,1,127,
-    127,127,161,1,63,208,3,112,80,135,5,60,144,4,62,136,7,0,14,14,179,14,
-    137,9,7,128,4,144,9,15,31,144,8,122,192,10,63,177,7,210,0,133,8,161,
-    16,62,128,7,206,28,224,3,240,0,0,4,146,8,144,7,131,2,191,7,81,15,128,
-    8,19,208,1,17,137,16,164,1,145,24,29,147,6,14,10,3,31,141,23,130,2,
-    7,63,47,129,4,16,95,190,33,240,7,191,137,32,3,129,8,1,81,255,4,240,
-    0,81,65,17,33,17,33,192,17,198,15,145,0,1,72,255,159,0,173,0,252,242,
-    0,254,0,0,124,127,127,127,126,126,0,0,3,0,62,62,62,60,0,0,134,4,83,
-    129,2,30,240,2,31,32,63,63,128,1,161,208,2,161,1,127,242,3,127,0,252,
-    254,10,241,7,254,0,0,120,144,4,124,136,7,21,0,28,28,28,137,9,15,128,
-    4,31,91,128,8,63,224,1,63,112,145,8,127,17,214,34,16,161,16,124,128,
-    7,56,160,14,240,0,69,0,8,34,0,0,14,131,2,15,126,97,31,80,35,208,1,133,
-    16,33,1,210,125,192,2,145,24,58,147,6,28,20,19,70,62,46,130,2,15,127,
-    95,129,4,32,255,191,97,240,7,33,20,129,8,17,97,191,17,1,8,240,0,1,17,
-    1,17,224,1,17,1,162,32,1,64,255,159,0,245,0,0,0,7,7,64,7,3,240,0,0,
-    14,15,15,7,130,7,128,1,31,31,31,15,15,143,7,17,8,0,3,3,3,0,137,2,15,
-    14,107,14,2,33,144,8,7,143,8,18,1,97,199,33,160,16,18,14,14,14,18,0,
-    234,127,19,32,19,100,0,20,7,31,16,1,66,255,159,0,237,0,0,0,15,0,161,
-    0,139,2,7,240,0,7,31,31,31,1,144,3,0,7,63,63,63,31,31,11,143,9,17,0,
-    7,7,7,89,3,144,8,43,144,9,7,15,112,15,143,7,10,14,145,16,115,144,0,
-    2,17,145,7,131,2,3,15,16,255,130,17,0,143,8,10,146,24,151,23,117,129,
-    16,50,128,240,7,15,7,1,66,255,159,0,237,0,0,62,63,1,1,0,0,0,31,31,31,
-    30,0,169,135,3,129,2,15,240,0,15,224,3,31,31,4,128,1,127,127,127,63,
-    63,143,8,0,0,161,60,144,4,62,56,0,14,14,14,101,137,9,7,128,4,144,9,
-    15,31,144,8,31,155,127,5,129,16,0,28,160,14,129,1,4,81,159,145,7,131,
-    2,7,31,130,4,128,17,32,143,8,10,255,240,5,2,240,0,2,132,2,129,16,2,
-    240,7,0,46,1,68,255,159,0,237,0,0,252,132,1,124,0,127,127,127,126,126,
-    0,0,0,13,62,62,62,60,0,135,4,129,2,30,76,240,0,31,32,63,63,16,224,2,
-    127,170,127,136,8,254,64,254,144,4,120,32,133,124,136,7,0,28,28,28,
-    137,9,15,92,128,4,31,0,63,240,1,193,0,140,16,0,109,120,126,48,65,56,
-    160,14,129,1,8,167,145,8,144,0,14,131,2,15,63,130,4,128,17,255,143,
-    24,3,130,16,130,8,240,5,98,240,0,2,132,2,192,81,2,137,32,1,104,255,
-    159,0,255,15,36,0,0,7,7,66,7,2,129,1,14,15,15,7,143,7,26,17,0,3,3,3,
-    138,2,15,14,14,18,143,8,27,1,3,3,51,7,7,50,128,7,111,24,1,107,255,159,
-    0,255,15,28,0,0,15,0,67,140,2,7,240,0,7,31,31,31,134,3,12,143,9,18,
-    0,7,7,7,72,240,7,31,184,31,143,16,19,10,130,14,130,8,2,11,15,128,15,
-    79,22,1,96,255,159,0,255,15,28,0,0,0,62,193,63,0,128,1,0,31,31,31,30,
-    171,136,3,129,2,15,240,0,15,240,3,31,143,8,9,67,241,7,63,137,7,0,14,
-    14,14,143,4,6,141,15,6,130,14,29,31,31,5,130,2,23,128,114,79,11,1,96,
-    255,159,0,255,15,36,0,0,0,124,16,127,127,127,126,128,1,0,62,62,106,
-    62,60,136,3,129,2,30,240,0,31,128,4,200,63,143,8,9,240,3,127,127,137,
-    7,0,28,113,28,28,111,6,127,6,130,14,58,62,62,176,53,130,2,46,98,63,
-    3,1,104,255,159,0,255,15,100,0,1,7,7,163,7,244,1,254,143,7,27,0,3,3,
-    252,2,0,143,8,23,1,104,255,159,0,255,15,92,0,1,15,15,194,15,143,2,6,
-    143,9,26,15,10,15,7,143,7,30,1,104,255,159,0,255,15,100,0,1,31,31,194,
-    31,143,2,6,143,9,26,31,21,31,14,143,6,22,1,104,255,159,0,255,15,108,
-    0,1,62,62,194,62,143,2,6,143,9,26,62,42,62,28,143,5,14,1,9,3,3,3,0,
-    145,0,7,7,68,131,1,255,15,37,7,7,7,130,8,14,0,15,14,15,15,15,0,0,28,
-    64,31,30,18,56,63,63,31,31,0,31,7,0,112,127,127,63,63,0,63,15,15,224,
-    255,255,127,127,44,127,31,31,133,7,0,151,16,147,8,15,32,14,14,6,17,
-    29,30,30,14,182,2,0,57,112,177,3,113,32,161,10,234,225,32,80,134,8,
-    1,36,1,20,17,1,14,14,14,130,1,28,30,30,109,1,1,160,16,161,3,1,80,1,
-    1,210,80,10,20,1,20,1,0,147,8,127,1,0,80,130,1,81,113,2,16,254,224,
-    6,138,8,3,16,2,16,2,16,63,28,30,28,18,0,18,144,31,129,8,255,193,7,10,
-    82,65,49,65,49,65,139,49,1,63,63,56,1,15,16,255,129,8,214,7,85,16,35,
-    84,0,19,252,160,24,130,11,144,16,33,48,2,24,1,12,7,7,7,0,145,0,133,
-    1,15,172,15,19,255,15,21,3,132,7,133,2,14,80,15,15,82,31,2,0,0,59,64,
-    63,63,18,127,127,127,63,63,100,63,15,129,5,176,1,31,31,227,5,63,8,63,
-    0,3,7,3,154,16,14,15,213,14,149,8,161,9,3,224,6,31,160,3,15,228,81,
-    192,11,145,7,240,7,31,63,67,63,57,0,2,7,19,128,1,18,2,15,127,3,3,242,
-    14,240,11,208,4,129,8,1,81,234,1,81,1,18,5,3,2,4,65,2,5,3,3,11,31,31,
-    28,109,17,23,225,4,16,47,33,16,95,19,34,63,63,191,131,24,2,4,128,7,
-    98,32,2,161,24,32,2,1,7,18,58,3,3,15,17,128,36,192,5,56,128,8,109,15,
-    63,16,241,26,127,17,99,255,237,32,160,3,17,131,1,7,98,17,3,149,224,
-    0,0,3,7,210,21,56,240,7,63,95,0,112,64,127,1,130,24,144,0,147,45,255,
-    128,7,138,1,146,29,18,241,32,145,8,241,7,1,192,17,1,20,255,1,66,255,
-    148,0,15,15,14,0,1,78,14,14,131,1,30,30,35,133,4,15,6,130,63,2,0,0,
-    7,15,15,138,6,49,28,30,30,130,2,69,119,127,127,134,34,176,3,127,127,
-    127,31,132,5,160,2,196,31,96,145,5,7,15,7,154,16,28,106,30,28,149,8,
-    129,4,7,224,6,63,176,12,210,31,17,160,6,63,212,15,0,28,132,8,82,4,15,
-    19,4,20,4,30,19,255,7,242,14,240,11,16,1,17,113,1,10,18,29,31,31,3,
-    129,6,10,3,144,4,4,4,10,3,7,23,63,91,63,56,17,47,32,1,95,17,88,0,191,
-    131,24,28,145,16,16,4,8,177,14,18,4,161,24,0,4,2,14,24,2,7,7,31,1,128,
-    36,63,127,95,127,112,241,29,127,144,7,241,26,18,130,24,126,49,4,224,
-    0,49,129,1,49,97,49,206,7,0,16,7,15,160,6,16,240,7,191,127,16,224,99,
-    147,9,160,36,145,18,144,0,251,208,48,130,1,128,8,98,80,147,48,63,241,
-    32,224,145,8,241,7,65,84,255,1,66,255,156,0,30,30,28,0,1,78,28,28,131,
-    1,60,60,35,141,5,5,76,123,127,1,0,0,242,0,32,14,70,30,30,138,6,56,60,
-    60,130,2,133,4,49,239,255,255,2,227,3,63,0,120,66,241,2,124,144,4,63,
-    63,63,62,145,5,34,14,30,14,154,16,56,60,56,66,41,126,126,126,49,15,
-    224,6,127,31,214,240,5,242,7,128,8,124,116,56,144,8,129,5,82,8,30,19,
-    8,20,8,60,19,222,14,16,1,31,128,7,113,49,18,129,125,130,18,0,56,58,
-    63,63,7,82,129,6,20,35,8,36,8,20,35,11,14,46,126,126,112,17,95,144,
-    7,84,17,191,131,24,124,145,16,3,16,56,139,62,2,8,16,28,2,8,161,24,17,
-    0,8,4,28,2,14,14,62,1,1,15,31,31,127,255,255,224,226,241,29,17,131,
-    24,1,56,62,62,2,253,8,128,1,1,33,1,33,1,14,156,32,16,15,31,224,5,16,
-    243,7,255,255,192,130,40,161,24,226,0,144,26,130,1,208,48,18,227,128,
-    8,18,32,18,126,126,15,18,128,160,23,132,52,1,84,255,159,0,45,3,0,0,
-    0,7,68,7,7,130,1,14,14,15,146,1,28,0,30,31,15,7,7,0,0,56,0,63,63,31,
-    15,15,7,0,112,16,127,127,63,31,160,1,224,255,255,184,127,176,2,31,141,
-    9,145,8,129,8,0,14,33,15,14,2,49,28,31,30,6,243,65,0,146,9,0,240,7,
-    15,0,0,152,112,134,8,0,0,148,16,6,15,14,136,14,130,1,29,30,30,146,9,
-    57,63,136,63,162,3,113,127,127,33,0,225,84,255,255,10,1,68,1,36,1,135,
-    14,35,1,28,30,30,82,160,16,110,145,3,1,96,17,1,224,6,26,4,181,1,4,1,
-    192,32,145,8,1,96,28,223,130,1,112,18,127,32,16,193,7,138,8,254,35,
-    16,2,16,2,16,2,16,47,63,63,56,17,15,32,129,8,217,39,255,130,4,16,3,
-    36,16,99,161,24,129,11,240,145,16,112,0,82,8,1,65,255,159,0,45,3,7,
-    7,7,0,196,0,240,0,130,1,14,15,15,50,28,64,31,31,146,1,63,63,63,31,15,
-    2,15,0,0,119,127,127,63,176,2,172,0,176,6,127,96,31,193,7,0,63,248,
-    0,144,8,129,6,133,10,49,129,2,0,31,68,31,31,146,3,59,63,63,145,9,7,
-    57,127,127,127,17,145,7,240,7,31,63,46,19,63,0,96,3,146,6,148,16,96,
-    219,14,21,241,0,3,208,4,129,8,15,16,243,81,17,81,1,18,2,7,3,148,128,
-    1,2,2,15,3,3,3,3,159,7,226,4,7,15,2,225,10,2,208,2,169,2,128,6,5,3,
-    2,4,2,5,5,3,3,11,31,31,28,129,8,23,182,1,48,47,0,49,95,1,192,2,218,
-    191,1,128,6,4,128,7,0,2,196,40,99,2,1,192,5,0,3,3,15,1,166,128,36,208,
-    5,56,192,2,15,63,193,32,144,0,215,127,179,23,129,8,255,130,22,15,130,
-    7,136,1,103,193,40,3,224,0,160,24,3,7,226,29,0,240,176,6,2,224,8,2,
-    130,46,1,64,255,159,0,45,3,31,31,31,30,2,0,0,0,7,15,15,14,240,0,98,
-    0,14,0,129,1,28,30,30,50,8,56,63,63,31,15,48,127,127,0,127,63,31,31,
-    0,0,239,255,89,255,127,176,2,0,193,7,96,63,0,248,132,8,145,8,241,0,
-    133,11,33,129,2,0,63,69,63,63,146,3,119,127,127,145,9,15,182,192,3,
-    1,63,129,8,16,0,81,146,5,59,7,15,7,130,1,148,16,80,28,21,222,242,3,
-    160,16,129,8,31,144,7,81,17,178,3,148,28,4,4,15,3,4,4,4,167,30,3,7,
-    3,7,15,0,129,8,225,34,225,10,18,0,29,31,31,3,82,129,6,10,19,4,20,4,
-    10,19,11,7,23,63,63,56,65,47,1,111,16,95,241,27,16,191,1,16,145,16,
-    20,32,4,8,14,18,4,196,40,4,70,2,14,18,7,7,31,17,128,36,19,63,127,127,
-    112,112,31,127,179,23,111,145,16,255,131,24,161,24,4,224,0,193,40,129,
-    1,217,114,161,43,80,7,96,128,21,7,15,216,160,6,176,32,240,7,127,144,
-    8,100,255,1,80,255,159,0,45,127,0,126,126,0,0,0,7,63,63,62,60,0,0,9,
-    0,14,30,30,28,240,0,0,28,136,0,129,1,56,60,60,66,112,126,48,126,62,
-    30,64,177,6,63,63,0,106,0,223,194,7,144,1,123,128,8,124,144,3,63,63,
-    63,63,33,145,8,241,0,133,12,81,8,129,2,0,126,126,126,50,239,255,5,255,
-    63,31,0,0,31,149,9,120,227,144,16,129,8,1,146,5,14,30,14,130,1,182,
-    68,96,56,37,241,0,15,144,7,129,8,220,63,0,98,124,32,130,5,20,8,165,
-    30,19,8,20,8,60,19,14,122,19,15,161,15,129,8,50,17,125,35,10,56,58,
-    63,63,7,129,6,20,35,144,8,36,8,20,35,14,46,126,90,126,112,17,95,1,16,
-    191,146,9,82,120,124,145,16,3,16,56,62,2,40,8,16,28,2,8,196,40,8,4,
-    136,28,2,14,14,62,1,15,31,13,31,127,255,255,224,96,81,255,143,131,24,
-    81,56,62,62,0,128,6,128,1,246,193,40,33,130,8,32,49,14,32,160,24,112,
-    15,31,224,5,176,32,243,7,255,1,74,255,159,0,109,0,3,0,0,0,34,7,7,7,
-    130,1,14,14,15,146,1,0,28,30,31,15,7,7,0,0,0,56,63,63,31,15,15,7,0,
-    3,112,127,127,63,31,31,15,143,9,7,130,145,8,129,8,0,14,15,14,2,49,31,
-    28,31,30,6,65,96,146,9,32,241,48,143,7,0,161,16,146,7,37,15,14,14,17,
-    130,1,29,30,30,50,57,63,63,21,162,3,113,127,127,143,8,3,1,20,1,33,20,
-    1,14,19,1,28,30,30,206,34,160,16,145,3,1,127,0,95,2,36,181,1,36,1,192,
-    32,65,1,0,28,23,130,1,63,63,63,49,15,16,143,8,3,255,16,51,100,80,3,
-    146,16,128,11,20,128,32,29,1,64,255,159,0,109,0,3,7,7,7,226,0,0,240,
-    0,130,1,14,15,15,50,32,28,31,31,146,1,63,63,63,31,1,15,15,0,0,119,127,
-    127,63,95,176,2,15,192,6,127,96,134,8,145,8,240,4,194,135,2,32,17,0,
-    31,31,31,146,9,33,59,63,63,113,7,127,127,127,123,240,7,15,161,7,0,135,
-    6,80,3,146,6,182,148,16,80,14,5,241,0,3,160,16,129,8,243,15,0,65,177,
-    7,74,2,7,19,148,128,1,18,2,15,19,3,19,3,253,7,16,1,19,208,2,1,121,5,
-    72,3,2,4,2,5,3,3,11,45,31,31,28,49,23,1,48,47,182,1,16,95,137,40,128,
-    6,4,132,7,164,24,109,2,1,192,5,17,3,208,5,130,36,31,40,19,15,63,20,
-    127,23,1,64,255,159,0,109,0,3,31,31,31,1,30,0,0,0,7,15,15,14,49,240,
-    0,0,14,0,129,1,28,30,30,4,50,56,63,63,31,15,48,127,0,127,127,63,31,
-    31,0,0,239,44,255,255,127,176,2,31,209,7,96,0,252,0,132,8,145,8,224,
-    0,134,11,33,241,2,0,33,63,63,63,145,3,0,119,127,127,101,145,9,15,208,
-    3,240,7,31,63,129,8,63,199,48,81,146,5,7,15,7,130,1,148,16,127,80,28,
-    21,242,3,160,16,129,8,161,15,81,165,17,162,3,28,4,4,15,3,4,41,4,4,30,
-    3,7,3,7,15,248,0,129,8,35,208,2,17,1,29,31,84,31,3,129,6,10,19,4,20,
-    4,130,10,19,7,23,63,63,56,65,218,47,1,16,95,1,16,191,130,29,69,0,28,
-    148,16,4,8,14,178,32,4,19,164,24,4,2,14,2,7,7,208,5,72,130,36,63,83,
-    31,127,87,1,72,255,159,0,109,0,127,0,126,126,0,0,0,7,63,63,62,60,0,
-    4,0,0,14,30,30,28,240,0,0,196,28,0,129,1,56,60,60,66,112,24,126,126,
-    62,30,64,192,6,127,63,21,63,0,0,223,145,1,127,0,123,71,128,8,124,144,
-    3,63,63,63,33,145,8,225,243,4,131,12,81,241,2,0,126,126,126,0,49,0,
-    239,255,255,63,31,0,94,0,31,149,9,120,144,16,129,8,1,146,5,59,14,30,
-    14,130,1,68,96,56,37,109,241,0,15,144,7,129,8,63,0,98,124,202,32,130,
-    5,20,8,30,19,8,20,87,8,60,19,14,19,15,161,15,129,8,160,50,17,125,35,
-    56,58,63,63,169,7,129,6,20,35,8,36,8,20,5,35,14,46,126,126,112,17,95,
-    164,1,16,191,146,9,120,124,148,16,56,69,56,62,82,8,16,28,178,32,8,17,
-    164,24,8,4,28,114,14,14,62,12,81,15,31,31,127,51,33,1,69,255,159,0,
-    173,0,0,3,0,0,17,0,7,7,7,130,1,14,14,15,0,146,1,28,30,31,15,7,7,0,3,
-    0,56,63,63,31,15,15,143,9,16,130,145,8,129,8,0,14,15,14,2,49,25,28,
-    31,30,6,65,96,15,7,226,143,7,9,161,16,146,7,37,15,14,14,2,34,29,30,
-    30,146,9,57,63,63,143,8,11,164,1,36,1,36,1,14,35,1,35,28,30,30,114,
-    63,63,63,31,11,250,160,16,83,20,192,32,3,20,7,31,8,1,64,255,159,0,173,
-    0,0,3,7,7,113,7,0,0,240,0,130,1,14,15,15,16,50,28,31,31,146,1,63,63,
-    63,0,31,15,15,0,0,119,127,127,51,63,31,31,143,8,0,145,8,0,0,136,2,132,
-    112,97,0,31,31,31,146,9,59,64,63,63,49,7,127,127,127,31,221,15,47,1,
-    16,3,146,6,148,16,16,14,236,21,242,3,160,16,81,15,16,95,3,2,229,7,19,
-    128,1,2,2,15,3,3,55,3,3,15,0,113,31,0,111,5,202,131,7,134,1,163,24,
-    3,15,4,31,2,32,31,31,63,15,0,1,64,255,159,0,173,0,0,3,31,31,0,31,30,
-    0,0,0,7,15,15,152,14,240,0,0,14,0,129,1,28,30,130,30,50,56,63,63,31,
-    15,48,0,127,127,127,63,31,31,0,0,5,239,255,255,127,63,63,135,8,0,156,
-    100,145,8,0,0,136,3,0,241,2,0,33,63,63,63,97,0,119,127,127,78,145,9,
-    15,224,3,63,31,136,5,1,146,5,59,7,15,7,130,1,148,16,0,28,5,244,242,
-    3,160,16,129,8,193,7,10,28,68,4,165,15,51,4,52,4,30,51,7,61,51,7,31,
-    48,33,214,23,29,14,165,161,24,24,14,67,7,31,20,63,16,18,63,63,127,7,
-    1,68,255,159,0,173,0,0,127,0,126,0,126,0,0,7,63,63,62,60,2,0,0,0,14,
-    30,30,28,240,0,98,0,28,0,129,1,56,60,60,66,12,112,126,126,62,30,64,
-    208,6,127,10,63,63,0,0,223,145,1,127,144,2,163,123,128,8,124,64,63,
-    63,63,33,240,145,8,226,4,132,12,81,241,2,0,126,126,128,126,33,0,239,
-    255,255,63,31,55,0,0,31,146,9,128,2,120,144,16,33,142,1,146,5,14,30,
-    14,130,1,84,112,219,56,53,241,0,15,144,7,129,8,63,16,114,114,126,48,
-    130,5,36,8,30,35,149,8,36,8,60,35,14,35,15,181,145,7,129,8,127,131,
-    20,39,62,36,28,167,161,24,40,28,99,14,62,36,144,7,64,16,127,17,1,67,
-    255,159,0,237,0,0,0,3,0,8,240,0,0,7,7,7,130,1,14,14,32,15,7,2,48,28,
-    30,31,15,225,7,143,9,25,145,8,33,0,14,15,14,7,129,8,0,28,31,30,6,143,
-    7,18,161,16,136,50,101,15,14,14,50,31,30,170,30,15,19,1,4,1,4,1,4,128,
-    7,15,16,1,64,255,159,0,237,0,0,0,3,7,88,7,7,240,0,0,16,130,1,14,15,
-    136,15,50,28,31,31,146,1,63,63,55,63,31,15,143,8,9,145,8,0,137,2,112,
-    11,33,0,31,31,31,97,7,16,176,63,11,16,3,130,1,148,16,0,15,15,236,14,
-    34,64,225,2,31,32,95,11,7,169,3,83,2,84,7,3,15,11,192,82,224,8,31,8,
-    1,64,255,159,0,237,0,0,0,3,31,33,31,31,30,128,1,7,15,15,14,49,240,0,
-    0,14,0,17,28,30,30,4,18,56,63,63,31,15,16,127,22,127,127,63,31,143,
-    8,1,0,116,145,8,225,0,137,3,16,241,0,0,63,63,63,104,1,15,16,15,6,31,
-    146,5,15,15,195,7,130,1,148,16,0,30,30,28,130,4,181,96,225,3,63,64,
-    127,3,29,116,14,169,32,145,0,4,4,14,19,31,23,192,2,224,8,15,0,1,66,
-    255,159,0,237,0,0,0,127,0,130,126,128,1,7,63,63,62,60,32,19,14,30,30,
-    28,240,0,0,28,0,16,33,56,60,60,34,112,126,126,101,62,30,32,224,6,127,
-    63,136,8,124,15,36,0,63,63,63,33,145,8,209,4,194,133,4,81,241,0,0,126,
-    126,126,33,214,31,240,2,45,127,113,62,0,146,5,48,30,30,14,130,1,148,
-    16,0,60,60,237,56,82,96,65,127,143,24,0,67,58,106,68,28,16,145,0,8,
-    68,28,3,112,62,46,34,224,5,39,1,107,255,159,0,255,15,28,0,0,3,0,8,129,
-    1,7,7,7,2,65,14,15,92,15,6,244,3,254,143,9,26,145,8,66,15,125,15,14,
-    143,8,22,130,6,160,16,19,36,7,0,47,24,1,104,255,159,0,255,15,28,0,0,
-    3,7,113,7,7,129,1,240,0,50,14,15,15,15,50,28,31,31,15,143,8,18,145,
-    8,138,2,138,0,50,31,31,31,127,19,2,131,15,84,0,5,52,2,68,15,79,16,1,
-    104,255,159,0,255,15,28,0,0,3,31,66,31,31,129,1,7,15,15,14,49,196,14,
-    0,49,28,30,30,50,56,47,63,63,31,143,8,10,0,4,145,8,138,3,138,32,98,
-    63,63,63,15,11,28,4,41,4,15,15,82,10,131,16,0,4,64,100,31,47,8,1,104,
-    255,159,0,255,15,36,0,0,7,63,66,63,62,129,1,14,30,30,28,49,196,28,0,
-    49,56,60,60,50,112,33,126,126,62,143,8,10,0,63,63,63,241,50,144,8,138,
-    3,32,18,126,126,126,69,15,11,62,4,8,30,30,18,20,40,131,16,0,8,100,62,
-    47,0
-};
-
-static MTB * mtb_KPK = NULL;
-
-void initMTB_KPK()
-{
-    mtb_KPK = new MTB ("KPK", 1, 48);
-    mtb_KPK->SetPackedData (mtbdata_KPK);
-    mtb_KPK->Add (A2, WHITE, 137);
-    mtb_KPK->Add (B2, WHITE, 160);
-    mtb_KPK->Add (C2, WHITE, 177);
-    mtb_KPK->Add (D2, WHITE, 173);
-    mtb_KPK->Add (A3, WHITE, 140);
-    mtb_KPK->Add (B3, WHITE, 166);
-    mtb_KPK->Add (C3, WHITE, 193);
-    mtb_KPK->Add (D3, WHITE, 204);
-    mtb_KPK->Add (A4, WHITE, 104);
-    mtb_KPK->Add (B4, WHITE, 130);
-    mtb_KPK->Add (C4, WHITE, 161);
-    mtb_KPK->Add (D4, WHITE, 174);
-    mtb_KPK->Add (A5, WHITE, 76);
-    mtb_KPK->Add (B5, WHITE, 89);
-    mtb_KPK->Add (C5, WHITE, 112);
-    mtb_KPK->Add (D5, WHITE, 124);
-    mtb_KPK->Add (A6, WHITE, 49);
-    mtb_KPK->Add (B6, WHITE, 53);
-    mtb_KPK->Add (C6, WHITE, 66);
-    mtb_KPK->Add (D6, WHITE, 68);
-    mtb_KPK->Add (A7, WHITE, 29);
-    mtb_KPK->Add (B7, WHITE, 27);
-    mtb_KPK->Add (C7, WHITE, 27);
-    mtb_KPK->Add (D7, WHITE, 27);
-    mtb_KPK->Add (A2, BLACK, 204);
-    mtb_KPK->Add (B2, BLACK, 237);
-    mtb_KPK->Add (C2, BLACK, 239);
-    mtb_KPK->Add (D2, BLACK, 249);
-    mtb_KPK->Add (A3, BLACK, 207);
-    mtb_KPK->Add (B3, BLACK, 244);
-    mtb_KPK->Add (C3, BLACK, 249);
-    mtb_KPK->Add (D3, BLACK, 251);
-    mtb_KPK->Add (A4, BLACK, 164);
-    mtb_KPK->Add (B4, BLACK, 181);
-    mtb_KPK->Add (C4, BLACK, 211);
-    mtb_KPK->Add (D4, BLACK, 216);
-    mtb_KPK->Add (A5, BLACK, 118);
-    mtb_KPK->Add (B5, BLACK, 135);
-    mtb_KPK->Add (C5, BLACK, 152);
-    mtb_KPK->Add (D5, BLACK, 174);
-    mtb_KPK->Add (A6, BLACK, 79);
-    mtb_KPK->Add (B6, BLACK, 92);
-    mtb_KPK->Add (C6, BLACK, 113);
-    mtb_KPK->Add (D6, BLACK, 123);
-    mtb_KPK->Add (A7, BLACK, 51);
-    mtb_KPK->Add (B7, BLACK, 56);
-    mtb_KPK->Add (C7, BLACK, 69);
-    mtb_KPK->Add (D7, BLACK, 72);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KQKQ
-
-static const byte mtbdata_KQKQ[7580] = {
-    1,68,85,159,0,157,81,85,80,224,0,68,0,84,69,81,65,69,69,21,69,85,85,
-    65,131,2,85,32,68,35,90,31,130,4,85,85,69,84,34,80,160,2,15,8,69,81,
-    69,69,16,192,1,176,5,15,152,0,5,85,20,0,128,3,2,129,6,58,143,16,1,1,
-    81,129,10,164,0,128,2,5,129,12,223,85,20,80,21,130,14,145,16,3,144,
-    12,63,18,85,85,3,144,22,0,129,20,162,10,244,139,16,0,49,134,10,128,
-    28,1,148,15,69,195,85,160,11,157,0,89,85,80,17,177,3,251,208,16,133,
-    18,208,17,194,19,128,6,130,2,17,20,253,128,32,130,24,131,4,129,12,63,
-    1,16,128,14,69,187,209,2,69,4,131,42,131,16,128,18,16,130,30,191,132,
-    4,111,1,4,129,36,160,5,192,28,130,14,129,6,111,128,8,65,130,48,128,
-    2,65,132,52,162,0,143,16,1,253,83,128,54,132,24,128,8,129,34,129,2,
-    130,10,64,187,137,4,131,36,68,128,1,129,60,95,3,16,131,54,255,160,16,
-    192,48,133,8,129,66,160,18,143,16,2,130,28,128,4,255,210,27,136,14,
-    133,2,131,68,211,71,130,66,133,64,177,2,247,128,18,130,24,161,1,180,
-    4,45,64,133,72,177,40,255,143,16,1,132,20,129,56,133,14,135,48,133,
-    2,136,4,129,54,254,132,92,98,4,129,72,6,130,80,161,18,157,0,1,88,85,
-    159,0,13,81,143,2,77,143,16,15,84,84,0,85,80,0,1,85,64,84,65,0,81,65,
-    69,65,21,81,85,84,20,85,84,1,85,22,65,17,5,16,85,85,68,85,24,21,85,
-    85,30,80,84,69,85,18,128,8,176,0,128,6,108,81,81,160,2,51,80,48,16,
-    81,112,69,69,160,12,208,3,152,0,65,84,4,1,0,21,0,21,85,68,84,69,97,
-    240,3,69,160,9,11,65,84,80,1,200,240,7,128,4,133,18,80,5,161,8,80,85,
-    17,69,81,65,5,129,12,80,21,86,144,128,2,128,14,85,69,80,1,17,80,136,
-    84,16,84,64,81,32,21,69,8,21,1,68,80,80,18,84,65,48,65,65,69,225,33,
-    141,16,84,0,85,195,0,241,2,3,65,84,64,1,160,2,88,192,24,81,128,22,81,
-    129,14,139,6,85,80,72,17,80,128,32,64,85,241,25,65,21,27,17,84,80,68,
-    1,128,28,85,144,2,96,129,18,17,3,128,22,85,81,21,1,207,85,128,4,33,
-    84,65,129,36,143,16,2,130,14,142,177,47,51,80,4,85,128,46,129,2,48,
-    141,5,128,18,16,84,4,244,1,130,32,88,206,180,58,243,0,129,6,80,68,161,
-    1,160,16,129,22,14,84,80,16,84,17,163,21,160,0,129,10,119,65,84,130,
-    4,130,40,143,16,2,4,129,60,242,2,124,0,97,129,8,128,14,193,24,176,44,
-    65,64,99,85,16,192,66,147,20,21,65,81,128,36,110,240,8,64,193,7,50,
-    88,226,11,225,17,193,0,6,65,80,80,16,80,16,129,30,162,18,31,21,81,80,
-    80,177,31,130,2,49,143,16,1,52,130,14,68,84,242,7,113,64,160,2,85,162,
-    17,226,70,84,128,32,80,0,84,208,13,229,68,128,50,160,12,128,78,80,1,
-    129,36,80,139,194,9,128,28,68,64,4,129,8,64,209,4,48,129,14,85,88,226,
-    31,165,18,65,68,80,159,64,160,24,85,0,128,50,227,22,142,16,112,244,
-    129,90,208,67,240,83,80,96,84,176,30,81,203,81,129,23,34,0,84,128,1,
-    69,128,32,87,130,64,1,129,38,68,128,50,65,129,2,129,54,106,96,80,96,
-    162,18,20,80,16,130,78,240,81,225,13,128,18,209,94,6,1,93,85,159,0,
-    29,69,143,2,61,143,16,32,160,0,1,2,129,1,84,85,84,84,84,81,162,2,5,
-    5,21,85,68,85,21,81,81,128,129,4,240,1,85,80,84,20,85,81,32,68,80,21,
-    18,84,21,85,64,42,84,16,81,240,5,17,18,80,16,12,85,80,81,80,68,129,
-    8,159,0,12,68,34,85,68,0,129,1,81,85,16,192,10,194,80,130,14,105,4,
-    81,64,5,0,0,69,16,17,4,65,1,1,0,16,1,68,84,0,161,12,85,68,69,9,20,21,
-    4,84,5,128,4,0,84,0,161,16,0,17,64,16,16,16,4,8,16,4,84,0,80,97,0,68,
-    0,0,65,64,64,16,64,68,80,32,8,84,5,64,5,5,21,21,32,69,84,5,142,18,68,
-    84,1,1,213,69,80,144,8,1,240,7,16,16,84,241,129,32,192,29,208,0,99,
-    90,4,64,68,0,17,0,21,64,68,16,4,4,24,4,68,16,0,224,15,128,3,85,4,0,
-    21,68,84,20,81,4,65,0,48,68,5,80,128,4,164,16,4,1,64,97,0,5,160,0,112,
-    0,17,0,65,129,194,29,138,10,16,0,4,5,1,85,0,22,68,84,65,84,69,4,85,
-    192,65,128,2,48,69,81,68,85,4,44,65,5,17,161,5,80,0,143,18,1,16,20,
-    64,16,5,17,65,0,194,32,68,176,64,16,68,129,29,160,18,84,4,81,44,6,84,
-    2,128,28,21,38,142,16,64,6,0,16,5,4,21,1,240,42,2,3,68,80,64,84,69,
-    16,69,176,37,128,193,32,224,33,85,1,1,5,65,21,66,17,66,128,2,64,5,16,
-    17,128,18,56,4,5,4,192,1,164,18,14,64,64,4,64,5,65,5,68,0,84,6,22,16,
-    10,4,37,160,16,17,84,142,16,70,0,0,240,10,21,4,17,84,114,234,64,128,
-    31,209,74,128,30,4,192,62,5,16,56,16,17,64,18,128,54,32,21,4,49,66,
-    68,0,226,48,128,18,16,5,16,193,65,208,0,79,1,6,64,10,16,37,97,160,44,
-    65,164,16,47,1,0,5,64,21,192,176,71,19,34,0,69,65,69,21,82,68,21,208,
-    42,85,36,1,4,20,111,4,4,128,54,160,0,16,128,65,1,128,18,14,112,21,16,
-    66,16,144,12,144,6,80,104,64,5,208,35,17,80,128,2,1,64,85,159,0,162,
-    1,85,85,81,85,4,81,84,81,81,81,69,130,2,5,53,85,85,21,161,2,16,21,240,
-    1,21,13,208,0,17,85,85,84,32,19,84,236,131,6,144,4,128,5,48,80,129,
-    4,129,8,85,3,69,20,85,5,0,5,0,53,134,132,10,152,0,21,85,84,80,240,7,
-    79,9,239,64,130,1,128,14,130,16,85,100,66,82,226,37,66,136,6,133,22,
-    1,64,21,0,5,21,81,81,68,81,17,130,8,80,99,129,2,85,209,14,130,12,80,
-    65,85,113,142,208,7,161,0,85,85,68,132,32,128,28,143,18,4,207,68,83,
-    128,14,85,85,114,1,20,221,130,16,7,81,65,17,225,13,132,10,64,142,3,
-    38,80,5,21,0,145,15,131,4,24,66,85,85,4,163,28,193,0,21,85,61,4,81,
-    21,209,31,130,12,81,142,18,17,253,116,49,128,1,128,16,176,24,130,2,
-    130,36,65,111,130,14,21,242,9,113,72,83,161,9,192,0,56,128,16,85,21,
-    2,209,38,130,20,84,85,192,16,228,15,0,21,85,2,81,21,207,65,229,17,129,
-    18,68,68,129,6,130,50,128,12,95,47,1,69,48,84,133,54,128,16,130,14,
-    128,2,225,225,18,17,39,114,16,64,21,16,120,112,81,133,4,80,178,56,162,
-    14,69,21,51,21,0,81,131,8,132,2,85,66,34,189,195,53,130,18,17,80,130,
-    46,195,6,29,65,198,130,14,130,70,208,16,69,84,8,96,224,6,33,69,69,17,
-    131,32,16,0,21,64,255,129,22,192,25,210,22,128,16,128,38,131,64,129,
-    4,115,245,130,96,21,130,32,18,128,2,66,50,65,48,38,68,69,128,10,134,
-    82,1,88,85,159,0,29,69,143,2,61,143,16,34,84,1,0,85,85,64,84,65,81,
-    65,69,64,65,21,193,2,84,5,85,85,68,225,85,19,130,4,224,2,80,84,69,85,
-    97,18,84,16,192,0,64,84,81,81,176,160,2,3,80,0,128,6,81,69,69,0,85,
-    68,84,4,0,21,0,85,116,85,81,136,10,159,0,26,128,15,5,176,10,85,32,80,
-    85,69,130,8,68,84,65,21,200,86,48,128,14,85,69,49,85,65,203,84,50,65,
-    85,69,129,4,80,3,69,65,81,128,16,81,0,84,48,69,212,128,20,63,2,128,
-    6,1,1,84,0,81,89,69,81,129,8,4,129,32,128,14,85,65,182,160,0,127,1,
-    68,49,64,85,128,28,97,214,17,51,0,85,128,8,85,128,4,49,129,84,224,7,
-    69,65,85,68,86,64,252,130,16,160,16,128,12,31,3,130,14,144,0,21,16,
-    96,84,4,244,1,128,6,70,85,73,65,115,84,17,129,2,224,11,239,59,1,68,
-    80,16,199,132,4,160,0,129,8,65,84,68,34,129,30,177,134,28,129,51,69,
-    128,12,13,100,84,97,227,130,14,193,24,176,20,97,64,84,16,129,32,182,
-    160,16,129,28,85,32,240,32,64,161,10,128,4,29,70,84,73,4,129,6,130,
-    2,143,36,2,68,241,96,33,128,10,17,133,28,81,84,69,81,224,20,69,143,
-    16,1,96,130,14,81,85,85,158,225,24,49,0,84,208,11,128,55,176,2,130,
-    42,254,1,131,50,193,13,130,28,99,209,12,128,2,128,18,251,16,129,8,49,
-    161,14,143,44,1,130,28,65,129,84,222,223,99,1,128,16,130,14,80,33,128,
-    4,18,128,38,245,65,128,92,144,90,129,80,82,68,128,50,65,158,67,34,80,
-    68,227,31,130,12,33,130,64,92,85,85,128,18,64,129,10,33,129,2,1,64,
-    85,159,0,173,69,5,64,85,80,1,5,16,17,4,65,1,1,0,32,160,0,69,21,128,
-    2,21,64,68,16,0,4,4,4,1,4,0,4,69,0,85,106,85,80,85,0,17,64,0,16,16,
-    16,4,16,1,16,69,128,81,32,81,0,68,0,65,64,0,64,16,64,4,64,21,1,64,0,
-    1,85,85,5,0,17,0,65,1,0,1,1,1,4,85,85,81,0,155,12,1,80,1,84,85,84,0,
-    68,17,1,240,7,16,16,64,142,16,17,8,64,5,84,85,64,166,16,69,69,13,64,
-    21,84,85,0,118,128,4,85,234,32,144,8,3,16,81,32,80,5,238,69,129,12,
-    72,142,16,5,83,21,129,14,128,80,96,80,1,68,4,65,16,128,64,142,18,101,
-    5,64,69,80,21,204,85,22,128,6,17,80,128,3,5,69,232,65,128,4,176,12,
-    5,69,128,32,80,1,103,5,0,5,142,16,17,80,1,128,3,153,3,128,8,17,84,8,
-    80,65,80,113,192,18,85,196,32,142,18,129,16,17,0,84,135,69,80,65,80,
-    69,0,176,18,67,156,128,56,48,17,0,69,30,224,11,80,102,5,17,38,131,14,
-    68,4,37,129,24,48,84,65,16,38,128,10,5,80,5,59,64,68,0,196,32,142,18,
-    129,16,65,160,15,188,20,131,42,65,96,115,143,64,1,128,14,16,199,4,37,
-    130,38,65,65,16,37,131,40,195,144,33,36,129,16,84,5,16,4,37,248,130,
-    48,128,31,144,12,194,32,142,72,128,42,80,5,206,1,70,143,80,2,80,65,
-    177,71,51,131,14,124,64,64,53,131,2,144,87,4,131,32,4,207,4,5,129,16,
-    84,17,32,4,130,48,192,128,63,176,61,194,32,1,88,85,159,0,29,69,143,
-    2,61,143,16,31,1,80,0,85,80,1,84,5,1,16,0,72,64,0,0,69,5,16,5,80,9,
-    17,0,65,0,1,161,0,5,21,0,16,21,80,69,16,4,4,4,4,1,4,0,4,5,84,16,85,
-    0,80,17,64,16,16,16,4,16,16,1,16,5,80,224,1,81,80,69,0,0,65,64,64,16,
-    64,4,64,1,4,0,0,1,85,169,85,1,2,146,8,1,1,4,85,85,81,138,14,100,5,1,
-    224,13,80,17,1,240,7,16,68,16,64,141,18,4,17,64,162,16,16,17,17,4,65,
-    1,128,16,69,68,64,50,114,64,68,19,128,4,85,90,16,132,0,20,4,68,64,81,
-    128,2,81,77,0,68,19,69,0,129,12,128,13,0,201,35,142,16,132,4,84,0,35,
-    4,16,128,128,14,128,2,80,1,68,4,65,16,0,64,64,69,85,64,17,80,5,0,85,
-    85,64,85,81,81,69,69,0,65,21,4,4,64,69,80,21,38,85,85,0,36,69,16,64,
-    128,9,217,0,144,8,2,65,16,176,12,0,80,22,3,4,1,64,160,28,85,70,142,
-    16,193,16,224,43,24,4,64,64,17,84,152,24,128,8,65,80,19,194,32,69,84,
-    16,96,5,80,69,193,8,64,85,65,44,69,81,21,128,22,17,128,4,70,69,200,
-    64,128,6,72,4,8,129,16,81,1,232,5,160,15,65,126,64,225,11,81,1,206,
-    84,160,3,17,132,0,129,14,23,129,24,173,84,160,2,64,85,4,128,36,32,0,
-    16,196,32,69,80,96,128,16,81,85,64,165,84,162,18,21,0,65,80,32,0,222,
-    176,18,17,131,42,81,128,64,19,143,48,1,130,28,192,17,20,128,16,1,80,
-    65,81,17,99,68,4,19,131,40,84,65,16,20,141,33,224,19,5,64,68,19,132,
-    32,17,130,176,27,0,0,84,69,68,96,96,251,81,160,1,208,0,160,18,129,42,
-    129,28,65,144,4,206,225,15,143,64,2,161,16,16,4,83,130,16,160,2,103,
-    65,16,83,115,81,1,176,49,18,13,131,32,84,5,16,4,19,33,84,53,32,64,16,
-    19,84,65,176,61,68,0,193,32,1,40,81,85,159,0,109,84,192,0,0,85,0,85,
-    69,84,69,81,69,69,69,129,21,255,7,34,84,5,85,85,68,85,195,131,8,130,
-    2,224,8,84,84,69,85,66,134,144,2,17,68,84,85,81,160,2,67,224,80,131,
-    6,144,4,142,10,80,84,21,0,161,21,128,18,68,111,5,84,84,81,65,143,240,
-    7,143,4,9,1,85,86,18,192,26,98,221,132,2,129,14,129,16,68,21,129,8,
-    145,6,17,100,131,32,80,128,20,130,22,5,0,129,2,85,33,68,69,17,130,12,
-    84,1,65,84,192,96,192,22,80,85,17,84,84,65,152,5,176,12,85,84,208,7,
-    161,0,84,85,225,17,132,32,131,8,142,18,80,1,17,86,92,98,65,16,85,3,
-    17,131,36,85,59,85,86,129,130,16,160,16,144,4,65,97,199,129,14,241,
-    66,131,10,84,1,5,176,6,145,0,216,17,128,4,192,4,4,130,2,130,20,86,85,
-    112,9,81,177,24,128,8,194,0,84,84,17,120,68,84,208,17,193,1,240,11,
-    143,18,0,68,1,159,69,132,4,81,65,129,16,209,25,130,64,129,38,112,129,
-    12,5,130,28,145,26,144,2,21,85,68,138,33,129,14,21,84,20,128,2,5,130,
-    16,56,64,85,16,128,53,176,7,144,20,21,84,8,69,1,81,84,65,245,5,85,21,
-    22,86,84,9,68,129,20,64,163,16,129,18,127,17,84,128,6,130,50,128,48,
-    159,72,0,208,13,129,14,156,112,130,52,84,5,130,28,128,6,225,26,21,0,
-    85,80,33,64,84,16,84,4,64,81,65,130,2,17,20,84,64,0,2,84,64,85,81,68,
-    68,81,16,173,81,129,42,81,129,8,84,131,32,64,1,246,131,16,128,44,17,
-    128,18,115,85,208,20,35,252,69,96,130,64,194,38,49,128,6,128,32,81,
-    242,69,128,57,16,128,14,241,63,69,17,241,3,32,17,0,32,96,84,16,16,4,
-    68,17,1,130,65,84,65,0,129,52,65,62,84,69,21,145,58,130,32,176,8,131,
-    64,130,80,188,68,129,10,80,129,60,116,83,50,85,44,86,68,9,130,16,4,
-    224,3,161,18,1,40,81,85,159,0,44,21,143,2,45,17,81,0,81,85,81,1,85,
-    85,81,81,26,21,69,21,21,237,3,15,16,17,192,0,0,80,5,84,85,1,81,4,69,
-    17,5,21,5,85,129,26,80,21,84,192,128,3,19,130,4,85,84,85,65,81,100,
-    20,85,18,144,4,80,81,129,6,68,24,69,21,85,5,224,3,171,32,17,81,3,17,
-    0,85,0,85,85,69,132,10,8,143,18,31,68,4,21,80,160,16,64,68,0,16,4,4,
-    4,1,4,17,17,32,4,85,90,0,0,17,64,16,20,16,16,4,16,128,4,81,128,2,81,
-    1,0,68,0,65,64,64,16,64,168,128,14,130,32,21,80,85,80,17,1,116,68,1,
-    50,144,4,240,7,4,142,12,81,50,4,85,80,128,4,192,24,69,81,160,13,8,17,
-    65,4,17,80,240,20,64,5,13,16,17,4,65,1,160,3,140,6,16,153,64,128,1,
-    0,84,163,16,16,69,80,35,176,30,0,80,67,17,81,69,130,16,0,160,14,65,
-    17,5,81,21,17,8,115,0,1,128,12,70,142,10,64,5,128,14,6,50,1,68,4,65,
-    16,130,18,162,12,0,80,69,16,21,20,85,25,4,11,36,5,80,69,80,144,57,0,
-    194,32,49,143,8,0,4,65,128,4,102,17,89,1,32,128,16,81,85,176,25,85,
-    21,5,17,199,21,128,22,224,11,84,1,21,192,48,81,1,141,8,145,0,128,65,
-    81,17,84,1,54,17,1,5,1,80,65,80,226,240,18,195,32,128,63,128,66,80,
-    85,16,192,5,132,16,208,10,25,16,36,17,128,20,17,120,0,84,115,143,42,
-    0,192,70,128,16,17,80,88,21,65,160,14,21,64,128,28,84,1,118,68,0,163,
-    16,15,1,128,14,81,240,56,67,44,17,1,4,128,54,84,144,33,3,17,39,4,5,
-    5,80,5,64,144,8,193,32,32,133,36,16,81,162,18,85,25,64,36,178,65,128,
-    22,65,176,27,160,30,0,80,66,136,5,192,96,80,17,65,241,77,17,0,142,0,
-    224,25,84,5,4,164,16,79,3,0,110,65,16,3,17,80,0,177,49,1,0,17,4,4,5,
-    81,5,84,5,76,16,4,3,17,16,128,54,160,0,64,106,16,0,194,32,130,18,65,
-    66,69,161,18,1,32,81,85,159,0,108,21,84,65,165,0,65,5,17,0,65,0,1,1,
-    16,1,4,1,16,143,10,46,16,21,0,0,85,64,21,64,68,16,4,4,2,4,1,4,0,4,16,
-    85,128,2,0,85,0,17,64,16,16,16,4,4,16,1,16,16,85,170,33,68,0,0,65,64,
-    64,16,64,4,64,67,85,85,171,32,80,4,0,5,224,18,164,144,4,240,7,4,112,
-    85,69,138,4,84,0,5,64,5,81,85,80,1,68,2,4,65,16,64,64,64,0,142,16,7,
-    69,0,21,81,85,0,166,16,129,18,118,128,2,84,53,16,81,80,53,192,1,75,
-    5,65,160,14,5,0,195,32,20,129,12,131,24,141,16,85,85,64,5,80,128,4,
-    160,21,129,14,64,128,2,64,5,16,17,19,4,65,1,1,141,18,144,21,0,218,128,
-    1,21,128,6,69,50,116,21,160,7,49,81,5,5,128,25,35,16,5,0,233,32,208,
-    22,36,142,32,69,224,43,5,80,131,128,3,3,17,85,0,69,80,8,248,130,8,192,
-    18,160,12,194,32,142,18,129,16,69,0,231,80,69,96,128,32,17,0,69,128,
-    38,48,48,68,0,69,143,48,1,69,65,21,102,68,4,37,131,14,16,17,37,129,
-    10,58,80,5,64,144,8,36,129,32,21,144,11,238,84,195,32,141,18,131,16,
-    65,144,4,19,131,42,118,4,1,5,143,64,2,0,16,5,97,115,65,5,145,33,4,130,
-    40,16,4,5,90,128,12,21,128,36,16,5,130,48,69,144,45,195,68,195,32,129,
-    4,65,5,1,1,37,241,128,8,160,44,177,71,35,143,16,3,5,64,64,246,37,131,
-    14,176,103,4,132,18,4,5,131,32,108,16,16,5,130,54,69,144,87,52,1,64,
-    85,159,0,29,81,85,84,1,64,4,5,1,16,0,64,0,2,81,0,85,68,5,64,17,0,65,
-    0,192,1,163,0,128,2,21,64,69,16,4,12,4,4,1,4,0,32,48,85,1,64,17,64,
-    16,16,16,4,16,64,224,6,16,48,81,64,69,0,65,1,64,64,16,64,4,64,1,64,
-    144,143,16,30,29,65,5,128,12,5,16,17,36,4,65,1,2,65,21,80,21,64,64,
-    68,165,16,65,85,100,85,64,72,85,0,86,65,81,64,81,0,192,68,85,143,14,
-    32,84,1,80,85,80,0,1,68,4,65,16,64,64,64,4,0,65,17,68,5,80,8,65,6,69,
-    68,21,80,85,0,6,128,4,181,85,112,84,5,64,81,80,80,32,37,65,1,128,42,
-    1,85,85,5,254,0,210,40,160,1,193,44,136,46,129,14,224,2,144,8,160,1,
-    240,7,16,142,18,65,84,17,64,64,5,85,166,16,97,5,68,69,64,108,21,85,
-    70,128,16,17,209,28,69,65,226,65,128,4,144,12,5,65,1,68,128,12,96,5,
-    0,5,110,17,84,5,80,127,1,84,128,3,3,128,8,128,14,161,18,195,32,104,
-    143,36,1,5,128,60,6,97,129,16,17,0,215,84,117,128,8,65,128,4,80,21,
-    128,56,56,128,32,17,0,21,14,128,28,80,5,193,17,70,128,22,65,64,17,68,
-    4,141,38,128,24,64,65,16,198,32,142,18,17,107,128,36,17,144,11,36,97,
-    129,16,65,160,15,142,36,129,56,64,1,65,144,4,35,62,3,1,84,65,80,17,
-    16,4,5,62,131,14,65,16,5,130,2,240,60,5,128,26,143,5,128,17,4,64,68,
-    195,32,143,54,1,129,16,177,118,131,42,1,86,79,1,1,80,65,251,177,71,
-    83,131,14,128,55,36,131,2,1,38,48,3,4,4,37,130,64,17,16,16,96,64,16,
-    195,32,157,0,1,64,85,159,0,29,65,85,84,1,64,4,5,81,17,0,64,0,2,65,0,
-    85,68,5,80,17,84,65,1,96,1,0,162,0,128,2,21,64,69,80,6,5,8,4,2,4,0,
-    32,48,0,85,64,17,80,17,16,16,4,10,16,1,16,0,16,48,81,128,4,0,65,64,
-    64,16,64,4,64,1,192,64,143,16,30,31,126,1,17,68,5,64,4,85,80,5,16,17,
-    4,194,32,65,128,68,128,32,85,80,21,64,68,16,8,4,4,4,1,4,112,85,106,
-    2,85,80,85,0,17,64,16,34,33,68,68,81,96,81,0,68,0,32,34,65,0,128,42,
-    1,85,85,5,4,0,17,0,65,0,1,160,1,85,104,85,81,137,12,64,80,224,2,84,
-    0,80,17,1,240,7,16,142,16,65,68,17,8,64,5,85,85,64,164,16,1,4,2,68,
-    69,64,21,85,85,0,116,110,65,16,48,128,5,0,144,8,1,128,6,72,69,64,144,
-    12,0,80,3,1,1,224,68,129,12,70,142,16,16,84,5,80,76,1,84,22,1,64,129,
-    14,161,18,80,97,1,68,224,11,141,18,9,4,100,5,164,128,60,22,1,33,17,
-    80,22,65,49,64,68,65,128,4,22,1,8,68,28,128,32,81,1,5,160,15,17,46,
-    64,3,84,17,80,5,81,1,84,160,3,46,1,129,0,128,8,17,193,18,3,129,24,50,
-    64,65,80,198,32,142,18,16,100,129,16,52,17,0,84,51,112,65,128,6,69,
-    233,0,176,18,49,128,20,5,33,17,0,199,51,143,48,1,224,11,80,5,17,36,
-    16,12,128,14,81,17,68,4,35,18,1,52,84,65,16,4,48,5,128,36,5,50,64,68,
-    0,194,32,142,18,64,100,17,227,65,192,31,34,131,42,81,1,65,0,207,227,
-    15,143,64,1,16,16,4,19,130,16,160,2,206,128,59,98,3,81,1,176,49,50,
-    3,31,84,5,16,4,51,132,48,160,47,144,12,128,192,32,157,0,1,64,85,159,
-    0,29,64,85,68,1,64,3,5,81,17,80,65,128,0,161,0,0,128,2,5,80,17,84,65,
-    81,1,128,0,18,64,85,4,21,64,69,3,80,5,84,5,1,4,0,16,8,32,84,64,17,80,
-    16,8,16,20,2,16,0,16,32,80,128,4,65,6,80,65,16,64,4,64,240,5,159,0,
-    31,192,69,95,61,143,32,29,4,68,1,80,85,64,80,1,128,28,16,0,64,0,0,73,
-    0,17,64,85,80,192,32,0,65,16,66,68,4,21,48,21,80,69,17,16,4,4,4,112,
-    17,4,84,0,16,85,80,17,64,16,16,16,4,4,16,0,68,4,80,224,1,81,0,80,69,
-    0,65,64,64,16,64,129,64,208,36,64,1,85,169,85,1,84,146,8,1,193,12,81,
-    136,14,0,208,8,80,2,1,85,1,84,1,17,1,240,7,130,16,141,18,0,65,4,17,
-    64,162,16,0,16,17,4,65,1,1,0,4,36,68,68,64,66,64,68,65,64,150,16,128,
-    4,85,90,64,0,2,128,6,34,68,64,81,128,2,81,0,68,1,16,0,1,68,0,129,12,
-    85,85,5,249,0,1,142,16,128,20,224,29,128,4,84,0,12,65,0,64,4,16,128,
-    14,128,2,80,0,1,68,4,65,16,0,1,4,186,65,128,60,5,160,35,50,128,22,4,
-    96,195,21,160,7,2,0,16,68,16,128,4,228,161,9,144,8,145,30,128,6,69,
-    80,176,12,0,199,80,65,129,28,64,1,81,54,142,16,24,128,14,80,5,81,54,
-    128,18,64,64,99,17,84,6,96,1,64,65,128,76,139,4,49,96,5,80,6,0,208,
-    38,134,130,80,36,0,64,68,64,128,6,39,35,32,4,8,97,81,1,5,192,15,186,
-    193,0,142,32,64,225,11,32,192,3,84,160,94,12,132,0,64,65,81,38,130,
-    16,1,180,84,32,64,34,130,32,5,128,66,69,211,0,2,97,96,130,96,0,84,2,
-    222,224,94,64,48,0,176,18,16,130,42,1,118,17,0,227,15,143,48,1,130,
-    28,17,18,82,124,1,80,16,144,73,16,35,224,19,65,238,16,18,35,112,5,192,
-    31,1,133,32,235,17,48,1,16,96,1,65,48,0,64,1,0,1,85,17,84,17,0,85,0,
-    0,85,1,1,4,1,16,1,64,64,85,159,0,29,1,85,16,5,16,6,17,84,65,85,1,0,
-    162,0,128,2,0,21,64,68,84,4,84,4,2,96,4,0,16,32,84,0,17,84,0,16,84,
-    16,4,16,1,16,0,128,16,32,80,0,68,84,65,84,11,64,16,64,4,64,129,14,81,
-    191,14,30,67,12,21,47,43,1,80,17,84,240,0,72,160,32,65,208,23,1,4,143,
-    32,31,17,16,0,5,85,85,84,5,84,17,4,72,65,1,81,68,16,144,12,84,21,4,
-    84,68,16,4,4,4,192,42,17,128,16,128,12,84,84,84,17,64,16,136,16,193,
-    32,68,16,80,128,2,80,84,26,68,0,65,64,32,141,10,0,208,36,149,21,129,
-    50,0,68,208,39,4,142,18,1,0,208,8,80,1,84,85,84,1,68,16,4,65,16,64,
-    142,8,1,4,16,20,68,0,21,86,160,16,64,67,16,28,16,17,0,84,128,2,224,
-    48,65,1,185,65,128,4,80,96,112,1,1,81,9,132,64,85,5,0,17,193,32,0,16,
-    177,240,54,33,21,66,129,14,1,1,84,176,0,144,8,1,192,1,208,8,64,4,84,
-    27,1,85,85,80,18,144,4,65,128,64,72,192,32,64,128,65,4,65,143,22,1,
-    16,0,88,17,86,32,0,114,128,8,65,0,80,68,86,32,0,98,1,88,17,119,86,129,
-    130,30,19,129,14,4,65,20,24,65,0,4,85,22,128,20,64,65,192,16,128,76,
-    4,1,1,18,1,8,113,65,84,6,128,32,160,0,84,68,84,115,224,48,0,192,32,
-    142,18,192,46,0,65,128,4,237,68,129,64,208,86,128,14,5,66,113,4,119,
-    224,11,84,225,48,0,17,32,16,5,222,129,36,224,46,160,2,80,66,128,32,
-    192,0,128,90,65,17,64,50,1,4,18,4,8,239,224,95,128,60,50,65,16,0,208,
-    44,194,32,221,142,18,129,16,224,11,85,192,31,161,16,129,14,4,196,130,
-    28,144,8,131,2,0,32,64,128,8,4,203,17,50,130,16,0,84,16,68,18,61,33,
-    0,1,224,19,224,48,17,129,32,1,32,33,4,64,2,1,16,18,16,240,8,33,112,
-    225,15,17,16,64,84,80,64,84,80,0,192,32,1,1,1,85,17,80,17,1,81,1,160,
-    0,1,8,1,32,1,128,85,0,159,0,29,1,85,16,5,16,17,84,24,65,81,1,0,2,128,
-    2,21,64,1,69,80,5,84,5,1,4,0,144,0,48,84,0,176,12,80,17,8,8,16,2,16,
-    0,16,48,80,0,0,69,80,65,80,65,16,64,4,6,64,1,64,85,85,81,191,14,30,
-    28,134,21,63,43,1,64,17,80,160,16,128,32,144,65,208,23,1,4,239,15,30,
-    0,17,16,1,5,81,85,80,5,80,17,0,136,144,8,160,0,68,16,21,32,21,80,8,
-    69,16,4,4,4,192,32,17,16,128,84,16,85,80,17,64,16,16,48,16,4,16,128,
-    4,128,14,80,81,80,6,69,0,65,64,64,16,128,32,171,32,4,0,1,16,0,17,1,
-    240,42,1,160,68,208,39,4,143,18,32,4,16,68,0,136,21,80,21,64,68,161,
-    16,0,16,10,16,17,0,85,90,64,0,66,81,0,65,128,4,81,128,2,81,0,68,49,
-    65,1,65,129,64,48,85,5,0,151,177,24,128,8,1,64,2,21,144,4,240,55,136,
-    141,12,128,38,0,5,80,128,3,80,1,20,68,4,65,16,128,22,65,128,60,5,38,
-    85,85,64,128,65,4,65,142,6,193,14,97,17,80,161,1,96,64,16,0,64,72,112,
-    69,177,46,0,80,161,16,1,68,48,17,81,65,224,47,85,0,32,16,24,8,0,1,84,
-    86,142,10,0,16,16,64,1,17,84,6,0,65,17,48,81,0,65,128,76,4,2,4,24,5,
-    4,32,5,80,69,80,240,60,0,152,192,32,143,24,0,16,64,128,6,38,1,96,41,
-    17,88,1,130,96,5,192,13,5,0,11,128,4,16,0,5,84,16,21,128,2,128,193,
-    0,155,0,128,0,144,0,128,65,234,81,22,128,32,240,70,84,16,64,18,26,0,
-    68,17,84,128,36,16,0,18,9,2,16,24,16,32,130,96,0,84,201,193,32,142,
-    104,128,42,1,5,129,16,17,0,174,1,129,28,0,224,11,84,128,64,239,15,0,
-    84,77,1,80,96,68,4,65,49,0,25,224,7,84,65,16,66,128,56,4,1,31,224,111,
-    80,5,64,144,8,193,28,128,54,99,4,2,2,64,24,64,32,32,80,192,65,208,43,
-    240,50,1,0,65,85,17,5,1,5,65,0,0,1,1,1,4,1,16,1,64,64,85,159,0,29,65,
-    85,80,5,0,8,21,5,64,1,0,3,65,85,3,16,21,0,69,1,4,0,164,0,0,128,2,85,
-    0,21,65,16,16,16,4,4,16,1,16,0,16,50,69,1,1,65,64,64,16,64,4,64,100,
-    129,14,81,191,14,30,15,11,0,21,81,64,12,68,16,4,4,4,226,16,1,85,36,
-    0,17,64,165,16,85,144,65,68,128,0,53,5,17,5,65,85,5,52,0,17,0,195,32,
-    15,32,80,128,8,64,0,5,16,17,4,65,1,1,0,7,1,0,69,16,21,65,57,128,18,
-    119,64,84,21,128,4,49,80,21,193,8,17,169,32,0,5,80,128,50,85,85,21,
-    218,144,4,240,7,130,18,69,137,46,128,14,64,128,4,3,80,1,68,4,65,16,
-    64,142,54,25,128,8,69,0,21,129,19,163,16,128,21,236,128,50,129,3,68,
-    128,16,69,130,2,68,1,88,5,81,128,14,5,176,2,195,32,0,5,240,16,128,12,
-    177,6,35,141,16,64,69,81,25,21,64,5,81,128,3,3,1,5,240,128,10,160,2,
-    160,3,194,32,143,36,1,21,0,21,81,0,84,53,128,129,16,69,0,80,140,21,
-    130,48,5,17,0,21,128,38,5,60,0,5,68,176,6,19,46,128,28,65,22,21,69,
-    4,80,4,0,128,8,192,26,238,17,5,128,18,64,64,144,8,195,32,126,179,69,
-    128,36,69,144,11,100,1,21,131,96,134,101,129,56,0,5,4,1,5,143,16,1,
-    6,5,64,69,65,16,68,4,129,38,113,1,5,144,33,4,130,18,5,16,4,71,37,1,
-    129,48,21,64,16,144,12,194,32,0,13,1,69,17,21,1,5,1,252,1,21,192,7,
-    128,42,177,71,99,143,16,3,5,110,65,64,37,49,0,128,8,37,132,18,165,4,
-    39,17,48,16,16,21,1,48,130,48,64,64,176,61,194,32,1,0,1,85,17,85,1,
-    5,65,0,5,1,1,1,4,1,16,1,64,64,85,159,0,29,1,85,80,5,0,8,21,68,69,1,
-    0,3,1,85,0,16,21,64,69,80,5,4,4,48,1,4,0,160,0,128,2,85,0,21,0,64,21,
-    32,16,8,16,2,16,64,0,16,50,69,64,69,64,64,22,16,64,4,64,129,14,81,191,
-    14,30,15,59,0,1,0,17,85,65,165,69,5,24,17,0,65,0,193,32,143,10,46,0,
-    68,0,16,21,0,85,64,21,64,68,80,16,4,2,17,128,30,85,64,85,1,0,17,64,
-    16,16,16,4,16,35,128,4,85,170,129,2,68,0,65,1,128,193,6,169,32,0,1,
-    80,5,1,5,58,85,85,21,144,4,240,7,130,18,69,138,22,32,80,5,64,128,4,
-    80,1,68,4,32,65,16,64,142,54,0,4,16,69,102,0,21,128,1,164,16,0,16,49,
-    128,2,154,84,3,0,65,80,33,80,3,41,1,1,81,128,64,69,160,10,5,0,240,193,
-    32,128,8,129,12,102,141,16,0,64,81,19,85,64,5,80,70,1,0,129,14,2,161,
-    18,64,5,16,17,4,65,141,18,25,32,16,144,21,128,4,70,0,64,100,128,6,69,
-    130,2,50,1,4,128,48,5,48,81,5,5,208,18,49,0,32,16,14,5,0,5,68,5,209,
-    22,17,142,16,1,0,80,69,65,21,68,5,80,138,160,19,17,0,2,17,128,74,80,
-    22,140,1,128,24,0,5,64,198,32,142,18,64,137,144,65,69,0,80,3,1,32,52,
-    131,96,17,0,3,128,20,21,65,68,244,0,83,31,1,224,11,32,4,35,0,66,2,16,
-    128,54,68,69,16,17,35,173,0,128,26,0,128,36,64,144,8,1,1,218,129,48,
-    128,96,176,27,84,193,32,142,80,64,131,16,208,65,144,4,65,0,130,42,68,
-    5,4,237,1,67,47,3,65,16,3,33,0,241,128,8,176,49,2,129,56,128,18,5,16,
-    4,85,4,16,112,21,128,36,16,3,1,80,131,48,69,176,61,68,193,32
-};
-
-static MTB * mtb_KQKQ = NULL;
-
-void initMTB_KQKQ()
-{
-    mtb_KQKQ = new MTB ("KQKQ", 2, 17);
-    mtb_KQKQ->SetPackedData (mtbdata_KQKQ);
-    mtb_KQKQ->Add (C2, A1, WHITE, 317);
-    mtb_KQKQ->Add (C3, A1, WHITE, 502);
-    mtb_KQKQ->Add (A3, B1, WHITE, 515);
-    mtb_KQKQ->Add (B3, B1, WHITE, 376);
-    mtb_KQKQ->Add (C3, B1, WHITE, 409);
-    mtb_KQKQ->Add (D2, B1, WHITE, 381);
-    mtb_KQKQ->Add (D3, B1, WHITE, 480);
-    mtb_KQKQ->Add (C3, C1, WHITE, 474);
-    mtb_KQKQ->Add (D3, C1, WHITE, 503);
-    mtb_KQKQ->Add (E2, C1, WHITE, 386);
-    mtb_KQKQ->Add (D2, B2, WHITE, 417);
-    mtb_KQKQ->Add (D3, B2, WHITE, 434);
-    mtb_KQKQ->Add (D4, B2, WHITE, 487);
-    mtb_KQKQ->Add (C4, C2, WHITE, 505);
-    mtb_KQKQ->Add (D4, C2, WHITE, 505);
-    mtb_KQKQ->Add (E2, C2, WHITE, 434);
-    mtb_KQKQ->Add (E3, C2, WHITE, 455);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KQKP
-
-static const byte mtbdata_KQKP[1662] = {
-    1,72,255,159,0,23,249,253,1,127,12,3,17,44,68,132,4,4,0,133,1,75,13,
-    248,143,8,12,255,15,76,252,143,16,28,98,79,49,108,79,28,132,7,255,249,
-    237,101,223,4,48,1,196,2,128,4,133,5,141,1,56,19,253,229,143,23,2,1,
-    132,9,68,132,101,7,6,132,3,4,6,2,3,12,168,139,6,63,24,0,20,12,15,14,
-    1,74,255,159,0,22,7,0,15,28,240,143,8,21,230,248,143,16,45,47,78,143,
-    31,27,2,2,239,36,29,15,111,1,64,255,159,0,23,7,9,25,49,97,57,193,127,
-    240,131,1,133,2,255,7,23,63,6,124,131,6,127,20,143,8,30,70,143,16,37,
-    64,89,218,177,111,34,23,121,15,3,80,48,84,145,55,133,3,101,197,138,
-    4,33,65,129,20,131,2,33,65,0,129,40,241,141,9,96,186,192,83,103,143,
-    12,4,70,53,97,51,103,49,225,133,4,132,8,97,199,47,12,70,0,5,193,1,74,
-    255,159,0,21,7,0,15,28,248,141,8,252,192,175,8,28,46,143,5,21,143,24,
-    30,47,14,130,7,32,76,64,128,239,23,20,2,2,255,36,29,159,0,111,1,64,
-    255,159,0,23,15,24,57,113,225,57,193,127,0,131,1,133,2,143,8,30,255,
-    127,217,63,45,30,63,23,56,63,36,136,6,49,97,84,18,121,141,9,112,20,
-    119,133,3,229,137,197,138,4,97,193,129,131,2,33,65,146,19,35,57,241,
-    133,6,224,192,51,237,231,143,20,1,41,5,225,4,142,5,199,192,143,8,12,
-    38,85,193,1,64,255,159,0,7,249,255,231,207,159,64,63,224,245,1,15,16,
-    44,68,132,0,5,4,0,8,16,32,64,128,109,1,0,134,1,15,6,240,252,9,143,8,
-    6,248,176,175,16,8,91,48,95,28,128,14,84,164,69,166,132,32,60,132,7,
-    12,4,144,0,32,163,96,10,8,128,19,133,5,5,34,60,131,5,44,64,143,13,2,
-    8,130,28,0,192,253,129,133,3,131,2,143,6,6,63,7,132,9,103,3,128,47,
-    36,31,41,1,64,255,159,0,7,15,23,36,68,132,64,4,224,228,1,63,0,8,16,
-    32,42,64,128,0,15,23,240,252,9,7,143,8,5,169,248,143,16,6,127,79,44,
-    23,133,6,8,0,88,132,1,48,79,3,16,132,5,1,66,130,224,2,138,2,143,30,
-    26,159,0,165,1,73,255,159,0,38,15,2,2,0,240,156,143,8,45,143,7,30,127,
-    126,47,44,160,8,47,27,31,0,2,7,27,19,35,67,0,15,58,2,7,11,131,1,51,
-    117,31,24,183,0,20,6,140,3,130,6,0,147,0,21,0,111,117,1,64,255,150,
-    0,62,125,236,204,140,88,12,0,15,5,192,151,5,3,0,2,190,1,146,3,254,77,
-    15,21,133,8,255,7,37,127,20,135,251,31,45,235,255,255,1,132,5,133,1,
-    152,133,7,79,19,8,8,135,2,159,0,132,1,88,255,150,0,159,183,1,15,19,
-    31,39,7,100,196,132,4,0,254,255,6,29,141,8,194,255,7,38,79,87,128,5,
-    212,148,20,0,128,1,49,68,132,4,128,6,133,7,31,39,228,214,4,133,2,6,
-    192,4,220,143,4,5,140,9,37,100,196,133,76,128,0,51,156,217,143,8,4,
-    38,6,5,124,52,132,28,0,63,12,132,4,1,64,255,159,0,23,31,48,108,196,
-    133,42,5,124,248,131,1,252,132,2,0,15,5,142,252,143,8,5,252,127,28,
-    79,11,143,7,0,5,240,63,106,23,143,22,12,128,5,32,76,132,69,5,4,129,
-    6,44,68,132,129,2,112,66,142,9,197,91,96,192,129,1,89,210,124,20,101,
-    108,35,48,236,134,7,239,132,131,3,133,10,133,8,220,143,22,2,16,37,183,
-    87,132,2,135,68,133,16,141,127,4,22,104,46,7,4,133,24,13,127,12,133,
-    5,1,64,255,159,0,7,15,31,36,68,132,67,4,248,228,1,7,248,7,0,15,26,240,
-    133,7,5,143,8,70,141,5,137,6,16,32,64,227,128,54,143,16,6,137,4,2,2,
-    2,138,2,0,79,209,1,64,255,150,0,30,63,108,204,140,115,12,0,15,5,135,
-    5,3,0,255,143,3,4,182,15,22,143,8,149,235,37,138,1,128,140,2,15,7,96,
-    8,8,22,95,133,1,64,255,159,0,39,63,97,228,197,132,160,4,143,8,169,225,
-    31,20,63,97,255,213,70,181,85,130,1,133,37,69,135,15,63,1,169,199,52,
-    193,52,221,143,5,5,133,4,75,140,3,133,140,2,128,0,51,156,143,8,4,178,
-    54,134,9,5,140,4,68,132,28,15,12,0,132,4,1,64,255,159,0,24,63,120,236,
-    221,141,1,127,248,255,63,112,236,197,133,200,129,2,132,1,239,7,21,0,
-    60,129,8,255,127,98,0,60,79,35,132,16,255,255,127,79,22,4,63,96,204,
-    133,5,254,32,88,34,172,93,140,17,56,108,220,64,67,255,127,44,47,88,
-    172,92,129,4,131,0,130,5,47,88,168,89,168,72,9,134,9,112,172,69,132,
-    130,3,108,196,44,18,240,237,5,196,19,133,11,224,91,193,129,138,2,252,
-    132,1,5,236,4,183,135,17,100,199,52,133,8,221,137,21,120,159,5,141,
-    3,197,135,116,133,16,129,30,143,5,2,0,37,5,1,64,255,159,0,24,47,95,
-    191,79,143,0,127,8,0,40,83,163,67,131,99,255,15,132,1,15,22,63,0,40,
-    129,8,224,134,7,63,30,132,16,63,25,47,79,135,7,193,7,161,15,66,255,
-    255,15,31,63,65,50,104,44,47,80,184,72,136,8,129,1,83,185,73,137,130,
-    13,187,75,194,139,130,2,132,5,67,131,3,3,65,148,115,100,19,35,3,211,
-    133,9,160,75,64,128,131,10,65,129,2,167,136,12,158,9,52,83,227,20,4,
-    128,4,132,8,156,67,67,163,71,79,4,118,4,163,216,195,77,133,2,135,31,
-    12,118,3,1,64,255,159,0,38,0,0,32,64,128,42,0,0,254,143,8,86,223,132,
-    1,191,31,29,160,239,31,40,225,209,0,32,0,255,89,54,174,73,136,134,6,
-    32,0,0,33,241,192,132,2,133,3,31,31,132,9,64,128,1,128,95,44,31,41,
-    1,73,255,159,0,43,254,254,143,8,212,254,239,0,131,1,222,254,255,191,
-    255,255,127,52,254,254,190,27,37,252,140,2,254,145,250,36,226,255,161,
-    2,241,220,242,41,35,252,178,4,114,2,255,253,37,131,3,255,252,4,240,
-    240,84,250,233,4,131,8,134,4,116,255,21,252,243,184,68,133,7,254,140,
-    6,29,54,1,75,255,159,0,43,199,254,143,8,44,231,255,7,44,45,31,87,247,
-    239,139,6,231,132,31,133,2,199,210,141,1,132,4,101,215,51,252,199,147,
-    6,34,252,199,191,114,228,199,221,143,7,4,92,193,248,67,223,133,3,148,
-    13,133,5,135,220,159,143,15,5,68,197,132,8,143,4,6,55,1,64,255,159,
-    0,15,31,47,73,136,8,104,8,192,159,5,16,15,121,247,191,2,0,127,254,189,
-    127,143,8,24,245,239,3,11,159,9,16,79,12,15,17,253,0,175,37,86,1,64,
-    255,158,0,124,250,217,152,24,90,24,0,15,5,128,159,6,0,15,145,247,143,
-    8,45,144,215,79,109,255,223,175,35,70,1,64,255,159,0,15,16,62,73,136,
-    8,212,8,143,5,16,15,187,247,143,8,109,245,159,29,86,1,64,255,158,0,
-    60,126,219,153,24,118,24,0,15,5,143,6,0,15,211,215,143,8,173,15,7
-};
-
-static MTB * mtb_KQKP = NULL;
-
-void initMTB_KQKP()
-{
-    mtb_KQKP = new MTB ("KQKP", 1, 23);
-    mtb_KQKP->SetPackedData (mtbdata_KQKP);
-    mtb_KQKP->Add (A2, A1, WHITE, 90);
-    mtb_KQKP->Add (A2, B1, WHITE, 31);
-    mtb_KQKP->Add (A2, C1, WHITE, 98);
-    mtb_KQKP->Add (A2, B2, WHITE, 44);
-    mtb_KQKP->Add (A2, C2, WHITE, 88);
-    mtb_KQKP->Add (C2, A1, WHITE, 111);
-    mtb_KQKP->Add (C2, B1, WHITE, 69);
-    mtb_KQKP->Add (C2, C1, WHITE, 61);
-    mtb_KQKP->Add (C2, D1, WHITE, 60);
-    mtb_KQKP->Add (C2, E1, WHITE, 87);
-    mtb_KQKP->Add (C2, A2, WHITE, 117);
-    mtb_KQKP->Add (C2, B2, WHITE, 52);
-    mtb_KQKP->Add (C2, D2, WHITE, 44);
-    mtb_KQKP->Add (C2, E2, WHITE, 74);
-    mtb_KQKP->Add (C2, A3, WHITE, 145);
-    mtb_KQKP->Add (C2, B3, WHITE, 138);
-    mtb_KQKP->Add (C2, C3, WHITE, 63);
-    mtb_KQKP->Add (C2, D3, WHITE, 82);
-    mtb_KQKP->Add (C2, E3, WHITE, 76);
-    mtb_KQKP->Add (D2, C1, WHITE, 46);
-    mtb_KQKP->Add (D2, E1, WHITE, 34);
-    mtb_KQKP->Add (D2, C2, WHITE, 26);
-    mtb_KQKP->Add (D2, E2, WHITE, 26);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KRKP
-
-static const byte mtbdata_KRKP[10529] = {
-    1,71,0,159,0,13,64,85,1,155,2,141,2,8,15,79,64,85,81,85,9,81,85,173,
-    64,168,0,85,143,18,47,84,234,11,15,2,65,125,140,16,0,31,93,143,32,0,
-    167,16,31,48,131,9,65,248,134,10,165,1,143,2,8,159,64,93,159,0,255,
-    63,58,1,74,0,159,0,239,80,85,15,14,64,168,0,54,85,85,5,143,16,61,225,
-    10,69,136,12,195,1,248,68,36,133,2,63,72,159,0,255,47,219,1,8,0,0,84,
-    85,159,0,43,1,84,60,85,1,0,167,0,129,10,143,2,28,15,46,84,172,1,140,
-    12,5,60,21,143,18,14,191,28,15,85,253,17,22,50,143,30,43,47,1,24,131,
-    14,21,123,43,84,167,16,143,16,67,138,49,67,21,143,12,61,255,63,16,27,
-    5,31,88,117,55,17,15,90,160,19,41,21,15,79,1,90,0,159,0,49,32,143,2,
-    45,143,16,47,2,28,204,8,28,156,14,32,128,47,61,31,41,64,7,85,85,85,
-    64,85,69,192,0,165,0,255,49,23,143,46,66,47,18,201,2,143,14,82,47,0,
-    231,2,240,143,16,100,143,1,0,143,18,96,173,33,127,64,1,32,84,85,159,
-    0,44,1,0,0,0,251,1,170,0,143,2,28,15,46,143,16,46,193,24,21,143,32,
-    105,183,194,8,16,0,4,35,21,194,2,3,253,131,4,6,143,14,45,46,194,24,
-    135,49,5,149,127,92,129,127,54,15,7,141,58,165,16,53,5,255,143,16,70,
-    3,25,3,23,3,63,72,195,1,240,89,1,57,1,31,60,1,43,84,85,159,0,2,81,166,
-    1,65,160,0,128,225,0,167,3,1,0,0,0,8,0,255,68,132,2,208,1,82,11,95,
-    30,15,8,175,19,0,127,129,16,0,76,15,72,195,33,140,32,192,0,79,86,248,
-    133,30,139,2,143,40,24,111,84,143,18,255,127,151,1,43,80,85,159,0,2,
-    69,166,1,64,194,1,47,135,2,5,0,3,5,164,0,15,94,165,14,170,133,18,244,
-    1,2,252,4,8,60,32,60,248,128,143,17,46,143,16,110,159,0,255,127,255,
-    15,20,1,45,84,85,159,0,2,1,175,1,1,132,2,0,63,161,6,1,0,167,0,47,44,
-    15,46,128,10,208,8,95,138,12,5,44,21,44,138,16,255,31,3,111,25,84,114,
-    1,28,5,44,21,79,14,65,119,85,64,164,0,47,35,143,48,41,65,228,8,149,
-    0,127,21,21,143,60,53,15,24,134,58,4,165,16,143,16,86,254,83,9,83,111,
-    88,17,11,17,47,76,1,64,0,159,0,13,64,85,5,0,132,20,168,6,0,4,164,0,
-    0,128,2,84,183,85,8,1,175,8,14,15,46,64,233,13,0,220,140,16,32,63,48,
-    1,166,0,35,169,12,5,127,0,20,28,197,33,130,34,47,53,139,16,169,16,248,
-    129,28,57,143,30,50,63,8,31,255,15,205,1,68,0,159,0,30,85,80,85,11,
-    0,73,0,4,160,0,84,85,35,85,5,243,175,10,31,15,29,143,16,13,128,2,159,
-    26,29,85,5,76,7,95,12,64,85,4,85,20,228,13,131,14,227,111,44,145,38,
-    129,7,37,21,0,20,192,18,111,7,5,224,11,141,30,84,160,16,163,0,143,14,
-    32,124,15,4,16,100,143,2,6,165,2,15,255,15,173,1,3,0,0,85,85,150,170,
-    167,0,43,159,0,31,2,0,18,85,21,5,131,2,124,31,24,64,182,7,31,36,140,
-    16,68,47,24,85,87,85,21,172,10,106,28,20,168,0,128,12,215,90,143,14,
-    32,47,18,42,174,2,16,84,31,86,224,175,16,0,31,94,95,255,31,67,1,16,
-    0,0,85,159,0,60,5,85,85,116,4,0,167,0,129,12,143,2,12,80,159,16,61,
-    85,179,5,140,14,21,143,18,14,15,30,4,84,23,214,50,143,30,27,31,18,10,
-    28,84,143,12,61,31,16,255,10,160,1,147,0,19,165,16,143,16,86,39,21,
-    254,35,63,88,37,23,1,63,90,3,25,128,84,143,14,63,1,64,0,159,0,13,84,
-    85,85,85,165,4,170,166,170,6,0,4,163,0,85,239,5,130,1,129,3,1,1,159,
-    8,14,15,45,133,13,87,5,69,143,16,93,68,192,12,21,232,16,21,187,165,
-    0,15,54,1,84,141,31,163,2,20,143,30,81,192,75,111,255,79,197,1,3,20,
-    85,85,85,150,170,167,0,94,159,0,30,0,31,1,128,143,2,25,240,15,31,43,
-    143,16,3,183,8,60,32,60,152,16,21,192,8,193,0,255,111,59,15,27,45,207,
-    10,4,159,0,10,143,14,28,95,36,201,2,254,31,82,47,0,231,2,143,16,100,
-    143,1,0,143,18,96,173,33,143,12,48,1,6,84,84,85,85,85,193,0,159,0,53,
-    125,4,0,1,169,0,143,2,12,175,15,56,134,16,5,118,140,14,21,111,31,143,
-    34,41,143,32,30,4,192,13,79,0,188,0,76,84,143,10,24,79,53,149,0,69,
-    84,255,2,140,6,29,143,18,63,139,16,167,16,38,29,255,79,63,91,101,120,
-    13,31,63,43,51,192,74,93,143,14,31,1,40,68,85,159,0,30,69,236,1,5,0,
-    13,0,0,32,0,4,164,0,3,32,234,132,1,5,143,2,14,15,8,84,142,16,5,15,1,
-    250,0,12,63,56,15,0,143,32,2,15,72,68,130,12,223,20,134,17,131,14,4,
-    128,3,143,48,10,159,0,46,129,9,127,137,10,4,160,16,139,2,31,12,143,
-    60,24,79,84,143,18,255,0,54,1,45,68,85,145,0,64,226,0,15,2,84,26,140,
-    4,4,85,0,2,133,2,68,82,243,42,211,2,6,95,30,15,24,85,85,177,15,255,
-    246,15,132,16,15,88,195,33,95,104,141,14,143,48,4,111,110,128,143,18,
-    255,79,135,1,46,84,85,145,0,21,226,0,15,2,141,4,41,1,85,0,16,20,134,
-    2,20,0,27,1,2,0,20,164,0,3,2,132,1,245,5,95,8,159,15,7,15,21,143,16,
-    5,8,140,14,32,119,60,128,111,54,156,27,31,31,20,162,8,229,23,255,133,
-    24,159,0,30,143,46,24,143,2,4,127,62,63,94,143,32,94,143,14,46,0,63,
-    120,1,47,84,85,145,0,5,243,0,15,7,135,4,63,173,5,4,0,227,0,167,0,143,
-    2,28,143,15,12,47,32,55,160,10,5,0,232,13,112,21,76,143,16,78,191,208,
-    36,140,14,21,143,34,9,227,42,76,167,22,159,0,30,223,143,50,46,53,141,
-    10,84,15,45,63,32,141,26,165,16,255,143,16,86,67,41,67,95,88,1,123,
-    1,0,143,14,60,1,72,0,159,0,13,64,85,15,32,17,0,141,16,166,0,0,0,84,
-    143,2,13,15,30,84,108,255,15,14,5,231,11,130,14,21,60,143,18,0,85,245,
-    16,38,79,50,143,32,28,15,18,0,12,80,255,143,12,61,15,16,137,48,1,165,
-    16,143,16,86,55,5,248,51,79,88,53,39,17,15,90,16,0,160,128,44,80,143,
-    12,47,1,65,0,159,0,30,85,85,85,26,162,55,167,0,170,86,178,2,7,5,175,
-    10,31,159,16,32,209,10,173,2,143,10,30,21,142,12,16,0,20,177,166,0,
-    129,2,149,205,14,58,0,0,16,186,40,159,0,54,4,36,127,255,31,235,128,
-    175,10,63,1,20,0,0,85,159,0,45,65,140,2,0,72,0,16,160,0,81,85,35,85,
-    21,184,0,15,13,84,143,16,76,16,15,12,21,85,6,17,84,20,84,21,85,35,95,
-    34,24,16,84,80,84,137,14,31,30,16,0,220,20,6,129,50,16,204,50,143,2,
-    12,16,0,62,80,85,80,192,16,65,143,14,16,159,0,255,79,235,1,4,0,0,0,
-    84,85,156,0,82,247,2,167,0,128,4,15,35,214,9,21,191,5,15,142,16,227,
-    15,44,108,132,2,15,10,81,84,80,166,0,92,161,6,80,172,10,168,28,231,
-    5,129,6,0,58,85,0,84,55,143,14,17,159,0,33,168,174,2,252,64,20,63,86,
-    175,16,0,63,94,127,255,15,51,1,16,0,0,85,159,0,76,21,85,85,116,16,0,
-    167,0,129,14,139,2,84,159,16,77,85,207,21,143,16,12,159,12,48,17,80,
-    23,49,47,14,77,15,32,40,12,80,1,143,12,60,15,16,40,255,160,1,147,0,
-    3,165,16,143,16,86,23,5,19,252,47,88,21,7,17,47,90,19,9,80,128,1,143,
-    12,46,1,74,0,159,0,13,84,85,15,28,17,193,0,94,0,16,168,0,64,143,2,11,
-    15,30,143,18,14,128,16,223,5,233,13,128,14,21,76,127,27,143,32,78,46,
-    175,141,8,34,0,44,80,143,6,13,63,41,15,5,255,149,0,5,167,16,143,12,
-    52,15,16,43,103,31,86,254,35,9,35,31,88,33,91,1,143,12,44,1,64,0,159,
-    0,29,80,85,85,85,154,128,170,167,0,16,84,21,0,26,0,119,16,0,240,2,2,
-    193,4,10,204,6,143,2,14,210,15,26,133,13,53,16,192,0,26,2,140,16,175,
-    8,140,14,32,108,128,63,40,129,32,33,239,58,112,165,0,47,64,64,194,26,
-    143,2,10,143,14,30,224,143,18,30,143,12,62,143,16,255,47,105,1,0,80,
-    84,0,0,80,86,80,102,84,85,149,0,160,1,90,170,167,0,15,30,171,0,15,2,
-    2,143,2,8,81,128,14,82,137,16,171,15,45,127,3,34,92,130,27,81,16,243,
-    165,0,129,32,194,0,31,58,111,9,0,0,192,6,255,141,2,154,0,207,10,5,79,
-    10,143,14,11,63,53,201,2,127,82,252,15,0,231,2,143,16,100,143,1,0,143,
-    18,96,173,33,143,10,31,1,44,84,85,156,0,81,196,0,15,53,16,251,0,17,
-    169,0,139,2,175,13,56,143,16,7,21,127,47,148,143,34,41,143,32,14,17,
-    80,95,18,0,60,80,242,1,143,10,6,63,70,149,0,53,80,9,140,6,255,8,143,
-    18,78,139,16,167,16,22,68,143,34,72,59,255,69,88,18,143,50,74,11,19,
-    42,16,0,143,12,28,1,45,84,85,159,0,2,81,239,2,3,143,4,18,17,63,227,
-    0,0,16,166,0,161,12,143,2,8,15,30,143,22,4,55,138,16,5,0,232,13,128,
-    14,21,108,31,27,170,63,14,143,32,3,5,60,21,31,40,16,226,10,255,1,135,
-    8,131,48,140,2,136,44,79,29,95,22,134,18,119,4,80,143,12,61,15,16,139,
-    16,16,166,16,95,86,254,99,41,3,95,88,1,27,1,143,12,44,1,1,80,85,0,85,
-    85,85,16,175,192,0,147,0,21,226,0,80,134,2,31,4,137,4,136,129,8,55,
-    16,85,0,16,128,0,223,16,164,0,3,128,132,1,5,125,15,24,43,207,17,0,0,
-    84,143,16,1,8,12,32,12,246,159,16,39,15,1,143,32,2,15,71,131,12,80,
-    134,44,131,2,126,144,2,27,143,48,72,159,0,4,127,14,111,142,143,34,232,
-    1,6,84,85,0,84,85,194,0,147,0,180,21,226,0,1,245,1,30,81,140,4,16,107,
-    84,0,18,134,2,85,225,1,168,211,2,255,70,127,8,192,9,138,16,47,32,139,
-    24,83,47,24,223,15,49,196,16,15,36,17,132,40,31,38,141,14,143,48,4,
-    192,31,110,143,18,255,31,119,1,1,84,85,1,84,85,85,85,233,193,0,153,
-    0,141,2,22,81,140,4,5,84,72,129,8,81,38,80,0,17,8,0,221,80,164,0,19,
-    8,132,1,21,103,85,213,143,16,4,31,39,84,8,92,32,12,128,203,15,70,156,
-    29,15,15,81,84,225,0,88,134,38,255,131,2,229,1,133,24,159,0,30,143,
-    46,8,127,20,47,62,143,14,126,192,143,16,62,63,110,127,40,1,38,84,85,
-    159,0,2,21,80,255,2,3,143,4,2,147,81,171,3,17,0,225,0,16,0,167,0,231,
-    143,2,12,143,13,12,15,48,160,12,21,0,232,15,143,16,97,175,240,37,63,
-    8,17,146,48,17,140,2,141,4,167,24,231,159,0,30,143,48,14,127,22,141,
-    10,80,1,95,44,79,32,255,141,26,165,16,143,16,86,19,73,19,47,88,17,192,
-    75,17,143,12,44,1,83,0,159,0,30,85,15,32,64,0,167,0,232,145,8,139,2,
-    15,46,255,15,15,21,143,16,12,85,85,189,69,141,4,84,39,255,29,33,143,
-    32,29,79,2,74,75,76,106,76,0,2,140,8,1,143,12,28,243,79,16,165,16,21,
-    37,21,0,2,135,48,255,162,3,36,143,16,54,5,55,3,55,5,255,134,8,2,31,
-    56,3,57,1,57,3,247,120,0,31,58,1,59,106,60,1,64,122,1,143,14,30,1,82,
-    0,148,0,85,15,41,104,136,167,0,47,170,90,1,203,9,21,165,12,15,12,143,
-    16,42,176,11,108,85,15,20,168,17,0,21,0,198,1,166,0,161,19,65,0,81,
-    54,162,21,216,2,189,14,11,65,56,159,0,52,40,0,223,16,20,36,2,159,11,
-    53,175,16,25,44,111,255,0,15,164,1,4,0,0,0,80,64,160,0,85,196,84,4,
-    159,0,63,1,0,65,0,69,157,85,3,85,85,160,14,43,143,16,111,65,8,123,84,
-    69,80,81,143,18,41,5,85,28,65,80,65,81,135,14,15,15,108,85,6,84,64,
-    0,85,0,81,164,0,128,2,232,85,96,74,111,12,5,16,64,85,120,64,81,83,143,
-    14,16,15,255,15,219,1,65,0,156,0,85,85,0,80,85,63,12,72,9,167,0,141,
-    6,15,22,231,9,191,5,15,205,142,16,15,44,127,32,64,81,39,161,6,64,48,
-    172,10,160,86,139,2,139,6,0,84,0,229,80,39,143,14,17,159,0,33,160,2,
-    190,2,1,248,116,31,85,175,16,1,31,93,95,255,31,35,1,2,0,0,85,85,64,
-    0,167,0,198,85,159,0,81,137,14,0,0,84,140,2,143,16,94,39,85,85,65,79,
-    61,69,64,7,47,2,86,63,48,160,141,4,5,12,32,127,60,15,0,239,160,160,
-    1,147,0,20,37,140,6,164,16,143,16,70,255,23,118,18,136,2,18,47,72,21,
-    72,250,16,106,16,15,74,19,42,37,76,128,32,143,10,14,1,86,0,159,0,29,
-    85,12,80,196,0,15,6,87,64,84,32,0,169,0,0,138,2,15,46,190,191,15,9,
-    133,16,21,31,58,197,30,127,22,143,32,14,111,4,212,74,103,130,8,106,
-    108,0,28,64,255,1,28,141,4,143,16,29,169,16,19,103,19,255,149,0,134,
-    8,6,143,12,20,31,16,5,21,69,255,5,140,16,22,63,54,21,7,19,39,255,60,
-    4,47,56,3,25,1,25,44,128,50,143,14,28,1,64,0,159,0,45,65,85,85,85,106,
-    128,170,167,0,65,80,85,0,105,0,119,65,0,240,2,2,193,4,40,204,6,141,
-    2,192,15,42,133,13,53,65,84,85,85,105,175,8,140,16,32,44,128,47,56,
-    129,32,193,0,255,42,80,165,0,15,18,143,34,16,170,4,194,26,143,2,10,
-    255,143,14,30,111,18,201,2,143,12,66,15,16,231,2,143,16,100,143,1,0,
-    192,143,18,96,173,33,47,48,1,88,0,145,0,8,226,0,2,69,81,3,0,0,64,89,
-    64,81,85,5,47,160,1,106,169,167,0,170,15,29,167,11,8,163,136,14,1,2,
-    138,16,85,85,72,57,191,15,45,63,3,136,143,18,8,49,129,32,165,0,17,255,
-    194,0,15,58,28,139,2,192,6,125,154,0,207,10,5,255,47,10,143,14,11,31,
-    53,201,2,95,82,31,0,231,2,143,16,100,224,143,1,0,143,18,96,173,33,143,
-    8,15,1,2,68,64,85,85,64,0,167,0,189,85,157,0,69,196,0,31,53,161,12,
-    137,14,69,222,143,16,14,31,61,111,2,65,15,30,143,34,41,143,32,14,143,
-    2,20,183,0,141,4,5,169,24,224,0,32,76,170,28,223,15,50,149,0,86,37,
-    140,6,166,16,143,16,68,101,255,72,4,134,2,4,127,70,3,26,2,255,56,2,
-    95,72,131,65,122,0,26,0,0,143,10,12,1,18,64,84,85,144,0,64,0,165,0,
-    218,80,241,1,14,64,239,2,3,143,4,18,69,140,12,121,64,0,139,2,15,46,
-    159,19,13,129,16,21,0,239,232,15,47,46,143,14,14,143,32,3,21,47,56,
-    131,42,167,26,127,132,44,1,140,2,141,4,127,7,63,38,149,0,70,255,166,
-    16,143,14,68,63,0,92,22,79,86,19,10,248,18,79,88,17,12,16,143,10,28,
-    1,17,64,84,0,145,0,2,64,0,1,163,0,85,85,1,84,85,85,65,223,192,0,4,162,
-    1,65,246,1,15,4,137,4,130,8,209,167,3,140,12,52,2,149,1,64,84,1,229,
-    81,125,63,23,207,17,0,1,80,143,16,1,34,127,44,130,159,13,31,31,24,143,
-    32,2,31,14,134,34,159,0,30,55,35,64,1,21,132,2,10,59,143,48,8,248,15,
-    69,29,63,30,143,16,110,143,18,126,31,71,1,19,68,80,0,144,0,160,2,225,
-    0,13,1,85,85,0,80,195,0,4,84,109,209,0,5,18,15,2,69,18,137,4,1,103,
-    224,8,1,128,9,67,65,84,139,12,67,61,135,2,69,80,129,6,71,192,9,192,
-    15,1,251,135,16,31,31,193,7,135,8,67,31,8,69,130,32,255,15,59,192,33,
-    15,30,137,34,143,6,12,31,14,141,14,143,48,4,192,15,110,143,18,126,15,
-    232,1,16,68,81,0,144,0,32,0,64,128,1,163,0,85,85,5,80,85,85,123,85,
-    81,192,0,9,141,2,6,69,142,4,79,129,8,69,70,69,1,139,12,225,1,1,46,85,
-    69,81,177,2,34,143,16,14,79,39,100,191,32,12,128,7,225,0,15,72,150,
-    29,122,223,13,135,36,131,2,96,228,1,21,37,133,24,252,111,30,135,46,
-    31,36,111,62,143,16,94,143,14,142,47,104,1,19,68,80,85,144,0,64,0,165,
-    0,181,225,1,15,1,64,143,3,5,143,4,0,69,155,3,69,238,162,10,135,12,163,
-    12,39,69,143,16,20,159,0,55,63,132,223,65,143,18,7,143,2,4,0,118,79,
-    30,61,15,38,127,142,10,5,79,44,15,32,142,26,164,16,47,86,3,252,138,
-    4,2,31,88,1,60,0,143,10,28,1,74,0,159,0,13,84,85,15,12,17,192,4,95,
-    16,0,135,2,1,159,10,14,15,29,143,16,206,143,12,14,110,13,69,60,131,
-    14,4,166,0,143,28,62,111,78,138,64,192,8,65,85,68,48,69,226,0,171,65,
-    22,64,28,84,28,4,2,128,95,46,159,0,238,1,93,0,159,0,37,85,4,143,8,38,
-    15,38,16,251,194,0,229,15,143,16,96,15,62,167,25,31,4,64,232,0,188,
-    143,30,66,161,11,69,128,12,193,0,163,0,131,2,64,124,85,68,114,7,19,
-    143,46,174,159,0,254,1,34,80,85,159,0,28,0,85,64,224,0,243,69,135,2,
-    4,45,15,70,85,85,171,22,245,143,16,96,143,14,4,127,86,143,26,64,129,
-    58,64,160,58,64,252,162,0,133,2,89,105,143,28,158,159,0,255,44,1,53,
-    84,85,159,0,46,171,8,0,6,20,220,140,2,15,4,239,10,6,81,143,16,85,143,
-    20,38,111,120,4,117,0,20,128,5,229,16,15,80,81,194,51,1,112,142,14,
-    4,162,0,143,2,8,159,0,20,64,85,64,112,85,1,127,25,15,255,15,29,1,32,
-    80,85,159,0,46,80,85,5,0,29,0,0,5,0,227,1,176,1,4,5,221,162,0,15,14,
-    239,10,6,69,143,16,85,15,30,127,0,16,252,228,13,31,86,173,17,139,2,
-    31,78,15,255,15,173,1,65,0,159,0,13,84,85,85,85,69,173,192,0,15,6,65,
-    16,5,226,4,3,1,191,159,10,14,15,29,68,207,15,9,132,16,176,14,31,72,
-    167,11,87,87,5,143,32,57,4,192,0,80,160,8,143,28,6,222,205,47,141,14,
-    131,2,4,166,0,239,28,46,131,24,79,14,220,4,166,2,143,46,58,1,80,159,
-    0,255,79,77,1,82,0,159,0,29,85,15,13,0,80,192,0,113,0,20,135,2,15,61,
-    143,16,15,84,85,5,254,95,90,240,29,34,139,14,95,24,143,26,14,79,2,25,
-    219,64,193,12,46,16,166,0,79,14,16,204,11,48,143,2,18,80,85,136,28,
-    194,14,16,85,16,254,84,192,0,0,239,42,36,141,22,63,2,229,0,37,224,80,
-    138,28,143,30,22,159,0,254,1,6,84,85,85,85,0,192,0,159,0,38,171,0,144,
-    8,0,168,8,0,31,29,21,207,15,41,55,132,14,84,0,152,24,128,2,80,143,16,
-    26,255,15,45,221,143,30,31,143,32,14,13,20,228,0,165,0,145,22,64,220,
-    162,46,116,159,0,87,16,34,63,255,63,173,1,41,84,85,159,0,46,65,236,
-    1,1,0,62,1,4,0,163,11,19,15,40,143,20,16,143,16,240,226,69,127,3,163,
-    0,239,12,22,1,0,5,18,171,68,160,1,80,136,10,69,143,2,3,5,134,19,111,
-    133,14,68,143,16,43,129,5,4,18,101,163,2,188,133,8,161,1,20,76,139,
-    12,143,14,28,159,0,248,1,34,84,85,159,0,44,1,84,0,224,0,172,20,134,
-    2,0,4,20,164,0,15,14,85,119,85,21,224,6,193,0,15,38,0,176,24,143,16,
-    42,247,15,46,133,14,143,32,42,239,15,46,15,40,20,160,8,128,40,240,134,
-    22,133,2,165,24,127,255,95,189,1,35,84,85,159,0,62,65,85,69,224,0,151,
-    193,0,227,1,1,0,3,4,162,0,13,246,167,5,143,16,100,79,14,143,36,32,15,
-    222,69,138,14,6,175,80,15,28,4,164,13,69,132,6,133,15,143,2,6,253,13,
-    37,143,14,8,111,14,139,4,195,1,43,20,224,28,143,12,12,143,46,36,159,
-    0,94,1,35,84,85,159,0,62,0,85,0,2,139,228,1,15,13,85,85,69,130,5,64,
-    143,10,7,247,163,20,143,4,8,143,16,44,31,30,143,14,0,68,194,19,135,
-    20,191,171,5,15,94,68,175,14,63,13,35,151,17,132,2,224,148,73,239,15,
-    76,159,0,255,111,47,1,34,84,85,159,0,62,0,85,0,0,110,20,0,228,1,6,20,
-    162,0,13,229,6,255,21,143,10,7,163,20,143,4,8,143,16,52,63,40,173,40,
-    229,13,187,47,86,171,9,84,134,2,141,14,47,6,20,207,43,25,191,239,15,
-    8,159,0,38,20,134,38,164,0,6,195,2,95,255,0,47,61,1,95,0,159,0,29,85,
-    0,210,0,15,5,193,3,221,227,4,132,2,15,61,80,207,15,9,143,16,82,207,
-    14,3,84,81,31,44,21,200,1,16,194,12,16,0,20,219,164,0,141,30,141,2,
-    64,140,50,51,16,102,112,159,0,40,4,50,79,255,79,189,1,36,84,85,159,
-    0,76,0,64,192,0,0,177,80,246,12,0,12,255,15,77,85,0,80,119,143,16,92,
-    65,20,139,14,95,8,21,192,2,97,237,53,201,1,207,14,26,53,1,193,12,14,
-    64,133,166,0,63,2,21,85,20,0,21,0,131,156,66,143,2,18,65,85,80,84,80,
-    193,0,129,16,81,0,85,64,84,64,80,240,96,48,159,14,3,159,0,255,79,104,
-    1,44,84,85,159,0,0,1,223,2,2,15,21,0,151,80,192,0,84,0,166,8,0,15,13,
-    158,8,181,207,15,41,132,14,80,233,10,128,2,64,143,16,14,1,222,191,14,
-    44,61,159,0,27,5,128,30,138,46,141,32,44,155,80,34,80,84,165,0,161,
-    45,0,162,46,184,68,159,0,87,64,66,15,255,15,157,1,44,80,85,159,0,62,
-    0,192,0,231,10,0,95,85,0,2,16,164,0,15,46,195,11,239,22,10,209,143,
-    16,78,143,32,114,57,16,140,34,0,85,21,78,15,57,16,128,8,64,85,129,7,
-    164,8,132,2,255,80,12,134,12,129,14,192,0,10,143,32,57,159,0,255,0,
-    127,45,1,42,84,85,159,0,62,1,176,10,81,246,10,190,0,4,80,164,0,13,159,
-    8,1,193,7,255,18,41,127,1,80,143,16,45,63,45,133,14,31,22,194,21,137,
-    30,205,191,50,43,158,12,47,13,81,84,193,16,229,19,80,224,143,2,7,165,
-    24,31,255,31,173,1,40,80,85,159,0,78,0,160,0,85,85,219,16,231,1,4,16,
-    18,143,6,0,21,226,0,248,143,19,14,143,23,24,143,16,12,63,30,143,32,
-    62,31,160,16,85,237,64,138,12,131,2,135,50,16,138,14,22,65,247,31,36,
-    224,9,228,5,164,6,102,80,108,164,0,224,141,12,143,14,7,143,32,36,159,
-    0,190,1,35,84,85,159,0,78,0,84,0,2,213,228,1,12,143,6,0,21,226,0,1,
-    143,10,7,81,222,143,4,13,143,16,46,31,46,17,194,19,135,20,171,13,15,
-    94,199,17,174,14,13,17,84,1,208,64,193,0,45,159,0,36,16,85,209,24,0,
-    134,8,132,2,80,192,143,30,4,47,255,47,109,1,34,84,85,159,0,78,1,84,
-    0,0,119,80,0,227,1,193,1,3,80,162,0,255,6,7,127,159,19,7,81,143,4,13,
-    143,16,53,79,39,132,20,23,228,13,243,31,86,133,12,229,17,139,14,79,
-    0,81,84,194,41,237,134,42,132,2,239,15,45,159,0,22,81,226,51,229,17,
-    80,224,95,9,195,2,127,255,79,45,1,84,0,144,0,1,160,0,85,15,41,1,251,
-    84,207,3,12,233,11,129,14,15,0,207,15,40,65,207,19,17,212,143,16,16,
-    47,40,223,14,3,80,31,40,1,193,10,0,209,21,194,50,161,0,65,194,12,65,
-    0,81,219,116,141,30,141,2,1,140,50,131,16,65,22,112,159,0,40,16,18,
-    127,255,31,173,1,41,84,85,159,0,92,0,2,65,81,189,255,14,93,135,16,64,
-    193,16,56,145,32,223,32,73,5,191,196,0,143,32,6,5,192,15,1,154,0,143,
-    18,45,137,14,148,141,12,131,16,0,1,165,0,1,208,1,5,28,0,17,0,16,48,
-    17,129,2,85,4,84,81,0,85,0,81,18,1,208,80,203,11,95,14,0,0,5,85,64,
-    114,81,64,49,64,1,0,84,144,9,224,65,148,10,159,0,255,127,108,1,6,4,
-    80,85,85,0,192,0,159,0,10,205,5,143,4,13,31,10,0,64,129,12,167,12,0,
-    216,28,193,11,143,16,11,81,207,15,40,132,14,64,0,211,0,138,2,14,1,12,
-    5,84,207,14,44,197,12,159,0,11,131,32,65,81,64,164,0,21,236,48,138,
-    46,29,60,64,50,229,7,64,157,81,193,41,0,84,161,46,164,0,159,0,88,1,
-    192,50,79,255,79,140,1,1,84,85,1,84,85,85,65,235,192,0,159,0,70,130,
-    12,232,1,0,18,64,164,0,255,132,4,143,16,41,193,11,39,127,32,133,2,143,
-    32,100,159,0,46,234,207,31,0,193,41,201,2,32,84,31,56,65,129,8,231,
-    85,129,7,51,133,2,64,1,108,164,0,248,130,14,192,0,106,143,32,57,159,
-    0,255,95,29,1,0,84,85,5,80,85,85,85,234,81,192,0,159,0,69,129,12,69,
-    38,0,20,124,64,1,163,0,131,4,143,16,11,193,3,143,20,41,5,241,64,127,
-    45,79,45,133,14,47,8,5,0,84,249,40,194,21,137,18,207,19,43,69,63,6,
-    5,80,129,192,0,8,69,81,0,0,85,85,248,229,1,143,2,8,165,6,143,12,14,
-    63,255,47,125,1,34,84,85,159,0,94,1,84,1,208,14,223,65,159,15,24,162,
-    5,65,223,4,13,143,4,14,143,16,9,191,32,93,125,109,0,148,0,15,100,229,
-    15,31,38,161,9,1,250,171,49,130,2,135,50,112,138,30,132,16,4,63,36,
-    84,65,84,131,1,64,194,0,64,24,64,236,1,28,138,4,165,2,1,12,159,0,238,
-    1,32,84,85,159,0,94,1,80,1,0,26,0,0,1,0,159,6,23,178,20,5,159,21,7,
-    231,69,226,0,143,4,8,143,16,14,69,84,193,7,143,8,24,179,159,0,30,141,
-    12,1,192,15,111,104,69,84,208,15,143,24,160,2,80,5,80,225,34,134,6,
-    130,2,142,193,0,111,36,65,84,1,192,13,135,10,36,240,64,227,1,235,15,
-    79,255,47,95,1,32,84,85,159,0,94,5,80,1,0,21,0,0,65,1,255,6,29,81,159,
-    3,6,69,247,143,4,13,143,16,53,15,39,132,20,71,0,79,92,208,13,204,82,
-    229,17,133,2,64,1,161,0,160,2,81,111,5,80,225,0,134,22,81,194,41,77,
-    239,15,45,63,159,0,6,69,81,139,18,37,227,1,47,10,195,2,192,143,12,14,
-    79,255,60,1,36,84,85,159,0,76,1,0,3,5,252,0,139,2,15,22,143,20,46,143,
-    16,255,143,12,61,13,5,91,0,4,224,10,4,228,13,95,82,69,196,0,90,46,5,
-    137,14,4,164,0,125,68,140,8,157,69,136,2,64,85,225,3,143,4,8,87,68,
-    254,212,27,140,32,160,16,143,16,14,149,0,61,237,5,131,2,224,20,143,
-    6,13,79,24,21,1,55,80,85,159,0,46,171,8,0,4,141,2,251,15,54,143,16,
-    240,173,54,143,14,4,95,22,239,60,22,64,134,1,22,161,1,69,85,0,50,64,
-    143,28,47,163,11,241,69,224,0,193,0,193,1,133,2,64,85,68,252,160,0,
-    41,53,35,163,16,143,44,62,159,0,222,1,34,80,85,159,0,62,80,85,0,0,119,
-    5,0,227,1,193,1,3,5,162,0,13,234,255,15,63,143,16,29,31,30,15,17,84,
-    15,108,16,241,4,191,80,15,74,64,129,11,134,14,173,19,139,2,143,64,18,
-    31,129,10,64,85,69,192,0,161,1,21,169,0,192,23,159,24,47,159,0,253,
-    1,32,84,85,159,0,78,84,85,4,0,63,4,0,0,192,0,193,13,144,1,6,129,3,175,
-    255,15,79,143,16,39,81,127,54,81,15,10,79,14,15,49,175,132,13,15,102,
-    20,15,79,68,234,1,225,10,135,14,140,141,2,63,14,68,85,64,160,0,193,
-    63,65,107,85,69,64,41,85,44,5,0,240,169,8,165,13,143,12,6,15,14,159,
-    0,98,1,34,80,85,159,0,78,80,85,0,2,125,5,0,225,1,195,1,165,2,195,2,
-    143,16,132,69,125,111,54,81,111,10,31,30,15,33,230,13,15,72,21,81,141,
-    2,84,199,2,16,15,61,5,0,64,215,192,0,199,17,91,21,200,5,21,162,0,43,
-    128,127,46,159,0,190,1,40,84,85,159,0,62,81,234,1,65,85,126,1,0,1,133,
-    13,19,15,24,143,20,32,143,16,238,246,65,127,27,143,64,48,139,30,15,
-    6,4,164,0,93,174,68,130,4,80,134,6,69,143,2,35,135,25,133,14,187,68,
-    148,2,1,143,16,13,85,227,3,5,10,188,225,0,121,20,28,137,10,31,14,95,
-    134,1,41,80,85,159,0,92,0,4,4,0,250,159,15,93,143,16,255,239,8,42,233,
-    70,193,1,135,2,16,115,215,84,141,14,63,40,16,234,1,21,2,25,185,11,133,
-    12,16,164,0,79,6,157,0,85,85,141,179,22,31,8,16,0,20,0,137,10,84,192,
-    28,143,14,26,47,118,1,47,84,85,159,0,62,0,196,0,228,1,4,221,141,2,141,
-    3,143,16,104,21,111,62,176,42,108,64,123,143,32,74,64,196,0,171,12,
-    47,40,141,2,20,228,0,95,163,0,0,194,27,64,130,1,2,159,0,45,141,22,29,
-    103,0,0,20,0,167,22,225,1,20,128,255,13,46,79,255,1,42,84,85,159,0,
-    80,65,192,0,69,198,1,78,1,0,3,4,0,207,15,82,143,16,255,95,95,170,65,
-    95,15,81,143,14,65,65,212,11,21,131,2,61,84,65,80,35,229,15,81,161,
-    0,68,235,162,2,212,3,130,21,47,28,5,36,1,228,25,191,35,145,0,84,146,
-    2,135,14,111,14,131,18,59,112,141,4,20,174,2,63,10,31,18,1,34,84,85,
-    159,0,78,0,85,0,0,110,20,0,228,1,6,20,162,0,255,15,79,143,16,17,215,
-    21,196,0,111,69,81,143,32,92,84,64,10,119,145,0,64,15,72,133,10,143,
-    2,4,84,118,131,14,120,63,0,20,160,0,73,13,229,5,0,0,223,4,48,79,62,
-    20,134,12,52,88,226,0,96,136,16,20,95,65,15,110,1,32,84,85,159,0,94,
-    5,85,65,85,6,5,0,0,0,4,0,207,15,96,143,16,4,165,69,47,92,69,143,32,
-    109,1,64,47,108,0,22,47,106,69,85,1,42,81,191,13,72,244,27,167,1,192,
-    13,64,128,1,65,80,227,31,151,0,255,145,4,135,2,243,5,127,24,131,24,
-    225,11,24,247,1,112,144,0,20,160,0,31,10,97,1,35,84,85,159,0,94,0,85,
-    0,4,175,191,15,95,143,16,5,69,226,0,64,175,6,7,195,36,143,4,8,87,63,
-    29,69,143,32,109,1,15,92,68,160,0,193,39,199,133,40,229,15,15,40,84,
-    85,80,224,0,193,0,13,143,2,2,16,0,16,0,41,139,14,68,163,40,145,0,4,
-    236,17,81,85,4,196,2,24,161,7,4,0,20,194,1,193,23,0,0,175,21,0,16,88,
-    80,160,13,89,225,1,252,83,147,0,31,2,45,135,16,31,20,47,62,1,34,84,
-    85,159,0,94,0,85,0,2,111,20,0,191,15,95,143,16,11,21,175,6,7,195,36,
-    143,4,8,93,63,29,69,143,32,109,1,63,78,128,38,248,74,84,62,160,0,64,
-    80,128,1,196,2,227,15,31,56,133,10,211,0,20,139,2,84,247,1,0,84,162,
-    0,92,151,0,20,194,16,20,80,201,59,59,65,223,85,225,4,227,5,21,65,138,
-    10,233,1,159,0,36,254,167,8,129,13,197,13,133,2,143,4,10,161,2,233,
-    25,15,36,1,42,80,85,159,0,78,0,192,0,64,232,1,253,0,2,255,14,79,143,
-    16,71,175,38,30,143,32,140,35,21,175,243,0,130,3,16,228,0,20,162,0,
-    143,30,0,143,2,2,219,135,12,141,14,143,16,6,16,68,157,0,16,132,4,255,
-    161,4,17,133,2,161,21,162,6,135,24,194,24,117,216,133,10,77,142,14,
-    84,143,16,12,159,0,222,1,54,84,85,159,0,255,15,221,21,196,0,15,42,197,
-    65,204,1,135,2,64,84,64,225,0,81,100,85,0,4,85,84,84,225,1,21,34,85,
-    20,0,163,0,85,85,20,191,82,13,224,64,138,4,128,8,16,65,85,80,84,149,
-    80,193,0,84,64,18,64,129,15,80,46,0,84,64,132,14,64,164,0,149,0,29,
-    190,4,0,16,80,135,20,127,0,161,2,57,240,80,56,161,9,137,6,159,0,150,
-    1,46,84,85,159,0,78,1,176,12,193,0,3,243,0,4,255,14,79,143,16,40,191,
-    34,61,1,80,143,32,92,254,177,11,249,11,139,14,111,1,193,19,201,30,13,
-    140,2,151,80,2,80,84,163,0,0,32,129,14,215,129,1,34,159,0,29,81,164,
-    6,80,36,133,2,254,160,12,226,13,39,195,4,37,5,159,0,255,31,29,1,40,
-    80,85,159,0,94,0,160,0,85,85,253,16,143,15,95,143,16,56,193,38,143,
-    39,40,143,32,176,63,58,0,171,12,165,2,16,194,0,16,128,2,16,204,21,210,
-    63,4,135,50,51,64,140,14,0,81,196,55,139,84,50,81,0,65,143,16,4,16,
-    60,219,39,164,2,40,80,44,162,0,84,166,9,128,12,159,0,132,1,32,84,85,
-    159,0,94,1,84,0,0,30,1,0,80,0,159,15,95,143,16,24,209,34,159,35,71,
-    91,1,68,143,32,92,81,160,0,42,0,47,76,214,240,11,146,0,143,2,4,81,54,
-    80,98,160,2,124,84,81,138,46,52,135,24,12,229,7,0,109,0,16,80,159,0,
-    46,81,224,65,231,17,80,248,15,11,225,0,7,193,0,143,16,48,95,110,1,39,
-    80,85,159,0,255,85,21,226,0,175,35,14,149,175,39,255,15,163,16,0,7,
-    16,160,0,69,149,160,2,195,2,16,0,163,60,16,96,64,233,169,6,143,2,5,
-    133,66,79,2,0,128,1,0,81,223,67,210,11,113,65,143,16,2,7,227,1,11,188,
-    225,0,9,80,142,6,153,0,15,14,33,1,37,84,85,159,0,255,85,21,226,0,1,
-    119,143,4,7,81,47,13,15,255,10,17,160,0,193,39,113,135,40,21,162,46,
-    227,46,79,34,81,85,65,205,224,0,193,0,143,2,2,64,0,160,0,104,0,171,
-    54,225,1,4,162,1,1,66,20,114,130,7,225,6,69,85,16,0,16,228,5,186,0,
-    48,80,182,7,208,0,240,0,64,104,127,64,1,193,13,88,162,0,151,0,95,2,
-    29,192,99,47,24,31,46,1,59,84,85,159,0,255,5,191,35,7,81,143,4,13,225,
-    15,254,128,38,180,38,227,1,81,85,1,65,217,128,1,200,2,79,58,0,38,143,
-    2,2,80,1,8,71,80,1,80,1,128,17,84,81,248,84,194,57,132,58,70,133,22,
-    26,5,85,39,80,0,80,227,7,0,85,160,10,195,1,255,134,10,232,1,159,0,20,
-    167,6,129,11,197,11,143,2,6,143,6,10,192,162,2,232,27,31,20,1,43,84,
-    85,159,0,94,1,176,14,1,239,14,95,64,143,16,72,65,63,45,21,0,0,0,84,
-    232,0,143,32,102,208,15,10,1,160,0,0,0,43,85,0,21,162,1,1,128,19,65,
-    129,2,127,224,0,81,2,143,30,0,63,2,137,28,109,143,16,12,35,157,0,65,
-    84,165,1,64,0,80,80,191,117,161,7,64,80,103,211,10,21,193,3,176,137,
-    4,21,1,28,159,0,238,1,62,84,85,159,0,255,15,222,177,64,253,64,255,66,
-    45,197,5,204,1,135,2,0,81,0,225,0,69,0,101,81,0,1,0,0,0,85,16,0,17,
-    0,16,192,0,17,0,81,185,81,2,84,240,4,176,5,209,5,81,80,34,207,13,12,
-    1,81,233,1,81,81,0,128,6,15,5,85,64,81,64,17,177,8,128,2,118,210,9,
-    65,146,10,241,11,145,0,1,164,0,127,5,191,16,192,0,64,0,21,192,8,142,
-    2,161,2,120,138,4,1,103,161,11,137,6,79,134,1,39,84,85,159,0,94,5,80,
-    195,0,15,102,87,143,16,31,81,63,61,64,143,32,92,0,176,60,17,253,134,
-    2,34,139,34,202,28,193,1,13,47,9,64,34,2,64,81,163,0,0,80,0,128,10,
-    114,0,84,129,1,2,159,0,13,69,81,35,127,64,1,3,127,6,160,12,226,13,119,
-    195,4,224,37,165,6,143,14,14,159,0,254,1,54,84,85,159,0,255,15,31,65,
-    196,0,15,164,245,0,4,239,15,108,15,6,165,4,64,34,64,30,128,2,65,84,
-    65,218,86,31,4,135,50,19,138,1,22,1,84,1,17,68,143,16,20,121,65,84,
-    11,55,163,2,41,64,1,188,44,129,4,80,165,9,52,38,159,0,116,1,54,84,85,
-    159,0,255,15,0,81,196,0,15,179,216,69,160,0,15,90,0,4,143,2,6,69,81,
-    159,69,192,43,21,80,123,135,46,20,135,24,156,28,227,9,64,1,192,0,160,
-    0,159,0,30,65,151,81,165,1,64,1,1,69,84,227,1,248,79,12,225,0,71,194,
-    2,143,16,31,159,0,110,1,59,84,85,159,0,255,15,0,210,36,65,223,4,13,
-    221,143,4,14,15,255,4,0,6,239,15,108,19,64,14,202,1,64,0,21,85,225,
-    0,228,1,131,61,31,84,65,84,1,137,63,143,2,5,133,66,127,3,63,160,74,
-    1,68,143,16,18,23,227,1,27,137,6,112,226,3,1,125,203,11,79,0,1,58,84,
-    85,159,0,255,15,0,210,36,5,191,37,7,255,69,226,0,143,4,8,15,255,143,
-    44,0,193,39,135,40,141,2,25,111,29,69,85,5,129,50,47,5,0,1,149,160,
-    0,38,16,0,49,5,226,0,80,176,96,93,80,56,225,6,21,85,64,83,0,64,212,
-    1,0,160,1,64,1,98,191,192,21,160,2,1,226,11,70,240,6,193,13,71,254,
-    225,1,67,147,0,11,47,3,99,31,24,47,30,1,53,84,85,159,0,255,15,6,81,
-    159,3,6,69,246,143,4,13,15,254,128,38,180,38,15,82,0,6,143,2,4,31,69,
-    81,69,81,194,55,132,56,31,7,229,23,2,26,21,84,64,1,64,1,225,9,6,64,
-    1,85,1,85,85,195,1,147,0,127,64,5,233,1,31,16,161,9,167,6,35,143,2,
-    12,208,225,0,143,4,6,162,2,5,207,14,14,1,41,84,85,159,0,221,21,175,
-    30,109,21,84,141,175,16,108,129,16,21,0,0,207,16,103,63,118,21,96,191,
-    10,56,69,196,0,143,2,6,5,0,0,0,93,4,0,197,1,4,160,0,7,143,8,12,64,182,
-    143,12,19,143,14,9,5,129,45,8,21,225,15,137,16,1,35,80,85,159,0,78,
-    80,85,0,2,244,227,1,195,1,135,2,15,80,143,16,239,84,208,13,64,181,5,
-    111,103,16,138,14,15,50,64,192,0,69,246,79,7,141,12,75,15,2,131,8,64,
-    50,73,59,64,85,68,44,165,0,35,0,17,0,159,0,173,1,34,80,85,159,0,94,
-    80,85,0,2,110,5,0,191,15,95,143,16,244,81,63,109,243,15,63,75,171,21,
-    141,2,84,199,2,16,63,1,64,140,6,29,115,64,85,68,132,1,49,197,4,69,26,
-    60,5,0,64,164,58,53,0,24,220,21,228,13,145,0,21,162,0,27,79,110,1,54,
-    84,85,159,0,255,15,119,81,159,32,238,15,209,55,84,85,80,162,0,169,62,
-    84,64,203,4,192,143,2,58,225,1,7,1,54,80,85,159,0,255,15,119,69,159,
-    32,238,15,211,253,64,162,0,169,62,135,2,131,1,101,195,117,69,240,15,
-    23,227,0,13,135,14,49,1,46,84,85,159,0,96,81,207,14,97,143,16,255,47,
-    231,139,65,160,0,85,85,69,143,14,87,1,83,82,148,109,21,133,2,80,181,
-    16,1,0,145,0,212,80,135,6,15,27,69,12,5,4,0,220,0,147,14,8,84,145,47,
-    229,1,149,0,1,58,80,85,159,0,255,15,255,15,189,21,255,9,60,158,16,160,
-    0,85,85,135,105,131,2,193,0,100,31,0,16,0,0,112,229,1,149,0,143,17,
-    38,216,177,7,224,0,36,69,1,50,16,0,248,21,208,2,246,11,0,163,15,15,
-    12,1,35,84,85,159,0,94,0,85,0,2,175,159,15,95,143,16,135,21,63,92,81,
-    143,32,94,203,76,227,15,222,63,40,128,40,224,0,64,140,2,247,9,141,12,
-    63,14,63,85,85,20,204,48,63,12,133,8,160,8,192,8,238,231,1,159,0,36,
-    143,20,12,141,4,20,170,14,218,17,95,37,1,61,84,85,159,0,255,15,255,
-    15,255,15,127,81,126,200,1,21,160,0,129,2,197,2,99,192,0,180,125,0,
-    1,0,0,0,64,85,5,0,64,64,80,225,1,1,55,84,85,159,0,255,15,97,21,196,
-    0,15,255,24,15,51,84,85,84,248,92,224,1,85,84,206,80,128,1,191,95,37,
-    20,0,161,0,113,131,2,211,0,70,63,18,84,247,1,0,84,16,247,128,9,18,135,
-    64,63,15,138,16,20,136,10,129,3,0,159,0,30,1,55,84,85,159,0,255,15,
-    97,69,159,32,238,15,255,0,15,86,1,53,84,85,159,0,255,15,97,69,226,0,
-    64,245,143,4,7,227,52,63,8,15,255,15,139,84,160,0,80,219,239,9,59,131,
-    10,195,10,80,4,133,2,16,2,112,88,0,1,82,240,78,1,55,84,85,159,0,255,
-    15,103,21,175,3,7,227,52,247,143,4,8,15,255,15,93,237,104,137,2,84,
-    162,0,15,8,106,95,46,20,18,231,11,0,18,64,192,1,208,64,162,13,39,0,
-    31,1,1,58,80,85,159,0,255,15,143,207,54,188,0,2,245,21,192,0,143,16,
-    106,1,133,43,16,64,64,164,168,4,143,2,12,16,47,29,0,84,145,29,0,191,
-    81,95,0,16,160,0,39,225,1,45,193,36,86,25,80,142,6,16,29,84,31,6,147,
-    0,1,62,84,85,159,0,255,15,255,15,204,175,96,47,156,8,46,64,84,0,160,
-    2,64,140,2,193,0,3,156,0,4,85,21,102,141,4,47,41,0,134,84,133,12,64,
-    0,85,0,231,13,15,6,1,58,84,85,159,0,255,15,112,223,50,205,81,239,9,
-    59,45,0,84,0,128,38,0,141,2,198,40,0,143,4,95,6,81,84,81,193,5,200,
-    46,15,28,187,133,10,160,10,1,233,1,127,20,167,6,80,162,0,208,63,12,
-    143,6,10,162,2,1,143,16,30,1,62,80,85,159,0,255,15,145,225,54,175,55,
-    255,15,37,95,16,0,161,0,64,128,1,175,42,100,135,16,132,2,198,82,135,
-    60,31,31,84,16,85,177,9,24,161,84,17,64,26,21,84,0,81,0,225,1,1,62,
-    84,85,159,0,255,15,114,241,50,191,51,255,15,54,19,81,85,81,81,239,9,
-    56,80,0,161,0,179,129,1,131,2,0,22,15,20,81,84,97,220,135,62,15,30,
-    139,16,80,247,11,130,3,31,14,1,55,80,85,159,0,255,15,113,21,226,0,207,
-    51,14,160,207,55,255,15,169,64,143,14,101,1,53,84,85,159,0,255,15,113,
-    21,226,0,1,117,143,4,7,81,63,13,15,255,15,217,80,162,0,65,141,160,1,
-    193,0,85,85,64,116,134,2,84,128,34,52,1,59,84,85,159,0,255,15,120,223,
-    51,7,81,143,4,13,236,15,255,15,93,132,52,143,2,3,81,162,0,31,72,80,
-    204,84,1,231,13,0,84,2,192,1,0,96,85,80,0,120,84,80,85,1,52,84,85,159,
-    0,255,15,159,65,239,23,171,1,28,0,1,0,0,192,0,165,81,131,2,85,246,0,
-    239,15,84,176,13,10,169,2,64,133,44,176,19,207,1,137,46,95,11,65,84,
-    95,26,143,16,11,160,0,252,87,225,1,29,137,6,130,4,240,10,75,65,160,
-    84,28,80,11,1,56,84,85,159,0,255,15,255,15,204,81,0,7,1,0,0,0,85,0,
-    134,2,159,10,47,15,251,1,0,81,0,80,224,0,61,192,0,52,17,1,80,208,15,
-    18,16,180,2,0,124,0,1,144,1,1,132,6,143,2,43,135,12,0,48,1,85,1,225,
-    3,59,1,54,84,85,159,0,255,15,128,81,159,32,238,15,26,31,0,80,0,80,224,
-    0,150,91,132,2,192,0,199,100,20,5,69,81,69,129,44,198,44,246,13,161,
-    2,15,26,133,12,160,12,5,233,1,159,0,4,79,69,81,165,1,64,1,161,0,167,
-    6,127,18,208,225,24,143,4,6,162,2,5,143,16,14,1,55,84,85,159,0,255,
-    15,161,65,196,0,15,255,101,15,17,0,6,195,97,64,0,161,0,0,249,128,1,
-    239,15,82,160,13,54,129,15,143,16,0,65,84,194,113,135,60,143,2,31,80,
-    65,84,1,8,20,64,80,64,80,1,1,226,1,1,55,84,85,159,0,255,15,130,81,196,
-    0,15,255,59,15,128,64,1,161,0,129,1,131,2,0,6,2,127,4,65,81,69,81,5,
-    80,135,60,185,69,16,69,136,62,31,30,139,16,64,1,224,135,14,129,3,231,
-    1,147,0,1,59,84,85,159,0,255,15,130,242,52,65,223,4,13,230,143,4,14,
-    15,255,140,44,15,100,65,0,161,0,239,15,102,1,58,84,85,159,0,255,15,
-    130,242,52,5,223,53,7,247,69,226,0,143,4,8,15,255,15,122,0,162,0,193,
-    55,90,143,56,67,64,2,5,129,68,248,13,0,132,2,1,53,84,85,159,0,255,15,
-    136,81,159,3,6,69,249,143,4,13,15,255,15,93,132,52,143,2,7,15,74,64,
-    81,152,161,0,167,15,0,80,18,144,127,0,84,0,64,85
-};
-
-static MTB * mtb_KRKP = NULL;
-
-void initMTB_KRKP()
-{
-    mtb_KRKP = new MTB ("KRKP", 2, 134);
-    mtb_KRKP->SetPackedData (mtbdata_KRKP);
-    mtb_KRKP->Add (A2, A1, WHITE, 69);
-    mtb_KRKP->Add (A2, B1, WHITE, 39);
-    mtb_KRKP->Add (A2, C1, WHITE, 85);
-    mtb_KRKP->Add (A2, B2, WHITE, 69);
-    mtb_KRKP->Add (A2, C2, WHITE, 87);
-    mtb_KRKP->Add (A2, A3, WHITE, 69);
-    mtb_KRKP->Add (A2, B3, WHITE, 53);
-    mtb_KRKP->Add (A2, C3, WHITE, 95);
-    mtb_KRKP->Add (B2, A1, WHITE, 75);
-    mtb_KRKP->Add (B2, B1, WHITE, 92);
-    mtb_KRKP->Add (B2, C1, WHITE, 72);
-    mtb_KRKP->Add (B2, D1, WHITE, 84);
-    mtb_KRKP->Add (B2, A2, WHITE, 70);
-    mtb_KRKP->Add (B2, C2, WHITE, 80);
-    mtb_KRKP->Add (B2, D2, WHITE, 88);
-    mtb_KRKP->Add (B2, A3, WHITE, 89);
-    mtb_KRKP->Add (B2, B3, WHITE, 60);
-    mtb_KRKP->Add (B2, C3, WHITE, 89);
-    mtb_KRKP->Add (B2, D3, WHITE, 92);
-    mtb_KRKP->Add (C2, A1, WHITE, 87);
-    mtb_KRKP->Add (C2, B1, WHITE, 66);
-    mtb_KRKP->Add (C2, C1, WHITE, 84);
-    mtb_KRKP->Add (C2, D1, WHITE, 80);
-    mtb_KRKP->Add (C2, E1, WHITE, 82);
-    mtb_KRKP->Add (C2, A2, WHITE, 84);
-    mtb_KRKP->Add (C2, B2, WHITE, 88);
-    mtb_KRKP->Add (C2, D2, WHITE, 100);
-    mtb_KRKP->Add (C2, E2, WHITE, 86);
-    mtb_KRKP->Add (C2, A3, WHITE, 102);
-    mtb_KRKP->Add (C2, B3, WHITE, 94);
-    mtb_KRKP->Add (C2, C3, WHITE, 75);
-    mtb_KRKP->Add (C2, D3, WHITE, 97);
-    mtb_KRKP->Add (C2, E3, WHITE, 97);
-    mtb_KRKP->Add (D2, B1, WHITE, 107);
-    mtb_KRKP->Add (D2, C1, WHITE, 78);
-    mtb_KRKP->Add (D2, D1, WHITE, 87);
-    mtb_KRKP->Add (D2, E1, WHITE, 77);
-    mtb_KRKP->Add (D2, F1, WHITE, 90);
-    mtb_KRKP->Add (D2, B2, WHITE, 106);
-    mtb_KRKP->Add (D2, C2, WHITE, 104);
-    mtb_KRKP->Add (D2, E2, WHITE, 106);
-    mtb_KRKP->Add (D2, F2, WHITE, 98);
-    mtb_KRKP->Add (D2, B3, WHITE, 97);
-    mtb_KRKP->Add (D2, C3, WHITE, 105);
-    mtb_KRKP->Add (D2, D3, WHITE, 100);
-    mtb_KRKP->Add (D2, E3, WHITE, 99);
-    mtb_KRKP->Add (D2, F3, WHITE, 86);
-    mtb_KRKP->Add (A3, A2, WHITE, 70);
-    mtb_KRKP->Add (A3, B2, WHITE, 59);
-    mtb_KRKP->Add (A3, B3, WHITE, 56);
-    mtb_KRKP->Add (A3, A4, WHITE, 67);
-    mtb_KRKP->Add (A3, B4, WHITE, 54);
-    mtb_KRKP->Add (B3, A2, WHITE, 87);
-    mtb_KRKP->Add (B3, B2, WHITE, 93);
-    mtb_KRKP->Add (B3, C2, WHITE, 71);
-    mtb_KRKP->Add (B3, A3, WHITE, 83);
-    mtb_KRKP->Add (B3, C3, WHITE, 68);
-    mtb_KRKP->Add (B3, A4, WHITE, 81);
-    mtb_KRKP->Add (B3, B4, WHITE, 70);
-    mtb_KRKP->Add (B3, C4, WHITE, 78);
-    mtb_KRKP->Add (C3, B2, WHITE, 67);
-    mtb_KRKP->Add (C3, C2, WHITE, 95);
-    mtb_KRKP->Add (C3, D2, WHITE, 82);
-    mtb_KRKP->Add (C3, B3, WHITE, 74);
-    mtb_KRKP->Add (C3, D3, WHITE, 69);
-    mtb_KRKP->Add (C3, B4, WHITE, 83);
-    mtb_KRKP->Add (C3, C4, WHITE, 78);
-    mtb_KRKP->Add (C3, D4, WHITE, 84);
-    mtb_KRKP->Add (D3, C2, WHITE, 81);
-    mtb_KRKP->Add (D3, D2, WHITE, 106);
-    mtb_KRKP->Add (D3, E2, WHITE, 97);
-    mtb_KRKP->Add (D3, C3, WHITE, 84);
-    mtb_KRKP->Add (D3, E3, WHITE, 90);
-    mtb_KRKP->Add (D3, C4, WHITE, 84);
-    mtb_KRKP->Add (D3, D4, WHITE, 93);
-    mtb_KRKP->Add (D3, E4, WHITE, 90);
-    mtb_KRKP->Add (A4, A3, WHITE, 91);
-    mtb_KRKP->Add (A4, B3, WHITE, 72);
-    mtb_KRKP->Add (A4, B4, WHITE, 77);
-    mtb_KRKP->Add (A4, A5, WHITE, 91);
-    mtb_KRKP->Add (A4, B5, WHITE, 72);
-    mtb_KRKP->Add (B4, A3, WHITE, 82);
-    mtb_KRKP->Add (B4, B3, WHITE, 73);
-    mtb_KRKP->Add (B4, C3, WHITE, 76);
-    mtb_KRKP->Add (B4, A4, WHITE, 90);
-    mtb_KRKP->Add (B4, C4, WHITE, 86);
-    mtb_KRKP->Add (B4, A5, WHITE, 88);
-    mtb_KRKP->Add (B4, B5, WHITE, 119);
-    mtb_KRKP->Add (B4, C5, WHITE, 116);
-    mtb_KRKP->Add (C4, B3, WHITE, 90);
-    mtb_KRKP->Add (C4, C3, WHITE, 104);
-    mtb_KRKP->Add (C4, D3, WHITE, 86);
-    mtb_KRKP->Add (C4, B4, WHITE, 85);
-    mtb_KRKP->Add (C4, D4, WHITE, 92);
-    mtb_KRKP->Add (C4, B5, WHITE, 79);
-    mtb_KRKP->Add (C4, C5, WHITE, 107);
-    mtb_KRKP->Add (C4, D5, WHITE, 103);
-    mtb_KRKP->Add (D4, C3, WHITE, 99);
-    mtb_KRKP->Add (D4, D3, WHITE, 122);
-    mtb_KRKP->Add (D4, E3, WHITE, 88);
-    mtb_KRKP->Add (D4, C4, WHITE, 73);
-    mtb_KRKP->Add (D4, E4, WHITE, 78);
-    mtb_KRKP->Add (D4, C5, WHITE, 79);
-    mtb_KRKP->Add (D4, D5, WHITE, 105);
-    mtb_KRKP->Add (D4, E5, WHITE, 92);
-    mtb_KRKP->Add (A5, A4, WHITE, 71);
-    mtb_KRKP->Add (A5, B4, WHITE, 67);
-    mtb_KRKP->Add (A5, B5, WHITE, 73);
-    mtb_KRKP->Add (A5, A6, WHITE, 34);
-    mtb_KRKP->Add (A5, B6, WHITE, 38);
-    mtb_KRKP->Add (B5, A4, WHITE, 64);
-    mtb_KRKP->Add (B5, B4, WHITE, 63);
-    mtb_KRKP->Add (B5, C4, WHITE, 77);
-    mtb_KRKP->Add (B5, A5, WHITE, 43);
-    mtb_KRKP->Add (B5, C5, WHITE, 70);
-    mtb_KRKP->Add (B5, A6, WHITE, 18);
-    mtb_KRKP->Add (B5, B6, WHITE, 50);
-    mtb_KRKP->Add (B5, C6, WHITE, 52);
-    mtb_KRKP->Add (C5, B4, WHITE, 66);
-    mtb_KRKP->Add (C5, C4, WHITE, 51);
-    mtb_KRKP->Add (C5, D4, WHITE, 68);
-    mtb_KRKP->Add (C5, B5, WHITE, 55);
-    mtb_KRKP->Add (C5, D5, WHITE, 54);
-    mtb_KRKP->Add (C5, B6, WHITE, 25);
-    mtb_KRKP->Add (C5, C6, WHITE, 43);
-    mtb_KRKP->Add (C5, D6, WHITE, 51);
-    mtb_KRKP->Add (D5, C4, WHITE, 74);
-    mtb_KRKP->Add (D5, D4, WHITE, 69);
-    mtb_KRKP->Add (D5, E4, WHITE, 80);
-    mtb_KRKP->Add (D5, C5, WHITE, 63);
-    mtb_KRKP->Add (D5, E5, WHITE, 59);
-    mtb_KRKP->Add (D5, C6, WHITE, 32);
-    mtb_KRKP->Add (D5, D6, WHITE, 45);
-    mtb_KRKP->Add (D5, E6, WHITE, 46);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KPKP
-
-static const byte mtbdata_KPKP[12152] = {
-    1,74,0,157,0,80,21,140,2,85,28,196,1,190,6,9,10,0,8,26,42,16,0,40,0,
-    32,24,170,0,168,98,0,160,192,0,15,6,64,85,21,160,0,77,0,64,21,85,84,
-    16,143,16,89,85,80,66,21,70,5,79,77,85,85,9,7,85,5,85,5,64,5,133,30,
-    128,2,136,65,165,16,5,84,1,47,75,42,84,36,41,84,37,137,8,84,9,31,12,
-    80,203,9,143,14,11,47,31,80,169,16,170,28,87,137,4,41,80,160,28,128,
-    143,12,7,31,14,1,13,170,2,168,2,160,2,170,214,160,0,56,192,2,168,92,
-    160,73,192,0,184,128,76,0,77,143,14,6,143,16,0,10,160,110,10,170,162,
-    0,11,168,5,129,20,0,191,160,12,128,9,129,24,13,146,49,142,28,0,12,1,
-    1,0,64,5,64,21,64,0,40,166,0,21,80,1,0,6,170,84,8,170,84,21,84,0,6,
-    170,86,68,170,86,138,2,90,170,90,74,106,49,170,106,149,137,8,145,0,
-    66,160,66,148,133,12,66,10,168,32,0,114,80,142,85,49,64,85,68,192,0,
-    63,8,135,4,95,170,86,225,0,0,36,128,2,128,16,32,84,59,106,60,170,60,
-    170,55,85,87,85,101,224,8,69,15,9,170,130,14,133,4,173,208,13,129,2,
-    84,5,106,160,16,96,80,85,38,85,34,64,38,86,34,170,123,38,90,34,0,37,
-    161,10,69,160,0,247,29,133,2,129,20,193,13,21,165,3,162,16,225,2,160,
-    34,114,5,90,149,106,128,151,38,0,106,149,1,0,12,161,2,110,144,0,106,
-    160,8,1,5,160,0,131,10,129,2,61,85,85,21,112,23,136,46,32,149,134,69,
-    130,50,170,86,149,86,228,29,132,66,55,149,90,128,166,16,115,160,38,
-    132,48,191,134,16,133,10,5,162,0,130,24,129,2,97,130,79,118,35,32,225,
-    15,133,28,32,160,166,62,51,223,160,166,16,35,160,54,36,54,39,141,51,
-    133,8,10,80,5,162,0,133,2,8,92,130,97,21,70,40,225,13,131,42,64,168,
-    119,86,168,29,164,16,131,16,168,160,0,51,124,67,168,80,51,73,17,131,
-    6,170,23,74,170,74,42,160,113,5,129,52,129,2,93,0,42,128,115,21,122,
-    132,56,36,170,189,136,14,34,82,44,162,16,131,16,225,0,170,192,2,153,
-    0,1,1,0,160,90,149,90,0,90,0,160,80,165,0,149,140,2,170,106,170,106,
-    165,0,1,64,3,170,90,0,88,187,136,4,17,82,139,6,90,145,0,98,229,7,132,
-    128,8,17,138,138,10,0,20,160,5,86,85,86,85,84,0,164,0,85,28,20,1,85,
-    17,192,0,31,8,135,4,170,58,90,86,85,128,13,134,8,130,22,64,140,16,169,
-    170,108,170,103,165,209,8,84,5,82,8,149,192,0,21,15,9,169,86,0,191,
-    84,133,4,105,48,162,13,37,160,16,2,94,21,86,18,0,136,40,17,161,2,20,
-    239,106,18,163,16,165,10,21,160,0,29,133,2,51,129,20,1,84,80,36,149,
-    86,196,13,111,37,90,52,37,106,32,166,16,45,62,128,6,85,170,18,1,224,
-    27,160,0,18,122,84,21,136,2,192,28,132,20,3,1,134,38,15,169,86,169,
-    86,0,166,16,129,50,112,185,88,130,66,106,112,4,132,48,170,128,199,4,
-    128,20,160,0,10,80,5,114,130,22,27,0,8,80,21,2,133,2,32,225,13,185,
-    133,26,0,128,77,165,16,131,82,90,128,188,22,131,16,128,22,71,19,130,
-    6,74,12,170,74,42,64,5,162,0,131,8,138,27,90,40,64,21,18,133,2,32,137,
-    96,99,64,160,225,13,70,170,90,160,193,96,198,9,164,16,131,16,170,106,
-    160,52,67,55,170,170,160,52,132,4,42,160,0,131,34,118,134,2,168,57,
-    16,63,15,66,137,14,48,232,74,12,162,16,151,0,168,34,1,0,160,106,165,
-    106,2,104,0,129,64,165,0,149,106,85,106,168,106,20,135,2,170,106,170,
-    138,4,170,146,0,10,100,168,0,52,33,170,98,139,6,170,232,74,108,41,49,
-    138,229,7,0,0,4,160,90,165,74,85,65,136,16,90,5,85,89,85,81,1,80,163,
-    0,85,3,20,5,84,69,84,5,84,229,1,129,31,0,7,169,106,89,85,1,64,14,135,
-    8,169,170,168,170,240,12,140,24,135,16,169,165,66,5,71,86,208,8,81,
-    21,202,73,161,2,15,9,165,90,0,80,133,4,246,165,192,0,0,164,16,0,86,
-    2,118,88,170,90,13,170,2,37,149,86,227,169,2,35,165,10,85,84,85,30,
-    155,134,2,128,20,5,80,160,0,21,90,18,188,114,18,106,180,13,9,135,56,
-    0,170,203,66,1,115,169,86,160,0,82,1,145,97,128,10,86,165,130,32,85,
-    80,85,108,137,2,85,133,28,67,5,134,38,128,42,90,118,1,88,21,129,50,
-    128,16,104,23,132,48,204,168,21,50,74,42,50,17,169,140,90,161,0,41,
-    64,21,34,133,2,33,108,64,85,132,36,67,129,192,13,73,0,95,82,0,11,90,
-    133,46,129,16,160,16,198,32,223,52,70,130,86,42,160,0,137,90,128,2,
-    149,36,10,50,106,42,106,160,60,128,61,199,66,224,13,54,170,106,128,
-    193,96,25,142,164,16,131,16,170,170,128,68,21,144,0,138,2,137,4,170,
-    170,168,60,160,47,30,176,10,44,42,44,98,1,9,0,64,5,0,154,0,64,21,194,
-    140,2,251,1,31,15,0,10,0,8,232,1,138,42,160,0,40,0,32,22,170,80,56,
-    168,0,160,192,0,133,8,140,16,80,21,130,80,169,16,85,80,0,80,85,105,
-    26,81,0,80,1,47,58,156,0,85,96,186,21,160,0,0,128,15,1,240,1,84,0,222,
-    64,128,13,2,21,192,2,143,16,82,81,3,192,5,15,75,129,14,9,85,5,85,5,
-    125,80,5,133,30,128,2,1,163,16,33,1,64,24,1,31,63,42,84,41,84,37,64,
-    64,5,162,0,0,64,85,85,10,92,84,9,96,21,66,87,23,10,112,80,9,10,139,
-    14,15,30,42,80,42,90,80,169,80,170,76,137,4,41,128,8,174,160,76,128,
-    5,64,139,12,141,14,143,16,14,0,170,64,170,2,168,2,160,2,245,170,160,
-    0,4,129,4,192,2,168,28,160,174,25,192,0,128,28,0,19,141,12,31,13,1,
-    0,160,106,165,106,170,106,149,64,106,160,160,0,0,64,0,64,149,16,90,
-    149,90,170,192,0,128,90,0,145,80,1,170,86,0,82,21,82,255,227,1,134,
-    2,68,80,224,5,90,224,9,128,8,130,23,145,0,106,149,66,160,66,225,11,
-    66,0,64,18,170,170,10,168,160,0,221,0,80,129,16,165,44,1,38,85,12,161,
-    14,21,86,0,84,3,34,86,236,149,228,1,128,2,1,90,136,4,1,106,69,149,88,
-    10,170,170,98,12,138,71,5,85,148,0,64,85,68,192,0,31,8,41,71,170,86,
-    225,0,0,164,0,106,85,215,1,128,2,117,85,18,106,136,38,146,0,106,22,
-    90,3,25,101,192,0,69,31,9,247,170,130,14,133,4,208,13,97,84,21,161,
-    16,118,80,80,22,112,16,64,136,54,17,221,170,22,34,168,32,21,161,10,
-    69,251,160,0,29,133,2,129,20,193,13,21,165,3,240,162,16,2,160,34,226,
-    13,5,90,149,106,203,128,36,2,106,149,1,0,12,238,161,2,0,161,8,1,5,160,
-    0,241,17,131,2,60,85,85,21,112,23,133,46,225,33,170,119,85,149,69,226,
-    15,128,66,86,68,129,16,255,131,88,164,16,113,130,92,68,134,48,36,130,
-    22,114,130,10,5,162,0,134,2,17,84,21,86,236,32,225,15,133,28,80,160,
-    164,16,132,82,86,199,160,52,67,149,90,160,52,68,248,129,92,50,132,80,
-    130,16,33,135,8,10,80,198,5,160,0,135,2,8,80,21,112,7,142,40,138,14,
-    168,86,168,13,82,133,16,238,168,16,81,101,168,112,1,27,0,168,170,1,
-    0,160,106,165,106,170,106,0,8,106,128,106,0,64,161,0,149,106,189,149,
-    138,2,170,224,1,17,0,19,90,25,0,74,170,72,35,22,64,10,226,5,129,6,9,
-    145,0,106,170,66,10,4,170,170,10,138,10,0,66,160,0,90,165,90,149,90,
-    0,90,0,186,80,163,0,149,224,1,25,142,16,86,162,14,199,88,134,4,1,90,
-    170,82,139,6,72,104,147,0,98,134,22,3,138,133,16,85,85,2,165,86,85,
-    86,85,84,0,164,0,142,85,4,1,85,17,192,0,15,8,135,4,25,170,90,86,85,
-    160,13,132,8,170,85,95,130,22,64,134,16,86,147,14,136,38,147,16,71,
-    42,209,8,84,5,8,149,192,0,21,15,9,87,169,86,0,84,133,4,105,48,194,13,
-    255,37,160,16,2,21,34,176,13,135,54,17,187,161,2,4,106,2,83,165,10,
-    21,160,0,204,13,133,2,129,20,1,84,96,52,149,219,86,228,29,53,90,68,
-    53,106,48,254,164,16,15,0,162,6,2,17,224,27,160,0,2,122,84,21,136,2,
-    192,28,132,20,19,1,134,38,15,169,86,169,86,0,166,16,225,15,0,243,102,
-    114,161,50,20,134,48,170,128,18,140,130,20,160,0,10,80,5,2,133,2,8,
-    110,80,21,2,37,32,225,13,133,26,0,231,128,45,164,16,132,82,90,128,20,
-    36,248,129,106,18,132,80,130,16,17,132,6,74,170,24,74,42,64,5,160,0,
-    133,8,138,90,54,40,64,21,48,135,2,32,135,56,98,143,160,138,14,170,90,
-    160,194,96,8,162,16,26,133,16,170,106,160,66,151,0,160,66,1,32,160,
-    170,145,0,0,168,0,170,178,0,2,128,140,2,242,1,170,2,44,20,170,170,10,
-    168,36,106,160,0,42,55,170,34,42,51,37,2,6,145,0,196,13,240,7,5,160,
-    106,165,96,2,32,104,0,64,99,149,106,149,106,23,85,106,168,106,5,85,
-    129,10,136,4,89,142,16,90,162,14,98,38,130,6,170,74,209,108,39,147,
-    0,138,244,15,0,165,90,16,160,0,74,85,65,134,14,85,149,90,5,85,89,85,
-    81,1,80,51,85,3,20,5,84,69,84,5,84,229,1,129,31,0,135,4,169,106,89,
-    85,1,64,6,133,8,169,86,169,170,168,193,39,133,16,212,90,147,16,77,5,
-    71,86,208,8,81,229,21,73,161,2,31,9,165,90,16,80,123,133,4,165,192,
-    0,16,164,16,16,86,18,238,4,16,2,25,170,18,3,17,227,169,18,3,165,10,
-    85,84,85,30,155,134,2,128,20,5,80,160,0,21,90,18,190,50,18,106,212,
-    13,25,132,56,224,15,16,121,170,66,17,49,128,40,160,6,169,82,200,17,
-    49,128,10,86,165,130,32,85,80,182,85,137,2,85,133,36,35,5,134,38,161,
-    26,118,1,88,117,192,9,225,73,104,117,134,48,194,168,35,130,18,74,170,
-    74,42,130,16,17,5,64,169,90,163,0,41,64,21,141,114,133,2,33,64,85,130,
-    36,21,129,138,192,13,25,0,82,0,27,90,131,46,127,169,106,129,16,160,
-    16,196,32,133,88,128,106,18,124,132,86,42,160,0,135,104,130,2,147,36,
-    20,106,43,42,106,160,28,128,29,66,136,96,120,112,128,194,96,24,162,
-    16,133,16,170,170,128,128,34,1,69,0,159,0,81,10,0,8,232,1,42,16,160,
-    0,40,0,32,245,1,64,5,64,132,5,217,5,64,21,64,21,56,85,37,64,0,64,249,
-    3,85,65,140,2,69,225,123,143,16,30,224,15,26,85,85,21,80,224,160,0,
-    224,2,128,11,64,85,85,85,80,33,0,80,85,9,81,0,80,1,250,7,140,20,143,
-    32,31,156,0,128,12,64,21,112,111,0,84,113,242,1,84,0,224,16,1,235,129,
-    20,129,16,130,33,111,75,21,6,5,15,9,226,114,143,14,11,15,12,67,9,85,
-    5,224,12,250,5,133,30,128,2,33,161,16,35,1,40,224,1,47,29,13,47,0,42,
-    84,41,84,49,37,80,5,162,0,129,2,10,84,9,227,32,130,65,40,7,10,80,9,
-    134,64,163,17,143,30,26,170,2,168,0,160,192,0,0,99,42,80,42,80,169,
-    64,165,118,64,170,161,81,128,76,133,2,168,161,97,116,13,41,64,37,64,
-    160,132,80,21,128,48,228,15,42,64,143,28,10,143,16,14,1,5,0,64,5,64,
-    0,160,0,21,137,226,0,128,1,80,21,80,137,2,0,84,128,0,63,9,160,90,160,
-    90,42,64,128,42,54,168,106,170,106,170,64,40,170,64,149,52,170,145,
-    0,66,170,8,66,149,66,160,66,54,10,170,19,10,128,10,168,64,0,0,129,16,
-    157,161,16,133,8,85,85,66,194,0,34,85,224,162,14,135,2,225,1,41,80,
-    160,90,170,97,90,42,38,130,16,106,170,80,149,250,36,130,14,160,16,106,
-    16,43,5,38,229,80,34,195,16,161,0,85,85,19,21,70,131,15,84,128,2,84,
-    168,86,57,81,74,170,90,53,168,85,82,106,86,218,86,21,4,90,21,3,85,150,
-    0,57,64,85,68,192,0,95,8,135,4,168,86,195,225,0,128,19,160,0,85,85,
-    106,85,65,207,33,68,162,16,149,106,70,116,6,171,20,9,101,192,0,69,15,
-    9,170,130,14,223,133,4,208,13,129,2,84,69,161,16,32,194,51,217,224,
-    15,66,48,64,22,2,160,170,187,22,2,168,0,21,161,10,69,160,0,247,45,133,
-    2,129,20,225,13,37,165,35,160,16,225,36,160,34,227,67,20,90,149,106,
-    128,159,2,20,106,149,17,224,61,26,160,2,223,129,10,161,8,17,5,160,0,
-    135,2,160,81,96,199,119,133,46,225,33,170,85,149,52,130,48,57,160,12,
-    149,86,52,226,15,128,16,90,128,223,162,16,19,129,34,160,50,136,48,36,
-    163,22,123,129,10,5,160,0,136,2,4,69,32,4,113,69,160,162,16,0,162,28,
-    149,86,160,223,18,227,15,113,160,18,83,98,66,128,134,80,18,168,170,
-    1,91,0,159,0,14,64,160,0,9,80,16,1,143,2,9,128,106,128,106,168,0,168,
-    46,70,160,170,0,2,17,67,241,1,75,170,10,49,138,10,131,12,5,32,179,226,
-    12,34,21,34,7,85,85,199,1,122,34,84,162,14,7,225,1,9,64,128,16,96,106,
-    168,86,114,170,170,66,170,224,66,67,243,1,23,85,85,5,80,49,37,80,21,
-    227,8,160,0,85,85,21,188,0,39,69,28,128,18,163,16,134,6,0,52,80,160,
-    90,9,18,106,5,160,216,86,164,14,21,90,53,21,85,85,62,37,84,85,160,0,
-    128,11,64,113,147,0,56,1,85,17,192,0,15,8,135,4,160,90,207,84,224,16,
-    133,8,168,85,130,16,164,35,96,233,164,16,6,20,7,165,209,0,84,5,82,8,
-    149,192,0,21,15,9,169,86,0,191,84,133,4,105,112,226,13,5,160,16,197,
-    51,246,224,15,100,195,73,82,18,138,161,2,52,247,106,50,17,131,12,161,
-    10,21,160,0,109,153,133,2,129,20,1,84,48,228,5,149,86,173,132,46,128,
-    8,170,49,90,229,67,132,16,106,245,80,162,16,95,2,162,6,2,5,2,169,157,
-    160,0,2,84,21,136,2,162,63,104,1,7,134,38,169,86,169,86,0,164,16,226,
-    9,126,160,12,0,36,228,15,160,0,128,16,160,14,136,48,112,170,128,128,
-    2,128,6,163,20,86,10,80,219,5,112,55,8,130,81,55,32,132,14,121,21,128,
-    29,162,16,16,163,28,90,128,188,82,229,15,170,128,115,1,134,80,130,16,
-    128,0,170,1,0,32,0,32,0,160,2,160,88,2,0,159,0,38,64,160,0,143,2,10,
-    170,0,129,170,137,12,128,170,168,170,168,10,219,17,165,2,143,16,12,
-    85,45,157,2,80,172,14,188,225,1,172,16,160,48,106,50,133,10,37,8,64,
-    165,66,165,66,132,7,64,85,7,85,21,64,149,64,85,36,129,2,188,199,1,161,
-    0,21,92,128,18,163,16,134,6,0,52,64,128,106,42,81,170,21,128,228,90,
-    34,112,133,8,165,82,160,0,66,64,85,65,134,14,85,149,80,85,81,28,85,
-    81,1,80,113,49,147,0,5,14,84,69,84,5,84,39,61,135,4,4,129,106,81,85,
-    1,64,133,8,161,125,86,161,129,16,164,35,0,164,16,13,5,78,7,86,208,0,
-    81,21,9,161,2,47,9,87,165,90,16,80,133,4,165,192,0,16,190,162,16,18,
-    86,18,98,224,15,20,6,14,90,165,90,170,74,1,17,3,243,169,2,17,132,12,
-    209,0,84,85,14,155,134,2,128,20,5,80,160,0,5,90,2,0,160,16,85,85,169,
-    86,169,86,168,212,106,229,67,8,168,5,170,0,170,178,66,1,5,2,161,6,169,
-    82,1,137,21,2,165,86,165,130,32,85,80,182,160,0,135,2,85,133,36,99,
-    5,134,38,161,26,107,1,88,163,16,226,9,106,128,16,104,212,81,228,225,
-    13,146,0,80,48,85,85,162,2,74,36,170,74,42,20,169,90,163,0,41,110,64,
-    21,80,135,2,33,224,82,80,118,205,129,132,14,37,0,82,160,81,41,90,63,
-    131,58,169,106,129,16,160,16,194,32,229,15,0,0,18,1,69,0,159,0,99,10,
-    0,8,175,5,21,85,84,141,2,1,44,5,44,21,143,16,28,85,42,85,5,64,161,0,
-    1,192,0,0,96,27,85,85,21,64,1,39,85,130,1,95,40,65,44,69,42,140,20,
-    143,16,22,17,17,132,10,85,21,80,17,5,80,0,176,16,81,85,129,1,89,81,
-    0,80,251,1,87,139,20,143,16,29,142,46,148,0,21,160,0,175,208,47,245,
-    1,84,81,0,0,131,20,129,16,251,225,16,63,0,255,47,19,63,21,161,16,147,
-    0,5,63,9,241,131,15,43,141,12,143,2,8,133,14,9,85,5,235,162,0,103,163,
-    16,69,1,56,1,63,9,163,243,47,123,42,0,40,0,32,31,3,1,131,12,42,84,41,
-    84,37,84,5,139,0,19,10,84,9,16,21,0,140,25,135,16,10,80,9,134,64,225,
-    14,10,81,64,9,15,23,170,98,168,0,160,0,192,0,0,0,1,4,0,64,5,64,0,153,
-    0,80,90,21,80,138,2,84,160,0,31,10,80,32,96,0,42,32,3,128,106,128,106,
-    82,170,0,33,128,18,160,170,96,129,2,33,128,2,160,2,0,0,34,241,1,170,
-    10,33,128,10,168,32,52,80,85,5,161,16,32,21,226,0,85,105,85,21,97,130,
-    2,1,64,85,85,240,162,14,38,226,1,41,129,16,80,42,64,160,42,37,64,0,
-    106,170,64,170,96,64,149,18,2,170,170,66,170,3,66,149,66,161,66,1,64,
-    243,1,95,3,169,160,0,85,2,161,16,5,20,123,192,0,1,112,0,19,128,2,5,
-    80,129,132,18,9,80,160,90,170,90,42,132,6,98,106,170,80,149,2,160,192,
-    86,164,14,80,165,66,5,64,170,251,90,165,16,5,161,48,161,0,5,80,2,192,
-    65,3,21,21,84,69,84,5,143,84,130,2,84,168,86,33,160,21,34,36,1,170,
-    90,37,168,85,2,106,57,149,80,21,160,9,0,166,16,165,86,158,2,54,169,
-    90,1,151,0,210,13,127,9,153,69,166,1,168,86,130,37,228,1,106,85,240,
-    225,10,144,1,226,17,128,16,162,16,149,106,21,48,81,85,85,224,15,22,
-    165,70,5,198,84,2,20,169,26,169,225,6,146,0,191,101,207,15,13,170,234,
-    1,246,6,68,81,167,5,243,162,0,49,224,41,52,66,165,170,22,60,34,169,
-    170,23,171,10,159,0,0,139,20,90,115,85,165,29,162,34,24,90,149,64,47,
-    24,106,149,17,0,28,244,26,161,8,219,173,26,143,2,10,161,12,149,232,
-    3,65,149,164,16,247,227,15,128,16,114,5,129,34,165,32,138,48,0,160,
-    0,1,91,0,159,0,14,64,160,0,9,80,16,176,143,2,10,130,6,168,16,4,170,
-    0,170,101,170,2,19,241,1,128,170,32,10,36,3,138,10,255,13,1,85,21,138,
-    2,85,253,85,171,16,80,171,14,225,1,89,225,5,64,248,136,16,96,144,0,
-    75,84,65,85,85,113,5,64,161,0,131,7,240,1,85,21,64,235,33,135,2,4,34,
-    5,225,1,84,162,14,112,16,16,128,5,132,18,105,64,128,106,49,168,106,
-    168,103,35,66,170,66,49,97,128,90,243,1,129,16,154,10,21,4,146,12,5,
-    80,37,80,21,192,8,64,246,85,240,5,161,16,224,1,23,69,60,118,8,80,21,
-    81,21,80,18,80,160,244,90,17,176,1,18,34,106,21,160,134,86,164,14,66,
-    85,84,85,1,166,16,67,154,90,3,85,85,37,84,192,3,180,211,7,152,0,81,
-    28,13,21,166,1,160,179,90,240,7,85,193,8,1,168,85,129,16,206,149,8,
-    112,164,16,85,68,192,3,17,36,69,154,26,131,24,85,85,165,206,11,149,
-    47,207,15,13,169,86,233,1,105,58,64,80,220,21,224,15,68,85,224,29,52,
-    66,154,248,170,23,246,5,175,10,3,159,0,0,139,20,105,85,170,149,136,
-    18,170,161,0,90,60,106,32,255,170,224,1,43,132,48,162,6,233,27,16,234,
-    29,127,143,2,14,86,128,34,166,16,225,15,128,16,84,100,224,160,0,145,
-    3,23,113,149,170,1,67,0,146,0,160,2,160,2,163,1,112,15,31,64,160,0,
-    143,2,10,141,12,168,0,168,115,168,10,51,143,4,3,12,85,85,79,15,226,
-    171,14,225,1,174,16,90,168,168,170,135,16,12,85,85,5,0,32,116,144,7,
-    85,62,85,85,21,185,9,128,2,151,14,36,168,16,163,21,225,1,80,165,14,
-    64,0,85,133,18,7,41,0,0,170,160,170,131,12,18,192,129,16,82,19,85,85,
-    37,64,165,40,66,165,66,209,11,80,34,21,64,60,149,64,85,210,13,51,193,
-    1,161,0,68,230,85,130,20,140,16,166,16,64,85,147,4,112,90,128,106,212,
-    21,65,130,2,97,170,53,97,128,90,66,16,85,81,85,1,35,129,4,165,82,161,
-    0,85,85,84,227,13,59,144,0,149,80,192,3,177,0,58,69,140,2,152,62,150,
-    8,129,106,194,7,20,161,86,28,161,170,161,170,21,128,16,164,16,85,231,
-    17,131,4,85,151,0,149,86,206,11,175,2,0,63,26,165,90,234,1,202,3,0,
-    160,16,85,226,224,15,4,227,31,193,7,165,90,170,138,8,62,149,86,169,
-    141,12,207,31,14,138,20,224,5,136,10,95,169,86,160,0,106,143,16,1,212,
-    12,225,15,64,199,168,8,162,6,136,2,165,86,165,139,12,63,14,43,160,26,
-    85,89,232,9,106,128,16,105,227,1,192,194,13,146,0,0,169,1,85,0,159,
-    0,141,85,141,2,1,28,5,122,28,21,28,157,8,141,10,92,85,92,255,85,12,
-    32,170,16,32,10,32,10,212,141,20,143,16,0,80,64,161,0,1,192,0,0,154,
-    64,97,21,64,49,87,85,128,1,175,0,89,65,28,69,26,139,20,143,16,23,136,
-    97,132,10,85,21,80,97,5,80,88,0,80,19,85,161,1,25,81,0,125,80,1,23,
-    139,20,139,16,143,62,30,150,0,21,167,160,0,248,1,84,17,0,84,85,97,235,
-    225,16,95,0,140,56,95,43,21,230,1,5,31,9,143,129,15,29,10,0,8,130,1,
-    245,47,133,2,199,149,0,5,135,14,9,85,5,160,0,9,93,139,16,1,104,1,111,
-    9,243,47,11,42,24,64,40,0,32,230,15,87,1,5,0,64,5,64,0,144,0,42,139,
-    160,0,2,80,21,80,138,2,84,16,105,63,10,80,16,57,64,16,0,170,100,2,128,
-    49,144,1,170,2,3,128,10,2,160,2,128,170,144,0,10,3,3,128,10,168,10,
-    80,85,5,164,16,51,244,7,85,21,116,83,85,85,165,14,239,143,2,4,130,16,
-    128,6,90,64,88,16,32,221,11,20,1,85,32,18,160,0,21,255,192,0,129,2,
-    66,40,162,14,38,226,1,41,16,50,42,64,42,5,64,128,106,3,170,106,170,
-    64,170,64,149,3,0,83,66,170,66,149,66,160,66,127,128,90,243,1,141,16,
-    17,133,14,20,194,0,240,98,195,14,135,2,19,87,80,160,90,48,170,90,42,
-    86,50,106,170,80,159,149,2,160,86,164,14,21,102,21,222,193,64,161,0,
-    21,80,18,195,16,131,18,83,198,21,192,0,132,2,84,168,86,121,17,75,170,
-    90,5,168,85,18,106,22,31,86,160,66,168,162,5,85,19,151,0,236,244,13,
-    31,7,244,35,193,12,86,131,19,19,106,243,85,225,10,97,132,16,162,16,
-    149,106,33,245,224,15,54,38,52,41,101,207,15,13,170,63,136,14,85,85,
-    208,13,130,33,21,1,161,49,249,163,5,162,0,97,144,22,52,114,160,170,
-    190,228,15,100,168,209,10,170,10,159,0,8,129,20,89,224,165,93,160,34,
-    90,90,149,106,128,144,106,8,106,149,15,1,1,93,0,148,0,168,160,0,9,134,
-    2,64,188,16,57,80,16,63,10,141,6,34,170,200,2,67,147,3,170,10,37,138,
-    10,34,80,85,5,138,15,64,85,21,122,127,85,85,165,10,102,164,14,15,4,
-    226,5,128,6,149,143,16,7,176,3,170,170,87,85,92,85,255,144,0,169,16,
-    129,2,41,50,168,14,228,1,57,255,34,133,16,0,240,15,144,0,36,0,33,221,
-    20,1,16,64,161,0,131,7,2,64,230,65,135,2,197,10,5,0,84,162,14,224,1,
-    224,16,3,163,16,7,64,128,106,168,97,106,168,7,146,14,170,66,170,66,
-    54,1,128,90,243,1,129,16,154,66,96,25,80,37,80,21,192,8,244,13,85,21,
-    177,224,1,39,69,76,102,80,21,81,26,36,80,160,90,33,244,7,85,50,145,
-    106,37,160,86,164,14,66,85,84,147,2,70,154,90,165,6,37,84,193,3,233,
-    150,8,148,0,244,11,15,7,21,166,1,160,90,103,240,7,85,193,8,1,168,85,
-    129,16,149,8,156,112,164,16,85,68,192,3,17,36,154,169,26,133,8,165,
-    206,11,149,207,15,13,169,86,126,233,1,105,58,64,80,21,224,15,68,231,
-    85,128,64,52,66,154,170,23,246,5,197,175,10,3,159,0,0,139,20,105,85,
-    149,136,18,170,222,161,0,198,43,52,106,48,128,14,228,15,52,1,67,0,148,
-    0,160,2,160,2,197,1,115,143,2,27,64,160,0,47,10,143,8,3,168,10,51,17,
-    143,4,0,64,85,21,249,6,0,85,85,241,250,1,47,2,164,14,47,4,143,16,14,
-    85,85,5,127,122,85,94,59,47,3,8,228,1,25,154,140,12,241,15,168,170,
-    119,161,16,32,132,4,254,64,66,7,35,151,14,36,8,226,1,240,80,165,14,
-    131,4,55,68,0,0,170,120,160,170,135,12,130,16,50,133,10,37,64,24,165,
-    66,165,66,208,47,148,0,21,64,61,149,64,85,192,8,69,193,1,161,0,68,204,
-    227,3,140,16,166,16,64,85,147,4,16,128,233,106,66,148,0,2,170,69,128,
-    90,146,2,16,85,81,163,2,165,82,161,0,39,85,85,84,150,14,149,80,193,
-    3,244,3,115,150,0,69,140,2,126,150,8,129,106,194,7,3,36,161,86,161,
-    170,161,170,37,153,128,16,164,16,85,17,107,23,149,86,207,206,11,175,
-    2,0,26,165,90,234,1,202,3,0,228,160,16,85,224,15,4,85,1,197,7,165,71,
-    90,170,138,8,149,86,169,141,12,207,31,14,202,138,20,224,5,136,10,169,
-    86,160,0,106,143,16,1,0,169,1,4,0,64,5,64,0,144,0,8,103,0,10,160,0,
-    0,64,21,140,2,16,118,57,80,16,63,10,130,6,42,52,160,2,38,170,0,170,
-    176,0,0,168,192,2,97,64,128,170,144,0,10,170,2,168,2,68,170,2,129,3,
-    0,80,5,80,0,17,64,9,64,10,161,7,64,21,80,222,1,135,2,130,12,84,63,13,
-    132,6,164,2,0,255,42,52,112,160,16,137,16,241,1,144,0,68,45,16,84,5,
-    162,0,1,131,9,192,11,21,100,17,85,7,18,85,9,11,84,60,9,80,10,5,130,
-    1,1,164,2,64,115,128,106,136,16,224,35,128,14,0,168,192,18,193,163,
-    20,128,15,72,85,85,0,85,5,55,160,0,69,85,131,11,129,2,21,48,128,1,192,
-    86,49,10,84,168,86,169,86,204,10,164,17,161,16,170,90,134,16,1,64,110,
-    170,106,80,133,18,80,177,48,192,13,85,243,66,67,132,32,156,0,144,13,
-    85,64,209,0,252,161,14,130,15,129,2,130,16,133,17,160,0,65,170,134,
-    86,135,20,10,80,169,90,224,13,132,18,7,42,84,42,64,169,106,16,69,13,
-    160,16,168,170,42,0,70,80,168,215,81,130,32,145,0,80,192,0,69,160,0,
-    165,15,245,129,2,169,17,97,129,13,3,64,64,1,204,132,51,132,16,128,52,
-    170,90,229,51,80,84,247,41,192,32,226,47,225,15,160,0,169,16,128,82,
-    53,5,66,169,225,64,120,81,18,69,247,132,14,157,0,129,4,133,32,19,65,
-    138,52,16,230,192,32,133,68,18,32,138,106,129,84,20,182,80,146,95,0,
-    227,15,64,74,129,2,41,255,64,40,51,43,225,48,42,162,16,24,130,163,81,
-    23,80,138,106,128,106,24,208,66,162,95,23,10,241,1,1,0,0,80,5,80,0,
-    0,8,37,0,10,0,148,0,80,21,139,2,84,131,160,0,31,9,160,90,160,90,42,
-    18,1,163,2,168,106,170,106,170,0,168,160,18,81,170,1,2,168,2,170,48,
-    2,42,0,33,50,10,168,10,196,170,16,33,0,84,5,16,1,75,64,0,20,21,84,16,
-    85,39,33,130,14,85,9,11,84,9,64,10,7,5,80,160,90,170,90,1,163,2,50,
-    130,16,106,169,103,164,16,170,2,36,200,2,3,102,85,5,160,0,69,85,75,
-    0,80,68,85,21,64,85,24,129,65,26,84,168,86,169,86,10,80,21,84,50,80,
-    53,0,84,42,165,64,48,106,21,170,80,164,16,66,56,163,2,170,66,119,50,
-    156,0,85,85,255,64,224,13,49,163,29,129,2,34,116,225,17,33,1,170,86,
-    135,20,10,80,169,90,125,135,18,42,160,16,2,18,48,96,168,250,145,14,
-    21,32,1,19,161,13,80,192,0,254,69,193,32,2,99,82,72,131,33,65,176,64,
-    160,0,1,195,17,132,18,10,85,9,57,80,170,90,193,15,17,128,16,84,41,206,
-    192,32,224,13,101,80,169,112,128,48,21,107,66,169,34,23,81,18,5,132,
-    14,223,157,0,129,4,75,65,232,33,50,192,32,227,31,159,52,96,138,106,
-    225,67,4,226,48,128,64,215,5,66,9,64,2,5,134,14,65,247,163,65,133,2,
-    12,168,16,2,84,136,48,1,193,160,66,132,64,3,64,170,0,10,170,255,128,
-    80,8,161,16,132,42,130,10,161,97,136,2,84,238,71,136,48,227,1,87,84,
-    16,163,16,19,171,80,16,80,24,64,16,64,16,176,128,81,132,48,2,160,80,
-    144,0,1,2,0,84,5,84,1,0,152,0,32,85,21,85,137,2,170,85,170,85,129,9,
-    25,86,170,86,9,0,10,12,23,90,170,90,41,16,165,2,170,5,106,170,42,168,
-    0,170,86,170,106,1,2,161,0,24,10,3,42,130,14,5,5,85,69,85,0,64,5,21,
-    88,128,16,85,24,0,80,26,84,168,25,86,169,86,10,6,81,169,10,3,39,42,
-    64,170,106,169,42,33,221,132,18,166,16,36,2,103,34,156,0,64,145,224,
-    13,33,0,80,163,0,64,85,0,228,225,1,134,2,128,18,16,170,66,135,20,10,
-    33,80,169,90,7,42,84,42,64,67,16,42,22,80,170,0,168,162,16,24,20,66,
-    170,2,130,2,20,85,85,175,80,128,12,69,193,32,84,161,0,3,98,193,8,16,
-    84,64,85,64,85,1,129,161,1,134,18,10,85,9,80,170,74,31,8,84,41,0,12,
-    194,32,8,100,94,69,81,18,5,132,14,157,0,129,4,27,216,65,193,1,25,85,
-    138,32,0,42,0,254,138,212,43,4,160,16,134,48,1,82,39,224,64,18,1,133,
-    2,85,85,21,84,253,21,72,28,136,50,31,4,140,32,161,82,170,246,10,160,
-    16,1,130,42,130,10,5,162,0,135,2,251,21,2,23,133,66,27,130,16,84,111,
-    19,229,139,32,32,144,101,144,32,10,84,133,8,5,190,16,137,2,21,16,105,
-    132,78,15,11,128,16,128,80,63,25,168,1,4,96,85,101,85,0,152,0,64,80,
-    85,85,138,2,170,29,86,169,86,130,10,25,90,169,10,42,0,167,2,3,170,106,
-    169,42,170,0,170,23,111,1,2,161,0,24,10,115,137,11,137,16,126,160,14,
-    64,22,225,1,8,49,197,9,145,0,19,10,80,169,90,169,9,42,64,0,155,160,
-    16,6,0,168,84,132,2,2,2,83,70,80,128,12,69,160,0,0,80,67,240,1,114,
-    8,32,6,64,85,1,33,84,0,84,135,18,10,85,9,80,1,26,84,41,0,170,42,138,
-    42,218,24,196,32,22,2,101,19,81,18,249,5,132,12,244,15,150,0,129,4,
-    27,65,85,102,28,85,153,41,1,42,0,12,160,16,247,10,180,34,2,68,5,64,
-    2,67,7,131,2,85,85,21,80,21,56,12,236,136,50,15,4,136,66,19,0,28,160,
-    16,42,169,168,131,62,1,130,10,5,162,0,0,64,125,133,2,21,98,119,5,29,
-    128,16,84,211,79,19,21,69,2,67,10,84,139,8,60,63,46,42,80,15,23,143,
-    6,3,47,62,15,14,1,84,0,159,0,99,42,160,0,40,179,1,64,168,5,16,0,16,
-    1,16,0,64,255,21,18,135,2,23,127,5,194,8,31,13,162,2,145,25,136,16,
-    80,5,2,0,80,1,95,0,0,0,21,2,55,5,52,248,227,15,63,14,160,2,52,149,0,
-    143,16,5,85,85,46,0,84,5,66,1,64,240,1,16,245,21,66,19,54,19,84,31,
-    7,80,249,20,135,36,43,162,16,47,2,160,14,85,5,7,160,0,21,85,65,85,64,
-    241,1,156,0,143,192,10,50,85,85,1,160,5,135,2,128,14,253,16,138,36,
-    139,16,160,16,90,131,51,95,1,85,91,128,15,69,130,1,80,196,14,133,17,
-    81,99,166,162,0,145,0,65,24,169,86,98,194,15,6,42,80,170,90,169,90,
-    128,14,20,191,42,112,41,129,30,20,70,143,64,0,156,0,238,5,169,33,7,
-    135,34,32,0,161,16,6,104,80,169,16,131,64,170,162,0,169,106,175,41,
-    132,30,170,82,168,166,14,137,2,135,14,234,193,27,149,0,27,131,16,1,
-    104,42,160,0,217,41,162,16,99,170,33,16,170,90,0,36,66,170,66,169,66,
-    169,106,80,170,106,224,13,2,3,168,2,168,96,170,170,225,15,135,2,2,170,
-    0,1,5,0,64,5,64,0,160,0,1,191,0,129,1,21,0,137,2,5,79,11,153,0,170,
-    77,112,42,48,40,230,1,170,34,145,168,165,2,80,5,32,0,80,1,3,32,0,80,
-    0,84,21,80,33,247,135,2,192,0,2,22,129,12,80,140,16,29,153,137,20,99,
-    64,41,70,28,84,5,73,162,0,1,64,0,85,64,84,21,223,66,38,19,84,140,18,
-    5,70,6,234,21,7,131,32,31,5,85,192,12,5,64,15,21,85,65,85,64,48,157,
-    0,160,14,63,50,85,85,128,18,56,130,20,17,135,16,245,131,2,135,32,163,
-    3,135,48,79,5,85,224,14,69,125,194,13,80,196,14,229,16,131,2,226,32,
-    132,3,80,32,134,18,169,86,135,36,42,80,170,90,111,169,90,128,14,132,
-    16,42,160,0,130,46,75,250,196,67,140,48,156,0,131,13,137,33,119,0,86,
-    153,32,80,169,86,160,1,85,80,169,163,160,16,131,68,170,162,0,169,106,
-    41,163,67,189,139,14,143,32,5,64,224,47,151,0,139,2,131,16,1,171,165,
-    1,224,15,42,160,0,41,136,48,170,49,96,160,16,160,17,81,66,170,66,169,
-    10,66,169,106,170,106,224,1,2,99,13,168,2,168,170,170,225,95,135,2,
-    2,252,113,135,12,165,14,15,8,143,16,4,225,15,65,41,112,80,160,161,16,
-    82,66,66,170,106,85,168,106,192,13,10,161,0,168,48,170,208,145,0,226,
-    1,130,2,42,96,2,1,1,0,80,5,80,0,80,1,0,160,0,0,80,0,84,0,84,21,114,
-    80,21,138,2,224,0,31,0,64,1,0,129,0,1,80,160,90,160,90,0,1,152,0,168,
-    106,170,106,170,0,40,70,22,170,97,2,168,2,165,2,50,82,10,168,64,170,
-    147,2,84,5,48,147,1,48,0,85,49,21,84,49,175,39,130,14,84,140,16,84,
-    55,161,16,128,20,76,21,64,50,106,41,22,34,170,50,170,66,41,27,7,85,
-    5,160,0,14,21,85,65,85,64,80,159,0,0,194,14,62,84,85,85,70,82,128,20,
-    130,35,19,102,160,90,52,32,168,80,20,132,2,86,170,66,147,14,42,5,10,
-    165,16,3,175,85,224,14,69,162,13,80,164,14,229,16,131,2,230,227,32,
-    131,3,161,2,131,16,169,86,128,36,163,0,6,80,42,80,170,90,169,160,16,
-    35,1,168,64,170,64,170,106,169,106,207,128,32,101,18,42,0,70,21,249,
-    54,234,150,0,129,13,137,33,71,0,70,32,64,115,169,86,160,1,130,52,64,
-    80,169,160,16,134,131,36,224,15,64,170,66,169,32,227,51,77,170,2,160,
-    0,10,168,243,13,21,42,189,129,2,135,32,64,224,47,151,0,43,19,1,174,
-    164,1,17,42,160,0,41,129,68,33,16,250,170,17,160,16,129,65,18,160,30,
-    169,112,255,170,50,225,15,130,78,52,81,16,50,186,147,16,131,12,65,224,
-    48,161,65,135,2,85,224,66,237,21,120,132,82,139,16,84,225,15,49,41,
-    135,135,62,51,66,170,106,128,160,16,130,30,239,131,94,162,14,65,82,
-    42,180,32,136,10,161,97,249,136,2,83,24,44,194,49,47,9,64,128,220,160,
-    16,40,112,160,144,65,6,18,1,4,0,84,5,84,1,160,0,0,130,85,1,85,85,21,
-    84,21,138,2,0,0,84,0,84,1,80,1,80,66,0,80,161,1,0,84,168,86,32,24,64,
-    1,64,0,1,224,2,170,90,48,170,90,41,37,0,170,106,170,10,106,169,0,40,
-    0,149,0,170,17,16,2,168,2,170,73,10,168,10,16,170,10,170,2,146,2,85,
-    5,85,14,21,85,65,85,64,0,31,2,193,14,124,85,85,135,16,130,18,160,18,
-    193,15,5,169,159,90,12,106,41,135,2,147,0,11,160,16,23,5,64,85,69,130,
-    13,80,132,14,229,16,243,101,227,32,129,3,163,2,33,169,86,128,36,53,
-    36,42,84,34,135,18,80,44,66,190,164,14,37,10,166,16,131,32,156,0,141,
-    33,21,173,0,22,32,16,169,224,48,163,0,42,102,160,14,169,160,16,8,82,
-    169,64,8,77,74,169,11,42,168,196,34,133,14,65,208,40,141,2,1,1,136,
-    17,42,84,42,114,84,41,129,68,164,0,32,80,169,160,16,195,160,36,16,66,
-    169,66,169,224,48,123,132,16,74,96,34,209,13,128,2,42,96,215,3,132,
-    64,129,12,65,224,48,5,160,81,160,10,251,36,3,39,128,1,131,30,143,16,
-    3,41,135,48,10,115,66,170,106,128,161,16,80,161,61,236,168,129,78,36,
-    20,42,181,34,134,10,80,223,5,162,0,135,2,21,18,39,224,13,130,62,247,
-    43,143,16,7,133,48,5,160,16,0,160,14,6,219,52,132,8,194,5,5,160,0,137,
-    2,21,16,250,41,130,10,41,135,16,39,127,8,2,16,192,170,24,160,16,128,
-    170,1,81,0,144,0,170,162,0,40,0,42,132,209,1,143,2,13,168,2,168,2,57,
-    170,181,74,16,2,17,52,106,16,10,108,1,168,51,145,0,42,1,128,7,2,226,
-    42,35,51,226,4,0,64,5,16,167,42,192,5,40,211,13,64,21,16,105,131,17,
-    89,168,66,168,82,170,88,119,128,16,90,161,11,6,163,16,0,19,70,146,129,
-    23,25,10,168,3,80,5,162,0,34,10,64,1,162,13,0,80,21,34,200,0,134,2,
-    34,84,32,88,168,82,48,168,86,160,88,16,90,42,66,195,41,5,163,16,72,
-    169,64,42,162,2,107,132,14,34,19,0,2,2,138,132,32,193,128,8,50,132,
-    10,85,85,0,84,21,60,161,0,85,1,162,15,1,82,122,168,55,86,0,84,24,50,
-    41,163,21,129,18,113,129,16,106,100,17,227,30,66,170,66,127,82,66,52,
-    98,131,10,49,192,30,82,123,159,0,0,0,162,0,163,5,132,2,160,1,1,130,
-    19,148,227,2,129,16,169,90,135,20,42,192,32,169,251,106,134,18,166,
-    16,4,72,133,12,5,226,30,37,115,85,85,240,32,85,21,160,0,1,94,68,64,
-    82,85,137,2,80,48,135,20,223,80,130,34,132,18,42,64,80,228,55,80,119,
-    64,168,208,64,86,0,168,2,134,8,87,34,5,38,64,34,21,255,33,2,132,4,246,
-    2,133,34,160,0,137,52,0,84,134,66,51,57,80,169,0,16,129,64,19,66,169,
-    175,178,32,23,1,28,1,24,131,2,12,251,162,16,8,128,65,10,160,81,10,64,
-    0,160,160,32,6,10,1,1,0,0,64,5,64,42,0,42,37,0,40,0,148,0,64,21,140,
-    2,0,4,26,168,82,168,82,170,24,170,168,90,160,0,2,24,106,64,10,170,112,
-    0,168,192,4,18,145,0,42,170,2,96,168,2,225,4,4,170,170,10,168,196,10,
-    32,128,5,0,80,5,96,10,101,64,1,128,13,97,21,80,1,0,38,54,0,80,0,84,
-    32,40,160,16,98,86,160,40,81,42,66,41,37,134,64,82,72,169,64,42,227,
-    1,147,0,171,34,134,16,2,2,138,226,15,10,144,2,73,66,5,135,10,84,21,
-    161,0,85,1,225,129,15,194,15,34,138,2,168,86,0,84,171,104,162,16,41,
-    101,80,130,16,106,37,139,16,195,30,66,170,66,130,18,66,4,245,37,128,
-    10,113,80,162,0,85,158,0,0,247,66,128,1,162,17,130,2,112,1,131,19,66,
-    41,129,16,169,90,135,20,42,192,32,169,106,237,134,18,166,16,84,24,0,
-    144,11,225,11,5,197,226,30,160,17,128,12,85,85,64,192,0,21,87,160,0,
-    1,52,64,130,16,85,137,2,64,175,32,135,20,80,130,34,84,161,3,64,160,
-    16,247,48,193,55,32,48,64,168,208,64,225,43,189,2,16,168,18,129,30,
-    130,8,66,5,89,22,64,18,21,223,33,0,20,64,85,246,18,226,5,131,36,131,
-    50,38,84,132,66,37,57,80,169,0,32,224,45,36,66,169,43,192,32,42,168,
-    39,1,28,1,24,255,131,2,28,162,16,24,128,65,134,48,33,192,88,247,132,
-    64,35,224,104,130,98,39,2,0,132,48,255,131,58,3,135,2,163,16,87,136,
-    66,82,99,183,23,72,80,130,82,71,64,82,71,0,131,114,168,0,168,1,0,0,
-    80,5,80,10,64,1,65,64,0,164,0,21,80,21,80,0,4,136,2,0,84,0,84,32,24,
-    168,18,86,168,86,160,24,170,90,0,80,66,41,23,106,0,74,169,64,192,42,
-    21,145,0,42,168,2,170,2,99,42,0,1,34,170,170,10,65,168,146,2,128,16,
-    5,89,84,48,85,1,104,80,0,53,80,0,42,168,86,86,0,84,40,80,161,16,41,
-    231,1,17,197,106,24,163,14,66,170,66,36,2,117,108,84,32,193,30,164,
-    0,85,156,0,0,219,82,128,1,129,17,84,130,2,0,1,226,1,74,34,84,129,16,
-    169,90,135,20,42,192,32,91,169,106,134,18,80,165,16,100,66,39,180,50,
-    160,10,5,193,30,224,9,5,128,10,85,85,85,64,192,0,21,160,0,1,68,64,122,
-    82,85,137,2,80,48,135,20,80,130,34,247,84,161,7,80,160,16,64,64,160,
-    2,160,3,135,64,80,168,170,42,170,194,57,17,117,32,168,34,160,4,18,0,
-    2,5,89,6,64,2,21,189,33,6,64,85,244,2,226,5,131,36,131,50,38,84,132,
-    66,81,68,0,85,34,80,169,0,32,0,100,0,68,224,37,33,66,169,192,32,42,
-    59,168,0,16,194,20,130,26,75,1,56,239,131,2,60,162,16,56,84,229,55,
-    4,192,88,183,162,0,5,64,227,79,8,2,0,96,255,5,225,47,1,135,2,163,16,
-    87,136,66,82,221,99,23,72,80,130,82,131,62,65,64,245,82,131,96,131,
-    48,134,98,128,6,80,68,80,255,161,16,137,2,97,121,130,16,24,1,25,222,
-    132,80,23,35,64,4,66,98,8,1,1,0,84,5,64,1,64,0,1,166,0,21,85,21,85,
-    1,80,0,24,4,85,85,0,0,137,2,168,86,65,0,84,57,170,90,170,90,41,2,57,
-    106,170,106,169,64,42,134,10,138,170,145,0,66,170,66,58,10,17,132,0,
-    115,84,5,80,21,134,8,0,238,85,45,129,16,224,0,84,17,2,128,1,228,1,226,
-    1,3,81,169,90,71,42,161,64,64,106,5,1,80,170,0,176,163,16,70,2,5,20,
-    85,5,84,238,21,160,0,128,17,130,10,64,192,0,194,32,20,191,64,18,85,
-    137,2,16,160,0,135,20,224,12,98,192,13,84,161,7,32,84,42,64,32,225,
-    64,193,25,130,34,160,16,168,170,42,170,102,160,2,84,193,0,192,33,2,
-    168,98,160,4,213,0,226,4,130,16,5,86,64,82,21,158,174,33,85,64,85,18,
-    226,5,137,52,22,88,84,41,195,29,81,161,23,16,80,169,141,0,0,0,0,68,
-    193,38,193,49,169,14,192,32,42,168,0,16,163,46,129,26,11,251,1,8,131,
-    2,12,162,16,8,84,229,47,102,4,80,228,45,5,64,42,196,61,6,255,2,0,225,
-    31,4,225,47,3,133,2,165,16,251,5,136,66,2,21,5,120,80,130,82,190,18,
-    114,64,2,20,130,48,33,131,2,127,80,42,160,0,131,88,67,119,35,55,253,
-    99,55,35,55,132,80,23,99,64,235,68,4,32,73,0,136,4,64,97,255,57,33,
-    57,130,94,56,33,57,225,14,224,26,97,25,143,16,1,1,8,96,85,101,85,145,
-    0,1,85,248,0,162,0,5,135,2,195,2,143,4,6,5,80,80,5,80,58,64,16,85,5,
-    85,5,9,84,10,84,0,84,48,0,2,16,36,5,1,168,0,170,17,2,85,5,84,5,168,
-    5,164,7,251,85,143,16,15,15,12,79,3,160,16,74,0,8,99,161,2,84,9,113,
-    69,85,1,225,5,175,160,0,143,14,4,0,143,4,7,69,68,54,16,101,192,2,64,
-    38,19,85,169,47,9,69,239,225,8,140,28,159,0,0,133,4,69,83,192,11,68,
-    42,162,16,1,80,70,84,2,64,4,62,69,5,16,10,129,2,12,157,0,162,12,123,
-    84,21,130,8,71,130,32,226,1,5,83,0,133,28,85,69,84,5,80,9,80,85,10,
-    80,18,84,64,84,66,64,84,6,4,12,4,7,0,146,0,170,38,0,160,2,161,0,170,
-    2,192,55,130,4,45,64,0,128,128,3,106,133,56,194,4,170,243,129,2,133,
-    8,192,5,160,0,55,10,170,97,7,55,42,170,42,170,106,16,132,10,165,134,
-    64,143,16,5,168,82,160,2,135,14,128,82,0,64,134,6,72,140,8,104,0,82,
-    222,86,136,10,49,90,136,2,162,16,141,14,138,18,140,168,57,170,2,128,
-    160,0,133,4,72,109,0,26,162,3,7,42,162,8,133,2,40,254,225,1,129,38,
-    137,12,160,16,10,64,142,16,12,1,8,96,85,101,85,147,0,65,85,236,64,160,
-    0,7,131,2,0,16,143,4,10,5,216,80,16,226,1,1,160,5,48,64,5,0,0,5,4,1,
-    1,65,0,40,8,0,42,0,106,85,112,0,5,130,36,24,84,5,168,5,164,23,190,85,
-    132,16,0,130,8,197,13,143,14,0,143,4,6,82,25,80,85,85,69,39,161,16,
-    1,69,219,41,137,2,161,2,84,103,161,7,69,143,28,3,248,157,0,131,4,195,
-    5,136,16,162,16,104,80,5,98,64,69,192,12,138,14,0,5,85,15,8,255,68,
-    114,133,8,161,9,147,0,143,2,4,192,11,10,72,128,10,84,65,1,80,4,69,5,
-    47,16,69,21,192,16,21,132,14,0,138,16,128,1,24,4,69,4,16,0,4,11,0,1,
-    64,0,32,131,4,64,64,94,2,0,135,6,4,209,13,36,130,42,16,36,1,168,2,69,
-    69,4,160,2,5,65,168,10,133,8,69,4,1,0,65,93,137,16,4,124,4,137,32,155,
-    0,3,1,88,0,128,160,14,90,133,56,176,63,0,0,228,170,129,2,132,8,49,170,
-    2,135,10,5,13,0,168,10,170,10,136,12,33,64,240,133,26,136,80,44,240,
-    10,144,0,162,0,170,107,2,160,162,0,227,13,1,48,128,48,217,131,42,131,
-    6,129,16,170,136,2,17,170,86,157,40,16,170,90,141,12,46,137,18,168,
-    7,23,162,2,168,2,128,160,0,131,4,109,129,6,20,162,3,135,8,24,17,136,
-    2,40,58,192,22,170,2,10,160,16,140,14,21,143,18,14,1,8,96,85,101,85,
-    147,0,64,85,193,65,192,0,9,81,85,80,85,0,168,160,0,141,4,129,228,3,
-    65,64,5,64,0,5,16,1,6,1,1,65,0,8,17,0,8,0,90,129,2,0,1,168,4,120,0,
-    0,36,8,84,5,47,168,1,164,7,101,134,16,131,14,159,0,10,48,133,4,1,84,
-    3,70,0,5,1,47,69,0,21,21,64,10,17,9,47,160,2,1,84,7,69,162,0,135,10,
-    197,1,245,133,28,153,0,130,14,134,16,132,32,69,98,0,12,134,12,80,5,
-    64,65,192,12,90,0,64,5,85,15,6,4,69,0,16,0,27,4,0,1,64,135,24,128,2,
-    128,96,131,132,38,16,85,4,85,1,85,133,4,184,49,97,1,137,28,161,0,135,
-    6,65,21,30,5,64,69,21,120,137,16,65,31,3,175,16,22,1,34,0,139,14,144,
-    65,103,35,17,85,1,6,4,5,0,145,27,2,133,8,65,4,1,0,65,21,135,18,85,1,
-    4,28,4,29,4,18,0,95,6,1,128,2,128,35,131,56,241,80,35,158,132,8,1,168,
-    2,39,161,16,138,12,129,32,250,64,135,10,134,80,12,157,0,135,42,128,
-    160,0,217,64,136,6,113,170,137,2,64,170,86,230,88,75,143,30,5,138,18,
-    168,90,209,10,144,0,3,162,0,162,2,170,2,128,160,0,253,131,4,131,6,65,
-    137,8,128,16,138,2,32,2,80,140,12,5,60,21,143,18,14,1,16,16,84,21,160,
-    0,85,85,0,16,84,1,84,0,0,21,85,21,246,85,145,0,133,2,210,1,56,1,0,143,
-    4,10,11,16,64,16,64,80,57,0,16,2,128,1,42,80,41,80,0,80,160,2,0,0,0,
-    168,0,152,0,4,170,3,2,168,2,170,2,170,86,225,15,75,137,16,85,95,13,
-    85,85,95,33,0,90,247,145,0,6,128,14,12,161,16,21,15,9,81,184,193,1,
-    47,5,85,81,136,2,33,85,0,168,0,22,80,18,84,27,21,84,188,5,28,21,140,
-    12,153,0,161,32,7,17,88,85,81,1,80,6,192,32,21,64,82,0,64,6,80,2,0,
-    40,133,32,179,64,11,17,192,4,160,16,81,21,130,4,75,143,14,2,17,129,
-    12,85,85,134,18,17,80,102,55,0,192,17,160,7,80,1,3,161,0,3,80,0,64,
-    42,64,41,64,132,30,172,113,49,0,38,16,39,132,2,4,91,16,1,241,18,74,
-    160,21,153,0,9,209,0,44,130,4,0,1,177,1,169,38,129,8,168,192,2,130,
-    2,34,170,10,168,10,170,129,10,135,10,170,42,168,106,170,42,232,104,
-    147,0,21,81,170,141,16,16,0,243,80,92,43,209,32,133,6,42,1,82,80,135,
-    12,1,34,90,134,2,2,168,42,30,170,106,170,106,88,146,16,88,138,32,63,
-    42,1,104,176,1,71,130,38,147,0,130,22,215,131,4,242,3,133,14,0,48,2,
-    134,2,224,1,120,32,74,138,12,160,16,58,47,1,1,20,16,84,21,160,0,85,
-    144,0,0,95,85,1,192,0,21,0,19,131,2,23,24,83,1,84,1,226,5,143,4,6,16,
-    64,35,16,64,0,160,4,4,85,0,160,5,0,80,0,16,0,0,24,0,4,0,4,1,170,0,168,
-    0,170,85,93,0,0,240,0,152,71,225,15,137,16,85,63,4,0,84,130,8,196,13,
-    137,4,3,79,6,8,2,64,85,85,20,9,0,16,127,4,20,7,145,0,134,2,15,2,164,
-    16,140,28,215,45,133,38,28,84,28,80,16,224,16,85,22,80,18,84,23,20,
-    162,16,21,31,132,6,85,85,17,2,135,2,229,17,135,4,64,161,32,84,52,17,
-    85,17,84,1,192,80,55,128,16,80,0,64,21,64,213,4,224,48,132,32,64,18,
-    85,54,64,128,25,130,4,16,16,4,16,1,32,24,0,40,0,42,0,112,16,0,30,4,
-    0,1,0,4,17,1,137,6,4,0,4,84,1,170,2,136,42,0,16,84,5,170,10,134,12,
-    16,0,64,9,84,85,170,106,169,7,0,4,123,137,16,16,23,4,16,145,35,74,239,
-    44,0,27,136,18,0,0,105,135,14,17,2,50,165,115,17,10,135,10,20,0,16,
-    42,165,40,27,0,128,80,168,170,8,20,111,2,80,134,12,157,0,4,226,0,135,
-    18,128,24,52,32,169,86,136,2,17,90,40,42,30,168,106,170,106,40,138,
-    30,47,0,29,249,10,149,1,130,22,131,4,242,3,132,6,106,0,69,0,2,138,10,
-    10,170,74,138,2,42,0,111,16,1,1,16,84,21,84,6,84,85,1,144,0,68,85,0,
-    85,65,85,21,173,160,0,3,0,16,1,196,1,3,64,25,128,4,1,84,1,226,5,105,
-    5,86,0,135,8,16,0,0,64,0,24,0,1,4,4,1,68,0,42,0,104,98,129,2,0,41,224,
-    1,168,0,152,39,175,21,128,16,5,162,10,84,108,131,14,159,0,10,36,133,
-    4,5,80,135,8,16,64,0,64,11,16,4,20,1,84,7,0,135,2,45,242,1,0,0,118,
-    20,86,131,24,17,255,166,16,133,18,149,0,133,36,101,132,4,192,7,131,
-    16,87,132,32,80,50,0,54,80,224,13,224,16,136,54,129,2,0,0,84,22,0,20,
-    4,64,5,16,16,4,16,135,22,64,8,21,16,0,4,0,136,4,0,16,54,0,6,0,135,42,
-    33,4,240,48,40,118,80,5,208,24,23,145,28,0,136,6,129,16,250,85,134,
-    32,129,4,102,131,8,17,80,138,12,42,0,1,64,140,2,0,29,1,136,42,161,0,
-    16,5,70,16,0,64,84,143,128,36,88,0,4,85,134,64,49,6,184,52,2,0,56,1,
-    27,0,0,204,170,137,14,0,170,2,135,4,1,170,150,10,23,20,0,42,4,138,32,
-    129,18,62,1,68,168,106,16,240,22,206,76,133,14,243,4,241,56,178,0,133,
-    6,81,168,86,136,2,33,64,168,90,88,21,42,64,105,241,57,138,30,95,2,65,
-    133,14,0,0,2,254,149,1,130,22,129,4,212,3,134,6,48,209,4,135,2,48,10,
-    168,74,143,14,26,65
-};
-
-static MTB * mtb_KPKP = NULL;
-
-void initMTB_KPKP()
-{
-    mtb_KPKP = new MTB ("KPKP", 2, 38);
-    mtb_KPKP->SetPackedData (mtbdata_KPKP);
-    mtb_KPKP->Add (A2, A3, WHITE, 180);
-    mtb_KPKP->Add (B2, B3, WHITE, 341);
-    mtb_KPKP->Add (C2, C3, WHITE, 335);
-    mtb_KPKP->Add (D2, D3, WHITE, 332);
-    mtb_KPKP->Add (A3, A4, WHITE, 211);
-    mtb_KPKP->Add (B3, B4, WHITE, 359);
-    mtb_KPKP->Add (C3, C4, WHITE, 360);
-    mtb_KPKP->Add (D3, D4, WHITE, 367);
-    mtb_KPKP->Add (A4, A5, WHITE, 229);
-    mtb_KPKP->Add (B4, B5, WHITE, 362);
-    mtb_KPKP->Add (C4, C5, WHITE, 348);
-    mtb_KPKP->Add (D4, D5, WHITE, 358);
-    mtb_KPKP->Add (A5, A6, WHITE, 206);
-    mtb_KPKP->Add (B5, B6, WHITE, 372);
-    mtb_KPKP->Add (C5, C6, WHITE, 322);
-    mtb_KPKP->Add (D5, D6, WHITE, 309);
-    mtb_KPKP->Add (A6, A7, WHITE, 174);
-    mtb_KPKP->Add (B6, B7, WHITE, 317);
-    mtb_KPKP->Add (C6, C7, WHITE, 303);
-    mtb_KPKP->Add (D6, D7, WHITE, 269);
-    mtb_KPKP->Add (A5, B7, WHITE, 392);
-    mtb_KPKP->Add (A4, B6, WHITE, 374);
-    mtb_KPKP->Add (A3, B5, WHITE, 318);
-    mtb_KPKP->Add (A2, B4, WHITE, 239);
-    mtb_KPKP->Add (B5, A7, WHITE, 278);
-    mtb_KPKP->Add (B4, A6, WHITE, 327);
-    mtb_KPKP->Add (B3, A5, WHITE, 384);
-    mtb_KPKP->Add (B2, A4, WHITE, 360);
-    mtb_KPKP->Add (B5, C7, WHITE, 353);
-    mtb_KPKP->Add (B4, C6, WHITE, 378);
-    mtb_KPKP->Add (B3, C5, WHITE, 361);
-    mtb_KPKP->Add (B2, C4, WHITE, 336);
-    mtb_KPKP->Add (A5, B4, WHITE, 310);
-    mtb_KPKP->Add (A6, B3, WHITE, 347);
-    mtb_KPKP->Add (A7, B2, WHITE, 323);
-    mtb_KPKP->Add (B5, C4, WHITE, 333);
-    mtb_KPKP->Add (B6, C3, WHITE, 341);
-    mtb_KPKP->Add (B7, C2, WHITE, 344);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KRPKR
-
-static const byte mtbdata_KRPKR[17301] = {
-    1,0,84,85,1,0,1,0,65,128,85,161,0,1,0,84,85,85,85,6,4,0,4,0,69,85,3,
-    155,0,0,5,0,0,0,85,85,64,0,180,64,132,6,69,50,240,7,1,131,2,5,228,1,
-    98,144,8,99,21,4,18,16,92,0,16,20,16,18,0,20,64,219,85,194,14,137,16,
-    4,160,12,15,10,64,162,16,51,7,0,1,49,7,0,4,49,55,7,0,16,49,7,0,192,
-    14,11,255,225,14,9,17,15,10,17,9,17,9,255,17,9,17,9,17,7,163,1,131,
-    1,255,1,133,15,15,6,137,6,33,137,2,17,41,255,17,42,16,42,22,163,2,23,
-    165,16,255,31,6,131,6,23,3,23,3,23,4,247,22,4,24,161,2,17,68,166,0,
-    161,16,127,143,48,1,85,37,128,4,128,6,137,2,17,9,255,17,10,16,10,209,
-    0,156,0,129,64,165,0,248,255,3,1,226,95,37,177,77,17,37,68,85,103,21,
-    1,248,7,224,15,21,4,113,5,120,128,2,16,24,240,11,175,16,2,159,0,92,
-    1,8,84,85,1,0,165,0,20,0,0,164,170,85,85,4,0,4,0,128,5,2,84,85,21,0,
-    0,0,65,80,85,155,0,64,0,64,0,65,28,2,84,0,100,128,6,177,7,35,20,52,
-    1,164,169,128,2,144,8,1,18,20,33,4,164,166,112,16,0,16,1,13,18,20,16,
-    164,154,32,0,1,6,18,21,64,165,106,1,224,12,137,16,118,4,0,161,16,9,
-    27,64,128,4,9,102,0,1,17,7,0,4,17,39,103,0,16,1,23,0,64,1,25,249,7,
-    19,1,27,9,225,31,85,85,255,25,1,25,1,25,33,9,17,239,11,21,5,195,32,
-    4,8,23,1,109,115,64,6,51,0,6,51,0,183,6,51,0,6,19,0,8,163,16,255,7,
-    83,9,85,3,19,7,19,255,7,19,7,19,7,19,9,19,189,7,17,85,10,147,0,5,49,
-    85,182,8,49,21,8,49,21,8,49,254,21,8,17,1,220,91,159,0,111,135,32,129,
-    48,236,69,142,4,119,1,4,130,63,165,0,20,103,1,68,66,85,20,4,128,2,38,
-    192,49,0,152,8,21,64,69,85,1,24,84,85,81,166,0,159,0,4,16,0,80,16,0,
-    133,4,69,47,1,0,1,0,249,1,137,8,141,10,143,2,12,225,0,143,16,10,16,
-    0,159,193,0,15,8,0,1,17,95,8,111,16,167,2,255,239,31,6,135,4,47,2,161,
-    16,79,8,95,18,101,159,0,2,255,11,31,0,141,8,79,36,83,15,8,127,24,47,
-    38,109,239,79,6,80,166,0,159,0,4,64,6,17,5,210,164,65,239,81,24,31,
-    138,5,192,0,20,0,3,50,84,85,84,130,2,61,4,1,3,56,68,85,68,128,4,141,
-    68,31,16,1,80,0,148,0,5,226,0,4,0,80,136,85,163,0,85,85,1,192,1,5,0,
-    40,84,85,68,34,69,128,2,68,85,168,21,80,20,34,21,80,20,85,160,69,0,
-    84,146,0,85,84,1,0,20,84,84,5,1,144,1,81,65,85,138,81,0,81,5,4,0,69,
-    65,69,85,69,0,69,5,16,0,21,130,65,176,9,0,84,21,4,64,143,16,14,111,
-    68,85,163,16,69,20,0,74,177,15,109,9,81,83,6,69,51,6,21,255,51,15,24,
-    51,7,19,8,18,8,255,18,8,18,8,18,15,26,17,9,255,17,10,240,15,10,48,10,
-    48,10,173,48,13,4,15,11,68,130,2,7,20,237,34,8,144,10,10,81,34,23,69,
-    190,2,23,21,2,16,144,9,154,0,23,255,5,10,139,32,140,48,139,64,139,80,
-    5,43,187,54,29,82,162,0,161,16,123,68,129,22,106,24,20,130,2,23,84,
-    1,85,7,170,84,1,85,7,84,1,85,7,152,84,1,85,5,14,159,0,94,1,86,0,148,
-    0,21,226,0,4,245,1,128,2,36,16,0,24,48,65,85,163,0,85,0,85,16,0,80,
-    85,21,0,81,81,85,17,82,21,48,16,85,69,36,48,81,84,81,85,84,128,4,84,
-    100,5,1,0,146,0,85,81,0,81,82,5,4,144,1,69,17,85,69,0,41,69,5,16,0,
-    21,113,85,21,19,0,21,4,64,143,16,30,17,85,163,16,123,70,84,83,70,177,
-    15,9,69,83,127,6,21,51,15,40,51,8,50,8,251,18,8,18,8,18,15,18,16,15,
-    5,255,49,10,32,10,240,15,10,32,26,173,0,29,20,31,27,17,130,2,24,84,
-    237,130,4,23,144,12,10,69,82,7,21,232,50,143,32,9,149,0,7,0,10,21,85,
-    254,80,11,107,139,48,139,64,138,80,144,11,154,0,128,89,164,0,81,85,
-    0,170,90,85,171,5,38,65,32,64,40,17,130,18,51,39,81,84,129,2,39,81,
-    81,114,55,6,81,69,18,6,81,144,30,16,128,14,159,0,94,1,57,84,85,159,
-    0,255,15,255,15,152,21,1,48,143,8,44,149,170,15,59,175,38,145,1,60,
-    84,85,159,0,255,15,255,15,255,15,185,1,57,84,85,159,0,255,15,255,15,
-    168,21,4,128,175,32,238,15,14,1,8,68,85,1,0,161,0,5,0,0,0,0,10,0,21,
-    0,69,85,88,4,0,1,5,192,0,129,2,85,85,165,68,2,69,128,5,80,85,49,84,
-    1,2,85,85,65,0,16,85,85,2,56,1,1,80,84,21,1,8,8,4,80,81,21,4,8,16,80,
-    96,69,21,176,6,6,64,80,21,21,54,64,1,0,163,16,133,16,4,84,101,255,68,
-    160,5,9,161,7,9,135,2,3,88,255,2,88,2,24,4,163,16,7,83,127,1,0,2,83,
-    7,163,8,7,133,2,255,5,102,20,6,20,6,22,161,16,223,17,129,14,227,47,
-    69,32,55,97,57,255,225,7,57,35,55,4,22,4,22,255,4,24,195,32,23,163,
-    16,23,33,25,255,139,32,139,48,139,64,136,2,56,103,178,17,152,0,11,37,
-    5,0,64,85,41,69,192,3,39,43,5,85,27,69,85,27,128,2,242,26,64,26,64,
-    16,86,85,163,0,13,84,85,129,170,90,221,17,144,0,70,138,34,161,16,129,
-    170,85,140,32,80,125,171,85,12,85,12,85,12,85,15,0,0,159,0,94,1,43,
-    84,85,149,0,81,132,1,84,162,0,127,15,8,68,32,129,4,141,6,143,2,38,139,
-    16,161,3,127,15,10,68,194,0,135,8,103,127,36,15,0,163,4,255,239,31,
-    10,131,4,93,111,36,127,2,239,47,14,161,16,63,0,253,79,36,95,4,143,32,
-    12,139,48,139,58,31,30,157,0,86,149,162,0,127,8,64,0,1,80,245,1,0,148,
-    141,22,111,30,2,0,1,82,160,1,1,11,0,64,85,17,0,31,42,64,143,23,9,179,
-    15,6,139,50,81,132,32,161,2,1,0,143,36,6,128,137,6,143,64,46,1,33,80,
-    85,163,0,85,85,5,0,89,225,0,0,148,0,21,0,17,65,85,0,3,85,85,16,0,69,
-    85,21,21,0,81,85,17,2,21,192,1,21,8,240,5,81,85,81,84,49,85,84,89,21,
-    0,192,0,1,128,2,146,0,85,81,20,48,81,21,4,144,1,69,17,85,140,69,48,
-    69,21,16,145,3,113,85,129,21,0,21,21,64,80,85,81,111,143,16,27,17,128,
-    6,74,84,163,16,70,177,15,111,9,69,115,6,21,51,7,51,255,15,24,51,8,18,
-    8,18,8,18,255,8,18,9,17,15,26,17,10,16,255,10,240,15,10,48,10,48,11,
-    143,32,28,222,161,0,144,8,41,84,129,2,40,145,12,41,253,69,50,39,176,
-    7,15,4,157,0,7,0,252,10,193,15,10,139,32,139,48,139,64,138,80,88,197,
-    85,161,0,161,16,21,170,90,127,1,73,239,66,81,128,6,106,16,128,2,41,
-    49,251,41,50,40,50,8,18,64,191,14,1,0,159,0,91,1,16,84,85,68,162,0,
-    84,85,64,0,0,69,85,85,85,80,85,4,160,0,1,84,0,64,0,85,0,26,84,85,16,
-    0,1,225,1,96,128,2,45,85,85,69,2,85,131,6,89,1,85,224,0,1,8,4,16,4,
-    8,16,83,16,16,8,64,16,64,68,160,1,63,137,16,4,0,163,16,21,160,15,34,
-    21,255,193,4,25,136,2,2,24,2,24,2,255,24,4,163,16,7,19,7,19,7,255,163,
-    8,7,134,2,4,22,4,22,4,255,22,6,161,16,9,17,9,17,225,1,255,113,229,39,
-    41,116,22,36,22,36,255,22,36,24,161,16,25,35,23,33,255,25,139,32,139,
-    48,139,64,136,2,88,13,17,247,195,57,69,133,30,157,0,135,8,0,225,0,9,
-    95,64,21,8,66,129,2,88,2,24,24,2,64,100,85,161,0,161,16,64,168,212,
-    90,130,35,155,0,101,84,69,128,4,85,127,85,21,138,32,209,16,57,32,58,
-    128,2,248,58,0,26,0,192,0,159,0,108,1,22,0,0,85,156,0,69,160,0,2,75,
-    1,80,138,3,65,0,131,2,68,16,234,161,4,78,132,6,70,4,28,16,28,247,64,
-    173,15,227,15,195,0,143,16,6,68,98,119,255,40,130,6,40,18,40,18,40,
-    31,5,255,228,15,31,7,161,8,25,6,20,6,20,255,6,20,6,31,7,143,32,12,163,
-    8,15,1,22,255,4,22,4,22,4,31,41,139,32,139,48,224,139,64,72,88,15,19,
-    170,74,85,85,164,0,157,0,64,162,0,65,0,160,3,85,4,69,0,81,0,0,1,113,
-    65,0,1,85,1,64,85,69,1,81,33,1,0,4,17,65,4,85,4,4,48,4,81,4,0,16,17,
-    65,35,16,85,16,48,16,81,16,147,8,79,192,15,64,48,64,81,207,16,3,153,
-    0,227,14,159,129,14,240,6,85,81,136,16,129,32,105,113,253,9,17,9,17,
-    9,17,15,0,101,141,162,0,161,32,64,168,90,138,34,129,14,65,127,140,2,
-    81,89,1,89,113,9,17,128,9,17,85,1,59,84,85,159,0,255,15,255,15,122,
-    69,160,0,192,143,4,42,143,32,178,175,28,58,1,22,0,0,85,159,0,10,1,239,
-    3,11,15,16,175,5,140,2,17,60,65,143,8,13,143,16,255,111,221,235,239,
-    83,12,143,22,16,111,46,159,0,13,86,47,78,0,61,64,143,20,28,84,143,2,
-    15,1,23,0,0,85,159,0,10,5,239,3,11,15,16,93,141,8,17,140,2,65,63,29,
-    143,16,255,111,77,1,155,111,109,47,110,168,170,28,251,3,86,143,38,14,
-    64,143,32,109,84,143,2,15,1,61,84,85,159,0,255,15,255,15,136,239,87,
-    15,4,92,159,12,45,170,15,59,86,143,32,94,141,4,143,2,0,1,58,84,85,159,
-    0,255,15,255,15,41,4,143,24,173,252,170,13,251,3,239,103,15,143,32,
-    109,141,20,143,2,0,1,59,84,85,159,0,255,15,255,15,185,16,255,27,204,
-    128,239,121,14,141,4,84,85,1,35,84,85,154,0,21,0,0,172,2,255,140,4,
-    223,3,10,15,17,143,8,14,128,16,175,16,11,111,83,127,9,243,15,85,31,
-    7,15,87,31,5,15,77,81,5,7,171,147,0,156,4,1,143,14,29,5,79,13,69,143,
-    26,45,83,12,90,63,32,21,169,0,84,1,143,32,14,160,143,4,13,95,13,80,
-    142,2,1,40,84,85,154,0,21,251,1,85,21,61,0,0,80,188,2,15,27,142,10,
-    143,6,30,0,247,0,175,16,41,143,16,51,127,41,15,30,5,141,2,3,214,31,
-    39,141,10,13,69,141,6,21,148,0,177,5,59,84,1,84,70,21,251,5,80,141,
-    12,205,63,46,31,110,140,20,84,1,141,32,143,34,12,90,94,47,46,1,235,
-    15,85,122,207,64,32,173,6,143,2,0,1,63,84,85,159,0,255,15,121,239,51,
-    46,141,8,143,10,30,18,143,6,14,84,1,0,12,21,85,163,0,1,85,85,21,84,
-    80,5,84,5,158,143,26,60,143,48,111,169,170,111,13,60,140,32,47,15,198,
-    1,143,2,13,47,27,80,5,80,173,2,110,1,58,84,85,159,0,255,15,255,15,152,
-    80,143,4,13,101,84,17,143,10,28,238,99,169,170,15,58,90,96,158,14,1,
-    143,32,76,61,80,85,1,16,84,85,69,162,0,85,85,21,84,0,5,224,0,85,134,
-    2,20,32,84,8,85,85,84,16,0,1,84,0,0,20,0,164,0,84,1,85,84,84,64,0,1,
-    84,0,4,33,85,75,69,84,1,85,84,129,6,85,33,12,133,8,4,5,81,85,176,2,
-    38,16,16,5,69,85,17,40,64,5,21,121,85,65,161,1,137,16,39,3,16,0,191,
-    163,16,5,64,16,9,160,15,50,37,255,136,8,18,88,18,72,20,163,2,23,255,
-    69,21,163,16,23,67,23,67,23,255,134,8,20,134,2,20,70,22,225,1,25,255,
-    67,23,161,16,17,129,14,227,47,33,23,255,65,25,132,8,22,4,22,4,24,255,
-    139,32,139,48,165,16,25,67,23,65,25,255,43,59,141,64,31,6,129,2,145,
-    27,155,0,21,76,32,84,26,84,84,209,77,24,86,159,80,27,86,84,27,128,2,
-    28,225,13,150,137,32,208,42,85,101,164,0,69,4,157,0,102,101,84,81,161,
-    16,20,168,240,1,136,16,102,0,4,128,18,56,0,16,96,56,88,0,64,96,65,158,
-    10,159,0,93,1,8,84,85,1,0,163,0,20,0,3,4,0,5,0,85,85,4,128,1,16,161,
-    1,84,85,5,224,0,0,0,80,144,85,155,0,64,0,1,65,0,84,24,0,68,0,69,128,
-    6,243,7,1,1,6,20,1,4,1,5,1,128,2,146,8,1,1,4,20,4,4,4,5,4,64,0,16,81,
-    1,16,20,16,4,48,16,5,16,0,18,1,64,21,91,64,5,48,1,128,13,137,16,4,164,
-    16,182,7,27,64,128,4,9,0,161,14,8,217,0,97,40,0,1,24,0,64,255,163,16,
-    23,69,21,69,23,9,193,15,255,227,0,21,5,21,5,21,5,21,255,5,23,3,23,3,
-    25,7,227,47,255,129,1,23,3,23,3,23,3,23,205,3,25,3,4,170,21,1,85,237,
-    26,149,0,19,1,85,24,1,21,183,24,1,21,24,1,21,24,1,254,19,220,75,159,
-    0,111,133,32,129,48,161,32,15,6,17,103,4,85,7,17,68,85,7,17,248,128,
-    2,54,1,16,38,1,69,85,80,5,64,245,3,20,160,0,164,170,86,253,85,129,1,
-    3,129,64,203,19,226,113,4,84,54,50,85,85,144,91,34,20,50,128,2,142,
-    4,19,20,4,84,128,48,48,148,0,52,20,16,84,240,2,48,64,67,20,64,64,85,
-    226,47,1,8,84,85,1,0,161,0,65,85,37,69,85,85,194,0,4,0,1,69,157,134,
-    2,157,0,64,0,1,135,6,242,7,1,222,56,146,8,8,16,81,8,18,5,102,1,0,163,
-    16,5,4,0,115,143,16,6,205,64,128,4,41,0,1,19,6,4,183,19,6,16,19,6,64,
-    19,71,255,19,71,19,79,8,19,7,19,7,255,19,7,19,7,19,9,17,9,255,17,15,
-    10,17,9,17,9,17,9,255,17,9,17,11,17,130,2,243,7,20,190,33,151,0,80,
-    8,17,132,4,148,8,84,221,1,37,52,4,115,1,50,16,250,244,7,112,2,128,1,
-    155,0,101,4,130,14,233,4,104,83,5,64,160,22,85,0,1,7,0,1,84,85,21,1,
-    5,36,6,0,4,128,2,4,5,6,0,152,16,96,16,5,38,161,16,21,64,226,5,30,159,
-    0,94,201,27,20,0,68,136,32,224,4,196,50,143,64,2,161,0,68,0,84,0,195,
-    5,4,17,4,1,20,1,5,12,17,4,4,20,4,5,17,4,48,16,20,16,5,17,5,64,21,0,
-    64,69,85,1,8,84,85,1,0,161,0,81,85,9,69,85,5,0,85,144,0,4,0,98,1,85,
-    134,2,23,5,0,0,160,2,86,64,0,1,65,128,4,69,114,242,7,157,1,129,8,21,
-    1,50,146,8,50,4,110,50,16,161,0,50,16,2,66,2,125,64,85,224,15,163,16,
-    133,16,161,15,111,10,64,182,84,5,0,192,14,9,0,48,9,223,0,48,9,0,48,
-    11,163,16,7,255,51,15,8,51,7,51,7,19,7,255,19,7,19,9,17,9,225,15,15,
-    10,255,17,9,17,9,17,9,17,9,183,17,11,65,134,14,3,69,15,13,131,6,255,
-    7,131,2,7,68,6,68,22,4,251,16,157,0,224,10,164,0,129,14,31,0,65,160,
-    31,239,99,193,5,129,33,35,81,196,4,143,2,30,157,0,30,131,32,4,0,5,192,
-    0,255,19,1,228,95,81,209,68,146,61,19,17,80,4,1,68,165,160,32,243,7,
-    17,0,4,4,128,2,4,74,115,17,0,4,16,64,16,211,7,152,17,48,5,64,175,32,
-    4,159,0,92,1,3,84,85,1,0,81,85,161,0,80,149,0,69,6,5,192,0,85,85,16,
-    242,0,1,129,4,131,2,29,0,1,1,135,65,134,6,0,4,0,4,137,10,241,7,17,106,
-    64,0,64,71,1,0,84,230,226,15,69,161,8,137,16,16,0,163,16,31,6,102,0,
-    1,35,21,0,4,33,135,10,111,0,16,193,0,136,2,64,1,57,69,255,231,31,69,
-    51,67,63,8,3,23,1,255,25,1,25,1,27,193,1,135,36,155,0,255,227,47,131,
-    4,31,6,1,134,12,112,3,39,255,3,39,3,41,239,63,0,161,1,133,32,227,31,
-    255,15,10,232,59,2,27,129,48,9,17,159,0,6,248,231,79,131,14,195,67,
-    163,20,143,16,8,161,0,84,85,62,5,1,5,160,17,135,6,145,0,143,2,14,95,
-    110,252,0,82,225,71,131,46,242,39,160,0,139,64,16,119,0,20,64,240,58,
-    143,32,7,4,66,135,38,128,229,41,143,2,14,1,8,84,85,1,0,161,0,21,0,129,
-    16,192,0,0,0,85,85,4,0,168,1,131,2,8,48,81,2,85,85,170,21,192,1,17,
-    56,85,96,65,137,4,27,1,85,85,1,192,0,54,4,48,186,32,54,16,48,32,54,
-    64,16,55,64,1,0,163,16,133,16,4,52,69,255,161,5,73,23,3,136,4,2,24,
-    2,255,24,2,24,4,163,16,7,51,7,255,163,6,7,21,5,134,4,4,22,4,255,22,
-    4,22,6,161,16,9,49,21,255,230,45,8,19,7,132,4,6,20,6,255,20,6,20,8,
-    163,16,7,51,7,255,139,32,139,48,139,64,136,2,72,123,9,179,25,255,159,
-    0,8,136,14,112,58,16,41,17,41,254,130,2,40,114,8,18,14,176,30,146,0,
-    189,1,194,97,1,143,32,111,107,225,18,25,80,128,238,37,143,2,30,1,1,
-    84,85,1,0,1,0,21,0,160,0,16,0,4,0,0,0,85,106,85,4,128,1,129,2,24,16,
-    8,32,208,16,160,3,131,4,20,160,5,85,85,64,17,0,64,0,85,0,80,0,68,144,
-    34,208,0,1,21,0,16,1,4,208,1,18,144,8,21,0,16,4,4,208,4,18,80,21,48,
-    16,16,4,208,16,66,0,21,16,17,64,5,237,64,193,15,161,16,135,16,4,98,
-    119,16,243,2,23,80,0,23,0,1,1,51,23,0,4,33,7,0,16,17,63,7,0,64,17,9,
-    17,9,17,255,135,2,225,31,37,3,17,9,17,89,255,1,25,1,25,1,27,3,23,255,
-    3,23,3,23,3,23,3,23,255,3,23,3,23,3,25,225,11,131,14,173,157,0,51,21,
-    160,14,17,57,128,2,81,85,56,21,160,16,17,56,21,0,17,87,24,21,0,17,24,
-    21,0,19,64,157,0,8,162,0,0,0,21,170,90,110,85,5,176,11,53,21,194,36,
-    56,128,2,130,69,56,21,1,84,85,5,56,103,21,4,96,8,21,16,16,8,220,161,
-    16,15,2,159,0,10,84,240,46,127,78,133,32,0,93,170,6,144,25,5,38,225,
-    0,137,48,80,252,138,64,225,63,121,1,25,1,25,21,64,64,16,20,1,59,84,
-    85,159,0,255,15,255,15,23,1,159,32,238,0,15,160,1,60,84,85,159,0,255,
-    15,255,15,255,15,185,1,60,84,85,159,0,255,15,255,15,255,15,185,1,17,
-    0,0,85,159,0,7,81,85,85,137,230,3,192,1,85,17,0,129,2,81,1,16,163,0,
-    80,1,81,60,17,1,85,160,1,48,5,67,80,5,17,4,80,85,4,48,17,67,80,17,17,
-    40,16,85,16,0,65,19,85,65,27,17,64,85,64,239,15,8,143,16,4,81,161,12,
-    204,88,9,81,81,5,165,16,19,81,255,17,37,19,128,15,36,31,38,165,14,5,
-    254,135,2,3,117,5,85,5,85,15,20,227,81,224,0,193,15,3,21,0,81,162,0,
-    60,128,2,0,80,129,1,34,163,16,17,21,48,1,81,4,97,112,4,80,4,6,17,4,
-    21,4,81,16,65,80,0,16,80,16,17,16,21,16,81,227,64,65,80,192,15,17,64,
-    21,63,0,155,193,58,130,59,5,0,135,14,161,14,85,30,182,55,161,16,85,
-    24,33,85,24,33,247,85,24,33,31,5,143,34,11,21,234,85,137,52,254,132,
-    4,129,17,137,2,97,121,1,25,142,16,75,5,0,161,0,1,0,145,0,85,129,14,
-    125,86,80,224,3,58,194,32,160,94,4,80,239,192,15,128,16,193,94,82,80,
-    96,112,1,127,18,80,32,48,193,62,21,63,1,207,30,0,255,21,130,80,138,
-    2,130,65,137,4,113,105,17,224,41,17,41,224,15,1,38,84,85,151,0,17,0,
-    216,1,0,255,81,251,1,230,2,163,0,160,1,143,6,13,143,2,30,143,16,18,
-    255,130,15,31,107,165,16,31,52,198,25,31,25,133,6,31,56,165,163,3,27,
-    64,128,2,21,0,157,0,64,190,164,0,128,4,0,16,170,6,143,48,46,143,78,
-    4,95,90,35,149,170,21,192,30,21,0,17,161,29,231,191,4,4,81,160,31,195,
-    32,85,170,175,14,46,133,48,27,193,31,25,0,16,142,46,153,0,80,194,62,
-    128,165,0,143,64,52,1,8,84,85,1,0,161,0,5,0,0,0,0,21,0,10,0,85,85,105,
-    4,0,1,131,2,0,32,16,0,70,1,21,196,0,85,85,84,2,147,0,149,64,44,0,1,
-    12,4,12,16,27,12,64,1,0,163,16,133,16,4,68,249,85,161,14,89,161,7,25,
-    139,2,0,1,95,76,4,76,16,76,16,163,16,7,255,19,7,19,7,163,8,7,137,2,
-    1,255,26,0,26,0,26,2,161,16,133,14,103,227,47,85,86,51,85,85,9,229,
-    7,255,5,71,3,24,2,24,2,24,255,4,163,16,7,19,129,34,3,19,7,255,139,32,
-    139,48,139,64,140,2,84,7,182,17,159,0,5,127,69,21,176,5,15,66,176,30,
-    114,195,11,229,1,240,71,7,131,65,143,32,94,79,78,1,41,84,85,153,0,86,
-    192,0,4,0,235,161,0,131,2,235,3,3,64,32,80,196,1,125,129,4,84,34,63,
-    40,139,16,161,15,63,10,64,255,194,0,55,165,10,47,38,63,0,163,16,47,
-    8,49,239,41,3,31,38,155,0,84,234,47,79,6,81,255,137,10,231,4,15,32,
-    63,4,69,129,66,15,2,139,32,206,139,44,127,28,159,0,30,64,0,161,0,63,
-    56,173,34,76,2,0,1,0,0,163,2,143,20,0,66,248,0,1,22,128,5,143,32,92,
-    159,0,62,1,10,84,85,1,0,161,0,85,2,6,69,0,85,85,4,0,1,131,2,154,5,48,
-    16,0,1,51,37,48,240,69,2,144,0,193,1,57,1,85,85,221,5,192,0,54,4,48,
-    64,54,16,209,0,64,6,64,0,64,1,0,255,163,16,133,16,160,15,82,101,112,
-    2,21,255,161,7,25,136,2,18,120,2,24,2,255,24,4,163,16,7,115,7,19,7,
-    255,163,8,7,134,2,4,118,4,22,4,255,22,6,161,16,9,113,9,17,85,255,230,
-    39,8,68,6,68,6,20,6,255,20,8,165,16,5,117,5,21,5,252,139,32,139,48,
-    139,64,136,2,40,93,97,64,127,0,81,192,0,159,0,14,136,8,129,41,12,26,
-    250,1,25,1,25,1,24,85,192,32,255,5,143,15,7,130,48,128,4,143,32,62,
-    148,17,159,0,37,141,22,192,253,9,133,29,143,2,8,1,26,0,0,85,159,0,8,
-    205,3,5,140,2,171,1,47,13,4,44,16,44,64,207,15,9,87,143,16,112,5,124,
-    5,12,5,140,14,63,0,86,11,21,12,69,15,3,1,162,0,128,1,89,84,0,16,1,226,
-    14,129,15,5,84,244,165,1,171,5,135,6,67,103,4,16,5,65,85,17,162,0,85,
-    85,17,84,16,21,16,17,85,65,34,85,192,0,64,255,32,113,31,2,161,10,9,
-    18,226,1,168,14,238,173,16,8,129,6,33,4,20,97,17,254,16,20,129,15,17,
-    175,64,14,159,0,14,15,0,69,107,5,84,163,48,53,17,128,2,17,56,220,197,
-    32,143,32,6,132,48,0,156,0,136,4,12,84,253,1,160,12,121,161,16,121,
-    1,25,65,255,128,2,143,32,10,227,42,233,97,75,141,4,13,141,86,192,227,
-    48,151,0,194,32,1,26,0,0,85,159,0,8,205,3,5,143,2,13,171,1,44,4,44,
-    16,44,64,207,15,9,232,143,16,255,127,113,141,12,15,66,1,166,0,0,84,
-    27,168,85,1,0,156,0,185,5,65,173,16,69,225,6,5,54,85,65,4,96,21,20,
-    6,85,65,16,16,69,6,85,125,65,64,175,32,15,159,0,0,136,12,234,1,32,84,
-    141,49,139,2,4,85,17,23,48,16,252,208,22,23,175,32,2,135,8,129,32,59,
-    139,44,5,198,84,235,31,63,14,17,84,17,58,162,16,1,62,84,85,159,0,255,
-    15,255,15,134,207,87,238,143,32,50,1,26,84,85,81,162,0,147,0,65,192,
-    0,105,4,0,1,131,2,104,32,16,0,175,1,67,64,33,0,49,135,6,141,8,255,79,
-    28,161,1,73,161,15,137,16,113,9,17,255,9,141,8,111,32,163,2,7,163,16,
-    7,19,255,7,19,143,8,6,143,2,34,229,1,37,229,15,129,14,255,233,47,21,
-    37,143,8,2,143,2,36,139,32,167,16,85,248,27,139,48,141,58,15,44,159,
-    0,14,63,76,85,85,253,0,4,31,20,229,89,135,8,127,30,11,1,224,245,15,
-    47,37,129,116,31,28,1,25,84,85,21,162,0,144,0,1,85,58,130,1,4,0,1,224,
-    1,0,21,192,2,96,16,0,1,132,2,0,85,85,64,222,0,49,3,149,0,138,8,194,
-    8,4,170,5,192,1,4,8,17,112,16,8,191,65,16,64,161,1,137,16,160,15,162,
-    16,85,253,96,114,5,16,34,5,140,8,1,255,136,2,50,8,18,8,20,163,2,23,
-    255,163,16,23,3,23,3,23,138,8,16,255,134,2,20,6,20,6,22,226,1,24,255,
-    161,16,25,1,25,1,133,2,140,8,18,255,84,22,4,22,4,24,139,32,167,16,255,
-    21,117,5,21,5,107,139,48,139,64,171,136,2,47,11,84,224,11,84,16,0,159,
-    0,13,255,136,8,129,43,25,1,25,1,25,1,231,26,193,97,134,32,129,44,85,
-    21,143,15,7,133,4,248,127,46,148,17,159,0,53,142,22,45,142,2,1,76,0,
-    154,0,64,85,139,2,5,130,134,170,161,0,2,0,128,170,156,3,140,6,85,64,
-    84,28,81,28,69,28,21,249,143,16,255,175,15,3,131,1,31,104,163,16,31,
-    98,128,170,250,165,0,227,1,31,76,159,0,14,143,68,6,47,56,8,120,122,
-    136,170,47,0,203,5,205,7,43,40,143,2,17,1,32,64,85,167,0,65,85,85,85,
-    170,0,159,0,3,2,2,40,177,1,85,7,210,1,128,6,128,7,84,5,65,84,130,2,
-    148,81,37,65,81,2,69,21,65,166,69,2,21,23,0,0,225,14,143,16,43,219,
-    84,167,16,114,81,7,18,69,7,127,18,21,9,17,7,31,37,6,20,255,6,20,6,20,
-    8,19,5,31,8,255,147,0,31,7,4,22,4,22,4,22,255,6,21,3,31,41,2,24,2,24,
-    255,2,24,4,23,225,15,17,153,0,129,2,255,142,84,31,6,96,26,96,26,0,26,
-    102,0,65,16,159,0,10,128,170,111,0,137,4,237,168,143,36,2,143,2,41,
-    143,46,0,32,172,2,224,32,0,162,169,0,80,1,106,84,0,4,10,48,80,0,16,
-    10,250,7,1,64,85,0,85,1,93,0,159,0,3,2,162,0,15,8,131,4,8,184,175,4,
-    9,1,160,143,2,29,143,16,248,15,5,170,170,228,170,249,1,127,207,128,
-    14,64,85,167,0,81,47,85,85,85,47,78,8,72,141,30,15,14,103,141,18,168,
-    143,2,29,94,0,128,63,11,143,32,14,128,141,4,47,2,1,12,84,85,1,0,149,
-    0,193,1,80,26,85,5,0,4,162,0,129,2,5,192,0,40,17,0,16,34,0,226,0,84,
-    85,44,65,0,64,34,0,98,192,6,1,191,0,35,0,1,128,2,243,7,145,8,0,235,
-    64,83,100,0,64,163,0,85,163,1,191,226,14,135,16,4,166,16,115,161,14,
-    3,19,255,33,3,19,34,24,2,24,2,255,153,24,133,15,22,167,16,19,5,244,
-    7,6,255,21,5,21,6,20,6,150,8,4,255,52,165,15,23,201,32,179,23,33,119,
-    35,255,55,35,55,36,54,4,152,8,2,248,22,195,15,19,214,63,159,0,5,213,
-    55,84,85,239,85,226,32,83,161,16,85,51,228,63,129,2,255,50,116,2,151,
-    24,80,0,134,16,225,15,255,21,155,0,231,81,123,145,55,73,160,15,74,255,
-    209,40,9,17,9,114,8,231,48,159,0,2,239,131,64,113,247,7,129,68,17,105,
-    35,87,254,195,97,87,129,2,25,33,26,99,155,0,40,161,170,162,164,0,170,
-    192,0,10,0,40,152,170,154,4,170,16,86,85,40,105,170,106,4,170,16,86,
-    85,34,169,169,170,5,170,170,169,128,2,71,166,170,5,170,170,166,112,
-    245,7,168,146,8,16,106,5,85,192,1,85,85,1,12,80,85,5,0,163,0,147,0,
-    85,225,85,148,1,16,193,3,85,85,16,0,66,3,0,226,0,85,85,64,0,3,214,0,
-    98,128,6,1,84,0,1,128,2,190,4,36,0,1,64,154,8,64,4,29,85,85,5,64,80,
-    226,12,135,16,4,255,168,16,161,3,161,14,89,1,25,2,24,255,2,24,2,154,
-    8,165,16,53,37,53,255,39,51,37,53,5,21,6,20,255,6,20,6,150,8,3,55,3,
-    55,255,201,32,179,23,1,55,3,23,4,22,255,4,22,4,152,8,1,51,231,61,159,
-    0,20,95,229,63,85,80,69,54,33,128,10,6,255,17,128,2,6,17,80,151,24,
-    224,15,63,44,219,144,7,58,160,0,84,57,96,80,57,249,161,15,25,133,32,
-    129,17,159,0,2,146,28,85,5,223,232,97,30,224,54,69,25,225,56,25,129,
-    2,244,25,97,26,161,16,141,15,170,154,0,10,40,0,154,170,165,0,170,192,
-    0,106,85,80,106,170,85,170,96,90,85,170,138,169,86,170,170,169,128,
-    2,166,22,60,170,170,166,32,154,8,32,86,85,64,85,165,0,1,8,0,0,168,170,
-    165,0,160,170,6,170,170,85,85,166,170,5,145,0,20,85,85,154,170,5,90,
-    130,2,106,207,170,5,131,4,170,169,8,65,151,8,216,176,8,0,23,154,2,23,
-    106,85,27,85,168,170,2,166,16,129,16,166,120,109,145,0,154,24,1,106,
-    24,34,169,255,26,144,8,26,0,26,0,25,129,16,255,23,99,23,3,23,3,23,3,
-    255,26,146,8,24,2,24,2,23,99,255,21,101,21,5,21,5,21,5,255,24,148,8,
-    22,4,22,4,21,101,232,19,103,19,145,0,138,164,0,0,0,40,138,170,10,128,
-    2,34,52,0,0,88,34,170,208,0,0,213,71,164,1,170,170,252,162,51,64,129,
-    1,150,8,32,49,170,121,170,42,19,32,226,0,231,79,2,0,201,137,16,161,
-    16,49,20,85,118,0,10,255,128,14,153,0,141,2,1,53,68,151,24,67,211,54,
-    67,192,0,85,167,0,165,85,159,0,0,239,246,7,210,7,63,0,202,15,2,137,
-    16,32,193,2,249,153,8,129,2,6,16,65,159,0,29,84,85,17,19,4,0,4,129,
-    32,85,0,1,91,26,84,234,111,166,176,70,73,154,201,111,0,131,64,1,8,0,
-    0,168,170,165,0,160,170,6,170,170,85,85,166,170,5,145,0,25,85,85,154,
-    170,7,129,2,106,170,159,7,65,170,169,9,64,151,8,48,176,0,25,154,0,24,
-    85,106,168,109,170,2,166,16,129,16,166,120,145,0,154,127,26,170,208,
-    6,25,32,161,14,25,113,223,9,17,9,106,9,129,16,7,99,255,39,3,25,1,25,
-    1,28,195,30,255,40,60,225,14,38,19,37,21,37,255,21,7,19,7,19,10,229,
-    46,24,254,42,131,15,20,5,19,7,19,145,0,145,138,164,0,0,0,225,0,170,
-    170,42,57,20,0,0,33,0,148,8,0,0,127,164,1,162,19,128,2,115,100,16,35,
-    211,52,16,225,0,42,231,79,2,0,137,16,255,161,16,41,51,39,51,39,52,229,
-    14,253,21,37,23,150,8,17,179,8,152,0,85,228,29,135,16,176,7,176,6,84,
-    85,163,0,4,1,0,4,0,10,0,0,85,0,207,24,128,2,56,170,162,0,134,32,97,
-    156,0,24,170,42,0,29,159,0,31,0,133,1,71,42,1,0,84,235,111,170,224,
-    60,80,249,119,128,65,1,8,0,0,168,170,165,0,160,170,6,170,170,0,0,162,
-    170,5,145,0,25,0,0,138,170,7,129,2,42,170,127,6,168,65,246,7,224,1,
-    66,151,8,176,8,246,16,42,16,40,225,15,2,166,16,129,16,215,162,24,145,
-    0,138,26,170,208,6,24,111,161,14,168,24,65,162,26,144,8,26,255,64,25,
-    227,31,21,131,16,23,35,25,255,33,24,34,24,2,26,146,8,24,255,66,23,229,
-    47,19,37,21,37,23,255,35,22,36,22,4,24,148,8,22,255,68,21,231,63,17,
-    39,19,39,21,255,37,20,38,20,6,22,150,8,20,207,70,19,233,79,2,0,41,49,
-    25,255,35,129,72,159,0,8,240,9,110,151,8,194,1,151,24,200,66,195,12,
-    15,26,84,85,163,0,4,0,38,4,0,10,223,17,10,0,128,130,18,234,95,245,2,
-    43,130,2,143,16,41,96,85,143,32,8,128,224,124,162,0,233,15,194,32,1,
-    8,84,85,1,0,153,0,80,85,48,5,0,4,162,0,3,84,85,17,80,0,16,18,0,226,
-    0,84,85,65,89,0,64,18,0,66,128,8,1,0,127,19,0,65,128,2,243,7,145,8,
-    16,32,208,51,68,32,64,163,0,85,85,5,27,64,5,64,1,161,8,136,16,4,166,
-    16,255,147,0,161,14,83,67,113,3,19,34,255,24,34,24,34,153,24,128,15,
-    11,171,16,255,177,6,83,5,85,5,21,5,22,255,4,22,4,22,148,8,161,15,11,
-    25,255,179,23,17,7,19,7,19,7,20,255,6,20,6,20,6,19,3,218,63,131,159,
-    0,1,213,55,84,85,85,85,21,6,31,17,85,85,69,6,17,128,10,6,255,17,128,
-    2,6,17,112,6,225,15,3,254,129,1,153,0,235,81,55,145,55,25,160,15,26,
-    219,8,208,40,25,8,96,25,8,176,7,110,25,9,132,32,133,15,65,129,26,160,
-    0,159,0,6,239,81,133,5,55,224,52,69,38,129,64,224,54,254,38,81,128,
-    2,38,1,16,38,165,32,88,169,170,159,0,12,138,166,0,193,1,42,0,40,104,
-    170,106,4,170,192,0,90,85,34,169,169,170,5,170,170,169,128,2,68,166,
-    170,5,170,170,166,80,154,138,170,5,170,170,154,80,106,5,32,85,85,165,
-    48,1,8,84,85,1,0,151,0,32,0,12,80,85,5,0,4,162,0,1,40,6,0,84,85,153,
-    170,154,18,1,140,42,128,2,65,0,64,17,224,1,106,68,170,98,80,1,1,0,16,
-    160,140,169,144,2,169,42,1,0,242,7,160,137,166,0,166,42,4,0,16,0,69,
-    16,160,145,8,154,42,16,48,64,0,113,161,106,85,85,149,106,41,108,64,
-    1,161,8,136,16,4,166,16,67,152,109,170,86,84,67,64,85,4,0,182,161,14,
-    8,0,49,8,0,49,8,63,0,64,85,18,7,25,1,21,127,5,154,20,5,20,6,19,7,239,
-    19,7,19,7,1,18,9,23,254,3,19,7,19,7,18,8,17,219,161,8,17,161,8,17,161,
-    8,255,17,5,216,63,2,155,0,5,17,128,6,255,38,113,80,22,33,128,4,22,33,
-    255,128,2,70,1,16,38,1,35,129,1,140,159,0,0,129,48,80,85,21,130,80,
-    19,149,65,170,106,192,0,102,85,69,0,68,132,162,0,144,23,85,85,0,68,
-    228,79,0,8,1,168,169,170,86,137,16,168,166,66,170,90,25,168,154,170,
-    106,25,248,169,132,32,165,14,233,96,159,0,10,227,15,100,170,129,98,
-    241,49,85,69,85,169,169,170,157,165,0,160,20,41,1,128,15,37,129,2,4,
-    195,64,37,97,16,168,106,169,4,197,225,14,129,64,145,0,160,170,162,130,
-    60,2,122,134,30,81,132,1,241,58,87,131,4,69,132,16,221,106,224,50,229,
-    111,162,130,64,128,2,100,162,222,114,0,20,162,34,0,20,35,0,85,85,1,
-    10,80,85,5,0,163,0,0,226,0,112,85,85,144,1,148,0,193,1,85,85,16,161,
-    0,3,0,18,85,85,64,0,84,3,0,192,0,74,193,4,1,84,0,20,0,1,10,1,128,2,
-    4,36,0,25,0,4,10,4,48,152,8,10,16,195,48,100,161,1,10,64,5,0,165,16,
-    123,131,16,4,40,17,161,14,57,64,38,109,51,0,97,24,0,33,24,0,255,33,
-    24,131,15,25,165,16,21,39,19,255,37,21,37,21,6,20,6,20,255,6,20,5,23,
-    3,23,201,32,179,23,255,1,23,3,23,4,22,4,22,253,4,22,195,15,19,231,61,
-    159,0,20,229,63,85,255,48,23,161,16,193,16,21,113,1,21,254,33,1,21,
-    225,15,31,44,144,7,26,160,0,223,84,25,64,80,25,178,39,24,133,32,17,
-    129,17,85,85,5,64,85,85,37,199,64,159,0,4,243,55,106,85,26,25,224,54,
-    255,224,0,7,128,2,32,7,65,9,65,139,10,16,64,160,170,165,0,170,194,0,
-    97,157,0,138,70,193,1,42,0,106,170,66,69,170,80,90,85,170,169,22,40,
-    170,170,169,128,2,166,22,170,170,163,166,64,154,22,170,170,154,64,64,
-    150,8,85,192,1,90,85,1,8,84,85,1,0,163,0,0,0,1,21,0,149,0,85,85,20,
-    0,154,3,147,0,85,85,148,1,32,64,192,1,31,85,85,64,0,3,128,2,0,128,4,
-    116,242,7,128,145,8,128,1,112,4,1,128,233,4,80,64,80,16,97,128,16,228,
-    16,0,16,242,7,128,64,163,1,1,219,0,165,16,131,16,20,6,147,0,16,8,255,
-    17,161,14,25,98,24,2,24,2,255,24,208,8,130,15,23,165,16,5,21,5,255,
-    23,3,21,5,19,7,19,7,255,19,7,19,9,19,7,19,7,255,202,32,22,7,17,41,1,
-    25,1,231,25,1,27,1,85,85,17,159,0,31,255,148,8,240,7,163,65,18,1,84,
-    147,24,112,254,4,244,71,130,2,104,133,32,111,38,177,24,225,0,64,1,4,
-    165,0,0,80,21,4,21,32,4,1,16,53,0,64,21,16,25,21,16,1,64,53,128,30,
-    64,21,220,130,17,145,0,145,24,21,213,1,15,1,145,36,64,63,85,16,84,143,
-    16,9,129,68,134,32,160,0,129,2,248,118,0,17,38,161,32,16,64,168,140,
-    170,5,170,170,128,192,0,159,0,12,42,40,0,106,170,5,170,17,85,170,128,
-    169,6,170,170,169,106,85,170,138,166,6,170,170,166,112,154,6,52,170,
-    170,154,112,150,8,85,192,1,106,0,85,1,12,84,85,169,170,163,0,147,0,
-    85,32,85,20,0,1,148,0,0,0,7,128,0,170,0,85,85,144,1,194,1,74,17,130,
-    128,2,64,0,3,0,192,0,164,66,129,4,1,33,128,1,144,2,1,82,130,1,96,4,
-    33,128,4,64,41,4,130,4,64,16,33,128,16,24,64,16,130,16,16,242,7,128,
-    64,0,85,85,21,64,130,64,169,170,219,86,164,16,147,0,20,160,10,137,16,
-    16,68,109,117,64,6,19,0,161,14,24,0,183,65,24,0,65,24,0,128,15,27,255,
-    165,16,21,67,23,67,23,5,21,255,3,23,3,23,3,23,3,25,255,3,23,1,25,1,
-    25,3,23,255,1,25,1,25,1,25,1,27,31,1,85,85,149,16,227,61,197,62,250,
-    1,191,18,155,0,2,20,65,128,10,22,65,252,128,2,22,65,0,22,33,19,149,
-    197,170,165,0,145,0,22,85,20,116,148,124,129,4,85,144,29,12,156,16,
-    102,16,84,231,21,225,0,100,112,80,21,1,20,55,32,64,21,2,167,16,21,132,
-    17,159,0,10,255,142,14,79,8,208,9,58,129,2,57,97,58,218,129,65,244,
-    62,148,0,2,150,30,145,42,21,22,174,130,164,47,128,212,7,66,161,63,130,
-    65,195,15,238,130,1,195,97,66,130,1,128,64,229,111,236,130,1,16,37,
-    130,160,0,129,32,85,0,85,1,33,168,170,155,0,0,0,130,170,237,163,0,133,
-    2,156,4,15,77,130,166,16,3,138,255,95,93,161,15,83,133,16,31,92,195,
-    31,17,39,166,207,32,94,159,0,138,0,0,2,170,161,0,176,1,98,168,2,130,
-    2,224,37,168,170,168,177,2,160,2,227,1,162,33,170,10,0,160,111,2,0,
-    148,40,1,42,228,0,4,192,42,249,129,1,143,56,67,229,55,128,16,239,29,
-    45,173,2,20,0,67,65,22,209,29,170,0,168,84,238,117,143,195,31,130,32,
-    170,0,160,128,8,7,208,73,128,196,30,6,0,0,1,12,84,85,169,170,165,0,
-    145,0,85,32,85,166,2,5,148,2,0,0,24,85,85,154,170,5,17,0,0,125,42,170,
-    7,129,6,246,7,178,8,128,2,6,36,37,168,6,129,8,170,18,37,168,162,18,
-    18,66,37,169,66,85,224,15,205,86,166,16,145,0,166,2,7,129,16,154,180,
-    160,10,57,42,10,16,169,10,170,102,170,6,7,49,170,18,7,33,255,170,192,
-    14,43,7,35,23,3,23,255,3,25,1,26,0,23,3,23,255,3,23,5,21,5,21,5,21,
-    255,5,23,3,24,2,21,5,21,200,5,21,1,136,2,163,0,0,0,141,136,209,59,0,
-    162,2,115,161,1,128,247,70,83,55,69,144,0,168,19,160,11,255,129,1,52,
-    67,55,67,55,67,49,148,176,14,149,0,2,128,208,0,20,166,0,16,61,0,148,
-    85,233,79,193,37,23,161,16,40,95,1,2,87,168,128,22,103,128,6,8,121,
-    17,169,40,1,17,157,0,162,170,172,167,0,79,19,0,87,40,128,24,23,170,
-    102,6,104,128,10,23,170,18,130,2,22,40,170,66,105,64,21,116,16,84,25,
-    16,86,129,84,128,4,40,168,0,197,63,17,132,48,116,2,170,166,74,90,160,
-    246,39,67,106,73,169,106,85,85,1,12,0,0,168,170,163,0,147,0,0,32,0,
-    162,10,3,160,10,130,10,153,0,16,138,170,5,131,4,42,170,245,7,129,2,
-    244,7,212,8,112,2,67,168,219,192,0,129,8,47,12,168,208,0,137,16,162,
-    192,14,109,9,138,160,10,9,42,170,16,144,0,168,127,90,170,193,10,137,
-    12,143,2,14,209,6,83,133,16,255,69,85,71,83,9,17,10,16,255,165,12,133,
-    14,143,2,12,243,6,33,55,35,55,255,37,53,39,51,8,18,163,12,135,14,184,
-    143,2,10,152,0,2,240,8,126,197,47,0,0,219,136,56,161,16,40,56,65,168,
-    56,232,162,27,136,14,143,2,8,157,0,20,166,0,16,0,18,149,85,85,85,231,
-    79,2,0,121,237,42,32,98,61,162,69,128,6,162,252,112,150,8,64,177,40,
-    86,192,17,192,0,21,128,85,163,0,16,84,17,86,129,84,17,85,85,162,84,
-    53,161,84,128,217,96,157,0,185,13,169,146,98,53,168,0,255,240,9,134,
-    28,129,44,95,19,137,10,92,159,0,19,233,111,32,144,73,170,166,167,0,
-    168,166,170,90,66,170,154,55,168,154,170,106,55,0,169,106,168,106,85,
-    85,1,8,84,85,169,170,167,0,170,170,20,0,0,162,170,5,130,128,2,85,48,
-    85,154,170,5,145,0,85,85,106,205,170,6,144,6,85,85,250,7,33,166,250,
-    24,1,151,8,176,8,0,56,85,224,15,187,86,168,16,170,224,14,7,129,16,154,
-    8,109,145,0,106,9,145,6,169,8,81,166,255,10,144,8,10,48,9,129,16,9,
-    113,255,7,19,7,19,8,18,8,18,255,10,146,8,8,50,7,19,7,19,255,5,21,5,
-    21,6,20,6,20,240,8,148,8,6,52,5,85,85,168,159,170,163,0,2,0,225,0,21,
-    35,23,166,35,145,0,42,116,0,0,0,144,6,64,138,168,99,0,0,138,168,168,
-    63,2,0,0,149,8,164,1,144,4,19,128,2,249,129,1,38,96,227,0,156,0,231,
-    79,0,0,200,137,16,161,16,129,4,80,84,166,0,0,105,17,85,85,85,0,54,2,
-    0,40,251,215,1,22,85,100,151,24,82,0,226,30,159,165,0,129,14,170,86,
-    25,144,12,156,0,246,7,207,16,95,2,137,18,168,10,137,16,48,129,36,249,
-    153,8,128,2,41,129,48,159,0,14,141,6,81,85,17,51,16,0,18,129,32,84,
-    0,4,17,58,80,168,154,57,170,106,168,0,58,85,85,1,8,0,0,168,170,167,
-    0,170,170,20,0,0,162,170,5,130,130,2,138,194,170,5,145,0,0,0,42,170,
-    4,123,168,42,34,216,7,35,151,8,162,2,229,23,176,8,0,22,170,42,225,15,
-    2,118,168,16,170,176,6,7,129,16,138,8,145,0,205,42,7,50,170,168,6,83,
-    162,255,10,144,8,10,48,9,227,31,7,129,16,255,7,19,7,19,6,20,6,20,255,
-    10,146,8,8,50,7,229,47,5,19,255,5,21,5,21,4,22,4,22,255,8,148,8,6,52,
-    5,231,63,3,21,255,3,23,3,131,60,159,0,6,160,8,140,2,97,252,149,8,164,
-    1,149,24,164,16,224,0,28,183,55,0,228,168,137,16,80,162,28,80,84,166,
-    0,1,118,105,85,127,0,141,18,152,8,2,25,64,230,2,137,32,17,129,14,170,
-    86,24,143,12,1,82,40,170,22,168,97,81,85,19,15,16,0,18,0,40,44,129,
-    2,233,95,248,240,13,74,49,137,16,181,56,159,0,38,168,1,16,0,84,168,
-    166,169,0,170,90,168,136,154,57,170,106,168,58,85,85,1,8,0,0,168,170,
-    167,0,170,170,20,0,0,162,170,5,130,130,2,138,195,170,5,145,0,0,0,42,
-    170,7,191,33,248,7,160,34,151,8,131,4,23,176,8,215,0,24,225,15,2,168,
-    16,170,208,6,7,109,129,16,138,8,145,0,42,10,80,168,155,8,48,170,162,
-    8,81,138,10,255,144,8,9,227,31,7,17,7,19,7,255,19,9,17,8,18,8,18,10,
-    255,146,8,7,229,47,5,19,5,21,5,255,21,7,19,6,20,6,20,8,255,148,8,5,
-    231,63,3,21,3,23,3,255,23,5,21,4,22,4,22,6,243,150,8,3,224,0,156,0,
-    215,71,0,0,57,255,65,41,51,129,74,15,6,160,26,239,2,1,201,63,228,137,
-    32,161,0,129,14,127,30,81,85,67,16,27,0,18,0,40,223,17,8,129,18,2,17,
-    58,233,95,170,10,139,16,129,48,79,40,16,209,12,164,84,143,32,9,2,209,
-    0,168,42,169,0,0,0,0,1,8,84,85,169,42,165,0,165,42,6,0,0,85,85,166,
-    170,5,145,0,20,85,85,154,42,5,90,130,4,106,195,170,5,33,0,0,170,168,
-    8,239,128,6,246,7,178,8,128,2,26,87,129,8,22,118,64,85,224,15,167,16,
-    129,16,166,160,6,41,108,154,42,23,33,106,128,4,41,170,136,168,26,170,
-    170,166,26,170,170,255,26,25,192,5,192,14,11,23,3,23,255,3,23,3,23,
-    3,26,0,26,255,0,25,1,25,3,21,5,21,255,5,21,5,21,5,24,2,24,225,2,23,
-    3,23,85,85,168,42,4,163,0,0,0,168,42,160,224,0,34,192,42,99,160,1,170,
-    160,2,0,0,111,138,42,3,17,10,70,83,71,31,86,170,170,162,3,128,2,129,
-    1,68,196,85,53,69,85,85,0,159,0,8,8,161,0,48,81,166,0,64,0,85,86,227,
-    233,79,160,8,8,100,170,170,10,25,239,192,2,198,63,81,128,2,74,101,32,
-    192,0,205,137,14,129,30,157,0,138,170,55,15,2,169,95,24,165,145,2,2,
-    24,128,8,233,95,176,22,202,192,14,56,128,2,1,0,39,5,128,6,64,85,84,
-    35,64,80,64,88,0,222,82,80,137,10,2,191,16,3,155,0,132,48,68,32,10,
-    168,154,71,160,154,170,106,160,168,72,160,48,1,9,84,85,41,42,165,0,
-    1,0,36,145,0,34,42,3,2,42,225,1,85,48,85,154,170,3,19,85,85,106,195,
-    170,5,17,0,0,42,168,5,119,242,7,0,20,0,132,2,10,5,68,155,118,225,1,
-    41,42,167,16,129,16,34,224,2,111,41,154,160,14,41,106,24,145,0,225,
-    14,255,41,17,41,17,41,17,43,167,16,255,3,21,5,21,5,23,3,23,253,229,
-    31,21,37,21,37,21,2,10,237,161,0,48,224,0,2,10,17,129,1,2,254,130,6,
-    195,31,33,71,21,37,21,130,12,255,2,161,0,131,1,231,47,133,17,143,2,
-    0,67,83,160,131,64,155,0,16,164,0,85,85,0,0,162,160,160,10,64,4,85,
-    85,64,50,127,32,8,3,176,5,136,16,163,16,233,63,163,49,46,63,0,168,170,
-    69,130,192,0,129,74,150,0,110,8,0,162,8,107,85,6,179,1,246,7,45,168,
-    42,0,146,8,162,165,0,128,2,162,111,96,138,85,96,138,145,38,5,16,41,
-    160,48,169,42,7,84,16,85,84,0,3,68,80,84,80,85,82,85,242,85,135,20,
-    192,95,196,48,153,0,165,10,5,45,149,170,165,145,2,6,8,17,26,179,88,
-    129,2,74,24,32,41,2,23,34,129,48,36,2,21,20,2,4,176,28,74,170,2,23,
-    160,2,143,16,0,32,22,137,0,193,39,168,32,162,21,0,162,36,129,38,32,
-    154,21,0,154,240,56,64,64,32,106,21,0,106,0,64,85,0,85,1,9,0,0,40,170,
-    164,0,168,0,51,146,0,34,170,4,132,2,10,170,4,9,50,85,85,106,170,4,169,
-    170,12,16,85,85,170,169,4,18,0,41,0,42,162,4,160,131,6,42,154,20,4,
-    152,0,16,82,106,4,104,3,0,64,85,85,40,170,2,165,16,109,130,12,34,160,
-    8,137,16,10,80,41,106,153,160,14,41,170,169,118,146,0,42,162,153,118,
-    34,42,154,22,34,42,106,255,22,36,22,36,22,4,22,4,255,22,4,22,4,22,4,
-    22,4,231,22,6,20,130,12,32,170,161,0,130,1,252,70,84,54,68,54,68,146,
-    0,40,201,162,161,0,224,58,162,40,82,40,154,159,33,128,13,154,40,70,
-    4,72,2,206,24,2,130,2,8,2,163,0,0,226,65,130,64,100,85,85,64,1,160,
-    224,2,67,0,1,19,85,85,0,0,82,249,156,0,131,16,114,104,114,2,168,170,
-    20,53,2,168,168,128,24,162,54,0,81,168,162,128,2,138,54,0,168,10,243,
-    130,78,150,0,192,14,31,0,167,15,8,0,145,32,112,152,8,2,192,0,144,52,
-    166,0,2,170,42,106,0,0,213,87,17,86,240,3,164,24,155,85,128,2,168,86,
-    21,64,86,143,14,0,168,213,99,48,170,129,4,69,52,65,4,47,73,5,161,65,
-    154,56,129,2,246,103,29,242,56,85,41,2,54,240,40,160,79,2,157,54,226,
-    24,42,2,54,225,25,158,0,160,4,9,2,0,168,32,6,6,4,6,0,4,0,80,32,154,
-    6,129,48,48,64,32,106,6,98,1,8,84,85,169,170,167,0,170,170,16,85,85,
-    166,170,7,170,170,0,41,0,138,170,5,10,130,4,106,170,151,5,145,0,85,
-    85,248,7,165,130,2,105,114,113,154,40,17,151,8,106,85,224,15,183,86,
-    168,16,170,208,6,105,170,224,14,7,108,129,16,106,8,146,0,169,8,48,170,
-    223,166,8,81,154,10,144,8,9,113,255,9,81,9,81,7,19,7,19,255,8,18,8,
-    18,10,146,8,7,19,255,7,19,7,19,5,21,5,21,255,6,20,6,20,8,148,8,5,21,
-    19,5,170,170,162,164,0,0,0,225,0,244,21,35,23,35,146,0,168,115,0,130,
-    0,0,160,170,170,42,162,99,7,0,0,42,162,160,10,224,2,148,8,255,164,1,
-    144,4,3,128,2,114,199,63,161,16,159,0,0,57,231,79,0,0,137,16,17,129,
-    4,64,81,2,166,0,1,165,85,85,85,0,38,63,8,0,160,215,1,6,69,83,57,171,
-    64,128,10,160,168,0,0,145,12,90,9,255,16,156,0,133,32,2,79,1,138,18,
-    228,30,38,63,128,36,169,10,41,227,46,39,128,6,159,0,13,136,137,22,240,
-    47,170,69,85,67,65,0,1,73,0,161,0,0,80,0,16,16,58,64,160,106,57,85,
-    85,1,8,84,85,1,0,161,0,41,0,129,0,224,0,169,0,85,85,20,0,19,3,0,0,84,
-    0,85,85,144,1,134,148,0,177,1,85,85,64,0,3,196,2,229,85,242,7,33,144,
-    8,168,1,128,2,4,34,33,40,4,64,4,168,4,64,145,16,33,40,16,64,16,168,
-    16,144,16,242,7,40,64,161,1,168,64,1,219,0,163,16,133,16,20,86,99,16,
-    88,123,145,1,64,22,148,0,161,14,8,0,81,111,8,0,81,8,0,130,15,9,163,
-    16,255,7,85,5,87,3,21,5,22,255,4,19,7,19,7,19,9,17,255,9,19,7,201,32,
-    195,31,49,7,52,254,6,49,9,49,9,17,11,17,191,85,224,32,169,215,61,159,
-    0,22,148,8,240,7,192,16,255,69,81,97,69,81,97,69,1,242,43,133,32,47,
-    38,177,24,33,1,4,163,0,0,41,4,0,80,1,4,169,4,89,1,16,19,169,160,15,
-    225,0,1,64,119,19,169,160,16,65,9,84,15,1,129,21,183,194,21,130,64,
-    80,143,4,7,145,32,1,208,40,133,32,254,160,0,145,6,6,16,129,2,6,226,
-    64,160,32,81,169,170,21,170,192,0,86,170,150,91,22,170,224,1,85,224,
-    0,153,0,0,140,2,98,96,168,22,193,1,2,170,166,22,138,170,192,0,85,170,
-    154,22,170,64,40,85,170,106,21,85,33,85,1,12,84,85,1,0,163,0,147,0,
-    85,42,85,4,0,3,20,224,1,4,128,2,69,16,0,1,18,0,40,64,8,41,64,64,0,5,
-    0,192,0,85,85,183,244,7,179,8,1,64,149,8,4,81,64,140,83,161,0,0,0,16,
-    64,244,7,1,3,64,85,85,1,64,1,0,165,16,223,147,0,161,14,137,16,16,68,
-    117,97,25,251,34,24,34,24,34,24,0,224,14,255,27,165,16,21,69,21,69,
-    21,7,255,19,8,18,8,148,8,6,18,5,255,23,3,23,3,23,3,23,5,255,21,6,20,
-    6,150,8,4,20,3,120,20,85,163,0,192,1,196,0,159,0,10,73,85,70,68,0,67,
-    85,85,65,24,33,219,1,24,33,1,24,33,1,24,224,33,19,173,12,215,71,128,
-    170,170,170,227,6,228,34,151,0,21,68,0,168,129,18,20,22,0,1,104,128,
-    2,9,6,0,204,4,18,6,0,16,18,6,0,126,64,105,17,14,167,13,129,46,129,98,
-    153,0,207,17,88,128,32,85,81,8,241,56,192,32,31,151,24,4,0,80,16,231,
-    95,192,15,16,149,118,131,32,64,85,165,0,65,192,10,81,112,126,32,20,
-    227,1,159,0,64,1,12,84,85,1,0,163,0,147,0,85,40,85,4,0,5,0,192,0,85,
-    85,64,16,0,1,146,170,154,170,170,2,170,138,170,85,85,64,0,9,127,85,
-    85,244,7,4,128,2,151,8,32,0,226,19,36,0,180,7,1,64,21,192,0,123,1,0,
-    165,16,147,0,161,14,137,16,16,20,255,69,49,73,50,89,17,41,17,127,41,
-    64,37,5,37,5,23,3,255,19,7,27,196,30,40,60,3,24,255,37,23,35,23,37,
-    5,17,9,255,25,230,46,8,26,101,40,3,41,198,4,192,0,245,6,85,85,5,40,
-    49,219,153,1,136,63,65,8,17,1,8,110,17,1,8,17,1,8,17,3,198,85,156,0,
-    151,39,80,85,21,130,17,89,44,9,0,68,166,0,84,242,40,167,47,84,76,85,
-    21,72,0,4,128,2,72,0,242,16,80,72,161,16,15,2,8,0,167,0,59,128,170,
-    10,228,50,151,0,167,5,89,208,1,169,192,0,38,8,208,9,9,24,8,4,154,128,
-    2,24,8,16,64,24,9,192,15,218,9,144,24,151,0,64,230,64,143,14,2,81,192,
-    0,192,65,136,37,47,52,1,12,84,85,1,0,163,0,147,0,85,34,85,4,0,5,0,0,
-    20,128,2,66,16,0,1,146,170,154,170,17,20,85,85,64,0,5,0,192,0,85,219,
-    85,244,7,179,8,1,48,153,8,4,32,140,51,97,0,0,16,32,4,1,3,64,85,85,1,
-    64,1,0,165,16,111,147,0,4,24,129,16,16,20,69,161,14,253,73,114,8,18,
-    8,18,8,0,255,224,14,11,165,16,5,23,3,19,7,255,23,3,24,2,24,2,24,2,255,
-    21,7,19,7,21,5,17,9,255,21,5,22,4,22,4,22,4,226,19,9,23,181,39,85,85,
-    5,8,182,146,128,63,153,8,17,65,8,17,219,1,56,1,1,24,1,1,24,185,1,19,
-    85,156,0,244,32,6,146,170,87,165,0,86,224,3,169,86,64,160,22,194,16,
-    249,230,79,129,2,112,6,18,86,0,16,237,18,86,161,16,95,16,40,128,29,
-    153,0,1,125,72,81,208,101,25,209,40,25,160,0,80,243,9,193,31,9,131,
-    32,139,14,128,170,141,2,138,157,0,229,15,68,0,72,128,4,168,232,111,
-    174,8,128,48,104,56,8,64,112,22,232,8,64,112,134,16,9,48,105,85,1,12,
-    84,85,1,0,163,0,147,0,85,40,85,4,0,5,0,192,0,85,85,80,16,0,5,0,48,85,
-    85,64,139,0,1,66,0,72,128,2,136,112,255,244,7,36,0,151,8,32,0,71,32,
-    132,48,67,64,1,64,85,192,0,1,253,0,165,16,147,0,161,14,137,16,97,121,
-    64,254,68,21,2,41,1,41,1,41,255,64,21,53,21,53,23,51,23,255,51,5,21,
-    12,195,30,24,12,176,7,241,35,55,19,39,19,132,170,170,255,36,21,37,19,
-    7,26,229,46,8,252,26,146,8,17,41,4,192,0,245,6,85,109,85,5,134,32,3,
-    17,24,1,73,182,24,1,1,24,1,1,24,1,238,1,24,1,19,85,156,0,244,32,32,
-    237,128,128,32,129,82,41,17,152,43,33,8,163,247,5,31,53,8,168,0,0,170,
-    10,142,16,254,175,7,10,139,18,143,2,30,209,29,229,64,131,17,192,32,
-    88,160,81,170,17,4,207,31,53,1,12,80,85,5,0,163,0,147,0,85,192,85,148,
-    1,18,80,85,85,85,16,176,0,1,26,224,1,129,1,85,85,64,173,0,1,74,128,
-    2,74,96,128,6,1,10,65,10,1,0,0,192,0,1,0,158,4,49,10,4,0,16,80,146,
-    8,120,10,16,16,0,16,34,10,64,3,85,85,10,64,5,64,5,192,12,95,137,16,
-    4,170,16,80,224,15,19,5,161,14,253,9,82,8,82,8,18,8,0,255,128,15,11,
-    165,16,5,121,1,19,7,255,19,7,19,7,19,7,19,7,249,19,9,19,7,25,227,47,
-    85,85,255,9,17,9,17,9,17,57,1,255,25,1,21,233,61,159,0,18,19,33,193,
-    16,255,5,17,34,4,17,34,52,65,253,2,149,24,224,15,19,129,1,159,0,10,
-    133,15,10,237,108,128,32,42,192,15,84,41,80,80,191,41,80,64,9,61,129,
-    12,231,48,159,0,0,183,15,1,192,32,69,41,128,2,5,25,33,249,25,33,26,
-    32,143,32,7,149,3,154,170,66,165,0,170,192,0,106,85,106,170,53,132,
-    170,64,90,85,170,169,54,170,81,170,169,0,166,54,170,170,166,232,0,154,
-    8,0,54,85,192,1,85,85,1,12,80,85,5,0,161,0,149,0,85,210,85,165,1,161,
-    3,85,16,16,0,1,165,0,226,0,16,160,2,64,0,1,0,86,98,64,129,6,1,2,0,131,
-    1,128,2,190,4,2,0,67,80,154,8,0,146,7,11,85,85,5,64,5,64,5,130,13,127,
-    135,16,4,166,16,99,161,14,9,17,9,255,18,8,18,8,18,154,8,163,16,7,255,
-    51,7,55,145,0,195,30,73,51,39,255,52,38,52,38,52,152,8,17,5,199,167,
-    13,159,0,20,229,46,85,85,69,6,17,255,128,10,6,17,128,2,6,17,80,151,
-    24,246,224,15,63,44,240,6,58,160,0,84,57,96,254,80,57,129,15,25,135,
-    32,161,17,159,0,0,146,28,31,85,5,85,4,168,49,28,224,54,161,16,255,22,
-    225,56,25,129,2,25,113,10,49,181,15,0,235,13,2,247,7,178,71,26,100,
-    106,84,16,96,160,10,74,68,42,48,32,20,1,80,85,10,52,42,48,32,211,4,
-    16,52,42,144,8,32,16,112,86,4,42,128,15,37,192,0,10,4,241,0,255,69,
-    232,16,39,57,231,111,133,80,54,68,248,54,68,54,148,8,21,133,48,85,85,
-    1,8,84,85,169,170,163,0,165,170,134,170,192,0,85,85,166,170,3,147,0,
-    21,85,85,154,170,3,90,224,1,154,60,128,2,106,170,3,133,4,244,7,33,170,
-    118,170,169,96,149,8,177,8,166,48,69,232,154,65,48,5,106,193,1,169,
-    170,222,86,164,16,131,16,166,70,147,0,161,14,89,255,106,70,116,1,24,
-    2,24,2,255,24,227,14,25,165,16,21,69,21,69,255,21,5,21,8,18,8,18,8,
-    255,148,8,5,53,3,55,3,55,3,64,51,98,162,0,85,85,98,170,170,63,0,0,0,
-    19,6,20,6,20,254,6,149,24,4,55,1,57,1,51,68,20,85,163,0,85,85,90,192,
-    0,16,68,85,80,4,85,85,10,48,42,40,85,0,1,3,85,161,1,0,42,255,1,149,
-    8,162,16,54,116,6,195,15,226,16,115,159,0,13,138,166,0,227,1,93,16,
-    1,101,127,176,24,85,135,16,34,193,16,231,79,129,2,54,254,242,56,15,
-    17,157,0,208,6,52,129,34,193,0,51,175,20,129,32,84,42,166,176,54,41,
-    193,15,255,41,31,19,15,8,129,48,234,111,128,64,42,16,224,42,16,41,129,
-    16,85,85,1,8,84,85,169,170,163,0,165,170,134,170,192,0,85,85,166,170,
-    3,147,0,20,85,85,154,170,5,170,16,85,63,85,106,170,5,131,4,244,7,36,
-    128,2,254,149,8,18,32,55,16,32,54,177,9,55,169,170,86,164,16,131,16,
-    166,54,147,0,191,161,14,73,106,56,98,113,25,49,223,25,49,25,106,7,131,
-    16,5,37,255,5,37,7,35,23,3,28,195,30,255,8,28,129,15,20,37,3,23,3,255,
-    23,5,21,5,21,10,229,46,24,255,10,163,15,2,23,33,9,17,9,246,19,7,19,
-    7,20,1,5,20,255,4,5,24,197,47,16,215,24,159,0,8,155,55,161,129,17,21,
-    90,192,0,106,85,16,1,143,165,0,128,2,1,42,1,167,47,112,134,32,207,97,
-    16,136,16,170,106,16,95,17,159,0,14,3,229,17,20,85,42,1,0,84,89,250,
-    209,40,41,209,23,41,228,48,15,13,146,166,0,34,16,0,0,232,31,85,85,74,
-    217,1,3,85,16,85,0,1,0,1,239,111,26,0,129,48,85,85,1,8,84,85,169,170,
-    163,0,165,170,134,170,192,0,85,85,166,170,3,147,0,17,85,85,154,170,
-    5,170,170,90,60,128,2,106,170,5,131,4,244,7,33,170,118,170,169,80,149,
-    8,177,8,166,32,57,227,154,32,54,193,1,169,170,86,164,16,109,131,16,
-    166,38,147,0,154,40,49,106,255,40,82,161,14,56,34,56,34,56,255,227,
-    14,57,165,16,21,37,21,39,19,255,39,19,8,18,8,18,8,18,255,7,21,3,23,
-    3,23,5,21,255,5,21,6,20,6,20,6,20,255,5,23,1,25,1,25,3,23,255,3,23,
-    4,22,4,22,4,22,208,195,15,226,16,159,0,13,138,166,0,42,0,0,71,0,10,
-    215,71,170,90,85,194,20,230,79,153,130,2,134,16,170,166,114,6,170,154,
-    159,18,6,170,106,18,15,15,157,0,208,6,224,116,129,8,193,0,19,20,85,
-    0,1,23,0,84,170,1,232,95,166,240,37,136,16,248,194,15,120,143,32,20,
-    249,7,224,15,143,18,7,16,85,7,42,1,0,0,170,86,26,145,6,128,157,8,27,
-    85,1,8,84,85,1,0,163,0,0,0,134,42,192,0,85,85,166,0,3,147,0,25,85,85,
-    16,0,7,129,2,64,0,247,7,81,244,7,212,8,80,4,72,33,215,154,8,32,212,
-    7,5,192,0,85,224,15,165,16,107,131,16,166,38,147,0,16,42,0,144,7,163,
-    41,80,1,10,0,0,4,10,255,144,8,10,64,5,133,16,37,5,21,255,5,25,1,25,
-    1,26,0,26,248,146,8,24,66,19,7,19,0,0,255,10,4,19,7,23,3,23,3,255,24,
-    2,24,148,8,22,68,17,3,19,151,0,69,85,10,249,1,85,21,8,109,81,21,8,65,
-    69,8,65,5,183,56,1,5,24,1,5,153,24,160,15,155,29,159,0,4,2,0,165,0,
-    3,34,22,81,132,2,1,21,80,193,32,0,34,4,206,21,64,128,16,34,16,53,0,
-    16,98,34,64,37,97,85,85,10,224,46,135,10,228,11,160,170,170,170,244,
-    10,159,0,3,255,129,68,233,95,129,20,73,130,2,72,114,8,168,18,246,7,
-    5,226,31,2,164,0,0,85,255,5,192,0,159,0,0,205,16,137,16,128,48,235,
-    111,144,73,22,74,4,0,80,74,16,217,7,51,1,8,84,85,1,0,163,0,21,0,1,5,
-    0,0,0,85,85,166,170,50,3,128,170,240,1,128,2,16,0,3,39,0,0,42,50,64,
-    0,7,49,223,244,7,19,49,4,24,33,149,8,210,8,185,32,88,85,224,15,165,
-    16,131,16,166,170,182,37,51,16,38,51,64,42,144,0,143,1,10,0,0,4,10,
-    144,8,10,255,64,9,129,16,37,5,21,5,21,255,5,25,1,26,0,26,146,8,24,251,
-    66,23,3,19,7,19,128,225,11,143,244,6,17,0,0,32,4,23,35,252,8,18,8,148,
-    8,6,84,5,85,110,85,65,164,0,145,0,5,226,0,37,242,15,57,0,20,85,19,129,
-    2,129,16,80,85,255,19,163,16,5,38,4,38,149,24,37,132,117,4,85,85,161,
-    170,165,0,170,200,170,129,32,159,0,14,84,85,53,0,85,149,106,224,16,
-    170,169,53,106,193,1,170,214,212,87,0,128,2,166,64,154,5,64,186,154,
-    64,106,53,0,144,8,66,20,185,64,16,101,230,30,147,0,225,32,40,0,159,
-    17,161,31,0,0,224,7,160,51,123,233,95,255,130,2,104,114,8,18,10,16,
-    168,0,127,159,0,2,32,192,15,97,117,233,111,128,48,59,139,144,73,58,
-    4,0,80,58,16,252,7,0,160,16,1,8,84,85,1,0,153,0,80,85,48,5,0,4,162,
-    0,3,84,85,17,65,0,16,24,84,85,65,0,64,101,16,72,196,0,128,8,1,0,16,
-    8,221,84,128,2,242,7,8,4,16,146,7,8,162,4,16,64,161,0,9,64,5,50,223,
-    1,161,8,136,16,4,166,16,147,0,161,14,73,255,97,65,53,2,40,2,40,2,31,
-    40,0,64,85,18,7,27,177,6,255,19,5,27,193,31,17,39,19,39,239,19,39,19,
-    39,1,18,9,25,251,195,47,17,7,25,1,17,73,8,109,17,9,8,17,9,8,17,9,251,
-    8,17,5,218,63,159,0,1,135,63,21,8,109,17,69,8,17,5,8,17,5,187,8,17,
-    5,8,17,3,85,159,0,13,220,168,47,194,1,5,72,192,36,193,32,5,8,102,1,
-    80,98,69,8,4,128,2,24,110,8,16,32,24,9,4,235,95,151,0,63,40,0,32,232,
-    48,7,55,99,55,254,67,55,67,55,3,23,193,31,135,32,188,81,192,0,69,160,
-    0,159,0,6,129,4,1,105,65,170,106,120,90,85,169,169,170,153,56,128,2,
-    166,170,56,80,154,170,168,56,80,106,5,165,192,0,85,85,1,10,84,85,1,
-    0,145,0,8,192,0,0,40,0,20,0,80,85,5,0,193,4,162,0,195,2,84,85,17,0,
-    16,81,32,24,18,40,128,2,65,0,64,40,33,170,74,32,66,96,1,1,137,0,0,168,
-    169,170,0,2,1,137,16,242,7,168,166,170,0,2,4,132,16,81,16,168,154,170,
-    32,2,160,16,64,64,17,169,106,165,106,118,169,106,160,1,161,8,136,16,
-    4,166,16,195,2,219,16,116,101,64,21,36,0,161,14,108,40,0,81,40,0,81,
-    8,0,127,64,85,50,7,51,7,53,5,255,51,7,20,6,19,7,19,7,186,19,7,1,18,
-    9,17,9,2,247,0,196,47,17,39,1,25,24,2,182,24,1,169,24,1,169,24,1,254,
-    169,24,1,21,210,63,148,0,17,121,237,8,128,34,3,65,21,8,65,69,182,8,
-    65,165,56,1,165,24,1,238,165,24,1,19,85,159,0,13,227,83,195,33,172,
-    8,56,64,192,36,73,224,0,53,168,102,169,90,192,32,55,168,166,128,2,8,
-    103,168,154,16,8,169,106,113,129,48,13,197,31,32,0,168,170,129,92,231,
-    14,80,227,192,30,199,33,227,4,131,4,85,85,81,133,20,25,134,16,170,169,
-    86,225,1,118,170,166,155,128,2,24,170,154,32,24,165,160,16,69,128,1,
-    170,153,0,150,170,86,136,12,69,93,143,35,9,89,192,0,73,48,131,63,165,
-    0,106,255,84,101,128,2,82,5,16,224,46,3,0,129,48,105,85,1,10,84,85,
-    1,0,163,0,85,160,1,153,21,224,0,20,0,3,147,0,85,85,129,148,1,66,64,
-    85,85,85,64,0,7,1,106,0,170,170,106,32,128,4,6,242,7,42,1,170,170,42,
-    0,128,2,157,4,113,42,4,64,0,64,16,60,81,42,16,0,48,0,146,8,42,205,64,
-    0,48,1,0,165,16,131,16,20,174,6,147,0,16,10,64,224,15,3,21,219,0,161,
-    14,24,0,1,24,0,1,255,24,177,24,27,165,16,21,5,21,9,255,17,3,23,3,23,
-    3,23,3,255,23,3,25,3,23,3,23,10,63,162,0,85,85,25,1,25,33,9,254,17,
-    9,17,11,17,3,159,0,24,128,8,127,0,42,4,81,9,65,9,65,216,57,1,27,1,160,
-    10,31,26,42,0,237,170,192,0,23,128,32,86,25,0,90,191,25,0,106,25,5,
-    239,27,7,128,28,224,36,95,202,2,40,59,170,225,0,56,129,6,57,254,129,
-    2,57,1,26,225,48,240,46,146,0,144,40,112,85,21,213,1,111,1,129,4,86,
-    85,64,57,85,106,170,165,0,128,64,176,8,170,169,1,54,170,170,169,106,
-    85,170,166,20,54,170,170,166,128,2,154,54,170,96,170,154,0,154,8,106,
-    85,1,12,84,85,169,170,163,0,147,0,85,32,85,166,0,3,148,0,0,0,4,150,
-    0,85,85,154,10,3,168,0,10,0,0,170,10,85,85,106,128,2,3,104,2,0,0,106,
-    2,18,85,85,170,1,3,168,1,128,4,165,1,128,2,4,3,168,4,112,4,74,16,16,
-    3,168,16,16,16,16,145,64,3,169,64,193,1,169,170,86,183,164,16,147,0,
-    166,160,14,137,16,154,32,57,254,33,57,34,56,2,24,2,24,238,170,226,14,
-    25,165,16,170,129,12,48,5,221,20,36,5,40,128,2,33,5,104,219,96,33,5,
-    40,36,5,40,36,105,5,40,36,53,41,130,1,137,2,4,161,0,84,85,137,2,168,
-    129,12,2,68,4,0,49,85,85,4,2,20,67,0,16,50,85,85,16,0,1,194,161,14,
-    165,16,1,64,0,0,1,81,63,85,85,0,2,192,1,146,8,128,2,50,247,96,114,0,
-    18,32,64,161,0,0,8,18,0,64,5,0,115,84,85,176,129,240,3,168,160,14,35,
-    85,85,21,55,85,85,85,21,161,16,41,18,242,7,27,32,85,85,105,8,113,41,
-    8,111,65,41,8,65,41,56,1,19,149,208,12,154,0,162,2,165,0,128,240,5,
-    130,150,192,60,91,170,2,101,168,194,3,229,79,51,170,1,104,224,7,135,
-    16,0,4,130,2,49,102,0,16,114,6,0,64,105,183,17,8,149,194,0,167,13,40,
-    224,65,237,15,246,153,0,128,50,161,16,132,32,130,48,84,137,16,80,222,
-    80,105,80,64,9,67,244,94,144,0,152,130,208,9,84,85,135,28,130,78,2,
-    170,254,0,165,0,130,2,56,132,4,54,129,64,160,74,238,6,21,97,32,18,5,
-    17,32,192,66,5,129,16,85,85,1,12,84,85,169,170,165,0,145,0,85,34,85,
-    166,0,3,150,0,0,192,0,16,85,85,154,10,3,170,10,0,2,0,170,10,85,85,106,
-    10,5,38,0,0,106,128,2,170,1,5,128,4,181,1,96,4,37,16,4,16,16,172,37,
-    16,16,16,64,37,193,1,169,109,170,86,166,16,145,0,166,160,14,137,16,
-    154,255,160,12,89,65,89,2,24,2,24,255,2,24,227,14,25,167,16,19,37,21,
-    255,37,21,39,19,7,19,7,19,255,7,19,7,21,5,21,3,23,215,3,23,4,2,16,2,
-    163,15,3,255,21,5,21,5,21,53,7,19,220,7,17,4,2,163,0,192,5,128,14,170,
-    127,2,106,116,48,72,51,71,51,250,71,51,7,195,15,195,0,149,0,149,64,
-    16,169,85,4,84,165,0,21,84,85,101,85,20,64,91,162,10,117,2,208,128,
-    34,162,14,36,106,224,3,0,1,0,163,4,37,66,129,2,4,0,16,37,30,2,16,0,
-    64,21,2,136,16,147,0,126,170,2,22,225,11,192,28,111,6,192,3,161,16,
-    132,1,117,42,1,0,84,137,16,42,45,4,0,80,105,42,193,15,104,42,189,0,
-    25,129,226,0,135,32,195,31,135,14,42,227,193,14,160,64,165,0,130,2,
-    10,170,9,37,157,128,16,224,54,170,6,37,80,49,18,216,37,0,17,66,37,1,
-    85,85,1,10,84,85,169,170,165,0,170,192,0,21,85,85,166,170,3,150,128,
-    2,150,48,48,154,170,3,147,0,85,85,106,167,170,5,170,16,85,85,246,7,
-    145,8,254,49,37,82,48,37,82,48,38,3,85,105,170,106,169,170,86,166,16,
-    111,129,16,166,54,67,154,54,147,0,161,14,63,73,170,169,56,1,17,57,17,
-    127,57,106,7,35,7,35,5,37,255,5,37,23,3,24,2,28,195,30,255,8,23,37,
-    5,21,3,23,3,255,23,5,21,6,20,10,229,46,24,255,5,23,35,7,17,9,17,9,246,
-    19,7,20,6,20,6,5,20,238,18,5,19,9,169,130,30,179,8,159,0,10,131,104,
-    166,0,168,85,85,169,104,128,17,67,53,104,192,0,169,85,64,4,69,26,128,
-    2,5,168,4,167,47,176,14,153,135,16,124,170,106,16,74,131,32,159,0,46,
-    229,17,80,13,84,168,4,0,80,73,160,0,86,218,41,195,32,43,86,228,44,15,
-    8,72,54,52,64,0,0,176,32,7,41,217,1,85,6,64,84,0,4,0,4,239,111,10,129,
-    48,0,85,85,1,8,80,85,5,0,161,0,165,170,0,170,170,165,0,149,170,85,85,
-    129,0,152,0,21,0,85,85,16,0,133,3,19,85,85,64,0,5,96,164,192,0,128,
-    6,1,1,170,169,1,128,164,169,128,2,4,1,170,166,1,128,201,166,16,242,
-    7,170,154,1,128,154,128,16,210,7,170,106,149,106,170,106,27,128,106,
-    5,0,163,16,133,16,4,161,10,121,40,16,22,147,0,161,14,41,0,1,153,19,
-    5,0,4,19,5,0,16,159,19,5,0,64,19,7,19,7,255,25,1,21,5,23,3,19,7,255,
-    19,7,19,7,19,9,17,9,255,25,195,31,17,7,21,5,17,9,252,17,9,17,9,17,5,
-    199,45,128,123,170,160,192,0,233,63,159,0,2,197,47,85,176,20,182,7,
-    161,16,149,120,1,149,24,1,225,149,24,1,27,160,170,170,0,200,17,159,
-    0,14,231,79,0,85,9,170,169,204,106,192,32,7,170,166,128,2,8,170,204,
-    154,96,56,170,106,65,26,170,143,10,140,32,32,0,40,198,1,193,15,160,
-    6,170,143,36,0,22,170,192,95,170,24,170,32,170,170,24,170,32,170,24,
-    149,162,16,124,165,170,165,0,128,64,194,12,147,0,144,36,85,59,149,85,
-    21,245,1,78,129,4,80,192,0,255,131,63,165,0,193,30,57,65,57,128,2,224,
-    46,128,51,129,48,106,85,1,8,84,85,1,0,161,0,169,2,65,169,170,192,0,
-    0,85,85,4,0,83,3,84,160,1,84,128,2,80,0,3,52,147,0,85,85,148,1,245,
-    1,85,242,7,1,14,170,1,170,170,168,224,0,64,18,21,170,4,170,170,192,
-    0,4,48,16,58,161,0,170,16,48,32,0,64,113,109,170,64,0,16,1,128,13,137,
-    16,4,183,166,16,115,80,6,147,0,64,11,224,15,182,3,21,0,161,14,24,0,
-    1,24,127,0,64,67,23,67,23,69,21,255,5,21,10,194,15,1,23,3,23,255,3,
-    23,3,25,1,25,3,23,231,3,23,10,146,8,85,85,25,1,255,25,33,9,17,11,18,
-    8,17,199,3,159,0,24,0,0,170,0,149,8,160,12,243,8,114,8,98,42,169,2,
-    194,32,199,22,36,239,15,24,0,170,2,192,0,22,110,192,15,90,25,0,106,
-    25,141,32,129,48,35,86,85,169,143,12,8,2,170,170,192,22,127,202,2,160,
-    27,208,3,26,129,6,25,129,2,236,26,225,48,224,46,168,0,86,192,2,147,
-    0,5,240,84,208,0,193,0,79,5,129,4,89,85,1,170,85,216,7,170,146,8,166,
-    166,0,170,96,43,85,170,154,54,170,64,85,224,32,64,53,170,64,85
-};
-
-static MTB * mtb_KRPKR = NULL;
-
-void initMTB_KRPKR()
-{
-    mtb_KRPKR = new MTB ("KRPKR", 2, 86);
-    mtb_KRPKR->SetPackedData (mtbdata_KRPKR);
-    mtb_KRPKR->Add (A7, A8, C8, WHITE, 235);
-    mtb_KRPKR->Add (A7, A8, C7, WHITE, 248);
-    mtb_KRPKR->Add (A7, A8, D7, WHITE, 134);
-    mtb_KRPKR->Add (A7, A6, A8, WHITE, 226);
-    mtb_KRPKR->Add (A7, B6, A8, WHITE, 226);
-    mtb_KRPKR->Add (B7, B8, D7, WHITE, 24);
-    mtb_KRPKR->Add (B7, B8, D8, WHITE, 13);
-    mtb_KRPKR->Add (B7, B8, E7, WHITE, 19);
-    mtb_KRPKR->Add (B7, A6, B8, WHITE, 244);
-    mtb_KRPKR->Add (B7, A6, C7, WHITE, 135);
-    mtb_KRPKR->Add (B7, B6, B8, WHITE, 223);
-    mtb_KRPKR->Add (B7, C6, B8, WHITE, 226);
-    mtb_KRPKR->Add (B7, C6, A7, WHITE, 229);
-    mtb_KRPKR->Add (B7, C6, E7, WHITE, 24);
-    mtb_KRPKR->Add (C7, C8, A7, WHITE, 54);
-    mtb_KRPKR->Add (C7, C8, A6, WHITE, 51);
-    mtb_KRPKR->Add (C7, C8, E7, WHITE, 31);
-    mtb_KRPKR->Add (C7, C8, E6, WHITE, 31);
-    mtb_KRPKR->Add (C7, C8, F7, WHITE, 23);
-    mtb_KRPKR->Add (D7, D8, B7, WHITE, 83);
-    mtb_KRPKR->Add (D7, D8, C6, WHITE, 97);
-    mtb_KRPKR->Add (D7, D8, E6, WHITE, 67);
-    mtb_KRPKR->Add (D7, D8, F7, WHITE, 38);
-    mtb_KRPKR->Add (D7, C6, D8, WHITE, 247);
-    mtb_KRPKR->Add (A6, A7, C6, WHITE, 287);
-    mtb_KRPKR->Add (A6, A7, C7, WHITE, 244);
-    mtb_KRPKR->Add (A6, A7, C8, WHITE, 236);
-    mtb_KRPKR->Add (A6, A7, D7, WHITE, 216);
-    mtb_KRPKR->Add (A6, B6, A8, WHITE, 191);
-    mtb_KRPKR->Add (A6, B5, A7, WHITE, 283);
-    mtb_KRPKR->Add (B6, B7, D6, WHITE, 18);
-    mtb_KRPKR->Add (B6, B7, D7, WHITE, 13);
-    mtb_KRPKR->Add (B6, B7, D8, WHITE, 13);
-    mtb_KRPKR->Add (B6, B8, A6, WHITE, 287);
-    mtb_KRPKR->Add (B6, B8, C6, WHITE, 122);
-    mtb_KRPKR->Add (B6, A6, B8, WHITE, 175);
-    mtb_KRPKR->Add (B6, A6, C8, WHITE, 122);
-    mtb_KRPKR->Add (B6, C6, B8, WHITE, 197);
-    mtb_KRPKR->Add (C6, C7, A6, WHITE, 192);
-    mtb_KRPKR->Add (C6, C7, A7, WHITE, 123);
-    mtb_KRPKR->Add (C6, C7, E7, WHITE, 17);
-    mtb_KRPKR->Add (C6, B6, C8, WHITE, 128);
-    mtb_KRPKR->Add (C6, D6, C8, WHITE, 197);
-    mtb_KRPKR->Add (A7, A8, C8, BLACK, 80);
-    mtb_KRPKR->Add (A7, A8, C7, BLACK, 181);
-    mtb_KRPKR->Add (A7, A8, D7, BLACK, 75);
-    mtb_KRPKR->Add (A7, A6, A8, BLACK, 282);
-    mtb_KRPKR->Add (A7, B6, A8, BLACK, 249);
-    mtb_KRPKR->Add (B7, B8, D7, BLACK, 266);
-    mtb_KRPKR->Add (B7, B8, D8, BLACK, 251);
-    mtb_KRPKR->Add (B7, B8, E7, BLACK, 211);
-    mtb_KRPKR->Add (B7, A6, B8, BLACK, 291);
-    mtb_KRPKR->Add (B7, A6, C7, BLACK, 350);
-    mtb_KRPKR->Add (B7, B6, B8, BLACK, 275);
-    mtb_KRPKR->Add (B7, C6, B8, BLACK, 296);
-    mtb_KRPKR->Add (B7, C6, A7, BLACK, 305);
-    mtb_KRPKR->Add (B7, C6, E7, BLACK, 134);
-    mtb_KRPKR->Add (C7, C8, A7, BLACK, 281);
-    mtb_KRPKR->Add (C7, C8, A6, BLACK, 265);
-    mtb_KRPKR->Add (C7, C8, E7, BLACK, 281);
-    mtb_KRPKR->Add (C7, C8, E6, BLACK, 245);
-    mtb_KRPKR->Add (C7, C8, F7, BLACK, 217);
-    mtb_KRPKR->Add (D7, D8, B7, BLACK, 293);
-    mtb_KRPKR->Add (D7, D8, C6, BLACK, 332);
-    mtb_KRPKR->Add (D7, D8, E6, BLACK, 345);
-    mtb_KRPKR->Add (D7, D8, F7, BLACK, 280);
-    mtb_KRPKR->Add (D7, C6, D8, BLACK, 306);
-    mtb_KRPKR->Add (A6, A7, C6, BLACK, 267);
-    mtb_KRPKR->Add (A6, A7, C7, BLACK, 254);
-    mtb_KRPKR->Add (A6, A7, C8, BLACK, 265);
-    mtb_KRPKR->Add (A6, A7, D7, BLACK, 215);
-    mtb_KRPKR->Add (A6, B6, A8, BLACK, 268);
-    mtb_KRPKR->Add (A6, B5, A7, BLACK, 263);
-    mtb_KRPKR->Add (B6, B7, D6, BLACK, 264);
-    mtb_KRPKR->Add (B6, B7, D7, BLACK, 236);
-    mtb_KRPKR->Add (B6, B7, D8, BLACK, 244);
-    mtb_KRPKR->Add (B6, B8, A6, BLACK, 265);
-    mtb_KRPKR->Add (B6, B8, C6, BLACK, 286);
-    mtb_KRPKR->Add (B6, A6, B8, BLACK, 264);
-    mtb_KRPKR->Add (B6, A6, C8, BLACK, 331);
-    mtb_KRPKR->Add (B6, C6, B8, BLACK, 276);
-    mtb_KRPKR->Add (C6, C7, A6, BLACK, 387);
-    mtb_KRPKR->Add (C6, C7, A7, BLACK, 305);
-    mtb_KRPKR->Add (C6, C7, E7, BLACK, 246);
-    mtb_KRPKR->Add (C6, B6, C8, BLACK, 305);
-    mtb_KRPKR->Add (C6, D6, C8, BLACK, 280);
-}
-
-//////////////////////////////////////////////////////////////////////
-//
-// KPPKP
-
-static const byte mtbdata_KPPKP[1992] = {
-    1,32,80,85,147,0,64,85,65,85,244,69,224,0,5,143,2,255,63,77,85,63,45,
-    0,188,80,143,16,44,69,194,0,137,12,47,0,139,4,1,159,84,107,1,80,143,
-    32,44,31,2,229,83,231,6,205,57,137,2,1,5,80,15,62,93,0,236,29,192,16,
-    133,10,1,0,150,22,15,96,65,136,85,15,10,106,85,105,95,3,1,35,80,85,
-    149,0,64,85,65,192,0,227,7,143,2,255,63,205,15,0,65,85,81,60,148,139,
-    6,59,0,80,143,16,48,84,143,18,11,85,201,85,231,85,81,1,84,27,1,80,141,
-    143,32,60,79,14,1,84,0,152,22,17,0,144,152,20,31,94,64,85,31,34,1,32,
-    80,85,147,0,5,85,21,85,58,1,85,0,228,1,143,2,255,47,79,69,44,151,85,
-    47,29,5,80,44,64,143,16,28,15,18,197,141,6,123,60,64,21,64,47,32,81,
-    85,60,84,60,64,43,84,44,80,82,12,64,143,32,60,85,45,84,0,150,20,159,
-    69,81,80,0,245,105,129,12,1,150,22,64,15,97,85,15,18,1,32,80,85,145,
-    0,1,84,0,84,155,1,160,0,0,84,242,1,143,2,96,85,63,45,31,143,16,45,69,
-    85,64,192,10,224,10,35,144,0,171,63,12,142,10,80,44,64,44,0,143,16,
-    12,85,31,2,65,160,0,64,230,1,0,111,14,80,120,44,64,44,162,16,47,4,195,
-    64,85,85,255,81,0,159,0,6,161,15,62,135,2,49,160,16,253,58,96,58,0,
-    143,32,14,253,33,133,18,81,107,164,13,65,198,35,131,8,1,21,85,18,255,
-    224,48,134,12,17,48,134,2,164,16,143,48,24,139,17,254,197,36,85,128,
-    66,162,8,70,192,32,225,32,131,2,31,9,84,10,84,66,148,56,34,146,16,253,
-    40,60,197,27,47,2,1,41,1,65,240,162,16,8,226,48,138,12,54,41,84,42,
-    112,84,41,98,74,32,1,32,64,85,145,0,0,84,1,84,59,5,84,0,160,0,242,1,
-    143,2,91,69,143,16,109,75,161,16,5,140,4,21,85,49,1,16,74,51,85,31,
-    13,0,80,28,64,28,70,0,0,143,18,11,5,84,21,26,159,0,2,241,5,196,0,134,
-    16,40,16,80,5,84,191,129,25,22,64,16,224,4,22,160,16,15,8,126,159,0,
-    4,81,140,2,161,53,7,225,36,53,34,103,80,69,230,9,18,64,1,135,4,3,153,
-    31,29,15,24,64,85,17,135,2,0,85,123,1,17,211,91,131,14,164,16,15,70,
-    65,225,49,224,86,32,224,37,23,42,84,41,84,255,5,146,16,136,2,31,63,
-    7,21,117,132,14,16,8,169,84,37,2,1,32,80,85,145,0,5,85,21,84,57,1,84,
-    0,160,0,242,1,129,2,85,17,143,18,55,85,85,69,228,1,53,193,0,59,52,84,
-    21,161,7,228,7,19,21,128,4,3,165,4,5,80,21,80,5,84,161,0,111,23,1,113,
-    4,69,140,16,161,13,57,255,1,197,0,51,227,15,134,12,21,37,20,255,8,18,
-    15,22,129,7,55,149,0,135,32,231,4,217,133,10,242,0,71,64,41,128,2,0,
-    0,255,24,34,138,16,159,0,18,31,8,84,200,9,0,135,160,16,72,0,0,0,4,11,
-    51,245,10,211,24,159,0,34,132,32,91,64,60,0,122,60,0,47,28,207,33,10,
-    129,66,138,2,84,60,91,80,21,33,0,25,32,0,47,56,247,233,45,132,96,168,
-    16,2,59,80,224,15,54,254,141,10,143,2,30,63,2,201,32,2,168,16,132,96,
-    0,1,42,160,170,145,0,0,160,0,2,16,213,0,226,1,143,2,224,10,76,42,143,
-    8,14,168,81,76,160,76,128,143,16,28,170,170,138,106,12,170,12,30,168,
-    28,160,28,191,128,143,32,28,42,192,0,27,159,0,0,28,160,16,253,26,64,
-    26,64,143,32,24,47,20,133,6,128,255,0,80,224,48,134,10,81,96,134,2,
-    1,151,16,15,70,128,170,2,168,120,1,43,243,105,170,21,160,0,22,1,128,
-    136,2,222,136,14,77,15,34,130,160,16,58,0,26,174,170,23,85,160,0,86,
-    160,2,133,8,135,2,1,35,160,170,147,0,2,170,0,194,0,213,5,143,2,255,
-    63,77,10,60,42,60,170,86,141,8,168,60,160,12,128,143,16,28,159,0,2,
-    42,130,170,128,47,10,168,44,160,44,175,128,47,32,162,143,18,11,170,
-    8,34,192,32,252,42,48,42,48,143,32,42,159,0,0,135,6,162,191,170,66,
-    168,136,10,49,245,105,129,2,49,175,150,22,15,14,162,192,27,138,170,
-    33,182,9,120,240,139,6,12,194,32,106,15,17,1,32,160,170,145,0,0,168,
-    10,168,56,0,168,2,192,0,242,1,143,2,79,21,168,53,26,168,21,143,14,89,
-    143,16,14,10,140,4,42,83,143,12,30,160,60,128,12,0,0,95,12,231,160,
-    16,105,49,138,8,170,170,44,193,0,95,42,160,28,128,28,80,31,8,159,0,
-    14,215,16,139,14,31,2,160,28,128,28,194,32,30,31,10,138,170,136,31,
-    5,225,84,194,16,132,2,128,128,140,18,5,170,10,160,5,160,195,2,64,134,
-    12,128,21,128,2,48,61,22,0,21,162,16,143,32,20,227,98,151,0,128,239,
-    192,19,139,16,96,75,170,66,148,7,130,12,254,65,145,21,85,178,32,47,
-    4,129,10,180,5,79,3,60,149,170,154,192,0,103,19,136,58,170,210,26,32,
-    60,168,60,168,2,15,1,1,0,160,170,170,170,0,160,2,120,160,0,160,0,225,
-    0,240,1,143,2,79,5,160,37,6,160,5,56,10,168,139,4,42,107,60,0,63,14,
-    141,14,128,60,0,225,1,51,136,16,0,4,59,160,0,0,168,224,10,238,192,0,
-    17,160,16,10,0,194,14,8,143,16,16,236,128,92,18,88,0,9,159,0,4,0,111,
-    170,2,162,15,15,3,168,15,15,112,10,212,96,26,134,2,128,160,0,138,224,
-    2,138,124,170,130,160,1,159,0,2,194,68,227,14,18,128,255,170,81,137,
-    36,142,52,17,226,48,132,20,164,16,199,134,64,81,134,2,160,170,162,164,
-    13,133,1,124,155,0,128,160,19,32,164,33,164,35,8,5,63,168,6,168,137,
-    42,130,76,1,192,32,134,2,219,164,16,15,2,143,30,0,162,194,30,105,128,
-    196,15,224,167,51,92,160,50,5,21,160,22,160,255,21,160,16,225,32,136,
-    2,146,14,95,2,129,14,229,48,199,89,139,16,39,5,170,6,192,0,224,16,208,
-    59,226,82,136,12,160,53,85,160,86,88,160,85,160,0,22,132,14,137,2,1,
-    0,32,170,10,170,0,160,0,108,160,2,160,0,129,1,170,144,0,143,2,78,21,
-    16,160,21,160,22,52,10,170,42,85,170,10,140,4,42,143,14,46,128,60,0,
-    81,140,16,0,7,170,128,32,10,160,10,58,11,168,42,74,161,12,138,10,168,
-    15,15,231,128,12,162,16,8,0,20,72,1,207,17,135,14,159,0,1,168,2,162,
-    0,36,228,1,255,52,20,1,22,160,16,26,112,10,139,56,159,0,22,34,170,2,
-    130,1,128,16,25,193,15,138,168,10,3,132,36,2,162,95,4,168,2,128,129,
-    21,132,20,102,132,64,223,1,134,2,159,0,30,128,160,0,163,31,228,51,48,
-    135,231,31,128,12,168,21,168,22,129,16,160,16,238,134,78,36,143,2,2,
-    31,34,130,88,18,32,212,162,192,0,6,168,2,128,81,85,26,160,85,160,86,
-    164,14,95,2,149,160,0,215,150,136,10,95,30,21,0,22,160,18,161,16,242,
-    136,16,224,48,106,114,36,161,86,48,128,84,32,1,0,160,170,170,170,0,
-    168,10,22,160,0,160,2,192,0,0,241,1,129,2,18,168,0,168,2,0,0,168,41,
-    49,34,170,2,17,36,10,168,138,160,0,34,160,129,8,10,160,42,162,208,42,
-    19,4,10,132,4,21,128,26,6,128,21,128,2,128,2,192,12,9,203,0,160,0,1,
-    10,170,139,16,42,160,2,173,26,1,170,225,15,34,24,81,138,151,24,81,42,
-    162,66,128,2,128,4,62,4,0,16,129,14,160,12,4,35,15,8,108,168,170,139,
-    32,192,0,160,226,15,53,170,44,170,128,170,60,170,81,52,128,102,10,128,
-    193,20,136,16,0,21,2,54,255,0,57,159,0,14,49,19,132,2,224,10,19,237,
-    36,22,36,1,128,229,15,132,14,0,183,161,28,8,0,17,9,138,192,26,255,17,
-    4,245,194,68,136,2,225,4,163,35,147,0,5,128,40,5,172,196,15,130,12,
-    168,129,60,10,160,16,132,2,160,190,26,129,14,0,136,64,50,143,32,6,184,
-    9,68,248,138,138,16,130,66,41,160,29,42,160,26,255,160,34,228,11,130,
-    78,33,160,16,137,2,49,254,133,12,153,17,79,14,128,30,192,0,140,16,192,
-    104,58,255,160,49,163,16,132,94,49,242,24,133,2,145,0,56,50,170,26,
-    192,0,136,24,63,30,168,26,127,168,21,230,61,133,16,224,45,43,162,16,
-    138,112,0,144,0
-};
-
-static MTB * mtb_KPPKP = NULL;
-
-void initMTB_KPPKP()
-{
-    mtb_KPPKP = new MTB ("KPPKP", 2, 12);
-    mtb_KPPKP->SetPackedData (mtbdata_KPPKP);
-    mtb_KPPKP->Add (A6, B5, A7, WHITE, 86);
-    mtb_KPPKP->Add (A5, B6, A7, WHITE, 70);
-    mtb_KPPKP->Add (A5, B5, A7, WHITE, 86);
-    mtb_KPPKP->Add (A4, B5, A7, WHITE, 186);
-    mtb_KPPKP->Add (A5, B4, A6, WHITE, 163);
-    mtb_KPPKP->Add (A4, B4, A6, WHITE, 195);
-    mtb_KPPKP->Add (A6, B5, A7, BLACK, 132);
-    mtb_KPPKP->Add (A5, B6, A7, BLACK, 109);
-    mtb_KPPKP->Add (A5, B5, A7, BLACK, 182);
-    mtb_KPPKP->Add (A4, B5, A7, BLACK, 248);
-    mtb_KPPKP->Add (A5, B4, A6, BLACK, 226);
-    mtb_KPPKP->Add (A4, B4, A6, BLACK, 309);
-}
-
-
-//////////////////////////////////////////////////////////////////////
-
-
-static void initMTBs (void)
-{
-    initMTB_KQK();
-    initMTB_KRK();
-    initMTB_KPK();
-    initMTB_KQKQ();
-    initMTB_KQKP();
-    initMTB_KRKP();
-    initMTB_KPKP();
-    initMTB_KRPKR();
-    initMTB_KPPKP();
-}
-
-
-#endif // SCID_MTBDATA_H
-
-//////////////////////////////////////////////////////////////////////
-//  EOF: mtbdata.h
-//////////////////////////////////////////////////////////////////////
diff -pruN 1:4.7.0+dfsg1-2/src/namebase.h 1:4.7.4+dfsg1-2/src/namebase.h
--- 1:4.7.0+dfsg1-2/src/namebase.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/namebase.h	2022-07-09 05:14:42.000000000 +0000
@@ -19,20 +19,31 @@
 #ifndef SCID_NAMEBASE_H
 #define SCID_NAMEBASE_H
 
-#include "common.h"
+#include "index.h"
+#include "indexentry.h"
 #include "misc.h"
 #include <algorithm>
+#include <array>
 #include <map>
 #include <memory>
 #include <vector>
 
+using nameT = unsigned;
+enum {
+	NAME_PLAYER,
+	NAME_EVENT,
+	NAME_SITE,
+	NAME_ROUND,
+	NUM_NAME_TYPES,
+	NAME_INVALID = 99
+};
+
 /**
  * This class stores the database's names (players, events, sites and rounds).
  * Assigns a idNumberT (which will be used as reference) to each name.
  */
 class NameBase {
-	std::vector<std::unique_ptr<const char[]> > names_[NUM_NAME_TYPES];
-	std::vector<eloT> eloV_;
+	std::vector<std::unique_ptr<const char[]>> names_[NUM_NAME_TYPES];
 	struct idxCmp {
 		bool operator()(const char* str1, const char* str2) const {
 			// *** Compatibility ***
@@ -49,25 +60,13 @@ class NameBase {
 			if (*str1 == *str2)
 				return strCompare(str1, str2) < 0;
 
-			return static_cast<uint>(*str1) < static_cast<uint>(*str2);
+			return static_cast<uint32_t>(*str1) < static_cast<uint32_t>(*str2);
 		}
 	};
 	std::map<const char*, idNumberT, idxCmp> idx_[NUM_NAME_TYPES];
 
 public:
 	/**
-	 * A NameBase stores the max ELO for each player. This functions updates
-	 * the max ELO of a player if it's greater than the previous one.
-	 * @param id:  a valid idNumberT corresponding to a NAME_PLAYER name.
-	 * @param elo: the ELO.
-	 */
-	void AddElo(idNumberT id, eloT elo) {
-		ASSERT(id < GetNumNames(NAME_PLAYER));
-		if (elo > eloV_[id])
-			eloV_[id] = elo;
-	}
-
-	/**
 	 * Add a name (string) to the NameBase.
 	 * If the name already exists the corresponding ID is returned.
 	 * @param nt:      @e nameT type of the name to add.
@@ -100,35 +99,34 @@ public:
 		names_[nt].emplace_back(buf);
 		idx_[nt].emplace_hint(exists, buf, newID);
 
-		if (nt == NAME_PLAYER)
-			eloV_.push_back(0);
-
 		return std::make_pair(OK, newID);
 	}
 
-	/**
-	 * Frees memory, leaving the object empty.
-	 */
-	void Clear() { *this = NameBase(); }
+	/// DEPRECATED
+	/// Add a name (string) and its associated id to the NameBase.
+	/// Return false if the name or the id already exists: the NameBase object
+	/// is then no longer valid and should be destroyed.
+	/// The caller should also ensure that before invoking any other object's
+	/// function none of names_[nt] == nullptr.
+	bool insert(const char* name, size_t nameLen, nameT nt, idNumberT id) {
+		if (id >= names_[nt].size())
+			names_[nt].resize(id + size_t{1});
 
-	/**
-	 * @returns references to the NameBase's containers.
-	 * (must be used only to read names from files)
-	 */
-	std::tuple<decltype(idx_) &, decltype(names_) &, decltype(eloV_) &>
-	getData() {
-		return std::tuple<decltype(idx_)&, decltype(names_)&, decltype(eloV_)&>(
-		    idx_, names_, eloV_);
+		if (names_[nt][id]) // A name with the same ID already exists
+			return false;
+
+		char* buf = new char[nameLen + 1];
+		std::copy_n(name, nameLen, buf);
+		buf[nameLen] = '\0';
+		names_[nt][id].reset(buf);
+		auto it = idx_[nt].emplace_hint(idx_[nt].end(), buf, id);
+		return it->second == id; // Check that the name doesn't already exists
 	}
 
 	/**
-	 * @param id: a valid idNumberT corresponding to a NAME_PLAYER name.
-	 * @returns the max ELO of a player.
+	 * Frees memory, leaving the object empty.
 	 */
-	eloT GetElo(idNumberT id) const {
-		ASSERT(id < GetNumNames(NAME_PLAYER));
-		return eloV_[id];
-	}
+	void Clear() { *this = NameBase(); }
 
 	/**
 	 * Get the first few matches of a name prefix.
@@ -168,7 +166,7 @@ public:
 	 * @returns a reference to a container with all the names and IDs (given as
 	 * std::pair<const char*, idNumberT>).
 	 */
-	const decltype(idx_) & getNames() const { return idx_; }
+	const decltype(idx_)& getNames() const { return idx_; }
 
 	/**
 	 * @param nt: a valid @e nameT type.
@@ -205,14 +203,34 @@ public:
 	 */
 	std::vector<uint32_t> generateHashMap(nameT nt) const {
 		std::vector<uint32_t> res(names_[nt].size());
-		std::transform(names_[nt].begin(), names_[nt].end(), res.begin(),
-		               [](const std::unique_ptr<const char[]>& name) {
-			               return strStartHash(name.get());
-		               });
+		std::transform(
+		    names_[nt].begin(), names_[nt].end(), res.begin(),
+		    [](auto const& name) { return strStartHash(name.get()); });
 		return res;
 	}
 
 	/**
+	 * Counts how many times each name is used.
+	 * @returns an array of std::vectors containing the count of each name.
+	 */
+	std::array<std::vector<int>, NUM_NAME_TYPES>
+	calcNameFreq(Index const& idx) const {
+		std::array<std::vector<int>, NUM_NAME_TYPES> resVec;
+		for (nameT n = NAME_PLAYER; n < NUM_NAME_TYPES; n++) {
+			resVec[n].resize(GetNumNames(n), 0);
+		}
+		for (gamenumT i = 0, n = idx.GetNumGames(); i < n; i++) {
+			const IndexEntry* ie = idx.GetEntry(i);
+			resVec[NAME_PLAYER][ie->GetWhite()] += 1;
+			resVec[NAME_PLAYER][ie->GetBlack()] += 1;
+			resVec[NAME_EVENT][ie->GetEvent()] += 1;
+			resVec[NAME_SITE][ie->GetSite()] += 1;
+			resVec[NAME_ROUND][ie->GetRound()] += 1;
+		}
+		return resVec;
+	}
+
+	/**
 	 * Validate a @e nameT type.
 	 * @param nt: @e nameT type to be validated.
 	 * @returns true if @e nt is valid.
@@ -249,4 +267,61 @@ public:
 	}
 };
 
+/// The Seven Tag Roster defined in the PGN standard is stored in the
+/// IndexEntry, but 5 are indexes that refer to a NameBase object.
+/// This helper struct stores the referred values.
+struct TagRoster {
+	const char* event;
+	const char* site;
+	const char* round;
+	const char* white;
+	const char* black;
+
+	template <typename TEntry>
+	static TagRoster make(TEntry const& ie, NameBase const& nb) {
+		TagRoster res;
+		res.event = nb.GetName(NAME_EVENT, ie.GetEvent());
+		res.site = nb.GetName(NAME_SITE, ie.GetSite());
+		res.white = nb.GetName(NAME_PLAYER, ie.GetWhite());
+		res.black = nb.GetName(NAME_PLAYER, ie.GetBlack());
+		res.round = nb.GetName(NAME_ROUND, ie.GetRound());
+		return res;
+	}
+
+	template <typename TEntry, typename Fn>
+	auto map(TEntry& dest, Fn getID) const {
+		{
+			auto [err, id] = getID(NAME_EVENT, event);
+			if (err)
+				return err;
+			dest.SetEvent(id);
+		}
+		{
+			auto [err, id] = getID(NAME_SITE, site);
+			if (err)
+				return err;
+			dest.SetSite(id);
+		}
+		{
+			auto [err, id] = getID(NAME_ROUND, round);
+			if (err)
+				return err;
+			dest.SetRound(id);
+		}
+		{
+			auto [err, id] = getID(NAME_PLAYER, white);
+			if (err)
+				return err;
+			dest.SetWhite(id);
+		}
+		{
+			auto [err, id] = getID(NAME_PLAYER, black);
+			if (!err)
+				dest.SetBlack(id);
+
+			return err;
+		}
+	}
+};
+
 #endif // SCID_NAMEBASE_H
diff -pruN 1:4.7.0+dfsg1-2/src/optable.cpp 1:4.7.4+dfsg1-2/src/optable.cpp
--- 1:4.7.0+dfsg1-2/src/optable.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/optable.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -16,6 +16,7 @@
 #include "crosstab.h"
 #include "dstring.h"
 #include "pbook.h"
+#include <algorithm>
 
 uint
 endgameTheme (matSigT msig)
@@ -174,7 +175,7 @@ OpLine::Init (Game * g, const IndexEntry
     // Now set positional themes:
     uint maxThemePly = maxThemeMoveNumber * 2;
     for (i=0; i < NUM_POSTHEMES; i++) { Theme[i] = 0; }
-    g->MoveToPly (0);
+    g->MoveToStart();
     for (i=0; i < maxThemePly; i++) {
         if (g->MoveForward() != OK) { break; }
         SetPositionalThemes (g->GetCurrentPos());
@@ -439,11 +440,15 @@ OpLine::PrintSummary (DString * dstr, ui
     if (BlackElo > 0) { dstr->Append (preElo, BlackElo, postElo); }
     dstr->Append (", ", Site, " ");
     if (fullDate) {
-        char dateStr [16];
+        char dateStr[16] = {};
         date_DecodeToString (Date, dateStr);
         // Remove any unknown date fields:
-        char * s = (char *) strFirstChar (dateStr+4, '?');
-        if (s != NULL) { s--; *s = 0; }
+        auto s_end = dateStr + 16;
+        auto s = std::find(dateStr + 4, s_end, '?');
+        if (s != s_end) {
+            s--;
+            *s = 0;
+        }
         dstr->Append (dateStr);
     } else {
         dstr->Append (date_GetYear (Date));
@@ -496,8 +501,8 @@ OpTable::Init (const char * type, Game *
         }
         if (ebook != NULL && ECOstr_.empty()) {
             auto eco = ebook->findECOstr(g->GetCurrentPos());
-            if (eco.first)
-                ECOstr_.append(eco.first, eco.second);
+            if (!eco.empty())
+                ECOstr_.append(eco);
         }
         g->MoveBackup();
         simpleMoveT * sm = g->GetCurrentMove();
@@ -2052,7 +2057,7 @@ OpTable::PopularMoveOrders (DString * ds
 
     for (uint i=0; i < count; i++) {
         if (i == NumMoveOrders) { break; }
-        char tempStr [10];
+        char tempStr [16];
         sprintf (tempStr, "%2u", i+1);
         dstr->Append (preNum, tempStr, postNum);
         if (Format == OPTABLE_CText) {
diff -pruN 1:4.7.0+dfsg1-2/src/pbook.cpp 1:4.7.4+dfsg1-2/src/pbook.cpp
--- 1:4.7.0+dfsg1-2/src/pbook.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/pbook.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -26,7 +26,7 @@
 
 namespace {
 
-inline const char *
+std::string_view
 epd_findOpcode (const char * epdStr, const char * opcode)
 {
     const char * s = epdStr;
@@ -40,7 +40,7 @@ epd_findOpcode (const char * epdStr, con
         }
         while (*s != '\n'  &&  *s != 0) { s++; }
     }
-    return NULL;
+    return {};
 }
 
 
@@ -67,7 +67,7 @@ errorT ReadLine(Position& pos, const cha
 		if (err != OK)
 			return err;
 
-		pos.DoSimpleMove(&sm);
+		pos.DoSimpleMove(sm);
 	}
 }
 
@@ -75,60 +75,51 @@ errorT ReadLine(Position& pos, const cha
 
 } // namespace
 
-std::pair<const char*, const char*> PBook::findECOstr(Position* pos) const {
-	auto range = pos_.equal_range(pos->HashValue());
-	if (range.first == pos_.end())
-		return std::make_pair(nullptr, nullptr);
+std::string_view PBook::findECOstr(Position* pos) const {
+	auto [it, end] = pos_.equal_range(pos->HashValue());
+	if (it == end)
+		return {};
 
 	char cboard[36];
 	pos->PrintCompactStr(cboard);
-	auto it = std::find_if(range.first, range.second,
-	                 [&](const std::pair<const unsigned, bookDataT>& data) {
-		                 return std::equal(cboard, cboard + 36,
-		                                   data.second.compactStr.get());
-	                 });
-	if (it == range.second)
-		return std::make_pair(nullptr, nullptr);
-
-	const char* end = NULL;
-	const char* begin = epd_findOpcode(it->second.comment.get(), "eco");
-	if (begin != NULL) {
-		end = begin;
-		while (*end != '\0' && *end != '\n') {
-			++end;
-		}
-	}
-	return std::make_pair(begin, end);
+	it = std::find_if(it, end, [&](const auto& data) {
+		return std::equal(cboard, cboard + 36, data.second.compactStr.get());
+	});
+	if (it == end)
+		return {};
+
+	auto res = epd_findOpcode(it->second.comment.get(), "eco");
+	return res.substr(0, res.find('\n'));
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // PBook::EcoSummary():
 //    Produce a summary from the PBook for the specified ECO code prefix.
-std::string PBook::EcoSummary(const char* ecoPrefix) const {
-    std::string dstr;
-    uint depth = strLength (ecoPrefix);
-    const char * prevEcoStr = "";
-    for (const char* comment : comments_) {
-        const char * ecoStr = epd_findOpcode (comment, "eco");
-        if (ecoStr != NULL  &&  strIsPrefix (ecoPrefix, ecoStr)) {
-            if (depth < 3  &&  strPrefix (ecoStr, prevEcoStr) >= depth+1) {
-                continue;
-            }
-            prevEcoStr = ecoStr;
-            while (*ecoStr != '\n'  &&  *ecoStr != 0) {
-                dstr.push_back(*ecoStr);
-                ecoStr++;
-            }
-            dstr.append("  ");
-            const char* movesStr = epd_findOpcode(comment, "moves");
-            while (*movesStr != '\n'  &&  *movesStr != 0) {
-                dstr.push_back(*movesStr);
-                movesStr++;
-            }
-            dstr.push_back('\n');
-        }
-    }
-    return dstr;
+std::string PBook::EcoSummary(const std::string_view prefix) const {
+	auto res = std::string();
+	auto prevEco = std::string_view();
+	for (const char* comment : comments_) {
+		const auto eco = epd_findOpcode(comment, "eco");
+		if (eco.empty() || eco.substr(0, prefix.size()) != prefix)
+			continue;
+
+		if (prefix.size() < 3) {
+			const auto common = std::mismatch(eco.begin(), eco.end(),
+			                                  prevEco.begin(), prevEco.end());
+			const size_t nChars = std::distance(eco.begin(), common.first);
+			if (nChars > prefix.size())
+				continue;
+
+			prevEco = eco;
+		}
+
+		res.append(eco.substr(0, eco.find('\n')));
+		res.append("  ");
+		const auto moves = epd_findOpcode(comment, "moves");
+		res.append(moves.substr(0, moves.find('\n')));
+		res.push_back('\n');
+	}
+	return res;
 }
 
 std::pair<errorT, std::unique_ptr<PBook> >
diff -pruN 1:4.7.0+dfsg1-2/src/pbook.h 1:4.7.4+dfsg1-2/src/pbook.h
--- 1:4.7.0+dfsg1-2/src/pbook.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/pbook.h	2022-07-09 05:14:42.000000000 +0000
@@ -26,6 +26,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 class Position;
@@ -64,12 +65,9 @@ public:
 	/**
 	 * Retrieve an ECO string containing the ECO code and the mnemonic name.
 	 * @param pos: the position to search for.
-	 * @returns
-	 * - if the position is found, a @e std::pair with the range iterators for
-	 *   the string (string_view).
-	 * - a @e std::pair containing nullptr otherwise.
+	 * @returns an empty string_view if the position is not found
 	 */
-	std::pair<const char*, const char*> findECOstr(Position* pos) const;
+	std::string_view findECOstr(Position* pos) const;
 
 	/**
 	 * Retrieve the ECO code of a position.
@@ -78,17 +76,15 @@ public:
 	 */
 	ecoT findECO(Position* pos) const {
 		auto it = findECOstr(pos);
-		if (!it.first)
+		if (it.empty())
 			return ECO_None;
 
-		char buf[7] = {0};
-		std::copy_n(it.first,
-		            std::min(ptrdiff_t(6), std::distance(it.first, it.second)),
-		            buf);
+		char buf[8] = {0};
+		it.copy(buf, 6);
 		return eco_FromString(buf);
 	}
 
-	std::string EcoSummary(const char* ecoPrefix) const;
+	std::string EcoSummary(std::string_view ecoPrefix) const;
 
 	unsigned GetLineNumber() const { return LineCount; }
 	unsigned FewestPieces() const { return LeastMaterial; }
diff -pruN 1:4.7.0+dfsg1-2/src/pgn_lexer.h 1:4.7.4+dfsg1-2/src/pgn_lexer.h
--- 1:4.7.0+dfsg1-2/src/pgn_lexer.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/pgn_lexer.h	2022-07-09 05:14:42.000000000 +0000
@@ -24,8 +24,8 @@
  * Split input into PGN tokens and dispatch them to a "visiting" parser.
  */
 
-#ifndef _PGN_LEXER_H
-#define _PGN_LEXER_H
+#ifndef PGN_LEXER_H
+#define PGN_LEXER_H
 
 #include <algorithm>
 #include <cassert>
@@ -107,7 +107,7 @@ inline bool is_PGNdigit(unsigned char ch
  * @returns true if the character is a white space, false otherwise.
  */
 inline bool is_PGNwhitespace(unsigned char ch) {
-	return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\v');
+	return (ch == ' ' || ch == '\r' || ch == '\t' || ch == '\v' || ch == '\n');
 }
 
 /**
@@ -488,4 +488,4 @@ template <typename TView> std::size_t tr
 
 } // namespace pgn
 
-#endif // _PGN_LEXER_H
+#endif // PGN_LEXER_H
diff -pruN 1:4.7.0+dfsg1-2/src/pgnparse.h 1:4.7.4+dfsg1-2/src/pgnparse.h
--- 1:4.7.0+dfsg1-2/src/pgnparse.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/pgnparse.h	2022-07-09 05:14:42.000000000 +0000
@@ -24,7 +24,6 @@
 #ifndef SCID_PGNPARSE_H
 #define SCID_PGNPARSE_H
 
-#include "common.h"
 #include "game.h"
 #include "pgn_lexer.h"
 #include <string>
@@ -152,7 +151,7 @@ public:
 				return logFatalErr("Failed to parse the move: ", tok);
 			}
 		}
-		return (game.AddMove(&sm) == OK)
+		return (game.AddMove(sm) == OK)
 		           ? true
 		           : logFatalErr("Failed to add the move: ", tok);
 	}
diff -pruN 1:4.7.0+dfsg1-2/src/polyglot/book.cpp 1:4.7.4+dfsg1-2/src/polyglot/book.cpp
--- 1:4.7.0+dfsg1-2/src/polyglot/book.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/polyglot/book.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -1,790 +1,676 @@
-
-// book.cpp
-
-// includes
-
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "board.h"
-#include "book.h"
-#include "move.h"
-#include "move_legal.h"
-#include "move_gen.h"
-#include "move_do.h"
-#include "san.h"
-#include "util.h"
-#include "list.h"
-
-// types
-
-struct entry_t {
-   uint64 key;
-   uint16 move;
-   uint16 count;
-   uint16 n;
-   uint16 sum;
-};
-
-// variables
-#ifdef WINCE
-static Tcl_Channel BookFile[MaxBook];
-#else
-static FILE * BookFile[MaxBook];
-#endif
-
-static int BookSize[MaxBook];
-
-// prototypes
-
-static int    find_pos      (uint64 key, const int BookNumber);
-
-static void   read_entry    (entry_t * entry, int n, const int BookNumber);
-static void   write_entry   (const entry_t * entry, int n, const int BookNumber);
-
-#ifdef WINCE
-static void   read_entry_file    (Tcl_Channel file, entry_t * entry);
-static void   write_entry_file   (Tcl_Channel file, const entry_t * entry);
-#else
-static void   read_entry_file    (FILE *f, entry_t * entry);
-static void   write_entry_file   (FILE *f, const entry_t * entry);
-#endif
-
-#ifdef WINCE
-static uint64 read_integer  (Tcl_Channel file, int size);
-static void   write_integer (Tcl_Channel file, int size, uint64 n);
-#else
-static uint64 read_integer  (FILE * file, int size);
-static void   write_integer (FILE * file, int size, uint64 n);
-#endif
-
-// functions
-
-// =================================================================
-// Pascal Georges : functions added to interface with Scid
-// =================================================================
-board_t scid_board[MaxBook][1]; 
-void scid_book_update(char * probs, const int BookNumber) {
- 
-  int first_pos;
-   int sum;
-   int pos;
-   entry_t entry[1];
-	 int prob[100];
-	 int prob_count = 0;
-	 int prob_sum = 0;
-	 int i;
-	 
-	 /* parse probs and fill prob array */
-	 char *s;
-	 
-	 s = strtok( probs, " " );
-	 sscanf( s, "%d", &(prob[prob_count]) );
-	 prob_count++;
-	 while ( (s = strtok(NULL, " ")) != NULL) {
-	 	sscanf( s, "%d", &(prob[prob_count]) );
-	 	prob_count++;
-	 }
- 	 
-   first_pos = find_pos(scid_board[BookNumber]->key, BookNumber);
-
-   // sum
-
-   sum = 10000;
-
-    for (i=0; i< prob_count; i++)
-      prob_sum += prob[i];
-
-    double coef = (prob_sum) ? double(sum)/double(prob_sum) : 0;
-
-	 i = 0;
-   for (pos = first_pos; pos < BookSize[BookNumber] && i < prob_count; pos++) {
-      read_entry(entry,pos,BookNumber);
-      if (entry->key != scid_board[BookNumber]->key) break;
-			// change entry weight : no entry count == 0
-      if (prob[i] != 0) {
-			 entry->count = int( double(prob[i]) * coef );
-      } else {
-        entry->count = 1;
-      }
-			i++;
-			write_entry( entry, pos, BookNumber );
-   }
-}
-
-#define MAX_MOVES 100
-
-int scid_book_movesupdate(char * moves, char * probs, const int BookNumber, char *tempfile) {
- 
-    int maximum;
-    int pos;
-    entry_t entry[1], entry1[1];
-    uint16 move[MAX_MOVES];
-    int prob[MAX_MOVES];
-    int move_count = 0;
-    int prob_count = 0;
-    int prob_max = 0;
-    double coef=1.0;
-    int probs_written;
-    int write_count;
-    int i;
-    FILE *f;
-    char *probs_copy, *moves_copy;
-    //	printf("Updating book: moves=%s; probs=%s; tempfile=%s; key=%016llx.\n",moves,probs,tempfile,scid_board[BookNumber]->key);
-        /* parse probs and fill prob array */
-    char *s;
-    probs_copy=strdup(probs);  // strtok modifies its first argument
-    s = strtok( probs_copy, " " );
-    if(s!=NULL){
-        sscanf( s, "%d", &(prob[prob_count]) );
-        prob_count++;
-        while ( (s = strtok(NULL, " ")) != NULL) {
-            if(prob_count>=MAX_MOVES){
-                free(probs_copy);
-                return -1; // fail
-            }
-            sscanf( s, "%d", &(prob[prob_count]) );
-            prob_count++;
-        }
-    }
-    free(probs_copy);
-        // max
-    maximum = 0xfff0;
-    
-    for (i=0; i< prob_count; i++)
-        if(prob[i]>prob_max) prob_max=prob[i];
-    if(prob_max!=0){   // avoid division by zero
-      coef = double(maximum)/double(prob_max);
-    }
-    
-        /* parse moves and fill move array */
-    moves_copy=strdup(moves);  // strtok modifies its first argument
-    move_count=0;
-    s = strtok( moves_copy, " " );
-    if(s!=NULL){
-        move[move_count]=move_from_san(s,scid_board[BookNumber]);
-        move_count++;
-        while ( (s = strtok(NULL, " ")) != NULL) {
-            if(move_count>=MAX_MOVES){
-                free(moves_copy);
-                return -1; // fail
-            }
-            move[move_count]=move_from_san(s,scid_board[BookNumber]);
-            move_count++;
-        }
-    }
-    free(moves_copy);   
-    if (prob_count!=move_count){
-        return -1; //fail
-    }
-    if(prob_count==0){
-	// Don't return. If we're deleting the last move, we have to write the file
-	// return 0; // nothing to do
-    }
-
-    if(!(f=fopen(tempfile,"wb+"))){
-        return -1;  //fail
-    }
-    probs_written=0;
-    write_count=0;
-
-    fseek(BookFile[BookNumber],0,SEEK_SET);
-
-    for(pos=0; pos<BookSize[BookNumber];pos++){
-        read_entry_file(BookFile[BookNumber],entry);
-        if ((entry->key < scid_board[BookNumber]->key)||
-	    ((entry->key >scid_board[BookNumber]->key) && probs_written)
-	    ){
-            write_count++;
-            write_entry_file(f,entry);
-        }else if(!probs_written) {
-            for(i=0;i<move_count;i++){
-                entry1->key=scid_board[BookNumber]->key;
-                entry1->move=move[i];
-                if (prob[i] != 0) {
-                    entry1->count = int( double(prob[i]) * coef );
-                } else {
-                    entry1->count = 1;
-                }
-                entry1->n=0;
-                entry1->sum=0;
-                write_count++;
-                write_entry_file(f,entry1);
-            }
-            if(entry->key> scid_board[BookNumber]->key){
-                write_count++;
-                write_entry_file(f,entry);
-            }
-            probs_written=1;
-        }
-    }
-    if(!probs_written) { // not nice...
-      for(i=0;i<move_count;i++){
-	entry1->key=scid_board[BookNumber]->key;
-	entry1->move=move[i];
-	if (prob[i] != 0) {
-	  entry1->count = int( double(prob[i]) * coef );
-	} else {
-	  entry1->count = 1;
-	}
-	entry1->n=0;
-	entry1->sum=0;
-	write_count++;
-	write_entry_file(f,entry1);
-      }
-      probs_written=1;
-    }
-    ASSERT(probs_written);
-    fseek(BookFile[BookNumber],0,SEEK_SET);
-    fseek(f,0,SEEK_SET);
-    for(pos=0; pos<write_count ;pos++){
-        read_entry_file(f,entry);
-        write_entry_file(BookFile[BookNumber],entry);
-    }
-    fclose(f);
-    BookSize[BookNumber]=write_count;
-    book_flush(BookNumber); // commit changes to disk
-    return 0; // success
-}
-
-// =================================================================
-int scid_book_close(const int BookNumber) {
-	if (BookFile[BookNumber] != NULL) {
-#ifdef WINCE
-    if (my_Tcl_Close(NULL, BookFile[BookNumber]) != TCL_OK) {
-#else
-    if (fclose(BookFile[BookNumber]) == EOF) {
-#endif
-      return -1;
-   	}
-
-
-	}
-	return 0;
-}
-// =================================================================
-int scid_book_open(const char file_name[], const int BookNumber) {
-
-   int ReadOnlyFile = 0;
-
-#ifdef WINCE
-   BookFile[BookNumber] = my_Tcl_OpenFileChannel(NULL, file_name, "r+", 0666);
-#else
-   BookFile[BookNumber] = fopen(file_name,"rb+");
-#endif
-
-   //--------------------------------------------------
-   if (BookFile[BookNumber] == NULL) {
-      // the book can not be opened in read/write mode, try read only
-#ifdef WINCE
-      BookFile[BookNumber] = my_Tcl_OpenFileChannel(NULL, file_name, "r", 0666);
-#else
-      BookFile[BookNumber] = fopen(file_name,"rb");
-#endif
-      ReadOnlyFile = 1;
-      if (BookFile[BookNumber] == NULL) return -1;
-   }
-   //--------------------------------------------------
-
-
-#ifdef WINCE
-   my_Tcl_SetChannelOption(NULL, BookFile[BookNumber], "-encoding", "binary");
-   my_Tcl_SetChannelOption(NULL, BookFile[BookNumber], "-translation", "binary");
-    if (my_Tcl_Seek(BookFile[BookNumber], 0, SEEK_END) == -1) 
-      return -1;
-#else
-   if (fseek(BookFile[BookNumber],0,SEEK_END) == -1) {
-      return -1;
-   }
-#endif
-
-#ifdef WINCE
-    BookSize[BookNumber] = my_Tcl_Tell(BookFile[BookNumber]) / 16;
-#else
-    BookSize[BookNumber] = ftell(BookFile[BookNumber]) / 16;
-#endif
-   if (BookSize[BookNumber] == 0) return -1;
-   return(0+ReadOnlyFile); //success
-}
-
-// =========================================================
-// similar signature as gen_legal_moves
-int gen_book_moves(list_t * list, const board_t * board, const int BookNumber){
-    int first_pos, pos;
-    entry_t entry[1];
-    list_clear(list);
-    first_pos = find_pos(board->key, BookNumber);
-    for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
-        read_entry(entry,pos,BookNumber);
-        if (entry->key != board->key) break;
-        if (entry->count > 0 &&
-            entry->move != MoveNone &&
-            move_is_legal(entry->move,board)) {
-            list_add(list,entry->move);
-        }
-    }
-    return 0; 
-}
-
-// =================================================================
-int scid_book_disp(const board_t * board, char * s, const int BookNumber) {
-
-   int first_pos;
-   int sum;
-   int pos;
-   entry_t entry[1];
-   int move;
-   int score;
-	 
-	 // keep board in memory to ease book update	 
-	 memcpy(scid_board[BookNumber], board, sizeof(board_t));
-	 	  	 
-   first_pos = find_pos(board->key, BookNumber);
-
-   // sum
-
-   sum = 0;
-
-   for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
-
-      read_entry(entry,pos,BookNumber);
-      if (entry->key != board->key) break;
-      sum += entry->count;
-   }
-
-   // disp
-	 s[0] = '\0';
-   for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
-
-      read_entry(entry,pos,BookNumber);
-      if (entry->key != board->key) break;
-
-      move = entry->move;
-      score = entry->count;
-
-      if (score > 0 && move != MoveNone && move_is_legal(move,board)) {
-         char tmp[256] = {' '};
-         move_to_san(move, board, tmp + 1, 255);
-         sprintf(tmp + std::strlen(tmp), " %.0f%%",
-                (double(score) / double(sum)) * 100.0);
-         strcat(s, tmp);
-      }
-   }
-
-	return 0;
-}
-
-// =================================================================
-int scid_position_book_disp(const board_t *board, char * s, const int BookNumber) {
-
-   int move;
-   list_t /*book_moves[1],*/ legal_moves[1];
-   int i;
-   s[0] = '\0';
-   gen_legal_moves(legal_moves,board);
-//   gen_book_moves(book_moves,board,BookNumber);
-   for (i = 0; i < list_size(legal_moves); i++) {
-       move = list_move(legal_moves,i);
-//       if(list_contain(book_moves,move)) continue;
-           // scratch_board
-       board_t new_board = *board;
-       move_do(&new_board, move);
-       if(is_in_book(&new_board, BookNumber)){
-           char tmp[256] = {' '};
-           move_to_san(move, board, tmp + 1, 255);
-           strcat(s, tmp);
-       }
-   }
-   return 0;
-}
-
-
-
-
-// =================================================================
-
-// book_clear()
-
-void book_clear(const int BookNumber) {
-
-   BookFile[BookNumber] = NULL;
-   BookSize[BookNumber] = 0;
-}
-
-// book_open()
-
-void book_open(const char file_name[], const int BookNumber) {
-
-   ASSERT(file_name!=NULL);
-
-#ifdef WINCE
-   BookFile[BookNumber] = my_Tcl_OpenFileChannel(NULL, file_name, "r+", 0666);
-   my_Tcl_SetChannelOption(NULL, BookFile[BookNumber], "-encoding", "binary");
-   my_Tcl_SetChannelOption(NULL, BookFile[BookNumber], "-translation", "binary");
-#else
-   BookFile[BookNumber] = fopen(file_name,"rb+");
-#endif
-
-   if (BookFile[BookNumber] == NULL) my_fatal("book_open(): can't open file \"%s\": %s\n",file_name,strerror(errno));
-
-
-#ifdef WINCE
-    if (my_Tcl_Seek(BookFile[BookNumber], 0, SEEK_END) == -1) {
-#else
-   if (fseek(BookFile[BookNumber],0,SEEK_END) == -1) {
-#endif
-      my_fatal("book_open(): fseek(): %s\n",strerror(errno));
-   }
-
-#ifdef WINCE
-    BookSize[BookNumber] = my_Tcl_Tell(BookFile[BookNumber]) / 16;
-#else
-    BookSize[BookNumber] = ftell(BookFile[BookNumber]) / 16;
-#endif
-
-   if (BookSize[BookNumber] == 0) my_fatal("book_open(): empty file\n");
-}
-
-// book_close()
-
-void book_close(const int BookNumber) {
-#ifdef WINCE
-    if (my_Tcl_Close(NULL, BookFile[BookNumber]) != TCL_OK) {
-#else
-    if (fclose(BookFile[BookNumber]) == EOF) {
-#endif
-      my_fatal("book_close(): fclose(): %s\n",strerror(errno));
-   }
-}
-
-// is_in_book()
-
-bool is_in_book(const board_t * board, const int BookNumber) {
-
-   int pos;
-   entry_t entry[1];
-
-   ASSERT(board!=NULL);
-   for (pos = find_pos(board->key, BookNumber); pos < BookSize[BookNumber]; pos++) {
-      read_entry(entry,pos,BookNumber);
-      if (entry->key == board->key) return true;
-   }
-
-   return false;
-}
-
-// book_move()
-
-int book_move(const board_t * board, bool random, const int BookNumber) {
-
-   int best_move;
-   int best_score;
-   int pos;
-   entry_t entry[1];
-   int move;
-   int score;
-
-   ASSERT(board!=NULL);
-   ASSERT(random==true||random==false);
-
-   best_move = MoveNone;
-   best_score = 0;
-
-   for (pos = find_pos(board->key, BookNumber); pos < BookSize[BookNumber]; pos++) {
-
-      read_entry(entry,pos, BookNumber);
-      if (entry->key != board->key) break;
-
-      move = entry->move;
-      score = entry->count;
-
-      if (move != MoveNone && move_is_legal(move,board)) {
-
-         // pick this move?
-
-         ASSERT(score>0);
-
-         if (random) {
-            best_score += score;
-            if (my_random_int(best_score) < score) best_move = move;
-         } else {
-            if (score > best_score) {
-               best_move = move;
-               best_score = score;
-            }
-         }
-
-      } else {
-
-         ASSERT(false);
-      }
-   }
-
-   return best_move;
-}
-
-// book_disp()
-
-void book_disp(const board_t * board, const int BookNumber) {
-
-   int first_pos;
-   int sum;
-   int pos;
-   entry_t entry[1];
-   int move;
-   int score;
-   char move_string[256];
-
-   ASSERT(board!=NULL);
-
-   first_pos = find_pos(board->key, BookNumber);
-
-   // sum
-
-   sum = 0;
-
-   for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
-
-      read_entry(entry,pos, BookNumber);
-      if (entry->key != board->key) break;
-
-      sum += entry->count;
-   }
-
-   // disp
-
-   for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
-
-      read_entry(entry,pos, BookNumber);
-      if (entry->key != board->key) break;
-
-      move = entry->move;
-      score = entry->count;
-
-      if (score > 0 && move != MoveNone && move_is_legal(move,board)) {
-         move_to_san(move,board,move_string,256);
-         printf(" %s (%.0f%%)\n",move_string,(double(score)/double(sum))*100.0);
-      }
-   }
-
-   printf("\n");
-}
-
-// book_learn_move()
-
-void book_learn_move(const board_t * board, int move, int result, const int BookNumber) {
-
-   int pos;
-   entry_t entry[1];
-
-   ASSERT(board!=NULL);
-   ASSERT(move_is_ok(move));
-   ASSERT(result>=-1&&result<=+1);
-
-   ASSERT(move_is_legal(move,board));
-
-   for (pos = find_pos(board->key, BookNumber); pos < BookSize[BookNumber]; pos++) {
-
-      read_entry(entry,pos,BookNumber);
-      if (entry->key != board->key) break;
-
-      if (entry->move == move) {
-
-         entry->n++;
-         entry->sum += result+1;
-
-         write_entry(entry,pos, BookNumber);
-
-         break;
-      }
-   }
-}
-
-// book_flush()
-
-void book_flush(const int BookNumber) {
-#ifdef WINCE
-   if (my_Tcl_Flush(BookFile[BookNumber]) != TCL_OK) {
-#else
-   if (fflush(BookFile[BookNumber]) == EOF) {
-#endif
-      my_fatal("book_flush(): fflush(): %s\n",strerror(errno));
-   }
-}
-
-// find_pos()
-
-static int find_pos(uint64 key, const int BookNumber) {
-
-   int left, right, mid;
-   entry_t entry[1];
-
-   // binary search (finds the leftmost entry)
-   left = 0;
-   right = BookSize[BookNumber]-1;
-
-   ASSERT(left<=right);
-
-   while (left < right) {
-
-      mid = (left + right) / 2;
-      ASSERT(mid>=left&&mid<right);
-
-      read_entry(entry,mid,BookNumber);
-
-      if (key <= entry->key) {
-         right = mid;
-      } else {
-         left = mid+1;
-      }
-   }
-
-   ASSERT(left==right);
-
-   read_entry(entry,left,BookNumber);
-
-   return (entry->key == key) ? left : BookSize[BookNumber];
-}
-
-// read_entry()
-#ifdef WINCE
-static void read_entry_file(Tcl_Channel f, entry_t * entry) {
-#else
-static void read_entry_file(FILE *f, entry_t * entry) {
-#endif
-   ASSERT(entry!=NULL);
-
-   entry->key   = read_integer(f,8);
-   entry->move  = read_integer(f,2);
-   entry->count = read_integer(f,2);
-   entry->n     = read_integer(f,2);
-   entry->sum   = read_integer(f,2);
-}
-
-
-
-// read_entry()
-
-static void read_entry(entry_t * entry, int n, const int BookNumber) {
-   ASSERT(entry!=NULL);
-   ASSERT(n>=0&&n<BookSize[BookNumber]);
-#ifdef WINCE
-    if (my_Tcl_Seek(BookFile[BookNumber], n*16,SEEK_SET) == -1) {
-#else
-   if (fseek(BookFile[BookNumber],n*16,SEEK_SET) == -1) {
-#endif
-      my_fatal("read_entry(): fseek(): %s\n",strerror(errno));
-   }
-
-   entry->key   = read_integer(BookFile[BookNumber],8);
-   entry->move  = read_integer(BookFile[BookNumber],2);
-   entry->count = read_integer(BookFile[BookNumber],2);
-   entry->n     = read_integer(BookFile[BookNumber],2);
-   entry->sum   = read_integer(BookFile[BookNumber],2);
-}
-
-// write_entry_file
-#ifdef WINCE
-static void write_entry_file(Tcl_Channel f, const entry_t * entry) {
-#else
-static void write_entry_file(FILE * f, const entry_t * entry) {
-#endif
-   ASSERT(entry!=NULL);
-   write_integer(f,8,entry->key);
-   write_integer(f,2,entry->move);
-   write_integer(f,2,entry->count);
-   write_integer(f,2,entry->n);
-   write_integer(f,2,entry->sum);
-}   
-
-// write_entry()
-
-static void write_entry(const entry_t * entry, int n, const int BookNumber) {
-
-   ASSERT(entry!=NULL);
-   ASSERT(n>=0&&n<BookSize[BookNumber]);
-#ifdef WINCE
-   if (my_Tcl_Seek(BookFile[BookNumber],n*16,SEEK_SET) == -1) {
-#else
-   if (fseek(BookFile[BookNumber],n*16,SEEK_SET) == -1) {
-#endif
-      my_fatal("write_entry(): fseek(): %s\n",strerror(errno));
-   }
-
-   write_integer(BookFile[BookNumber],8,entry->key);
-   write_integer(BookFile[BookNumber],2,entry->move);
-   write_integer(BookFile[BookNumber],2,entry->count);
-   write_integer(BookFile[BookNumber],2,entry->n);
-   write_integer(BookFile[BookNumber],2,entry->sum);
-}
-
-// read_integer()
-#ifdef WINCE
-static uint64 read_integer(Tcl_Channel file, int size) {
-#else
-static uint64 read_integer(FILE * file, int size) {
-#endif
-   uint64 n;
-   int i;
-   int b;
-   ASSERT(file!=NULL);
-   ASSERT(size>0&&size<=8);
-
-   n = 0;
-
-   for (i = 0; i < size; i++) {
-
-#ifdef WINCE
-      unsigned char c;
-      my_Tcl_Read(file, (char *)&c , 1);
-      b = c;
-#else
-      b = fgetc(file);
-      if (b == EOF) {
-         if (feof(file)) {
-            my_fatal("read_integer(): fgetc(): EOF reached\n");
-         } else { // error
-            my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));
-         }
-      }
-#endif
-
-      ASSERT(b>=0&&b<256);
-      n = (n << 8) | b;
-   }
-
-   return n;
-}
-
-// write_integer()
-#ifdef WINCE
-static void write_integer(Tcl_Channel file, int size, uint64 n) {
-#else
-static void write_integer(FILE * file, int size, uint64 n) {
-#endif
-   int i;
-   int b;
-   ASSERT(file!=NULL);
-   ASSERT(size>0&&size<=8);
-   ASSERT(size==8||n>>(size*8)==0);
-
-   for (i = size-1; i >= 0; i--) {
-
-      b = (n >> (i*8)) & 0xFF;
-      ASSERT(b>=0&&b<256);
-#ifdef WINCE
-      unsigned char c;
-      c = b;
-      if (my_Tcl_Write(file, (char*) &c, 1) != 1) {
-#else
-      if (fputc(b,file) == EOF) {
-#endif
-         my_fatal("write_integer(): fputc(): %s\n",strerror(errno));
-      }
-   }
-}
-
-// end of book.cpp
-
+
+// book.cpp
+
+// includes
+
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "board.h"
+#include "book.h"
+#include "list.h"
+#include "move.h"
+#include "move_do.h"
+#include "move_gen.h"
+#include "move_legal.h"
+#include "san.h"
+#include "util.h"
+
+// types
+
+struct entry_t {
+	uint64_t key;
+	uint16_t move;
+	uint16_t count;
+	int16_t engine_score;
+	uint8_t engine_depth;
+	uint8_t engine_name_idx;
+};
+
+// variables
+static FILE* BookFile[MaxBook];
+
+static int BookSize[MaxBook];
+
+// prototypes
+
+static int find_pos(uint64 key, const int BookNumber);
+
+static void read_entry(entry_t* entry, int n, const int BookNumber);
+static void write_entry(const entry_t* entry, int n, const int BookNumber);
+
+static void read_entry_file(FILE* f, entry_t* entry);
+static void write_entry_file(FILE* f, const entry_t* entry);
+
+static uint64 read_integer(FILE* file, int size);
+static void write_integer(FILE* file, int size, uint64 n);
+
+// functions
+
+// =================================================================
+// Pascal Georges : functions added to interface with Scid
+// =================================================================
+board_t scid_board[MaxBook][1];
+void scid_book_update(char* probs, const int BookNumber) {
+
+	int first_pos;
+	int sum;
+	int pos;
+	entry_t entry[1];
+	int prob[100];
+	int prob_count = 0;
+	int prob_sum = 0;
+	int i;
+
+	/* parse probs and fill prob array */
+	char* s;
+
+	s = strtok(probs, " ");
+	sscanf(s, "%d", &(prob[prob_count]));
+	prob_count++;
+	while ((s = strtok(NULL, " ")) != NULL) {
+		sscanf(s, "%d", &(prob[prob_count]));
+		prob_count++;
+	}
+
+	first_pos = find_pos(scid_board[BookNumber]->key, BookNumber);
+
+	// sum
+
+	sum = 10000;
+
+	for (i = 0; i < prob_count; i++)
+		prob_sum += prob[i];
+
+	double coef = (prob_sum) ? double(sum) / double(prob_sum) : 0;
+
+	i = 0;
+	for (pos = first_pos; pos < BookSize[BookNumber] && i < prob_count; pos++) {
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != scid_board[BookNumber]->key)
+			break;
+		// change entry weight : no entry count == 0
+		if (prob[i] != 0) {
+			entry->count = int(double(prob[i]) * coef);
+		} else {
+			entry->count = 1;
+		}
+		i++;
+		write_entry(entry, pos, BookNumber);
+	}
+}
+
+#define MAX_MOVES 100
+
+int scid_book_movesupdate(char* moves, char* probs, const int BookNumber,
+                          char* tempfile) {
+
+	int maximum;
+	int pos;
+	entry_t entry[1], entry1[1];
+	uint16 move[MAX_MOVES];
+	int prob[MAX_MOVES];
+	int move_count = 0;
+	int prob_count = 0;
+	int prob_max = 0;
+	double coef = 1.0;
+	int probs_written;
+	int write_count;
+	int i;
+	FILE* f;
+	char *probs_copy, *moves_copy;
+	//	printf("Updating book: moves=%s; probs=%s; tempfile=%s;
+	// key=%016llx.\n",moves,probs,tempfile,scid_board[BookNumber]->key);
+	/* parse probs and fill prob array */
+	char* s;
+	probs_copy = strdup(probs); // strtok modifies its first argument
+	s = strtok(probs_copy, " ");
+	if (s != NULL) {
+		sscanf(s, "%d", &(prob[prob_count]));
+		prob_count++;
+		while ((s = strtok(NULL, " ")) != NULL) {
+			if (prob_count >= MAX_MOVES) {
+				free(probs_copy);
+				return -1; // fail
+			}
+			sscanf(s, "%d", &(prob[prob_count]));
+			prob_count++;
+		}
+	}
+	free(probs_copy);
+	// max
+	maximum = 0xfff0;
+
+	for (i = 0; i < prob_count; i++)
+		if (prob[i] > prob_max)
+			prob_max = prob[i];
+	if (prob_max != 0) { // avoid division by zero
+		coef = double(maximum) / double(prob_max);
+	}
+
+	/* parse moves and fill move array */
+	moves_copy = strdup(moves); // strtok modifies its first argument
+	move_count = 0;
+	s = strtok(moves_copy, " ");
+	if (s != NULL) {
+		move[move_count] = move_from_san(s, scid_board[BookNumber]);
+		move_count++;
+		while ((s = strtok(NULL, " ")) != NULL) {
+			if (move_count >= MAX_MOVES) {
+				free(moves_copy);
+				return -1; // fail
+			}
+			move[move_count] = move_from_san(s, scid_board[BookNumber]);
+			move_count++;
+		}
+	}
+	free(moves_copy);
+	if (prob_count != move_count) {
+		return -1; // fail
+	}
+	if (prob_count == 0) {
+		// Don't return. If we're deleting the last move, we have to write the
+		// file return 0; // nothing to do
+	}
+
+	if (!(f = fopen(tempfile, "wb+"))) {
+		return -1; // fail
+	}
+	probs_written = 0;
+	write_count = 0;
+
+	fseek(BookFile[BookNumber], 0, SEEK_SET);
+
+	for (pos = 0; pos < BookSize[BookNumber]; pos++) {
+		read_entry_file(BookFile[BookNumber], entry);
+		if ((entry->key < scid_board[BookNumber]->key) ||
+		    ((entry->key > scid_board[BookNumber]->key) && probs_written)) {
+			write_count++;
+			write_entry_file(f, entry);
+		} else if (!probs_written) {
+			for (i = 0; i < move_count; i++) {
+				entry1->key = scid_board[BookNumber]->key;
+				entry1->move = move[i];
+				if (prob[i] != 0) {
+					entry1->count = int(double(prob[i]) * coef);
+				} else {
+					entry1->count = 1;
+				}
+				entry1->engine_score = 0;
+				entry1->engine_depth = 0;
+				entry1->engine_name_idx = 0;
+				write_count++;
+				write_entry_file(f, entry1);
+			}
+			if (entry->key > scid_board[BookNumber]->key) {
+				write_count++;
+				write_entry_file(f, entry);
+			}
+			probs_written = 1;
+		}
+	}
+	if (!probs_written) { // not nice...
+		for (i = 0; i < move_count; i++) {
+			entry1->key = scid_board[BookNumber]->key;
+			entry1->move = move[i];
+			if (prob[i] != 0) {
+				entry1->count = int(double(prob[i]) * coef);
+			} else {
+				entry1->count = 1;
+			}
+			entry1->engine_score = 0;
+			entry1->engine_depth = 0;
+			entry1->engine_name_idx = 0;
+			write_count++;
+			write_entry_file(f, entry1);
+		}
+		probs_written = 1;
+	}
+	ASSERT(probs_written);
+	fseek(BookFile[BookNumber], 0, SEEK_SET);
+	fseek(f, 0, SEEK_SET);
+	for (pos = 0; pos < write_count; pos++) {
+		read_entry_file(f, entry);
+		write_entry_file(BookFile[BookNumber], entry);
+	}
+	fclose(f);
+	BookSize[BookNumber] = write_count;
+	book_flush(BookNumber); // commit changes to disk
+	return 0;               // success
+}
+
+// =================================================================
+int scid_book_close(const int BookNumber) {
+	if (BookFile[BookNumber] != NULL) {
+		if (fclose(BookFile[BookNumber]) == EOF) {
+			return -1;
+		}
+	}
+	return 0;
+}
+// =================================================================
+int scid_book_open(const char file_name[], const int BookNumber) {
+
+	int ReadOnlyFile = 0;
+
+	BookFile[BookNumber] = fopen(file_name, "rb+");
+
+	//--------------------------------------------------
+	if (BookFile[BookNumber] == NULL) {
+		// the book can not be opened in read/write mode, try read only
+		BookFile[BookNumber] = fopen(file_name, "rb");
+		ReadOnlyFile = 1;
+		if (BookFile[BookNumber] == NULL)
+			return -1;
+	}
+	//--------------------------------------------------
+
+	if (fseek(BookFile[BookNumber], 0, SEEK_END) == -1) {
+		return -1;
+	}
+
+	BookSize[BookNumber] = ftell(BookFile[BookNumber]) / 16;
+	if (BookSize[BookNumber] == 0)
+		return -1;
+	return (0 + ReadOnlyFile); // success
+}
+
+// =========================================================
+// similar signature as gen_legal_moves
+int gen_book_moves(list_t* list, const board_t* board, const int BookNumber) {
+	int first_pos, pos;
+	entry_t entry[1];
+	list_clear(list);
+	first_pos = find_pos(board->key, BookNumber);
+	for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != board->key)
+			break;
+		if (entry->count > 0 && entry->move != MoveNone &&
+		    move_is_legal(entry->move, board)) {
+			list_add(list, entry->move);
+		}
+	}
+	return 0;
+}
+
+// =================================================================
+std::vector<std::tuple<int16_t, uint8_t, uint8_t>>
+scid_book_disp(const board_t* board, char* s, const int BookNumber) {
+
+	int first_pos;
+	int sum;
+	int pos;
+	entry_t entry[1];
+	int move;
+	int score;
+
+	// keep board in memory to ease book update
+	memcpy(scid_board[BookNumber], board, sizeof(board_t));
+
+	first_pos = find_pos(board->key, BookNumber);
+
+	// sum
+
+	sum = 0;
+
+	for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
+
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != board->key)
+			break;
+		sum += entry->count;
+	}
+
+	// disp
+	std::vector<std::tuple<int16_t, uint8_t, uint8_t>> extra_info;
+	s[0] = '\0';
+	for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != board->key)
+			break;
+
+		move = entry->move;
+		score = entry->count;
+		if (score > 0 && move != MoveNone && move_is_legal(move, board)) {
+			char tmp[256] = {' '};
+			move_to_san(move, board, tmp + 1, 255);
+			sprintf(tmp + std::strlen(tmp), " %.0f%%",
+			        (double(score) / double(sum)) * 100.0);
+			strcat(s, tmp);
+
+			extra_info.emplace_back(entry->engine_score, entry->engine_depth,
+			                        entry->engine_name_idx);
+		}
+	}
+
+	return extra_info;
+}
+
+// =================================================================
+int scid_position_book_disp(const board_t* board, char* s,
+                            const int BookNumber) {
+
+	int move;
+	list_t /*book_moves[1],*/ legal_moves[1];
+	int i;
+	s[0] = '\0';
+	gen_legal_moves(legal_moves, board);
+	//   gen_book_moves(book_moves,board,BookNumber);
+	for (i = 0; i < list_size(legal_moves); i++) {
+		move = list_move(legal_moves, i);
+		//       if(list_contain(book_moves,move)) continue;
+		// scratch_board
+		board_t new_board = *board;
+		move_do(&new_board, move);
+		if (is_in_book(&new_board, BookNumber)) {
+			char tmp[256] = {' '};
+			move_to_san(move, board, tmp + 1, 255);
+			strcat(s, tmp);
+		}
+	}
+	return 0;
+}
+
+// =================================================================
+
+// book_clear()
+
+void book_clear(const int BookNumber) {
+
+	BookFile[BookNumber] = NULL;
+	BookSize[BookNumber] = 0;
+}
+
+// book_open()
+
+void book_open(const char file_name[], const int BookNumber) {
+
+	ASSERT(file_name != NULL);
+
+	BookFile[BookNumber] = fopen(file_name, "rb+");
+
+	if (BookFile[BookNumber] == NULL)
+		my_fatal("book_open(): can't open file \"%s\": %s\n", file_name,
+		         strerror(errno));
+
+	if (fseek(BookFile[BookNumber], 0, SEEK_END) == -1) {
+		my_fatal("book_open(): fseek(): %s\n", strerror(errno));
+	}
+
+	BookSize[BookNumber] = ftell(BookFile[BookNumber]) / 16;
+
+	if (BookSize[BookNumber] == 0)
+		my_fatal("book_open(): empty file\n");
+}
+
+// book_close()
+
+void book_close(const int BookNumber) {
+	if (fclose(BookFile[BookNumber]) == EOF) {
+		my_fatal("book_close(): fclose(): %s\n", strerror(errno));
+	}
+}
+
+// is_in_book()
+
+bool is_in_book(const board_t* board, const int BookNumber) {
+
+	int pos;
+	entry_t entry[1];
+
+	ASSERT(board != NULL);
+	for (pos = find_pos(board->key, BookNumber); pos < BookSize[BookNumber];
+	     pos++) {
+		read_entry(entry, pos, BookNumber);
+		if (entry->key == board->key)
+			return true;
+	}
+
+	return false;
+}
+
+// book_move()
+
+int book_move(const board_t* board, bool random, const int BookNumber) {
+
+	int best_move;
+	int best_score;
+	int pos;
+	entry_t entry[1];
+	int move;
+	int score;
+
+	ASSERT(board != NULL);
+	ASSERT(random == true || random == false);
+
+	best_move = MoveNone;
+	best_score = 0;
+
+	for (pos = find_pos(board->key, BookNumber); pos < BookSize[BookNumber];
+	     pos++) {
+
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != board->key)
+			break;
+
+		move = entry->move;
+		score = entry->count;
+
+		if (move != MoveNone && move_is_legal(move, board)) {
+
+			// pick this move?
+
+			ASSERT(score > 0);
+
+			if (random) {
+				best_score += score;
+				if (my_random_int(best_score) < score)
+					best_move = move;
+			} else {
+				if (score > best_score) {
+					best_move = move;
+					best_score = score;
+				}
+			}
+
+		} else {
+
+			ASSERT(false);
+		}
+	}
+
+	return best_move;
+}
+
+// book_disp()
+
+void book_disp(const board_t* board, const int BookNumber) {
+
+	int first_pos;
+	int sum;
+	int pos;
+	entry_t entry[1];
+	int move;
+	int score;
+	char move_string[256];
+
+	ASSERT(board != NULL);
+
+	first_pos = find_pos(board->key, BookNumber);
+
+	// sum
+
+	sum = 0;
+
+	for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
+
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != board->key)
+			break;
+
+		sum += entry->count;
+	}
+
+	// disp
+
+	for (pos = first_pos; pos < BookSize[BookNumber]; pos++) {
+
+		read_entry(entry, pos, BookNumber);
+		if (entry->key != board->key)
+			break;
+
+		move = entry->move;
+		score = entry->count;
+
+		if (score > 0 && move != MoveNone && move_is_legal(move, board)) {
+			move_to_san(move, board, move_string, 256);
+			printf(" %s (%.0f%%)\n", move_string,
+			       (double(score) / double(sum)) * 100.0);
+		}
+	}
+
+	printf("\n");
+}
+
+// book_flush()
+
+void book_flush(const int BookNumber) {
+	if (fflush(BookFile[BookNumber]) == EOF) {
+		my_fatal("book_flush(): fflush(): %s\n", strerror(errno));
+	}
+}
+
+// find_pos()
+
+static int find_pos(uint64 key, const int BookNumber) {
+
+	int left, right, mid;
+	entry_t entry[1];
+
+	// binary search (finds the leftmost entry)
+	left = 0;
+	right = BookSize[BookNumber] - 1;
+
+	ASSERT(left <= right);
+
+	while (left < right) {
+
+		mid = (left + right) / 2;
+		ASSERT(mid >= left && mid < right);
+
+		read_entry(entry, mid, BookNumber);
+
+		if (key <= entry->key) {
+			right = mid;
+		} else {
+			left = mid + 1;
+		}
+	}
+
+	ASSERT(left == right);
+
+	read_entry(entry, left, BookNumber);
+
+	return (entry->key == key) ? left : BookSize[BookNumber];
+}
+
+// read_entry()
+static void read_entry_file(FILE* f, entry_t* entry) {
+	ASSERT(entry != NULL);
+
+	entry->key = read_integer(f, 8);
+	entry->move = static_cast<uint16_t>(read_integer(f, 2));
+	entry->count = static_cast<uint16_t>(read_integer(f, 2));
+	entry->engine_score = static_cast<uint8_t>(read_integer(f, 1));
+	int score_hi = static_cast<int8_t>(read_integer(f, 1)) * 256;
+	entry->engine_score += static_cast<int16_t>(score_hi);
+	entry->engine_depth = static_cast<uint8_t>(read_integer(f, 1));
+	entry->engine_name_idx = static_cast<uint8_t>(read_integer(f, 1));
+}
+
+// read_entry()
+static void read_entry(entry_t* entry, int n, const int BookNumber) {
+	ASSERT(entry != NULL);
+	ASSERT(n >= 0 && n < BookSize[BookNumber]);
+	if (fseek(BookFile[BookNumber], n * 16, SEEK_SET) == -1) {
+		my_fatal("read_entry(): fseek(): %s\n", strerror(errno));
+	}
+
+	read_entry_file(BookFile[BookNumber], entry);
+}
+
+// write_entry_file
+static void write_entry_file(FILE* f, const entry_t* entry) {
+	ASSERT(entry != NULL);
+	write_integer(f, 8, entry->key);
+	write_integer(f, 2, entry->move);
+	write_integer(f, 2, entry->count);
+	const auto score = static_cast<uint16_t>(entry->engine_score);
+	write_integer(f, 1, score & 0xFF);
+	write_integer(f, 1, score >> 8);
+	write_integer(f, 1, entry->engine_depth);
+	write_integer(f, 1, entry->engine_name_idx);
+}
+
+// write_entry()
+static void write_entry(const entry_t* entry, int n, const int BookNumber) {
+
+	ASSERT(entry != NULL);
+	ASSERT(n >= 0 && n < BookSize[BookNumber]);
+	if (fseek(BookFile[BookNumber], n * 16, SEEK_SET) == -1) {
+		my_fatal("write_entry(): fseek(): %s\n", strerror(errno));
+	}
+
+	write_entry_file(BookFile[BookNumber], entry);
+}
+
+// read_integer()
+static uint64 read_integer(FILE* file, int size) {
+	uint64 n;
+	int i;
+	int b;
+	ASSERT(file != NULL);
+	ASSERT(size > 0 && size <= 8);
+
+	n = 0;
+
+	for (i = 0; i < size; i++) {
+
+		b = fgetc(file);
+		if (b == EOF) {
+			if (feof(file)) {
+				my_fatal("read_integer(): fgetc(): EOF reached\n");
+			} else { // error
+				my_fatal("read_integer(): fgetc(): %s\n", strerror(errno));
+			}
+		}
+
+		ASSERT(b >= 0 && b < 256);
+		n = (n << 8) | b;
+	}
+
+	return n;
+}
+
+// write_integer()
+static void write_integer(FILE* file, int size, uint64 n) {
+	int i;
+	int b;
+	ASSERT(file != NULL);
+	ASSERT(size > 0 && size <= 8);
+	ASSERT(size == 8 || n >> (size * 8) == 0);
+
+	for (i = size - 1; i >= 0; i--) {
+
+		b = (n >> (i * 8)) & 0xFF;
+		ASSERT(b >= 0 && b < 256);
+		if (fputc(b, file) == EOF) {
+			my_fatal("write_integer(): fputc(): %s\n", strerror(errno));
+		}
+	}
+}
+
+// end of book.cpp
diff -pruN 1:4.7.0+dfsg1-2/src/polyglot/book.h 1:4.7.4+dfsg1-2/src/polyglot/book.h
--- 1:4.7.0+dfsg1-2/src/polyglot/book.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/polyglot/book.h	2022-07-09 05:14:42.000000000 +0000
@@ -9,10 +9,9 @@
 #include "board.h"
 #include "util.h"
 #include "list.h"
-
-#ifdef WINCE
-#include <tcl.h>
-#endif
+#include <stdint.h>
+#include <tuple>
+#include <vector>
 
 // constants
 const int MaxBook = 4;
@@ -25,14 +24,16 @@ extern void book_open       (const char
 extern void book_close      (const int BookNumber);
 extern int scid_book_open       (const char file_name[], const int BookNumber);
 extern int scid_book_close      (const int BookNumber);
-extern int scid_book_disp      (const board_t * board, char * s, const int BookNumber);
+
+std::vector<std::tuple<int16_t, uint8_t, uint8_t>>
+scid_book_disp(const board_t* board, char* s, const int BookNumber);
+
 extern int scid_position_book_disp      (const board_t * board, char * s, const int BookNumber);
 
 extern bool is_in_book      (const board_t * board, const int BookNumber);
 extern int  book_move       (const board_t * board, bool random, const int BookNumber);
 extern void book_disp       (const board_t * board, const int BookNumber);
 
-extern void book_learn_move (const board_t * board, int move, int result, const int BookNumber);
 extern void book_flush      (const int BookNumber);
 extern void scid_book_update	   (char * probs, const int BookNumber);
 extern int  scid_book_movesupdate  (char *moves, char * probs, const int BookNumber, char *tempfile);
diff -pruN 1:4.7.0+dfsg1-2/src/polyglot/main.cpp 1:4.7.4+dfsg1-2/src/polyglot/main.cpp
--- 1:4.7.0+dfsg1-2/src/polyglot/main.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/polyglot/main.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -76,11 +76,11 @@ int polyglot_close(const int BookNumber)
 }
 /////////////////////////////////////////////////////////////////////
 // fill parameter moves with opening book moves
-int polyglot_moves(char *moves, const char *fen, const int BookNumber) {
-  board_t board[1];
- 	board_from_fen(board, fen);
-  scid_book_disp(board, moves, BookNumber);
-	return 0;
+std::vector<std::tuple<int16_t, uint8_t, uint8_t>>
+polyglot_moves(char *moves, const char *fen, const int BookNumber) {
+  board_t board;
+  board_from_fen(&board, fen);
+  return scid_book_disp(&board, moves, BookNumber);
 }
 /////////////////////////////////////////////////////////////////////
 // find moves to positions in the book
diff -pruN 1:4.7.0+dfsg1-2/src/polyglot.h 1:4.7.4+dfsg1-2/src/polyglot.h
--- 1:4.7.0+dfsg1-2/src/polyglot.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/polyglot.h	2022-07-09 05:14:42.000000000 +0000
@@ -1,9 +1,14 @@
 #ifndef POLYGLOT_H
 #define POLYGLOT_H
 
+#include <vector>
+
 int polyglot_open			(const char * BookFile, const int BookNumber);
 int polyglot_close		(const int BookNumber);
-int polyglot_moves		(char *moves, const char *fen, const int BookNumber);
+
+std::vector<std::tuple<int16_t, uint8_t, uint8_t>>
+polyglot_moves(char* moves, const char* fen, const int BookNumber);
+
 int polyglot_positions	(char *moves, const char *fen, const int BookNumber);
 void scid_book_update	(char * probs, const int BookNumber);
 int scid_book_movesupdate  (char *moves, char * probs, const int BookNumber, char *tempfile);
diff -pruN 1:4.7.0+dfsg1-2/src/position.cpp 1:4.7.4+dfsg1-2/src/position.cpp
--- 1:4.7.0+dfsg1-2/src/position.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/position.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -12,23 +12,16 @@
 //
 //////////////////////////////////////////////////////////////////////
 
-#include "common.h"
 #include "position.h"
 #include "attacks.h"
-#include "misc.h"
-#include "hash.h"
-#include "sqmove.h"
+#include "common.h"
 #include "dstring.h"
+#include "hash.h"
+#include "misc.h"
 #include "movegen.h"
+#include "sqmove.h"
 #include <algorithm>
-
-static uint hashVal [16][64];
-static uint stdStartHash = 0;
-static uint stdStartPawnHash = 0;
-
-// HASH and UNHASH are identical: XOR the hash value for a (piece,square).
-#define HASH(h,p,sq)    (h) ^= hashVal[(p)][(sq)]
-#define UNHASH(h,p,sq)  (h) ^= hashVal[(p)][(sq)]
+#include <array>
 
 inline void
 Position::AddHash (pieceT p, squareT sq)
@@ -74,56 +67,6 @@ Position::RemoveFromBoard (pieceT p, squ
     UnHash (p, sq);
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// initHashValues:
-//    Initialises the table of Zobrist hash values.
-static void initHashValues (void)
-{
-    // Ensure we set up the hash values only once:
-    static int firstCall = 1;
-    if (! firstCall) { return; }
-    firstCall = 0;
-
-
-    // First, set all values to 0:
-    uint sq;
-    for (uint p = 0; p < 16; p++) {
-        for (sq = A1; sq <= H8; sq++) { hashVal[p][sq] = 0; }
-    }
-
-    // Fill in the hash values for each valid [piece][square] index,
-    // using a table of pre-generated good values:
-    const unsigned int * hash = goodHashValues;
-    for (sq=A1; sq <= H8; sq++) { hashVal[WK][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[WQ][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[WR][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[WB][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[WN][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[WP][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[BK][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[BQ][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[BR][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[BB][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[BN][sq] = *hash; hash++; }
-    for (sq=A1; sq <= H8; sq++) { hashVal[BP][sq] = *hash; hash++; }
-
-    // Compute the hash values for the standard starting position:
-    uint h = 0;
-    // First the pawns:
-    HASH (h,WP,A2);  HASH (h,WP,B2);  HASH (h,WP,C2);  HASH (h,WP,D2);
-    HASH (h,WP,E2);  HASH (h,WP,F2);  HASH (h,WP,G2);  HASH (h,WP,H2);
-    HASH (h,BP,A7);  HASH (h,BP,B7);  HASH (h,BP,C7);  HASH (h,BP,D7);
-    HASH (h,BP,E7);  HASH (h,BP,F7);  HASH (h,BP,G7);  HASH (h,BP,H7);
-    stdStartPawnHash = h;
-    // Now the nonpawns:
-    HASH (h,WR,A1);  HASH (h,WN,B1);  HASH (h,WB,C1);  HASH (h,WQ,D1);
-    HASH (h,WK,E1);  HASH (h,WB,F1);  HASH (h,WN,G1);  HASH (h,WR,H1);
-    HASH (h,BR,A8);  HASH (h,BN,B8);  HASH (h,BB,C8);  HASH (h,BQ,D8);
-    HASH (h,BK,E8);  HASH (h,BB,F8);  HASH (h,BN,G8);  HASH (h,BR,H8);
-    stdStartHash = h;
-}
-
-
 ///////////////////////////////////////////////////////////////////////////
 // sqDir[][]: Array listing the direction between any two squares.
 //    For example, sqDir[A1][B2] == UP_RIGHT, and sqDir[A1][C2] == NULL_DIR.
@@ -217,10 +160,8 @@ inline void
 Position::AddLegalMove (MoveList * mlist, squareT from, squareT to, pieceT promo)
 {
     ASSERT (mlist != NULL);
-    // We do NOT set the pre-move castling/ep flags, or the captured
-    // piece info, here since that is ONLY needed if the move is
-    // going to be executed with DoSimpleMove() and then undone.
-    mlist->emplace_back(from, to, promo, Board[from], Board[to]);
+    auto& sm = mlist->emplace_back();
+    makeMove(from, to, promo, sm);
 }
 
 
@@ -281,56 +222,95 @@ Position::GenKnightMoves (MoveList * mli
     }
 }
 
+template <typename TFunc>
+bool Position::under_attack(squareT target_sq, squareT captured_sq,
+                            TFunc not_empty) const {
+	const auto enemy = color_Flip(GetToMove());
+	const auto lpawn_atk = square_Move(target_sq,
+	                                   enemy == WHITE ? DOWN_LEFT : UP_LEFT);
+	const auto rpawn_atk = square_Move(target_sq,
+	                                   enemy == WHITE ? DOWN_RIGHT : UP_RIGHT);
+	const auto enemy_pieces = GetList(enemy);
+	for (size_t i = 1, n = GetCount(enemy); i < n; ++i) {
+		const auto piece_sq = enemy_pieces[i];
+		if (piece_sq == captured_sq)
+			continue;
+
+		const auto piece_type = piece_Type(GetPiece(piece_sq));
+		if (piece_type == KNIGHT) {
+			if (movegen::valid_knight(piece_sq, target_sq))
+				return true; // Knight check
+
+		} else if (piece_type == PAWN) {
+			if (piece_sq == lpawn_atk || piece_sq == rpawn_atk)
+				return true; // Pawn check
+
+		} else {
+			if (movegen::attack_slider(piece_sq, target_sq, piece_type,
+			                           not_empty))
+				return true; // Slider check
+		}
+	}
+	assert(GetCount(enemy) >= 1);
+	return movegen::valid_king(enemy_pieces[0], target_sq);
+}
+
+bool Position::under_attack(squareT target_sq) const {
+	return under_attack(target_sq, target_sq,
+	                    [&](auto sq) { return GetPiece(sq) != EMPTY; });
+}
+
+template <bool check_legal> bool Position::canCastle(bool king_side) const {
+	if (check_legal && !GetCastling(ToMove, king_side ? KSIDE : QSIDE))
+		return false;
+
+	const squareT kingFrom = GetKingSquare();
+	const squareT rookFrom = castleRookSq(ToMove, king_side);
+	const squareT rookTo = king_side ? square_Relative(ToMove, F1)
+	                                 : square_Relative(ToMove, D1);
+	const squareT kingTo = king_side ? square_Relative(ToMove, G1)
+	                                 : square_Relative(ToMove, C1);
+	if (Board[rookFrom] != piece_Make(ToMove, ROOK))
+		return false;
+
+	const int stepRook = rookFrom < rookTo ? -1 : 1;
+	for (int sq = rookTo; sq != rookFrom; sq += stepRook) {
+		if (Board[sq] != EMPTY && sq != kingFrom)
+			return false;
+
+		if (!check_legal)
+			break;
+	}
+
+	const int stepKing = kingFrom < kingTo ? -1 : 1;
+	for (int sq = kingTo; sq != kingFrom; sq += stepKing) {
+		if (Board[sq] != EMPTY && sq != rookFrom)
+			return false;
+
+		if (!check_legal)
+			break;
+
+		if (under_attack(sq))
+			return false;
+	}
+	return true;
+}
+template bool Position::canCastle<false>(bool king_side) const;
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Position::GenCastling():
 //    Generate the legal castling moves.
 //    Assumes the side to move is NOT in check, so the caller
 //    should verify this first.
 //
-void
-Position::GenCastling (MoveList * mlist)
-{
-    ASSERT (! IsKingInCheck());
-    squareT from = GetKingSquare(ToMove);
-    if (from != (ToMove == WHITE ? E1 : E8))  { return; }
-    squareT enemyKingSq = GetEnemyKingSquare();
-    squareT target, skip, rookSq;
-    pieceT rookPiece;
+void Position::GenCastling(MoveList* mlist) {
+	const squareT from = GetKingSquare();
 
-    // Try kingside first
+	if (canCastle(true))
+		makeMove(from, from, KING, mlist->emplace_back());
 
-    // Kingside Castling:
-    if (GetCastling (ToMove, KSIDE)) {
-        if (ToMove == WHITE) {
-            target = G1; skip = F1; rookSq = H1; rookPiece = WR;
-        } else {
-            target = G8; skip = F8; rookSq = H8; rookPiece = BR;
-        }
-        if (Board[target] == EMPTY  &&  Board[skip] == EMPTY
-                &&  Board[rookSq] == rookPiece
-                &&  CalcNumChecks (target) == 0
-                &&  CalcNumChecks (skip) == 0
-                &&  ! square_Adjacent (target, enemyKingSq)) {
-            AddLegalMove (mlist, from, target, EMPTY);
-        }
-    }
-
-    // Queenside Castling:
-    if (GetCastling (ToMove, QSIDE)) {
-        if (ToMove == WHITE) {
-            target = C1; skip = D1; rookSq = A1; rookPiece = WR;
-        } else {
-            target = C8; skip = D8; rookSq = A8; rookPiece = BR;
-        }
-        if (Board[target] == EMPTY  &&  Board[skip] == EMPTY
-                &&  Board[rookSq] == rookPiece
-                &&  Board[target - 1] == EMPTY // B1 or B8 must be empty too!
-                &&  CalcNumChecks (target) == 0
-                &&  CalcNumChecks (skip) == 0
-                &&  ! square_Adjacent (target, enemyKingSq)) {
-            AddLegalMove (mlist, from, target, EMPTY);
-        }
-    }
+	if (canCastle(false))
+		makeMove(from, from, QUEEN, mlist->emplace_back());
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -339,7 +319,7 @@ Position::GenCastling (MoveList * mlist)
 //      the specified flag is true.
 //
 void
-Position::GenKingMoves (MoveList * mlist, genMovesT genType, bool castling)
+Position::GenKingMoves (MoveList * mlist, genMovesT genType)
 {
     squareT kingSq = GetKingSquare();
     squareT enemyKingSq = GetEnemyKingSquare();
@@ -380,8 +360,6 @@ Position::GenKingMoves (MoveList * mlist
         if (addThisMove) { AddLegalMove (mlist, kingSq, destSq, EMPTY); }
         destPtr++;
     }
-    // Now generate castling moves, if possible:
-    if (genNonCaptures  &&  castling) { GenCastling (mlist); }
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -548,10 +526,6 @@ Position::Position() {
     // Setting up a valid board is left to StdStart() or Clear().
     Board [COLOR_SQUARE] = EMPTY;
     Board [NULL_SQUARE] = END_OF_BOARD;
-
-    // Make sure all tables used for move generation, hashing,
-    // square tests, etc have been computed:
-    initHashValues();
 }
 
 
@@ -577,6 +551,8 @@ Position::Clear (void)
     Count[WHITE] = Count[BLACK] = 0;
     EPTarget = NULL_SQUARE;
     Castling = 0;
+    variant_ = 0;
+    std::fill_n(castleRookSq_, 4, 0);
     Board [NULL_SQUARE] = END_OF_BOARD;
     PlyCounter = 0;
     HalfMoveClock = 0;
@@ -585,6 +561,32 @@ Position::Clear (void)
     return;
 }
 
+void Position::setCastling(colorT col, squareT rsq) {
+	static_assert(1 << castlingIdx(WHITE, QSIDE) == 1);
+	static_assert(1 << castlingIdx(WHITE, KSIDE) == 2);
+	static_assert(1 << castlingIdx(BLACK, QSIDE) == 4);
+	static_assert(1 << castlingIdx(BLACK, KSIDE) == 8);
+
+	const auto ksq = GetKingSquare(col);
+	if (square_Rank(ksq) == square_Rank(rsq)) {
+		const auto rook = piece_Make(col, ROOK);
+		while (Board[rsq] != rook && rsq != ksq) {
+			if (rsq < ksq)
+				++rsq;
+			else
+				--rsq;
+		}
+	}
+
+	const auto dir = square_Fyle(ksq) < square_Fyle(rsq) ? KSIDE : QSIDE;
+	const auto idx = castlingIdx(col, dir);
+	castleRookSq_[idx] = rsq;
+	Castling |= 1u << idx;
+	const auto std_rsq = (dir == QSIDE) ? square_Relative(col, A1)
+	                                    : square_Relative(col, H1);
+	if (ksq != square_Relative(col, E1) || rsq != std_rsq)
+		variant_ = 1;
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Position::StdStart():
@@ -630,9 +632,10 @@ const Position& Position::getStdStart()
             p->AddToBoard(BP, A7+i); p->List[BLACK][i+8] = A7+i; p->ListPos[A7+i] = i+8;
         }
 
-        p->Castling = 0;
-        p->SetCastling (WHITE, QSIDE, true);  p->SetCastling (WHITE, KSIDE, true);
-        p->SetCastling (BLACK, QSIDE, true);  p->SetCastling (BLACK, KSIDE, true);
+        p->setCastling(WHITE, A1);
+        p->setCastling(WHITE, H1);
+        p->setCastling(BLACK, A8);
+        p->setCastling(BLACK, H8);
         p->EPTarget = NULL_SQUARE;
         p->ToMove = WHITE;
         p->PlyCounter = 0;
@@ -861,216 +864,129 @@ Position::GenerateMoves (MoveList* mlist
 
     // Lastly, king moves...
     if (mask & (1 << KING)) {
-        bool castling = !numChecks;
-        GenKingMoves (mlist, genType, castling);
-    }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::IsLegalMove
-//   Determines whether the specified move is legal in this position,
-//   without requiring move generation (except for castling moves).
-bool
-Position::IsLegalMove (simpleMoveT * sm) {
-    squareT from = sm->from;
-    squareT to = sm->to;
-    if (from > H8  ||  to > H8) { return false; }
-    if (from == to) { return false; }
-    pieceT mover = Board[from];
-    pieceT captured = Board[to];
-    if (piece_Color(mover) != ToMove) { return false; }
-    if (piece_Color(captured) == ToMove) { return false; }
-    if (sm->movingPiece != mover) { return false; }
-    mover = piece_Type (mover);
-    if (sm->promote != EMPTY  &&  mover != PAWN) { return false; }
-
-    if (mover == PAWN) {
-        rankT rfrom = square_Rank(from);
-        rankT rto = square_Rank(to);
-        if (ToMove == BLACK) { rfrom = RANK_8 - rfrom; rto = RANK_8 - rto; }
-        int rdiff = (int)rto - (int)rfrom;
-        int fdiff = (int)square_Fyle(to) - (int)square_Fyle(from);
-        if (rdiff < 1  ||  rdiff > 2) { return false; }
-        if (fdiff < -1  ||  fdiff > 1) { return false; }
-        if (fdiff == 0) {  // Pawn push:
-            if (captured != EMPTY) { return false; }
-            if (rdiff == 2) {  // Two-square push:
-                if (rfrom != RANK_2) { return false; }
-                // Make sure the square in between is empty:
-                squareT midsquare = from + ((to - from) / 2);
-                if (Board[midsquare] != EMPTY) { return false; }
-            }
-        } else {  // Pawn capture:
-            if (rdiff != 1) { return false; }
-            if (captured == EMPTY) {
-                // It must be en passant, or illegal
-                if (to != EPTarget) { return false; }
-            }
-        }
-        // Check the promotion piece:
-        if (rto == RANK_8) {
-            pieceT p = sm->promote;
-            if (p != QUEEN  &&  p != ROOK  &&  p != BISHOP  &&  p != KNIGHT) {
-                return false;
-            }
-        } else {
-            if (sm->promote != EMPTY) { return false; }
-        }
-
-    } else if (piece_IsSlider(mover)) {
-        // Make sure the direction is valid:
-        directionT dir = sqDir[from][to];
-        if (dir == NULL_DIR) { return false; }
-        if (mover == ROOK  &&  direction_IsDiagonal(dir)) { return false; }
-        if (mover == BISHOP  &&  !direction_IsDiagonal(dir)) { return false; }
-        int delta = direction_Delta (dir);
-        // Make sure all the in-between squares are empty:
-        squareT dest = from + delta;
-        while (dest != to) {
-            if (Board[dest] != EMPTY) { return false; }
-            dest += delta;
-        }
-
-    } else if (mover == KNIGHT) {
-        if (! square_IsKnightHop (from, to)) { return false; }
-
-    } else /* (mover == KING) */ {
-        colorT enemy = color_Flip(ToMove);
-        if (square_Adjacent (to, GetKingSquare(enemy))) { return false; }
-        if (! square_Adjacent (from, to)) {
-            // The move must be castling, or illegal.
-            if (IsKingInCheck()) { return false; }
-            MoveList mlist;
-            GenCastling (&mlist);
-            return std::find(mlist.begin(), mlist.end(), cmpMove(*sm)) != mlist.end();
+        GenKingMoves (mlist, genType);
+        if (!numChecks && genNonCaptures) {
+            GenCastling(mlist);
         }
     }
-
-    // The move looks good, but does it leave the king in check?
-    squareT kingSq = (mover == KING) ? to : GetKingSquare(ToMove);
-    colorT enemy = color_Flip(ToMove);
-    DoSimpleMove (sm);
-    uint nchecks = CalcAttacks (enemy, kingSq, NULL);
-    UndoSimpleMove (sm);
-    return (nchecks == 0);
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::MatchPawnMove():
-//      Sets the LegalMoves list to contain the matching pawn move,
-//      if there is one.
-//
-errorT
-Position::MatchPawnMove (MoveList * mlist, fyleT fromFyle, squareT to, pieceT promote)
-{
-    ASSERT(mlist != NULL);
-    pieceT promote2 = promote;
-
-    mlist->Clear();
-
-    sint diff = (int)square_Fyle(to) - (int)fromFyle;
-    if (diff < -1  ||  diff > 1) { return ERROR_InvalidMove; }
-    pieceT pawn;
-    rankT toRank = square_Rank(to);
-    fyleT toFyle = square_Fyle(to);
-    rankT promoteRank = (ToMove == WHITE ? RANK_8 : RANK_1);
-
-    // from is the from square; backup is the alternative from square
-    // for a pawn move two squares forward.
-
-    squareT from, backup = NS;
-
-    if (ToMove == WHITE) {
-        pawn = WP;
-        if (toRank < RANK_3) { return ERROR_InvalidMove; }
-        from = square_Make(fromFyle, toRank - 1);
-        if (toRank == RANK_4  &&  fromFyle == toFyle) { backup = to - 16; }
-    } else {
-        pawn = BP;
-        if (toRank > RANK_6) { return ERROR_InvalidMove; }
-        from = square_Make(fromFyle, toRank + 1);
-        if (toRank == RANK_5  &&  fromFyle == toFyle) { backup = to + 16; }
-    }
-
-    // See if the promotion piece is valid:
-
-    if (toRank == promoteRank) {
-        // if (promote == EMPTY)  { return ERROR_InvalidMove; }
-        if (promote == EMPTY)  {
-          // autopromote to queen
-          promote2 = (ToMove == WHITE ? WQ : BQ);
-        }
-    } else {
-        if (promote != EMPTY)  { return ERROR_InvalidMove; }
-    }
-
-    if (Board[from] != pawn) {
-        // No match; but it could be a foward-two-squares move:
-        if (backup == NS || Board[from] != EMPTY || Board[backup] != pawn) {
-            // A forward-two-squares move is impossible.
-            return ERROR_InvalidMove;
-        }
-        from = backup;
-    }
-
-    // OK, now 'from' is the only possible from-square. Is the move legal?
-    // We make the move on the board and see if the King is in check.
-
-    uint legal = 0;
-    if (fromFyle == toFyle) {
-        // Not a capture:
-
-        if (Board[to] != EMPTY) { return ERROR_InvalidMove; }
-        Board[to] = pawn;  Board[from] = EMPTY;
-        if (CalcNumChecks (GetKingSquare()) == 0) {
-            legal = 1;
-        }
-       Board[to] = EMPTY; Board[from] = pawn;
-
-    } else {
-        // It is a capture -- is it legal?
+// Return true if moving the piece from square @e from to square @e dest
+// (NULL_SQUARE if the piece is captured) would leave the king in check.
+// It can also be used to check if the last move created a discovered check.
+static bool xray_check(Position const& pos, squareT from, squareT to) {
+	const auto [atk_piece, atk_sq] = movegen::opens_ray(
+	    from, to, pos.GetKingSquare(),
+	    [&](auto sq) { return pos.GetPiece(sq) != EMPTY; });
+
+	if (atk_piece == INVALID_PIECE)
+		return false;
+
+	const auto piece = pos.GetPiece(atk_sq);
+	const auto pt = piece_Type(piece);
+	const auto col = piece_Color_NotEmpty(piece);
+	return col != pos.GetToMove() && (pt == QUEEN || pt == atk_piece);
+}
+
+/// Check that the move is contained within the board, that a piece of the wrong
+/// color is not moved, that a king or piece of the same color is not captured,
+/// and that only pawns are promoted.
+static bool invalid_move(Position const& pos, squareT from, squareT to,
+                         pieceT promo) {
+	if (from > H8 || to > H8)
+		return true; // Invalid square
+
+	const auto toMove = pos.GetToMove();
+	const auto mover = pos.GetPiece(from);
+	const auto captured = pos.GetPiece(to);
+	const auto pt = piece_Type(mover);
+	return piece_Color(mover) != toMove || // Wrong side to move
+	       piece_Type(captured) == KING || // Capturing the king
+	       (promo != EMPTY && pt != PAWN); // Only pawn can promote
+}
+
+/// Return NULL_SQUARE (if the move is not pseudo legal) or the captured square
+/// (which is different from @e to for en passant moves).
+static squareT pseudo_legal(Position const& pos, squareT from, squareT to,
+                            pieceT promo) {
+	const auto toMove = pos.GetToMove();
+	const auto captured = pos.GetPiece(to);
+	const auto pt = piece_Type(pos.GetPiece(from));
+	if (!movegen::pseudo(from, to, toMove, pt,
+	                     [&](auto sq) { return pos.GetPiece(sq) != EMPTY; }))
+		return NULL_SQUARE; // Invalid move
+
+	auto captured_sq = to;
+	if (pt == PAWN) {
+		if (captured == EMPTY && square_Fyle(from) != square_Fyle(to)) {
+			captured_sq = (toMove == WHITE) ? to - 8 : to + 8;
+			const auto enemy = color_Flip(toMove);
+			if (to != pos.GetEPTarget() ||
+			    pos.GetPiece(captured_sq) != piece_Make(enemy, PAWN))
+				return NULL_SQUARE; // Invalid en passant
+		}
+		if (RANK_8 != square_Rank(square_Relative(toMove, to))) {
+			if (promo != EMPTY)
+				return NULL_SQUARE; // Wrong promotion rank
+		} else {
+			if (promo != QUEEN && promo != ROOK && promo != BISHOP &&
+			    promo != KNIGHT)
+				return NULL_SQUARE; // Wrong promotion piece type
+		}
+	}
+	return captured_sq;
+}
 
-        pieceT captured = Board[to];
-        if (captured == EMPTY) {
-            // Must be an en passant or illegal move.
-            if (to != EPTarget) { return ERROR_InvalidMove; }
-            squareT epSquare = square_Make(toFyle, square_Rank(from));
-
-            pieceT enemyPawn = piece_Make (color_Flip(ToMove), PAWN);
-            // If following assert fails, eptarget was corrupt
-            ASSERT (Board[epSquare] == enemyPawn);
-
-            Board[to] = pawn; Board[from] = EMPTY;
-            Board[epSquare] = EMPTY;
-            Material[enemyPawn] --;
-            if (CalcNumChecks (GetKingSquare()) == 0) { legal = 1; }
-            Board[epSquare] = enemyPawn;
-            Board[to] = EMPTY;
-            Board[from] = pawn;
-            Material[enemyPawn]++;
+/// Return the captured square (which is different from @e to for en passant
+/// moves) if the move is pseudo legal and the moving piece is not pinned.
+/// Return NULL_SQUARE otherwise.
+/// The move is also legal if the king is not in check or if there is only one
+/// attacker and it is captured or blocked.
+static squareT pseudo_not_pinned(Position const& pos, squareT from, squareT to,
+                                 pieceT promo) {
+	const auto captured_sq = pseudo_legal(pos, from, to, promo);
+	if (captured_sq == NULL_SQUARE)
+		return NULL_SQUARE; // Invalid move
+
+	if (captured_sq != to && xray_check(pos, captured_sq, NULL_SQUARE))
+		return NULL_SQUARE; // Capturing en passant leaves our king in check
+
+	return xray_check(pos, from, to) ? NULL_SQUARE : captured_sq;
+}
+
+/// Returns:
+///  0 -> invalid move
+///  1 -> valid normal move
+///  2 -> valid castle king side
+/// -2 -> valid castle queen side
+int Position::IsLegalMove(squareT from, squareT to, pieceT promo) const {
+	if (invalid_move(*this, from, to, promo))
+		return 0;
+
+	const auto king_sq = GetKingSquare();
+	const auto captured_sq = piece_Color(GetPiece(to)) == ToMove
+	                             ? NULL_SQUARE // Capturing its own piece
+	                             : pseudo_legal(*this, from, to, promo);
+	if (captured_sq == NULL_SQUARE) {
+		if (promo != EMPTY || from != king_sq)
+			return 0; // Invalid move
+
+		const bool king_side = to > from;
+		const auto castleStd = square_Relative(ToMove, king_side ? G1 : C1);
+		const auto castle960 = castleRookSq(ToMove, king_side);
+		if ((to == castleStd || to == castle960) && canCastle(king_side) &&
+		    !under_attack(king_sq))
+			return king_side ? 2 : -2;
 
-        } else {
-            if (piece_Color(captured) == ToMove) {
-                // Capturing a friendly!
-                return ERROR_InvalidMove;
-            } else {
-                // A regular capture. See if it leaves King in check:
-                Board[to] = pawn;  Board[from] = EMPTY;
-                Material[captured]--;
-                if (CalcNumChecks (GetKingSquare()) == 0) {
-                    legal = 1;
-                }
-                Material[captured]++;
-                Board[to] = captured; Board[from] = pawn;
-            }
-        }
-    }
+		return 0; // Invalid move
+	}
 
-    if (legal == 1) {
-        AddLegalMove (mlist, from, to, promote2);
-        return OK;
-    }
-    return ERROR_InvalidMove;
+	const auto target_sq = (from == king_sq) ? to : king_sq;
+	auto not_empty = [&](auto sq) {
+		return sq == to ||
+		       (sq != from && sq != captured_sq && GetPiece(sq) != EMPTY);
+	};
+	return under_attack(target_sq, captured_sq, not_empty) ? 0 : 1;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1148,9 +1064,7 @@ Position::GenCheckEvasions (MoveList * m
     }
 
     // Now king moves -- just compute them normally:
-    if (mask == EMPTY  ||  mask == KING) { GenKingMoves(mlist, genType, false); }
-
-    return;
+    if (mask == EMPTY  ||  mask == KING) { GenKingMoves(mlist, genType); }
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1169,7 +1083,7 @@ Position::TreeCalcAttacks(colorT side, s
     if (smPtr->to == target) {
       if (piece_IsKing(Board[target])) return -1;
       moveCount++;
-      DoSimpleMove(smPtr);
+      DoSimpleMove(*smPtr);
       int score = TreeCalcAttacks(color_Flip(side), target);
       UndoSimpleMove(smPtr);
       if (!score && ++zeroCount > 1) return -2;
@@ -1195,7 +1109,7 @@ Position::TreeCalcAttacks(colorT side, s
 //      Material[]) and detect whether they leave the king in check,
 //      without having to update other information.
 uint
-Position::CalcAttacks (colorT side, squareT target, SquareList * fromSquares)
+Position::CalcAttacks (colorT side, squareT target, SquareList * fromSquares) const
 {
     // If squares is NULL, caller doesn't want a list of the squares of
     // attacking pieces. To avoid comparing fromSquares with NULL every time
@@ -1337,74 +1251,6 @@ Position::CalcAttacks (colorT side, squa
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::IsKingInCheckDir
-//   Returns true if the King of the side to move is attacked
-//   by an enemy sliding piece (Queen/Rook/Bishop) from the
-//   specified direction.
-bool
-Position::IsKingInCheckDir (directionT dir)
-{
-    ASSERT (dir != NULL_DIR);
-    squareT kingSq = GetKingSquare(ToMove);
-    colorT enemy = color_Flip(ToMove);
-    bool isDiagonal = direction_IsDiagonal(dir);
-    pieceT queen = piece_Make (enemy, QUEEN);
-    pieceT slider = piece_Make (enemy, (isDiagonal ? BISHOP : ROOK));
-
-    // First, make sure the enemy has sliding pieces that could give check:
-    uint nSliders = PieceCount(queen) + PieceCount(slider);
-    if (nSliders == 0) { return false; }
-
-    // Now make sure the enemy has a sliding piece on the appropriate
-    // rank, file or diagonal:
-    fyleT fyle = square_Fyle (kingSq);
-    rankT rank = square_Rank (kingSq);
-    leftDiagT ldiag = square_LeftDiag (kingSq);
-    rightDiagT rdiag = square_RightDiag (kingSq);
-
-    switch (dir) {
-    case UP:
-    case DOWN:
-        nSliders = FyleCount(queen,fyle) + FyleCount(slider,fyle);
-        break;
-    case LEFT:
-    case RIGHT:
-        nSliders = RankCount(queen,rank) + RankCount(slider,rank);
-        break;
-    case UP_LEFT:
-    case DOWN_RIGHT:
-        nSliders = LeftDiagCount(queen,ldiag) + LeftDiagCount(slider,ldiag);
-        break;
-    case UP_RIGHT:
-    case DOWN_LEFT:
-        nSliders = RightDiagCount(queen,rdiag) + RightDiagCount(slider,rdiag);
-        break;
-    }
-    if (nSliders == 0) { return false; }
-
-    // Now move along the specified direction looking for a checking piece:
-    squareT dest = kingSq;
-    squareT last = square_Last (kingSq, dir);
-    int delta = direction_Delta (dir);
-
-    while (dest != last) {
-        dest += delta;
-        pieceT p = Board[dest];
-        if (p == EMPTY) {
-             // empty square: keep searching
-        } else if (p == queen  ||  p == slider) {
-            // Found an checking slider piece
-            return true;
-        } else {
-            // Found a piece, but not an enemy queen or rook/bishop
-            break;
-        }
-    }
-
-    return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Position::IsKingInCheck
 //   Returns true if the king of the side to move is in check.
 //   If the specified move is not NULL, it must be the legal move
@@ -1413,60 +1259,29 @@ Position::IsKingInCheckDir (directionT d
 //   move could not have left the king in check.
 //
 bool
-Position::IsKingInCheck (simpleMoveT * sm)
+Position::IsKingInCheck (simpleMoveT const& sm)
 {
-    if (sm == NULL) { return IsKingInCheck(); }
-
-    squareT kingSq = GetKingSquare(ToMove);
-    pieceT p = piece_Type (sm->movingPiece);
-    if (sm->promote != EMPTY) { p = piece_Type(sm->promote); }
+    pieceT p = (sm.promote == EMPTY) ? piece_Type(sm.movingPiece) : sm.promote;
 
     // No optimization of the last move was castling:
-    if (p == KING  &&  square_Fyle(sm->from) == E_FYLE) {
-        fyleT toFyle = square_Fyle(sm->to);
-        if (toFyle == C_FYLE  ||  toFyle == G_FYLE) {
-            return IsKingInCheck();
-        }
+    if (sm.isCastle()) {
+        return IsKingInCheck();
     }
     // No optimization for en passant capture:
-    if (p == PAWN  &&  piece_Type(sm->capturedPiece) == PAWN) {
-        rankT fromRank = square_Rank(sm->from);
-        rankT capturedRank = square_Rank(sm->capturedSquare);
-        if (fromRank == capturedRank) { return IsKingInCheck(); }
-    }
-
-    if (p == PAWN) {
-        if (ToMove == WHITE) {
-            if (Material[BP] > 0) {
-                squareT sq = square_Move (kingSq, UP_LEFT);
-                if (Board[sq] == BP)  { return true; }
-                sq = square_Move (kingSq, UP_RIGHT);
-                if (Board[sq] == BP)  { return true; }
-            }
-        } else {
-            if (Material[WP] > 0) {
-                squareT sq = square_Move (kingSq, DOWN_LEFT);
-                if (Board[sq] == WP)  { return true; }
-                sq = square_Move (kingSq, DOWN_RIGHT);
-                if (Board[sq] == WP)  { return true; }
-            }
+    if (p == PAWN && piece_Type(sm.capturedPiece) == PAWN) {
+        rankT fromRank = square_Rank(sm.from);
+        rankT capturedRank = square_Rank(sm.capturedSquare);
+        if (fromRank == capturedRank) {
+            return IsKingInCheck();
         }
-    } else if (p == KNIGHT) {
-        if (square_IsKnightHop (kingSq, sm->to)) { return true; }
-    } else if (p == KING) {
-        // A king cannot directly check its adversary.
-    } else {
-        // A sliding piece:
-        directionT toDir = sqDir[kingSq][sm->to];
-        if (toDir != NULL_DIR  &&  IsKingInCheckDir(toDir)) { return true; }
     }
 
-    // Now look for a discovered check from a sliding piece:
-    directionT dir = sqDir[kingSq][sm->from];
-    if (dir != NULL_DIR  &&  IsKingInCheckDir(dir)) { return true; }
+    if (movegen::attack(sm.to, GetKingSquare(), color_Flip(ToMove), p,
+                        [&](auto sq) { return GetPiece(sq) != EMPTY; }))
+        return true;
 
-    ASSERT (IsKingInCheck() == false);
-    return false;
+    // Now look for a discovered check from a sliding piece:
+    return xray_check(*this, sm.from, sm.to);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1568,6 +1383,73 @@ Position::IsPromoMove (squareT from, squ
     return 0;
 }
 
+/// Make a simpleMoveT.
+/// If @e promo != INVALID_PIECE it is a special move:
+/// promo == PAWN -> null move
+/// from != to -> promotion
+/// from == to && PAWN == KING -> castle kingside
+/// from == to && PAWN != KING -> castle queenside
+void Position::makeMove(squareT from, squareT to, pieceT promo,
+                        simpleMoveT& res) const {
+	res.castling = 0;
+	if (promo == EMPTY || promo == INVALID_PIECE) { // NORMAL MOVE
+		res.from = from;
+		res.to = to;
+		res.promote = EMPTY;
+	} else if (promo == PAWN) { // NULL MOVE
+		res.from = GetKingSquare(ToMove);
+		res.to = res.from;
+		res.promote = EMPTY;
+		res.movingPiece = KING;
+	} else if (from != to) { // PROMOTION
+		res.from = from;
+		res.to = to;
+		res.promote = promo;
+	} else { // CASTLING
+		res.from = GetKingSquare(ToMove);
+		if (isChess960()) {
+			res.to = castleRookSq(ToMove, promo == KING);
+		} else {
+			res.to = res.from;
+			res.to += promo == KING ? 2 : -2;
+		}
+		res.promote = EMPTY;
+		res.castling = 1;
+	}
+	fillMove(res);
+}
+
+/// Use the current position to retrieve all the information needed to create a
+/// SimpleMove which can also be used in UndoSimpleMove.
+void Position::fillMove(simpleMoveT& sm) const {
+	const auto from = sm.from;
+	const auto to = sm.to;
+	sm.movingPiece = GetPiece(sm.from);
+	sm.pieceNum = ListPos[from];
+	sm.castleFlags = Castling;
+	sm.epSquare = EPTarget;
+	sm.oldHalfMoveClock = HalfMoveClock;
+	sm.capturedSquare = to;
+	if (sm.isNullMove() || sm.isCastle()) {
+		sm.capturedPiece = EMPTY;
+	} else {
+		sm.capturedPiece = GetPiece(to);
+	}
+
+	// Handle en passant capture:
+	if (piece_Type(sm.movingPiece) == PAWN && sm.capturedPiece == EMPTY &&
+	    square_Fyle(from) != square_Fyle(to)) {
+		// This was an EP capture. We do not need to check it was a capture
+		// since if a pawn lands on EPTarget it must capture to get there.
+		sm.capturedSquare = WhiteToMove() ? to - 8 : to + 8;
+		sm.capturedPiece = GetPiece(sm.capturedSquare);
+		ASSERT(sm.capturedPiece == piece_Make(color_Flip(GetToMove()), PAWN));
+	}
+
+	if (sm.capturedPiece != EMPTY) {
+		sm.capturedNum = ListPos[sm.capturedSquare];
+	}
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Position::DoSimpleMove():
@@ -1578,120 +1460,96 @@ void
 Position::DoSimpleMove (simpleMoveT * sm)
 {
     ASSERT (sm != NULL);
-    squareT from = sm->from;
-    squareT to = sm->to;
-    pieceT p = Board[from];
-    pieceT ptype = piece_Type(p);
-    colorT enemy = color_Flip(ToMove);
-    ASSERT (p != EMPTY);
-
     // update move fields that (maybe) have not yet been set:
+	fillMove(*sm);
+	DoSimpleMove(*sm);
+}
 
-    sm->pieceNum = ListPos[from];
-    sm->capturedPiece = Board[to];
-    sm->capturedSquare = to;
-    sm->castleFlags = Castling;
-    sm->epSquare = EPTarget;
-    sm->oldHalfMoveClock = HalfMoveClock;
+void Position::DoSimpleMove(simpleMoveT const& sm) {
+    const auto from = sm.from;
+    const auto to = sm.to;
+    const auto promo = sm.promote;
+    const auto movingPiece = GetPiece(from);
+    const auto ptype = piece_Type(movingPiece);
+    const auto pieceNum = ListPos[from];
+    const auto color = ToMove;
+    const auto enemy = color_Flip(color);
 
     HalfMoveClock++;
     PlyCounter++;
+    EPTarget = NULL_SQUARE;
+    ToMove = enemy;
 
-    // Check for a null (empty) move:
-    if (sm->isNullMove()) {
-        ToMove = enemy;
-        EPTarget = NULL_SQUARE;
-        return;
-    }
-
-    // Handle en passant capture:
-
-    if (ptype == PAWN  &&  sm->capturedPiece == EMPTY
-            && square_Fyle(from) != square_Fyle(to)) {
+	auto addPiece = [&](auto idx, auto pieceType, squareT destSq) {
+		List[color][idx] = destSq;
+		ListPos[destSq] = idx;
+		AddToBoard(piece_Make(color, pieceType), destSq);
+	};
 
-        // This was an EP capture. We do not need to check it was a capture
-        // since if a pawn lands on EPTarget it must capture to get there.
+	if (sm.isNullMove())
+		return;
 
-        pieceT enemyPawn = piece_Make(enemy, PAWN);
-        sm->capturedSquare = (ToMove == WHITE ? (to - 8) : (to + 8));
-        ASSERT (Board[sm->capturedSquare] == enemyPawn);
-        sm->capturedPiece = enemyPawn;
-    }
+	if (ptype == KING) {
+		ClearCastlingFlags(color);
+		if (auto castleSide = sm.isCastle()) {
+			squareT rookfrom, rookto, kingTo;
+			if (castleSide > 0) {
+				kingTo = square_Relative(color, G1);
+				rookfrom = castleRookSq(color, true);
+				rookto = kingTo - 1;
+			} else {
+				kingTo = square_Relative(color, C1);
+				rookfrom = castleRookSq(color, false);
+				rookto = kingTo + 1;
+			}
+			const int kingIdx = 0;
+			const int rookIdx = ListPos[rookfrom];
+			RemoveFromBoard(piece_Make(color, ROOK), rookfrom);
+			RemoveFromBoard(piece_Make(color, KING), GetKingSquare(color));
+			addPiece(kingIdx, KING, kingTo);
+			addPiece(rookIdx, ROOK, rookto);
+			return;
+		}
+	}
 
     // handle captures:
-
-    if (sm->capturedPiece != EMPTY) {
-        ASSERT (piece_Type(sm->capturedPiece) != KING);
-        sm->capturedNum = ListPos[sm->capturedSquare];
+    if (sm.capturedPiece != EMPTY) {
+        ASSERT(piece_Type(sm.capturedPiece) != KING);
         // update opponents List of pieces
         Count[enemy]--;
-        ListPos[List[enemy][Count[enemy]]] = sm->capturedNum;
-        List[enemy][sm->capturedNum] = List[enemy][Count[enemy]];
-        Material[sm->capturedPiece]--;
+        ListPos[List[enemy][Count[enemy]]] = sm.capturedNum;
+        List[enemy][sm.capturedNum] = List[enemy][Count[enemy]];
+        Material[sm.capturedPiece]--;
         HalfMoveClock = 0;
-        RemoveFromBoard (sm->capturedPiece, sm->capturedSquare);
-    }
-
-    // handle promotion:
-
-    if (sm->promote != EMPTY) {
-        ASSERT (p == piece_Make(ToMove, PAWN));
-        Material[p]--;
-        RemoveFromBoard (p, from);
-        p = piece_Make(ToMove, sm->promote);
-        Material[p]++;
-        AddToBoard (p, from);
+        RemoveFromBoard (sm.capturedPiece, sm.capturedSquare);
     }
 
     // now make the move:
-    List[ToMove][sm->pieceNum] = to;
-    ListPos[to] = sm->pieceNum;
-    RemoveFromBoard (p, from);
-    AddToBoard (p, to);
-
-    // handle Castling:
-
-    if (ptype == KING  &&  square_Fyle(from) == E_FYLE  &&
-            (square_Fyle(to) == C_FYLE  ||  square_Fyle(to) == G_FYLE)) {
-        squareT rookfrom, rookto;
-        pieceT rook = piece_Make (ToMove, ROOK);
-        if (square_Fyle(to) == C_FYLE) {
-            rookfrom = to - 2;
-            rookto = to + 1;
-        } else {
-            rookfrom = to + 1;
-            rookto = to - 1;
-        }
-        ListPos[rookto] = ListPos[rookfrom];
-        List[ToMove][ListPos[rookto]] = rookto;
-        RemoveFromBoard (rook, rookfrom);
-        AddToBoard (rook, rookto);
+    RemoveFromBoard(movingPiece, from);
+    if (promo != EMPTY) {
+        ASSERT(movingPiece == piece_Make(color, PAWN));
+        Material[movingPiece]--;
+        Material[piece_Make(color, promo)]++;
+        addPiece(pieceNum, promo, to);
+    } else {
+        addPiece(pieceNum, ptype, to);
     }
 
     // Handle clearing of castling flags:
-
     if (Castling) {
-        if (ptype == KING) {   // The king moved.
-            SetCastling (ToMove, QSIDE, false);
-            SetCastling (ToMove, KSIDE, false);
-        }
         // See if a rook moved or was captured:
-        if (ToMove == WHITE) {
-            if (from == A1)  { SetCastling (WHITE, QSIDE, false); }
-            if (from == H1)  { SetCastling (WHITE, KSIDE, false); }
-            if (to == A8)    { SetCastling (BLACK, QSIDE, false); }
-            if (to == H8)    { SetCastling (BLACK, KSIDE, false); }
-        } else {
-            if (from == A8)  { SetCastling (BLACK, QSIDE, false); }
-            if (from == H8)  { SetCastling (BLACK, KSIDE, false); }
-            if (to == A1)    { SetCastling (WHITE, QSIDE, false); }
-            if (to == H1)    { SetCastling (WHITE, KSIDE, false); }
-        }
+		if (from == castleRookSq(color, false))
+			ClearCastling(color, QSIDE);
+		if (from == castleRookSq(color, true))
+			ClearCastling(color, KSIDE);
+		if (to == castleRookSq(enemy, false))
+			ClearCastling(enemy, QSIDE);
+		if (to == castleRookSq(enemy, true))
+			ClearCastling(enemy, KSIDE);
     }
 
     // Set the EPTarget square, if a pawn advanced two squares and an
     // enemy pawn is on a square where en passant may be possible.
-    EPTarget = NULL_SQUARE;
     if (ptype == PAWN) {
         rankT fromRank = square_Rank(from);
         rankT toRank = square_Rank(to);
@@ -1707,9 +1565,6 @@ Position::DoSimpleMove (simpleMoveT * sm
         }
         HalfMoveClock = 0; // 50-move clock resets on pawn moves.
     }
-
-    ToMove = enemy;
-    return;
 }
 
 
@@ -1718,30 +1573,55 @@ Position::DoSimpleMove (simpleMoveT * sm
 //      Take back a simple move that has been made with DoSimpleMove().
 //
 void
-Position::UndoSimpleMove (simpleMoveT * m)
+Position::UndoSimpleMove (simpleMoveT const* m)
 {
     ASSERT (m != NULL);
-    squareT from = m->from;
-    squareT to = m->to;
+    const squareT from = m->from;
+    const squareT to = m->to;
+    const auto pieceNum = ListPos[to];
     pieceT p = Board[to];
     EPTarget = m->epSquare;
     Castling = m->castleFlags;
     HalfMoveClock = m->oldHalfMoveClock;
     PlyCounter--;
     ToMove = color_Flip(ToMove);
-    m->pieceNum = ListPos[to];
 
     // Check for a null move:
     if (m->isNullMove()) {
         return;
     }
 
+	auto addPiece = [&](auto idx, auto pieceType, squareT destSq) {
+		List[ToMove][idx] = destSq;
+		ListPos[destSq] = idx;
+		AddToBoard(piece_Make(ToMove, pieceType), destSq);
+	};
+
+	// handle Castling:
+		if (auto castleSide = m->isCastle()) {
+			const auto kingSq = GetKingSquare(ToMove);
+			squareT rookfrom, rookto;
+			if (castleSide > 0) {
+				rookfrom = kingSq - 1;
+				rookto = castleRookSq(ToMove, true);
+			} else {
+				rookfrom = kingSq + 1;
+				rookto = castleRookSq(ToMove, false);
+			}
+			const int kingIdx = 0;
+			const int rookIdx = ListPos[rookfrom];
+			RemoveFromBoard(piece_Make(ToMove, KING), kingSq);
+			RemoveFromBoard(piece_Make(ToMove, ROOK), rookfrom);
+			addPiece(rookIdx, ROOK, rookto);
+			addPiece(kingIdx, KING, from);
+			return;
+		}
+
     // Handle a capture: insert piece back into piecelist.
     // This works for EP captures too, since the square of the captured
     // piece is in the "capturedSquare" field rather than assuming the
     // value of the "to" field. The only time these two fields are
     // different is for an en passant move.
-
     if (m->capturedPiece != EMPTY) {
         colorT c = color_Flip(ToMove);
         ListPos[List[c][m->capturedNum]] = Count[c];
@@ -1753,7 +1633,6 @@ Position::UndoSimpleMove (simpleMoveT *
     }
 
     // handle promotion:
-
     if (m->promote != EMPTY) {
         Material[p]--;
         RemoveFromBoard (p, to);
@@ -1763,32 +1642,13 @@ Position::UndoSimpleMove (simpleMoveT *
     }
 
     // now make the move:
-
-    List[ToMove][m->pieceNum] = from;
-    ListPos[from] = m->pieceNum;
+    List[ToMove][pieceNum] = from;
+    ListPos[from] = pieceNum;
     RemoveFromBoard (p, to);
     AddToBoard (p, from);
     if (m->capturedPiece != EMPTY) {
         AddToBoard (m->capturedPiece, m->capturedSquare);
     }
-
-    // handle Castling:
-
-    if ((piece_Type(p) == KING) && square_Fyle(from) == E_FYLE
-            && (square_Fyle(to) == C_FYLE || square_Fyle(to) == G_FYLE)) {
-        squareT rookfrom, rookto;
-        pieceT rook = (ToMove == WHITE? WR : BR);
-        if (square_Fyle(to) == C_FYLE) {
-            rookfrom = to - 2;   rookto = to + 1;
-        } else {
-            rookfrom = to + 1;   rookto = to - 1;
-        }
-        ListPos[rookfrom] = ListPos[rookto];
-        List[ToMove][ListPos[rookto]] = rookfrom;
-        RemoveFromBoard (rook, rookto);
-        AddToBoard (rook, rookfrom);
-    }
-    return;
 }
 
 
@@ -1888,11 +1748,9 @@ void
 Position::MakeSANString (simpleMoveT * m, char * s, sanFlagT flag)
 {
     ASSERT (m != NULL  &&  s != NULL);
-
-    // Make sure m->pieceNum is updated:
-    m->pieceNum = ListPos[m->from];
-    squareT from = List[ToMove][m->pieceNum];
-    squareT to   = m->to;
+    ASSERT(m->from == List[ToMove][ListPos[m->from]]);
+    const squareT from = m->from;
+    const squareT to   = m->to;
     char * c     = s;
     pieceT piece = Board[from];
     pieceT p = piece_Type(piece);
@@ -1914,28 +1772,20 @@ Position::MakeSANString (simpleMoveT * m
         if (m->isNullMove()) {
             //*c++ = 'n'; *c++ = 'u'; *c++ = 'l'; *c++ = 'l';
             *c++ = '-'; *c++ = '-';
-        } else {
-            switch (m->isCastle()) {
-            case 0:
-                *c++ = 'K';
-                if (Board[to] != EMPTY)
-                    *c++ = 'x';
-                *c++ = square_FyleChar(to);
-                *c++ = square_RankChar(to);
-                break;
-            case 1:
-                *c++ = 'O';
+        } else if (auto castle = m->isCastle()) {
+            *c++ = 'O';
+            *c++ = '-';
+            *c++ = 'O';
+            if (castle < 0) {
                 *c++ = '-';
                 *c++ = 'O';
-                break;
-            case 2:
-                *c++ = 'O';
-                *c++ = '-';
-                *c++ = 'O';
-                *c++ = '-';
-                *c++ = 'O';
-                break;
             }
+        } else {
+            *c++ = 'K';
+            if (Board[to] != EMPTY)
+                *c++ = 'x';
+            *c++ = square_FyleChar(to);
+            *c++ = square_RankChar(to);
         }
 
     } else {    // Queen/Rook/Bishop/Knight
@@ -1950,18 +1800,9 @@ Position::MakeSANString (simpleMoveT * m
                 if (sq == from || Board[sq] != piece)
                     continue;
 
-                if (!movegen::pseudo(sq, to, ToMove, p, Board, EMPTY))
+                if (pseudo_not_pinned(*this, sq, to, EMPTY) == NULL_SQUARE)
                     continue; // Skip illegal move
 
-                std::pair<pieceT, squareT> pin =
-                    movegen::opens_ray(sq, to, GetKingSquare(), Board, EMPTY);
-                if (pin.first != INVALID_PIECE &&
-                    piece_Color_NotEmpty(Board[pin.second]) != ToMove) {
-                    pieceT pt = piece_Type(Board[pin.second]);
-                    if (pt == QUEEN || pt == pin.first)
-                        continue; // Skip pinned piece
-                }
-
                 // Ambiguity:
                 // 1 (0001) --> need from-file (preferred) or from-rank
                 // 3 (0011) --> need from-file
@@ -1987,36 +1828,11 @@ Position::MakeSANString (simpleMoveT * m
         *c++ = square_RankChar(to);
     }
 
-    bool check;
-    if (flag != SAN_NO_CHECKTEST) {
-        squareT oldTo = Board[to];
-        Board[to] = Board[from];
-        Board[from] = EMPTY;
-        squareT enemyKingSq = GetEnemyKingSquare();
-        check = (p != KING) &&
-                movegen::attack(to, enemyKingSq, ToMove, p, Board, EMPTY);
-        if (!check) {
-            bool enpassant = (p == PAWN && oldTo == EMPTY &&
-                              square_Fyle(from) != square_Fyle(to));
-            if (!enpassant && (p != KING || !m->isCastle()) &&
-                !movegen::attack_slider(from, enemyKingSq, QUEEN, Board,
-                                        EMPTY)) {
-                flag = SAN_NO_CHECKTEST;
-            }
-
-        } else if (flag != SAN_MATETEST) {
-            *c++ = '+';
-            flag = SAN_NO_CHECKTEST;
-        }
-        Board[from] = Board[to];
-        Board[to] = oldTo;
-    }
-
     // Now do the check or mate symbol:
     if (flag != SAN_NO_CHECKTEST) {
         // Now we make the move to test for check:
         DoSimpleMove (m);
-        if (check || CalcNumChecks(GetKingSquare()) > 0) {
+        if (IsKingInCheck(*m)) {
             char ch = '+';
             if (flag == SAN_MATETEST) {
                 MoveList mlist;
@@ -2030,44 +1846,46 @@ Position::MakeSANString (simpleMoveT * m
     *c = 0;
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::MakeUCIString():
-//      Make the UCI string for a simpleMove.
-//
-void
-Position::MakeUCIString (simpleMoveT * m, char * s)
-{
-    ASSERT (m != NULL  &&  s != NULL);
-
-    // Make sure m->pieceNum is updated:
-    m->pieceNum = ListPos[m->from];
-    pieceT  p    = piece_Type (Board[List[ToMove][m->pieceNum]]);
-    squareT from = List[ToMove][m->pieceNum];
-    squareT to   = m->to;
-
-    char * c     = s;
+// Make on the board a sequence of moves in coordinate notation.
+// Convert and store the moves in SAN notation if @e toSAN is not nullptr.
+errorT Position::MakeCoordMoves(const char* moves, size_t moveslen,
+                                std::string* toSAN) {
+    auto is_space = [](char ch) {
+        return isspace(static_cast<unsigned char>(ch));
+    };
 
-    if (from == to && to != NULL_SQUARE) {
-      // UCI standard for null move
-        c[0] = '0';
-        c[1] = '0';
-        c[2] = '0';
-        c[3] = '0';
-        c[4] = 0;
-        return;
+    while (moveslen > 0 && is_space(moves[moveslen - 1])) {
+        --moveslen; // Trim right
     }
+    const char* end = moves + moveslen;
+    moves = std::find_if_not(moves, end, is_space);
 
-    *c++ = square_FyleChar(from);
-    *c++ = square_RankChar(from);
-    *c++ = square_FyleChar(to);
-    *c++ = square_RankChar(to);
-    if (p == PAWN) {
-        if ((square_Rank(to)==RANK_1) || (square_Rank(to)==RANK_8)) {
-            *c++ = piece_Char(m->promote);
+    while (auto len = std::find_if(moves, end, is_space) - moves) {
+        simpleMoveT sm;
+        if (auto err = ReadCoordMove(&sm, moves, len, false))
+            return err;
+
+        moves = std::find_if_not(moves + len, end, is_space);
+
+        if (toSAN) {
+            char san[8];
+            MakeSANString(&sm, san,
+                          moves != end ? SAN_CHECKTEST : SAN_MATETEST);
+            if (WhiteToMove()) {
+                toSAN->append(std::to_string(GetFullMoveCount()));
+                toSAN->push_back('.');
+            } else if (toSAN->empty()) {
+                toSAN->append(std::to_string(GetFullMoveCount()));
+                toSAN->append("...");
+            }
+            toSAN->append(san);
+            if (moves != end)
+                toSAN->push_back(' ');
         }
-    }
 
-    *c = 0;
+        DoSimpleMove(sm);
+    }
+    return OK;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2078,47 +1896,41 @@ Position::MakeUCIString (simpleMoveT * m
 //      If "reverse" is true, coordinates in reverse order are acceptable,
 //      e.g. "f3g1" for 1.Nf3.
 //
-errorT Position::ReadCoordMove(simpleMoveT* m, const char* str, int slen,
+errorT Position::ReadCoordMove(simpleMoveT* m, const char* str, size_t slen,
                                bool reverse) {
-    ASSERT (m != NULL  &&  str != NULL);
-    fyleT fromFyle, toFyle;
-    rankT fromRank, toRank;
-    squareT from, to;
-    pieceT promo = EMPTY;
+    ASSERT(m != NULL && str != NULL);
 
+    auto promote = EMPTY;
     if (slen == 5) {
-        promo = piece_FromChar(toupper(str[4]));
-    } else if (slen != 4) { return ERROR_InvalidMove; }
-
-    fromFyle = fyle_FromChar (str[0]);
-    fromRank = rank_FromChar (str[1]);
-    from = square_Make (fromFyle, fromRank);
-    if (from == NS) { return ERROR_InvalidMove; }
-
-    toFyle = fyle_FromChar (str[2]);
-    toRank = rank_FromChar (str[3]);
-    to = square_Make (toFyle, toRank);
-    if (to == NS) { return ERROR_InvalidMove; }
+        promote = piece_FromChar(toupper(str[4]));
+    } else if (slen != 4) {
+        return ERROR_InvalidMove;
+    }
+
+    const auto fromFyle = fyle_FromChar(str[0]);
+    const auto fromRank = rank_FromChar(str[1]);
+    const auto toFyle = fyle_FromChar(str[2]);
+    const auto toRank = rank_FromChar(str[3]);
+    auto from = square_Make(fromFyle, fromRank);
+    auto to = square_Make(toFyle, toRank);
+
+    auto legal = IsLegalMove(from, to, promote);
+    if (!legal && reverse) {
+        std::swap(from, to);
+        legal = IsLegalMove(from, to, promote);
+    }
+    if (!legal)
+        return ERROR_InvalidMove;
 
-    MoveList mlist;
-    GenerateMoves(&mlist);
-    for (size_t i = 0, n = mlist.Size(); i < n; i++) {
-        simpleMoveT* sm = mlist.Get(i);
-        if (sm->promote == promo) {
-            if (sm->from == from  &&  sm->to == to) {
-                *m = *sm;
-                return OK;
-            }
-            if (reverse  &&  sm->to == from  &&  sm->from == to) {
-                *m = *sm;
-                return OK;
-            }
-        }
+    if (legal == 1) {
+        makeMove(from, to, promote, *m);
+    } else {
+        makeMove(from, from, legal == 2 ? KING : QUEEN, *m);
     }
-    return ERROR_InvalidMove;
+    return OK;
 }
 
-static int trimCheck(const char* str, int slen) {
+static size_t trimCheck(const char* str, size_t slen) {
 	while (slen > 0) { // trim mate '#' or check '+'
 		--slen;
 		if (str[slen] != '#' && str[slen] != '+') {
@@ -2129,11 +1941,10 @@ static int trimCheck(const char* str, in
 	return slen;
 }
 
-errorT Position::ReadMovePawn(simpleMoveT* sm, const char* str, int slen,
+errorT Position::ReadMovePawn(simpleMoveT* sm, const char* str, size_t slen,
                               fyleT frFyle) {
 	ASSERT(sm != NULL && str != NULL && frFyle <= H_FYLE);
 
-	slen = trimCheck(str, slen);
 	if (slen < 2)
 		return ERROR_InvalidMove;
 
@@ -2168,39 +1979,50 @@ errorT Position::ReadMovePawn(simpleMove
 	if (slen < 2)
 		return ERROR_InvalidMove;
 
+	auto isLegal = [&](fyleT toFyle, rankT toRank) {
+		static_assert(NO_RANK > 8 && (NO_RANK - 1) > 8 && (NO_RANK + 1) > 8);
+		const auto fromRank = (ToMove == WHITE) ? toRank - 1 : toRank + 1;
+		if (toFyle > 8 || fromRank <= 0 || fromRank >= 8)
+			return false;
+
+		auto from = square_Make(frFyle, fromRank);
+		const auto to = square_Make(toFyle, toRank);
+		const auto pawn = piece_Make(ToMove, PAWN);
+		if (GetPiece(from) == pawn && IsLegalMove(from, to, promo)) {
+			makeMove(from, to, promo, *sm);
+			return true;
+		}
+		if (frFyle == toFyle) {
+			from += (ToMove == WHITE) ? -8 : +8;
+			if (GetPiece(from) == pawn && IsLegalMove(from, to, promo)) {
+				makeMove(from, to, promo, *sm);
+				return true;
+			}
+		}
+		return false;
+	};
+
+	const auto toFile = fyle_FromChar(str[slen - 2]);
+	const auto toRank = rank_FromChar(str[slen - 1]);
+	if (isLegal(toFile, toRank))
+		return OK;
+
 	// Check for the compact form of capture with no rank,
 	// e.g. "ed" or "de=Q":
 	if (slen == 2 && (str[1] >= 'a' && str[1] <= 'h')) {
-		auto toFyle = fyle_FromChar(str[1]);
-		// Check each rank in turn, looking for the capture:
+		const auto compact_toFyle = fyle_FromChar(str[1]);
 		for (rankT r = RANK_1; r <= RANK_8; r++) {
-			auto to = square_Make(toFyle, r);
-			if (MatchPawnMove(&mlist, frFyle, to, promo) == OK) {
-				*sm = *(mlist.Get(0));
+			if (isLegal(compact_toFyle, r))
 				return OK;
-			}
 		}
-		// It is NOT a valid capture with no rank:
-		return ERROR_InvalidMove;
 	}
-
-	auto toFyle = fyle_FromChar(str[slen - 2]);
-	auto toRank = rank_FromChar(str[slen - 1]);
-	if (toRank == NO_RANK || toFyle == NO_FYLE)
-		return ERROR_InvalidMove;
-
-	auto to = square_Make(toFyle, toRank);
-	if (MatchPawnMove(&mlist, frFyle, to, promo) != OK)
-		return ERROR_InvalidMove;
-
-	*sm = *(mlist.Get(0));
-	return OK;
+	return ERROR_InvalidMove;
 }
 
-errorT Position::ReadMoveKing(simpleMoveT* sm, const char* str, int slen) {
+errorT Position::ReadMoveKing(simpleMoveT* sm, const char* str,
+                              size_t slen) const {
 	ASSERT(sm != NULL && str != NULL);
 
-	slen = trimCheck(str, slen);
 	if (slen < 3 || slen > 6)
 		return ERROR_InvalidMove;
 
@@ -2209,42 +2031,21 @@ errorT Position::ReadMoveKing(simpleMove
 	if (toRank == NO_RANK || toFyle == NO_FYLE)
 		return ERROR_InvalidMove;
 
-	auto target = square_Make(toFyle, toRank);
-	squareT kingSq = GetKingSquare(ToMove);
-	if (!movegen::valid_king(kingSq, target))
+	const auto from = GetKingSquare(ToMove);
+	const auto to = square_Make(toFyle, toRank);
+	const auto captured = GetPiece(to);
+	if (!movegen::valid_king(from, to) || piece_Color(captured) == ToMove ||
+	    piece_Type(captured) == KING)
 		return ERROR_InvalidMove;
 
-	pieceT captured = Board[target];
-	if (captured != EMPTY && (piece_Color_NotEmpty(captured) == ToMove ||
-	                          piece_Type(captured) == KING)) {
-		return ERROR_InvalidMove;
-	}
-
-	// XXX We should also check for adjacency to enemy King!!
-	if (movegen::valid_king(GetKingSquare(color_Flip(ToMove)), target))
-		return ERROR_InvalidMove;
-
-	// Now make the move on the Board and Material lists, and see if it
 	// leaves the King in check:
-	auto movingPiece = piece_Make(ToMove, KING);
-	Board[target] = movingPiece;
-	Board[kingSq] = EMPTY;
-	if (captured != EMPTY) {
-		Material[captured]--;
-	}
-	auto nChecks = CalcNumChecks(target);
-	if (captured != EMPTY) {
-		Material[captured]++;
-	}
-	Board[target] = captured;
-	Board[kingSq] = movingPiece;
-	if (nChecks)
+	auto not_empty = [&](auto sq) {
+		return sq != from && GetPiece(sq) != EMPTY;
+	};
+	if (under_attack(to, to, not_empty))
 		return ERROR_InvalidMove;
 
-	sm->from = kingSq;
-	sm->to = target;
-	sm->promote = EMPTY;
-	sm->movingPiece = movingPiece;
+	makeMove(from, to, INVALID_PIECE, *sm);
 	return OK;
 }
 
@@ -2254,13 +2055,12 @@ errorT Position::ReadMoveKing(simpleMove
 //      generates the legal move it corresponds to.
 //      Returns: OK or ERROR_InvalidMove.
 //
-errorT Position::ReadMove(simpleMoveT* sm, const char* str, int slen,
-                          pieceT piece) {
+errorT Position::ReadMove(simpleMoveT* sm, const char* str, size_t slen,
+                          pieceT piece) const {
 	ASSERT(sm != NULL && str != NULL);
 	ASSERT(piece == QUEEN || piece == ROOK || piece == BISHOP ||
 	       piece == KNIGHT);
 
-	slen = trimCheck(str, slen);
 	if (slen < 3 || slen > 6)
 		return ERROR_InvalidMove;
 
@@ -2270,12 +2070,6 @@ errorT Position::ReadMove(simpleMoveT* s
 		return ERROR_InvalidMove;
 	auto to = square_Make(toFyle, toRank);
 
-	pieceT captured = Board[to];
-	if (captured != EMPTY && (piece_Color_NotEmpty(captured) == ToMove ||
-	                          piece_Type(captured) == KING)) {
-		return ERROR_InvalidMove;
-	}
-
 	auto frFyle = NO_FYLE;
 	auto frRank = NO_RANK;
 	if (slen > 3) { // There is some ambiguity information in the input string.
@@ -2291,7 +2085,6 @@ errorT Position::ReadMove(simpleMoveT* s
 	int matchCount = 0;
 	auto movingPiece = piece_Make(ToMove, piece);
 	int nPieces = Material[movingPiece];
-	squareT kingSq = GetKingSquare(ToMove);
 	for (unsigned i = 1, n = Count[ToMove]; i < n && nPieces; i++) {
 		auto from = List[ToMove][i];
 		if (Board[from] != movingPiece)
@@ -2302,72 +2095,33 @@ errorT Position::ReadMove(simpleMoveT* s
 		    (frRank != NO_RANK && frRank != square_Rank(from)))
 			continue;
 
-		if (!movegen::pseudo(from, to, ToMove, piece, Board, EMPTY))
+		if (!IsLegalMove(from, to, EMPTY))
 			continue;
 
-		auto pin = movegen::opens_ray(from, to, kingSq, Board, EMPTY);
-		if (pin.first != INVALID_PIECE) {
-			auto p = Board[pin.second];
-			if (piece_Color_NotEmpty(p) != ToMove &&
-			    (piece_Type(p) == QUEEN || piece_Type(p) == pin.first))
-				continue;
-		}
-
 		++matchCount;
-		sm->from = from;
-		sm->to = to;
-		sm->promote = EMPTY;
-		sm->movingPiece = movingPiece;
+		makeMove(from, to, INVALID_PIECE, *sm);
 	}
 	return (matchCount == 1) ? OK                 // ok.
 	                         : ERROR_InvalidMove; // No match, or too many
 	                                              // (ambiguous) moves match.
 }
 
-errorT Position::ReadMoveCastle(simpleMoveT* sm, const char* str, int slen) {
-	slen = trimCheck(str, slen);
-
-	auto str_equal = [&](const char* const_str, const int len) {
-		return slen == len && std::equal(str, str + len, const_str);
-	};
-
-	// short castle
-	if (str_equal("O-O", 3) || str_equal("OO", 2)) {
-		squareT kingSq = GetKingSquare(ToMove);
-		if (kingSq != (ToMove == WHITE ? E1 : E8))
-			return ERROR_InvalidMove;
+errorT Position::ReadMoveCastle(simpleMoveT* sm, std::string_view str) const {
+	bool king_side = true;
+	if (str == "O-O" || str == "OO") {
+		// side = KSIDE;
+	} else if (str == "O-O-O" || str == "OOO") {
+		king_side = false; // QSIDE
+	} else
+		return ERROR_InvalidMove;
 
-		if (Board[kingSq + 1] != EMPTY || Board[kingSq + 2] != EMPTY ||
-		    CalcNumChecks(kingSq) > 0 || CalcNumChecks(kingSq + 1) > 0 ||
-		    CalcNumChecks(kingSq + 2) > 0) {
-			return ERROR_InvalidMove;
-		}
-		sm->from = kingSq;
-		sm->to = kingSq + 2;
-		sm->promote = EMPTY;
-		sm->movingPiece = KING;
-		sm->capturedPiece = EMPTY;
-		return GetCastling(ToMove, KSIDE) ? OK : ERROR_CastlingAvailability;
-	}
-	// long castle
-	if (str_equal("O-O-O", 5) || str_equal("OOO", 3)) {
-		squareT kingSq = GetKingSquare(ToMove);
-		if (kingSq != (ToMove == WHITE ? E1 : E8))
-			return ERROR_InvalidMove;
+	const auto king_sq = GetKingSquare();
+	makeMove(king_sq, king_sq, king_side ? KING : QUEEN, *sm);
+	if (!under_attack(king_sq) && canCastle(king_side))
+		return OK;
 
-		if (Board[kingSq - 1] != EMPTY || Board[kingSq - 2] != EMPTY ||
-		    Board[kingSq - 3] != EMPTY || CalcNumChecks(kingSq) > 0 ||
-		    CalcNumChecks(kingSq - 1) > 0 || CalcNumChecks(kingSq - 2) > 0) {
-			return ERROR_InvalidMove;
-		}
-		sm->from = kingSq;
-		sm->to = kingSq - 2;
-		sm->promote = EMPTY;
-		sm->movingPiece = KING;
-		sm->capturedPiece = EMPTY;
-		return GetCastling(ToMove, QSIDE) ? OK : ERROR_CastlingAvailability;
-	}
-	return ERROR_InvalidMove;
+	return canCastle<false>(king_side) ? ERROR_CastlingAvailability
+	                                   : ERROR_InvalidMove;
 }
 
 errorT Position::ParseMove(simpleMoveT* sm, const char* str) {
@@ -2389,81 +2143,53 @@ errorT Position::ParseMove(simpleMoveT*
                            const char* strEnd) {
 	ASSERT(str != NULL);
 
-	int length = static_cast<int>(std::distance(str, strEnd));
+	const auto length = trimCheck(str, std::distance(str, strEnd));
 	if (length < 2 || length > 9)
 		return ERROR_InvalidMove;
 
-	switch (str[0]) {
-	case 'a':
-		return ReadMovePawn(sm, str, length, A_FYLE);
-	case 'b':
-		return ReadMovePawn(sm, str, length, B_FYLE);
-	case 'c':
-		return ReadMovePawn(sm, str, length, C_FYLE);
-	case 'd':
-		return ReadMovePawn(sm, str, length, D_FYLE);
-	case 'e':
-		return ReadMovePawn(sm, str, length, E_FYLE);
-	case 'f':
-		return ReadMovePawn(sm, str, length, F_FYLE);
-	case 'g':
-		return ReadMovePawn(sm, str, length, G_FYLE);
-	case 'h':
-		return ReadMovePawn(sm, str, length, H_FYLE);
-	case 'K':
+	static const auto piece_map = [] {
+		auto res = std::array<uint8_t, 256>();
+		std::fill(res.begin(), res.end(), INVALID_PIECE);
+		res['a'] = res['A'] = PAWN | (A_FYLE << 4);
+		res['b'] = PAWN | (B_FYLE << 4);
+		res['c'] = res['C'] = PAWN | (C_FYLE << 4);
+		res['d'] = res['D'] = PAWN | (D_FYLE << 4);
+		res['e'] = res['E'] = PAWN | (E_FYLE << 4);
+		res['f'] = res['F'] = PAWN | (F_FYLE << 4);
+		res['g'] = res['G'] = PAWN | (G_FYLE << 4);
+		res['h'] = res['H'] = PAWN | (H_FYLE << 4);
+		res['k'] = res['K'] = KING;
+		res['q'] = res['Q'] = QUEEN;
+		res['r'] = res['R'] = ROOK;
+		res['B'] = BISHOP;
+		res['N'] = KNIGHT;
+		res['o'] = res['O'] = 7; // CASTLE
+		res['P'] = 8;            // explicit PAWN move
+		return res;
+	}();
+	const auto ptype = piece_map[static_cast<unsigned char>(*str)];
+	switch (ptype & 0x0F) {
+	case INVALID_PIECE:
+		// Check for a null move:
+		if ((length == 2 && std::equal(str, str + 2, "--")) ||
+		    (length == 2 && std::equal(str, str + 2, "Z0")) ||
+		    (length == 4 && std::equal(str, str + 4, "null"))) {
+			const auto king_sq = GetKingSquare(ToMove);
+			makeMove(king_sq, king_sq, PAWN, *sm);
+			return OK;
+		}
+		return ERROR_InvalidMove;
+	case PAWN:
+		return ReadMovePawn(sm, str, length, ptype >> 4);
+	case KING:
 		return ReadMoveKing(sm, str, length);
-	case 'Q':
-		return ReadMove(sm, str, length, QUEEN);
-	case 'R':
-		return ReadMove(sm, str, length, ROOK);
-	case 'B':
-		return ReadMove(sm, str, length, BISHOP);
-	case 'N':
-		return ReadMove(sm, str, length, KNIGHT);
-	case 'O':
-		return ReadMoveCastle(sm, str, length);
-	}
-
-	// Check for a null move:
-	if ((length == 2 && std::equal(str, str + 2, "--")) ||
-	    (length == 2 && std::equal(str, str + 2, "Z0")) ||
-	    (length == 4 && std::equal(str, str + 4, "null"))) {
-		sm->pieceNum = 0;
-		sm->from = GetKingSquare(ToMove);
-		sm->to = sm->from;
-		sm->movingPiece = Board[sm->from];
-		sm->promote = EMPTY;
-		return OK;
-	}
-
-	// Invalid move, check for a misspelled first char:
-	switch (str[0]) {
-	case 'A':
-		return ReadMovePawn(sm, str, length, A_FYLE);
-	case 'C':
-		return ReadMovePawn(sm, str, length, C_FYLE);
-	case 'D':
-		return ReadMovePawn(sm, str, length, D_FYLE);
-	case 'E':
-		return ReadMovePawn(sm, str, length, E_FYLE);
-	case 'F':
-		return ReadMovePawn(sm, str, length, F_FYLE);
-	case 'G':
-		return ReadMovePawn(sm, str, length, G_FYLE);
-	case 'H':
-		return ReadMovePawn(sm, str, length, H_FYLE);
-	case 'P':
+	case 7:
+		return ReadMoveCastle(sm, {str, length});
+	case 8:
 		return ParseMove(sm, str + 1, strEnd);
-	case 'k':
-		return ReadMoveKing(sm, str, length);
-	case 'q':
-		return ReadMove(sm, str, length, QUEEN);
-	case 'r':
-		return ReadMove(sm, str, length, ROOK);
-	case 'n':
-		return ReadMove(sm, str, length, KNIGHT);
-	}
-	return ERROR_InvalidMove;
+	default:
+		return ReadMove(sm, str, length, ptype);
+	};
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2485,14 +2211,6 @@ Position::CalcSANStrings (sanListT *sanL
 errorT
 Position::ReadFromLongStr (const char * str)
 {
-    pieceT pieceFromByte [256] = {EMPTY};
-    pieceFromByte [(int) 'K'] = WK;  pieceFromByte [(int) 'k'] = BK;
-    pieceFromByte [(int) 'Q'] = WQ;  pieceFromByte [(int) 'q'] = BQ;
-    pieceFromByte [(int) 'R'] = WR;  pieceFromByte [(int) 'r'] = BR;
-    pieceFromByte [(int) 'B'] = WB;  pieceFromByte [(int) 'b'] = BB;
-    pieceFromByte [(int) 'N'] = WN;  pieceFromByte [(int) 'n'] = BN;
-    pieceFromByte [(int) 'P'] = WP;  pieceFromByte [(int) 'p'] = BP;
-
     Clear();
     for (squareT sq=A1; sq <= H8; sq++) {
         if (str[sq] == '.') { continue; }
@@ -2522,21 +2240,17 @@ Position::ReadFromLongStr (const char *
 //      indicating the side to move. Example for the starting position:
 //      "RNBQKBNRPPPPPPPP................................pppppppprbnqkbnr w"
 //
-void
-Position::MakeLongStr (char * str)
-{
-    ASSERT (str != NULL);
-    char * s = str;
-    for (squareT sq = A1; sq <= H8; sq++) {
-        *s++ = PIECE_CHAR[Board[sq]];
-    }
-    *s++ = ' ';
-    *s++ = (ToMove == WHITE ? 'w' : 'b');
-    *s = 0;
+void Position::MakeLongStr(char* str) const {
+	ASSERT(str != NULL);
+	char* s = str;
+	for (squareT sq = A1; sq <= H8; sq++) {
+		*s++ = PIECE_CHAR[Board[sq]];
+	}
+	*s++ = ' ';
+	*s++ = (ToMove == WHITE ? 'w' : 'b');
+	*s = 0;
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::ReadFromCompactStr():
 //    Sets the position from the provided Null-terminated 33-byte
 //    compact string.
 //    The first 32 bytes contain the square valued, 4 bits per value,
@@ -2546,34 +2260,6 @@ Position::MakeLongStr (char * str)
 //    To ensure no bytes within the staring are zero-valued (so it
 //    can be used as a regular null-terminated string), the value 1
 //    is added to the color, castling and en passant fields.
-errorT
-Position::ReadFromCompactStr (const byte * str)
-{
-    Clear();
-    for (uint i=0; i < 32; i++) {
-        pieceT p = str[i] >> 4;
-        if (p != EMPTY) {
-            if (AddPiece (p, i * 2) != OK) {
-                return ERROR_Corrupt;
-            }
-        }
-        p = str[i] & 15;
-        if (p != EMPTY) {
-            if (AddPiece (p, i * 2 + 1) != OK) {
-                return ERROR_Corrupt;
-            }
-        }
-    }
-    colorT toMove = str[32] - 1;
-    if (toMove != WHITE  &&  toMove != BLACK) {
-        return ERROR_Corrupt;
-    }
-    ToMove = toMove;
-    Castling = str[33] - 1;
-    EPTarget = str[34] - 1;
-    return OK;
-}
-
 void
 Position::PrintCompactStr (char * cboard)
 {
@@ -2603,102 +2289,77 @@ Position::PrintCompactStr (char * cboard
     cboard[35] = 0;
 }
 
-void
-Position::PrintCompactStrFlipped (char * cboard)
-{
-    for (uint i=0; i < 32; i++) {
-        uint i2 = i << 1;
-        // Flip 1st rank to 8th, etc:
-        i2 = ((7 - (i2)/8) * 8 + ((i2) % 8));
-        cboard[i] = (byte)(PIECE_FLIP[Board[i2]] << 4) |
-            (byte)(PIECE_FLIP[Board[i2+1]]);
+template <typename AddPiece>
+const char* FEN_parsePieces(const char* str, AddPiece add) {
+    ASSERT(str);
+
+    for (int row = 7; row >= 0; --row) {
+        for (int col = 0; col < 8;) {
+            const auto ch = *str++;
+            if (ch == '/') {
+                // A FEN string does not have to contain '/'s but if one
+                // appears anywhere except the start of a row, it is an error:
+                if (col != 0)
+                    return nullptr;
+
+            } else if (ch > '0' && ch < '9') {
+                col += (ch - '0');
+                if (col > 8)
+                    return nullptr;
+
+            } else {
+                auto piece = pieceFromByte[static_cast<unsigned char>(ch)];
+                if (piece == EMPTY)
+                    return nullptr;
+
+                if (!add(piece, static_cast<squareT>(row * 8 + col)))
+                    return nullptr;
+
+                col++;
+            }
+        }
     }
-    cboard[32] = 1 + color_Flip(ToMove);
-    cboard[33] = 1 + Castling;
-    cboard[34] = 1 + EPTarget;
-    cboard[35] = 0;
-}
+    return str;
+}
+
+/// Setup the position from a FEN string.
+/// Note: the slashes usually found in Fen strings to mark the start
+/// of a new row do not need to be present, but if they are, they must
+/// appear at the actual start of a new row or the string will be
+/// considered corrupt.
+///
+/// IMPORTANT: the shortcut of having a two-digit number to represent
+/// a number of empty rows (e.g. "/24/" instead of "/8/8/8/") is NOT
+/// accepted by this function.
+///
+/// It is not considered an error for the halfmove clock or fullmove
+/// counter to be invalid, so this routine can also read positions
+/// from EPD lines (which only share the first four fields with FEN).
+errorT Position::ReadFromFEN(const char* str) {
+    ASSERT (str != NULL);
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::ReadFromFEN():
-//      Setup the position from a FEN string.
-//      Note: the slashes usually found in Fen strings to mark the start
-//      of a new row do not need to be present, but if they are, they must
-//      appear at the actual start of a new row or the string will be
-//      considered corrupt.
-//
-//      IMPORTANT: the shortcut of having a two-digit number to represent
-//      a number of empty rows (e.g. "/24/" instead of "/8/8/8/") is NOT
-//      accepted by this function.
-//
-//      It is not considered an error for the halfmove clock or fullmove
-//      counter to be invalid, so this routine can also read positions
-//      from EPD lines (which only share the first four fields with FEN).
-errorT
-Position::ReadFromFEN (const char * str)
-{
     auto is_space = [](char ch) {
         return isspace(static_cast<unsigned char>(ch));
     };
-
-    // pieceFromByte[] converts a character to its piece, e.g. 'k' -> BK.
-    static pieceT pieceFromByte [256];
-
-    // fenSqToRealSquare[] converts a fen square (0 to 63) to its real
-    // square. E.g: [0] -> A8, [1] -> B8, .... [63] -> H1.
-    static squareT fenSqToRealSquare [64];
-
-    // Note the first Call to set up the static arrays only once:
-    static int firstCall = 1;
-
-    ASSERT (str != NULL);
-    const char * s = str;
-    int count = 0;
-
-    if (firstCall) {
-        firstCall = 0;
-
-        // Set up pieceFromByte[]:
-        for (int i=0; i < 256; i++) { pieceFromByte[i] = EMPTY; }
-        pieceFromByte [(int) 'K'] = WK;  pieceFromByte [(int) 'k'] = BK;
-        pieceFromByte [(int) 'Q'] = WQ;  pieceFromByte [(int) 'q'] = BQ;
-        pieceFromByte [(int) 'R'] = WR;  pieceFromByte [(int) 'r'] = BR;
-        pieceFromByte [(int) 'B'] = WB;  pieceFromByte [(int) 'b'] = BB;
-        pieceFromByte [(int) 'N'] = WN;  pieceFromByte [(int) 'n'] = BN;
-        pieceFromByte [(int) 'P'] = WP;  pieceFromByte [(int) 'p'] = BP;
-
-        // Set up fenSqToRealSq[]:
-        for (int sq=0; sq < 64; sq++) {
-            fenSqToRealSquare [sq] = (squareT)((7 - (sq)/8) * 8 + ((sq) % 8));
+    auto skip_spaces = [&]() {
+        while (isspace(*str)) {
+            str++;
         }
-    }
-
-    Clear ();
-    while (count < 64) {
-        if (*s == '/') {
-            // A FEN string does not have to contain '/'s but if one
-            // appears anywhere except the start of a row, it is an error:
-
-            if (count % 8) { return ERROR_InvalidFEN; }
+    };
 
-        } else if (*s > '0'  &&  *s < '9') {
-            count += (*s - '0');
+    Clear();
 
-        } else {
-            pieceT p = pieceFromByte [(byte) *s];
-            if (p == EMPTY) { return ERROR_InvalidFEN; }
-            if (AddPiece (p, fenSqToRealSquare[count]) != OK) {
-                return ERROR_InvalidFEN;
-            }
-            count++;
-        }
-        s++;
-    }
-    if (Material[WK] != 1  ||  Material[BK] != 1) { return ERROR_InvalidFEN; }
+    // Piece placement
+    skip_spaces();
+    str = FEN_parsePieces(str, [&](auto piece, auto sq){
+        return this->AddPiece(piece, sq) == OK;
+    });
+    if (!str)
+        return ERROR_InvalidFEN;
 
     // Now the side to move:
-    while (is_space(*s)) { s++; }
-    switch (*s) {
+    skip_spaces();
+    switch (*str++) {
     case 'w':
         SetToMove (WHITE);
         break;
@@ -2708,61 +2369,106 @@ Position::ReadFromFEN (const char * str)
     default:
         return ERROR_InvalidFEN;
     }
-    s++;
 
-    if (! IsLegal()) { return ERROR_InvalidFEN; }
+    if (Material[WK] != 1 || Material[BK] != 1 || !IsLegal())
+        return ERROR_InvalidFEN;
 
     // Now the castling flags:
-    while (is_space(*s)) { s++; }
-    if (*s == '-') {
-        s++;  // do nothing
-    } else if (*s == 0) {
+    skip_spaces();
+    if (*str == '-') {
+        str++;  // do nothing
+    } else if (*str == 0) {
         // The FEN has no castling field, so just guess that
         // castling is possible whenever a king and rook are
         // still on their starting squares:
         if (Board[E1] == WK) {
-            if (Board[A1] == WR) { SetCastling (WHITE, QSIDE, true); }
-            if (Board[H1] == WR) { SetCastling (WHITE, KSIDE, true); }
+            if (Board[A1] == WR) { setCastling(WHITE, A1); }
+            if (Board[H1] == WR) { setCastling(WHITE, H1); }
         }
         if (Board[E8] == BK) {
-            if (Board[A8] == BR) { SetCastling (BLACK, QSIDE, true); }
-            if (Board[H8] == BR) { SetCastling (BLACK, KSIDE, true); }
+            if (Board[A8] == BR) { setCastling(BLACK, A8); }
+            if (Board[H8] == BR) { setCastling(BLACK, H8); }
         }
     } else {
-        while (!is_space(*s)  &&  *s != 0) {
-            switch (*s) {
+        while (!is_space(*str)  &&  *str != 0) {
+            switch (*str++) {
             case 'Q':
-                SetCastling (WHITE, QSIDE, true);
+                setCastling(WHITE, A1);
                 break;
             case 'q':
-                SetCastling (BLACK, QSIDE, true);
+                setCastling(BLACK, A8);
                 break;
             case 'K':
-                SetCastling (WHITE, KSIDE, true);
+                setCastling(WHITE, H1);
                 break;
             case 'k':
-                SetCastling (BLACK, KSIDE, true);
+                setCastling(BLACK, H8);
+                break;
+            case 'A':
+                setCastling(WHITE, A1);
+                break;
+            case 'B':
+                setCastling(WHITE, B1);
+                break;
+            case 'C':
+                setCastling(WHITE, C1);
+                break;
+            case 'D':
+                setCastling(WHITE, D1);
+                break;
+            case 'E':
+                setCastling(WHITE, E1);
+                break;
+            case 'F':
+                setCastling(WHITE, F1);
+                break;
+            case 'G':
+                setCastling(WHITE, G1);
+                break;
+            case 'H':
+                setCastling(WHITE, H1);
+                break;
+            case 'a':
+                setCastling(BLACK, A8);
+                break;
+            case 'b':
+                setCastling(BLACK, B8);
+                break;
+            case 'c':
+                setCastling(BLACK, C8);
+                break;
+            case 'd':
+                setCastling(BLACK, D8);
+                break;
+            case 'e':
+                setCastling(BLACK, E8);
+                break;
+            case 'f':
+                setCastling(BLACK, F8);
+                break;
+            case 'g':
+                setCastling(BLACK, G8);
+                break;
+            case 'h':
+                setCastling(BLACK, H8);
                 break;
             default:
                 return ERROR_InvalidFEN;
             }
-            s++;
         }
     }
 
     // Now the EP target:
-    while (is_space(*s)) { s++; }
-    if (*s == 0) {
-        // do nothing
-    } else if (*s == '-') {
+    skip_spaces();
+    if (*str == '-') {
         EPTarget = NULL_SQUARE;
-        s++;  // No EP target
-    } else {
-        char fylec = *s; s++;
+        str++;  // No EP target
+    } else if (*str) {
+        char fylec = *str++;
         if (fylec < 'a'  ||  fylec > 'h') {
             return ERROR_InvalidFEN;
         }
-        char rankc = *s; s++;
+        char rankc = *str++;
         if (rankc != '3'  &&  rankc != '6') {
             return ERROR_InvalidFEN;
         }
@@ -2770,16 +2476,17 @@ Position::ReadFromFEN (const char * str)
     }
 
     // Now the capture/pawn halfmove clock:
-    while (is_space(*s)) { s++; }
-    if (*s) {
-        HalfMoveClock = (ushort) atoi(s);
+    skip_spaces();
+    if (*str) {
+        char* end;
+        HalfMoveClock = (ushort)std::max(0l, strtol(str, &end, 10));
+        str = end;
     }
-    while (!is_space(*s)  && *s != 0) { s++; }
 
     // Finally, the fullmove counter:
-    while (is_space(*s)) { s++; }
-    if (*s) {
-        int i = atoi(s);
+    skip_spaces();
+    if (*str) {
+        int i = atoi(str);
         if (i >= 1) {
             PlyCounter = (i - 1) * 2;
         }
@@ -2788,6 +2495,33 @@ Position::ReadFromFEN (const char * str)
     return OK;
 }
 
+/// Setup the position from a FEN string or UCI "position".
+/// Accept strings like "position startpos", "position startpos moves e2e4",
+/// "FENSTRING", "FENSTRING moves e2e4", "position fen FENSTRING moves e2e4".
+errorT Position::ReadFromFENorUCI(std::string_view str) {
+    auto trimLeft = std::find_if_not(
+        str.begin(), str.end(),
+        [](char ch) { return isspace(static_cast<unsigned char>(ch)); });
+    str.remove_prefix(trimLeft - str.begin());
+
+    auto fen = str.substr(0, str.find("moves"));
+    str.remove_prefix(fen.size());
+    if (str.substr(0, 5) == "moves") {
+        str.remove_prefix(5);
+    }
+
+    if (fen.substr(0, 17) == "position startpos") {
+        *this = Position::getStdStart();
+    } else {
+        if (fen.substr(0, 12) == "position fen") {
+            fen.remove_prefix(12);
+        }
+        if (auto err = ReadFromFEN(std::string(fen).c_str()))
+            return err;
+    }
+
+    return MakeCoordMoves(str.data(), str.size());
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Position::PrintFEN():
@@ -2994,24 +2728,6 @@ Position::Compare (Position * p)
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::GetSquares
-//    Adds to the provided square list all squares containing the specified
-//    piece, and return the number of pieces of that type on the board.
-uint
-Position::GetSquares (pieceT piece, SquareList * sqlist)
-{
-    colorT color = piece_Color(piece);
-    squareT * squares = GetList(color);
-    uint npieces = GetCount(color);
-    for (uint i=0; i < npieces; i++) {
-        squareT sq = squares[i];
-        pieceT p = Board[sq];
-        if (p == piece) { sqlist->Add (sq); }
-    }
-    return Material[piece];
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Position::Random
 //    Given a string such as "KRPKR" or "KRP-kr", sets up a
 //    random position with that material configuration.
diff -pruN 1:4.7.0+dfsg1-2/src/position.h 1:4.7.4+dfsg1-2/src/position.h
--- 1:4.7.0+dfsg1-2/src/position.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/position.h	2022-07-09 05:14:42.000000000 +0000
@@ -19,6 +19,8 @@
 #include "common.h"
 #include "movelist.h"
 #include <stdio.h>
+#include <string>
+#include <string_view>
 
 class DString;
 class SquareSet;
@@ -102,6 +104,8 @@ private:
                                     // or pawn move.
     ushort          PlyCounter;
     byte            Castling;       // castling flags
+    byte            variant_;       // 0 -> normal; 1 -> chess960
+    squareT         castleRookSq_[4];  // start rook squares
 
     uint            Hash;           // Hash value.
     uint            PawnHash;       // Pawn structure hash value.
@@ -125,7 +129,7 @@ private:
 
     void  AddLegalMove (MoveList * mlist, squareT from, squareT to, pieceT promo);
     void  GenCastling (MoveList * mlist);
-    void  GenKingMoves (MoveList * mlist, genMovesT genType, bool castling);
+    void  GenKingMoves (MoveList * mlist, genMovesT genType);
     void  AddPromotions (MoveList * mlist, squareT from, squareT dest);
     bool  IsValidEnPassant (squareT from, squareT to);
     void  GenPawnMoves (MoveList * mlist, squareT from, directionT dir,
@@ -133,14 +137,23 @@ private:
 
     void GenCheckEvasions(MoveList* mlist, pieceT mask, genMovesT genType,
                           SquareList* checkSquares);
-    errorT MatchPawnMove(MoveList* mlist, fyleT fromFyle, squareT to,
-                         pieceT promote);
 
-    errorT ReadMove(simpleMoveT* sm, const char* str, int slen, pieceT p);
-    errorT ReadMoveCastle(simpleMoveT* sm, const char* str, int slen);
-    errorT ReadMovePawn(simpleMoveT* sm, const char* str, int slen, fyleT from);
-    errorT ReadMoveKing(simpleMoveT* sm, const char* str, int slen);
+    errorT ReadMove(simpleMoveT* sm, const char* str, size_t slen, pieceT p) const;
+    errorT ReadMoveCastle(simpleMoveT* sm, std::string_view str) const;
+    errorT ReadMovePawn(simpleMoveT* sm, const char* str, size_t slen, fyleT from);
+    errorT ReadMoveKing(simpleMoveT* sm, const char* str, size_t slen) const;
+
+    template <typename TFunc>
+    bool under_attack(squareT target_sq, squareT captured_sq,
+                      TFunc not_empty) const;
+    bool under_attack(squareT target_sq) const;
 
+    static constexpr unsigned castlingIdx(colorT color, castleDirT side) {
+        return 2 * color + side;
+    }
+    squareT castleRookSq(colorT color, bool king_side) const {
+        return castleRookSq_[2 * color + (king_side ? 1 : 0)];
+    }
 
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     //  Position:  Public Functions
@@ -153,6 +166,8 @@ public:
     bool        IsStdStart() const;
     errorT      AddPiece (pieceT p, squareT sq);
 
+    bool isChess960() const { return variant_ == 1; }
+
     // Set and Get attributes -- one-liners
     byte        PieceCount (pieceT p)    { return Material[p]; }
     const byte* GetMaterial() const      { return Material; }
@@ -160,6 +175,7 @@ public:
     squareT     GetEPTarget () const     { return EPTarget; }
     void        SetToMove (colorT c)     { ToMove = c; }
     colorT      GetToMove () const       { return ToMove; }
+    bool        WhiteToMove () const     { return ToMove == WHITE; }
     void        SetPlyCounter (ushort x) { PlyCounter = x; }
     ushort      GetPlyCounter () const   { return PlyCounter; }
     ushort      GetFullMoveCount() const { return PlyCounter / 2 + 1; }
@@ -183,16 +199,15 @@ public:
     inline uint RankCount (pieceT p, rankT r) const {
         return NumOnRank[p][r];
     }
-    inline uint LeftDiagCount (pieceT p, leftDiagT diag) {
+    inline uint LeftDiagCount (pieceT p, leftDiagT diag) const {
         return NumOnLeftDiag[p][diag];
     }
-    inline uint RightDiagCount (pieceT p, rightDiagT diag) {
+    inline uint RightDiagCount (pieceT p, rightDiagT diag) const {
         return NumOnRightDiag[p][diag];
     }
-    inline uint SquareColorCount (pieceT p, colorT sqColor) {
+    inline uint SquareColorCount (pieceT p, colorT sqColor)  const {
         return NumOnSquareColor[p][sqColor];
     }
-    uint        GetSquares (pieceT p, SquareList * sqlist);
 
     const pieceT* GetBoard() const {
         const_cast<Position*>(this)->Board[COLOR_SQUARE] = COLOR_CHAR[ToMove];
@@ -205,22 +220,15 @@ public:
     }
 
     // Other one-line methods
-    squareT     GetKingSquare (colorT c)  { return List[c][0]; }
-    squareT     GetKingSquare ()          { return List[ToMove][0]; }
-    squareT     GetEnemyKingSquare ()     { return List[1-ToMove][0]; }
+    squareT     GetKingSquare (colorT c) const { return List[c][0]; }
+    squareT     GetKingSquare () const         { return List[ToMove][0]; }
+    squareT     GetEnemyKingSquare () const    { return List[1-ToMove][0]; }
 
     // Castling flags
-    inline void SetCastling (colorT c, castleDirT dir, bool flag);
     bool GetCastling(colorT c, castleDirT dir) const {
-        int b = (c == WHITE) ? 1 : 4;
-        if (dir == KSIDE)
-            b += b;
-        // Now b == 1 or 2 (white flags), or 4 or 8 (black flags)
-        return Castling & b;
-    }
-    inline bool CastlingPossible () { return (Castling ? true : false); }
-    byte        GetCastlingFlags () { return Castling; }
-    void        SetCastlingFlags (byte b) { Castling = b; }
+        return Castling & (1u << castlingIdx(c, dir));
+    }
+    byte GetCastlingFlags() const { return Castling; }
 
     // Hashing
     inline uint HashValue (void) { return Hash; }
@@ -237,52 +245,57 @@ public:
     void  GenerateMoves (MoveList * mlist) { GenerateMoves (mlist, EMPTY, GEN_ALL_MOVES, true); }
     void  GenerateMoves (MoveList * mlist, genMovesT genType) { GenerateMoves (mlist, EMPTY, genType, true); }
     void  GenerateCaptures (MoveList * mlist) { GenerateMoves (mlist, EMPTY, GEN_CAPTURES, true); }
-    bool  IsLegalMove (simpleMoveT * sm);
+    int IsLegalMove(squareT from, squareT to, pieceT promo) const;
+
+    /// Check that the minimum requirements for castling are satisfied:
+    /// - both the king and the rook exists in the position
+    /// - the final squares of the king and the rook are empty
+    /// @param check_legal: also test for checks or blocking pieces.
+    /// Ignore the castling flags and if the king is already in check.
+    template <bool check_legal = true> bool canCastle(bool king_side) const;
 
-    uint        CalcAttacks (colorT toMove, squareT kingSq, SquareList * squares);
+    uint        CalcAttacks (colorT toMove, squareT kingSq, SquareList * squares) const;
     int         TreeCalcAttacks (colorT toMove, squareT target);
-    uint        CalcNumChecks () {
+    uint        CalcNumChecks () const {
                     return CalcAttacks (1-ToMove, GetKingSquare(), NULL);
                 }
-    uint        CalcNumChecks (squareT kingSq) {
+    uint        CalcNumChecks (squareT kingSq) const {
                     return CalcAttacks (1-ToMove, kingSq, NULL);
                 }
-    uint        CalcNumChecks (squareT kingSq, SquareList * checkSquares) {
+    uint        CalcNumChecks (squareT kingSq, SquareList * checkSquares) const {
                     return CalcAttacks (1-ToMove, kingSq, checkSquares);
                 }
 
     uint        Mobility (pieceT p, colorT color, squareT from);
     bool        IsKingInCheck () { return (CalcNumChecks() > 0); }
-    bool        IsKingInCheckDir (directionT dir);
-    bool        IsKingInCheck (simpleMoveT * sm);
+    bool        IsKingInCheck (simpleMoveT const& sm);
     bool        IsKingInMate ();
     bool        IsLegal ();
 
     bool        IsPromoMove (squareT from, squareT to);
 
+    void        makeMove(squareT from, squareT to, pieceT promo, simpleMoveT& res) const;
+    void        fillMove(simpleMoveT& sm) const;
+    void        DoSimpleMove(simpleMoveT const& sm);
     void        DoSimpleMove (simpleMoveT * sm);    // move execution ...
-    void        UndoSimpleMove (simpleMoveT * sm);  // ... and taking back
+    void        UndoSimpleMove (simpleMoveT const* sm);  // ... and taking back
 
     errorT      RelocatePiece (squareT fromSq, squareT toSq);
 
     void        MakeSANString (simpleMoveT * sm, char * s, sanFlagT flag);
-    void        MakeUCIString (simpleMoveT * sm, char * s);
 	void        CalcSANStrings (sanListT *sanList, sanFlagT flag);
 
-    errorT      ReadCoordMove(simpleMoveT* m, const char* s, int slen, bool reverse);
+    errorT      MakeCoordMoves(const char* moves, size_t movesLen, std::string* toSAN = nullptr);
+    errorT      ReadCoordMove(simpleMoveT* m, const char* s, size_t slen, bool reverse);
     errorT      ParseMove(simpleMoveT* sm, const char* str);
     errorT      ParseMove(simpleMoveT* sm, const char* begin, const char* end);
 
     // Board I/O
-    void        MakeLongStr (char * str);
+    void        MakeLongStr (char* str) const;
     errorT      ReadFromLongStr (const char * str);
-    errorT      ReadFromCompactStr (const byte * str);
     errorT      ReadFromFEN (const char * s);
+    errorT      ReadFromFENorUCI (std::string_view str);
     void        PrintCompactStr (char * cboard);
-    void        PrintCompactStrFlipped (char * cboard);
-    byte        CompactStrFirstByte () {
-        return (Board[0] << 4) | Board[1];
-    }
     void        PrintFEN(char* str, uint flags) const;
     void        DumpLatexBoard (DString * dstr, bool flip);
     void        DumpLatexBoard (DString * dstr) {
@@ -300,27 +313,16 @@ public:
 
     // Set up a random position:
     errorT      Random (const char * material);
-};
-
-
 
-//////////////////////////////////////////////////////////////////////
-//  Position:  Public Inline Functions
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Position::SetCastling():
-//      Set a castling flag.
-//
-inline void
-Position::SetCastling (colorT c, castleDirT dir, bool flag)
-{
-    byte b = (c==WHITE ? 1 : 4);
-    if (dir == KSIDE) b += b;
-    // Now b = 1 or 2 (white flags), or 4 or 8 (black flags)
-    if (flag) { Castling |= b; } else { Castling &= (255-b); }
-    return;
-}
+private:
+    void setCastling(colorT col, squareT rsq);
+    void ClearCastling(colorT col, castleDirT dir) {
+        Castling &= ~(1u << castlingIdx(col, dir));
+    }
+    void ClearCastlingFlags(colorT c) {
+        Castling &= (c == WHITE) ? 0b11111100 : 0b11110011;
+    }
+};
 
 #endif  // SCID_POSITION_H
 
diff -pruN 1:4.7.0+dfsg1-2/src/sc_base.cpp 1:4.7.4+dfsg1-2/src/sc_base.cpp
--- 1:4.7.0+dfsg1-2/src/sc_base.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/sc_base.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -1,5 +1,5 @@
 /*
-# Copyright (C) 2015 Fulvio Benini
+ * Copyright (C) 2015-2019 Fulvio Benini
 
 * This file is part of Scid (Shane's Chess Information Database).
 *
@@ -22,8 +22,11 @@
 #include "scidbase.h"
 #include "searchtournaments.h"
 #include "ui.h"
+#include <algorithm>
 #include <cstring>
+#include <map>
 #include <string>
+#include <vector>
 
 namespace {
 /*
@@ -53,23 +56,26 @@ namespace {
  * If @fMode == FMODE_Both, and the file cannot be opened for writing, this
  * function will try to open the database read-only.
  */
-static UI_res_t doOpenBase(UI_handle_t ti, const char* filename,
-                           fileModeT fMode, ICodecDatabase::Codec codec) {
-	if (DBasePool::find(filename) != 0) return UI_Result(ti, ERROR_FileInUse);
+static UI_res_t doOpenBase(UI_handle_t ti, const char* codec, fileModeT fMode,
+                           const char* filename) {
+	if (DBasePool::find(filename))
+		return UI_Result(ti, ERROR_FileInUse);
 
 	scidBaseT* dbase = DBasePool::getFreeSlot();
-	if (dbase == 0) return UI_Result(ti, ERROR_Full);
+	if (!dbase)
+		return UI_Result(ti, ERROR_Full);
 
 	Progress progress = UI_CreateProgress(ti);
-	errorT err = dbase->Open(codec, fMode, filename, progress);
+	errorT err = dbase->open(codec, fMode, filename, progress);
 
 	if ((err == ERROR_FileOpen || err == ERROR_FileMode) &&
 	    fMode == FMODE_Both) {
-		err = dbase->Open(codec, FMODE_ReadOnly, filename, progress);
+		err = dbase->open(codec, FMODE_ReadOnly, filename, progress);
 	}
-	progress.report(1,1);
+	progress.report(1, 1);
 
-	if (err != OK && err != ERROR_NameDataLoss) return UI_Result(ti, err);
+	if (err != OK && err != ERROR_NameDataLoss)
+		return UI_Result(ti, err);
 
 	int res = DBasePool::switchCurrent(dbase);
 	return UI_Result(ti, err, res);
@@ -83,7 +89,8 @@ UI_res_t sc_base_close(scidBaseT* dbase,
 	if (dbase->getFileName() == "<clipbase>") {
 		return UI_Result(ti, ERROR_BadArg, "Cannot close clipbase.");
 	}
-	return UI_Result(ti, dbase->Close());
+	dbase->Close();
+	return UI_Result(ti, OK);
 }
 
 
@@ -131,18 +138,26 @@ UI_res_t sc_base_copygames(scidBaseT* db
 	scidBaseT* targetBase = DBasePool::getBase(strGetUnsigned(argv[4]));
 	if (targetBase == 0)
 		return UI_Result(ti, ERROR_BadArg, "sc_base copygames error: wrong targetBaseId");
-	if (targetBase->isReadOnly())
-		return UI_Result(ti, ERROR_FileReadOnly);
 
 	errorT err = OK;
 	const HFilter filter = dbase->getFilter(argv[3]);
 	if (filter != 0) {
 		err = targetBase->importGames(dbase, filter, UI_CreateProgress(ti));
 	} else {
-		uint gNum = strGetUnsigned (argv[3]);
-		if (gNum == 0)
-			return UI_Result(ti, ERROR_BadArg, "sc_base copygames error: wrong <gameNum|filterName>");
-		err = targetBase->importGame(dbase, gNum -1);
+		uint gNum = strGetUnsigned(argv[3]);
+		const IndexEntry* ie = (gNum > 0)
+		                           ? dbase->getIndexEntry_bounds(gNum - 1)
+		                           : nullptr;
+		if (ie == nullptr)
+			return UI_Result(
+			    ti, ERROR_BadArg,
+			    "sc_base copygames error: wrong <gameNum|filterName>");
+
+		Game game;
+		err = dbase->getGame(*ie, game);
+		if (err == OK) {
+			err = targetBase->saveGame(&game);
+		}
 	}
 	return UI_Result(ti, err);
 }
@@ -160,24 +175,14 @@ UI_res_t sc_base_copygames(scidBaseT* db
  */
 UI_res_t sc_base_create(UI_handle_t ti, int argc, const char** argv) {
 	const char* usage = "Usage: sc_base create <MEMORY|SCID4|PGN> filename";
-	if (argc != 4 && argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
 
-	// Old interface defaults to SCID4
-	ICodecDatabase::Codec codec = ICodecDatabase::SCID4;
-	if (argc == 4) {
-		if (std::strcmp("MEMORY", argv[2]) == 0)
-			codec = ICodecDatabase::MEMORY;
-		else if (std::strcmp("SCID4", argv[2]) == 0)
-			codec = ICodecDatabase::SCID4;
-		else if (std::strcmp("PGN", argv[2]) == 0)
-			codec = ICodecDatabase::PGN;
-		else
-			return UI_Result(ti, ERROR_BadArg, usage);
-	}
+	if (argc == 3) // Old interface defaults to SCID4
+		return doOpenBase(ti, "SCID4", FMODE_Create, argv[2]);
+
+	if (argc == 4)
+		return doOpenBase(ti, argv[2], FMODE_Create, argv[3]);
 
-	return doOpenBase(
-	    ti, argc == 4 ? argv[3] : argv[2],
-	    codec == ICodecDatabase::MEMORY ? FMODE_Memory : FMODE_Create, codec);
+	return UI_Result(ti, ERROR_BadArg, usage);
 }
 
 
@@ -243,14 +248,15 @@ UI_res_t sc_base_gameflag(scidBaseT* dba
 		cmd = 4;
 	uint flagType = IndexEntry::CharToFlagMask(argv[5][0]);
 	if (flagType != 0 && cmd != 0) {
-		bool value = (cmd == 2);
-
-		const HFilter filter = dbase->getFilter(argv[3]);
-		if (filter != 0 || (std::strcmp("all", argv[3]) == 0)) {
+		Filter filter_all(dbase->numGames());
+		const HFilter filter = std::strcmp("all", argv[3]) == 0
+		                           ? HFilter(&filter_all)
+		                           : dbase->getFilter(argv[3]);
+		if (filter != 0) {
 			switch (cmd) {
-			case 2:
-			case 3: return UI_Result(ti, dbase->setFlag(value, flagType, filter));
-			case 4: return UI_Result(ti, dbase->invertFlag(flagType, filter));
+			case 2: return UI_Result(ti, dbase->setFlags(true, flagType, filter));
+			case 3: return UI_Result(ti, dbase->setFlags(false, flagType, filter));
+			case 4: return UI_Result(ti, dbase->invertFlags(flagType, filter));
 			}
 		} else {
 			gamenumT gNum = strGetUnsigned(argv[3]);
@@ -258,8 +264,8 @@ UI_res_t sc_base_gameflag(scidBaseT* dba
 				gNum--;
 				switch (cmd) {
 				case 1: return UI_Result(ti, OK, dbase->getFlag(flagType, gNum));
-				case 2:
-				case 3: return UI_Result(ti, dbase->setFlag(value, flagType, gNum));
+				case 2: return UI_Result(ti, dbase->setFlag(true, flagType, gNum));
+				case 3: return UI_Result(ti, dbase->setFlag(false, flagType, gNum));
 				case 4: return UI_Result(ti, dbase->invertFlag(flagType, gNum));
 				}
 			}
@@ -302,20 +308,21 @@ UI_res_t sc_base_gamelocation(scidBaseT*
 		const NameBase* nb = dbase->getNameBase();
 		auto contains = [dbase, nb, text](gamenumT g) {
 			const IndexEntry* ie = dbase->getIndexEntry(g);
-			return strAlphaContains(ie->GetWhiteName(nb), text) ||
-			       strAlphaContains(ie->GetBlackName(nb), text) ||
-			       strAlphaContains(ie->GetEventName(nb), text) ||
-			       strAlphaContains(ie->GetSiteName(nb), text);
+			return strAlphaContains(nb->GetName(NAME_PLAYER, ie->GetWhite()), text) ||
+			       strAlphaContains(nb->GetName(NAME_PLAYER, ie->GetBlack()), text) ||
+			       strAlphaContains(nb->GetName(NAME_EVENT, ie->GetEvent()), text) ||
+			       strAlphaContains(nb->GetName(NAME_SITE, ie->GetSite()), text);
 		};
 		if (strGetBoolean(argv[8])) {
-			std::vector<gamenumT> buf(filter->size() - start);
+			std::vector<gamenumT> buf(
+			    start >= filter->size() ? 1 : filter->size() - start);
 			buf.resize(
 			    dbase->listGames(sort, start, buf.size(), filter, buf.data()));
 			auto it = std::find_if(buf.begin(), buf.end(), contains);
 			if (it != buf.end())
 				location = start + std::distance(buf.begin(), it);
 		} else {
-			std::vector<gamenumT> buf(start);
+			std::vector<gamenumT> buf(start ? start : 1);
 			buf.resize(dbase->listGames(sort, 0, start, filter, buf.data()));
 			auto it = std::find_if(buf.rbegin(), buf.rend(), contains);
 			if (it != buf.rend())
@@ -357,18 +364,19 @@ UI_res_t sc_base_gameslist(scidBaseT* db
 		uint ply = filter->get(idx) -1;
 
 		const IndexEntry* ie = dbase->getIndexEntry(idx);
+		const auto tags = TagRoster::make(*ie, *nb);
 
 		ginfo.clear();
 		ginfo.push_back(idx +1);
 		ginfo.push_back(RESULT_STR[ie->GetResult()]);
 		ginfo.push_back((ie->GetNumHalfMoves() + 1) / 2);
-		ginfo.push_back(ie->GetWhiteName(nb));
+		ginfo.push_back(tags.white);
 		std::string eloStr;
 		eloT welo = ie->GetWhiteElo();
 		if (welo != 0) {
 			eloStr = std::to_string(welo);
 		} else {
-			welo = ie->GetWhiteElo(nb);
+			welo = dbase->peakElo(ie->GetWhite());
 			eloStr = std::to_string(welo);
 			if (welo != 0) {
 				eloStr.insert(eloStr.begin(), '(');
@@ -376,12 +384,12 @@ UI_res_t sc_base_gameslist(scidBaseT* db
 			}
 		}
 		ginfo.push_back(eloStr);
-		ginfo.push_back(ie->GetBlackName(nb));
+		ginfo.push_back(tags.black);
 		eloT belo = ie->GetBlackElo();
 		if (belo != 0) {
 			eloStr = std::to_string(belo);
 		} else {
-			belo = ie->GetBlackElo(nb);
+			belo = dbase->peakElo(ie->GetBlack());
 			eloStr = std::to_string(belo);
 			if (belo != 0) {
 				eloStr.insert(eloStr.begin(), '(');
@@ -390,11 +398,11 @@ UI_res_t sc_base_gameslist(scidBaseT* db
 		}
 		ginfo.push_back(eloStr);
 		char buf_date[16];
-		date_DecodeToString (ie->GetDate(), buf_date);
+		date_DecodeToString(ie->GetDate(), buf_date);
 		ginfo.push_back(buf_date);
-		ginfo.push_back(ie->GetEventName(nb));
-		ginfo.push_back(ie->GetRoundName(nb));
-		ginfo.push_back(ie->GetSiteName(nb));
+		ginfo.push_back(tags.event);
+		ginfo.push_back(tags.round);
+		ginfo.push_back(tags.site);
 		ginfo.push_back(ie->GetNagCount());
 		ginfo.push_back(ie->GetCommentCount());
 		ginfo.push_back(ie->GetVariationCount());
@@ -417,9 +425,8 @@ UI_res_t sc_base_gameslist(scidBaseT* db
 		ginfo.push_back(buf_eventdate);
 		ginfo.push_back(ie->GetYear());
 		ginfo.push_back((welo + belo)/2);
-		ginfo.push_back(ie->GetRating(nb));
-		FastGame game = dbase->getGame(ie);
-		ginfo.push_back(game.getMoveSAN(ply, 10));
+		ginfo.push_back(ie->GetRating());
+		ginfo.push_back(dbase->getGame(ie).getMoveSAN(ply, 10));
 
 		res.push_back(std::to_string(idx+1) + "_" + std::to_string(ply));
 		res.push_back(ginfo);
@@ -569,20 +576,14 @@ UI_res_t sc_base_numGames(scidBaseT* dba
  */
 UI_res_t sc_base_open(UI_handle_t ti, int argc, const char** argv) {
 	const char* usage = "Usage: sc_base open <SCID4|PGN> filename";
-	if (argc != 4 && argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
 
-	// Old interface defaults to SCID4
-	ICodecDatabase::Codec codec = ICodecDatabase::SCID4;
-	if (argc == 4) {
-		if (std::strcmp("SCID4", argv[2]) == 0)
-			codec = ICodecDatabase::SCID4;
-		else if (std::strcmp("PGN", argv[2]) == 0)
-			codec = ICodecDatabase::PGN;
-		else
-			return UI_Result(ti, ERROR_BadArg, usage);
-	}
+	if (argc == 3) // Old interface defaults to SCID4
+		return doOpenBase(ti, "SCID4", FMODE_Both, argv[2]);
+
+	if (argc == 4)
+		return doOpenBase(ti, argv[2], FMODE_Both, argv[3]);
 
-	return doOpenBase(ti, argc == 4 ? argv[3] : argv[2], FMODE_Both, codec);
+	return UI_Result(ti, ERROR_BadArg, usage);
 }
 
 
@@ -617,7 +618,7 @@ UI_res_t sc_base_sortcache(scidBaseT* db
 	if (argc != 5) return UI_Result(ti, ERROR_BadArg, usage);
 
 	if (std::strcmp("create", argv[3]) == 0) {
-		if (dbase->createSortCache(argv[4]) == NULL)
+		if (!dbase->createSortCache(argv[4]))
 			return UI_Result(ti, ERROR);
 	} else {
 		dbase->releaseSortCache(argv[4]);
@@ -722,6 +723,53 @@ UI_res_t sc_base_switch(scidBaseT* dbase
 	return UI_Result(ti, OK, res);
 }
 
+/// Remove all occurrences of the specified tags from the database.
+/// @returns the number of changed games.
+UI_res_t sc_base_strip(scidBaseT& dbase, UI_handle_t ti, int argc,
+                       const char** argv) {
+	const char* usage = "Usage: sc_base strip baseId tagNames...";
+	if (argc < 4)
+		return UI_Result(ti, ERROR_BadArg, usage);
+
+	std::vector<std::string_view> tags(argv + 3, argv + argc);
+	Filter filter_all(dbase.numGames());
+	auto progress = UI_CreateProgress(ti);
+	const auto res = dbase.stripGames(HFilter(&filter_all), progress, tags);
+	return UI_Result(ti, res.first, res.second);
+}
+
+/// Produce a list of PGN tags used in the database
+/// @returns a even-sized list, where each pair of elements is a tag name and
+/// its frequency
+UI_res_t sc_base_taglist(scidBaseT& dbase, UI_handle_t ti) {
+	std::map<std::string, gamenumT, std::less<>> tag_freq;
+	auto progress = UI_CreateProgress(ti);
+	for (gamenumT gnum = 0, n = dbase.numGames(); gnum < n; ++gnum) {
+		if ((gnum % 1024 == 0) && !progress.report(gnum, n))
+			return UI_Result(ti, ERROR_UserCancel);
+
+		const auto ie = dbase.getIndexEntry(gnum);
+		const auto err = dbase.getGame(*ie).decodeTags(
+		    [&](auto const& tag, auto const&) {
+			    auto it = tag_freq.find(tag);
+			    if (it == tag_freq.end())
+				    tag_freq.emplace(tag, 1);
+			    else
+				    it->second++;
+		    });
+		if (err)
+			return UI_Result(ti, err);
+	}
+
+	UI_List res(tag_freq.size() * 2);
+	for (const auto& [tag, freq] : tag_freq) {
+		if (tag != "SetUp") {
+			res.push_back(tag.c_str());
+			res.push_back(freq);
+		}
+	}
+	return UI_Result(ti, OK, res);
+}
 
 /**
  * sc_base_tournaments() - return a list of tournaments
@@ -906,8 +954,8 @@ UI_res_t sc_base_player_elo(const scidBa
 UI_res_t sc_base_inUse       (UI_extra_t, UI_handle_t, int argc, const char ** argv);
 UI_res_t sc_base_export      (UI_extra_t, UI_handle_t, int argc, const char ** argv);
 UI_res_t sc_base_piecetrack  (UI_extra_t, UI_handle_t, int argc, const char ** argv);
-UI_res_t sc_base_tag         (UI_extra_t, UI_handle_t, int argc, const char ** argv);
-uint sc_base_duplicates (scidBaseT* dbase, UI_handle_t, int argc, const char ** argv);
+UI_res_t sc_base_duplicates  (scidBaseT* dbase, UI_handle_t, int argc, const char ** argv);
+UI_res_t sc_base_gamesummary (const scidBaseT& dbase, UI_handle_t, int, const char**);
 
 
 UI_res_t sc_base (UI_extra_t cd, UI_handle_t ti, int argc, const char ** argv)
@@ -919,7 +967,8 @@ UI_res_t sc_base (UI_extra_t cd, UI_hand
 	    "gamelocation",    "gameslist",       "getGame",         "import",
 	    "inUse",           "isReadOnly",      "list",            "numGames",        "open",
 	    "piecetrack",      "player_elo",      "slot",            "sortcache",       "stats",
-	    "switch",          "tag",             "tournaments",     "type",
+	    "strip",           "switch",          "taglist",         "tournaments",     "type",
+	    "gamesummary",
 	    NULL
 	};
 	enum {
@@ -929,7 +978,8 @@ UI_res_t sc_base (UI_extra_t cd, UI_hand
 	    BASE_GAMELOCATION, BASE_GAMESLIST,    BASE_GETGAME,      BASE_IMPORT,
 	    BASE_INUSE,        BASE_ISREADONLY,   BASE_LIST,         BASE_NUMGAMES,     BASE_OPEN,
 	    BASE_PTRACK,       BASE_PLAYER_ELO,   BASE_SLOT,         BASE_SORTCACHE,    BASE_STATS,
-	    BASE_SWITCH,       BASE_TAG,          BASE_TOURNAMENTS,  BASE_TYPE
+	    BASE_STRIP,        BASE_SWITCH,       BASE_TAGLIST,      BASE_TOURNAMENTS,  BASE_TYPE,
+	    BASE_GAMESUMMARY
 	};
 
 	if (argc <= 1) return UI_Result(ti, ERROR_BadArg, "Usage: sc_base <cmd>");
@@ -959,9 +1009,6 @@ UI_res_t sc_base (UI_extra_t cd, UI_hand
 
 	case BASE_SLOT:
 		return sc_base_slot (ti, argc, argv);
-
-	case BASE_TAG:
-		return sc_base_tag (cd, ti, argc, argv);
 	}
 
 	//New multi-base functions
@@ -980,8 +1027,7 @@ UI_res_t sc_base (UI_extra_t cd, UI_hand
 		return sc_base_copygames(dbase, ti, argc, argv);
 
 	case BASE_DUPLICATES:
-		if (dbase->isReadOnly()) return UI_Result(ti, ERROR_FileReadOnly);
-		return UI_Result(ti, OK, sc_base_duplicates (dbase, ti, argc, argv));
+		return sc_base_duplicates(dbase, ti, argc, argv);
 
 	case BASE_EXTRA:
 		return sc_base_extra(dbase, ti, argc, argv);
@@ -1022,8 +1068,17 @@ UI_res_t sc_base (UI_extra_t cd, UI_hand
 	case BASE_SWITCH:
 		return sc_base_switch (dbase, ti);
 
+	case BASE_STRIP:
+		return sc_base_strip(*dbase, ti, argc, argv);
+
+	case BASE_TAGLIST:
+		return sc_base_taglist(*dbase, ti);
+
 	case BASE_TOURNAMENTS:
 		return sc_base_tournaments (dbase, ti, argc, argv);
+
+	case BASE_GAMESUMMARY:
+		return sc_base_gamesummary(*dbase, ti, argc, argv);
 	}
 
 	std::string res = "sc_base\nInvalid minor command: ";
diff -pruN 1:4.7.0+dfsg1-2/src/sc_filter.cpp 1:4.7.4+dfsg1-2/src/sc_filter.cpp
--- 1:4.7.0+dfsg1-2/src/sc_filter.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/sc_filter.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -16,7 +16,6 @@
 * along with Scid. If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include "common.h"
 #include "dbasepool.h"
 #include "scidbase.h"
 #include "ui.h"
@@ -37,6 +36,23 @@ namespace {
 * Every database has a default filter with id "dbfilter".
 */
 
+/// @baseId: valid database identifier
+/// @filterId: valid identifier of a filter.
+/// @return the ids of the "main" and "mask" filters of the composed filter
+///         @e filterId (if it is not composed returns @filterId and empty).
+UI_res_t sc_filter_components(UI_handle_t ti, const scidBaseT& dbase, int argc,
+                              const char** argv) {
+	const char* usage = "Usage: sc_filter components baseId filterId";
+	if (argc != 4)
+		return UI_Result(ti, ERROR_BadArg, usage);
+
+	auto filters = dbase.getFilterComponents(argv[3]);
+	UI_List res(2);
+	res.push_back(filters.first);
+	res.push_back(filters.second);
+	return UI_Result(ti, OK, res);
+}
+
 /**
  * sc_filter_compose() - compose a new filter
  * @baseId: valid database identifier
@@ -142,19 +158,13 @@ UI_res_t sc_filter_reset(UI_handle_t ti,
  * - the number of games included in the "main" filter composing @filterId
  *   (if @filterId is not a combined filter, this value is equal to the first).
  */
-UI_res_t sc_filter_sizes(UI_handle_t ti, const scidBaseT& dbase, HFilter& filter,
-                       int argc, const char** argv) {
-	const char* usage = "Usage: sc_filter sizes baseId filterId";
-	if (argc != 4) return UI_Result(ti, ERROR_BadArg, usage);
-
-	HFilter unmasked = dbase.getMainFilter(argv[3]);
-	if (unmasked == 0)
-		return UI_Result(ti, ERROR_BadArg, "sc_filter: invalid filterId");
-
+UI_res_t sc_filter_sizes(UI_handle_t ti, const scidBaseT& dbase,
+                         HFilter& filter) {
+	// "Usage: sc_filter sizes baseId filterId";
 	UI_List res(3);
 	res.push_back(filter.size());
 	res.push_back(dbase.numGames());
-	res.push_back(unmasked.size());
+	res.push_back(filter.mainSize());
 	return UI_Result(ti, OK, res);
 }
 
@@ -166,7 +176,7 @@ UI_res_t sc_filter(UI_extra_t cd, UI_han
 	const char* usage = "Usage: sc_filter <cmd> baseId filterId [args]";
 	if (argc < 2) return UI_Result(ti, ERROR_BadArg, usage);
 
-	static const char* options[] = {"compose", "remove", "reset", "sizes", NULL};
+	static const char* options[] = {"components", "compose", "remove", "reset", "sizes", NULL};
 	if (strUniqueMatch(argv[1], options) == - 1)
 		return sc_filter_old(cd, ti, argc, argv);
 
@@ -182,6 +192,8 @@ UI_res_t sc_filter(UI_extra_t cd, UI_han
 		return UI_Result(ti, ERROR_BadArg, usage);
 
 	const char* cmd = argv[1];
+	if (strcmp("components", cmd) == 0)
+		return sc_filter_components(ti, *dbase, argc, argv);
 	if (strcmp("compose", cmd) == 0)
 		return sc_filter_compose(ti, *dbase, argc, argv);
 	if (strcmp("remove", cmd) == 0)
@@ -189,7 +201,7 @@ UI_res_t sc_filter(UI_extra_t cd, UI_han
 	if (strcmp("reset", cmd) == 0)
 		return sc_filter_reset(ti, filter, argc, argv);
 	if (strcmp("sizes", cmd) == 0)
-		return sc_filter_sizes(ti, *dbase, filter, argc, argv);
+		return sc_filter_sizes(ti, *dbase, filter);
 
 	std::string err = "sc_filter\nInvalid minor command: ";
 	return UI_Result(ti, ERROR_BadArg, err + cmd);
diff -pruN 1:4.7.0+dfsg1-2/src/scidbase.cpp 1:4.7.4+dfsg1-2/src/scidbase.cpp
--- 1:4.7.0+dfsg1-2/src/scidbase.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/scidbase.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -24,7 +24,6 @@
 #include "sortcache.h"
 #include "stored.h"
 #include <algorithm>
-#include <math.h>
 
 std::pair<ICodecDatabase*, errorT>
 ICodecDatabase::open(Codec codec, fileModeT fMode, const char* filename,
@@ -58,12 +57,9 @@ scidBaseT::scidBaseT() {
 	gameNumber = -1;
 	gameAltered = false;
 	inUse = false;
-	tree.moveCount = tree.totalCount = 0;
 	fileMode_ = FMODE_None;
-	bbuf = new ByteBuffer(BBUF_SIZE);
 	dbFilter = new Filter(0);
 	treeFilter = new Filter(0);
-	duplicates_ = NULL;
 	stats_ = NULL;
 }
 
@@ -71,22 +67,20 @@ scidBaseT::~scidBaseT() {
 	if (inUse)
 		Close();
 
-	delete[] duplicates_;
 	delete idx;
 	delete nb_;
 	delete game;
-	delete bbuf;
 	delete stats_;
 	delete dbFilter;
 	delete treeFilter;
 }
 
-errorT scidBaseT::Open(ICodecDatabase::Codec dbtype, fileModeT fMode,
-                       const char* filename, const Progress& progress) {
+errorT scidBaseT::openHelper(ICodecDatabase::Codec dbtype, fileModeT fMode,
+                             const char* filename, const Progress& progress) {
+	assert(filename);
+
 	if (inUse)
 		return ERROR_FileInUse;
-	if (filename == 0)
-		filename = "";
 
 	auto obj = ICodecDatabase::open(dbtype, fMode, filename, progress, idx, nb_);
 	if (obj.first) {
@@ -111,7 +105,7 @@ errorT scidBaseT::Open(ICodecDatabase::C
 	return obj.second;
 }
 
-errorT scidBaseT::Close () {
+void scidBaseT::Close () {
 	ASSERT(inUse);
 
 	for (auto& sortCache : sortCaches_) {
@@ -119,7 +113,7 @@ errorT scidBaseT::Close () {
 	}
 	sortCaches_.clear();
 
-	errorT errIdx = idx->Close();
+	idx->Close();
 	nb_->Clear();
 	codec_ = nullptr;
 
@@ -134,24 +128,27 @@ errorT scidBaseT::Close () {
 	for (size_t i=0, n = filters_.size(); i < n; i++) delete filters_[i].second;
 	filters_.clear();
 	inUse = false;
-
-	return errIdx;
 }
 
 
 void scidBaseT::clear() {
 	if (stats_ != NULL) { delete stats_; stats_ = NULL;}
-	if (duplicates_ != NULL) { delete[] duplicates_; duplicates_ = NULL; }
+	duplicates_.reset();
 	treeCache.Clear();
 	for (nameT nt = NAME_PLAYER; nt < NUM_NAME_TYPES; nt++) {
 		nameFreq_[nt].resize(0);
 	}
+	peakEloCache_.clear();
 }
 
-void scidBaseT::beginTransaction() {
+errorT scidBaseT::beginTransaction() {
+	if (isReadOnly())
+		return ERROR_FileReadOnly;
+
 	for (auto& sortCache : sortCaches_) {
 		sortCache.second->prepareForChanges();
 	}
+	return OK;
 }
 
 errorT scidBaseT::endTransaction(gamenumT gNum) {
@@ -175,49 +172,39 @@ errorT scidBaseT::endTransaction(gamenum
 }
 
 errorT scidBaseT::saveGame(Game* game, gamenumT replacedGameId) {
-	beginTransaction();
-	errorT err1 = saveGameHelper(game, replacedGameId);
-	errorT err2 = endTransaction(replacedGameId);
-	return (err1 != OK) ? err1 : err2;
-}
-
-errorT scidBaseT::saveGameHelper(Game* game, gamenumT gameId) {
-	if (isReadOnly())
-		return ERROR_FileReadOnly;
-
-	if (gameId < numGames())
-		return codec_->saveGame(game, gameId);
+	if (auto errModify = beginTransaction())
+		return errModify;
 
-	return codec_->addGame(game);
-}
-
-errorT scidBaseT::importGame(const scidBaseT* srcBase, uint gNum) {
-	if (srcBase == this) return ERROR_BadArg;
-	if (isReadOnly()) return ERROR_FileReadOnly;
-	if (gNum >= srcBase->numGames()) return ERROR_BadArg;
-
-	beginTransaction();
-	errorT err = importGameHelper(srcBase, gNum);
-	errorT errClear = endTransaction();
-	return (err == OK) ? errClear : err;
+	std::vector<byte> buf;
+	auto [ie, tags] = game->Encode(buf);
+	auto gamedata = ByteBuffer(buf.data(), buf.size());
+
+	errorT err = (replacedGameId < numGames())
+	                 ? codec_->saveGame(ie, tags, gamedata, replacedGameId)
+	                 : codec_->addGame(ie, tags, gamedata);
+	errorT errClear = endTransaction(replacedGameId);
+	return (err != OK) ? err : errClear;
 }
 
 errorT scidBaseT::importGames(const scidBaseT* srcBase, const HFilter& filter, const Progress& progress) {
 	ASSERT(srcBase != 0);
 	ASSERT(filter != 0);
 	if (srcBase == this) return ERROR_BadArg;
-	if (isReadOnly()) return ERROR_FileReadOnly;
 
-	beginTransaction();
+	if (auto errModify = beginTransaction())
+		return errModify;
+
 	errorT err = OK;
 	size_t iProgress = 0;
 	size_t totGames = filter->size();
-	for (gamenumT gNum = 0, n = srcBase->numGames(); gNum < n; gNum++) {
-		if (filter.get(gNum) == 0) continue;
+	for (const auto gNum : filter) {
 		err = importGameHelper(srcBase, gNum);
-		if (err != OK) break;
+		if (err != OK)
+			break;
+
 		if (++iProgress % 8192 == 0) {
-			if (!progress.report(iProgress, totGames)) break;
+			if (!progress.report(iProgress, totGames))
+				break;
 		}
 	}
 	errorT errClear = endTransaction();
@@ -228,10 +215,11 @@ errorT scidBaseT::importGameHelper(const
 	auto srcIe = srcBase->getIndexEntry(gNum);
 	auto dataSz = srcIe->GetLength();
 	auto data = srcBase->codec_->getGameData(srcIe->GetOffset(), dataSz);
-	if (data == nullptr)
+	if (!data)
 		return ERROR_FileRead;
 
-	return codec_->addGame(srcIe, srcBase->getNameBase(), data, dataSz);
+	return codec_->addGame(
+	    *srcIe, TagRoster::make(*srcIe, *srcBase->getNameBase()), data);
 }
 
 errorT scidBaseT::importGames(ICodecDatabase::Codec dbtype,
@@ -239,23 +227,73 @@ errorT scidBaseT::importGames(ICodecData
                               std::string& errorMsg) {
 	ASSERT(dbtype == ICodecDatabase::PGN);
 
-	if (isReadOnly())
-		return ERROR_FileReadOnly;
-
-	beginTransaction();
+	if (auto errModify = beginTransaction())
+		return errModify;
 
 	CodecPgn pgn;
 	auto res = pgn.open(filename, FMODE_ReadOnly);
 	if (res == OK) {
-		res = CodecPgn::parseGames(
-		    progress, pgn, [&](Game& game) { return codec_->addGame(&game); });
+		uint64_t nChess960Errors = 0;
+		std::vector<byte> buf;
+		res = CodecPgn::parseGames(progress, pgn, [&](Game& game) {
+			buf.clear();
+			auto [ie, tags] = game.Encode(buf);
+			auto err = codec_->addGame(ie, tags, {buf.data(), buf.size()});
+			if (err == ERROR_CodecChess960) {
+				++nChess960Errors;
+				err = OK;
+			}
+			return err;
+		});
 		errorMsg = pgn.parseErrors();
+		if (nChess960Errors) {
+			errorMsg.append("Ignored ");
+			errorMsg.append(std::to_string(nChess960Errors));
+			errorMsg.append(" chess960 game(s).\n");
+		}
 	}
 
 	auto res_endTrans = endTransaction();
 	return (res != OK) ? res : res_endTrans;
 }
 
+errorT scidBaseT::invertFlag(uint flag, uint gNum) {
+	return setFlag(!getFlag(flag, gNum), flag, gNum);
+}
+
+errorT scidBaseT::invertFlags(uint flag, const HFilter& filter) {
+	return transformIndex(filter, Progress(),
+	                      [&](IndexEntry& ie) {
+		                      const auto value = ie.GetFlag(flag);
+		                      ie.SetFlag(flag, !value);
+		                      return true;
+	                      })
+	    .first;
+}
+
+errorT scidBaseT::setFlag(bool value, uint flag, uint gNum) {
+	ASSERT(gNum < idx->GetNumGames());
+
+	IndexEntry ie = *getIndexEntry(gNum);
+	ie.SetFlag(flag, value);
+
+	if (auto errModify = beginTransaction())
+		return errModify;
+
+	const auto res = codec_->saveIndexEntry(ie, gNum);
+	const auto err = endTransaction(gNum);
+	return res != OK ? res : err;
+}
+
+errorT scidBaseT::setFlags(bool value, uint flag, const HFilter& filter) {
+	return transformIndex(filter, Progress(),
+	                      [&](IndexEntry& ie) {
+		                      ie.SetFlag(flag, value);
+		                      return true;
+	                      })
+	    .first;
+}
+
 /**
  * Filters
  */
@@ -272,8 +310,8 @@ std::string scidBaseT::newFilter() {
 	return newname;
 }
 
-std::string scidBaseT::composeFilter(const std::string& mainFilter,
-                                     const std::string& maskFilter) const {
+std::string scidBaseT::composeFilter(std::string_view mainFilter,
+                                     std::string_view maskFilter) const {
 	std::string res;
 	if (mainFilter.empty()) return res;
 
@@ -286,7 +324,8 @@ std::string scidBaseT::composeFilter(con
 	}
 
 	if (!maskFilter.empty()) {
-		res = '+' + res + "+" + maskFilter;
+		res = '+' + res + "+";
+		res.append(maskFilter);
 	}
 
 	if (getFilter(res) == 0) res.clear();
@@ -303,33 +342,51 @@ void scidBaseT::deleteFilter(const char*
 	}
 }
 
-Filter* scidBaseT::fetchFilter(const std::string& filterId) const {
-	if (filterId == "dbfilter") return dbFilter;
-	if (filterId == "tree") return treeFilter;
-
-	for (size_t i = 0, n = filters_.size(); i < n; i++) {
-		if (filterId == filters_[i].first)
-			return filters_[i].second;
-	}
-	return 0;
-}
+HFilter scidBaseT::getFilter(std::string_view filterId) const {
+	const auto findFilter = [&](auto const& id) -> Filter* {
+		if (id == "dbfilter")
+			return dbFilter;
+		if (id == "tree")
+			return treeFilter;
+
+		for (auto const& [name, filter] : filters_) {
+			if (name == id)
+				return filter;
+		}
+		return nullptr;
+	};
 
-HFilter scidBaseT::getFilterHelper(const std::string& filterId,
-                                   bool unmasked) const {
-	Filter* main = 0;
-	const Filter* mask = 0;
+	Filter* main = nullptr;
+	const Filter* mask = nullptr;
 	if (filterId.empty() || filterId[0] != '+') {
-		main = fetchFilter(filterId);
+		main = findFilter(filterId);
 	} else {
 		size_t maskName = filterId.find('+', 1);
 		if (maskName != std::string::npos) {
-			main = fetchFilter(filterId.substr(1, maskName - 1));
-			if (!unmasked) mask = fetchFilter(filterId.substr(maskName + 1));
+			main = findFilter(filterId.substr(1, maskName - 1));
+			mask = findFilter(filterId.substr(maskName + 1));
 		}
 	}
 	return HFilter(main, mask);
 }
 
+std::pair<std::string, std::string>
+scidBaseT::getFilterComponents(std::string_view filterID) const {
+	if (filterID.empty())
+		return {};
+
+	if (filterID[0] != '+')
+		return {std::string(filterID), {}};
+
+	size_t maskName = filterID.find('+', 1);
+	ASSERT(maskName != std::string::npos);
+	ASSERT(getFilter(filterID.substr(1, maskName - 1)) != nullptr);
+	ASSERT(getFilter(filterID.substr(maskName + 1)) != nullptr);
+
+	return {std::string(filterID.substr(1, maskName - 1)),
+	        std::string(filterID.substr(maskName + 1))};
+}
+
 /**
  * Statistics
  */
@@ -444,23 +501,8 @@ const scidBaseT::Stats::Eco* scidBaseT::
 }
 
 
-
-double scidBaseT::TreeStat::expVect_[1600];
-
-scidBaseT::TreeStat::TreeStat()
-: toMove(NOCOLOR), resultW(0), resultD(0), resultB(0), exp(0), ngames(0), nexp(0)
-{
-	if (TreeStat::expVect_[0] == 0) {
-		for (int i=-800; i < 800; i++) TreeStat::expVect_[i+800] = 1/(1 + pow(10, i/400.0));
-	}
-}
-
-std::vector<scidBaseT::TreeStat> scidBaseT::getTreeStat(const HFilter& filter) {
-	ASSERT(filter != 0);
-
-	std::vector<scidBaseT::TreeStat> res;
-	std::vector<FullMove> v;
-	auto nb = getNameBase();
+std::vector<TreeNode> scidBaseT::getTreeStat(const HFilter& filter) const {
+	std::vector<TreeNode> res;
 	for (gamenumT gnum = 0, n = numGames(); gnum < n; gnum++) {
 		uint ply = filter.get(gnum);
 		if (ply == 0) continue;
@@ -471,20 +513,16 @@ std::vector<scidBaseT::TreeStat> scidBas
 		if (!move)
 			move = getGame(ie).getMove(ply);
 
-		size_t i = 0;
-		while (i < v.size() && v[i] != move) i++;
-		if (i == v.size()) {
-			v.push_back(move);
-			res.push_back(scidBaseT::TreeStat());
-		}
-		res[i].add(ie->GetResult(), ie->GetWhiteElo(nb), ie->GetBlackElo(nb));
-	}
+		auto it = std::find_if(
+		    res.begin(), res.end(),
+		    [move](auto const& stat) { return stat.move == move; });
 
-	for (size_t i = 0, n = v.size(); i < n; i++) {
-		res[i].SAN = !v[i] ? "[end]" : v[i].getSAN(&(res[i].toMove));
+		auto& node = (it != res.end()) ? *it : res.emplace_back(move);
+		node.add(ie->GetResult(), ie->GetWhiteElo(), ie->GetBlackElo(),
+		         ie->GetYear());
 	}
 
-	std::sort(res.begin(), res.end());
+	std::sort(res.begin(), res.end(), TreeNode::cmp_ngames_desc());
 	return res;
 }
 
@@ -528,26 +566,15 @@ errorT scidBaseT::compact(const Progress
 	std::vector<std::string> filenames = codec_->getFilenames();
 	if (filenames.empty()) return ERROR_CodecUnsupFeat;
 
-	if (fileMode_ != FMODE_Both) {
-		//Older scid version to be upgraded are opened read only
-		if (idx->GetVersion() == SCID_VERSION) return ERROR_FileMode;
-	}
-
 	//1) Create a new temporary database
 	std::string filename = fileName_;
 	std::string tmpfile = filename + "__COMPACT__";
 	ICodecDatabase::Codec dbtype = codec_->getType();
 	scidBaseT tmp;
-	errorT err_Create = tmp.Open(dbtype, FMODE_Create, tmpfile.c_str());
+	errorT err_Create = tmp.openHelper(dbtype, FMODE_Create, tmpfile.c_str());
 	if (err_Create != OK) return err_Create;
 
-	//2) Copy the Index Header
-	tmp.beginTransaction();
-	tmp.idx->copyHeaderInfo(*idx);
-	gamenumT autoloadOld = idx->GetAutoLoad();
-	gamenumT autoloadNew = 1;
-
-	//3) Create the list of games to be copied
+	//2) Create the list of games to be copied
 	std::vector< std::pair<uint64_t, gamenumT> > sort;
 	uint n_deleted = 0;
 	for (gamenumT i = 0, n = numGames(); i < n; i++) {
@@ -565,7 +592,29 @@ errorT scidBaseT::compact(const Progress
 		order |= ie->GetFinalMatSig() & 0xFFFFFF;
 		sort.emplace_back(order, i);
 	}
-	std::stable_sort(sort.begin(), sort.end());
+	if (sort.size() > 10000) // Reorder only larger databases
+		std::stable_sort(sort.begin(), sort.end());
+
+	//3) Copy the Index Header
+	auto extraInfo = getExtraInfo();
+	errorT err_Header = tmp.beginTransaction();
+	for (auto& pair : extraInfo) {
+		if (err_Header != OK)
+			break;
+
+		if (std::strcmp(pair.first, "autoload") == 0) {
+			gamenumT autoloadOld = strGetUnsigned(pair.second.c_str());
+			size_t autoloadNew = 1;
+			for (size_t i = 0, n = sort.size(); i < n; ++i) {
+				if (sort[i].second + 1 == autoloadOld) {
+					autoloadNew = i + 1;
+					break;
+				}
+			}
+			pair.second = std::to_string(autoloadNew);
+		}
+		err_Header = tmp.codec_->setExtraInfo(pair.first, pair.second.c_str());
+	}
 
 	//4) Copy the games
 	uint iProgress = 0;
@@ -575,8 +624,6 @@ errorT scidBaseT::compact(const Progress
 		err_AddGame = tmp.importGameHelper(this, it->second);
 		if (err_AddGame != OK) break;
 
-		gamenumT oldGnum = it->second + 1;
-		if (oldGnum == autoloadOld) autoloadNew = tmp.numGames();
 		//TODO:
 		//- update bookmarks game number
 		//  (*it).second   == old game number
@@ -590,24 +637,26 @@ errorT scidBaseT::compact(const Progress
 	}
 
 	//5) Finalize the new database
-	tmp.idx->SetAutoLoad(autoloadNew);
 	std::vector<std::string> tmp_filenames = tmp.codec_->getFilenames();
 	errorT err_NbWrite = tmp.endTransaction();
-	errorT err_Close = tmp.Close();
-	if (err_Close == OK) err_Close = (filenames.size() == tmp_filenames.size()) ? OK : ERROR;
+	tmp.Close();
+	auto err_Close = (filenames.size() == tmp_filenames.size()) ? OK : ERROR;
 
 	//6) Error: cleanup and report
-	if (err_NbWrite != OK || err_Close != OK || err_UserCancel || err_AddGame != OK) {
+	if (err_Header != OK || err_AddGame != OK || err_UserCancel ||
+	    err_NbWrite != OK || err_Close != OK) {
 		for (size_t i = 0, n = tmp_filenames.size(); i < n; i++) {
 			std::remove(tmp_filenames[i].c_str());
 		}
+		if (err_Header != OK)
+			return err_Header;
 		if (err_AddGame != OK)
 			return err_AddGame;
 		if (err_UserCancel)
 			return ERROR_UserCancel;
 		if (err_NbWrite != OK)
 			return err_NbWrite;
-		ASSERT(err_Close != OK);
+
 		return err_Close;
 	}
 
@@ -624,7 +673,7 @@ errorT scidBaseT::compact(const Progress
 	}
 
 	//8) Remove the old database
-	if (Close() != OK) return ERROR_FileInUse;
+	Close();
 	for (size_t i = 0, n = filenames.size(); i < n; i++) {
 		if (std::remove(filenames[i].c_str()) != 0) return ERROR_CompactRemove;
 	}
@@ -635,7 +684,7 @@ errorT scidBaseT::compact(const Progress
 		const char* s2 = filenames[i].c_str();
 		std::rename(s1, s2);
 	}
-	errorT res = Open(dbtype, FMODE_Both, filename.c_str());
+	errorT res = openHelper(dbtype, FMODE_Both, filename.c_str());
 
 	//10) Re-create filters and SortCaches
 	if (res == OK || res == ERROR_NameDataLoss) {
@@ -696,12 +745,12 @@ void scidBaseT::releaseSortCache(const c
 	}
 }
 
-SortCache* scidBaseT::createSortCache(const char* criteria) {
-	SortCache* sc = getSortCache(criteria);
-	if (sc != NULL)
+bool scidBaseT::createSortCache(const char* criteria) {
+	if (auto sc = getSortCache(criteria)) {
 		sc->incrRef(1);
-
-	return sc;
+		return true;
+	}
+	return false;
 }
 
 size_t scidBaseT::listGames(const char* criteria, size_t start, size_t count,
diff -pruN 1:4.7.0+dfsg1-2/src/scidbase.h 1:4.7.4+dfsg1-2/src/scidbase.h
--- 1:4.7.0+dfsg1-2/src/scidbase.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/scidbase.h	2022-07-09 05:14:42.000000000 +0000
@@ -1,5 +1,5 @@
 /*
-# Copyright (C) 2014-2016 Fulvio Benini
+# Copyright (C) 2014-2019 Fulvio Benini
 
 * This file is part of Scid (Shane's Chess Information Database).
 *
@@ -22,14 +22,16 @@
 #include "bytebuf.h"
 #include "codec.h"
 #include "containers.h"
-#include "fastgame.h"
+#include "gameview.h"
 #include "game.h"
 #include "index.h"
 #include "namebase.h"
 #include "tree.h"
 #include <array>
-#include <vector>
+#include <cassert>
 #include <memory>
+#include <string_view>
+#include <vector>
 
 class SortCache;
 
@@ -68,31 +70,23 @@ struct scidBaseT {
 		Eco ecoGroup3_[(1 + (1<<16)/131)];
 	};
 
-	struct TreeStat {
-		colorT toMove;
-		std::string SAN;
-		int resultW, resultD, resultB;
-		double exp;
-		int ngames, nexp;
-
-	public:
-		TreeStat();
-		void add(int result, int eloW, int eloB);
-		bool operator<(const TreeStat& cmp) const { return ngames > cmp.ngames; }
-
-	private:
-		static double expVect_[1600];
-	};
-
 	scidBaseT();
 	~scidBaseT();
 
-	errorT Open(ICodecDatabase::Codec dbtype,
-	            fileModeT mode,
-	            const char* filename = 0,
-	            const Progress& progress = Progress());
+	errorT open(std::string_view dbType, fileModeT fMode, const char* filename,
+	            const Progress& progress = {}) {
+		auto codec = ICodecDatabase::SCID4;
+		if (dbType == "PGN") {
+			codec = ICodecDatabase::PGN;
+		} else if (dbType == "MEMORY") {
+			codec = ICodecDatabase::MEMORY;
+		} else if (dbType != "SCID4") {
+			return ERROR_BadArg;
+		}
+		return openHelper(codec, fMode, filename, progress);
+	}
 
-	errorT Close ();
+	void Close ();
 
 	const std::string& getFileName() const { return fileName_; }
 	bool isReadOnly() const { return fileMode_ == FMODE_ReadOnly; }
@@ -106,60 +100,78 @@ struct scidBaseT {
 
 	/// Store an extra information about the database (type, description, etc..)
 	errorT setExtraInfo(const char* tagname, const char* new_value) {
-		// TODO: move the code to CodecMemory and CodecSCID4
-		if (std::strcmp(tagname, "type") == 0)
-			return idx->SetType(strGetUnsigned(new_value));
-
-		if (codec_->getType() == ICodecDatabase::SCID4) {
-			if (std::strcmp(tagname, "description") == 0)
-				return idx->SetDescription(new_value);
-
-			if (std::strcmp(tagname, "autoload") == 0)
-				return idx->SetAutoLoad(strGetUnsigned(new_value));
-
-			auto len = std::strlen(tagname);
-			if (len == 5 && std::equal(tagname, tagname + 4, "flag")) {
-				uint flagType = IndexEntry::CharToFlag(tagname[4]);
-				if (flagType != 0 &&
-				    idx->GetCustomFlagDesc(flagType) != nullptr)
-					return idx->SetCustomFlagDesc(flagType, new_value);
-			}
-		}
-		return ERROR_CodecUnsupFeat;
+		if (isReadOnly())
+			return ERROR_FileReadOnly;
+
+		const auto res = codec_->setExtraInfo(tagname, new_value);
+		return (res != OK) ? res : codec_->flush();
 	}
 
 	const IndexEntry* getIndexEntry(gamenumT g) const {
+		assert(g < numGames());
 		return idx->GetEntry(g);
 	}
 	const IndexEntry* getIndexEntry_bounds(gamenumT g) const {
-		if (g < numGames()) return getIndexEntry(g);
-		return 0;
+		static_assert(std::is_unsigned_v<gamenumT>);
+		return g < numGames() ? getIndexEntry(g) : nullptr;
 	}
 	const NameBase* getNameBase() const {
 		return nb_;
 	}
-	FastGame getGame(const IndexEntry* ie) const {
-		uint length = ie->GetLength();
-		const byte* b = codec_->getGameData(ie->GetOffset(), length);
-		if (b == 0) length = 0; // Error
-		return FastGame::Create(b, b + length);
-	}
-	errorT getGame(const IndexEntry* ie, ByteBuffer* destBuf) const {
-		uint length = ie->GetLength();
-		const byte* b = codec_->getGameData(ie->GetOffset(), length);
-		if (b == 0) return ERROR_FileRead;
-		// The data for the game is not actually copied into the bytebuffer, which would
-		// be slower and a waste of time if the bytebuffer is not going to be modified.
-		destBuf->ProvideExternal(const_cast<byte*>(b), length);
-		return OK;
+
+	/// Return the highest elo of the player (in the database's games)
+	eloT peakElo(idNumberT playerID) const {
+		if (peakEloCache_.empty()) {
+			const auto maxPlayerID = nb_->GetNumNames(NAME_PLAYER);
+			peakEloCache_.resize(maxPlayerID, 0);
+			auto updateMax = [&](auto id, auto elo) {
+				if (elo > peakEloCache_[id])
+					peakEloCache_[id] = elo;
+			};
+			for (gamenumT gnum = 0, n = numGames(); gnum < n; gnum++) {
+				IndexEntry const& ie = *getIndexEntry(gnum);
+				updateMax(ie.GetWhite(), ie.GetWhiteElo());
+				updateMax(ie.GetBlack(), ie.GetBlackElo());
+			}
+		}
+
+		ASSERT(playerID < peakEloCache_.size());
+		return peakEloCache_[playerID];
+	}
+
+	eloT peakElo(const char* player) const {
+		idNumberT playerID;
+		if (getNameBase()->FindExactName(NAME_PLAYER, player, &playerID) == OK)
+			return peakElo(playerID);
+
+		return 0;
+	}
+
+	GameView getGame(const IndexEntry* ie) const {
+		auto data = codec_->getGameMoves(*ie);
+		if (data) {
+			auto [errPos, fen] = data.decodeStartBoard();
+			if (errPos == OK) {
+				if (fen) {
+					Position startPos;
+					if (startPos.ReadFromFEN(fen) == OK) {
+						return GameView(data, startPos);
+					}
+				} else {
+					return GameView(data);
+				}
+			}
+		}
+		return GameView({nullptr, 0});
+	}
+	ByteBuffer getGame(const IndexEntry& ie) const {
+		return codec_->getGameData(ie.GetOffset(), ie.GetLength());
 	}
 	errorT getGame(const IndexEntry& ie, Game& dest) const {
-		ByteBuffer buf(0);
-		auto res = getGame(&ie, &buf);
-		return (res != OK) ? res : dest.Decode(&buf, GAME_DECODE_ALL);
+		return dest.Decode(ie, TagRoster::make(ie, *getNameBase()),
+		                   getGame(ie));
 	}
 
-	errorT importGame(const scidBaseT* srcBase, uint gNum);
 	errorT importGames(const scidBaseT* srcBase, const HFilter& filter,
 	                   const Progress& progress);
 	errorT importGames(ICodecDatabase::Codec dbtype, const char* filename,
@@ -173,42 +185,45 @@ struct scidBaseT {
 	 * @returns OK if successful or an error code.
 	 */
 	errorT saveGame(Game* game, gamenumT replacedGameId = INVALID_GAMEID);
-	// TODO: private:
-	errorT saveGameHelper(Game* game, gamenumT gameId);
-
 
 	bool getFlag(uint flag, uint gNum) const {
 		return idx->GetEntry(gNum)->GetFlag (flag);
 	}
 	errorT setFlag(bool value, uint flag, uint gNum);
-	errorT setFlag(bool value, uint flag, const HFilter& filter);
+	errorT setFlags(bool value, uint flag, const HFilter& filter);
 	errorT invertFlag(uint flag, uint gNum);
-	errorT invertFlag(uint flag, const HFilter& filter);
+	errorT invertFlags(uint flag, const HFilter& filter);
 
 	/**
 	 * A Filter is a selection of games, usually obtained searching the
 	 * database. A new Filter is created calling the function newFilter()
 	 * and must be released calling the function deleteFilter().
-	 * A composed Filter is a special construct created combining two Filters
-	 * and includes only the games contained in both Filters.
-	 * A composed Filter should NOT be released.
 	 */
 	std::string newFilter();
-	std::string composeFilter(const std::string& mainFilter,
-	                          const std::string& maskFilter) const;
 	void deleteFilter(const char* filterId);
-	HFilter getFilter(const std::string& filterId) const {
-		return getFilterHelper(filterId, false);
-	}
-	HFilter getMainFilter(const std::string& filterId) const {
-		return getFilterHelper(filterId, true);
-	}
+	HFilter getFilter(std::string_view filterId) const;
+
+	/// A composed filter is a special construct created combining two filters
+	/// and includes only the games contained in both filters. It should NOT be
+	/// deleted and became invalid if any of its components is deleted.
+	/// @mainFilter: valid identifier of the main filter (the filter which is
+	///              modified by non-const operations).
+	/// @maskFilter: valid identifier of the mask filter (const).
+	/// @return the id of the composed filter.
+	std::string composeFilter(std::string_view mainFilter,
+	                          std::string_view maskFilter) const;
+
+	/// Get the components of a composed filter.
+	/// @filterId: valid identifier of a filter.
+	/// @return the components (second is empty if its not a a composed filter).
+	std::pair<std::string, std::string>
+	getFilterComponents(std::string_view filterId) const;
 
 	const Stats& getStats() const;
-	std::vector<scidBaseT::TreeStat> getTreeStat(const HFilter& filter);
+	std::vector<TreeNode> getTreeStat(const HFilter& filter) const;
 	uint getNameFreq (nameT nt, idNumberT id) {
 		if (nameFreq_[nt].size() == 0)
-			nameFreq_ = idx->calcNameFreq(*getNameBase());
+			nameFreq_ = getNameBase()->calcNameFreq(*idx);
 		return nameFreq_[nt][id];
 	}
 
@@ -224,10 +239,9 @@ struct scidBaseT {
 	 * @param criteria: the list of fields by which games will be ordered.
 	 *                  Each field should be followed by '+' to indicate an
 	 *                  ascending order or by '-' for a descending order.
-	 * @returns a pointer to a SortCache object in case of success, NULL
-	 * otherwise.
+	 * @returns true on success
 	 */
-	SortCache* createSortCache(const char* criteria);
+	bool createSortCache(const char* criteria);
 
 	/**
 	 * Decrement the reference count of the SortCache object matching @e
@@ -274,14 +288,6 @@ struct scidBaseT {
 	size_t sortedPosition(const char* criteria, const HFilter& filter,
 	                      gamenumT gameId);
 
-	void setDuplicates(uint* duplicates) {
-		if (duplicates_ != NULL) { delete[] duplicates_; duplicates_ = NULL; }
-		duplicates_ = duplicates;
-	}
-	uint getDuplicates(gamenumT gNum) {
-		return (duplicates_ == NULL) ? 0 : duplicates_[gNum];
-	}
-
 	/**
 	 * Transform the IndexEntries of the games included in @e hfilter.
 	 * The @e entry_op must accept a IndexEntry& parameter and return true when
@@ -295,7 +301,9 @@ struct scidBaseT {
 	template <typename TOper>
 	std::pair<errorT, size_t>
 	transformIndex(HFilter hfilter, const Progress& progress, TOper entry_op) {
-		beginTransaction();
+		if (auto errModify = beginTransaction())
+			return {errModify, 0};
+
 		auto res = transformIndex_(hfilter, progress, entry_op);
 		auto err = endTransaction();
 		res.first = (res.first == OK) ? err : res.first;
@@ -324,34 +332,83 @@ struct scidBaseT {
 	               const std::vector<std::string>& newNames, TInitFunc fnInit,
 	               TMapFunc getID);
 
-	// TODO: private:
 	/**
-	 * This function must be called before modifying the games of the database.
-	 * Currently this function do not guarantees that the database is not altered
-	 * in case of errors.
+	 * Strip the games included in @e hfilter.
+	 * @param hfilter:  HFilter containing the games to be transformed.
+	 * @param progress: a Progress object used for GUI communications.
+	 * @param entry_op: operator that will be applied to games.
+	 * @returns a std::pair containing OK (or an error code) and the number of
+	 * games modified.
 	 */
-	void beginTransaction();
+	std::pair<errorT, size_t>
+	stripGames(HFilter hfilter, const Progress& progress,
+	           std::vector<std::string_view> const& removeTags) {
+		if (auto errModify = beginTransaction())
+			return {errModify, 0};
 
-	// TODO: private:
-	/**
-	 * Update caches and flush the database's files.
-	 * This function must be called after changing one or more games.
-	 * @param gameId: id of the modified game
-	 *                INVALID_GAMEID to update all games.
-	 * @returns OK if successful or an error code.
-	 */
-	errorT endTransaction(gamenumT gameId = INVALID_GAMEID);
+		std::vector<std::pair<std::string_view, std::string_view>> tagsBuf;
+		std::vector<byte> encodeBuf;
+		size_t nCorrections = 0;
+		size_t iProg = 0;
+		const size_t totProg = hfilter->size();
+		errorT err = OK;
+		for (const auto gnum : hfilter) {
+			if ((++iProg % 1024 == 0) && !progress.report(iProg, totProg)) {
+				err = ERROR_UserCancel;
+				break;
+			}
+
+			bool changed = false;
+			tagsBuf.clear();
+			IndexEntry const& ie = *getIndexEntry(gnum);
+			auto gamedata = getGame(ie);
+			auto err = gamedata.decodeTags(
+			    [&](auto const& tag, auto const& value) {
+				    if (std::find(removeTags.begin(), removeTags.end(), tag) !=
+				        removeTags.end())
+					    changed = true;
+				    else
+					    tagsBuf.emplace_back(tag, value);
+			    });
+			if (err != OK)
+				break;
+
+			if (!changed)
+				continue;
+
+			encodeBuf.clear();
+			encodeTags(tagsBuf, encodeBuf);
+			encodeBuf.insert(encodeBuf.end(), gamedata.data(),
+			                 gamedata.data() + gamedata.size());
+			err = codec_->saveGame(ie, TagRoster::make(ie, *nb_),
+			                       {encodeBuf.data(), encodeBuf.size()}, gnum);
+			if (err != OK)
+				break;
+
+			++nCorrections;
+		}
+		const auto err_trans = endTransaction();
+		if (err == OK)
+			err = err_trans;
+		return {err, nCorrections};
+	}
+
+	std::unique_ptr<gamenumT[]> extractDuplicates() {
+		return std::move(duplicates_);
+	}
+	void setDuplicates(std::unique_ptr<gamenumT[]> duplicates) {
+		duplicates_ = std::move(duplicates);
+	}
+	gamenumT getDuplicates(gamenumT gNum) const {
+		return duplicates_ ? duplicates_[gNum] : 0;
+	}
 
 public:
-	Index* idx;       // the Index file in memory for this base.
 	bool inUse;       // true if the database is open (in use).
-	treeT tree;
 	TreeCache treeCache;
-	ByteBuffer* bbuf;
 	Filter* dbFilter;
 	Filter* treeFilter;
 
-
 	//TODO: this vars do not belong to scidBaseT class
 	Game* game;       // the active game for this base.
 	int gameNumber;   // game number of active game.
@@ -361,22 +418,37 @@ public:
 
 private:
 	std::unique_ptr<ICodecDatabase> codec_;
+	Index* idx;
 	NameBase* nb_;
 	std::string fileName_; // File name without ".si" suffix
 	fileModeT fileMode_; // Read-only, write-only, or both.
 	std::vector< std::pair<std::string, Filter*> > filters_;
 	mutable Stats* stats_;
 	std::array<std::vector<int>, NUM_NAME_TYPES> nameFreq_;
-	uint* duplicates_; // For each game: idx of duplicate game + 1 (0 if there is no duplicate).
+	std::unique_ptr<gamenumT[]> duplicates_; // For each game: idx of duplicate game + 1 (0 if there is no duplicate).
 	std::vector< std::pair<std::string, SortCache*> > sortCaches_;
+	mutable std::vector<eloT> peakEloCache_;
 
 private:
+	errorT openHelper(ICodecDatabase::Codec dbtype, fileModeT mode,
+	                  const char* filename, const Progress& progress = {});
+
 	void clear();
+
+	/// This function must be called before modifying the games of the database.
+	/// Currently this function do not guarantees that the database is not
+	/// altered in case of errors.
+	errorT beginTransaction();
+
+	/// Update caches and flush the database's files.
+	/// This function must be called after changing one or more games.
+	/// @param gameId: id of the modified game
+	///                INVALID_GAMEID to update all games.
+	/// @returns OK if successful or an error code.
+	errorT endTransaction(gamenumT gameId = INVALID_GAMEID);
+
 	errorT importGameHelper(const scidBaseT* sourceBase, uint gNum);
 
-	Filter* fetchFilter(const std::string& filterId) const;
-	HFilter getFilterHelper(const std::string& filterId,
-	                        bool unmasked = false) const;
 	SortCache* getSortCache(const char* criteria);
 
 	/**
@@ -413,65 +485,13 @@ private:
 	}
 };
 
-inline void scidBaseT::TreeStat::add(int result, int eloW, int eloB) {
-	ngames++;
-	double r = 0;
-	switch (result) {
-		case RESULT_White: resultW++; r = 1; break;
-		case RESULT_Draw: resultD++; r = 0.5; break;
-		case RESULT_Black: resultB++; break;
-		default: return;
-	}
-	if (eloW == 0 || eloB == 0) return;
-	int eloDiff = eloB - eloW;
-	if (eloDiff < 800 && eloDiff >= -800) {
-		exp += r - expVect_[eloDiff+800];
-		nexp++;
-	}
-}
-
-inline errorT scidBaseT::invertFlag(uint flag, uint gNum) {
-	return setFlag(! getFlag(flag, gNum), flag, gNum);
-}
-
-inline errorT scidBaseT::invertFlag(uint flag, const HFilter& filter) {
-	errorT res = OK;
-	for (gamenumT i = 0, n = numGames(); i < n; i++) {
-		if (filter != 0 && filter->get(i) == 0) continue;
-		res = invertFlag(flag, i);
-		if (res != OK) return res;
-	}
-	return res;
-}
-
-inline errorT scidBaseT::setFlag(bool value, uint flag, uint gNum){
-	ASSERT(gNum < idx->GetNumGames());
-	IndexEntry* ie = idx->FetchEntry (gNum);
-	ie->SetFlag (flag, value);
-	errorT res = idx->WriteEntry(ie, gNum);
-	if (stats_ != NULL) { delete stats_; stats_ = NULL;}
-	// TODO: necessary only for sortcaches with SORTING_deleted (and SORTING_flags when implemented)
-	// idx->IndexUpdated(gNum);
-	return res;
-}
-
-inline errorT scidBaseT::setFlag(bool value, uint flag, const HFilter& filter) {
-	errorT res = OK;
-	for (gamenumT gNum = 0, n = numGames(); gNum < n; gNum++) {
-		if (filter != 0 && filter->get(gNum) == 0) continue;
-		res = setFlag(value, flag, gNum);
-		if (res != OK) return res;
-	}
-	return res;
-}
-
-
 template <typename TInitFunc, typename TMapFunc>
 std::pair<errorT, size_t>
 scidBaseT::transformNames(nameT nt, HFilter hfilter, const Progress& progress,
                           const std::vector<std::string>& newNames,
-                          TInitFunc initFunc, TMapFunc getID) {
-	beginTransaction();
+                          TInitFunc initFunc, TMapFunc getNewID) {
+	if (auto errModify = beginTransaction())
+		return {errModify, 0};
 
 	std::vector<idNumberT> nameIDs(newNames.size());
 	auto it = nameIDs.begin();
@@ -486,43 +506,46 @@ scidBaseT::transformNames(nameT nt, HFil
 
 	initFunc(nameIDs);
 
-	auto fnGet = [](nameT nt, const IndexEntry& ie) {
-		switch (nt) { // clang-format off
-		case NAME_PLAYER: return ie.GetWhite();
-		case NAME_EVENT:  return ie.GetEvent();
-		case NAME_SITE:   return ie.GetSite();
-		} // clang-format on
-		ASSERT(nt == NAME_ROUND);
-		return ie.GetRound();
-	};
-	auto fnSet = [](nameT nt, IndexEntry& ie, idNumberT newID) {
-		switch (nt) { // clang-format off
-		case NAME_PLAYER: return ie.SetWhite(newID);
-		case NAME_EVENT:  return ie.SetEvent(newID);
-		case NAME_SITE:   return ie.SetSite(newID);
-		} // clang-format on
-		ASSERT(nt == NAME_ROUND);
-		return ie.SetRound(newID);
-	};
 	auto res = transformIndex_(hfilter, progress, [&](IndexEntry& ie) {
 		const IndexEntry& ie_const = ie;
-		auto oldID = fnGet(nt, ie);
-		auto newID = getID(oldID, ie_const);
-		bool b1 = (oldID != newID);
-		idNumberT newBlack = 0;
-		bool b2 = (nt == NAME_PLAYER);
-		if (b2) {
-			auto oldBlack = ie.GetBlack();
-			newBlack = getID(oldBlack, ie_const);
-			b2 = (oldBlack != newBlack);
+		idNumberT oldID;
+		idNumberT oldBlackID = 0;
+		idNumberT newBlackID = 0;
+		switch (nt) {
+		case NAME_PLAYER:
+			oldID = ie_const.GetWhite();
+			oldBlackID = ie_const.GetBlack();
+			newBlackID = getNewID(oldBlackID, ie_const);
+			break;
+		case NAME_EVENT:
+			oldID = ie_const.GetEvent();
+			break;
+		case NAME_SITE:
+			oldID = ie_const.GetSite();
+			break;
+		default:
+			ASSERT(nt == NAME_ROUND);
+			oldID = ie_const.GetRound();
 		}
-		if (!b1 && !b2)
+		const auto newID = getNewID(oldID, ie_const);
+		if (oldID == newID && oldBlackID == newBlackID)
 			return false;
 
-		if (b1)
-			fnSet(nt, ie, newID);
-		if (b2)
-			ie.SetBlack(newBlack);
+		switch (nt) {
+		case NAME_PLAYER:
+			ie.SetWhite(newID);
+			ie.SetBlack(newBlackID);
+			break;
+		case NAME_EVENT:
+			ie.SetEvent(newID);
+			break;
+		case NAME_SITE:
+			ie.SetSite(newID);
+			break;
+		default:
+			ASSERT(nt == NAME_ROUND);
+			ie.SetRound(newID);
+		}
 		return true;
 	});
 
diff -pruN 1:4.7.0+dfsg1-2/src/sc_info.cpp 1:4.7.4+dfsg1-2/src/sc_info.cpp
--- 1:4.7.0+dfsg1-2/src/sc_info.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/sc_info.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -17,7 +17,7 @@
  * along with Scid. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifdef WIN32
+#if defined(WIN32) || defined(_WIN32)
 #define NOMINMAX
 #include <windows.h>
 #undef NOMINMAX
diff -pruN 1:4.7.0+dfsg1-2/src/searchindex.cpp 1:4.7.4+dfsg1-2/src/searchindex.cpp
--- 1:4.7.0+dfsg1-2/src/searchindex.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/searchindex.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -21,6 +21,7 @@
 #include "scidbase.h"
 #include <algorithm>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace {
@@ -50,7 +51,7 @@ public:
 		}
 	}
 
-	void Init_exact (idNumberT n, nameT name_type, std::string pattern) {
+	void Init_exact (idNumberT n, nameT name_type, std::string const& pattern) {
 		const NameBase* nb = base_->getNameBase();
 		for (idNumberT i=0; i < n; i++) {
 			const char* name = nb->GetName (name_type, i);
@@ -144,6 +145,19 @@ public:
 	}
 };
 
+class SearchVariant {
+	const scidBaseT* base_;
+	bool std_;
+
+public:
+	SearchVariant(const scidBaseT* base, std::string_view variant)
+	    : base_(base), std_(variant == "std") {}
+
+	bool operator()(gamenumT gnum) const {
+		return base_->getIndexEntry(gnum)->isChessStd() == std_;
+	}
+};
+
 template <typename T>
 class SearchRange : public StrRange {
 protected:
@@ -224,41 +238,39 @@ public:
 
 class SearchRangeElo : public SearchRange<eloT> {
 protected:
-	eloT (IndexEntry::* fElo1_) (const NameBase*) const;
-	eloT (IndexEntry::* fElo2_) (const NameBase*) const;
-	const NameBase* nb_;
+	eloT (IndexEntry::*fElo1_)() const;
+	eloT (IndexEntry::*fElo2_)() const;
 
 public:
-	SearchRangeElo(const scidBaseT* base,
-	               const char* range,
-	               eloT (IndexEntry::* f1) (const NameBase*) const,
-	               eloT (IndexEntry::* f2) (const NameBase*) const = 0)
-	: SearchRange<eloT>(base, range, 0), fElo1_(f1), fElo2_(f2) {
-		nb_ = base_->getNameBase();
-	}
+	SearchRangeElo(const scidBaseT* base, const char* range,
+	               eloT (IndexEntry::*f1)() const,
+	               eloT (IndexEntry::*f2)() const = 0)
+	    : SearchRange<eloT>(base, range, 0), fElo1_(f1), fElo2_(f2) {}
 
-	bool operator() (gamenumT gnum) const {
-		long v1 = (base_->getIndexEntry(gnum)->*fElo1_)(nb_);
+	bool operator()(gamenumT gnum) const {
+		long v1 = (base_->getIndexEntry(gnum)->*fElo1_)();
 		long v2 = min_;
-		if (fElo2_ != 0) v2 = (base_->getIndexEntry(gnum)->*fElo2_)(nb_);
-		if (v1 < min_ || v1 > max_ || v2 < min_ || v2 > max_) return false;
+		if (fElo2_ != 0)
+			v2 = (base_->getIndexEntry(gnum)->*fElo2_)();
+		if (v1 < min_ || v1 > max_ || v2 < min_ || v2 > max_)
+			return false;
 		return true;
 	}
 };
 
 class SearchRangeEloDiff : public SearchRangeElo {
 public:
-	SearchRangeEloDiff(const scidBaseT* base,
-	                   const char* range,
-	                   eloT (IndexEntry::* f1) (const NameBase*) const,
-	                   eloT (IndexEntry::* f2) (const NameBase*) const)
-	: SearchRangeElo(base, range, f1, f2) {}
-
-	bool operator() (gamenumT gnum) const {
-		long v1 = (base_->getIndexEntry(gnum)->*fElo1_)(nb_);
-		long v2 = (base_->getIndexEntry(gnum)->*fElo2_)(nb_);
+	SearchRangeEloDiff(const scidBaseT* base, const char* range,
+	                   eloT (IndexEntry::*f1)() const,
+	                   eloT (IndexEntry::*f2)() const)
+	    : SearchRangeElo(base, range, f1, f2) {}
+
+	bool operator()(gamenumT gnum) const {
+		long v1 = (base_->getIndexEntry(gnum)->*fElo1_)();
+		long v2 = (base_->getIndexEntry(gnum)->*fElo2_)();
 		long v = v1 - v2;
-		if (v < min_ || v > max_) return false;
+		if (v < min_ || v > max_)
+			return false;
 		return true;
 	}
 };
@@ -451,6 +463,9 @@ I doSearch(I itB, I itR, I itE, const sc
 	if (param == "result") return std::stable_partition(itB, itE,
 		SearchResult(base, param.getValue())
 	);
+	if (param == "variant")
+		return std::stable_partition(itB, itE,
+		                             SearchVariant(base, param.getValue()));
 
 	param.invalidate();
 	return itR;
diff -pruN 1:4.7.0+dfsg1-2/src/searchpos.h 1:4.7.4+dfsg1-2/src/searchpos.h
--- 1:4.7.0+dfsg1-2/src/searchpos.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/searchpos.h	2022-07-09 05:14:42.000000000 +0000
@@ -24,7 +24,6 @@
 #define SEARCHPOS_H
 
 #include "common.h"
-#include "fastgame.h"
 #include "matsig.h"
 #include "position.h"
 #include "scidbase.h"
@@ -73,18 +72,18 @@ class SearchPos {
 	bool isStdStard_;
 
 public:
-	explicit SearchPos(const Position* pos) {
-		std::copy_n(pos->GetBoard(), 64, board_);
+	explicit SearchPos(Position const& pos) {
+		std::copy_n(pos.GetBoard(), 64, board_);
 
 		for (auto piece : board_) {
 			if (piece != EMPTY) {
-				nPieces_.incr(piece_Color(piece), piece_Type(piece));
+				nPieces_.incr(piece_Color_NotEmpty(piece), piece_Type(piece));
 			}
 		}
 
 		hpSig_ = hpSig_make(board_);
-		toMove_ = pos->GetToMove();
-		isStdStard_ = pos->IsStdStart();
+		toMove_ = pos.GetToMove();
+		isStdStard_ = pos.IsStdStart();
 
 		if ((board_[E1] == WK || board_[G1] == WK) &&
 		    (board_[E8] == BK || board_[G8] == BK)) {
@@ -120,9 +119,37 @@ public:
 		return -1;
 	}
 
+	/// Search the position in the main line of the specified game.
+	/// @returns a std::pair containg the ply where the position was reached and
+	///          the next move. Returns ply==0 if the position was not found.
+	/// TODO: filling the SAN info of the returned move may be unnecessary
+	std::pair<int, FullMove> match(scidBaseT const& base, gamenumT gnum) const {
+		const IndexEntry* ie = base.getIndexEntry(gnum);
+		int ply = index_match(*ie);
+		if (ply < -1)
+			return {};
+
+		if (ply >= 0) {
+			auto move = StoredLine::getMove(ie->GetStoredLineCode(), ply);
+			if (!move) // Matched at the end of the stored line
+				move = base.getGame(ie).getMove(ply);
+
+			return {ply + 1, move};
+		}
+
+		auto gameview = base.getGame(ie);
+		ply = (toMove_ == WHITE) ? gameview.search<WHITE>(board_, nPieces_)
+		                         : gameview.search<BLACK>(board_, nPieces_);
+		if (ply > 0)
+			return {ply, gameview.getMove(0)};
+
+		return {};
+	}
+
 	/// Reset @e filter to include only the games that reached the searched
 	/// position in their main line.
-	bool setFilter(scidBaseT* base, HFilter& filter, const Progress& progress) {
+	bool setFilter(scidBaseT const& base, HFilter& filter,
+	               const Progress& progress) const {
 		if (toMove_ == BLACK)
 			return SetFilter<BLACK>(base, filter, progress);
 
@@ -133,12 +160,12 @@ public:
 	}
 
 private:
-	bool setFilterStdStart(scidBaseT* base, HFilter& filter) {
+	bool setFilterStdStart(scidBaseT const& base, HFilter& filter) const {
 		filter->includeAll();
-		for (gamenumT i = 0, n = base->numGames(); i < n; i++) {
-			const IndexEntry* ie = base->getIndexEntry(i);
+		for (gamenumT i = 0, n = base.numGames(); i < n; i++) {
+			const IndexEntry* ie = base.getIndexEntry(i);
 			if (ie->GetStartFlag()) {
-				int ply = base->getGame(ie).search<WHITE>(board_, nPieces_);
+				int ply = base.getGame(ie).search<WHITE>(board_, nPieces_);
 				filter.set(i, (ply > 255) ? 255 : ply);
 			}
 		}
@@ -146,16 +173,17 @@ private:
 	}
 
 	template <colorT TOMOVE>
-	bool SetFilter(scidBaseT* base, HFilter& filter, const Progress& prg) {
+	bool SetFilter(scidBaseT const& base, HFilter& filter,
+	               const Progress& prg) const {
 		filter->clear();
 		long long progress = 0;
-		for (gamenumT i = 0, n = base->numGames(); i < n; i++) {
-			const IndexEntry* ie = base->getIndexEntry(i);
+		for (gamenumT i = 0, n = base.numGames(); i < n; i++) {
+			const IndexEntry* ie = base.getIndexEntry(i);
 			int ply = index_match(*ie);
 			if (ply >= 0) {
 				filter.set(i, static_cast<byte>(ply + 1));
 			} else if (ply == -1) {
-				ply = base->getGame(ie).search<TOMOVE>(board_, nPieces_);
+				ply = base.getGame(ie).search<TOMOVE>(board_, nPieces_);
 				if (ply != 0)
 					filter.set(i, (ply > 255) ? 255 : ply);
 
diff -pruN 1:4.7.0+dfsg1-2/src/sortcache.cpp 1:4.7.4+dfsg1-2/src/sortcache.cpp
--- 1:4.7.0+dfsg1-2/src/sortcache.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/sortcache.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -25,12 +25,11 @@
 #include "hfilter.h"
 #include "index.h"
 #include "misc.h"
+#include "namebase.h"
 #include <algorithm>
 #include <climits>
 #include <cstring>
 #include <numeric>
-
-#ifndef MULTITHREADING_OFF
 #include <thread>
 
 /**
@@ -96,11 +95,6 @@ void SortCache::th_sort() {
 	this->valid_fullMap_ = true;
 }
 
-#else
-void SortCache::th_join() {}
-void SortCache::th_sort() {}
-#endif
-
 
 SortCache::SortCache(const Index* idx, const NameBase* nbase)
     : nGames_(0), valid_fullMap_(false), th_interrupt_(false),
@@ -162,7 +156,7 @@ size_t SortCache::select(size_t row_offs
 	ASSERT(filter != NULL && filter->size() <= nGames_);
 	ASSERT(result != NULL);
 
-	size_t maxResults = filter->size();
+	const size_t maxResults = filter->size();
 	if (row_count == 0 || row_offset >= maxResults)
 		return 0;
 
@@ -291,11 +285,9 @@ void SortCache::generateHashCache() {
 void SortCache::sortAsynchronously() {
 	ASSERT(th_ == nullptr);
 
-#ifndef MULTITHREADING_OFF
 	delete[] fullMap_;
 	fullMap_ = new gamenumT[nGames_];
 	th_ = new std::thread(&SortCache::th_sort, this);
-#endif
 }
 
 /*
@@ -405,16 +397,16 @@ int SortCache::fullCompare(gamenumT left
 
 		case SORTING_avgElo:  // Average Elo rating:
 			{
-				int r1 = ie1->GetWhiteElo(nbase_) + ie1->GetBlackElo(nbase_);
-				int r2 = ie2->GetWhiteElo(nbase_) + ie2->GetBlackElo(nbase_);
+				int r1 = ie1->GetWhiteElo() + ie1->GetBlackElo();
+				int r2 = ie2->GetWhiteElo() + ie2->GetBlackElo();
 				res = r1 - r2;
 			}
 			break;
 
 		case SORTING_country:  // Last 3 characters of site field:
 			{
-				const char* sOne = ie1->GetSiteName(nbase_);
-				const char* sTwo = ie2->GetSiteName(nbase_);
+				const char* sOne = nbase_->GetName(NAME_SITE, ie1->GetSite());
+				const char* sTwo = nbase_->GetName(NAME_SITE, ie2->GetSite());
 				size_t slenOne = std::strlen(sOne);
 				size_t slenTwo = std::strlen(sTwo);
 				if (slenOne > 3) { sOne += slenOne - 3; }
@@ -432,11 +424,11 @@ int SortCache::fullCompare(gamenumT left
 			break;
 
 		case SORTING_whiteelo:
-			res = (int)ie1->GetWhiteElo(nbase_) - (int)ie2->GetWhiteElo(nbase_);
+			res = (int)ie1->GetWhiteElo() - (int)ie2->GetWhiteElo();
 			break;
 
 		case SORTING_blackelo:
-			res = (int)ie1->GetBlackElo(nbase_) - (int)ie2->GetBlackElo(nbase_);
+			res = (int)ie1->GetBlackElo() - (int)ie2->GetBlackElo();
 			break;
 
 		case SORTING_commentcount:
@@ -452,7 +444,7 @@ int SortCache::fullCompare(gamenumT left
 			break;
 
 		case SORTING_rating:
-			res = (int)ie1->GetRating(nbase_) - (int)ie2->GetRating(nbase_);
+			res = (int)ie1->GetRating() - (int)ie2->GetRating();
 			break;
 
 		case SORTING_number:
@@ -487,33 +479,33 @@ uint32_t SortCache::calcHash(gamenumT ga
 		size_t bitsUsed;
 		switch (*field) {
 			case SORTING_white:
-				value = strStartHash(ie->GetWhiteName(nbase_));
+				value = strStartHash(nbase_->GetName(NAME_PLAYER, ie->GetWhite()));
 				bitsUsed = nHashBits;
 				partialHash_ = true;
 				break;
 			case SORTING_black:
-				value = strStartHash(ie->GetBlackName(nbase_));
+				value = strStartHash(nbase_->GetName(NAME_PLAYER, ie->GetBlack()));
 				bitsUsed = nHashBits;
 				partialHash_ = true;
 				break;
 			case SORTING_site:
-				value = strStartHash(ie->GetSiteName(nbase_));
+				value = strStartHash(nbase_->GetName(NAME_SITE, ie->GetSite()));
 				bitsUsed = nHashBits;
 				partialHash_ = true;
 				break;
 			case SORTING_event:
-				value = strStartHash(ie->GetEventName(nbase_));
+				value = strStartHash(nbase_->GetName(NAME_EVENT, ie->GetEvent()));
 				bitsUsed = nHashBits;
 				partialHash_ = true;
 				break;
 			case SORTING_round:
-				value = strGetUnsigned(ie->GetRoundName(nbase_));
+				value = strGetUnsigned(nbase_->GetName(NAME_ROUND, ie->GetRound()));
 				bitsUsed = nHashBits;
 				partialHash_ = true;
 				break;
 			case SORTING_country:
 			{
-				const char *scountry = ie->GetSiteName (nbase_);
+				const char *scountry = nbase_->GetName(NAME_SITE, ie->GetSite());
 				size_t slen = std::strlen(scountry);
 				if (slen > 3) 
 					scountry += slen - 3;
@@ -535,15 +527,15 @@ uint32_t SortCache::calcHash(gamenumT ga
 				bitsUsed = 16;
 				break;
 			case SORTING_whiteelo:
-				value = ie->GetWhiteElo(nbase_);
+				value = ie->GetWhiteElo();
 				bitsUsed = 16;
 				break;
 			case SORTING_blackelo:
-				value = ie->GetBlackElo(nbase_);
+				value = ie->GetBlackElo();
 				bitsUsed = 16;
 				break;
 			case SORTING_avgElo:
-				value = ie->GetWhiteElo(nbase_) + ie->GetBlackElo(nbase_);
+				value = ie->GetWhiteElo() + ie->GetBlackElo();
 				bitsUsed = 16;
 				break;
 			case SORTING_result:
@@ -587,7 +579,7 @@ uint32_t SortCache::calcHash(gamenumT ga
 				bitsUsed = 8;
 				break;
 			case SORTING_rating:
-				value = ie->GetRating(nbase_);
+				value = ie->GetRating();
 				bitsUsed = 8;
 				break;
 			case SORTING_number:
diff -pruN 1:4.7.0+dfsg1-2/src/sortcache.h 1:4.7.4+dfsg1-2/src/sortcache.h
--- 1:4.7.0+dfsg1-2/src/sortcache.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/sortcache.h	2022-07-09 05:14:42.000000000 +0000
@@ -26,12 +26,8 @@
 
 #include "common.h"
 
-#ifndef MULTITHREADING_OFF
 #include <atomic>
 using std::atomic_bool;
-#else
-typedef bool atomic_bool;
-#endif
 
 class HFilter;
 class Index;
diff -pruN 1:4.7.0+dfsg1-2/src/spellchk.cpp 1:4.7.4+dfsg1-2/src/spellchk.cpp
--- 1:4.7.0+dfsg1-2/src/spellchk.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/spellchk.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -292,19 +292,20 @@ errorT SpellChecker::read(const char* fi
 	}
 
 	// Free unused memory
-	char* shrink = (char*) realloc(staticStrings_, 1 + std::distance(staticStrings_,line));
-	if (shrink != NULL && shrink != staticStrings_) {
-		// Unlikely, but realloc() moved the memory: update the pointers.
-		const char* oldAddr = staticStrings_;
+	const auto new_size = std::distance(staticStrings_, line);
+	char* shrink = (char*)realloc(staticStrings_, new_size ? new_size : 1);
+	if (shrink) {
+		const auto offset = shrink - staticStrings_;
 		staticStrings_ = shrink;
-		for (nameT i=0; i < NUM_NAME_TYPES; i++) {
-			for (auto& e : (names_[i]))
-				e = staticStrings_ + std::distance(oldAddr, e);
-		}
-		for (auto& e : pInfo_) {
-			e.comment_ = staticStrings_ + std::distance(oldAddr, e.comment_);
-			for (auto& bio : e.bio_) {
-				bio = staticStrings_ + std::distance(oldAddr, bio);
+		if (offset) { // realloc() moved the memory: update the pointers.
+			for (nameT i = 0; i < NUM_NAME_TYPES; i++) {
+				for (auto& e : names_[i])
+					e += offset;
+			}
+			for (auto& e : pInfo_) {
+				e.comment_ += offset;
+				for (auto& bio : e.bio_)
+					bio += offset;
 			}
 		}
 	}
@@ -388,9 +389,9 @@ const char *
 PlayerInfo::getTitle() const
 {
     static const char * titles[] = {
-        "gm", "im", "fm",
-        "wgm", "wim", "wfm", "w",
-        "cgm", "cim", "hgm",
+        "GM", "IM", "FM",
+        "WGM", "WIM", "WFM", "W",
+        "CGM", "CIM", "HGM",
         NULL
     };
     const char ** titlePtr = titles;
@@ -399,7 +400,7 @@ PlayerInfo::getTitle() const
     if (*comment == 0) { return ""; }
 
     while (*titlePtr != NULL) {
-        if (strIsPrefix (*titlePtr, comment)) { return *titlePtr; }
+        if (strIsCasePrefix (*titlePtr, comment)) { return *titlePtr; }
         titlePtr++;
     }
     return "";
diff -pruN 1:4.7.0+dfsg1-2/src/sqmove.h 1:4.7.4+dfsg1-2/src/sqmove.h
--- 1:4.7.0+dfsg1-2/src/sqmove.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/sqmove.h	2022-07-09 05:14:42.000000000 +0000
@@ -58,60 +58,17 @@ public:
 };
 
 class SquareSet {
-	uint Bits_a1h4;
-	uint Bits_a5h8;
+	unsigned long long bits_ = 0;
 
 public:
-	SquareSet() { Bits_a1h4 = Bits_a5h8 = 0; }
-	SquareSet(squareT* squares) {
-		Bits_a1h4 = Bits_a5h8 = 0;
-		AddAll(squares);
-	}
-
-	void Clear(void) { Bits_a1h4 = Bits_a5h8 = 0; }
-	void AddAll(void) { Bits_a1h4 = Bits_a5h8 = 0xFFFFFFFFu; }
-
 	void Add(squareT sq) {
-		ASSERT(sq <= H8);
-		if (sq <= H4) {
-			Bits_a1h4 |= (1 << sq);
-		} else {
-			Bits_a5h8 |= (1 << (sq & 31));
-		}
-	}
-
-	void AddAll(squareT* squares) {
-		while (true) {
-			squareT sq = *squares;
-			if (sq == NULL_SQUARE) {
-				break;
-			}
-			ASSERT(sq <= H8);
-			squares++;
-			if (sq <= H4) {
-				Bits_a1h4 |= (1 << sq);
-			} else {
-				Bits_a5h8 |= (1 << (sq & 31));
-			}
-		}
+		ASSERT(sq < 64);
+		bits_ |= 1ull << sq;
 	}
 
 	bool Contains(squareT sq) {
-		ASSERT(sq <= H8);
-		if (sq <= H4) {
-			return (Bits_a1h4 & (1 << sq)) != 0;
-		} else {
-			return (Bits_a5h8 & (1 << (sq & 31))) != 0;
-		}
-	}
-
-	void Remove(squareT sq) {
-		ASSERT(sq <= H8);
-		if (sq <= H4) {
-			Bits_a1h4 &= ~(1 << sq);
-		} else {
-			Bits_a5h8 &= ~(1 << (sq & 31));
-		}
+		ASSERT(sq < 64);
+		return (bits_ & (1ull << sq)) != 0;
 	}
 };
 
@@ -119,8 +76,8 @@ public:
 // sqMove
 //   Array indexed by square value and direction, giving the square
 //   obtained by moving from the square in that direction.
-constexpr squareT
-sqMove[66][11] = {
+inline constexpr squareT
+precomputed_sqMove[66][11] = {
                 /* UP DOWN    LEFT UL  DL    RIGHT UR  DR */
    { /* A1 */  NS, A2, NS, NS, NS, NS, NS, NS, B1, B2, NS   },
    { /* B1 */  NS, B2, NS, NS, A1, A2, NS, NS, C1, C2, NS   },
@@ -196,8 +153,8 @@ sqMove[66][11] = {
 //   square reached by moving from the square in that direction.
 //   The last square is the same as the original square if moving
 //   in the specified direction would move off the board.
-constexpr squareT
-sqLast[66][11] = {
+inline constexpr squareT
+precomputed_sqLast[66][11] = {
                 /* UP DOWN    LEFT UL  DL    RIGHT UR  DR */
    { /* A1 */  NS, A8, A1, NS, A1, A1, A1, NS, H1, H8, A1   },
    { /* B1 */  NS, B8, B1, NS, A1, A2, B1, NS, H1, H7, B1   },
@@ -272,7 +229,7 @@ sqLast[66][11] = {
 constexpr squareT
 square_Move(squareT sq, directionT dir)
 {
-    return sqMove[sq][dir];
+    return precomputed_sqMove[sq][dir];
 }
 
 // square_Last():
@@ -285,7 +242,7 @@ square_Move(squareT sq, directionT dir)
 constexpr squareT
 square_Last (squareT sq, directionT dir)
 {
-    return sqLast[sq][dir];
+    return precomputed_sqLast[sq][dir];
 }
 
 #endif
diff -pruN 1:4.7.0+dfsg1-2/src/stored.cpp 1:4.7.4+dfsg1-2/src/stored.cpp
--- 1:4.7.0+dfsg1-2/src/stored.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/stored.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -17,7 +17,8 @@
 */
 
 #include "stored.h"
-#include <cstring>
+#include <algorithm>
+#include <cassert>
 
 // Stored line codes: used to speed up tree searches.
 
@@ -40,24 +41,30 @@ class Board {
 	pieceT b_[64];
 
 public:
-	Board(const pieceT* b) {
-		for (int i=0; i < 64; i++) b_[i] = b[i];
+	explicit Board(const pieceT* b) {
+		std::copy_n(b, 64, b_);
 	}
 	void doMove(FullMove m) {
 		//No promo, no null moves, no queenside castle
 		if (! m.isCastle()) {
+			assert(m && !m.isNull() && !m.isPromo() && !m.isEnpassant());
+			b_[m.getTo()] = b_[m.getFrom()];
 			b_[m.getFrom()] = EMPTY;
-			b_[m.getTo()] = piece_Make(m.getColor(), m.getPiece());
 		} else {
+			const auto king = b_[m.getFrom()];
+			const auto rook = b_[m.getTo()];
 			b_[m.getFrom()] = EMPTY;
 			b_[m.getTo()] = EMPTY;
+			assert(king == piece_Make(m.getColor(), KING));
+			assert(rook == piece_Make(m.getColor(), ROOK));
+			assert(m.getFrom() < m.getTo());
 			int black = (m.getColor() == BLACK) ? 56 : 0;
-			b_[black + G1] = piece_Make(m.getColor(), KING);
-			b_[black + F1] = piece_Make(m.getColor(), ROOK);
+			b_[black + G1] = king;
+			b_[black + F1] = rook;
 		}
 	}
 	bool operator==(const Board& b) const {
-		return (std::memcmp(b_, b.b_, sizeof b_) == 0);
+		return std::equal(b_, b_ + 64, b.b_);
 	}
 	bool neverMatch(const Board& m) const {
 		// Pawns allows to exclude some games:
@@ -636,7 +643,7 @@ StoredLine::StoredLine(const pieceT* boa
 	Board search(board);
 	matches_[0] = -1;
 	matches_[STORED_LINES] = -1;
-	for (uint line = 1; line < STORED_LINES; line++) {
+	for (int line = 1; line < STORED_LINES; line++) {
 		Board b(START_BOARD);
 		const FullMove* end = Moves_[line +1];
 		for (int ply=0; ply < 99; ply++) {
diff -pruN 1:4.7.0+dfsg1-2/src/stored.h 1:4.7.4+dfsg1-2/src/stored.h
--- 1:4.7.0+dfsg1-2/src/stored.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/stored.h	2022-07-09 05:14:42.000000000 +0000
@@ -22,7 +22,7 @@
 #include "fullmove.h"
 
 class StoredLine {
-	static const unsigned STORED_LINES = 255;
+	static constexpr int STORED_LINES = 255;
 	static const FullMove* Moves_[STORED_LINES + 1];
 
 	int8_t matches_[STORED_LINES + 1];
@@ -36,9 +36,22 @@ public:
 	//>=0: the game reach the searched position at the returned ply
 	int match(byte code) const { return matches_[code]; }
 
-	static uint count () { return STORED_LINES; }
-	static FullMove getMove (uint code, uint ply = 0) {
-		if ((code < STORED_LINES) && (Moves_[code] + ply) < Moves_[code +1]) {
+	template <typename CompareOp> static byte classify(CompareOp comp) {
+		int res = 0;
+		std::ptrdiff_t longest = 0;
+		for (int i = 1; i < STORED_LINES; ++i) {
+			const auto begin = Moves_[i];
+			const auto end = Moves_[i + 1];
+			if (std::distance(begin, end) > longest && comp(begin, end)) {
+				res = i;
+				longest = std::distance(begin, end);
+			}
+		}
+		return static_cast<byte>(res);
+	}
+
+	static FullMove getMove(byte code, uint ply = 0) {
+		if ((code < STORED_LINES) && (Moves_[code] + ply) < Moves_[code + 1]) {
 			return Moves_[code][ply];
 		}
 		return FullMove();
diff -pruN 1:4.7.0+dfsg1-2/src/timer.h 1:4.7.4+dfsg1-2/src/timer.h
--- 1:4.7.0+dfsg1-2/src/timer.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/timer.h	2022-07-09 05:14:42.000000000 +0000
@@ -21,27 +21,24 @@
 
 #include <chrono>
 class Timer {
-   decltype(std::chrono::system_clock::now()) start_;
+	decltype(std::chrono::high_resolution_clock::now()) start_;
+
 public:
-   Timer() { Reset(); }
-   void Reset() { start_ = std::chrono::system_clock::now(); }
-   long long MilliSecs (void) const {
-		auto t = std::chrono::system_clock::now();
-		return std::chrono::duration_cast<std::chrono::milliseconds>(t - start_).count();
-   }
-   long long CentiSecs () const { return MilliSecs() / 10; }
-};
+	Timer() { Reset(); }
 
-template <class S>
-auto operator<<(S& os, const Timer& timer) -> decltype(os) {
-   return os << timer.MilliSecs() << " milliseconds\n";
-}
-
-/* Usage:
-Timer t;
-//do some stuff
-std::cout << "Elapsed: " << t;
-*/
+	void Reset() { start_ = std::chrono::high_resolution_clock::now(); }
 
+	int MilliSecs() const {
+		using namespace std::chrono;
+		const auto t = high_resolution_clock::now() - start_;
+		return duration_cast<duration<int, std::milli>>(t).count();
+	}
+
+	int CentiSecs() const {
+		using namespace std::chrono;
+		const auto t = high_resolution_clock::now() - start_;
+		return duration_cast<duration<int, std::centi>>(t).count();
+	}
+};
 
 #endif //SCID_TIMER_H
diff -pruN 1:4.7.0+dfsg1-2/src/tkscid.cpp 1:4.7.4+dfsg1-2/src/tkscid.cpp
--- 1:4.7.0+dfsg1-2/src/tkscid.cpp	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/tkscid.cpp	2022-07-09 05:14:42.000000000 +0000
@@ -520,13 +520,9 @@ sc_base_export (ClientData, Tcl_Interp *
                 // Print the game, skipping any corrupt games:
                 const IndexEntry* ie = db->getIndexEntry(i);
                 if (ie->GetLength() == 0) { continue; }
-                if (db->getGame(ie, db->bbuf) != OK) {
+                if (db->getGame(*ie, *g) != OK) {
                     continue;
                 }
-                if (g->Decode (db->bbuf, GAME_DECODE_ALL) != OK) {
-                    continue;
-                }
-                g->LoadStandardTags (ie, db->getNameBase());
                 exportGame (g, exportFile, outputFormat, pgnStyle);
             }
         }
@@ -603,15 +599,11 @@ sc_base_piecetrack (ClientData, Tcl_Inte
     // Examine every filter game and track the selected pieces:
 
     Progress progress = UI_CreateProgress(ti);
-    uint filterCount = db->dbFilter->Count();
-    uint filterSeen = 0;
-
-    for (uint gnum = 0, n = db->numGames(); gnum < n; gnum++) {
-        // Skip over non-filter games:
-        if (!db->dbFilter->Get(gnum)) { continue; }
-
-        // Update progress bar:
-        if ((filterSeen++ % 1000) == 0) {
+    const auto filter = db->getFilter("dbfilter");
+    const size_t filterCount = filter->size();
+    size_t filterSeen = 0;
+    for (const auto gnum : filter) {
+        if (++filterSeen % 1024 == 0) {
             if (!progress.report(filterSeen, filterCount)) {
                 return UI_Result(ti, ERROR_UserCancel);
             }
@@ -632,42 +624,45 @@ sc_base_piecetrack (ClientData, Tcl_Inte
         int ntrack = nTrackSquares;
         for (uint sq=0; sq < 64; sq++) { track[sq] = trackSquare[sq]; }
 
-        Game * g = scratchGame;
-        if (db->getGame(ie, db->bbuf) != OK) {
-            continue;
-        }
-        db->bbuf->BackToStart();
-        g->Clear();
-        if (g->DecodeStart (db->bbuf) != OK) { continue; }
-
-        uint plyCount = 0;
-        simpleMoveT sm;
-
         // Process each game move until the maximum ply or end of
         // the game is reached:
+        uint plyCount = 0;
+        db->getGame(ie).mainLine([&](auto move) {
+            if (plyCount++ >= maxPly)
+                return false;
 
-        while (plyCount < maxPly) {
-            if (g->DecodeNextMove (db->bbuf, &sm) != OK) { break; }
-            plyCount++;
-            squareT toSquare = sm.to;
-            squareT fromSquare = sm.from;
+            squareT toSquare = move.getTo();
+            squareT fromSquare = move.getFrom();
 
             // Special hack for castling:
-            if (piece_Type(sm.movingPiece) == KING) {
-                if (fromSquare == E1) {
-                    if (toSquare == G1  &&  track[H1]) {
-                        fromSquare = H1; toSquare = F1;
+            if (move.isCastle()) {
+                if (toSquare == H1) {
+                    if (track[H1]) {
+                        fromSquare = H1;
+                        toSquare = F1;
+                    } else {
+                        toSquare = G1;
                     }
-                    if (toSquare == C1  &&  track[A1]) {
-                        fromSquare = A1; toSquare = D1;
+                } else if (toSquare == A1) {
+                    if (track[A1]) {
+                        fromSquare = A1;
+                        toSquare = D1;
+                    } else {
+                        toSquare = C1;
                     }
-                }
-                if (fromSquare == E8) {
-                    if (toSquare == G8  &&  track[H8]) {
-                        fromSquare = H8; toSquare = F8;
+                } else if (toSquare == H8) {
+                    if (track[H8]) {
+                        fromSquare = H8;
+                        toSquare = F8;
+                    } else {
+                        toSquare = G8;
                     }
-                    if (toSquare == C8  &&  track[A8]) {
-                        fromSquare = A8; toSquare = D8;
+                } else if (toSquare == A8) {
+                    if (track[A8]) {
+                        fromSquare = A8;
+                        toSquare = D8;
+                    } else {
+                        toSquare = C8;
                     }
                 }
             }
@@ -678,7 +673,8 @@ sc_base_piecetrack (ClientData, Tcl_Inte
                 // A tracked piece has been captured:
                 track[toSquare] = false;
                 ntrack--;
-                if (ntrack <= 0) { break; }
+                if (ntrack <= 0)
+                    return false;
 
             } else if (track[fromSquare]) {
                 // A tracked piece is moving:
@@ -707,11 +703,14 @@ sc_base_piecetrack (ClientData, Tcl_Inte
                         nleft--;
                         // We can stop early when all tracked
                         // squares have been found:
-                        if (nleft <= 0) { break; }
+                        if (nleft <= 0)
+                            return false;
                     }
                 }
             }
-        } // while (plyCount < maxPly)
+
+            return true;
+        }); // while (plyCount < maxPly)
     } // foreach game
 
     progress.report(1, 1);
@@ -822,9 +821,8 @@ checkDuplicate (scidBaseT * base,
     return true;
 }
 
-uint
-sc_base_duplicates (scidBaseT* dbase, UI_handle_t ti, int argc, const char ** argv)
-{
+UI_res_t sc_base_duplicates(scidBaseT* dbase, UI_handle_t ti, int argc,
+                            const char** argv) {
     dupCriteriaT criteria;
     criteria.exactNames  = false;
     criteria.sameColors  = true;
@@ -843,7 +841,6 @@ sc_base_duplicates (scidBaseT* dbase, UI
     bool keepAllGamesWithVars  = true;
     bool setFilterToDups = false;
     bool onlyFilterGames = false;
-    bool copyRatings = false;
 
     // Deletion strategy: delete the shorter game, the game with the
     // smaller game number, or the game with the larger game number.
@@ -856,14 +853,14 @@ sc_base_duplicates (scidBaseT* dbase, UI
         "-players", "-colors", "-event", "-site", "-round", "-year",
         "-month", "-day", "-result", "-eco", "-moves", "-skipshort",
         "-comments", "-variations", "-setfilter", "-usefilter",
-        "-copyratings", "-delete",
+        "-delete",
         NULL
     };
     enum {
         OPT_PLAYERS, OPT_COLORS, OPT_EVENT, OPT_SITE, OPT_ROUND, OPT_YEAR,
         OPT_MONTH, OPT_DAY, OPT_RESULT, OPT_ECO, OPT_MOVES, OPT_SKIPSHORT,
         OPT_COMMENTS, OPT_VARIATIONS, OPT_SETFILTER, OPT_USEFILTER,
-        OPT_COPYRATINGS, OPT_DELETE
+        OPT_DELETE
     };
 
     for (int arg = 3; arg < argc; arg += 2) {
@@ -888,8 +885,9 @@ sc_base_duplicates (scidBaseT* dbase, UI
             case OPT_VARIATIONS:  keepAllGamesWithVars = b;  break;
             case OPT_SETFILTER:   setFilterToDups = b;       break;
             case OPT_USEFILTER:   onlyFilterGames = b;       break;
-            case OPT_COPYRATINGS: copyRatings = b;           break;
             case OPT_DELETE:
+                if (dbase->isReadOnly())
+                    return UI_Result(ti, ERROR_FileReadOnly);
                 if (strIsCasePrefix (valueStr, "shorter")) {
                     deleteStrategy = DELETE_SHORTER;
                 } else if (strIsCasePrefix (valueStr, "older")) {
@@ -897,19 +895,22 @@ sc_base_duplicates (scidBaseT* dbase, UI
                 } else if (strIsCasePrefix (valueStr, "newer")) {
                     deleteStrategy = DELETE_NEWER;
                 } else {
-                    return errorResult (ti, "Invalid option.");
+                    return UI_Result(ti, ERROR_BadArg, "Invalid option.");
                 }
                 break;
             default:
-                return InvalidCommand (ti, "sc_base duplicates", options);
+                return UI_Result(ti, ERROR_BadArg, "Invalid option.");
         }
     }
-    uint deletedCount = 0;
     const gamenumT numGames = dbase->numGames();
 
+    Filter tmp_filter(numGames);
+    HFilter filter = setFilterToDups ? dbase->getFilter("dbfilter")
+                                     : HFilter(&tmp_filter);
+    filter.clear();
+
     // Setup duplicates array:
-    uint* duplicates = new uint [numGames];
-    std::fill(duplicates, duplicates + numGames, 0);
+    auto duplicates = std::make_unique<gamenumT[]>(numGames);
 
     // We use a hashtable to limit duplicate game comparisons; each game
     // is only compared to others that hash to the same value.
@@ -941,248 +942,68 @@ sc_base_duplicates (scidBaseT* dbase, UI
     hash.resize(n_hash);
     std::sort(hash.begin(), hash.end());
 
-    if (setFilterToDups) { dbase->dbFilter->Fill (0); }
     Progress progress = UI_CreateProgress(ti);
-
-    dbase->beginTransaction();
-
     // Now check same-hash games for duplicates:
     for (size_t i=0; i < n_hash; i++) {
         if ((i % 1024) == 0) {
             if (!progress.report(i, numGames)) break;
         }
-        gNumListT* head = &(hash[i]);
-        IndexEntry* ieHead = dbase->idx->FetchEntry (head->gNumber);
+        const gNumListT& head = hash[i];
+        const IndexEntry* ieHead = dbase->getIndexEntry(head.gNumber);
 
         for (size_t comp=i+1; comp < n_hash; comp++) {
-            gNumListT* compare = &(hash[comp]);
-            if (compare->hash != head->hash) break;
-
-            IndexEntry * ieComp = dbase->idx->FetchEntry (compare->gNumber);
-
-            if (checkDuplicate (dbase, ieHead, ieComp, &criteria)) {
-                    duplicates[head->gNumber] = compare->gNumber + 1;
-                    duplicates[compare->gNumber] = head->gNumber + 1;
-
-                    // Found a duplicate! Decide which one to delete:
-
-                    bool headImmune = false;
-                    bool compImmune = false;
-                    bool doDeletion = false;
-                    bool copiedRatings = false;
-                    gamenumT gnumKeep, gnumDelete;
-                    IndexEntry * ieDelete, * ieKeep;
-
-                    if (keepAllCommentedGames) {
-                        if (ieHead->GetCommentsFlag()) { headImmune = true; }
-                        if (ieComp->GetCommentsFlag()) { compImmune = true; }
-                    }
-                    if (keepAllGamesWithVars) {
-                        if (ieHead->GetVariationsFlag()) { headImmune = true; }
-                        if (ieComp->GetVariationsFlag()) { compImmune = true; }
-                    }
-
-                    // Decide which game should get deleted:
-                    bool deleteHead = false;
-                    if (deleteStrategy == DELETE_OLDER) {
-                        deleteHead = (head->gNumber < compare->gNumber);
-                    } else if (deleteStrategy == DELETE_NEWER) {
-                        deleteHead = (head->gNumber > compare->gNumber);
-                    } else {
-                        ASSERT (deleteStrategy == DELETE_SHORTER);
-                        uint a = ieHead->GetNumHalfMoves();
-                        uint b = ieComp->GetNumHalfMoves();
-                        deleteHead = (a <= b);
-                        if (a == b && headImmune) deleteHead = false;
-                    }
-
-                    if (deleteHead) {
-                        ieDelete = ieHead;
-                        ieKeep = ieComp;
-                        gnumDelete = head->gNumber;
-                        gnumKeep = compare->gNumber;
-                        doDeletion = ! headImmune;
-                    } else {
-                        ieDelete = ieComp;
-                        ieKeep = ieHead;
-                        gnumDelete = compare->gNumber;
-                        gnumKeep = head->gNumber;
-                        doDeletion = ! compImmune;
-                    }
-                    // Delete whichever game is to be deleted:
-                    if (doDeletion) {
-                        deletedCount++;
-                        ieDelete->SetDeleteFlag (true);
-                        if (copyRatings  &&  ieKeep->GetWhiteElo() == 0) {
-                            eloT elo = ieDelete->GetWhiteElo();
-                            byte rtype = ieDelete->GetWhiteRatingType();
-                            if (elo != 0) {
-                                ieKeep->SetWhiteElo (elo);
-                                ieKeep->SetWhiteRatingType (rtype);
-                                copiedRatings = true;
-                            }
-                        }
-                        if (copyRatings  &&  ieKeep->GetBlackElo() == 0) {
-                            eloT elo = ieDelete->GetBlackElo();
-                            byte rtype = ieDelete->GetBlackRatingType();
-                            if (elo != 0) {
-                                ieKeep->SetBlackElo (elo);
-                                ieKeep->SetBlackRatingType (rtype);
-                                copiedRatings = true;
-                            }
-                        }
-                        dbase->idx->WriteEntry (ieDelete, gnumDelete);
-                        if (copiedRatings) {
-                            dbase->idx->WriteEntry (ieKeep, gnumKeep);
-                        }
-                        if (setFilterToDups) {
-                            dbase->dbFilter->Set (gnumDelete, 1);
-                        }
-                    }
-            }
-        }
-    }
-
-    dbase->endTransaction();
-    dbase->setDuplicates(duplicates);
-    progress.report(1,1);
-
-    return deletedCount;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sc_base_tag:
-//   Produce a list of PGN tags used in the database,
-//   or strip an unwanted non-essential tag from the
-//   database. It cannot be used for in-index tags
-//   such as ratings, ECO or EventDate, or the FEN
-//   or Setup tags.
-//   The command has three subcommands:
-//      find <tag>: set the filter to contain all games
-//                  that have the specified tag.
-//      list: return a even-sized list, where each pair
-//            of elements is a tag name and its frequency,
-//            for all non-standard tags stored as Extra
-//            tags in the game file of the database.
-//      strip <tag>: Remove all occurrences of the
-//                   specified tag from the database.
-int
-sc_base_tag (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
-{
-    const char * usage = "Usage: sc_base tag [filter <tagname> | list | strip <tagname>]";
-    const char * options[] = {
-        "find", "list", "strip", NULL
-    };
-    enum {
-        TAG_FIND, TAG_LIST, TAG_STRIP
-    };
-
-    const char * tag = NULL;  // For "find" or "strip" commands
-    std::vector< std::pair <std::string, uint> > tag_freq;  // For "list" command
-
-    if (! db->inUse) {
-        return errorResult (ti, errMsgNotOpen(ti));
-    }
-
-    int cmd = -1;
-    if (argc >= 3) { cmd = strUniqueMatch (argv[2], options); }
-
-    switch (cmd) {
-    case TAG_LIST:
-        if (argc != 3) { return errorResult (ti, usage); }
-        break;
-    case TAG_FIND:  // Same extra parameter as TAG_STRIP
-    case TAG_STRIP:
-        if (argc != 4) { return errorResult (ti,usage); }
-        tag = argv[3];
-        break;
-    default:
-        return errorResult (ti, usage);
-    };
+            const gNumListT& compare = hash[comp];
+            if (compare.hash != head.hash) break;
 
-    if (cmd == TAG_STRIP) {
-        // If stripping a tag, make sure we have a writable database:
-        if (db->isReadOnly())
-            return errorResult(ti, ERROR_FileReadOnly);
-        db->beginTransaction();
-    }
+            const IndexEntry* ieComp = dbase->getIndexEntry(compare.gNumber);
 
-    // If setting filter, clear it now:
-    if (cmd == TAG_FIND) { db->dbFilter->Fill (0); }
-
-    // Process each game in the database:
-    Progress progress = UI_CreateProgress(ti);
-    Game * g = scratchGame;
-    uint nEditedGames = 0;
-
-    for (gamenumT gnum = 0, n = db->numGames(); gnum < n; gnum++) {
-        if ((gnum % 1000) == 0) {
-            if (!progress.report(gnum, n)) break;
-        }
+            if (checkDuplicate(dbase, ieHead, ieComp, &criteria)) {
+                duplicates[head.gNumber] = compare.gNumber + 1;
+                duplicates[compare.gNumber] = head.gNumber + 1;
+
+                auto isImmune = [&](const IndexEntry* ie) {
+                    if (keepAllCommentedGames && ie->GetCommentsFlag())
+                        return true;
+                    return keepAllGamesWithVars && ie->GetVariationsFlag();
+                };
+
+                // Decide which game should get deleted:
+                bool deleteHead = false;
+                if (deleteStrategy == DELETE_OLDER) {
+                    deleteHead = (head.gNumber < compare.gNumber);
+                } else if (deleteStrategy == DELETE_NEWER) {
+                    deleteHead = (head.gNumber > compare.gNumber);
+                } else {
+                    ASSERT(deleteStrategy == DELETE_SHORTER);
+                    uint a = ieHead->GetNumHalfMoves();
+                    uint b = ieComp->GetNumHalfMoves();
+                    deleteHead = (a <= b);
+                    if (a == b && isImmune(ieHead))
+                        deleteHead = false;
+                }
 
-        const IndexEntry* ie = db->getIndexEntry(gnum);
-        if (ie->GetLength() == 0) { continue; }
-        if (db->getGame(ie, db->bbuf) != OK) {
-            continue;
-        }
-        if (g->Decode (db->bbuf, GAME_DECODE_ALL) != OK) {
-            continue;
-        }
-        if (cmd == TAG_FIND) {
-            if (g->FindExtraTag (tag) != NULL) {
-                // Found the tag, so add this game to the filter:
-                db->dbFilter->Set (gnum, 1);
-            }
-        } else if (cmd == TAG_STRIP) {
-            if (g->RemoveExtraTag (tag)) {
-                // The tag was found and stripped. Re-save the game,
-                // remembering to load its standard tags first:
-                g->LoadStandardTags (ie, db->getNameBase());
-                errorT res = db->saveGameHelper(g, gnum);
-                if (res != OK) return UI_Result(ti, res);
-                nEditedGames++;
-            }
-        } else {
-            ASSERT (cmd == TAG_LIST);
-            // Increment frequency for each extra tag:
-            for (auto& tag : g->GetExtraTags()) {
-                bool found = false;
-                for (uint i=0; i < tag_freq.size(); i++) {
-                    if (tag_freq[i].first == tag.first) {
-                        tag_freq[i].second++;
-                        found = true;
-                        break;
-                    }
+                gamenumT gnumDelete = compare.gNumber;
+                const IndexEntry* ieDelete = ieComp;
+                if (deleteHead) {
+                    gnumDelete = head.gNumber;
+                    ieDelete = ieHead;
                 }
-                if (!found) {
-                    tag_freq.emplace_back(tag.first, 1);
+                // Delete whichever game is to be deleted:
+                if (!isImmune(ieDelete)) {
+                    filter->set(gnumDelete, 1);
                 }
             }
         }
     }
-    // Done searching through all games.
-
-    // If necessary, update index and name files:
-    if (cmd == TAG_STRIP) {
-        db->endTransaction();
-        setUintResult (ti, nEditedGames);
-    }
-
-    // If listing extra tags, generate the list now:
-    if (cmd == TAG_LIST) {
-        for (uint i=0; i < tag_freq.size(); i++) {
-            uint freq = tag_freq[i].second;
-            const char* name = tag_freq[i].first.c_str();
-            if (freq > 0  &&  !strEqual (name, "SetUp")) {
-                Tcl_AppendElement (ti, name);
-                appendUintElement (ti, freq);
-            }
-        }
-    }
-    return TCL_OK;
+    auto[err, nDel] = dbase->transformIndex(filter, {}, [](IndexEntry& ie) {
+        ie.SetDeleteFlag(true);
+        return true;
+    });
+    dbase->setDuplicates(std::move(duplicates));
+    progress.report(1, 1);
+    return (err == OK) ? UI_Result(ti, OK, nDel) : UI_Result(ti, err);
 }
 
-
 //////////////////////////////////////////////////////////////////////
 /// CLIPBASE functions
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1208,8 +1029,7 @@ sc_clipbase (ClientData, Tcl_Interp * ti
     switch (index) {
     case CLIP_CLEAR:
         clipbase->Close();
-        clipbase->Open(ICodecDatabase::MEMORY, FMODE_Memory, "<clipbase>");
-        clipbase->setExtraInfo("type", "2");
+        DBasePool::clearClipBase();
         break;
 
     case CLIP_PASTE: // Paste the active clipbase game
@@ -1249,11 +1069,11 @@ sc_eco (ClientData cd, Tcl_Interp * ti,
 {
     int index = -1;
     static const char * options [] = {
-        "base", "game", "read", "reset", "size", "summary",
+        "base", "game", "read", "reset", "summary",
         "translate", NULL
     };
     enum {
-        ECO_BASE, ECO_GAME, ECO_READ, ECO_RESET, ECO_SIZE, ECO_SUMMARY,
+        ECO_BASE, ECO_GAME, ECO_READ, ECO_RESET, ECO_SUMMARY,
         ECO_TRANSLATE
     };
 
@@ -1273,9 +1093,6 @@ sc_eco (ClientData cd, Tcl_Interp * ti,
         if (ecoBook) { ecoBook = NULL; }
         break;
 
-    case ECO_SIZE:
-        return setUintResult (ti, ecoBook == NULL ? 0 : ecoBook->Size());
-
     case ECO_SUMMARY:
         return sc_eco_summary (cd, ti, argc, argv);
 
@@ -1312,7 +1129,6 @@ sc_eco_base (ClientData, Tcl_Interp * ti
     }
     if (!ecoBook) { return errorResult (ti, "No ECO Book is loaded."); }
     if (! db->inUse) return errorResult (ti, ERROR_FileNotOpen);
-    if (db->isReadOnly()) return errorResult (ti, ERROR_FileReadOnly);
 
     int option = -1;
     enum {ECO_NOCODE, ECO_ALL, ECO_DATE, ECO_FILTER};
@@ -1341,54 +1157,39 @@ sc_eco_base (ClientData, Tcl_Interp * ti
             return false;
 
         // Ignore games with existing ECO code if directed:
-        if (option == ECO_NOCODE  &&  ie.GetEcoCode() != 0)
+        if (option == ECO_NOCODE && ie.GetEcoCode() != 0)
             return false;
 
         // Ignore games before starting date if directed:
-        if (option == ECO_DATE  &&  ie.GetDate() < startDate)
+        if (option == ECO_DATE && ie.GetDate() < startDate)
             return false;
 
-        if (dbase.getGame(&ie, dbase.bbuf) != OK)
-            return false;
-        db->bbuf->BackToStart();
+        auto bbuf = dbase.getGame(ie);
         Game* g = scratchGame;
-        g->Clear();
-        if (g->DecodeStart(dbase.bbuf) != OK)
+        if (g->DecodeSkipTags(&bbuf) != OK)
             return false;
 
-        // First, read in the game -- with a limit of 30 moves per
-        // side, since an ECO match after move 31 is very unlikely and
-        // we can save time by setting a limit. Also, stop when the
-        // material left in on the board is less than that of the
-        // book position with the least material, since no further
-        // positions in the game could possibly match.
-
-        uint maxPly = 60;
-        uint leastMaterial = ecoBook->FewestPieces();
-        uint material;
-
-        errorT err = OK;
-        do {
-            err = g->DecodeNextMove (db->bbuf, NULL);
-            maxPly--;
-            material = g->GetCurrentPos()->TotalMaterial();
-        } while (err == OK  &&  maxPly > 0  &&  material >= leastMaterial);
-
-        // Now, move back through the game to the start searching for a
-        // match in the ECO book. Stop at the first match found since it
-        // is the deepest.
-
         ecoT ecoCode = ECO_None;
-        do {
-            ecoCode = ecoBook->findECO(g->GetCurrentPos());
-            if (ecoCode != ECO_None) {
-                if (! extendedCodes) {
-                    ecoCode = eco_BasicCode (ecoCode);
-                }
+        for (;;) {
+            auto pos = g->GetCurrentPos();
+            if (pos->TotalMaterial() < ecoBook->FewestPieces())
                 break;
+
+            const auto eco = ecoBook->findECO(pos);
+            if (eco != ECO_None) {
+                ecoCode = eco;
             }
-            err = g->MoveBackup();
-        } while (err == OK);
+
+            simpleMoveT sm;
+            if (g->DecodeNextMove(&bbuf, sm) != OK)
+                break;
+
+            g->GetCurrentPos()->DoSimpleMove(sm);
+        }
+
+        if (!extendedCodes) {
+            ecoCode = eco_BasicCode(ecoCode);
+        }
 
         if (ie.GetEcoCode() != ecoCode) {
             ie.SetEcoCode(ecoCode);
@@ -1426,9 +1227,7 @@ sc_eco_game (ClientData, Tcl_Interp * ti
     if (!ecoBook) { return TCL_OK; }
 
     auto location = db->game->currentLocation();
-    db->game->MoveToPly (0);
-
-    do {} while (db->game->MoveForward() == OK);
+    db->game->MoveToEnd();
     ecoT ecoCode = ECO_None;
     do {
         ecoCode = ecoBook->findECO(db->game->GetCurrentPos());
@@ -1469,7 +1268,7 @@ sc_eco_read (ClientData, Tcl_Interp * ti
         return book.first;
     }
     ecoBook = std::move(book.second);
-    return setUintResult (ti, ecoBook->Size());
+    return UI_Result(ti, OK, ecoBook->Size());
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1628,23 +1427,13 @@ sc_filter_old(ClientData cd, Tcl_Interp
         break;
 
     case FILTER_NEW:
-        if (argc == 3 || argc == 4) {
-            scidBaseT* dbase = DBasePool::getBase(strGetUnsigned(argv[2]));
-            if (dbase == NULL) return UI_Result(ti, ERROR_BadArg, "sc_filter: invalid baseId");
-            if (argc == 4) {
-                //TODO: Use argv[4] (FEN) instead of current Position
-                SearchPos fp(db->game->GetCurrentPos());
-                //TODO: use a dedicated filter instead of treeFilter
-                HFilter maskfilter = HFilter(dbase->treeFilter);
-                std::string val;
-                if (fp.setFilter(dbase, maskfilter, UI_CreateProgressPosMask(ti))) {
-                    val = "tree";
-                }
-                return UI_Result(ti, OK, val);
-            }
-            return UI_Result(ti, OK, dbase->newFilter());
+        if (argc == 3) {
+            if (auto dbase = DBasePool::getBase(strGetUnsigned(argv[2])))
+                return UI_Result(ti, OK, dbase->newFilter());
+
+            return UI_Result(ti, ERROR_BadArg, "sc_filter: invalid baseId");
         }
-        return UI_Result(ti, ERROR_BadArg, "Usage: sc_filter new baseId [FEN]");
+        return UI_Result(ti, ERROR_BadArg, "Usage: sc_filter new baseId");
 
     case FILTER_FIRST:
         return sc_filter_first (cd, ti, argc, argv);
@@ -1715,41 +1504,62 @@ sc_filter_old(ClientData cd, Tcl_Interp
         return UI_Result(ti, OK);
 
     case FILTER_COUNT:
-        return setUintResult (ti, filter->size());
+        return UI_Result(ti, OK, filter->size());
 
     case FILTER_RELEASE:
         dbase->deleteFilter(argv[3]);
         return TCL_OK;
 
     case FILTER_SEARCH:
-        if (argc > 5) {
-            if (strCompare("header", argv[4]) == 0)
+        if (argc >= 5) {
+            std::string_view subcmd = argv[4];
+            if (subcmd == "header")
                 return sc_search_header (cd, ti, dbase, filter, argc -3, argv +3);
 
-            if (strCompare("board", argv[4]) == 0 && argc == 10) {
-                const char* args[6] = {argv[3], argv[4], argv[9], argv[5],
-                                       argv[6], argv[7]};
-                return sc_search_board(ti, dbase, filter, 6, args);
+            if (subcmd == "board") {
+                if (argc == 5 || argc == 6) {
+                    // sc_filter search baseId filterTo board [filterFrom]
+                    bool useCache = (argc == 5);
+
+                    auto const& pos = *db->game->GetCurrentPos();
+                    if (useCache &&
+                        dbase->treeCache.cacheRestore(pos, *dbase->treeFilter))
+                        return UI_Result(ti, OK);
+
+                    if (!SearchPos(pos).setFilter(
+                            *dbase, filter, UI_CreateProgress(ti)))
+                        return UI_Result(ti, ERROR_UserCancel);
+
+                    if (useCache)
+                        dbase->treeCache.cacheAdd(pos, *dbase->treeFilter);
+
+                    return UI_Result(ti, OK);
+                }
+
+                if (argc == 10) {
+                    const char* args[6] = {argv[3], argv[4], argv[9],
+                                           argv[5], argv[6], argv[7]};
+                    return sc_search_board(ti, dbase, filter, 6, args);
+                }
             }
         }
-        return errorResult (ti, "Usage: sc_filter search baseId filterName <header> [args]");
+        return errorResult (ti, "Usage: sc_filter search baseId filterName <header|board> [args]");
 
     case FILTER_TREESTATS: {
-            std::vector<scidBaseT::TreeStat> stats = dbase->getTreeStat(filter);
+            const auto stats = dbase->getTreeStat(filter);
             UI_List res (stats.size());
-            UI_List ginfo(8);
-            for (uint i=0; i < stats.size(); i++) {
+            UI_List ginfo(9);
+            for (auto const& node : stats) {
                 ginfo.clear();
-                ginfo.push_back(stats[i].SAN);
-                ginfo.push_back(stats[i].ngames);
-                ginfo.push_back(stats[i].resultW);
-                ginfo.push_back(stats[i].resultD);
-                ginfo.push_back(stats[i].resultB);
-                ginfo.push_back(stats[i].exp);
-                ginfo.push_back(stats[i].nexp);
-                if (stats[i].toMove == WHITE) ginfo.push_back("W");
-                else ginfo.push_back(stats[i].toMove == BLACK ? "B" : " ");
-
+                ginfo.push_back(node.move ? node.move.getSAN() : "[end]");
+                ginfo.push_back(node.freq[0]);
+                ginfo.push_back(node.freq[RESULT_White]);
+                ginfo.push_back(node.freq[RESULT_Draw]);
+                ginfo.push_back(node.freq[RESULT_Black]);
+                ginfo.push_back(node.avgElo());
+                ginfo.push_back(node.eloPerformance());
+                ginfo.push_back(node.eloCount);
+                ginfo.push_back(node.move.getColor() == WHITE ? "W" : "B");
                 res.push_back(ginfo);
             }
             return UI_Result(ti, OK, res);
@@ -1769,7 +1579,6 @@ sc_filter_old(ClientData cd, Tcl_Interp
             }
             if (argc > 7) fprintf(exportFile, "%s", argv[7]);
             Progress progress = UI_CreateProgress(ti);
-            const NameBase* nb = dbase->getNameBase();
             size_t count = filter->size();
             gamenumT* idxList = new gamenumT[count];
             count = dbase->listGames(argv[4], 0, count, filter, idxList);
@@ -1777,9 +1586,8 @@ sc_filter_old(ClientData cd, Tcl_Interp
                 for (size_t i = 0; i < count; ++i) {
                     const IndexEntry* ie = dbase->getIndexEntry(idxList[i]);
                     // Skip any corrupt games:
-                    if (dbase->getGame(ie, dbase->bbuf) != OK) continue;
-                    if (g.Decode (dbase->bbuf, GAME_DECODE_ALL) != OK) continue;
-                    g.LoadStandardTags (ie, nb);
+                    if (dbase->getGame(*ie, g) != OK) continue;
+
                     std::pair<const char*, unsigned> pgn = g.WriteToPGN(75, true);
                     if (pgn.second != fwrite(pgn.first, 1, pgn.second, exportFile)) {
                         err = ERROR_FileWrite;
@@ -2096,8 +1904,8 @@ sc_game (ClientData cd, Tcl_Interp * ti,
         "info",        "load",      "merge",      "moves",
         "new",        "novelty",    "number",     "pgn",
         "pop",        "push",       "SANtoUCI",   "save",
-        "startBoard", "strip",      "summary",
-        "tags",       "truncate",
+        "startBoard", "strip",
+        "tags",       "truncate",   "variant", "UCI_currentPos",
         "undo",       "undoAll",    "undoPoint",  "redo",       NULL
     };
     enum {
@@ -2106,8 +1914,8 @@ sc_game (ClientData cd, Tcl_Interp * ti,
         GAME_INFO,       GAME_LOAD,       GAME_MERGE,      GAME_MOVES,
         GAME_NEW,        GAME_NOVELTY,    GAME_NUMBER,     GAME_PGN,
         GAME_POP,        GAME_PUSH,       GAME_SANTOUCI,   GAME_SAVE,
-        GAME_STARTBOARD, GAME_STRIP,      GAME_SUMMARY,
-        GAME_TAGS,       GAME_TRUNCATE,
+        GAME_STARTBOARD, GAME_STRIP,
+        GAME_TAGS,       GAME_TRUNCATE,   GAME_VARIANT, GAME_UCI_CURRENTPOS,
         GAME_UNDO,       GAME_UNDO_ALL,   GAME_UNDO_POINT, GAME_REDO
     };
     int index = -1;
@@ -2179,8 +1987,8 @@ sc_game (ClientData cd, Tcl_Interp * ti,
             if (err != OK)
                 return UI_Result(ti, err);
 
-            char buf[16];
-            pos->MakeUCIString(&sm, buf);
+            char buf[16] = {};
+            sm.toLongNotation(buf);
             return UI_Result(ti, OK, buf);
         }
         return errorResult(ti, "usage sc_game SANtoUCI move");
@@ -2194,9 +2002,6 @@ sc_game (ClientData cd, Tcl_Interp * ti,
     case GAME_STRIP:
         return sc_game_strip (cd, ti, argc, argv);
 
-    case GAME_SUMMARY:
-        return sc_game_summary (cd, ti, argc, argv);
-
     case GAME_TAGS:
         return sc_game_tags (cd, ti, argc, argv);
 
@@ -2214,6 +2019,15 @@ sc_game (ClientData cd, Tcl_Interp * ti,
         db->gameAltered = true;
         language = old_language;
         break;
+
+    case GAME_VARIANT:
+        return UI_Result(ti, OK,
+                         db->game->currentPos()->isChess960() ? "chess960"
+                                                              : "standard");
+
+    case GAME_UCI_CURRENTPOS:
+        return UI_Result(ti, OK, db->game->currentPosUCI());
+
     case GAME_UNDO:
         if (argc > 2 && strCompare("size", argv[2]) == 0) {
             return UI_Result(ti, OK, (uint) db->gameAlterations.undoSize());
@@ -2228,12 +2042,9 @@ sc_game (ClientData cd, Tcl_Interp * ti,
             db->game->Clear();
         } else {
             const IndexEntry* ie = db->getIndexEntry(db->gameNumber);
-            errorT err = db->getGame(ie, db->bbuf);
+            errorT err = db->getGame(*ie, *db->game);
             if (err != OK) return UI_Result(ti, err);
-            err = db->game->Decode (db->bbuf, GAME_DECODE_ALL);
-            if (err != OK) return UI_Result(ti, err);
-            db->game->LoadStandardTags (ie, db->getNameBase());
-            db->game->MoveToPly(0);
+            db->game->MoveToStart();
         }
         return UI_Result(ti, OK);
 
@@ -2300,6 +2111,7 @@ sc_game_crosstable (ClientData, Tcl_Inte
         "-breaks", "+breaks",           // Show tiebreak scores
         "-colors", "+colors",           // Show game colors in Swiss table
         "-countries", "+countries",     // Show current countries
+        "-flags", "+flags",             // Show flags
         "-tallies", "+tallies",
         "-ratings", "+ratings",         // Show Elo ratings
         "-titles", "+titles",           // Show FIDE titles
@@ -2317,6 +2129,7 @@ sc_game_crosstable (ClientData, Tcl_Inte
         EOPT_BREAKS_OFF, EOPT_BREAKS_ON,
         EOPT_COLORS_OFF, EOPT_COLORS_ON,
         EOPT_COUNTRIES_OFF, EOPT_COUNTRIES_ON,
+        EOPT_FLAGS_OFF, EOPT_FLAGS_ON,
         EOPT_TALLIES_OFF, EOPT_TALLIES_ON,
         EOPT_RATINGS_OFF, EOPT_RATINGS_ON,
         EOPT_TITLES_OFF, EOPT_TITLES_ON,
@@ -2332,6 +2145,7 @@ sc_game_crosstable (ClientData, Tcl_Inte
     bool showAges = true;
     bool showColors = true;
     bool showCountries = true;
+    bool showFlags = true;
     bool showTallies = true;
     bool showRatings = true;
     bool showTitles = true;
@@ -2365,6 +2179,8 @@ sc_game_crosstable (ClientData, Tcl_Inte
             case EOPT_COLORS_ON:      showColors = true;       break;
             case EOPT_COUNTRIES_OFF:  showCountries = false;   break;
             case EOPT_COUNTRIES_ON:   showCountries = true;    break;
+            case EOPT_FLAGS_OFF:      showFlags = false;       break;
+            case EOPT_FLAGS_ON:       showFlags = true;        break;
             case EOPT_TALLIES_OFF:    showTallies = false;     break;
             case EOPT_TALLIES_ON:     showTallies = true;      break;
             case EOPT_RATINGS_OFF:    showRatings = false;     break;
@@ -2411,13 +2227,9 @@ sc_game_crosstable (ClientData, Tcl_Inte
         if (ie->GetLength() == 0) {
             return errorResult (ti, "Error: empty game file record.");
         }
-        if (db->getGame(ie, db->bbuf) != OK) {
+        if (db->getGame(*ie, *g) != OK) {
             return errorResult (ti, "Error reading game file.");
         }
-        if (g->Decode (db->bbuf, GAME_DECODE_ALL) != OK) {
-            return errorResult (ti, "Error decoding game.");
-            }
-        g->LoadStandardTags (ie, db->getNameBase());
     }
 
     idNumberT eventId = 0, siteId = 0;
@@ -2441,6 +2253,7 @@ sc_game_crosstable (ClientData, Tcl_Inte
     ctable->SetSwissColors (showColors);
     ctable->SetAges (showAges);
     ctable->SetCountries (showCountries);
+    ctable->SetFlags (showFlags);
     ctable->SetTallies (showTallies);
     ctable->SetElos (showRatings);
     ctable->SetTitles (showTitles);
@@ -2679,12 +2492,20 @@ int sc_game_import(ClientData, Tcl_Inter
 	if (argc != 3)
 		return errorResult(ti, "Usage: sc_game import <pgn-text>");
 
-	db->game->Clear();
 	db->gameAltered = true;
+	bool new_variation = false;
+	if (db->game->MoveForward() == OK) {
+		new_variation = (db->game->AddVariation() == OK);
+	}
 
 	PgnParseLog pgn;
-	if (!pgnParseGame(argv[2], std::strlen(argv[2]), *db->game, pgn) &&
-	    pgn.log.empty())
+	auto ok = pgnParseGame(argv[2], std::strlen(argv[2]), *db->game, pgn);
+
+	if (new_variation && db->game->AtEmptyVar()) {
+		db->game->DeleteVariation();
+	}
+
+	if (!ok && pgn.log.empty())
 		return UI_Result(ti, OK, "No PGN text found.");
 
 	if (pgn.log.empty())
@@ -2752,7 +2573,7 @@ probe_tablebase (Tcl_Interp * ti, int mo
 
         for (uint i=0; i < moveList.Size(); i++) {
             simpleMoveT * smPtr = moveList.Get(i);
-            scratchPos.DoSimpleMove (smPtr);
+            scratchPos.DoSimpleMove(*smPtr);
             moveFound[i] = false;
             movePrinted[i] = false;
             int newScore = 0;
@@ -3157,8 +2978,8 @@ sc_game_info (ClientData, Tcl_Interp * t
             std::string str;
             if (ecoBook) {
                 auto ecoStr = ecoBook->findECOstr(db->game->GetCurrentPos());
-                if (ecoStr.first)
-                    str.append(ecoStr.first, ecoStr.second);
+                if (!ecoStr.empty())
+                    str.append(ecoStr);
             }
             return UI_Result(ti, OK, str);
         }
@@ -3176,7 +2997,7 @@ sc_game_info (ClientData, Tcl_Interp * t
     eloT elo = db->game->GetWhiteElo();
     bool eloEstimated = false;
     if (elo == 0) {
-        elo = db->game->GetWhiteEstimateElo();
+        elo = db->peakElo(db->game->GetWhiteStr());
         eloEstimated = true;
     }
     if (elo != 0) {
@@ -3192,7 +3013,7 @@ sc_game_info (ClientData, Tcl_Interp * t
     elo = db->game->GetBlackElo();
     eloEstimated = false;
     if (elo == 0) {
-        elo = db->game->GetBlackEstimateElo();
+        elo = db->peakElo(db->game->GetBlackStr());
         eloEstimated = true;
     }
     if (elo != 0) {
@@ -3476,8 +3297,8 @@ sc_game_info (ClientData, Tcl_Interp * t
     // Now check ECO book for the current position:
     if (ecoBook) {
         auto ecoStr = ecoBook->findECOstr(db->game->GetCurrentPos());
-        if (ecoStr.first) {
-            std::string ecoComment(ecoStr.first, ecoStr.second);
+        if (!ecoStr.empty()) {
+            std::string ecoComment(ecoStr);
             ecoT eco = eco_FromString(ecoComment.c_str());
             ecoStringT estr;
             eco_ToExtendedString (eco, estr);
@@ -3526,20 +3347,16 @@ sc_game_load (ClientData, Tcl_Interp * t
 
     const IndexEntry* ie = db->getIndexEntry(gnum);
 
-    if (db->getGame(ie, db->bbuf) != OK) {
-        return errorResult (ti, corruptMsg);
-    }
-    if (db->game->Decode (db->bbuf, GAME_DECODE_ALL) != OK) {
+    if (db->getGame(*ie, *db->game) != OK) {
         return errorResult (ti, corruptMsg);
     }
 
     if (db->dbFilter->Get(gnum) > 0) {
         db->game->MoveToPly(db->dbFilter->Get(gnum) - 1);
     } else {
-        db->game->MoveToPly(0);
+        db->game->MoveToStart();
     }
 
-    db->game->LoadStandardTags (ie, db->getNameBase());
     db->gameNumber = gnum;
     db->gameAltered = false;
     return OK;
@@ -3582,15 +3399,11 @@ sc_game_merge (ClientData, Tcl_Interp *
     // Load the merge game:
 
     const IndexEntry* ie = base->getIndexEntry(gnum);
-    if (base->getGame(ie, base->bbuf) != OK) {
-        return errorResult (ti, "Error loading game.");
-    }
+    auto bbuf = base->getGame(*ie);
     Game * merge = scratchGame;
-    merge->Clear();
-    if (merge->Decode (base->bbuf, GAME_DECODE_NONE) != OK) {
+    if (merge->DecodeMovesOnly(bbuf) != OK) {
         return errorResult (ti, "Error decoding game.");
     }
-    merge->LoadStandardTags (ie, base->getNameBase());
     if (merge->HasNonStandardStart()) {
         return errorResult (ti, "The merge game has a non-standard start position.");
     }
@@ -3599,7 +3412,7 @@ sc_game_merge (ClientData, Tcl_Interp *
     uint nMergePos = merge->GetNumHalfMoves() + 1;
     typedef char compactBoardStr [36];
     compactBoardStr * mergeBoards = new compactBoardStr [nMergePos];
-    merge->MoveToPly (0);
+    merge->MoveToStart();
     for (uint i=0; i < nMergePos; i++) {
         merge->GetCurrentPos()->PrintCompactStr (mergeBoards[i]);
         merge->MoveForward();
@@ -3607,7 +3420,7 @@ sc_game_merge (ClientData, Tcl_Interp *
 
     // Now find the deepest position in the current game that occurs
     // in the merge game:
-    db->game->MoveToPly (0);
+    db->game->MoveToStart();
     uint matchPly = 0;
     uint mergePly = 0;
     uint ply = 0;
@@ -3638,14 +3451,15 @@ sc_game_merge (ClientData, Tcl_Interp *
         // at the start of the variation:
         db->game->MoveBackup();
         sm = db->game->GetCurrentMove();
+        ASSERT(sm);
         db->game->MoveForward();
     }
     db->game->MoveForward();
     db->game->AddVariation();
     db->gameAltered = true;
-    if (atLastMove) {
+    if (sm) {
         // We need to replicate the last move of the current game.
-        db->game->AddMove(sm);
+        db->game->AddMove(*sm);
     }
     merge->MoveToPly (mergePly);
     ply = mergePly;
@@ -3653,29 +3467,33 @@ sc_game_merge (ClientData, Tcl_Interp *
         simpleMoveT * mergeMove = merge->GetCurrentMove();
         if (merge->MoveForward() != OK) { break; }
         if (mergeMove == NULL) { break; }
-        if (db->game->AddMove(mergeMove) != OK) { break; }
+        if (db->game->AddMove(*mergeMove) != OK) { break; }
         ply++;
     }
 
     // Finally, add a comment describing the merge-game details:
-    DString * dstr = new DString;
-    dstr->Append (RESULT_LONGSTR[ie->GetResult()]);
+    const auto tags = TagRoster::make(*ie, *base->getNameBase());
+    const auto welo = ie->GetWhiteElo();
+    const auto belo = ie->GetBlackElo();
+    auto dstr = DString();
+    dstr.Append(RESULT_LONGSTR[ie->GetResult()]);
     if (ply < merge->GetNumHalfMoves()) {
-        dstr->Append ("(", (merge->GetNumHalfMoves()+1) / 2, ")");
+        dstr.Append("(", (merge->GetNumHalfMoves() + 1) / 2, ")");
     }
-    dstr->Append (" ", ie->GetWhiteName (base->getNameBase()));
-    eloT elo = ie->GetWhiteElo();
-    if (elo > 0) { dstr->Append (" (", elo, ")"); }
-    dstr->Append (" - ");
-    dstr->Append (ie->GetBlackName (base->getNameBase()));
-    elo = ie->GetBlackElo();
-    if (elo > 0) { dstr->Append (" (", elo, ")"); }
-    dstr->Append (" / ", ie->GetEventName (base->getNameBase()));
-    dstr->Append (" (", ie->GetRoundName (base->getNameBase()), ")");
-    dstr->Append (", ", ie->GetSiteName (base->getNameBase()));
-    dstr->Append (" ", ie->GetYear());
-    db->game->SetMoveComment ((char *) dstr->Data());
-    delete dstr;
+    dstr.Append(" ", tags.white);
+    if (welo > 0) {
+        dstr.Append(" (", welo, ")");
+    }
+    dstr.Append(" - ");
+    dstr.Append(tags.black);
+    if (belo > 0) {
+        dstr.Append(" (", belo, ")");
+    }
+    dstr.Append(" / ", tags.event);
+    dstr.Append(" (", tags.round, ")");
+    dstr.Append(", ", tags.site);
+    dstr.Append(" ", ie->GetYear());
+    db->game->SetMoveComment(dstr.Data());
 
     // And exit the new variation:
     db->game->MoveExitVariation();
@@ -3717,13 +3535,7 @@ sc_game_moves (ClientData, Tcl_Interp *
         if (sanFormat) {
             g->GetSAN (s);
         } else {
-            *s++ = square_FyleChar(sm->from);
-            *s++ = square_RankChar(sm->from);
-            *s++ = square_FyleChar(sm->to);
-            *s++ = square_RankChar(sm->to);
-            if (sm->promote != EMPTY) {
-                *s++ = piece_Char (piece_Type (sm->promote));
-            }
+            s = sm->toLongNotation(s);
             *s = 0;
         }
         plyCount++;
@@ -3799,7 +3611,7 @@ sc_game_novelty (ClientData, Tcl_Interp
     Game* g = base->game;
     if (ecoBook) {
         while (g->MoveForward() == OK) {}
-        while (!ecoBook->findECOstr(g->GetCurrentPos()).first) {
+        while (ecoBook->findECOstr(g->GetCurrentPos()).empty()) {
             if (g->MoveBackup() != OK) break;
         }
     }
@@ -3812,7 +3624,7 @@ sc_game_novelty (ClientData, Tcl_Interp
     HFilter filter = base->getFilter(filtername);
     dateT currentDate = g->GetDate();
     while (g->MoveForward() == OK) {
-        SearchPos(g->GetCurrentPos()).setFilter(base, filter, Progress());
+        SearchPos(*g->GetCurrentPos()).setFilter(*base, filter, Progress());
         int count = 0;
         for (uint i=0, n = base->numGames(); i < n; i++) {
             if (filter.get(i) == 0) continue;
@@ -3829,7 +3641,8 @@ sc_game_novelty (ClientData, Tcl_Interp
             return UI_Result(ti, OK, g->GetCurrentPly());
         }
 
-        if (!progress.report(g->GetCurrentPly() +1, g->GetNumHalfMoves())) {
+        auto work_done = g->GetCurrentPly() + 1;
+        if (!progress.report(work_done, g->GetNumHalfMoves())) {
             base->deleteFilter(filtername.c_str());
             return UI_Result(ti, ERROR_UserCancel);
         }
@@ -3926,13 +3739,9 @@ sc_game_pgn (ClientData, Tcl_Interp * ti
             if (ie->GetLength() == 0) {
                 return errorResult (ti, "Error: empty game file record.");
             }
-            if (base->getGame(ie, base->bbuf) != OK) {
+            if (base->getGame(*ie, *g) != OK) {
                 return errorResult (ti, "Error reading game file.");
             }
-            if (g->Decode (base->bbuf, GAME_DECODE_ALL) != OK) {
-                return errorResult (ti, "Error decoding game.");
-            }
-            g->LoadStandardTags (ie, base->getNameBase());
 
         } else if (index == OPT_FORMAT) {
             // The option value should be "plain", "html" or "latex".
@@ -4128,114 +3937,68 @@ int sc_game_strip(ClientData, Tcl_Interp
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sc_game_summary:
 //    Returns summary information of the specified game:
 //    its players, site, etc; or its moves; or all its boards
 //    positions.
-int
-sc_game_summary (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
-{
-    const char * usage = "Usage: sc_game summary [-base <baseNum>] [-gameNumber <gameNum>] header|boards|moves";
-
-    const char * options[] = {
-        "-base", "-gameNumber", NULL
-    };
-    enum { OPT_BASE, OPT_GNUM };
-
-    const scidBaseT* base = db;
-    uint gnum = 0;
-
-    int arg = 2;
-    while (arg+1 < argc) {
-        const char * value = argv[arg+1];
-        int index = strUniqueMatch (argv[arg], options);
-        arg += 2;
-
-        if (index == OPT_BASE) {
-            base = DBasePool::getBase(strGetUnsigned(value));
-            if (base == 0) return UI_Result(ti, ERROR_FileNotOpen);
-        } else if (index == OPT_GNUM) {
-            gnum = strGetUnsigned (value);
-        } else {
-            return errorResult (ti, usage);
-        }
-    }
-    if (arg+1 != argc) { return errorResult (ti, usage); }
+UI_res_t sc_base_gamesummary(const scidBaseT& base, UI_handle_t ti, int argc,
+                             const char** argv) {
+	const char* usage = "Usage: sc_base gamesummary baseId gameNum";
+	if (argc != 4)
+		return UI_Result(ti, ERROR_BadArg, usage);
 
-    enum modeT { MODE_HEADER, MODE_BOARDS, MODE_MOVES };
-    modeT mode = MODE_HEADER;
-    switch (tolower(argv[arg][0])) {
-        case 'h': mode = MODE_HEADER; break;
-        case 'b': mode = MODE_BOARDS; break;
-        case 'm': mode = MODE_MOVES; break;
-        default: return errorResult (ti, usage);
-    }
+	Game* g = scratchGame;
+	gamenumT gnum = strGetUnsigned(argv[3]);
+	if (gnum > 0) {
+		auto ie = base.getIndexEntry_bounds(gnum - 1);
+		if (!ie || base.getGame(*ie, *scratchGame) != OK) {
+			return UI_Result(ti, ERROR_BadArg, usage);
+		}
+	} else {
+		g = base.game;
+	}
 
-    Game * g = scratchGame;
-    if (gnum == 0) {
-        g = base->game;
-    } else {
-        // Load the specified game number:
-        if (! base->inUse) {
-            return errorResult (ti, "This database is not in use.");
-        }
-        if (gnum > base->numGames()) {
-            return errorResult (ti, "Invalid game number.");
-        }
-        gnum--;
-        const IndexEntry* ie = base->getIndexEntry(gnum);
-        if (base->getGame(ie, base->bbuf) != OK) {
-            return errorResult (ti, "Error loading game.");
-        }
-        g->Clear();
-        if (g->Decode (base->bbuf, GAME_DECODE_NONE) != OK) {
-            return errorResult (ti, "Error decoding game.");
-        }
-        g->LoadStandardTags (ie, base->getNameBase());
-    }
+    UI_List res(3);
 
     // Return header summary if requested:
-    if (mode == MODE_HEADER) {
-        DString * dstr = new DString;
-        dstr->Append (g->GetWhiteStr());
+        DString dstr;
+        dstr.Append (g->GetWhiteStr());
         eloT elo = g->GetWhiteElo();
-        if (elo > 0) { dstr->Append (" (", elo, ")"); }
-        dstr->Append ("  --  ", g->GetBlackStr());
+        if (elo > 0) { dstr.Append (" (", elo, ")"); }
+        dstr.Append ("  --  ", g->GetBlackStr());
         elo = g->GetBlackElo();
-        if (elo > 0) { dstr->Append (" (", elo, ")"); }
-        dstr->Append ("\n", g->GetEventStr());
+        if (elo > 0) { dstr.Append (" (", elo, ")"); }
+        dstr.Append ("\n", g->GetEventStr());
         const char * round = g->GetRoundStr();
         if (! strIsUnknownName(round)) {
-            dstr->Append (" (", round, ")");
+            dstr.Append (" (", round, ")");
         }
-        dstr->Append ("  ", g->GetSiteStr(), "\n");
+        dstr.Append ("  ", g->GetSiteStr(), "\n");
         char dateStr [20];
         date_DecodeToString (g->GetDate(), dateStr);
         // Remove ".??" or ".??.??" from end of date:
         if (dateStr[4] == '.'  &&  dateStr[5] == '?') { dateStr[4] = 0; }
         if (dateStr[7] == '.'  &&  dateStr[8] == '?') { dateStr[7] = 0; }
-        dstr->Append (dateStr, "  ");
-        dstr->Append (RESULT_LONGSTR[g->GetResult()]);
+        dstr.Append (dateStr, "  ");
+        dstr.Append (RESULT_LONGSTR[g->GetResult()]);
         ecoT eco = g->GetEco();
         if (eco != 0) {
             ecoStringT ecoStr;
             eco_ToExtendedString (eco, ecoStr);
-            dstr->Append ("  ", ecoStr);
+            dstr.Append ("  ", ecoStr);
         }
-        Tcl_AppendResult (ti, dstr->Data(), NULL);
-        delete dstr;
-        return TCL_OK;
-    }
+        res.push_back(dstr.Data());
 
     // Here, a list of the boards or moves is requested:
+    const auto n_moves = g->GetNumHalfMoves() + 1;
+    UI_List boards(n_moves);
+    UI_List moves(n_moves);
     auto location = g->currentLocation();
-    g->MoveToPly (0);
-    while (1) {
-        if (mode == MODE_BOARDS) {
+    g->MoveToStart();
+    do {
             char boardStr[100];
             g->GetCurrentPos()->MakeLongStr (boardStr);
-            Tcl_AppendElement (ti, boardStr);
-        } else {
+            boards.push_back(boardStr);
+
             colorT toMove = g->GetCurrentPos()->GetToMove();
             uint moveCount = g->GetCurrentPos()->GetFullMoveCount();
             char san [20];
@@ -4260,16 +4023,17 @@ sc_game_summary (ClientData, Tcl_Interp
                         strAppend (temp, nagstr);
                     }
                 }
-                Tcl_AppendElement (ti, temp);
+                moves.push_back(temp);
             } else {
-                Tcl_AppendElement (ti, (char *)RESULT_LONGSTR[g->GetResult()]);
+                moves.push_back(RESULT_LONGSTR[g->GetResult()]);
             }
-        }
-        if (g->MoveForward() != OK) { break; }
-    }
 
+    } while (g->MoveForward() == OK);
     g->restoreLocation(location);
-    return TCL_OK;
+
+    res.push_back(boards);
+    res.push_back(moves);
+    return UI_Result(ti, OK, res);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -4338,13 +4102,9 @@ sc_game_tags_get (ClientData, Tcl_Interp
         if (db->numGames() > 0) {
             g = scratchGame;
             const IndexEntry* ie = db->getIndexEntry(db->numGames() - 1);
-            if (db->getGame(ie, db->bbuf) != OK) {
+            if (db->getGame(*ie, *g) != OK) {
                 return errorResult (ti, "Error reading game file.");
             }
-            if (g->Decode (db->bbuf, GAME_DECODE_ALL) != OK) {
-                return errorResult (ti, "Error decoding game.");
-            }
-            g->LoadStandardTags (ie, db->getNameBase());
         }
     }
     const char * s;
@@ -4446,6 +4206,15 @@ sc_game_tags_get (ClientData, Tcl_Interp
     return TCL_OK;
 }
 
+static uint strGetRatingType (const char * name) {
+    uint i = 0;
+    while (ratingTypeNames[i] != NULL) {
+        if (strEqual (name, ratingTypeNames[i])) { return i; }
+        i++;
+    }
+    return 0;
+}
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // sc_game_tags_set:
 //    Set the standard tags for this game.
@@ -4549,7 +4318,7 @@ sc_game_tags_reload(ClientData, Tcl_Inte
 {
     if (!db->inUse  ||   db->gameNumber < 0) { return TCL_OK; }
     const IndexEntry* ie = db->getIndexEntry(db->gameNumber);
-    db->game->LoadStandardTags (ie, db->getNameBase());
+    db->game->LoadStandardTags(*ie, TagRoster::make(*ie, *db->getNameBase()));
     return TCL_OK;
 }
 
@@ -4640,8 +4409,8 @@ sc_game_tags_share (ClientData, Tcl_Inte
     // Check if an event name can be updated:
     idNumberT event1 = ie1.GetEvent();
     idNumberT event2 = ie2.GetEvent();
-    const char * eventStr1 = ie1.GetEventName (db->getNameBase());
-    const char * eventStr2 = ie2.GetEventName (db->getNameBase());
+    const char* eventStr1 = db->getNameBase()->GetName(NAME_EVENT, event1);
+    const char* eventStr2 = db->getNameBase()->GetName(NAME_EVENT, event2);
     bool event1empty = strEqual (eventStr1, "")  ||  strEqual (eventStr1, "?");
     bool event2empty = strEqual (eventStr2, "")  ||  strEqual (eventStr2, "?");
     if (event1empty  && !event2empty) {
@@ -4672,8 +4441,8 @@ sc_game_tags_share (ClientData, Tcl_Inte
     // Check if a round name can be updated:
     idNumberT round1 = ie1.GetRound();
     idNumberT round2 = ie2.GetRound();
-    const char * roundStr1 = ie1.GetRoundName (db->getNameBase());
-    const char * roundStr2 = ie2.GetRoundName (db->getNameBase());
+    const char* roundStr1 = db->getNameBase()->GetName(NAME_ROUND, round1);
+    const char* roundStr2 = db->getNameBase()->GetName(NAME_ROUND, round2);
     bool round1empty = strEqual (roundStr1, "")  ||  strEqual (roundStr1, "?");
     bool round2empty = strEqual (roundStr2, "")  ||  strEqual (roundStr2, "?");
     if (round1empty  && !round2empty) {
@@ -4755,18 +4524,28 @@ sc_game_tags_share (ClientData, Tcl_Inte
         }
     }
 
-    // Write changes to the index file:
-    if (updateMode && (updated1 || updated2)) {
-        db->beginTransaction();
-        if (updated1) {
-            db->idx->WriteEntry (&ie1, gn1 - 1);
-        }
-        if (updated2) {
-            db->idx->WriteEntry (&ie2, gn2 - 1);
+    if (!updateMode)
+        return TCL_OK;
+
+    auto duplicates = db->extractDuplicates();
+    errorT err1 = OK;
+    errorT err2 = OK;
+    if (updated1) {
+        Game game;
+        err1 = db->getGame(ie1, game);
+        if (err1 == OK) {
+            err1 = db->saveGame(&game, gn1 - 1);
+        }
+    }
+    if (updated2) {
+        Game game;
+        err2 = db->getGame(ie2, game);
+        if (err2 == OK) {
+            err2 = db->saveGame(&game, gn2 - 1);
         }
-        db->endTransaction();
     }
-    return TCL_OK;
+    db->setDuplicates(std::move(duplicates));
+    return UI_Result(ti, err1 != OK ? err1 : err2);
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -4862,9 +4641,6 @@ sc_info (ClientData cd, Tcl_Interp * ti,
         }
         break;
 
-    case INFO_SUFFIX:
-        return sc_info_suffix (cd, ti, argc, argv);
-
     case INFO_TB:
         return sc_info_tb (cd, ti, argc, argv);
 
@@ -4944,33 +4720,6 @@ sc_info_limit (ClientData, Tcl_Interp *
     return UI_Result(ti, OK, result);
 }
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sc_info suffix:
-//    Returns a Scid file suffix for a database file type.
-//    The suffix is returned with the leading dot.
-int
-sc_info_suffix (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
-{
-    static const char * options [] = {
-        "index", NULL
-    };
-    enum {
-        SUFFIX_OPT_INDEX
-    };
-    int index = -1;
-
-    if (argc == 3) { index = strUniqueMatch (argv[2], options); }
-
-    const char * suffix = "";
-
-    switch (index) {
-        case SUFFIX_OPT_INDEX: suffix = INDEX_SUFFIX;    break;
-        default: return InvalidCommand (ti, "sc_info suffix", options);
-    }
-
-    return setResult (ti, suffix);
-}
-
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // sc_info_tb:
@@ -5033,12 +4782,12 @@ int
 sc_move (ClientData cd, Tcl_Interp * ti, int argc, const char ** argv)
 {
     static const char * options [] = {
-        "add", "addSan", "addUCI", "back", "end", "forward",
+        "add", "addSan", "addUCI", "back", "end", "endVar", "forward",
         "pgn", "ply", "start", NULL
     };
     enum {
-        MOVE_ADD, MOVE_ADDSAN, MOVE_ADDUCI, MOVE_BACK, MOVE_END, MOVE_FORWARD,
-        MOVE_PGN, MOVE_PLY, MOVE_START
+        MOVE_ADD, MOVE_ADDSAN, MOVE_ADDUCI, MOVE_BACK, MOVE_END, MOVE_ENDVAR,
+        MOVE_FORWARD, MOVE_PGN, MOVE_PLY, MOVE_START
     };
     int index = -1;
 
@@ -5058,12 +4807,11 @@ sc_move (ClientData cd, Tcl_Interp * ti,
         return sc_move_back (cd, ti, argc, argv);
 
     case MOVE_END:
-        db->game->MoveToPly(0);
-        {
-            errorT err = OK;
-            do {
-                err = db->game->MoveForward();
-            } while (err == OK);
+        db->game->MoveToEnd();
+        break;
+
+    case MOVE_ENDVAR:
+        while (db->game->MoveForward() == OK) {
         }
         break;
 
@@ -5081,7 +4829,7 @@ sc_move (ClientData cd, Tcl_Interp * ti,
         return errorResult (ti, "Usage: sc_move ply <plynumber>");
 
     case MOVE_START:
-        db->game->MoveToPly (0);
+        db->game->MoveToStart();
         break;
 
     default:
@@ -5122,7 +4870,7 @@ sc_move_add (ClientData, Tcl_Interp * ti
     Position * pos = db->game->GetCurrentPos();
     errorT err = pos->ReadCoordMove(&sm, s, s[4] == 0 ? 4 : 5, true);
     if (err == OK) {
-        err = db->game->AddMove(&sm);
+        err = db->game->AddMove(sm);
         if (err == OK) {
             db->gameAltered = true;
             return TCL_OK;
@@ -5179,7 +4927,7 @@ sc_move_addUCI (ClientData, Tcl_Interp *
       Position * pos = db->game->GetCurrentPos();
       errorT err = pos->ReadCoordMove(&sm, s, s[4] == 0 ? 4 : 5, true);
       if (err == OK) {
-        err = db->game->AddMove(&sm);
+        err = db->game->AddMove(sm);
         if (err == OK) {
             db->gameAltered = true;
             db->game->GetPrevSAN (tmp);
@@ -5245,14 +4993,17 @@ sc_move_forward (ClientData, Tcl_Interp
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // sc_move_pgn:
-//    Set the current board to the position closest to
+//    Get or set the current board to the position closest to
 //    the specified place in the PGN output (given as a byte count
 //    from the start of the output).
 int
 sc_move_pgn (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
 {
+    if (argc == 2)
+        return UI_Result(ti, OK, db->game->GetLocationInPGN());
+
     if (argc != 3) {
-        return errorResult (ti, "Usage: sc_move pgn <offset>");
+        return errorResult (ti, "Usage: sc_move pgn [offset]");
     }
 
     uint offset = strGetUnsigned (argv[2]);
@@ -5274,7 +5025,7 @@ sc_pos (ClientData cd, Tcl_Interp * ti,
         "isAt", "isCheck", "isLegal", "isPromotion",
         "matchMoves", "moveNumber", "pgnOffset",
         "probe", "setComment", "side", "tex", "moves", "location",
-        "attacks", "getPrevComment", NULL
+        "attacks", "getPrevComment", "coordToSAN", NULL
     };
     enum {
         POS_ADDNAG, POS_ANALYZE, POS_BESTSQ, POS_BOARD, POS_CLEARNAGS,
@@ -5282,7 +5033,7 @@ sc_pos (ClientData cd, Tcl_Interp * ti,
         POS_ISAT, POS_ISCHECK, POS_ISLEGAL, POS_ISPROMO,
         POS_MATCHMOVES, POS_MOVENUM, POS_PGNOFFSET,
         POS_PROBE, POS_SETCOMMENT, POS_SIDE, POS_TEX, POS_MOVES, LOCATION,
-        POS_ATTACKS, POS_GETPREVCOMMENT
+        POS_ATTACKS, POS_GETPREVCOMMENT, POS_COORDTOSAN
     };
 
     char boardStr[200];
@@ -5300,9 +5051,33 @@ sc_pos (ClientData cd, Tcl_Interp * ti,
         return sc_pos_bestSquare (cd, ti, argc, argv);
 
     case POS_BOARD:
-        db->game->GetCurrentPos()->MakeLongStr (boardStr);
-        Tcl_AppendResult (ti, boardStr, NULL);
-        break;
+        if (argc == 2) {
+            db->game->currentPos()->MakeLongStr(boardStr);
+            return UI_Result(ti, OK, boardStr);
+        }
+        if (argc == 4) {
+            Position pos;
+            if (auto err = pos.ReadFromFENorUCI(argv[2]))
+                return UI_Result(ti, err);
+
+            auto game = Game();
+            game.SetStartPos(pos);
+            if (const auto len = std::strlen(argv[3])) {
+                PgnParseLog pgn;
+                if (!pgnParseGame(argv[3], len, game, pgn))
+                    return UI_Result(ti, ERROR_InvalidMove);
+            }
+
+            game.MoveToEnd();
+            game.currentPos()->MakeLongStr(boardStr);
+            char lastmove[8] = {};
+            game.GetPrevMoveUCI(lastmove);
+            UI_List result(2);
+            result.push_back(boardStr);
+            result.push_back(lastmove);
+            return UI_Result(ti, OK, result);
+        }
+        return UI_Result(ti, ERROR_BadArg, "sc_pos board [startpos moves]");
 
     case POS_CLEARNAGS:
         db->game->ClearNags();
@@ -5408,6 +5183,21 @@ sc_pos (ClientData cd, Tcl_Interp * ti,
         }
         break;
 
+    case POS_COORDTOSAN: {
+        if (argc != 4)
+            return UI_Result(ti, ERROR_BadArg,
+                             "Usage: sc_pos coordToSAN position moves");
+
+        Position pos;
+        if (auto err = pos.ReadFromFENorUCI(argv[2]))
+            return UI_Result(ti, err);
+
+        std::string sanMoves;
+        auto res = pos.MakeCoordMoves(argv[3], std::strlen(argv[3]), &sanMoves);
+        return UI_Result(ti, res, sanMoves);
+
+        }
+
     default:
         return InvalidCommand (ti, "sc_pos", options);
     }
@@ -5518,19 +5308,6 @@ sc_pos_analyze (ClientData, Tcl_Interp *
     return UI_Result(ti, OK, res);
 }
 
-// Lambda class used by sc_pos_bestSquare() and sc_pos_isLegal()
-namespace {
-class SelectBySquare {
-	squareT sq_;
-
-public:
-	explicit SelectBySquare(squareT sq) : sq_(sq) {}
-	bool operator()(const simpleMoveT& sm) {
-		return sm.from == sq_ || sm.to == sq_;
-	}
-};
-}
-
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // sc_pos_bestSquare:
 //    Takes a square and returns the best square that makes a move
@@ -5562,9 +5339,10 @@ sc_pos_bestSquare (ClientData, Tcl_Inter
 
     // Restrict the list of legal moves to contain only those that
     // move to or from the specified square:
-    mlist.resize(std::distance(mlist.begin(),
-                 std::partition(mlist.begin(), mlist.end(), SelectBySquare(sq))
-    ));
+    auto end = std::remove_if(mlist.begin(), mlist.end(), [&](auto const& sm) {
+        return sm.from != sq && sm.to != sq;
+    });
+    mlist.resize(std::distance(mlist.begin(), end));
 
     // If no matching legal moves, return -1:
     if (mlist.Size() == 0) {
@@ -5583,7 +5361,7 @@ sc_pos_bestSquare (ClientData, Tcl_Inter
         ecoT secondBestEco = ECO_None;
         if (ecoBook != NULL) {
             for (uint i=0; i < mlist.Size(); i++) {
-                pos->DoSimpleMove (mlist.Get(i));
+                pos->DoSimpleMove(*mlist.Get(i));
                 ecoT eco = ecoBook->findECO(pos);
                 pos->UndoSimpleMove (mlist.Get(i));
                 if (eco >= bestEco) {
@@ -5749,7 +5527,7 @@ int
 sc_pos_isPromo (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
 {
     if (argc != 4) {
-        return errorResult (ti, "Usage: sc_move isPromo <square> <square>");
+        return errorResult (ti, "Usage: sc_pos isPromotion <square> <square>");
     }
 
     Position * pos = db->game->GetCurrentPos();
@@ -5757,7 +5535,7 @@ sc_pos_isPromo (ClientData, Tcl_Interp *
     int toSq = strGetInteger (argv[3]);
 
     if (fromSq < A1  ||  fromSq > H8  ||  toSq < A1  ||  toSq > H8) {
-        return errorResult (ti, "Usage: sc_move isPromo <square> <square>");
+        return errorResult (ti, "Usage: sc_pos isPromotion <square> <square>");
     }
 
     return UI_Result(ti, OK, pos->IsPromoMove ((squareT) fromSq, (squareT) toSq));
@@ -5773,21 +5551,18 @@ sc_pos_isLegal (ClientData, Tcl_Interp *
         return errorResult (ti, "Usage: sc_pos isLegal <square> <square>");
     }
 
-    Position * pos = db->game->GetCurrentPos();
     int sq1 = strGetInteger (argv[2]);
     int sq2 = strGetInteger (argv[3]);
     if (sq1 < 0  ||  sq1 > 63  ||  sq2 < 0  ||  sq2 > 63) {
         return UI_Result(ti, OK, false);
     }
 
-    // Compute all legal moves, then restrict the list to only
-    // contain moves that include sq1 and sq2 as to/from squares:
-    MoveList mlist;
-    pos->GenerateMoves(&mlist);
-    simpleMoveT* end1 = std::partition(mlist.begin(), mlist.end(), SelectBySquare(sq1));
-    bool found = mlist.begin() !=
-                 std::partition(mlist.begin(), end1, SelectBySquare(sq2));
-    return UI_Result(ti, OK, found);
+    auto pos = db->game->GetCurrentPos();
+    bool legal = pos->IsLegalMove(sq1, sq2, EMPTY) ||
+                 pos->IsLegalMove(sq2, sq1, EMPTY) ||
+                 pos->IsLegalMove(sq1, sq2, QUEEN) ||
+                 pos->IsLegalMove(sq2, sq1, QUEEN);
+    return UI_Result(ti, OK, legal);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -5800,6 +5575,26 @@ sc_pos_isLegal (ClientData, Tcl_Interp *
 int
 sc_pos_matchMoves (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
 {
+	// copies original to target, filtering out any characters in excludeChars.
+	auto strCopyExclude = [](char* target, const char* original,
+	                         const char* excludeChars) {
+		while (*original != 0) {
+			int exclude = 0;
+			for (auto s = excludeChars; *s; s++) {
+				if (*original == *s) {
+					exclude = 1;
+					break;
+				}
+			}
+			if (!exclude) {
+				*target = *original;
+				target++;
+			}
+			original++;
+		}
+		*target = 0;
+	};
+
     if (argc != 3  &&  argc != 4) {
         return errorResult (ti, "Usage: sc_pos matchMoves <movetext-prefix>");
     }
@@ -5846,16 +5641,7 @@ sc_pos_matchMoves (ClientData, Tcl_Inter
         p->GenerateMoves(&mList);
         for (uint i=0; i < mList.Size(); i++) {
             simpleMoveT * sm = mList.Get(i);
-            str[0] = square_FyleChar (sm->from);
-            str[1] = square_RankChar (sm->from);
-            str[2] = square_FyleChar (sm->to);
-            str[3] = square_RankChar (sm->to);
-            if (sm->promote == EMPTY) {
-                str[4] = 0;
-            } else {
-                str[4] = piece_Char (sm->promote);
-                str[5] = 0;
-            }
+            *sm->toLongNotation(str) = '\0';
             if (strIsPrefix (prefix, str)) {
                 Tcl_AppendElement (ti, str);
             }
@@ -5876,7 +5662,7 @@ sc_pos_moves(ClientData, Tcl_Interp * ti
     }
     Position * p = db->game->GetCurrentPos();
     sanListT sanList; 
-    p->CalcSANStrings (&sanList, SAN_NO_CHECKTEST);
+    p->CalcSANStrings (&sanList, SAN_CHECKTEST);
 
     for (uint i=0; i < sanList.num; i++) {
             Tcl_AppendElement (ti, sanList.list[i]);
@@ -6940,7 +6726,7 @@ sc_name_match (ClientData, Tcl_Interp *
         appendUintElement (ti, freq);
         Tcl_AppendElement (ti, str);
         if (nt == NAME_PLAYER  &&  eloMode) {
-            appendUintElement (ti, db->getNameBase()->GetElo (nameID));
+            appendUintElement (ti, db->peakElo(nameID));
         }
     }
     return TCL_OK;
@@ -6972,11 +6758,10 @@ public:
 	}
     bool operator() (idNumberT p1, idNumberT p2)
     {
-        const NameBase* nb = dbase_->getNameBase();
         int compare = 0;
         switch (sort_) {
         case SORT_ELO:
-            compare = nb->GetElo(p2) - nb->GetElo(p1);
+            compare = dbase_->peakElo(p2) - dbase_->peakElo(p1);
             break;
         case SORT_GAMES:
             compare = dbase_->getNameFreq(NAME_PLAYER, p2) - dbase_->getNameFreq(NAME_PLAYER, p1);
@@ -6994,6 +6779,7 @@ public:
         // If equal, resolve by comparing names, first case-insensitive and
         // then case-sensitively if still tied:
         if (compare == 0) {
+            const NameBase* nb = dbase_->getNameBase();
             const char* name1 = nb->GetName (NAME_PLAYER, p1);
             const char* name2 = nb->GetName (NAME_PLAYER, p2);
             compare = strCaseCompare (name1, name2);
@@ -7067,8 +6853,8 @@ sc_name_plist (ClientData, Tcl_Interp *
     std::vector<idNumberT> plist;
     for (idNumberT id = 0; id < nPlayers; id++) {
         const char * name = nb->GetName (NAME_PLAYER, id);
-        uint nGames = db->getNameFreq (NAME_PLAYER, id);
-        eloT elo = nb->GetElo (id);
+        uint nGames = dbase->getNameFreq(NAME_PLAYER, id);
+        eloT elo = dbase->peakElo(id);
         if (nGames < minGames  ||  nGames > maxGames) { continue; }
         if (elo < minElo  ||  elo > maxElo) { continue; }
         if (! strIsCasePrefix (namePrefix, name)) { continue; }
@@ -7096,7 +6882,7 @@ sc_name_plist (ClientData, Tcl_Interp *
         info.push_back(dbase->getNameFreq(NAME_PLAYER, id));
         info.push_back(date_GetYear(activity[id].firstDate));
         info.push_back(date_GetYear(activity[id].lastDate));
-        info.push_back(nb->GetElo(id));
+        info.push_back(dbase->peakElo(id));
         info.push_back(nb->GetName(NAME_PLAYER, id));
         res.push_back(info);
     }
@@ -7244,7 +7030,7 @@ sc_name_read (ClientData, Tcl_Interp * t
 
     UI_List res(NUM_NAME_TYPES);
     for (nameT i = 0; i < NUM_NAME_TYPES; i++) {
-        uint n = (spellChk == NULL) ? 0 : spellChk->numCorrectNames(i);
+        size_t n = (spellChk == NULL) ? 0 : spellChk->numCorrectNames(i);
         res.push_back(n);
     }
     return UI_Result(ti, OK, res);
@@ -7796,13 +7582,9 @@ sc_report_create (ClientData, Tcl_Interp
         byte ply = db->dbFilter->Get(gnum);
         const IndexEntry* ie = db->getIndexEntry(gnum);
         if (ply != 0) {
-            if (db->getGame(ie, db->bbuf) != OK) {
+            if (db->getGame(*ie, *scratchGame) != OK) {
                 return errorResult (ti, "Error reading game file.");
             }
-            if (scratchGame->Decode (db->bbuf, GAME_DECODE_ALL) != OK) {
-                return errorResult (ti, "Error decoding game.");
-            }
-            scratchGame->LoadStandardTags (ie, db->getNameBase());
             scratchGame->MoveToPly (ply - 1);
             if (scratchGame->AtEnd()) ply = 0;
             if (ply != 0) {
@@ -7870,30 +7652,18 @@ int
 sc_tree (ClientData cd, Tcl_Interp * ti, int argc, const char ** argv)
 {
     static const char * options [] = {
-        "best", "move", "positions", "search", "size",
-        "cachesize", "cacheinfo", NULL
+        "stats", "cachesize", "cacheinfo", NULL
     };
     enum {
-        TREE_BEST, TREE_MOVE, TREE_POSITIONS, TREE_SEARCH, TREE_SIZE,
-        TREE_CACHESIZE, TREE_CACHEINFO
+        TREE_STATS, TREE_CACHESIZE, TREE_CACHEINFO
     };
 
     int index = -1;
     if (argc > 1) { index = strUniqueMatch (argv[1], options); }
 
     switch (index) {
-    case TREE_MOVE:
-        return sc_tree_move (cd, ti, argc, argv);
-
-    case TREE_POSITIONS:
-        // Return the number of positions cached:
-        return setUintResult (ti, db->treeCache.UsedSize());
-
-    case TREE_SEARCH:
-        return sc_tree_search (cd, ti, argc, argv);
-
-    case TREE_SIZE:
-        return setUintResult (ti, db->treeCache.Size());
+    case TREE_STATS:
+        return sc_tree_stats(cd, ti, argc, argv);
 
     case TREE_CACHESIZE:
         return sc_tree_cachesize (cd, ti, argc, argv);
@@ -7908,545 +7678,175 @@ sc_tree (ClientData cd, Tcl_Interp * ti,
     return TCL_OK;
 }
 
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sc_tree_move:
-//    Returns the move for a tree line.
-//    Arg can be in the range [1.. numTreeLines].
-//    It can also be "random" to request a random move selected
-//    according to the frequency of each move in the tree.
-int
-sc_tree_move (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
-{
-    if (argc != 4) {
-        return errorResult (ti, "Usage: sc_tree move <baseNum> <lineNum>");
-    }
-
-    const scidBaseT* base = DBasePool::getBase(strGetUnsigned(argv[2]));
-    if (base == 0) return UI_Result(ti, ERROR_FileNotOpen);
-
-    int selection = strGetInteger (argv[3]);
-    if (argv[3][0] == 'r'  &&  strIsPrefix (argv[3], "random")) {
-        uint total = base->tree.totalCount;
-        if (total == 0) { return TCL_OK; }
-        uint r = rand() % total;
-        uint sum = 0;
-        for (uint i=0; i < base->tree.moveCount; i++) {
-            sum += base->tree.node[i].total;
-            if (r <= sum) {
-                selection = i + 1;
-                break;
-            }
-        }
-    }
-
-    if (selection < 1  ||  selection > (int)(base->tree.moveCount)) {
-        // Not a valid selection. We ignore it (e.g. the user clicked on a
-        // line with no move on it).
-        return TCL_OK;
-    }
-
-    const treeNodeT* node = &(base->tree.node[selection - 1]);
-
-    // If the san string first char is not a letter, it is the
-    // empty move (e.g. "[end]") so we do NOT add a move:
-    if (! isalpha(node->san[0])) {
-        return TCL_OK;
-    }
-
-    Tcl_AppendResult (ti, node->san, NULL);
-    return TCL_OK;
-}
-
-// Enumeration of possible move-sorting methods for tree mode:
-enum moveSortE { SORT_ALPHA, SORT_ECO, SORT_FREQUENCY, SORT_SCORE };
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sortTreeMoves():
-//    Sorts the moves of a tree node according to a specified order.
-//    Slow sort method, but the typical number of moves is under 20
-//    so it is easily fast enough.
-void
-sortTreeMoves (treeT * tree, int sortMethod, colorT toMove)
-{
-    // Only sort if there are at least two moves in the tree node:
-    if (tree->moveCount <= 1) { return; }
-
-    for (uint outer=0; outer < tree->moveCount - 1; outer++) {
-        for (uint inner=outer+1; inner < tree->moveCount; inner++) {
-            int result = 0;
-
-            switch (sortMethod) {
-            case SORT_FREQUENCY:  // Most frequent moves first:
-                result = tree->node[outer].total - tree->node[inner].total;
-                break;
-
-            case SORT_ALPHA:  // Alphabetical order:
-                result = strCompare (tree->node[inner].san,
-                                     tree->node[outer].san);
-                break;
-
-            case SORT_ECO:  // ECO code order:
-                result = tree->node[outer].ecoCode - tree->node[inner].ecoCode;
-                break;
-
-            case SORT_SCORE:  // Order by success:
-                result = tree->node[outer].score - tree->node[inner].score;
-                if (toMove == BLACK) { result = -result; }
-                break;
-
-            default:  // Unreachable:
-                return;
-            }
-
-            if (result < 0) {
-                // Swap the nodes:
-                treeNodeT temp = tree->node[outer];
-                tree->node[outer] = tree->node[inner];
-                tree->node[inner] = temp;
-            }
-        }  // for (inner)
-    } // for (outer)
-    return;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sc_tree_search:
-//    Returns the tree for the current position
+// @returns the tree stats of the specified filter
 int
-sc_tree_search (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
+sc_tree_stats (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
 {
-    static const char * usageStr =
-      "Usage: sc_tree search [-hideMoves <0|1>] [-sort alpha|eco|frequency|score]";
+    static const char * usage =
+      "Usage: sc_tree stats baseId filterId [<0|1>] [alpha|eco|frequency|score]";
 
     // Sort options: these should match the moveSortE enumerated type.
     static const char * sortOptions[] = {
         "alpha", "eco", "frequency", "score", NULL
     };
+    // Enumeration of possible move-sorting methods for tree mode:
+    enum moveSortE { SORT_ALPHA, SORT_ECO, SORT_FREQUENCY, SORT_SCORE };
 
-    char tempTrans[10];
-    bool hideMoves = false;
-    const bool listMode = false;
-    bool inFilterOnly = false;
-    int sortMethod = SORT_FREQUENCY; // default move order: frequency
-
-    scidBaseT * base = db;
-    static std::set<scidBaseT**> search_pool;
-
-    // Check that there is an even number of optional arguments and
-    // parse them as option-value pairs:
-    int arg = 2;
-    int argsLeft = (argc - arg);
-    if (argsLeft % 2 != 0) { return errorResult (ti, usageStr); }
+    if (argc < 4)
+        return UI_Result(ti, ERROR_BadArg, usage);
 
-    while (arg < argc) {
-        if (strIsPrefix (argv[arg], "-sort")) {
-            sortMethod = strUniqueMatch (argv[arg+1], sortOptions);
-        } else if (strIsPrefix (argv[arg], "-hideMoves")) {
-            hideMoves = strGetBoolean (argv[arg+1]);
-        } else if (strIsPrefix (argv[arg], "-base")) {
-            base = DBasePool::getBase(strGetUnsigned(argv[arg+1]));
-            if (base == 0) return UI_Result(ti, ERROR_FileNotOpen);
-        } else if (strIsPrefix (argv[arg], "-filtered")) {
-            inFilterOnly = strGetBoolean (argv[arg+1]);
-        } else if (strIsPrefix (argv[arg], "-cancel")) {
-        	search_pool.clear();
-        	return TCL_OK;
-        } else {
-            return errorResult (ti, usageStr);
+    scidBaseT* base = DBasePool::getBase(strGetUnsigned(argv[2]));
+    if (!base)
+        return UI_Result(ti, ERROR_BadArg, usage);
+
+    HFilter filter = base->getFilter(argv[3]);
+    if (filter == nullptr)
+        return UI_Result(ti, ERROR_BadArg, usage);
+
+    bool hideMoves = (argc > 4) ? strGetBoolean(argv[4]) : false;
+    int sortMethod = (argc > 5) ? strUniqueMatch(argv[5], sortOptions)
+                                : SORT_FREQUENCY;
+    if (sortMethod < 0)
+        return UI_Result(ti, ERROR_BadArg, usage);
+
+    Position searchPos = *(db->game->GetCurrentPos());
+    auto tree = base->getTreeStat(filter);
+
+    auto calc_eco = [&](auto const& move) {
+        ecoT eco = ECO_None;
+        if (ecoBook && move) {
+            simpleMoveT sm;
+            if (move.isCastle()) {
+                auto side = move.getTo() > move.getFrom() ? KING : QUEEN;
+                searchPos.makeMove(move.getFrom(), move.getFrom(), side, sm);
+            } else {
+                auto promo = move.isPromo() ? move.getPromo() : INVALID_PIECE;
+                searchPos.makeMove(move.getFrom(), move.getTo(), promo, sm);
+            }
+            searchPos.DoSimpleMove(sm);
+            eco = ecoBook->findECO(&searchPos);
+            searchPos.UndoSimpleMove(&sm);
         }
-        arg += 2;
-    }
-
-    if (sortMethod < 0) { return errorResult (ti, usageStr); }
-    if (!base->inUse) { return UI_Result(ti, ERROR_FileNotOpen);  }
-
-    search_pool.insert(&base);
-    base->treeFilter->Fill(0);
-    const HFilter filter = base->getFilter("dbfilter");
-
-    Progress progress = UI_CreateProgress(ti);
-    Timer timer;  // Start timing this search.
-    uint skipcount = 0;
+        return eco;
+    };
 
-    // 1. Cache Search
-    bool foundInCache = false;
-    // #TODO: cache even filtered searches (requires a filter hash)
-    if (! inFilterOnly) {
-        // Lookup the cache before searching:
-        const cachedTreeT* pct = base->treeCache.Lookup (db->game->GetCurrentPos());
-        if (pct != NULL) {
-            // It was in the cache! Use it to save time:
-                if (pct->restoreFilter(base->treeFilter) == OK) {
-                    base->tree = pct->getTree();
-                    foundInCache = true;
-                }
-        }
-    }
-
-    if (!foundInCache) {
-        // OK, not in the cache so do the search:
-        // 2. Set vars
-        Position* pos = db->game->GetCurrentPos();
-        treeT* tree = &(base->tree);
-        tree->moveCount = tree->totalCount = 0;
-        matSigT msig = matsig_Make (pos->GetMaterial());
-        uint hpSig = pos->GetHPSig();
-        simpleMoveT sm;
-        base->treeFilter->Fill (0); // Reset the filter to be empty
-        skipcount = 0;
-
-    	// 3. Set up the stored line code matches:
-    	StoredLine stored_line(pos->GetBoard(), pos->GetToMove());
-
-    	// 4. Search through each game:
-    	for (uint i=0, n = base->numGames(); i < n; i++) {
-            if ((i % 5000) == 0) {  // Update the percentage done slider:
-                if (!progress.report(i,n) || search_pool.count(&base) == 0) {
-                    return setResult (ti, "canceled");
-                }
-            }
-
-            if (inFilterOnly && filter.get(i) == 0) { continue; }
-
-    		const IndexEntry* ie = base->getIndexEntry(i);
-    		if (ie->GetLength() == 0) { skipcount++; continue; }
-    		// We do not skip deleted games, so next line is commented out:
-    		// if (ie->GetDeleteFlag()) { skipcount++; continue; }
-
-    		bool foundMatch = false;
-
-            // Check the stored line result for this game:
-            int ply = stored_line.match(ie->GetStoredLineCode());
-            if (ply < -1) { skipcount++; continue; }
-            if (ply >= 0) {
-                FullMove m = StoredLine::getMove(ie->GetStoredLineCode(), ply);
-                if (m) {
-                    sm.from = m.getFrom();
-                    sm.to = m.getTo();
-                    if (m.isCastle()) sm.to = sm.from + ((sm.to > sm.from) ? 2 : -2);
-                    sm.promote = EMPTY;
-                    ply += 1;
-                    foundMatch = true;
-                }
-            }
-            if (!foundMatch) {
-                const pieceT* bd = pos->GetBoard();
-                bool isStartPos =
-                    (bd[A1]==WR  &&  bd[B1]==WN  &&  bd[C1]==WB  &&  bd[D1]==WQ  &&
-                     bd[E1]==WK  &&  bd[F1]==WB  &&  bd[G1]==WN  &&  bd[H1]==WR  &&
-                     bd[A2]==WP  &&  bd[B2]==WP  &&  bd[C2]==WP  &&  bd[D2]==WP  &&
-                     bd[E2]==WP  &&  bd[F2]==WP  &&  bd[G2]==WP  &&  bd[H2]==WP  &&
-                     bd[A7]==BP  &&  bd[B7]==BP  &&  bd[C7]==BP  &&  bd[D7]==BP  &&
-                     bd[E7]==BP  &&  bd[F7]==BP  &&  bd[G7]==BP  &&  bd[H7]==BP  &&
-                     bd[A8]==BR  &&  bd[B8]==BN  &&  bd[C8]==BB  &&  bd[D8]==BQ  &&
-                     bd[E8]==BK  &&  bd[F8]==BB  &&  bd[G8]==BN  &&  bd[H8]==BR);
-                if (!isStartPos  &&  ie->GetNumHalfMoves() == 0) {
-                    skipcount++;
-                    continue;
-                }
+    char tempTrans[10];
+    auto calc_san = [&](auto const& move) {
+        strcpy(tempTrans, move ? move.getSAN().c_str() : "[end]");
+        transPieces(tempTrans);
+    };
 
-                if (! ie->GetStartFlag()) {
-                    // Speedups that only apply to standard start games:
-                    if (hpSig != HPSIG_StdStart) { // Not the start mask
-                        if (! hpSig_PossibleMatch(hpSig, ie->GetHomePawnData())) { continue; }
-                    }
-                }
+    if (sortMethod == SORT_ALPHA) { // icase alphabetical order
+        std::sort(tree.begin(), tree.end(), [&](auto const& a, auto const& b) {
+            calc_san(a.move);
+            std::string temp = tempTrans;
+            calc_san(b.move);
+            return temp < std::string_view(tempTrans);
+        });
 
-                if (msig != MATSIG_StdStart
-                    &&  !matsig_isReachable (msig, ie->GetFinalMatSig(),
-                                ie->GetPromotionsFlag(),
-                                ie->GetUnderPromoFlag()))
-                {
-                    skipcount++;
-                    continue;
-                }
+    } else if (sortMethod == SORT_ECO) { // Order by eco code
+        std::sort(tree.begin(), tree.end(), [&](auto const& a, auto const& b) {
+            return calc_eco(a.move) < calc_eco(b.move);
+        });
 
-                if (base->getGame(ie, base->bbuf) != OK) {
-                    search_pool.erase(&base);
-                    return errorResult (ti, "Error reading game file.");
-    			}
-    			Game *g = scratchGame;
-    			if (g->ExactMatch (pos, base->bbuf, &sm)) {
-    				ply = g->GetCurrentPly() + 1;
-    				foundMatch = true;
-    			}
-    		}
-
-    		// If match was found, add it to the list of found moves:
-    		if (foundMatch) {
-    			if (ply > 255) { ply = 255; }
-    			base->treeFilter->Set (i, (byte) ply);
-    			uint search;
-    			treeNodeT* node = tree->node;
-    			for (search = 0; search < tree->moveCount; search++, node++) {
-    				if (sm.from == node->sm.from
-    						&&  sm.to == node->sm.to
-    						&&  sm.promote == node->sm.promote) {
-    					break;
-    				}
-    			}
-
-    			// Now node is the node to update or add.
-    			// Check for exceeding max number of nodes:
-    			if (search >= MAX_TREE_NODES) {
-    				search_pool.erase(&base);
-    				return errorResult (ti, "Too many moves.");
-    			}
-
-    			if (search == tree->moveCount) {
-    				// A new move to add:
-    				initTreeNode (node);
-    				node->sm = sm;
-    				if (sm.from == NULL_SQUARE) {
-    					strCopy(node->san, "[end]");
-    				} else {
-    					pos->MakeSANString (&sm, node->san, SAN_CHECKTEST);
-    				}
-    				tree->moveCount++;
-    			}
-    			node->total++;
-    			node->freq[ie->GetResult()]++;
-    			eloT elo = 0;
-    			eloT oppElo = 0;
-    			uint year = ie->GetYear();
-    			if (pos->GetToMove() == WHITE) {
-    				elo = ie->GetWhiteElo();
-    				oppElo = ie->GetBlackElo();
-    			} else {
-    				elo = ie->GetBlackElo();
-    				oppElo = ie->GetWhiteElo();
-    			}
-    			if (elo > 0) {
-    				node->eloSum += elo;
-    				node->eloCount++;
-    			}
-    			if (oppElo > 0) {
-    				node->perfSum += oppElo;
-    				node->perfCount++;
-    			}
-    			if (year != 0) {
-    				node->yearSum += year;
-    				node->yearCount++;
-    			}
-    			tree->totalCount++;
-    		} // end: if (foundMatch) ...
-    	} // end: for
-    }
-
-    treeT* tree = &(base->tree);
-    treeNodeT* node = tree->node;
-    if (!foundInCache) {
-        // Now we generate the score of each move: it is the expected score per
-        // 1000 games. Also generate the ECO code of each move.
-
-        for (uint i=0; i < tree->moveCount; i++, node++) {
-            node->score = (node->freq[RESULT_White] * 2
-                    + node->freq[RESULT_Draw] + node->freq[RESULT_None])
-                    * 500 / node->total;
-
-            node->ecoCode = 0;
-            if (ecoBook != NULL) {
-                Position tmpPos = *(db->game->GetCurrentPos());
-                if (node->sm.from != NULL_SQUARE) {
-                    tmpPos.DoSimpleMove (&(node->sm));
-                }
-                ecoT eco = ecoBook->findECO(&tmpPos);
-                if (eco != ECO_None)
-                    node->ecoCode = eco;
-            }
-        }
-
-        // Now we sort the move list:
-        sortTreeMoves (tree, sortMethod, db->game->GetCurrentPos()->GetToMove());
-
-        // If it wasn't in the cache, maybe it belongs there:
-        if (!foundInCache  && !inFilterOnly) {
-            base->treeCache.Add (db->game->GetCurrentPos(), tree, base->treeFilter);
+    } else if (sortMethod == SORT_SCORE) { // Order by success
+        if (searchPos.GetToMove() == WHITE) {
+            std::sort(tree.begin(), tree.end(),
+                      [&](auto const& a, auto const& b) {
+                          return a.score() > b.score();
+                      });
+        } else {
+            std::sort(tree.begin(), tree.end(),
+                      [&](auto const& a, auto const& b) {
+                          return a.score() < b.score();
+                      });
         }
     }
 
-    progress.report(1,1);
-    search_pool.erase(&base);
-
-    DString * output = new DString;
-    char temp [200];
-    if (! listMode) {
-        const char * titleRow =
-            "    Move   ECO       Frequency    Score  AvElo Perf AvYear %Draws";
-        titleRow = translate (ti, "TreeTitleRow", titleRow);
-        output->Append (titleRow);
-    }
-
-    // Now we print the list into the return string:
-    node = tree->node;
-    for (uint count=0; count < tree->moveCount; count++, node++) {
-        ecoStringT ecoStr;
-        eco_ToExtendedString (node->ecoCode, ecoStr);
-        uint avgElo = 0;
-        if (node->eloCount >= 1)
-        	avgElo = node->eloSum / node->eloCount;
-
-        uint perf = 0;
-        if (node->perfCount >= 10) {
-            perf = node->perfSum / node->perfCount;
-            uint score = (node->score + 5) / 10;
-            if (db->game->GetCurrentPos()->GetToMove() == BLACK) { score = 100 - score; }
-            perf = Crosstable::Performance (perf, score);
-        }
-        uint avgYear = 0;
-        if (node->yearCount > 0) {
-            avgYear = (node->yearSum + (node->yearCount/2)) / node->yearCount;
-        }
-        node->san[6] = 0;
-
-        strcpy(tempTrans, node->san);
-        transPieces(tempTrans);
-
-        if (listMode) {
-            if (ecoStr[0] == 0) { strCopy (ecoStr, "{}"); }
-            sprintf (temp, "%2u %-6s %-5s %7u %3d%c%1d %3d%c%1d",
-                     count + 1,
-                     hideMoves ? "---" : tempTrans,//node->san,
-                     hideMoves ? "{}" : ecoStr,
-                     node->total,
-                     100 * node->total / tree->totalCount,
-                     decimalPointChar,
-                     (1000 * node->total / tree->totalCount) % 10,
-                     node->score / 10,
-                     decimalPointChar,
-                     node->score % 10);
-            output->Append (temp);
-        } else {
-            sprintf (temp, "\n%2u: %-6s %-5s %7u:%3d%c%1d%%  %3d%c%1d%%",
-                     count + 1,
-                     hideMoves ? "---" : tempTrans,//node->san,
-                     hideMoves ? "" : ecoStr,
-                     node->total,
-                     100 * node->total / tree->totalCount,
-                     decimalPointChar,
-                     (1000 * node->total / tree->totalCount) % 10,
-                     node->score / 10,
-                     decimalPointChar,
-                     node->score % 10);
-            output->Append (temp);
-        }
+    char temp[256];
+    std::string output;
+    const char * titleRow =
+        "    Move   ECO       Frequency    Score  AvElo Perf AvYear %Draws";
+    titleRow = translate (ti, "TreeTitleRow", titleRow);
+    output.append(titleRow);
+
+    auto format_output = [&](auto const& node, auto& dest) {
+        const auto avgElo = static_cast<int>(node.avgElo());
+        const auto avgYear = static_cast<int>(node.avgYear());
+        const auto pctDraws = static_cast<int>(node.percDraws());
+        const auto score = node.score();
+        const auto perf = node.eloCount < 10
+                              ? 0
+                              : static_cast<int>(node.eloPerformance());
 
+        sprintf(temp, "  %3d%c%1d%%", score / 10, decimalPointChar, score % 10);
+        dest.append(temp);
         if (avgElo == 0) {
-            strCopy (temp, listMode ? " {}" : "      ");
+            dest.append("      ");
         } else {
-            sprintf (temp, "  %4u", avgElo);
+            sprintf(temp, "  %4d", avgElo);
+            dest.append(temp);
         }
-        output->Append (temp);
         if (perf == 0) {
-            strCopy (temp, listMode ? " {}" : "      ");
+            dest.append("      ");
         } else {
-            sprintf (temp, "  %4u", perf);
+            sprintf(temp, "  %4d", perf);
+            dest.append(temp);
         }
-        output->Append (temp);
         if (avgYear == 0) {
-            strCopy (temp, listMode ? " {}" : "      ");
+            dest.append("      ");
         } else {
-            sprintf (temp, "  %4u", avgYear);
+            sprintf(temp, "  %4d", avgYear);
+            dest.append(temp);
         }
-        output->Append (temp);
-        uint pctDraws = node->freq[RESULT_Draw] * 1000 / node->total;
-        sprintf (temp, "  %3d%%", (pctDraws + 5) / 10);
-        output->Append (temp);
-
-        if (listMode) {
-            Tcl_AppendElement (ti, (char *) output->Data());
-            output->Clear();
-        }
-    }
+        sprintf(temp, "  %3d%%", pctDraws);
+        dest.append(temp);
+    };
 
-    // Print a totals line as well, if there are any moves in the tree:
-
-    if (tree->moveCount > 0) {
-        int totalScore = 0;
-        uint64_t eloSum = 0;
-        uint64_t eloCount = 0;
-        uint64_t perfSum = 0;
-        uint64_t perfCount = 0;
-        uint64_t yearCount = 0;
-        uint64_t yearSum = 0;
-        uint nDraws = 0;
-        node = tree->node;
-        for (uint count=0; count < tree->moveCount; count++, node++) {
-            totalScore += node->freq[RESULT_White] * 2;
-            totalScore += node->freq[RESULT_Draw] + node->freq[RESULT_None];
-            eloCount += node->eloCount;
-            eloSum += node->eloSum;
-            perfCount += node->perfCount;
-            perfSum += node->perfSum;
-            yearCount += node->yearCount;
-            yearSum += node->yearSum;
-            nDraws += node->freq[RESULT_Draw];
-        }
-        totalScore = totalScore * 500 / tree->totalCount;
-        uint avgElo = 0;
-        if (eloCount >= 10) {
-            avgElo = eloSum / eloCount;
-        }
-        uint perf = 0;
-        if (perfCount >= 10) {
-            perf = perfSum / perfCount;
-            uint score = (totalScore + 5) / 10;
-            if (db->game->GetCurrentPos()->GetToMove() == BLACK) { score = 100 - score; }
-            perf = Crosstable::Performance (perf, score);
-        }
-
-        if (listMode) {
-            sprintf (temp, "%2u %-6s %-5s %7u %3d%c%1d %3d%c%1d",
-                     0,
-                     "TOTAL",
-                     "{}",
-                     tree->totalCount,
-                     100, decimalPointChar, 0,
-                     totalScore / 10, decimalPointChar, totalScore % 10);
-            output->Append (temp);
-        } else {
-            const char * totalString = translate (ti, "TreeTotal:", "TOTAL:");
-            output->Append ("\n_______________________________________________________________\n");
-            sprintf (temp, "%-12s     %7u:100%c0%%  %3d%c%1d%%",
-                     totalString, tree->totalCount, decimalPointChar,
-                     totalScore / 10, decimalPointChar, totalScore % 10);
-            output->Append (temp);
-        }
-        if (avgElo == 0) {
-            output->Append (listMode ? " {}" : "      ");
-        } else {
-            sprintf (temp, "  %4u", avgElo);
-            output->Append (temp);
-        }
-        if (perf == 0) {
-            output->Append (listMode ? " {} " : "      ");
-        } else {
-            sprintf (temp, "  %4u", perf);
-            output->Append (temp);
-        }
-        if (yearCount == 0) {
-            output->Append (listMode ? " {}" : "      ");
-        } else {
-            sprintf (temp, "  %4u", static_cast<uint>((yearSum + (yearCount/2)) / yearCount));
-            output->Append (temp);
-        }
-        uint pctDraws = nDraws * 1000 / tree->totalCount;
-        sprintf (temp, "  %3d%%", (pctDraws + 5) / 10);
-        output->Append (temp);
-        if (listMode) {
-            Tcl_AppendElement (ti, (char *) output->Data());
-            output->Clear();
-        } else {
-            output->Append ("\n");
+    if (tree.size() > 0) {
+        TreeNode totals({});
+        for (auto const& node : tree) { // reduce
+            totals.freq[0] += node.freq[0];
+            totals.freq[RESULT_White] += node.freq[RESULT_White];
+            totals.freq[RESULT_Black] += node.freq[RESULT_Black];
+            totals.freq[RESULT_Draw] += node.freq[RESULT_Draw];
+            totals.eloWhiteSum += node.eloWhiteSum;
+            totals.eloBlackSum += node.eloBlackSum;
+            totals.eloCount += node.eloCount;
+            totals.yearSum += node.yearSum;
+            totals.yearCount += node.yearCount;
+        }
+
+        // Now we print the list into the return string:
+        unsigned count = 0;
+        for (auto const& node : tree) {
+            calc_san(node.move);
+            ecoT eco = calc_eco(node.move);
+            ecoStringT ecoStr;
+            eco_ToExtendedString(eco, ecoStr);
+            auto freq = long(1000ll * node.freq[0] / totals.freq[0]);
+            sprintf (temp, "\n%2u: %-6s %-5s %7u:%3ld%c%1ld%%",
+                     ++count,
+                     hideMoves ? "---" : tempTrans,//node->san,
+                     hideMoves ? "" : ecoStr,
+                     node.freq[0],
+                     freq / 10,
+                     decimalPointChar,
+                     freq % 10);
+            output.append(temp);
+            format_output(node, output);
         }
-    }
 
-    if (! listMode) {
-    	Tcl_AppendResult (ti, output->Data(), NULL);
+        // Print a totals line as well, if there are any moves in the tree:
+        const char * totalString = translate (ti, "TreeTotal:", "TOTAL:");
+        output.append("\n_______________________________________________________________\n");
+        sprintf (temp, "%-12s     %7u:100%c0%%",
+                 totalString, totals.freq[0], decimalPointChar);
+        output.append(temp);
+        format_output(totals, output);
+        output.append("\n");
     }
-    delete output;
-    return TCL_OK;
+    return UI_Result(ti, OK, output);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -8472,14 +7872,10 @@ sc_tree_cacheinfo (ClientData, Tcl_Inter
     return errorResult (ti, "Usage: sc_tree cacheinfo <base>");
   }
   scidBaseT* base = DBasePool::getBase(strGetInteger(argv[2]));
-  if (base) {
-    appendUintElement (ti, base->treeCache.UsedSize());
-    appendUintElement (ti, base->treeCache.Size());
-  } else {
-    appendUintElement (ti, 0);
-    appendUintElement (ti, 0);
-  }
-  return TCL_OK;
+  if (base)
+      return UI_Result(ti, OK, base->treeCache.Size());
+
+  return UI_Result(ti, OK, 0);
 }
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // sc_search:
@@ -8578,15 +7974,21 @@ int sc_search_board(Tcl_Interp* ti, cons
 
     if (flip) {
         posFlip = new Position;
-        char cboard [40];
-        pos->PrintCompactStrFlipped (cboard);
-        posFlip->ReadFromCompactStr ((byte *) cboard);
+        posFlip->Clear();
+        for (auto sq = A1; sq <= H8; ++sq) {
+            const auto piece = pos->GetPiece(sq);
+            if (piece != EMPTY) {
+                const auto sq_flip = square_Relative(BLACK, sq);
+                posFlip->AddPiece(PIECE_FLIP[piece], sq_flip);
+            }
+        }
+        posFlip->SetToMove(color_Flip(pos->GetToMove()));
+        posFlip->SetEPTarget(pos->GetEPTarget());
+        //NOTE: the search ignores the castling flags
         hpSigFlip = posFlip->GetHPSig();
         msigFlip = matsig_Make (posFlip->GetMaterial());
     }
 
-    uint skipcount = 0;
-
     // If filter operation is to reset the filter, reset it:
     if (filterOp == FILTEROP_RESET) {
         filter->includeAll();
@@ -8605,12 +8007,10 @@ int sc_search_board(Tcl_Interp* ti, cons
         // First, apply the filter operation:
         if (filterOp == FILTEROP_AND) {  // Skip any games not in the filter:
             if (filter.get(gameNum) == 0) {
-                skipcount++;
                 continue;
             }
         } else /* filterOp==FILTEROP_OR*/ { // Skip any games in the filter:
             if (filter.get(gameNum) != 0) {
-                skipcount++;
                 continue;
             } else {
                 // OK, this game is NOT in the filter.
@@ -8623,7 +8023,6 @@ int sc_search_board(Tcl_Interp* ti, cons
         if (ie->GetLength() == 0) {
             // Skip games with no gamefile record:
             filter.set (gameNum, 0);
-            skipcount++;
             continue;
         }
 
@@ -8671,36 +8070,36 @@ int sc_search_board(Tcl_Interp* ti, cons
 
         if (!possibleMatch  &&  !possibleFlippedMatch) {
             filter.set (gameNum, 0);
-            skipcount++;
             continue;
         }
 
         // At this point, the game needs to be loaded:
-        if (dbase->getGame(ie, dbase->bbuf) != OK) {
+        auto bbuf = dbase->getGame(*ie);
+        if (!bbuf) {
             return errorResult (ti, "Error reading game file.");
         }
         uint ply = 0;
         if (useVars) {
-            g->Decode (dbase->bbuf, GAME_DECODE_NONE);
+            g->DecodeMovesOnly(bbuf);
             // Try matching the game without variations first:
             if (ply == 0  &&  possibleMatch) {
-                if (g->ExactMatch (pos, NULL, NULL, searchType)) {
+                if (g->ExactMatch (pos, NULL, searchType)) {
                     ply = g->GetCurrentPly() + 1;
                 }
             }
             if (ply == 0  &&  possibleFlippedMatch) {
-                if (g->ExactMatch (posFlip, NULL, NULL, searchType)) {
+                if (g->ExactMatch (posFlip, NULL, searchType)) {
                     ply = g->GetCurrentPly() + 1;
                 }
             }
             if (ply == 0  &&  possibleMatch) {
-                g->MoveToPly (0);
+                g->MoveToStart();
                 if (g->VarExactMatch (pos, searchType)) {
                     ply = g->GetCurrentPly() + 1;
                 }
             }
             if (ply == 0  &&  possibleFlippedMatch) {
-                g->MoveToPly (0);
+                g->MoveToStart();
                 if (g->VarExactMatch (posFlip, searchType)) {
                     ply = g->GetCurrentPly() + 1;
                 }
@@ -8708,14 +8107,14 @@ int sc_search_board(Tcl_Interp* ti, cons
         } else {
             // No searching in variations:
             if (possibleMatch) {
-                if (g->ExactMatch (pos, dbase->bbuf, NULL, searchType)) {
+                auto bbuf_clone = bbuf;
+                if (g->ExactMatch(pos, &bbuf_clone, searchType)) {
                     // Set its auto-load move number to the matching move:
                     ply = g->GetCurrentPly() + 1;
                 }
             }
             if (ply == 0  &&  possibleFlippedMatch) {
-                dbase->bbuf->BackToStart();
-                if (g->ExactMatch (posFlip, dbase->bbuf, NULL, searchType)) {
+                if (g->ExactMatch (posFlip, &bbuf, searchType)) {
                     ply = g->GetCurrentPly() + 1;
                 }
             }
@@ -8738,56 +8137,7 @@ int sc_search_board(Tcl_Interp* ti, cons
              static_cast<unsigned long>(startFilterCount),
              centisecs / 100, decimalPointChar, centisecs % 100);
     Tcl_AppendResult (ti, temp, NULL);
-#ifdef SHOW_SKIPPED_STATS
-    sprintf(temp, "  Skipped %u games.", skipcount);
-    Tcl_AppendResult (ti, temp, NULL);
-#endif
-
-return TCL_OK;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// addPattern():
-//    Called by the parameter parsing section of sc_search_material()
-//    to add a pattern to a pattern list.
-//    Returns the new head of the pattern list.
-patternT *
-addPattern (patternT * pattHead, patternT * addPatt)
-{
-    // Create a new pattern structure:
-#ifdef WINCE
-    patternT * newPatt = (patternT *) my_Tcl_Alloc(sizeof(new patternT));
-#else
-    patternT * newPatt = new patternT;
-#endif
-
-    // Initialise it:
-    newPatt->flag = addPatt->flag;
-    newPatt->pieceMatch = addPatt->pieceMatch;
-    newPatt->fyleMatch = addPatt->fyleMatch;
-    newPatt->rankMatch = addPatt->rankMatch;
-
-    // Add to the head of the list of patterns, and return:
-    newPatt->next = pattHead;
-    return newPatt;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// freePatternList():
-//    Frees the memory used by a list of patterns.
-void
-freePatternList (patternT * patt)
-{
-    patternT * nextPatt;
-    while (patt) {
-        nextPatt = patt->next;
-#ifdef WINCE
-        my_Tcl_Free((char*)patt);
-#else
-        delete patt;
-#endif
-        patt = nextPatt;
-    }
+    return TCL_OK;
 }
 
 void
@@ -8899,8 +8249,8 @@ sc_search_material (ClientData, Tcl_Inte
     bool sameBishops = true;
     uint hpExcludeMask = HPSIG_Empty;
     uint hpExMaskFlip = HPSIG_Empty;
-    patternT * patt = NULL;
-    patternT * flippedPatt = NULL;
+    std::vector<patternT> patt;
+    std::vector<patternT> flippedPatt;
     patternT tempPatt;
 
     const char * options[] = {
@@ -8992,9 +8342,10 @@ sc_search_material (ClientData, Tcl_Inte
                 hpExMaskFlip = hpSig_AddPawn (hpExMaskFlip, flipColor, fyle);
             }
             // Add the pattern and its flipped equivalent:
-            patt = addPattern (patt, &tempPatt);
+            // TODO: why not push_back() ?
+            patt.insert(patt.begin(), tempPatt);
             flipPattern (&tempPatt);
-            flippedPatt = addPattern (flippedPatt, &tempPatt);
+            flippedPatt.insert(flippedPatt.begin(), tempPatt);
             break;
 
         default:
@@ -9061,7 +8412,6 @@ sc_search_material (ClientData, Tcl_Inte
     Progress progress = UI_CreateProgress(ti);
     Timer timer;  // Start timing this search.
 
-    uint skipcount = 0;
     char temp [250];
     Game * g = scratchGame;
     HFilter filter = db->getFilter("dbfilter");
@@ -9082,12 +8432,10 @@ sc_search_material (ClientData, Tcl_Inte
         // First, apply the filter operation:
         if (filterOp == FILTEROP_AND) {  // Skip any games not in the filter:
             if (filter.get(gameNum) == 0) {
-                skipcount++;
                 continue;
             }
         } else /* filterOp == FILTEROP_OR*/ { // Skip any games in the filter:
             if (filter.get(gameNum) != 0) {
-                skipcount++;
                 continue;
             }
             // OK, this game is NOT in the filter.
@@ -9098,7 +8446,6 @@ sc_search_material (ClientData, Tcl_Inte
         const IndexEntry* ie = db->getIndexEntry(gameNum);
         if (ie->GetLength() == 0) {  // Skip games with no gamefile record
             filter.set (gameNum, 0);
-            skipcount++;
             continue;
         }
 
@@ -9106,7 +8453,6 @@ sc_search_material (ClientData, Tcl_Inte
             // Skip games without enough moves to match, if they
             // have the standard starting position:
             filter.set (gameNum, 0);
-            skipcount++;
             continue;
         }
 
@@ -9151,27 +8497,28 @@ sc_search_material (ClientData, Tcl_Inte
 
         if (!possibleMatch  &&  !possibleFlippedMatch) {
             filter.set (gameNum, 0);
-            skipcount++;
             continue;
         }
 
         // Now, the game must be loaded and searched:
-
-        if (db->getGame(ie, db->bbuf) != OK) {
+        auto bbuf = db->getGame(*ie);
+        if (!bbuf) {
             continue;
         }
 
         bool result = false;
         if (possibleMatch) {
-            result = g->MaterialMatch (db->bbuf, min, max, patt,
+            auto bbuf_clone = bbuf;
+            bool hasPromo = ie->GetPromotionsFlag() || ie->GetUnderPromoFlag();
+            result = g->MaterialMatch (hasPromo, bbuf_clone, min, max, patt.data(), patt.size(),
                                        minPly, maxPly, matchLength,
                                        oppBishops, sameBishops,
                                        matDiff[0], matDiff[1]);
         }
         if (result == 0  &&  possibleFlippedMatch) {
-            db->bbuf->BackToStart();
-            result = g->MaterialMatch (db->bbuf, minFlipped, maxFlipped,
-                                       flippedPatt, minPly, maxPly,
+            bool hasPromo = ie->GetPromotionsFlag() || ie->GetUnderPromoFlag();
+            result = g->MaterialMatch (hasPromo, bbuf, minFlipped, maxFlipped,
+                                       flippedPatt.data(), flippedPatt.size(), minPly, maxPly,
                                        matchLength, oppBishops, sameBishops,
                                        matDiff[0], matDiff[1]);
         }
@@ -9188,8 +8535,6 @@ sc_search_material (ClientData, Tcl_Inte
         }
     }
 
-    freePatternList (patt);
-    freePatternList (flippedPatt);
     progress.report(1,1);
 
     int centisecs = timer.CentiSecs();
@@ -9202,11 +8547,6 @@ sc_search_material (ClientData, Tcl_Inte
              static_cast<unsigned long>(startFilterCount),
              centisecs / 100, decimalPointChar, centisecs % 100);
     Tcl_AppendResult (ti, temp, NULL);
-#ifdef SHOW_SKIPPED_STATS
-    sprintf(temp, "  Skipped %u games.", skipcount);
-    Tcl_AppendResult (ti, temp, NULL);
-#endif
-
     return TCL_OK;
 }
 
@@ -9465,43 +8805,36 @@ sc_search_header (ClientData, Tcl_Interp
         // algorithm like Boyer-Moore or Knuth-Morris-Pratt since
         // profiling showed most that most of the time is spent
         // generating the PGN representation of each game.
-        if (match  &&  (pgnTextCount > 0 || !sAnnotator.empty())) {
-            if (match  &&  (base->getGame(ie, base->bbuf) != OK)) {
-                match = false;
-            }
 
-			if (!sAnnotator.empty()) {
-				// Need the annotator flag, so decode the flags
-				scratchGame->Clear();
-				if (match && scratchGame->DecodeTags(base->bbuf, true) != OK)
-					match = false;
-				if (match) {
-					auto ann = scratchGame->FindExtraTag("Annotator");
-					match = (ann != NULL) && strAlphaContains(ann, sAnnotator.c_str());
-				}
-			}
+		if (match && !sAnnotator.empty()) {
+			match = false;
+			base->getGame(*ie).decodeTags(
+				[&](auto const& tag, auto const& value) {
+					if (tag == "Annotator") {
+						match = strAlphaContains(std::string(value).c_str(),
+						                         sAnnotator.c_str());
+					}
+				});
+		}
 
-			if(pgnTextCount > 0)
-			{
-				scratchGame->Clear();
-				if (match  &&  scratchGame->Decode (base->bbuf, GAME_DECODE_ALL) != OK) {
-					match = false;
-				}
-				if (match) {
-					scratchGame->LoadStandardTags (ie, base->getNameBase());
-					scratchGame->ResetPgnStyle ();
-					scratchGame->AddPgnStyle (PGN_STYLE_TAGS);
-					scratchGame->AddPgnStyle (PGN_STYLE_COMMENTS);
-					scratchGame->AddPgnStyle (PGN_STYLE_VARS);
-					scratchGame->AddPgnStyle (PGN_STYLE_SYMBOLS);
-					scratchGame->SetPgnFormat (PGN_FORMAT_Plain);
-					const char* buf = scratchGame->WriteToPGN().first;
-					for (int m=0; m < pgnTextCount; m++) {
-					   if (match) { match = strContains (buf, sPgnText[m]); }
+		if (match && pgnTextCount > 0) {
+			if (base->getGame(*ie, *scratchGame) != OK) {
+				match = false;
+			} else {
+				scratchGame->ResetPgnStyle();
+				scratchGame->AddPgnStyle(PGN_STYLE_TAGS);
+				scratchGame->AddPgnStyle(PGN_STYLE_COMMENTS);
+				scratchGame->AddPgnStyle(PGN_STYLE_VARS);
+				scratchGame->AddPgnStyle(PGN_STYLE_SYMBOLS);
+				scratchGame->SetPgnFormat(PGN_FORMAT_Plain);
+				const char* buf = scratchGame->WriteToPGN().first;
+				for (int m = 0; m < pgnTextCount; m++) {
+					if (match) {
+						match = strContains(buf, sPgnText[m]);
 					}
 				}
 			}
-        }
+		}
 
         if (match) {
             filter.set (i, 1);
@@ -9743,16 +9076,27 @@ sc_book_close (ClientData, Tcl_Interp *
 int
 sc_book_moves (ClientData, Tcl_Interp * ti, int argc, const char ** argv)
 {
-		char moves[200] = "";
-		char boardStr[100];
     if (argc != 3) {
         return errorResult (ti, "Usage: sc_book moves slot");
     }
     uint slot = strGetUnsigned (argv[2]);
-		db->game->GetCurrentPos()->PrintFEN (boardStr, FEN_ALL_FIELDS);
-		polyglot_moves(moves, (const char *) boardStr, slot);
-    Tcl_AppendResult (ti, moves, NULL);
-    return TCL_OK;
+    char boardStr[100];
+    db->game->GetCurrentPos()->PrintFEN(boardStr, FEN_ALL_FIELDS);
+
+    char moves[1024] = {};
+    auto extra_info = polyglot_moves(moves, boardStr, slot);
+    UI_List extra_list(extra_info.size());
+    for (auto [score, depth, engine_name_idx] : extra_info) {
+        UI_List entry(3);
+        entry.push_back(score);
+        entry.push_back(depth);
+        entry.push_back(engine_name_idx);
+        extra_list.push_back(entry);
+    }
+    UI_List res(2);
+    res.push_back(moves);
+    res.push_back(extra_list);
+    return UI_Result(ti, OK, res);
 }
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // sc_positions:
diff -pruN 1:4.7.0+dfsg1-2/src/tkscid.h 1:4.7.4+dfsg1-2/src/tkscid.h
--- 1:4.7.0+dfsg1-2/src/tkscid.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/tkscid.h	2022-07-09 05:14:42.000000000 +0000
@@ -54,7 +54,6 @@ int sc_game_push      (TCL_ARGS);
 int sc_game_save      (TCL_ARGS);
 int sc_game_startBoard (TCL_ARGS);
 int sc_game_strip     (TCL_ARGS);
-int sc_game_summary   (TCL_ARGS);
 int sc_game_tags      (TCL_ARGS);
 int sc_game_tags_get  (TCL_ARGS);
 int sc_game_tags_set  (TCL_ARGS);
@@ -62,7 +61,6 @@ int sc_game_tags_reload (TCL_ARGS);
 int sc_game_tags_share (TCL_ARGS);
 
 int sc_info_limit     (TCL_ARGS);
-int sc_info_suffix    (TCL_ARGS);
 int sc_info_tb        (TCL_ARGS);
 int sc_info_priority  (TCL_ARGS);
 
@@ -91,8 +89,7 @@ int sc_pos_probe      (TCL_ARGS);
 int sc_pos_probe_board (TCL_ARGS);
 int sc_pos_setComment (TCL_ARGS);
 
-int sc_tree_move      (TCL_ARGS);
-int sc_tree_search    (TCL_ARGS);
+int sc_tree_stats    (TCL_ARGS);
 int sc_tree_cachesize (TCL_ARGS);
 int sc_tree_cacheinfo (TCL_ARGS);
 
diff -pruN 1:4.7.0+dfsg1-2/src/tree.h 1:4.7.4+dfsg1-2/src/tree.h
--- 1:4.7.0+dfsg1-2/src/tree.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/tree.h	2022-07-09 05:14:42.000000000 +0000
@@ -1,6 +1,6 @@
 /*
 * Copyright (C) 1999 Shane Hudson
-* Copyright (C) 2015 Fulvio Benini
+* Copyright (C) 2015-2020 Fulvio Benini
 
 * This file is part of Scid (Shane's Chess Information Database).
 *
@@ -17,225 +17,179 @@
 * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef SCID_TREE_H
-#define SCID_TREE_H
+#pragma once
 
-#include "common.h"
-#include "filter.h"
-#include <vector>
+#include "fullmove.h"
+#include "hfilter.h"
+#include "position.h"
 #include <algorithm>
+#include <vector>
 
+struct TreeNode {
+	unsigned long long eloWhiteSum = 0;   // Sum of white Elos.
+	unsigned long long eloBlackSum = 0;   // Sum of bLack Elos.
+	unsigned long long yearSum = 0;       // Sum of years.
+	gamenumT freq[NUM_RESULT_TYPES] = {}; // freq[0] is the total count.
+	gamenumT eloCount = 0;                // Count of games with an Elo.
+	gamenumT yearCount = 0;               // Count of games with year != 0.
+	FullMove move;
 
-//
-// Our tree structures:
-//
-
-// MAX_TREE_NODES: Fixed maximum number of moves a treeT can store.
-//    The number of played legal moves in a position rarely is over
-//    20, so 60 is a sane limit.
-//
-
-#define MAX_TREE_NODES 60
-
-// treeNodeT:
-//    Stores the move data, frequency, score, results by result type,
-//    and eco code of a single move played from a position.
-//
-struct treeNodeT {
-    simpleMoveT sm;
-    char        san[8];
-    uint        freq [NUM_RESULT_TYPES];
-    uint        total;      // Total count
-    uint        score;      // Score for white, in points per 1000 games, so
-                            // 55.1% would be a score of 551.
-    ecoT        ecoCode;
-    uint        eloCount;   // Count of games with an Elo.
-    uint        eloSum;     // Sum of Elos.
-    uint        perfCount;  // Count of games with an opponent Elo.
-    uint        perfSum;    // Sum of opponent Elos.
-    uint64_t    yearCount;  // Count of games with year != 0.
-    uint64_t    yearSum;    // Sum of years.
-};
-
-inline void initTreeNode (treeNodeT * tnode) {
-    tnode->freq[RESULT_White] = tnode->freq[RESULT_Black]
-        = tnode->freq[RESULT_Draw] = tnode->freq[RESULT_None] = 0;
-    for (uint i=0; i < 8; i++) { tnode->san[i] = 0; }
-    tnode->total = 0;
-    tnode->score = 0;
-    tnode->ecoCode = 0;
-    tnode->eloCount = 0;
-    tnode->eloSum = 0;
-    tnode->perfCount = 0;
-    tnode->perfSum = 0;
-    tnode->yearCount = 0;
-    tnode->yearSum = 0;
-}
-
+public:
+	explicit TreeNode(FullMove m) : move(m) {}
 
+	void add(resultT result, int eloW, int eloB, unsigned year) {
+		static_assert(RESULT_None == 0);
+		freq[0]++; // total count of games
+		if (result != RESULT_None) {
+			freq[result]++;
+		}
+		if (eloW > 0 && eloB > 0) {
+			++eloCount;
+			eloWhiteSum += eloW;
+			eloBlackSum += eloB;
+		}
+		if (year > 0) {
+			yearSum += year;
+			++yearCount;
+		}
+	}
+
+	/// @return a value in the range [0, 1000] representing the score percentage
+	/// from the white prospective (999 = white won 99.9% of the games).
+	int score() const {
+		auto n = freq[RESULT_White] + freq[RESULT_Draw] + freq[RESULT_Black];
+		auto res = 1000ull * freq[RESULT_White] + 500ull * freq[RESULT_Draw];
+		return n ? static_cast<int>(res / n) : 500;
+	}
+
+	double eloPerformance() const {
+		if (eloCount == 0)
+			return 0;
+
+		int score = (this->score() + 5) / 10;
+		auto eloOpp = eloBlackSum;
+		if (move.getColor() != WHITE) {
+			score = 100 - score;
+			eloOpp = eloWhiteSum;
+		}
+		return 1.0 * eloOpp / eloCount + FIDE_ratingTable[score];
+	}
+
+	double avgElo() const {
+		if (eloCount == 0)
+			return 0;
+
+		auto elo = (move.getColor() == WHITE) ? eloWhiteSum : eloBlackSum;
+		return 1.0 * elo / eloCount;
+	}
+
+	double avgYear() const { return yearCount ? 1.0 * yearSum / yearCount : 0; }
+
+	double percDraws() const {
+		return freq[0] ? 100.0 * freq[RESULT_Draw] / freq[0] : 0;
+	}
+
+	static auto cmp_ngames_desc() {
+		return
+		    [](auto const& a, auto const& b) { return a.freq[0] > b.freq[0]; };
+	}
 
-// treeT:
-//    Stores an array of tree nodes (each has a move, its frequency,
-//    score and ECO code) for a certain position.
-//
-struct treeT {
-    treeNodeT node [MAX_TREE_NODES];
-    uint      moveCount;
-    uint      totalCount;
+private:
+	// FIDE table 8.1a of conversion from fractional score (from 0 to 1 with
+	// 0.01 increments) into rating differences.
+	static constexpr short FIDE_ratingTable[] = {
+	    -800, -677, -589, -538, -501, -470, -444, -422, -401, -383, -366, -351,
+	    -336, -322, -309, -296, -284, -273, -262, -251, -240, -230, -220, -211,
+	    -202, -193, -184, -175, -166, -158, -149, -141, -133, -125, -117, -110,
+	    -102, -95,  -87,  -80,  -72,  -65,  -57,  -50,  -43,  -36,  -29,  -21,
+	    -14,  -7,   0,    7,    14,   21,   29,   36,   43,   50,   57,   65,
+	    72,   80,   87,   95,   102,  110,  117,  125,  133,  141,  149,  158,
+	    166,  175,  184,  193,  202,  211,  220,  230,  240,  251,  262,  273,
+	    284,  296,  309,  322,  336,  351,  366,  383,  401,  422,  444,  470,
+	    501,  538,  589,  677,  800};
 };
 
-
-// cachedTreeT:
-//    Stores a board position and its associated tree of all moves
-//    played at that position.
-//
-class cachedTreeT {
-    pieceT board_[64];
-    colorT toMove_;
-    treeT tree_;
-    CompressedFilter cfilter_;
-    uint32_t time_;
-
-    friend class TreeCache;
+//////////////////////////////////////////////////////////////////////
+// CompressedFilter class:
+//    Holds the same data as a filter, in compressed format.
+//    Random access to individual values is not possible.
+//    A CompressedFilter is created from, or restored to, a regular
+//    filter with the methods CompressFrom() and UncompressTo().
+class CompressedFilter {
+	byte* CompressedData = nullptr;
+	gamenumT CFilterSize = 0;
+	gamenumT CompressedLength = 0;
 
 public:
-    void set(Position* pos, treeT* tree, Filter* filter, uint32_t time) {
-        std::copy(pos->GetBoard(), pos->GetBoard() + 64, board_);
-        toMove_ = pos->GetToMove();
-        tree_ = *tree;
-        cfilter_.CompressFrom(filter);
-        time_ = time;
-    }
-    errorT restoreFilter(Filter* filter) const {
-        return cfilter_.UncompressTo(filter);
-    }
-    const treeT& getTree() const {
-        return tree_;
-    }
-    static bool cmpTime(const cachedTreeT& a, const cachedTreeT& b) {
-        return a.time_ < b.time_;
-    }
-};
+	CompressedFilter() = default;
+	CompressedFilter(CompressedFilter&&) = default;
+	~CompressedFilter() { delete[] CompressedData; }
 
+	void CompressFrom(Filter* filter);
+	errorT UncompressTo(Filter* filter) const;
 
+private:
+	errorT Verify(Filter* filter);
+};
 
-// A TreeCache object stores a fixed number of positions and their
-// tree data. The idea is that the common positions (the starting
-// position, the basic opening positions like 1.e4, 1.d4, etc) will
-// be have their tree information stored in a cache to save doing a
-// position search.
-
+struct CachedFilter {
+	CompressedFilter cfilter_;
+	pieceT board_[64];
+	colorT toMove_;
+};
 
 class TreeCache {
-    size_t NumInUse;
-    cachedTreeT* Cache;
-    size_t CacheSize;
-    uint32_t counter_;
+	std::vector<CachedFilter> cache_;
+	std::vector<uint32_t> cacheTime_;
+	uint32_t cacheTimeCounter_ = 0;
 
 public:
-    TreeCache()
-    : NumInUse(0),
-      Cache(NULL),
-      CacheSize(0),
-      counter_(0) {
-    }
-    ~TreeCache() {
-        if (Cache) delete [] Cache;
-    }
-
-    void Clear() {
-        NumInUse = 0;
-        counter_ = 0;
-    }
-    size_t Size() {
-        return CacheSize;
-    }
-    size_t UsedSize() {
-        return NumInUse;
-    }
-    void CacheResize(size_t max_size) {
-        if (max_size != CacheSize) {
-            CacheSize = max_size;
-            if (Cache) delete [] Cache;
-            if (max_size == 0) Cache = NULL;
-            else Cache = new cachedTreeT[max_size];
-        }
-        Clear();
-    }
-
-    const cachedTreeT* Lookup(Position* pos) {
-        int idx = LookupIndex(pos);
-        if (idx == -1) return NULL;
-        return &(Cache[idx]);
-    }
-    bool Add(Position* pos, treeT* tree, Filter* filter);
-
-private:
-    TreeCache(const TreeCache&);
-    void operator=(const TreeCache&);
-
-    int LookupIndex(Position* pos);
+	void Clear() {
+		cache_.clear();
+		cacheTime_.clear();
+	}
+
+	size_t Size() const { return cache_.capacity(); }
+
+	void CacheResize(size_t max_size) {
+		Clear();
+		cache_.reserve(max_size);
+		cacheTime_.reserve(max_size);
+	}
+
+	template <typename PosT> void cacheAdd(PosT const& pos, Filter& filter) {
+		size_t idx;
+		if (cache_.size() < Size() || cache_.empty()) {
+			idx = cache_.size();
+			cache_.emplace_back();
+			cacheTime_.emplace_back();
+		} else {
+			auto it = std::min_element(cacheTime_.begin(), cacheTime_.end());
+			idx = std::distance(cacheTime_.begin(), it);
+		}
+		auto board = pos.GetBoard();
+		std::copy(board, board + 64, cache_[idx].board_);
+		cache_[idx].toMove_ = pos.GetToMove();
+		cache_[idx].cfilter_.CompressFrom(&filter);
+		cacheTime_[idx] = cacheTimeCounter_++;
+	}
+
+	template <typename PosT>
+	bool cacheRestore(PosT const& pos, Filter& filter) {
+		auto it = std::find_if(
+		    cache_.begin(), cache_.end(), [&pos](auto const& e) {
+			    return e.toMove_ == pos.GetToMove() &&
+			           std::equal(e.board_, e.board_ + 64, pos.GetBoard());
+		    });
+		if (it == cache_.end())
+			return false;
+
+		auto idx = std::distance(cache_.begin(), it);
+		if (it->cfilter_.UncompressTo(&filter) != OK) {
+			ASSERT(false); // corrupted data: should not happen
+			return false;
+		}
+		cacheTime_[idx] = cacheTimeCounter_++;
+		return true;
+	}
 };
-
-
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// TreeCache::LookupIndex():
-//      Lookup a position in the tree cache, and return its index or -1
-//      if it is not in the cache.
-//
-inline int
-TreeCache::LookupIndex (Position * pos)
-{
-    for (uint i=0; i < NumInUse; i++) {
-        if (Cache[i].toMove_ != pos->GetToMove()) { continue; }
-
-        const pieceT* board = pos->GetBoard();
-        const pieceT* board2 = Cache[i].board_;
-        bool found = true;
-        for (squareT sq=A1; sq <= H8; sq++, board++, board2++) {
-            if (*board != *board2) { found = false; break; }
-        }
-        if (found) {
-            Cache[i].time_ = counter_++;
-            return static_cast<int>(i);
-        }
-    }
-    // Ended the search, no match:
-    return -1;
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// TreeCache::Add():
-//      Add a position to the cache if applicable..
-//
-inline bool
-TreeCache::Add (Position * pos, treeT * pTree, Filter * filter)
-{
-    ASSERT(LookupIndex(pos) == -1);
-
-    if (NumInUse < CacheSize) {
-        // Cache is not yet full. Add the position to the cache:
-        Cache[NumInUse++].set(pos, pTree, filter, counter_++);
-    } else {
-        // Cache is full!
-        // Replace the oldest node:
-        cachedTreeT* end = Cache + CacheSize;
-        cachedTreeT* replace = std::min_element(Cache, end, cachedTreeT::cmpTime);
-        if (replace == end) return false;
-
-        ASSERT(replace->time_ <= counter_);
-        replace->set(pos, pTree, filter, counter_++);
-    }
-    return true;
-}
-
-
-#endif
-
-//////////////////////////////////////////////////////////////////////
-//  EOF: tree.h
-//////////////////////////////////////////////////////////////////////
-
diff -pruN 1:4.7.0+dfsg1-2/src/ui.h 1:4.7.4+dfsg1-2/src/ui.h
--- 1:4.7.0+dfsg1-2/src/ui.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/ui.h	2022-07-09 05:14:42.000000000 +0000
@@ -16,8 +16,8 @@
 * along with Scid. If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef SCID_UI__H
-#define SCID_UI__H
+#ifndef SCID_UI_H
+#define SCID_UI_H
 
 #include "misc.h"
 #ifndef CHECKUIDEP
@@ -36,9 +36,6 @@ inline int Main (int argc, char* argv[],
 inline Progress CreateProgress(UI_handle_t) {
 	return Progress();
 }
-inline Progress CreateProgressPosMask(UI_handle_t) {
-	return Progress();
-}
 class List {
 public:
 	explicit List(size_t) {}
@@ -122,10 +119,6 @@ inline int UI_Main (int argc, char* argv
 inline Progress UI_CreateProgress(UI_handle_t ti) {
 	return UI_impl::CreateProgress(ti);
 }
-inline Progress UI_CreateProgressPosMask(UI_handle_t ti) {
-	return UI_impl::CreateProgressPosMask(ti);
-}
-
 
 /**
  * UI_Result() - pass the result of an operation from c++ to UI
diff -pruN 1:4.7.0+dfsg1-2/src/ui_tcltk.h 1:4.7.4+dfsg1-2/src/ui_tcltk.h
--- 1:4.7.0+dfsg1-2/src/ui_tcltk.h	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/src/ui_tcltk.h	2022-07-09 05:14:42.000000000 +0000
@@ -19,7 +19,7 @@
 #ifndef SCID_UI_TCLTK_H
 #define SCID_UI_TCLTK_H
 
-#include "timer.h"
+#include <chrono>
 #include <tcl.h>
 #include <sstream>
 #include <limits>
@@ -74,57 +74,43 @@ inline int Main (int argc, char* argv[],
 
 class tcl_Progress : public Progress::Impl {
 	UI_handle_t ti_;
-	Timer timer_;
+	using clock = std::chrono::high_resolution_clock;
+	decltype(clock::now()) timer_ = clock::now();
 
 public:
-	tcl_Progress(UI_handle_t ti) : ti_(ti) {}
-	virtual ~tcl_Progress() {}
+	explicit tcl_Progress(UI_handle_t ti) : ti_(ti) {}
 
-	virtual bool report(size_t done, size_t total, const char* msg) {
-		ASSERT(done <= static_cast<size_t>(std::numeric_limits<int>::max()));
-		ASSERT(total <= static_cast<size_t>(std::numeric_limits<int>::max()));
-
-		uint64_t elapsed = timer_.MilliSecs();
-		uint64_t estimated = (done == 0) ? 0 : elapsed * total / done;
-		Tcl_Obj* tmp[5];
-		tmp[0] = Tcl_NewStringObj("::progressCallBack", -1);
-		tmp[1] = Tcl_NewIntObj(static_cast<int>(done));
-		tmp[2] = Tcl_NewIntObj(static_cast<int>(total));
-		tmp[3] = Tcl_NewIntObj(static_cast<int>(elapsed / 1000));
-		tmp[4] = Tcl_NewIntObj(static_cast<int>(estimated / 1000));
-		Tcl_Obj* cmd = Tcl_NewListObj(5, tmp);
-		if (msg != NULL)
-			Tcl_ListObjAppendElement(ti_, cmd, Tcl_NewStringObj(msg, -1));
-		Tcl_IncrRefCount(cmd);
-		int res = Tcl_EvalObjEx(ti_, cmd, TCL_EVAL_GLOBAL);
-		Tcl_DecrRefCount(cmd);
+	bool report(size_t done, size_t total, const char* msg) final {
+		Tcl_Obj* cmd[3] = {};
+		cmd[0] = Tcl_NewStringObj("::progressCallBack", -1);
+		cmd[1] = Tcl_NewDoubleObj(total ? (1.0 * done / total) : 1);
+		int n = 2;
+		if (msg) {
+			cmd[2] = Tcl_NewStringObj(msg, -1);
+			n = 3;
+		}
+		std::for_each(cmd, cmd + n, [](Tcl_Obj* e) { Tcl_IncrRefCount(e); });
+		auto res = Tcl_EvalObjv(ti_, n, cmd, 0);
+		std::for_each(cmd, cmd + n, [](Tcl_Obj* e) { Tcl_DecrRefCount(e); });
 		return res == TCL_OK;
 	}
 };
 
-class tcl_ProgressPosMask : public Progress::Impl {
-	UI_handle_t ti_;
-
-public:
-	tcl_ProgressPosMask(UI_handle_t ti) : ti_(ti) {}
-	virtual ~tcl_ProgressPosMask() {}
-
-	virtual bool report(size_t, size_t, const char*) {
-		return TCL_OK == Tcl_EvalEx(ti_, "::windows::gamelist::PosMaskProgress", -1, 0);
-	}
-};
-
-inline Progress CreateProgress(UI_handle_t data) {
-	int err = Tcl_EvalEx(data, "::progressCallBack init", -1, 0);
-	if (err != TCL_OK) return Progress();
-	return Progress(new UI_impl::tcl_Progress(data));
-}
+inline Progress CreateProgress(UI_handle_t ti) {
+	Tcl_Obj* cmd[2];
+	cmd[0] = Tcl_NewStringObj("::progressCallBack", -1);
+	cmd[1] = Tcl_NewStringObj("init", -1);
+	Tcl_IncrRefCount(cmd[0]);
+	Tcl_IncrRefCount(cmd[1]);
+	auto err = Tcl_EvalObjv(ti, 2, cmd, 0);
+	Tcl_DecrRefCount(cmd[0]);
+	Tcl_DecrRefCount(cmd[1]);
+	if (err != TCL_OK)
+		return {};
 
-inline Progress CreateProgressPosMask(UI_handle_t data) {
-	return Progress(new UI_impl::tcl_ProgressPosMask(data));
+	return Progress(new UI_impl::tcl_Progress(ti));
 }
 
-
 class List {
 	Tcl_Obj** list_;
 	mutable int i_;
diff -pruN 1:4.7.0+dfsg1-2/tcl/appearance.tcl 1:4.7.4+dfsg1-2/tcl/appearance.tcl
--- 1:4.7.0+dfsg1-2/tcl/appearance.tcl	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/tcl/appearance.tcl	2022-07-09 05:14:42.000000000 +0000
@@ -35,14 +35,12 @@ proc ::appearance::applyMenuBarColor { m
   }
 }
 
-proc ::appearance::chooseMenuColor {col_var} {
+proc ::appearance::chooseMenuColor {col_var w} {
   set col [ tk_chooseColor -initialcolor [set $col_var] -title "Scid"]
   if { $col != "" } {
       set $col_var $col
-      ::appearance::Refresh
+      ::appearance::Refresh $w
   }
-  raiseWin .menuOptions
-  focus .menuOptions.f.bg
 }
 
 proc ::appearance::setNewMenuColors { m configure_cmd } {
@@ -86,12 +84,7 @@ proc ::appearance::setMenuColors {} {
       unset -nocomplain ::menuBarColor($col)
     }
   }
-
   updateMenuColors
-
-  set w .menuOptions
-  catch {grab release $w}
-  destroy $w
 }
 
 proc ::appearance::defaultColors {} {
@@ -106,7 +99,7 @@ proc ::appearance::defaultColors {} {
 # Menu Color Config
 #
 #   Dialog window for configuring Menu colors
-proc ::appearance::menuConfigDialog {} {
+proc ::appearance::menuConfigDialog { w } {
   ::appearance::defaultColors
   foreach {col value} [array get ::menuColor] {
     set ::menuDialog_($col) $value
@@ -115,76 +108,49 @@ proc ::appearance::menuConfigDialog {} {
     set ::menuBarDialog_($col) $value
   }
 
-  set w .menuOptions
-  win::createDialog $w
-  wm title $w "Scid: [tr OptionsMenuColor]"
-
-  ttk::label $w.status -text ""
-  pack [ttk::frame $w.b] -side bottom -fill x
-  pack [ttk::frame $w.f] \
-      -side top -fill x
-
+  pack [ttk::frame $w.f] -side top
   set f $w.f
   set r 0
 
   ttk::label $f.mainmenu -text "Menu bar Menu"
   if { ! $::windowsOS &&  ! $::macOS} {
-    ttk::button $f.mbg -text $::tr(MenuColorBackground) -command { ::appearance::chooseMenuColor ::menuBarDialog_(background) }
+    ttk::button $f.mbg -text $::tr(MenuColorBackground) -command "::appearance::chooseMenuColor ::menuBarDialog_(background) $w"
     grid $f.mainmenu -row $r -column 0 -columnspan 3 -padx 4 -sticky e
     grid $f.mbg -row $r -column 4 -padx 4
     incr r
   }
   ttk::label $f.menuline0 -text "Disabled"
   grid $f.menuline0 -row $r -column 2 -padx 4
-  ttk::button $f.dfg -text $::tr(MenuColorForeground) -command { ::appearance::chooseMenuColor ::menuDialog_(disabledForeground) }
+  ttk::button $f.dfg -text $::tr(MenuColorForeground) -command "::appearance::chooseMenuColor ::menuDialog_(disabledForeground) $w"
   grid $f.dfg -row $r -column 3 -padx 4
   incr r
-  ttk::button $f.sel -text $::tr(MenuColorSelect) -command { ::appearance::chooseMenuColor ::menuDialog_(selectColor) }
+  ttk::button $f.sel -text $::tr(MenuColorSelect) -command "::appearance::chooseMenuColor ::menuDialog_(selectColor) $w"
   grid $f.sel -row $r -column 0 -padx 4
   ttk::label $f.select -text "x"
   grid $f.select -row $r -column 1
   ttk::label $f.menuline1 -text "Menu1"
   grid $f.menuline1 -row $r -column 2 -padx 4
-  ttk::button $f.fg -text $::tr(MenuColorForeground) -command { ::appearance::chooseMenuColor ::menuDialog_(foreground) }
+  ttk::button $f.fg -text $::tr(MenuColorForeground) -command "::appearance::chooseMenuColor ::menuDialog_(foreground) $w"
   grid $f.fg -row $r -column 3 -padx 4 -pady 5
-  ttk::button $f.bg -text $::tr(MenuColorBackground) -command { ::appearance::chooseMenuColor ::menuDialog_(background) }
+  ttk::button $f.bg -text $::tr(MenuColorBackground) -command "::appearance::chooseMenuColor ::menuDialog_(background) $w"
   grid $f.bg -row $r -column 4 -padx 4
   incr r
   ttk::label $f.menuline2 -text "Menu2"
   grid $f.menuline2 -row $r -column 2 -padx 4
-  ttk::button $f.afg -text $::tr(MenuColorForeground) -command { ::appearance::chooseMenuColor ::menuDialog_(activeForeground) }
+  ttk::button $f.afg -text $::tr(MenuColorForeground) -command "::appearance::chooseMenuColor ::menuDialog_(activeForeground) $w"
   grid $f.afg -row $r -column 3 -padx 4
-  ttk::button $f.abg -text $::tr(MenuColorBackground) -command { ::appearance::chooseMenuColor ::menuDialog_(activeBackground) }
+  ttk::button $f.abg -text $::tr(MenuColorBackground) -command "::appearance::chooseMenuColor ::menuDialog_(activeBackground) $w"
   grid $f.abg -row $r -column 4 -padx 4 -pady 5
-  incr r
-  addHorizontalRule $w
-  ttk::button $w.b.help -image tb_help -command {grab release .menuOptions; helpWindow Appearance }
-  ::utils::tooltip::Set $w.b.help $::tr(Help)
-  dialogbutton $w.b.ok -text OK -command ::appearance::setMenuColors
-  dialogbutton $w.b.reset -text $::tr(Defaults) -command {
-    ::appearance::defaultColors
-    ::appearance::setMenuColors
-  }
-  dialogbutton $w.b.cancel -text $::tr(Cancel) -command "::win::closeWindow $w"
-  packdlgbuttons $w.b.cancel $w.b.ok $w.b.reset $w.b.help
-  bind $w <Return> [list $w.b.ok invoke]
-  bind $w <Escape> [list $w.b.cancel invoke]
-  bind $w.f <Destroy>  { unset ::menuDialog_ }
-  bind $w <F1> { grab release .menuOptions; helpWindow Appearance }
-  ::appearance::Refresh
-  ::utils::win::Centre $w
-  wm resizable $w 0 0
-  raiseWin $w
-  grab $w
-  focus $w.f.bg
+  ::appearance::Refresh $w
 }
 
-proc ::appearance::Refresh {} {
+proc ::appearance::Refresh { w } {
   global menuDialog_
-  set w .menuOptions
+
   $w.f.menuline0 configure -background $menuDialog_(background) -foreground $menuDialog_(disabledForeground)
   $w.f.menuline1 configure -background $menuDialog_(background) -foreground $menuDialog_(foreground)
   $w.f.menuline2 configure -background $menuDialog_(activeBackground) -foreground $menuDialog_(activeForeground)
   $w.f.select configure -foreground $menuDialog_(selectColor)
   $w.f.mainmenu configure -background $::menuBarDialog_(background) -foreground $menuDialog_(foreground)
+  ::appearance::setMenuColors
 }
diff -pruN 1:4.7.0+dfsg1-2/tcl/board.tcl 1:4.7.4+dfsg1-2/tcl/board.tcl
--- 1:4.7.0+dfsg1-2/tcl/board.tcl	2019-01-28 19:11:44.000000000 +0000
+++ 1:4.7.4+dfsg1-2/tcl/board.tcl	2022-07-09 05:14:42.000000000 +0000
@@ -1,6 +1,7 @@
 # board.tcl: part of Scid
 # Copyright (C) 2001-2003 Shane Hudson. All rights reserved.
-# Copyright (C) 2014 Fulvio Benini
+# Copyright (C) 2014-2021 Fulvio Benini
+# Copyright (C) 2021 Uwe Klimmek
 
 # letterToPiece
 #    Array that maps piece letters to their two-character value.
@@ -23,6 +24,22 @@ set colorSchemes {
 }
 array set newColors {}
 
+# calculate an average of the colors from a graphic file
+proc avgImgColor { file } {
+    set i 0; set r 0; set g 0; set b 0
+    set textureSize [image height $file]
+    for { set p 2 } { $p < $textureSize } {incr p 4; incr i } {
+        lassign [$file get $p $p] r1 g1 b1
+        incr r $r1
+        incr g $g1
+        incr b $b1
+    }
+    set r [expr int(($r) / $i)]
+    set g [expr int(($g) / $i)]
+    set b [expr int(($b) / $i)]
+    return [ format "#%02x%02x%02x" $r $g $b ]
+}
+
 proc SetBoardTextures {} {
   global boardfile_dark boardfile_lite
   # handle cases of old configuration files
@@ -81,7 +98,6 @@ proc setPieceFont {font} {
 		if {$size >= $::boardSize} { break }
 	}
 	set ::boardSize $size
-	menuUpdateBoardSizes
 	SetBoardTextures
 }
 
@@ -95,69 +111,87 @@ proc chooseBoardTextures {i} {
   set boardfile_dark ${prefix}-d
   set boardfile_lite ${prefix}-l
   SetBoardTextures
-  
+  # create colors for coords on the board
+  # use variable lite and dark from colorschemes
+  set ::squareColor_lite [::avgImgColor $boardfile_lite]
+  set ::squareColor_dark [::avgImgColor $boardfile_dark]
+  ::board::innercoords .main.board
+  updateBoard
+}
+
+proc updateBoardColors { w {choice -1}} {
+  global squareColor_lite squareColor_dark highcolor bestcolor
+  global colorSchemes newColors
+  set colors {squareColor_lite squareColor_dark highcolor bestcolor}
+  # Just update the dialog box colors and return:
+  if {$choice >= 0} {
+    set list [lindex $colorSchemes $choice]
+    set newColors(squareColor_lite) [lindex $list 1]
+    set newColors(squareColor_dark) [lindex $list 2]
+    set newColors(highcolor) [lindex $list 3]
+    set newColors(bestcolor) [lindex $list 4]
+
+    # Remove board textures
+    set ::boardfile_dark emptySquare
+    set ::boardfile_lite emptySquare
+    ::SetBoardTextures
+  }
+  set nlite $newColors(squareColor_lite)
+  set ndark $newColors(squareColor_dark)
+
+  foreach i {wr bn wb bq wk bp} {
+    $w.bd.$i configure -background $ndark
+  }
+  foreach i {br wn bb wq bk wp} {
+    $w.bd.$i configure -background $nlite
+  }
+  $w.bd.bb configure -background $newColors(highcolor)
+  $w.bd.wk configure -background $newColors(bestcolor)
+  foreach i $colors {
+    $w.select.b$i configure -background $newColors($i)
+  }
+
+  foreach i {squareColor_lite squareColor_dark highcolor bestcolor} {
+    set $i $newColors($i)
+  }
+  foreach i {0 1 2 3} {
+    set c $w.border.c$i
+    $c itemconfigure dark -fill $squareColor_dark -outline $squareColor_dark
+    $c itemconfigure lite -fill $squareColor_lite -outline $squareColor_lite
+  }
+  foreach i {0 1 2 3 4} {
+    set c $w.coords.c$i
+    $c itemconfigure dark -fill $squareColor_dark -outline $squareColor_dark
+    $c itemconfigure lite -fill $squareColor_lite -outline $squareColor_lite
+  }
+  ::board::innercoords .main.board
+  updateBoard
+  return
 }
 
 # chooseBoardColors:
 #   Dialog for selecting board colors.
+#   Frame for this dialog is generated by preferences dialog
 #
-proc chooseBoardColors {{choice -1}} {
-  global lite dark highcolor bestcolor
+proc chooseBoardColors { w {choice -1}} {
+  global squareColor_lite squareColor_dark highcolor bestcolor
   global colorSchemes newColors
-  
-  set colors {lite dark highcolor bestcolor}
-  
-  set w .boardColorDialog
-  
-  if {[winfo exists $w]} {
-    # Just update the dialog box colors and return:
-    if {$choice >= 0} {
-      set list [lindex $colorSchemes $choice]
-      set newColors(lite) [lindex $list 1]
-      set newColors(dark) [lindex $list 2]
-      set newColors(highcolor) [lindex $list 3]
-      set newColors(bestcolor) [lindex $list 4]
-    }
-    set nlite $newColors(lite)
-    set ndark $newColors(dark)
-    
-    foreach i {wr bn wb bq wk bp} {
-      $w.bd.$i configure -background $ndark
-    }
-    foreach i {br wn bb wq bk wp} {
-      $w.bd.$i configure -background $nlite
-    }
-    $w.bd.bb configure -background $newColors(highcolor)
-    $w.bd.wk configure -background $newColors(bestcolor)
-    foreach i $colors {
-      $w.select.b$i configure -background $newColors($i)
-    }
-    
-    foreach i {0 1 2 3} {
-      set c $w.border.c$i
-      $c itemconfigure dark -fill $dark -outline $dark
-      $c itemconfigure lite -fill $lite -outline $lite
-    }
-    
-    return
-  }
-  
-  ::win::createDialog $w
-  wm title $w "Scid: [tr OptionsBoardColors]"
-  
+
+  set colors {squareColor_lite squareColor_dark highcolor bestcolor}
+
   foreach i $colors { set newColors($i) [set $i] }
   set bd $w.bd
-  pack [ttk::frame $bd] -side top -expand 1
+  pack [ttk::frame $bd] -side top -expand 1 -anchor w
   addHorizontalRule $w
   pack [ttk::frame $w.select] -side top -fill x -padx 5
   addHorizontalRule $w
   pack [ttk::frame $w.preset] -side top -fill x
-  pack [ttk::frame $w.texture] -side top -fill x
+  pack [ttk::frame $w.texture] -side top -fill x -pady 2
   addHorizontalRule $w
-  pack [ttk::frame $w.border] -side top
+  pack [ttk::frame $w.coords] -side top -fill x -pady 4
   addHorizontalRule $w
-  pack [ttk::frame $w.buttons] -side top -fill x
-  
+  pack [ttk::frame $w.border] -side top -anchor w -pady { 4 0 }
+
   foreach psize $::boardSizes {
     if {$psize >= 40} { break }
   }
@@ -169,28 +203,43 @@ proc chooseBoardColors {{choice -1}} {
     grid $bd.w$j -row 1 -column $column
     incr column
   }
-  
+  ttk::frame $bd.p
+  ttk::label $bd.p.lb -text [tr OptionsBoardPieces]
+  ttk::combobox $bd.p.pieces -width 12 -textvar ::boardStyle -values $::boardStyles
+  bind $bd.p.pieces <<ComboboxSelected>> { setPieceFont $::boardStyle; updateBoard }
+  ttk::frame $bd.s
+  ttk::label $bd.s.lb -text [tr OptionsBoardSize]
+  ttk::checkbutton $bd.s.auto -text "Auto" -variable ::autoResizeBoard -command "::resizeMainBoard"
+  ttk::combobox $bd.s.size -width 4 -textvar ::boardSize  -values $::boardSizes
+  bind $bd.s.size <<ComboboxSelected>> { ::board::resize .main.board $::boardSize }
+  pack $bd.p.lb $bd.p.pieces -side left -anchor w -padx 5
+  pack $bd.s.lb $bd.s.auto $bd.s.size -side left -anchor w -padx 5
+  ttk::label $bd.empty -text "  "
+  grid $bd.empty -row 0 -column 7
+  grid $bd.p -row 0 -column 8
+  grid $bd.s -row 1 -column 8
+
   set f $w.select
   foreach row {0 1 0 1} column {0 0 2 2} c {
-    lite dark highcolor bestcolor
+    squareColor_lite squareColor_dark highcolor bestcolor
   } n {
     LightSquares DarkSquares SelectedSquares SuggestedSquares
   } {
     button $f.b$c -image e20 -background [set $c] -command "
     set x \[ tk_chooseColor -initialcolor \$newColors($c) -title Scid \]
-    if {\$x != \"\"} { set newColors($c) \$x; chooseBoardColors }
+    if {\$x != \"\"} { set newColors($c) \$x; updateBoardColors $w}
     "
     ttk::label $f.l$c -text "$::tr($n)  "
     grid $f.b$c -row $row -column $column
     grid $f.l$c -row $row -column [expr {$column + 1} ] -sticky w
   }
-  
+
   # Border width option:
   set f $w.border
   foreach i {0 1 2 3} {
     if {$i != 0} { pack [ttk::frame $f.gap$i -width 20] -side left -padx 1 }
     set b $f.b$i
-    ttk::radiobutton $b -text "$i:" -variable newborderwidth -value $i
+    ttk::radiobutton $b -text "$i:" -variable ::borderwidth -value $i -command "set ::borderwidth $i; ::board::border .main.board $i; updateBoard"
     set c $f.c$i
     canvas $c -height $psize -width $psize -background black
     $c create rectangle 0 0 [expr {20 - $i}] [expr {20 - $i}] -tag dark
@@ -198,10 +247,51 @@ proc chooseBoardColors {{choice -1}} {
     $c create rectangle 0 [expr {20 + $i}] [expr 20 - $i] $psize -tag lite
     $c create rectangle [expr {20 + $i}] 0 $psize [expr {20 - $i}] -tag lite
     pack $b $c -side left -padx 1
-    bind $c <Button-1> "set newborderwidth $i"
+    bind $c <Button-1> "set ::borderwidth $i; ::board::border .main.board $i; updateBoard"
   }
-  set ::newborderwidth $::borderwidth
-  
+
+  # Coords option:
+  set f $w.coords
+  # bl: border left, bt: border top, ssi: squaresize
+  set slist [list 0 0 20 8 0 16 8 8 12 8 0 20 8 0 20]
+  foreach i {0 1 2 3 4} {bl bt ssi} $slist {
+    if {$i != 0} { pack [ttk::frame $f.gap$i -width 20] -side left -padx 1 }
+    set b $f.b$i
+    set fcom "set ::boardCoords $i
+              ::board::coords .main.board $i
+              ::board::resize .main.board redraw"
+    ttk::radiobutton $b -text "$i:" -variable ::boardCoords -value $i -command $fcom
+    set c $f.c$i
+    canvas $c -height $psize -width $psize -background white
+    if { $i >= 3 } {
+        set x0 0
+        set y0 0
+    } else {
+        set x0 [expr $bl]
+        set y0 [expr $bt]
+    }
+    set x1 [expr $x0 + $ssi]
+    set y1 [expr $y0 + $ssi]
+    set x2 [expr $x1 + $ssi]
+    set y2 [expr $y1 + $ssi]
+    $c create rectangle $x0 $y0 $x1 $y1 -tag dark
+    $c create rectangle $x1 $y1 $x2 $y2 -tag dark
+    $c create rectangle $x0 $y1 $x1 $y2 -tag lite
+    $c create rectangle $x1 $y0 $x2 $y1 -tag lite
+    if { $i > 0 } {
+        foreach { x y co } { 4 30 1 4 10 2 14 36 a 28 36 b } {