summaryrefslogtreecommitdiffstats
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches')
-rw-r--r--debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch7004
-rw-r--r--debian/patches/kodi/0001-Implement-hashes-using-Libgcrypt.patch145
-rw-r--r--debian/patches/kodi/0002-Find-and-link-with-Libgcrypt.patch58
-rw-r--r--debian/patches/kodi/0003-differentiate-from-vanilla-Kodi.patch90
-rw-r--r--debian/patches/kodi/0004-use-system-groovy.patch128
-rw-r--r--debian/patches/kodi/0005-fix-tests.patch28
-rw-r--r--debian/patches/kodi/0006-dont-use-openssl.patch29
-rw-r--r--debian/patches/kodi/0007-support-omitting-addons-service.patch27
-rw-r--r--debian/patches/kodi/0008-Find-test-fixtures-in-source-directory.patch22
-rw-r--r--debian/patches/kodi/0009-Skip-long-time-broken-test.patch23
-rw-r--r--debian/patches/kodi/0010-Disable-flaky-TestMassEvent.General-and-TestMassEven.patch31
-rw-r--r--debian/patches/kodi/0011-Skip-checking-errno-against-ENOENT-because-this-test.patch22
-rw-r--r--debian/patches/kodi/0012-The-baseline-of-the-i386-port-does-not-include-SSE.patch96
-rw-r--r--debian/patches/kodi/0013-Disable-GetCPUFrequency-test.patch22
-rw-r--r--debian/patches/kodi/0014-Fix-C++-example-includes.patch52
-rw-r--r--debian/patches/kodi/0015-debian-cross-compile.patch33
-rw-r--r--debian/patches/kodi/0016-ports-architectures.patch132
-rw-r--r--debian/patches/libdvdnav/0001-libdvdnav-PR48-enen92.patch184
-rw-r--r--debian/patches/libdvdread/0001-libdvdread-PR40-enen92.patch1694
-rw-r--r--debian/patches/libdvdread/debian-0001-libdvdcss.patch25
-rw-r--r--debian/patches/libdvdread/debian-0002-descriptor.patch104
-rw-r--r--debian/patches/series28
-rw-r--r--debian/patches/workarounds/0001-Workaround-989814.patch71
-rw-r--r--debian/patches/workarounds/0002-ffmpeg5.patch2429
-rw-r--r--debian/patches/workarounds/0003-xbmc-libdvd_vfs-enen92.patch5056
-rw-r--r--debian/patches/workarounds/0004-ffmpeg6.patch662
-rw-r--r--debian/patches/workarounds/0005-pcre2.patch1219
-rw-r--r--debian/patches/workarounds/0006-loongarch.patch40
-rw-r--r--debian/patches/workarounds/0007-swig.patch10
29 files changed, 19464 insertions, 0 deletions
diff --git a/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch b/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch
new file mode 100644
index 0000000..5c553eb
--- /dev/null
+++ b/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch
@@ -0,0 +1,7004 @@
+From c34f01771ab31da1450e0e8aec0dd4654ea5c557 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Mon, 17 Feb 2020 22:41:25 -0800
+Subject: [PATCH 01/19] add support for date library and tzdata
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ CMakeLists.txt | 6 +
+ cmake/modules/FindDate.cmake | 110 ++++++++++++++++++
+ cmake/modules/FindTZData.cmake | 33 ++++++
+ cmake/scripts/linux/Install.cmake | 7 ++
+ .../date/0001-cmake-install-all-headers.patch | 74 ++++++++++++
+ .../target/date/0002-Fix-Android-build.patch | 37 ++++++
+ .../target/date/0003-Fix-UWP-build.patch | 58 +++++++++
+ tools/depends/target/date/DATE-VERSION | 6 +
+ .../target/tzdata/001-cmakelists.patch | 11 ++
+ tools/depends/target/tzdata/TZDATA-VERSION | 5 +
+ 10 files changed, 347 insertions(+)
+ create mode 100644 cmake/modules/FindDate.cmake
+ create mode 100644 cmake/modules/FindTZData.cmake
+ create mode 100644 tools/depends/target/date/0001-cmake-install-all-headers.patch
+ create mode 100644 tools/depends/target/date/0002-Fix-Android-build.patch
+ create mode 100644 tools/depends/target/date/0003-Fix-UWP-build.patch
+ create mode 100644 tools/depends/target/date/DATE-VERSION
+ create mode 100644 tools/depends/target/tzdata/001-cmakelists.patch
+ create mode 100644 tools/depends/target/tzdata/TZDATA-VERSION
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 19881e4708..55344f7626 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -85,6 +85,10 @@ if(UNIX)
+ endif()
+ # prefer kissfft from xbmc/contrib but let use system one on unices
+ cmake_dependent_option(ENABLE_INTERNAL_KISSFFT "Enable internal kissfft?" ON "UNIX" ON)
++# prefer internal date and tzdata but let use system one on unices
++cmake_dependent_option(ENABLE_INTERNAL_DATE "Enable internal date library?" ON "UNIX" ON)
++cmake_dependent_option(ENABLE_INTERNAL_TZDATA "Use Kodi bundled timezone information?" ON "NOT ENABLE_INTERNAL_DATE" ON)
++cmake_dependent_option(DATE_HAS_STRINGVIEW "Is system date library compiled with StringView support?" ON "NOT ENABLE_INTERNAL_DATE" ON)
+ # System options
+ if(NOT WIN32)
+ option(WITH_ARCH "build with given arch" OFF)
+@@ -163,6 +167,7 @@ set(required_deps ASS
+ Cdio
+ CrossGUID
+ Curl
++ Date
+ FFMPEG
+ FlatBuffers
+ Fmt
+@@ -181,6 +186,7 @@ set(required_deps ASS
+ Sqlite3
+ TagLib
+ TinyXML
++ TZData
+ ZLIB
+ ${PLATFORM_REQUIRED_DEPS})
+
+diff --git a/cmake/modules/FindDate.cmake b/cmake/modules/FindDate.cmake
+new file mode 100644
+index 0000000000..61746e4d33
+--- /dev/null
++++ b/cmake/modules/FindDate.cmake
+@@ -0,0 +1,110 @@
++#.rst:
++# FindDate
++# -------
++# Finds the DATE library and builds internal
++# DATE if requested
++#
++# This will define the following variables::
++#
++# DATE_FOUND - system has DATE
++# DATE_INCLUDE_DIRS - the DATE include directory
++# DATE_LIBRARIES - the DATE libraries
++# DATE_DEFINITIONS - the DATE definitions
++#
++# and the following imported targets::
++#
++# Date::Date - The Date library
++
++if(ENABLE_INTERNAL_DATE)
++ include(cmake/scripts/common/ModuleHelpers.cmake)
++
++ set(MODULE_LC date)
++
++ SETUP_BUILD_VARS()
++
++ set(DATE_VERSION ${${MODULE}_VER})
++
++ # Debug postfix only used for windows
++ if(WIN32 OR WINDOWS_STORE)
++ set(DATE_DEBUG_POSTFIX "d")
++ endif()
++
++ # Propagate CMake definitions
++
++ if(CORE_SYSTEM_NAME STREQUAL darwin_embedded)
++ set(EXTRA_ARGS -DIOS=ON)
++ elseif(WINDOWS_STORE)
++ set(EXTRA_ARGS -DWINRT=ON)
++ endif()
++
++ set(CMAKE_ARGS -DCMAKE_CXX_STANDARD=17
++ -DUSE_SYSTEM_TZ_DB=OFF
++ -DMANUAL_TZ_DB=ON
++ -DUSE_TZ_DB_IN_DOT=OFF
++ -DBUILD_SHARED_LIBS=OFF
++ -DBUILD_TZ_LIB=ON
++ ${EXTRA_ARGS})
++
++ # Work around old release
++
++ file(GLOB patches "${CMAKE_SOURCE_DIR}/tools/depends/target/date/*.patch")
++
++ generate_patchcommand("${patches}")
++
++ BUILD_DEP_TARGET()
++else()
++ if(PKG_CONFIG_FOUND)
++ pkg_check_modules(PC_DATE libdate-tz>=3.0.1 QUIET)
++ endif()
++
++ find_path(DATE_INCLUDE_DIR date/date.h
++ PATHS ${PC_DATE_INCLUDEDIR})
++ find_library(DATE_LIBRARY_RELEASE NAMES date-tz libdate-tz
++ PATHS ${PC_DATE_LIBDIR})
++ find_library(DATE_LIBRARY_DEBUG NAMES date-tzd libdate-tzd
++ PATHS ${PC_DATE_LIBDIR})
++ set(DATE_VERSION ${PC_DATE_VERSION})
++endif()
++
++include(SelectLibraryConfigurations)
++select_library_configurations(DATE)
++
++include(FindPackageHandleStandardArgs)
++find_package_handle_standard_args(Date
++ REQUIRED_VARS DATE_LIBRARY DATE_INCLUDE_DIR
++ VERSION_VAR DATE_VERSION)
++
++if(DATE_FOUND)
++ set(DATE_INCLUDE_DIRS ${DATE_INCLUDE_DIR})
++ set(DATE_LIBRARIES ${DATE_LIBRARY})
++
++ if(ENABLE_INTERNAL_TZDATA)
++ set(DATE_DEFINITIONS ${DATE_DEFINITIONS} -DDATE_INTERNAL_TZDATA)
++ endif()
++
++ if(DATE_HAS_STRINGVIEW)
++ set(DATE_DEFINITIONS ${DATE_DEFINITIONS} -DDATE_HAS_STRINGVIEW)
++ endif()
++
++ if(NOT TARGET Date::Date)
++ add_library(Date::Date UNKNOWN IMPORTED)
++ if(DATE_LIBRARY_RELEASE)
++ set_target_properties(Date::Date PROPERTIES
++ IMPORTED_CONFIGURATIONS RELEASE
++ IMPORTED_LOCATION "${DATE_LIBRARY_RELEASE}")
++ endif()
++ if(DATE_LIBRARY_DEBUG)
++ set_target_properties(Date::Date PROPERTIES
++ IMPORTED_CONFIGURATIONS DEBUG
++ IMPORTED_LOCATION "${DATE_LIBRARY_DEBUG}")
++ endif()
++ set_target_properties(Date::Date PROPERTIES
++ INTERFACE_INCLUDE_DIRECTORIES "${DATE_INCLUDE_DIR}")
++ if(TARGET date)
++ add_dependencies(Date::Date date)
++ endif()
++ endif()
++ set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP Date::Date)
++endif()
++
++mark_as_advanced(DATE_INCLUDE_DIR DATE_LIBRARY)
+diff --git a/cmake/modules/FindTZData.cmake b/cmake/modules/FindTZData.cmake
+new file mode 100644
+index 0000000000..b7241a6b04
+--- /dev/null
++++ b/cmake/modules/FindTZData.cmake
+@@ -0,0 +1,33 @@
++#.rst:
++# FindTZData
++# -------
++# Populates resource.timezone with TZDATA if requested
++#
++# This will define the following variables::
++#
++# TZDATA_FOUND - system has internal TZDATA
++# TZDATA_VERSION - version of internal TZDATA
++
++if(ENABLE_INTERNAL_TZDATA)
++ include(cmake/scripts/common/ModuleHelpers.cmake)
++
++ set(MODULE_LC tzdata)
++
++ SETUP_BUILD_VARS()
++
++ # Mirror tzdata to resource.timezone
++
++ set(CMAKE_ARGS -DINSTALL_DIR=${CMAKE_BINARY_DIR}/addons/resource.timezone/resources/tzdata)
++
++ # Add CMakeLists.txt installing sources as target
++
++ set(patches ${CMAKE_SOURCE_DIR}/tools/depends/target/tzdata/001-cmakelists.patch)
++
++ generate_patchcommand("${patches}")
++
++ BUILD_DEP_TARGET()
++else()
++ set(TZDATA_VERSION "none")
++endif()
++
++set(TZDATA_FOUND TRUE)
+diff --git a/cmake/scripts/linux/Install.cmake b/cmake/scripts/linux/Install.cmake
+index 331722cb56..e3ceacbf76 100644
+--- a/cmake/scripts/linux/Install.cmake
++++ b/cmake/scripts/linux/Install.cmake
+@@ -85,6 +85,13 @@ foreach(file ${install_data})
+ COMPONENT kodi)
+ endforeach()
+
++# Install timezone resources
++if(ENABLE_INTERNAL_TZDATA)
++ install(DIRECTORY ${CMAKE_BINARY_DIR}/addons/resource.timezone/resources/tzdata
++ DESTINATION ${datarootdir}/${APP_NAME_LC}/addons/resource.timezone/resources
++ COMPONENT kodi)
++endif()
++
+ # Install xsession entry
+ install(FILES ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/${APP_NAME_LC}-xsession.desktop
+ RENAME ${APP_NAME_LC}.desktop
+diff --git a/tools/depends/target/date/0001-cmake-install-all-headers.patch b/tools/depends/target/date/0001-cmake-install-all-headers.patch
+new file mode 100644
+index 0000000000..e8ac76a48b
+--- /dev/null
++++ b/tools/depends/target/date/0001-cmake-install-all-headers.patch
+@@ -0,0 +1,74 @@
++From 8b3fac88cb5e4fef81b55b7537232a6418752153 Mon Sep 17 00:00:00 2001
++From: Andrea Pappacoda <andrea@pappacoda.it>
++Date: Thu, 4 Aug 2022 11:35:41 +0200
++Subject: [PATCH 1/4] cmake: install all headers
++
++Lazy solution: mark all headers as either PUBLIC_ or PRIVATE_HEADER,
++requiring CMake 3.15.
++
++Bug: https://github.com/HowardHinnant/date/pull/503
++Bug-Debian: https://bugs.debian.org/1001895
++---
++ CMakeLists.txt | 20 +++++++++++++++-----
++ 1 file changed, 15 insertions(+), 5 deletions(-)
++
++diff --git a/CMakeLists.txt b/CMakeLists.txt
++index 012512a..c0bda7f 100644
++--- a/CMakeLists.txt
+++++ b/CMakeLists.txt
++@@ -75,7 +75,13 @@ target_sources( date INTERFACE
++ )
++ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
++ # public headers will get installed:
++- set_target_properties( date PROPERTIES PUBLIC_HEADER include/date/date.h )
+++ set_property(TARGET date PROPERTY PUBLIC_HEADER
+++ include/date/date.h
+++ include/date/islamic.h
+++ include/date/iso_week.h
+++ include/date/julian.h
+++ include/date/solar_hijri.h
+++ )
++ endif ()
++
++ # These used to be set with generator expressions,
++@@ -137,14 +143,16 @@ if( BUILD_TZ_LIB )
++ target_compile_definitions( date-tz PUBLIC DATE_BUILD_DLL=1 )
++ endif()
++
++- set(TZ_HEADERS include/date/tz.h)
+++ set(TZ_PUBLIC_HEADERS include/date/ptz.h include/date/tz.h)
+++ set(TZ_PRIVATE_HEADERS include/date/tz_private.h)
++
++ if( IOS )
++- list(APPEND TZ_HEADERS include/date/ios.h)
+++ list(APPEND TZ_PRIVATE_HEADERS include/date/ios.h)
++ endif( )
++ set_target_properties( date-tz PROPERTIES
++ POSITION_INDEPENDENT_CODE ON
++- PUBLIC_HEADER "${TZ_HEADERS}"
+++ PUBLIC_HEADER "${TZ_PUBLIC_HEADERS}"
+++ PRIVATE_HEADER "${TZ_PRIVATE_HEADERS}"
++ VERSION "${PROJECT_VERSION}"
++ SOVERSION "${ABI_VERSION}" )
++ if( NOT MSVC )
++@@ -170,7 +178,8 @@ write_basic_package_version_file( "${version_config}"
++
++ install( TARGETS date
++ EXPORT dateConfig
++- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
+++ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date
+++ PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
++ export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake )
++ if (CMAKE_VERSION VERSION_LESS 3.15)
++ install(
++@@ -182,6 +191,7 @@ if( BUILD_TZ_LIB )
++ install( TARGETS date-tz
++ EXPORT dateConfig
++ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date
+++ PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date
++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
++ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # This is for Windows
++--
++2.39.0
++
+diff --git a/tools/depends/target/date/0002-Fix-Android-build.patch b/tools/depends/target/date/0002-Fix-Android-build.patch
+new file mode 100644
+index 0000000000..e497b41bcf
+--- /dev/null
++++ b/tools/depends/target/date/0002-Fix-Android-build.patch
+@@ -0,0 +1,37 @@
++From 5790d6aed57bda1dc1577860d8382678100ebfa3 Mon Sep 17 00:00:00 2001
++From: Vasyl Gello <vasek.gello@gmail.com>
++Date: Wed, 11 Jan 2023 11:35:41 +0200
++Subject: [PATCH 2/4] Fix Android build
++
++Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
++---
++ src/tz.cpp | 6 ++++--
++ 1 file changed, 4 insertions(+), 2 deletions(-)
++
++diff --git a/src/tz.cpp b/src/tz.cpp
++index 26babbd..69bab7c 100644
++--- a/src/tz.cpp
+++++ b/src/tz.cpp
++@@ -141,7 +141,7 @@
++ # endif // HAS_REMOTE_API
++ #else // !_WIN32
++ # include <unistd.h>
++-# if !USE_OS_TZDB && !defined(INSTALL)
+++# if !USE_OS_TZDB && !defined(INSTALL) && !defined(__ANDROID__)
++ # include <wordexp.h>
++ # endif
++ # include <limits.h>
++@@ -245,7 +245,9 @@ expand_path(std::string path)
++ {
++ # if TARGET_OS_IPHONE
++ return date::iOSUtils::get_tzdata_path();
++-# else // !TARGET_OS_IPHONE
+++# elif defined(__ANDROID__)
+++ return "/data/local/tmp";
+++# else // !TARGET_OS_IPHONE && !__ANDROID__
++ ::wordexp_t w{};
++ std::unique_ptr<::wordexp_t, void(*)(::wordexp_t*)> hold{&w, ::wordfree};
++ ::wordexp(path.c_str(), &w, 0);
++--
++2.39.0
++
+diff --git a/tools/depends/target/date/0003-Fix-UWP-build.patch b/tools/depends/target/date/0003-Fix-UWP-build.patch
+new file mode 100644
+index 0000000000..683ed4126d
+--- /dev/null
++++ b/tools/depends/target/date/0003-Fix-UWP-build.patch
+@@ -0,0 +1,58 @@
++From 1e512b082eb99b5d9cd790158f3bdaefcbac823d Mon Sep 17 00:00:00 2001
++From: Vasyl Gello <vasek.gello@gmail.com>
++Date: Wed, 11 Jan 2023 09:58:34 +0200
++Subject: [PATCH 3/4] Fix UWP build
++
++We don't need download_folder to be determined.
++Bringing full UWP runtime just for that is an overkill.
++---
++ CMakeLists.txt | 4 ++++
++ src/tz.cpp | 17 +++++++++++++++++
++ 2 files changed, 21 insertions(+)
++
++diff --git a/CMakeLists.txt b/CMakeLists.txt
++index c0bda7f..0fa3dc2 100644
++--- a/CMakeLists.txt
+++++ b/CMakeLists.txt
++@@ -133,6 +133,10 @@ if( BUILD_TZ_LIB )
++ target_compile_definitions( date-tz PRIVATE AUTO_DOWNLOAD=1 HAS_REMOTE_API=1 )
++ endif()
++
+++ if(WINRT)
+++ target_compile_definitions( date-tz PRIVATE WINRT=1 INSTALL=. )
+++ endif()
+++
++ if ( USE_SYSTEM_TZ_DB AND NOT WIN32 AND NOT MANUAL_TZ_DB )
++ target_compile_definitions( date-tz PRIVATE INSTALL=. PUBLIC USE_OS_TZDB=1 )
++ else()
++diff --git a/src/tz.cpp b/src/tz.cpp
++index 69bab7c..aa15192 100644
++--- a/src/tz.cpp
+++++ b/src/tz.cpp
++@@ -234,6 +234,23 @@ get_download_folder()
++
++ # endif // !INSTALL
++
+++# else // WINRT
+++static
+++std::string
+++get_program_folder()
+++{
+++ return "";
+++}
+++
+++# ifndef INSTALL
+++
+++static
+++std::string
+++get_download_folder()
+++{
+++ return "";
+++}
+++# endif // !INSTALL
++ # endif // WINRT
++ # else // !_WIN32
++
++--
++2.39.0
++
+diff --git a/tools/depends/target/date/DATE-VERSION b/tools/depends/target/date/DATE-VERSION
+new file mode 100644
+index 0000000000..0608029104
+--- /dev/null
++++ b/tools/depends/target/date/DATE-VERSION
+@@ -0,0 +1,6 @@
++LIBNAME=date
++VERSION=3.0.1
++ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
++SHA512=6bdc7cba821d66e17a559250cc0ce0095808e9db81cec9e16eaa4c31abdfa705299c67b72016d9b06b302bc306d063e83a374eb00728071b83a5ad650d59034f
++BYPRODUCT=libdate-tz.a
++BYPRODUCT_WIN=date-tz.lib
+diff --git a/tools/depends/target/tzdata/001-cmakelists.patch b/tools/depends/target/tzdata/001-cmakelists.patch
+new file mode 100644
+index 0000000000..dda65fc542
+--- /dev/null
++++ b/tools/depends/target/tzdata/001-cmakelists.patch
+@@ -0,0 +1,11 @@
++diff --git a/CMakeLists.txt b/CMakeLists.txt
++new file mode 100644
++index 0000000..8a015cd
++--- /dev/null
+++++ b/CMakeLists.txt
++@@ -0,0 +1,5 @@
+++cmake_minimum_required(VERSION 3.2)
+++
+++project(kodi-tzdata)
+++
+++install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION ${INSTALL_DIR})
+diff --git a/tools/depends/target/tzdata/TZDATA-VERSION b/tools/depends/target/tzdata/TZDATA-VERSION
+new file mode 100644
+index 0000000000..7f92f0b74c
+--- /dev/null
++++ b/tools/depends/target/tzdata/TZDATA-VERSION
+@@ -0,0 +1,5 @@
++LIBNAME=tzdata
++VERSION=2022g
++ARCHIVE=$(LIBNAME)$(VERSION).tar.gz
++SHA512=7f79394295e00e3a24ebdbf9af3bc454a65f432a93b517e7e96c7f9db9949f6f5fdae9892a9d3789ff44ae0eb1bfe4744d36976b4624659af951d26414f94e65
++SOURCE_DIR=addons/resource.timezone/resources/tzdata
+--
+2.43.0
+
+
+From b4a3fafe6da802ae1d690ceb07fbf5a781e0301a Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Tue, 17 Mar 2020 13:12:04 -0700
+Subject: [PATCH 02/19] add resource.timezone
+
+---
+ addons/kodi.resource/addon.xml | 1 +
+ addons/kodi.resource/timezone.xsd | 16 ++++++++++++
+ addons/resource.timezone/addon.xml | 12 +++++++++
+ cmake/installdata/common/addons.txt | 1 +
+ system/addon-manifest.xml | 1 +
+ xbmc/ServiceManager.cpp | 20 +++++++++++++++
+ xbmc/addons/AddonBuilder.cpp | 3 +++
+ xbmc/addons/CMakeLists.txt | 2 ++
+ xbmc/addons/TimeZoneResource.cpp | 39 +++++++++++++++++++++++++++++
+ xbmc/addons/TimeZoneResource.h | 25 ++++++++++++++++++
+ xbmc/addons/addoninfo/AddonInfo.cpp | 3 ++-
+ xbmc/addons/addoninfo/AddonType.h | 1 +
+ xbmc/application/Application.cpp | 20 +++++++++++++++
+ 13 files changed, 143 insertions(+), 1 deletion(-)
+ create mode 100644 addons/kodi.resource/timezone.xsd
+ create mode 100644 addons/resource.timezone/addon.xml
+ create mode 100644 xbmc/addons/TimeZoneResource.cpp
+ create mode 100644 xbmc/addons/TimeZoneResource.h
+
+diff --git a/addons/kodi.resource/addon.xml b/addons/kodi.resource/addon.xml
+index 2bb3221b42..b615575744 100644
+--- a/addons/kodi.resource/addon.xml
++++ b/addons/kodi.resource/addon.xml
+@@ -7,4 +7,5 @@
+ <extension-point id="resource.language" schema="language.xsd"/>
+ <extension-point id="resource.uisounds" schema="uisounds.xsd"/>
+ <extension-point id="resource.images" schema="images.xsd"/>
++ <extension-point id="resource.timezone" schema="timezone.xsd"/>
+ </addon>
+diff --git a/addons/kodi.resource/timezone.xsd b/addons/kodi.resource/timezone.xsd
+new file mode 100644
+index 0000000000..990f8893ea
+--- /dev/null
++++ b/addons/kodi.resource/timezone.xsd
+@@ -0,0 +1,16 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd">
++<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
++ <xs:element name="extension">
++ <xs:complexType>
++ <xs:attribute name="point" type="xs:string" use="required"/>
++ <xs:attribute name="id" type="simpleIdentifier"/>
++ <xs:attribute name="name" type="xs:string"/>
++ </xs:complexType>
++ </xs:element>
++ <xs:simpleType name="simpleIdentifier">
++ <xs:restriction base="xs:string">
++ <xs:pattern value="kodi\.resource\.timezone"/>
++ </xs:restriction>
++ </xs:simpleType>
++</xs:schema>
+diff --git a/addons/resource.timezone/addon.xml b/addons/resource.timezone/addon.xml
+new file mode 100644
+index 0000000000..174b3f5687
+--- /dev/null
++++ b/addons/resource.timezone/addon.xml
+@@ -0,0 +1,12 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon id="resource.timezone" version="1.0.0" name="Kodi Timezone Database" provider-name="Team Kodi">
++ <requires>
++ <import addon="kodi.resource" version="1.0.0"/>
++ </requires>
++ <extension point="kodi.resource.timezone"/>
++ <extension point="kodi.addon.metadata">
++ <summary lang="en_GB">Kodi Timezone Database</summary>
++ <description lang="en_GB">Kodi Timezone Database</description>
++ <platform>all</platform>
++ </extension>
++</addon>
+diff --git a/cmake/installdata/common/addons.txt b/cmake/installdata/common/addons.txt
+index 263e57bb66..0411f8a3da 100644
+--- a/cmake/installdata/common/addons.txt
++++ b/cmake/installdata/common/addons.txt
+@@ -26,6 +26,7 @@ addons/metadata.tvshows.themoviedb.org.python/*
+ addons/repository.xbmc.org/*
+ addons/resource.images.weathericons.default/*
+ addons/resource.language.en_gb/*
++addons/resource.timezone/*
+ addons/resource.uisounds.kodi/*
+ addons/screensaver.xbmc.builtin.black/*
+ addons/screensaver.xbmc.builtin.dim/*
+diff --git a/system/addon-manifest.xml b/system/addon-manifest.xml
+index 7df13a6d6f..842e4e8206 100644
+--- a/system/addon-manifest.xml
++++ b/system/addon-manifest.xml
+@@ -38,6 +38,7 @@
+ <addon optional="true">repository.xbmc.org</addon>
+ <addon>resource.images.weathericons.default</addon>
+ <addon>resource.language.en_gb</addon>
++ <addon>resource.timezone</addon>
+ <addon>resource.uisounds.kodi</addon>
+ <addon>screensaver.xbmc.builtin.black</addon>
+ <addon>screensaver.xbmc.builtin.dim</addon>
+diff --git a/xbmc/ServiceManager.cpp b/xbmc/ServiceManager.cpp
+index 02d9900e9b..83070489e3 100644
+--- a/xbmc/ServiceManager.cpp
++++ b/xbmc/ServiceManager.cpp
+@@ -17,6 +17,7 @@
+ #include "addons/RepositoryUpdater.h"
+ #include "addons/Service.h"
+ #include "addons/VFSEntry.h"
++#include "addons/addoninfo/AddonType.h"
+ #include "addons/binary-addons/BinaryAddonManager.h"
+ #include "cores/DataCacheCore.h"
+ #include "cores/RetroPlayer/guibridge/GUIGameRenderManager.h"
+@@ -45,9 +46,14 @@
+ #endif
+ #include "storage/MediaManager.h"
+ #include "utils/FileExtensionProvider.h"
++#include "utils/URIUtils.h"
+ #include "utils/log.h"
+ #include "weather/WeatherManager.h"
+
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
++#include <date/tz.h>
++
+ using namespace KODI;
+
+ CServiceManager::CServiceManager() = default;
+@@ -79,6 +85,20 @@ bool CServiceManager::InitForTesting()
+ m_extsMimeSupportList.reset(new ADDONS::CExtsMimeSupportList(*m_addonMgr));
+ m_fileExtensionProvider.reset(new CFileExtensionProvider(*m_addonMgr));
+
++#if defined(DATE_INTERNAL_TZDATA)
++ ADDON::AddonPtr addon;
++ if (!m_addonMgr->GetAddon("resource.timezone", addon, ADDON::AddonType::RESOURCE_TIMEZONE,
++ ADDON::OnlyEnabled::CHOICE_YES))
++ {
++ CLog::LogF(LOGFATAL, "failed to find resource.timezone");
++ return false;
++ }
++
++ std::string tzdataPath = URIUtils::AddFileToFolder(addon->Path(), "resources", "tzdata");
++ CLog::LogF(LOGDEBUG, "tzdata path: {}", tzdataPath);
++ date::set_install(tzdataPath);
++#endif
++
+ init_level = 1;
+ return true;
+ }
+diff --git a/xbmc/addons/AddonBuilder.cpp b/xbmc/addons/AddonBuilder.cpp
+index 7d52ee0431..dd72a08705 100644
+--- a/xbmc/addons/AddonBuilder.cpp
++++ b/xbmc/addons/AddonBuilder.cpp
+@@ -19,6 +19,7 @@
+ #include "addons/Scraper.h"
+ #include "addons/Service.h"
+ #include "addons/Skin.h"
++#include "addons/TimeZoneResource.h"
+ #include "addons/UISoundsResource.h"
+ #include "addons/Webinterface.h"
+ #include "addons/addoninfo/AddonInfo.h"
+@@ -106,6 +107,8 @@ AddonPtr CAddonBuilder::Generate(const AddonInfoPtr& info, AddonType type)
+ return std::make_shared<CLanguageResource>(info);
+ case AddonType::RESOURCE_UISOUNDS:
+ return std::make_shared<CUISoundsResource>(info);
++ case AddonType::RESOURCE_TIMEZONE:
++ return std::make_shared<CTimeZoneResource>(info);
+ case AddonType::REPOSITORY:
+ return std::make_shared<CRepository>(info);
+ case AddonType::CONTEXTMENU_ITEM:
+diff --git a/xbmc/addons/CMakeLists.txt b/xbmc/addons/CMakeLists.txt
+index 947a885b4c..101371f12d 100644
+--- a/xbmc/addons/CMakeLists.txt
++++ b/xbmc/addons/CMakeLists.txt
+@@ -26,6 +26,7 @@ set(SOURCES Addon.cpp
+ ScreenSaver.cpp
+ Service.cpp
+ Skin.cpp
++ TimeZoneResource.cpp
+ UISoundsResource.cpp
+ VFSEntry.cpp
+ Visualization.cpp
+@@ -66,6 +67,7 @@ set(HEADERS Addon.h
+ ScreenSaver.h
+ Service.h
+ Skin.h
++ TimeZoneResource.h
+ UISoundsResource.h
+ VFSEntry.h
+ Visualization.h
+diff --git a/xbmc/addons/TimeZoneResource.cpp b/xbmc/addons/TimeZoneResource.cpp
+new file mode 100644
+index 0000000000..94246c9e18
+--- /dev/null
++++ b/xbmc/addons/TimeZoneResource.cpp
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2015-2018 Team Kodi
++ * This file is part of Kodi - https://kodi.tv
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ * See LICENSES/README.md for more information.
++ */
++#include "TimeZoneResource.h"
++
++#include "addons/addoninfo/AddonType.h"
++#include "utils/StringUtils.h"
++#include "utils/URIUtils.h"
++
++#include <date/tz.h>
++
++namespace ADDON
++{
++
++CTimeZoneResource::CTimeZoneResource(const AddonInfoPtr& addonInfo)
++ : CResource(addonInfo, AddonType::RESOURCE_TIMEZONE)
++{
++}
++
++bool CTimeZoneResource::IsAllowed(const std::string& file) const
++{
++ return true;
++}
++
++bool CTimeZoneResource::IsInUse() const
++{
++ return true;
++}
++
++void CTimeZoneResource::OnPostInstall(bool update, bool modal)
++{
++ date::reload_tzdb();
++}
++
++} // namespace ADDON
+diff --git a/xbmc/addons/TimeZoneResource.h b/xbmc/addons/TimeZoneResource.h
+new file mode 100644
+index 0000000000..a979f05dec
+--- /dev/null
++++ b/xbmc/addons/TimeZoneResource.h
+@@ -0,0 +1,25 @@
++/*
++ * Copyright (C) 2015-2018 Team Kodi
++ * This file is part of Kodi - https://kodi.tv
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ * See LICENSES/README.md for more information.
++ */
++
++#pragma once
++
++#include "addons/Resource.h"
++
++namespace ADDON
++{
++
++class CTimeZoneResource : public CResource
++{
++public:
++ explicit CTimeZoneResource(const AddonInfoPtr& addonInfo);
++ bool IsAllowed(const std::string& file) const override;
++ bool IsInUse() const override;
++ void OnPostInstall(bool update, bool modal) override;
++};
++
++} // namespace ADDON
+diff --git a/xbmc/addons/addoninfo/AddonInfo.cpp b/xbmc/addons/addoninfo/AddonInfo.cpp
+index 61bc087443..f82a947981 100644
+--- a/xbmc/addons/addoninfo/AddonInfo.cpp
++++ b/xbmc/addons/addoninfo/AddonInfo.cpp
+@@ -37,7 +37,7 @@ typedef struct
+ } TypeMapping;
+
+ // clang-format off
+-static constexpr const std::array<TypeMapping, 40> types =
++static constexpr const std::array<TypeMapping, 41> types =
+ {{
+ {"unknown", "", AddonType::UNKNOWN, 0, AddonInstanceSupport::SUPPORT_NONE, "" },
+ {"xbmc.metadata.scraper.albums", "", AddonType::SCRAPER_ALBUMS, 24016, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonAlbumInfo.png" },
+@@ -73,6 +73,7 @@ static constexpr const std::array<TypeMapping, 40> types =
+ {"xbmc.service", "", AddonType::SERVICE, 24018, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonService.png" },
+ {"kodi.resource.images", "", AddonType::RESOURCE_IMAGES, 24035, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonImages.png" },
+ {"kodi.resource.language", "", AddonType::RESOURCE_LANGUAGE, 24026, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonLanguage.png" },
++ {"kodi.resource.timezone", "", AddonType::RESOURCE_TIMEZONE, 24026, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonTimeZone.png" },
+ {"kodi.resource.uisounds", "", AddonType::RESOURCE_UISOUNDS, 24006, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonUISounds.png" },
+ {"kodi.resource.games", "", AddonType::RESOURCE_GAMES, 35209, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonGame.png" },
+ {"kodi.resource.font", "", AddonType::RESOURCE_FONT, 13303, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonFont.png" },
+diff --git a/xbmc/addons/addoninfo/AddonType.h b/xbmc/addons/addoninfo/AddonType.h
+index 9a0ee260cf..27f49e3138 100644
+--- a/xbmc/addons/addoninfo/AddonType.h
++++ b/xbmc/addons/addoninfo/AddonType.h
+@@ -46,6 +46,7 @@ enum class AddonType
+ AUDIODECODER,
+ RESOURCE_IMAGES,
+ RESOURCE_LANGUAGE,
++ RESOURCE_TIMEZONE,
+ RESOURCE_UISOUNDS,
+ RESOURCE_GAMES,
+ RESOURCE_FONT,
+diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp
+index 0765cf3723..ea1b152dfd 100644
+--- a/xbmc/application/Application.cpp
++++ b/xbmc/application/Application.cpp
+@@ -195,8 +195,12 @@
+ #include "pictures/GUIWindowSlideShow.h"
+ #include "utils/CharsetConverter.h"
+
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
+ #include <mutex>
+
++#include <date/tz.h>
++
+ using namespace ADDON;
+ using namespace XFILE;
+ #ifdef HAS_DVD_DRIVE
+@@ -435,6 +439,22 @@ bool CApplication::Create()
+ return false;
+ }
+
++ // Load timezone information
++#if defined(DATE_INTERNAL_TZDATA)
++ ADDON::AddonPtr addon;
++ if (!CServiceBroker::GetAddonMgr().GetAddon("resource.timezone", addon,
++ ADDON::AddonType::RESOURCE_TIMEZONE,
++ ADDON::OnlyEnabled::CHOICE_YES))
++ {
++ CLog::LogF(LOGFATAL, "failed to find resource.timezone");
++ return false;
++ }
++
++ std::string tzdataPath = URIUtils::AddFileToFolder(addon->Path(), "resources", "tzdata");
++ CLog::LogF(LOGDEBUG, "tzdata path: {}", tzdataPath);
++ date::set_install(tzdataPath);
++#endif
++
+ m_pActiveAE.reset(new ActiveAE::CActiveAE());
+ CServiceBroker::RegisterAE(m_pActiveAE.get());
+
+--
+2.43.0
+
+
+From 6f004e0aa809505cbf71612df44343a15011d0cf Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Mon, 10 Feb 2020 15:13:43 -0800
+Subject: [PATCH 03/19] CDateTime: convert to use std::chrono
+
+---
+ xbmc/XBDateTime.cpp | 604 ++++++++--------------
+ xbmc/XBDateTime.h | 31 +-
+ xbmc/platform/posix/PosixTimezone.cpp | 3 -
+ xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp | 2 -
+ 4 files changed, 244 insertions(+), 396 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index 85d5d466d3..47c6b8d1a6 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -17,29 +17,17 @@
+
+ #include <cstdlib>
+
+-#define SECONDS_PER_DAY 86400L
+-#define SECONDS_PER_HOUR 3600L
+-#define SECONDS_PER_MINUTE 60L
+-#define SECONDS_TO_FILETIME 10000000L
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
++#include <date/date.h>
++#include <date/iso_week.h>
++#include <date/tz.h>
+
+-static const char *DAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+-/////////////////////////////////////////////////
+-//
+-// CDateTimeSpan
+-//
+-
+-CDateTimeSpan::CDateTimeSpan()
+-{
+- m_timeSpan.highDateTime = 0;
+- m_timeSpan.lowDateTime = 0;
+-}
+-
+ CDateTimeSpan::CDateTimeSpan(const CDateTimeSpan& span)
+ {
+- m_timeSpan.highDateTime = span.m_timeSpan.highDateTime;
+- m_timeSpan.lowDateTime = span.m_timeSpan.lowDateTime;
++ m_timeSpan = span.m_timeSpan;
+ }
+
+ CDateTimeSpan::CDateTimeSpan(int day, int hour, int minute, int second)
+@@ -49,7 +37,7 @@ CDateTimeSpan::CDateTimeSpan(int day, int hour, int minute, int second)
+
+ bool CDateTimeSpan::operator >(const CDateTimeSpan& right) const
+ {
+- return KODI::TIME::CompareFileTime(&m_timeSpan, &right.m_timeSpan) > 0;
++ return m_timeSpan > right.m_timeSpan;
+ }
+
+ bool CDateTimeSpan::operator >=(const CDateTimeSpan& right) const
+@@ -59,7 +47,7 @@ bool CDateTimeSpan::operator >=(const CDateTimeSpan& right) const
+
+ bool CDateTimeSpan::operator <(const CDateTimeSpan& right) const
+ {
+- return KODI::TIME::CompareFileTime(&m_timeSpan, &right.m_timeSpan) < 0;
++ return m_timeSpan < right.m_timeSpan;
+ }
+
+ bool CDateTimeSpan::operator <=(const CDateTimeSpan& right) const
+@@ -69,7 +57,7 @@ bool CDateTimeSpan::operator <=(const CDateTimeSpan& right) const
+
+ bool CDateTimeSpan::operator ==(const CDateTimeSpan& right) const
+ {
+- return KODI::TIME::CompareFileTime(&m_timeSpan, &right.m_timeSpan) == 0;
++ return m_timeSpan == right.m_timeSpan;
+ }
+
+ bool CDateTimeSpan::operator !=(const CDateTimeSpan& right) const
+@@ -81,15 +69,7 @@ CDateTimeSpan CDateTimeSpan::operator +(const CDateTimeSpan& right) const
+ {
+ CDateTimeSpan left(*this);
+
+- LARGE_INTEGER timeLeft;
+- left.ToLargeInt(timeLeft);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeLeft.QuadPart+=timeRight.QuadPart;
+-
+- left.FromLargeInt(timeLeft);
++ left.m_timeSpan += right.m_timeSpan;
+
+ return left;
+ }
+@@ -98,122 +78,75 @@ CDateTimeSpan CDateTimeSpan::operator -(const CDateTimeSpan& right) const
+ {
+ CDateTimeSpan left(*this);
+
+- LARGE_INTEGER timeLeft;
+- left.ToLargeInt(timeLeft);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeLeft.QuadPart-=timeRight.QuadPart;
+-
+- left.FromLargeInt(timeLeft);
++ left.m_timeSpan -= right.m_timeSpan;
+
+ return left;
+ }
+
+ const CDateTimeSpan& CDateTimeSpan::operator +=(const CDateTimeSpan& right)
+ {
+- LARGE_INTEGER timeThis;
+- ToLargeInt(timeThis);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeThis.QuadPart+=timeRight.QuadPart;
+-
+- FromLargeInt(timeThis);
++ m_timeSpan += right.m_timeSpan;
+
+ return *this;
+ }
+
+ const CDateTimeSpan& CDateTimeSpan::operator -=(const CDateTimeSpan& right)
+ {
+- LARGE_INTEGER timeThis;
+- ToLargeInt(timeThis);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeThis.QuadPart-=timeRight.QuadPart;
+-
+- FromLargeInt(timeThis);
++ m_timeSpan -= right.m_timeSpan;
+
+ return *this;
+ }
+
+-void CDateTimeSpan::ToLargeInt(LARGE_INTEGER& time) const
+-{
+- time.u.HighPart = m_timeSpan.highDateTime;
+- time.u.LowPart = m_timeSpan.lowDateTime;
+-}
+-
+-void CDateTimeSpan::FromLargeInt(const LARGE_INTEGER& time)
+-{
+- m_timeSpan.highDateTime = time.u.HighPart;
+- m_timeSpan.lowDateTime = time.u.LowPart;
+-}
+-
+ void CDateTimeSpan::SetDateTimeSpan(int day, int hour, int minute, int second)
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
+-
+- time.QuadPart= static_cast<long long>(day) *SECONDS_PER_DAY*SECONDS_TO_FILETIME;
+- time.QuadPart+= static_cast<long long>(hour) *SECONDS_PER_HOUR*SECONDS_TO_FILETIME;
+- time.QuadPart+= static_cast<long long>(minute) *SECONDS_PER_MINUTE*SECONDS_TO_FILETIME;
+- time.QuadPart+= static_cast<long long>(second) *SECONDS_TO_FILETIME;
+-
+- FromLargeInt(time);
++ m_timeSpan = std::chrono::duration_cast<std::chrono::seconds>(date::days(day)) +
++ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(hour)) +
++ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::minutes(minute)) +
++ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::seconds(second));
+ }
+
+ void CDateTimeSpan::SetFromTimeString(const std::string& time) // hh:mm
+ {
+ if (time.size() >= 5 && time[2] == ':')
+ {
+- int hour = atoi(time.substr(0, 2).c_str());
+- int minutes = atoi(time.substr(3, 2).c_str());
+- SetDateTimeSpan(0,hour,minutes,0);
++ int hour = std::stoi(time.substr(0, 2));
++ int minutes = std::stoi(time.substr(3, 2));
++ SetDateTimeSpan(0, hour, minutes, 0);
+ }
+ }
+
+ int CDateTimeSpan::GetDays() const
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
+-
+- return (int)(time.QuadPart/SECONDS_TO_FILETIME)/SECONDS_PER_DAY;
++ return date::floor<date::days>(m_timeSpan).count();
+ }
+
+ int CDateTimeSpan::GetHours() const
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
++ auto time = date::floor<date::days>(m_timeSpan);
++ auto dp = date::make_time(m_timeSpan - time);
+
+- return (int)((time.QuadPart/SECONDS_TO_FILETIME)%SECONDS_PER_DAY)/SECONDS_PER_HOUR;
++ return dp.hours().count();
+ }
+
+ int CDateTimeSpan::GetMinutes() const
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
++ auto time = date::floor<date::days>(m_timeSpan);
++ auto dp = date::make_time(m_timeSpan - time);
+
+- return (int)((time.QuadPart/SECONDS_TO_FILETIME%SECONDS_PER_DAY)%SECONDS_PER_HOUR)/SECONDS_PER_MINUTE;
++ return dp.minutes().count();
+ }
+
+ int CDateTimeSpan::GetSeconds() const
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
++ auto time = date::floor<date::days>(m_timeSpan);
++ auto dp = date::make_time(m_timeSpan - time);
+
+- return (int)(((time.QuadPart/SECONDS_TO_FILETIME%SECONDS_PER_DAY)%SECONDS_PER_HOUR)%SECONDS_PER_MINUTE)%SECONDS_PER_MINUTE;
++ return dp.seconds().count();
+ }
+
+ int CDateTimeSpan::GetSecondsTotal() const
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
+-
+- return (int)(time.QuadPart/SECONDS_TO_FILETIME);
++ return std::chrono::duration_cast<std::chrono::seconds>(m_timeSpan).count();
+ }
+
+ void CDateTimeSpan::SetFromPeriod(const std::string &period)
+@@ -233,25 +166,26 @@ void CDateTimeSpan::SetFromPeriod(const std::string &period)
+ SetDateTimeSpan(days, 0, 0, 0);
+ }
+
+-/////////////////////////////////////////////////
+-//
+-// CDateTime
+-//
+-
+ CDateTime::CDateTime()
+ {
+ Reset();
+ }
+
+-CDateTime::CDateTime(const KODI::TIME::SystemTime& time)
++CDateTime::CDateTime(const KODI::TIME::SystemTime& systemTime)
+ {
+- // we store internally as a FileTime
+- m_state = ToFileTime(time, m_time) ? valid : invalid;
++ KODI::TIME::FileTime fileTime;
++ m_state = ToFileTime(systemTime, fileTime) ? valid : invalid;
++
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&fileTime, &time);
++ m_time = std::chrono::system_clock::from_time_t(time);
+ }
+
+-CDateTime::CDateTime(const KODI::TIME::FileTime& time) : m_time(time)
++CDateTime::CDateTime(const KODI::TIME::FileTime& fileTime)
+ {
+- SetValid(true);
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&fileTime, &time);
++ m_time = std::chrono::system_clock::from_time_t(time);
+ }
+
+ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time)
+@@ -261,12 +195,20 @@ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time)
+
+ CDateTime::CDateTime(const time_t& time)
+ {
+- m_state = ToFileTime(time, m_time) ? valid : invalid;
++ m_time = std::chrono::system_clock::from_time_t(time);
++ SetValid(true);
++}
++
++CDateTime::CDateTime(const std::chrono::system_clock::time_point& time)
++{
++ m_time = time;
++ SetValid(true);
+ }
+
+ CDateTime::CDateTime(const tm& time)
+ {
+- m_state = ToFileTime(time, m_time) ? valid : invalid;
++ m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&time)));
++ SetValid(true);
+ }
+
+ CDateTime::CDateTime(int year, int month, int day, int hour, int minute, int second)
+@@ -276,52 +218,66 @@ CDateTime::CDateTime(int year, int month, int day, int hour, int minute, int sec
+
+ CDateTime CDateTime::GetCurrentDateTime()
+ {
+- // get the current time
+- KODI::TIME::SystemTime time;
+- KODI::TIME::GetLocalTime(&time);
++ auto zone = date::make_zoned(date::current_zone(), std::chrono::system_clock::now());
+
+- return CDateTime(time);
++ return CDateTime(
++ std::chrono::duration_cast<std::chrono::seconds>(zone.get_local_time().time_since_epoch())
++ .count());
+ }
+
+ CDateTime CDateTime::GetUTCDateTime()
+ {
+- CDateTime time(GetCurrentDateTime());
+- time += GetTimezoneBias();
+- return time;
++ return CDateTime(std::chrono::system_clock::now());
+ }
+
+ const CDateTime& CDateTime::operator=(const KODI::TIME::SystemTime& right)
+ {
+- m_state = ToFileTime(right, m_time) ? valid : invalid;
++ KODI::TIME::FileTime fileTime;
++ m_state = ToFileTime(right, fileTime) ? valid : invalid;
++
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&fileTime, &time);
++ m_time = std::chrono::system_clock::from_time_t(time);
+
+ return *this;
+ }
+
+ const CDateTime& CDateTime::operator=(const KODI::TIME::FileTime& right)
+ {
+- m_time=right;
+- SetValid(true);
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&right, &time);
++ m_time = std::chrono::system_clock::from_time_t(time);
+
+ return *this;
+ }
+
+ const CDateTime& CDateTime::operator =(const time_t& right)
+ {
+- m_state = ToFileTime(right, m_time) ? valid : invalid;
++ m_time = std::chrono::system_clock::from_time_t(right);
++ SetValid(true);
+
+ return *this;
+ }
+
+ const CDateTime& CDateTime::operator =(const tm& right)
+ {
+- m_state = ToFileTime(right, m_time) ? valid : invalid;
++ m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
++ SetValid(true);
++
++ return *this;
++}
++
++const CDateTime& CDateTime::operator=(const std::chrono::system_clock::time_point& right)
++{
++ m_time = right;
++ SetValid(true);
+
+ return *this;
+ }
+
+ bool CDateTime::operator >(const CDateTime& right) const
+ {
+- return operator >(right.m_time);
++ return m_time > right.m_time;
+ }
+
+ bool CDateTime::operator >=(const CDateTime& right) const
+@@ -331,7 +287,7 @@ bool CDateTime::operator >=(const CDateTime& right) const
+
+ bool CDateTime::operator <(const CDateTime& right) const
+ {
+- return operator <(right.m_time);
++ return m_time < right.m_time;
+ }
+
+ bool CDateTime::operator <=(const CDateTime& right) const
+@@ -341,7 +297,7 @@ bool CDateTime::operator <=(const CDateTime& right) const
+
+ bool CDateTime::operator ==(const CDateTime& right) const
+ {
+- return operator ==(right.m_time);
++ return m_time == right.m_time;
+ }
+
+ bool CDateTime::operator !=(const CDateTime& right) const
+@@ -351,7 +307,10 @@ bool CDateTime::operator !=(const CDateTime& right) const
+
+ bool CDateTime::operator>(const KODI::TIME::FileTime& right) const
+ {
+- return KODI::TIME::CompareFileTime(&m_time, &right) > 0;
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&right, &time);
++
++ return m_time > std::chrono::system_clock::from_time_t(time);
+ }
+
+ bool CDateTime::operator>=(const KODI::TIME::FileTime& right) const
+@@ -361,7 +320,10 @@ bool CDateTime::operator>=(const KODI::TIME::FileTime& right) const
+
+ bool CDateTime::operator<(const KODI::TIME::FileTime& right) const
+ {
+- return KODI::TIME::CompareFileTime(&m_time, &right) < 0;
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&right, &time);
++
++ return m_time < std::chrono::system_clock::from_time_t(time);
+ }
+
+ bool CDateTime::operator<=(const KODI::TIME::FileTime& right) const
+@@ -371,7 +333,10 @@ bool CDateTime::operator<=(const KODI::TIME::FileTime& right) const
+
+ bool CDateTime::operator==(const KODI::TIME::FileTime& right) const
+ {
+- return KODI::TIME::CompareFileTime(&m_time, &right) == 0;
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&right, &time);
++
++ return m_time == std::chrono::system_clock::from_time_t(time);
+ }
+
+ bool CDateTime::operator!=(const KODI::TIME::FileTime& right) const
+@@ -420,10 +385,7 @@ bool CDateTime::operator!=(const KODI::TIME::SystemTime& right) const
+
+ bool CDateTime::operator >(const time_t& right) const
+ {
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator >(time);
++ return m_time > std::chrono::system_clock::from_time_t(right);
+ }
+
+ bool CDateTime::operator >=(const time_t& right) const
+@@ -433,10 +395,7 @@ bool CDateTime::operator >=(const time_t& right) const
+
+ bool CDateTime::operator <(const time_t& right) const
+ {
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator <(time);
++ return m_time < std::chrono::system_clock::from_time_t(right);
+ }
+
+ bool CDateTime::operator <=(const time_t& right) const
+@@ -446,10 +405,7 @@ bool CDateTime::operator <=(const time_t& right) const
+
+ bool CDateTime::operator ==(const time_t& right) const
+ {
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator ==(time);
++ return m_time == std::chrono::system_clock::from_time_t(right);
+ }
+
+ bool CDateTime::operator !=(const time_t& right) const
+@@ -459,10 +415,7 @@ bool CDateTime::operator !=(const time_t& right) const
+
+ bool CDateTime::operator >(const tm& right) const
+ {
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator >(time);
++ return m_time > std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
+ }
+
+ bool CDateTime::operator >=(const tm& right) const
+@@ -472,10 +425,7 @@ bool CDateTime::operator >=(const tm& right) const
+
+ bool CDateTime::operator <(const tm& right) const
+ {
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator <(time);
++ return m_time < std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
+ }
+
+ bool CDateTime::operator <=(const tm& right) const
+@@ -485,10 +435,7 @@ bool CDateTime::operator <=(const tm& right) const
+
+ bool CDateTime::operator ==(const tm& right) const
+ {
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator ==(time);
++ return m_time == std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
+ }
+
+ bool CDateTime::operator !=(const tm& right) const
+@@ -496,66 +443,64 @@ bool CDateTime::operator !=(const tm& right) const
+ return !operator ==(right);
+ }
+
+-CDateTime CDateTime::operator +(const CDateTimeSpan& right) const
++bool CDateTime::operator>(const std::chrono::system_clock::time_point& right) const
+ {
+- CDateTime left(*this);
++ return m_time > right;
++}
+
+- LARGE_INTEGER timeLeft;
+- left.ToLargeInt(timeLeft);
++bool CDateTime::operator>=(const std::chrono::system_clock::time_point& right) const
++{
++ return operator>(right) || operator==(right);
++}
+
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
++bool CDateTime::operator<(const std::chrono::system_clock::time_point& right) const
++{
++ return m_time < right;
++}
+
+- timeLeft.QuadPart+=timeRight.QuadPart;
++bool CDateTime::operator<=(const std::chrono::system_clock::time_point& right) const
++{
++ return operator<(right) || operator==(right);
++}
+
+- left.FromLargeInt(timeLeft);
++bool CDateTime::operator==(const std::chrono::system_clock::time_point& right) const
++{
++ return m_time == right;
++}
+
+- return left;
++bool CDateTime::operator!=(const std::chrono::system_clock::time_point& right) const
++{
++ return !operator==(right);
+ }
+
+-CDateTime CDateTime::operator -(const CDateTimeSpan& right) const
++CDateTime CDateTime::operator+(const CDateTimeSpan& right) const
+ {
+ CDateTime left(*this);
+
+- LARGE_INTEGER timeLeft;
+- left.ToLargeInt(timeLeft);
++ left.m_time + right.m_timeSpan;
+
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
++ return left;
++}
+
+- timeLeft.QuadPart-=timeRight.QuadPart;
++CDateTime CDateTime::operator-(const CDateTimeSpan& right) const
++{
++ CDateTime left(*this);
+
+- left.FromLargeInt(timeLeft);
++ left.m_time - right.m_timeSpan;
+
+ return left;
+ }
+
+ const CDateTime& CDateTime::operator +=(const CDateTimeSpan& right)
+ {
+- LARGE_INTEGER timeThis;
+- ToLargeInt(timeThis);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeThis.QuadPart+=timeRight.QuadPart;
+-
+- FromLargeInt(timeThis);
++ m_time += right.m_timeSpan;
+
+ return *this;
+ }
+
+ const CDateTime& CDateTime::operator -=(const CDateTimeSpan& right)
+ {
+- LARGE_INTEGER timeThis;
+- ToLargeInt(timeThis);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeThis.QuadPart-=timeRight.QuadPart;
+-
+- FromLargeInt(timeThis);
++ m_time -= right.m_timeSpan;
+
+ return *this;
+ }
+@@ -564,25 +509,16 @@ CDateTimeSpan CDateTime::operator -(const CDateTime& right) const
+ {
+ CDateTimeSpan left;
+
+- LARGE_INTEGER timeLeft;
+- left.ToLargeInt(timeLeft);
+-
+- LARGE_INTEGER timeThis;
+- ToLargeInt(timeThis);
+-
+- LARGE_INTEGER timeRight;
+- right.ToLargeInt(timeRight);
+-
+- timeLeft.QuadPart=timeThis.QuadPart-timeRight.QuadPart;
+-
+- left.FromLargeInt(timeLeft);
+-
++ left.m_timeSpan = std::chrono::duration_cast<std::chrono::seconds>(m_time - right.m_time);
+ return left;
+ }
+
+ CDateTime::operator KODI::TIME::FileTime() const
+ {
+- return m_time;
++ KODI::TIME::FileTime fileTime;
++ time_t time = std::chrono::system_clock::to_time_t(m_time);
++ KODI::TIME::TimeTToFileTime(time, &fileTime);
++ return fileTime;
+ }
+
+ void CDateTime::Archive(CArchive& ar)
+@@ -607,25 +543,38 @@ void CDateTime::Archive(CArchive& ar)
+ {
+ KODI::TIME::SystemTime st;
+ ar>>st;
+- ToFileTime(st, m_time);
++ ToTimePoint(st, m_time);
+ }
+ }
+ }
+
+ void CDateTime::Reset()
+ {
+- SetDateTime(1601, 1, 1, 0, 0, 0);
++ m_time = {};
+ SetValid(false);
+ }
+
+ void CDateTime::SetValid(bool yesNo)
+ {
+- m_state=yesNo ? valid : invalid;
++ m_state = yesNo ? valid : invalid;
+ }
+
+ bool CDateTime::IsValid() const
+ {
+- return m_state==valid;
++ return m_state == valid;
++}
++
++bool CDateTime::ToTimePoint(const KODI::TIME::SystemTime& systemTime,
++ std::chrono::system_clock::time_point& timePoint) const
++{
++ KODI::TIME::FileTime fileTime;
++ KODI::TIME::SystemTimeToFileTime(&systemTime, &fileTime);
++
++ time_t time;
++ KODI::TIME::FileTimeToTimeT(&fileTime, &time);
++
++ timePoint = std::chrono::system_clock::from_time_t(time);
++ return true;
+ }
+
+ bool CDateTime::ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const
+@@ -646,33 +595,6 @@ bool CDateTime::ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) c
+ return true;
+ }
+
+-bool CDateTime::ToFileTime(const tm& time, KODI::TIME::FileTime& fileTime) const
+-{
+- KODI::TIME::SystemTime st = {};
+-
+- st.year = time.tm_year + 1900;
+- st.month = time.tm_mon + 1;
+- st.dayOfWeek = time.tm_wday;
+- st.day = time.tm_mday;
+- st.hour = time.tm_hour;
+- st.minute = time.tm_min;
+- st.second = time.tm_sec;
+-
+- return SystemTimeToFileTime(&st, &fileTime) == 1;
+-}
+-
+-void CDateTime::ToLargeInt(LARGE_INTEGER& time) const
+-{
+- time.u.HighPart = m_time.highDateTime;
+- time.u.LowPart = m_time.lowDateTime;
+-}
+-
+-void CDateTime::FromLargeInt(const LARGE_INTEGER& time)
+-{
+- m_time.highDateTime = time.u.HighPart;
+- m_time.lowDateTime = time.u.LowPart;
+-}
+-
+ bool CDateTime::SetFromDateString(const std::string &date)
+ {
+ //! @todo STRING_CLEANUP
+@@ -714,80 +636,83 @@ bool CDateTime::SetFromDateString(const std::string &date)
+
+ int CDateTime::GetDay() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto ymd = date::year_month_day{dp};
+
+- return st.day;
++ return static_cast<unsigned int>(ymd.day());
+ }
+
+ int CDateTime::GetMonth() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto ymd = date::year_month_day{dp};
+
+- return st.month;
++ return static_cast<unsigned int>(ymd.month());
+ }
+
+ int CDateTime::GetYear() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto ymd = date::year_month_day{dp};
+
+- return st.year;
++ return static_cast<int>(ymd.year());
+ }
+
+ int CDateTime::GetHour() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto time = date::make_time(m_time - dp);
+
+- return st.hour;
++ return time.hours().count();
+ }
+
+ int CDateTime::GetMinute() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto time = date::make_time(m_time - dp);
+
+- return st.minute;
++ return time.minutes().count();
+ }
+
+ int CDateTime::GetSecond() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto time = date::make_time(m_time - dp);
+
+- return st.second;
++ return time.seconds().count();
+ }
+
+ int CDateTime::GetDayOfWeek() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto yww = iso_week::year_weeknum_weekday{dp};
+
+- return st.dayOfWeek;
++ return static_cast<unsigned int>(yww.weekday());
+ }
+
+ int CDateTime::GetMinuteOfDay() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
+- return st.hour * 60 + st.minute;
++ auto dp = date::floor<std::chrono::hours>(m_time);
++ ;
++ auto time = date::make_time(m_time - dp);
++
++ return time.hours().count() * 60 + time.minutes().count();
+ }
+
+ bool CDateTime::SetDateTime(int year, int month, int day, int hour, int minute, int second)
+ {
+- KODI::TIME::SystemTime st = {};
++ auto ymd = date::year(year) / month / day;
++ if (!ymd.ok())
++ {
++ SetValid(false);
++ return false;
++ }
+
+- st.year = year;
+- st.month = month;
+- st.day = day;
+- st.hour = hour;
+- st.minute = minute;
+- st.second = second;
++ m_time = date::sys_days(ymd) + std::chrono::hours(hour) + std::chrono::minutes(minute) +
++ std::chrono::seconds(second);
+
+- m_state = ToFileTime(st, m_time) ? valid : invalid;
+- return m_state == valid;
++ SetValid(true);
++ return true;
+ }
+
+ bool CDateTime::SetDate(int year, int month, int day)
+@@ -797,119 +722,75 @@ bool CDateTime::SetDate(int year, int month, int day)
+
+ bool CDateTime::SetTime(int hour, int minute, int second)
+ {
+- // 01.01.1601 00:00:00 is 0 as filetime
+- return SetDateTime(1601, 1, 1, hour, minute, second);
++ m_time = date::sys_seconds(std::chrono::seconds(0)) + std::chrono::hours(hour) +
++ std::chrono::minutes(minute) + std::chrono::seconds(second);
++
++ SetValid(true);
++ return true;
+ }
+
+-void CDateTime::GetAsSystemTime(KODI::TIME::SystemTime& time) const
++void CDateTime::GetAsSystemTime(KODI::TIME::SystemTime& systemTime) const
+ {
+- FileTimeToSystemTime(&m_time, &time);
++ const time_t time = std::chrono::system_clock::to_time_t(m_time);
++ KODI::TIME::FileTime fileTime;
++ ToFileTime(time, fileTime);
++ KODI::TIME::FileTimeToSystemTime(&fileTime, &systemTime);
+ }
+
+-#define UNIX_BASE_TIME 116444736000000000LL /* nanoseconds since epoch */
+ void CDateTime::GetAsTime(time_t& time) const
+ {
+- long long ll = (static_cast<long long>(m_time.highDateTime) << 32) + m_time.lowDateTime;
+- time=(time_t)((ll - UNIX_BASE_TIME) / 10000000);
++ time = std::chrono::system_clock::to_time_t(m_time);
+ }
+
+ void CDateTime::GetAsTm(tm& time) const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto t = std::chrono::system_clock::to_time_t(m_time);
+
+ time = {};
+- time.tm_year = st.year - 1900;
+- time.tm_mon = st.month - 1;
+- time.tm_wday = st.dayOfWeek;
+- time.tm_mday = st.day;
+- time.tm_hour = st.hour;
+- time.tm_min = st.minute;
+- time.tm_sec = st.second;
+- time.tm_isdst = -1;
+-
+- mktime(&time);
++ localtime_r(&t, &time);
+ }
+
+-void CDateTime::GetAsTimeStamp(KODI::TIME::FileTime& time) const
++std::chrono::system_clock::time_point CDateTime::GetAsTimePoint() const
+ {
+- KODI::TIME::LocalFileTimeToFileTime(&m_time, &time);
++ return m_time;
+ }
+
++// void CDateTime::GetAsTimeStamp(KODI::TIME::FileTime& fileTime) const
++// {
++// time_t time = std::chrono::system_clock::to_time_t(m_time);
++// KODI::TIME::TimeTToFileTime(time, &fileTime);
++// }
++
+ std::string CDateTime::GetAsDBDate() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
+-
+- return StringUtils::Format("{:04}-{:02}-{:02}", st.year, st.month, st.day);
++ return date::format("%F", m_time);
+ }
+
+ std::string CDateTime::GetAsDBTime() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
+-
+- return StringUtils::Format("{:02}:{:02}:{:02}", st.hour, st.minute, st.second);
++ auto sp = date::floor<std::chrono::seconds>(m_time);
++ return date::format("%T", sp);
+ }
+
+ std::string CDateTime::GetAsDBDateTime() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto sp = date::floor<std::chrono::seconds>(m_time);
+
+- return StringUtils::Format("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", st.year, st.month, st.day,
+- st.hour, st.minute, st.second);
++ return date::format("%F %T", sp);
+ }
+
+ std::string CDateTime::GetAsSaveString() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto sp = date::floor<std::chrono::seconds>(m_time);
+
+- return StringUtils::Format("{:04}{:02}{:02}_{:02}{:02}{:02}", st.year, st.month, st.day, st.hour,
+- st.minute, st.second);
++ return date::format("%Y%m%d_%H%M%S", sp);
+ }
+
+ bool CDateTime::SetFromUTCDateTime(const CDateTime &dateTime)
+ {
+- CDateTime tmp(dateTime);
+- tmp -= GetTimezoneBias();
+-
+- m_time = tmp.m_time;
+- m_state = tmp.m_state;
+- return m_state == valid;
+-}
+-
+-static bool bGotTimezoneBias = false;
+-
+-void CDateTime::ResetTimezoneBias(void)
+-{
+- bGotTimezoneBias = false;
+-}
+-
+-CDateTimeSpan CDateTime::GetTimezoneBias(void)
+-{
+- static CDateTimeSpan timezoneBias;
+-
+- if (!bGotTimezoneBias)
+- {
+- bGotTimezoneBias = true;
+- KODI::TIME::TimeZoneInformation tz;
+- switch (KODI::TIME::GetTimeZoneInformation(&tz))
+- {
+- case KODI::TIME::KODI_TIME_ZONE_ID_DAYLIGHT:
+- timezoneBias = CDateTimeSpan(0, 0, tz.bias + tz.daylightBias, 0);
+- break;
+- case KODI::TIME::KODI_TIME_ZONE_ID_STANDARD:
+- timezoneBias = CDateTimeSpan(0, 0, tz.bias + tz.standardBias, 0);
+- break;
+- case KODI::TIME::KODI_TIME_ZONE_ID_UNKNOWN:
+- timezoneBias = CDateTimeSpan(0, 0, tz.bias, 0);
+- break;
+- }
+- }
+-
+- return timezoneBias;
++ m_time = dateTime.m_time;
++ m_state = valid;
++ return true;
+ }
+
+ bool CDateTime::SetFromUTCDateTime(const time_t &dateTime)
+@@ -1511,60 +1392,31 @@ std::string CDateTime::GetAsLocalizedTime(TIME_FORMAT format, bool withSeconds /
+ CDateTime CDateTime::GetAsUTCDateTime() const
+ {
+ CDateTime time(m_time);
+- time += GetTimezoneBias();
+ return time;
+ }
+
+ std::string CDateTime::GetAsRFC1123DateTime() const
+ {
+- CDateTime time(GetAsUTCDateTime());
++ auto time = date::floor<std::chrono::seconds>(m_time);
+
+- int weekDay = time.GetDayOfWeek();
+- if (weekDay < 0)
+- weekDay = 0;
+- else if (weekDay > 6)
+- weekDay = 6;
+- if (weekDay != time.GetDayOfWeek())
+- CLog::Log(LOGWARNING, "Invalid day of week {} in {}", time.GetDayOfWeek(),
+- time.GetAsDBDateTime());
+-
+- int month = time.GetMonth();
+- if (month < 1)
+- month = 1;
+- else if (month > 12)
+- month = 12;
+- if (month != time.GetMonth())
+- CLog::Log(LOGWARNING, "Invalid month {} in {}", time.GetMonth(), time.GetAsDBDateTime());
+-
+- return StringUtils::Format("{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT", DAY_NAMES[weekDay],
+- time.GetDay(), MONTH_NAMES[month - 1], time.GetYear(), time.GetHour(),
+- time.GetMinute(), time.GetSecond());
++ return date::format("%a, %d %b %Y %T GMT", time);
+ }
+
+ std::string CDateTime::GetAsW3CDate() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
+-
+- return StringUtils::Format("{:04}-{:02}-{:02}", st.year, st.month, st.day);
++ return GetAsDBDate();
+ }
+
+ std::string CDateTime::GetAsW3CDateTime(bool asUtc /* = false */) const
+ {
+- CDateTime w3cDate = *this;
+- if (asUtc)
+- w3cDate = GetAsUTCDateTime();
+- KODI::TIME::SystemTime st;
+- w3cDate.GetAsSystemTime(st);
++ auto time = date::floor<std::chrono::seconds>(m_time);
+
+- std::string result = StringUtils::Format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}", st.year, st.month,
+- st.day, st.hour, st.minute, st.second);
+ if (asUtc)
+- return result + "Z";
++ return date::format("%FT%TZ", time);
++
++ auto zt = date::make_zoned(date::current_zone(), time);
+
+- CDateTimeSpan bias = GetTimezoneBias();
+- return result + StringUtils::Format("{}{:02}:{:02}", (bias.GetSecondsTotal() >= 0 ? '+' : '-'),
+- abs(bias.GetHours()), abs(bias.GetMinutes()));
++ return date::format("%FT%T%Ez", zt);
+ }
+
+ int CDateTime::MonthStringToMonthNum(const std::string& month)
+diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h
+index fb18e7c0de..60d06e417a 100644
+--- a/xbmc/XBDateTime.h
++++ b/xbmc/XBDateTime.h
+@@ -12,6 +12,7 @@
+ #include "utils/TimeFormat.h"
+ #include "utils/XTimeUtils.h"
+
++#include <chrono>
+ #include <string>
+
+ #include "PlatformDefs.h"
+@@ -21,7 +22,7 @@ class CDateTime;
+ class CDateTimeSpan
+ {
+ public:
+- CDateTimeSpan();
++ CDateTimeSpan() = default;
+ CDateTimeSpan(const CDateTimeSpan& span);
+ CDateTimeSpan& operator=(const CDateTimeSpan&) = default;
+ CDateTimeSpan(int day, int hour, int minute, int second);
+@@ -50,11 +51,7 @@ public:
+ int GetSecondsTotal() const;
+
+ private:
+- void ToLargeInt(LARGE_INTEGER& time) const;
+- void FromLargeInt(const LARGE_INTEGER& time);
+-
+-private:
+- KODI::TIME::FileTime m_timeSpan;
++ std::chrono::duration<int64_t> m_timeSpan{0};
+
+ friend class CDateTime;
+ };
+@@ -70,6 +67,7 @@ public:
+ explicit CDateTime(const KODI::TIME::FileTime& time);
+ explicit CDateTime(const time_t& time);
+ explicit CDateTime(const tm& time);
++ explicit CDateTime(const std::chrono::system_clock::time_point& time);
+ CDateTime(int year, int month, int day, int hour, int minute, int second);
+
+ static CDateTime GetCurrentDateTime();
+@@ -90,6 +88,7 @@ public:
+ const CDateTime& operator=(const KODI::TIME::FileTime& right);
+ const CDateTime& operator =(const time_t& right);
+ const CDateTime& operator =(const tm& right);
++ const CDateTime& operator=(const std::chrono::system_clock::time_point& right);
+
+ bool operator >(const CDateTime& right) const;
+ bool operator >=(const CDateTime& right) const;
+@@ -126,6 +125,13 @@ public:
+ bool operator ==(const tm& right) const;
+ bool operator !=(const tm& right) const;
+
++ bool operator>(const std::chrono::system_clock::time_point& right) const;
++ bool operator>=(const std::chrono::system_clock::time_point& right) const;
++ bool operator<(const std::chrono::system_clock::time_point& right) const;
++ bool operator<=(const std::chrono::system_clock::time_point& right) const;
++ bool operator==(const std::chrono::system_clock::time_point& right) const;
++ bool operator!=(const std::chrono::system_clock::time_point& right) const;
++
+ CDateTime operator +(const CDateTimeSpan& right) const;
+ CDateTime operator -(const CDateTimeSpan& right) const;
+
+@@ -170,7 +176,7 @@ public:
+ void GetAsSystemTime(KODI::TIME::SystemTime& time) const;
+ void GetAsTime(time_t& time) const;
+ void GetAsTm(tm& time) const;
+- void GetAsTimeStamp(KODI::TIME::FileTime& time) const;
++ std::chrono::system_clock::time_point GetAsTimePoint() const;
+
+ CDateTime GetAsUTCDateTime() const;
+ std::string GetAsSaveString() const;
+@@ -189,19 +195,14 @@ public:
+ void SetValid(bool yesNo);
+ bool IsValid() const;
+
+- static void ResetTimezoneBias(void);
+- static CDateTimeSpan GetTimezoneBias(void);
+-
+ private:
++ bool ToTimePoint(const KODI::TIME::SystemTime& time,
++ std::chrono::system_clock::time_point& timePoint) const;
+ bool ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const;
+ bool ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) const;
+- bool ToFileTime(const tm& time, KODI::TIME::FileTime& fileTime) const;
+-
+- void ToLargeInt(LARGE_INTEGER& time) const;
+- void FromLargeInt(const LARGE_INTEGER& time);
+
+ private:
+- KODI::TIME::FileTime m_time;
++ std::chrono::system_clock::time_point m_time;
+
+ typedef enum _STATE
+ {
+diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp
+index 6f276a37b2..ab2ddcf570 100644
+--- a/xbmc/platform/posix/PosixTimezone.cpp
++++ b/xbmc/platform/posix/PosixTimezone.cpp
+@@ -143,8 +143,6 @@ void CPosixTimezone::OnSettingChanged(const std::shared_ptr<const CSetting>& set
+ if (settingId == CSettings::SETTING_LOCALE_TIMEZONE)
+ {
+ SetTimezone(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
+-
+- CDateTime::ResetTimezoneBias();
+ }
+ else if (settingId == CSettings::SETTING_LOCALE_TIMEZONECOUNTRY)
+ {
+@@ -157,7 +155,6 @@ void CPosixTimezone::OnSettingChanged(const std::shared_ptr<const CSetting>& set
+ void CPosixTimezone::OnSettingsLoaded()
+ {
+ SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE));
+- CDateTime::ResetTimezoneBias();
+ }
+
+ std::vector<std::string> CPosixTimezone::GetCounties()
+diff --git a/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp b/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp
+index 53bbf815f6..4ae42a4803 100644
+--- a/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp
++++ b/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp
+@@ -205,8 +205,6 @@ bool CPVRGUIActionsDatabase::ResetDatabase(bool bResetEPGOnly)
+ bResetClients = selector.IsResetClientsSelected();
+ }
+
+- CDateTime::ResetTimezoneBias();
+-
+ CLog::LogFC(LOGDEBUG, LOGPVR, "PVR clearing {} database", bResetEPGOnly ? "EPG" : "PVR and EPG");
+
+ pDlgProgress->SetHeading(CVariant{313}); // "Cleaning database"
+--
+2.43.0
+
+
+From 8433756427c68642013698c678263b4d6ee5759d Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Mon, 17 Feb 2020 22:40:56 -0800
+Subject: [PATCH 04/19] fix TestDateTime to work with std::chrono
+
+---
+ xbmc/test/TestDateTime.cpp | 89 ++++++++------------------------------
+ 1 file changed, 17 insertions(+), 72 deletions(-)
+
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index 7e51df8cee..a553bd1887 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -13,6 +13,10 @@
+ #include <array>
+ #include <iostream>
+
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
++#include <date/date.h>
++#include <date/tz.h>
+ #include <gtest/gtest.h>
+
+ class TestDateTime : public testing::Test
+@@ -32,24 +36,6 @@ TEST_F(TestDateTime, DateTimeOperators)
+ EXPECT_FALSE(dateTime1 == dateTime2);
+ }
+
+-TEST_F(TestDateTime, FileTimeOperators)
+-{
+- CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+- CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+-
+- KODI::TIME::FileTime fileTime1;
+- KODI::TIME::FileTime fileTime2;
+-
+- dateTime1.GetAsTimeStamp(fileTime1);
+- dateTime2.GetAsTimeStamp(fileTime2);
+-
+- CDateTime dateTime3(fileTime1);
+-
+- EXPECT_TRUE(dateTime3 < fileTime2);
+- EXPECT_FALSE(dateTime3 > fileTime2);
+- EXPECT_FALSE(dateTime3 == fileTime2);
+-}
+-
+ TEST_F(TestDateTime, SystemTimeOperators)
+ {
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+@@ -161,7 +147,7 @@ TEST_F(TestDateTime, MonthStringToMonthNum)
+ }
+
+ // this method is broken as SetFromDBDate() will return true
+-TEST_F(TestDateTime, DISABLED_SetFromDateString)
++TEST_F(TestDateTime, SetFromDateString)
+ {
+ CDateTime dateTime;
+ EXPECT_TRUE(dateTime.SetFromDateString("tuesday may 14, 1991"));
+@@ -190,13 +176,7 @@ TEST_F(TestDateTime, SetFromDBDate)
+ EXPECT_EQ(dateTime.GetDay(), 2);
+ }
+
+-// disabled on osx and freebsd as their mktime functions
+-// don't work for dates before 1900
+-#if defined(TARGET_DARWIN_OSX) || defined(TARGET_FREEBSD)
+-TEST_F(TestDateTime, DISABLED_SetFromDBTime)
+-#else
+ TEST_F(TestDateTime, SetFromDBTime)
+-#endif
+ {
+ CDateTime dateTime1;
+ EXPECT_TRUE(dateTime1.SetFromDBTime("12:34"));
+@@ -237,10 +217,8 @@ TEST_F(TestDateTime, SetFromW3CDate)
+
+ TEST_F(TestDateTime, SetFromW3CDateTime)
+ {
+- CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+ CDateTime dateTime;
+ dateTime.SetFromDBDateTime("1994-11-05 13:15:30");
+- dateTime += bias;
+ std::string dateTimeStr = dateTime.GetAsDBDate() + "T" + dateTime.GetAsDBTime() + "Z";
+
+ CDateTime dateTime1;
+@@ -264,11 +242,8 @@ TEST_F(TestDateTime, SetFromW3CDateTime)
+
+ TEST_F(TestDateTime, SetFromUTCDateTime)
+ {
+- CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+-
+ CDateTime dateTime1;
+ dateTime1.SetFromDBDateTime("1991-05-14 12:34:56");
+- dateTime1 += bias;
+
+ CDateTime dateTime2;
+ EXPECT_TRUE(dateTime2.SetFromUTCDateTime(dateTime1));
+@@ -279,7 +254,7 @@ TEST_F(TestDateTime, SetFromUTCDateTime)
+ EXPECT_EQ(dateTime2.GetMinute(), 34);
+ EXPECT_EQ(dateTime2.GetSecond(), 56);
+
+- const time_t time = 674224496 + bias.GetSecondsTotal();
++ const time_t time = 674224496;
+
+ CDateTime dateTime3;
+ EXPECT_TRUE(dateTime3.SetFromUTCDateTime(time));
+@@ -325,18 +300,14 @@ TEST_F(TestDateTime, SetDateTime)
+ EXPECT_EQ(dateTime2.GetMinute(), 0);
+ EXPECT_EQ(dateTime2.GetSecond(), 0);
+
+-// disabled on osx and freebsd as their mktime functions
+-// don't work for dates before 1900
+-#if !defined(TARGET_DARWIN_OSX) && !defined(TARGET_FREEBSD)
+ CDateTime dateTime3;
+ EXPECT_TRUE(dateTime3.SetTime(12, 34, 56));
+- EXPECT_EQ(dateTime3.GetYear(), 1601);
++ EXPECT_EQ(dateTime3.GetYear(), 1970);
+ EXPECT_EQ(dateTime3.GetMonth(), 1);
+ EXPECT_EQ(dateTime3.GetDay(), 1);
+ EXPECT_EQ(dateTime3.GetHour(), 12);
+ EXPECT_EQ(dateTime3.GetMinute(), 34);
+ EXPECT_EQ(dateTime3.GetSecond(), 56);
+-#endif
+ }
+
+ TEST_F(TestDateTime, GetAsStrings)
+@@ -351,22 +322,20 @@ TEST_F(TestDateTime, GetAsStrings)
+ EXPECT_EQ(dateTime.GetAsW3CDate(), "1991-05-14");
+ }
+
+-// disabled because we have no way to validate these values
+-// GetTimezoneBias() always returns a positive value so
+-// there is no way to detect the direction of the offset
+-TEST_F(TestDateTime, DISABLED_GetAsStringsWithBias)
++TEST_F(TestDateTime, GetAsStringsWithBias)
+ {
+- CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+-
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+- CDateTime dateTimeWithBias(dateTime);
+- dateTimeWithBias += bias;
++ std::cout << dateTime.GetAsRFC1123DateTime() << std::endl;
++ std::cout << dateTime.GetAsW3CDateTime(false) << std::endl;
++ std::cout << dateTime.GetAsW3CDateTime(true) << std::endl;
+
+- EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 20:34:56 GMT");
+- EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T12:34:56+08:00");
+- EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T20:34:56Z");
++ auto zone = date::make_zoned(date::current_zone(), dateTime.GetAsTimePoint());
++
++ EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 12:34:56 GMT");
++ EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T05:34:56" + date::format("%Ez", zone));
++ EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T12:34:56Z");
+ }
+
+ TEST_F(TestDateTime, GetAsLocalized)
+@@ -604,31 +573,13 @@ TEST_F(TestDateTime, GetAsTm)
+ }
+ }
+
+-// Disabled pending std::chrono and std::date changes.
+-TEST_F(TestDateTime, DISABLED_GetAsTimeStamp)
+-{
+- CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+-
+- CDateTime dateTime;
+- dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+-
+- KODI::TIME::FileTime fileTime;
+- dateTime.GetAsTimeStamp(fileTime);
+- dateTime += bias;
+-
+- EXPECT_TRUE(dateTime == fileTime);
+-}
+-
+ TEST_F(TestDateTime, GetAsUTCDateTime)
+ {
+- CDateTimeSpan bias = CDateTime::GetTimezoneBias();
+-
+ CDateTime dateTime1;
+ dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ CDateTime dateTime2;
+ dateTime2 = dateTime1.GetAsUTCDateTime();
+- dateTime2 -= bias;
+
+ EXPECT_EQ(dateTime2.GetYear(), 1991);
+ EXPECT_EQ(dateTime2.GetMonth(), 5);
+@@ -638,20 +589,14 @@ TEST_F(TestDateTime, GetAsUTCDateTime)
+ EXPECT_EQ(dateTime2.GetSecond(), 56);
+ }
+
+-// disabled on osx and freebsd as their mktime functions
+-// don't work for dates before 1900
+-#if defined(TARGET_DARWIN_OSX) || defined(TARGET_FREEBSD)
+-TEST_F(TestDateTime, DISABLED_Reset)
+-#else
+ TEST_F(TestDateTime, Reset)
+-#endif
+ {
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ dateTime.Reset();
+
+- EXPECT_EQ(dateTime.GetYear(), 1601);
++ EXPECT_EQ(dateTime.GetYear(), 1970);
+ EXPECT_EQ(dateTime.GetMonth(), 1);
+ EXPECT_EQ(dateTime.GetDay(), 1);
+ EXPECT_EQ(dateTime.GetHour(), 0);
+--
+2.43.0
+
+
+From ca3e502a5c37975173e376653a167053928b5311 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Thu, 10 Sep 2020 08:19:35 -0700
+Subject: [PATCH 05/19] cleanup CPosixTimezone
+
+---
+ xbmc/application/Application.cpp | 6 +-
+ xbmc/platform/posix/PosixTimezone.cpp | 418 ++++++++++++++++----------
+ xbmc/platform/posix/PosixTimezone.h | 16 +-
+ 3 files changed, 266 insertions(+), 174 deletions(-)
+
+diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp
+index ea1b152dfd..3227933296 100644
+--- a/xbmc/application/Application.cpp
++++ b/xbmc/application/Application.cpp
+@@ -172,8 +172,9 @@
+ #include "utils/URIUtils.h"
+
+ #ifdef TARGET_POSIX
+-#include "platform/posix/XHandle.h"
+ #include "platform/posix/PlatformPosix.h"
++#include "platform/posix/PosixTimezone.h"
++#include "platform/posix/XHandle.h"
+ #endif
+
+ #if defined(TARGET_ANDROID)
+@@ -358,6 +359,9 @@ bool CApplication::Create()
+ // Register JobManager service
+ CServiceBroker::RegisterJobManager(std::make_shared<CJobManager>());
+
++ // Initialize,timezone
++ g_timezone.Init();
++
+ // Announcement service
+ m_pAnnouncementManager = std::make_shared<ANNOUNCEMENT::CAnnouncementManager>();
+ m_pAnnouncementManager->Start();
+diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp
+index ab2ddcf570..7f0b8d4096 100644
+--- a/xbmc/platform/posix/PosixTimezone.cpp
++++ b/xbmc/platform/posix/PosixTimezone.cpp
+@@ -6,7 +6,6 @@
+ * See LICENSES/README.md for more information.
+ */
+
+-#include <time.h>
+ #ifdef TARGET_ANDROID
+ #include "platform/android/bionic_supplement/bionic_supplement.h"
+ #endif
+@@ -23,120 +22,215 @@
+ #include "settings/SettingsComponent.h"
+ #include <stdlib.h>
+
+-#include <algorithm>
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
++#include "filesystem/File.h"
++#include "platform/MessagePrinter.h"
+
+-CPosixTimezone::CPosixTimezone()
++#include <date/tz.h>
++
++void CPosixTimezone::Init()
+ {
+- char* line = NULL;
+- size_t linelen = 0;
+- int nameonfourthfield = 0;
+- std::string s;
+- std::vector<std::string> tokens;
+-
+- // Load timezones
+- FILE* fp = fopen("/usr/share/zoneinfo/zone.tab", "r");
+- if (fp)
+- {
+- std::string countryCode;
+- std::string timezoneName;
+-
+- while (getdelim(&line, &linelen, '\n', fp) > 0)
++#if defined(DATE_INTERNAL_TZDATA)
++ XFILE::CFileStream zonetab;
++
++ if (!zonetab.Open("special://xbmc/addons/resource.timezone/resources/tzdata/zone.tab"))
++ {
++ CMessagePrinter::DisplayMessage("failed to open zone.tab");
++ return;
++ }
++
++ std::string countryCode;
++ std::string timezoneName;
++
++ std::vector<std::string> tokens;
++
++ for (std::string s; std::getline(zonetab, s);)
++ {
++ tokens.clear();
++ std::string line = s;
++ StringUtils::Trim(line);
++
++ if (line.length() == 0)
++ continue;
++
++ if (line[0] == '#')
++ continue;
++
++ StringUtils::Tokenize(line, tokens, " \t");
++ if (tokens.size() < 3)
++ continue;
++
++ countryCode = tokens[0];
++ timezoneName = tokens[2];
++
++ if (m_timezonesByCountryCode.count(countryCode) == 0)
++ {
++ std::vector<std::string> timezones;
++ timezones.push_back(timezoneName);
++ m_timezonesByCountryCode[countryCode] = timezones;
++ }
++ else
++ {
++ std::vector<std::string>& timezones = m_timezonesByCountryCode[countryCode];
++ timezones.push_back(timezoneName);
++ }
++
++ m_countriesByTimezoneName[timezoneName] = countryCode;
++ }
++
++ XFILE::CFileStream isotab;
++
++ if (!isotab.Open("special://xbmc/addons/resource.timezone/resources/tzdata/iso3166.tab"))
++ {
++ CMessagePrinter::DisplayMessage("failed to open iso3166.tab");
++ return;
++ }
++
++ std::string countryName;
++
++ for (std::string s; std::getline(isotab, s);)
++ {
++ tokens.clear();
++ std::string line = s;
++ StringUtils::Trim(line);
++
++ if (line.length() == 0)
++ continue;
++
++ if (line[0] == '#')
++ continue;
++
++ StringUtils::Tokenize(line, tokens, "\t");
++ if (tokens.size() < 2)
++ continue;
++
++ countryCode = tokens[0];
++ countryName = tokens[1];
++
++ m_countries.push_back(countryName);
++ m_countryByCode[countryCode] = countryName;
++ m_countryByName[countryName] = countryCode;
++ }
++
++ sort(m_countries.begin(), m_countries.end(), sortstringbyname());
++#else
++ char* line = NULL;
++ size_t linelen = 0;
++ int nameonfourthfield = 0;
++ std::string s;
++ std::vector<std::string> tokens;
++
++ // Load timezones
++ FILE* fp = fopen("/usr/share/zoneinfo/zone.tab", "r");
++ if (fp)
++ {
++ std::string countryCode;
++ std::string timezoneName;
++
++ while (getdelim(&line, &linelen, '\n', fp) > 0)
++ {
++ tokens.clear();
++ s = line;
++ StringUtils::Trim(s);
++
++ if (s.length() == 0)
++ continue;
++
++ if (s[0] == '#')
++ continue;
++
++ StringUtils::Tokenize(s, tokens, " \t");
++ if (tokens.size() < 3)
++ continue;
++
++ countryCode = tokens[0];
++ timezoneName = tokens[2];
++
++ if (m_timezonesByCountryCode.count(countryCode) == 0)
++ {
++ std::vector<std::string> timezones;
++ timezones.push_back(timezoneName);
++ m_timezonesByCountryCode[countryCode] = timezones;
++ }
++ else
+ {
+- tokens.clear();
+- s = line;
+- StringUtils::Trim(s);
+-
+- if (s.length() == 0)
+- continue;
+-
+- if (s[0] == '#')
+- continue;
+-
+- StringUtils::Tokenize(s, tokens, " \t");
+- if (tokens.size() < 3)
+- continue;
+-
+- countryCode = tokens[0];
+- timezoneName = tokens[2];
+-
+- if (m_timezonesByCountryCode.count(countryCode) == 0)
+- {
+- std::vector<std::string> timezones;
+- timezones.push_back(timezoneName);
+- m_timezonesByCountryCode[countryCode] = timezones;
+- }
+- else
+- {
+- std::vector<std::string>& timezones = m_timezonesByCountryCode[countryCode];
+- timezones.push_back(timezoneName);
+- }
+-
+- m_countriesByTimezoneName[timezoneName] = countryCode;
++ std::vector<std::string>& timezones = m_timezonesByCountryCode[countryCode];
++ timezones.push_back(timezoneName);
+ }
+- fclose(fp);
+- }
+-
+- if (line)
+- {
+- free(line);
+- line = NULL;
+- linelen = 0;
+- }
+-
+- // Load countries
+- fp = fopen("/usr/share/zoneinfo/iso3166.tab", "r");
+- if (!fp)
+- {
+- fp = fopen("/usr/share/misc/iso3166", "r");
+- nameonfourthfield = 1;
+- }
+- if (fp)
+- {
+- std::string countryCode;
+- std::string countryName;
+-
+- while (getdelim(&line, &linelen, '\n', fp) > 0)
++
++ m_countriesByTimezoneName[timezoneName] = countryCode;
++ }
++ fclose(fp);
++ }
++
++ if (line)
++ {
++ free(line);
++ line = NULL;
++ linelen = 0;
++ }
++
++ // Load countries
++ fp = fopen("/usr/share/zoneinfo/iso3166.tab", "r");
++ if (!fp)
++ {
++ fp = fopen("/usr/share/misc/iso3166", "r");
++ nameonfourthfield = 1;
++ }
++ if (fp)
++ {
++ std::string countryCode;
++ std::string countryName;
++
++ while (getdelim(&line, &linelen, '\n', fp) > 0)
++ {
++ s = line;
++ StringUtils::Trim(s);
++
++ //! @todo STRING_CLEANUP
++ if (s.length() == 0)
++ continue;
++
++ if (s[0] == '#')
++ continue;
++
++ // Search for the first non space from the 2nd character and on
++ int i = 2;
++ while (s[i] == ' ' || s[i] == '\t')
++ i++;
++
++ if (nameonfourthfield)
+ {
+- s = line;
+- StringUtils::Trim(s);
+-
+- //! @todo STRING_CLEANUP
+- if (s.length() == 0)
+- continue;
+-
+- if (s[0] == '#')
+- continue;
+-
+- // Search for the first non space from the 2nd character and on
+- int i = 2;
+- while (s[i] == ' ' || s[i] == '\t') i++;
+-
+- if (nameonfourthfield)
+- {
+- // skip three letter
+- while (s[i] != ' ' && s[i] != '\t') i++;
+- while (s[i] == ' ' || s[i] == '\t') i++;
+- // skip number
+- while (s[i] != ' ' && s[i] != '\t') i++;
+- while (s[i] == ' ' || s[i] == '\t') i++;
+- }
+-
+- countryCode = s.substr(0, 2);
+- countryName = s.substr(i);
+-
+- m_counties.push_back(countryName);
+- m_countryByCode[countryCode] = countryName;
+- m_countryByName[countryName] = countryCode;
++ // skip three letter
++ while (s[i] != ' ' && s[i] != '\t')
++ i++;
++ while (s[i] == ' ' || s[i] == '\t')
++ i++;
++ // skip number
++ while (s[i] != ' ' && s[i] != '\t')
++ i++;
++ while (s[i] == ' ' || s[i] == '\t')
++ i++;
+ }
+- sort(m_counties.begin(), m_counties.end(), sortstringbyname());
+- fclose(fp);
+- }
+- free(line);
++
++ countryCode = s.substr(0, 2);
++ countryName = s.substr(i);
++
++ m_countries.push_back(countryName);
++ m_countryByCode[countryCode] = countryName;
++ m_countryByName[countryName] = countryCode;
++ }
++ sort(m_countries.begin(), m_countries.end(), sortstringbyname());
++ fclose(fp);
++ }
++ free(line);
++#endif
+ }
+
+ void CPosixTimezone::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+ {
+- if (setting == NULL)
++ if (setting == nullptr)
+ return;
+
+ const std::string &settingId = setting->GetId();
+@@ -157,82 +251,74 @@ void CPosixTimezone::OnSettingsLoaded()
+ SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE));
+ }
+
+-std::vector<std::string> CPosixTimezone::GetCounties()
++std::vector<std::string> CPosixTimezone::GetCountries()
+ {
+- return m_counties;
++ return m_countries;
+ }
+
+ std::vector<std::string> CPosixTimezone::GetTimezonesByCountry(const std::string& country)
+ {
+- return m_timezonesByCountryCode[m_countryByName[country]];
++ return m_timezonesByCountryCode[m_countryByName[country]];
+ }
+
+ std::string CPosixTimezone::GetCountryByTimezone(const std::string& timezone)
+ {
+ #if defined(TARGET_DARWIN)
+- return "?";
+-#else
+- return m_countryByCode[m_countriesByTimezoneName[timezone]];
++ return "?";
+ #endif
++
++ return m_countryByCode[m_countriesByTimezoneName[timezone]];
+ }
+
+ void CPosixTimezone::SetTimezone(const std::string& timezoneName)
+ {
+-#if !defined(TARGET_DARWIN)
+- bool use_timezone = true;
+-#else
+- bool use_timezone = false;
++#if defined(TARGET_DARWIN)
++ return;
+ #endif
+
+- if (use_timezone)
+- {
+- static char env_var[255];
+- sprintf(env_var, "TZ=:%s", timezoneName.c_str());
+- putenv(env_var);
+- tzset();
+- }
++ setenv("TZ", timezoneName.c_str(), 1);
++ tzset();
+ }
+
+ std::string CPosixTimezone::GetOSConfiguredTimezone()
+ {
+- char timezoneName[255];
+-
+- // try Slackware approach first
+- ssize_t rlrc = readlink("/etc/localtime-copied-from"
+- , timezoneName, sizeof(timezoneName)-1);
+-
+- // RHEL and maybe other distros make /etc/localtime a symlink
+- if (rlrc == -1)
+- rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName)-1);
+-
+- if (rlrc != -1)
+- {
+- timezoneName[rlrc] = '\0';
+-
+- char* p = strrchr(timezoneName,'/');
+- if (p)
+- { // we want the previous '/'
+- char* q = p;
+- *q = 0;
+- p = strrchr(timezoneName,'/');
+- *q = '/';
+- if (p)
+- p++;
+- }
+- return p;
+- }
+-
+- // now try Debian approach
+- timezoneName[0] = 0;
+- FILE* fp = fopen("/etc/timezone", "r");
+- if (fp)
+- {
+- if (fgets(timezoneName, sizeof(timezoneName), fp))
+- timezoneName[strlen(timezoneName)-1] = '\0';
+- fclose(fp);
+- }
+-
+- return timezoneName;
++ char timezoneName[255];
++
++ // try Slackware approach first
++ ssize_t rlrc = readlink("/etc/localtime-copied-from", timezoneName, sizeof(timezoneName) - 1);
++
++ // RHEL and maybe other distros make /etc/localtime a symlink
++ if (rlrc == -1)
++ rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName) - 1);
++
++ if (rlrc != -1)
++ {
++ timezoneName[rlrc] = '\0';
++
++ char* p = strrchr(timezoneName, '/');
++ if (p)
++ { // we want the previous '/'
++ char* q = p;
++ *q = 0;
++ p = strrchr(timezoneName, '/');
++ *q = '/';
++ if (p)
++ p++;
++ }
++ return p;
++ }
++
++ // now try Debian approach
++ timezoneName[0] = 0;
++ FILE* fp = fopen("/etc/timezone", "r");
++ if (fp)
++ {
++ if (fgets(timezoneName, sizeof(timezoneName), fp))
++ timezoneName[strlen(timezoneName) - 1] = '\0';
++ fclose(fp);
++ }
++
++ return timezoneName;
+ }
+
+ void CPosixTimezone::SettingOptionsTimezoneCountriesFiller(
+@@ -241,9 +327,9 @@ void CPosixTimezone::SettingOptionsTimezoneCountriesFiller(
+ std::string& current,
+ void* data)
+ {
+- std::vector<std::string> countries = g_timezone.GetCounties();
+- for (unsigned int i = 0; i < countries.size(); i++)
+- list.emplace_back(countries[i], countries[i]);
++ std::vector<std::string> countries = g_timezone.GetCountries();
++ for (const auto& country : countries)
++ list.emplace_back(country, country);
+ }
+
+ void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr<const CSetting>& setting,
+@@ -254,12 +340,12 @@ void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr<const C
+ current = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ bool found = false;
+ std::vector<std::string> timezones = g_timezone.GetTimezonesByCountry(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY));
+- for (unsigned int i = 0; i < timezones.size(); i++)
++ for (const auto& timezone : timezones)
+ {
+- if (!found && StringUtils::EqualsNoCase(timezones[i], current))
++ if (!found && StringUtils::EqualsNoCase(timezone, current))
+ found = true;
+
+- list.emplace_back(timezones[i], timezones[i]);
++ list.emplace_back(timezone, timezone);
+ }
+
+ if (!found && !timezones.empty())
+diff --git a/xbmc/platform/posix/PosixTimezone.h b/xbmc/platform/posix/PosixTimezone.h
+index 076e87fa51..238c6daf85 100644
+--- a/xbmc/platform/posix/PosixTimezone.h
++++ b/xbmc/platform/posix/PosixTimezone.h
+@@ -21,7 +21,9 @@ struct StringSettingOption;
+ class CPosixTimezone : public ISettingCallback, public ISettingsHandler
+ {
+ public:
+- CPosixTimezone();
++ CPosixTimezone() = default;
++
++ void Init();
+
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+@@ -29,7 +31,7 @@ public:
+
+ std::string GetOSConfiguredTimezone();
+
+- std::vector<std::string> GetCounties();
++ std::vector<std::string> GetCountries();
+ std::vector<std::string> GetTimezonesByCountry(const std::string& country);
+ std::string GetCountryByTimezone(const std::string& timezone);
+
+@@ -46,12 +48,12 @@ public:
+ void* data);
+
+ private:
+- std::vector<std::string> m_counties;
+- std::map<std::string, std::string> m_countryByCode;
+- std::map<std::string, std::string> m_countryByName;
++ std::vector<std::string> m_countries;
++ std::map<std::string, std::string> m_countryByCode;
++ std::map<std::string, std::string> m_countryByName;
+
+- std::map<std::string, std::vector<std::string> > m_timezonesByCountryCode;
+- std::map<std::string, std::string> m_countriesByTimezoneName;
++ std::map<std::string, std::vector<std::string>> m_timezonesByCountryCode;
++ std::map<std::string, std::string> m_countriesByTimezoneName;
+ };
+
+ extern CPosixTimezone g_timezone;
+--
+2.43.0
+
+
+From a36c95155a2b8d22f69ad0809d895c11838620ed Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Thu, 10 Sep 2020 08:41:57 -0700
+Subject: [PATCH 06/19] utils/XTimeUtils: clean up SystemTime and FileTime
+ functions
+
+---
+ xbmc/XBDateTime.cpp | 213 ++----------------
+ xbmc/XBDateTime.h | 27 ---
+ .../addons/interfaces/gui/dialogs/Numeric.cpp | 10 +-
+ xbmc/dialogs/GUIDialogNumeric.cpp | 203 ++++++++++-------
+ xbmc/dialogs/GUIDialogNumeric.h | 12 +-
+ xbmc/filesystem/NFSDirectory.cpp | 18 +-
+ xbmc/guilib/GUIEditControl.cpp | 13 +-
+ xbmc/guilib/GUIRSSControl.h | 2 +-
+ xbmc/interfaces/legacy/Dialog.cpp | 44 ++--
+ xbmc/music/infoscanner/MusicInfoScanner.cpp | 4 +-
+ xbmc/network/upnp/UPnPInternal.cpp | 18 +-
+ xbmc/platform/posix/XTimeUtils.cpp | 211 -----------------
+ .../posix/filesystem/PosixDirectory.cpp | 5 +-
+ .../posix/filesystem/SMBDirectory.cpp | 16 +-
+ .../pvr/dialogs/GUIDialogPVRTimerSettings.cpp | 18 +-
+ xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h | 1 -
+ xbmc/pvr/windows/GUIWindowPVRGuide.cpp | 7 +-
+ xbmc/settings/windows/GUIControlSettings.cpp | 14 +-
+ xbmc/test/TestDateTime.cpp | 18 +-
+ xbmc/utils/Archive.cpp | 8 +-
+ xbmc/utils/Archive.h | 4 +-
+ xbmc/utils/RssManager.cpp | 2 +-
+ xbmc/utils/RssManager.h | 2 +-
+ xbmc/utils/RssReader.cpp | 25 +-
+ xbmc/utils/RssReader.h | 10 +-
+ xbmc/utils/XTimeUtils.h | 44 ----
+ xbmc/utils/test/TestArchive.cpp | 22 +-
+ xbmc/video/VideoInfoScanner.cpp | 4 +-
+ 28 files changed, 253 insertions(+), 722 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index 47c6b8d1a6..d8187b71e6 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -171,23 +171,6 @@ CDateTime::CDateTime()
+ Reset();
+ }
+
+-CDateTime::CDateTime(const KODI::TIME::SystemTime& systemTime)
+-{
+- KODI::TIME::FileTime fileTime;
+- m_state = ToFileTime(systemTime, fileTime) ? valid : invalid;
+-
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&fileTime, &time);
+- m_time = std::chrono::system_clock::from_time_t(time);
+-}
+-
+-CDateTime::CDateTime(const KODI::TIME::FileTime& fileTime)
+-{
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&fileTime, &time);
+- m_time = std::chrono::system_clock::from_time_t(time);
+-}
+-
+ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time)
+ {
+ m_state=time.m_state;
+@@ -230,27 +213,6 @@ CDateTime CDateTime::GetUTCDateTime()
+ return CDateTime(std::chrono::system_clock::now());
+ }
+
+-const CDateTime& CDateTime::operator=(const KODI::TIME::SystemTime& right)
+-{
+- KODI::TIME::FileTime fileTime;
+- m_state = ToFileTime(right, fileTime) ? valid : invalid;
+-
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&fileTime, &time);
+- m_time = std::chrono::system_clock::from_time_t(time);
+-
+- return *this;
+-}
+-
+-const CDateTime& CDateTime::operator=(const KODI::TIME::FileTime& right)
+-{
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&right, &time);
+- m_time = std::chrono::system_clock::from_time_t(time);
+-
+- return *this;
+-}
+-
+ const CDateTime& CDateTime::operator =(const time_t& right)
+ {
+ m_time = std::chrono::system_clock::from_time_t(right);
+@@ -305,84 +267,6 @@ bool CDateTime::operator !=(const CDateTime& right) const
+ return !operator ==(right);
+ }
+
+-bool CDateTime::operator>(const KODI::TIME::FileTime& right) const
+-{
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&right, &time);
+-
+- return m_time > std::chrono::system_clock::from_time_t(time);
+-}
+-
+-bool CDateTime::operator>=(const KODI::TIME::FileTime& right) const
+-{
+- return operator >(right) || operator ==(right);
+-}
+-
+-bool CDateTime::operator<(const KODI::TIME::FileTime& right) const
+-{
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&right, &time);
+-
+- return m_time < std::chrono::system_clock::from_time_t(time);
+-}
+-
+-bool CDateTime::operator<=(const KODI::TIME::FileTime& right) const
+-{
+- return operator <(right) || operator ==(right);
+-}
+-
+-bool CDateTime::operator==(const KODI::TIME::FileTime& right) const
+-{
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&right, &time);
+-
+- return m_time == std::chrono::system_clock::from_time_t(time);
+-}
+-
+-bool CDateTime::operator!=(const KODI::TIME::FileTime& right) const
+-{
+- return !operator ==(right);
+-}
+-
+-bool CDateTime::operator>(const KODI::TIME::SystemTime& right) const
+-{
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator >(time);
+-}
+-
+-bool CDateTime::operator>=(const KODI::TIME::SystemTime& right) const
+-{
+- return operator >(right) || operator ==(right);
+-}
+-
+-bool CDateTime::operator<(const KODI::TIME::SystemTime& right) const
+-{
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator <(time);
+-}
+-
+-bool CDateTime::operator<=(const KODI::TIME::SystemTime& right) const
+-{
+- return operator <(right) || operator ==(right);
+-}
+-
+-bool CDateTime::operator==(const KODI::TIME::SystemTime& right) const
+-{
+- KODI::TIME::FileTime time;
+- ToFileTime(right, time);
+-
+- return operator ==(time);
+-}
+-
+-bool CDateTime::operator!=(const KODI::TIME::SystemTime& right) const
+-{
+- return !operator ==(right);
+-}
+-
+ bool CDateTime::operator >(const time_t& right) const
+ {
+ return m_time > std::chrono::system_clock::from_time_t(right);
+@@ -477,7 +361,7 @@ CDateTime CDateTime::operator+(const CDateTimeSpan& right) const
+ {
+ CDateTime left(*this);
+
+- left.m_time + right.m_timeSpan;
++ left.m_time += right.m_timeSpan;
+
+ return left;
+ }
+@@ -486,7 +370,7 @@ CDateTime CDateTime::operator-(const CDateTimeSpan& right) const
+ {
+ CDateTime left(*this);
+
+- left.m_time - right.m_timeSpan;
++ left.m_time -= right.m_timeSpan;
+
+ return left;
+ }
+@@ -513,14 +397,6 @@ CDateTimeSpan CDateTime::operator -(const CDateTime& right) const
+ return left;
+ }
+
+-CDateTime::operator KODI::TIME::FileTime() const
+-{
+- KODI::TIME::FileTime fileTime;
+- time_t time = std::chrono::system_clock::to_time_t(m_time);
+- KODI::TIME::TimeTToFileTime(time, &fileTime);
+- return fileTime;
+-}
+-
+ void CDateTime::Archive(CArchive& ar)
+ {
+ if (ar.IsStoring())
+@@ -528,9 +404,7 @@ void CDateTime::Archive(CArchive& ar)
+ ar<<(int)m_state;
+ if (m_state==valid)
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
+- ar<<st;
++ ar << GetAsTimePoint();
+ }
+ }
+ else
+@@ -541,9 +415,7 @@ void CDateTime::Archive(CArchive& ar)
+ m_state = CDateTime::STATE(state);
+ if (m_state==valid)
+ {
+- KODI::TIME::SystemTime st;
+- ar>>st;
+- ToTimePoint(st, m_time);
++ ar >> m_time;
+ }
+ }
+ }
+@@ -564,37 +436,6 @@ bool CDateTime::IsValid() const
+ return m_state == valid;
+ }
+
+-bool CDateTime::ToTimePoint(const KODI::TIME::SystemTime& systemTime,
+- std::chrono::system_clock::time_point& timePoint) const
+-{
+- KODI::TIME::FileTime fileTime;
+- KODI::TIME::SystemTimeToFileTime(&systemTime, &fileTime);
+-
+- time_t time;
+- KODI::TIME::FileTimeToTimeT(&fileTime, &time);
+-
+- timePoint = std::chrono::system_clock::from_time_t(time);
+- return true;
+-}
+-
+-bool CDateTime::ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const
+-{
+- return KODI::TIME::SystemTimeToFileTime(&time, &fileTime) == 1 &&
+- (fileTime.lowDateTime > 0 || fileTime.highDateTime > 0);
+-}
+-
+-bool CDateTime::ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) const
+-{
+- long long ll = time;
+- ll *= 10000000ll;
+- ll += 0x19DB1DED53E8000LL;
+-
+- fileTime.lowDateTime = (DWORD)(ll & 0xFFFFFFFF);
+- fileTime.highDateTime = (DWORD)(ll >> 32);
+-
+- return true;
+-}
+-
+ bool CDateTime::SetFromDateString(const std::string &date)
+ {
+ //! @todo STRING_CLEANUP
+@@ -729,14 +570,6 @@ bool CDateTime::SetTime(int hour, int minute, int second)
+ return true;
+ }
+
+-void CDateTime::GetAsSystemTime(KODI::TIME::SystemTime& systemTime) const
+-{
+- const time_t time = std::chrono::system_clock::to_time_t(m_time);
+- KODI::TIME::FileTime fileTime;
+- ToFileTime(time, fileTime);
+- KODI::TIME::FileTimeToSystemTime(&fileTime, &systemTime);
+-}
+-
+ void CDateTime::GetAsTime(time_t& time) const
+ {
+ time = std::chrono::system_clock::to_time_t(m_time);
+@@ -755,12 +588,6 @@ std::chrono::system_clock::time_point CDateTime::GetAsTimePoint() const
+ return m_time;
+ }
+
+-// void CDateTime::GetAsTimeStamp(KODI::TIME::FileTime& fileTime) const
+-// {
+-// time_t time = std::chrono::system_clock::to_time_t(m_time);
+-// KODI::TIME::TimeTToFileTime(time, &fileTime);
+-// }
+-
+ std::string CDateTime::GetAsDBDate() const
+ {
+ return date::format("%F", m_time);
+@@ -1052,12 +879,9 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe
+ std::string strOut;
+ const std::string& strFormat = format.empty() ? g_langInfo.GetTimeFormat() : format;
+
+- KODI::TIME::SystemTime dateTime;
+- GetAsSystemTime(dateTime);
+-
+ // Prefetch meridiem symbol
+ const std::string& strMeridiem =
+- CLangInfo::MeridiemSymbolToString(dateTime.hour > 11 ? MeridiemSymbolPM : MeridiemSymbolAM);
++ CLangInfo::MeridiemSymbolToString(GetHour() > 11 ? MeridiemSymbolPM : MeridiemSymbolAM);
+
+ size_t length = strFormat.size();
+ for (size_t i=0; i < length; ++i)
+@@ -1106,7 +930,7 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe
+ i=length;
+ }
+
+- int hour = dateTime.hour;
++ int hour = GetHour();
+ if (c=='h')
+ { // recalc to 12 hour clock
+ if (hour > 11)
+@@ -1145,9 +969,9 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe
+ // Format minute string with the length of the mask
+ std::string str;
+ if (partLength==1)
+- str = std::to_string(dateTime.minute);
++ str = std::to_string(GetMinute());
+ else
+- str = StringUtils::Format("{:02}", dateTime.minute);
++ str = StringUtils::Format("{:02}", GetMinute());
+
+ strOut+=str;
+ }
+@@ -1174,9 +998,9 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe
+ // Format seconds string with the length of the mask
+ std::string str;
+ if (partLength==1)
+- str = std::to_string(dateTime.second);
++ str = std::to_string(GetSecond());
+ else
+- str = StringUtils::Format("{:02}", dateTime.second);
++ str = StringUtils::Format("{:02}", GetSecond());
+
+ strOut+=str;
+ }
+@@ -1215,9 +1039,6 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const
+ {
+ std::string strOut;
+
+- KODI::TIME::SystemTime dateTime;
+- GetAsSystemTime(dateTime);
+-
+ size_t length = strFormat.size();
+ for (size_t i = 0; i < length; ++i)
+ {
+@@ -1267,12 +1088,12 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const
+ // Format string with the length of the mask
+ std::string str;
+ if (partLength==1) // single-digit number
+- str = std::to_string(dateTime.day);
++ str = std::to_string(GetDay());
+ else if (partLength==2) // two-digit number
+- str = StringUtils::Format("{:02}", dateTime.day);
++ str = StringUtils::Format("{:02}", GetDay());
+ else // Day of week string
+ {
+- int wday = dateTime.dayOfWeek;
++ int wday = GetDayOfWeek();
+ if (wday < 1 || wday > 7) wday = 7;
+ str = g_localizeStrings.Get((c =='d' ? 40 : 10) + wday);
+ }
+@@ -1299,12 +1120,12 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const
+ // Format string with the length of the mask
+ std::string str;
+ if (partLength==1) // single-digit number
+- str = std::to_string(dateTime.month);
++ str = std::to_string(GetMonth());
+ else if (partLength==2) // two-digit number
+- str = StringUtils::Format("{:02}", dateTime.month);
++ str = StringUtils::Format("{:02}", GetMonth());
+ else // Month string
+ {
+- int wmonth = dateTime.month;
++ int wmonth = GetMonth();
+ if (wmonth < 1 || wmonth > 12) wmonth = 12;
+ str = g_localizeStrings.Get((c =='m' ? 50 : 20) + wmonth);
+ }
+@@ -1329,7 +1150,7 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const
+ }
+
+ // Format string with the length of the mask
+- std::string str = std::to_string(dateTime.year); // four-digit number
++ std::string str = std::to_string(GetYear()); // four-digit number
+ if (partLength <= 2)
+ str.erase(0, 2); // two-digit number
+
+diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h
+index 60d06e417a..16d6217c8a 100644
+--- a/xbmc/XBDateTime.h
++++ b/xbmc/XBDateTime.h
+@@ -63,8 +63,6 @@ public:
+ CDateTime();
+ CDateTime(const CDateTime& time);
+ CDateTime& operator=(const CDateTime&) = default;
+- explicit CDateTime(const KODI::TIME::SystemTime& time);
+- explicit CDateTime(const KODI::TIME::FileTime& time);
+ explicit CDateTime(const time_t& time);
+ explicit CDateTime(const tm& time);
+ explicit CDateTime(const std::chrono::system_clock::time_point& time);
+@@ -84,8 +82,6 @@ public:
+ static CDateTime FromUTCDateTime(const time_t &dateTime);
+ static CDateTime FromRFC1123DateTime(const std::string &dateTime);
+
+- const CDateTime& operator=(const KODI::TIME::SystemTime& right);
+- const CDateTime& operator=(const KODI::TIME::FileTime& right);
+ const CDateTime& operator =(const time_t& right);
+ const CDateTime& operator =(const tm& right);
+ const CDateTime& operator=(const std::chrono::system_clock::time_point& right);
+@@ -97,20 +93,6 @@ public:
+ bool operator ==(const CDateTime& right) const;
+ bool operator !=(const CDateTime& right) const;
+
+- bool operator>(const KODI::TIME::FileTime& right) const;
+- bool operator>=(const KODI::TIME::FileTime& right) const;
+- bool operator<(const KODI::TIME::FileTime& right) const;
+- bool operator<=(const KODI::TIME::FileTime& right) const;
+- bool operator==(const KODI::TIME::FileTime& right) const;
+- bool operator!=(const KODI::TIME::FileTime& right) const;
+-
+- bool operator>(const KODI::TIME::SystemTime& right) const;
+- bool operator>=(const KODI::TIME::SystemTime& right) const;
+- bool operator<(const KODI::TIME::SystemTime& right) const;
+- bool operator<=(const KODI::TIME::SystemTime& right) const;
+- bool operator==(const KODI::TIME::SystemTime& right) const;
+- bool operator!=(const KODI::TIME::SystemTime& right) const;
+-
+ bool operator >(const time_t& right) const;
+ bool operator >=(const time_t& right) const;
+ bool operator <(const time_t& right) const;
+@@ -140,8 +122,6 @@ public:
+
+ CDateTimeSpan operator -(const CDateTime& right) const;
+
+- operator KODI::TIME::FileTime() const;
+-
+ void Archive(CArchive& ar) override;
+
+ void Reset();
+@@ -173,7 +153,6 @@ public:
+ */
+ bool SetFromDBDateTime(const std::string &dateTime);
+
+- void GetAsSystemTime(KODI::TIME::SystemTime& time) const;
+ void GetAsTime(time_t& time) const;
+ void GetAsTm(tm& time) const;
+ std::chrono::system_clock::time_point GetAsTimePoint() const;
+@@ -195,12 +174,6 @@ public:
+ void SetValid(bool yesNo);
+ bool IsValid() const;
+
+-private:
+- bool ToTimePoint(const KODI::TIME::SystemTime& time,
+- std::chrono::system_clock::time_point& timePoint) const;
+- bool ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const;
+- bool ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) const;
+-
+ private:
+ std::chrono::system_clock::time_point m_time;
+
+diff --git a/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp b/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp
+index a78aa4af60..3acc48e768 100644
+--- a/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp
++++ b/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp
+@@ -135,12 +135,9 @@ bool Interface_GUIDialogNumeric::show_and_get_time(KODI_HANDLE kodiBase,
+ return false;
+ }
+
+- KODI::TIME::SystemTime systemTime;
+ CDateTime dateTime(*time);
+- dateTime.GetAsSystemTime(systemTime);
+- if (CGUIDialogNumeric::ShowAndGetTime(systemTime, heading))
++ if (CGUIDialogNumeric::ShowAndGetTime(dateTime, heading))
+ {
+- dateTime = systemTime;
+ dateTime.GetAsTm(*time);
+ return true;
+ }
+@@ -167,12 +164,9 @@ bool Interface_GUIDialogNumeric::show_and_get_date(KODI_HANDLE kodiBase,
+ return false;
+ }
+
+- KODI::TIME::SystemTime systemTime;
+ CDateTime dateTime(*date);
+- dateTime.GetAsSystemTime(systemTime);
+- if (CGUIDialogNumeric::ShowAndGetDate(systemTime, heading))
++ if (CGUIDialogNumeric::ShowAndGetDate(dateTime, heading))
+ {
+- dateTime = systemTime;
+ dateTime.GetAsTm(*date);
+ return true;
+ }
+diff --git a/xbmc/dialogs/GUIDialogNumeric.cpp b/xbmc/dialogs/GUIDialogNumeric.cpp
+index 182cf6a3af..bba219e44e 100644
+--- a/xbmc/dialogs/GUIDialogNumeric.cpp
++++ b/xbmc/dialogs/GUIDialogNumeric.cpp
+@@ -45,7 +45,7 @@ CGUIDialogNumeric::CGUIDialogNumeric(void)
+ m_lastblock{},
+ m_dirty{false}
+ {
+- memset(&m_datetime, 0, sizeof(KODI::TIME::SystemTime));
++ m_datetime.Reset();
+ m_loadType = KEEP_IN_MEMORY;
+ }
+
+@@ -231,59 +231,76 @@ void CGUIDialogNumeric::OnBackSpace()
+ }
+ else if (m_mode == INPUT_TIME)
+ {
++ int hour = m_datetime.GetHour();
++ int minute = m_datetime.GetMinute();
++
+ if (m_block == 0)
+- m_datetime.hour /= 10;
+- else if (m_datetime.minute)
+- m_datetime.minute /= 10;
++ hour /= 10;
++ else if (minute)
++ minute /= 10;
+ else
+ {
+ m_block = 0;
+ m_dirty = false;
+ }
++
++ m_datetime.SetTime(hour, minute, 0);
+ }
+ else if (m_mode == INPUT_TIME_SECONDS)
+ {
++ int hour = m_datetime.GetHour();
++ int minute = m_datetime.GetMinute();
++ int second = m_datetime.GetSecond();
++
+ if (m_block == 0)
+- m_datetime.hour /= 10;
++ hour /= 10;
+ else if (m_block == 1)
+ {
+- if (m_datetime.minute)
+- m_datetime.minute /= 10;
++ if (minute)
++ minute /= 10;
+ else
+ {
+ m_block = 0;
+ m_dirty = false;
+ }
+ }
+- else if (m_datetime.second)
+- m_datetime.minute /= 10;
++ else if (second)
++ minute /= 10;
+ else
+ {
+ m_block = 0;
+ m_dirty = false;
+ }
++
++ m_datetime.SetTime(hour, minute, second);
+ }
+ else if (m_mode == INPUT_DATE)
+ {
++ int day = m_datetime.GetDay();
++ int month = m_datetime.GetMonth();
++ int year = m_datetime.GetYear();
++
+ if (m_block == 0)
+- m_datetime.day /= 10;
++ day /= 10;
+ else if (m_block == 1)
+ {
+- if (m_datetime.month)
+- m_datetime.month /= 10;
++ if (month)
++ month /= 10;
+ else
+ {
+ m_block = 0;
+ m_dirty = false;
+ }
+ }
+- else if (m_datetime.year) // m_block == 2
+- m_datetime.year /= 10;
++ else if (year) // m_block == 2
++ year /= 10;
+ else
+ {
+ m_block = 1;
+ m_dirty = false;
+ }
++
++ m_datetime.SetDate(year, month, day);
+ }
+ }
+
+@@ -317,21 +334,21 @@ void CGUIDialogNumeric::FrameMove()
+ strLabel = m_number;
+ else if (m_mode == INPUT_TIME)
+ { // format up the time
+- strLabel = StringUtils::Format("{:2}:{:02}", m_datetime.hour, m_datetime.minute);
++ strLabel = StringUtils::Format("{:2}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute());
+ start = m_block * 3;
+ end = m_block * 3 + 2;
+ }
+ else if (m_mode == INPUT_TIME_SECONDS)
+ { // format up the time
+- strLabel = StringUtils::Format("{:2}:{:02}:{:02}", m_datetime.hour, m_datetime.minute,
+- m_datetime.second);
++ strLabel = StringUtils::Format("{:2}:{:02}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute(),
++ m_datetime.GetSecond());
+ start = m_block * 3;
+ end = m_block * 3 + 2;
+ }
+ else if (m_mode == INPUT_DATE)
+ { // format up the date
+- strLabel =
+- StringUtils::Format("{:2}/{:2}/{:4}", m_datetime.day, m_datetime.month, m_datetime.year);
++ strLabel = StringUtils::Format("{:2}/{:2}/{:4}", m_datetime.GetDay(), m_datetime.GetMonth(),
++ m_datetime.GetYear());
+ start = m_block * 3;
+ end = m_block * 3 + 2;
+ if (m_block == 2)
+@@ -377,7 +394,7 @@ void CGUIDialogNumeric::OnNumber(uint32_t num)
+ }
+ }
+
+-void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const KODI::TIME::SystemTime& initial)
++void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const CDateTime& initial)
+ {
+ m_mode = mode;
+ m_block = 0;
+@@ -425,7 +442,7 @@ void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const std::string &initial)
+ if (!dateTime.IsValid())
+ return;
+
+- dateTime.GetAsSystemTime(m_datetime);
++ m_datetime = dateTime;
+ m_lastblock = (m_mode == INPUT_DATE) ? 2 : 1;
+ }
+ else if (m_mode == INPUT_IP_ADDRESS)
+@@ -447,7 +464,7 @@ void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const std::string &initial)
+ m_number = initial;
+ }
+
+-KODI::TIME::SystemTime CGUIDialogNumeric::GetOutput() const
++CDateTime CGUIDialogNumeric::GetOutput() const
+ {
+ assert(m_mode == INPUT_TIME || m_mode == INPUT_TIME_SECONDS || m_mode == INPUT_DATE);
+ return m_datetime;
+@@ -458,13 +475,13 @@ std::string CGUIDialogNumeric::GetOutputString() const
+ switch (m_mode)
+ {
+ case INPUT_DATE:
+- return StringUtils::Format("{:02}/{:02}/{:04}", m_datetime.day, m_datetime.month,
+- m_datetime.year);
++ return StringUtils::Format("{:02}/{:02}/{:04}", m_datetime.GetDay(), m_datetime.GetMonth(),
++ m_datetime.GetYear());
+ case INPUT_TIME:
+- return StringUtils::Format("{}:{:02}", m_datetime.hour, m_datetime.minute);
++ return StringUtils::Format("{}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute());
+ case INPUT_TIME_SECONDS:
+- return StringUtils::Format("{}:{:02}:{:02}", m_datetime.hour, m_datetime.minute,
+- m_datetime.second);
++ return StringUtils::Format("{}:{:02}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute(),
++ m_datetime.GetSecond());
+ case INPUT_IP_ADDRESS:
+ return StringUtils::Format("{}.{}.{}.{}", m_ip[0], m_ip[1], m_ip[2], m_ip[3]);
+ case INPUT_NUMBER:
+@@ -480,23 +497,25 @@ bool CGUIDialogNumeric::ShowAndGetSeconds(std::string &timeString, const std::st
+ {
+ CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
+ if (!pDialog) return false;
+- int seconds = StringUtils::TimeStringToSeconds(timeString);
+- KODI::TIME::SystemTime time = {};
+- time.hour = seconds / 3600;
+- time.minute = (seconds - time.hour * 3600) / 60;
+- time.second = seconds - time.hour * 3600 - time.minute * 60;
+- pDialog->SetMode(INPUT_TIME_SECONDS, time);
++
++ std::chrono::system_clock::time_point time{
++ std::chrono::seconds{StringUtils::TimeStringToSeconds(timeString)}};
++
++ CDateTime datetime(time);
++
++ pDialog->SetMode(INPUT_TIME_SECONDS, datetime);
+ pDialog->SetHeading(heading);
+ pDialog->Open();
+ if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
+ return false;
+- time = pDialog->GetOutput();
+- seconds = time.hour * 3600 + time.minute * 60 + time.second;
+- timeString = StringUtils::SecondsToTimeString(seconds);
++ datetime = pDialog->GetOutput();
++ time = datetime.GetAsTimePoint();
++ timeString = StringUtils::SecondsToTimeString(
++ std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count());
+ return true;
+ }
+
+-bool CGUIDialogNumeric::ShowAndGetTime(KODI::TIME::SystemTime& time, const std::string& heading)
++bool CGUIDialogNumeric::ShowAndGetTime(CDateTime& time, const std::string& heading)
+ {
+ CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
+ if (!pDialog) return false;
+@@ -509,7 +528,7 @@ bool CGUIDialogNumeric::ShowAndGetTime(KODI::TIME::SystemTime& time, const std::
+ return true;
+ }
+
+-bool CGUIDialogNumeric::ShowAndGetDate(KODI::TIME::SystemTime& date, const std::string& heading)
++bool CGUIDialogNumeric::ShowAndGetDate(CDateTime& date, const std::string& heading)
+ {
+ CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
+ if (!pDialog) return false;
+@@ -680,28 +699,33 @@ void CGUIDialogNumeric::SetHeading(const std::string& strHeading)
+
+ void CGUIDialogNumeric::VerifyDate(bool checkYear)
+ {
+- if (m_datetime.day == 0)
+- m_datetime.day = 1;
+- if (m_datetime.month == 0)
+- m_datetime.month = 1;
++ int day = m_datetime.GetDay();
++ int month = m_datetime.GetMonth();
++ int year = m_datetime.GetYear();
++
++ if (day == 0)
++ day = 1;
++ if (month == 0)
++ month = 1;
+ // check for number of days in the month
+- if (m_datetime.day == 31)
++ if (day == 31)
+ {
+- if (m_datetime.month == 4 || m_datetime.month == 6 || m_datetime.month == 9 ||
+- m_datetime.month == 11)
+- m_datetime.day = 30;
++ if (month == 4 || month == 6 || month == 9 || month == 11)
++ day = 30;
+ }
+- if (m_datetime.month == 2 && m_datetime.day > 28)
++ if (month == 2 && day > 28)
+ {
+- m_datetime.day = 29; // max in february.
++ day = 29; // max in february.
+ if (checkYear)
+ {
+ // leap years occur when the year is divisible by 4 but not by 100, or the year is divisible by 400
+ // thus they don't occur, if the year has a remainder when divided by 4, or when the year is divisible by 100 but not by 400
+- if ((m_datetime.year % 4) || (!(m_datetime.year % 100) && (m_datetime.year % 400)))
+- m_datetime.day = 28;
++ if ((year % 4) || (!(year % 100) && (year % 400)))
++ day = 28;
+ }
+ }
++
++ m_datetime.SetDate(year, month, day);
+ }
+
+ void CGUIDialogNumeric::OnOK()
+@@ -741,17 +765,21 @@ void CGUIDialogNumeric::HandleInputIP(uint32_t num)
+
+ void CGUIDialogNumeric::HandleInputDate(uint32_t num)
+ {
++ int day = m_datetime.GetDay();
++ int month = m_datetime.GetMonth();
++ int year = m_datetime.GetYear();
++
+ if (m_block == 0) // day of month
+ {
+- if (m_dirty && (m_datetime.day < 3 || num < 2))
++ if (m_dirty && (day < 3 || num < 2))
+ {
+- m_datetime.day *= 10;
+- m_datetime.day += num;
++ day *= 10;
++ day += num;
+ }
+ else
+- m_datetime.day = num;
++ day = num;
+
+- if (m_datetime.day > 3)
++ if (day > 3)
+ {
+ m_block = 1; // move to months
+ m_dirty = false;
+@@ -763,13 +791,13 @@ void CGUIDialogNumeric::HandleInputDate(uint32_t num)
+ {
+ if (m_dirty && num < 3)
+ {
+- m_datetime.month *= 10;
+- m_datetime.month += num;
++ month *= 10;
++ month += num;
+ }
+ else
+- m_datetime.month = num;
++ month = num;
+
+- if (m_datetime.month > 1)
++ if (month > 1)
+ {
+ VerifyDate(false);
+ m_block = 2; // move to year
+@@ -780,15 +808,15 @@ void CGUIDialogNumeric::HandleInputDate(uint32_t num)
+ }
+ else // year
+ {
+- if (m_dirty && m_datetime.year < 1000) // have taken input
++ if (m_dirty && year < 1000) // have taken input
+ {
+- m_datetime.year *= 10;
+- m_datetime.year += num;
++ year *= 10;
++ year += num;
+ }
+ else
+- m_datetime.year = num;
++ year = num;
+
+- if (m_datetime.year > 1000)
++ if (year > 1000)
+ {
+ VerifyDate(true);
+ m_block = 0; // move to day of month
+@@ -797,22 +825,28 @@ void CGUIDialogNumeric::HandleInputDate(uint32_t num)
+ else
+ m_dirty = true;
+ }
++
++ m_datetime.SetDate(year, month, day);
+ }
+
+ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num)
+ {
++ int hour = m_datetime.GetHour();
++ int minute = m_datetime.GetMinute();
++ int second = m_datetime.GetSecond();
++
+ if (m_block == 0) // hour
+ {
+ if (m_dirty) // have input the first digit
+ {
+- m_datetime.hour *= 10;
+- m_datetime.hour += num;
++ hour *= 10;
++ hour += num;
+ m_block = 1; // move to minutes - allows up to 99 hours
+ m_dirty = false;
+ }
+ else // this is the first digit
+ {
+- m_datetime.hour = num;
++ hour = num;
+ m_dirty = true;
+ }
+ }
+@@ -820,14 +854,14 @@ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num)
+ {
+ if (m_dirty) // have input the first digit
+ {
+- m_datetime.minute *= 10;
+- m_datetime.minute += num;
++ minute *= 10;
++ minute += num;
+ m_block = 2; // move to seconds - allows up to 99 minutes
+ m_dirty = false;
+ }
+ else // this is the first digit
+ {
+- m_datetime.minute = num;
++ minute = num;
+ if (num > 5)
+ {
+ m_block = 2; // move to seconds
+@@ -841,14 +875,14 @@ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num)
+ {
+ if (m_dirty) // have input the first digit
+ {
+- m_datetime.second *= 10;
+- m_datetime.second += num;
++ second *= 10;
++ second += num;
+ m_block = 0; // move to hours
+ m_dirty = false;
+ }
+ else // this is the first digit
+ {
+- m_datetime.second = num;
++ second = num;
+ if (num > 5)
+ {
+ m_block = 0; // move to hours
+@@ -858,28 +892,33 @@ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num)
+ m_dirty = true;
+ }
+ }
++
++ m_datetime.SetTime(hour, minute, second);
+ }
+
+ void CGUIDialogNumeric::HandleInputTime(uint32_t num)
+ {
++ int hour = m_datetime.GetHour();
++ int minute = m_datetime.GetMinute();
++
+ if (m_block == 0) // hour
+ {
+ if (m_dirty) // have input the first digit
+ {
+- if (m_datetime.hour < 2 || num < 4)
++ if (hour < 2 || num < 4)
+ {
+- m_datetime.hour *= 10;
+- m_datetime.hour += num;
++ hour *= 10;
++ hour += num;
+ }
+ else
+- m_datetime.hour = num;
++ hour = num;
+
+ m_block = 1; // move to minutes
+ m_dirty = false;
+ }
+ else // this is the first digit
+ {
+- m_datetime.hour = num;
++ hour = num;
+
+ if (num > 2)
+ {
+@@ -894,14 +933,14 @@ void CGUIDialogNumeric::HandleInputTime(uint32_t num)
+ {
+ if (m_dirty) // have input the first digit
+ {
+- m_datetime.minute *= 10;
+- m_datetime.minute += num;
++ minute *= 10;
++ minute += num;
+ m_block = 0; // move to hours
+ m_dirty = false;
+ }
+ else // this is the first digit
+ {
+- m_datetime.minute = num;
++ minute = num;
+
+ if (num > 5)
+ {
+@@ -912,5 +951,7 @@ void CGUIDialogNumeric::HandleInputTime(uint32_t num)
+ m_dirty = true;
+ }
+ }
++
++ m_datetime.SetTime(hour, minute, 0);
+ }
+
+diff --git a/xbmc/dialogs/GUIDialogNumeric.h b/xbmc/dialogs/GUIDialogNumeric.h
+index 2932053021..d39b2702cd 100644
+--- a/xbmc/dialogs/GUIDialogNumeric.h
++++ b/xbmc/dialogs/GUIDialogNumeric.h
+@@ -8,8 +8,8 @@
+
+ #pragma once
+
++#include "XBDateTime.h"
+ #include "guilib/GUIDialog.h"
+-#include "utils/XTimeUtils.h"
+
+ #include <cstdint>
+
+@@ -41,13 +41,13 @@ public:
+ static InputVerificationResult ShowAndVerifyInput(std::string& strPassword, const std::string& strHeading, bool bGetUserInput);
+
+ void SetHeading(const std::string &strHeading);
+- void SetMode(INPUT_MODE mode, const KODI::TIME::SystemTime& initial);
++ void SetMode(INPUT_MODE mode, const CDateTime& initial);
+ void SetMode(INPUT_MODE mode, const std::string &initial);
+- KODI::TIME::SystemTime GetOutput() const;
++ CDateTime GetOutput() const;
+ std::string GetOutputString() const;
+
+- static bool ShowAndGetTime(KODI::TIME::SystemTime& time, const std::string& heading);
+- static bool ShowAndGetDate(KODI::TIME::SystemTime& date, const std::string& heading);
++ static bool ShowAndGetTime(CDateTime& time, const std::string& heading);
++ static bool ShowAndGetDate(CDateTime& date, const std::string& heading);
+ static bool ShowAndGetIPAddress(std::string &IPAddress, const std::string &heading);
+ static bool ShowAndGetNumber(std::string& strInput, const std::string &strHeading, unsigned int iAutoCloseTimeoutMs = 0, bool bSetHidden = false);
+ static bool ShowAndGetSeconds(std::string& timeString, const std::string &heading);
+@@ -73,7 +73,7 @@ protected:
+ bool m_bCanceled;
+
+ INPUT_MODE m_mode; // the current input mode
+- KODI::TIME::SystemTime m_datetime; // for time and date modes
++ CDateTime m_datetime; // for time and date modes
+ uint8_t m_ip[4]; // for ip address mode
+ uint32_t m_block; // for time, date, and IP methods.
+ uint32_t m_lastblock;
+diff --git a/xbmc/filesystem/NFSDirectory.cpp b/xbmc/filesystem/NFSDirectory.cpp
+index 7feba534c7..69a5dad0f6 100644
+--- a/xbmc/filesystem/NFSDirectory.cpp
++++ b/xbmc/filesystem/NFSDirectory.cpp
+@@ -202,7 +202,6 @@ bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ {
+ // We accept nfs://server/path[/file]]]]
+ int ret = 0;
+- KODI::TIME::FileTime fileTime, localTime;
+ std::unique_lock<CCriticalSection> lock(gNfsConnection);
+ std::string strDirName="";
+ std::string myStrPath(url.Get());
+@@ -248,7 +247,6 @@ bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ std::string path(myStrPath + strName);
+ int64_t iSize = 0;
+ bool bIsDir = false;
+- int64_t lTimeDate = 0;
+
+ //reslove symlinks
+ if(tmpDirent.type == NF3LNK)
+@@ -265,25 +263,13 @@ bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+
+ iSize = tmpDirent.size;
+ bIsDir = tmpDirent.type == NF3DIR;
+- lTimeDate = tmpDirent.mtime.tv_sec;
+
+ if (!StringUtils::EqualsNoCase(strName,".") && !StringUtils::EqualsNoCase(strName,"..")
+ && !StringUtils::EqualsNoCase(strName,"lost+found"))
+ {
+- if(lTimeDate == 0) // if modification date is missing, use create date
+- {
+- lTimeDate = tmpDirent.ctime.tv_sec;
+- }
+-
+- long long ll = lTimeDate & 0xffffffff;
+- ll *= 10000000ll;
+- ll += 116444736000000000ll;
+- fileTime.lowDateTime = (DWORD)(ll & 0xffffffff);
+- fileTime.highDateTime = (DWORD)(ll >> 32);
+- KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime);
+-
+ CFileItemPtr pItem(new CFileItem(tmpDirent.name));
+- pItem->m_dateTime=localTime;
++ pItem->m_dateTime =
++ tmpDirent.mtime.tv_sec != 0 ? tmpDirent.mtime.tv_sec : tmpDirent.ctime.tv_sec;
+ pItem->m_dwSize = iSize;
+
+ if (bIsDir)
+diff --git a/xbmc/guilib/GUIEditControl.cpp b/xbmc/guilib/GUIEditControl.cpp
+index 325a703152..f5c25adb37 100644
+--- a/xbmc/guilib/GUIEditControl.cpp
++++ b/xbmc/guilib/GUIEditControl.cpp
+@@ -297,11 +297,9 @@ void CGUIEditControl::OnClick()
+ {
+ CDateTime dateTime;
+ dateTime.SetFromDBTime(utf8);
+- KODI::TIME::SystemTime time;
+- dateTime.GetAsSystemTime(time);
+- if (CGUIDialogNumeric::ShowAndGetTime(time, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
++ if (CGUIDialogNumeric::ShowAndGetTime(
++ dateTime, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
+ {
+- dateTime = CDateTime(time);
+ utf8 = dateTime.GetAsLocalizedTime("", false);
+ textChanged = true;
+ }
+@@ -313,11 +311,10 @@ void CGUIEditControl::OnClick()
+ dateTime.SetFromDBDate(utf8);
+ if (dateTime < CDateTime(2000,1, 1, 0, 0, 0))
+ dateTime = CDateTime(2000, 1, 1, 0, 0, 0);
+- KODI::TIME::SystemTime date;
+- dateTime.GetAsSystemTime(date);
+- if (CGUIDialogNumeric::ShowAndGetDate(date, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
++
++ if (CGUIDialogNumeric::ShowAndGetDate(
++ dateTime, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
+ {
+- dateTime = CDateTime(date);
+ utf8 = dateTime.GetAsDBDate();
+ textChanged = true;
+ }
+diff --git a/xbmc/guilib/GUIRSSControl.h b/xbmc/guilib/GUIRSSControl.h
+index 851f72fb3c..1ade92dbaf 100644
+--- a/xbmc/guilib/GUIRSSControl.h
++++ b/xbmc/guilib/GUIRSSControl.h
+@@ -62,7 +62,7 @@ protected:
+ KODI::GUILIB::GUIINFO::CGUIInfoColor m_headlineColor;
+
+ std::vector<std::string> m_vecUrls;
+- std::vector<int> m_vecIntervals;
++ std::vector<std::chrono::nanoseconds> m_vecIntervals;
+ bool m_rtl;
+ CScrollInfo m_scrollInfo;
+ bool m_dirty;
+diff --git a/xbmc/interfaces/legacy/Dialog.cpp b/xbmc/interfaces/legacy/Dialog.cpp
+index 6dd00f179b..5e9db03da6 100644
+--- a/xbmc/interfaces/legacy/Dialog.cpp
++++ b/xbmc/interfaces/legacy/Dialog.cpp
+@@ -286,8 +286,8 @@ namespace XBMCAddon
+ {
+ DelayedCallGuard dcguard(languageHook);
+ std::string value;
+- KODI::TIME::SystemTime timedate;
+- KODI::TIME::GetLocalTime(&timedate);
++
++ auto timedate = CDateTime::GetCurrentDateTime();
+
+ if (!heading.empty())
+ {
+@@ -295,14 +295,13 @@ namespace XBMCAddon
+ {
+ if (!defaultt.empty() && defaultt.size() == 10)
+ {
+- const std::string& sDefault = defaultt;
+- timedate.day = atoi(sDefault.substr(0, 2).c_str());
+- timedate.month = atoi(sDefault.substr(3, 4).c_str());
+- timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str());
++ const std::string sDefault = defaultt;
++ timedate.SetFromDBDate(sDefault.substr(sDefault.size() - 4) + "-" +
++ sDefault.substr(3, 4) + "-" + sDefault.substr(0, 2));
+ }
+ if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading))
+- value =
+- StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, timedate.year);
++ value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.GetDay(), timedate.GetMonth(),
++ timedate.GetYear());
+ else
+ return emptyString;
+ }
+@@ -310,12 +309,11 @@ namespace XBMCAddon
+ {
+ if (!defaultt.empty() && defaultt.size() == 5)
+ {
+- const std::string& sDefault = defaultt;
+- timedate.hour = atoi(sDefault.substr(0, 2).c_str());
+- timedate.minute = atoi(sDefault.substr(3, 2).c_str());
++ const std::string sDefault = defaultt;
++ timedate.SetFromDBTime(sDefault.substr(0, 2) + ":" + sDefault.substr(3, 2));
+ }
+ if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading))
+- value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute);
++ value = StringUtils::Format("{:2}:{:02}", timedate.GetHour(), timedate.GetMinute());
+ else
+ return emptyString;
+ }
+@@ -367,8 +365,8 @@ namespace XBMCAddon
+ {
+ DelayedCallGuard dcguard(languageHook);
+ std::string value(defaultt);
+- KODI::TIME::SystemTime timedate;
+- KODI::TIME::GetLocalTime(&timedate);
++
++ auto timedate = CDateTime::GetCurrentDateTime();
+
+ switch (type)
+ {
+@@ -389,14 +387,13 @@ namespace XBMCAddon
+ {
+ if (!defaultt.empty() && defaultt.size() == 10)
+ {
+- const std::string& sDefault = defaultt;
+- timedate.day = atoi(sDefault.substr(0, 2).c_str());
+- timedate.month = atoi(sDefault.substr(3, 4).c_str());
+- timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str());
++ const std::string sDefault = defaultt;
++ timedate.SetFromDBDate(sDefault.substr(sDefault.size() - 4) + "-" +
++ sDefault.substr(3, 4) + "-" + sDefault.substr(0, 2));
+ }
+ if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading))
+- value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month,
+- timedate.year);
++ value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.GetDay(), timedate.GetMonth(),
++ timedate.GetYear());
+ else
+ value = emptyString;
+ }
+@@ -405,12 +402,11 @@ namespace XBMCAddon
+ {
+ if (!defaultt.empty() && defaultt.size() == 5)
+ {
+- const std::string& sDefault = defaultt;
+- timedate.hour = atoi(sDefault.substr(0, 2).c_str());
+- timedate.minute = atoi(sDefault.substr(3, 2).c_str());
++ const std::string sDefault = defaultt;
++ timedate.SetFromDBTime(sDefault.substr(0, 2) + ":" + sDefault.substr(3, 2));
+ }
+ if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading))
+- value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute);
++ value = StringUtils::Format("{:2}:{:02}", timedate.GetHour(), timedate.GetMinute());
+ else
+ value = emptyString;
+ }
+diff --git a/xbmc/music/infoscanner/MusicInfoScanner.cpp b/xbmc/music/infoscanner/MusicInfoScanner.cpp
+index f828b44914..3912892343 100644
+--- a/xbmc/music/infoscanner/MusicInfoScanner.cpp
++++ b/xbmc/music/infoscanner/MusicInfoScanner.cpp
+@@ -1269,8 +1269,8 @@ int CMusicInfoScanner::GetPathHash(const CFileItemList &items, std::string &hash
+ const CFileItemPtr pItem = items[i];
+ digest.Update(pItem->GetPath());
+ digest.Update((unsigned char *)&pItem->m_dwSize, sizeof(pItem->m_dwSize));
+- KODI::TIME::FileTime time = pItem->m_dateTime;
+- digest.Update((unsigned char*)&time, sizeof(KODI::TIME::FileTime));
++ const auto time = pItem->m_dateTime.GetAsTimePoint().time_since_epoch().count();
++ digest.Update(&time, sizeof(std::chrono::nanoseconds));
+ if (pItem->IsAudio() && !pItem->IsPlayList() && !pItem->IsNFO())
+ count++;
+ }
+diff --git a/xbmc/network/upnp/UPnPInternal.cpp b/xbmc/network/upnp/UPnPInternal.cpp
+index 406615768a..1a98b86c50 100644
+--- a/xbmc/network/upnp/UPnPInternal.cpp
++++ b/xbmc/network/upnp/UPnPInternal.cpp
+@@ -1067,11 +1067,19 @@ std::shared_ptr<CFileItem> BuildObject(PLT_MediaObject* entry,
+ }
+
+ // look for date?
+- if(entry->m_Description.date.GetLength()) {
+- KODI::TIME::SystemTime time = {};
+- sscanf(entry->m_Description.date, "%hu-%hu-%huT%hu:%hu:%hu", &time.year, &time.month, &time.day,
+- &time.hour, &time.minute, &time.second);
+- pItem->m_dateTime = time;
++ if (entry->m_Description.date.GetLength())
++ {
++ int year;
++ int month;
++ int day;
++ int hour;
++ int minute;
++ int second;
++
++ sscanf(entry->m_Description.date, "%u-%u-%uT%u:%u:%u", &year, &month, &day, &hour, &minute,
++ &second);
++
++ pItem->m_dateTime.SetDateTime(year, month, day, hour, minute, second);
+ }
+
+ // if there is a thumbnail available set it here
+diff --git a/xbmc/platform/posix/XTimeUtils.cpp b/xbmc/platform/posix/XTimeUtils.cpp
+index e78e5cff48..7d4b17686a 100644
+--- a/xbmc/platform/posix/XTimeUtils.cpp
++++ b/xbmc/platform/posix/XTimeUtils.cpp
+@@ -8,221 +8,10 @@
+
+ #include "utils/XTimeUtils.h"
+
+-#include "PosixTimezone.h"
+-
+-#include <errno.h>
+-#include <mutex>
+-#include <time.h>
+-
+-#include <sys/times.h>
+-
+-#if defined(TARGET_ANDROID) && !defined(__LP64__)
+-#include <time64.h>
+-#endif
+-
+-#define WIN32_TIME_OFFSET ((unsigned long long)(369 * 365 + 89) * 24 * 3600 * 10000000)
+-
+ namespace KODI
+ {
+ namespace TIME
+ {
+
+-/*
+- * A Leap year is any year that is divisible by four, but not by 100 unless also
+- * divisible by 400
+- */
+-#define IsLeapYear(y) ((!(y % 4)) ? (((!(y % 400)) && (y % 100)) ? 1 : 0) : 0)
+-
+-uint32_t GetTimeZoneInformation(TimeZoneInformation* timeZoneInformation)
+-{
+- if (!timeZoneInformation)
+- return KODI_TIME_ZONE_ID_INVALID;
+-
+- struct tm t;
+- time_t tt = time(NULL);
+- if (localtime_r(&tt, &t))
+- timeZoneInformation->bias = -t.tm_gmtoff / 60;
+-
+- timeZoneInformation->standardName = tzname[0];
+- timeZoneInformation->daylightName = tzname[1];
+-
+- return KODI_TIME_ZONE_ID_UNKNOWN;
+-}
+-
+-void GetLocalTime(SystemTime* systemTime)
+-{
+- const time_t t = time(NULL);
+- struct tm now;
+-
+- localtime_r(&t, &now);
+- systemTime->year = now.tm_year + 1900;
+- systemTime->month = now.tm_mon + 1;
+- systemTime->dayOfWeek = now.tm_wday;
+- systemTime->day = now.tm_mday;
+- systemTime->hour = now.tm_hour;
+- systemTime->minute = now.tm_min;
+- systemTime->second = now.tm_sec;
+- systemTime->milliseconds = 0;
+- // NOTE: localtime_r() is not required to set this, but we Assume that it's set here.
+- g_timezone.m_IsDST = now.tm_isdst;
+-}
+-
+-int FileTimeToLocalFileTime(const FileTime* fileTime, FileTime* localFileTime)
+-{
+- ULARGE_INTEGER l;
+- l.u.LowPart = fileTime->lowDateTime;
+- l.u.HighPart = fileTime->highDateTime;
+-
+- time_t ft;
+- struct tm tm_ft;
+- FileTimeToTimeT(fileTime, &ft);
+- localtime_r(&ft, &tm_ft);
+-
+- l.QuadPart += static_cast<unsigned long long>(tm_ft.tm_gmtoff) * 10000000;
+-
+- localFileTime->lowDateTime = l.u.LowPart;
+- localFileTime->highDateTime = l.u.HighPart;
+- return 1;
+-}
+-
+-int SystemTimeToFileTime(const SystemTime* systemTime, FileTime* fileTime)
+-{
+- static const int dayoffset[12] = {0, 31, 59, 90, 120, 151, 182, 212, 243, 273, 304, 334};
+-#if defined(TARGET_DARWIN)
+- static std::mutex timegm_lock;
+-#endif
+-
+- struct tm sysTime = {};
+- sysTime.tm_year = systemTime->year - 1900;
+- sysTime.tm_mon = systemTime->month - 1;
+- sysTime.tm_wday = systemTime->dayOfWeek;
+- sysTime.tm_mday = systemTime->day;
+- sysTime.tm_hour = systemTime->hour;
+- sysTime.tm_min = systemTime->minute;
+- sysTime.tm_sec = systemTime->second;
+- sysTime.tm_yday = dayoffset[sysTime.tm_mon] + (sysTime.tm_mday - 1);
+- sysTime.tm_isdst = g_timezone.m_IsDST;
+-
+- // If this is a leap year, and we're past the 28th of Feb, increment tm_yday.
+- if (IsLeapYear(systemTime->year) && (sysTime.tm_yday > 58))
+- sysTime.tm_yday++;
+-
+-#if defined(TARGET_DARWIN)
+- std::lock_guard<std::mutex> lock(timegm_lock);
+-#endif
+-
+-#if defined(TARGET_ANDROID) && !defined(__LP64__)
+- time64_t t = timegm64(&sysTime);
+-#else
+- time_t t = timegm(&sysTime);
+-#endif
+-
+- LARGE_INTEGER result;
+- result.QuadPart = (long long)t * 10000000 + (long long)systemTime->milliseconds * 10000;
+- result.QuadPart += WIN32_TIME_OFFSET;
+-
+- fileTime->lowDateTime = result.u.LowPart;
+- fileTime->highDateTime = result.u.HighPart;
+-
+- return 1;
+-}
+-
+-long CompareFileTime(const FileTime* fileTime1, const FileTime* fileTime2)
+-{
+- ULARGE_INTEGER t1;
+- t1.u.LowPart = fileTime1->lowDateTime;
+- t1.u.HighPart = fileTime1->highDateTime;
+-
+- ULARGE_INTEGER t2;
+- t2.u.LowPart = fileTime2->lowDateTime;
+- t2.u.HighPart = fileTime2->highDateTime;
+-
+- if (t1.QuadPart == t2.QuadPart)
+- return 0;
+- else if (t1.QuadPart < t2.QuadPart)
+- return -1;
+- else
+- return 1;
+-}
+-
+-int FileTimeToSystemTime(const FileTime* fileTime, SystemTime* systemTime)
+-{
+- LARGE_INTEGER file;
+- file.u.LowPart = fileTime->lowDateTime;
+- file.u.HighPart = fileTime->highDateTime;
+-
+- file.QuadPart -= WIN32_TIME_OFFSET;
+- file.QuadPart /= 10000; /* to milliseconds */
+- systemTime->milliseconds = file.QuadPart % 1000;
+- file.QuadPart /= 1000; /* to seconds */
+-
+- time_t ft = file.QuadPart;
+-
+- struct tm tm_ft;
+- gmtime_r(&ft, &tm_ft);
+-
+- systemTime->year = tm_ft.tm_year + 1900;
+- systemTime->month = tm_ft.tm_mon + 1;
+- systemTime->dayOfWeek = tm_ft.tm_wday;
+- systemTime->day = tm_ft.tm_mday;
+- systemTime->hour = tm_ft.tm_hour;
+- systemTime->minute = tm_ft.tm_min;
+- systemTime->second = tm_ft.tm_sec;
+-
+- return 1;
+-}
+-
+-int LocalFileTimeToFileTime(const FileTime* localFileTime, FileTime* fileTime)
+-{
+- ULARGE_INTEGER l;
+- l.u.LowPart = localFileTime->lowDateTime;
+- l.u.HighPart = localFileTime->highDateTime;
+-
+- l.QuadPart += (unsigned long long) timezone * 10000000;
+-
+- fileTime->lowDateTime = l.u.LowPart;
+- fileTime->highDateTime = l.u.HighPart;
+-
+- return 1;
+-}
+-
+-
+-int FileTimeToTimeT(const FileTime* localFileTime, time_t* pTimeT)
+-{
+- if (!localFileTime || !pTimeT)
+- return false;
+-
+- ULARGE_INTEGER fileTime;
+- fileTime.u.LowPart = localFileTime->lowDateTime;
+- fileTime.u.HighPart = localFileTime->highDateTime;
+-
+- fileTime.QuadPart -= WIN32_TIME_OFFSET;
+- fileTime.QuadPart /= 10000; /* to milliseconds */
+- fileTime.QuadPart /= 1000; /* to seconds */
+-
+- time_t ft = fileTime.QuadPart;
+-
+- struct tm tm_ft;
+- localtime_r(&ft, &tm_ft);
+-
+- *pTimeT = mktime(&tm_ft);
+- return 1;
+-}
+-
+-int TimeTToFileTime(time_t timeT, FileTime* localFileTime)
+-{
+- if (!localFileTime)
+- return false;
+-
+- ULARGE_INTEGER result;
+- result.QuadPart = (unsigned long long) timeT * 10000000;
+- result.QuadPart += WIN32_TIME_OFFSET;
+-
+- localFileTime->lowDateTime = result.u.LowPart;
+- localFileTime->highDateTime = result.u.HighPart;
+-
+- return 1;
+-}
+-
+ } // namespace TIME
+ } // namespace KODI
+diff --git a/xbmc/platform/posix/filesystem/PosixDirectory.cpp b/xbmc/platform/posix/filesystem/PosixDirectory.cpp
+index 6685c0e1db..7482a73960 100644
+--- a/xbmc/platform/posix/filesystem/PosixDirectory.cpp
++++ b/xbmc/platform/posix/filesystem/PosixDirectory.cpp
+@@ -79,10 +79,7 @@ bool CPosixDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ {
+ if (bStat || stat(pItem->GetPath().c_str(), &buffer) == 0)
+ {
+- KODI::TIME::FileTime fileTime, localTime;
+- KODI::TIME::TimeTToFileTime(buffer.st_mtime, &fileTime);
+- KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime);
+- pItem->m_dateTime = localTime;
++ pItem->m_dateTime = buffer.st_mtime;
+
+ if (!pItem->m_bIsFolder)
+ pItem->m_dwSize = buffer.st_size;
+diff --git a/xbmc/platform/posix/filesystem/SMBDirectory.cpp b/xbmc/platform/posix/filesystem/SMBDirectory.cpp
+index e2305afb4a..f0b94c22fc 100644
+--- a/xbmc/platform/posix/filesystem/SMBDirectory.cpp
++++ b/xbmc/platform/posix/filesystem/SMBDirectory.cpp
+@@ -134,10 +134,10 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ && strFile != "lost+found"
+ && aDir.type != SMBC_PRINTER_SHARE && aDir.type != SMBC_IPC_SHARE)
+ {
+- int64_t iSize = 0;
++ int64_t iSize = 0;
+ bool bIsDir = true;
+- int64_t lTimeDate = 0;
+ bool hidden = false;
++ struct stat info = {};
+
+ if(StringUtils::EndsWith(strFile, "$") && aDir.type == SMBC_FILE_SHARE )
+ continue;
+@@ -152,7 +152,6 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ // set this here to if the stat should fail
+ bIsDir = (aDir.type == SMBC_DIR);
+
+- struct stat info = {};
+ if ((m_flags & DIR_FLAG_NO_FILE_INFO)==0 && CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_sambastatfiles)
+ {
+ // make sure we use the authenticated path which contains any default username
+@@ -187,9 +186,6 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ CURL::GetRedacted(strFullName), errno, strerror(errno));
+
+ bIsDir = S_ISDIR(info.st_mode);
+- lTimeDate = info.st_mtime;
+- if(lTimeDate == 0) // if modification date is missing, use create date
+- lTimeDate = info.st_ctime;
+ iSize = info.st_size;
+ }
+ else
+@@ -200,10 +196,6 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ }
+ }
+
+- KODI::TIME::FileTime fileTime, localTime;
+- KODI::TIME::TimeTToFileTime(lTimeDate, &fileTime);
+- KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime);
+-
+ if (bIsDir)
+ {
+ CFileItemPtr pItem(new CFileItem(strFile));
+@@ -223,7 +215,7 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ URIUtils::AddSlashAtEnd(path);
+ pItem->SetPath(path);
+ pItem->m_bIsFolder = true;
+- pItem->m_dateTime=localTime;
++ pItem->m_dateTime = info.st_mtime != 0 ? info.st_mtime : info.st_ctime;
+ if (hidden)
+ pItem->SetProperty("file:hidden", true);
+ items.Add(pItem);
+@@ -234,7 +226,7 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+ pItem->SetPath(strRoot + aDir.name);
+ pItem->m_bIsFolder = false;
+ pItem->m_dwSize = iSize;
+- pItem->m_dateTime=localTime;
++ pItem->m_dateTime = info.st_mtime != 0 ? info.st_mtime : info.st_ctime;
+ if (hidden)
+ pItem->SetProperty("file:hidden", true);
+ items.Add(pItem);
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
+index f7866278e7..e61761b0ab 100644
+--- a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
++++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp
+@@ -573,22 +573,16 @@ void CGUIDialogPVRTimerSettings::OnSettingAction(const std::shared_ptr<const CSe
+ const std::string& settingId = setting->GetId();
+ if (settingId == SETTING_TMR_BEGIN)
+ {
+- KODI::TIME::SystemTime timerStartTime;
+- m_startLocalTime.GetAsSystemTime(timerStartTime);
+- if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066)))
++ if (CGUIDialogNumeric::ShowAndGetTime(m_startLocalTime, g_localizeStrings.Get(14066)))
+ {
+- SetTimeFromSystemTime(m_startLocalTime, timerStartTime);
+ m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false);
+ SetButtonLabels();
+ }
+ }
+ else if (settingId == SETTING_TMR_END)
+ {
+- KODI::TIME::SystemTime timerEndTime;
+- m_endLocalTime.GetAsSystemTime(timerEndTime);
+- if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066)))
++ if (CGUIDialogNumeric::ShowAndGetTime(m_endLocalTime, g_localizeStrings.Get(14066)))
+ {
+- SetTimeFromSystemTime(m_endLocalTime, timerEndTime);
+ m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false);
+ SetButtonLabels();
+ }
+@@ -800,14 +794,6 @@ void CGUIDialogPVRTimerSettings::SetDateFromIndex(CDateTime& datetime, int date)
+ datetime.GetMinute(), datetime.GetSecond());
+ }
+
+-void CGUIDialogPVRTimerSettings::SetTimeFromSystemTime(CDateTime& datetime,
+- const KODI::TIME::SystemTime& time)
+-{
+- const CDateTime newTime(time);
+- datetime.SetDateTime(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(),
+- newTime.GetHour(), newTime.GetMinute(), newTime.GetSecond());
+-}
+-
+ void CGUIDialogPVRTimerSettings::InitializeTypesList()
+ {
+ m_typeEntries.clear();
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h
+index d3e9e3eadf..d419e4a722 100644
+--- a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h
++++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h
+@@ -59,7 +59,6 @@ private:
+
+ static int GetDateAsIndex(const CDateTime& datetime);
+ static void SetDateFromIndex(CDateTime& datetime, int date);
+- static void SetTimeFromSystemTime(CDateTime& datetime, const KODI::TIME::SystemTime& time);
+
+ static int GetWeekdaysFromSetting(const std::shared_ptr<const CSetting>& setting);
+
+diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
+index f4c247f910..d85e7bf493 100644
+--- a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
++++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp
+@@ -800,13 +800,12 @@ bool CGUIWindowPVRGuideBase::OpenDateSelectionDialog()
+ {
+ bool bReturn = false;
+
+- KODI::TIME::SystemTime date;
+ CGUIEPGGridContainer* epgGridContainer = GetGridControl();
+- epgGridContainer->GetSelectedDate().GetAsSystemTime(date);
++ CDateTime datetime = epgGridContainer->GetSelectedDate();
+
+- if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(19288))) /* Go to date */
++ if (CGUIDialogNumeric::ShowAndGetDate(datetime, g_localizeStrings.Get(19288))) /* Go to date */
+ {
+- epgGridContainer->GoToDate(CDateTime(date));
++ epgGridContainer->GoToDate(datetime);
+ bReturn = true;
+ }
+
+diff --git a/xbmc/settings/windows/GUIControlSettings.cpp b/xbmc/settings/windows/GUIControlSettings.cpp
+index fcbf1fa908..6c9eb3ea68 100644
+--- a/xbmc/settings/windows/GUIControlSettings.cpp
++++ b/xbmc/settings/windows/GUIControlSettings.cpp
+@@ -989,21 +989,19 @@ bool CGUIControlButtonSetting::OnClick()
+ std::shared_ptr<CSettingDate> settingDate =
+ std::static_pointer_cast<CSettingDate>(m_pSetting);
+
+- KODI::TIME::SystemTime systemdate;
+- settingDate->GetDate().GetAsSystemTime(systemdate);
+- if (CGUIDialogNumeric::ShowAndGetDate(systemdate, Localize(buttonControl->GetHeading())))
+- SetValid(settingDate->SetDate(CDateTime(systemdate)));
++ CDateTime datetime = settingDate->GetDate();
++ if (CGUIDialogNumeric::ShowAndGetDate(datetime, Localize(buttonControl->GetHeading())))
++ SetValid(settingDate->SetDate(datetime));
+ }
+ else if (controlFormat == "time")
+ {
+ std::shared_ptr<CSettingTime> settingTime =
+ std::static_pointer_cast<CSettingTime>(m_pSetting);
+
+- KODI::TIME::SystemTime systemtime;
+- settingTime->GetTime().GetAsSystemTime(systemtime);
++ CDateTime datetime = settingTime->GetTime();
+
+- if (CGUIDialogNumeric::ShowAndGetTime(systemtime, Localize(buttonControl->GetHeading())))
+- SetValid(settingTime->SetTime(CDateTime(systemtime)));
++ if (CGUIDialogNumeric::ShowAndGetTime(datetime, Localize(buttonControl->GetHeading())))
++ SetValid(settingTime->SetTime(datetime));
+ }
+ else if (controlFormat == "action")
+ {
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index a553bd1887..a8e61e9785 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -36,17 +36,16 @@ TEST_F(TestDateTime, DateTimeOperators)
+ EXPECT_FALSE(dateTime1 == dateTime2);
+ }
+
+-TEST_F(TestDateTime, SystemTimeOperators)
++TEST_F(TestDateTime, TimePointOperators)
+ {
+ CDateTime dateTime1(1991, 5, 14, 12, 34, 56);
+ CDateTime dateTime2(1991, 5, 14, 12, 34, 57);
+
+- KODI::TIME::SystemTime systemTime;
+- dateTime2.GetAsSystemTime(systemTime);
++ auto tp = dateTime2.GetAsTimePoint();
+
+- EXPECT_TRUE(dateTime1 < systemTime);
+- EXPECT_FALSE(dateTime1 > systemTime);
+- EXPECT_FALSE(dateTime1 == systemTime);
++ EXPECT_TRUE(dateTime1 < tp);
++ EXPECT_FALSE(dateTime1 > tp);
++ EXPECT_FALSE(dateTime1 == tp);
+ }
+
+ TEST_F(TestDateTime, TimeTOperators)
+@@ -529,15 +528,14 @@ TEST_F(TestDateTime, GetAsLocalized)
+ EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(256)), "5");
+ }
+
+-TEST_F(TestDateTime, GetAsSystemTime)
++TEST_F(TestDateTime, GetAsTimePoint)
+ {
+ CDateTime dateTime;
+ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+- KODI::TIME::SystemTime systemTime;
+- dateTime.GetAsSystemTime(systemTime);
++ auto tp = dateTime.GetAsTimePoint();
+
+- EXPECT_TRUE(dateTime == systemTime);
++ EXPECT_TRUE(dateTime == tp);
+ }
+
+ TEST_F(TestDateTime, GetAsTime)
+diff --git a/xbmc/utils/Archive.cpp b/xbmc/utils/Archive.cpp
+index 4f69929a42..0275c057f9 100644
+--- a/xbmc/utils/Archive.cpp
++++ b/xbmc/utils/Archive.cpp
+@@ -152,9 +152,9 @@ CArchive& CArchive::operator<<(const std::wstring& wstr)
+ return streamout(wstr.data(), size * sizeof(wchar_t));
+ }
+
+-CArchive& CArchive::operator<<(const KODI::TIME::SystemTime& time)
++CArchive& CArchive::operator<<(const std::chrono::system_clock::time_point& time)
+ {
+- return streamout(&time, sizeof(KODI::TIME::SystemTime));
++ return streamout(&time, sizeof(std::chrono::system_clock::time_point));
+ }
+
+ CArchive& CArchive::operator<<(IArchivable& obj)
+@@ -265,9 +265,9 @@ CArchive& CArchive::operator>>(std::wstring& wstr)
+ return *this;
+ }
+
+-CArchive& CArchive::operator>>(KODI::TIME::SystemTime& time)
++CArchive& CArchive::operator>>(std::chrono::system_clock::time_point& time)
+ {
+- return streamin(&time, sizeof(KODI::TIME::SystemTime));
++ return streamin(&time, sizeof(std::chrono::system_clock::time_point));
+ }
+
+ CArchive& CArchive::operator>>(IArchivable& obj)
+diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h
+index a1af0c3cf8..86a2bebc23 100644
+--- a/xbmc/utils/Archive.h
++++ b/xbmc/utils/Archive.h
+@@ -57,7 +57,7 @@ public:
+ CArchive& operator<<(char c);
+ CArchive& operator<<(const std::string &str);
+ CArchive& operator<<(const std::wstring& wstr);
+- CArchive& operator<<(const KODI::TIME::SystemTime& time);
++ CArchive& operator<<(const std::chrono::system_clock::time_point& time);
+ CArchive& operator<<(IArchivable& obj);
+ CArchive& operator<<(const CVariant& variant);
+ CArchive& operator<<(const std::vector<std::string>& strArray);
+@@ -126,7 +126,7 @@ public:
+
+ CArchive& operator>>(std::string &str);
+ CArchive& operator>>(std::wstring& wstr);
+- CArchive& operator>>(KODI::TIME::SystemTime& time);
++ CArchive& operator>>(std::chrono::system_clock::time_point& time);
+ CArchive& operator>>(IArchivable& obj);
+ CArchive& operator>>(CVariant& variant);
+ CArchive& operator>>(std::vector<std::string>& strArray);
+diff --git a/xbmc/utils/RssManager.cpp b/xbmc/utils/RssManager.cpp
+index b8d350cac5..5e77dd7c13 100644
+--- a/xbmc/utils/RssManager.cpp
++++ b/xbmc/utils/RssManager.cpp
+@@ -142,7 +142,7 @@ bool CRssManager::Load()
+ //! What about the xml encoding?
+ std::string strUrl = pFeed->FirstChild()->ValueStr();
+ set.url.push_back(strUrl);
+- set.interval.push_back(iInterval);
++ set.interval.push_back(std::chrono::minutes(iInterval));
+ }
+ pFeed = pFeed->NextSiblingElement("feed");
+ }
+diff --git a/xbmc/utils/RssManager.h b/xbmc/utils/RssManager.h
+index 2b807d739f..59ac506307 100644
+--- a/xbmc/utils/RssManager.h
++++ b/xbmc/utils/RssManager.h
+@@ -22,7 +22,7 @@ class IRssObserver;
+ typedef struct
+ {
+ bool rtl;
+- std::vector<int> interval;
++ std::vector<std::chrono::nanoseconds> interval;
+ std::vector<std::string> url;
+ } RssSet;
+ typedef std::map<int, RssSet> RssUrls;
+diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp
+index 0b227b63d3..f9f1e4a845 100644
+--- a/xbmc/utils/RssReader.cpp
++++ b/xbmc/utils/RssReader.cpp
+@@ -51,11 +51,14 @@ CRssReader::~CRssReader()
+ if (m_pObserver)
+ m_pObserver->OnFeedRelease();
+ StopThread();
+- for (unsigned int i = 0; i < m_vecTimeStamps.size(); i++)
+- delete m_vecTimeStamps[i];
++ m_vecTimeStamps.clear();
+ }
+
+-void CRssReader::Create(IRssObserver* aObserver, const std::vector<std::string>& aUrls, const std::vector<int> &times, int spacesBetweenFeeds, bool rtl)
++void CRssReader::Create(IRssObserver* aObserver,
++ const std::vector<std::string>& aUrls,
++ const std::vector<std::chrono::nanoseconds>& times,
++ int spacesBetweenFeeds,
++ bool rtl)
+ {
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+@@ -73,9 +76,7 @@ void CRssReader::Create(IRssObserver* aObserver, const std::vector<std::string>&
+ for (unsigned int i = 0; i < m_vecUpdateTimes.size(); ++i)
+ {
+ AddToQueue(i);
+- KODI::TIME::SystemTime* time = new KODI::TIME::SystemTime;
+- KODI::TIME::GetLocalTime(time);
+- m_vecTimeStamps.push_back(time);
++ m_vecTimeStamps.push_back(std::chrono::system_clock::now().time_since_epoch());
+ }
+ }
+
+@@ -395,18 +396,14 @@ void CRssReader::UpdateObserver()
+
+ void CRssReader::CheckForUpdates()
+ {
+- KODI::TIME::SystemTime time;
+- KODI::TIME::GetLocalTime(&time);
+-
+ for (unsigned int i = 0;i < m_vecUpdateTimes.size(); ++i )
+ {
+- if (m_requestRefresh || ((time.day * 24 * 60) + (time.hour * 60) + time.minute) -
+- ((m_vecTimeStamps[i]->day * 24 * 60) +
+- (m_vecTimeStamps[i]->hour * 60) + m_vecTimeStamps[i]->minute) >
+- m_vecUpdateTimes[i])
++ if (m_requestRefresh ||
++ (std::chrono::system_clock::now().time_since_epoch() - m_vecTimeStamps[i] >
++ m_vecUpdateTimes[i]))
+ {
+ CLog::Log(LOGDEBUG, "Updating RSS");
+- KODI::TIME::GetLocalTime(m_vecTimeStamps[i]);
++ m_vecTimeStamps[i] = std::chrono::system_clock::now().time_since_epoch();
+ AddToQueue(i);
+ }
+ }
+diff --git a/xbmc/utils/RssReader.h b/xbmc/utils/RssReader.h
+index ae9d2f73ca..43c6bde609 100644
+--- a/xbmc/utils/RssReader.h
++++ b/xbmc/utils/RssReader.h
+@@ -28,7 +28,11 @@ public:
+ CRssReader();
+ ~CRssReader() override;
+
+- void Create(IRssObserver* aObserver, const std::vector<std::string>& aUrl, const std::vector<int>& times, int spacesBetweenFeeds, bool rtl);
++ void Create(IRssObserver* aObserver,
++ const std::vector<std::string>& aUrl,
++ const std::vector<std::chrono::nanoseconds>& times,
++ int spacesBetweenFeeds,
++ bool rtl);
+ bool Parse(const std::string& data, int iFeed, const std::string& charset);
+ void getFeed(vecText &text);
+ void AddTag(const std::string &addTag);
+@@ -52,8 +56,8 @@ private:
+
+ std::vector<std::wstring> m_strFeed;
+ std::vector<std::wstring> m_strColors;
+- std::vector<KODI::TIME::SystemTime*> m_vecTimeStamps;
+- std::vector<int> m_vecUpdateTimes;
++ std::vector<std::chrono::nanoseconds> m_vecTimeStamps;
++ std::vector<std::chrono::nanoseconds> m_vecUpdateTimes;
+ int m_spacesBetweenFeeds;
+ CXBMCTinyXML m_xml;
+ std::list<std::string> m_tagSet;
+diff --git a/xbmc/utils/XTimeUtils.h b/xbmc/utils/XTimeUtils.h
+index 91cda40305..9d3b0e86a3 100644
+--- a/xbmc/utils/XTimeUtils.h
++++ b/xbmc/utils/XTimeUtils.h
+@@ -27,42 +27,6 @@ namespace KODI
+ {
+ namespace TIME
+ {
+-struct SystemTime
+-{
+- unsigned short year;
+- unsigned short month;
+- unsigned short dayOfWeek;
+- unsigned short day;
+- unsigned short hour;
+- unsigned short minute;
+- unsigned short second;
+- unsigned short milliseconds;
+-};
+-
+-struct TimeZoneInformation
+-{
+- long bias;
+- std::string standardName;
+- SystemTime standardDate;
+- long standardBias;
+- std::string daylightName;
+- SystemTime daylightDate;
+- long daylightBias;
+-};
+-
+-constexpr int KODI_TIME_ZONE_ID_INVALID{-1};
+-constexpr int KODI_TIME_ZONE_ID_UNKNOWN{0};
+-constexpr int KODI_TIME_ZONE_ID_STANDARD{1};
+-constexpr int KODI_TIME_ZONE_ID_DAYLIGHT{2};
+-
+-struct FileTime
+-{
+- unsigned int lowDateTime;
+- unsigned int highDateTime;
+-};
+-
+-void GetLocalTime(SystemTime* systemTime);
+-uint32_t GetTimeZoneInformation(TimeZoneInformation* timeZoneInformation);
+
+ template<typename Rep, typename Period>
+ void Sleep(std::chrono::duration<Rep, Period> duration)
+@@ -76,13 +40,5 @@ void Sleep(std::chrono::duration<Rep, Period> duration)
+ std::this_thread::sleep_for(duration);
+ }
+
+-int FileTimeToLocalFileTime(const FileTime* fileTime, FileTime* localFileTime);
+-int SystemTimeToFileTime(const SystemTime* systemTime, FileTime* fileTime);
+-long CompareFileTime(const FileTime* fileTime1, const FileTime* fileTime2);
+-int FileTimeToSystemTime(const FileTime* fileTime, SystemTime* systemTime);
+-int LocalFileTimeToFileTime(const FileTime* LocalFileTime, FileTime* fileTime);
+-
+-int FileTimeToTimeT(const FileTime* localFileTime, time_t* pTimeT);
+-int TimeTToFileTime(time_t timeT, FileTime* localFileTime);
+ } // namespace TIME
+ } // namespace KODI
+diff --git a/xbmc/utils/test/TestArchive.cpp b/xbmc/utils/test/TestArchive.cpp
+index 90628ea2ed..b9ab780cf9 100644
+--- a/xbmc/utils/test/TestArchive.cpp
++++ b/xbmc/utils/test/TestArchive.cpp
+@@ -220,22 +220,22 @@ TEST_F(TestArchive, StringArchive)
+ EXPECT_STREQ(string_ref.c_str(), string_var.c_str());
+ }
+
+-TEST_F(TestArchive, SystemTimeArchive)
++TEST_F(TestArchive, TimePointArchive)
+ {
+ ASSERT_NE(nullptr, file);
+- KODI::TIME::SystemTime SystemTime_ref = {1, 2, 3, 4, 5, 6, 7, 8};
+- KODI::TIME::SystemTime SystemTime_var = {0, 0, 0, 0, 0, 0, 0, 0};
++ std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now();
++ std::chrono::system_clock::time_point tp_var;
+
+ CArchive arstore(file, CArchive::store);
+- arstore << SystemTime_ref;
++ arstore << tp_ref;
+ arstore.Close();
+
+ ASSERT_EQ(0, file->Seek(0, SEEK_SET));
+ CArchive arload(file, CArchive::load);
+- arload >> SystemTime_var;
++ arload >> tp_var;
+ arload.Close();
+
+- EXPECT_TRUE(!memcmp(&SystemTime_ref, &SystemTime_var, sizeof(KODI::TIME::SystemTime)));
++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point)));
+ }
+
+ TEST_F(TestArchive, CVariantArchive)
+@@ -335,8 +335,8 @@ TEST_F(TestArchive, MultiTypeArchive)
+ char char_ref = 'A', char_var = '\0';
+ std::string string_ref = "test string", string_var;
+ std::wstring wstring_ref = L"test wstring", wstring_var;
+- KODI::TIME::SystemTime SystemTime_ref = {1, 2, 3, 4, 5, 6, 7, 8};
+- KODI::TIME::SystemTime SystemTime_var = {0, 0, 0, 0, 0, 0, 0, 0};
++ std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now();
++ std::chrono::system_clock::time_point tp_var;
+ CVariant CVariant_ref((int)1), CVariant_var;
+ std::vector<std::string> strArray_ref, strArray_var;
+ strArray_ref.emplace_back("test strArray_ref 0");
+@@ -362,7 +362,7 @@ TEST_F(TestArchive, MultiTypeArchive)
+ arstore << char_ref;
+ arstore << string_ref;
+ arstore << wstring_ref;
+- arstore << SystemTime_ref;
++ arstore << tp_ref;
+ arstore << CVariant_ref;
+ arstore << strArray_ref;
+ arstore << iArray_ref;
+@@ -382,7 +382,7 @@ TEST_F(TestArchive, MultiTypeArchive)
+ arload >> char_var;
+ arload >> string_var;
+ arload >> wstring_var;
+- arload >> SystemTime_var;
++ arload >> tp_var;
+ arload >> CVariant_var;
+ arload >> strArray_var;
+ arload >> iArray_var;
+@@ -398,7 +398,7 @@ TEST_F(TestArchive, MultiTypeArchive)
+ EXPECT_EQ(char_ref, char_var);
+ EXPECT_STREQ(string_ref.c_str(), string_var.c_str());
+ EXPECT_STREQ(wstring_ref.c_str(), wstring_var.c_str());
+- EXPECT_TRUE(!memcmp(&SystemTime_ref, &SystemTime_var, sizeof(KODI::TIME::SystemTime)));
++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point)));
+ EXPECT_TRUE(CVariant_var.isInteger());
+ EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str());
+ EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str());
+diff --git a/xbmc/video/VideoInfoScanner.cpp b/xbmc/video/VideoInfoScanner.cpp
+index 92fa5c191f..8d9cf6979d 100644
+--- a/xbmc/video/VideoInfoScanner.cpp
++++ b/xbmc/video/VideoInfoScanner.cpp
+@@ -2005,8 +2005,8 @@ namespace VIDEO
+ else
+ {
+ digest.Update(&pItem->m_dwSize, sizeof(pItem->m_dwSize));
+- KODI::TIME::FileTime time = pItem->m_dateTime;
+- digest.Update(&time, sizeof(KODI::TIME::FileTime));
++ const auto time = pItem->m_dateTime.GetAsTimePoint().time_since_epoch().count();
++ digest.Update(&time, sizeof(std::chrono::nanoseconds));
+ }
+ if (pItem->IsVideo() && !pItem->IsPlayList() && !pItem->IsNFO())
+ count++;
+--
+2.43.0
+
+
+From 466178a3ca4239ff1c837f28ba84283777519990 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Wed, 21 Oct 2020 09:17:33 +0300
+Subject: [PATCH 07/19] TestDateTime: fix GetAsStringsWithBias and add Tzdata
+ tests
+
+The 'GetAsStringsWithBias' test logic assumed the timezone on
+machine bullding Kodi is set to 'UTC'. This is not always the case,
+plus hour value was declared with a typo.
+
+To address this, we compare outputs of two runs of date library,
+and instead add a 'Tzdata' test case comparing local date and time
+against computed values from different timezones:
+
+find /usr/share/zoneinfo/Etc/ -type f | sort -Vu | sed 's,^/usr/share/zoneinfo/,,' | while read TZ
+do
+ TMSTR=$(LANG=C TZ="$TZ" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/')
+ echo " // LANG=C TZ=\"$TZ\" date '+%Y-%m-%dT%H:%M:%S%Ez' -d \"1991-05-14 12:34:56 UTC\" | sed 's/[0-9][0-9]$/:&/'"
+ echo " tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());"
+ echo " zone = date::make_zoned(\"$TZ\", tps);"
+ echo " EXPECT_EQ(date::format(\"%FT%T%Ez\", zone), \"$TMSTR\") << \"tzdata information not valid for '$TZ'\";"
+ echo ""
+done
+---
+ xbmc/test/TestDateTime.cpp | 168 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 166 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index a8e61e9785..0ff2bb443b 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -11,6 +11,7 @@
+ #include "guilib/LocalizeStrings.h"
+
+ #include <array>
++#include <chrono>
+ #include <iostream>
+
+ #define USE_OS_TZDB 0
+@@ -330,10 +331,11 @@ TEST_F(TestDateTime, GetAsStringsWithBias)
+ std::cout << dateTime.GetAsW3CDateTime(false) << std::endl;
+ std::cout << dateTime.GetAsW3CDateTime(true) << std::endl;
+
+- auto zone = date::make_zoned(date::current_zone(), dateTime.GetAsTimePoint());
++ auto tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ auto zone = date::make_zoned(date::current_zone(), tps);
+
+ EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 12:34:56 GMT");
+- EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T05:34:56" + date::format("%Ez", zone));
++ EXPECT_EQ(dateTime.GetAsW3CDateTime(false), date::format("%FT%T%Ez", zone));
+ EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T12:34:56Z");
+ }
+
+@@ -601,3 +603,165 @@ TEST_F(TestDateTime, Reset)
+ EXPECT_EQ(dateTime.GetMinute(), 0);
+ EXPECT_EQ(dateTime.GetSecond(), 0);
+ }
++
++TEST_F(TestDateTime, Tzdata)
++{
++ CDateTime dateTime;
++ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
++
++ // LANG=C TZ="Etc/GMT+1" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ auto tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ auto zone = date::make_zoned("Etc/GMT+1", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T11:34:56-01:00")
++ << "tzdata information not valid for 'Etc/GMT+1'";
++
++ // LANG=C TZ="Etc/GMT+2" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+2", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T10:34:56-02:00")
++ << "tzdata information not valid for 'Etc/GMT+2'";
++
++ // LANG=C TZ="Etc/GMT+3" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+3", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T09:34:56-03:00")
++ << "tzdata information not valid for 'Etc/GMT+3'";
++
++ // LANG=C TZ="Etc/GMT+4" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+4", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T08:34:56-04:00")
++ << "tzdata information not valid for 'Etc/GMT+4'";
++
++ // LANG=C TZ="Etc/GMT+5" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+5", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T07:34:56-05:00")
++ << "tzdata information not valid for 'Etc/GMT+5'";
++
++ // LANG=C TZ="Etc/GMT+6" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+6", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T06:34:56-06:00")
++ << "tzdata information not valid for 'Etc/GMT+6'";
++
++ // LANG=C TZ="Etc/GMT+7" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+7", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T05:34:56-07:00")
++ << "tzdata information not valid for 'Etc/GMT+7'";
++
++ // LANG=C TZ="Etc/GMT+8" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+8", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T04:34:56-08:00")
++ << "tzdata information not valid for 'Etc/GMT+8'";
++
++ // LANG=C TZ="Etc/GMT+9" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+9", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T03:34:56-09:00")
++ << "tzdata information not valid for 'Etc/GMT+9'";
++
++ // LANG=C TZ="Etc/GMT+10" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+10", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T02:34:56-10:00")
++ << "tzdata information not valid for 'Etc/GMT+10'";
++
++ // LANG=C TZ="Etc/GMT+11" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+11", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T01:34:56-11:00")
++ << "tzdata information not valid for 'Etc/GMT+11'";
++
++ // LANG=C TZ="Etc/GMT+12" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT+12", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T00:34:56-12:00")
++ << "tzdata information not valid for 'Etc/GMT+12'";
++
++ // LANG=C TZ="Etc/GMT-1" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-1", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T13:34:56+01:00")
++ << "tzdata information not valid for 'Etc/GMT-1'";
++
++ // LANG=C TZ="Etc/GMT-2" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-2", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T14:34:56+02:00")
++ << "tzdata information not valid for 'Etc/GMT-2'";
++
++ // LANG=C TZ="Etc/GMT-3" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-3", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T15:34:56+03:00")
++ << "tzdata information not valid for 'Etc/GMT-3'";
++
++ // LANG=C TZ="Etc/GMT-4" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-4", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T16:34:56+04:00")
++ << "tzdata information not valid for 'Etc/GMT-4'";
++
++ // LANG=C TZ="Etc/GMT-5" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-5", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T17:34:56+05:00")
++ << "tzdata information not valid for 'Etc/GMT-5'";
++
++ // LANG=C TZ="Etc/GMT-6" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-6", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T18:34:56+06:00")
++ << "tzdata information not valid for 'Etc/GMT-6'";
++
++ // LANG=C TZ="Etc/GMT-7" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-7", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T19:34:56+07:00")
++ << "tzdata information not valid for 'Etc/GMT-7'";
++
++ // LANG=C TZ="Etc/GMT-8" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-8", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T20:34:56+08:00")
++ << "tzdata information not valid for 'Etc/GMT-8'";
++
++ // LANG=C TZ="Etc/GMT-9" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-9", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T21:34:56+09:00")
++ << "tzdata information not valid for 'Etc/GMT-9'";
++
++ // LANG=C TZ="Etc/GMT-10" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-10", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T22:34:56+10:00")
++ << "tzdata information not valid for 'Etc/GMT-10'";
++
++ // LANG=C TZ="Etc/GMT-11" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-11", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T23:34:56+11:00")
++ << "tzdata information not valid for 'Etc/GMT-11'";
++
++ // LANG=C TZ="Etc/GMT-12" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-12", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T00:34:56+12:00")
++ << "tzdata information not valid for 'Etc/GMT-12'";
++
++ // LANG=C TZ="Etc/GMT-13" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-13", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T01:34:56+13:00")
++ << "tzdata information not valid for 'Etc/GMT-13'";
++
++ // LANG=C TZ="Etc/GMT-14" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/'
++ tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ zone = date::make_zoned("Etc/GMT-14", tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T02:34:56+14:00")
++ << "tzdata information not valid for 'Etc/GMT-14'";
++}
+--
+2.43.0
+
+
+From 72720c4e84bbbb1d5859cd727b1068a74a8be3af Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Tue, 3 Nov 2020 17:51:26 +0200
+Subject: [PATCH 08/19] Remove GetAsUTCDateTime and add GetAsLocalDateTime
+
+The 'm_time' now represents a UTC time-point, so 'GetAsUTCDateTime'
+does nothing useful. Remove it fully, and add 'GetAsLocalDateTime'
+function that we will use later to convert UTC (system) time to the
+local time needed to get displayed in UI.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/XBDateTime.cpp | 9 ++++++---
+ xbmc/XBDateTime.h | 4 +++-
+ xbmc/network/WebServer.cpp | 6 +++---
+ xbmc/pvr/epg/EpgContainer.cpp | 14 +++++++-------
+ xbmc/pvr/epg/EpgInfoTag.cpp | 4 ++--
+ xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp | 1 -
+ xbmc/pvr/timers/PVRTimerRuleMatcher.cpp | 2 +-
+ xbmc/pvr/timers/PVRTimers.cpp | 1 -
+ xbmc/test/TestDateTime.cpp | 16 ++++++++--------
+ 9 files changed, 30 insertions(+), 27 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index d8187b71e6..afd2d8c6a5 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -1210,10 +1210,13 @@ std::string CDateTime::GetAsLocalizedTime(TIME_FORMAT format, bool withSeconds /
+ return GetAsLocalizedTime("", false);
+ }
+
+-CDateTime CDateTime::GetAsUTCDateTime() const
++CDateTime CDateTime::GetAsLocalDateTime() const
+ {
+- CDateTime time(m_time);
+- return time;
++ auto zone = date::make_zoned(date::current_zone(), m_time);
++
++ return CDateTime(
++ std::chrono::duration_cast<std::chrono::seconds>(zone.get_local_time().time_since_epoch())
++ .count());
+ }
+
+ std::string CDateTime::GetAsRFC1123DateTime() const
+diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h
+index 16d6217c8a..a810619566 100644
+--- a/xbmc/XBDateTime.h
++++ b/xbmc/XBDateTime.h
+@@ -157,7 +157,9 @@ public:
+ void GetAsTm(tm& time) const;
+ std::chrono::system_clock::time_point GetAsTimePoint() const;
+
+- CDateTime GetAsUTCDateTime() const;
++ /*! \brief convert UTC datetime to local datetime
++ */
++ CDateTime GetAsLocalDateTime() const;
+ std::string GetAsSaveString() const;
+ std::string GetAsDBDateTime() const;
+ std::string GetAsDBDate() const;
+diff --git a/xbmc/network/WebServer.cpp b/xbmc/network/WebServer.cpp
+index 0ee1696033..fc3583c16d 100644
+--- a/xbmc/network/WebServer.cpp
++++ b/xbmc/network/WebServer.cpp
+@@ -222,7 +222,7 @@ MHD_RESULT CWebServer::HandlePartialRequest(struct MHD_Connection* connection,
+ CDateTime ifUnmodifiedSinceDate;
+ // handle If-Modified-Since (but only if the response is cacheable)
+ if (cacheable && ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince) &&
+- lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate)
++ lastModified <= ifModifiedSinceDate)
+ {
+ struct MHD_Response* response = create_response(0, nullptr, MHD_NO, MHD_NO);
+ if (response == nullptr)
+@@ -235,7 +235,7 @@ MHD_RESULT CWebServer::HandlePartialRequest(struct MHD_Connection* connection,
+ }
+ // handle If-Unmodified-Since
+ else if (ifUnmodifiedSinceDate.SetFromRFC1123DateTime(ifUnmodifiedSince) &&
+- lastModified.GetAsUTCDateTime() > ifUnmodifiedSinceDate)
++ lastModified > ifUnmodifiedSinceDate)
+ return SendErrorResponse(request, MHD_HTTP_PRECONDITION_FAILED, request.method);
+ }
+
+@@ -504,7 +504,7 @@ bool CWebServer::IsRequestRanged(const HTTPRequest& request, const CDateTime& la
+
+ // check if the last modification is newer than the If-Range date
+ // if so we have to server the whole file instead
+- if (lastModified.GetAsUTCDateTime() > ifRangeDate)
++ if (lastModified > ifRangeDate)
+ ranges.Clear();
+ }
+ }
+diff --git a/xbmc/pvr/epg/EpgContainer.cpp b/xbmc/pvr/epg/EpgContainer.cpp
+index d6f20153ef..38aa15809e 100644
+--- a/xbmc/pvr/epg/EpgContainer.cpp
++++ b/xbmc/pvr/epg/EpgContainer.cpp
+@@ -335,7 +335,7 @@ void CPVREpgContainer::Process()
+ time_t iLastEpgCleanup = 0;
+ bool bUpdateEpg = true;
+
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ CDateTime::GetUTCDateTime().GetAsTime(iNow);
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ bUpdateEpg = (iNow >= m_iNextEpgUpdate) && !m_bSuspended;
+@@ -593,7 +593,7 @@ std::shared_ptr<CPVREpg> CPVREpgContainer::CreateChannelEpg(int iEpgId, const st
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ m_bPreventUpdates = false;
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
++ CDateTime::GetUTCDateTime().GetAsTime(m_iNextEpgUpdate);
+ }
+
+ m_events.Publish(PVREvent::EpgContainer);
+@@ -613,7 +613,7 @@ bool CPVREpgContainer::RemoveOldEntries()
+ epgEntry.second->Cleanup(cleanupTime);
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iLastEpgCleanup);
++ CDateTime::GetUTCDateTime().GetAsTime(m_iLastEpgCleanup);
+
+ return true;
+ }
+@@ -787,7 +787,7 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
+ {
+ /* the update has been interrupted. try again later */
+ time_t iNow;
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ CDateTime::GetUTCDateTime().GetAsTime(iNow);
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ m_iNextEpgUpdate = iNow + advancedSettings->m_iEpgRetryInterruptedUpdateInterval;
+@@ -795,7 +795,7 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
+ else
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
++ CDateTime::GetUTCDateTime().GetAsTime(m_iNextEpgUpdate);
+ m_iNextEpgUpdate += advancedSettings->m_iEpgUpdateCheckInterval;
+ if (m_pendingUpdates == pendingUpdates)
+ m_pendingUpdates = 0;
+@@ -852,7 +852,7 @@ bool CPVREpgContainer::CheckPlayingEvents()
+ m_critSection.unlock();
+
+ time_t iNow;
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
++ CDateTime::GetUTCDateTime().GetAsTime(iNow);
+ if (iNow >= iNextEpgActiveTagCheck)
+ {
+ bFoundChanges = std::accumulate(epgs.cbegin(), epgs.cend(), bFoundChanges,
+@@ -860,7 +860,7 @@ bool CPVREpgContainer::CheckPlayingEvents()
+ return epgEntry.second->CheckPlayingEvent() ? true : found;
+ });
+
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNextEpgActiveTagCheck);
++ CDateTime::GetUTCDateTime().GetAsTime(iNextEpgActiveTagCheck);
+ iNextEpgActiveTagCheck += CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iEpgActiveTagCheckInterval;
+
+ /* pvr tags always start on the full minute */
+diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp
+index 8b0930e5d2..70daa5033b 100644
+--- a/xbmc/pvr/epg/EpgInfoTag.cpp
++++ b/xbmc/pvr/epg/EpgInfoTag.cpp
+@@ -233,7 +233,7 @@ float CPVREpgInfoTag::ProgressPercentage() const
+ float fReturn = 0.0f;
+
+ time_t currentTime, startTime, endTime;
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime);
++ CDateTime::GetUTCDateTime().GetAsTime(currentTime);
+ m_startTime.GetAsTime(startTime);
+ m_endTime.GetAsTime(endTime);
+ int iDuration = endTime - startTime > 0 ? endTime - startTime : 3600;
+@@ -249,7 +249,7 @@ float CPVREpgInfoTag::ProgressPercentage() const
+ int CPVREpgInfoTag::Progress() const
+ {
+ time_t currentTime, startTime;
+- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime);
++ CDateTime::GetUTCDateTime().GetAsTime(currentTime);
+ m_startTime.GetAsTime(startTime);
+ int iDuration = currentTime - startTime;
+
+diff --git a/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp b/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp
+index 1a0a99a7a2..5e27ea69c5 100644
+--- a/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp
++++ b/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp
+@@ -92,7 +92,6 @@ bool CPVRGUIActionsPowerManagement::CanSystemPowerdown(bool bAskUser /*= true*/)
+ CDateTime dailywakeuptime;
+ dailywakeuptime.SetFromDBTime(
+ m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME));
+- dailywakeuptime = dailywakeuptime.GetAsUTCDateTime();
+
+ const CDateTimeSpan diff(dailywakeuptime - now);
+ int mins = diff.GetSecondsTotal() / 60;
+diff --git a/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp b/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp
+index 9178e85ec2..b34688bdce 100644
+--- a/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp
++++ b/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp
+@@ -66,7 +66,7 @@ CDateTime CPVRTimerRuleMatcher::GetNextTimerStart() const
+ }
+ }
+
+- return nextStart.GetAsUTCDateTime();
++ return nextStart;
+ }
+
+ bool CPVRTimerRuleMatcher::Matches(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
+diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp
+index 760bb192f7..5fe64ec7e4 100644
+--- a/xbmc/pvr/timers/PVRTimers.cpp
++++ b/xbmc/pvr/timers/PVRTimers.cpp
+@@ -1274,7 +1274,6 @@ CDateTime CPVRTimers::GetNextEventTime() const
+ CDateTime dailywakeuptime;
+ dailywakeuptime.SetFromDBTime(
+ m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME));
+- dailywakeuptime = dailywakeuptime.GetAsUTCDateTime();
+
+ dailywakeuptime.SetDateTime(now.GetYear(), now.GetMonth(), now.GetDay(),
+ dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(),
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index 0ff2bb443b..4c55fafe06 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -573,20 +573,20 @@ TEST_F(TestDateTime, GetAsTm)
+ }
+ }
+
+-TEST_F(TestDateTime, GetAsUTCDateTime)
++TEST_F(TestDateTime, GetAsLocalDateTime)
+ {
+ CDateTime dateTime1;
+ dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56);
+
+ CDateTime dateTime2;
+- dateTime2 = dateTime1.GetAsUTCDateTime();
++ dateTime2 = dateTime1.GetAsLocalDateTime();
+
+- EXPECT_EQ(dateTime2.GetYear(), 1991);
+- EXPECT_EQ(dateTime2.GetMonth(), 5);
+- EXPECT_EQ(dateTime2.GetDay(), 14);
+- EXPECT_EQ(dateTime2.GetHour(), 12);
+- EXPECT_EQ(dateTime2.GetMinute(), 34);
+- EXPECT_EQ(dateTime2.GetSecond(), 56);
++ auto zoned_time = date::make_zoned(date::current_zone(), dateTime1.GetAsTimePoint());
++ auto time = zoned_time.get_local_time().time_since_epoch();
++
++ CDateTime cmpTime(std::chrono::duration_cast<std::chrono::seconds>(time).count());
++
++ EXPECT_TRUE(dateTime1 == cmpTime);
+ }
+
+ TEST_F(TestDateTime, Reset)
+--
+2.43.0
+
+
+From d394f4639fd434647a393b99956d22417c68401b Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Tue, 3 Nov 2020 22:49:09 +0200
+Subject: [PATCH 09/19] Compensate the representation change
+
+... from local time by default to UTC time by default.
+
+Namely, 'CDateTime::SetFromUTCDateTime' converted the UTC time
+to local time in the implementation of CDateTime before
+'std::chrono'. Now this conversion has gone, and to avoid
+displaying PVR times in UTC, 'GetAsLocalDateTime' is added
+where necessary.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/pvr/epg/EpgInfoTag.cpp | 8 ++------
+ xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp | 4 ++--
+ xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp | 2 +-
+ xbmc/pvr/recordings/PVRRecording.cpp | 4 ++--
+ 4 files changed, 7 insertions(+), 11 deletions(-)
+
+diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp
+index 70daa5033b..3f104aa032 100644
+--- a/xbmc/pvr/epg/EpgInfoTag.cpp
++++ b/xbmc/pvr/epg/EpgInfoTag.cpp
+@@ -293,9 +293,7 @@ CDateTime CPVREpgInfoTag::StartAsUTC() const
+
+ CDateTime CPVREpgInfoTag::StartAsLocalTime() const
+ {
+- CDateTime retVal;
+- retVal.SetFromUTCDateTime(m_startTime);
+- return retVal;
++ return m_startTime.GetAsLocalDateTime();
+ }
+
+ CDateTime CPVREpgInfoTag::EndAsUTC() const
+@@ -305,9 +303,7 @@ CDateTime CPVREpgInfoTag::EndAsUTC() const
+
+ CDateTime CPVREpgInfoTag::EndAsLocalTime() const
+ {
+- CDateTime retVal;
+- retVal.SetFromUTCDateTime(m_endTime);
+- return retVal;
++ return m_endTime.GetAsLocalDateTime();
+ }
+
+ void CPVREpgInfoTag::SetEndFromUTC(const CDateTime& end)
+diff --git a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
+index e8fac010ca..ef7a715f70 100644
+--- a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
++++ b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
+@@ -121,9 +121,9 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList>&
+ ////////////////////////////////////////////////////////////////////////
+ // Create ruler items
+ CDateTime ruler;
+- ruler.SetFromUTCDateTime(m_gridStart);
++ ruler.SetFromUTCDateTime(m_gridStart.GetAsLocalDateTime());
+ CDateTime rulerEnd;
+- rulerEnd.SetFromUTCDateTime(m_gridEnd);
++ rulerEnd.SetFromUTCDateTime(m_gridEnd.GetAsLocalDateTime());
+ CFileItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true)));
+ rulerItem->SetProperty("DateLabel", true);
+ m_rulerItems.emplace_back(rulerItem);
+diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
+index e5dfdb9ea0..3d2cf09b3f 100644
+--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
++++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
+@@ -239,7 +239,7 @@ std::string CPVRGUITimesInfo::TimeToTimeString(time_t datetime, TIME_FORMAT form
+ {
+ CDateTime time;
+ time.SetFromUTCDateTime(datetime);
+- return time.GetAsLocalizedTime(format, withSeconds);
++ return time.GetAsLocalDateTime().GetAsLocalizedTime(format, withSeconds);
+ }
+
+ std::string CPVRGUITimesInfo::GetTimeshiftStartTime(TIME_FORMAT format) const
+diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp
+index a2e1cbd131..8e047b8d52 100644
+--- a/xbmc/pvr/recordings/PVRRecording.cpp
++++ b/xbmc/pvr/recordings/PVRRecording.cpp
+@@ -524,7 +524,7 @@ void CPVRRecording::UpdatePath()
+ const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const
+ {
+ static CDateTime tmp;
+- tmp.SetFromUTCDateTime(m_recordingTime);
++ tmp.SetFromUTCDateTime(m_recordingTime.GetAsLocalDateTime());
+
+ return tmp;
+ }
+@@ -538,7 +538,7 @@ CDateTime CPVRRecording::EndTimeAsUTC() const
+ CDateTime CPVRRecording::EndTimeAsLocalTime() const
+ {
+ CDateTime ret;
+- ret.SetFromUTCDateTime(EndTimeAsUTC());
++ ret.SetFromUTCDateTime(EndTimeAsUTC().GetAsLocalDateTime());
+ return ret;
+ }
+
+--
+2.43.0
+
+
+From e9cbb86184358f6eb3235a76936fedbf38ab4d97 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Wed, 4 Nov 2020 11:13:15 +0200
+Subject: [PATCH 10/19] Get rid of {Set,}FromUTCDateTime
+
+ * The 'const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const'
+ method needs 'static' CDateTime instance set explicitly. This manifested
+ in Debian Bug #980038 : the return value of function is explicitly nulled
+ out causing immediate crash when using any PVR addon manipulating PVR
+ recordings.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/FileItem.cpp | 2 +-
+ xbmc/XBDateTime.cpp | 33 ++-----------------
+ xbmc/XBDateTime.h | 4 ---
+ .../savestates/SavestateDatabase.cpp | 2 +-
+ xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp | 8 ++---
+ xbmc/pvr/epg/Epg.cpp | 2 +-
+ xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp | 4 +--
+ xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp | 3 +-
+ xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp | 3 +-
+ xbmc/pvr/recordings/PVRRecording.cpp | 8 ++---
+ xbmc/test/TestDateTime.cpp | 26 ---------------
+ 11 files changed, 14 insertions(+), 81 deletions(-)
+
+diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp
+index 2eb5eb072c..8f14467aff 100644
+--- a/xbmc/FileItem.cpp
++++ b/xbmc/FileItem.cpp
+@@ -199,7 +199,7 @@ CFileItem::CFileItem(const std::shared_ptr<PVR::CPVREpgSearchFilter>& filter)
+
+ const CDateTime lastExec = filter->GetLastExecutedDateTime();
+ if (lastExec.IsValid())
+- m_dateTime.SetFromUTCDateTime(lastExec);
++ m_dateTime = lastExec;
+
+ SetArt("icon", "DefaultPVRSearch.png");
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index afd2d8c6a5..86168ecc13 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -613,19 +613,6 @@ std::string CDateTime::GetAsSaveString() const
+ return date::format("%Y%m%d_%H%M%S", sp);
+ }
+
+-bool CDateTime::SetFromUTCDateTime(const CDateTime &dateTime)
+-{
+- m_time = dateTime.m_time;
+- m_state = valid;
+- return true;
+-}
+-
+-bool CDateTime::SetFromUTCDateTime(const time_t &dateTime)
+-{
+- CDateTime tmp(dateTime);
+- return SetFromUTCDateTime(tmp);
+-}
+-
+ bool CDateTime::SetFromW3CDate(const std::string &dateTime)
+ {
+ std::string date;
+@@ -700,10 +687,8 @@ bool CDateTime::SetFromW3CDateTime(const std::string &dateTime, bool ignoreTimez
+
+ if (!ignoreTimezone && !zone.empty())
+ {
+- // check if the timezone is UTC
+- if (StringUtils::StartsWith(zone, "Z"))
+- return SetFromUTCDateTime(tmpDateTime);
+- else
++ // check if the timezone is not UTC
++ if (!StringUtils::StartsWith(zone, "Z"))
+ {
+ // retrieve the timezone offset (ignoring the + or -)
+ CDateTimeSpan zoneSpan; zoneSpan.SetFromTimeString(zone.substr(1));
+@@ -853,20 +838,6 @@ CDateTime CDateTime::FromW3CDateTime(const std::string &date, bool ignoreTimezon
+ return dt;
+ }
+
+-CDateTime CDateTime::FromUTCDateTime(const CDateTime &dateTime)
+-{
+- CDateTime dt;
+- dt.SetFromUTCDateTime(dateTime);
+- return dt;
+-}
+-
+-CDateTime CDateTime::FromUTCDateTime(const time_t &dateTime)
+-{
+- CDateTime dt;
+- dt.SetFromUTCDateTime(dateTime);
+- return dt;
+-}
+-
+ CDateTime CDateTime::FromRFC1123DateTime(const std::string &dateTime)
+ {
+ CDateTime dt;
+diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h
+index a810619566..ff2d758caa 100644
+--- a/xbmc/XBDateTime.h
++++ b/xbmc/XBDateTime.h
+@@ -78,8 +78,6 @@ public:
+ static CDateTime FromDBTime(const std::string &time);
+ static CDateTime FromW3CDate(const std::string &date);
+ static CDateTime FromW3CDateTime(const std::string &date, bool ignoreTimezone = false);
+- static CDateTime FromUTCDateTime(const CDateTime &dateTime);
+- static CDateTime FromUTCDateTime(const time_t &dateTime);
+ static CDateTime FromRFC1123DateTime(const std::string &dateTime);
+
+ const CDateTime& operator =(const time_t& right);
+@@ -144,8 +142,6 @@ public:
+ bool SetFromDBTime(const std::string &time);
+ bool SetFromW3CDate(const std::string &date);
+ bool SetFromW3CDateTime(const std::string &date, bool ignoreTimezone = false);
+- bool SetFromUTCDateTime(const CDateTime &dateTime);
+- bool SetFromUTCDateTime(const time_t &dateTime);
+ bool SetFromRFC1123DateTime(const std::string &dateTime);
+
+ /*! \brief set from a database datetime format YYYY-MM-DD HH:MM:SS
+diff --git a/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp b/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp
+index 2611f25ad8..2a44e07596 100644
+--- a/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp
++++ b/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp
+@@ -149,7 +149,7 @@ void CSavestateDatabase::GetSavestateItem(const ISavestate& savestate,
+ const std::string& savestatePath,
+ CFileItem& item)
+ {
+- CDateTime dateUTC = CDateTime::FromUTCDateTime(savestate.Created());
++ CDateTime dateUTC = savestate.Created();
+
+ std::string label;
+ std::string label2;
+diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
+index 4cf7f2f0c4..13b826c1b1 100644
+--- a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp
+@@ -252,7 +252,7 @@ CDateTime CGUIDialogPVRGuideSearch::ReadDateTime(const std::string& strDate, con
+ sscanf(strTime.c_str(), "%d:%d", &iHours, &iMinutes);
+ dateTime.SetFromDBDate(strDate);
+ dateTime.SetDateTime(dateTime.GetYear(), dateTime.GetMonth(), dateTime.GetDay(), iHours, iMinutes, 0);
+- return dateTime.GetAsUTCDateTime();
++ return dateTime;
+ }
+
+ bool CGUIDialogPVRGuideSearch::IsRadioSelected(int controlID)
+@@ -358,10 +358,8 @@ void CGUIDialogPVRGuideSearch::Update()
+ if (!m_endDateTime.IsValid())
+ m_endDateTime = m_startDateTime + CDateTimeSpan(10, 0, 0, 0); // default to start + 10 days
+
+- CDateTime startLocal;
+- startLocal.SetFromUTCDateTime(m_startDateTime);
+- CDateTime endLocal;
+- endLocal.SetFromUTCDateTime(m_endDateTime);
++ CDateTime startLocal = m_startDateTime;
++ CDateTime endLocal = m_endDateTime;
+
+ SET_CONTROL_LABEL2(CONTROL_EDIT_START_TIME, startLocal.GetAsLocalizedTime("", false));
+ {
+diff --git a/xbmc/pvr/epg/Epg.cpp b/xbmc/pvr/epg/Epg.cpp
+index 1112b2ff89..bb0dbf967b 100644
+--- a/xbmc/pvr/epg/Epg.cpp
++++ b/xbmc/pvr/epg/Epg.cpp
+@@ -264,7 +264,7 @@ bool CPVREpg::Update(time_t start,
+
+ if (!m_lastScanTime.IsValid())
+ {
+- m_lastScanTime.SetFromUTCDateTime(time_t(0));
++ m_lastScanTime = time_t(0);
+ m_bUpdateLastScanTime = true;
+ }
+ }
+diff --git a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
+index ef7a715f70..3e89c06d6e 100644
+--- a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
++++ b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp
+@@ -121,9 +121,9 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr<CFileItemList>&
+ ////////////////////////////////////////////////////////////////////////
+ // Create ruler items
+ CDateTime ruler;
+- ruler.SetFromUTCDateTime(m_gridStart.GetAsLocalDateTime());
++ ruler = m_gridStart.GetAsLocalDateTime();
+ CDateTime rulerEnd;
+- rulerEnd.SetFromUTCDateTime(m_gridEnd.GetAsLocalDateTime());
++ rulerEnd = m_gridEnd.GetAsLocalDateTime();
+ CFileItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true)));
+ rulerItem->SetProperty("DateLabel", true);
+ m_rulerItems.emplace_back(rulerItem);
+diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
+index 9cffb9a934..80c25118b0 100644
+--- a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
++++ b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
+@@ -568,8 +568,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item,
+ {
+ case LISTITEM_DATE:
+ {
+- CDateTime lastExecLocal;
+- lastExecLocal.SetFromUTCDateTime(filter->GetLastExecutedDateTime());
++ CDateTime lastExecLocal = filter->GetLastExecutedDateTime();
+ strValue = GetAsLocalizedDateTimeString(lastExecLocal);
+ if (strValue.empty())
+ strValue = g_localizeStrings.Get(10006); // "N/A"
+diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
+index 3d2cf09b3f..c5b44b840c 100644
+--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
++++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp
+@@ -237,8 +237,7 @@ void CPVRGUITimesInfo::Update()
+
+ std::string CPVRGUITimesInfo::TimeToTimeString(time_t datetime, TIME_FORMAT format, bool withSeconds)
+ {
+- CDateTime time;
+- time.SetFromUTCDateTime(datetime);
++ CDateTime time(datetime);
+ return time.GetAsLocalDateTime().GetAsLocalizedTime(format, withSeconds);
+ }
+
+diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp
+index 8e047b8d52..5a82c7b1dc 100644
+--- a/xbmc/pvr/recordings/PVRRecording.cpp
++++ b/xbmc/pvr/recordings/PVRRecording.cpp
+@@ -523,9 +523,7 @@ void CPVRRecording::UpdatePath()
+
+ const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const
+ {
+- static CDateTime tmp;
+- tmp.SetFromUTCDateTime(m_recordingTime.GetAsLocalDateTime());
+-
++ static CDateTime tmp = m_recordingTime.GetAsLocalDateTime();
+ return tmp;
+ }
+
+@@ -537,9 +535,7 @@ CDateTime CPVRRecording::EndTimeAsUTC() const
+
+ CDateTime CPVRRecording::EndTimeAsLocalTime() const
+ {
+- CDateTime ret;
+- ret.SetFromUTCDateTime(EndTimeAsUTC().GetAsLocalDateTime());
+- return ret;
++ return EndTimeAsUTC().GetAsLocalDateTime();
+ }
+
+ bool CPVRRecording::WillBeExpiredWithNewLifetime(int iLifetime) const
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index 4c55fafe06..ea910a8763 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -240,32 +240,6 @@ TEST_F(TestDateTime, SetFromW3CDateTime)
+ EXPECT_EQ(dateTime2.GetSecond(), 30);
+ }
+
+-TEST_F(TestDateTime, SetFromUTCDateTime)
+-{
+- CDateTime dateTime1;
+- dateTime1.SetFromDBDateTime("1991-05-14 12:34:56");
+-
+- CDateTime dateTime2;
+- EXPECT_TRUE(dateTime2.SetFromUTCDateTime(dateTime1));
+- EXPECT_EQ(dateTime2.GetYear(), 1991);
+- EXPECT_EQ(dateTime2.GetMonth(), 5);
+- EXPECT_EQ(dateTime2.GetDay(), 14);
+- EXPECT_EQ(dateTime2.GetHour(), 12);
+- EXPECT_EQ(dateTime2.GetMinute(), 34);
+- EXPECT_EQ(dateTime2.GetSecond(), 56);
+-
+- const time_t time = 674224496;
+-
+- CDateTime dateTime3;
+- EXPECT_TRUE(dateTime3.SetFromUTCDateTime(time));
+- EXPECT_EQ(dateTime3.GetYear(), 1991);
+- EXPECT_EQ(dateTime3.GetMonth(), 5);
+- EXPECT_EQ(dateTime3.GetDay(), 14);
+- EXPECT_EQ(dateTime3.GetHour(), 12);
+- EXPECT_EQ(dateTime3.GetMinute(), 34);
+- EXPECT_EQ(dateTime3.GetSecond(), 56);
+-}
+-
+ TEST_F(TestDateTime, SetFromRFC1123DateTime)
+ {
+ std::string dateTime1("Mon, 21 Oct 2018 12:16:24 GMT");
+--
+2.43.0
+
+
+From 9583d0b325df73f5e29b4c901715b9a6bc389295 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Thu, 13 May 2021 11:18:31 +0000
+Subject: [PATCH 11/19] Fix one-time assignment of PVR recording times
+
+Fixes pvr.dvbviewer#49
+Closes: #984682
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/pvr/recordings/PVRRecording.cpp | 5 ++---
+ xbmc/pvr/recordings/PVRRecording.h | 2 +-
+ 2 files changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp
+index 5a82c7b1dc..39eea88740 100644
+--- a/xbmc/pvr/recordings/PVRRecording.cpp
++++ b/xbmc/pvr/recordings/PVRRecording.cpp
+@@ -521,10 +521,9 @@ void CPVRRecording::UpdatePath()
+ m_strChannelName, m_recordingTime, m_strRecordingId);
+ }
+
+-const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const
++CDateTime CPVRRecording::RecordingTimeAsLocalTime() const
+ {
+- static CDateTime tmp = m_recordingTime.GetAsLocalDateTime();
+- return tmp;
++ return m_recordingTime.GetAsLocalDateTime();
+ }
+
+ CDateTime CPVRRecording::EndTimeAsUTC() const
+diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h
+index bdd8378225..dd125ad1ea 100644
+--- a/xbmc/pvr/recordings/PVRRecording.h
++++ b/xbmc/pvr/recordings/PVRRecording.h
+@@ -188,7 +188,7 @@ public:
+ * @brief Retrieve the recording start as local time
+ * @return the recording start time
+ */
+- const CDateTime& RecordingTimeAsLocalTime() const;
++ CDateTime RecordingTimeAsLocalTime() const;
+
+ /*!
+ * @brief Retrieve the recording end as UTC time
+--
+2.43.0
+
+
+From 732620e37163d653ec75bbefd2a58293e2303420 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Wed, 21 Dec 2022 18:27:24 +0000
+Subject: [PATCH 12/19] Move date includes to separate header
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/ServiceManager.cpp | 5 +----
+ xbmc/XBDateTime.cpp | 8 +-------
+ xbmc/addons/TimeZoneResource.cpp | 5 +++--
+ xbmc/application/Application.cpp | 5 +----
+ xbmc/platform/posix/PosixTimezone.cpp | 21 +++++++++------------
+ xbmc/test/TestDateTime.cpp | 5 +----
+ xbmc/utils/CMakeLists.txt | 1 +
+ xbmc/utils/DateLib.h | 24 ++++++++++++++++++++++++
+ 8 files changed, 41 insertions(+), 33 deletions(-)
+ create mode 100644 xbmc/utils/DateLib.h
+
+diff --git a/xbmc/ServiceManager.cpp b/xbmc/ServiceManager.cpp
+index 83070489e3..b95091e993 100644
+--- a/xbmc/ServiceManager.cpp
++++ b/xbmc/ServiceManager.cpp
+@@ -45,15 +45,12 @@
+ #include "storage/DetectDVDType.h"
+ #endif
+ #include "storage/MediaManager.h"
++#include "utils/DateLib.h"
+ #include "utils/FileExtensionProvider.h"
+ #include "utils/URIUtils.h"
+ #include "utils/log.h"
+ #include "weather/WeatherManager.h"
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+-#include <date/tz.h>
+-
+ using namespace KODI;
+
+ CServiceManager::CServiceManager() = default;
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index 86168ecc13..12ae853c43 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -11,18 +11,12 @@
+ #include "LangInfo.h"
+ #include "guilib/LocalizeStrings.h"
+ #include "utils/Archive.h"
++#include "utils/DateLib.h"
+ #include "utils/StringUtils.h"
+-#include "utils/XTimeUtils.h"
+ #include "utils/log.h"
+
+ #include <cstdlib>
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+-#include <date/date.h>
+-#include <date/iso_week.h>
+-#include <date/tz.h>
+-
+ static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ CDateTimeSpan::CDateTimeSpan(const CDateTimeSpan& span)
+diff --git a/xbmc/addons/TimeZoneResource.cpp b/xbmc/addons/TimeZoneResource.cpp
+index 94246c9e18..6dfba75e89 100644
+--- a/xbmc/addons/TimeZoneResource.cpp
++++ b/xbmc/addons/TimeZoneResource.cpp
+@@ -8,11 +8,10 @@
+ #include "TimeZoneResource.h"
+
+ #include "addons/addoninfo/AddonType.h"
++#include "utils/DateLib.h"
+ #include "utils/StringUtils.h"
+ #include "utils/URIUtils.h"
+
+-#include <date/tz.h>
+-
+ namespace ADDON
+ {
+
+@@ -33,7 +32,9 @@ bool CTimeZoneResource::IsInUse() const
+
+ void CTimeZoneResource::OnPostInstall(bool update, bool modal)
+ {
++#if defined(DATE_INTERNAL_TZDATA)
+ date::reload_tzdb();
++#endif
+ }
+
+ } // namespace ADDON
+diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp
+index 3227933296..6aa584e5c9 100644
+--- a/xbmc/application/Application.cpp
++++ b/xbmc/application/Application.cpp
+@@ -100,6 +100,7 @@
+ #include "speech/ISpeechRecognition.h"
+ #include "threads/SingleLock.h"
+ #include "utils/CPUInfo.h"
++#include "utils/DateLib.h"
+ #include "utils/FileExtensionProvider.h"
+ #include "utils/RegExp.h"
+ #include "utils/SystemInfo.h"
+@@ -196,12 +197,8 @@
+ #include "pictures/GUIWindowSlideShow.h"
+ #include "utils/CharsetConverter.h"
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+ #include <mutex>
+
+-#include <date/tz.h>
+-
+ using namespace ADDON;
+ using namespace XFILE;
+ #ifdef HAS_DVD_DRIVE
+diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp
+index 7f0b8d4096..fa9ea1dde9 100644
+--- a/xbmc/platform/posix/PosixTimezone.cpp
++++ b/xbmc/platform/posix/PosixTimezone.cpp
+@@ -9,25 +9,22 @@
+ #ifdef TARGET_ANDROID
+ #include "platform/android/bionic_supplement/bionic_supplement.h"
+ #endif
+-#include "PlatformDefs.h"
+ #include "PosixTimezone.h"
+-#include "utils/SystemInfo.h"
+-
+ #include "ServiceBroker.h"
+-#include "utils/StringUtils.h"
+ #include "XBDateTime.h"
+-#include "settings/lib/Setting.h"
+-#include "settings/lib/SettingDefinitions.h"
++#include "filesystem/File.h"
++#include "platform/MessagePrinter.h"
+ #include "settings/Settings.h"
+ #include "settings/SettingsComponent.h"
+-#include <stdlib.h>
++#include "settings/lib/Setting.h"
++#include "settings/lib/SettingDefinitions.h"
++#include "utils/DateLib.h"
++#include "utils/StringUtils.h"
++#include "utils/SystemInfo.h"
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+-#include "filesystem/File.h"
+-#include "platform/MessagePrinter.h"
++#include <stdlib.h>
+
+-#include <date/tz.h>
++#include "PlatformDefs.h"
+
+ void CPosixTimezone::Init()
+ {
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index ea910a8763..de3f75e5e4 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -9,15 +9,12 @@
+ #include "LangInfo.h"
+ #include "XBDateTime.h"
+ #include "guilib/LocalizeStrings.h"
++#include "utils/DateLib.h"
+
+ #include <array>
+ #include <chrono>
+ #include <iostream>
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+-#include <date/date.h>
+-#include <date/tz.h>
+ #include <gtest/gtest.h>
+
+ class TestDateTime : public testing::Test
+diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt
+index 8e1328999f..bb4851c0ce 100644
+--- a/xbmc/utils/CMakeLists.txt
++++ b/xbmc/utils/CMakeLists.txt
+@@ -97,6 +97,7 @@ set(HEADERS ActorProtocol.h
+ Crc32.h
+ CSSUtils.h
+ DatabaseUtils.h
++ DateLib.h
+ Digest.h
+ DiscsUtils.h
+ EndianSwap.h
+diff --git a/xbmc/utils/DateLib.h b/xbmc/utils/DateLib.h
+new file mode 100644
+index 0000000000..e39650aa03
+--- /dev/null
++++ b/xbmc/utils/DateLib.h
+@@ -0,0 +1,24 @@
++/*
++ * Copyright (C) 2005-2022 Team Kodi
++ * This file is part of Kodi - https://kodi.tv
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ * See LICENSES/README.md for more information.
++ */
++
++#if defined(DATE_INTERNAL_TZDATA)
++#define USE_OS_TZDB 0
++#else
++#define USE_OS_TZDB 1
++#endif
++
++#if defined(DATE_HAS_STRINGVIEW)
++#define HAS_STRING_VIEW 1
++#else
++#define HAS_STRING_VIEW 0
++#endif
++
++#define HAS_REMOTE_API 0
++#include <date/date.h>
++#include <date/iso_week.h>
++#include <date/tz.h>
+--
+2.43.0
+
+
+From 46f92320cc18c3975bddb5dbe8e0d5b66a0b64c6 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Fri, 14 May 2021 09:39:55 +0000
+Subject: [PATCH 13/19] CDateTime: Use 'long double' as underlying storage type
+ for time points
+
+This should fix the GUI date dialog issue:
+
+https://github.com/xbmc/xbmc/pull/18727#issuecomment-840805031
+https://github.com/xbmc/xbmc/pull/18727#issuecomment-840805031
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/XBDateTime.cpp | 18 +++++++++---------
+ xbmc/XBDateTime.h | 20 ++++++++++----------
+ xbmc/dialogs/GUIDialogNumeric.cpp | 3 +--
+ xbmc/utils/Archive.cpp | 8 ++++----
+ xbmc/utils/Archive.h | 6 ++++--
+ xbmc/utils/XTimeUtils.h | 3 +++
+ xbmc/utils/test/TestArchive.cpp | 12 ++++++------
+ 7 files changed, 37 insertions(+), 33 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index 12ae853c43..fdcc27c9af 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -176,7 +176,7 @@ CDateTime::CDateTime(const time_t& time)
+ SetValid(true);
+ }
+
+-CDateTime::CDateTime(const std::chrono::system_clock::time_point& time)
++CDateTime::CDateTime(const KODI::TIME::time_point& time)
+ {
+ m_time = time;
+ SetValid(true);
+@@ -223,7 +223,7 @@ const CDateTime& CDateTime::operator =(const tm& right)
+ return *this;
+ }
+
+-const CDateTime& CDateTime::operator=(const std::chrono::system_clock::time_point& right)
++const CDateTime& CDateTime::operator=(const KODI::TIME::time_point& right)
+ {
+ m_time = right;
+ SetValid(true);
+@@ -321,32 +321,32 @@ bool CDateTime::operator !=(const tm& right) const
+ return !operator ==(right);
+ }
+
+-bool CDateTime::operator>(const std::chrono::system_clock::time_point& right) const
++bool CDateTime::operator>(const KODI::TIME::time_point& right) const
+ {
+ return m_time > right;
+ }
+
+-bool CDateTime::operator>=(const std::chrono::system_clock::time_point& right) const
++bool CDateTime::operator>=(const KODI::TIME::time_point& right) const
+ {
+ return operator>(right) || operator==(right);
+ }
+
+-bool CDateTime::operator<(const std::chrono::system_clock::time_point& right) const
++bool CDateTime::operator<(const KODI::TIME::time_point& right) const
+ {
+ return m_time < right;
+ }
+
+-bool CDateTime::operator<=(const std::chrono::system_clock::time_point& right) const
++bool CDateTime::operator<=(const KODI::TIME::time_point& right) const
+ {
+ return operator<(right) || operator==(right);
+ }
+
+-bool CDateTime::operator==(const std::chrono::system_clock::time_point& right) const
++bool CDateTime::operator==(const KODI::TIME::time_point& right) const
+ {
+ return m_time == right;
+ }
+
+-bool CDateTime::operator!=(const std::chrono::system_clock::time_point& right) const
++bool CDateTime::operator!=(const KODI::TIME::time_point& right) const
+ {
+ return !operator==(right);
+ }
+@@ -577,7 +577,7 @@ void CDateTime::GetAsTm(tm& time) const
+ localtime_r(&t, &time);
+ }
+
+-std::chrono::system_clock::time_point CDateTime::GetAsTimePoint() const
++KODI::TIME::time_point CDateTime::GetAsTimePoint() const
+ {
+ return m_time;
+ }
+diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h
+index ff2d758caa..b0a7f386d9 100644
+--- a/xbmc/XBDateTime.h
++++ b/xbmc/XBDateTime.h
+@@ -65,7 +65,7 @@ public:
+ CDateTime& operator=(const CDateTime&) = default;
+ explicit CDateTime(const time_t& time);
+ explicit CDateTime(const tm& time);
+- explicit CDateTime(const std::chrono::system_clock::time_point& time);
++ explicit CDateTime(const KODI::TIME::time_point& time);
+ CDateTime(int year, int month, int day, int hour, int minute, int second);
+
+ static CDateTime GetCurrentDateTime();
+@@ -82,7 +82,7 @@ public:
+
+ const CDateTime& operator =(const time_t& right);
+ const CDateTime& operator =(const tm& right);
+- const CDateTime& operator=(const std::chrono::system_clock::time_point& right);
++ const CDateTime& operator=(const KODI::TIME::time_point& right);
+
+ bool operator >(const CDateTime& right) const;
+ bool operator >=(const CDateTime& right) const;
+@@ -105,12 +105,12 @@ public:
+ bool operator ==(const tm& right) const;
+ bool operator !=(const tm& right) const;
+
+- bool operator>(const std::chrono::system_clock::time_point& right) const;
+- bool operator>=(const std::chrono::system_clock::time_point& right) const;
+- bool operator<(const std::chrono::system_clock::time_point& right) const;
+- bool operator<=(const std::chrono::system_clock::time_point& right) const;
+- bool operator==(const std::chrono::system_clock::time_point& right) const;
+- bool operator!=(const std::chrono::system_clock::time_point& right) const;
++ bool operator>(const KODI::TIME::time_point& right) const;
++ bool operator>=(const KODI::TIME::time_point& right) const;
++ bool operator<(const KODI::TIME::time_point& right) const;
++ bool operator<=(const KODI::TIME::time_point& right) const;
++ bool operator==(const KODI::TIME::time_point& right) const;
++ bool operator!=(const KODI::TIME::time_point& right) const;
+
+ CDateTime operator +(const CDateTimeSpan& right) const;
+ CDateTime operator -(const CDateTimeSpan& right) const;
+@@ -151,7 +151,7 @@ public:
+
+ void GetAsTime(time_t& time) const;
+ void GetAsTm(tm& time) const;
+- std::chrono::system_clock::time_point GetAsTimePoint() const;
++ KODI::TIME::time_point GetAsTimePoint() const;
+
+ /*! \brief convert UTC datetime to local datetime
+ */
+@@ -173,7 +173,7 @@ public:
+ bool IsValid() const;
+
+ private:
+- std::chrono::system_clock::time_point m_time;
++ KODI::TIME::time_point m_time;
+
+ typedef enum _STATE
+ {
+diff --git a/xbmc/dialogs/GUIDialogNumeric.cpp b/xbmc/dialogs/GUIDialogNumeric.cpp
+index bba219e44e..985fd05722 100644
+--- a/xbmc/dialogs/GUIDialogNumeric.cpp
++++ b/xbmc/dialogs/GUIDialogNumeric.cpp
+@@ -498,8 +498,7 @@ bool CGUIDialogNumeric::ShowAndGetSeconds(std::string &timeString, const std::st
+ CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogNumeric>(WINDOW_DIALOG_NUMERIC);
+ if (!pDialog) return false;
+
+- std::chrono::system_clock::time_point time{
+- std::chrono::seconds{StringUtils::TimeStringToSeconds(timeString)}};
++ KODI::TIME::time_point time{std::chrono::seconds{StringUtils::TimeStringToSeconds(timeString)}};
+
+ CDateTime datetime(time);
+
+diff --git a/xbmc/utils/Archive.cpp b/xbmc/utils/Archive.cpp
+index 0275c057f9..bb207c6ca5 100644
+--- a/xbmc/utils/Archive.cpp
++++ b/xbmc/utils/Archive.cpp
+@@ -152,9 +152,9 @@ CArchive& CArchive::operator<<(const std::wstring& wstr)
+ return streamout(wstr.data(), size * sizeof(wchar_t));
+ }
+
+-CArchive& CArchive::operator<<(const std::chrono::system_clock::time_point& time)
++CArchive& CArchive::operator<<(const KODI::TIME::time_point& time)
+ {
+- return streamout(&time, sizeof(std::chrono::system_clock::time_point));
++ return streamout(&time, sizeof(KODI::TIME::time_point));
+ }
+
+ CArchive& CArchive::operator<<(IArchivable& obj)
+@@ -265,9 +265,9 @@ CArchive& CArchive::operator>>(std::wstring& wstr)
+ return *this;
+ }
+
+-CArchive& CArchive::operator>>(std::chrono::system_clock::time_point& time)
++CArchive& CArchive::operator>>(KODI::TIME::time_point& time)
+ {
+- return streamin(&time, sizeof(std::chrono::system_clock::time_point));
++ return streamin(&time, sizeof(KODI::TIME::time_point));
+ }
+
+ CArchive& CArchive::operator>>(IArchivable& obj)
+diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h
+index 86a2bebc23..ece5f9ac9c 100644
+--- a/xbmc/utils/Archive.h
++++ b/xbmc/utils/Archive.h
+@@ -8,6 +8,8 @@
+
+ #pragma once
+
++#include "utils/XTimeUtils.h"
++
+ #include <cstring>
+ #include <memory>
+ #include <string>
+@@ -57,7 +59,7 @@ public:
+ CArchive& operator<<(char c);
+ CArchive& operator<<(const std::string &str);
+ CArchive& operator<<(const std::wstring& wstr);
+- CArchive& operator<<(const std::chrono::system_clock::time_point& time);
++ CArchive& operator<<(const KODI::TIME::time_point& time);
+ CArchive& operator<<(IArchivable& obj);
+ CArchive& operator<<(const CVariant& variant);
+ CArchive& operator<<(const std::vector<std::string>& strArray);
+@@ -126,7 +128,7 @@ public:
+
+ CArchive& operator>>(std::string &str);
+ CArchive& operator>>(std::wstring& wstr);
+- CArchive& operator>>(std::chrono::system_clock::time_point& time);
++ CArchive& operator>>(KODI::TIME::time_point& time);
+ CArchive& operator>>(IArchivable& obj);
+ CArchive& operator>>(CVariant& variant);
+ CArchive& operator>>(std::vector<std::string>& strArray);
+diff --git a/xbmc/utils/XTimeUtils.h b/xbmc/utils/XTimeUtils.h
+index 9d3b0e86a3..ed1b25fb74 100644
+--- a/xbmc/utils/XTimeUtils.h
++++ b/xbmc/utils/XTimeUtils.h
+@@ -28,6 +28,9 @@ namespace KODI
+ namespace TIME
+ {
+
++using time_point = std::chrono::time_point<std::chrono::system_clock,
++ std::chrono::duration<long double, std::nano>>;
++
+ template<typename Rep, typename Period>
+ void Sleep(std::chrono::duration<Rep, Period> duration)
+ {
+diff --git a/xbmc/utils/test/TestArchive.cpp b/xbmc/utils/test/TestArchive.cpp
+index b9ab780cf9..de912eba21 100644
+--- a/xbmc/utils/test/TestArchive.cpp
++++ b/xbmc/utils/test/TestArchive.cpp
+@@ -223,8 +223,8 @@ TEST_F(TestArchive, StringArchive)
+ TEST_F(TestArchive, TimePointArchive)
+ {
+ ASSERT_NE(nullptr, file);
+- std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now();
+- std::chrono::system_clock::time_point tp_var;
++ KODI::TIME::time_point tp_ref = std::chrono::system_clock::now();
++ KODI::TIME::time_point tp_var;
+
+ CArchive arstore(file, CArchive::store);
+ arstore << tp_ref;
+@@ -235,7 +235,7 @@ TEST_F(TestArchive, TimePointArchive)
+ arload >> tp_var;
+ arload.Close();
+
+- EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point)));
++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(KODI::TIME::time_point)));
+ }
+
+ TEST_F(TestArchive, CVariantArchive)
+@@ -335,8 +335,8 @@ TEST_F(TestArchive, MultiTypeArchive)
+ char char_ref = 'A', char_var = '\0';
+ std::string string_ref = "test string", string_var;
+ std::wstring wstring_ref = L"test wstring", wstring_var;
+- std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now();
+- std::chrono::system_clock::time_point tp_var;
++ KODI::TIME::time_point tp_ref = std::chrono::system_clock::now();
++ KODI::TIME::time_point tp_var;
+ CVariant CVariant_ref((int)1), CVariant_var;
+ std::vector<std::string> strArray_ref, strArray_var;
+ strArray_ref.emplace_back("test strArray_ref 0");
+@@ -398,7 +398,7 @@ TEST_F(TestArchive, MultiTypeArchive)
+ EXPECT_EQ(char_ref, char_var);
+ EXPECT_STREQ(string_ref.c_str(), string_var.c_str());
+ EXPECT_STREQ(wstring_ref.c_str(), wstring_var.c_str());
+- EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point)));
++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(KODI::TIME::time_point)));
+ EXPECT_TRUE(CVariant_var.isInteger());
+ EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str());
+ EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str());
+--
+2.43.0
+
+
+From c2f4add32ebf1c3a965f1a88af0d49afcdf9e847 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Fri, 14 May 2021 12:35:54 +0000
+Subject: [PATCH 14/19] Use "long double" type in time_t / tm conversions
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/XBDateTime.cpp | 60 +++++++++++++++++++++++++--------------------
+ 1 file changed, 34 insertions(+), 26 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index fdcc27c9af..c71478085b 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -93,10 +93,10 @@ const CDateTimeSpan& CDateTimeSpan::operator -=(const CDateTimeSpan& right)
+
+ void CDateTimeSpan::SetDateTimeSpan(int day, int hour, int minute, int second)
+ {
+- m_timeSpan = std::chrono::duration_cast<std::chrono::seconds>(date::days(day)) +
+- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(hour)) +
+- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::minutes(minute)) +
+- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::seconds(second));
++ m_timeSpan = date::floor<std::chrono::seconds>(date::days(day)) +
++ date::floor<std::chrono::seconds>(std::chrono::hours(hour)) +
++ date::floor<std::chrono::seconds>(std::chrono::minutes(minute)) +
++ date::floor<std::chrono::seconds>(std::chrono::seconds(second));
+ }
+
+ void CDateTimeSpan::SetFromTimeString(const std::string& time) // hh:mm
+@@ -140,7 +140,7 @@ int CDateTimeSpan::GetSeconds() const
+
+ int CDateTimeSpan::GetSecondsTotal() const
+ {
+- return std::chrono::duration_cast<std::chrono::seconds>(m_timeSpan).count();
++ return date::floor<std::chrono::seconds>(m_timeSpan).count();
+ }
+
+ void CDateTimeSpan::SetFromPeriod(const std::string &period)
+@@ -172,7 +172,8 @@ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time)
+
+ CDateTime::CDateTime(const time_t& time)
+ {
+- m_time = std::chrono::system_clock::from_time_t(time);
++ Reset();
++ m_time += std::chrono::duration<long double>(time);
+ SetValid(true);
+ }
+
+@@ -184,7 +185,8 @@ CDateTime::CDateTime(const KODI::TIME::time_point& time)
+
+ CDateTime::CDateTime(const tm& time)
+ {
+- m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&time)));
++ Reset();
++ m_time += std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&time)));
+ SetValid(true);
+ }
+
+@@ -198,8 +200,7 @@ CDateTime CDateTime::GetCurrentDateTime()
+ auto zone = date::make_zoned(date::current_zone(), std::chrono::system_clock::now());
+
+ return CDateTime(
+- std::chrono::duration_cast<std::chrono::seconds>(zone.get_local_time().time_since_epoch())
+- .count());
++ date::floor<std::chrono::seconds>(zone.get_local_time().time_since_epoch()).count());
+ }
+
+ CDateTime CDateTime::GetUTCDateTime()
+@@ -209,7 +210,8 @@ CDateTime CDateTime::GetUTCDateTime()
+
+ const CDateTime& CDateTime::operator =(const time_t& right)
+ {
+- m_time = std::chrono::system_clock::from_time_t(right);
++ Reset();
++ m_time += std::chrono::duration<long double>(right);
+ SetValid(true);
+
+ return *this;
+@@ -217,7 +219,8 @@ const CDateTime& CDateTime::operator =(const time_t& right)
+
+ const CDateTime& CDateTime::operator =(const tm& right)
+ {
+- m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
++ Reset();
++ m_time += std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
+ SetValid(true);
+
+ return *this;
+@@ -263,7 +266,8 @@ bool CDateTime::operator !=(const CDateTime& right) const
+
+ bool CDateTime::operator >(const time_t& right) const
+ {
+- return m_time > std::chrono::system_clock::from_time_t(right);
++ return m_time >
++ std::chrono::system_clock::from_time_t(0) + std::chrono::duration<long double>(right);
+ }
+
+ bool CDateTime::operator >=(const time_t& right) const
+@@ -273,7 +277,8 @@ bool CDateTime::operator >=(const time_t& right) const
+
+ bool CDateTime::operator <(const time_t& right) const
+ {
+- return m_time < std::chrono::system_clock::from_time_t(right);
++ return m_time <
++ std::chrono::system_clock::from_time_t(0) + std::chrono::duration<long double>(right);
+ }
+
+ bool CDateTime::operator <=(const time_t& right) const
+@@ -283,7 +288,8 @@ bool CDateTime::operator <=(const time_t& right) const
+
+ bool CDateTime::operator ==(const time_t& right) const
+ {
+- return m_time == std::chrono::system_clock::from_time_t(right);
++ return m_time ==
++ std::chrono::system_clock::from_time_t(0) + std::chrono::duration<long double>(right);
+ }
+
+ bool CDateTime::operator !=(const time_t& right) const
+@@ -293,7 +299,8 @@ bool CDateTime::operator !=(const time_t& right) const
+
+ bool CDateTime::operator >(const tm& right) const
+ {
+- return m_time > std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
++ return m_time > std::chrono::system_clock::from_time_t(0) +
++ std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
+ }
+
+ bool CDateTime::operator >=(const tm& right) const
+@@ -303,7 +310,8 @@ bool CDateTime::operator >=(const tm& right) const
+
+ bool CDateTime::operator <(const tm& right) const
+ {
+- return m_time < std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
++ return m_time < std::chrono::system_clock::from_time_t(0) +
++ std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
+ }
+
+ bool CDateTime::operator <=(const tm& right) const
+@@ -313,7 +321,8 @@ bool CDateTime::operator <=(const tm& right) const
+
+ bool CDateTime::operator ==(const tm& right) const
+ {
+- return m_time == std::chrono::system_clock::from_time_t(std::mktime(const_cast<tm*>(&right)));
++ return m_time == std::chrono::system_clock::from_time_t(0) +
++ std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
+ }
+
+ bool CDateTime::operator !=(const tm& right) const
+@@ -387,7 +396,7 @@ CDateTimeSpan CDateTime::operator -(const CDateTime& right) const
+ {
+ CDateTimeSpan left;
+
+- left.m_timeSpan = std::chrono::duration_cast<std::chrono::seconds>(m_time - right.m_time);
++ left.m_timeSpan = date::floor<std::chrono::seconds>(m_time - right.m_time);
+ return left;
+ }
+
+@@ -496,7 +505,7 @@ int CDateTime::GetYear() const
+ int CDateTime::GetHour() const
+ {
+ auto dp = date::floor<date::days>(m_time);
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.hours().count();
+ }
+@@ -504,7 +513,7 @@ int CDateTime::GetHour() const
+ int CDateTime::GetMinute() const
+ {
+ auto dp = date::floor<date::days>(m_time);
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.minutes().count();
+ }
+@@ -512,7 +521,7 @@ int CDateTime::GetMinute() const
+ int CDateTime::GetSecond() const
+ {
+ auto dp = date::floor<date::days>(m_time);
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.seconds().count();
+ }
+@@ -529,7 +538,7 @@ int CDateTime::GetMinuteOfDay() const
+ {
+ auto dp = date::floor<std::chrono::hours>(m_time);
+ ;
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.hours().count() * 60 + time.minutes().count();
+ }
+@@ -566,12 +575,12 @@ bool CDateTime::SetTime(int hour, int minute, int second)
+
+ void CDateTime::GetAsTime(time_t& time) const
+ {
+- time = std::chrono::system_clock::to_time_t(m_time);
++ time = date::floor<std::chrono::seconds>(m_time.time_since_epoch()).count();
+ }
+
+ void CDateTime::GetAsTm(tm& time) const
+ {
+- auto t = std::chrono::system_clock::to_time_t(m_time);
++ auto t = date::floor<std::chrono::seconds>(m_time.time_since_epoch()).count();
+
+ time = {};
+ localtime_r(&t, &time);
+@@ -1180,8 +1189,7 @@ CDateTime CDateTime::GetAsLocalDateTime() const
+ auto zone = date::make_zoned(date::current_zone(), m_time);
+
+ return CDateTime(
+- std::chrono::duration_cast<std::chrono::seconds>(zone.get_local_time().time_since_epoch())
+- .count());
++ date::floor<std::chrono::seconds>(zone.get_local_time().time_since_epoch()).count());
+ }
+
+ std::string CDateTime::GetAsRFC1123DateTime() const
+--
+2.43.0
+
+
+From 0a476801a4d337a1e3e9c7115a03c58d302cdd75 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Mon, 17 May 2021 09:41:32 +0000
+Subject: [PATCH 15/19] Avoid time_t in tm conversions
+
+This should leave the possibility of time_t truncation on 32-bit
+architectures only in GetAsTime(), what we can do nothing with.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/XBDateTime.cpp | 56 +++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 46 insertions(+), 10 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index c71478085b..ce5c242128 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -186,7 +186,16 @@ CDateTime::CDateTime(const KODI::TIME::time_point& time)
+ CDateTime::CDateTime(const tm& time)
+ {
+ Reset();
+- m_time += std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&time)));
++
++ auto ymd = date::local_days(date::year(time.tm_year + 1900) / date::month(time.tm_mon + 1) /
++ time.tm_mday);
++ auto dur = ymd + std::chrono::hours(time.tm_hour) + std::chrono::minutes(time.tm_min) +
++ std::chrono::seconds(time.tm_sec);
++
++ auto timeT = date::floor<std::chrono::seconds>(dur.time_since_epoch()).count();
++
++ m_time += std::chrono::duration<long double>(timeT);
++
+ SetValid(true);
+ }
+
+@@ -220,7 +229,16 @@ const CDateTime& CDateTime::operator =(const time_t& right)
+ const CDateTime& CDateTime::operator =(const tm& right)
+ {
+ Reset();
+- m_time += std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
++
++ auto ymd = date::local_days(date::year(right.tm_year + 1900) / date::month(right.tm_mon + 1) /
++ right.tm_mday);
++ auto dur = ymd + std::chrono::hours(right.tm_hour) + std::chrono::minutes(right.tm_min) +
++ std::chrono::seconds(right.tm_sec);
++
++ auto timeT = date::floor<std::chrono::seconds>(dur.time_since_epoch()).count();
++
++ m_time += std::chrono::duration<long double>(timeT);
++
+ SetValid(true);
+
+ return *this;
+@@ -299,8 +317,8 @@ bool CDateTime::operator !=(const time_t& right) const
+
+ bool CDateTime::operator >(const tm& right) const
+ {
+- return m_time > std::chrono::system_clock::from_time_t(0) +
+- std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
++ CDateTime temp(right);
++ return m_time > temp.m_time;
+ }
+
+ bool CDateTime::operator >=(const tm& right) const
+@@ -310,8 +328,8 @@ bool CDateTime::operator >=(const tm& right) const
+
+ bool CDateTime::operator <(const tm& right) const
+ {
+- return m_time < std::chrono::system_clock::from_time_t(0) +
+- std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
++ CDateTime temp(right);
++ return m_time < temp.m_time;
+ }
+
+ bool CDateTime::operator <=(const tm& right) const
+@@ -321,8 +339,8 @@ bool CDateTime::operator <=(const tm& right) const
+
+ bool CDateTime::operator ==(const tm& right) const
+ {
+- return m_time == std::chrono::system_clock::from_time_t(0) +
+- std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&right)));
++ CDateTime temp(right);
++ return m_time == temp.m_time;
+ }
+
+ bool CDateTime::operator !=(const tm& right) const
+@@ -580,10 +598,28 @@ void CDateTime::GetAsTime(time_t& time) const
+
+ void CDateTime::GetAsTm(tm& time) const
+ {
+- auto t = date::floor<std::chrono::seconds>(m_time.time_since_epoch()).count();
++ auto dp = date::floor<date::days>(m_time);
+
+ time = {};
+- localtime_r(&t, &time);
++
++ auto ymd = date::year_month_day{dp};
++ time.tm_year = int(ymd.year()) - 1900;
++ time.tm_mon = unsigned(ymd.month()) - 1;
++ time.tm_mday = unsigned(ymd.day());
++
++ auto hms = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
++ time.tm_hour = hms.hours().count();
++ time.tm_min = hms.minutes().count();
++ time.tm_sec = hms.seconds().count();
++
++ date::weekday wd{dp};
++ time.tm_wday = wd.c_encoding();
++
++ auto newyear = date::sys_days(date::year(time.tm_year + 1900) / 1 / 1);
++ auto seconds_to_newyear = date::floor<std::chrono::seconds>(m_time - newyear);
++ time.tm_yday = seconds_to_newyear.count() / 86400;
++
++ time.tm_isdst = date::current_zone()->get_info(m_time).save.count() != 0;
+ }
+
+ KODI::TIME::time_point CDateTime::GetAsTimePoint() const
+--
+2.43.0
+
+
+From 431a3cdbdc388bb8661d4d35fe5a42b579e3fc86 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?P=C3=A4r=20Bj=C3=B6rklund?= <per.bjorklund@gmail.com>
+Date: Sun, 31 Oct 2021 10:33:30 +0100
+Subject: [PATCH 16/19] Windows patches
+
+---
+ xbmc/application/Application.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp
+index 6aa584e5c9..df55665b5e 100644
+--- a/xbmc/application/Application.cpp
++++ b/xbmc/application/Application.cpp
+@@ -357,7 +357,9 @@ bool CApplication::Create()
+ CServiceBroker::RegisterJobManager(std::make_shared<CJobManager>());
+
+ // Initialize,timezone
++#if defined(TARGET_POSIX)
+ g_timezone.Init();
++#endif
+
+ // Announcement service
+ m_pAnnouncementManager = std::make_shared<ANNOUNCEMENT::CAnnouncementManager>();
+--
+2.43.0
+
+
+From 257a56972008869e448fd81b15c67bb46c17f338 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Mon, 11 Jul 2022 09:24:01 +0000
+Subject: [PATCH 17/19] XBDateTime: Use timezone info specified via timezone
+ picker.
+
+Fixes broken timezone picker as reported in Debian Bug#1011294.
+
+Also fixes TZ-related issue spotted on reproducibility build
+where TZ envvar specifying timezone as absolute pathname on Linux
+and FreeBSD screwed date lib up.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/XBDateTime.cpp | 8 +++---
+ xbmc/test/TestDateTime.cpp | 20 ++++++++++++---
+ xbmc/utils/CMakeLists.txt | 1 +
+ xbmc/utils/DateLib.cpp | 51 ++++++++++++++++++++++++++++++++++++++
+ xbmc/utils/DateLib.h | 13 ++++++++++
+ 5 files changed, 86 insertions(+), 7 deletions(-)
+ create mode 100644 xbmc/utils/DateLib.cpp
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index ce5c242128..29118e7570 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -206,7 +206,7 @@ CDateTime::CDateTime(int year, int month, int day, int hour, int minute, int sec
+
+ CDateTime CDateTime::GetCurrentDateTime()
+ {
+- auto zone = date::make_zoned(date::current_zone(), std::chrono::system_clock::now());
++ auto zone = date::make_zoned(KODI::TIME::GetTimeZone(), std::chrono::system_clock::now());
+
+ return CDateTime(
+ date::floor<std::chrono::seconds>(zone.get_local_time().time_since_epoch()).count());
+@@ -619,7 +619,7 @@ void CDateTime::GetAsTm(tm& time) const
+ auto seconds_to_newyear = date::floor<std::chrono::seconds>(m_time - newyear);
+ time.tm_yday = seconds_to_newyear.count() / 86400;
+
+- time.tm_isdst = date::current_zone()->get_info(m_time).save.count() != 0;
++ time.tm_isdst = KODI::TIME::GetTimeZone()->get_info(m_time).save.count() != 0;
+ }
+
+ KODI::TIME::time_point CDateTime::GetAsTimePoint() const
+@@ -1222,7 +1222,7 @@ std::string CDateTime::GetAsLocalizedTime(TIME_FORMAT format, bool withSeconds /
+
+ CDateTime CDateTime::GetAsLocalDateTime() const
+ {
+- auto zone = date::make_zoned(date::current_zone(), m_time);
++ auto zone = date::make_zoned(KODI::TIME::GetTimeZone(), m_time);
+
+ return CDateTime(
+ date::floor<std::chrono::seconds>(zone.get_local_time().time_since_epoch()).count());
+@@ -1247,7 +1247,7 @@ std::string CDateTime::GetAsW3CDateTime(bool asUtc /* = false */) const
+ if (asUtc)
+ return date::format("%FT%TZ", time);
+
+- auto zt = date::make_zoned(date::current_zone(), time);
++ auto zt = date::make_zoned(KODI::TIME::GetTimeZone(), time);
+
+ return date::format("%FT%T%Ez", zt);
+ }
+diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp
+index de3f75e5e4..6604146a61 100644
+--- a/xbmc/test/TestDateTime.cpp
++++ b/xbmc/test/TestDateTime.cpp
+@@ -303,7 +303,7 @@ TEST_F(TestDateTime, GetAsStringsWithBias)
+ std::cout << dateTime.GetAsW3CDateTime(true) << std::endl;
+
+ auto tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
+- auto zone = date::make_zoned(date::current_zone(), tps);
++ auto zone = date::make_zoned(KODI::TIME::GetTimeZone(), tps);
+
+ EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 12:34:56 GMT");
+ EXPECT_EQ(dateTime.GetAsW3CDateTime(false), date::format("%FT%T%Ez", zone));
+@@ -552,12 +552,12 @@ TEST_F(TestDateTime, GetAsLocalDateTime)
+ CDateTime dateTime2;
+ dateTime2 = dateTime1.GetAsLocalDateTime();
+
+- auto zoned_time = date::make_zoned(date::current_zone(), dateTime1.GetAsTimePoint());
++ auto zoned_time = date::make_zoned(KODI::TIME::GetTimeZone(), dateTime1.GetAsTimePoint());
+ auto time = zoned_time.get_local_time().time_since_epoch();
+
+ CDateTime cmpTime(std::chrono::duration_cast<std::chrono::seconds>(time).count());
+
+- EXPECT_TRUE(dateTime1 == cmpTime);
++ EXPECT_TRUE(dateTime2 == cmpTime);
+ }
+
+ TEST_F(TestDateTime, Reset)
+@@ -736,3 +736,17 @@ TEST_F(TestDateTime, Tzdata)
+ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T02:34:56+14:00")
+ << "tzdata information not valid for 'Etc/GMT-14'";
+ }
++
++TEST_F(TestDateTime, ExtractTzName)
++{
++ CDateTime dateTime;
++ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56);
++
++ auto tps = date::floor<std::chrono::seconds>(dateTime.GetAsTimePoint());
++ auto zone = date::make_zoned(KODI::TIME::ExtractTzName("/usr/share/zoneinfo/Etc/GMT-14"), tps);
++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T02:34:56+14:00")
++ << "extractTzName failed for '/usr/share/zoneinfo/Etc/GMT-14'";
++
++ EXPECT_EQ(KODI::TIME::ExtractTzName("/usr/share/z0neinfo/Etc/GMT+12"), "")
++ << "extractTzName failed for '/usr/share/z0neinfo/Etc/GMT+12'";
++}
+diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt
+index bb4851c0ce..8f3a199a65 100644
+--- a/xbmc/utils/CMakeLists.txt
++++ b/xbmc/utils/CMakeLists.txt
+@@ -17,6 +17,7 @@ set(SOURCES ActorProtocol.cpp
+ Crc32.cpp
+ CSSUtils.cpp
+ DatabaseUtils.cpp
++ DateLib.cpp
+ Digest.cpp
+ DiscsUtils.cpp
+ EndianSwap.cpp
+diff --git a/xbmc/utils/DateLib.cpp b/xbmc/utils/DateLib.cpp
+new file mode 100644
+index 0000000000..bd86dd5791
+--- /dev/null
++++ b/xbmc/utils/DateLib.cpp
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (C) 2005-2022 Team Kodi
++ * This file is part of Kodi - https://kodi.tv
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ * See LICENSES/README.md for more information.
++ */
++
++#include "DateLib.h"
++
++#include "utils/XTimeUtils.h"
++
++#include <cstdlib>
++
++const std::string KODI::TIME::ExtractTzName(const char* tzname)
++{
++ if (tzname[0] != '/')
++ return tzname;
++
++ std::string result = tzname;
++ const char zoneinfo[] = "zoneinfo";
++ size_t pos = result.rfind(zoneinfo);
++ if (pos == result.npos)
++ {
++ return "";
++ }
++
++ pos = result.find('/', pos);
++ result.erase(0, pos + 1);
++ return result;
++}
++
++const date::time_zone* KODI::TIME::GetTimeZone()
++{
++#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
++ const date::time_zone* tz = nullptr;
++ const char* tzname = std::getenv("TZ");
++
++ if (tzname && tzname[0] != '\0')
++ {
++ const std::string tzn = ExtractTzName(tzname);
++ if (!tzn.empty())
++ tz = date::locate_zone(tzn);
++ }
++
++ if (tz)
++ return tz;
++ else
++#endif
++ return date::current_zone();
++}
+diff --git a/xbmc/utils/DateLib.h b/xbmc/utils/DateLib.h
+index e39650aa03..9838dd94fe 100644
+--- a/xbmc/utils/DateLib.h
++++ b/xbmc/utils/DateLib.h
+@@ -19,6 +19,19 @@
+ #endif
+
+ #define HAS_REMOTE_API 0
++#include <cstring>
++
+ #include <date/date.h>
+ #include <date/iso_week.h>
+ #include <date/tz.h>
++
++namespace KODI
++{
++namespace TIME
++{
++
++const std::string ExtractTzName(const char* tzname);
++const date::time_zone* GetTimeZone();
++
++} // namespace TIME
++} // namespace KODI
+--
+2.43.0
+
+
+From b67dabf8a304565713b413d4efb30c567a0a639b Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Mon, 9 Jan 2023 23:00:17 +0200
+Subject: [PATCH 18/19] Apply formatting fixes
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/XBDateTime.cpp | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp
+index 29118e7570..402aecfa9c 100644
+--- a/xbmc/XBDateTime.cpp
++++ b/xbmc/XBDateTime.cpp
+@@ -167,7 +167,7 @@ CDateTime::CDateTime()
+
+ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time)
+ {
+- m_state=time.m_state;
++ m_state = time.m_state;
+ }
+
+ CDateTime::CDateTime(const time_t& time)
+@@ -217,7 +217,7 @@ CDateTime CDateTime::GetUTCDateTime()
+ return CDateTime(std::chrono::system_clock::now());
+ }
+
+-const CDateTime& CDateTime::operator =(const time_t& right)
++const CDateTime& CDateTime::operator=(const time_t& right)
+ {
+ Reset();
+ m_time += std::chrono::duration<long double>(right);
+@@ -226,7 +226,7 @@ const CDateTime& CDateTime::operator =(const time_t& right)
+ return *this;
+ }
+
+-const CDateTime& CDateTime::operator =(const tm& right)
++const CDateTime& CDateTime::operator=(const tm& right)
+ {
+ Reset();
+
+@@ -282,68 +282,68 @@ bool CDateTime::operator !=(const CDateTime& right) const
+ return !operator ==(right);
+ }
+
+-bool CDateTime::operator >(const time_t& right) const
++bool CDateTime::operator>(const time_t& right) const
+ {
+ return m_time >
+ std::chrono::system_clock::from_time_t(0) + std::chrono::duration<long double>(right);
+ }
+
+-bool CDateTime::operator >=(const time_t& right) const
++bool CDateTime::operator>=(const time_t& right) const
+ {
+ return operator >(right) || operator ==(right);
+ }
+
+-bool CDateTime::operator <(const time_t& right) const
++bool CDateTime::operator<(const time_t& right) const
+ {
+ return m_time <
+ std::chrono::system_clock::from_time_t(0) + std::chrono::duration<long double>(right);
+ }
+
+-bool CDateTime::operator <=(const time_t& right) const
++bool CDateTime::operator<=(const time_t& right) const
+ {
+ return operator <(right) || operator ==(right);
+ }
+
+-bool CDateTime::operator ==(const time_t& right) const
++bool CDateTime::operator==(const time_t& right) const
+ {
+ return m_time ==
+ std::chrono::system_clock::from_time_t(0) + std::chrono::duration<long double>(right);
+ }
+
+-bool CDateTime::operator !=(const time_t& right) const
++bool CDateTime::operator!=(const time_t& right) const
+ {
+ return !operator ==(right);
+ }
+
+-bool CDateTime::operator >(const tm& right) const
++bool CDateTime::operator>(const tm& right) const
+ {
+ CDateTime temp(right);
+ return m_time > temp.m_time;
+ }
+
+-bool CDateTime::operator >=(const tm& right) const
++bool CDateTime::operator>=(const tm& right) const
+ {
+ return operator >(right) || operator ==(right);
+ }
+
+-bool CDateTime::operator <(const tm& right) const
++bool CDateTime::operator<(const tm& right) const
+ {
+ CDateTime temp(right);
+ return m_time < temp.m_time;
+ }
+
+-bool CDateTime::operator <=(const tm& right) const
++bool CDateTime::operator<=(const tm& right) const
+ {
+ return operator <(right) || operator ==(right);
+ }
+
+-bool CDateTime::operator ==(const tm& right) const
++bool CDateTime::operator==(const tm& right) const
+ {
+ CDateTime temp(right);
+ return m_time == temp.m_time;
+ }
+
+-bool CDateTime::operator !=(const tm& right) const
++bool CDateTime::operator!=(const tm& right) const
+ {
+ return !operator ==(right);
+ }
+--
+2.43.0
+
+
+From ae3e34c85b2c8fe476d22b7adbe9e2457a025edd Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Thu, 13 Apr 2023 20:18:28 +0300
+Subject: [PATCH 19/19] Move LoadTimeZoneDatabase to xbmc/utils/DateLib.cpp
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/addons/TimeZoneResource.cpp | 4 +---
+ xbmc/settings/SettingsComponent.cpp | 5 +++++
+ xbmc/utils/DateLib.cpp | 23 +++++++++++++++++++++++
+ xbmc/utils/DateLib.h | 1 +
+ 4 files changed, 30 insertions(+), 3 deletions(-)
+
+diff --git a/xbmc/addons/TimeZoneResource.cpp b/xbmc/addons/TimeZoneResource.cpp
+index 6dfba75e89..12c4371e19 100644
+--- a/xbmc/addons/TimeZoneResource.cpp
++++ b/xbmc/addons/TimeZoneResource.cpp
+@@ -32,9 +32,7 @@ bool CTimeZoneResource::IsInUse() const
+
+ void CTimeZoneResource::OnPostInstall(bool update, bool modal)
+ {
+-#if defined(DATE_INTERNAL_TZDATA)
+- date::reload_tzdb();
+-#endif
++ KODI::TIME::LoadTimeZoneDatabase();
+ }
+
+ } // namespace ADDON
+diff --git a/xbmc/settings/SettingsComponent.cpp b/xbmc/settings/SettingsComponent.cpp
+index 0f5f7ae8da..8ce2730d3b 100644
+--- a/xbmc/settings/SettingsComponent.cpp
++++ b/xbmc/settings/SettingsComponent.cpp
+@@ -24,6 +24,7 @@
+ #include "settings/AdvancedSettings.h"
+ #include "settings/Settings.h"
+ #include "settings/SubtitlesSettings.h"
++#include "utils/DateLib.h"
+ #include "utils/StringUtils.h"
+ #include "utils/URIUtils.h"
+ #include "utils/log.h"
+@@ -56,6 +57,10 @@ void CSettingsComponent::Initialize()
+ if (!inited)
+ inited = InitDirectoriesWin32(params->HasPlatformDirectories());
+
++ // Try loading timezone information after directories were provisioned
++ //!@todo check if the whole dirs logic should be moved to AppEnvironment
++ KODI::TIME::LoadTimeZoneDatabase();
++
+ m_settings->Initialize();
+
+ m_advancedSettings->Initialize(*m_settings->GetSettingsManager());
+diff --git a/xbmc/utils/DateLib.cpp b/xbmc/utils/DateLib.cpp
+index bd86dd5791..d8a6d67764 100644
+--- a/xbmc/utils/DateLib.cpp
++++ b/xbmc/utils/DateLib.cpp
+@@ -49,3 +49,26 @@ const date::time_zone* KODI::TIME::GetTimeZone()
+ #endif
+ return date::current_zone();
+ }
++
++void KODI::TIME::LoadTimeZoneDatabase()
++{
++#if defined(DATE_INTERNAL_TZDATA)
++ // First check the timezone resource from userprofile
++ auto tzdataPath =
++ CSpecialProtocol::TranslatePath("special://home/addons/resource.timezone/resources/tzdata");
++ if (!XFILE::CDirectory::Exists(tzdataPath))
++ {
++ // Then check system-wide Kodi profile and bail out if not found
++ tzdataPath =
++ CSpecialProtocol::TranslatePath("special://xbmc/addons/resource.timezone/resources/tzdata");
++ if (!XFILE::CDirectory::Exists(tzdataPath))
++ {
++ CLog::LogF(LOGFATAL, "failed to find resource.timezone");
++ return;
++ }
++ }
++
++ CLog::LogF(LOGDEBUG, "Loading tzdata from path: {}", tzdataPath);
++ date::set_install(tzdataPath);
++#endif
++}
+diff --git a/xbmc/utils/DateLib.h b/xbmc/utils/DateLib.h
+index 9838dd94fe..09c4ba0b17 100644
+--- a/xbmc/utils/DateLib.h
++++ b/xbmc/utils/DateLib.h
+@@ -32,6 +32,7 @@ namespace TIME
+
+ const std::string ExtractTzName(const char* tzname);
+ const date::time_zone* GetTimeZone();
++void LoadTimeZoneDatabase();
+
+ } // namespace TIME
+ } // namespace KODI
+--
+2.43.0
+
diff --git a/debian/patches/kodi/0001-Implement-hashes-using-Libgcrypt.patch b/debian/patches/kodi/0001-Implement-hashes-using-Libgcrypt.patch
new file mode 100644
index 0000000..10c46de
--- /dev/null
+++ b/debian/patches/kodi/0001-Implement-hashes-using-Libgcrypt.patch
@@ -0,0 +1,145 @@
+From 418ce26d409cadc089c5e2e624578432727d7de5 Mon Sep 17 00:00:00 2001
+From: Balint Reczey <balint.reczey@canonical.com>
+Date: Fri, 22 Feb 2019 23:03:21 +0100
+Subject: [PATCH] Implement hashes using Libgcrypt
+Forwarded: not-needed
+
+---
+ xbmc/utils/Digest.cpp | 36 ++++++++++++++++--------------------
+ xbmc/utils/Digest.h | 10 +++++-----
+ 2 files changed, 21 insertions(+), 25 deletions(-)
+
+diff --git a/xbmc/utils/Digest.cpp b/xbmc/utils/Digest.cpp
+index 445a755..63ce81e 100644
+--- a/xbmc/utils/Digest.cpp
++++ b/xbmc/utils/Digest.cpp
+@@ -13,7 +13,7 @@
+ #include <array>
+ #include <stdexcept>
+
+-#include <openssl/evp.h>
++#include <gcrypt.h>
+
+ namespace KODI
+ {
+@@ -23,18 +23,18 @@ namespace UTILITY
+ namespace
+ {
+
+-EVP_MD const * TypeToEVPMD(CDigest::Type type)
++int const TypeToInt(CDigest::Type type)
+ {
+ switch (type)
+ {
+ case CDigest::Type::MD5:
+- return EVP_md5();
++ return GCRY_MD_MD5;
+ case CDigest::Type::SHA1:
+- return EVP_sha1();
++ return GCRY_MD_SHA1;
+ case CDigest::Type::SHA256:
+- return EVP_sha256();
++ return GCRY_MD_SHA256;
+ case CDigest::Type::SHA512:
+- return EVP_sha512();
++ return GCRY_MD_SHA512;
+ default:
+ throw std::invalid_argument("Unknown digest type");
+ }
+@@ -92,18 +92,20 @@ CDigest::Type CDigest::TypeFromString(std::string const& type)
+ }
+ }
+
+-void CDigest::MdCtxDeleter::operator()(EVP_MD_CTX* context)
++void CDigest::MdCtxDeleter::operator()(gcry_md_hd_t context)
+ {
+- EVP_MD_CTX_destroy(context);
++ gcry_md_close(context);
+ }
+
+ CDigest::CDigest(Type type)
+-: m_context{EVP_MD_CTX_create()}, m_md(TypeToEVPMD(type))
++ : m_context(), m_md(TypeToInt(type))
+ {
+- if (1 != EVP_DigestInit_ex(m_context.get(), m_md, nullptr))
++ gcry_md_hd_t hd = NULL;
++ if (GPG_ERR_NO_ERROR != gcry_md_open(&hd, m_md, 0))
+ {
+- throw std::runtime_error("EVP_DigestInit_ex failed");
++ throw std::runtime_error("gcry_md_open failed");
+ }
++ m_context.reset(hd);
+ }
+
+ void CDigest::Update(std::string const& data)
+@@ -118,10 +120,7 @@ void CDigest::Update(void const* data, std::size_t size)
+ throw std::logic_error("Finalized digest cannot be updated any more");
+ }
+
+- if (1 != EVP_DigestUpdate(m_context.get(), data, size))
+- {
+- throw std::runtime_error("EVP_DigestUpdate failed");
+- }
++ gcry_md_write(m_context.get(), data, size);
+ }
+
+ std::string CDigest::FinalizeRaw()
+@@ -134,15 +133,12 @@ std::string CDigest::FinalizeRaw()
+ m_finalized = true;
+
+ std::array<unsigned char, 64> digest;
+- std::size_t size = EVP_MD_size(m_md);
++ std::size_t size = gcry_md_get_algo_dlen(m_md);
+ if (size > digest.size())
+ {
+ throw std::runtime_error("Digest unexpectedly long");
+ }
+- if (1 != EVP_DigestFinal_ex(m_context.get(), digest.data(), nullptr))
+- {
+- throw std::runtime_error("EVP_DigestFinal_ex failed");
+- }
++ memcpy(digest.data(), gcry_md_read(m_context.get(), m_md), size);
+ return {reinterpret_cast<char*> (digest.data()), size};
+ }
+
+diff --git a/xbmc/utils/Digest.h b/xbmc/utils/Digest.h
+index 6452857..6dfed47 100644
+--- a/xbmc/utils/Digest.h
++++ b/xbmc/utils/Digest.h
+@@ -15,7 +15,7 @@
+ #include <stdexcept>
+ #include <string>
+
+-#include <openssl/evp.h>
++#include <gcrypt.h>
+
+ namespace KODI
+ {
+@@ -23,7 +23,7 @@ namespace UTILITY
+ {
+
+ /**
+- * Utility class for calculating message digests/hashes, currently using OpenSSL
++ * Utility class for calculating message digests/hashes
+ */
+ class CDigest
+ {
+@@ -93,12 +93,12 @@ public:
+ private:
+ struct MdCtxDeleter
+ {
+- void operator()(EVP_MD_CTX* context);
++ void operator()(gcry_md_hd_t context);
+ };
+
+ bool m_finalized{false};
+- std::unique_ptr<EVP_MD_CTX, MdCtxDeleter> m_context;
+- EVP_MD const* m_md;
++ std::unique_ptr<struct gcry_md_handle, MdCtxDeleter> m_context;
++ int const m_md;
+ };
+
+ struct TypedDigest
+--
+2.20.1
+
diff --git a/debian/patches/kodi/0002-Find-and-link-with-Libgcrypt.patch b/debian/patches/kodi/0002-Find-and-link-with-Libgcrypt.patch
new file mode 100644
index 0000000..30be79a
--- /dev/null
+++ b/debian/patches/kodi/0002-Find-and-link-with-Libgcrypt.patch
@@ -0,0 +1,58 @@
+From 9f0f2742dee33c33303b6158751a0baab17543f2 Mon Sep 17 00:00:00 2001
+From: Balint Reczey <balint.reczey@canonical.com>
+Date: Sat, 23 Feb 2019 11:06:43 +0100
+Subject: [PATCH] Find and link with Libgcrypt
+Forwarded: not-needed
+
+---
+ CMakeLists.txt | 1 +
+ cmake/modules/FindLibgcrypt.cmake | 25 +++++++++++++++++++++++++
+ 2 files changed, 26 insertions(+)
+ create mode 100644 cmake/modules/FindLibgcrypt.cmake
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index f902211..f20909a 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -139,6 +139,7 @@
+ Iconv
+ KissFFT
+ LibDvd
++ Libgcrypt
+ Lzo2
+ OpenSSL>=1.1.0
+ PCRE
+diff --git a/cmake/modules/FindLibgcrypt.cmake b/cmake/modules/FindLibgcrypt.cmake
+new file mode 100644
+index 0000000..9de789a
+--- /dev/null
++++ b/cmake/modules/FindLibgcrypt.cmake
+@@ -0,0 +1,25 @@
++#.rst:
++# FindLibgcrypt
++# -------
++# Finds the Libgcrypt library
++#
++# This will define the following variables::
++#
++# LIBGCRYPT_FOUND - system has Libgcrypt
++# LIBGCRYPT_INCLUDE_DIRS - the Libgcrypt include directory
++# LIBGCRYPT_LIBRARIES - the Libgcrypt libraries
++# LIBGCRYPT_DEFINITIONS - the Libgcrypt compile definitions
++
++find_path(LIBGCRYPT_INCLUDE_DIR gcrypt.h)
++find_library(LIBGCRYPT_LIBRARY NAMES gcrypt)
++
++include(FindPackageHandleStandardArgs)
++find_package_handle_standard_args(Libgcrypt REQUIRED_VARS LIBGCRYPT_LIBRARY LIBGCRYPT_INCLUDE_DIR)
++
++if(LIBGCRYPT_FOUND)
++ set(LIBGCRYPT_LIBRARIES ${LIBGCRYPT_LIBRARY})
++ set(LIBGCRYPT_INCLUDE_DIRS ${LIBGCRYPT_INCLUDE_DIR})
++ set(LIBGCRYPT_DEFINITIONS -DHAVE_GCRYPT=1)
++endif()
++
++mark_as_advanced(LIBGCRYPT_LIBRARY LIBGCRYPT_INCLUDE_DIR)
+--
+2.20.1
+
diff --git a/debian/patches/kodi/0003-differentiate-from-vanilla-Kodi.patch b/debian/patches/kodi/0003-differentiate-from-vanilla-Kodi.patch
new file mode 100644
index 0000000..f909400
--- /dev/null
+++ b/debian/patches/kodi/0003-differentiate-from-vanilla-Kodi.patch
@@ -0,0 +1,90 @@
+From d7ce565e1de5515afab869f8d1de35840567af7a Mon Sep 17 00:00:00 2001
+From: Debian Multimedia Maintainers <debian-multimedia@lists.debian.org>
+Date: Wed, 26 Feb 2020 13:48:55 +0100
+Subject: [PATCH] differentiate-from-vanilla-Kodi
+Forwarded: not-needed
+
+---
+ addons/skin.estuary/xml/Home.xml | 8 ++++++++
+ xbmc/utils/SystemInfo.cpp | 12 ++++++++----
+ xbmc/utils/test/TestSystemInfo.cpp | 2 +-
+ 3 files changed, 17 insertions(+), 5 deletions(-)
+
+diff --git a/addons/skin.estuary/xml/Home.xml b/addons/skin.estuary/xml/Home.xml
+index b5e1290..f441b04 100644
+--- a/addons/skin.estuary/xml/Home.xml
++++ b/addons/skin.estuary/xml/Home.xml
+@@ -1046,6 +1046,14 @@
+ <param name="visible" value="Player.HasMedia" />
+ </include>
+ </control>
++ <control type="image">
++ <left>140</left>
++ <top>71</top>
++ <width>250</width>
++ <height>56</height>
++ <aspectratio>keep</aspectratio>
++ <texture>from-debian-logo.png</texture>
++ </control>
+ </control>
+ <include>BottomBar</include>
+ <control type="group">
+diff --git a/xbmc/utils/SystemInfo.cpp b/xbmc/utils/SystemInfo.cpp
+index f14d1e8a..c64821e9 100644
+--- a/xbmc/utils/SystemInfo.cpp
++++ b/xbmc/utils/SystemInfo.cpp
+@@ -75,6 +75,9 @@ using namespace winrt::Windows::System::Profile;
+
+ #include <system_error>
+
++#include <algorithm>
++#include <cctype>
++
+ /* Expand macro before stringify */
+ #define STR_MACRO(x) #x
+ #define XSTR_MACRO(x) STR_MACRO(x)
+@@ -422,7 +425,7 @@ bool CSysInfo::Save(TiXmlNode *settings) const
+ const std::string& CSysInfo::GetAppName(void)
+ {
+ assert(CCompileInfo::GetAppName() != NULL);
+- static const std::string appName(CCompileInfo::GetAppName());
++ static const std::string appName(StringUtils::Format("{} from Debian", CCompileInfo::GetAppName()));
+
+ return appName;
+ }
+@@ -1017,7 +1020,9 @@ std::string CSysInfo::GetUserAgent()
+ if (!result.empty())
+ return result;
+
+- result = GetAppName() + "/" + CSysInfo::GetVersionShort() + " (";
++ std::string appName = GetAppName();
++ appName.erase(std::remove_if(appName.begin(), appName.end(), ::isspace), appName.end());
++ result = appName + "/" + CSysInfo::GetVersionShort() + " (";
+ #if defined(TARGET_WINDOWS)
+ result += GetKernelName() + " " + GetKernelVersion();
+ #ifndef TARGET_WINDOWS_STORE
+@@ -1185,8 +1190,7 @@ std::string CSysInfo::GetVersionShort()
+
+ std::string CSysInfo::GetVersion()
+ {
+- return GetVersionShort() + " (" + CCompileInfo::GetVersionCode() + ")" +
+- " Git:" + CCompileInfo::GetSCMID();
++ return GetVersionShort() + StringUtils::Format(" Debian package version: {}", DEB_VERSION);
+ }
+
+ std::string CSysInfo::GetVersionCode()
+diff --git a/xbmc/utils/test/TestSystemInfo.cpp b/xbmc/utils/test/TestSystemInfo.cpp
+index 1f2b0a1..1145aa4 100644
+--- a/xbmc/utils/test/TestSystemInfo.cpp
++++ b/xbmc/utils/test/TestSystemInfo.cpp
+@@ -208,7 +208,7 @@ TEST_F(TestSystemInfo, GetXbmcBitness)
+
+ TEST_F(TestSystemInfo, GetUserAgent)
+ {
+- EXPECT_STREQ(g_sysinfo.GetAppName().c_str(), g_sysinfo.GetUserAgent().substr(0, g_sysinfo.GetAppName().size()).c_str()) << "'GetUserAgent()' string must start with app name'";
++ // EXPECT_STREQ(g_sysinfo.GetAppName().c_str(), g_sysinfo.GetUserAgent().substr(0, g_sysinfo.GetAppName().size()).c_str()) << "'GetUserAgent()' string must start with app name'";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find('(')) << "'GetUserAgent()' must contain brackets around second parameter";
+ EXPECT_NE(std::string::npos, g_sysinfo.GetUserAgent().find(')')) << "'GetUserAgent()' must contain brackets around second parameter";
+ EXPECT_EQ(g_sysinfo.GetUserAgent().find(' '), g_sysinfo.GetUserAgent().find(" (")) << "Second parameter in 'GetUserAgent()' string must be in brackets";
+--
+2.27.0.rc2
diff --git a/debian/patches/kodi/0004-use-system-groovy.patch b/debian/patches/kodi/0004-use-system-groovy.patch
new file mode 100644
index 0000000..e2df3c2
--- /dev/null
+++ b/debian/patches/kodi/0004-use-system-groovy.patch
@@ -0,0 +1,128 @@
+From 9c5f184cee41bb65c6dbe49b0c0b8cf049785d51 Mon Sep 17 00:00:00 2001
+From: Balint Reczey <balint@balintreczey.hu>
+Date: Tue, 3 Mar 2020 23:56:06 +0100
+Subject: [PATCH] Use system's groovy instead of the embedded one
+Forwarded: not-needed
+
+---
+ xbmc/interfaces/swig/CMakeLists.txt | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/xbmc/interfaces/swig/CMakeLists.txt b/xbmc/interfaces/swig/CMakeLists.txt
+index fc73821..0b5895b 100644
+--- a/xbmc/interfaces/swig/CMakeLists.txt
++++ b/xbmc/interfaces/swig/CMakeLists.txt
+@@ -1,8 +1,6 @@
+ function(generate_file file)
+- set(classpath ${GROOVY_DIR}/groovy-${GROOVY_VER}.jar
+- ${GROOVY_DIR}/groovy-xml-${GROOVY_VER}.jar
+- ${GROOVY_DIR}/groovy-templates-${GROOVY_VER}.jar
+- ${GROOVY_DIR}/commons-lang-${COMMONS_VER}.jar
++ set(classpath /usr/share/java/groovy.jar
++ /usr/share/java/commons-lang-2.6.jar
+ ${CMAKE_SOURCE_DIR}/tools/codegenerator
+ ${CMAKE_CURRENT_SOURCE_DIR}/../python)
+ if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
+@@ -24,8 +24,8 @@
+ add_custom_command(OUTPUT ${CPP_FILE}
+ COMMAND ${SWIG_EXECUTABLE}
+ ARGS -w401 -c++ -o ${file}.xml -xml -I${CMAKE_SOURCE_DIR}/xbmc -xmllang python ${CMAKE_CURRENT_SOURCE_DIR}/../swig/${file}
+- COMMAND ${Java_JAVA_EXECUTABLE}
+- ARGS ${JAVA_OPEN_OPTS} -cp "${classpath}" groovy.ui.GroovyMain ${CMAKE_SOURCE_DIR}/tools/codegenerator/Generator.groovy ${file}.xml ${CMAKE_CURRENT_SOURCE_DIR}/../python/PythonSwig.cpp.template ${file}.cpp > ${devnull}
++ COMMAND groovy
++ ARGS -cp "${classpath}" ${CMAKE_SOURCE_DIR}/tools/codegenerator/Generator.groovy ${file}.xml ${CMAKE_CURRENT_SOURCE_DIR}/../python/PythonSwig.cpp.template ${file}.cpp > ${devnull}
+ ${CLANG_FORMAT_COMMAND}
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../swig/${file} ${CMAKE_CURRENT_SOURCE_DIR}/../python/PythonSwig.cpp.template)
+ set(SOURCES ${SOURCES} "${CPP_FILE}" PARENT_SCOPE)
+--
+2.20.1
+
+
+From 8178f46f2f692bd57d5fe851e16b1b377bb4867b Mon Sep 17 00:00:00 2001
+From: Jose Luis Marti <joseluis.marti@gmail.com>
+Date: Sun, 23 Oct 2022 23:54:58 +0200
+Subject: [PATCH 2/2] add imports needed for the bump
+
+---
+ tools/codegenerator/Generator.groovy | 1 -
+ tools/codegenerator/Helper.groovy | 1 -
+ tools/codegenerator/SwigTypeParser.groovy | 2 --
+ 3 files changed, 4 deletions(-)
+
+diff --git b/tools/codegenerator/Generator.groovy a/tools/codegenerator/Generator.groovy
+index 79cfe4f5fb..53cf7864f9 100644
+--- b/tools/codegenerator/Generator.groovy
++++ a/tools/codegenerator/Generator.groovy
+@@ -19,7 +19,6 @@
+ */
+
+ import groovy.text.SimpleTemplateEngine
+-import groovy.xml.XmlParser
+ import groovy.xml.XmlUtil
+
+ import Helper
+diff --git b/tools/codegenerator/Helper.groovy a/tools/codegenerator/Helper.groovy
+index 4308ffd3f3..6324e537e1 100644
+--- b/tools/codegenerator/Helper.groovy
++++ a/tools/codegenerator/Helper.groovy
+@@ -18,7 +18,6 @@
+ *
+ */
+
+-import groovy.xml.XmlParser
+ import groovy.xml.XmlUtil
+ import org.apache.commons.lang.StringEscapeUtils
+
+diff --git b/tools/codegenerator/SwigTypeParser.groovy a/tools/codegenerator/SwigTypeParser.groovy
+index 24dfa8f8ab..ab4e5292f9 100644
+--- b/tools/codegenerator/SwigTypeParser.groovy
++++ a/tools/codegenerator/SwigTypeParser.groovy
+@@ -18,8 +18,6 @@
+ *
+ */
+
+-import groovy.xml.XmlParser
+-
+ /**
+ * These methods are somewhat ugly because they have been copied out of
+ * the Swig source code and simply made compilable with groovy. They could
+--
+2.35.1
+
+
+From b99c39bee1c12d4471959c4854927a60f5dc4393 Mon Sep 17 00:00:00 2001
+From: Jose Luis Marti <joseluis.marti@gmail.com>
+Date: Sun, 23 Oct 2022 23:46:38 +0200
+Subject: [PATCH 1/2] delete imports that were unnecessary
+
+---
+ tools/codegenerator/Generator.groovy | 1 +
+ tools/codegenerator/Helper.groovy | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git b/tools/codegenerator/Generator.groovy a/tools/codegenerator/Generator.groovy
+index 53cf7864f9..305fb0a94b 100644
+--- b/tools/codegenerator/Generator.groovy
++++ a/tools/codegenerator/Generator.groovy
+@@ -18,6 +18,7 @@
+ *
+ */
+
++import groovy.util.Node
+ import groovy.text.SimpleTemplateEngine
+ import groovy.xml.XmlUtil
+
+diff --git b/tools/codegenerator/Helper.groovy a/tools/codegenerator/Helper.groovy
+index 6324e537e1..764e6c83a9 100644
+--- b/tools/codegenerator/Helper.groovy
++++ a/tools/codegenerator/Helper.groovy
+@@ -21,6 +21,7 @@
+ import groovy.xml.XmlUtil
+ import org.apache.commons.lang.StringEscapeUtils
+
++import groovy.text.SimpleTemplateEngine
+ import groovy.text.SimpleTemplateEngine
+ import java.util.regex.Pattern
+
+--
+2.35.1
diff --git a/debian/patches/kodi/0005-fix-tests.patch b/debian/patches/kodi/0005-fix-tests.patch
new file mode 100644
index 0000000..4099ced
--- /dev/null
+++ b/debian/patches/kodi/0005-fix-tests.patch
@@ -0,0 +1,28 @@
+From 239e1d72ebe9c2a8c3cd890b443317877f04d42b Mon Sep 17 00:00:00 2001
+From: Debian Multimedia Maintainers <debian-multimedia@lists.debian.org>
+Date: Tue, 3 Mar 2020 23:56:07 +0100
+Subject: [PATCH] fix-tests
+Forwarded: not-needed
+
+---
+ xbmc/utils/test/TestSystemInfo.cpp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/utils/test/TestSystemInfo.cpp b/xbmc/utils/test/TestSystemInfo.cpp
+index 1145aa4..7b5f00d 100644
+--- a/xbmc/utils/test/TestSystemInfo.cpp
++++ b/xbmc/utils/test/TestSystemInfo.cpp
+@@ -73,8 +73,8 @@ TEST_F(TestSystemInfo, GetKernelName)
+ TEST_F(TestSystemInfo, GetKernelVersionFull)
+ {
+ EXPECT_FALSE(g_sysinfo.GetKernelVersionFull().empty()) << "'GetKernelVersionFull()' must not return empty string";
+- EXPECT_STRNE("0.0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0.0'";
+- EXPECT_STRNE("0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0'";
++ // EXPECT_STRNE("0.0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0.0'";
++ // EXPECT_STRNE("0.0", g_sysinfo.GetKernelVersionFull().c_str()) << "'GetKernelVersionFull()' must not return '0.0'";
+ EXPECT_EQ(0U, g_sysinfo.GetKernelVersionFull().find_first_of("0123456789")) << "'GetKernelVersionFull()' must not return version not starting from digit";
+ }
+
+--
+2.20.1
+
diff --git a/debian/patches/kodi/0006-dont-use-openssl.patch b/debian/patches/kodi/0006-dont-use-openssl.patch
new file mode 100644
index 0000000..012f187
--- /dev/null
+++ b/debian/patches/kodi/0006-dont-use-openssl.patch
@@ -0,0 +1,29 @@
+From 7eda44dad7fc5964d934c73612bb3c2a7001c26f Mon Sep 17 00:00:00 2001
+From: Debian Multimedia Maintainers <debian-multimedia@lists.debian.org>
+Date: Tue, 3 Mar 2020 23:56:07 +0100
+Subject: [PATCH] dont-use-openssl
+Forwarded: not-needed
+
+---
+ CMakeLists.txt | 2 +-
+ xbmc/utils/CryptThreading.cpp | 2 +-
+ xbmc/utils/CryptThreading.h | 4 +---
+ xbmc/utils/test/TestCryptThreading.cpp | 2 +-
+ 4 files changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index f20909a..400707d 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -141,7 +141,7 @@
+ LibDvd
+ Libgcrypt
+ Lzo2
+- OpenSSL>=1.1.0
++# OpenSSL>=1.1.0
+ PCRE
+ RapidJSON
+ Spdlog
+--
+2.20.1
+
diff --git a/debian/patches/kodi/0007-support-omitting-addons-service.patch b/debian/patches/kodi/0007-support-omitting-addons-service.patch
new file mode 100644
index 0000000..c63e58f
--- /dev/null
+++ b/debian/patches/kodi/0007-support-omitting-addons-service.patch
@@ -0,0 +1,27 @@
+From: Jonas Smedegaard <dr@jones.dk>
+Date: Tue, 3 Mar 2020 23:56:07 +0100
+Subject: Support omitting addons repository feed
+Forwarded: not-needed
+Last-Update: 2017-10-03
+
+Upstream official addon repository feed contain non-free addons.
+
+Extending the system at runtime is arguably an anti-feature -
+either for political reasons or due to security risks.
+
+This patch makes it possible to omit the addons repository feed.
+---
+ system/addon-manifest.xml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/system/addon-manifest.xml
++++ b/system/addon-manifest.xml
+@@ -37,7 +37,7 @@
+ <addon>metadata.local</addon>
+ <addon>metadata.themoviedb.org.python</addon>
+ <addon>metadata.tvshows.themoviedb.org.python</addon>
+- <addon>repository.xbmc.org</addon>
++ <addon optional="true">repository.xbmc.org</addon>
+ <addon>resource.images.weathericons.default</addon>
+ <addon>resource.language.en_gb</addon>
+ <addon>resource.uisounds.kodi</addon>
diff --git a/debian/patches/kodi/0008-Find-test-fixtures-in-source-directory.patch b/debian/patches/kodi/0008-Find-test-fixtures-in-source-directory.patch
new file mode 100644
index 0000000..9c22ab0
--- /dev/null
+++ b/debian/patches/kodi/0008-Find-test-fixtures-in-source-directory.patch
@@ -0,0 +1,22 @@
+From: Balint Reczey <balint.reczey@canonical.com>
+Date: Thu, 5 Mar 2020 16:14:33 +0100
+Subject: Find test fixtures in source directory
+Forwarded: not-needed
+
+---
+ xbmc/test/TestUtils.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/xbmc/test/TestUtils.h b/xbmc/test/TestUtils.h
+index b0dbcf6..0e46579 100644
+--- a/xbmc/test/TestUtils.h
++++ b/xbmc/test/TestUtils.h
+@@ -94,7 +94,7 @@ private:
+ double probability;
+ };
+
+-#define XBMC_REF_FILE_PATH(s) CXBMCTestUtils::Instance().ReferenceFilePath(s)
++#define XBMC_REF_FILE_PATH(s) std::string("../" s)
+ #define XBMC_CREATETEMPFILE(a) CXBMCTestUtils::Instance().CreateTempFile(a)
+ #define XBMC_DELETETEMPFILE(a) CXBMCTestUtils::Instance().DeleteTempFile(a)
+ #define XBMC_TEMPFILEPATH(a) CXBMCTestUtils::Instance().TempFilePath(a)
diff --git a/debian/patches/kodi/0009-Skip-long-time-broken-test.patch b/debian/patches/kodi/0009-Skip-long-time-broken-test.patch
new file mode 100644
index 0000000..eadf33f
--- /dev/null
+++ b/debian/patches/kodi/0009-Skip-long-time-broken-test.patch
@@ -0,0 +1,23 @@
+From: Balint Reczey <balint.reczey@canonical.com>
+Date: Thu, 5 Mar 2020 19:27:51 +0100
+Subject: Skip long time broken test
+Forwarded: not-needed
+
+---
+ xbmc/network/test/TestWebServer.cpp | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/network/test/TestWebServer.cpp b/xbmc/network/test/TestWebServer.cpp
+index a87d9f4..3a4e9e2 100644
+--- a/xbmc/network/test/TestWebServer.cpp
++++ b/xbmc/network/test/TestWebServer.cpp
+@@ -155,7 +155,8 @@
+ // Must be only one "Content-Length" header
+ ASSERT_EQ(1U, httpHeader.GetValues(MHD_HTTP_HEADER_CONTENT_LENGTH).size());
+ // Content-Length must be "4"
+- EXPECT_STREQ("4", httpHeader.GetValue(MHD_HTTP_HEADER_CONTENT_LENGTH).c_str());
++ // FIXME it is "0" actually
++ // EXPECT_STREQ("4", httpHeader.GetValue(MHD_HTTP_HEADER_CONTENT_LENGTH).c_str());
+ // Accept-Ranges must be "bytes"
+ EXPECT_STREQ("bytes", httpHeader.GetValue(MHD_HTTP_HEADER_ACCEPT_RANGES).c_str());
+
diff --git a/debian/patches/kodi/0010-Disable-flaky-TestMassEvent.General-and-TestMassEven.patch b/debian/patches/kodi/0010-Disable-flaky-TestMassEvent.General-and-TestMassEven.patch
new file mode 100644
index 0000000..7572210
--- /dev/null
+++ b/debian/patches/kodi/0010-Disable-flaky-TestMassEvent.General-and-TestMassEven.patch
@@ -0,0 +1,31 @@
+From: Balint Reczey <balint.reczey@canonical.com>
+Date: Sat, 7 Mar 2020 20:31:55 +0100
+Subject: Disable flaky TestMassEvent.General and TestMassEvent.Polling tests
+Forwarded: not-needed
+
+---
+ xbmc/threads/test/TestEvent.cpp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/threads/test/TestEvent.cpp b/xbmc/threads/test/TestEvent.cpp
+index 1e3bb93..3293ca8 100644
+--- a/xbmc/threads/test/TestEvent.cpp
++++ b/xbmc/threads/test/TestEvent.cpp
+@@ -593,7 +593,7 @@ template <class W> void RunMassEventTest(std::vector<std::shared_ptr<W>>& m, boo
+ }
+
+
+-TEST(TestMassEvent, General)
++TEST(DISABLED_TestMassEvent, General)
+ {
+ g_event = new CEvent();
+
+@@ -605,7 +605,7 @@ TEST(TestMassEvent, General)
+ delete g_event;
+ }
+
+-TEST(TestMassEvent, Polling)
++TEST(DISABLED_TestMassEvent, Polling)
+ {
+ g_event = new CEvent(true); // polling needs to avoid the auto-reset
+
diff --git a/debian/patches/kodi/0011-Skip-checking-errno-against-ENOENT-because-this-test.patch b/debian/patches/kodi/0011-Skip-checking-errno-against-ENOENT-because-this-test.patch
new file mode 100644
index 0000000..8f6d155
--- /dev/null
+++ b/debian/patches/kodi/0011-Skip-checking-errno-against-ENOENT-because-this-test.patch
@@ -0,0 +1,22 @@
+From: Balint Reczey <balint.reczey@canonical.com>
+Date: Tue, 17 Mar 2020 08:19:53 +0100
+Subject: Skip checking errno against ENOENT because this test fails on armhf
+Forwarded: not-needed
+
+---
+ xbmc/filesystem/test/TestFile.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/xbmc/filesystem/test/TestFile.cpp b/xbmc/filesystem/test/TestFile.cpp
+index 898bdf9..d5f7a17 100644
+--- a/xbmc/filesystem/test/TestFile.cpp
++++ b/xbmc/filesystem/test/TestFile.cpp
+@@ -133,7 +133,7 @@ TEST(TestFile, Stat)
+ file->Close();
+ EXPECT_NE(0U, buffer.st_mode | _S_IFREG);
+ EXPECT_EQ(-1, XFILE::CFile::Stat("", &buffer));
+- EXPECT_EQ(ENOENT, errno);
++ // EXPECT_EQ(ENOENT, errno);
+ EXPECT_TRUE(XBMC_DELETETEMPFILE(file));
+ }
+
diff --git a/debian/patches/kodi/0012-The-baseline-of-the-i386-port-does-not-include-SSE.patch b/debian/patches/kodi/0012-The-baseline-of-the-i386-port-does-not-include-SSE.patch
new file mode 100644
index 0000000..fc0d6f9
--- /dev/null
+++ b/debian/patches/kodi/0012-The-baseline-of-the-i386-port-does-not-include-SSE.patch
@@ -0,0 +1,96 @@
+From 93dd546db947f872f337d25038de60e6bc939440 Mon Sep 17 00:00:00 2001
+From: Adrian Bunk <bunk@debian.org>
+Date: Sat, 30 May 2020 21:50:37 +0200
+Subject: [PATCH] The baseline of the i386 port does not include SSE
+
+SSE2 is always enabled on amd64.
+---
+ cmake/scripts/linux/ArchSetup.cmake | 2 +-
+ xbmc/cores/AudioEngine/CMakeLists.txt | 16 ++++++++--------
+ xbmc/rendering/CMakeLists.txt | 16 ++++++++--------
+ xbmc/utils/CMakeLists.txt | 10 +++++-----
+ 4 files changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/cmake/scripts/linux/ArchSetup.cmake b/cmake/scripts/linux/ArchSetup.cmake
+index d3ad51a7..c02ac4a1 100644
+--- a/cmake/scripts/linux/ArchSetup.cmake
++++ b/cmake/scripts/linux/ArchSetup.cmake
+@@ -18,7 +18,7 @@ else()
+ elseif(CPU MATCHES "i.86")
+ set(ARCH i486-linux)
+ set(NEON False)
+- add_options(CXX ALL_BUILDS "-msse")
++ #add_options(CXX ALL_BUILDS "-msse")
+ elseif(CPU STREQUAL arm1176jzf-s)
+ set(ARCH arm)
+ set(NEON False)
+diff --git a/xbmc/cores/AudioEngine/CMakeLists.txt b/xbmc/cores/AudioEngine/CMakeLists.txt
+index c5d4d978..f5ad9a47 100644
+--- a/xbmc/cores/AudioEngine/CMakeLists.txt
++++ b/xbmc/cores/AudioEngine/CMakeLists.txt
+@@ -156,11 +156,11 @@ endif()
+
+ core_add_library(audioengine)
+ target_include_directories(${CORE_LIBRARY} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+-if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
+- if(HAVE_SSE)
+- target_compile_options(${CORE_LIBRARY} PRIVATE -msse)
+- endif()
+- if(HAVE_SSE2)
+- target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
+- endif()
+-endif()
++#if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
++# if(HAVE_SSE)
++# target_compile_options(${CORE_LIBRARY} PRIVATE -msse)
++# endif()
++# if(HAVE_SSE2)
++# target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
++# endif()
++#endif()
+diff --git a/xbmc/rendering/CMakeLists.txt b/xbmc/rendering/CMakeLists.txt
+index c212a962..aa999703 100644
+--- a/xbmc/rendering/CMakeLists.txt
++++ b/xbmc/rendering/CMakeLists.txt
+@@ -16,12 +16,12 @@ if(OPENGL_FOUND OR OPENGLES_FOUND)
+ endif()
+
+ core_add_library(rendering)
+-if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
+- if(HAVE_SSE)
+- target_compile_options(${CORE_LIBRARY} PRIVATE -msse)
+- endif()
+- if(HAVE_SSE2)
+- target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
+- endif()
+-endif()
++#if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
++# if(HAVE_SSE)
++# target_compile_options(${CORE_LIBRARY} PRIVATE -msse)
++# endif()
++# if(HAVE_SSE2)
++# target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
++# endif()
++#endif()
+
+diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt
+index 904b5cc9..48406633 100644
+--- a/xbmc/utils/CMakeLists.txt
++++ b/xbmc/utils/CMakeLists.txt
+@@ -232,8 +232,8 @@ endif()
+
+ core_add_library(utils)
+
+-if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
+- if(HAVE_SSE2)
+- target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
+- endif()
+-endif()
++#if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore)
++# if(HAVE_SSE2)
++# target_compile_options(${CORE_LIBRARY} PRIVATE -msse2)
++# endif()
++#endif()
+--
+2.35.1
+
diff --git a/debian/patches/kodi/0013-Disable-GetCPUFrequency-test.patch b/debian/patches/kodi/0013-Disable-GetCPUFrequency-test.patch
new file mode 100644
index 0000000..fe06a92
--- /dev/null
+++ b/debian/patches/kodi/0013-Disable-GetCPUFrequency-test.patch
@@ -0,0 +1,22 @@
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Mon, 02 Nov 2020 08:05:00 +0200
+Subject: Disable TestCPUInfo.GetCPUFrequency test
+Origin: vendor
+Forwarded: not-needed
+
+Fixes test failure on many architectures, like s390x.
+See #970236 for example.
+
+---
+
+--- a/xbmc/utils/test/TestCPUInfo.cpp
++++ b/xbmc/utils/test/TestCPUInfo.cpp
+@@ -36,7 +36,7 @@
+ EXPECT_GT(CServiceBroker::GetCPUInfo()->GetCPUCount(), 0);
+ }
+
+-TEST_F(TestCPUInfo, GetCPUFrequency)
++TEST_F(TestCPUInfo, DISABLED_GetCPUFrequency)
+ {
+ EXPECT_GE(CServiceBroker::GetCPUInfo()->GetCPUFrequency(), 0.f);
+ }
diff --git a/debian/patches/kodi/0014-Fix-C++-example-includes.patch b/debian/patches/kodi/0014-Fix-C++-example-includes.patch
new file mode 100644
index 0000000..a37339c
--- /dev/null
+++ b/debian/patches/kodi/0014-Fix-C++-example-includes.patch
@@ -0,0 +1,52 @@
+From: Vasyl Gello <vaek.gello@gmail.com>
+Date: Mon, 16 Nov 2020 20:20:00 +0200
+Subject: Fix C++ example includes
+Forwarded: not-needed
+
+---
+
+--- a/tools/EventClients/examples/c++/example_button1.cpp
++++ b/tools/EventClients/examples/c++/example_button1.cpp
+@@ -1,4 +1,5 @@
+-#include "../../lib/c++/xbmcclient.h"
++#include <kodi/xbmcclient.h>
++
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/socket.h>
+--- a/tools/EventClients/examples/c++/example_button2.cpp
++++ b/tools/EventClients/examples/c++/example_button2.cpp
+@@ -1,4 +1,5 @@
+-#include "../../lib/c++/xbmcclient.h"
++#include <kodi/xbmcclient.h>
++
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/socket.h>
+--- a/tools/EventClients/examples/c++/example_log.cpp
++++ b/tools/EventClients/examples/c++/example_log.cpp
+@@ -1,4 +1,5 @@
+-#include "../../lib/c++/xbmcclient.h"
++#include <kodi/xbmcclient.h>
++
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/socket.h>
+--- a/tools/EventClients/examples/c++/example_mouse.cpp
++++ b/tools/EventClients/examples/c++/example_mouse.cpp
+@@ -1,4 +1,5 @@
+-#include "../../lib/c++/xbmcclient.h"
++#include <kodi/xbmcclient.h>
++
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/socket.h>
+--- a/tools/EventClients/examples/c++/example_notification.cpp
++++ b/tools/EventClients/examples/c++/example_notification.cpp
+@@ -1,4 +1,5 @@
+-#include "../../lib/c++/xbmcclient.h"
++#include <kodi/xbmcclient.h>
++
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/socket.h>
diff --git a/debian/patches/kodi/0015-debian-cross-compile.patch b/debian/patches/kodi/0015-debian-cross-compile.patch
new file mode 100644
index 0000000..6d315bc
--- /dev/null
+++ b/debian/patches/kodi/0015-debian-cross-compile.patch
@@ -0,0 +1,33 @@
+Author: Vasyl Gello <vasek.gello@gmail.com>
+Last-Updated: 2021-10-30
+Forwarded: not-needed
+Subject: Patch native dependencies for cross-compiling
+
+ * Don't build TexturePacker statically - it breaks fakeroot
+
+---
+
+diff --git a/tools/depends/native/TexturePacker/Makefile b/tools/depends/native/TexturePacker/Makefile
+index f50dc879..9ba9da48 100644
+--- a/tools/depends/native/TexturePacker/Makefile
++++ b/tools/depends/native/TexturePacker/Makefile
+@@ -10,15 +10,15 @@ endif
+
+ ifeq ($(NATIVEPLATFORM),)
+ PLATFORM = native
+- EXTRA_CONFIGURE = --enable-static
++# EXTRA_CONFIGURE = --enable-static
+ else
+ PLATFORM = $(NATIVEPLATFORM)
+ DEPS += ../../Makefile.include
+ endif
+
+-ifeq ($(NATIVE_OS), linux)
+- EXTRA_CONFIGURE = --enable-static
+-endif
++#ifeq ($(NATIVE_OS), linux)
++# EXTRA_CONFIGURE = --enable-static
++#endif
+ ifeq ($(NATIVE_OS), android)
+ EXTRA_CONFIGURE = --enable-static
+ endif
diff --git a/debian/patches/kodi/0016-ports-architectures.patch b/debian/patches/kodi/0016-ports-architectures.patch
new file mode 100644
index 0000000..6595d31
--- /dev/null
+++ b/debian/patches/kodi/0016-ports-architectures.patch
@@ -0,0 +1,132 @@
+From 5627432b717cf7df565c30490400ef11efa016e0 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Fri, 30 Dec 2022 14:39:36 +0200
+Subject: [PATCH] Introduce buildable Debian ports architectures
+
+Tested by building against sid/ppc64 and running via
+xvfb + x11vnc.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ cmake/scripts/linux/ArchSetup.cmake | 18 ++++++++++++++++++
+ xbmc/cores/DllLoader/DllLoader.h | 1 +
+ xbmc/cores/DllLoader/ldt_keeper.c | 1 +
+ xbmc/utils/MathUtils.h | 1 +
+ xbmc/utils/SystemInfo.cpp | 19 ++++++++++++++++++-
+ 5 files changed, 39 insertions(+), 1 deletion(-)
+
+diff --git a/cmake/scripts/linux/ArchSetup.cmake b/cmake/scripts/linux/ArchSetup.cmake
+index 4083483173..917d8f06a4 100644
+--- a/cmake/scripts/linux/ArchSetup.cmake
++++ b/cmake/scripts/linux/ArchSetup.cmake
+@@ -40,9 +40,27 @@ else()
+ elseif(CPU MATCHES riscv64)
+ set(ARCH riscv64)
+ set(NEON False)
++ elseif(CPU MATCHES ppc)
++ set(ARCH ppc)
++ set(NEON False)
++ elseif(CPU MATCHES ppc64)
++ set(ARCH ppc64)
++ set(NEON False)
+ elseif(CPU MATCHES ppc64le)
+ set(ARCH ppc64le)
+ set(NEON False)
++ elseif(CPU MATCHES m68k)
++ set(ARCH m68k)
++ set(NEON False)
++ elseif(CPU MATCHES sh4)
++ set(ARCH sh4)
++ set(NEON False)
++ elseif(CPU MATCHES sparc64)
++ set(ARCH sparc64)
++ set(NEON False)
++ elseif(CPU MATCHES alpha)
++ set(ARCH alpha)
++ set(NEON False)
+ elseif(CPU MATCHES loongarch64)
+ set(ARCH loongarch64)
+ set(NEON False)
+diff --git a/xbmc/cores/DllLoader/DllLoader.h b/xbmc/cores/DllLoader/DllLoader.h
+index 79e019d0e3..216c2dd067 100644
+--- a/xbmc/cores/DllLoader/DllLoader.h
++++ b/xbmc/cores/DllLoader/DllLoader.h
+@@ -18,6 +18,7 @@
+ !defined(__arc__) && \
+ !defined(__arm__) && \
+ !defined(__loongarch__) && \
++ !defined(__m68k__) && \
+ !defined(__mips__) && \
+ !defined(__powerpc__) && \
+ !defined(__or1k__) && \
+diff --git a/xbmc/cores/DllLoader/ldt_keeper.c b/xbmc/cores/DllLoader/ldt_keeper.c
+index 325d50cb70..01e2e9ad61 100644
+--- a/xbmc/cores/DllLoader/ldt_keeper.c
++++ b/xbmc/cores/DllLoader/ldt_keeper.c
+@@ -24,6 +24,7 @@
+ !defined(__arc__) &&\
+ !defined(__arm__) && \
+ !defined(__loongarch__) && \
++ !defined(__m68k__) && \
+ !defined(__mips__) && \
+ !defined(__or1k__) && \
+ !defined(__powerpc__) && \
+diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h
+index 2b1dbcc51f..62301deef9 100644
+--- a/xbmc/utils/MathUtils.h
++++ b/xbmc/utils/MathUtils.h
+@@ -29,6 +29,7 @@
+ defined(__arm__) || \
+ defined(__loongarch__) || \
+ defined(_M_ARM) || \
++ defined(__m68k__) || \
+ defined(__mips__) || \
+ defined(__or1k__) || \
+ defined(__powerpc__) || \
+diff --git a/xbmc/utils/SystemInfo.cpp b/xbmc/utils/SystemInfo.cpp
+index 6e4c1ff2a9..e3da06d70e 100644
+--- a/xbmc/utils/SystemInfo.cpp
++++ b/xbmc/utils/SystemInfo.cpp
+@@ -933,7 +933,8 @@ int CSysInfo::GetKernelBitness(void)
+ std::string machine(un.machine);
+ if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" ||
+ machine == "ppc64" || machine == "ppc64el" || machine == "ppc64le" || machine == "ia64" ||
+- machine == "mips64" || machine == "s390x" || machine == "riscv64")
++ machine == "mips64" || machine == "s390x" || machine == "riscv64" ||
++ machine == "sparc64" || machine == "alpha")
+ kernelBitness = 64;
+ else
+ kernelBitness = 32;
+@@ -990,6 +991,14 @@ const std::string& CSysInfo::GetKernelCpuFamily(void)
+ kernelCpuFamily = "PowerPC";
+ else if (machine.compare(0, 5, "riscv", 5) == 0)
+ kernelCpuFamily = "RISC-V";
++ else if (machine.compare(0, 4, "m68k", 4) == 0)
++ kernelCpuFamily = "m68k";
++ else if (machine.compare(0, 3, "sh4", 3) == 0)
++ kernelCpuFamily = "sh4";
++ else if (machine.compare(0, 7, "sparc64", 7) == 0)
++ kernelCpuFamily = "SPARC";
++ else if (machine.compare(0, 5, "alpha", 5) == 0)
++ kernelCpuFamily = "alpha";
+ }
+ #endif
+ if (kernelCpuFamily.empty())
+@@ -1373,6 +1382,14 @@ std::string CSysInfo::GetBuildTargetCpuFamily(void)
+ return "PowerPC";
+ #elif defined(__riscv)
+ return "RISC-V";
++#elif defined(__m68k__)
++ return "m68k";
++#elif defined(__SH4__)
++ return "sh4";
++#elif defined(__sparc__)
++ return "SPARC";
++#elif defined(__alpha__)
++ return "alpha";
+ #else
+ return "unknown CPU family";
+ #endif
+--
+2.39.0
+
diff --git a/debian/patches/libdvdnav/0001-libdvdnav-PR48-enen92.patch b/debian/patches/libdvdnav/0001-libdvdnav-PR48-enen92.patch
new file mode 100644
index 0000000..17ae1fa
--- /dev/null
+++ b/debian/patches/libdvdnav/0001-libdvdnav-PR48-enen92.patch
@@ -0,0 +1,184 @@
+From 3984a232ae432fc9aca028a980139d12b087b32e Mon Sep 17 00:00:00 2001
+From: Miguel Borges de Freitas <enen92@kodi.tv>
+Date: Tue, 26 Jul 2022 18:20:50 +0100
+Subject: [PATCH] dvdnav_open_files implementation
+
+Attempts to open files given that the calling application provides the dvd_reader_filesystem implementation
+and the path to the file. Supports logging callbacks similarly to other dvdnav_open methods.
+Useful for opening files located on a virtual file system (vfs) such as smb, nfs, etc
+---
+ libdvdnav-embedded/src/dvdnav.c | 21 ++++++++++++++-------
+ libdvdnav-embedded/src/dvdnav/dvdnav.h | 12 ++++++++++++
+ libdvdnav-embedded/src/vm/vm.c | 9 ++++++---
+ libdvdnav-embedded/src/vm/vm.h | 3 ++-
+ 4 files changed, 34 insertions(+), 11 deletions(-)
+
+diff --git a/libdvdnav-embedded/src/dvdnav.c b/libdvdnav-embedded/src/dvdnav.c
+index 4ef7d1a..b180b18 100644
+--- a/libdvdnav-embedded/src/dvdnav.c
++++ b/libdvdnav-embedded/src/dvdnav.c
+@@ -151,7 +151,8 @@ dvdnav_status_t dvdnav_free_dup(dvdnav_t *this) {
+ static dvdnav_status_t dvdnav_open_common(dvdnav_t** dest,
+ void *priv, const dvdnav_logger_cb *logcb,
+ const char *path,
+- dvdnav_stream_cb *stream_cb) {
++ dvdnav_stream_cb *stream_cb,
++ dvdnav_filesystem_h *fs) {
+ dvdnav_t *this;
+ struct timeval time;
+
+@@ -174,7 +175,7 @@ static dvdnav_status_t dvdnav_open_common(dvdnav_t** dest,
+ if(!this->vm) {
+ goto fail;
+ }
+- if(!vm_reset(this->vm, path, priv, stream_cb)) {
++ if(!vm_reset(this->vm, path, priv, stream_cb, fs)) {
+ goto fail;
+ }
+
+@@ -213,24 +214,30 @@ fail:
+ }
+
+ dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) {
+- return dvdnav_open_common(dest, NULL, NULL, path, NULL);
++ return dvdnav_open_common(dest, NULL, NULL, path, NULL, NULL);
+ }
+
+ dvdnav_status_t dvdnav_open2(dvdnav_t** dest,
+ void *priv,const dvdnav_logger_cb *logcb,
+ const char *path) {
+- return dvdnav_open_common(dest, priv, logcb, path, NULL);
++ return dvdnav_open_common(dest, priv, logcb, path, NULL, NULL);
+ }
+
+ dvdnav_status_t dvdnav_open_stream(dvdnav_t** dest,
+ void *priv, dvdnav_stream_cb *stream_cb) {
+- return dvdnav_open_common(dest, priv, NULL, NULL, stream_cb);
++ return dvdnav_open_common(dest, priv, NULL, NULL, stream_cb, NULL);
+ }
+
+ dvdnav_status_t dvdnav_open_stream2(dvdnav_t** dest,
+ void *priv,const dvdnav_logger_cb *logcb,
+ dvdnav_stream_cb *stream_cb) {
+- return dvdnav_open_common(dest, priv, logcb, NULL, stream_cb);
++ return dvdnav_open_common(dest, priv, logcb, NULL, stream_cb, NULL);
++}
++
++dvdnav_status_t dvdnav_open_files(dvdnav_t** dest,
++ void *priv, const dvdnav_logger_cb *logcb,
++ const char *path, dvdnav_filesystem_h *fs) {
++ return dvdnav_open_common(dest, priv, logcb, path, NULL, fs);
+ }
+
+ dvdnav_status_t dvdnav_close(dvdnav_t *this) {
+@@ -280,7 +287,7 @@ dvdnav_status_t dvdnav_reset(dvdnav_t *this) {
+ #ifdef LOG_DEBUG
+ Log3(this, "resetting vm");
+ #endif
+- if(!vm_reset(this->vm, NULL, NULL, NULL)) {
++ if(!vm_reset(this->vm, NULL, NULL, NULL, NULL)) {
+ printerr("Error restarting the VM.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+diff --git a/libdvdnav-embedded/src/dvdnav/dvdnav.h b/libdvdnav-embedded/src/dvdnav/dvdnav.h
+index 85136a4..ebb3751 100644
+--- a/libdvdnav-embedded/src/dvdnav/dvdnav.h
++++ b/libdvdnav-embedded/src/dvdnav/dvdnav.h
+@@ -32,6 +32,7 @@ extern "C" {
+
+ #include "version.h"
+ #include <dvdnav/dvd_types.h>
++#include <dvdread/dvd_filesystem.h>
+ #include <dvdread/dvd_reader.h>
+ #include <dvdread/nav_types.h>
+ #include <dvdnav/dvdnav_events.h>
+@@ -55,6 +56,8 @@ typedef int32_t dvdnav_status_t;
+
+ typedef dvd_reader_stream_cb dvdnav_stream_cb;
+
++typedef dvd_reader_filesystem_h dvdnav_filesystem_h;
++
+ /*
+ * Unless otherwise stated, all functions return DVDNAV_STATUS_OK if
+ * they succeeded, otherwise DVDNAV_STATUS_ERR is returned and the error may
+@@ -110,6 +113,15 @@ dvdnav_status_t dvdnav_open_stream2(dvdnav_t **dest,
+ void *priv, const dvdnav_logger_cb *,
+ dvdnav_stream_cb *stream_cb);
+
++/*
++ * Attempts to open files given that the calling application provides the dvd_reader_filesystem implementation
++ * and the path to the file. Supports logging callbacks similarly to other dvdnav_open methods.
++ * Useful for opening files located on a virtual file system (vfs) such as smb, nfs, etc
++ */
++dvdnav_status_t dvdnav_open_files(dvdnav_t **dest,
++ void *priv, const dvdnav_logger_cb *,
++ const char *path, dvdnav_filesystem_h *fs);
++
+ dvdnav_status_t dvdnav_dup(dvdnav_t **dest, dvdnav_t *src);
+ dvdnav_status_t dvdnav_free_dup(dvdnav_t * _this);
+
+diff --git a/libdvdnav-embedded/src/vm/vm.c b/libdvdnav-embedded/src/vm/vm.c
+index 9276c91..23c187b 100644
+--- a/libdvdnav-embedded/src/vm/vm.c
++++ b/libdvdnav-embedded/src/vm/vm.c
+@@ -334,7 +334,7 @@ dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
+
+ int vm_start(vm_t *vm) {
+ if (vm->stopped) {
+- if (!vm_reset(vm, NULL, NULL, NULL))
++ if (!vm_reset(vm, NULL, NULL, NULL, NULL))
+ return 0;
+
+ vm->stopped = 0;
+@@ -368,7 +368,7 @@ static void vm_close(vm_t *vm) {
+ }
+
+ int vm_reset(vm_t *vm, const char *dvdroot,
+- void *priv, dvdnav_stream_cb *stream_cb) {
++ void *priv, dvdnav_stream_cb *stream_cb, dvdnav_filesystem_h *fs) {
+ /* Setup State */
+ memset(vm->state.registers.SPRM, 0, sizeof(vm->state.registers.SPRM));
+ memset(vm->state.registers.GPRM, 0, sizeof(vm->state.registers.GPRM));
+@@ -410,6 +410,7 @@ int vm_reset(vm_t *vm, const char *dvdroot,
+ vm->streamcb = *stream_cb;
+ else
+ vm->streamcb = (dvdnav_stream_cb) { NULL, NULL, NULL };
++ vm->dvdreaderfs = fs;
+
+ /* bind local callbacks */
+ vm->dvdstreamcb.pf_seek = vm->streamcb.pf_seek ? dvd_reader_seek_handler : NULL;
+@@ -426,7 +427,9 @@ int vm_reset(vm_t *vm, const char *dvdroot,
+ dvd_logger_cb dvdread_logcb = { .pf_log = dvd_reader_logger_handler };
+ /* Only install log handler if we have one ourself */
+ dvd_logger_cb *p_dvdread_logcb = vm->logcb.pf_log ? &dvdread_logcb : NULL;
+- if(dvdroot)
++ if(dvdroot && fs)
++ vm->dvd = DVDOpenFiles(vm, p_dvdread_logcb, dvdroot, vm->dvdreaderfs);
++ else if(dvdroot)
+ vm->dvd = DVDOpen2(vm, p_dvdread_logcb, dvdroot);
+ else if(vm->priv && vm->dvdstreamcb.pf_read)
+ vm->dvd = DVDOpenStream2(vm, p_dvdread_logcb, &vm->dvdstreamcb);
+diff --git a/libdvdnav-embedded/src/vm/vm.h b/libdvdnav-embedded/src/vm/vm.h
+index bada9f0..e97c6c0 100644
+--- a/libdvdnav-embedded/src/vm/vm.h
++++ b/libdvdnav-embedded/src/vm/vm.h
+@@ -72,6 +72,7 @@ typedef struct {
+ dvdnav_stream_cb streamcb;
+ dvd_reader_t *dvd;
+ dvd_reader_stream_cb dvdstreamcb;
++ dvdnav_filesystem_h *dvdreaderfs;
+ ifo_handle_t *vmgi;
+ ifo_handle_t *vtsi;
+ dvd_state_t state;
+@@ -119,7 +120,7 @@ dvd_reader_t *vm_get_dvd_reader(vm_t *vm);
+ int vm_start(vm_t *vm);
+ void vm_stop(vm_t *vm);
+ int vm_reset(vm_t *vm, const char *dvdroot, void *priv,
+- dvdnav_stream_cb *stream_cb);
++ dvdnav_stream_cb *stream_cb, dvdnav_filesystem_h *fs);
+
+ /* copying and merging - useful for try-running an operation */
+ vm_t *vm_new_copy(vm_t *vm);
+--
+2.35.1
+
diff --git a/debian/patches/libdvdread/0001-libdvdread-PR40-enen92.patch b/debian/patches/libdvdread/0001-libdvdread-PR40-enen92.patch
new file mode 100644
index 0000000..f512696
--- /dev/null
+++ b/debian/patches/libdvdread/0001-libdvdread-PR40-enen92.patch
@@ -0,0 +1,1694 @@
+From 4510e2ab7f85f87f781b59cafb0ad278c9edeb61 Mon Sep 17 00:00:00 2001
+From: Miguel Borges de Freitas <enen92@kodi.tv>
+Date: Fri, 17 Jun 2022 09:43:49 +0100
+Subject: [PATCH 1/3] Split filesystem implementation into platform based code
+
+---
+ libdvdread-embedded/Makefile.am | 17 ++-
+ libdvdread-embedded/configure.ac | 4 +
+ libdvdread-embedded/src/dvd_input.c | 58 +++-------
+ libdvdread-embedded/src/dvd_input.h | 23 +---
+ libdvdread-embedded/src/dvd_reader.c | 216 ++++++++++-------------------------
+ libdvdread-embedded/src/dvdread/dvd_filesystem.h | 124 ++++++++++++++++++++
+ libdvdread-embedded/src/dvdread/dvd_reader.h | 13 +--
+ libdvdread-embedded/src/dvdread_internal.h | 1 +
+ libdvdread-embedded/src/file/dir_posix.c | 98 ++++++++++++++++
+ libdvdread-embedded/src/file/dir_win32.c | 108 ++++++++++++++++++
+ libdvdread-embedded/src/file/file_posix.c | 113 ++++++++++++++++++
+ libdvdread-embedded/src/file/file_win32.c | 98 ++++++++++++++++
+ libdvdread-embedded/src/file/filesystem.c | 37 ++++++
+ libdvdread-embedded/src/file/filesystem.h | 46 ++++++++
+ libdvdread-embedded/src/file/stat_posix.c | 36 ++++++
+ libdvdread-embedded/src/file/stat_win32.c | 53 +++++++++
+ 16 files changed, 821 insertions(+), 224 deletions(-)
+ create mode 100644 libdvdread-embedded/src/dvdread/dvd_filesystem.h
+ create mode 100644 libdvdread-embedded/src/file/dir_posix.c
+ create mode 100644 libdvdread-embedded/src/file/dir_win32.c
+ create mode 100644 libdvdread-embedded/src/file/file_posix.c
+ create mode 100644 libdvdread-embedded/src/file/file_win32.c
+ create mode 100644 libdvdread-embedded/src/file/filesystem.c
+ create mode 100644 libdvdread-embedded/src/file/filesystem.h
+ create mode 100644 libdvdread-embedded/src/file/stat_posix.c
+ create mode 100644 libdvdread-embedded/src/file/stat_win32.c
+
+diff --git a/libdvdread-embedded/Makefile.am b/libdvdread-embedded/Makefile.am
+index f2849b8..29dca92 100644
+--- a/libdvdread-embedded/Makefile.am
++++ b/libdvdread-embedded/Makefile.am
+@@ -32,7 +32,21 @@ libdvdread_la_SOURCES = \
+ src/md5.h \
+ src/nav_print.c \
+ src/nav_read.c \
+- msvc/contrib/win32_cs.h
++ msvc/contrib/win32_cs.h \
++ src/file/filesystem.c \
++ src/file/filesystem.h
++
++if HAVE_WIN32
++libdvdread_la_SOURCES+= \
++ src/file/dir_win32.c \
++ src/file/file_win32.c \
++ src/file/stat_win32.c
++else
++libdvdread_la_SOURCES+= \
++ src/file/dir_posix.c \
++ src/file/file_posix.c \
++ src/file/stat_posix.c
++endif
+
+ libdvdread_la_LIBADD = $(CSS_LIBS)
+
+@@ -42,6 +56,7 @@ libdvdread_la_LDFLAGS = -version-info $(DVDREAD_LTVERSION) \
+ pkgincludedir = $(includedir)/dvdread
+ pkginclude_HEADERS = \
+ src/dvdread/bitreader.h \
++ src/dvdread/dvd_filesystem.h \
+ src/dvdread/dvd_reader.h \
+ src/dvdread/dvd_udf.h \
+ src/dvdread/ifo_print.h \
+diff --git a/libdvdread-embedded/configure.ac b/libdvdread-embedded/configure.ac
+index a60ef0c..e3cb5ac 100644
+--- a/libdvdread-embedded/configure.ac
++++ b/libdvdread-embedded/configure.ac
+@@ -77,6 +77,9 @@ AC_ARG_ENABLE([dlfcn],
+ [use_builtin_dlfcn=$enableval],
+ [use_builtin_dlfcn=no])
+
++# for filesystem/dir access
++AC_CHECK_HEADERS([dirent.h])
++
+ AS_IF([test x"$with_libdvdcss" = "xyes"], [
+ CSS_REQUIRES="libdvdcss >= 1.2"
+ PKG_CHECK_MODULES([CSS], [$CSS_REQUIRES])
+@@ -108,6 +111,7 @@ AS_IF([test "x$DOXYGEN" = "x"], [
+ ])
+ ])
+ AM_CONDITIONAL([APIDOC], [test "x$DOXYGEN" != "x" && test "x$enable_apidoc" = "xyes"])
++AM_CONDITIONAL(HAVE_WIN32, expr $host : '.*-mingw' >/dev/null 2>&1)
+
+ AS_IF([test "x$ac_cv_c_compiler_gnu" = "xyes"], [
+ AC_DEFINE([UNUSED], [__attribute__((unused))], [Unused parameter annotation])
+diff --git a/libdvdread-embedded/src/dvd_input.c b/libdvdread-embedded/src/dvd_input.c
+index 17f0d36..1baf8f7 100644
+--- a/libdvdread-embedded/src/dvd_input.c
++++ b/libdvdread-embedded/src/dvd_input.c
+@@ -22,16 +22,9 @@
+ #include "config.h" /* Required for HAVE_DVDCSS_DVDCSS_H */
+ #include <stdio.h> /* fprintf */
+ #include <stdlib.h> /* free */
+-#include <fcntl.h> /* open */
+-#include <unistd.h> /* lseek */
+ #include <string.h> /* strerror */
+ #include <errno.h>
+
+-#ifdef _WIN32
+-#include <windows.h>
+-#include "../msvc/contrib/win32_cs.h"
+-#endif
+-
+ #include "dvdread/dvd_reader.h" /* DVD_VIDEO_LB_LEN */
+ #include "dvdread_internal.h"
+ #include "dvd_input.h"
+@@ -40,7 +33,8 @@
+
+ /* The function pointers that is the exported interface of this file. */
+ dvd_input_t (*dvdinput_open) (void *, dvd_logger_cb *,
+- const char *,dvd_reader_stream_cb *);
++ const char *, dvd_reader_stream_cb *,
++ dvd_reader_filesystem_h *);
+ int (*dvdinput_close) (dvd_input_t);
+ int (*dvdinput_seek) (dvd_input_t, int);
+ int (*dvdinput_title) (dvd_input_t, int);
+@@ -77,22 +71,6 @@ static int (*DVDcss_read) (dvdcss_t, void *, int, int);
+ #define DVDCSS_SEEK_KEY (1 << 1)
+ #endif
+
+-#ifdef _WIN32
+-static int open_win32(const char *path, int flags)
+-{
+- wchar_t *wpath;
+- int fd;
+-
+- wpath = _utf8_to_wchar(path);
+- if (!wpath) {
+- return -1;
+- }
+- fd = _wopen(wpath, flags);
+- free(wpath);
+- return fd;
+-}
+-#endif
+-
+ /* The DVDinput handle, add stuff here for new input methods.
+ * NOTE: All members of this structure must be initialized in dvd_input_New
+ */
+@@ -104,8 +82,8 @@ struct dvd_input_s {
+ dvd_logger_cb *logcb;
+ off_t ipos;
+
+- /* dummy file input */
+- int fd;
++ /* file input */
++ dvd_file_h* file;
+ /* stream input */
+ dvd_reader_stream_cb *stream_cb;
+ };
+@@ -121,7 +99,7 @@ static dvd_input_t dvd_input_New(void *priv, dvd_logger_cb *logcb)
+
+ /* Initialize all inputs to safe defaults */
+ dev->dvdcss = NULL;
+- dev->fd = -1;
++ dev->file = NULL;
+ dev->stream_cb = NULL;
+ }
+ return dev;
+@@ -132,7 +110,8 @@ static dvd_input_t dvd_input_New(void *priv, dvd_logger_cb *logcb)
+ */
+ static dvd_input_t css_open(void *priv, dvd_logger_cb *logcb,
+ const char *target,
+- dvd_reader_stream_cb *stream_cb)
++ dvd_reader_stream_cb *stream_cb,
++ dvd_reader_filesystem_h *fs UNUSED)
+ {
+ dvd_input_t dev;
+
+@@ -210,7 +189,8 @@ static int css_close(dvd_input_t dev)
+ */
+ static dvd_input_t file_open(void *priv, dvd_logger_cb *logcb,
+ const char *target,
+- dvd_reader_stream_cb *stream_cb)
++ dvd_reader_stream_cb *stream_cb,
++ dvd_reader_filesystem_h *fs)
+ {
+ dvd_input_t dev;
+
+@@ -239,14 +219,8 @@ static dvd_input_t file_open(void *priv, dvd_logger_cb *logcb,
+ free(dev);
+ return NULL;
+ }
+-#if defined(_WIN32)
+- dev->fd = open_win32(target, O_RDONLY | O_BINARY);
+-#elif defined(__OS2__)
+- dev->fd = open(target, O_RDONLY | O_BINARY);
+-#else
+- dev->fd = open(target, O_RDONLY);
+-#endif
+- if(dev->fd < 0) {
++ dev->file = fs->file_open(fs, target);
++ if(!dev->file) {
+ char buf[256];
+ #if defined(HAVE_STRERROR_R) && defined(HAVE_DECL_STRERROR_R)
+ #ifdef STRERROR_R_CHAR_P
+@@ -279,7 +253,7 @@ static dvd_input_t file_open(void *priv, dvd_logger_cb *logcb,
+ */
+ static int file_seek(dvd_input_t dev, int blocks)
+ {
+- off_t pos = -1;
++ int64_t pos = -1;
+
+ if(dev->ipos == blocks)
+ {
+@@ -298,7 +272,7 @@ static int file_seek(dvd_input_t dev, int blocks)
+ /* Returns position as the number of bytes from beginning of file
+ * or -1 on error
+ */
+- pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
++ pos = dev->file->seek(dev->file, (int64_t)blocks * (int64_t)DVD_VIDEO_LB_LEN, SEEK_SET);
+
+ if (pos >= 0) {
+ dev->ipos = pos / DVD_VIDEO_LB_LEN;
+@@ -342,7 +316,7 @@ static int file_read(dvd_input_t dev, void *buffer, int blocks,
+ ret = dev->stream_cb->pf_read(dev->priv, ((char*)buffer) + bytes, len);
+ } else {
+ /* Returns the number of bytes read or -1 on error */
+- ret = read(dev->fd, ((char*)buffer) + bytes, len);
++ ret = dev->file->read(dev->file, ((char*)buffer) + bytes, len);
+ }
+
+ if(ret < 0) {
+@@ -381,8 +355,8 @@ static int file_close(dvd_input_t dev)
+
+ /* close file if it was open */
+
+- if (dev->fd >= 0) {
+- ret = close(dev->fd);
++ if (dev->file) {
++ ret = dev->file->close(dev->file);
+ }
+
+ free(dev);
+diff --git a/libdvdread-embedded/src/dvd_input.h b/libdvdread-embedded/src/dvd_input.h
+index 470cfa4..56fe170 100644
+--- a/libdvdread-embedded/src/dvd_input.h
++++ b/libdvdread-embedded/src/dvd_input.h
+@@ -31,33 +31,14 @@
+
+ typedef struct dvd_input_s *dvd_input_t;
+
+-#if defined( __MINGW32__ )
+-# undef lseek
+-# define lseek _lseeki64
+-# undef off_t
+-# define off_t off64_t
+-# undef stat
+-# define stat _stati64
+-# undef fstat
+-# define fstat _fstati64
+-# undef wstat
+-# define wstat _wstati64
+-#endif
+-
+-#ifdef __ANDROID__
+-# undef lseek
+-# define lseek lseek64
+-# undef off_t
+-# define off_t off64_t
+-#endif
+-
+ /**
+ * Function pointers that will be filled in by the input implementation.
+ * These functions provide the main API.
+ */
+ extern dvd_input_t (*dvdinput_open) (void *, dvd_logger_cb *,
+ const char *,
+- dvd_reader_stream_cb *);
++ dvd_reader_stream_cb *,
++ dvd_reader_filesystem_h *);
+ extern int (*dvdinput_close) (dvd_input_t);
+ extern int (*dvdinput_seek) (dvd_input_t, int);
+ extern int (*dvdinput_title) (dvd_input_t, int);
+diff --git a/libdvdread-embedded/src/dvd_reader.c b/libdvdread-embedded/src/dvd_reader.c
+index c4d9641..5a21056 100644
+--- a/libdvdread-embedded/src/dvd_reader.c
++++ b/libdvdread-embedded/src/dvd_reader.c
+@@ -21,17 +21,13 @@
+ */
+
+ #include "config.h"
+-#include <sys/types.h> /* off_t */
+-#include <sys/stat.h> /* stat */
+ #include <sys/time.h> /* For the timing of dvdcss_title crack. */
+-#include <fcntl.h> /* open */
+ #include <stdlib.h> /* free */
+ #include <stdio.h> /* fprintf */
+ #include <errno.h> /* errno, EIN* */
+ #include <string.h> /* memcpy, strlen */
+ #include <unistd.h> /* pclose */
+ #include <limits.h> /* PATH_MAX */
+-#include <dirent.h> /* opendir, readdir */
+ #include <ctype.h> /* isalpha */
+
+ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
+@@ -57,11 +53,8 @@
+ #include "dvdread_internal.h"
+ #include "md5.h"
+ #include "dvdread/ifo_read.h"
++#include "file/filesystem.h"
+
+-#if defined(_WIN32)
+-# include <windows.h>
+-# include "msvc/contrib/win32_cs.h"
+-#endif
+
+ /* misc win32 helpers */
+
+@@ -81,107 +74,6 @@ static inline int _private_gettimeofday( struct timeval *tv, void *tz )
+ # endif
+ #endif /* _WIN32 */
+
+-/* Compat wrapper for stat() */
+-
+-#if defined(_WIN32)
+-/* can't re-define stat (used in both function name and struct name) */
+-typedef struct _stat64 dvdstat_t;
+-static inline int dvdstat(const char *path, dvdstat_t *st)
+-{
+- wchar_t *wpath, *it;
+- int ret;
+-
+- wpath = _utf8_to_wchar(path);
+- if (!wpath) {
+- return -1;
+- }
+-
+- /* need to strip possible trailing \\ */
+- for (it = wpath; *it; it++)
+- if ((*it == '\\' || *it == '/') && *(it+1) == 0)
+- *it = 0;
+-
+- ret = _wstat64(wpath, st);
+- free(wpath);
+- return ret;
+-}
+-#else
+-typedef struct stat dvdstat_t;
+-static inline int dvdstat(const char *file, dvdstat_t *st) {
+- return stat(file, st);
+-}
+-#endif
+-
+-#if defined(_WIN32)
+-/* UTF-8 aware version of opendir()/readdir() */
+-
+-#include <io.h>
+-
+-typedef struct {
+- intptr_t handle;
+- struct _wfinddata_t went;
+- struct dirent ent;
+-} win32_dir_t;
+-
+-win32_dir_t *win32_opendir(const char *path)
+-{
+- char *filespec;
+- wchar_t *wfilespec;
+- win32_dir_t *d;
+-
+- d = calloc(1, sizeof(*d));
+- if (!d)
+- return NULL;
+-
+- filespec = malloc(strlen(path) + 3);
+- if (!filespec) {
+- goto fail;
+- }
+- sprintf(filespec, "%s\\*", path);
+-
+- wfilespec = _utf8_to_wchar(filespec);
+- free(filespec);
+- if (!wfilespec) {
+- goto fail;
+- }
+-
+- d->handle = _wfindfirst(wfilespec, &d->went);
+- free(wfilespec);
+- if (d->handle != -1) {
+- return d;
+- }
+-
+- fail:
+- free(d);
+- return NULL;
+-}
+-
+-static struct dirent *win32_readdir(win32_dir_t *dir)
+-{
+- if (dir->went.name[0]) {
+- if (!WideCharToMultiByte(CP_UTF8, 0, dir->went.name, -1, dir->ent.d_name, sizeof(dir->ent.d_name), NULL, NULL))
+- dir->ent.d_name[0] = 0; /* allow reading next */
+- dir->went.name[0] = 0;
+- _wfindnext(dir->handle, &dir->went);
+- return &dir->ent;
+- }
+-
+- return NULL;
+-}
+-
+-static void win32_closedir(win32_dir_t *dir)
+-{
+- _findclose(dir->handle);
+- free(dir);
+-}
+-
+-#define DIR win32_dir_t
+-#define opendir win32_opendir
+-#define readdir win32_readdir
+-#define closedir win32_closedir
+-
+-#endif /* _WIN32 */
+-
+ #define DEFAULT_UDF_CACHE_LEVEL 1
+
+ struct dvd_reader_device_s {
+@@ -338,7 +230,7 @@ static dvd_reader_device_t *DVDOpenImageFile( dvd_reader_t *ctx,
+ dvd_reader_device_t *dvd;
+ dvd_input_t dev;
+
+- dev = dvdinput_open( ctx->priv, &ctx->logcb, location, stream_cb );
++ dev = dvdinput_open( ctx->priv, &ctx->logcb, location, stream_cb, ctx->fs );
+ if( !dev ) {
+ Log0(ctx,"Can't open %s for reading", location );
+ return NULL;
+@@ -452,6 +344,13 @@ static dvd_reader_t *DVDOpenCommon( void *priv,
+ if(logcb)
+ ctx->logcb = *logcb;
+
++ ctx->fs = InitInternalFilesystem();
++ if (!ctx->fs)
++ {
++ free(ctx);
++ return NULL;
++ }
++
+ #if defined(_WIN32) || defined(__OS2__)
+ int len;
+ #endif
+@@ -490,7 +389,7 @@ static dvd_reader_t *DVDOpenCommon( void *priv,
+ }
+ #endif
+
+- ret = dvdstat( path, &fileinfo );
++ ret = ctx->fs->stat(ctx->fs, path, &fileinfo);
+
+ if( ret < 0 ) {
+
+@@ -513,9 +412,9 @@ static dvd_reader_t *DVDOpenCommon( void *priv,
+ }
+
+ /* First check if this is a block/char device or a file*/
+- if( S_ISBLK( fileinfo.st_mode ) ||
+- S_ISCHR( fileinfo.st_mode ) ||
+- S_ISREG( fileinfo.st_mode ) ) {
++ if( (fileinfo.st_mode & DVD_S_IFMT) == DVD_S_IFBLK ||
++ (fileinfo.st_mode & DVD_S_IFMT) == DVD_S_IFCHR ||
++ (fileinfo.st_mode & DVD_S_IFMT) == DVD_S_IFREG ) {
+
+ /**
+ * Block devices and regular files are assumed to be DVD-Video images.
+@@ -538,7 +437,7 @@ static dvd_reader_t *DVDOpenCommon( void *priv,
+ return NULL;
+ }
+ return ctx;
+- } else if( S_ISDIR( fileinfo.st_mode ) ) {
++ } else if ((fileinfo.st_mode & DVD_S_IFMT) == DVD_S_IFDIR ) {
+ #if defined(SYS_BSD)
+ struct fstab* fe;
+ #elif defined(__sun) || defined(__linux__)
+@@ -758,6 +657,9 @@ void DVDClose( dvd_reader_t *dvd )
+ if( dvd->rd->path_root ) free( dvd->rd->path_root );
+ if( dvd->rd->udfcache ) FreeUDFCache( dvd->rd->udfcache );
+ free( dvd->rd );
++ if (dvd->fs) {
++ dvd->fs->close(dvd->fs);
++ }
+ free( dvd );
+ }
+ }
+@@ -813,24 +715,33 @@ static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *ctx, const char *filename,
+ * or -1 on file not found.
+ * or -2 on path not found.
+ */
+-static int findDirFile( const char *path, const char *file, char *filename )
++static int findDirFile(dvd_reader_t *ctx, const char *path, const char *file, char *filename )
+ {
+- DIR *dir;
+- struct dirent *ent;
+-
+- dir = opendir( path );
+- if( !dir ) return -2;
++ dvd_dirent_t entry;
++ dvd_dir_h *dir = ctx->fs->dir_open(ctx->fs, path);
++ if( !dir ) {
++ Log0(ctx, "findDirFile: Could not open dir %s ", path);
++ return -2;
++ }
+
+- while( ( ent = readdir( dir ) ) != NULL ) {
+- if( !strcasecmp( ent->d_name, file ) ) {
++ int result = 0;
++ do
++ {
++ result = dir->read(dir, &entry);
++ if (result < 0) {
++ Log0(ctx, "findDirFile: Error reading dir %s (errorno: %d)", path, result);
++ return -1;
++ }
++ if( !strcasecmp( entry.d_name, file ) ) {
+ sprintf( filename, "%s%s%s", path,
+ ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+- ent->d_name );
+- closedir(dir);
++ entry.d_name );
++ dir->close(dir);
+ return 0;
+ }
+- }
+- closedir(dir);
++ } while (result == 0);
++
++ dir->close(dir);
+ return -1;
+ }
+
+@@ -846,17 +757,17 @@ static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+ nodirfile = file;
+ }
+
+- ret = findDirFile( dvd->rd->path_root, nodirfile, filename );
++ ret = findDirFile(dvd, dvd->rd->path_root, nodirfile, filename );
+ if( ret < 0 ) {
+ char video_path[ PATH_MAX + 1 ];
+
+ /* Try also with adding the path, just in case. */
+ sprintf( video_path, "%s/VIDEO_TS/", dvd->rd->path_root );
+- ret = findDirFile( video_path, nodirfile, filename );
++ ret = findDirFile(dvd, video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ /* Try with the path, but in lower case. */
+ sprintf( video_path, "%s/video_ts/", dvd->rd->path_root );
+- ret = findDirFile( video_path, nodirfile, filename );
++ ret = findDirFile(dvd, video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ return 0;
+ }
+@@ -882,7 +793,7 @@ static dvd_file_t *DVDOpenFilePath( dvd_reader_t *ctx, const char *filename )
+ return NULL;
+ }
+
+- dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
++ dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL, ctx->fs );
+ if( !dev ) {
+ Log0(ctx, "DVDOpenFilePath:dvdinput_open %s failed", full_path );
+ return NULL;
+@@ -896,13 +807,13 @@ static dvd_file_t *DVDOpenFilePath( dvd_reader_t *ctx, const char *filename )
+ }
+ dvd_file->ctx = ctx;
+
+- if( dvdstat( full_path, &fileinfo ) < 0 ) {
++ if (ctx->fs->stat(ctx->fs, full_path, &fileinfo) < 0) {
+ Log0(ctx, "Can't stat() %s.", filename );
+ free( dvd_file );
+ dvdinput_close( dev );
+ return NULL;
+ }
+- dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
++ dvd_file->title_sizes[ 0 ] = fileinfo.size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+@@ -979,23 +890,22 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
+ return NULL;
+ }
+
+- dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
++ dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL, ctx->fs );
+ if( dev == NULL ) {
+ free( dvd_file );
+ return NULL;
+ }
+
+- if( dvdstat( full_path, &fileinfo ) < 0 ) {
++ if (ctx->fs->stat(ctx->fs, full_path, &fileinfo) > 0) {
+ Log0(ctx, "Can't stat() %s.", filename );
+ dvdinput_close(dev);
+ free( dvd_file );
+ return NULL;
+ }
+- dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
++ dvd_file->title_sizes[ 0 ] = fileinfo.size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvdinput_title( dvd_file->title_devs[0], 0);
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+-
+ } else {
+ int i;
+
+@@ -1006,13 +916,13 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
+ break;
+ }
+
+- if( dvdstat( full_path, &fileinfo ) < 0 ) {
++ if (ctx->fs->stat(ctx->fs, full_path, &fileinfo) < 0) {
+ Log0(ctx, "Can't stat() %s.", filename );
+ break;
+ }
+
+- dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+- dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
++ dvd_file->title_sizes[ i ] = fileinfo.size / DVD_VIDEO_LB_LEN;
++ dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL, ctx->fs );
+ dvdinput_title( dvd_file->title_devs[ i ], 0 );
+ dvd_file->filesize += dvd_file->title_sizes[ i ];
+ }
+@@ -1105,8 +1015,8 @@ static int DVDFileStatVOBUDF( dvd_reader_t *dvd, int title,
+ {
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t size;
+- off_t tot_size;
+- off_t parts_size[ 9 ];
++ int64_t tot_size;
++ int64_t parts_size[ 9 ];
+ int nr_parts = 0;
+ int n;
+
+@@ -1151,8 +1061,8 @@ static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ char full_path[ PATH_MAX + 1 ];
+ dvdstat_t fileinfo;
+- off_t tot_size;
+- off_t parts_size[ 9 ];
++ int64_t tot_size;
++ int64_t parts_size[ 9 ];
+ int nr_parts = 0;
+ int n;
+
+@@ -1164,14 +1074,14 @@ static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
+ if( !findDVDFile( dvd, filename, full_path ) )
+ return -1;
+
+- if( dvdstat( full_path, &fileinfo ) < 0 ) {
++ if (dvd->fs->stat(dvd->fs, full_path, &fileinfo) < 0) {
+ Log1(dvd, "Can't stat() %s.", filename );
+ return -1;
+ }
+
+- tot_size = fileinfo.st_size;
++ tot_size = fileinfo.size;
+ nr_parts = 1;
+- parts_size[ 0 ] = fileinfo.st_size;
++ parts_size[ 0 ] = fileinfo.size;
+
+ if( !menu ) {
+ int cur;
+@@ -1180,12 +1090,12 @@ static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
+ if( !findDVDFile( dvd, filename, full_path ) )
+ break;
+
+- if( dvdstat( full_path, &fileinfo ) < 0 ) {
++ if (dvd->fs->stat(dvd->fs, full_path, &fileinfo) < 0) {
+ Log1(dvd, "Can't stat() %s.", filename );
+ break;
+ }
+
+- parts_size[ nr_parts ] = fileinfo.st_size;
++ parts_size[ nr_parts ] = fileinfo.size;
+ tot_size += parts_size[ nr_parts ];
+ nr_parts++;
+ }
+@@ -1263,10 +1173,10 @@ int DVDFileStat( dvd_reader_t *reader, int titlenum,
+ char full_path[ PATH_MAX + 1 ];
+
+ if( findDVDFile( reader, filename, full_path ) ) {
+- if( dvdstat( full_path, &fileinfo ) < 0 )
++ if (reader->fs->stat(reader->fs, full_path, &fileinfo) < 0)
+ Log1(reader, "Can't stat() %s.", filename );
+ else {
+- statbuf->size = fileinfo.st_size;
++ statbuf->size = fileinfo.size;
+ statbuf->nr_parts = 1;
+ statbuf->parts_size[ 0 ] = statbuf->size;
+ return 0;
+@@ -1318,8 +1228,8 @@ static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
+
+ /* Copy the cache at a specified offset into data. offset and block_count
+ * must be converted into bytes */
+- memcpy( data, dvd_file->cache + (off_t)offset * (off_t)DVD_VIDEO_LB_LEN,
+- (off_t)block_count * (off_t)DVD_VIDEO_LB_LEN );
++ memcpy( data, dvd_file->cache + (int64_t)offset * (int64_t)DVD_VIDEO_LB_LEN,
++ (int64_t)block_count * (int64_t)DVD_VIDEO_LB_LEN );
+
+ /* return the amount of blocks copied */
+ return block_count;
+diff --git a/libdvdread-embedded/src/dvdread/dvd_filesystem.h b/libdvdread-embedded/src/dvdread/dvd_filesystem.h
+new file mode 100644
+index 0000000..291f4d9
+--- /dev/null
++++ b/libdvdread-embedded/src/dvdread/dvd_filesystem.h
+@@ -0,0 +1,124 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef DVDREAD_FILESYSTEM_H_
++#define DVDREAD_FILESYSTEM_H_
++
++#include <stdint.h>
++#include <sys/types.h>
++
++/*
++ * directory access
++ */
++
++/**
++ * The dvd_dirent_t struct abstracts dirent usage from the respective platform implementation.
++ * For this reason, it only includes the parts the lib cares about (in this case d_name - the directory name)
++ */
++typedef struct
++{
++ char d_name[256];
++} dvd_dirent_t;
++
++typedef struct dvd_filesystem_dir_s dvd_dir_h;
++/**
++ * Abstraction for directory access
++ * internal - opaque handler internal to the specific implementation used to store the data type of directory stream objects.
++ * This is tipically a dir handler (e.g. a DIR* in posix)
++ * close(dvd_dir_h *dir) - called to close the directory and cleanup any alloc'd dir structures
++ * read(dvd_dir_h *dir, dvd_dirent_t *entry) - provided the dvd_dirent_t, reads the respective directory and returns -1 if the
++ * directory could not be read, 0 if successfully read.
++ */
++struct dvd_filesystem_dir_s
++{
++ void *internal;
++ void (*close)(dvd_dir_h *dir);
++ int (*read)(dvd_dir_h *dir, dvd_dirent_t *entry);
++};
++
++/*
++ * Stat access
++ */
++
++#define DVD_S_IFMT 0170000 /* These bits determine file type. */
++
++#define DVD_S_IFCHR 0020000 /* character special */
++#define DVD_S_IFDIR 0040000 /* directory */
++#define DVD_S_IFBLK 0060000 /* block special */
++#define DVD_S_IFREG 0100000 /* regular */
++
++/**
++ * Abstraction for stat buffer structure
++ * size - size of the stat'd file
++ * st_mode - file mode
++ */
++typedef struct
++{
++ off_t size;
++ unsigned int st_mode;
++} dvdstat_t;
++
++
++/*
++ * file access
++ */
++
++/**
++ * Abstraction for file access
++ * internal - opaque handler internal to the specific implementation used to store the data type of the file object.
++ * This is tipically a file reference (e.g. the file descriptor/fd)
++ * close(dvd_file_h *file) - called to close the file and cleanup any alloc'd file structs
++ * seek(dvd_file_h *file, int64_t offset, int32_t origin) - used to seek into the given file (provided the offset and the origin). Returns
++ * the position on the file after seek.
++ * read(dvd_file_h *file, char *buf, int64_t size) - used to read the file into the passed buffer, given the read size. Returns the read
++ * read from the file
++ */
++typedef struct dvd_filesystem_file_s dvd_file_h;
++struct dvd_filesystem_file_s
++{
++ void *internal;
++ int (*close) (dvd_file_h *file);
++ int64_t (*seek) (dvd_file_h *file, int64_t offset, int32_t origin);
++ ssize_t (*read) (dvd_file_h *file, char *buf, size_t size);
++};
++
++/*
++ * Filesystem implementation
++ */
++
++/**
++ * Groups all filesystem operations into a common struct. This is the struct external applications should override to
++ * provide custom filesystem implementations:
++ * internal - opaque data pointer to user data (used to convey custom data within the filesystem struct)
++ * close(dvd_reader_filesystem_h *fs) - called to destroy the filesystem implementation (free any alloc'd structs)
++ * stat(dvd_reader_filesystem_h *fs, const char *path, dvdstat_t* statbuf) - stat a file/dir provided the statbuf, initializes the dvdstat_t.
++ * dir_open(dvd_reader_filesystem_h *fs, const char* dirname) - open the provided dir, initializes dvd_dir_h
++ * file_open(dvd_reader_filesystem_h *fs, const char* filename, const char *mode) - open a file, initializes dvd_file_h
++ */
++typedef struct dvd_reader_filesystem_s dvd_reader_filesystem_h;
++struct dvd_reader_filesystem_s
++{
++ void *internal;
++ void (*close) (dvd_reader_filesystem_h *fs);
++ int (*stat) (dvd_reader_filesystem_h *fs, const char *path, dvdstat_t *statbuf);
++ dvd_dir_h* (*dir_open) (dvd_reader_filesystem_h *fs, const char *dirname);
++ dvd_file_h* (*file_open) (dvd_reader_filesystem_h *fs, const char *filename);
++};
++
++#endif /* DVDREAD_FILESYSTEM_H_ */
+diff --git a/libdvdread-embedded/src/dvdread/dvd_reader.h b/libdvdread-embedded/src/dvdread/dvd_reader.h
+index 54ef5dd..5b15704 100644
+--- a/libdvdread-embedded/src/dvdread/dvd_reader.h
++++ b/libdvdread-embedded/src/dvdread/dvd_reader.h
+@@ -23,13 +23,6 @@
+ #ifndef LIBDVDREAD_DVD_READER_H
+ #define LIBDVDREAD_DVD_READER_H
+
+-#ifdef _MSC_VER
+-#include <config.h>
+-
+-#include <stdio.h>
+-#include <stdlib.h>
+-#endif
+-
+ #include <sys/types.h>
+ #include <inttypes.h>
+ #include <stdarg.h>
+@@ -46,6 +39,12 @@
+ */
+ #include "version.h"
+
++/**
++ * Filesystem types
++ */
++#include "dvd_filesystem.h"
++
++
+ /**
+ * The length of one Logical Block of a DVD.
+ */
+diff --git a/libdvdread-embedded/src/dvdread_internal.h b/libdvdread-embedded/src/dvdread_internal.h
+index bf4e2e1..1a35059 100644
+--- a/libdvdread-embedded/src/dvdread_internal.h
++++ b/libdvdread-embedded/src/dvdread_internal.h
+@@ -39,6 +39,7 @@ struct dvd_reader_s
+ dvd_reader_device_t *rd;
+ void *priv; /* User provided context */
+ dvd_logger_cb logcb;
++ dvd_reader_filesystem_h* fs;
+ /* Set 100 flags for BUP fallback, most signifiant left
+ [0] for upper remaining VTS, [1] for the first Main + 63 VTS */
+ uint64_t ifoBUPflags[2];
+diff --git a/libdvdread-embedded/src/file/dir_posix.c b/libdvdread-embedded/src/file/dir_posix.c
+new file mode 100644
+index 0000000..f55e62a
+--- /dev/null
++++ b/libdvdread-embedded/src/file/dir_posix.c
+@@ -0,0 +1,98 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#include "config.h"
++
++#include <stdlib.h>
++#include <string.h>
++#if HAVE_DIRENT_H
++#include <dirent.h>
++#endif
++
++#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
++# if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 24)
++# define USE_READDIR
++# include <errno.h>
++# endif
++#endif
++
++#include <dvdread/dvd_filesystem.h>
++#include "filesystem.h"
++
++void _dir_close_posix(dvd_dir_h *dir)
++{
++ if (dir) {
++ closedir((DIR *)dir->internal);
++ free(dir);
++ dir = NULL;
++ }
++}
++
++int _dir_read_posix(dvd_dir_h *dir, dvd_dirent_t *entry)
++{
++ struct dirent *p_e;
++
++#ifdef USE_READDIR
++ errno = 0;
++ p_e = readdir((DIR*)dir->internal);
++ if (!p_e && errno) {
++ return -errno;
++ }
++#else /* USE_READDIR */
++ int result;
++ struct dirent e;
++
++ result = readdir_r((DIR*)dir->internal, &e, &p_e);
++ if (result) {
++ return -result;
++ }
++#endif /* USE_READDIR */
++
++ if (p_e == NULL) {
++ return 1;
++ }
++ strncpy(entry->d_name, p_e->d_name, sizeof(entry->d_name));
++ entry->d_name[sizeof(entry->d_name) - 1] = 0;
++
++ return 0;
++}
++
++dvd_dir_h *dir_open_default(dvd_reader_filesystem_h *fs, const char* dirname)
++{
++ if (!fs)
++ return NULL;
++
++ dvd_dir_h *dir = calloc(1, sizeof(dvd_dir_h));
++
++ if (!dir) {
++ return NULL;
++ }
++
++ dir->close = _dir_close_posix;
++ dir->read = _dir_read_posix;
++
++ if ((dir->internal = opendir(dirname))) {
++ return dir;
++ }
++
++ free(dir);
++ dir = NULL;
++
++ return NULL;
++}
+diff --git a/libdvdread-embedded/src/file/dir_win32.c b/libdvdread-embedded/src/file/dir_win32.c
+new file mode 100644
+index 0000000..cb89728
+--- /dev/null
++++ b/libdvdread-embedded/src/file/dir_win32.c
+@@ -0,0 +1,108 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#include <io.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++
++#include <windows.h>
++#include "../msvc/contrib/win32_cs.h"
++
++
++#include <dvdread/dvd_filesystem.h>
++#include "filesystem.h"
++
++
++typedef struct {
++ intptr_t handle;
++ struct _wfinddata_t went;
++} win32_dir_t;
++
++
++void _dir_close_win32(dvd_dir_h *dir)
++{
++ if (dir) {
++ _findclose(((win32_dir_t*)dir->internal)->handle);
++ free((win32_dir_t*)dir->internal);
++ free(dir);
++ dir = NULL;
++ }
++}
++
++int _dir_read_win32(dvd_dir_h *dir, dvd_dirent_t *entry)
++{
++ win32_dir_t *wdir = (win32_dir_t*)dir->internal;
++ if (wdir->went.name[0]) {
++ if (!WideCharToMultiByte(CP_UTF8, 0, wdir->went.name, -1, entry->d_name, sizeof(entry->d_name), NULL, NULL))
++ entry->d_name[0] = 0; /* allow reading next */
++ wdir->went.name[0] = 0;
++ _wfindnext(wdir->handle, &wdir->went);
++ return 0;
++ }
++ return -1;
++}
++
++dvd_dir_h *dir_open_default(dvd_reader_filesystem_h *fs, const char* dirname)
++{
++ if (!fs)
++ return NULL;
++
++ char *filespec;
++ wchar_t *wfilespec;
++ win32_dir_t *d;
++ dvd_dir_h *dir = calloc(1, sizeof(dvd_dir_h));
++
++ if (!dir) {
++ return NULL;
++ }
++
++ d = calloc(1, sizeof(*d));
++ if (!d)
++ {
++ free(dir);
++ return NULL;
++ }
++
++ filespec = malloc(strlen(dirname) + 3);
++ if (!filespec) {
++ goto fail;
++ }
++ sprintf(filespec, "%s\\*", dirname);
++
++ wfilespec = _utf8_to_wchar(filespec);
++ free(filespec);
++ if (!wfilespec) {
++ goto fail;
++ }
++
++ d->handle = _wfindfirst(wfilespec, &d->went);
++ free(wfilespec);
++ if (d->handle != -1) {
++ dir->internal = (void*)d;
++ dir->close = _dir_close_win32;
++ dir->read = _dir_read_win32;
++ return dir;
++ }
++
++ fail:
++ free(d);
++ free(dir);
++ return NULL;
++}
+diff --git a/libdvdread-embedded/src/file/file_posix.c b/libdvdread-embedded/src/file/file_posix.c
+new file mode 100644
+index 0000000..cbdbd58
+--- /dev/null
++++ b/libdvdread-embedded/src/file/file_posix.c
+@@ -0,0 +1,113 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <errno.h>
++
++
++#include <sys/stat.h>
++#include <fcntl.h>
++
++#include <dvdread/dvd_filesystem.h>
++
++#include "filesystem.h"
++
++#ifdef __ANDROID__
++# undef lseek
++# define lseek lseek64
++# undef off_t
++# define off_t off64_t
++#endif
++
++
++int _file_close(dvd_file_h *file)
++{
++ if (file) {
++ int ret = close((int)(intptr_t)file->internal);
++ free(file);
++ return ret;
++ }
++ return 0;
++}
++
++int64_t _file_seek(dvd_file_h *file, int64_t offset, int32_t origin)
++{
++ off_t result = lseek((int)(intptr_t)file->internal, offset, origin);
++ if (result == (off_t)-1) {
++ return -1;
++ }
++ return (int64_t)result;
++}
++
++ssize_t _file_read(dvd_file_h *file, char *buf, size_t size)
++{
++ ssize_t result;
++
++ if (size <= 0) {
++ return 0;
++ }
++
++ result = read((int)(intptr_t)file->internal, buf, size);
++ return result;
++}
++
++
++dvd_file_h* file_open_default(dvd_reader_filesystem_h *fs, const char* filename)
++{
++ if (!fs)
++ return NULL;
++
++ dvd_file_h *file;
++ int fd = -1;
++ int flags = 0;
++ int mode = 0;
++
++ #if defined(__OS2__) // not posix but kept here for legacy compatibility reasons
++ flags = O_RDONLY | O_BINARY;
++ #else
++ flags = O_RDONLY;
++ #endif
++
++#ifdef O_CLOEXEC
++ flags |= O_CLOEXEC;
++#endif
++#ifdef O_BINARY
++ flags |= O_BINARY;
++#endif
++
++ if ((fd = open(filename, flags, mode)) < 0) {
++ return NULL;
++ }
++
++ file = calloc(1, sizeof(dvd_file_h));
++ if (!file) {
++ close(fd);
++ return NULL;
++ }
++
++ file->close = _file_close;
++ file->read = _file_read;
++ file->seek = _file_seek;
++ file->internal = (void*)(intptr_t)fd;
++
++ return file;
++}
+diff --git a/libdvdread-embedded/src/file/file_win32.c b/libdvdread-embedded/src/file/file_win32.c
+new file mode 100644
+index 0000000..9787076
+--- /dev/null
++++ b/libdvdread-embedded/src/file/file_win32.c
+@@ -0,0 +1,98 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <errno.h>
++
++#include <sys/stat.h>
++#include <fcntl.h>
++
++#include <windows.h>
++#include "../msvc/contrib/win32_cs.h"
++
++#include <dvdread/dvd_filesystem.h>
++#include "filesystem.h"
++
++
++int _file_close_win32(dvd_file_h *file)
++{
++ if (file) {
++ int ret = close((int)(intptr_t)file->internal);
++ free(file);
++ return ret;
++ }
++ return 0;
++}
++
++int64_t _file_seek_win32(dvd_file_h *file, int64_t offset, int32_t origin)
++{
++ off64_t result = _lseeki64((int)(intptr_t)file->internal, offset, origin);
++ if (result == (off64_t)-1) {
++ return -1;
++ }
++ return (int64_t)result;
++}
++
++ssize_t _file_read_win32(dvd_file_h *file, char *buf, size_t size)
++{
++ ssize_t result;
++
++ if (size <= 0) {
++ return 0;
++ }
++
++ result = read((int)(intptr_t)file->internal, buf, size);
++ return result;
++}
++
++
++dvd_file_h* file_open_default(dvd_reader_filesystem_h *fs, const char* filename)
++{
++ if (!fs)
++ return NULL;
++
++ dvd_file_h *file;
++ int fd = -1;
++ wchar_t *wpath;
++
++ wpath = _utf8_to_wchar(filename);
++ if (!wpath) {
++ return NULL;
++ }
++
++ if ((fd = _wopen(wpath, O_RDONLY | O_BINARY)) < 0) {
++ free(wpath);
++ return NULL;
++ }
++
++ file = calloc(1, sizeof(dvd_file_h));
++ if (!file) {
++ close(fd);
++ return NULL;
++ }
++
++ file->close = _file_close_win32;
++ file->read = _file_read_win32;
++ file->seek = _file_seek_win32;
++ file->internal = (void*)(intptr_t)fd;
++
++ return file;
++}
+diff --git a/libdvdread-embedded/src/file/filesystem.c b/libdvdread-embedded/src/file/filesystem.c
+new file mode 100644
+index 0000000..b79edae
+--- /dev/null
++++ b/libdvdread-embedded/src/file/filesystem.c
+@@ -0,0 +1,37 @@
++/*
++ * This file is part of libdvdread.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Lesser General Public License as published by
++ * the Free Software Foundation; either version 2.1 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
++ */
++
++#include "filesystem.h"
++
++#include <stdlib.h>
++
++static void default_filesystem_close(dvd_reader_filesystem_h *fs) {
++ free(fs);
++}
++
++dvd_reader_filesystem_h* InitInternalFilesystem() {
++ dvd_reader_filesystem_h* fs = calloc( 1, sizeof(dvd_reader_filesystem_h));
++ if (!fs) {
++ return NULL;
++ }
++ fs->dir_open = dir_open_default;
++ fs->stat = stat_default;
++ fs->file_open = file_open_default;
++ fs->close = default_filesystem_close;
++ return fs;
++}
+diff --git a/libdvdread-embedded/src/file/filesystem.h b/libdvdread-embedded/src/file/filesystem.h
+new file mode 100644
+index 0000000..1b8a014
+--- /dev/null
++++ b/libdvdread-embedded/src/file/filesystem.h
+@@ -0,0 +1,46 @@
++/*
++ * This file is part of libdvdread.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Lesser General Public License as published by
++ * the Free Software Foundation; either version 2.1 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
++ */
++
++#ifndef LIBDVDREAD_FILESYSTEM_H
++#define LIBDVDREAD_FILESYSTEM_H
++
++#include "dvdread/dvd_filesystem.h"
++
++/**
++ * Prototype definition for default file open function (implemented by each platform)
++ */
++dvd_file_h* file_open_default(dvd_reader_filesystem_h *fs, const char* filename);
++
++/**
++ * Prototype definition for default dir open function (implemented by each platform)
++ */
++dvd_dir_h* dir_open_default(dvd_reader_filesystem_h *fs, const char* dirname);
++
++/**
++ * Prototype definition for default stat function (implemented by each platform)
++ */
++int stat_default(dvd_reader_filesystem_h *fs, const char *path, dvdstat_t* statbuf);
++
++/**
++ * Inits the internal (platform specific) filesystem implementation
++ * bundled with libdvdread. This includes initializing the default internal
++ * implmentations of file_open, dir_open, stat, etc.
++ */
++dvd_reader_filesystem_h* InitInternalFilesystem();
++
++#endif
+diff --git a/libdvdread-embedded/src/file/stat_posix.c b/libdvdread-embedded/src/file/stat_posix.c
+new file mode 100644
+index 0000000..61670fc
+--- /dev/null
++++ b/libdvdread-embedded/src/file/stat_posix.c
+@@ -0,0 +1,36 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#include <stdlib.h>
++#include <sys/stat.h>
++
++#include <dvdread/dvd_filesystem.h>
++#include "filesystem.h"
++
++int stat_default(dvd_reader_filesystem_h *fs, const char *path, dvdstat_t* statbuf)
++{
++ if (!fs)
++ return -1;
++
++ struct stat posixstatbuf;
++ int ret = stat(path, &posixstatbuf);
++ statbuf->size = posixstatbuf.st_size;
++ statbuf->st_mode = posixstatbuf.st_mode;
++ return ret;
++}
+diff --git a/libdvdread-embedded/src/file/stat_win32.c b/libdvdread-embedded/src/file/stat_win32.c
+new file mode 100644
+index 0000000..0b8245d
+--- /dev/null
++++ b/libdvdread-embedded/src/file/stat_win32.c
+@@ -0,0 +1,53 @@
++/*
++ * This file is part of libdvdread
++ * Copyright (C) 2022 VideoLAN
++ *
++ * This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This file 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library. If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#include <stdlib.h>
++#include <sys/stat.h>
++
++#include <windows.h>
++#include "../msvc/contrib/win32_cs.h"
++
++#include <dvdread/dvd_filesystem.h>
++#include "filesystem.h"
++
++
++int stat_default(dvd_reader_filesystem_h *fs, const char *path, dvdstat_t* statbuf)
++{
++ if (!fs)
++ return -1;
++
++ struct _stat64 win32statbuf;
++
++ wchar_t *wpath, *it;
++
++ wpath = _utf8_to_wchar(path);
++ if (!wpath) {
++ return -1;
++ }
++
++ /* need to strip possible trailing \\ */
++ for (it = wpath; *it; it++)
++ if ((*it == '\\' || *it == '/') && *(it+1) == 0)
++ *it = 0;
++
++ int ret = _wstat64(wpath, &win32statbuf);
++ statbuf->size = win32statbuf.st_size;
++ statbuf->st_mode = win32statbuf.st_mode;
++ return ret;
++}
+--
+2.35.1
+
+
+From 7215ed052b632be49c4f1ed444c7c9447e2d4c61 Mon Sep 17 00:00:00 2001
+From: Miguel Borges de Freitas <enen92@kodi.tv>
+Date: Fri, 29 Jul 2022 12:50:24 +0100
+Subject: [PATCH 2/3] Decouple dvdinput_setup providing dvdinput_setup_builtin
+ for minimal/internal fs access
+
+---
+ libdvdread-embedded/src/dvd_input.c | 23 +++++++++++++++++------
+ libdvdread-embedded/src/dvd_input.h | 8 ++++++++
+ 2 files changed, 25 insertions(+), 6 deletions(-)
+
+diff --git a/libdvdread-embedded/src/dvd_input.c b/libdvdread-embedded/src/dvd_input.c
+index 1baf8f7..70f31b3 100644
+--- a/libdvdread-embedded/src/dvd_input.c
++++ b/libdvdread-embedded/src/dvd_input.c
+@@ -446,12 +446,23 @@ int dvdinput_setup(void *priv, dvd_logger_cb *logcb)
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_WARN,
+ "Encrypted DVD support unavailable.");
+
+- /* libdvdcss replacement functions */
+- dvdinput_open = file_open;
+- dvdinput_close = file_close;
+- dvdinput_seek = file_seek;
+- dvdinput_title = file_title;
+- dvdinput_read = file_read;
++ dvdinput_setup_builtin(priv, logcb);
+ return 0;
+ }
+ }
++
++/**
++ * Setup read functions with the builtin libdvdread implementation (minimal DVD access without css).
++ */
++void dvdinput_setup_builtin(void *priv, dvd_logger_cb *logcb)
++{
++ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_INFO,
++ "Setting up builtin libdvdread implementation");
++
++ /* libdvdcss replacement functions */
++ dvdinput_open = file_open;
++ dvdinput_close = file_close;
++ dvdinput_seek = file_seek;
++ dvdinput_title = file_title;
++ dvdinput_read = file_read;
++}
+diff --git a/libdvdread-embedded/src/dvd_input.h b/libdvdread-embedded/src/dvd_input.h
+index 56fe170..36fe279 100644
+--- a/libdvdread-embedded/src/dvd_input.h
++++ b/libdvdread-embedded/src/dvd_input.h
+@@ -46,7 +46,15 @@ extern int (*dvdinput_read) (dvd_input_t, void *, int, int);
+
+ /**
+ * Setup function accessed by dvd_reader.c. Returns 1 if there is CSS support.
++ * Otherwise it falls back to the internal dvdread implementation (without css support)
++ * which is basically the same as calling dvdinput_setup_builtin.
+ */
+ int dvdinput_setup(void *, dvd_logger_cb *);
+
++/**
++ * Setup function accessed by dvd_reader.c using the builtin libdvdread implementation
++ * (without css support)
++ */
++void dvdinput_setup_builtin(void *, dvd_logger_cb *);
++
+ #endif /* LIBDVDREAD_DVD_INPUT_H */
+--
+2.35.1
+
+
+From 665a98ee15896e97aa8f0c0d6499b281d0602e60 Mon Sep 17 00:00:00 2001
+From: Miguel Borges de Freitas <enen92@kodi.tv>
+Date: Fri, 29 Jul 2022 12:55:19 +0100
+Subject: [PATCH 3/3] Add DVDOpenFiles supporting caller provided filesystem
+ implementation
+
+---
+ libdvdread-embedded/src/dvd_reader.c | 32 +++++++++++++++++++++++++++-----
+ libdvdread-embedded/src/dvdread/dvd_reader.h | 13 +++++++++++++
+ 2 files changed, 40 insertions(+), 5 deletions(-)
+
+diff --git a/libdvdread-embedded/src/dvd_reader.c b/libdvdread-embedded/src/dvd_reader.c
+index 5a21056..84bef88 100644
+--- a/libdvdread-embedded/src/dvd_reader.c
++++ b/libdvdread-embedded/src/dvd_reader.c
+@@ -330,7 +330,8 @@ static char *bsd_block2char( const char *path )
+ static dvd_reader_t *DVDOpenCommon( void *priv,
+ const dvd_logger_cb *logcb,
+ const char *ppath,
+- dvd_reader_stream_cb *stream_cb )
++ dvd_reader_stream_cb *stream_cb,
++ dvd_reader_filesystem_h * fs )
+ {
+ dvdstat_t fileinfo;
+ int ret, have_css, cdir = -1;
+@@ -344,6 +345,21 @@ static dvd_reader_t *DVDOpenCommon( void *priv,
+ if(logcb)
+ ctx->logcb = *logcb;
+
++ // open files using the provided filesystem implementation
++ if (fs != NULL && ppath != NULL)
++ {
++ ctx->fs = fs;
++ dvdinput_setup_builtin(ctx->priv, &ctx->logcb);
++ ctx->rd = DVDOpenPath(ppath);
++ if (!ctx->rd)
++ {
++ free(ctx);
++ return NULL;
++ }
++ return ctx;
++ }
++
++ // create the internal filesystem
+ ctx->fs = InitInternalFilesystem();
+ if (!ctx->fs)
+ {
+@@ -629,25 +645,31 @@ DVDOpen_error:
+
+ dvd_reader_t *DVDOpen( const char *ppath )
+ {
+- return DVDOpenCommon( NULL, NULL, ppath, NULL );
++ return DVDOpenCommon( NULL, NULL, ppath, NULL, NULL );
+ }
+
+ dvd_reader_t *DVDOpenStream( void *stream,
+ dvd_reader_stream_cb *stream_cb )
+ {
+- return DVDOpenCommon( stream, NULL, NULL, stream_cb );
++ return DVDOpenCommon( stream, NULL, NULL, stream_cb, NULL );
+ }
+
+ dvd_reader_t *DVDOpen2( void *priv, const dvd_logger_cb *logcb,
+ const char *ppath )
+ {
+- return DVDOpenCommon( priv, logcb, ppath, NULL );
++ return DVDOpenCommon( priv, logcb, ppath, NULL, NULL );
+ }
+
+ dvd_reader_t *DVDOpenStream2( void *priv, const dvd_logger_cb *logcb,
+ dvd_reader_stream_cb *stream_cb )
+ {
+- return DVDOpenCommon( priv, logcb, NULL, stream_cb );
++ return DVDOpenCommon( priv, logcb, NULL, stream_cb, NULL );
++}
++
++dvd_reader_t *DVDOpenFiles( void *priv, const dvd_logger_cb *logcb,
++ const char *ppath, dvd_reader_filesystem_h *fs)
++{
++ return DVDOpenCommon( priv, logcb, ppath, NULL, fs);
+ }
+
+ void DVDClose( dvd_reader_t *dvd )
+diff --git a/libdvdread-embedded/src/dvdread/dvd_reader.h b/libdvdread-embedded/src/dvdread/dvd_reader.h
+index 5b15704..40ddaf2 100644
+--- a/libdvdread-embedded/src/dvdread/dvd_reader.h
++++ b/libdvdread-embedded/src/dvdread/dvd_reader.h
+@@ -153,6 +153,19 @@ dvd_reader_t *DVDOpenStream( void *, dvd_reader_stream_cb * );
+ dvd_reader_t *DVDOpen2( void *, const dvd_logger_cb *, const char * );
+ dvd_reader_t *DVDOpenStream2( void *, const dvd_logger_cb *, dvd_reader_stream_cb * );
+
++/**
++ * Open unencrypted DVD files providing the respective filesystem implementation
++ * Useful to open files located on virtual file systems
++ *
++ * @param path Specifies the file or directory to use
++ * @param priv is a private handle
++ * @param logcb is a custom logger callback struct, or NULL if none needed
++ * @param fs is a struct containing the filesystem implementation
++ * @return If successful a read handle is returned. Otherwise 0 is returned.
++ *
++ */
++dvd_reader_t *DVDOpenFiles( void *priv, const dvd_logger_cb *logcb, const char * path, dvd_reader_filesystem_h *fs);
++
+ /**
+ * Closes and cleans up the DVD reader object.
+ *
+--
+2.35.1
+
diff --git a/debian/patches/libdvdread/debian-0001-libdvdcss.patch b/debian/patches/libdvdread/debian-0001-libdvdcss.patch
new file mode 100644
index 0000000..3b30917
--- /dev/null
+++ b/debian/patches/libdvdread/debian-0001-libdvdcss.patch
@@ -0,0 +1,25 @@
+From: Daniel Baumann <mail@daniel-baumann.ch>
+Date: Tue, 22 Apr 2014 11:14:26 +0200
+Subject: libdvdcss
+Forwarded: not-needed
+
+Print information about the CSS README.
+---
+ libdvdread-embedded/src/dvd_input.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/libdvdread-embedded/src/dvd_input.c b/libdvdread-embedded/src/dvd_input.c
+index 0c44387..f640020 100644
+--- a/libdvdread-embedded/src/dvd_input.c
++++ b/libdvdread-embedded/src/dvd_input.c
+@@ -477,8 +477,8 @@ int dvdinput_setup(void *priv, dvd_logger_cb *logcb, unsigned char stream_mode)
+
+ } else {
+ DVDReadLog(priv, logcb, DVD_LOGGER_LEVEL_WARN,
+- "Encrypted DVD support unavailable.");
+-
++ "Encrypted DVD support unavailable. No css library available. "
++ "See /usr/share/doc/libdvdread4/README.css for more information");
+ dvdinput_setup_builtin(priv, logcb);
+ return 0;
+ }
diff --git a/debian/patches/libdvdread/debian-0002-descriptor.patch b/debian/patches/libdvdread/debian-0002-descriptor.patch
new file mode 100644
index 0000000..a61d4f4
--- /dev/null
+++ b/debian/patches/libdvdread/debian-0002-descriptor.patch
@@ -0,0 +1,104 @@
+From: Mario Holbe <mario.holbe@tu-ilmenau.de>
+Date: Tue, 22 Apr 2014 11:49:42 +0200
+Subject: descriptor
+Forwarded: not-needed
+
+libdvdread is very likely to fail on discs/images that store their File
+System Descriptor at the end of the disc/image rather than at the
+beginning. This is due to the "strategy" libdvdread uses to find it:
+libdvdread scans sequentially from the beginning of the disc/image for
+the File System Descriptor and identifies it by a single byte tag.
+
+Aside from wasting lots of time on discs/images that store their File
+System Descriptor at the end there is quite a good chance to stumble
+across a random data block that accidentally starts with this tag (and
+failing on it) before finding the real File System Descriptor.
+
+As far as I can see, at least CDBurnerXP seems to (be able to) create
+such images - at least if my interpretation of the Implementation
+Identifier "NMS DVDProLib" is correct.
+
+This... well, let's call it ugly hack fixes this by obtaining
+the File System Descriptor location from the Logical Volume Descriptor
+
+Closes: #663512
+---
+ libdvdread-embedded/src/dvd_udf.c | 37 ++++++++++++++++++++++++++++++++++---
+ 1 file changed, 34 insertions(+), 3 deletions(-)
+
+diff --git a/libdvdread-embedded/src/dvd_udf.c b/libdvdread-embedded/src/dvd_udf.c
+index 41517fa..7b22b43 100644
+--- a/libdvdread-embedded/src/dvd_udf.c
++++ b/libdvdread-embedded/src/dvd_udf.c
+@@ -82,6 +82,8 @@ struct Partition {
+ uint32_t AccessType;
+ uint32_t Start;
+ uint32_t Length;
++ uint32_t FSD_Location;
++ uint32_t FSD_Length;
+ };
+
+ struct AD {
+@@ -101,6 +103,12 @@ struct avdp_t {
+ struct extent_ad rvds;
+ };
+
++struct fsd_t {
++ uint16_t Partition;
++ uint32_t Location;
++ uint32_t Length;
++};
++
+ struct pvd_t {
+ uint8_t VolumeIdentifier[32];
+ uint8_t VolumeSetIdentifier[128];
+@@ -427,6 +435,16 @@ static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
+ return 0;
+ }
+
++/**
++ * Reads the File Set Descriptor from the Logical Volume Descriptor.
++ */
++static void UDFFSD( uint8_t *data, struct fsd_t *fsd )
++{
++ fsd->Length = GETN4(248); /* always 2048? */
++ fsd->Location = GETN4(252);
++ fsd->Partition = GETN2(256); /* always 0? */
++}
++
+ static int UDFFileEntry( uint8_t *data, uint8_t *FileType,
+ struct Partition *partition, struct AD *ad )
+ {
+@@ -801,8 +819,18 @@ static int UDFFindPartition( dvd_reader_t *ctx, int partnum,
+ /* Logical Volume Descriptor */
+ if( UDFLogVolume( LogBlock ) ) {
+ /* TODO: sector size wrong! */
+- } else
+- volvalid = 1;
++ } else {
++ struct fsd_t fsd;
++
++ UDFFSD(LogBlock, &fsd);
++ if(part->Number == fsd.Partition) {
++ part->FSD_Location = fsd.Location;
++ part->FSD_Length = fsd.Length;
++ volvalid = 1;
++ } else {
++ /* TODO: Oups, how to handle this? */
++ }
++ }
+ }
+
+ } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+@@ -845,7 +873,10 @@ uint32_t UDFFindFile( dvd_reader_t *ctx, const char *filename,
+ SetUDFCache(ctx, PartitionCache, 0, &partition);
+
+ /* Find root dir ICB */
+- lbnum = partition.Start;
++ lbnum = partition.Start + partition.FSD_Location;
++ /*
++ fprintf(stderr, "Looking for FSD at 0x%x\n", lbnum);
++ */
+ do {
+ ret = DVDReadLBUDF( ctx, lbnum++, 1, LogBlock, 0 );
+ if( ret < 0 ) {
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..3b63e5d
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,28 @@
+kodi/0001-Implement-hashes-using-Libgcrypt.patch
+kodi/0002-Find-and-link-with-Libgcrypt.patch
+kodi/0003-differentiate-from-vanilla-Kodi.patch
+kodi/0004-use-system-groovy.patch
+kodi/0005-fix-tests.patch
+kodi/0006-dont-use-openssl.patch
+kodi/0007-support-omitting-addons-service.patch
+kodi/0008-Find-test-fixtures-in-source-directory.patch
+kodi/0009-Skip-long-time-broken-test.patch
+kodi/0010-Disable-flaky-TestMassEvent.General-and-TestMassEven.patch
+kodi/0011-Skip-checking-errno-against-ENOENT-because-this-test.patch
+kodi/0012-The-baseline-of-the-i386-port-does-not-include-SSE.patch
+kodi/0013-Disable-GetCPUFrequency-test.patch
+kodi/0014-Fix-C++-example-includes.patch
+kodi/0015-debian-cross-compile.patch
+kodi/0016-ports-architectures.patch
+libdvdnav/0001-libdvdnav-PR48-enen92.patch
+libdvdread/0001-libdvdread-PR40-enen92.patch
+libdvdread/debian-0001-libdvdcss.patch
+libdvdread/debian-0002-descriptor.patch
+cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch
+workarounds/0001-Workaround-989814.patch
+workarounds/0002-ffmpeg5.patch
+workarounds/0003-xbmc-libdvd_vfs-enen92.patch
+workarounds/0004-ffmpeg6.patch
+workarounds/0005-pcre2.patch
+workarounds/0006-loongarch.patch
+workarounds/0007-swig.patch
diff --git a/debian/patches/workarounds/0001-Workaround-989814.patch b/debian/patches/workarounds/0001-Workaround-989814.patch
new file mode 100644
index 0000000..aac9c94
--- /dev/null
+++ b/debian/patches/workarounds/0001-Workaround-989814.patch
@@ -0,0 +1,71 @@
+From 82880587b3a578d61e8335199f316e6794750ba6 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Fri, 25 Jun 2021 01:37:02 +0000
+Subject: [PATCH 1/2] Check if applied locale correctly lowers chars and
+ fallback
+
+.. to default region if it does not.
+
+Fixes #19883.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/LangInfo.cpp | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/xbmc/LangInfo.cpp b/xbmc/LangInfo.cpp
+index 24f0419cfe..1c93ebf440 100644
+--- a/xbmc/LangInfo.cpp
++++ b/xbmc/LangInfo.cpp
+@@ -981,6 +981,18 @@ void CLangInfo::SetCurrentRegion(const std::string& strName)
+
+ m_currentRegion->SetGlobalLocale();
+
++ // Check if locale is affected by "Turkish I"
++ // See https://github.com/xbmc/xbmc/issue/19883 for details
++ if (std::tolower('i') != std::tolower('I'))
++ {
++ CLog::Log(
++ LOGWARNING,
++ "region '{}' is affected by 'Turkish I' problem - falling back to default region '{}'",
++ m_currentRegion->m_strName, m_defaultRegion.m_strName);
++ m_currentRegion = &m_defaultRegion;
++ m_currentRegion->SetGlobalLocale();
++ }
++
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (settings->GetString(CSettings::SETTING_LOCALE_SHORTDATEFORMAT) == SETTING_REGIONAL_DEFAULT)
+ SetShortDateFormat(m_currentRegion->m_strDateFormatShort);
+--
+2.33.0
+
+
+From 2be9ed286e1478c7b1a3002242330b6101492621 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Sun, 27 Jun 2021 19:31:39 +0000
+Subject: [PATCH 2/2] kodi.sh.in: Unset LC_{ALL,CTYPE}, LANG
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ tools/Linux/kodi.sh.in | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/tools/Linux/kodi.sh.in b/tools/Linux/kodi.sh.in
+index 108c0b007b..b148fd49ee 100644
+--- a/tools/Linux/kodi.sh.in
++++ b/tools/Linux/kodi.sh.in
+@@ -171,6 +171,11 @@ if command_exists gdb; then
+ fi
+ fi
+
++
++# Check if locale is affected by "Turkish I"
++# See https://github.com/xbmc/xbmc/issue/19883 for details
++unset LC_CTYPE LC_ALL LANG
++
+ LOOP=1
+ while [ $(( $LOOP )) = "1" ]
+ do
+--
+2.33.0
+
diff --git a/debian/patches/workarounds/0002-ffmpeg5.patch b/debian/patches/workarounds/0002-ffmpeg5.patch
new file mode 100644
index 0000000..64c31d2
--- /dev/null
+++ b/debian/patches/workarounds/0002-ffmpeg5.patch
@@ -0,0 +1,2429 @@
+From 2218c1c37c16df4ce3da34c8566f5e1335a66c01 Mon Sep 17 00:00:00 2001
+From: Alwin Esch <alwin.esch@web.de>
+Date: Sun, 10 Jul 2022 18:59:52 +0200
+Subject: [PATCH 1/3] FFmpeg5 port
+
+---
+ xbmc/cdrip/EncoderFFmpeg.cpp | 95 ++++++---------
+ xbmc/cdrip/EncoderFFmpeg.h | 1 -
+ .../AudioEngine/Encoders/AEEncoderFFmpeg.cpp | 111 +++++++++++-------
+ .../AudioEngine/Engines/ActiveAE/ActiveAE.cpp | 14 +--
+ xbmc/cores/FFmpeg.cpp | 14 ++-
+ xbmc/cores/FFmpeg.h | 26 ++++
+ .../DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp | 12 +-
+ .../Overlay/DVDOverlayCodecFFmpeg.cpp | 3 +-
+ .../DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp | 36 +++++-
+ .../DVDCodecs/Video/DVDVideoPPFFmpeg.cpp | 5 +
+ .../VideoPlayer/DVDCodecs/Video/VAAPI.cpp | 10 +-
+ .../DVDDemuxers/DVDDemuxClient.cpp | 3 +-
+ .../DVDDemuxers/DVDDemuxFFmpeg.cpp | 71 +++++++++--
+ .../DVDInputStreams/InputStreamAddon.cpp | 3 +-
+ xbmc/filesystem/AudioBookFileDirectory.cpp | 3 +-
+ xbmc/guilib/FFmpegImage.cpp | 6 +-
+ xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp | 2 +-
+ xbmc/video/tags/VideoTagLoaderFFmpeg.cpp | 2 +-
+ 18 files changed, 281 insertions(+), 136 deletions(-)
+
+diff --git a/xbmc/cdrip/EncoderFFmpeg.cpp b/xbmc/cdrip/EncoderFFmpeg.cpp
+index 2867b7cb67..4c0628b5cc 100644
+--- a/xbmc/cdrip/EncoderFFmpeg.cpp
++++ b/xbmc/cdrip/EncoderFFmpeg.cpp
+@@ -11,6 +11,7 @@
+ #include "ServiceBroker.h"
+ #include "addons/Addon.h"
+ #include "addons/AddonManager.h"
++#include "cores/FFmpeg.h"
+ #include "settings/Settings.h"
+ #include "settings/SettingsComponent.h"
+ #include "utils/StringUtils.h"
+@@ -19,23 +20,8 @@
+ #include "utils/log.h"
+
+ using namespace KODI::CDRIP;
+-
+-namespace
+-{
+-
+-struct EncoderException : public std::exception
+-{
+- std::string s;
+- template<typename... Args>
+- EncoderException(const std::string& fmt, Args&&... args)
+- : s(StringUtils::Format(fmt, std::forward<Args>(args)...))
+- {
+- }
+- ~EncoderException() throw() {} // Updated
+- const char* what() const throw() { return s.c_str(); }
+-};
+-
+-} /* namespace */
++using FFMPEG_HELP_TOOLS::FFMpegErrorToString;
++using FFMPEG_HELP_TOOLS::FFMpegException;
+
+ bool CEncoderFFmpeg::Init()
+ {
+@@ -54,7 +40,7 @@ bool CEncoderFFmpeg::Init()
+ }
+ else
+ {
+- throw EncoderException("Could not get add-on: {}", addonId);
++ throw FFMpegException("Could not get add-on: {}", addonId);
+ }
+
+ // Hack fix about PTS on generated files.
+@@ -66,45 +52,45 @@ bool CEncoderFFmpeg::Init()
+ else if (addonId == "audioencoder.kodi.builtin.wma")
+ m_samplesCountMultiply = 1000;
+ else
+- throw EncoderException("Internal add-on id \"{}\" not known as usable", addonId);
++ throw FFMpegException("Internal add-on id \"{}\" not known as usable", addonId);
+
+ const std::string filename = URIUtils::GetFileName(m_strFile);
+
+ m_formatCtx = avformat_alloc_context();
+ if (!m_formatCtx)
+- throw EncoderException("Could not allocate output format context");
++ throw FFMpegException("Could not allocate output format context");
+
+ m_bcBuffer = static_cast<uint8_t*>(av_malloc(BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE));
+ if (!m_bcBuffer)
+- throw EncoderException("Could not allocate buffer");
++ throw FFMpegException("Could not allocate buffer");
+
+ m_formatCtx->pb = avio_alloc_context(m_bcBuffer, BUFFER_SIZE, AVIO_FLAG_WRITE, this, nullptr,
+ avio_write_callback, avio_seek_callback);
+ if (!m_formatCtx->pb)
+- throw EncoderException("Failed to allocate ByteIOContext");
++ throw FFMpegException("Failed to allocate ByteIOContext");
+
+ /* Guess the desired container format based on the file extension. */
+ m_formatCtx->oformat = av_guess_format(nullptr, filename.c_str(), nullptr);
+ if (!m_formatCtx->oformat)
+- throw EncoderException("Could not find output file format");
++ throw FFMpegException("Could not find output file format");
+
+ m_formatCtx->url = av_strdup(filename.c_str());
+ if (!m_formatCtx->url)
+- throw EncoderException("Could not allocate url");
++ throw FFMpegException("Could not allocate url");
+
+ /* Find the encoder to be used by its name. */
+- AVCodec* codec = avcodec_find_encoder(m_formatCtx->oformat->audio_codec);
++ FFMPEG_FMT_CONST AVCodec* codec = avcodec_find_encoder(m_formatCtx->oformat->audio_codec);
+ if (!codec)
+- throw EncoderException("Unable to find a suitable FFmpeg encoder");
++ throw FFMpegException("Unable to find a suitable FFmpeg encoder");
+
+ /* Create a new audio stream in the output file container. */
+ m_stream = avformat_new_stream(m_formatCtx, nullptr);
+ if (!m_stream)
+- throw EncoderException("Failed to allocate AVStream context");
++ throw FFMpegException("Failed to allocate AVStream context");
+
+ m_codecCtx = avcodec_alloc_context3(codec);
+ if (!m_codecCtx)
+- throw EncoderException("Failed to allocate the encoder context");
++ throw FFMpegException("Failed to allocate the encoder context");
+
+ /* Set the basic encoder parameters.
+ * The input file's sample rate is used to avoid a sample rate conversion. */
+@@ -128,14 +114,14 @@ bool CEncoderFFmpeg::Init()
+
+ int err = avcodec_open2(m_codecCtx, codec, nullptr);
+ if (err < 0)
+- throw EncoderException("Failed to open the codec {} (error '{}')",
+- codec->long_name ? codec->long_name : codec->name,
+- FFmpegErrorToString(err));
++ throw FFMpegException("Failed to open the codec {} (error '{}')",
++ codec->long_name ? codec->long_name : codec->name,
++ FFMpegErrorToString(err));
+
+ err = avcodec_parameters_from_context(m_stream->codecpar, m_codecCtx);
+ if (err < 0)
+- throw EncoderException("Failed to copy encoder parameters to output stream (error '{}')",
+- FFmpegErrorToString(err));
++ throw FFMpegException("Failed to copy encoder parameters to output stream (error '{}')",
++ FFMpegErrorToString(err));
+
+ m_inFormat = GetInputFormat(m_iInBitsPerSample);
+ m_outFormat = m_codecCtx->sample_fmt;
+@@ -150,7 +136,7 @@ bool CEncoderFFmpeg::Init()
+
+ m_bufferFrame = av_frame_alloc();
+ if (!m_bufferFrame || !m_buffer)
+- throw EncoderException("Failed to allocate necessary buffers");
++ throw FFMpegException("Failed to allocate necessary buffers");
+
+ m_bufferFrame->nb_samples = m_codecCtx->frame_size;
+ m_bufferFrame->format = m_inFormat;
+@@ -159,8 +145,8 @@ bool CEncoderFFmpeg::Init()
+
+ err = av_frame_get_buffer(m_bufferFrame, 0);
+ if (err < 0)
+- throw EncoderException("Could not allocate output frame samples (error '{}')",
+- FFmpegErrorToString(err));
++ throw FFMpegException("Could not allocate output frame samples (error '{}')",
++ FFMpegErrorToString(err));
+
+ avcodec_fill_audio_frame(m_bufferFrame, m_iInChannels, m_inFormat, m_buffer, m_neededBytes, 0);
+
+@@ -170,14 +156,14 @@ bool CEncoderFFmpeg::Init()
+ m_codecCtx->sample_rate, m_codecCtx->channel_layout, m_inFormat,
+ m_codecCtx->sample_rate, 0, nullptr);
+ if (!m_swrCtx || swr_init(m_swrCtx) < 0)
+- throw EncoderException("Failed to initialize the resampler");
++ throw FFMpegException("Failed to initialize the resampler");
+
+ m_resampledBufferSize =
+ av_samples_get_buffer_size(nullptr, m_iInChannels, m_neededFrames, m_outFormat, 0);
+ m_resampledBuffer = static_cast<uint8_t*>(av_malloc(m_resampledBufferSize));
+ m_resampledFrame = av_frame_alloc();
+ if (!m_resampledBuffer || !m_resampledFrame)
+- throw EncoderException("Failed to allocate a frame for resampling");
++ throw FFMpegException("Failed to allocate a frame for resampling");
+
+ m_resampledFrame->nb_samples = m_neededFrames;
+ m_resampledFrame->format = m_outFormat;
+@@ -186,8 +172,8 @@ bool CEncoderFFmpeg::Init()
+
+ err = av_frame_get_buffer(m_resampledFrame, 0);
+ if (err < 0)
+- throw EncoderException("Could not allocate output resample frame samples (error '{}')",
+- FFmpegErrorToString(err));
++ throw FFMpegException("Could not allocate output resample frame samples (error '{}')",
++ FFMpegErrorToString(err));
+
+ avcodec_fill_audio_frame(m_resampledFrame, m_iInChannels, m_outFormat, m_resampledBuffer,
+ m_resampledBufferSize, 0);
+@@ -204,7 +190,7 @@ bool CEncoderFFmpeg::Init()
+ /* write the header */
+ err = avformat_write_header(m_formatCtx, nullptr);
+ if (err != 0)
+- throw EncoderException("Failed to write the header (error '{}')", FFmpegErrorToString(err));
++ throw FFMpegException("Failed to write the header (error '{}')", FFMpegErrorToString(err));
+
+ CLog::Log(LOGDEBUG, "CEncoderFFmpeg::{} - Successfully initialized with muxer {} and codec {}",
+ __func__,
+@@ -212,7 +198,7 @@ bool CEncoderFFmpeg::Init()
+ : m_formatCtx->oformat->name,
+ codec->long_name ? codec->long_name : codec->name);
+ }
+- catch (EncoderException& caught)
++ catch (const FFMpegException& caught)
+ {
+ CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what());
+
+@@ -299,7 +285,7 @@ bool CEncoderFFmpeg::WriteFrame()
+ if (swr_convert(m_swrCtx, m_resampledFrame->data, m_neededFrames,
+ const_cast<const uint8_t**>(m_bufferFrame->extended_data),
+ m_neededFrames) < 0)
+- throw EncoderException("Error resampling audio");
++ throw FFMpegException("Error resampling audio");
+
+ frame = m_resampledFrame;
+ }
+@@ -316,8 +302,8 @@ bool CEncoderFFmpeg::WriteFrame()
+ m_bufferSize = 0;
+ err = avcodec_send_frame(m_codecCtx, frame);
+ if (err < 0)
+- throw EncoderException("Error sending a frame for encoding (error '{}')", __func__,
+- FFmpegErrorToString(err));
++ throw FFMpegException("Error sending a frame for encoding (error '{}')",
++ FFMpegErrorToString(err));
+
+ while (err >= 0)
+ {
+@@ -329,19 +315,18 @@ bool CEncoderFFmpeg::WriteFrame()
+ }
+ else if (err < 0)
+ {
+- throw EncoderException("Error during encoding (error '{}')", __func__,
+- FFmpegErrorToString(err));
++ throw FFMpegException("Error during encoding (error '{}')", FFMpegErrorToString(err));
+ }
+
+ err = av_write_frame(m_formatCtx, pkt);
+ if (err < 0)
+- throw EncoderException("Failed to write the frame data (error '{}')", __func__,
+- FFmpegErrorToString(err));
++ throw FFMpegException("Failed to write the frame data (error '{}')",
++ FFMpegErrorToString(err));
+
+ av_packet_unref(pkt);
+ }
+ }
+- catch (EncoderException& caught)
++ catch (const FFMpegException& caught)
+ {
+ CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what());
+ }
+@@ -400,14 +385,6 @@ AVSampleFormat CEncoderFFmpeg::GetInputFormat(int inBitsPerSample)
+ case 32:
+ return AV_SAMPLE_FMT_S32;
+ default:
+- throw EncoderException("Invalid input bits per sample");
++ throw FFMpegException("Invalid input bits per sample");
+ }
+ }
+-
+-std::string CEncoderFFmpeg::FFmpegErrorToString(int err)
+-{
+- std::string text;
+- text.reserve(AV_ERROR_MAX_STRING_SIZE);
+- av_strerror(err, text.data(), AV_ERROR_MAX_STRING_SIZE);
+- return text;
+-}
+diff --git a/xbmc/cdrip/EncoderFFmpeg.h b/xbmc/cdrip/EncoderFFmpeg.h
+index 39112ba3ba..0c2391c7ab 100644
+--- a/xbmc/cdrip/EncoderFFmpeg.h
++++ b/xbmc/cdrip/EncoderFFmpeg.h
+@@ -39,7 +39,6 @@ private:
+ void SetTag(const std::string& tag, const std::string& value);
+ bool WriteFrame();
+ AVSampleFormat GetInputFormat(int inBitsPerSample);
+- std::string FFmpegErrorToString(int err);
+
+ AVFormatContext* m_formatCtx{nullptr};
+ AVCodecContext* m_codecCtx{nullptr};
+diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+index 09bb26a327..86f65f57f3 100644
+--- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
++++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+@@ -10,13 +10,24 @@
+ #define DTS_ENCODE_BITRATE 1411200
+
+ #include "cores/AudioEngine/Encoders/AEEncoderFFmpeg.h"
+-#include "cores/AudioEngine/Utils/AEUtil.h"
++
+ #include "ServiceBroker.h"
+-#include "utils/log.h"
++#include "cores/AudioEngine/Utils/AEUtil.h"
++#include "cores/FFmpeg.h"
+ #include "settings/Settings.h"
+ #include "settings/SettingsComponent.h"
+-#include <string.h>
++#include "utils/log.h"
++
++extern "C"
++{
++#include <libavutil/channel_layout.h>
++}
++
+ #include <cassert>
++#include <string.h>
++
++using FFMPEG_HELP_TOOLS::FFMpegErrorToString;
++using FFMPEG_HELP_TOOLS::FFMpegException;
+
+ CAEEncoderFFmpeg::CAEEncoderFFmpeg() : m_CodecCtx(NULL), m_SwrCtx(NULL)
+ {
+@@ -81,7 +92,7 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format, bool allow_planar_input
+
+ bool ac3 = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_AUDIOOUTPUT_AC3PASSTHROUGH);
+
+- AVCodec *codec = NULL;
++ FFMPEG_FMT_CONST AVCodec* codec = nullptr;
+
+ /* fallback to ac3 if we support it, we might not have DTS support */
+ if (ac3)
+@@ -242,63 +253,77 @@ unsigned int CAEEncoderFFmpeg::GetFrames()
+
+ int CAEEncoderFFmpeg::Encode(uint8_t *in, int in_size, uint8_t *out, int out_size)
+ {
+- int got_output;
+- AVFrame *frame;
++ int size = 0;
++ int err = AVERROR_UNKNOWN;
++ AVFrame* frame = nullptr;
++ AVPacket* pkt = nullptr;
+
+ if (!m_CodecCtx)
+- return 0;
+-
+- /* allocate the input frame
+- * sadly, we have to alloc/dealloc it everytime since we have no guarantee the
+- * data argument will be constant over iterated calls and the frame needs to
+- * setup pointers inside data */
+- frame = av_frame_alloc();
+- if (!frame)
+- return 0;
++ return size;
+
+- frame->nb_samples = m_CodecCtx->frame_size;
+- frame->format = m_CodecCtx->sample_fmt;
+- frame->channel_layout = m_CodecCtx->channel_layout;
+- frame->channels = m_CodecCtx->channels;
++ try
++ {
++ /* allocate the input frame and output packet
++ * sadly, we have to alloc/dealloc it everytime since we have no guarantee the
++ * data argument will be constant over iterated calls and the frame needs to
++ * setup pointers inside data */
++ frame = av_frame_alloc();
++ pkt = av_packet_alloc();
++ if (!frame || !pkt)
++ throw FFMpegException(
++ "Failed to allocate \"AVFrame\" or \"AVPacket\" for encoding (error '{}')",
++ strerror(errno));
++
++ frame->nb_samples = m_CodecCtx->frame_size;
++ frame->format = m_CodecCtx->sample_fmt;
++ frame->channel_layout = m_CodecCtx->channel_layout;
++ frame->channels = m_CodecCtx->channels;
++
++ avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt, in, in_size, 0);
++
++ pkt->size = out_size;
++ pkt->data = out;
++
++ /* encode it */
++ err = avcodec_send_frame(m_CodecCtx, frame);
++ if (err < 0)
++ throw FFMpegException("Error sending a frame for encoding (error '{}')",
++ FFMpegErrorToString(err));
++
++ while (err >= 0)
++ {
++ err = avcodec_receive_packet(m_CodecCtx, pkt);
++ if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
++ {
++ av_frame_free(&frame);
++ av_packet_free(&pkt);
++ return (err == AVERROR(EAGAIN)) ? -1 : 0;
++ }
++ else if (err < 0)
++ {
++ throw FFMpegException("Error during encoding (error '{}')", FFMpegErrorToString(err));
++ }
+
+- avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt,
+- in, in_size, 0);
++ av_packet_unref(pkt);
++ }
+
+- /* initialize the output packet */
+- AVPacket* pkt = av_packet_alloc();
+- if (!pkt)
++ size = pkt->size;
++ }
++ catch (const FFMpegException& caught)
+ {
+- CLog::Log(LOGERROR, "CAEEncoderFFmpeg::{} - av_packet_alloc failed: {}", __FUNCTION__,
+- strerror(errno));
+- av_frame_free(&frame);
+- return 0;
++ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::{} - {}", __func__, caught.what());
+ }
+
+- pkt->size = out_size;
+- pkt->data = out;
+-
+- /* encode it */
+- int ret = avcodec_encode_audio2(m_CodecCtx, pkt, frame, &got_output);
+-
+- int size = pkt->size;
+-
+ /* free temporary data */
+ av_frame_free(&frame);
+
+ /* free the packet */
+ av_packet_free(&pkt);
+
+- if (ret < 0 || !got_output)
+- {
+- CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Encoding failed");
+- return 0;
+- }
+-
+ /* return the number of frames used */
+ return size;
+ }
+
+-
+ int CAEEncoderFFmpeg::GetData(uint8_t **data)
+ {
+ int size;
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
+index 3947dd5523..6000fe9c63 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
+@@ -16,17 +16,17 @@ using namespace ActiveAE;
+ #include "ActiveAESound.h"
+ #include "ActiveAEStream.h"
+ #include "ServiceBroker.h"
++#include "cores/AudioEngine/AEResampleFactory.h"
++#include "cores/AudioEngine/Encoders/AEEncoderFFmpeg.h"
+ #include "cores/AudioEngine/Interfaces/IAudioCallback.h"
+-#include "cores/AudioEngine/Utils/AEUtil.h"
+ #include "cores/AudioEngine/Utils/AEStreamData.h"
+ #include "cores/AudioEngine/Utils/AEStreamInfo.h"
+-#include "cores/AudioEngine/AEResampleFactory.h"
+-#include "cores/AudioEngine/Encoders/AEEncoderFFmpeg.h"
+-
++#include "cores/AudioEngine/Utils/AEUtil.h"
++#include "cores/FFmpeg.h"
+ #include "settings/Settings.h"
+ #include "settings/SettingsComponent.h"
+-#include "windowing/WinSystem.h"
+ #include "utils/log.h"
++#include "windowing/WinSystem.h"
+
+ using namespace std::chrono_literals;
+
+@@ -3016,8 +3016,8 @@ IAE::SoundPtr CActiveAE::MakeSound(const std::string& file)
+ AVFormatContext *fmt_ctx = nullptr;
+ AVCodecContext *dec_ctx = nullptr;
+ AVIOContext *io_ctx;
+- AVInputFormat *io_fmt = nullptr;
+- AVCodec *dec = nullptr;
++ FFMPEG_FMT_CONST AVInputFormat* io_fmt = nullptr;
++ FFMPEG_FMT_CONST AVCodec* dec = nullptr;
+ SampleConfig config;
+
+ // No custom deleter until sound is registered
+diff --git a/xbmc/cores/FFmpeg.cpp b/xbmc/cores/FFmpeg.cpp
+index 03a29f7db4..de1765ed52 100644
+--- a/xbmc/cores/FFmpeg.cpp
++++ b/xbmc/cores/FFmpeg.cpp
+@@ -21,6 +21,19 @@
+
+ static thread_local CFFmpegLog* CFFmpegLogTls;
+
++namespace FFMPEG_HELP_TOOLS
++{
++
++std::string FFMpegErrorToString(int err)
++{
++ std::string text;
++ text.resize(AV_ERROR_MAX_STRING_SIZE);
++ av_strerror(err, text.data(), AV_ERROR_MAX_STRING_SIZE);
++ return text;
++}
++
++} // namespace FFMPEG_HELP_TOOLS
++
+ void CFFmpegLog::SetLogLevel(int level)
+ {
+ CFFmpegLog::ClearLogLevel();
+@@ -116,4 +129,3 @@ void ff_avutil_log(void* ptr, int level, const char* format, va_list va)
+ }
+ buffer.erase(0, start);
+ }
+-
+diff --git a/xbmc/cores/FFmpeg.h b/xbmc/cores/FFmpeg.h
+index e1194d19e9..8230701ba7 100644
+--- a/xbmc/cores/FFmpeg.h
++++ b/xbmc/cores/FFmpeg.h
+@@ -10,6 +10,7 @@
+
+ #include "ServiceBroker.h"
+ #include "utils/CPUInfo.h"
++#include "utils/StringUtils.h"
+
+ extern "C" {
+ #include <libavcodec/avcodec.h>
+@@ -21,6 +22,31 @@ extern "C" {
+ #include <libpostproc/postprocess.h>
+ }
+
++// https://github.com/FFmpeg/FFmpeg/blob/56450a0ee4/doc/APIchanges#L18-L26
++#if LIBAVFORMAT_BUILD >= AV_VERSION_INT(59, 0, 100)
++#define FFMPEG_FMT_CONST const
++#else
++#define FFMPEG_FMT_CONST
++#endif
++
++namespace FFMPEG_HELP_TOOLS
++{
++
++struct FFMpegException : public std::exception
++{
++ std::string s;
++ template<typename... Args>
++ FFMpegException(const std::string& fmt, Args&&... args)
++ : s(StringUtils::Format(fmt, std::forward<Args>(args)...))
++ {
++ }
++ const char* what() const noexcept override { return s.c_str(); }
++};
++
++std::string FFMpegErrorToString(int err);
++
++} // namespace FFMPEG_HELP_TOOLS
++
+ inline int PPCPUFlags()
+ {
+ unsigned int cpuFeatures = CServiceBroker::GetCPUInfo()->GetCPUFeatures();
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+index 25f3f08e1f..87e7ae2c57 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+@@ -7,14 +7,16 @@
+ */
+
+ #include "DVDAudioCodecFFmpeg.h"
+-#include "ServiceBroker.h"
++
+ #include "../../DVDStreamInfo.h"
+-#include "utils/log.h"
++#include "DVDCodecs/DVDCodecs.h"
++#include "ServiceBroker.h"
++#include "cores/AudioEngine/Utils/AEUtil.h"
++#include "cores/FFmpeg.h"
+ #include "settings/AdvancedSettings.h"
+ #include "settings/Settings.h"
+ #include "settings/SettingsComponent.h"
+-#include "DVDCodecs/DVDCodecs.h"
+-#include "cores/AudioEngine/Utils/AEUtil.h"
++#include "utils/log.h"
+
+ extern "C" {
+ #include <libavutil/opt.h>
+@@ -44,7 +46,7 @@ bool CDVDAudioCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
+ return false;
+ }
+
+- AVCodec* pCodec = NULL;
++ FFMPEG_FMT_CONST AVCodec* pCodec = nullptr;
+ bool allowdtshddecode = true;
+
+ // set any special options
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Overlay/DVDOverlayCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Overlay/DVDOverlayCodecFFmpeg.cpp
+index 8c26cad306..3657fc093c 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Overlay/DVDOverlayCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Overlay/DVDOverlayCodecFFmpeg.cpp
+@@ -10,6 +10,7 @@
+
+ #include "DVDOverlayImage.h"
+ #include "DVDStreamInfo.h"
++#include "cores/FFmpeg.h"
+ #include "cores/VideoPlayer/Interface/DemuxPacket.h"
+ #include "cores/VideoPlayer/Interface/TimingConstants.h"
+ #include "utils/EndianSwap.h"
+@@ -39,7 +40,7 @@ bool CDVDOverlayCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &optio
+ if (hints.codec == AV_CODEC_ID_EIA_608)
+ return false;
+
+- AVCodec* pCodec = avcodec_find_decoder(hints.codec);
++ FFMPEG_FMT_CONST AVCodec* pCodec = avcodec_find_decoder(hints.codec);
+ if (!pCodec)
+ {
+ CLog::Log(LOGDEBUG, "{} - Unable to find codec {}", __FUNCTION__, hints.codec);
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+index 881c02ec12..21b5e834b2 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+@@ -12,6 +12,7 @@
+ #include "DVDCodecs/DVDFactoryCodec.h"
+ #include "DVDStreamInfo.h"
+ #include "ServiceBroker.h"
++#include "cores/FFmpeg.h"
+ #include "cores/VideoPlayer/Interface/TimingConstants.h"
+ #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
+ #include "cores/VideoSettings.h"
+@@ -27,12 +28,13 @@
+ #include <mutex>
+
+ extern "C" {
+-#include <libavutil/opt.h>
+-#include <libavutil/mastering_display_metadata.h>
+ #include <libavfilter/avfilter.h>
+ #include <libavfilter/buffersink.h>
+ #include <libavfilter/buffersrc.h>
++#include <libavutil/mastering_display_metadata.h>
++#include <libavutil/opt.h>
+ #include <libavutil/pixdesc.h>
++#include <libavutil/video_enc_params.h>
+ }
+
+ #ifndef TARGET_POSIX
+@@ -327,7 +329,7 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
+ m_hints = hints;
+ m_options = options;
+
+- AVCodec* pCodec = nullptr;
++ FFMPEG_FMT_CONST AVCodec* pCodec = nullptr;
+
+ m_iOrientation = hints.orientation;
+
+@@ -1048,6 +1050,33 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(VideoPicture* pVideoPicture)
+ pVideoPicture->qscale_type = 0;
+
+ AVFrameSideData* sd;
++
++ // https://github.com/FFmpeg/FFmpeg/blob/991d417692/doc/APIchanges#L18-L20
++ // basilgello: AV_VIDEO_ENC_PARAMS_MPEG2 is introduced in 4.4!
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(58, 134, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 45, 100)
++ sd = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_VIDEO_ENC_PARAMS);
++ if (sd)
++ {
++ unsigned int mb_h = (m_pFrame->height + 15) / 16;
++ unsigned int mb_w = (m_pFrame->width + 15) / 16;
++ unsigned int nb_mb = mb_h * mb_w;
++ unsigned int block_idx;
++
++ auto par = reinterpret_cast<AVVideoEncParams*>(sd->data);
++ if (par->type == AV_VIDEO_ENC_PARAMS_MPEG2 && (par->nb_blocks == 0 || par->nb_blocks == nb_mb))
++ {
++ pVideoPicture->qstride = mb_w;
++ pVideoPicture->qscale_type = par->type;
++ pVideoPicture->qp_table = static_cast<int8_t*>(av_malloc(nb_mb));
++ for (block_idx = 0; block_idx < nb_mb; block_idx++)
++ {
++ AVVideoBlockParams* b = av_video_enc_params_block(par, block_idx);
++ pVideoPicture->qp_table[block_idx] = par->qp + b->delta_qp;
++ }
++ }
++ }
++#else
+ sd = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_QP_TABLE_PROPERTIES);
+ if (sd)
+ {
+@@ -1068,6 +1097,7 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(VideoPicture* pVideoPicture)
+ pVideoPicture->qscale_type = qp->type;
+ }
+ }
++#endif
+
+ pVideoPicture->pict_type = m_pFrame->pict_type;
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoPPFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoPPFFmpeg.cpp
+index a98fbb1710..3b2739cd22 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoPPFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoPPFFmpeg.cpp
+@@ -118,6 +118,11 @@ void CDVDVideoPPFFmpeg::Process(VideoPicture* pPicture)
+ m_pMode, m_pContext,
+ pSource->pict_type | pSource->qscale_type ? PP_PICT_TYPE_QP2 : 0);
+
++ // https://github.com/FFmpeg/FFmpeg/blob/991d417692/doc/APIchanges#L18-L20
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(58, 84, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 45, 100)
++ av_free(pSource->qp_table);
++#endif
+
+ pPicture->SetParams(*pSource);
+ if (pPicture->videoBuffer)
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+index 7ad891083c..eaf7b78d32 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+@@ -1259,8 +1259,16 @@ void CDecoder::ReturnRenderPicture(CVaapiRenderPicture *renderPic)
+
+ IHardwareDecoder* CDecoder::Create(CDVDStreamInfo &hint, CProcessInfo &processInfo, AVPixelFormat fmt)
+ {
+- if (fmt == AV_PIX_FMT_VAAPI_VLD && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_VIDEOPLAYER_USEVAAPI))
++ // https://github.com/FFmpeg/FFmpeg/blob/56450a0ee4fdda160f4039fc2ae33edfd27765c9/doc/APIchanges#L18-L26
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(55, 8, 0)
++#define PIX_FMT_VAAPI AV_PIX_FMT_VAAPI
++#else
++#define PIX_FMT_VAAPI AV_PIX_FMT_VAAPI_VLD
++#endif
++ if (fmt == PIX_FMT_VAAPI &&
++ CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_VIDEOPLAYER_USEVAAPI))
+ return new VAAPI::CDecoder(processInfo);
++#undef PIX_FMT_VAAPI
+
+ return nullptr;
+ }
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+index 2bdc3ea5a9..5be134e381 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+@@ -10,6 +10,7 @@
+
+ #include "DVDDemuxUtils.h"
+ #include "DVDInputStreams/DVDInputStream.h"
++#include "cores/FFmpeg.h"
+ #include "cores/VideoPlayer/Interface/TimingConstants.h"
+ #include "utils/log.h"
+
+@@ -130,7 +131,7 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+
+ if (stream->m_context == nullptr)
+ {
+- AVCodec *codec = avcodec_find_decoder(st->codec);
++ FFMPEG_FMT_CONST AVCodec* codec = avcodec_find_decoder(st->codec);
+ if (codec == nullptr)
+ {
+ CLog::Log(LOGERROR, "{} - can't find decoder", __FUNCTION__);
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+index 739bf51922..bf6f322274 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+@@ -38,6 +38,7 @@
+
+ extern "C"
+ {
++#include "libavutil/channel_layout.h"
+ #include "libavutil/pixdesc.h"
+ }
+
+@@ -235,7 +236,7 @@ bool CDVDDemuxFFmpeg::Aborted()
+
+ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool fileinfo)
+ {
+- AVInputFormat* iformat = NULL;
++ FFMPEG_FMT_CONST AVInputFormat* iformat = nullptr;
+ std::string strFile;
+ m_streaminfo = !pInput->IsRealtime() && !m_reopen;
+ m_reopen = false;
+@@ -422,9 +423,7 @@ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool
+ // is present, we assume it is PCM audio.
+ // AC3 is always wrapped in iec61937 (ffmpeg "spdif"), while DTS
+ // may be just padded.
+- AVInputFormat* iformat2;
+- iformat2 = av_find_input_format("spdif");
+-
++ FFMPEG_FMT_CONST AVInputFormat* iformat2 = av_find_input_format("spdif");
+ if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4)
+ {
+ iformat = iformat2;
+@@ -544,11 +543,14 @@ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool
+ m_streaminfo = true;
+ }
+
++ // https://github.com/FFmpeg/FFmpeg/blob/56450a0ee4/doc/APIchanges#L18-L26
++#if LIBAVFORMAT_BUILD < AV_VERSION_INT(59, 0, 100)
+ if (iformat && (strcmp(iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0))
+ {
+ if (URIUtils::IsRemote(strFile))
+ m_pFormatContext->iformat->flags |= AVFMT_NOGENSEARCH;
+ }
++#endif
+
+ // we need to know if this is matroska, avi or sup later
+ m_bMatroska = strncmp(m_pFormatContext->iformat->name, "matroska", 8) == 0; // for "matroska.webm"
+@@ -604,8 +606,11 @@ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool
+ // if format can be nonblocking, let's use that
+ m_pFormatContext->flags |= AVFMT_FLAG_NONBLOCK;
+
+- // deprecated, will be always set in future versions
++ // https://github.com/FFmpeg/FFmpeg/blob/d682ae70b4/doc/APIchanges#L18-L21
++#if LIBAVFORMAT_BUILD < AV_VERSION_INT(57, 66, 105) && \
++ LIBAVCODEC_BUILD < AV_VERSION_INT(57, 83, 101)
+ m_pFormatContext->flags |= AVFMT_FLAG_KEEP_SIDE_DATA;
++#endif
+
+ UpdateCurrentPTS();
+
+@@ -647,12 +652,24 @@ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool
+ {
+ int idx = m_pFormatContext->programs[i]->stream_index[j];
+ AVStream* st = m_pFormatContext->streams[idx];
++#if LIBAVFORMAT_BUILD >= AV_VERSION_INT(59, 3, 100)
++ // Related to https://patchwork.ffmpeg.org/project/ffmpeg/patch/20210429143825.53040-1-jamrial@gmail.com/
++ // has been replaced with AVSTREAM_EVENT_FLAG_NEW_PACKETS.
++ if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
++ (st->event_flags & AVSTREAM_EVENT_FLAG_NEW_PACKETS)) ||
++ (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate > 0))
++ {
++ nProgram = i;
++ break;
++ }
++#else
+ if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && st->codec_info_nb_frames > 0) ||
+ (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate > 0))
+ {
+ nProgram = i;
+ break;
+ }
++#endif
+ }
+ }
+ }
+@@ -1401,11 +1418,19 @@ void CDVDDemuxFFmpeg::UpdateCurrentPTS()
+ if (idx >= 0)
+ {
+ AVStream* stream = m_pFormatContext->streams[idx];
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100)
++ if (stream && m_pkt.pkt.dts != (int64_t)AV_NOPTS_VALUE)
++ {
++ double ts = ConvertTimestamp(m_pkt.pkt.dts, stream->time_base.den, stream->time_base.num);
++ m_currentPts = ts;
++ }
++#else
+ if (stream && stream->cur_dts != (int64_t)AV_NOPTS_VALUE)
+ {
+ double ts = ConvertTimestamp(stream->cur_dts, stream->time_base.den, stream->time_base.num);
+ m_currentPts = ts;
+ }
++#endif
+ }
+ }
+
+@@ -1621,7 +1646,12 @@ CDemuxStream* CDVDDemuxFFmpeg::AddStream(int streamIdx)
+ st->iBitsPerSample = pStream->codecpar->bits_per_raw_sample;
+ st->iChannelLayout = pStream->codecpar->channel_layout;
+ char buf[32] = {};
++ // https://github.com/FFmpeg/FFmpeg/blob/6ccc3989d15/doc/APIchanges#L50-L53
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 24, 100)
++ av_channel_layout_describe(st->iChannelLayout, buf, sizeof(buf));
++#else
+ av_get_channel_layout_string(buf, 31, st->iChannels, st->iChannelLayout);
++#endif
+ st->m_channelLayoutName = buf;
+ if (st->iBitsPerSample == 0)
+ st->iBitsPerSample = pStream->codecpar->bits_per_coded_sample;
+@@ -1663,6 +1693,7 @@ CDemuxStream* CDVDDemuxFFmpeg::AddStream(int streamIdx)
+ st->iFpsScale = 0;
+ }
+
++#if LIBAVFORMAT_BUILD < AV_VERSION_INT(59, 3, 100)
+ if (pStream->codec_info_nb_frames > 0 &&
+ pStream->codec_info_nb_frames <= 2 &&
+ m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD))
+@@ -1672,6 +1703,7 @@ CDemuxStream* CDVDDemuxFFmpeg::AddStream(int streamIdx)
+ st->iFpsRate = 0;
+ st->iFpsScale = 0;
+ }
++#endif
+
+ st->iWidth = pStream->codecpar->width;
+ st->iHeight = pStream->codecpar->height;
+@@ -1693,7 +1725,12 @@ CDemuxStream* CDVDDemuxFFmpeg::AddStream(int streamIdx)
+ st->colorRange = pStream->codecpar->color_range;
+ st->hdr_type = DetermineHdrType(pStream);
+
++ // https://github.com/FFmpeg/FFmpeg/blob/release/5.0/doc/APIchanges
++#if LIBAVFORMAT_BUILD >= AV_VERSION_INT(59, 10, 100)
++ size_t size = 0;
++#else
+ int size = 0;
++#endif
+ uint8_t* side_data = nullptr;
+
+ side_data = av_stream_get_side_data(pStream, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, &size);
+@@ -2103,7 +2140,7 @@ std::string CDVDDemuxFFmpeg::GetStreamCodecName(int iStreamId)
+ return strName;
+ }
+
+- AVCodec* codec = avcodec_find_decoder(stream->codec);
++ FFMPEG_FMT_CONST AVCodec* codec = avcodec_find_decoder(stream->codec);
+ if (codec)
+ strName = avcodec_get_name(codec->id);
+ }
+@@ -2279,7 +2316,7 @@ void CDVDDemuxFFmpeg::ParsePacket(AVPacket* pkt)
+
+ parser->second->m_parserCtx = av_parser_init(st->codecpar->codec_id);
+
+- AVCodec* codec = avcodec_find_decoder(st->codecpar->codec_id);
++ FFMPEG_FMT_CONST AVCodec* codec = avcodec_find_decoder(st->codecpar->codec_id);
+ if (codec == nullptr)
+ {
+ CLog::Log(LOGERROR, "{} - can't find decoder", __FUNCTION__);
+@@ -2357,7 +2394,12 @@ TRANSPORT_STREAM_STATE CDVDDemuxFFmpeg::TransportStreamAudioState()
+ {
+ if (!m_startTime)
+ {
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100)
++ m_startTime =
++ av_rescale(m_pkt.pkt.dts, st->time_base.num, st->time_base.den) - 0.000001;
++#else
+ m_startTime = av_rescale(st->cur_dts, st->time_base.num, st->time_base.den) - 0.000001;
++#endif
+ m_seekStream = idx;
+ }
+ return TRANSPORT_STREAM_STATE::READY;
+@@ -2377,7 +2419,12 @@ TRANSPORT_STREAM_STATE CDVDDemuxFFmpeg::TransportStreamAudioState()
+ {
+ if (!m_startTime)
+ {
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100)
++ m_startTime =
++ av_rescale(m_pkt.pkt.dts, st->time_base.num, st->time_base.den) - 0.000001;
++#else
+ m_startTime = av_rescale(st->cur_dts, st->time_base.num, st->time_base.den) - 0.000001;
++#endif
+ m_seekStream = i;
+ }
+ return TRANSPORT_STREAM_STATE::READY;
+@@ -2410,7 +2457,12 @@ TRANSPORT_STREAM_STATE CDVDDemuxFFmpeg::TransportStreamVideoState()
+ {
+ if (!m_startTime)
+ {
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100)
++ m_startTime =
++ av_rescale(m_pkt.pkt.dts, st->time_base.num, st->time_base.den) - 0.000001;
++#else
+ m_startTime = av_rescale(st->cur_dts, st->time_base.num, st->time_base.den) - 0.000001;
++#endif
+ m_seekStream = idx;
+ }
+ return TRANSPORT_STREAM_STATE::READY;
+@@ -2430,7 +2482,12 @@ TRANSPORT_STREAM_STATE CDVDDemuxFFmpeg::TransportStreamVideoState()
+ {
+ if (!m_startTime)
+ {
++#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100)
++ m_startTime =
++ av_rescale(m_pkt.pkt.dts, st->time_base.num, st->time_base.den) - 0.000001;
++#else
+ m_startTime = av_rescale(st->cur_dts, st->time_base.num, st->time_base.den) - 0.000001;
++#endif
+ m_seekStream = i;
+ }
+ return TRANSPORT_STREAM_STATE::READY;
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
+index b8494e504e..392853cc68 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
+@@ -12,6 +12,7 @@
+ #include "addons/addoninfo/AddonType.h"
+ #include "addons/binary-addons/AddonDll.h"
+ #include "addons/kodi-dev-kit/include/kodi/addon-instance/VideoCodec.h"
++#include "cores/FFmpeg.h"
+ #include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h"
+ #include "cores/VideoPlayer/DVDDemuxers/DVDDemuxUtils.h"
+ #include "cores/VideoPlayer/Interface/DemuxCrypto.h"
+@@ -392,7 +393,7 @@ KODI_HANDLE CInputStreamAddon::cb_get_stream_transfer(KODI_HANDLE handle,
+ return nullptr;
+
+ std::string codecName(stream->m_codecName);
+- AVCodec* codec = nullptr;
++ FFMPEG_FMT_CONST AVCodec* codec = nullptr;
+
+ if (stream->m_streamType != INPUTSTREAM_TYPE_TELETEXT &&
+ stream->m_streamType != INPUTSTREAM_TYPE_RDS && stream->m_streamType != INPUTSTREAM_TYPE_ID3)
+diff --git a/xbmc/filesystem/AudioBookFileDirectory.cpp b/xbmc/filesystem/AudioBookFileDirectory.cpp
+index 04d1c14343..d82b2a9bbd 100644
+--- a/xbmc/filesystem/AudioBookFileDirectory.cpp
++++ b/xbmc/filesystem/AudioBookFileDirectory.cpp
+@@ -11,6 +11,7 @@
+ #include "TextureDatabase.h"
+ #include "URL.h"
+ #include "Util.h"
++#include "cores/FFmpeg.h"
+ #include "filesystem/File.h"
+ #include "guilib/LocalizeStrings.h"
+ #include "music/tags/MusicInfoTag.h"
+@@ -149,7 +150,7 @@ bool CAudioBookFileDirectory::ContainsFiles(const CURL& url)
+
+ m_ioctx->max_packet_size = 32768;
+
+- AVInputFormat* iformat=nullptr;
++ FFMPEG_FMT_CONST AVInputFormat* iformat = nullptr;
+ av_probe_input_buffer(m_ioctx, &iformat, url.Get().c_str(), nullptr, 0, 0);
+
+ bool contains = false;
+diff --git a/xbmc/guilib/FFmpegImage.cpp b/xbmc/guilib/FFmpegImage.cpp
+index fa255278b7..9d01a21f38 100644
+--- a/xbmc/guilib/FFmpegImage.cpp
++++ b/xbmc/guilib/FFmpegImage.cpp
+@@ -52,7 +52,7 @@ struct ThumbDataManagement
+ AVFrame* frame_temporary = nullptr;
+ SwsContext* sws = nullptr;
+ AVCodecContext* avOutctx = nullptr;
+- AVCodec* codec = nullptr;
++ FFMPEG_FMT_CONST AVCodec* codec = nullptr;
+ ~ThumbDataManagement()
+ {
+ av_free(intermediateBuffer);
+@@ -198,7 +198,7 @@ bool CFFmpegImage::Initialize(unsigned char* buffer, size_t bufSize)
+ bool is_png = (bufSize > 3 && buffer[1] == 'P' && buffer[2] == 'N' && buffer[3] == 'G');
+ bool is_tiff = (bufSize > 2 && buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*');
+
+- AVInputFormat* inp = nullptr;
++ FFMPEG_FMT_CONST AVInputFormat* inp = nullptr;
+ if (is_jpeg)
+ inp = av_find_input_format("image2");
+ else if (m_strMimeType == "image/apng")
+@@ -236,7 +236,7 @@ bool CFFmpegImage::Initialize(unsigned char* buffer, size_t bufSize)
+ return false;
+ }
+ AVCodecParameters* codec_params = m_fctx->streams[0]->codecpar;
+- AVCodec* codec = avcodec_find_decoder(codec_params->codec_id);
++ FFMPEG_FMT_CONST AVCodec* codec = avcodec_find_decoder(codec_params->codec_id);
+ m_codec_ctx = avcodec_alloc_context3(codec);
+ if (!m_codec_ctx)
+ {
+diff --git a/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp b/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp
+index 9bb2cdc09b..dbd8893c97 100644
+--- a/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp
++++ b/xbmc/music/tags/MusicInfoTagLoaderFFmpeg.cpp
+@@ -58,7 +58,7 @@ bool CMusicInfoTagLoaderFFmpeg::Load(const std::string& strFileName, CMusicInfoT
+ if (file.IoControl(IOCTRL_SEEK_POSSIBLE, NULL) != 1)
+ ioctx->seekable = 0;
+
+- AVInputFormat* iformat=NULL;
++ FFMPEG_FMT_CONST AVInputFormat* iformat = nullptr;
+ av_probe_input_buffer(ioctx, &iformat, strFileName.c_str(), NULL, 0, 0);
+
+ if (avformat_open_input(&fctx, strFileName.c_str(), iformat, NULL) < 0)
+diff --git a/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp b/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp
+index c1b0495179..5564696be6 100644
+--- a/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp
++++ b/xbmc/video/tags/VideoTagLoaderFFmpeg.cpp
+@@ -65,7 +65,7 @@ CVideoTagLoaderFFmpeg::CVideoTagLoaderFFmpeg(const CFileItem& item,
+ if (m_file->IoControl(IOCTRL_SEEK_POSSIBLE, nullptr) != 1)
+ m_ioctx->seekable = 0;
+
+- AVInputFormat* iformat = nullptr;
++ FFMPEG_FMT_CONST AVInputFormat* iformat = nullptr;
+ av_probe_input_buffer(m_ioctx, &iformat, m_item.GetPath().c_str(), nullptr, 0, 0);
+ if (avformat_open_input(&m_fctx, m_item.GetPath().c_str(), iformat, nullptr) < 0)
+ {
+--
+2.35.1
+
+
+From 3de036ef45b0df3368997500d210090a7f6ff5e7 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Thu, 13 Oct 2022 04:56:26 +0000
+Subject: [PATCH 2/3] Use Channel Layout API if built against FFmpeg 5.1
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/cdrip/EncoderFFmpeg.cpp | 51 +++++++++++++
+ .../AudioEngine/Encoders/AEEncoderFFmpeg.cpp | 60 ++++++++++++++-
+ .../AudioEngine/Engines/ActiveAE/ActiveAE.cpp | 22 ++++++
+ .../Engines/ActiveAE/ActiveAEFilter.cpp | 38 +++++++++-
+ .../ActiveAE/ActiveAEResampleFFMPEG.cpp | 55 +++++++++++++-
+ .../Engines/ActiveAE/ActiveAEStream.cpp | 2 +-
+ xbmc/cores/AudioEngine/Utils/AEUtil.cpp | 73 +++++++++++++++++--
+ xbmc/cores/AudioEngine/Utils/AEUtil.h | 9 ++-
+ .../DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp | 58 ++++++++++++---
+ .../DVDDemuxers/DVDDemuxClient.cpp | 15 +++-
+ .../DVDDemuxers/DVDDemuxFFmpeg.cpp | 42 +++++++++--
+ 11 files changed, 388 insertions(+), 37 deletions(-)
+
+diff --git a/xbmc/cdrip/EncoderFFmpeg.cpp b/xbmc/cdrip/EncoderFFmpeg.cpp
+index 4c0628b5cc..c05cdeab9e 100644
+--- a/xbmc/cdrip/EncoderFFmpeg.cpp
++++ b/xbmc/cdrip/EncoderFFmpeg.cpp
+@@ -94,8 +94,14 @@ bool CEncoderFFmpeg::Init()
+
+ /* Set the basic encoder parameters.
+ * The input file's sample rate is used to avoid a sample rate conversion. */
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_codecCtx->ch_layout);
++ av_channel_layout_default(&m_codecCtx->ch_layout, m_iInChannels);
++#else
+ m_codecCtx->channels = m_iInChannels;
+ m_codecCtx->channel_layout = av_get_default_channel_layout(m_iInChannels);
++#endif
+ m_codecCtx->sample_rate = m_iInSampleRate;
+ m_codecCtx->sample_fmt = codec->sample_fmts[0];
+ m_codecCtx->bit_rate = bitrate;
+@@ -140,7 +146,14 @@ bool CEncoderFFmpeg::Init()
+
+ m_bufferFrame->nb_samples = m_codecCtx->frame_size;
+ m_bufferFrame->format = m_inFormat;
++
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_bufferFrame->ch_layout);
++ av_channel_layout_copy(&m_bufferFrame->ch_layout, &m_codecCtx->ch_layout);
++#else
+ m_bufferFrame->channel_layout = m_codecCtx->channel_layout;
++#endif
+ m_bufferFrame->sample_rate = m_codecCtx->sample_rate;
+
+ err = av_frame_get_buffer(m_bufferFrame, 0);
+@@ -152,10 +165,18 @@ bool CEncoderFFmpeg::Init()
+
+ if (m_needConversion)
+ {
++#if LIBSWRESAMPLE_BUILD >= AV_VERSION_INT(4, 7, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int ret = swr_alloc_set_opts2(&m_swrCtx, &m_codecCtx->ch_layout, m_outFormat,
++ m_codecCtx->sample_rate, &m_codecCtx->ch_layout, m_inFormat,
++ m_codecCtx->sample_rate, 0, nullptr);
++ if (ret || swr_init(m_swrCtx) < 0)
++#else
+ m_swrCtx = swr_alloc_set_opts(nullptr, m_codecCtx->channel_layout, m_outFormat,
+ m_codecCtx->sample_rate, m_codecCtx->channel_layout, m_inFormat,
+ m_codecCtx->sample_rate, 0, nullptr);
+ if (!m_swrCtx || swr_init(m_swrCtx) < 0)
++#endif
+ throw FFMpegException("Failed to initialize the resampler");
+
+ m_resampledBufferSize =
+@@ -167,7 +188,13 @@ bool CEncoderFFmpeg::Init()
+
+ m_resampledFrame->nb_samples = m_neededFrames;
+ m_resampledFrame->format = m_outFormat;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_resampledFrame->ch_layout);
++ av_channel_layout_copy(&m_resampledFrame->ch_layout, &m_codecCtx->ch_layout);
++#else
+ m_resampledFrame->channel_layout = m_codecCtx->channel_layout;
++#endif
+ m_resampledFrame->sample_rate = m_codecCtx->sample_rate;
+
+ err = av_frame_get_buffer(m_resampledFrame, 0);
+@@ -203,11 +230,23 @@ bool CEncoderFFmpeg::Init()
+ CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what());
+
+ av_freep(&m_buffer);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_bufferFrame->ch_layout);
++#endif
+ av_frame_free(&m_bufferFrame);
+ swr_free(&m_swrCtx);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_resampledFrame->ch_layout);
++#endif
+ av_frame_free(&m_resampledFrame);
+ av_freep(&m_resampledBuffer);
+ av_free(m_bcBuffer);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_codecCtx->ch_layout);
++#endif
+ avcodec_free_context(&m_codecCtx);
+ if (m_formatCtx)
+ {
+@@ -351,8 +390,16 @@ bool CEncoderFFmpeg::Close()
+
+ /* Flush if needed */
+ av_freep(&m_buffer);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_bufferFrame->ch_layout);
++#endif
+ av_frame_free(&m_bufferFrame);
+ swr_free(&m_swrCtx);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_resampledFrame->ch_layout);
++#endif
+ av_frame_free(&m_resampledFrame);
+ av_freep(&m_resampledBuffer);
+ m_needConversion = false;
+@@ -364,6 +411,10 @@ bool CEncoderFFmpeg::Close()
+
+ /* cleanup */
+ av_free(m_bcBuffer);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_codecCtx->ch_layout);
++#endif
+ avcodec_free_context(&m_codecCtx);
+ av_freep(&m_formatCtx->pb);
+ avformat_free_context(m_formatCtx);
+diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+index 86f65f57f3..5d8ca71de1 100644
+--- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
++++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+@@ -37,6 +37,10 @@ CAEEncoderFFmpeg::~CAEEncoderFFmpeg()
+ {
+ Reset();
+ swr_free(&m_SwrCtx);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_CodecCtx->ch_layout);
++#endif
+ avcodec_free_context(&m_CodecCtx);
+ }
+
+@@ -113,7 +117,13 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format, bool allow_planar_input
+
+ m_CodecCtx->bit_rate = m_BitRate;
+ m_CodecCtx->sample_rate = format.m_sampleRate;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_CodecCtx->ch_layout);
++ av_channel_layout_from_mask(&m_CodecCtx->ch_layout, AV_CH_LAYOUT_5POINT1_BACK);
++#else
+ m_CodecCtx->channel_layout = AV_CH_LAYOUT_5POINT1_BACK;
++#endif
+
+ /* select a suitable data format */
+ if (codec->sample_fmts)
+@@ -190,22 +200,44 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format, bool allow_planar_input
+ LOGERROR,
+ "CAEEncoderFFmpeg::Initialize - Unable to find a suitable data format for the codec ({})",
+ m_CodecName);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_CodecCtx->ch_layout);
++#endif
+ avcodec_free_context(&m_CodecCtx);
+ return false;
+ }
+ }
+
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ uint64_t mask = m_CodecCtx->ch_layout.u.mask;
++ av_channel_layout_uninit(&m_CodecCtx->ch_layout);
++ av_channel_layout_from_mask(&m_CodecCtx->ch_layout, mask);
++ m_CodecCtx->ch_layout.nb_channels = BuildChannelLayout(mask, m_Layout);
++#else
+ m_CodecCtx->channels = BuildChannelLayout(m_CodecCtx->channel_layout, m_Layout);
++#endif
+
+ /* open the codec */
+ if (avcodec_open2(m_CodecCtx, codec, NULL))
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_CodecCtx->ch_layout);
++#endif
+ avcodec_free_context(&m_CodecCtx);
+ return false;
+ }
+
+ format.m_frames = m_CodecCtx->frame_size;
+- format.m_frameSize = m_CodecCtx->channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int channels = m_CodecCtx->ch_layout.nb_channels;
++#else
++ int channels = m_CodecCtx->channels;
++#endif
++ format.m_frameSize = channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+ format.m_channelLayout = m_Layout;
+
+ m_CurrentFormat = format;
+@@ -215,14 +247,26 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format, bool allow_planar_input
+
+ if (m_NeedConversion)
+ {
++#if LIBSWRESAMPLE_BUILD >= AV_VERSION_INT(4, 7, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int ret = swr_alloc_set_opts2(&m_SwrCtx, &m_CodecCtx->ch_layout, m_CodecCtx->sample_fmt,
++ m_CodecCtx->sample_rate, &m_CodecCtx->ch_layout,
++ AV_SAMPLE_FMT_FLT, m_CodecCtx->sample_rate, 0, NULL);
++ if (ret || swr_init(m_SwrCtx) < 0)
++#else
+ m_SwrCtx = swr_alloc_set_opts(NULL,
+ m_CodecCtx->channel_layout, m_CodecCtx->sample_fmt, m_CodecCtx->sample_rate,
+ m_CodecCtx->channel_layout, AV_SAMPLE_FMT_FLT, m_CodecCtx->sample_rate,
+ 0, NULL);
+ if (!m_SwrCtx || swr_init(m_SwrCtx) < 0)
++#endif
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - Failed to initialise resampler.");
+ swr_free(&m_SwrCtx);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_CodecCtx->ch_layout);
++#endif
+ avcodec_free_context(&m_CodecCtx);
+ return false;
+ }
+@@ -276,10 +320,18 @@ int CAEEncoderFFmpeg::Encode(uint8_t *in, int in_size, uint8_t *out, int out_siz
+
+ frame->nb_samples = m_CodecCtx->frame_size;
+ frame->format = m_CodecCtx->sample_fmt;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&frame->ch_layout);
++ av_channel_layout_copy(&frame->ch_layout, &m_CodecCtx->ch_layout);
++ int channelNum = m_CodecCtx->ch_layout.nb_channels;
++#else
+ frame->channel_layout = m_CodecCtx->channel_layout;
+ frame->channels = m_CodecCtx->channels;
++ int channelNum = m_CodecCtx->channels;
++#endif
+
+- avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt, in, in_size, 0);
++ avcodec_fill_audio_frame(frame, channelNum, m_CodecCtx->sample_fmt, in, in_size, 0);
+
+ pkt->size = out_size;
+ pkt->data = out;
+@@ -295,6 +347,10 @@ int CAEEncoderFFmpeg::Encode(uint8_t *in, int in_size, uint8_t *out, int out_siz
+ err = avcodec_receive_packet(m_CodecCtx, pkt);
+ if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&frame->ch_layout);
++#endif
+ av_frame_free(&frame);
+ av_packet_free(&pkt);
+ return (err == AVERROR(EAGAIN)) ? -1 : 0;
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
+index 6000fe9c63..9cd3bddda8 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
+@@ -3069,8 +3069,14 @@ IAE::SoundPtr CActiveAE::MakeSound(const std::string& file)
+ AVCodecID codecId = fmt_ctx->streams[0]->codecpar->codec_id;
+ dec = avcodec_find_decoder(codecId);
+ config.sample_rate = fmt_ctx->streams[0]->codecpar->sample_rate;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ config.channels = fmt_ctx->streams[0]->codecpar->ch_layout.nb_channels;
++ config.channel_layout = fmt_ctx->streams[0]->codecpar->ch_layout.u.mask;
++#else
+ config.channels = fmt_ctx->streams[0]->codecpar->channels;
+ config.channel_layout = fmt_ctx->streams[0]->codecpar->channel_layout;
++#endif
+ }
+ }
+ if (dec == nullptr)
+@@ -3086,10 +3092,22 @@ IAE::SoundPtr CActiveAE::MakeSound(const std::string& file)
+
+ dec_ctx = avcodec_alloc_context3(dec);
+ dec_ctx->sample_rate = config.sample_rate;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannelLayout layout = {};
++ if (!config.channel_layout)
++ av_channel_layout_default(&layout, config.channels);
++ else
++ av_channel_layout_from_mask(&layout, config.channel_layout);
++ config.channel_layout = layout.u.mask;
++ av_channel_layout_copy(&dec_ctx->ch_layout, &layout);
++ av_channel_layout_uninit(&layout);
++#else
+ dec_ctx->channels = config.channels;
+ if (!config.channel_layout)
+ config.channel_layout = av_get_default_channel_layout(config.channels);
+ dec_ctx->channel_layout = config.channel_layout;
++#endif
+
+ AVPacket* avpkt = av_packet_alloc();
+ if (!avpkt)
+@@ -3156,6 +3174,10 @@ IAE::SoundPtr CActiveAE::MakeSound(const std::string& file)
+
+ av_packet_free(&avpkt);
+ av_frame_free(&decoded_frame);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&dec_ctx->ch_layout);
++#endif
+ avcodec_free_context(&dec_ctx);
+ avformat_close_input(&fmt_ctx);
+ if (io_ctx)
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEFilter.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEFilter.cpp
+index 26b669a2cf..1d79dc6db4 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEFilter.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEFilter.cpp
+@@ -12,10 +12,11 @@
+ #include <algorithm>
+
+ extern "C" {
+-#include <libavfilter/avfilter.h>
+ #include <libavcodec/avcodec.h>
++#include <libavfilter/avfilter.h>
+ #include <libavfilter/buffersink.h>
+ #include <libavfilter/buffersrc.h>
++#include <libavutil/version.h>
+ #include <libswresample/swresample.h>
+ }
+
+@@ -171,7 +172,13 @@ void CActiveAEFilter::CloseFilter()
+ }
+
+ if (m_pOutFrame)
++ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_pOutFrame->ch_layout);
++#endif
+ av_frame_free(&m_pOutFrame);
++ }
+
+ if (m_pConvertFrame)
+ av_frame_free(&m_pConvertFrame);
+@@ -205,10 +212,17 @@ int CActiveAEFilter::ProcessFilter(uint8_t **dst_buffer, int dst_samples, uint8_
+ if (!frame)
+ return -1;
+
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&frame->ch_layout);
++ av_channel_layout_from_mask(&frame->ch_layout, m_channelLayout);
++ int channels = frame->ch_layout.nb_channels;
++#else
+ int channels = av_get_channel_layout_nb_channels(m_channelLayout);
+
+ frame->channel_layout = m_channelLayout;
+ frame->channels = channels;
++#endif
+ frame->sample_rate = m_sampleRate;
+ frame->nb_samples = src_samples;
+ frame->format = m_sampleFormat;
+@@ -224,6 +238,10 @@ int CActiveAEFilter::ProcessFilter(uint8_t **dst_buffer, int dst_samples, uint8_
+ src_buffer[0], src_bufsize, 16);
+ if (result < 0)
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&frame->ch_layout);
++#endif
+ av_frame_free(&frame);
+ CLog::Log(LOGERROR, "CActiveAEFilter::ProcessFilter - avcodec_fill_audio_frame failed");
+ m_filterEof = true;
+@@ -231,6 +249,10 @@ int CActiveAEFilter::ProcessFilter(uint8_t **dst_buffer, int dst_samples, uint8_
+ }
+
+ result = av_buffersrc_write_frame(m_pFilterCtxIn, frame);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&frame->ch_layout);
++#endif
+ av_frame_free(&frame);
+ if (result < 0)
+ {
+@@ -284,7 +306,13 @@ int CActiveAEFilter::ProcessFilter(uint8_t **dst_buffer, int dst_samples, uint8_
+ {
+ av_frame_unref(m_pOutFrame);
+ m_pOutFrame->format = m_sampleFormat;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_pOutFrame->ch_layout);
++ av_channel_layout_from_mask(&m_pOutFrame->ch_layout, m_channelLayout);
++#else
+ m_pOutFrame->channel_layout = m_channelLayout;
++#endif
+ m_pOutFrame->sample_rate = m_sampleRate;
+ result = swr_convert_frame(m_pConvertCtx, m_pOutFrame, m_pConvertFrame);
+ av_frame_unref(m_pConvertFrame);
+@@ -302,7 +330,15 @@ int CActiveAEFilter::ProcessFilter(uint8_t **dst_buffer, int dst_samples, uint8_
+
+ if (m_hasData)
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannelLayout layout = {};
++ av_channel_layout_from_mask(&layout, m_channelLayout);
++ int channels = layout.nb_channels;
++ av_channel_layout_uninit(&layout);
++#else
+ int channels = av_get_channel_layout_nb_channels(m_channelLayout);
++#endif
+ int planes = av_sample_fmt_is_planar(m_sampleFormat) ? channels : 1;
+ int samples = std::min(dst_samples, m_pOutFrame->nb_samples - m_sampleOffset);
+ int bytes = samples * av_get_bytes_per_sample(m_sampleFormat) * channels / planes;
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp
+index bfef837114..379dfe6446 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp
+@@ -11,8 +11,10 @@
+ #include "utils/log.h"
+
+ extern "C" {
++#include <libavcodec/version.h>
+ #include <libavutil/channel_layout.h>
+ #include <libavutil/opt.h>
++#include <libavutil/version.h>
+ #include <libswresample/swresample.h>
+ }
+
+@@ -49,15 +51,49 @@ bool CActiveAEResampleFFMPEG::Init(SampleConfig dstConfig, SampleConfig srcConfi
+ m_doesResample = true;
+
+ if (m_dst_chan_layout == 0)
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ {
++ AVChannelLayout layout = {};
++ av_channel_layout_default(&layout, m_dst_channels);
++ m_dst_chan_layout = layout.u.mask;
++ av_channel_layout_uninit(&layout);
++ }
++#else
+ m_dst_chan_layout = av_get_default_channel_layout(m_dst_channels);
++#endif
+ if (m_src_chan_layout == 0)
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ {
++ AVChannelLayout layout = {};
++ av_channel_layout_default(&layout, m_src_channels);
++ m_src_chan_layout = layout.u.mask;
++ av_channel_layout_uninit(&layout);
++ }
++#else
+ m_src_chan_layout = av_get_default_channel_layout(m_src_channels);
++#endif
++
++#if LIBSWRESAMPLE_BUILD >= AV_VERSION_INT(4, 7, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannelLayout dstChLayout = {};
++ AVChannelLayout srcChLayout = {};
+
++ av_channel_layout_from_mask(&dstChLayout, m_dst_chan_layout);
++ av_channel_layout_from_mask(&srcChLayout, m_src_chan_layout);
++
++ int ret = swr_alloc_set_opts2(&m_pContext, &dstChLayout, m_dst_fmt, m_dst_rate, &srcChLayout,
++ m_src_fmt, m_src_rate, 0, NULL);
++
++ if (ret)
++#else
+ m_pContext = swr_alloc_set_opts(NULL, m_dst_chan_layout, m_dst_fmt, m_dst_rate,
+ m_src_chan_layout, m_src_fmt, m_src_rate,
+ 0, NULL);
+
+ if (!m_pContext)
++#endif
+ {
+ CLog::Log(LOGERROR, "CActiveAEResampleFFMPEG::Init - create context failed");
+ return false;
+@@ -126,10 +162,20 @@ bool CActiveAEResampleFFMPEG::Init(SampleConfig dstConfig, SampleConfig srcConfi
+ else if (upmix && m_src_channels == 2 && m_dst_channels > 2)
+ {
+ memset(m_rematrix, 0, sizeof(m_rematrix));
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&dstChLayout);
++ av_channel_layout_from_mask(&dstChLayout, m_dst_chan_layout);
++#endif
+ for (int out=0; out<m_dst_channels; out++)
+ {
+- uint64_t out_chan = av_channel_layout_extract_channel(m_dst_chan_layout, out);
+- switch(out_chan)
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannel outChan = av_channel_layout_channel_from_index(&dstChLayout, out);
++#else
++ uint64_t outChan = av_channel_layout_extract_channel(m_dst_chan_layout, out);
++#endif
++ switch (outChan)
+ {
+ case AV_CH_FRONT_LEFT:
+ case AV_CH_BACK_LEFT:
+@@ -154,6 +200,11 @@ bool CActiveAEResampleFFMPEG::Init(SampleConfig dstConfig, SampleConfig srcConfi
+ }
+ }
+
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&dstChLayout);
++#endif
++
+ if (swr_set_matrix(m_pContext, (const double*)m_rematrix, AE_CH_MAX) < 0)
+ {
+ CLog::Log(LOGERROR, "CActiveAEResampleFFMPEG::Init - setting channel matrix failed");
+diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp
+index 858b0f2f22..a52ac2829f 100644
+--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp
++++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp
+@@ -88,7 +88,7 @@ void CActiveAEStream::InitRemapper()
+ for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++)
+ {
+ avLast = avCur;
+- avCur = CAEUtil::GetAVChannel(m_format.m_channelLayout[i]);
++ avCur = CAEUtil::GetAVChannelMask(m_format.m_channelLayout[i]);
+ if(avCur < avLast)
+ {
+ needRemap = true;
+diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
+index bfa2cf9e4c..98f82816ef 100644
+--- a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
++++ b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
+@@ -19,10 +19,6 @@
+ #include <xmmintrin.h>
+ #endif
+
+-extern "C" {
+-#include <libavutil/channel_layout.h>
+-}
+-
+ void AEDelayStatus::SetDelay(double d)
+ {
+ delay = d;
+@@ -550,8 +546,15 @@ AVSampleFormat CAEUtil::GetAVSampleFormat(AEDataFormat format)
+ }
+ }
+
+-uint64_t CAEUtil::GetAVChannel(enum AEChannel aechannel)
++uint64_t CAEUtil::GetAVChannelMask(enum AEChannel aechannel)
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ enum AVChannel ch = GetAVChannel(aechannel);
++ if (ch == AV_CHAN_NONE)
++ return 0;
++ return (1ULL << ch);
++#else
+ switch (aechannel)
+ {
+ case AE_CH_FL: return AV_CH_FRONT_LEFT;
+@@ -575,9 +578,67 @@ uint64_t CAEUtil::GetAVChannel(enum AEChannel aechannel)
+ default:
+ return 0;
+ }
++#endif
+ }
+
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++enum AVChannel CAEUtil::GetAVChannel(enum AEChannel aechannel)
++{
++ switch (aechannel)
++ {
++ case AE_CH_FL:
++ return AV_CHAN_FRONT_LEFT;
++ case AE_CH_FR:
++ return AV_CHAN_FRONT_RIGHT;
++ case AE_CH_FC:
++ return AV_CHAN_FRONT_CENTER;
++ case AE_CH_LFE:
++ return AV_CHAN_LOW_FREQUENCY;
++ case AE_CH_BL:
++ return AV_CHAN_BACK_LEFT;
++ case AE_CH_BR:
++ return AV_CHAN_BACK_RIGHT;
++ case AE_CH_FLOC:
++ return AV_CHAN_FRONT_LEFT_OF_CENTER;
++ case AE_CH_FROC:
++ return AV_CHAN_FRONT_RIGHT_OF_CENTER;
++ case AE_CH_BC:
++ return AV_CHAN_BACK_CENTER;
++ case AE_CH_SL:
++ return AV_CHAN_SIDE_LEFT;
++ case AE_CH_SR:
++ return AV_CHAN_SIDE_RIGHT;
++ case AE_CH_TC:
++ return AV_CHAN_TOP_CENTER;
++ case AE_CH_TFL:
++ return AV_CHAN_TOP_FRONT_LEFT;
++ case AE_CH_TFC:
++ return AV_CHAN_TOP_FRONT_CENTER;
++ case AE_CH_TFR:
++ return AV_CHAN_TOP_FRONT_RIGHT;
++ case AE_CH_TBL:
++ return AV_CHAN_TOP_BACK_LEFT;
++ case AE_CH_TBC:
++ return AV_CHAN_TOP_BACK_CENTER;
++ case AE_CH_TBR:
++ return AV_CHAN_TOP_BACK_RIGHT;
++ default:
++ return AV_CHAN_NONE;
++ }
++}
++#endif
++
+ int CAEUtil::GetAVChannelIndex(enum AEChannel aechannel, uint64_t layout)
+ {
+- return av_get_channel_layout_channel_index(layout, GetAVChannel(aechannel));
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannelLayout ch_layout = {};
++ av_channel_layout_from_mask(&ch_layout, layout);
++ int idx = av_channel_layout_index_from_channel(&ch_layout, GetAVChannel(aechannel));
++ av_channel_layout_uninit(&ch_layout);
++ return idx;
++#else
++ return av_get_channel_layout_channel_index(layout, GetAVChannelMask(aechannel));
++#endif
+ }
+diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.h b/xbmc/cores/AudioEngine/Utils/AEUtil.h
+index 034e115ee8..8acbeec442 100644
+--- a/xbmc/cores/AudioEngine/Utils/AEUtil.h
++++ b/xbmc/cores/AudioEngine/Utils/AEUtil.h
+@@ -13,7 +13,10 @@
+ #include <math.h>
+
+ extern "C" {
++#include <libavcodec/version.h>
++#include <libavutil/channel_layout.h>
+ #include <libavutil/samplefmt.h>
++#include <libavutil/version.h>
+ }
+
+ // AV sync options
+@@ -171,6 +174,10 @@ public:
+ static uint64_t GetAVChannelLayout(const CAEChannelInfo &info);
+ static CAEChannelInfo GetAEChannelLayout(uint64_t layout);
+ static AVSampleFormat GetAVSampleFormat(AEDataFormat format);
+- static uint64_t GetAVChannel(enum AEChannel aechannel);
++ static uint64_t GetAVChannelMask(enum AEChannel aechannel);
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ static enum AVChannel GetAVChannel(enum AEChannel aechannel);
++#endif
+ static int GetAVChannelIndex(enum AEChannel aechannel, uint64_t layout);
+ };
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+index 87e7ae2c57..44dcb32620 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+@@ -78,7 +78,14 @@ bool CDVDAudioCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
+
+ m_matrixEncoding = AV_MATRIX_ENCODING_NONE;
+ m_channels = 0;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&m_pCodecContext->ch_layout);
++ m_pCodecContext->ch_layout.order = AV_CHANNEL_ORDER_NATIVE;
++ m_pCodecContext->ch_layout.nb_channels = hints.channels;
++#else
+ m_pCodecContext->channels = hints.channels;
++#endif
+ m_hint_layout = hints.channellayout;
+ m_pCodecContext->sample_rate = hints.samplerate;
+ m_pCodecContext->block_align = hints.blockalign;
+@@ -261,12 +268,18 @@ int CDVDAudioCodecFFmpeg::GetData(uint8_t** dst)
+ m_format.m_frameSize = m_format.m_channelLayout.Count() *
+ CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3;
+
+- int planes = av_sample_fmt_is_planar(m_pCodecContext->sample_fmt) ? m_pFrame->channels : 1;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int channels = m_pFrame->ch_layout.nb_channels;
++#else
++ int channels = m_pFrame->channels;
++#endif
++ int planes = av_sample_fmt_is_planar(m_pCodecContext->sample_fmt) ? channels : 1;
++
+ for (int i=0; i<planes; i++)
+ dst[i] = m_pFrame->extended_data[i];
+
+- return m_pFrame->nb_samples * m_pFrame->channels *
+- av_get_bytes_per_sample(m_pCodecContext->sample_fmt);
++ return m_pFrame->nb_samples * channels * av_get_bytes_per_sample(m_pCodecContext->sample_fmt);
+ }
+
+ return 0;
+@@ -280,7 +293,12 @@ void CDVDAudioCodecFFmpeg::Reset()
+
+ int CDVDAudioCodecFFmpeg::GetChannels()
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ return m_pCodecContext->ch_layout.nb_channels;
++#else
+ return m_pCodecContext->channels;
++#endif
+ }
+
+ int CDVDAudioCodecFFmpeg::GetSampleRate()
+@@ -347,28 +365,44 @@ static unsigned count_bits(int64_t value)
+
+ void CDVDAudioCodecFFmpeg::BuildChannelMap()
+ {
+- if (m_channels == m_pCodecContext->channels && m_layout == m_pCodecContext->channel_layout)
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int codecChannels = m_pCodecContext->ch_layout.nb_channels;
++ uint64_t codecChannelLayout = m_pCodecContext->ch_layout.u.mask;
++#else
++ int codecChannels = m_pCodecContext->channels;
++ uint64_t codecChannelLayout = m_pCodecContext->channel_layout;
++#endif
++ if (m_channels == codecChannels && m_layout == codecChannelLayout)
+ return; //nothing to do here
+
+- m_channels = m_pCodecContext->channels;
+- m_layout = m_pCodecContext->channel_layout;
++ m_channels = codecChannels;
++ m_layout = codecChannelLayout;
+
+ int64_t layout;
+
+- int bits = count_bits(m_pCodecContext->channel_layout);
+- if (bits == m_pCodecContext->channels)
+- layout = m_pCodecContext->channel_layout;
++ int bits = count_bits(codecChannelLayout);
++ if (bits == codecChannels)
++ layout = codecChannelLayout;
+ else
+ {
+ CLog::Log(LOGINFO,
+ "CDVDAudioCodecFFmpeg::GetChannelMap - FFmpeg reported {} channels, but the layout "
+ "contains {} - trying hints",
+- m_pCodecContext->channels, bits);
+- if (static_cast<int>(count_bits(m_hint_layout)) == m_pCodecContext->channels)
++ codecChannels, bits);
++ if (static_cast<int>(count_bits(m_hint_layout)) == codecChannels)
+ layout = m_hint_layout;
+ else
+ {
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannelLayout def_layout = {};
++ av_channel_layout_default(&def_layout, codecChannels);
++ layout = def_layout.u.mask;
++ av_channel_layout_uninit(&def_layout);
++#else
+ layout = av_get_default_channel_layout(m_pCodecContext->channels);
++#endif
+ CLog::Log(LOGINFO, "Using default layout...");
+ }
+ }
+@@ -394,7 +428,7 @@ void CDVDAudioCodecFFmpeg::BuildChannelMap()
+ if (layout & AV_CH_TOP_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+
+- m_channels = m_pCodecContext->channels;
++ m_channels = codecChannels;
+ }
+
+ CAEChannelInfo CDVDAudioCodecFFmpeg::GetChannelMap()
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+index 5be134e381..70f0562462 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+@@ -220,10 +220,17 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ case STREAM_AUDIO:
+ {
+ CDemuxStreamClientInternalTpl<CDemuxStreamAudio>* sta = static_cast<CDemuxStreamClientInternalTpl<CDemuxStreamAudio>*>(st);
+- if (stream->m_context->channels != sta->iChannels && stream->m_context->channels != 0)
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int streamChannels = stream->m_context->ch_layout.nb_channels;
++#else
++ int streamChannels = stream->m_context->channels;
++#endif
++ if (streamChannels != sta->iChannels && streamChannels != 0)
+ {
+- CLog::Log(LOGDEBUG, "CDVDDemuxClient::ParsePacket - ({}) channels changed from {} to {}", st->uniqueId, sta->iChannels, stream->m_context->channels);
+- sta->iChannels = stream->m_context->channels;
++ CLog::Log(LOGDEBUG, "CDVDDemuxClient::ParsePacket - ({}) channels changed from {} to {}",
++ st->uniqueId, sta->iChannels, streamChannels);
++ sta->iChannels = streamChannels;
+ sta->changes++;
+ sta->disabled = false;
+ }
+@@ -235,7 +242,7 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ sta->changes++;
+ sta->disabled = false;
+ }
+- if (stream->m_context->channels)
++ if (streamChannels)
+ st->changes = -1; // stop parsing
+ break;
+ }
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+index bf6f322274..4ad52cd32a 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+@@ -1235,8 +1235,16 @@ DemuxPacket* CDVDDemuxFFmpeg::Read()
+ else if (stream->type == STREAM_AUDIO)
+ {
+ CDemuxStreamAudioFFmpeg* audiostream = dynamic_cast<CDemuxStreamAudioFFmpeg*>(stream);
+- if (audiostream && (audiostream->iChannels != m_pFormatContext->streams[pPacket->iStreamId]->codecpar->channels ||
+- audiostream->iSampleRate != m_pFormatContext->streams[pPacket->iStreamId]->codecpar->sample_rate))
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int codecparChannels =
++ m_pFormatContext->streams[pPacket->iStreamId]->codecpar->ch_layout.nb_channels;
++#else
++ int codecparChannels = m_pFormatContext->streams[pPacket->iStreamId]->codecpar->channels;
++#endif
++ if (audiostream && (audiostream->iChannels != codecparChannels ||
++ audiostream->iSampleRate !=
++ m_pFormatContext->streams[pPacket->iStreamId]->codecpar->sample_rate))
+ {
+ // content has changed
+ stream = AddStream(pPacket->iStreamId);
+@@ -1418,6 +1426,7 @@ void CDVDDemuxFFmpeg::UpdateCurrentPTS()
+ if (idx >= 0)
+ {
+ AVStream* stream = m_pFormatContext->streams[idx];
++
+ #if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100)
+ if (stream && m_pkt.pkt.dts != (int64_t)AV_NOPTS_VALUE)
+ {
+@@ -1639,16 +1648,28 @@ CDemuxStream* CDVDDemuxFFmpeg::AddStream(int streamIdx)
+ {
+ CDemuxStreamAudioFFmpeg* st = new CDemuxStreamAudioFFmpeg(pStream);
+ stream = st;
+- st->iChannels = pStream->codecpar->channels;
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int codecparChannels = pStream->codecpar->ch_layout.nb_channels;
++ int codecparChannelLayout = pStream->codecpar->ch_layout.u.mask;
++#else
++ int codecparChannels = pStream->codecpar->channels;
++ int codecparChannelLayout = pStream->codecpar->channel_layout;
++#endif
++ st->iChannels = codecparChannels;
++ st->iChannelLayout = codecparChannelLayout;
+ st->iSampleRate = pStream->codecpar->sample_rate;
+ st->iBlockAlign = pStream->codecpar->block_align;
+ st->iBitRate = static_cast<int>(pStream->codecpar->bit_rate);
+ st->iBitsPerSample = pStream->codecpar->bits_per_raw_sample;
+- st->iChannelLayout = pStream->codecpar->channel_layout;
+ char buf[32] = {};
+ // https://github.com/FFmpeg/FFmpeg/blob/6ccc3989d15/doc/APIchanges#L50-L53
+-#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 24, 100)
+- av_channel_layout_describe(st->iChannelLayout, buf, sizeof(buf));
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ AVChannelLayout layout = {};
++ av_channel_layout_from_mask(&layout, st->iChannelLayout);
++ av_channel_layout_describe(&layout, buf, sizeof(buf));
++ av_channel_layout_uninit(&layout);
+ #else
+ av_get_channel_layout_string(buf, 31, st->iChannels, st->iChannelLayout);
+ #endif
+@@ -2195,8 +2216,13 @@ bool CDVDDemuxFFmpeg::IsProgramChange()
+ if (m_pFormatContext->streams[idx]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
+ {
+ CDemuxStreamAudioFFmpeg* audiostream = dynamic_cast<CDemuxStreamAudioFFmpeg*>(stream);
+- if (audiostream &&
+- m_pFormatContext->streams[idx]->codecpar->channels != audiostream->iChannels)
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ int codecparChannels = m_pFormatContext->streams[idx]->codecpar->ch_layout.nb_channels;
++#else
++ int codecparChannels = m_pFormatContext->streams[idx]->codecpar->channels;
++#endif
++ if (audiostream && codecparChannels != audiostream->iChannels)
+ {
+ return true;
+ }
+--
+2.35.1
+
+
+From 0a10e05700dfd12b7f5d720588b993089ea33be4 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+Date: Sat, 15 Oct 2022 11:46:52 +0000
+Subject: [PATCH 3/3] ffmpeg5: Get extradata with extract_extradata BSF
+
+Fixes the transport stream playback failures described in
+https://bugs.debian.org/1016925
+
+Rogo95 made an excellent technical analysis of the root cause
+and reported that to the bug thread.
+
+Later on, James Almer (jamrial) suggested the solution to use
+extract_extradata bitstream filter to replace the removed split()
+function.
+
+Finally, I adapted the following code snippet:
+https://gist.github.com/moonpfe/f6795d51294d91ee0f82f62ff6985db0
+to Kodi and tested it by playing the affected files in TS format.
+
+HiassofT form LibreELEC found another edge case decoding HTSP
+streams from pvr.hts. The comparison of log files revealed that
+the split function is also used in DVDDemuxClient.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ xbmc/cores/FFmpeg.cpp | 154 ++++++++++++++++++
+ xbmc/cores/FFmpeg.h | 5 +
+ .../DVDDemuxers/DVDDemuxClient.cpp | 37 ++---
+ .../DVDDemuxers/DVDDemuxFFmpeg.cpp | 61 +++----
+ 4 files changed, 203 insertions(+), 54 deletions(-)
+
+diff --git a/xbmc/cores/FFmpeg.cpp b/xbmc/cores/FFmpeg.cpp
+index de1765ed52..eb9c653fa3 100644
+--- a/xbmc/cores/FFmpeg.cpp
++++ b/xbmc/cores/FFmpeg.cpp
+@@ -16,6 +16,11 @@
+ #include "utils/StringUtils.h"
+ #include "utils/log.h"
+
++extern "C"
++{
++#include <libavcodec/bsf.h>
++}
++
+ #include <map>
+ #include <mutex>
+
+@@ -129,3 +134,152 @@ void ff_avutil_log(void* ptr, int level, const char* format, va_list va)
+ }
+ buffer.erase(0, start);
+ }
++
++std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt,
++ const AVCodecParserContext* parserCtx,
++ AVCodecContext* codecCtx)
++{
++ constexpr int FF_MAX_EXTRADATA_SIZE = ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE);
++
++ if (!pkt)
++ return std::make_tuple(nullptr, 0);
++
++ uint8_t* extraData = nullptr;
++ int extraDataSize = 0;
++
++#if LIBAVFORMAT_BUILD >= AV_VERSION_INT(59, 0, 100)
++ /* extract_extradata bitstream filter is implemented only
++ * for certain codecs, as noted in discussion of PR#21248
++ */
++
++ AVCodecID codecId = codecCtx->codec_id;
++
++ // clang-format off
++ if (
++ codecId != AV_CODEC_ID_MPEG1VIDEO &&
++ codecId != AV_CODEC_ID_MPEG2VIDEO &&
++ codecId != AV_CODEC_ID_H264 &&
++ codecId != AV_CODEC_ID_HEVC &&
++ codecId != AV_CODEC_ID_MPEG4 &&
++ codecId != AV_CODEC_ID_VC1 &&
++ codecId != AV_CODEC_ID_AV1 &&
++ codecId != AV_CODEC_ID_AVS2 &&
++ codecId != AV_CODEC_ID_AVS3 &&
++ codecId != AV_CODEC_ID_CAVS
++ )
++ // clang-format on
++ return std::make_tuple(nullptr, 0);
++
++ const AVBitStreamFilter* f = av_bsf_get_by_name("extract_extradata");
++ if (!f)
++ return std::make_tuple(nullptr, 0);
++
++ AVBSFContext* bsf = nullptr;
++ int ret = av_bsf_alloc(f, &bsf);
++ if (ret < 0)
++ return std::make_tuple(nullptr, 0);
++
++ bsf->par_in->codec_id = codecId;
++
++ ret = av_bsf_init(bsf);
++ if (ret < 0)
++ {
++ av_bsf_free(&bsf);
++ return std::make_tuple(nullptr, 0);
++ }
++
++ AVPacket* dstPkt = av_packet_alloc();
++ if (!dstPkt)
++ {
++ CLog::LogF(LOGERROR, "failed to allocate packet");
++
++ av_bsf_free(&bsf);
++ return std::make_tuple(nullptr, 0);
++ }
++ AVPacket* pktRef = dstPkt;
++
++ ret = av_packet_ref(pktRef, pkt);
++ if (ret < 0)
++ {
++ av_bsf_free(&bsf);
++ av_packet_free(&dstPkt);
++ return std::make_tuple(nullptr, 0);
++ }
++
++ ret = av_bsf_send_packet(bsf, pktRef);
++ if (ret < 0)
++ {
++ av_packet_unref(pktRef);
++ av_bsf_free(&bsf);
++ av_packet_free(&dstPkt);
++ return std::make_tuple(nullptr, 0);
++ }
++
++ ret = 0;
++ while (ret >= 0)
++ {
++ ret = av_bsf_receive_packet(bsf, pktRef);
++ if (ret < 0)
++ {
++ if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
++ break;
++
++ continue;
++ }
++
++ size_t retExtraDataSize = 0;
++ uint8_t* retExtraData =
++ av_packet_get_side_data(pktRef, AV_PKT_DATA_NEW_EXTRADATA, &retExtraDataSize);
++ if (retExtraData && retExtraDataSize > 0 && retExtraDataSize < FF_MAX_EXTRADATA_SIZE)
++ {
++ extraData = static_cast<uint8_t*>(av_malloc(retExtraDataSize + AV_INPUT_BUFFER_PADDING_SIZE));
++ if (!extraData)
++ {
++ CLog::LogF(LOGERROR, "failed to allocate {} bytes for extradata", retExtraDataSize);
++
++ av_packet_unref(pktRef);
++ av_bsf_free(&bsf);
++ av_packet_free(&dstPkt);
++ return std::make_tuple(nullptr, 0);
++ }
++
++ CLog::LogF(LOGDEBUG, "fetching extradata, extradata_size({})", retExtraDataSize);
++
++ memcpy(extraData, retExtraData, retExtraDataSize);
++ memset(extraData + retExtraDataSize, 0, AV_INPUT_BUFFER_PADDING_SIZE);
++ extraDataSize = retExtraDataSize;
++
++ av_packet_unref(pktRef);
++ break;
++ }
++
++ av_packet_unref(pktRef);
++ }
++
++ av_bsf_free(&bsf);
++ av_packet_free(&dstPkt);
++#else
++ if (codecCtx && parserCtx && parserCtx->parser && parserCtx->parser->split)
++ extraDataSize = parserCtx->parser->split(codecCtx, pkt->data, pkt->size);
++
++ if (extraDataSize <= 0 || extraDataSize >= FF_MAX_EXTRADATA_SIZE)
++ {
++ CLog::LogF(LOGDEBUG, "fetched extradata of weird size {}", extraDataSize);
++ return std::make_tuple(nullptr, 0);
++ }
++
++ extraData = static_cast<uint8_t*>(av_malloc(extraDataSize + AV_INPUT_BUFFER_PADDING_SIZE));
++ if (!extraData)
++ {
++ CLog::LogF(LOGERROR, "failed to allocate {} bytes for extradata", extraDataSize);
++ return std::make_tuple(nullptr, 0);
++ }
++
++ CLog::LogF(LOGDEBUG, "fetching extradata, extradata_size({})", extraDataSize);
++
++ memcpy(extraData, pkt->data, extraDataSize);
++ memset(extraData + extraDataSize, 0, AV_INPUT_BUFFER_PADDING_SIZE);
++#endif
++
++ return std::make_tuple(extraData, extraDataSize);
++}
+diff --git a/xbmc/cores/FFmpeg.h b/xbmc/cores/FFmpeg.h
+index 8230701ba7..22a253e191 100644
+--- a/xbmc/cores/FFmpeg.h
++++ b/xbmc/cores/FFmpeg.h
+@@ -22,6 +22,8 @@ extern "C" {
+ #include <libpostproc/postprocess.h>
+ }
+
++#include <tuple>
++
+ // https://github.com/FFmpeg/FFmpeg/blob/56450a0ee4/doc/APIchanges#L18-L26
+ #if LIBAVFORMAT_BUILD >= AV_VERSION_INT(59, 0, 100)
+ #define FFMPEG_FMT_CONST const
+@@ -77,3 +79,6 @@ public:
+ int level;
+ };
+
++std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt,
++ const AVCodecParserContext* parserCtx,
++ AVCodecContext* codecCtx);
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+index 70f0562462..f42710282d 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+@@ -14,10 +14,9 @@
+ #include "cores/VideoPlayer/Interface/TimingConstants.h"
+ #include "utils/log.h"
+
++#include <tuple>
+ #include <utility>
+
+-#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE)
+-
+ class CDemuxStreamClientInternal
+ {
+ public:
+@@ -150,17 +149,26 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ stream->m_context->time_base.den = DVD_TIME_BASE;
+ }
+
+- if (stream->m_parser_split && stream->m_parser->parser->split)
++ if (stream->m_parser_split && stream->m_parser && stream->m_parser->parser)
+ {
+- int len = stream->m_parser->parser->split(stream->m_context, pkt->pData, pkt->iSize);
+- if (len > 0 && len < FF_MAX_EXTRADATA_SIZE)
++ AVPacket* avpkt = av_packet_alloc();
++ if (!avpkt)
++ {
++ CLog::LogF(LOGERROR, "av_packet_alloc failed: {}", strerror(errno));
++ return false;
++ }
++
++ avpkt->data = pkt->pData;
++ avpkt->size = pkt->iSize;
++ avpkt->dts = avpkt->pts = AV_NOPTS_VALUE;
++
++ auto [retExtraData, len] = GetPacketExtradata(avpkt, stream->m_parser, stream->m_context);
++ if (len > 0)
+ {
+ st->changes++;
+ st->disabled = false;
+ st->ExtraSize = len;
+- st->ExtraData = std::make_unique<uint8_t[]>(len + AV_INPUT_BUFFER_PADDING_SIZE);
+- memcpy(st->ExtraData.get(), pkt->pData, len);
+- memset(st->ExtraData.get() + len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
++ st->ExtraData = std::unique_ptr<uint8_t[]>(retExtraData);
+ stream->m_parser_split = false;
+ change = true;
+ CLog::Log(LOGDEBUG, "CDVDDemuxClient::ParsePacket - split extradata");
+@@ -168,21 +176,12 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ // Allow ffmpeg to transport codec information to stream->m_context
+ if (!avcodec_open2(stream->m_context, stream->m_context->codec, nullptr))
+ {
+- AVPacket* avpkt = av_packet_alloc();
+- if (!avpkt)
+- {
+- CLog::Log(LOGERROR, "CDVDDemuxClient::{} - av_packet_alloc failed: {}", __FUNCTION__,
+- strerror(errno));
+- return false;
+- }
+- avpkt->data = pkt->pData;
+- avpkt->size = pkt->iSize;
+- avpkt->dts = avpkt->pts = AV_NOPTS_VALUE;
+ avcodec_send_packet(stream->m_context, avpkt);
+ avcodec_close(stream->m_context);
+- av_packet_free(&avpkt);
+ }
+ }
++
++ av_packet_free(&avpkt);
+ }
+
+ uint8_t *outbuf = nullptr;
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+index 4ad52cd32a..3081dd8e9a 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+@@ -34,6 +34,7 @@
+
+ #include <mutex>
+ #include <sstream>
++#include <tuple>
+ #include <utility>
+
+ extern "C"
+@@ -105,8 +106,6 @@ bool AttachmentIsFont(const AVDictionaryEntry* dict)
+ }
+ } // namespace
+
+-#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE)
+-
+ std::string CDemuxStreamAudioFFmpeg::GetStreamName()
+ {
+ if (!m_stream)
+@@ -2358,44 +2357,36 @@ void CDVDDemuxFFmpeg::ParsePacket(AVPacket* pkt)
+
+ if (parser->second->m_parserCtx &&
+ parser->second->m_parserCtx->parser &&
+- parser->second->m_parserCtx->parser->split &&
+ !st->codecpar->extradata)
+ {
+- int i = parser->second->m_parserCtx->parser->split(parser->second->m_codecCtx, pkt->data, pkt->size);
+- if (i > 0 && i < FF_MAX_EXTRADATA_SIZE)
++ auto [retExtraData, i] =
++ GetPacketExtradata(pkt, parser->second->m_parserCtx, parser->second->m_codecCtx);
++ if (i > 0)
+ {
+- st->codecpar->extradata = (uint8_t*)av_malloc(i + AV_INPUT_BUFFER_PADDING_SIZE);
+- if (st->codecpar->extradata)
+- {
+- CLog::Log(LOGDEBUG,
+- "CDVDDemuxFFmpeg::ParsePacket() fetching extradata, extradata_size({})", i);
+- st->codecpar->extradata_size = i;
+- memcpy(st->codecpar->extradata, pkt->data, i);
+- memset(st->codecpar->extradata + i, 0, AV_INPUT_BUFFER_PADDING_SIZE);
++ st->codecpar->extradata_size = i;
++ st->codecpar->extradata = retExtraData;
+
+- if (parser->second->m_parserCtx->parser->parser_parse)
++ if (parser->second->m_parserCtx->parser->parser_parse)
++ {
++ parser->second->m_codecCtx->extradata = st->codecpar->extradata;
++ parser->second->m_codecCtx->extradata_size = st->codecpar->extradata_size;
++ const uint8_t* outbufptr;
++ int bufSize;
++ parser->second->m_parserCtx->flags |= PARSER_FLAG_COMPLETE_FRAMES;
++ parser->second->m_parserCtx->parser->parser_parse(parser->second->m_parserCtx,
++ parser->second->m_codecCtx, &outbufptr,
++ &bufSize, pkt->data, pkt->size);
++ parser->second->m_codecCtx->extradata = nullptr;
++ parser->second->m_codecCtx->extradata_size = 0;
++
++ if (parser->second->m_parserCtx->width != 0)
+ {
+- parser->second->m_codecCtx->extradata = st->codecpar->extradata;
+- parser->second->m_codecCtx->extradata_size = st->codecpar->extradata_size;
+- const uint8_t* outbufptr;
+- int bufSize;
+- parser->second->m_parserCtx->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+- parser->second->m_parserCtx->parser->parser_parse(parser->second->m_parserCtx,
+- parser->second->m_codecCtx,
+- &outbufptr, &bufSize,
+- pkt->data, pkt->size);
+- parser->second->m_codecCtx->extradata = nullptr;
+- parser->second->m_codecCtx->extradata_size = 0;
+-
+- if (parser->second->m_parserCtx->width != 0)
+- {
+- st->codecpar->width = parser->second->m_parserCtx->width;
+- st->codecpar->height = parser->second->m_parserCtx->height;
+- }
+- else
+- {
+- CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::ParsePacket() invalid width/height");
+- }
++ st->codecpar->width = parser->second->m_parserCtx->width;
++ st->codecpar->height = parser->second->m_parserCtx->height;
++ }
++ else
++ {
++ CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::ParsePacket() invalid width/height");
+ }
+ }
+ }
+--
+2.35.1
+
+From d61499e7f8451bf080112a3f1332c8826d6ce1ac Mon Sep 17 00:00:00 2001
+From: fritsch <Peter.Fruehberger@gmail.com>
+Date: Tue, 17 Jan 2023 20:28:31 +0100
+Subject: [PATCH] AEEncoderFFmpeg: Break when one packet is received
+
+---
+ .../AudioEngine/Encoders/AEEncoderFFmpeg.cpp | 23 +++++++++----------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+index ba35c9506395e..bc6ce300ad028 100644
+--- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
++++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
+@@ -333,43 +333,42 @@ int CAEEncoderFFmpeg::Encode(uint8_t *in, int in_size, uint8_t *out, int out_siz
+
+ avcodec_fill_audio_frame(frame, channelNum, m_CodecCtx->sample_fmt, in, in_size, 0);
+
+- pkt->size = out_size;
+- pkt->data = out;
+-
+ /* encode it */
+ err = avcodec_send_frame(m_CodecCtx, frame);
+ if (err < 0)
+ throw FFMpegException("Error sending a frame for encoding (error '{}')",
+ FFMpegErrorToString(err));
+
+- while (err >= 0)
++ err = avcodec_receive_packet(m_CodecCtx, pkt);
++ // err < 0 - we cannot cope with it
++ // err is EAGAIN or EOF - return to caller as well
++ if (err >= 0)
+ {
+- err = avcodec_receive_packet(m_CodecCtx, pkt);
+- if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
++ if (pkt->size <= out_size)
+ {
+-#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
+- LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
+- av_channel_layout_uninit(&frame->ch_layout);
+-#endif
+- av_frame_free(&frame);
+- av_packet_free(&pkt);
+- return (err == AVERROR(EAGAIN)) ? -1 : 0;
++ memset(out, 0, out_size);
++ memcpy(out, pkt->data, pkt->size);
++ size = pkt->size;
+ }
+- else if (err < 0)
++ else
+ {
+- throw FFMpegException("Error during encoding (error '{}')", FFMpegErrorToString(err));
++ CLog::LogF(LOGERROR, "Encoded pkt size ({}) is bigger than buffer ({})", pkt->size,
++ out_size);
+ }
+
+ av_packet_unref(pkt);
+ }
+-
+- size = pkt->size;
+ }
+ catch (const FFMpegException& caught)
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::{} - {}", __func__, caught.what());
+ }
+
++#if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
++ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
++ av_channel_layout_uninit(&frame->ch_layout);
++#endif
++
+ /* free temporary data */
+ av_frame_free(&frame);
diff --git a/debian/patches/workarounds/0003-xbmc-libdvd_vfs-enen92.patch b/debian/patches/workarounds/0003-xbmc-libdvd_vfs-enen92.patch
new file mode 100644
index 0000000..d3ac4ee
--- /dev/null
+++ b/debian/patches/workarounds/0003-xbmc-libdvd_vfs-enen92.patch
@@ -0,0 +1,5056 @@
+From 089f6faaeaf67e3fb027f60635dd5331bcca9c47 Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Sat, 25 Jun 2022 16:17:09 +0100
+Subject: [PATCH 1/9] Unwrap dll
+
+---
+ xbmc/CMakeLists.txt | 1 -
+ xbmc/DllPaths.h | 4 +-
+ xbmc/DllPaths_generated.h.in | 3 -
+ xbmc/DllPaths_generated_android.h.in | 3 -
+ xbmc/DllPaths_win32.h | 12 -
+ .../DVDInputStreams/CMakeLists.txt | 1 -
+ .../DVDInputStreamNavigator.cpp | 207 +++--
+ .../DVDInputStreams/DVDInputStreamNavigator.h | 5 +-
+ .../VideoPlayer/DVDInputStreams/DllDvdNav.h | 275 ------
+ .../DVDInputStreams/dvdnav/config.h | 76 --
+ .../DVDInputStreams/dvdnav/dvd_reader.h | 370 --------
+ .../DVDInputStreams/dvdnav/dvd_types.h | 282 -------
+ .../DVDInputStreams/dvdnav/dvdnav.h | 789 ------------------
+ .../DVDInputStreams/dvdnav/dvdnav_events.h | 226 -----
+ .../DVDInputStreams/dvdnav/ifo_types.h | 754 -----------------
+ .../DVDInputStreams/dvdnav/nav_types.h | 258 ------
+ .../DVDInputStreams/dvdnav/version.h | 29 -
+ 17 files changed, 106 insertions(+), 3189 deletions(-)
+ delete mode 100644 xbmc/DllPaths_win32.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/DllDvdNav.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/config.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_reader.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_types.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav_events.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/ifo_types.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/nav_types.h
+ delete mode 100644 xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/version.h
+
+diff --git a/xbmc/CMakeLists.txt b/xbmc/CMakeLists.txt
+index 4c69707dd5..4db2dc79a8 100644
+--- a/xbmc/CMakeLists.txt
++++ b/xbmc/CMakeLists.txt
+@@ -42,7 +42,6 @@ set(HEADERS AutoSwitch.h
+ DatabaseManager.h
+ DbUrl.h
+ DllPaths.h
+- DllPaths_win32.h
+ DynamicDll.h
+ FileItem.h
+ FileItemListModification.h
+diff --git a/xbmc/DllPaths.h b/xbmc/DllPaths.h
+index 09bdd1ba3d..33fb46635e 100644
+--- a/xbmc/DllPaths.h
++++ b/xbmc/DllPaths.h
+@@ -8,9 +8,7 @@
+
+ #pragma once
+
+-#ifdef TARGET_WINDOWS
+-#include "DllPaths_win32.h"
+-#elif defined (TARGET_ANDROID)
++#if defined (TARGET_ANDROID)
+ #include "DllPaths_generated_android.h"
+ #else
+ #include "DllPaths_generated.h"
+diff --git a/xbmc/DllPaths_generated.h.in b/xbmc/DllPaths_generated.h.in
+index f29faa41fd..405a7b5b29 100644
+--- a/xbmc/DllPaths_generated.h.in
++++ b/xbmc/DllPaths_generated.h.in
+@@ -11,9 +11,6 @@
+ /* prefix install location */
+ #define PREFIX_USR_PATH "@prefix@"
+
+-/* VideoPlayer */
+-#define DLL_PATH_LIBDVDNAV "special://xbmcbin/system/players/VideoPlayer/libdvdnav-@ARCH@.so"
+-
+ /* sse4 */
+ #define DLL_PATH_LIBSSE4 "special://xbmcbin/system/libsse4-@ARCH@.so"
+
+diff --git a/xbmc/DllPaths_generated_android.h.in b/xbmc/DllPaths_generated_android.h.in
+index d15988e361..70ceb669aa 100644
+--- a/xbmc/DllPaths_generated_android.h.in
++++ b/xbmc/DllPaths_generated_android.h.in
+@@ -17,8 +17,5 @@
+ // We only keep @ARCH@ here to retain the same structure as *nix.
+ // * foo.so will be renamed libfoo.so in the packaging stage
+
+-/* VideoPlayer */
+-#define DLL_PATH_LIBDVDNAV "libdvdnav-@ARCH@.so"
+-
+ /* Android's libui for gralloc */
+ #define DLL_PATH_LIBUI "libui.so"
+diff --git a/xbmc/DllPaths_win32.h b/xbmc/DllPaths_win32.h
+deleted file mode 100644
+index ea41058a54..0000000000
+--- a/xbmc/DllPaths_win32.h
++++ /dev/null
+@@ -1,12 +0,0 @@
+-/*
+- * Copyright (C) 2005-2018 Team Kodi
+- * This file is part of Kodi - https://kodi.tv
+- *
+- * SPDX-License-Identifier: GPL-2.0-or-later
+- * See LICENSES/README.md for more information.
+- */
+-
+-#pragma once
+-
+-/* VideoPlayer */
+-#define DLL_PATH_LIBDVDNAV "special://xbmcbin/libdvdnav.dll"
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/CMakeLists.txt b/xbmc/cores/VideoPlayer/DVDInputStreams/CMakeLists.txt
+index 576ddda150..1aecf6f032 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/CMakeLists.txt
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/CMakeLists.txt
+@@ -22,7 +22,6 @@ set(HEADERS BlurayStateSerializer.h
+ DVDInputStreamNavigator.h
+ DVDInputStreamStack.h
+ DVDStateSerializer.h
+- DllDvdNav.h
+ InputStreamAddon.h
+ InputStreamMultiStreams.h
+ InputStreamMultiSource.h
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index e7b54e3351..859762348b 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -109,9 +109,6 @@ bool CDVDInputStreamNavigator::Open()
+ CEnvironment::putenv("DVDCSS_CACHE=" + CSpecialProtocol::TranslatePath("special://masterprofile/cache"));
+ #endif
+
+- // load libdvdnav.dll
+- if (!m_dll.Load())
+- return false;
+
+ // load the dvd language codes
+ // g_LangCodeExpander.LoadStandardCodes();
+@@ -146,10 +143,10 @@ bool CDVDInputStreamNavigator::Open()
+ // if dvd image file (ISO or alike) open using libdvdnav stream callback functions
+ m_pstream.reset(new CDVDInputStreamFile(m_item, XFILE::READ_TRUNCATED | XFILE::READ_BITRATE | XFILE::READ_CHUNKED));
+ #if DVDNAV_VERSION >= 60100
+- if (!m_pstream->Open() || m_dll.dvdnav_open_stream2(&m_dvdnav, m_pstream.get(), &loggerCallback,
++ if (!m_pstream->Open() || dvdnav_open_stream2(&m_dvdnav, m_pstream.get(), &loggerCallback,
+ &m_dvdnav_stream_cb) != DVDNAV_STATUS_OK)
+ #else
+- if (!m_pstream->Open() || m_dll.dvdnav_open_stream(&m_dvdnav, m_pstream.get(), &m_dvdnav_stream_cb) != DVDNAV_STATUS_OK)
++ if (!m_pstream->Open() || dvdnav_open_stream(&m_dvdnav, m_pstream.get(), &m_dvdnav_stream_cb) != DVDNAV_STATUS_OK)
+ #endif
+ {
+ CLog::Log(LOGERROR, "Error opening image file or Error on dvdnav_open_stream");
+@@ -158,10 +155,10 @@ bool CDVDInputStreamNavigator::Open()
+ }
+ }
+ #if DVDNAV_VERSION >= 60100
+- else if (m_dll.dvdnav_open2(&m_dvdnav, nullptr, &loggerCallback, path.c_str()) !=
++ else if (dvdnav_open2(&m_dvdnav, nullptr, &loggerCallback, path.c_str()) !=
+ DVDNAV_STATUS_OK)
+ #else
+- else if (m_dll.dvdnav_open(&m_dvdnav, path.c_str()) != DVDNAV_STATUS_OK)
++ else if (dvdnav_open(&m_dvdnav, path.c_str()) != DVDNAV_STATUS_OK)
+ #endif
+ {
+ CLog::Log(LOGERROR, "Error on dvdnav_open");
+@@ -176,16 +173,16 @@ bool CDVDInputStreamNavigator::Open()
+ else
+ {
+ // find out what region dvd reports itself to be from, and use that as mask if available
+- if (m_dll.dvdnav_get_disk_region_mask(m_dvdnav, &mask) == DVDNAV_STATUS_ERR)
++ if (dvdnav_get_disk_region_mask(m_dvdnav, &mask) == DVDNAV_STATUS_ERR)
+ {
+ CLog::LogF(LOGERROR, "Error getting DVD region code: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ mask = 0xff;
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "{} - Setting region mask {:02x}", __FUNCTION__, mask);
+- m_dll.dvdnav_set_region_mask(m_dvdnav, mask);
++ dvdnav_set_region_mask(m_dvdnav, mask);
+
+ // get default language settings
+ char language_menu[3];
+@@ -206,48 +203,48 @@ bool CDVDInputStreamNavigator::Open()
+ if (language_subtitle[0] == '\0') strcpy(language_subtitle, "en");
+
+ // set default language settings
+- if (m_dll.dvdnav_menu_language_select(m_dvdnav, (char*)language_menu) != DVDNAV_STATUS_OK)
++ if (dvdnav_menu_language_select(m_dvdnav, (char*)language_menu) != DVDNAV_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "Error on setting default menu language: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ CLog::Log(LOGERROR, "Defaulting to \"en\"");
+ //! @bug libdvdnav isn't const correct
+- m_dll.dvdnav_menu_language_select(m_dvdnav, const_cast<char*>("en"));
++ dvdnav_menu_language_select(m_dvdnav, const_cast<char*>("en"));
+ }
+
+- if (m_dll.dvdnav_audio_language_select(m_dvdnav, (char*)language_audio) != DVDNAV_STATUS_OK)
++ if (dvdnav_audio_language_select(m_dvdnav, (char*)language_audio) != DVDNAV_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "Error on setting default audio language: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ CLog::Log(LOGERROR, "Defaulting to \"en\"");
+ //! @bug libdvdnav isn't const correct
+- m_dll.dvdnav_audio_language_select(m_dvdnav, const_cast<char*>("en"));
++ dvdnav_audio_language_select(m_dvdnav, const_cast<char*>("en"));
+ }
+
+- if (m_dll.dvdnav_spu_language_select(m_dvdnav, (char*)language_subtitle) != DVDNAV_STATUS_OK)
++ if (dvdnav_spu_language_select(m_dvdnav, (char*)language_subtitle) != DVDNAV_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "Error on setting default subtitle language: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ CLog::Log(LOGERROR, "Defaulting to \"en\"");
+ //! @bug libdvdnav isn't const correct
+- m_dll.dvdnav_spu_language_select(m_dvdnav, const_cast<char*>("en"));
++ dvdnav_spu_language_select(m_dvdnav, const_cast<char*>("en"));
+ }
+
+ // set read ahead cache usage
+- if (m_dll.dvdnav_set_readahead_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
++ if (dvdnav_set_readahead_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "Error on dvdnav_set_readahead_flag: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ Close();
+ return false;
+ }
+
+ // set the PGC positioning flag to have position information relatively to the
+ // whole feature instead of just relatively to the current chapter
+- if (m_dll.dvdnav_set_PGC_positioning_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
++ if (dvdnav_set_PGC_positioning_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "Error on dvdnav_set_PGC_positioning_flag: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ Close();
+ return false;
+ }
+@@ -260,18 +257,18 @@ bool CDVDInputStreamNavigator::Open()
+ uint8_t* buf_ptr = buf;
+
+ // must startup vm and pgc
+- m_dll.dvdnav_get_next_cache_block(m_dvdnav,&buf_ptr,&event,&len);
+- m_dll.dvdnav_sector_search(m_dvdnav, 0, SEEK_SET);
++ dvdnav_get_next_cache_block(m_dvdnav,&buf_ptr,&event,&len);
++ dvdnav_sector_search(m_dvdnav, 0, SEEK_SET);
+
+ // first try title menu
+- if(m_dll.dvdnav_menu_call(m_dvdnav, DVD_MENU_Title) != DVDNAV_STATUS_OK)
++ if(dvdnav_menu_call(m_dvdnav, DVD_MENU_Title) != DVDNAV_STATUS_OK)
+ {
+ CLog::Log(LOGERROR, "Error on dvdnav_menu_call(Title): {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ // next try root menu
+- if(m_dll.dvdnav_menu_call(m_dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK )
++ if(dvdnav_menu_call(m_dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK )
+ CLog::Log(LOGERROR, "Error on dvdnav_menu_call(Root): {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ }
+ }
+
+@@ -295,9 +292,9 @@ void CDVDInputStreamNavigator::Close()
+ if (!m_dvdnav) return;
+
+ // finish off by closing the dvdnav device
+- if (m_dll.dvdnav_close(m_dvdnav) != DVDNAV_STATUS_OK)
++ if (dvdnav_close(m_dvdnav) != DVDNAV_STATUS_OK)
+ {
+- CLog::Log(LOGERROR, "Error on dvdnav_close: {}", m_dll.dvdnav_err_to_string(m_dvdnav));
++ CLog::Log(LOGERROR, "Error on dvdnav_close: {}", dvdnav_err_to_string(m_dvdnav));
+ return ;
+ }
+
+@@ -383,11 +380,11 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ result = DVDNAV_STATUS_OK;
+ }
+ else
+- result = m_dll.dvdnav_get_next_cache_block(m_dvdnav, &buf, &m_lastevent, &len);
++ result = dvdnav_get_next_cache_block(m_dvdnav, &buf, &m_lastevent, &len);
+
+ if (result == DVDNAV_STATUS_ERR)
+ {
+- CLog::Log(LOGERROR, "Error getting next block: {}", m_dll.dvdnav_err_to_string(m_dvdnav));
++ CLog::Log(LOGERROR, "Error getting next block: {}", dvdnav_err_to_string(m_dvdnav));
+ m_bEOF = true;
+ return NAVRESULT_ERROR;
+ }
+@@ -488,7 +485,7 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ }
+ else
+ {
+- bool menu = (0 == m_dll.dvdnav_is_domain_vts(m_dvdnav));
++ bool menu = (0 == dvdnav_is_domain_vts(m_dvdnav));
+ if (menu != m_bInMenu)
+ {
+ m_bInMenu = menu;
+@@ -508,13 +505,13 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ uint32_t pos = 0;
+ uint32_t len = 0;
+
+- m_dll.dvdnav_current_title_info(m_dvdnav, &m_iTitle, &m_iPart);
+- m_dll.dvdnav_get_number_of_titles(m_dvdnav, &m_iTitleCount);
++ dvdnav_current_title_info(m_dvdnav, &m_iTitle, &m_iPart);
++ dvdnav_get_number_of_titles(m_dvdnav, &m_iTitleCount);
+ if(m_iTitle > 0)
+- m_dll.dvdnav_get_number_of_parts(m_dvdnav, m_iTitle, &m_iPartCount);
++ dvdnav_get_number_of_parts(m_dvdnav, m_iTitle, &m_iPartCount);
+ else
+ m_iPartCount = 0;
+- m_dll.dvdnav_get_position(m_dvdnav, &pos, &len);
++ dvdnav_get_position(m_dvdnav, &pos, &len);
+
+ // get chapters' timestamps if we have not cached them yet
+ if (m_mapTitleChapters.find(m_iTitle) == m_mapTitleChapters.end())
+@@ -522,7 +519,7 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ uint64_t* times = NULL;
+ uint64_t duration;
+ //dvdnav_describe_title_chapters returns 0 on failure and NULL for times
+- int entries = m_dll.dvdnav_describe_title_chapters(m_dvdnav, m_iTitle, &times, &duration);
++ int entries = dvdnav_describe_title_chapters(m_dvdnav, m_iTitle, &times, &duration);
+
+ if (entries != m_iPartCount)
+ CLog::Log(LOGDEBUG,
+@@ -570,11 +567,11 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+
+ // Calculate current time
+ //unsigned int pos, len;
+- //m_dll.dvdnav_get_position(m_dvdnav, &pos, &len);
++ //dvdnav_get_position(m_dvdnav, &pos, &len);
+ //m_iTime = (int)(((int64_t)m_iTotalTime * pos) / len);
+
+- pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
+- m_dll.dvdnav_get_current_nav_dsi(m_dvdnav);
++ pci_t* pci = dvdnav_get_current_nav_pci(m_dvdnav);
++ dvdnav_get_current_nav_dsi(m_dvdnav);
+
+ if(!pci)
+ {
+@@ -583,7 +580,7 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ }
+
+ /* if we have any buttons or are not in vts domain we assume we are in menu */
+- bool menu = pci->hli.hl_gi.hli_ss || (0 == m_dll.dvdnav_is_domain_vts(m_dvdnav));
++ bool menu = pci->hli.hl_gi.hli_ss || (0 == dvdnav_is_domain_vts(m_dvdnav));
+ if (menu != m_bInMenu)
+ {
+ m_bInMenu = menu;
+@@ -611,7 +608,7 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ m_iVobUnitStart = pci->pci_gi.vobu_s_ptm;
+ m_iVobUnitStop = pci->pci_gi.vobu_e_ptm;
+
+- m_iTime = (int) ( m_dll.dvdnav_get_current_time(m_dvdnav) / 90 );
++ m_iTime = (int) ( dvdnav_get_current_time(m_dvdnav) / 90 );
+
+ iNavresult = m_pVideoPlayer->OnDiscNavResult((void*)pci, DVDNAV_NAV_PACKET);
+ }
+@@ -650,7 +647,7 @@ int CDVDInputStreamNavigator::ProcessBlock(uint8_t* dest_buffer, int* read)
+ // probably not needed since function will check if buf
+ // is part of the internal cache, but do it for good measure
+ if( buf != m_lastblock )
+- m_dll.dvdnav_free_cache_block(m_dvdnav, buf);
++ dvdnav_free_cache_block(m_dvdnav, buf);
+
+ return iNavresult;
+ }
+@@ -662,11 +659,11 @@ bool CDVDInputStreamNavigator::SetActiveAudioStream(int iId)
+ if (!m_dvdnav)
+ return false;
+
+- dvdnav_status_t ret = m_dll.dvdnav_set_active_stream(m_dvdnav, iId, DVD_AUDIO_STREAM);
++ dvdnav_status_t ret = dvdnav_set_active_stream(m_dvdnav, iId, DVD_AUDIO_STREAM);
+ if (ret == DVDNAV_STATUS_ERR)
+ {
+ CLog::LogF(LOGERROR, "dvdnav_set_active_stream (audio) failed: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ }
+
+ return ret == DVDNAV_STATUS_OK;
+@@ -679,11 +676,11 @@ bool CDVDInputStreamNavigator::SetActiveSubtitleStream(int iId)
+ if (!m_dvdnav)
+ return false;
+
+- dvdnav_status_t ret = m_dll.dvdnav_set_active_stream(m_dvdnav, iId, DVD_SUBTITLE_STREAM);
++ dvdnav_status_t ret = dvdnav_set_active_stream(m_dvdnav, iId, DVD_SUBTITLE_STREAM);
+ if (ret == DVDNAV_STATUS_ERR)
+ {
+ CLog::LogF(LOGERROR, "dvdnav_set_active_stream (subtitles) failed: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ }
+
+ return ret == DVDNAV_STATUS_OK;
+@@ -693,14 +690,14 @@ void CDVDInputStreamNavigator::ActivateButton()
+ {
+ if (m_dvdnav)
+ {
+- m_dll.dvdnav_button_activate(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
++ dvdnav_button_activate(m_dvdnav, dvdnav_get_current_nav_pci(m_dvdnav));
+ }
+ }
+
+ void CDVDInputStreamNavigator::SelectButton(int iButton)
+ {
+ if (!m_dvdnav) return;
+- m_dll.dvdnav_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav), iButton);
++ dvdnav_button_select(m_dvdnav, dvdnav_get_current_nav_pci(m_dvdnav), iButton);
+ }
+
+ int CDVDInputStreamNavigator::GetCurrentButton()
+@@ -711,10 +708,10 @@ int CDVDInputStreamNavigator::GetCurrentButton()
+ }
+
+ int button = 0;
+- if (m_dll.dvdnav_get_current_highlight(m_dvdnav, &button) == DVDNAV_STATUS_ERR)
++ if (dvdnav_get_current_highlight(m_dvdnav, &button) == DVDNAV_STATUS_ERR)
+ {
+ CLog::LogF(LOGERROR, "dvdnav_get_current_highlight failed: {}",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ return -1;
+ }
+ return button;
+@@ -725,7 +722,7 @@ void CDVDInputStreamNavigator::CheckButtons()
+ if (m_dvdnav && m_bCheckButtons)
+ {
+ m_bCheckButtons = false;
+- pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
++ pci_t* pci = dvdnav_get_current_nav_pci(m_dvdnav);
+ int iCurrentButton = GetCurrentButton();
+
+ if( iCurrentButton > 0 && iCurrentButton < 37 )
+@@ -751,7 +748,7 @@ void CDVDInputStreamNavigator::CheckButtons()
+ {
+ CLog::Log(LOGWARNING, "CDVDInputStreamNavigator: found invalid button({})", iCurrentButton);
+ CLog::Log(LOGWARNING, "CDVDInputStreamNavigator: switching to button({}) instead", i + 1);
+- m_dll.dvdnav_button_select(m_dvdnav, pci, i + 1);
++ dvdnav_button_select(m_dvdnav, pci, i + 1);
+ break;
+ }
+ }
+@@ -762,7 +759,7 @@ int CDVDInputStreamNavigator::GetTotalButtons()
+ {
+ if (!m_dvdnav) return 0;
+
+- pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
++ pci_t* pci = dvdnav_get_current_nav_pci(m_dvdnav);
+
+ int counter = 0;
+ for (const btni_t& buttonInfo : pci->hli.btnit)
+@@ -780,30 +777,30 @@ int CDVDInputStreamNavigator::GetTotalButtons()
+
+ void CDVDInputStreamNavigator::OnUp()
+ {
+- if (m_dvdnav) m_dll.dvdnav_upper_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
++ if (m_dvdnav) dvdnav_upper_button_select(m_dvdnav, dvdnav_get_current_nav_pci(m_dvdnav));
+ }
+
+ void CDVDInputStreamNavigator::OnDown()
+ {
+- if (m_dvdnav) m_dll.dvdnav_lower_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
++ if (m_dvdnav) dvdnav_lower_button_select(m_dvdnav, dvdnav_get_current_nav_pci(m_dvdnav));
+ }
+
+ void CDVDInputStreamNavigator::OnLeft()
+ {
+- if (m_dvdnav) m_dll.dvdnav_left_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
++ if (m_dvdnav) dvdnav_left_button_select(m_dvdnav, dvdnav_get_current_nav_pci(m_dvdnav));
+ }
+
+ void CDVDInputStreamNavigator::OnRight()
+ {
+- if (m_dvdnav) m_dll.dvdnav_right_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
++ if (m_dvdnav) dvdnav_right_button_select(m_dvdnav, dvdnav_get_current_nav_pci(m_dvdnav));
+ }
+
+ bool CDVDInputStreamNavigator::OnMouseMove(const CPoint &point)
+ {
+ if (m_dvdnav)
+ {
+- pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
+- return (DVDNAV_STATUS_OK == m_dll.dvdnav_mouse_select(m_dvdnav, pci, (int32_t)point.x, (int32_t)point.y));
++ pci_t* pci = dvdnav_get_current_nav_pci(m_dvdnav);
++ return (DVDNAV_STATUS_OK == dvdnav_mouse_select(m_dvdnav, pci, (int32_t)point.x, (int32_t)point.y));
+ }
+ return false;
+ }
+@@ -812,8 +809,8 @@ bool CDVDInputStreamNavigator::OnMouseClick(const CPoint &point)
+ {
+ if (m_dvdnav)
+ {
+- pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
+- return (DVDNAV_STATUS_OK == m_dll.dvdnav_mouse_activate(m_dvdnav, pci, (int32_t)point.x, (int32_t)point.y));
++ pci_t* pci = dvdnav_get_current_nav_pci(m_dvdnav);
++ return (DVDNAV_STATUS_OK == dvdnav_mouse_activate(m_dvdnav, pci, (int32_t)point.x, (int32_t)point.y));
+ }
+ return false;
+ }
+@@ -825,12 +822,12 @@ bool CDVDInputStreamNavigator::OnMenu()
+ return false;
+ }
+
+- return m_dll.dvdnav_menu_call(m_dvdnav, DVD_MENU_Escape) == DVDNAV_STATUS_OK;
++ return dvdnav_menu_call(m_dvdnav, DVD_MENU_Escape) == DVDNAV_STATUS_OK;
+ }
+
+ void CDVDInputStreamNavigator::OnBack()
+ {
+- if (m_dvdnav) m_dll.dvdnav_go_up(m_dvdnav);
++ if (m_dvdnav) dvdnav_go_up(m_dvdnav);
+ }
+
+ // we don't allow skipping in menu's cause it will remove menu overlays
+@@ -838,7 +835,7 @@ void CDVDInputStreamNavigator::OnNext()
+ {
+ if (m_dvdnav && !(IsInMenu() && GetTotalButtons() > 0))
+ {
+- m_dll.dvdnav_next_pg_search(m_dvdnav);
++ dvdnav_next_pg_search(m_dvdnav);
+ }
+ }
+
+@@ -847,7 +844,7 @@ void CDVDInputStreamNavigator::OnPrevious()
+ {
+ if (m_dvdnav && !(IsInMenu() && GetTotalButtons() > 0))
+ {
+- m_dll.dvdnav_prev_pg_search(m_dvdnav);
++ dvdnav_prev_pg_search(m_dvdnav);
+ }
+ }
+
+@@ -855,13 +852,13 @@ void CDVDInputStreamNavigator::SkipStill()
+ {
+ if (!m_dvdnav)
+ return ;
+- m_dll.dvdnav_still_skip(m_dvdnav);
++ dvdnav_still_skip(m_dvdnav);
+ }
+
+ void CDVDInputStreamNavigator::SkipWait()
+ {
+ if (!m_dvdnav) return ;
+- m_dll.dvdnav_wait_skip(m_dvdnav);
++ dvdnav_wait_skip(m_dvdnav);
+ }
+
+ CDVDInputStream::ENextStream CDVDInputStreamNavigator::NextStream()
+@@ -886,7 +883,7 @@ int CDVDInputStreamNavigator::GetActiveSubtitleStream()
+ return activeStream;
+ }
+
+- const int8_t logicalSubStreamId = m_dll.dvdnav_get_active_spu_stream(m_dvdnav);
++ const int8_t logicalSubStreamId = dvdnav_get_active_spu_stream(m_dvdnav);
+ if (logicalSubStreamId < 0)
+ {
+ return activeStream;
+@@ -895,7 +892,7 @@ int CDVDInputStreamNavigator::GetActiveSubtitleStream()
+ int subStreamCount = GetSubTitleStreamCount();
+ for (int subpN = 0; subpN < subStreamCount; subpN++)
+ {
+- if (m_dll.dvdnav_get_spu_logical_stream(m_dvdnav, subpN) == logicalSubStreamId)
++ if (dvdnav_get_spu_logical_stream(m_dvdnav, subpN) == logicalSubStreamId)
+ {
+ activeStream = subpN;
+ break;
+@@ -913,7 +910,7 @@ SubtitleStreamInfo CDVDInputStreamNavigator::GetSubtitleStreamInfo(const int iId
+
+ subp_attr_t subp_attributes;
+
+- if (m_dll.dvdnav_get_spu_attr(m_dvdnav, iId, &subp_attributes) == DVDNAV_STATUS_OK)
++ if (dvdnav_get_spu_attr(m_dvdnav, iId, &subp_attributes) == DVDNAV_STATUS_OK)
+ {
+ SetSubtitleStreamName(info, subp_attributes);
+
+@@ -966,7 +963,7 @@ int CDVDInputStreamNavigator::GetSubTitleStreamCount()
+ {
+ return 0;
+ }
+- return m_dll.dvdnav_get_number_of_streams(m_dvdnav, DVD_SUBTITLE_STREAM);
++ return dvdnav_get_number_of_streams(m_dvdnav, DVD_SUBTITLE_STREAM);
+ }
+
+ int CDVDInputStreamNavigator::GetActiveAudioStream()
+@@ -976,7 +973,7 @@ int CDVDInputStreamNavigator::GetActiveAudioStream()
+ return -1;
+ }
+
+- const int8_t logicalAudioStreamId = m_dll.dvdnav_get_active_audio_stream(m_dvdnav);
++ const int8_t logicalAudioStreamId = dvdnav_get_active_audio_stream(m_dvdnav);
+ if (logicalAudioStreamId < 0)
+ {
+ return -1;
+@@ -986,7 +983,7 @@ int CDVDInputStreamNavigator::GetActiveAudioStream()
+ int audioStreamCount = GetAudioStreamCount();
+ for (int audioN = 0; audioN < audioStreamCount; audioN++)
+ {
+- if (m_dll.dvdnav_get_audio_logical_stream(m_dvdnav, audioN) == logicalAudioStreamId)
++ if (dvdnav_get_audio_logical_stream(m_dvdnav, audioN) == logicalAudioStreamId)
+ {
+ activeStream = audioN;
+ break;
+@@ -1083,7 +1080,7 @@ AudioStreamInfo CDVDInputStreamNavigator::GetAudioStreamInfo(const int iId)
+
+ audio_attr_t audio_attributes;
+
+- if (m_dll.dvdnav_get_audio_attr(m_dvdnav, iId, &audio_attributes) == DVDNAV_STATUS_OK)
++ if (dvdnav_get_audio_attr(m_dvdnav, iId, &audio_attributes) == DVDNAV_STATUS_OK)
+ {
+ SetAudioStreamName(info, audio_attributes);
+
+@@ -1105,7 +1102,7 @@ int CDVDInputStreamNavigator::GetAudioStreamCount()
+ {
+ return 0;
+ }
+- return m_dll.dvdnav_get_number_of_streams(m_dvdnav, DVD_AUDIO_STREAM);
++ return dvdnav_get_number_of_streams(m_dvdnav, DVD_AUDIO_STREAM);
+ }
+
+ int CDVDInputStreamNavigator::GetAngleCount()
+@@ -1115,7 +1112,7 @@ int CDVDInputStreamNavigator::GetAngleCount()
+
+ int number_of_angles;
+ int current_angle;
+- dvdnav_status_t status = m_dll.dvdnav_get_angle_info(m_dvdnav, &current_angle, &number_of_angles);
++ dvdnav_status_t status = dvdnav_get_angle_info(m_dvdnav, &current_angle, &number_of_angles);
+
+ if (status == DVDNAV_STATUS_OK)
+ return number_of_angles;
+@@ -1130,9 +1127,9 @@ int CDVDInputStreamNavigator::GetActiveAngle()
+
+ int number_of_angles;
+ int current_angle;
+- if (m_dll.dvdnav_get_angle_info(m_dvdnav, &current_angle, &number_of_angles) == DVDNAV_STATUS_ERR)
++ if (dvdnav_get_angle_info(m_dvdnav, &current_angle, &number_of_angles) == DVDNAV_STATUS_ERR)
+ {
+- CLog::LogF(LOGERROR, "Failed to get current angle: {}", m_dll.dvdnav_err_to_string(m_dvdnav));
++ CLog::LogF(LOGERROR, "Failed to get current angle: {}", dvdnav_err_to_string(m_dvdnav));
+ return -1;
+ }
+ return current_angle;
+@@ -1143,7 +1140,7 @@ bool CDVDInputStreamNavigator::SetAngle(int angle)
+ if (!m_dvdnav)
+ return false;
+
+- dvdnav_status_t status = m_dll.dvdnav_angle_change(m_dvdnav, angle);
++ dvdnav_status_t status = dvdnav_angle_change(m_dvdnav, angle);
+
+ return (status == DVDNAV_STATUS_OK);
+ }
+@@ -1164,8 +1161,8 @@ bool CDVDInputStreamNavigator::GetCurrentButtonInfo(CDVDOverlaySpu* pOverlayPict
+ return false;
+ }
+
+- if (DVDNAV_STATUS_OK == m_dll.dvdnav_get_highlight_area(
+- m_dll.dvdnav_get_current_nav_pci(m_dvdnav), button, iButtonType, &hl))
++ if (DVDNAV_STATUS_OK == dvdnav_get_highlight_area(
++ dvdnav_get_current_nav_pci(m_dvdnav), button, iButtonType, &hl))
+ {
+ // button cropping information
+ pOverlayPicture->crop_i_x_start = hl.sx;
+@@ -1213,10 +1210,10 @@ int CDVDInputStreamNavigator::GetTime()
+
+ bool CDVDInputStreamNavigator::PosTime(int iTimeInMsec)
+ {
+- if( m_dll.dvdnav_jump_to_sector_by_time(m_dvdnav, iTimeInMsec * 90, 0) == DVDNAV_STATUS_ERR )
++ if( dvdnav_jump_to_sector_by_time(m_dvdnav, iTimeInMsec * 90, 0) == DVDNAV_STATUS_ERR )
+ {
+ CLog::Log(LOGDEBUG, "dvdnav: dvdnav_jump_to_sector_by_time failed( {} )",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ return false;
+ }
+ m_iTime = iTimeInMsec;
+@@ -1243,26 +1240,26 @@ bool CDVDInputStreamNavigator::SeekChapter(int iChapter)
+
+ if (iChapter == (m_iPart + 1))
+ {
+- if (m_dll.dvdnav_next_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
++ if (dvdnav_next_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
+ {
+ CLog::Log(LOGERROR, "dvdnav: dvdnav_next_pg_search( {} )",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ return false;
+ }
+ }
+ else if (iChapter == (m_iPart - 1))
+ {
+- if (m_dll.dvdnav_prev_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
++ if (dvdnav_prev_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
+ {
+ CLog::Log(LOGERROR, "dvdnav: dvdnav_prev_pg_search( {} )",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ return false;
+ }
+ }
+- else if (m_dll.dvdnav_part_play(m_dvdnav, m_iTitle, iChapter) == DVDNAV_STATUS_ERR)
++ else if (dvdnav_part_play(m_dvdnav, m_iTitle, iChapter) == DVDNAV_STATUS_ERR)
+ {
+ CLog::Log(LOGERROR, "dvdnav: dvdnav_part_play failed( {} )",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ return false;
+ }
+
+@@ -1274,8 +1271,8 @@ bool CDVDInputStreamNavigator::SeekChapter(int iChapter)
+
+ float CDVDInputStreamNavigator::GetVideoAspectRatio()
+ {
+- int iAspect = m_dll.dvdnav_get_video_aspect(m_dvdnav);
+- int iPerm = m_dll.dvdnav_get_video_scale_permission(m_dvdnav);
++ int iAspect = dvdnav_get_video_aspect(m_dvdnav);
++ int iPerm = dvdnav_get_video_scale_permission(m_dvdnav);
+
+ //The video scale permissions should give if the source is letterboxed
+ //and such. should be able to give us info that we can zoom in automatically
+@@ -1298,7 +1295,7 @@ void CDVDInputStreamNavigator::EnableSubtitleStream(bool bEnable)
+ if (!m_dvdnav)
+ return;
+
+- m_dll.dvdnav_toggle_spu_stream(m_dvdnav, static_cast<uint8_t>(bEnable));
++ dvdnav_toggle_spu_stream(m_dvdnav, static_cast<uint8_t>(bEnable));
+ }
+
+ bool CDVDInputStreamNavigator::IsSubtitleStreamEnabled()
+@@ -1306,7 +1303,7 @@ bool CDVDInputStreamNavigator::IsSubtitleStreamEnabled()
+ if (!m_dvdnav)
+ return false;
+
+- return m_dll.dvdnav_get_active_spu_stream(m_dvdnav) >= 0;
++ return dvdnav_get_active_spu_stream(m_dvdnav) >= 0;
+ }
+
+ bool CDVDInputStreamNavigator::FillDVDState(DVDState& dvdState)
+@@ -1316,11 +1313,11 @@ bool CDVDInputStreamNavigator::FillDVDState(DVDState& dvdState)
+ return false;
+ }
+
+- if (m_dll.dvdnav_current_title_program(m_dvdnav, &dvdState.title, &dvdState.pgcn,
++ if (dvdnav_current_title_program(m_dvdnav, &dvdState.title, &dvdState.pgcn,
+ &dvdState.pgn) == DVDNAV_STATUS_ERR)
+ {
+ CLog::LogF(LOGERROR, "Failed to get current title info ({})",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ return false;
+ }
+
+@@ -1346,7 +1343,7 @@ bool CDVDInputStreamNavigator::GetState(std::string& xmlstate)
+ }
+
+ // do not save state if we are not playing a title stream (e.g. if we are in menus)
+- if (!m_dll.dvdnav_is_domain_vts(m_dvdnav))
++ if (!dvdnav_is_domain_vts(m_dvdnav))
+ {
+ return false;
+ }
+@@ -1380,8 +1377,8 @@ bool CDVDInputStreamNavigator::SetState(const std::string& xmlstate)
+ return false;
+ }
+
+- m_dll.dvdnav_program_play(m_dvdnav, dvdState.title, dvdState.pgcn, dvdState.pgn);
+- m_dll.dvdnav_angle_change(m_dvdnav, dvdState.current_angle);
++ dvdnav_program_play(m_dvdnav, dvdState.title, dvdState.pgcn, dvdState.pgn);
++ dvdnav_angle_change(m_dvdnav, dvdState.current_angle);
+ SetActiveSubtitleStream(dvdState.subp_num);
+ SetActiveAudioStream(dvdState.audio_num);
+ EnableSubtitleStream(dvdState.sub_enabled);
+@@ -1394,7 +1391,7 @@ std::string CDVDInputStreamNavigator::GetDVDTitleString()
+ return "";
+
+ const char* str = NULL;
+- if (m_dll.dvdnav_get_title_string(m_dvdnav, &str) == DVDNAV_STATUS_OK)
++ if (dvdnav_get_title_string(m_dvdnav, &str) == DVDNAV_STATUS_OK)
+ return str;
+ else
+ return "";
+@@ -1406,7 +1403,7 @@ std::string CDVDInputStreamNavigator::GetDVDSerialString()
+ return "";
+
+ const char* str = NULL;
+- if (m_dll.dvdnav_get_serial_string(m_dvdnav, &str) == DVDNAV_STATUS_OK)
++ if (dvdnav_get_serial_string(m_dvdnav, &str) == DVDNAV_STATUS_OK)
+ return str;
+ else
+ return "";
+@@ -1447,12 +1444,12 @@ void CDVDInputStreamNavigator::GetVideoResolution(uint32_t* width, uint32_t* hei
+ if (!m_dvdnav) return;
+
+ // for version <= 5.0.3 this functions returns 0 instead of DVDNAV_STATUS_OK and -1 instead of DVDNAV_STATUS_ERR
+- int status = m_dll.dvdnav_get_video_resolution(m_dvdnav, width, height);
++ int status = dvdnav_get_video_resolution(m_dvdnav, width, height);
+ if (status == -1)
+ {
+ CLog::Log(LOGWARNING,
+ "CDVDInputStreamNavigator::GetVideoResolution - Failed to get resolution ({})",
+- m_dll.dvdnav_err_to_string(m_dvdnav));
++ dvdnav_err_to_string(m_dvdnav));
+ *width = 0;
+ *height = 0;
+ }
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h
+index d33364b48f..319c84b47d 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h
+@@ -14,10 +14,12 @@
+ #include "DVDInputStream.h"
+ #include "DVDInputStreamFile.h"
+ #include "DVDStateSerializer.h"
+-#include "DllDvdNav.h"
+ #include "cores/MenuType.h"
+ #include "utils/Geometry.h"
+
++#include <dvdnav/dvdnav.h>
++#include <dvdnav/dvd_types.h>
++
+ #include <string>
+
+ #define DVD_VIDEO_BLOCKSIZE DVD_VIDEO_LB_LEN // 2048 bytes
+@@ -155,7 +157,6 @@ protected:
+ */
+ bool FillDVDState(DVDState& dvdstate);
+
+- DllDvdNav m_dll;
+ bool m_bCheckButtons;
+ bool m_bEOF;
+
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DllDvdNav.h b/xbmc/cores/VideoPlayer/DVDInputStreams/DllDvdNav.h
+deleted file mode 100644
+index 7a0c149e02..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DllDvdNav.h
++++ /dev/null
+@@ -1,275 +0,0 @@
+-/*
+- * Copyright (C) 2005-2018 Team Kodi
+- * This file is part of Kodi - https://kodi.tv
+- *
+- * SPDX-License-Identifier: GPL-2.0-or-later
+- * See LICENSES/README.md for more information.
+- */
+-
+-#pragma once
+-
+-extern "C" {
+-#define DVDNAV_COMPILE
+- #include <stdint.h>
+-
+- #include "dvdnav/dvdnav.h"
+-
+- #ifndef WIN32
+- #define WIN32
+- #endif // WIN32
+-
+- #ifndef HAVE_CONFIG_H
+- #define HAVE_CONFIG_H
+- #endif
+-
+- #include "dvdnav/dvd_types.h"
+-
+- #ifdef WIN32 // WIN32INCLUDES
+- #undef HAVE_CONFIG_H
+- #endif
+-}
+-#include "DynamicDll.h"
+-
+-class DllDvdNavInterface
+-{
+-public:
+- virtual ~DllDvdNavInterface() = default;
+- virtual dvdnav_status_t dvdnav_open(dvdnav_t **dest, const char *path)=0;
+- virtual dvdnav_status_t dvdnav_open2(dvdnav_t** dest,
+- void*,
+- const dvdnav_logger_cb*,
+- const char* path) = 0;
+- virtual dvdnav_status_t dvdnav_open_stream(dvdnav_t **dest, void *stream, dvdnav_stream_cb *stream_cb) = 0;
+- virtual dvdnav_status_t dvdnav_open_stream2(dvdnav_t** dest,
+- void* stream,
+- const dvdnav_logger_cb*,
+- dvdnav_stream_cb* stream_cb) = 0;
+- virtual dvdnav_status_t dvdnav_close(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_reset(dvdnav_t *self)=0;
+- virtual const char* dvdnav_err_to_string(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int32_t read_ahead_flag)=0;
+- virtual dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *self, int32_t pgc_based_flag)=0;
+- virtual dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, uint8_t **buf, int32_t *event, int32_t *len)=0;
+- virtual dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf)=0;
+- virtual dvdnav_status_t dvdnav_still_skip(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_wait_skip(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_stop(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_button_select(dvdnav_t *self, pci_t *pci, int32_t button)=0;
+- virtual dvdnav_status_t dvdnav_button_activate(dvdnav_t *self, pci_t *pci)=0;
+- virtual dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self, pci_t *pci)=0;
+- virtual dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self, pci_t *pci)=0;
+- virtual dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self, pci_t *pci)=0;
+- virtual dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self, pci_t *pci)=0;
+- virtual dvdnav_status_t dvdnav_sector_search(dvdnav_t *self, uint64_t offset, int32_t origin)=0;
+- virtual pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self)=0;
+- virtual dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_get_position(dvdnav_t *self, uint32_t *pos, uint32_t *len)=0;
+- virtual dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int32_t *title, int32_t *part)=0;
+- virtual dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self, char *code)=0;
+- virtual dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self, char *code)=0;
+- virtual dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self, char *code)=0;
+- virtual int8_t dvdnav_is_domain_vts(dvdnav_t *self)=0;
+- virtual int8_t dvdnav_get_active_spu_stream(dvdnav_t *self)=0;
+- virtual int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num)=0;
+- virtual uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream)=0;
+- virtual dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int32_t *button)=0;
+- virtual dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu)=0;
+- virtual dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_get_highlight_area(pci_t *nav_pci , int32_t button, int32_t mode, dvdnav_highlight_area_t *highlight)=0;
+- virtual dvdnav_status_t dvdnav_go_up(dvdnav_t *self)=0;
+- virtual int8_t dvdnav_get_active_audio_stream(dvdnav_t *self)=0;
+- virtual uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream)=0;
+- virtual int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num)=0;
+- virtual dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int32_t region_mask)=0;
+- virtual uint8_t dvdnav_get_video_aspect(dvdnav_t *self)=0;
+- virtual uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self)=0;
+- virtual dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int32_t *titles)=0;
+- virtual dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *self, int32_t title, int32_t *parts)=0;
+- virtual dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int32_t title)=0;
+- virtual dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part)=0;
+- virtual dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t * self, int32_t streamid, audio_attr_t* audio_attributes)=0;
+- virtual dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t * self, int32_t streamid, subp_attr_t* stitle_attributes)=0;
+- virtual dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t* self,
+- uint64_t offset,
+- int32_t origin) = 0;
+- virtual int64_t dvdnav_convert_time(dvd_time_t *time)=0;
+- virtual dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int32_t *current_angle,int32_t *number_of_angles)=0;
+- virtual dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int32_t angle) = 0;
+- virtual dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y)=0;
+- virtual dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y)=0;
+- virtual dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str)=0;
+- virtual dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *self, const char **serial_str)=0;
+- virtual const char* dvdnav_get_volid_string(dvdnav_t* self) = 0;
+- virtual dvdnav_status_t dvdnav_get_disk_region_mask(dvdnav_t* self, int32_t* region_mask) = 0;
+- virtual uint32_t dvdnav_describe_title_chapters(dvdnav_t* self, uint32_t title, uint64_t** times, uint64_t* duration)=0;
+- virtual int64_t dvdnav_get_current_time(dvdnav_t* self) = 0;
+- virtual int dvdnav_get_video_resolution(dvdnav_t* self, uint32_t* width, uint32_t* height)=0;
+- virtual int8_t dvdnav_get_number_of_streams(dvdnav_t* self, dvdnav_stream_type_t stream_type) = 0;
+- virtual dvdnav_status_t dvdnav_toggle_spu_stream(dvdnav_t* self, uint8_t visibility) = 0;
+- virtual dvdnav_status_t dvdnav_set_active_stream(dvdnav_t* self,
+- uint8_t stream_num,
+- dvdnav_stream_type_t stream_type) = 0;
+- virtual dvdnav_status_t dvdnav_program_play(dvdnav_t* self,
+- int32_t title,
+- int32_t pgcn,
+- int32_t pgn) = 0;
+- virtual dvdnav_status_t dvdnav_current_title_program(dvdnav_t* self,
+- int32_t* title,
+- int32_t* pgcn,
+- int32_t* pgn) = 0;
+-};
+-
+-class DllDvdNav : public DllDynamic, DllDvdNavInterface
+-{
+- DECLARE_DLL_WRAPPER(DllDvdNav, DLL_PATH_LIBDVDNAV)
+-
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_open, (dvdnav_t **p1, const char *p2))
+- DEFINE_METHOD4(dvdnav_status_t,
+- dvdnav_open2,
+- (dvdnav_t * *p1, void* p2, const dvdnav_logger_cb* p3, const char* p4))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_open_stream, (dvdnav_t **p1, void *p2, dvdnav_stream_cb *p3))
+- DEFINE_METHOD4(dvdnav_status_t,
+- dvdnav_open_stream2,
+- (dvdnav_t * *p1, void* p2, const dvdnav_logger_cb* p3, dvdnav_stream_cb* p4))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_close, (dvdnav_t *p1))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_reset, (dvdnav_t *p1))
+- DEFINE_METHOD1(const char*, dvdnav_err_to_string, (dvdnav_t *p1))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_set_readahead_flag, (dvdnav_t *p1, int32_t p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_set_PGC_positioning_flag, (dvdnav_t *p1, int32_t p2))
+- DEFINE_METHOD4(dvdnav_status_t, dvdnav_get_next_cache_block, (dvdnav_t *p1, uint8_t **p2, int32_t *p3, int32_t *p4))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_free_cache_block, (dvdnav_t *p1, unsigned char *p2))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_still_skip, (dvdnav_t *p1))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_wait_skip, (dvdnav_t *p1))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_stop, (dvdnav_t *p1))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_button_select, (dvdnav_t *p1, pci_t *p2, int32_t p3))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_button_activate,(dvdnav_t *p1, pci_t *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_upper_button_select, (dvdnav_t *p1, pci_t *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_lower_button_select, (dvdnav_t *p1, pci_t *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_right_button_select, (dvdnav_t *p1, pci_t *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_left_button_select, (dvdnav_t *p1, pci_t *p2))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_sector_search, (dvdnav_t *p1, uint64_t p2, int32_t p3))
+- DEFINE_METHOD1(pci_t*, dvdnav_get_current_nav_pci, (dvdnav_t *p1))
+- DEFINE_METHOD1(dsi_t*, dvdnav_get_current_nav_dsi, (dvdnav_t *p1))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_get_position, (dvdnav_t *p1, uint32_t *p2, uint32_t *p3))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_current_title_info, (dvdnav_t *p1, int32_t *p2, int32_t *p3))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_spu_language_select, (dvdnav_t *p1, char *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_audio_language_select, (dvdnav_t *p1, char *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_menu_language_select, (dvdnav_t *p1, char *p2))
+- DEFINE_METHOD1(int8_t, dvdnav_is_domain_vts, (dvdnav_t *p1))
+- DEFINE_METHOD1(int8_t, dvdnav_get_active_spu_stream, (dvdnav_t *p1))
+- DEFINE_METHOD2(int8_t, dvdnav_get_spu_logical_stream, (dvdnav_t *p1, uint8_t p2))
+- DEFINE_METHOD2(uint16_t, dvdnav_spu_stream_to_lang, (dvdnav_t *p1, uint8_t p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_current_highlight, (dvdnav_t *p1, int32_t *p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_menu_call, (dvdnav_t *p1, DVDMenuID_t p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_disk_region_mask, (dvdnav_t * p1, int32_t* p2))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_prev_pg_search, (dvdnav_t *p1))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_next_pg_search, (dvdnav_t *p1))
+- DEFINE_METHOD4(dvdnav_status_t, dvdnav_get_highlight_area, (pci_t *p1, int32_t p2, int32_t p3, dvdnav_highlight_area_t *p4))
+- DEFINE_METHOD1(dvdnav_status_t, dvdnav_go_up, (dvdnav_t *p1))
+- DEFINE_METHOD1(int8_t, dvdnav_get_active_audio_stream, (dvdnav_t *p1))
+- DEFINE_METHOD2(uint16_t, dvdnav_audio_stream_to_lang, (dvdnav_t *p1, uint8_t p2))
+- DEFINE_METHOD2(int8_t, dvdnav_get_audio_logical_stream, (dvdnav_t *p1, uint8_t p2))
+- DEFINE_METHOD2(int8_t, dvdnav_get_number_of_streams, (dvdnav_t * p1, dvdnav_stream_type_t p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_set_region_mask, (dvdnav_t *p1, int32_t p2))
+- DEFINE_METHOD1(uint8_t, dvdnav_get_video_aspect, (dvdnav_t *p1))
+- DEFINE_METHOD1(uint8_t, dvdnav_get_video_scale_permission, (dvdnav_t *p1))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_number_of_titles, (dvdnav_t *p1, int32_t *p2))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_get_number_of_parts, (dvdnav_t *p1, int32_t p2, int32_t *p3))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_title_play, (dvdnav_t *p1, int32_t p2))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_part_play, (dvdnav_t *p1, int32_t p2, int32_t p3))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_get_audio_attr, (dvdnav_t * p1, int32_t p2, audio_attr_t* p3))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_get_spu_attr, (dvdnav_t * p1, int32_t p2, subp_attr_t* p3))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_jump_to_sector_by_time, (dvdnav_t * p1, uint64_t p2, int32_t p3))
+- DEFINE_METHOD1(int64_t, dvdnav_convert_time, (dvd_time_t *p1))
+- DEFINE_METHOD3(dvdnav_status_t, dvdnav_get_angle_info, (dvdnav_t *p1, int32_t *p2,int32_t *p3))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_angle_change, (dvdnav_t *p1, int32_t p2))
+- DEFINE_METHOD4(dvdnav_status_t, dvdnav_mouse_activate, (dvdnav_t *p1, pci_t *p2, int32_t p3, int32_t p4))
+- DEFINE_METHOD4(dvdnav_status_t, dvdnav_mouse_select, (dvdnav_t *p1, pci_t *p2, int32_t p3, int32_t p4))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_title_string, (dvdnav_t *p1, const char **p2))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_serial_string, (dvdnav_t *p1, const char **p2))
+- DEFINE_METHOD1(const char*, dvdnav_get_volid_string, (dvdnav_t * p1))
+- DEFINE_METHOD4(uint32_t, dvdnav_describe_title_chapters, (dvdnav_t* p1, uint32_t p2, uint64_t** p3, uint64_t* p4))
+- DEFINE_METHOD1(int64_t, dvdnav_get_current_time, (dvdnav_t * p1))
+- DEFINE_METHOD3(int, dvdnav_get_video_resolution, (dvdnav_t * p1, uint32_t* p2, uint32_t* p3))
+- DEFINE_METHOD2(dvdnav_status_t, dvdnav_toggle_spu_stream, (dvdnav_t * p1, uint8_t p2))
+- DEFINE_METHOD3(dvdnav_status_t,
+- dvdnav_set_active_stream,
+- (dvdnav_t * p1, uint8_t p2, dvdnav_stream_type_t p3))
+- DEFINE_METHOD4(dvdnav_status_t,
+- dvdnav_program_play,
+- (dvdnav_t * p1, int32_t p2, int32_t p3, int32_t p4))
+- DEFINE_METHOD4(dvdnav_status_t,
+- dvdnav_current_title_program,
+- (dvdnav_t * p1, int32_t* p2, int32_t* p3, int32_t* p4))
+- BEGIN_METHOD_RESOLVE()
+- RESOLVE_METHOD(dvdnav_open)
+- RESOLVE_METHOD(dvdnav_open2)
+- RESOLVE_METHOD(dvdnav_open_stream)
+- RESOLVE_METHOD(dvdnav_open_stream2)
+- RESOLVE_METHOD(dvdnav_close)
+- RESOLVE_METHOD(dvdnav_reset)
+- RESOLVE_METHOD(dvdnav_err_to_string)
+- RESOLVE_METHOD(dvdnav_set_readahead_flag)
+- RESOLVE_METHOD(dvdnav_set_PGC_positioning_flag)
+- RESOLVE_METHOD(dvdnav_get_next_cache_block)
+- RESOLVE_METHOD(dvdnav_free_cache_block)
+- RESOLVE_METHOD(dvdnav_still_skip)
+- RESOLVE_METHOD(dvdnav_wait_skip)
+- RESOLVE_METHOD(dvdnav_stop)
+- RESOLVE_METHOD(dvdnav_get_number_of_streams)
+- RESOLVE_METHOD(dvdnav_get_disk_region_mask)
+- RESOLVE_METHOD(dvdnav_button_select)
+- RESOLVE_METHOD(dvdnav_button_activate)
+- RESOLVE_METHOD(dvdnav_upper_button_select)
+- RESOLVE_METHOD(dvdnav_lower_button_select)
+- RESOLVE_METHOD(dvdnav_right_button_select)
+- RESOLVE_METHOD(dvdnav_left_button_select)
+- RESOLVE_METHOD(dvdnav_sector_search)
+- RESOLVE_METHOD(dvdnav_get_current_nav_pci)
+- RESOLVE_METHOD(dvdnav_get_current_nav_dsi)
+- RESOLVE_METHOD(dvdnav_get_position)
+- RESOLVE_METHOD(dvdnav_current_title_info)
+- RESOLVE_METHOD(dvdnav_spu_language_select)
+- RESOLVE_METHOD(dvdnav_audio_language_select)
+- RESOLVE_METHOD(dvdnav_menu_language_select)
+- RESOLVE_METHOD(dvdnav_is_domain_vts)
+- RESOLVE_METHOD(dvdnav_get_active_spu_stream)
+- RESOLVE_METHOD(dvdnav_get_spu_logical_stream)
+- RESOLVE_METHOD(dvdnav_spu_stream_to_lang)
+- RESOLVE_METHOD(dvdnav_get_current_highlight)
+- RESOLVE_METHOD(dvdnav_menu_call)
+- RESOLVE_METHOD(dvdnav_prev_pg_search)
+- RESOLVE_METHOD(dvdnav_next_pg_search)
+- RESOLVE_METHOD(dvdnav_get_highlight_area)
+- RESOLVE_METHOD(dvdnav_go_up)
+- RESOLVE_METHOD(dvdnav_get_active_audio_stream)
+- RESOLVE_METHOD(dvdnav_audio_stream_to_lang)
+- RESOLVE_METHOD(dvdnav_get_audio_logical_stream)
+- RESOLVE_METHOD(dvdnav_set_region_mask)
+- RESOLVE_METHOD(dvdnav_get_video_aspect)
+- RESOLVE_METHOD(dvdnav_get_video_scale_permission)
+- RESOLVE_METHOD(dvdnav_get_number_of_titles)
+- RESOLVE_METHOD(dvdnav_get_number_of_parts)
+- RESOLVE_METHOD(dvdnav_title_play)
+- RESOLVE_METHOD(dvdnav_part_play)
+- RESOLVE_METHOD(dvdnav_get_audio_attr)
+- RESOLVE_METHOD(dvdnav_get_spu_attr)
+- RESOLVE_METHOD(dvdnav_jump_to_sector_by_time)
+- RESOLVE_METHOD(dvdnav_convert_time)
+- RESOLVE_METHOD(dvdnav_get_angle_info)
+- RESOLVE_METHOD(dvdnav_angle_change)
+- RESOLVE_METHOD(dvdnav_mouse_activate)
+- RESOLVE_METHOD(dvdnav_mouse_select)
+- RESOLVE_METHOD(dvdnav_get_title_string)
+- RESOLVE_METHOD(dvdnav_get_serial_string)
+- RESOLVE_METHOD(dvdnav_get_volid_string)
+- RESOLVE_METHOD(dvdnav_describe_title_chapters)
+- RESOLVE_METHOD(dvdnav_get_current_time)
+- RESOLVE_METHOD(dvdnav_get_video_resolution)
+- RESOLVE_METHOD(dvdnav_toggle_spu_stream)
+- RESOLVE_METHOD(dvdnav_set_active_stream)
+- RESOLVE_METHOD(dvdnav_program_play)
+- RESOLVE_METHOD(dvdnav_current_title_program)
+- END_METHOD_RESOLVE()
+-};
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/config.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/config.h
+deleted file mode 100644
+index 0a310ea40f..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/config.h
++++ /dev/null
+@@ -1,76 +0,0 @@
+-/*
+- * Copyright (C) 2005-2018 Team Kodi
+- * This file is part of Kodi - https://kodi.tv
+- *
+- * SPDX-License-Identifier: GPL-2.0-or-later
+- * See LICENSES/README.md for more information.
+- */
+-
+-#pragma once
+-
+-/* config.h. Generated by hand. */
+-#if defined(TARGET_POSIX)
+-#include "PlatformDefs.h"
+-#else
+-#include <windows.h>
+-#endif
+-#include <stdio.h>
+-
+-//#define HAVE_DLFCN_H 1
+-#define HAVE_DVDCSS_DVDCSS_H 1
+-/* #undef HAVE_DVDCSS_DVDCSS_H*/
+-/* #undef HAVE_INTTYPES_H */
+-#define HAVE_MEMORY_H 1
+-#define HAVE_STDINT_H 1
+-#define HAVE_STDLIB_H 1
+-#define HAVE_STRINGS_H 1
+-#define HAVE_STRING_H 1
+-#define HAVE_SYS_STAT_H 1
+-#define HAVE_SYS_TYPES_H 1
+-/* #undef HAVE_UNISTD_H */
+-#ifndef PACKAGE
+-#define PACKAGE "libdvdread"
+-#endif
+-#ifndef PACKAGE_BUGREPORT
+-#define PACKAGE_BUGREPORT ""
+-#endif
+-#ifndef PACKAGE_NAME
+-#define PACKAGE_NAME ""
+-#endif
+-#ifndef PACKAGE_STRING
+-#define PACKAGE_STRING ""
+-#endif
+-#ifndef PACKAGE_TARNAME
+-#define PACKAGE_TARNAME ""
+-#endif
+-#ifndef PACKAGE_VERSION
+-#define PACKAGE_VERSION ""
+-#endif
+-#define STDC_HEADERS 1
+-#ifndef VERSION
+-#define VERSION "1.2.6"
+-#endif
+-/* #undef WORDS_BIGENDIAN */
+-/* #undef __DARWIN__ */
+-/* #undef const */
+-#define inline __inline
+-/* #undef size_t */
+-
+-#define ssize_t int
+-
+-#ifndef PATH_MAX
+-#define PATH_MAX MAX_PATH
+-#endif
+-
+-#ifndef S_ISDIR
+-#define S_ISDIR(m) ((m) & _S_IFDIR)
+-#endif
+-#ifndef S_ISREG
+-#define S_ISREG(m) ((m) & _S_IFREG)
+-#endif
+-#ifndef S_ISBLK
+-#define S_ISBLK(m) 0
+-#endif
+-#ifndef S_ISCHR
+-#define S_ISCHR(m) 0
+-#endif
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_reader.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_reader.h
+deleted file mode 100644
+index ba802628f2..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_reader.h
++++ /dev/null
+@@ -1,370 +0,0 @@
+-/*
+- * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+- * Håkan Hjort <d95hjort@dtek.chalmers.se>,
+- * Björn Englund <d4bjorn@dtek.chalmers.se>
+- *
+- * This file is part of libdvdread.
+- *
+- * libdvdread 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; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * libdvdread 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 libdvdread; if not, write to the Free Software Foundation, Inc.,
+- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+- */
+-
+-#pragma once
+-
+-#ifdef _MSC_VER
+-#include "config.h"
+-
+-#include <stdio.h>
+-#include <stdlib.h>
+-#endif
+-
+-#include <sys/types.h>
+-//#include <inttypes.h>
+-#include <stdarg.h>
+-
+-/*****************************************************************************
+-* iovec structure: vectored data entry
+-*****************************************************************************/
+-#ifdef TARGET_WINDOWS
+-struct iovec
+-{
+- void *iov_base; /* Pointer to data. */
+- size_t iov_len; /* Length of data. */
+-};
+-#else
+-# include <sys/uio.h> /* struct iovec */
+-#endif
+-
+-/**
+- * The DVD access interface.
+- *
+- * This file contains the functions that form the interface for
+- * reading files located on a DVD.
+- */
+-
+-/**
+- * The length of one Logical Block of a DVD.
+- */
+-#define DVD_VIDEO_LB_LEN 2048
+-
+-/**
+- * Maximum length of filenames allowed in UDF.
+- */
+-#define MAX_UDF_FILE_NAME_LEN 2048
+-
+-#ifdef __cplusplus
+-extern "C" {
+-#endif
+-
+-/**
+- * Opaque type that is used as a handle for one instance of an opened DVD.
+- */
+-typedef struct dvd_reader_s dvd_reader_t;
+-typedef struct dvd_reader_device_s dvd_reader_device_t;
+-
+-/**
+- * Opaque type for a file read handle, much like a normal fd or FILE *.
+- */
+-typedef struct dvd_file_s dvd_file_t;
+-
+-struct dvd_reader_stream_cb
+-{
+- int (*pf_seek)(void* p_stream, uint64_t i_pos);
+- int (*pf_read)(void* p_stream, void* buffer, int i_read);
+- int (*pf_readv)(void* p_stream, void* p_iovec, int i_blocks);
+-};
+-typedef struct dvd_reader_stream_cb dvd_reader_stream_cb;
+-
+-/**
+- * Custom logger callback for DVDOpen[Stream]2
+- * @param private Handle as provided in Open functions
+- * @param level Log level
+- * @param fmt Format string
+- * @param args Arguments list
+- * pf_log(priv, level, fmt, args);
+- */
+-typedef enum
+-{
+- DVD_LOGGER_LEVEL_INFO,
+- DVD_LOGGER_LEVEL_ERROR,
+- DVD_LOGGER_LEVEL_WARN,
+- DVD_LOGGER_LEVEL_DEBUG,
+-} dvd_logger_level_t;
+-
+-typedef struct
+-{
+- void (*pf_log)(void*, dvd_logger_level_t, const char*, va_list);
+-} dvd_logger_cb;
+-
+-/**
+- * Public type that is used to provide statistics on a handle.
+- */
+-typedef struct {
+- off_t size; /**< Total size of file in bytes */
+- int nr_parts; /**< Number of file parts */
+- off_t parts_size[9]; /**< Size of each part in bytes */
+-} dvd_stat_t;
+-
+-/**
+- * Opens a block device of a DVD-ROM file, or an image file, or a directory
+- * name for a mounted DVD or HD copy of a DVD.
+- * The second form of Open function (DVDOpenStream) can be used to
+- * provide custom stream_cb functions to access the DVD (see libdvdcss).
+- *
+- * If the given file is a block device, or is the mountpoint for a block
+- * device, then that device is used for CSS authentication using libdvdcss.
+- * If no device is available, then no CSS authentication is performed,
+- * and we hope that the image is decrypted.
+- *
+- * If the path given is a directory, then the files in that directory may be
+- * in any one of these formats:
+- *
+- * path/VIDEO_TS/VTS_01_1.VOB
+- * path/video_ts/vts_01_1.vob
+- * path/VTS_01_1.VOB
+- * path/vts_01_1.vob
+- *
+- * @param path Specifies the the device, file or directory to be used.
+- * @param stream is a private handle used by stream_cb
+- * @param stream_cb is a struct containing seek and read functions
+- * @return If successful a a read handle is returned. Otherwise 0 is returned.
+- *
+- * dvd = DVDOpen(path);
+- * dvd = DVDOpenStream(stream, &stream_cb);
+- */
+-dvd_reader_t *DVDOpen( const char * );
+-dvd_reader_t* DVDOpenStream(void*, dvd_reader_stream_cb*);
+-
+-/**
+- * Same as DVDOpen, but with private handle to be passed back on callbacks
+- *
+- * @param path Specifies the the device, file or directory to be used.
+- * @param priv is a private handle
+- * @param logcb is a custom logger callback struct, or NULL if none needed
+- * @param stream_cb is a struct containing seek and read functions
+- * @return If successful a a read handle is returned. Otherwise 0 is returned.
+- *
+- * dvd = DVDOpen2(priv, logcb, path);
+- * dvd = DVDOpenStream2(priv, logcb, &stream_cb);
+- */
+-dvd_reader_t* DVDOpen2(void*, const dvd_logger_cb*, const char*);
+-dvd_reader_t* DVDOpenStream2(void*, const dvd_logger_cb*, dvd_reader_stream_cb*);
+-
+-/**
+- * Closes and cleans up the DVD reader object.
+- *
+- * You must close all open files before calling this function.
+- *
+- * @param dvd A read handle that should be closed.
+- *
+- * DVDClose(dvd);
+- */
+-void DVDClose( dvd_reader_t * );
+-
+-/**
+- *
+- */
+-typedef enum
+-{
+- DVD_READ_INFO_FILE, /**< VIDEO_TS.IFO or VTS_XX_0.IFO (title) */
+- DVD_READ_INFO_BACKUP_FILE, /**< VIDEO_TS.BUP or VTS_XX_0.BUP (title) */
+- DVD_READ_MENU_VOBS, /**< VIDEO_TS.VOB or VTS_XX_0.VOB (title) */
+- DVD_READ_TITLE_VOBS /**< VTS_XX_[1-9].VOB (title). All files in
+- the title set are opened and read as a
+- single file. */
+-} dvd_read_domain_t;
+-
+-/**
+- * Stats a file on the DVD given the title number and domain.
+- * The information about the file is stored in a dvd_stat_t
+- * which contains information about the size of the file and
+- * the number of parts in case of a multipart file and the respective
+- * sizes of the parts.
+- * A multipart file is for instance VTS_02_1.VOB, VTS_02_2.VOB, VTS_02_3.VOB
+- * The size of VTS_02_1.VOB will be stored in stat->parts_size[0],
+- * VTS_02_2.VOB in stat->parts_size[1], ...
+- * The total size (sum of all parts) is stored in stat->size and
+- * stat->nr_parts will hold the number of parts.
+- * Only DVD_READ_TITLE_VOBS (VTS_??_[1-9].VOB) can be multipart files.
+- *
+- * This function is only of use if you want to get the size of each file
+- * in the filesystem. These sizes are not needed to use any other
+- * functions in libdvdread.
+- *
+- * @param dvd A dvd read handle.
+- * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+- * @param domain Which domain.
+- * @param stat Pointer to where the result is stored.
+- * @return If successful 0, otherwise -1.
+- *
+- * int DVDFileStat(dvd, titlenum, domain, stat);
+- */
+-int DVDFileStat(dvd_reader_t *, int, dvd_read_domain_t, dvd_stat_t *);
+-
+-/**
+- * Opens a file on the DVD given the title number and domain.
+- *
+- * If the title number is 0, the video manager information is opened
+- * (VIDEO_TS.[IFO,BUP,VOB]). Returns a file structure which may be
+- * used for reads, or 0 if the file was not found.
+- *
+- * @param dvd A dvd read handle.
+- * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+- * @param domain Which domain.
+- * @return If successful a a file read handle is returned, otherwise 0.
+- *
+- * dvd_file = DVDOpenFile(dvd, titlenum, domain); */
+-dvd_file_t *DVDOpenFile( dvd_reader_t *, int, dvd_read_domain_t );
+-
+-/**
+- * Closes a file and frees the associated structure.
+- *
+- * @param dvd_file The file read handle to be closed.
+- *
+- * DVDCloseFile(dvd_file);
+- */
+-void DVDCloseFile( dvd_file_t * );
+-
+-/**
+- * Reads block_count number of blocks from the file at the given block offset.
+- * Returns number of blocks read on success, -1 on error. This call is only
+- * for reading VOB data, and should not be used when reading the IFO files.
+- * When reading from an encrypted drive, blocks are decrypted using libdvdcss
+- * where required.
+- *
+- * @param dvd_file A file read handle.
+- * @param offset Block offset from the start of the file to start reading at.
+- * @param block_count Number of block to read.
+- * @param data Pointer to a buffer to write the data into.
+- * @return Returns number of blocks read on success, -1 on error.
+- *
+- * blocks_read = DVDReadBlocks(dvd_file, offset, block_count, data);
+- */
+-ssize_t DVDReadBlocks( dvd_file_t *, int, size_t, unsigned char * );
+-
+-/**
+- * Seek to the given position in the file. Returns the resulting position in
+- * bytes from the beginning of the file. The seek position is only used for
+- * byte reads from the file, the block read call always reads from the given
+- * offset.
+- *
+- * @param dvd_file A file read handle.
+- * @param seek_offset Byte offset from the start of the file to seek to.
+- * @return The resulting position in bytes from the beginning of the file.
+- *
+- * offset_set = DVDFileSeek(dvd_file, seek_offset);
+- */
+-int32_t DVDFileSeek( dvd_file_t *, int32_t );
+-
+-/**
+- * Reads the given number of bytes from the file. This call can only be used
+- * on the information files, and may not be used for reading from a VOB. This
+- * reads from and increments the current seek position for the file.
+- *
+- * @param dvd_file A file read handle.
+- * @param data Pointer to a buffer to write the data into.
+- * @param bytes Number of bytes to read.
+- * @return Returns number of bytes read on success, -1 on error.
+- *
+- * bytes_read = DVDReadBytes(dvd_file, data, bytes);
+- */
+-ssize_t DVDReadBytes( dvd_file_t *, void *, size_t );
+-
+-/**
+- * Returns the file size in blocks.
+- *
+- * @param dvd_file A file read handle.
+- * @return The size of the file in blocks, -1 on error.
+- *
+- * blocks = DVDFileSize(dvd_file);
+- */
+-ssize_t DVDFileSize( dvd_file_t * );
+-
+-/**
+- * Get a unique 128 bit disc ID.
+- * This is the MD5 sum of VIDEO_TS.IFO and the VTS_0?_0.IFO files
+- * in title order (those that exist).
+- * If you need a 'text' representation of the id, print it as a
+- * hexadecimal number, using lowercase letters, discid[0] first.
+- * I.e. the same format as the command-line 'md5sum' program uses.
+- *
+- * @param dvd A read handle to get the disc ID from
+- * @param discid The buffer to put the disc ID into. The buffer must
+- * have room for 128 bits (16 chars).
+- * @return 0 on success, -1 on error.
+- */
+-int DVDDiscID( dvd_reader_t *, unsigned char * );
+-
+-/**
+- * Get the UDF VolumeIdentifier and VolumeSetIdentifier
+- * from the PrimaryVolumeDescriptor.
+- *
+- * @param dvd A read handle to get the disc ID from
+- * @param volid The buffer to put the VolumeIdentifier into.
+- * The VolumeIdentifier is latin-1 encoded (8bit unicode)
+- * null terminated and max 32 bytes (including '\0')
+- * @param volid_size No more than volid_size bytes will be copied to volid.
+- * If the VolumeIdentifier is truncated because of this
+- * it will still be null terminated.
+- * @param volsetid The buffer to put the VolumeSetIdentifier into.
+- * The VolumeIdentifier is 128 bytes as
+- * stored in the UDF PrimaryVolumeDescriptor.
+- * Note that this is not a null terminated string.
+- * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+- * @return 0 on success, -1 on error.
+- */
+-int DVDUDFVolumeInfo(dvd_reader_t*, char*, unsigned int, unsigned char*, unsigned int);
+-
+-int DVDFileSeekForce( dvd_file_t *, int offset, int force_size);
+-
+-/**
+- * Get the ISO9660 VolumeIdentifier and VolumeSetIdentifier
+- *
+- * * Only use this function as fallback if DVDUDFVolumeInfo returns -1 *
+- * * this will happen on a disc mastered only with a iso9660 filesystem *
+- * * All video DVD discs have UDF filesystem *
+- *
+- * @param dvd A read handle to get the disc ID from
+- * @param volid The buffer to put the VolumeIdentifier into.
+- * The VolumeIdentifier is coded with '0-9','A-Z','_'
+- * null terminated and max 33 bytes (including '\0')
+- * @param volid_size No more than volid_size bytes will be copied to volid.
+- * If the VolumeIdentifier is truncated because of this
+- * it will still be null terminated.
+- * @param volsetid The buffer to put the VolumeSetIdentifier into.
+- * The VolumeIdentifier is 128 bytes as
+- * stored in the ISO9660 PrimaryVolumeDescriptor.
+- * Note that this is not a null terminated string.
+- * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+- * @return 0 on success, -1 on error.
+- */
+-int DVDISOVolumeInfo(dvd_reader_t*, char*, unsigned int, unsigned char*, unsigned int);
+-
+-/**
+- * Sets the level of caching that is done when reading from a device
+- *
+- * @param dvd A read handle to get the disc ID from
+- * @param level The level of caching wanted.
+- * -1 - returns the current setting.
+- * 0 - UDF Cache turned off.
+- * 1 - (default level) Pointers to IFO files and some data from
+- * PrimaryVolumeDescriptor are cached.
+- *
+- * @return The level of caching.
+- */
+-int DVDUDFCacheLevel( dvd_reader_t *, int );
+-
+-#ifdef __cplusplus
+-};
+-#endif
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_types.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_types.h
+deleted file mode 100644
+index 5b2e802951..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_types.h
++++ /dev/null
+@@ -1,282 +0,0 @@
+-/*
+- * Copyright (C) 2000, 2001 Björn Englund, Håkan Hjort
+- *
+- * This file is part of libdvdnav, a DVD navigation library. It is a modified
+- * file originally part of the Ogle DVD player project.
+- *
+- * libdvdnav 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; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * libdvdnav 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+- */
+-
+-/*
+- * Various useful structs and enums for DVDs.
+- */
+-
+-#pragma once
+-
+-#include <stdint.h>
+-
+-/*
+- * DVD Menu ID
+- * (see dvdnav_menu_call())
+- */
+-typedef enum {
+- /* When used in VTS domain, DVD_MENU_Escape behaves like DVD_MENU_Root,
+- * but from within a menu domain, DVD_MENU_Escape resumes playback. */
+- DVD_MENU_Escape = 0,
+- DVD_MENU_Title = 2,
+- DVD_MENU_Root = 3,
+- DVD_MENU_Subpicture = 4,
+- DVD_MENU_Audio = 5,
+- DVD_MENU_Angle = 6,
+- DVD_MENU_Part = 7
+-} DVDMenuID_t;
+-
+-/*
+- * Stream Types
+- * (see dvdnav_get_number_of_streams())
+- */
+-typedef enum
+-{
+- DVD_SUBTITLE_STREAM = 0,
+- DVD_AUDIO_STREAM = 1
+-} dvdnav_stream_type_t;
+-
+-/* Domain */
+-typedef enum
+-{
+- DVD_DOMAIN_FirstPlay = 1, /* First Play Domain */
+- DVD_DOMAIN_VTSTitle = 2, /* Video Title Set Domain */
+- DVD_DOMAIN_VMGM = 4, /* Video Manager Domain */
+- DVD_DOMAIN_VTSMenu = 8 /* Video Title Set Menu Domain */
+-} DVDDomain_t;
+-
+-/*
+- * Structure containing info on highlight areas
+- * (see dvdnav_get_highlight_area())
+- */
+-typedef struct {
+- uint32_t palette; /* The CLUT entries for the highlight palette
+- (4-bits per entry -> 4 entries) */
+- uint16_t sx,sy,ex,ey; /* The start/end x,y positions */
+- uint32_t pts; /* Highlight PTS to match with SPU */
+-
+- /* button number for the SPU decoder/overlaying engine */
+- uint32_t buttonN;
+-} dvdnav_highlight_area_t;
+-
+-/* The audio format */
+-typedef enum
+-{
+- DVD_AUDIO_FORMAT_AC3 = 0,
+- DVD_AUDIO_FORMAT_UNKNOWN_1 = 1,
+- DVD_AUDIO_FORMAT_MPEG = 2,
+- DVD_AUDIO_FORMAT_MPEG2_EXT = 3,
+- DVD_AUDIO_FORMAT_LPCM = 4,
+- DVD_AUDIO_FORMAT_UNKNOWN_5 = 5,
+- DVD_AUDIO_FORMAT_DTS = 6,
+- DVD_AUDIO_FORMAT_SDDS = 7
+-} DVDAudioFormat_t;
+-
+-/* the following types are currently unused */
+-
+-#if 0
+-
+-/* User operation permissions */
+-typedef enum {
+- UOP_FLAG_TitleOrTimePlay = 0x00000001,
+- UOP_FLAG_ChapterSearchOrPlay = 0x00000002,
+- UOP_FLAG_TitlePlay = 0x00000004,
+- UOP_FLAG_Stop = 0x00000008,
+- UOP_FLAG_GoUp = 0x00000010,
+- UOP_FLAG_TimeOrChapterSearch = 0x00000020,
+- UOP_FLAG_PrevOrTopPGSearch = 0x00000040,
+- UOP_FLAG_NextPGSearch = 0x00000080,
+- UOP_FLAG_ForwardScan = 0x00000100,
+- UOP_FLAG_BackwardScan = 0x00000200,
+- UOP_FLAG_TitleMenuCall = 0x00000400,
+- UOP_FLAG_RootMenuCall = 0x00000800,
+- UOP_FLAG_SubPicMenuCall = 0x00001000,
+- UOP_FLAG_AudioMenuCall = 0x00002000,
+- UOP_FLAG_AngleMenuCall = 0x00004000,
+- UOP_FLAG_ChapterMenuCall = 0x00008000,
+- UOP_FLAG_Resume = 0x00010000,
+- UOP_FLAG_ButtonSelectOrActivate = 0x00020000,
+- UOP_FLAG_StillOff = 0x00040000,
+- UOP_FLAG_PauseOn = 0x00080000,
+- UOP_FLAG_AudioStreamChange = 0x00100000,
+- UOP_FLAG_SubPicStreamChange = 0x00200000,
+- UOP_FLAG_AngleChange = 0x00400000,
+- UOP_FLAG_KaraokeAudioPresModeChange = 0x00800000,
+- UOP_FLAG_VideoPresModeChange = 0x01000000
+-} DVDUOP_t;
+-
+-/* Parental Level */
+-typedef enum {
+- DVD_PARENTAL_LEVEL_1 = 1,
+- DVD_PARENTAL_LEVEL_2 = 2,
+- DVD_PARENTAL_LEVEL_3 = 3,
+- DVD_PARENTAL_LEVEL_4 = 4,
+- DVD_PARENTAL_LEVEL_5 = 5,
+- DVD_PARENTAL_LEVEL_6 = 6,
+- DVD_PARENTAL_LEVEL_7 = 7,
+- DVD_PARENTAL_LEVEL_8 = 8,
+- DVD_PARENTAL_LEVEL_None = 15
+-} DVDParentalLevel_t;
+-
+-/* Language ID (ISO-639 language code) */
+-typedef uint16_t DVDLangID_t;
+-
+-/* Country ID (ISO-3166 country code) */
+-typedef uint16_t DVDCountryID_t;
+-
+-/* Register */
+-typedef uint16_t DVDRegister_t;
+-typedef enum {
+- DVDFalse = 0,
+- DVDTrue = 1
+-} DVDBool_t;
+-typedef DVDRegister_t DVDGPRMArray_t[16];
+-typedef DVDRegister_t DVDSPRMArray_t[24];
+-
+-/* Navigation */
+-typedef int DVDStream_t;
+-typedef int DVDPTT_t;
+-typedef int DVDTitle_t;
+-
+-/* Angle number (1-9 or default?) */
+-typedef int DVDAngle_t;
+-
+-/* Timecode */
+-typedef struct {
+- uint8_t Hours;
+- uint8_t Minutes;
+- uint8_t Seconds;
+- uint8_t Frames;
+-} DVDTimecode_t;
+-
+-/* Subpicture stream number (0-31,62,63) */
+-typedef int DVDSubpictureStream_t;
+-
+-/* Audio stream number (0-7, 15(none)) */
+-typedef int DVDAudioStream_t;
+-
+-/* The audio application mode */
+-typedef enum {
+- DVD_AUDIO_APP_MODE_None = 0,
+- DVD_AUDIO_APP_MODE_Karaoke = 1,
+- DVD_AUDIO_APP_MODE_Surround = 2,
+- DVD_AUDIO_APP_MODE_Other = 3
+-} DVDAudioAppMode_t;
+-
+-/* Audio language extension */
+-typedef enum {
+- DVD_AUDIO_LANG_EXT_NotSpecified = 0,
+- DVD_AUDIO_LANG_EXT_NormalCaptions = 1,
+- DVD_AUDIO_LANG_EXT_VisuallyImpaired = 2,
+- DVD_AUDIO_LANG_EXT_DirectorsComments1 = 3,
+- DVD_AUDIO_LANG_EXT_DirectorsComments2 = 4
+-} DVDAudioLangExt_t;
+-
+-/* Subpicture language extension */
+-typedef enum {
+- DVD_SUBPICTURE_LANG_EXT_NotSpecified = 0,
+- DVD_SUBPICTURE_LANG_EXT_NormalCaptions = 1,
+- DVD_SUBPICTURE_LANG_EXT_BigCaptions = 2,
+- DVD_SUBPICTURE_LANG_EXT_ChildrensCaptions = 3,
+- DVD_SUBPICTURE_LANG_EXT_NormalCC = 5,
+- DVD_SUBPICTURE_LANG_EXT_BigCC = 6,
+- DVD_SUBPICTURE_LANG_EXT_ChildrensCC = 7,
+- DVD_SUBPICTURE_LANG_EXT_Forced = 9,
+- DVD_SUBPICTURE_LANG_EXT_NormalDirectorsComments = 13,
+- DVD_SUBPICTURE_LANG_EXT_BigDirectorsComments = 14,
+- DVD_SUBPICTURE_LANG_EXT_ChildrensDirectorsComments = 15,
+-} DVDSubpictureLangExt_t;
+-
+-/* Karaoke Downmix mode */
+-typedef enum {
+- DVD_KARAOKE_DOWNMIX_0to0 = 0x0001,
+- DVD_KARAOKE_DOWNMIX_1to0 = 0x0002,
+- DVD_KARAOKE_DOWNMIX_2to0 = 0x0004,
+- DVD_KARAOKE_DOWNMIX_3to0 = 0x0008,
+- DVD_KARAOKE_DOWNMIX_4to0 = 0x0010,
+- DVD_KARAOKE_DOWNMIX_Lto0 = 0x0020,
+- DVD_KARAOKE_DOWNMIX_Rto0 = 0x0040,
+- DVD_KARAOKE_DOWNMIX_0to1 = 0x0100,
+- DVD_KARAOKE_DOWNMIX_1to1 = 0x0200,
+- DVD_KARAOKE_DOWNMIX_2to1 = 0x0400,
+- DVD_KARAOKE_DOWNMIX_3to1 = 0x0800,
+- DVD_KARAOKE_DOWNMIX_4to1 = 0x1000,
+- DVD_KARAOKE_DOWNMIX_Lto1 = 0x2000,
+- DVD_KARAOKE_DOWNMIX_Rto1 = 0x4000
+-} DVDKaraokeDownmix_t;
+-typedef int DVDKaraokeDownmixMask_t;
+-
+-/* Display mode */
+-typedef enum {
+- DVD_DISPLAY_MODE_ContentDefault = 0,
+- DVD_DISPLAY_MODE_16x9 = 1,
+- DVD_DISPLAY_MODE_4x3PanScan = 2,
+- DVD_DISPLAY_MODE_4x3Letterboxed = 3
+-} DVDDisplayMode_t;
+-
+-/* Audio attributes */
+-typedef struct {
+- DVDAudioAppMode_t AppMode;
+- DVDAudioFormat_t AudioFormat;
+- DVDLangID_t Language;
+- DVDAudioLangExt_t LanguageExtension;
+- DVDBool_t HasMultichannelInfo;
+- DVDAudioSampleFreq_t SampleFrequency;
+- DVDAudioSampleQuant_t SampleQuantization;
+- DVDChannelNumber_t NumberOfChannels;
+-} DVDAudioAttributes_t;
+-typedef int DVDAudioSampleFreq_t;
+-typedef int DVDAudioSampleQuant_t;
+-typedef int DVDChannelNumber_t;
+-
+-/* Subpicture attributes */
+-typedef enum {
+- DVD_SUBPICTURE_TYPE_NotSpecified = 0,
+- DVD_SUBPICTURE_TYPE_Language = 1,
+- DVD_SUBPICTURE_TYPE_Other = 2
+-} DVDSubpictureType_t;
+-typedef enum {
+- DVD_SUBPICTURE_CODING_RunLength = 0,
+- DVD_SUBPICTURE_CODING_Extended = 1,
+- DVD_SUBPICTURE_CODING_Other = 2
+-} DVDSubpictureCoding_t;
+-typedef struct {
+- DVDSubpictureType_t Type;
+- DVDSubpictureCoding_t CodingMode;
+- DVDLangID_t Language;
+- DVDSubpictureLangExt_t LanguageExtension;
+-} DVDSubpictureAttributes_t;
+-
+-/* Video attributes */
+-typedef struct {
+- DVDBool_t PanscanPermitted;
+- DVDBool_t LetterboxPermitted;
+- int AspectX;
+- int AspectY;
+- int FrameRate;
+- int FrameHeight;
+- DVDVideoCompression_t Compression;
+- DVDBool_t Line21Field1InGop;
+- DVDBool_t Line21Field2InGop;
+- int more_to_come;
+-} DVDVideoAttributes_t;
+-typedef int DVDVideoCompression_t;
+-
+-#endif
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav.h
+deleted file mode 100644
+index 63d501b959..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav.h
++++ /dev/null
+@@ -1,789 +0,0 @@
+-/*
+- * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+- *
+- * This file is part of libdvdnav, a DVD navigation library.
+- *
+- * libdvdnav 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; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * libdvdnav 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+- */
+-
+-/*
+- * This is the main header file applications should include if they want
+- * to access dvdnav functionality.
+- */
+-
+-#pragma once
+-
+-#ifdef __cplusplus
+-extern "C" {
+-#endif
+-
+-#include "dvd_reader.h"
+-#include "dvd_types.h"
+-#include "dvdnav_events.h"
+-#include "nav_types.h"
+-#include "version.h"
+-
+-#include <stdarg.h>
+-
+- /*********************************************************************
+- * dvdnav data types *
+- *********************************************************************/
+-
+- /*
+- * Opaque data-type can be viewed as a 'DVD handle'. You should get
+- * a pointer to a dvdnav_t from the dvdnav_open() function.
+- * Never call free() on the pointer, you have to give it back with
+- * dvdnav_close().
+- */
+- typedef struct dvdnav_s dvdnav_t;
+-
+- /* Status as reported by most of libdvdnav's functions */
+- typedef int32_t dvdnav_status_t;
+-
+- typedef dvd_reader_stream_cb dvdnav_stream_cb;
+-
+-/*
+- * Unless otherwise stated, all functions return DVDNAV_STATUS_OK if
+- * they succeeded, otherwise DVDNAV_STATUS_ERR is returned and the error may
+- * be obtained by calling dvdnav_err_to_string().
+- */
+-#define DVDNAV_STATUS_ERR 0
+-#define DVDNAV_STATUS_OK 1
+-
+-/*********************************************************************
+- * initialisation & housekeeping functions *
+- *********************************************************************/
+-
+-/*
+- * Logger callback definition
+- */
+-typedef enum
+-{
+- DVDNAV_LOGGER_LEVEL_INFO,
+- DVDNAV_LOGGER_LEVEL_ERROR,
+- DVDNAV_LOGGER_LEVEL_WARN,
+- DVDNAV_LOGGER_LEVEL_DEBUG,
+-} dvdnav_logger_level_t;
+-
+-typedef struct
+-{
+- void (*pf_log)(void*, dvdnav_logger_level_t, const char*, va_list);
+-} dvdnav_logger_cb;
+-
+-/*
+- * These functions allow you to open a DVD device and associate it
+- * with a dvdnav_t.
+- */
+-
+-/*
+- * Attempts to open the DVD drive at the specified path or using external
+- * seek/read functions (dvdnav_open_stream) and pre-cache the CSS-keys.
+- * libdvdread is used to access the DVD, so any source supported by libdvdread
+- * can be given with "path" or "stream_cb". Currently, using dvdnav_open,
+- * libdvdread can access : DVD drives, DVD image files, DVD file-by-file
+- * copies. Using dvdnav_open_stream, libdvdread can access any kind of DVD
+- * storage via custom implementation of seek/read functions.
+- *
+- * The resulting dvdnav_t handle will be written to *dest.
+- */
+-dvdnav_status_t dvdnav_open(dvdnav_t **dest, const char *path);
+-dvdnav_status_t dvdnav_open_stream(dvdnav_t** dest, void* priv, dvdnav_stream_cb* stream_cb);
+-
+-dvdnav_status_t dvdnav_open2(dvdnav_t** dest, void*, const dvdnav_logger_cb*, const char* path);
+-dvdnav_status_t dvdnav_open_stream2(dvdnav_t** dest,
+- void* priv,
+- const dvdnav_logger_cb*,
+- dvdnav_stream_cb* stream_cb);
+-
+-dvdnav_status_t dvdnav_dup(dvdnav_t** dest, dvdnav_t* src);
+-dvdnav_status_t dvdnav_free_dup(dvdnav_t* _this);
+-
+-/*
+- * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any
+- * memory associated with it.
+- */
+-dvdnav_status_t dvdnav_close(dvdnav_t *self);
+-
+-/*
+- * Resets the DVD virtual machine and cache buffers.
+- */
+-dvdnav_status_t dvdnav_reset(dvdnav_t *self);
+-
+-/*
+- * Fills a pointer with a value pointing to a string describing
+- * the path associated with an open dvdnav_t. It assigns *path to NULL
+- * on error.
+- */
+-dvdnav_status_t dvdnav_path(dvdnav_t *self, const char **path);
+-
+-/*
+- * Returns a human-readable string describing the last error.
+- */
+-const char* dvdnav_err_to_string(dvdnav_t *self);
+-
+-const char* dvdnav_version(void);
+-
+-/*********************************************************************
+- * changing and reading DVD player characteristics *
+- *********************************************************************/
+-
+-/*
+- * These functions allow you to manipulate the various global characteristics
+- * of the DVD playback engine.
+- */
+-
+-/*
+- * Sets the region mask (bit 0 set implies region 1, bit 1 set implies
+- * region 2, etc) of the virtual machine. Generally you will only need to set
+- * this if you are playing RCE discs which query the virtual machine as to its
+- * region setting.
+- *
+- * This has _nothing_ to do with the region setting of the DVD drive.
+- */
+-dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int32_t region_mask);
+-
+-/*
+- * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+- * region 2, etc) of the virtual machine.
+- *
+- * This has _nothing_ to do with the region setting of the DVD drive.
+- */
+-dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int32_t *region_mask);
+-
+-/*
+- * Specify whether read-ahead caching should be used. You may not want this if your
+- * decoding engine does its own buffering.
+- *
+- * The default read-ahead cache does not use an additional thread for the reading
+- * (see read_cache.c for a threaded cache, but note that this code is currently
+- * unmaintained). It prebuffers on VOBU level by reading ahead several buffers
+- * on every read request. The speed of this prebuffering has been optimized to
+- * also work on slow DVD drives.
+- *
+- * If in addition you want to prevent memcpy's to improve performance, have a look
+- * at dvdnav_get_next_cache_block().
+- */
+-dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int32_t read_ahead_flag);
+-
+-/*
+- * Query whether read-ahead caching/buffering will be used.
+- */
+-dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int32_t *read_ahead_flag);
+-
+-/*
+- * Specify whether the positioning works PGC or PG based.
+- * Programs (PGs) on DVDs are similar to Chapters and a program chain (PGC)
+- * usually covers a whole feature. This affects the behaviour of the
+- * functions dvdnav_get_position() and dvdnav_sector_search(). See there.
+- * Default is PG based positioning.
+- */
+-dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *self, int32_t pgc_based_flag);
+-
+-/*
+- * Query whether positioning is PG or PGC based.
+- */
+-dvdnav_status_t dvdnav_get_PGC_positioning_flag(dvdnav_t *self, int32_t *pgc_based_flag);
+-
+-
+-/*********************************************************************
+- * reading data *
+- *********************************************************************/
+-
+-/*
+- * These functions are used to poll the playback engine and actually get data
+- * off the DVD.
+- */
+-
+-/*
+- * Attempts to get the next block off the DVD and copies it into the buffer 'buf'.
+- * If there is any special actions that may need to be performed, the value
+- * pointed to by 'event' gets set accordingly.
+- *
+- * If 'event' is DVDNAV_BLOCK_OK then 'buf' is filled with the next block
+- * (note that means it has to be at /least/ 2048 bytes big). 'len' is
+- * then set to 2048.
+- *
+- * Otherwise, buf is filled with an appropriate event structure and
+- * len is set to the length of that structure.
+- *
+- * See the dvdnav_events.h header for information on the various events.
+- */
+-dvdnav_status_t dvdnav_get_next_block(dvdnav_t* self, uint8_t* buf, int32_t* event, int32_t* len);
+-
+-/*
+- * This basically does the same as dvdnav_get_next_block. The only difference is
+- * that it avoids a memcopy, when the requested block was found in the cache.
+- * In such a case (cache hit) this function will return a different pointer than
+- * the one handed in, pointing directly into the relevant block in the cache.
+- * Those pointers must _never_ be freed but instead returned to the library via
+- * dvdnav_free_cache_block().
+- */
+-dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t* self,
+- uint8_t** buf,
+- int32_t* event,
+- int32_t* len);
+-
+-/*
+- * All buffers which came from the internal cache (when dvdnav_get_next_cache_block()
+- * returned a buffer different from the one handed in) have to be freed with this
+- * function. Although handing in other buffers not from the cache doesn't cause any harm.
+- */
+-dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf);
+-
+-/*
+- * If we are currently in a still-frame this function skips it.
+- *
+- * See also the DVDNAV_STILL_FRAME event.
+- */
+-dvdnav_status_t dvdnav_still_skip(dvdnav_t *self);
+-
+-/*
+- * If we are currently in WAIT state, that is: the application is required to
+- * wait for its fifos to become empty, calling this signals libdvdnav that this
+- * is achieved and that it can continue.
+- *
+- * See also the DVDNAV_WAIT event.
+- */
+-dvdnav_status_t dvdnav_wait_skip(dvdnav_t *self);
+-
+-/*
+- * Returns the still time from the currently playing cell.
+- * The still time is given in seconds with 0xff meaning an indefinite still.
+- *
+- * This function can be used to detect still frames before they are reached.
+- * Some players might need this to prepare for a frame to be shown for a
+- * longer time than usual.
+- */
+-uint32_t dvdnav_get_next_still_flag(dvdnav_t *self);
+-
+-/*
+- * Stops playback. The next event obtained with one of the get_next_block
+- * functions will be a DVDNAV_STOP event.
+- *
+- * It is not required to call this before dvdnav_close().
+- */
+-dvdnav_status_t dvdnav_stop(dvdnav_t *self);
+-
+-/*
+- * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+- * region 2, etc) reported by the dvd disc being played.
+- *
+- * Note this has no relation with the region setting of the DVD drive.
+- * Old DVD drives (RPC-I) used to delegate most of the RCE handling to the CPU and
+- * will actually call the virtual machine (VM) for its region setting. In those cases,
+- * changing the VM region mask via dvdnav_set_region_mask() will circunvent
+- * the region protection scheme. This is no longer the case with more recent (RPC-II) drives
+- * as RCE is handled internally by the drive firmware.
+- *
+- */
+-dvdnav_status_t dvdnav_get_disk_region_mask(dvdnav_t* self, int32_t* region_mask);
+-
+-/*********************************************************************
+- * title/part navigation *
+- *********************************************************************/
+-
+-/*
+- * Returns the number of titles on the disk.
+- */
+-dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int32_t *titles);
+-
+-/*
+- * Returns the number of parts within the given title.
+- */
+-dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *self, int32_t title, int32_t *parts);
+-
+-/*
+- * Returns the number of angles for the given title.
+- */
+-dvdnav_status_t dvdnav_get_number_of_angles(dvdnav_t* self, int32_t title, int32_t* angles);
+-
+-/*
+- * Plays the specified title of the DVD from its beginning (that is: part 1).
+- */
+-dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int32_t title);
+-
+-/*
+- * Plays the specified title, starting from the specified part.
+- */
+-dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part);
+-
+-/*
+- * Plays the specified title, starting from the specified program
+- */
+-dvdnav_status_t dvdnav_program_play(dvdnav_t *self, int32_t title, int32_t pgcn, int32_t pgn);
+-
+-/*
+- * Stores in *times an array (that the application *must* free) of
+- * dvdtimes corresponding to the chapter times for the chosen title.
+- * *duration will have the duration of the title
+- * The number of entries in *times is the result of the function.
+- * On error *times is NULL and the output is 0
+- */
+-uint32_t dvdnav_describe_title_chapters(dvdnav_t *self, int32_t title, uint64_t **times, uint64_t *duration);
+-
+-/*
+- * Play the specified amount of parts of the specified title of
+- * the DVD then STOP.
+- *
+- * Currently unimplemented!
+- */
+-dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t* self,
+- int32_t title,
+- int32_t part,
+- int32_t parts_to_play);
+-
+-/*
+- * Play the specified title starting from the specified time.
+- *
+- * Currently unimplemented!
+- */
+-dvdnav_status_t dvdnav_time_play(dvdnav_t* self, int32_t title, uint64_t time);
+-
+-/*
+- * Stop playing the current position and jump to the specified menu.
+- *
+- * See also DVDMenuID_t from libdvdread
+- */
+-dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu);
+-
+-/*
+- * Return the title number and part currently being played.
+- * A title of 0 indicates we are in a menu. In this case, part
+- * is set to the current menu's ID.
+- */
+-dvdnav_status_t dvdnav_current_title_info(dvdnav_t* self, int32_t* title, int32_t* part);
+-
+-/*
+- * Return the title number, pgcn and pgn currently being played.
+- * A title of 0 indicates, we are in a menu.
+- */
+-dvdnav_status_t dvdnav_current_title_program(dvdnav_t* self,
+- int32_t* title,
+- int32_t* pgcn,
+- int32_t* pgn);
+-
+-/*
+- * Return the current position (in blocks) within the current
+- * title and the length (in blocks) of said title.
+- *
+- * Current implementation is wrong and likely to behave unpredictably!
+- * Use is discouraged!
+- */
+-dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t* self, uint32_t* pos, uint32_t* len);
+-
+-/*
+- * This function is only available for compatibility reasons.
+- *
+- * Stop playing the current position and start playback of the current title
+- * from the specified part.
+- */
+-dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int32_t part);
+-
+-
+-/*********************************************************************
+- * program chain/program navigation *
+- *********************************************************************/
+-
+-/*
+- * Stop playing the current position and start playback from the last
+- * VOBU boundary before the given sector. The sector number is not
+- * meant to be an absolute physical DVD sector, but a relative sector
+- * in the current program. This function cannot leave the current
+- * program and will fail if asked to do so.
+- *
+- * If program chain based positioning is enabled
+- * (see dvdnav_set_PGC_positioning_flag()), this will seek to the relative
+- * sector inside the current program chain.
+- *
+- * 'origin' can be one of SEEK_SET, SEEK_CUR, SEEK_END as defined in
+- * fcntl.h.
+- */
+-dvdnav_status_t dvdnav_sector_search(dvdnav_t* self, int64_t offset, int32_t origin);
+-
+-/*
+- returns the current stream time in PTS ticks as reported by the IFO structures
+- divide it by 90000 to get the current play time in seconds
+- */
+-int64_t dvdnav_get_current_time(dvdnav_t *self);
+-
+-/*
+- * Stop playing the current position and start playback of the title
+- * from the specified timecode.
+- *
+- * Currently implemented using interpolation. That interpolation is slightly
+- * inaccurate.
+- */
+-dvdnav_status_t dvdnav_time_search(dvdnav_t* self, uint64_t time);
+-
+-/*
+- * Find the nearest vobu and jump to it
+- *
+- * Alternative to dvdnav_time_search (see full documentation on searching.jump_to_time.readme)
+- * Jumps to the provided PTS (which is defined as time_in_ms * 90). mode means the navigation mode,
+- * currently only the Default (0) is implemented:
+- * 0: Default. Jump to a time which may be either <> time_in_pts_ticks
+- * 1: After. Always jump to a time that is > time_in_pts_ticks
+- * -1: Before. Always jump to a time that is < time_in_pts_ticks
+- */
+-dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t* self,
+- uint64_t time_in_pts_ticks,
+- int32_t mode);
+-
+-/*
+- * Stop playing current position and play the "GoUp"-program chain.
+- * (which generally leads to the title menu or a higher-level menu).
+- */
+-dvdnav_status_t dvdnav_go_up(dvdnav_t *self);
+-
+-/*
+- * Stop playing the current position and start playback at the
+- * previous program (if it exists).
+- */
+-dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self);
+-
+-/*
+- * Stop playing the current position and start playback at the
+- * first program.
+- */
+-dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self);
+-
+-/*
+- * Stop playing the current position and start playback at the
+- * next program (if it exists).
+- */
+-dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self);
+-
+-/*
+- * Return the current position (in blocks) within the current
+- * program and the length (in blocks) of current program.
+- *
+- * If program chain based positioning is enabled
+- * (see dvdnav_set_PGC_positioning_flag()), this will return the
+- * relative position in and the length of the current program chain.
+- */
+-dvdnav_status_t dvdnav_get_position(dvdnav_t* self, uint32_t* pos, uint32_t* len);
+-
+-/*********************************************************************
+- * menu highlights *
+- *********************************************************************/
+-
+-/*
+- * Most functions related to highlights take a NAV PCI packet as a parameter.
+- * While you can get such a packet from libdvdnav, this will result in
+- * errors for players with internal FIFOs because due to the FIFO length,
+- * libdvdnav will be ahead in the stream compared to what the user is
+- * seeing on screen. Therefore, player applications who have a NAV
+- * packet available, which is better in sync with the actual playback,
+- * should always pass this one to these functions.
+- */
+-
+-/*
+- * Get the currently highlighted button
+- * number (1..36) or 0 if no button is highlighted.
+- */
+-dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int32_t *button);
+-
+-/*
+- * Returns the Presentation Control Information (PCI) structure associated
+- * with the current position.
+- *
+- * Read the general notes above.
+- * See also libdvdreads nav_types.h for definition of pci_t.
+- */
+-pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self);
+-
+-/*
+- * Returns the DSI (data search information) structure associated
+- * with the current position.
+- *
+- * Read the general notes above.
+- * See also libdvdreads nav_types.h for definition of dsi_t.
+- */
+-dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self);
+-
+-/*
+- * Get the area associated with a certain button.
+- */
+-dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci,
+- int32_t button,
+- int32_t mode,
+- dvdnav_highlight_area_t* highlight);
+-
+-/*
+- * Move button highlight around as suggested by function name (e.g. with arrow keys).
+- */
+-dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self, pci_t *pci);
+-dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self, pci_t *pci);
+-dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self, pci_t *pci);
+-dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self, pci_t *pci);
+-
+-/*
+- * Activate ("press") the currently highlighted button.
+- */
+-dvdnav_status_t dvdnav_button_activate(dvdnav_t *self, pci_t *pci);
+-
+-/*
+- * Highlight a specific button.
+- */
+-dvdnav_status_t dvdnav_button_select(dvdnav_t *self, pci_t *pci, int32_t button);
+-
+-/*
+- * Activate ("press") specified button.
+- */
+-dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, pci_t *pci, int32_t button);
+-
+-/*
+- * Activate ("press") a button and execute specified command.
+- */
+-dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *self, int32_t button, vm_cmd_t *cmd);
+-
+-/*
+- * Select button at specified video frame coordinates.
+- */
+-dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y);
+-
+-/*
+- * Activate ("press") button at specified video frame coordinates.
+- */
+-dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y);
+-
+-
+-/*********************************************************************
+- * languages *
+- *********************************************************************/
+-
+-/*
+- * The language codes expected by these functions are two character
+- * codes as defined in ISO639.
+- */
+-
+-/*
+- * Set which menu language we should use per default.
+- */
+-dvdnav_status_t dvdnav_menu_language_select(dvdnav_t* self, char* code);
+-
+-/*
+- * Set which audio language we should use per default.
+- */
+-dvdnav_status_t dvdnav_audio_language_select(dvdnav_t* self, char* code);
+-
+-/*
+- * Set which spu language we should use per default.
+- */
+-dvdnav_status_t dvdnav_spu_language_select(dvdnav_t* self, char* code);
+-
+-/*********************************************************************
+- * obtaining stream attributes *
+- *********************************************************************/
+-
+-/*
+- * Return a string describing the title of the DVD.
+- * This is an ID string encoded on the disc by the author. In many cases
+- * this is a descriptive string such as `THE_MATRIX' but sometimes is singularly
+- * uninformative such as `PDVD-011421'. Some DVD authors even forget to set this,
+- * so you may also read the default of the authoring software they used, like
+- * `DVDVolume'.
+- */
+-dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str);
+-
+-/*
+- * Returns a string containing the serial number of the DVD.
+- * This has a max of 15 characters and should be more unique than the
+- * title string.
+- */
+-dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *self, const char **serial_str);
+-
+-/*
+- * Returns the VolumeIdentifier of the disc or NULL if it could
+- * not be obtained. The VolumeIdentifier might be latin-1 encoded
+- * (8bit unicode) null terminated and max 32 bytes (including '\0');
+- * or coded with '0-9','A-Z','_' null terminated and max 33 bytes
+- * (including '\0').
+- * See also dvdnav_get_title_string
+- *
+- * Note: The string is malloc'd so caller has to free() the returned
+- * string when done with it.
+- */
+-const char* dvdnav_get_volid_string(dvdnav_t* self);
+-
+-/*
+- * Get video aspect code.
+- * The aspect code does only change on VTS boundaries.
+- * See the DVDNAV_VTS_CHANGE event.
+- *
+- * 0 -- 4:3, 2 -- 16:9
+- */
+-uint8_t dvdnav_get_video_aspect(dvdnav_t *self);
+-
+-/*
+- * Get video resolution.
+- */
+-dvdnav_status_t dvdnav_get_video_resolution(dvdnav_t* self, uint32_t* width, uint32_t* height);
+-
+-/*
+- * Get video scaling permissions.
+- * The scaling permission does only change on VTS boundaries.
+- * See the DVDNAV_VTS_CHANGE event.
+- *
+- * bit0 set = deny letterboxing, bit1 set = deny pan&scan
+- */
+-uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self);
+-
+-/*
+- * Converts a *logical* audio stream id into language code
+- * (returns 0xffff if no such stream).
+- */
+-uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream);
+-
+-/*
+- * Returns the format of *logical* audio stream 'stream'
+- * (returns 0xffff if no such stream).
+- */
+-uint16_t dvdnav_audio_stream_format(dvdnav_t *self, uint8_t stream);
+-
+-/*
+- * Returns number of channels in *logical* audio stream 'stream'
+- * (returns 0xffff if no such stream).
+- */
+-uint16_t dvdnav_audio_stream_channels(dvdnav_t *self, uint8_t stream);
+-
+-/*
+- * Converts a *logical* subpicture stream id into country code
+- * (returns 0xffff if no such stream).
+- */
+-uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream);
+-
+-/*
+- * Converts a *physical* (MPEG) audio stream id into a logical stream number.
+- */
+-int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num);
+-
+-#define HAVE_GET_AUDIO_ATTR
+-/*
+- * Get audio attr
+- */
+-dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t *self, uint8_t audio_mum, audio_attr_t *audio_attr);
+-
+-/*
+- * Converts a *physical* (MPEG) subpicture stream id into a logical stream number.
+- */
+-int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num);
+-
+-#define HAVE_GET_SPU_ATTR
+-/*
+- * Get spu attr
+- */
+-dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t *self, uint8_t audio_mum, subp_attr_t *subp_attr);
+-
+-/*
+- * Get active audio stream.
+- */
+-int8_t dvdnav_get_active_audio_stream(dvdnav_t *self);
+-
+-/*
+- * Get active spu stream.
+- */
+-int8_t dvdnav_get_active_spu_stream(dvdnav_t *self);
+-
+-/*
+- * Get the set of user operations that are currently prohibited.
+- * There are potentially new restrictions right after
+- * DVDNAV_CHANNEL_HOP and DVDNAV_NAV_PACKET.
+- */
+-user_ops_t dvdnav_get_restrictions(dvdnav_t *self);
+-
+-/*
+- * Returns the number of streams provided its type (e.g. subtitles, audio, etc)
+- */
+-int8_t dvdnav_get_number_of_streams(dvdnav_t* self, dvdnav_stream_type_t stream_type);
+-
+-/*********************************************************************
+- * setting stream attributes *
+- *********************************************************************/
+-
+-/*
+- * Set the visible (enable) status of the current spu stream
+- * (to enable/disable subtitles)
+- * visibility defines if the spu stream should be enabled/visible (1) or disabled (0)
+- */
+-dvdnav_status_t dvdnav_toggle_spu_stream(dvdnav_t* self, uint8_t visibility);
+-
+-/*
+- * Set the given stream id and stream type as active
+- * stream_num - the physical index of the stream
+- * stream_type - the stream type (audio or subtitles)
+- */
+-dvdnav_status_t dvdnav_set_active_stream(dvdnav_t* self,
+- uint8_t stream_num,
+- dvdnav_stream_type_t stream_type);
+-
+-/*********************************************************************
+- * multiple angles *
+- *********************************************************************/
+-
+-/*
+- * The libdvdnav library abstracts away the difference between seamless and
+- * non-seamless angles. From the point of view of the programmer you just set the
+- * angle number and all is well in the world. You will always see only the
+- * selected angle coming from the get_next_block functions.
+- *
+- * Note:
+- * It is quite possible that some tremendously strange DVD feature might change the
+- * angle number from under you. Generally you should always view the results from
+- * dvdnav_get_angle_info() as definitive only up to the next time you call
+- * dvdnav_get_next_block().
+- */
+-
+-/*
+- * Sets the current angle. If you try to follow a non existent angle
+- * the call fails.
+- */
+-dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int32_t angle);
+-
+-/*
+- * Returns the current angle and number of angles present.
+- */
+-dvdnav_status_t dvdnav_get_angle_info(dvdnav_t* self,
+- int32_t* current_angle,
+- int32_t* number_of_angles);
+-
+-/*********************************************************************
+- * domain queries *
+- *********************************************************************/
+-
+-/*
+- * Are we in the First Play domain?
+- */
+-int8_t dvdnav_is_domain_fp(dvdnav_t *self);
+-
+-/*
+- * Are we in the Video management Menu domain?
+- */
+-int8_t dvdnav_is_domain_vmgm(dvdnav_t *self);
+-
+-/*
+- * Are we in the Video Title Menu domain?
+- */
+-int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
+-
+-/*
+- * Are we in the Video Title Set domain?
+- */
+-int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+-
+-
+-#ifdef __cplusplus
+-}
+-#endif
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav_events.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav_events.h
+deleted file mode 100644
+index 970fa6a937..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav_events.h
++++ /dev/null
+@@ -1,226 +0,0 @@
+-/*
+- * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+- *
+- * This file is part of libdvdnav, a DVD navigation library.
+- *
+- * libdvdnav 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; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * libdvdnav 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+- */
+-
+-/*
+- * This header defines events and event types
+- */
+-
+-#pragma once
+-
+-/*
+- * DVDNAV_BLOCK_OK
+- *
+- * A regular data block from the DVD has been returned.
+- * This one should be demuxed and decoded for playback.
+- */
+-#define DVDNAV_BLOCK_OK 0
+-
+-/*
+- * DVDNAV_NOP
+- *
+- * Just ignore this.
+- */
+-#define DVDNAV_NOP 1
+-
+-/*
+- * DVDNAV_STILL_FRAME
+- *
+- * We have reached a still frame. The player application should wait
+- * the amount of time specified by the still's length while still handling
+- * user input to make menus and other interactive stills work.
+- * The last delivered frame should be kept showing.
+- * Once the still has timed out, call dvdnav_skip_still().
+- * A length of 0xff means an infinite still which has to be skipped
+- * indirectly by some user interaction.
+- */
+-#define DVDNAV_STILL_FRAME 2
+-
+-typedef struct {
+- /* The length (in seconds) the still frame should be displayed for,
+- * or 0xff if infinite. */
+- int length;
+-} dvdnav_still_event_t;
+-
+-
+-/*
+- * DVDNAV_SPU_STREAM_CHANGE
+- *
+- * Inform the SPU decoding/overlaying engine to switch SPU channels.
+- */
+-#define DVDNAV_SPU_STREAM_CHANGE 3
+-
+-typedef struct {
+- /* The physical (MPEG) stream number for widescreen SPU display.
+- * Use this, if you blend the SPU on an anamorphic image before
+- * unsqueezing it. */
+- int physical_wide;
+-
+- /* The physical (MPEG) stream number for letterboxed display.
+- * Use this, if you blend the SPU on an anamorphic image after
+- * unsqueezing it. */
+- int physical_letterbox;
+-
+- /* The physical (MPEG) stream number for pan&scan display.
+- * Use this, if you blend the SPU on an anamorphic image after
+- * unsqueezing it the pan&scan way. */
+- int physical_pan_scan;
+-
+- /* The logical (DVD) stream number. */
+- int logical;
+-} dvdnav_spu_stream_change_event_t;
+-
+-
+-/*
+- * DVDNAV_AUDIO_STREAM_CHANGE
+- *
+- * Inform the audio decoder to switch channels.
+- */
+-#define DVDNAV_AUDIO_STREAM_CHANGE 4
+-
+-typedef struct {
+- /* The physical (MPEG) stream number. */
+- int physical;
+-
+- /* The logical (DVD) stream number. */
+- int logical;
+-} dvdnav_audio_stream_change_event_t;
+-
+-
+-/*
+- * DVDNAV_VTS_CHANGE
+- *
+- * Some status information like video aspect and video scale permissions do
+- * not change inside a VTS. Therefore this event can be used to query such
+- * information only when necessary and update the decoding/displaying
+- * accordingly.
+- */
+-#define DVDNAV_VTS_CHANGE 5
+-
+-typedef struct {
+- int old_vtsN; /* the old VTS number */
+- DVDDomain_t old_domain; /* the old domain */
+- int new_vtsN; /* the new VTS number */
+- DVDDomain_t new_domain; /* the new domain */
+-} dvdnav_vts_change_event_t;
+-
+-
+-/*
+- * DVDNAV_CELL_CHANGE
+- *
+- * Some status information like the current Title and Part numbers do not
+- * change inside a cell. Therefore this event can be used to query such
+- * information only when necessary and update the decoding/displaying
+- * accordingly.
+- * Some useful information for accurate time display is also reported
+- * together with this event.
+- */
+-#define DVDNAV_CELL_CHANGE 6
+-
+-typedef struct {
+- int cellN; /* the new cell number */
+- int pgN; /* the current program number */
+- int64_t cell_length; /* the length of the current cell in sectors */
+- int64_t pg_length; /* the length of the current program in sectors */
+- int64_t pgc_length; /* the length of the current program chain in PTS ticks */
+- int64_t cell_start; /* the start offset of the current cell relatively to the PGC in sectors */
+- int64_t pg_start; /* the start offset of the current PG relatively to the PGC in sectors */
+-} dvdnav_cell_change_event_t;
+-
+-
+-/*
+- * DVDNAV_NAV_PACKET
+- *
+- * NAV packets are useful for various purposes. They define the button
+- * highlight areas and VM commands of DVD menus, so they should in any
+- * case be sent to the SPU decoder/overlaying engine for the menus to work.
+- * NAV packets also provide a way to detect PTS discontinuities, because
+- * they carry the start and end PTS values for the current VOBU.
+- * (pci.vobu_s_ptm and pci.vobu_e_ptm) Whenever the start PTS of the
+- * current NAV does not match the end PTS of the previous NAV, a PTS
+- * discontinuity has occurred.
+- * NAV packets can also be used for time display, because they are
+- * timestamped relatively to the current Cell.
+- */
+-#define DVDNAV_NAV_PACKET 7
+-
+-/*
+- * DVDNAV_STOP
+- *
+- * Applications should end playback here. A subsequent dvdnav_get_next_block()
+- * call will restart the VM from the beginning of the DVD.
+- */
+-#define DVDNAV_STOP 8
+-
+-/*
+- * DVDNAV_HIGHLIGHT
+- *
+- * The current button highlight changed. Inform the overlaying engine to
+- * highlight a different button. Please note, that at the moment only mode 1
+- * highlights are reported this way. That means, when the button highlight
+- * has been moved around by some function call, you will receive an event
+- * telling you the new button. But when a button gets activated, you have
+- * to handle the mode 2 highlighting (that is some different colour the
+- * button turns to on activation) in your application.
+- */
+-#define DVDNAV_HIGHLIGHT 9
+-
+-typedef struct {
+- /* highlight mode: 0 - hide, 1 - show, 2 - activate, currently always 1 */
+- int display;
+-
+- /* FIXME: these fields are currently not set */
+- uint32_t palette; /* The CLUT entries for the highlight palette
+- (4-bits per entry -> 4 entries) */
+- uint16_t sx,sy,ex,ey; /* The start/end x,y positions */
+- uint32_t pts; /* Highlight PTS to match with SPU */
+-
+- /* button number for the SPU decoder/overlaying engine */
+- uint32_t buttonN;
+-} dvdnav_highlight_event_t;
+-
+-
+-/*
+- * DVDNAV_SPU_CLUT_CHANGE
+- *
+- * Inform the SPU decoder/overlaying engine to update its colour lookup table.
+- * The CLUT is given as 16 uint32_t's in the buffer.
+- */
+-#define DVDNAV_SPU_CLUT_CHANGE 10
+-
+-/*
+- * DVDNAV_HOP_CHANNEL
+- *
+- * A non-seamless operation has been performed. Applications can drop all
+- * their internal fifo's content, which will speed up the response.
+- */
+-#define DVDNAV_HOP_CHANNEL 12
+-
+-/*
+- * DVDNAV_WAIT
+- *
+- * We have reached a point in DVD playback, where timing is critical.
+- * Player application with internal fifos can introduce state
+- * inconsistencies, because libdvdnav is always the fifo's length
+- * ahead in the stream compared to what the application sees.
+- * Such applications should wait until their fifos are empty
+- * when they receive this type of event.
+- * Once this is achieved, call dvdnav_skip_wait().
+- */
+-#define DVDNAV_WAIT 13
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/ifo_types.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/ifo_types.h
+deleted file mode 100644
+index 4191b67922..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/ifo_types.h
++++ /dev/null
+@@ -1,754 +0,0 @@
+-/*
+- * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+- * Håkan Hjort <d95hjort@dtek.chalmers.se>
+- *
+- * This file is part of libdvdread.
+- *
+- * libdvdread 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; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * libdvdread 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 libdvdread; if not, write to the Free Software Foundation, Inc.,
+- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+- */
+-
+-#pragma once
+-
+-//#include <inttypes.h>
+-#include "dvd_reader.h"
+-
+-
+-#undef ATTRIBUTE_PACKED
+-
+-#if defined(__GNUC__)
+-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+-#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && !defined(__clang__)
+-#define ATTRIBUTE_PACKED __attribute__((packed, gcc_struct))
+-#else
+-#define ATTRIBUTE_PACKED __attribute__((packed))
+-#endif
+-#define PRAGMA_PACK 0
+-#endif
+-#endif
+-
+-#if !defined(ATTRIBUTE_PACKED)
+-#define ATTRIBUTE_PACKED
+-#define PRAGMA_PACK 1
+-#endif
+-
+-#if PRAGMA_PACK
+-#pragma pack(1)
+-#endif
+-
+-
+-/**
+- * Common
+- *
+- * The following structures are used in both the VMGI and VTSI.
+- */
+-
+-
+-/**
+- * DVD Time Information.
+- */
+-typedef struct {
+- uint8_t hour;
+- uint8_t minute;
+- uint8_t second;
+- uint8_t frame_u; /* The two high bits are the frame rate. */
+-} ATTRIBUTE_PACKED dvd_time_t;
+-
+-/**
+- * Type to store per-command data.
+- */
+-typedef struct {
+- uint8_t bytes[8];
+-} ATTRIBUTE_PACKED vm_cmd_t;
+-#define COMMAND_DATA_SIZE 8U
+-
+-
+-/**
+- * Video Attributes.
+- */
+-typedef struct {
+- unsigned char mpeg_version : 2;
+- unsigned char video_format : 2;
+- unsigned char display_aspect_ratio : 2;
+- unsigned char permitted_df : 2;
+-
+- unsigned char line21_cc_1 : 1;
+- unsigned char line21_cc_2 : 1;
+- unsigned char unknown1 : 1;
+- unsigned char bit_rate : 1;
+-
+- unsigned char picture_size : 2;
+- unsigned char letterboxed : 1;
+- unsigned char film_mode : 1;
+-} ATTRIBUTE_PACKED video_attr_t;
+-
+-/**
+- * Audio Attributes.
+- */
+-typedef struct {
+- unsigned char audio_format : 3;
+- unsigned char multichannel_extension : 1;
+- unsigned char lang_type : 2;
+- unsigned char application_mode : 2;
+-
+- unsigned char quantization : 2;
+- unsigned char sample_frequency : 2;
+- unsigned char unknown1 : 1;
+- unsigned char channels : 3;
+- uint16_t lang_code;
+- uint8_t lang_extension;
+- uint8_t code_extension;
+- uint8_t unknown3;
+- union {
+- struct ATTRIBUTE_PACKED {
+- unsigned char unknown4 : 1;
+- unsigned char channel_assignment : 3;
+- unsigned char version : 2;
+- unsigned char mc_intro : 1; /* probably 0: true, 1:false */
+- unsigned char mode : 1; /* Karaoke mode 0: solo 1: duet */
+- } karaoke;
+- struct ATTRIBUTE_PACKED {
+- unsigned char unknown5 : 4;
+- unsigned char dolby_encoded : 1; /* suitable for surround decoding */
+- unsigned char unknown6 : 3;
+- } surround;
+- } ATTRIBUTE_PACKED app_info;
+-} ATTRIBUTE_PACKED audio_attr_t;
+-
+-
+-/**
+- * MultiChannel Extension
+- */
+-typedef struct {
+- unsigned char zero1 : 7;
+- unsigned char ach0_gme : 1;
+-
+- unsigned char zero2 : 7;
+- unsigned char ach1_gme : 1;
+-
+- unsigned char zero3 : 4;
+- unsigned char ach2_gv1e : 1;
+- unsigned char ach2_gv2e : 1;
+- unsigned char ach2_gm1e : 1;
+- unsigned char ach2_gm2e : 1;
+-
+- unsigned char zero4 : 4;
+- unsigned char ach3_gv1e : 1;
+- unsigned char ach3_gv2e : 1;
+- unsigned char ach3_gmAe : 1;
+- unsigned char ach3_se2e : 1;
+-
+- unsigned char zero5 : 4;
+- unsigned char ach4_gv1e : 1;
+- unsigned char ach4_gv2e : 1;
+- unsigned char ach4_gmBe : 1;
+- unsigned char ach4_seBe : 1;
+- uint8_t zero6[19];
+-} ATTRIBUTE_PACKED multichannel_ext_t;
+-
+-
+-/**
+- * Subpicture Attributes.
+- */
+-typedef struct {
+- /*
+- * type: 0 not specified
+- * 1 language
+- * 2 other
+- * coding mode: 0 run length
+- * 1 extended
+- * 2 other
+- * language: indicates language if type == 1
+- * lang extension: if type == 1 contains the lang extension
+- */
+- unsigned char code_mode : 3;
+- unsigned char zero1 : 3;
+- unsigned char type : 2;
+- uint8_t zero2;
+- uint16_t lang_code;
+- uint8_t lang_extension;
+- uint8_t code_extension;
+-} ATTRIBUTE_PACKED subp_attr_t;
+-
+-
+-
+-/**
+- * PGC Command Table.
+- */
+-typedef struct {
+- uint16_t nr_of_pre;
+- uint16_t nr_of_post;
+- uint16_t nr_of_cell;
+- uint16_t last_byte;
+- vm_cmd_t *pre_cmds;
+- vm_cmd_t *post_cmds;
+- vm_cmd_t *cell_cmds;
+-} ATTRIBUTE_PACKED pgc_command_tbl_t;
+-#define PGC_COMMAND_TBL_SIZE 8U
+-
+-/**
+- * PGC Program Map
+- */
+-typedef uint8_t pgc_program_map_t;
+-
+-/**
+- * Cell Playback Information.
+- */
+-typedef struct {
+- unsigned char block_mode : 2;
+- unsigned char block_type : 2;
+- unsigned char seamless_play : 1;
+- unsigned char interleaved : 1;
+- unsigned char stc_discontinuity : 1;
+- unsigned char seamless_angle : 1;
+- unsigned char zero_1 : 1;
+- unsigned char playback_mode : 1; /**< When set, enter StillMode after each VOBU */
+- unsigned char restricted : 1; /**< ?? drop out of fastforward? */
+- unsigned char cell_type : 5; /** for karaoke, reserved otherwise */
+- uint8_t still_time;
+- uint8_t cell_cmd_nr;
+- dvd_time_t playback_time;
+- uint32_t first_sector;
+- uint32_t first_ilvu_end_sector;
+- uint32_t last_vobu_start_sector;
+- uint32_t last_sector;
+-} ATTRIBUTE_PACKED cell_playback_t;
+-
+-#define BLOCK_TYPE_NONE 0x0
+-#define BLOCK_TYPE_ANGLE_BLOCK 0x1
+-
+-#define BLOCK_MODE_NOT_IN_BLOCK 0x0
+-#define BLOCK_MODE_FIRST_CELL 0x1
+-#define BLOCK_MODE_IN_BLOCK 0x2
+-#define BLOCK_MODE_LAST_CELL 0x3
+-
+-/**
+- * Cell Position Information.
+- */
+-typedef struct {
+- uint16_t vob_id_nr;
+- uint8_t zero_1;
+- uint8_t cell_nr;
+-} ATTRIBUTE_PACKED cell_position_t;
+-
+-/**
+- * User Operations.
+- */
+-typedef struct {
+- unsigned char zero : 7; /* 25-31 */
+- unsigned char video_pres_mode_change : 1; /* 24 */
+-
+- unsigned char karaoke_audio_pres_mode_change : 1; /* 23 */
+- unsigned char angle_change : 1;
+- unsigned char subpic_stream_change : 1;
+- unsigned char audio_stream_change : 1;
+- unsigned char pause_on : 1;
+- unsigned char still_off : 1;
+- unsigned char button_select_or_activate : 1;
+- unsigned char resume : 1; /* 16 */
+-
+- unsigned char chapter_menu_call : 1; /* 15 */
+- unsigned char angle_menu_call : 1;
+- unsigned char audio_menu_call : 1;
+- unsigned char subpic_menu_call : 1;
+- unsigned char root_menu_call : 1;
+- unsigned char title_menu_call : 1;
+- unsigned char backward_scan : 1;
+- unsigned char forward_scan : 1; /* 8 */
+-
+- unsigned char next_pg_search : 1; /* 7 */
+- unsigned char prev_or_top_pg_search : 1;
+- unsigned char time_or_chapter_search : 1;
+- unsigned char go_up : 1;
+- unsigned char stop : 1;
+- unsigned char title_play : 1;
+- unsigned char chapter_search_or_play : 1;
+- unsigned char title_or_time_play : 1; /* 0 */
+-} ATTRIBUTE_PACKED user_ops_t;
+-
+-/**
+- * Program Chain Information.
+- */
+-typedef struct {
+- uint16_t zero_1;
+- uint8_t nr_of_programs;
+- uint8_t nr_of_cells;
+- dvd_time_t playback_time;
+- user_ops_t prohibited_ops;
+- uint16_t audio_control[8]; /* New type? */
+- uint32_t subp_control[32]; /* New type? */
+- uint16_t next_pgc_nr;
+- uint16_t prev_pgc_nr;
+- uint16_t goup_pgc_nr;
+- uint8_t pg_playback_mode;
+- uint8_t still_time;
+- uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
+- uint16_t command_tbl_offset;
+- uint16_t program_map_offset;
+- uint16_t cell_playback_offset;
+- uint16_t cell_position_offset;
+- pgc_command_tbl_t *command_tbl;
+- pgc_program_map_t *program_map;
+- cell_playback_t *cell_playback;
+- cell_position_t *cell_position;
+- int ref_count;
+-} ATTRIBUTE_PACKED pgc_t;
+-#define PGC_SIZE 236U
+-
+-/**
+- * Program Chain Information Search Pointer.
+- */
+-typedef struct {
+- uint8_t entry_id;
+- unsigned char block_mode : 2;
+- unsigned char block_type : 2;
+- unsigned char zero_1 : 4;
+- uint16_t ptl_id_mask;
+- uint32_t pgc_start_byte;
+- pgc_t *pgc;
+-} ATTRIBUTE_PACKED pgci_srp_t;
+-#define PGCI_SRP_SIZE 8U
+-
+-/**
+- * Program Chain Information Table.
+- */
+-typedef struct {
+- uint16_t nr_of_pgci_srp;
+- uint16_t zero_1;
+- uint32_t last_byte;
+- pgci_srp_t *pgci_srp;
+- int ref_count;
+-} ATTRIBUTE_PACKED pgcit_t;
+-#define PGCIT_SIZE 8U
+-
+-/**
+- * Menu PGCI Language Unit.
+- */
+-typedef struct {
+- uint16_t lang_code;
+- uint8_t lang_extension;
+- uint8_t exists;
+- uint32_t lang_start_byte;
+- pgcit_t *pgcit;
+-} ATTRIBUTE_PACKED pgci_lu_t;
+-#define PGCI_LU_SIZE 8U
+-
+-/**
+- * Menu PGCI Unit Table.
+- */
+-typedef struct {
+- uint16_t nr_of_lus;
+- uint16_t zero_1;
+- uint32_t last_byte;
+- pgci_lu_t *lu;
+-} ATTRIBUTE_PACKED pgci_ut_t;
+-#define PGCI_UT_SIZE 8U
+-
+-/**
+- * Cell Address Information.
+- */
+-typedef struct {
+- uint16_t vob_id;
+- uint8_t cell_id;
+- uint8_t zero_1;
+- uint32_t start_sector;
+- uint32_t last_sector;
+-} ATTRIBUTE_PACKED cell_adr_t;
+-
+-/**
+- * Cell Address Table.
+- */
+-typedef struct {
+- uint16_t nr_of_vobs; /* VOBs */
+- uint16_t zero_1;
+- uint32_t last_byte;
+- cell_adr_t *cell_adr_table; /* No explicit size given. */
+-} ATTRIBUTE_PACKED c_adt_t;
+-#define C_ADT_SIZE 8U
+-
+-/**
+- * VOBU Address Map.
+- */
+-typedef struct {
+- uint32_t last_byte;
+- uint32_t *vobu_start_sectors;
+-} ATTRIBUTE_PACKED vobu_admap_t;
+-#define VOBU_ADMAP_SIZE 4U
+-
+-
+-
+-
+-/**
+- * VMGI
+- *
+- * The following structures relate to the Video Manager.
+- */
+-
+-/**
+- * Video Manager Information Management Table.
+- */
+-typedef struct {
+- char vmg_identifier[12];
+- uint32_t vmg_last_sector;
+- uint8_t zero_1[12];
+- uint32_t vmgi_last_sector;
+- uint8_t zero_2;
+- uint8_t specification_version;
+- uint32_t vmg_category;
+- uint16_t vmg_nr_of_volumes;
+- uint16_t vmg_this_volume_nr;
+- uint8_t disc_side;
+- uint8_t zero_3[19];
+- uint16_t vmg_nr_of_title_sets; /* Number of VTSs. */
+- char provider_identifier[32];
+- uint64_t vmg_pos_code;
+- uint8_t zero_4[24];
+- uint32_t vmgi_last_byte;
+- uint32_t first_play_pgc;
+- uint8_t zero_5[56];
+- uint32_t vmgm_vobs; /* sector */
+- uint32_t tt_srpt; /* sector */
+- uint32_t vmgm_pgci_ut; /* sector */
+- uint32_t ptl_mait; /* sector */
+- uint32_t vts_atrt; /* sector */
+- uint32_t txtdt_mgi; /* sector */
+- uint32_t vmgm_c_adt; /* sector */
+- uint32_t vmgm_vobu_admap; /* sector */
+- uint8_t zero_6[32];
+-
+- video_attr_t vmgm_video_attr;
+- uint8_t zero_7;
+- uint8_t nr_of_vmgm_audio_streams; /* should be 0 or 1 */
+- audio_attr_t vmgm_audio_attr;
+- audio_attr_t zero_8[7];
+- uint8_t zero_9[17];
+- uint8_t nr_of_vmgm_subp_streams; /* should be 0 or 1 */
+- subp_attr_t vmgm_subp_attr;
+- subp_attr_t zero_10[27]; /* XXX: how much 'padding' here? */
+-} ATTRIBUTE_PACKED vmgi_mat_t;
+-
+-typedef struct {
+- unsigned char zero_1 : 1;
+- unsigned char multi_or_random_pgc_title : 1; /* 0: one sequential pgc title */
+- unsigned char jlc_exists_in_cell_cmd : 1;
+- unsigned char jlc_exists_in_prepost_cmd : 1;
+- unsigned char jlc_exists_in_button_cmd : 1;
+- unsigned char jlc_exists_in_tt_dom : 1;
+- unsigned char chapter_search_or_play : 1; /* UOP 1 */
+- unsigned char title_or_time_play : 1; /* UOP 0 */
+-} ATTRIBUTE_PACKED playback_type_t;
+-
+-/**
+- * Title Information.
+- */
+-typedef struct {
+- playback_type_t pb_ty;
+- uint8_t nr_of_angles;
+- uint16_t nr_of_ptts;
+- uint16_t parental_id;
+- uint8_t title_set_nr;
+- uint8_t vts_ttn;
+- uint32_t title_set_sector;
+-} ATTRIBUTE_PACKED title_info_t;
+-
+-/**
+- * PartOfTitle Search Pointer Table.
+- */
+-typedef struct {
+- uint16_t nr_of_srpts;
+- uint16_t zero_1;
+- uint32_t last_byte;
+- title_info_t *title;
+-} ATTRIBUTE_PACKED tt_srpt_t;
+-#define TT_SRPT_SIZE 8U
+-
+-
+-/**
+- * Parental Management Information Unit Table.
+- * Level 1 (US: G), ..., 7 (US: NC-17), 8
+- */
+-#define PTL_MAIT_NUM_LEVEL 8
+-typedef uint16_t pf_level_t[PTL_MAIT_NUM_LEVEL];
+-
+-/**
+- * Parental Management Information Unit Table.
+- */
+-typedef struct {
+- uint16_t country_code;
+- uint16_t zero_1;
+- uint16_t pf_ptl_mai_start_byte;
+- uint16_t zero_2;
+- pf_level_t *pf_ptl_mai; /* table of (nr_of_vtss + 1), video_ts is first */
+-} ATTRIBUTE_PACKED ptl_mait_country_t;
+-#define PTL_MAIT_COUNTRY_SIZE 8U
+-
+-/**
+- * Parental Management Information Table.
+- */
+-typedef struct {
+- uint16_t nr_of_countries;
+- uint16_t nr_of_vtss;
+- uint32_t last_byte;
+- ptl_mait_country_t *countries;
+-} ATTRIBUTE_PACKED ptl_mait_t;
+-#define PTL_MAIT_SIZE 8U
+-
+-/**
+- * Video Title Set Attributes.
+- */
+-typedef struct {
+- uint32_t last_byte;
+- uint32_t vts_cat;
+-
+- video_attr_t vtsm_vobs_attr;
+- uint8_t zero_1;
+- uint8_t nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+- audio_attr_t vtsm_audio_attr;
+- audio_attr_t zero_2[7];
+- uint8_t zero_3[16];
+- uint8_t zero_4;
+- uint8_t nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+- subp_attr_t vtsm_subp_attr;
+- subp_attr_t zero_5[27];
+-
+- uint8_t zero_6[2];
+-
+- video_attr_t vtstt_vobs_video_attr;
+- uint8_t zero_7;
+- uint8_t nr_of_vtstt_audio_streams;
+- audio_attr_t vtstt_audio_attr[8];
+- uint8_t zero_8[16];
+- uint8_t zero_9;
+- uint8_t nr_of_vtstt_subp_streams;
+- subp_attr_t vtstt_subp_attr[32];
+-} ATTRIBUTE_PACKED vts_attributes_t;
+-#define VTS_ATTRIBUTES_SIZE 542U
+-#define VTS_ATTRIBUTES_MIN_SIZE 356U
+-
+-/**
+- * Video Title Set Attribute Table.
+- */
+-typedef struct {
+- uint16_t nr_of_vtss;
+- uint16_t zero_1;
+- uint32_t last_byte;
+- vts_attributes_t *vts;
+- uint32_t *vts_atrt_offsets; /* offsets table for each vts_attributes */
+-} ATTRIBUTE_PACKED vts_atrt_t;
+-#define VTS_ATRT_SIZE 8U
+-
+-/**
+- * Text Data. (Incomplete)
+- */
+-typedef struct {
+- uint32_t last_byte; /* offsets are relative here */
+- uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
+-#if 0
+- uint16_t unknown; /* 0x48 ?? 0x48 words (16bit) info following */
+- uint16_t zero_1;
+-
+- uint8_t type_of_info; /* ?? 01 == disc, 02 == Title, 04 == Title part */
+- uint8_t unknown1;
+- uint8_t unknown2;
+- uint8_t unknown3;
+- uint8_t unknown4; /* ?? always 0x30 language?, text format? */
+- uint8_t unknown5;
+- uint16_t offset; /* from first */
+-
+- char text[12]; /* ended by 0x09 */
+-#endif
+-} ATTRIBUTE_PACKED txtdt_t;
+-
+-/**
+- * Text Data Language Unit. (Incomplete)
+- */
+-typedef struct {
+- uint16_t lang_code;
+- uint8_t zero_1;
+- uint8_t
+- char_set; /* 0x00 reserved Unicode, 0x01 ISO 646, 0x10 JIS Roman & JIS Kanji, 0x11 ISO 8859-1, 0x12 Shift JIS Kanji */
+- uint32_t txtdt_start_byte; /* prt, rel start of vmg_txtdt_mgi */
+- txtdt_t *txtdt;
+-} ATTRIBUTE_PACKED txtdt_lu_t;
+-#define TXTDT_LU_SIZE 8U
+-
+-/**
+- * Text Data Manager Information. (Incomplete)
+- */
+-typedef struct {
+- char disc_name[12];
+- uint16_t unknown1;
+- uint16_t nr_of_language_units;
+- uint32_t last_byte;
+- txtdt_lu_t *lu;
+-} ATTRIBUTE_PACKED txtdt_mgi_t;
+-#define TXTDT_MGI_SIZE 20U
+-
+-
+-/**
+- * VTS
+- *
+- * Structures relating to the Video Title Set (VTS).
+- */
+-
+-/**
+- * Video Title Set Information Management Table.
+- */
+-typedef struct {
+- char vts_identifier[12];
+- uint32_t vts_last_sector;
+- uint8_t zero_1[12];
+- uint32_t vtsi_last_sector;
+- uint8_t zero_2;
+- uint8_t specification_version;
+- uint32_t vts_category;
+- uint16_t zero_3;
+- uint16_t zero_4;
+- uint8_t zero_5;
+- uint8_t zero_6[19];
+- uint16_t zero_7;
+- uint8_t zero_8[32];
+- uint64_t zero_9;
+- uint8_t zero_10[24];
+- uint32_t vtsi_last_byte;
+- uint32_t zero_11;
+- uint8_t zero_12[56];
+- uint32_t vtsm_vobs; /* sector */
+- uint32_t vtstt_vobs; /* sector */
+- uint32_t vts_ptt_srpt; /* sector */
+- uint32_t vts_pgcit; /* sector */
+- uint32_t vtsm_pgci_ut; /* sector */
+- uint32_t vts_tmapt; /* sector */
+- uint32_t vtsm_c_adt; /* sector */
+- uint32_t vtsm_vobu_admap; /* sector */
+- uint32_t vts_c_adt; /* sector */
+- uint32_t vts_vobu_admap; /* sector */
+- uint8_t zero_13[24];
+-
+- video_attr_t vtsm_video_attr;
+- uint8_t zero_14;
+- uint8_t nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+- audio_attr_t vtsm_audio_attr;
+- audio_attr_t zero_15[7];
+- uint8_t zero_16[17];
+- uint8_t nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+- subp_attr_t vtsm_subp_attr;
+- subp_attr_t zero_17[27];
+- uint8_t zero_18[2];
+-
+- video_attr_t vts_video_attr;
+- uint8_t zero_19;
+- uint8_t nr_of_vts_audio_streams;
+- audio_attr_t vts_audio_attr[8];
+- uint8_t zero_20[17];
+- uint8_t nr_of_vts_subp_streams;
+- subp_attr_t vts_subp_attr[32];
+- uint16_t zero_21;
+- multichannel_ext_t vts_mu_audio_attr[8];
+- /* XXX: how much 'padding' here, if any? */
+-} ATTRIBUTE_PACKED vtsi_mat_t;
+-
+-/**
+- * PartOfTitle Unit Information.
+- */
+-typedef struct {
+- uint16_t pgcn;
+- uint16_t pgn;
+-} ATTRIBUTE_PACKED ptt_info_t;
+-
+-/**
+- * PartOfTitle Information.
+- */
+-typedef struct {
+- uint16_t nr_of_ptts;
+- ptt_info_t *ptt;
+-} ATTRIBUTE_PACKED ttu_t;
+-
+-/**
+- * PartOfTitle Search Pointer Table.
+- */
+-typedef struct {
+- uint16_t nr_of_srpts;
+- uint16_t zero_1;
+- uint32_t last_byte;
+- ttu_t *title;
+- uint32_t *ttu_offset; /* offset table for each ttu */
+-} ATTRIBUTE_PACKED vts_ptt_srpt_t;
+-#define VTS_PTT_SRPT_SIZE 8U
+-
+-
+-/**
+- * Time Map Entry.
+- */
+-/* Should this be bit field at all or just the uint32_t? */
+-typedef uint32_t map_ent_t;
+-
+-/**
+- * Time Map.
+- */
+-typedef struct {
+- uint8_t tmu; /* Time unit, in seconds */
+- uint8_t zero_1;
+- uint16_t nr_of_entries;
+- map_ent_t *map_ent;
+-} ATTRIBUTE_PACKED vts_tmap_t;
+-#define VTS_TMAP_SIZE 4U
+-
+-/**
+- * Time Map Table.
+- */
+-typedef struct {
+- uint16_t nr_of_tmaps;
+- uint16_t zero_1;
+- uint32_t last_byte;
+- vts_tmap_t *tmap;
+- uint32_t *tmap_offset; /* offset table for each tmap */
+-} ATTRIBUTE_PACKED vts_tmapt_t;
+-#define VTS_TMAPT_SIZE 8U
+-
+-
+-#if PRAGMA_PACK
+-#pragma pack()
+-#endif
+-
+-
+-/**
+- * The following structure defines an IFO file. The structure is divided into
+- * two parts, the VMGI, or Video Manager Information, which is read from the
+- * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
+- * is read in from the VTS_XX_0.[IFO,BUP] files.
+- */
+-typedef struct {
+- /* VMGI */
+- vmgi_mat_t *vmgi_mat;
+- tt_srpt_t *tt_srpt;
+- pgc_t *first_play_pgc;
+- ptl_mait_t *ptl_mait;
+- vts_atrt_t *vts_atrt;
+- txtdt_mgi_t *txtdt_mgi;
+-
+- /* Common */
+- pgci_ut_t *pgci_ut;
+- c_adt_t *menu_c_adt;
+- vobu_admap_t *menu_vobu_admap;
+-
+- /* VTSI */
+- vtsi_mat_t *vtsi_mat;
+- vts_ptt_srpt_t *vts_ptt_srpt;
+- pgcit_t *vts_pgcit;
+- vts_tmapt_t *vts_tmapt;
+- c_adt_t *vts_c_adt;
+- vobu_admap_t *vts_vobu_admap;
+-} ifo_handle_t;
+-
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/nav_types.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/nav_types.h
+deleted file mode 100644
+index aa33f23d4d..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/nav_types.h
++++ /dev/null
+@@ -1,258 +0,0 @@
+-/*
+- * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
+- *
+- * SPDX-License-Identifier: GPL-2.0-only
+- * See LICENSES/README.md for more information.
+- *
+- * The data structures in this file should represent the layout of the
+- * pci and dsi packets as they are stored in the stream. Information
+- * found by reading the source to VOBDUMP is the base for the structure
+- * and names of these data types.
+- *
+- * VOBDUMP: a program for examining DVD .VOB files.
+- * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+- */
+-
+-#pragma once
+-
+-//#include <inttypes.h>
+-#include "ifo_types.h" /* only dvd_time_t, vm_cmd_t and user_ops_t */
+-
+-
+-#undef ATTRIBUTE_PACKED
+-#undef PRAGMA_PACK_BEGIN
+-#undef PRAGMA_PACK_END
+-
+-#if defined(__GNUC__)
+-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+-#define ATTRIBUTE_PACKED __attribute__ ((packed))
+-#define PRAGMA_PACK 0
+-#endif
+-#endif
+-
+-#if !defined(ATTRIBUTE_PACKED)
+-#define ATTRIBUTE_PACKED
+-#define PRAGMA_PACK 1
+-#endif
+-
+-
+-/* The length including the substream id byte. */
+-#define PCI_BYTES 0x3d4
+-#define DSI_BYTES 0x3fa
+-
+-#define PS2_PCI_SUBSTREAM_ID 0x00
+-#define PS2_DSI_SUBSTREAM_ID 0x01
+-
+-/* Remove this */
+-#define DSI_START_BYTE 1031
+-
+-
+-#if PRAGMA_PACK
+-#pragma pack(1)
+-#endif
+-
+-
+-/**
+- * PCI General Information
+- */
+-typedef struct {
+- uint32_t nv_pck_lbn; /**< sector address of this nav pack */
+- uint16_t vobu_cat; /**< 'category' of vobu */
+- uint16_t zero1; /**< reserved */
+- user_ops_t vobu_uop_ctl; /**< UOP of vobu */
+- uint32_t vobu_s_ptm; /**< start presentation time of vobu */
+- uint32_t vobu_e_ptm; /**< end presentation time of vobu */
+- uint32_t vobu_se_e_ptm; /**< end ptm of sequence end in vobu */
+- dvd_time_t e_eltm; /**< Cell elapsed time */
+- char vobu_isrc[32];
+-} ATTRIBUTE_PACKED pci_gi_t;
+-
+-/**
+- * Non Seamless Angle Information
+- */
+-typedef struct {
+- uint32_t nsml_agl_dsta[9]; /**< address of destination vobu in AGL_C#n */
+-} ATTRIBUTE_PACKED nsml_agli_t;
+-
+-/**
+- * Highlight General Information
+- *
+- * For btngrX_dsp_ty the bits have the following meaning:
+- * 000b: normal 4/3 only buttons
+- * XX1b: wide (16/9) buttons
+- * X1Xb: letterbox buttons
+- * 1XXb: pan&scan buttons
+- */
+-typedef struct {
+- uint16_t hli_ss; /**< status, only low 2 bits 0: no buttons, 1: different 2: equal 3: equal except for button cmds */
+- uint32_t hli_s_ptm; /**< start ptm of hli */
+- uint32_t hli_e_ptm; /**< end ptm of hli */
+- uint32_t btn_se_e_ptm; /**< end ptm of button select */
+- unsigned int zero1 : 2; /**< reserved */
+- unsigned int btngr_ns : 2; /**< number of button groups 1, 2 or 3 with 36/18/12 buttons */
+- unsigned int zero2 : 1; /**< reserved */
+- unsigned int btngr1_dsp_ty : 3; /**< display type of subpic stream for button group 1 */
+- unsigned int zero3 : 1; /**< reserved */
+- unsigned int btngr2_dsp_ty : 3; /**< display type of subpic stream for button group 2 */
+- unsigned int zero4 : 1; /**< reserved */
+- unsigned int btngr3_dsp_ty : 3; /**< display type of subpic stream for button group 3 */
+- uint8_t btn_ofn; /**< button offset number range 0-255 */
+- uint8_t btn_ns; /**< number of valid buttons <= 36/18/12 (low 6 bits) */
+- uint8_t nsl_btn_ns; /**< number of buttons selectable by U_BTNNi (low 6 bits) nsl_btn_ns <= btn_ns */
+- uint8_t zero5; /**< reserved */
+- uint8_t fosl_btnn; /**< forcedly selected button (low 6 bits) */
+- uint8_t foac_btnn; /**< forcedly activated button (low 6 bits) */
+-} ATTRIBUTE_PACKED hl_gi_t;
+-
+-
+-/**
+- * Button Color Information Table
+- * Each entry being a 32bit word that contains the color indexes and alpha
+- * values to use. They are all represented by 4 bit number and stored
+- * like this [Ci3, Ci2, Ci1, Ci0, A3, A2, A1, A0]. The actual palette
+- * that the indexes reference is in the PGC.
+- * @TODO split the uint32_t into a struct
+- */
+-typedef struct {
+- uint32_t btn_coli[3][2]; /**< [button color number-1][select:0/action:1] */
+-} ATTRIBUTE_PACKED btn_colit_t;
+-
+-/**
+- * Button Information
+- *
+- * NOTE: I've had to change the structure from the disk layout to get
+- * the packing to work with Sun's Forte C compiler.
+- * The 4 and 7 bytes are 'rotated' was: ABC DEF GHIJ is: ABCG DEFH IJ
+- */
+-typedef struct {
+- unsigned int btn_coln : 2; /**< button color number */
+- unsigned int x_start : 10; /**< x start offset within the overlay */
+- unsigned int zero1 : 2; /**< reserved */
+- unsigned int x_end : 10; /**< x end offset within the overlay */
+-
+- unsigned int auto_action_mode : 2; /**< 0: no, 1: activated if selected */
+- unsigned int y_start : 10; /**< y start offset within the overlay */
+- unsigned int zero2 : 2; /**< reserved */
+- unsigned int y_end : 10; /**< y end offset within the overlay */
+-
+- unsigned int zero3 : 2; /**< reserved */
+- unsigned int up : 6; /**< button index when pressing up */
+- unsigned int zero4 : 2; /**< reserved */
+- unsigned int down : 6; /**< button index when pressing down */
+- unsigned int zero5 : 2; /**< reserved */
+- unsigned int left : 6; /**< button index when pressing left */
+- unsigned int zero6 : 2; /**< reserved */
+- unsigned int right : 6; /**< button index when pressing right */
+- vm_cmd_t cmd;
+-} ATTRIBUTE_PACKED btni_t;
+-
+-/**
+- * Highlight Information
+- */
+-typedef struct {
+- hl_gi_t hl_gi;
+- btn_colit_t btn_colit;
+- btni_t btnit[36];
+-} ATTRIBUTE_PACKED hli_t;
+-
+-/**
+- * PCI packet
+- */
+-typedef struct {
+- pci_gi_t pci_gi;
+- nsml_agli_t nsml_agli;
+- hli_t hli;
+- uint8_t zero1[189];
+-} ATTRIBUTE_PACKED pci_t;
+-
+-
+-
+-
+-/**
+- * DSI General Information
+- */
+-typedef struct {
+- uint32_t nv_pck_scr;
+- uint32_t nv_pck_lbn; /**< sector address of this nav pack */
+- uint32_t vobu_ea; /**< end address of this VOBU */
+- uint32_t vobu_1stref_ea; /**< end address of the 1st reference image */
+- uint32_t vobu_2ndref_ea; /**< end address of the 2nd reference image */
+- uint32_t vobu_3rdref_ea; /**< end address of the 3rd reference image */
+- uint16_t vobu_vob_idn; /**< VOB Id number that this VOBU is part of */
+- uint8_t zero1; /**< reserved */
+- uint8_t vobu_c_idn; /**< Cell Id number that this VOBU is part of */
+- dvd_time_t c_eltm; /**< Cell elapsed time */
+-} ATTRIBUTE_PACKED dsi_gi_t;
+-
+-/**
+- * Seamless Playback Information
+- */
+-typedef struct {
+- uint16_t category; /**< 'category' of seamless VOBU */
+- uint32_t ilvu_ea; /**< end address of interleaved Unit */
+- uint32_t ilvu_sa; /**< start address of next interleaved unit */
+- uint16_t size; /**< size of next interleaved unit */
+- uint32_t vob_v_s_s_ptm; /**< video start ptm in vob */
+- uint32_t vob_v_e_e_ptm; /**< video end ptm in vob */
+- struct {
+- uint32_t stp_ptm1;
+- uint32_t stp_ptm2;
+- uint32_t gap_len1;
+- uint32_t gap_len2;
+- } vob_a[8];
+-} ATTRIBUTE_PACKED sml_pbi_t;
+-
+-/**
+- * Seamless Angle Information for one angle
+- */
+-typedef struct {
+- uint32_t address; /**< offset to next ILVU, high bit is before/after */
+- uint16_t size; /**< byte size of the ILVU pointed to by address */
+-} ATTRIBUTE_PACKED sml_agl_data_t;
+-
+-/**
+- * Seamless Angle Information
+- */
+-typedef struct {
+- sml_agl_data_t data[9];
+-} ATTRIBUTE_PACKED sml_agli_t;
+-
+-/**
+- * VOBU Search Information
+- */
+-typedef struct {
+- uint32_t next_video; /**< Next vobu that contains video */
+- uint32_t fwda[19]; /**< Forwards, time */
+- uint32_t next_vobu;
+- uint32_t prev_vobu;
+- uint32_t bwda[19]; /**< Backwards, time */
+- uint32_t prev_video;
+-} ATTRIBUTE_PACKED vobu_sri_t;
+-
+-#define SRI_END_OF_CELL 0x3fffffff
+-
+-/**
+- * Synchronous Information
+- */
+-typedef struct {
+- uint16_t a_synca[8]; /**< offset to first audio packet for this VOBU */
+- uint32_t sp_synca[32]; /**< offset to first subpicture packet */
+-} ATTRIBUTE_PACKED synci_t;
+-
+-/**
+- * DSI packet
+- */
+-typedef struct {
+- dsi_gi_t dsi_gi;
+- sml_pbi_t sml_pbi;
+- sml_agli_t sml_agli;
+- vobu_sri_t vobu_sri;
+- synci_t synci;
+- uint8_t zero1[471];
+-} ATTRIBUTE_PACKED dsi_t;
+-
+-
+-#if PRAGMA_PACK
+-#pragma pack()
+-#endif
+-
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/version.h b/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/version.h
+deleted file mode 100644
+index dced5e766a..0000000000
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/version.h
++++ /dev/null
+@@ -1,29 +0,0 @@
+-/*
+-* This file is part of libdvdnav, a DVD navigation library.
+-*
+- * This program is free software; you can redistribute it and/or modify it
+- * under the terms of the GNU Lesser General Public License as published by
+- * the Free Software Foundation; either version 2.1 of the License, or
+- * (at your option) any later version.
+- *
+- * This program 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 Lesser General Public License for more details.
+- *
+- * You should have received a copy of the GNU Lesser General Public License
+- * along with this program; if not, write to the Free Software Foundation,
+- * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+-*/
+-#pragma once
+-
+-#define DVDNAV_VERSION_CODE(major, minor, micro) (((major)*10000) + ((minor)*100) + ((micro)*1))
+-
+-#define DVDNAV_VERSION_MAJOR 6
+-#define DVDNAV_VERSION_MINOR 1
+-#define DVDNAV_VERSION_MICRO 1
+-
+-#define DVDNAV_VERSION_STRING "6.1.1"
+-
+-#define DVDNAV_VERSION \
+- DVDNAV_VERSION_CODE(DVDNAV_VERSION_MAJOR, DVDNAV_VERSION_MINOR, DVDNAV_VERSION_MICRO)
+--
+2.35.1
+
+
+From d5d87a09d04f427826387f980ffaeeab737c2d78 Mon Sep 17 00:00:00 2001
+From: fuzzard <fuzzard@kodi.tv>
+Date: Mon, 27 Jun 2022 19:48:48 +1000
+Subject: [PATCH 2/9] WIP!: allow system libs for dvdread/nav/css
+
+---
+ CMakeLists.txt | 1 +
+ cmake/modules/FindLibDvd.cmake | 13 --
+ cmake/modules/FindLibDvdCSS.cmake | 124 ++++++++-------
+ cmake/modules/FindLibDvdNav.cmake | 140 +++++++++--------
+ cmake/modules/FindLibDvdRead.cmake | 144 ++++++++++--------
+ .../DVDInputStreamNavigator.cpp | 4 +-
+ .../DVDInputStreams/DVDInputStreamNavigator.h | 8 +-
+ 7 files changed, 233 insertions(+), 201 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 19881e4708..71660392e5 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -70,6 +70,7 @@ option(ENABLE_INTERNAL_FFMPEG "Enable internal ffmpeg?" OFF)
+ dependent_option(ENABLE_INTERNAL_FLATBUFFERS "Enable internal flatbuffers?")
+ dependent_option(ENABLE_INTERNAL_FMT "Enable internal fmt?")
++dependent_option(ENABLE_INTERNAL_LIBDVD "Enable internal libdvd* libs?")
+ dependent_option(ENABLE_INTERNAL_NFS "Enable internal libnfs?")
+ dependent_option(ENABLE_INTERNAL_PCRE "Enable internal pcre?")
+ dependent_option(ENABLE_INTERNAL_SPDLOG "Enable internal spdlog?")
+ dependent_option(ENABLE_INTERNAL_TAGLIB "Enable internal taglib?")
+diff --git a/cmake/modules/FindLibDvd.cmake b/cmake/modules/FindLibDvd.cmake
+index 6853e84618..91e98d3d46 100644
+--- a/cmake/modules/FindLibDvd.cmake
++++ b/cmake/modules/FindLibDvd.cmake
+@@ -7,19 +7,6 @@ unset(FPHSA_NAME_MISMATCHED)
+
+ set(_dvdlibs ${LIBDVDREAD_LIBRARY} ${LIBDVDCSS_LIBRARY})
+
+-if(NOT CORE_SYSTEM_NAME MATCHES windows)
+- # link a shared dvdnav library that includes the whole archives of dvdread and dvdcss as well
+- # the quotes around _dvdlibs are on purpose, since we want to pass a list to the function that will be unpacked automatically
+- core_link_library(${LIBDVDNAV_LIBRARY} system/players/VideoPlayer/libdvdnav libdvdnav archives "${_dvdlibs}")
+-else()
+- set(LIBDVD_TARGET_DIR .)
+- if(CORE_SYSTEM_NAME STREQUAL windowsstore)
+- set(LIBDVD_TARGET_DIR dlls)
+- endif()
+- copy_file_to_buildtree(${DEPENDS_PATH}/bin/libdvdnav.dll DIRECTORY ${LIBDVD_TARGET_DIR})
+- add_dependencies(export-files LibDvdNav::LibDvdNav)
+-endif()
+-
+ set(LIBDVD_INCLUDE_DIRS ${LIBDVDREAD_INCLUDE_DIR} ${LIBDVDNAV_INCLUDE_DIR})
+ set(LIBDVD_LIBRARIES ${LIBDVDNAV_LIBRARY} ${LIBDVDREAD_LIBRARY})
+ if(TARGET LibDvdCSS::LibDvdCSS)
+diff --git a/cmake/modules/FindLibDvdNav.cmake b/cmake/modules/FindLibDvdNav.cmake
+index 681610ea6c..0135aba11e 100644
+--- a/cmake/modules/FindLibDvdNav.cmake
++++ b/cmake/modules/FindLibDvdNav.cmake
+@@ -25,82 +25,94 @@ if(NOT TARGET LibDvdNav::LibDvdNav)
+
+ set(MODULE_LC libdvdnav)
+
+- # We require this due to the odd nature of github URL's compared to our other tarball
+- # mirror system. If User sets LIBDVDNAV_URL or libdvdnav_URL, allow get_filename_component in SETUP_BUILD_VARS
+- if(LIBDVDNAV_URL OR ${MODULE_LC}_URL)
+- if(${MODULE_LC}_URL)
+- set(LIBDVDNAV_URL ${${MODULE_LC}_URL})
++ if(ENABLE_INTERNAL_LIBDVD)
++
++ # We require this due to the odd nature of github URL's compared to our other tarball
++ # mirror system. If User sets LIBDVDNAV_URL or libdvdnav_URL, allow get_filename_component in SETUP_BUILD_VARS
++ if(LIBDVDNAV_URL OR ${MODULE_LC}_URL)
++ set(LIBDVDNAV_URL_PROVIDED TRUE)
+ endif()
+- set(LIBDVDNAV_URL_PROVIDED TRUE)
+- endif()
+
+- SETUP_BUILD_VARS()
++ SETUP_BUILD_VARS()
+
+- if(NOT LIBDVDNAV_URL_PROVIDED)
+- # override LIBDVDNAV_URL due to tar naming when retrieved from github release
+- set(LIBDVDNAV_URL ${LIBDVDNAV_BASE_URL}/archive/${LIBDVDNAV_VER}.tar.gz)
+- endif()
++ if(NOT LIBDVDNAV_URL_PROVIDED)
++ # override LIBDVDNAV_URL due to tar naming when retrieved from github release
++ set(LIBDVDNAV_URL ${LIBDVDNAV_BASE_URL}/archive/${LIBDVDNAV_VER}.tar.gz)
++ endif()
+
+- set(LIBDVDNAV_VERSION ${${MODULE}_VER})
+-
+- set(HOST_ARCH ${ARCH})
+- if(CORE_SYSTEM_NAME STREQUAL android)
+- if(ARCH STREQUAL arm)
+- set(HOST_ARCH arm-linux-androideabi)
+- elseif(ARCH STREQUAL aarch64)
+- set(HOST_ARCH aarch64-linux-android)
+- elseif(ARCH STREQUAL i486-linux)
+- set(HOST_ARCH i686-linux-android)
+- elseif(ARCH STREQUAL x86_64)
+- set(HOST_ARCH x86_64-linux-android)
++ set(LIBDVDNAV_VERSION ${${MODULE}_VER})
++
++ set(HOST_ARCH ${ARCH})
++ if(CORE_SYSTEM_NAME STREQUAL android)
++ if(ARCH STREQUAL arm)
++ set(HOST_ARCH arm-linux-androideabi)
++ elseif(ARCH STREQUAL aarch64)
++ set(HOST_ARCH aarch64-linux-android)
++ elseif(ARCH STREQUAL i486-linux)
++ set(HOST_ARCH i686-linux-android)
++ elseif(ARCH STREQUAL x86_64)
++ set(HOST_ARCH x86_64-linux-android)
++ endif()
++ elseif(CORE_SYSTEM_NAME STREQUAL windowsstore)
++ set(LIBDVD_ADDITIONAL_ARGS "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}" "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}")
+ endif()
+- elseif(CORE_SYSTEM_NAME STREQUAL windowsstore)
+- set(LIBDVD_ADDITIONAL_ARGS "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}" "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}")
+- endif()
+
+- string(APPEND LIBDVDNAV_CFLAGS "-D_XBMC")
++ string(APPEND LIBDVDNAV_CFLAGS "-D_XBMC")
+
+- if(APPLE)
+- set(LIBDVDNAV_LDFLAGS "-framework CoreFoundation")
+- string(APPEND LIBDVDNAV_CFLAGS " -D__DARWIN__")
+- if(NOT CORE_SYSTEM_NAME STREQUAL darwin_embedded)
+- string(APPEND LIBDVDNAV_LDFLAGS " -framework IOKit")
++ if(APPLE)
++ set(LIBDVDNAV_LDFLAGS "-framework CoreFoundation")
++ string(APPEND LIBDVDNAV_CFLAGS " -D__DARWIN__")
++ if(NOT CORE_SYSTEM_NAME STREQUAL darwin_embedded)
++ string(APPEND LIBDVDNAV_LDFLAGS " -framework IOKit")
++ endif()
+ endif()
+- endif()
+
+- if(CORE_SYSTEM_NAME MATCHES windows)
+- set(CMAKE_ARGS -DDUMMY_DEFINE=ON
+- ${LIBDVD_ADDITIONAL_ARGS})
+- else()
++ if(CORE_SYSTEM_NAME MATCHES windows)
++ set(CMAKE_ARGS -DDUMMY_DEFINE=ON
++ ${LIBDVD_ADDITIONAL_ARGS})
++ else()
++
++ string(APPEND LIBDVDNAV_CFLAGS " -I$<TARGET_PROPERTY:LibDvdRead::LibDvdRead,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:LibDvdRead::LibDvdRead,INTERFACE_COMPILE_DEFINITIONS>")
++
++ find_program(AUTORECONF autoreconf REQUIRED)
++ find_program(MAKE_EXECUTABLE make REQUIRED)
++
++ set(CONFIGURE_COMMAND ${AUTORECONF} -vif
++ COMMAND ac_cv_path_GIT= ./configure
++ --target=${HOST_ARCH}
++ --host=${HOST_ARCH}
++ --enable-static
++ --disable-shared
++ --with-pic
++ --prefix=${DEPENDS_PATH}
++ --libdir=${DEPENDS_PATH}/lib
++ "CC=${CMAKE_C_COMPILER}"
++ "CFLAGS=${CMAKE_C_FLAGS} ${LIBDVDNAV_CFLAGS}"
++ "LDFLAGS=${CMAKE_EXE_LINKER_FLAGS} ${LIBDVDNAV_LDFLAGS}"
++ "PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig")
++
++ set(BUILD_COMMAND ${MAKE_EXECUTABLE})
++ set(INSTALL_COMMAND ${MAKE_EXECUTABLE} install)
++ set(BUILD_IN_SOURCE 1)
++ endif()
+
+- string(APPEND LIBDVDNAV_CFLAGS " -I$<TARGET_PROPERTY:LibDvdRead::LibDvdRead,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:LibDvdRead::LibDvdRead,INTERFACE_COMPILE_DEFINITIONS>")
+-
+- find_program(AUTORECONF autoreconf REQUIRED)
+- find_program(MAKE_EXECUTABLE make REQUIRED)
+-
+- set(CONFIGURE_COMMAND ${AUTORECONF} -vif
+- COMMAND ac_cv_path_GIT= ./configure
+- --target=${HOST_ARCH}
+- --host=${HOST_ARCH}
+- --enable-static
+- --disable-shared
+- --with-pic
+- --prefix=${DEPENDS_PATH}
+- --libdir=${DEPENDS_PATH}/lib
+- "CC=${CMAKE_C_COMPILER}"
+- "CFLAGS=${CMAKE_C_FLAGS} ${LIBDVDNAV_CFLAGS}"
+- "LDFLAGS=${CMAKE_EXE_LINKER_FLAGS} ${LIBDVDNAV_LDFLAGS}"
+- "PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig")
+-
+- set(BUILD_COMMAND ${MAKE_EXECUTABLE})
+- set(INSTALL_COMMAND ${MAKE_EXECUTABLE} install)
+- set(BUILD_IN_SOURCE 1)
+- endif()
++ BUILD_DEP_TARGET()
++
++ if(TARGET LibDvdRead::LibDvdRead)
++ add_dependencies(libdvdnav LibDvdRead::LibDvdRead)
++ endif()
++ else()
++ if(PKG_CONFIG_FOUND)
++ pkg_check_modules(PC_DVDNAV dvdnav QUIET)
++ endif()
+
+- BUILD_DEP_TARGET()
++ find_path(LIBDVDNAV_INCLUDE_DIR NAMES dvdnav.h
++ PATH_SUFFIXES dvdnav
++ PATHS ${PC_DVDNAV_INCLUDEDIR})
++ find_library(LIBDVDNAV_LIBRARY NAMES dvdnav libdvdnav
++ PATHS ${PC_DVDNAV_LIBDIR})
+
+- if(TARGET LibDvdRead::LibDvdRead)
+- add_dependencies(libdvdnav LibDvdRead::LibDvdRead)
++ set(LIBDVDNAV_VERSION ${PC_DVDNAV_VERSION})
+ endif()
+ endif()
+
+diff --git a/cmake/modules/FindLibDvdRead.cmake b/cmake/modules/FindLibDvdRead.cmake
+index d7e8e882fa..0a8261e758 100644
+--- a/cmake/modules/FindLibDvdRead.cmake
++++ b/cmake/modules/FindLibDvdRead.cmake
+@@ -27,86 +27,98 @@ if(NOT TARGET LibDvdRead::LibDvdRead)
+
+ set(MODULE_LC libdvdread)
+
+- # We require this due to the odd nature of github URL's compared to our other tarball
+- # mirror system. If User sets LIBDVDREAD_URL or libdvdread_URL, allow get_filename_component in SETUP_BUILD_VARS
+- if(LIBDVDREAD_URL OR ${MODULE_LC}_URL)
+- if(${MODULE_LC}_URL)
+- set(LIBDVDREAD_URL ${${MODULE_LC}_URL})
++ if(ENABLE_INTERNAL_LIBDVD)
++
++ # We require this due to the odd nature of github URL's compared to our other tarball
++ # mirror system. If User sets LIBDVDREAD_URL or libdvdread_URL, allow get_filename_component in SETUP_BUILD_VARS
++ if(LIBDVDREAD_URL OR ${MODULE_LC}_URL)
++ set(LIBDVDREAD_URL_PROVIDED TRUE)
+ endif()
+- set(LIBDVDREAD_URL_PROVIDED TRUE)
+- endif()
+
+- SETUP_BUILD_VARS()
++ SETUP_BUILD_VARS()
+
+- if(NOT LIBDVDREAD_URL_PROVIDED)
+- # override LIBDVDREAD_URL due to tar naming when retrieved from github release
+- set(LIBDVDREAD_URL ${LIBDVDREAD_BASE_URL}/archive/${LIBDVDREAD_VER}.tar.gz)
+- endif()
++ if(NOT LIBDVDREAD_URL_PROVIDED)
++ # override LIBDVDREAD_URL due to tar naming when retrieved from github release
++ set(LIBDVDREAD_URL ${LIBDVDREAD_BASE_URL}/archive/${LIBDVDREAD_VER}.tar.gz)
++ endif()
+
+- set(LIBDVDREAD_VERSION ${${MODULE}_VER})
+-
+- set(HOST_ARCH ${ARCH})
+- if(CORE_SYSTEM_NAME STREQUAL android)
+- if(ARCH STREQUAL arm)
+- set(HOST_ARCH arm-linux-androideabi)
+- elseif(ARCH STREQUAL aarch64)
+- set(HOST_ARCH aarch64-linux-android)
+- elseif(ARCH STREQUAL i486-linux)
+- set(HOST_ARCH i686-linux-android)
+- elseif(ARCH STREQUAL x86_64)
+- set(HOST_ARCH x86_64-linux-android)
++ set(LIBDVDREAD_VERSION ${${MODULE}_VER})
++
++ set(HOST_ARCH ${ARCH})
++ if(CORE_SYSTEM_NAME STREQUAL android)
++ if(ARCH STREQUAL arm)
++ set(HOST_ARCH arm-linux-androideabi)
++ elseif(ARCH STREQUAL aarch64)
++ set(HOST_ARCH aarch64-linux-android)
++ elseif(ARCH STREQUAL i486-linux)
++ set(HOST_ARCH i686-linux-android)
++ elseif(ARCH STREQUAL x86_64)
++ set(HOST_ARCH x86_64-linux-android)
++ endif()
++ elseif(CORE_SYSTEM_NAME STREQUAL windowsstore)
++ set(LIBDVD_ADDITIONAL_ARGS "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}" "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}")
+ endif()
+- elseif(CORE_SYSTEM_NAME STREQUAL windowsstore)
+- set(LIBDVD_ADDITIONAL_ARGS "-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}" "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}")
+- endif()
+
+- string(APPEND LIBDVDREAD_CFLAGS "-D_XBMC")
++ string(APPEND LIBDVDREAD_CFLAGS "-D_XBMC")
+
+- if(APPLE)
+- set(LIBDVDREAD_LDFLAGS "-framework CoreFoundation")
+- string(APPEND LIBDVDREAD_CFLAGS " -D__DARWIN__")
+- if(NOT CORE_SYSTEM_NAME STREQUAL darwin_embedded)
+- string(APPEND LIBDVDREAD_LDFLAGS " -framework IOKit")
++ if(APPLE)
++ set(LIBDVDREAD_LDFLAGS "-framework CoreFoundation")
++ string(APPEND LIBDVDREAD_CFLAGS " -D__DARWIN__")
++ if(NOT CORE_SYSTEM_NAME STREQUAL darwin_embedded)
++ string(APPEND LIBDVDREAD_LDFLAGS " -framework IOKit")
++ endif()
+ endif()
+- endif()
+
+- if(CORE_SYSTEM_NAME MATCHES windows)
+- set(CMAKE_ARGS -DDUMMY_DEFINE=ON
+- ${LIBDVD_ADDITIONAL_ARGS})
+- else()
++ if(CORE_SYSTEM_NAME MATCHES windows)
++ set(CMAKE_ARGS -DDUMMY_DEFINE=ON
++ ${LIBDVD_ADDITIONAL_ARGS})
++ else()
++
++ if(TARGET LibDvdCSS::LibDvdCSS)
++ string(APPEND LIBDVDREAD_CFLAGS " -I$<TARGET_PROPERTY:LibDvdCSS::LibDvdCSS,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:LibDvdCSS::LibDvdCSS,INTERFACE_COMPILE_DEFINITIONS>")
++ string(APPEND with-css "--with-libdvdcss")
++ endif()
++
++ find_program(AUTORECONF autoreconf REQUIRED)
++ find_program(MAKE_EXECUTABLE make REQUIRED)
++
++ set(CONFIGURE_COMMAND ${AUTORECONF} -vif
++ COMMAND ac_cv_path_GIT= ./configure
++ --target=${HOST_ARCH}
++ --host=${HOST_ARCH}
++ --enable-static
++ --disable-shared
++ --with-pic
++ --prefix=${DEPENDS_PATH}
++ --libdir=${DEPENDS_PATH}/lib
++ ${with-css}
++ "CC=${CMAKE_C_COMPILER}"
++ "CFLAGS=${CMAKE_C_FLAGS} ${LIBDVDREAD_CFLAGS}"
++ "LDFLAGS=${CMAKE_EXE_LINKER_FLAGS} ${LIBDVDREAD_LDFLAGS}"
++ "PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig")
++
++ set(BUILD_COMMAND ${MAKE_EXECUTABLE})
++ set(INSTALL_COMMAND ${MAKE_EXECUTABLE} install)
++ set(BUILD_IN_SOURCE 1)
++ endif()
++
++ BUILD_DEP_TARGET()
+
+ if(TARGET LibDvdCSS::LibDvdCSS)
+- string(APPEND LIBDVDREAD_CFLAGS " -I$<TARGET_PROPERTY:LibDvdCSS::LibDvdCSS,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:LibDvdCSS::LibDvdCSS,INTERFACE_COMPILE_DEFINITIONS>")
+- string(APPEND with-css "--with-libdvdcss")
++ add_dependencies(libdvdread LibDvdCSS::LibDvdCSS)
++ endif()
++ else()
++ if(PKG_CONFIG_FOUND)
++ pkg_check_modules(PC_DVDREAD dvdread QUIET)
+ endif()
+
+- find_program(AUTORECONF autoreconf REQUIRED)
+- find_program(MAKE_EXECUTABLE make REQUIRED)
+-
+- set(CONFIGURE_COMMAND ${AUTORECONF} -vif
+- COMMAND ac_cv_path_GIT= ./configure
+- --target=${HOST_ARCH}
+- --host=${HOST_ARCH}
+- --enable-static
+- --disable-shared
+- --with-pic
+- --prefix=${DEPENDS_PATH}
+- --libdir=${DEPENDS_PATH}/lib
+- ${with-css}
+- "CC=${CMAKE_C_COMPILER}"
+- "CFLAGS=${CMAKE_C_FLAGS} ${LIBDVDREAD_CFLAGS}"
+- "LDFLAGS=${CMAKE_EXE_LINKER_FLAGS} ${LIBDVDREAD_LDFLAGS}"
+- "PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig")
+-
+- set(BUILD_COMMAND ${MAKE_EXECUTABLE})
+- set(INSTALL_COMMAND ${MAKE_EXECUTABLE} install)
+- set(BUILD_IN_SOURCE 1)
+- endif()
+-
+- BUILD_DEP_TARGET()
++ find_path(LIBDVDREAD_INCLUDE_DIR NAMES dvd_reader.h
++ PATH_SUFFIXES dvdread
++ PATHS ${PC_DVDREAD_INCLUDEDIR})
++ find_library(LIBDVDREAD_LIBRARY NAMES dvdread libdvdread
++ PATHS ${PC_DVDREAD_LIBDIR})
+
+- if(TARGET LibDvdCSS::LibDvdCSS)
+- add_dependencies(libdvdread LibDvdCSS::LibDvdCSS)
++ set(LIBDVDREAD_VERSION ${PC_DVDREAD_VERSION})
+ endif()
+ endif()
+
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index 859762348b..069bfccd7b 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -27,6 +27,8 @@
+ #include "platform/Environment.h"
+ #endif
+
++#include <sys/uio.h>
++
+ namespace
+ {
+ constexpr int HOLDMODE_NONE = 0;
+@@ -1414,7 +1416,7 @@ std::string CDVDInputStreamNavigator::GetDVDVolIdString()
+ if (!m_dvdnav)
+ return "";
+
+- const char* volIdTmp = m_dll.dvdnav_get_volid_string(m_dvdnav);
++ const char* volIdTmp = dvdnav_get_volid_string(m_dvdnav);
+ if (volIdTmp)
+ {
+ std::string volId{volIdTmp};
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h
+index 319c84b47d..99b01df2ab 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h
+@@ -17,9 +17,15 @@
+ #include "cores/MenuType.h"
+ #include "utils/Geometry.h"
+
++#ifdef __cplusplus
++extern "C"
++{
++#endif
+ #include <dvdnav/dvdnav.h>
+ #include <dvdnav/dvd_types.h>
+-
++#ifdef __cplusplus
++}
++#endif
+ #include <string>
+
+ #define DVD_VIDEO_BLOCKSIZE DVD_VIDEO_LB_LEN // 2048 bytes
+--
+2.35.1
+
+
+From 9625e95c0ac5b17125d875178461e54ba7ae5057 Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Tue, 26 Jul 2022 23:43:21 +0100
+Subject: [PATCH 3/9] Add dvdcallbacks for vfs files
+
+---
+ .../DVDInputStreamNavigator.cpp | 43 ++---
+ xbmc/filesystem/CMakeLists.txt | 2 +
+ xbmc/filesystem/DvdCallback.cpp | 150 ++++++++++++++++++
+ xbmc/filesystem/DvdCallback.h | 47 ++++++
+ 4 files changed, 216 insertions(+), 26 deletions(-)
+ create mode 100644 xbmc/filesystem/DvdCallback.cpp
+ create mode 100644 xbmc/filesystem/DvdCallback.h
+
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index 069bfccd7b..8280a5a1b9 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -8,6 +8,7 @@
+
+ #include "DVDInputStreamNavigator.h"
+ #include "filesystem/IFileTypes.h"
++#include "filesystem/DvdCallback.h"
+ #include "utils/LangCodeExpander.h"
+ #include "../DVDDemuxSPU.h"
+ #include "settings/Settings.h"
+@@ -67,7 +68,8 @@ constexpr int DVD_AUDIO_LANG_EXT_DIRECTORSCOMMENTS2 = 4;
+ static int dvd_inputstreamnavigator_cb_seek(void * p_stream, uint64_t i_pos);
+ static int dvd_inputstreamnavigator_cb_read(void * p_stream, void * buffer, int i_read);
+ static int dvd_inputstreamnavigator_cb_readv(void * p_stream, void * p_iovec, int i_blocks);
+-static void dvd_logger(void* priv, dvdnav_logger_level_t level, const char* fmt, va_list va);
++
++static dvdnav_filesystem kodiDvdFilesystem;
+
+ CDVDInputStreamNavigator::CDVDInputStreamNavigator(IVideoPlayer* player, const CFileItem& fileitem)
+ : CDVDInputStream(DVDSTREAM_TYPE_DVD, fileitem), m_pstream(nullptr)
+@@ -136,7 +138,7 @@ bool CDVDInputStreamNavigator::Open()
+
+ #if DVDNAV_VERSION >= 60100
+ dvdnav_logger_cb loggerCallback;
+- loggerCallback.pf_log = dvd_logger;
++ loggerCallback.pf_log = CDVDCallback::dvd_logger;
+ #endif
+
+ // open up the DVD device
+@@ -157,6 +159,19 @@ bool CDVDInputStreamNavigator::Open()
+ }
+ }
+ #if DVDNAV_VERSION >= 60100
++ else if (URIUtils::IsNetworkFilesystem(path))
++ {
++ kodiDvdFilesystem.dir_open = CDVDCallback::dir_open;
++ kodiDvdFilesystem.file_open = CDVDCallback::file_open;
++ kodiDvdFilesystem.stat = CDVDCallback::stat;
++ kodiDvdFilesystem.close = CDVDCallback::close;
++ if (dvdnav_open_vfs_files(&m_dvdnav, nullptr, &loggerCallback, path.c_str(), &kodiDvdFilesystem) != DVDNAV_STATUS_OK)
++ {
++ CLog::Log(LOGERROR, "Error on dvdnav_open_vfs_files");
++ Close();
++ return false;
++ }
++ }
+ else if (dvdnav_open2(&m_dvdnav, nullptr, &loggerCallback, path.c_str()) !=
+ DVDNAV_STATUS_OK)
+ #else
+@@ -1511,30 +1526,6 @@ int dvd_inputstreamnavigator_cb_read(void * p_stream, void * buffer, int i_read)
+ return i_ret;
+ }
+
+-void dvd_logger(void* priv, dvdnav_logger_level_t level, const char* fmt, va_list va)
+-{
+- const std::string message = StringUtils::FormatV(fmt, va);
+- auto logLevel = LOGDEBUG;
+- switch (level)
+- {
+- case DVDNAV_LOGGER_LEVEL_INFO:
+- logLevel = LOGINFO;
+- break;
+- case DVDNAV_LOGGER_LEVEL_ERROR:
+- logLevel = LOGERROR;
+- break;
+- case DVDNAV_LOGGER_LEVEL_WARN:
+- logLevel = LOGWARNING;
+- break;
+- case DVDNAV_LOGGER_LEVEL_DEBUG:
+- logLevel = LOGDEBUG;
+- break;
+- default:
+- break;
+- };
+- CLog::Log(logLevel, "Libdvd: {}", message);
+-}
+-
+ int dvd_inputstreamnavigator_cb_readv(void * p_stream, void * p_iovec, int i_blocks)
+ {
+ // NOTE/TODO: this vectored read callback somehow doesn't seem to be called by libdvdnav.
+diff --git a/xbmc/filesystem/CMakeLists.txt b/xbmc/filesystem/CMakeLists.txt
+index 8bbaa44201..449736a392 100644
+--- a/xbmc/filesystem/CMakeLists.txt
++++ b/xbmc/filesystem/CMakeLists.txt
+@@ -11,6 +11,7 @@ set(SOURCES AddonsDirectory.cpp
+ DirectoryFactory.cpp
+ DirectoryHistory.cpp
+ DllLibCurl.cpp
++ DvdCallback.cpp
+ EventsDirectory.cpp
+ FavouritesDirectory.cpp
+ FileCache.cpp
+@@ -73,6 +74,7 @@ set(HEADERS AddonsDirectory.h
+ DirectoryFactory.h
+ DirectoryHistory.h
+ DllLibCurl.h
++ DvdCallback.h
+ EventsDirectory.h
+ FTPDirectory.h
+ FTPParse.h
+diff --git a/xbmc/filesystem/DvdCallback.cpp b/xbmc/filesystem/DvdCallback.cpp
+new file mode 100644
+index 0000000000..2f247fff1f
+--- /dev/null
++++ b/xbmc/filesystem/DvdCallback.cpp
+@@ -0,0 +1,150 @@
++/*
++ * Copyright (C) 2005-2018 Team Kodi
++ * This file is part of Kodi - https://kodi.tv
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ * See LICENSES/README.md for more information.
++ */
++
++#include "DvdCallback.h"
++
++#include "FileItem.h"
++#include "filesystem/Directory.h"
++#include "filesystem/File.h"
++#include "utils/StringUtils.h"
++#include "utils/URIUtils.h"
++#include "utils/log.h"
++
++using namespace XFILE;
++
++struct SDirState
++{
++ CFileItemList list;
++ int curr = 0;
++};
++
++void CDVDCallback::dvd_logger(void* priv, dvdnav_logger_level_t level, const char* fmt, va_list va)
++{
++ const std::string message = StringUtils::FormatV(fmt, va);
++ auto logLevel = LOGDEBUG;
++ switch (level)
++ {
++ case DVDNAV_LOGGER_LEVEL_INFO:
++ logLevel = LOGINFO;
++ break;
++ case DVDNAV_LOGGER_LEVEL_ERROR:
++ logLevel = LOGERROR;
++ break;
++ case DVDNAV_LOGGER_LEVEL_WARN:
++ logLevel = LOGWARNING;
++ break;
++ case DVDNAV_LOGGER_LEVEL_DEBUG:
++ logLevel = LOGDEBUG;
++ break;
++ default:
++ break;
++ };
++ CLog::Log(logLevel, "Libdvd: {}", message);
++}
++
++void CDVDCallback::dir_close(dvd_dir_h *dir)
++{
++ if (dir)
++ {
++ CLog::Log(LOGDEBUG, "CDVDCallback - Closed dir ({})", fmt::ptr(dir));
++ delete static_cast<SDirState*>(dir->internal);
++ delete dir;
++ }
++}
++
++dvd_dir_h* CDVDCallback::dir_open(const char* strDirname)
++{
++ CLog::Log(LOGDEBUG, "CDVDCallback - Opening dir {}", CURL::GetRedacted(strDirname));
++
++ SDirState *st = new SDirState();
++ if (!CDirectory::GetDirectory(strDirname, st->list, "", DIR_FLAG_DEFAULTS))
++ {
++ if (!CFile::Exists(strDirname))
++ CLog::Log(LOGDEBUG, "CDVDCallback - Error opening dir! ({})",
++ CURL::GetRedacted(strDirname));
++ delete st;
++ return nullptr;
++ }
++
++ dvd_dir_h* dir = new dvd_dir_h;
++ dir->close = dir_close;
++ dir->read = dir_read;
++ dir->internal = (void*)st;
++
++ return dir;
++}
++
++int CDVDCallback::dir_read(dvd_dir_h *dir, dvd_dirent_t *entry)
++{
++ SDirState* state = static_cast<SDirState*>(dir->internal);
++
++ if (state->curr >= state->list.Size())
++ return 1;
++
++ strncpy(entry->d_name, state->list[state->curr]->GetLabel().c_str(), sizeof(entry->d_name));
++ entry->d_name[sizeof(entry->d_name) - 1] = 0;
++ state->curr++;
++
++ return 0;
++}
++
++int64_t CDVDCallback::file_close(dvd_file_h *file)
++{
++ if (file)
++ {
++ delete static_cast<CFile*>(file->internal);
++ delete file;
++ }
++ return 0;
++}
++
++dvd_file_h * CDVDCallback::file_open(const char *filename, const char *cmode)
++{
++ dvd_file_h* file = new dvd_file_h;
++
++ file->close = file_close;
++ file->seek = file_seek;
++ file->read = file_read;
++
++ CFile* fp = new CFile();
++ if (fp->Open(filename))
++ {
++ file->internal = (void*)fp;
++ return file;
++ }
++
++ CLog::Log(LOGDEBUG, "CDVDCallback - Error opening file! ({})", CURL::GetRedacted(filename));
++
++ delete fp;
++ delete file;
++
++ return nullptr;
++}
++
++int64_t CDVDCallback::file_seek(dvd_file_h *file, int64_t offset, int32_t origin)
++{
++ return static_cast<CFile*>(file->internal)->Seek(offset, origin);
++}
++
++int64_t CDVDCallback::file_read(dvd_file_h *file, char *buf, int64_t size)
++{
++ return static_cast<int64_t>(static_cast<CFile*>(file->internal)->Read(buf, static_cast<size_t>(size)));
++}
++
++
++ int CDVDCallback::stat(const char *path, dvdstat_t* statbuff)
++ {
++ struct __stat64 tStat;
++ int result = CFile::Stat(path, &tStat);
++ statbuff->size = tStat.st_size;
++ statbuff->is_blk = S_ISBLK(tStat.st_mode);
++ statbuff->is_chr = S_ISCHR(tStat.st_mode);
++ statbuff->is_dir = S_ISDIR(tStat.st_mode);
++ statbuff->is_reg = S_ISREG(tStat.st_mode);
++ return result;
++ }
+diff --git a/xbmc/filesystem/DvdCallback.h b/xbmc/filesystem/DvdCallback.h
+new file mode 100644
+index 0000000000..673108ec66
+--- /dev/null
++++ b/xbmc/filesystem/DvdCallback.h
+@@ -0,0 +1,47 @@
++/*
++ * Copyright (C) 2005-2018 Team Kodi
++ * This file is part of Kodi - https://kodi.tv
++ *
++ * SPDX-License-Identifier: GPL-2.0-or-later
++ * See LICENSES/README.md for more information.
++ */
++
++#pragma once
++
++#ifdef __cplusplus
++extern "C"
++{
++#endif
++#include <dvdread/dvd_filesystem.h>
++#include <dvdnav/dvdnav.h>
++#ifdef __cplusplus
++}
++#endif
++
++class CDVDCallback
++{
++public:
++ // logger implementation
++ static void dvd_logger(void* priv, dvdnav_logger_level_t level, const char* fmt, va_list va);
++
++ // filesystem close
++ static void close(void *) {};
++
++ // dir
++ static void dir_close(dvd_dir_h* dir);
++ static dvd_dir_h* dir_open(const char* strDirname);
++ static int dir_read(dvd_dir_h* dir, dvd_dirent_t* entry);
++
++ // file
++ static dvd_file_h* file_open(const char* filename, const char *cmode);
++ static int64_t file_close(dvd_file_h* file);
++ static int64_t file_read(dvd_file_h* file, char* buf, int64_t size);
++ static int64_t file_seek(dvd_file_h* file, int64_t offset, int32_t origin);
++
++ // stat
++ static int stat(const char *path, dvdstat_t* statbuf);
++
++private:
++ CDVDCallback() = default;
++ ~CDVDCallback() = default;
++};
+\ No newline at end of file
+--
+2.35.1
+
+
+From 8dd8d11a55a7060bd7352623da92d102e7861bc3 Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Fri, 29 Jul 2022 14:05:57 +0100
+Subject: [PATCH 4/9] Updates
+
+---
+ xbmc/DllPaths.h | 2 +-
+ .../VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/xbmc/DllPaths.h b/xbmc/DllPaths.h
+index 33fb46635e..9f8979355b 100644
+--- a/xbmc/DllPaths.h
++++ b/xbmc/DllPaths.h
+@@ -10,7 +10,7 @@
+
+ #if defined (TARGET_ANDROID)
+ #include "DllPaths_generated_android.h"
+-#else
++#elif !defined (TARGET_WINDOWS)
+ #include "DllPaths_generated.h"
+ #endif
+
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index 8280a5a1b9..9221048789 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -165,9 +165,9 @@ bool CDVDInputStreamNavigator::Open()
+ kodiDvdFilesystem.file_open = CDVDCallback::file_open;
+ kodiDvdFilesystem.stat = CDVDCallback::stat;
+ kodiDvdFilesystem.close = CDVDCallback::close;
+- if (dvdnav_open_vfs_files(&m_dvdnav, nullptr, &loggerCallback, path.c_str(), &kodiDvdFilesystem) != DVDNAV_STATUS_OK)
++ if (dvdnav_open_files(&m_dvdnav, nullptr, &loggerCallback, path.c_str(), &kodiDvdFilesystem) != DVDNAV_STATUS_OK)
+ {
+- CLog::Log(LOGERROR, "Error on dvdnav_open_vfs_files");
++ CLog::Log(LOGERROR, "Error on dvdnav_open_files");
+ Close();
+ return false;
+ }
+--
+2.35.1
+
+
+From 8cca2455a993b822270610cc87b63f60d2228079 Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Fri, 29 Jul 2022 14:41:39 +0100
+Subject: [PATCH 5/9] update dvdnav version
+
+---
+ tools/depends/target/libdvdnav/LIBDVDNAV-VERSION | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION b/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION
+index 3d6d1ec699..f5541697ac 100644
+--- a/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION
++++ b/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION
+@@ -1,7 +1,7 @@
+ LIBNAME=libdvdnav
+-BASE_URL=https://github.com/xbmc/libdvdnav
+-VERSION=6.1.1-Next-Nexus-Alpha2-2
++BASE_URL=https://github.com/enen92/libdvdnav
++VERSION=upstream_submission_kodi
+ ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+-SHA512=51e6fc033121241354a5f0b3fc9a430577ae3ff6bb7f31445aa548ef4893037fb80eea3b2c6774c81e9ebaf9c45e9b490c98c2c65eb38f9f7daba84b236f7e1d
++SHA512=fca8c19a6787bb7a88a6a5e35f6a524505b607861b3bb391e3eca1e91b67d05b12417153542b161da0f13c4f5152f1d4860a34a6d230155c9c8c767fb35725b2
+ BYPRODUCT=libdvdnav.a
+ BYPRODUCT_WIN=libdvdnav.lib
+--
+2.35.1
+
+
+From be49396a22eb405d63a65bc56d3d1a24c6dcefab Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Fri, 29 Jul 2022 15:13:55 +0100
+Subject: [PATCH 6/9] update versions
+
+---
+ tools/depends/target/libdvdread/LIBDVDREAD-VERSION | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/tools/depends/target/libdvdread/LIBDVDREAD-VERSION b/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
+index d51b629bd2..74428acf49 100644
+--- a/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
++++ b/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
+@@ -1,7 +1,7 @@
+ LIBNAME=libdvdread
+-BASE_URL=https://github.com/xbmc/libdvdread
+-VERSION=6.1.3-Next-Nexus-Alpha2-2
++BASE_URL=https://github.com/enen92/libdvdread
++VERSION=upstream_submission_kodi
+ ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+-SHA512=629a41157d07b8ec0ea1fe89ae5ec48f63047472a862782b805c531ae31a0376fc4dc15175f8280c3ef91d7fa977bacebb1b51232640034a34bab2293210fc5e
++SHA512=d2fb890ccca8b8e7834470922f0b010f70b4016e829d71f51d3aaf30e2517ab7d98b46448313dd9c63141a71e794a8e679db4c1e0905e887c4897f7a5c194dc0
+ BYPRODUCT=libdvdread.a
+ BYPRODUCT_WIN=dvdread.lib
+--
+2.35.1
+
+
+From 4cf9e76978906e92ffb66441701d19f6e40c96fa Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Fri, 29 Jul 2022 15:39:43 +0100
+Subject: [PATCH 7/9] fixup
+
+---
+ tools/depends/target/libdvdread/LIBDVDREAD-VERSION | 4 ++--
+ .../VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/tools/depends/target/libdvdread/LIBDVDREAD-VERSION b/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
+index 74428acf49..38b7a10d8b 100644
+--- a/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
++++ b/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
+@@ -1,7 +1,7 @@
+ LIBNAME=libdvdread
+ BASE_URL=https://github.com/enen92/libdvdread
+-VERSION=upstream_submission_kodi
++VERSION=upstream_skodi7
+ ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+-SHA512=d2fb890ccca8b8e7834470922f0b010f70b4016e829d71f51d3aaf30e2517ab7d98b46448313dd9c63141a71e794a8e679db4c1e0905e887c4897f7a5c194dc0
++SHA512=932e83a9aeb949265538d935aab4689b9e8d8fe0611faad8ab066c97a418ad2dd907e65655b299950b296d7ff51394b89f51d41609cbfabb7db3d9201711e370
+ BYPRODUCT=libdvdread.a
+ BYPRODUCT_WIN=dvdread.lib
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index 9221048789..db5679d9bf 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -28,7 +28,7 @@
+ #include "platform/Environment.h"
+ #endif
+
+-#include <sys/uio.h>
++//#include <sys/uio.h>
+
+ namespace
+ {
+--
+2.35.1
+
+
+From 423f4633ba64465004f5ad9894c934ba8b4725bf Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Fri, 29 Jul 2022 21:16:11 +0100
+Subject: [PATCH 8/9] update libs for win
+
+---
+ .../depends/target/libdvdnav/LIBDVDNAV-VERSION | 6 +++---
+ .../target/libdvdread/LIBDVDREAD-VERSION | 4 ++--
+ .../DVDInputStreams/DVDInputStreamNavigator.cpp | 17 ++++++++++++++++-
+ xbmc/filesystem/DvdCallback.cpp | 6 ++++++
+ 4 files changed, 27 insertions(+), 6 deletions(-)
+
+diff --git a/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION b/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION
+index f5541697ac..2f6110e0b6 100644
+--- a/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION
++++ b/tools/depends/target/libdvdnav/LIBDVDNAV-VERSION
+@@ -1,7 +1,7 @@
+ LIBNAME=libdvdnav
+ BASE_URL=https://github.com/enen92/libdvdnav
+-VERSION=upstream_submission_kodi
++VERSION=upstream_submission_kodi2
+ ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+-SHA512=fca8c19a6787bb7a88a6a5e35f6a524505b607861b3bb391e3eca1e91b67d05b12417153542b161da0f13c4f5152f1d4860a34a6d230155c9c8c767fb35725b2
++SHA512=36bfedfd5628014164d757588e9165a7c4eb9f0dcd0c3fc393c7d6e1457d33f64f88a3a488c90d2862bfe65f99de274baa81c8e446ca9ad74d76d13166cebe24
+ BYPRODUCT=libdvdnav.a
+-BYPRODUCT_WIN=libdvdnav.lib
++BYPRODUCT_WIN=libdvdnav.lib
+\ No newline at end of file
+diff --git a/tools/depends/target/libdvdread/LIBDVDREAD-VERSION b/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
+index 38b7a10d8b..001f7572f6 100644
+--- a/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
++++ b/tools/depends/target/libdvdread/LIBDVDREAD-VERSION
+@@ -1,7 +1,7 @@
+ LIBNAME=libdvdread
+ BASE_URL=https://github.com/enen92/libdvdread
+-VERSION=upstream_skodi7
++VERSION=upstream_skodi8
+ ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz
+-SHA512=932e83a9aeb949265538d935aab4689b9e8d8fe0611faad8ab066c97a418ad2dd907e65655b299950b296d7ff51394b89f51d41609cbfabb7db3d9201711e370
++SHA512=7946172ce3eeba869017038eaa215d915dff96550bc9b20d400fbe218ffe2622afb8de47df804ab165763330e4c818092c8d687ebc45422969062e0367c9fb6f
+ BYPRODUCT=libdvdread.a
+ BYPRODUCT_WIN=dvdread.lib
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index db5679d9bf..be369d95dc 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -28,7 +28,22 @@
+ #include "platform/Environment.h"
+ #endif
+
+-//#include <sys/uio.h>
++//FIXME
++#ifdef TARGET_WINDOWS
++#include "platform/win32/dirent.h"
++#else
++#include <dirent.h>
++#endif
++
++#ifdef TARGET_WINDOWS
++struct iovec
++{
++ void* iov_base; /* Pointer to data. */
++ size_t iov_len; /* Length of data. */
++};
++#else
++#include <sys/uio.h> /* struct iovec */
++#endif
+
+ namespace
+ {
+diff --git a/xbmc/filesystem/DvdCallback.cpp b/xbmc/filesystem/DvdCallback.cpp
+index 2f247fff1f..a3bb72e808 100644
+--- a/xbmc/filesystem/DvdCallback.cpp
++++ b/xbmc/filesystem/DvdCallback.cpp
+@@ -17,6 +17,12 @@
+
+ using namespace XFILE;
+
++#ifdef TARGET_WINDOWS
++#include "platform/win32/dirent.h"
++#else
++#include <dirent.h>
++#endif
++
+ struct SDirState
+ {
+ CFileItemList list;
+--
+2.35.1
+
+
+From 070cccd1e3900dabbc21248055c31329ec1bc4e5 Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Tue, 9 Aug 2022 11:03:01 +0100
+Subject: [PATCH 9/9] Backup new version
+
+---
+ .../DVDInputStreams/DVDInputStreamNavigator.cpp | 2 +-
+ xbmc/filesystem/DvdCallback.cpp | 17 +++++++----------
+ xbmc/filesystem/DvdCallback.h | 12 ++++++------
+ 3 files changed, 14 insertions(+), 17 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+index be369d95dc..253c95cd1c 100644
+--- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
++++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+@@ -84,7 +84,7 @@ static int dvd_inputstreamnavigator_cb_seek(void * p_stream, uint64_t i_pos);
+ static int dvd_inputstreamnavigator_cb_read(void * p_stream, void * buffer, int i_read);
+ static int dvd_inputstreamnavigator_cb_readv(void * p_stream, void * p_iovec, int i_blocks);
+
+-static dvdnav_filesystem kodiDvdFilesystem;
++static dvdnav_filesystem_h kodiDvdFilesystem;
+
+ CDVDInputStreamNavigator::CDVDInputStreamNavigator(IVideoPlayer* player, const CFileItem& fileitem)
+ : CDVDInputStream(DVDSTREAM_TYPE_DVD, fileitem), m_pstream(nullptr)
+diff --git a/xbmc/filesystem/DvdCallback.cpp b/xbmc/filesystem/DvdCallback.cpp
+index a3bb72e808..16b359b27e 100644
+--- a/xbmc/filesystem/DvdCallback.cpp
++++ b/xbmc/filesystem/DvdCallback.cpp
+@@ -63,7 +63,7 @@ void CDVDCallback::dir_close(dvd_dir_h *dir)
+ }
+ }
+
+-dvd_dir_h* CDVDCallback::dir_open(const char* strDirname)
++dvd_dir_h* CDVDCallback::dir_open(dvdnav_filesystem_h *fs, const char* strDirname)
+ {
+ CLog::Log(LOGDEBUG, "CDVDCallback - Opening dir {}", CURL::GetRedacted(strDirname));
+
+@@ -99,7 +99,7 @@ int CDVDCallback::dir_read(dvd_dir_h *dir, dvd_dirent_t *entry)
+ return 0;
+ }
+
+-int64_t CDVDCallback::file_close(dvd_file_h *file)
++int CDVDCallback::file_close(dvd_file_h *file)
+ {
+ if (file)
+ {
+@@ -109,7 +109,7 @@ int64_t CDVDCallback::file_close(dvd_file_h *file)
+ return 0;
+ }
+
+-dvd_file_h * CDVDCallback::file_open(const char *filename, const char *cmode)
++dvd_file_h * CDVDCallback::file_open(dvdnav_filesystem_h *fs, const char *filename)
+ {
+ dvd_file_h* file = new dvd_file_h;
+
+@@ -137,20 +137,17 @@ int64_t CDVDCallback::file_seek(dvd_file_h *file, int64_t offset, int32_t origin
+ return static_cast<CFile*>(file->internal)->Seek(offset, origin);
+ }
+
+-int64_t CDVDCallback::file_read(dvd_file_h *file, char *buf, int64_t size)
++ssize_t CDVDCallback::file_read(dvd_file_h *file, char *buf, size_t size)
+ {
+- return static_cast<int64_t>(static_cast<CFile*>(file->internal)->Read(buf, static_cast<size_t>(size)));
++ return static_cast<ssize_t>(static_cast<CFile*>(file->internal)->Read(buf, size));
+ }
+
+
+- int CDVDCallback::stat(const char *path, dvdstat_t* statbuff)
++ int CDVDCallback::stat(dvdnav_filesystem_h *fs, const char *path, dvdstat_t* statbuff)
+ {
+ struct __stat64 tStat;
+ int result = CFile::Stat(path, &tStat);
+ statbuff->size = tStat.st_size;
+- statbuff->is_blk = S_ISBLK(tStat.st_mode);
+- statbuff->is_chr = S_ISCHR(tStat.st_mode);
+- statbuff->is_dir = S_ISDIR(tStat.st_mode);
+- statbuff->is_reg = S_ISREG(tStat.st_mode);
++ statbuff->st_mode = tStat.st_mode;
+ return result;
+ }
+diff --git a/xbmc/filesystem/DvdCallback.h b/xbmc/filesystem/DvdCallback.h
+index 673108ec66..8451372422 100644
+--- a/xbmc/filesystem/DvdCallback.h
++++ b/xbmc/filesystem/DvdCallback.h
+@@ -25,21 +25,21 @@ public:
+ static void dvd_logger(void* priv, dvdnav_logger_level_t level, const char* fmt, va_list va);
+
+ // filesystem close
+- static void close(void *) {};
++ static void close(dvdnav_filesystem_h *fs) {};
+
+ // dir
+ static void dir_close(dvd_dir_h* dir);
+- static dvd_dir_h* dir_open(const char* strDirname);
++ static dvd_dir_h* dir_open(dvdnav_filesystem_h *fs, const char* strDirname);
+ static int dir_read(dvd_dir_h* dir, dvd_dirent_t* entry);
+
+ // file
+- static dvd_file_h* file_open(const char* filename, const char *cmode);
+- static int64_t file_close(dvd_file_h* file);
+- static int64_t file_read(dvd_file_h* file, char* buf, int64_t size);
++ static dvd_file_h* file_open(dvdnav_filesystem_h *fs, const char* filename);
++ static int file_close(dvd_file_h* file);
++ static ssize_t file_read(dvd_file_h* file, char* buf, size_t size);
+ static int64_t file_seek(dvd_file_h* file, int64_t offset, int32_t origin);
+
+ // stat
+- static int stat(const char *path, dvdstat_t* statbuf);
++ static int stat(dvdnav_filesystem_h *fs, const char *path, dvdstat_t* statbuf);
+
+ private:
+ CDVDCallback() = default;
+--
+2.35.1
+
diff --git a/debian/patches/workarounds/0004-ffmpeg6.patch b/debian/patches/workarounds/0004-ffmpeg6.patch
new file mode 100644
index 0000000..d0c4563
--- /dev/null
+++ b/debian/patches/workarounds/0004-ffmpeg6.patch
@@ -0,0 +1,662 @@
+From c9e25dc15acf1214b079da7021ad89acf85fa77d Mon Sep 17 00:00:00 2001
+From: CastagnaIT <gottardo.stefano.83@gmail.com>
+Date: Sun, 29 Jan 2023 17:51:51 +0100
+Subject: [PATCH] [VideoPlayerVideo] Log an error when codec extradata is
+ required
+
+---
+ xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
+index 20f6b3b1cb51d..38f63f0766718 100644
+--- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
++++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp
+@@ -114,8 +114,11 @@ bool CVideoPlayerVideo::OpenStream(CDVDStreamInfo hint)
+ hint.codec == AV_CODEC_ID_WMV3 ||
+ hint.codec == AV_CODEC_ID_VC1 ||
+ hint.codec == AV_CODEC_ID_AV1)
+- // clang-format on
++ {
++ CLog::LogF(LOGERROR, "Codec id {} require extradata.", hint.codec);
+ return false;
++ }
++ // clang-format on
+ }
+
+ CLog::Log(LOGINFO, "Creating video codec with codec id: {}", hint.codec);
+
+
+From 2559466404d342428d43076bf90fcacc24313af0 Mon Sep 17 00:00:00 2001
+From: enen92 <92enen@gmail.com>
+Date: Mon, 6 Feb 2023 15:36:11 +0000
+Subject: [PATCH] video: remove ffmpeg bsf hack
+
+Manually setting the codecID on the bsf filter is wrong.
+avcodec_parameters_copy should be used instead.
+---
+ xbmc/cores/FFmpeg.cpp | 13 ++++++++-----
+ xbmc/cores/FFmpeg.h | 3 +--
+ .../VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp | 10 +++++++++-
+ .../VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp | 3 +--
+ 4 files changed, 19 insertions(+), 10 deletions(-)
+
+diff --git a/xbmc/cores/FFmpeg.cpp b/xbmc/cores/FFmpeg.cpp
+index d071f6d8e33a3..73b7ea2ae875c 100644
+--- a/xbmc/cores/FFmpeg.cpp
++++ b/xbmc/cores/FFmpeg.cpp
+@@ -135,9 +135,7 @@ void ff_avutil_log(void* ptr, int level, const char* format, va_list va)
+ buffer.erase(0, start);
+ }
+
+-std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt,
+- const AVCodecParserContext* parserCtx,
+- AVCodecContext* codecCtx)
++std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt, const AVCodecParameters* codecPar)
+ {
+ constexpr int FF_MAX_EXTRADATA_SIZE = ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE);
+
+@@ -151,7 +149,7 @@ std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt,
+ * for certain codecs, as noted in discussion of PR#21248
+ */
+
+- AVCodecID codecId = codecCtx->codec_id;
++ AVCodecID codecId = codecPar->codec_id;
+
+ // clang-format off
+ if (
+@@ -178,7 +176,12 @@ std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt,
+ if (ret < 0)
+ return std::make_tuple(nullptr, 0);
+
+- bsf->par_in->codec_id = codecId;
++ ret = avcodec_parameters_copy(bsf->par_in, codecPar);
++ if (ret < 0)
++ {
++ av_bsf_free(&bsf);
++ return std::make_tuple(nullptr, 0);
++ }
+
+ ret = av_bsf_init(bsf);
+ if (ret < 0)
+diff --git a/xbmc/cores/FFmpeg.h b/xbmc/cores/FFmpeg.h
+index 05547a0ba2b5f..5e35d58c6b0a6 100644
+--- a/xbmc/cores/FFmpeg.h
++++ b/xbmc/cores/FFmpeg.h
+@@ -73,5 +73,4 @@ class CFFmpegLog
+ };
+
+ std::tuple<uint8_t*, int> GetPacketExtradata(const AVPacket* pkt,
+- const AVCodecParserContext* parserCtx,
+- AVCodecContext* codecCtx);
++ const AVCodecParameters* codecPar);
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+index 052332331702a..9ca07b9a2dd39 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+@@ -162,7 +162,15 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ avpkt->size = pkt->iSize;
+ avpkt->dts = avpkt->pts = AV_NOPTS_VALUE;
+
+- auto [retExtraData, len] = GetPacketExtradata(avpkt, stream->m_parser, stream->m_context);
++ AVCodecParameters* codecPar = nullptr;
++ int ret = avcodec_parameters_from_context(codecPar, stream->m_context);
++ if (ret < 0)
++ {
++ CLog::LogF(LOGERROR, "avcodec_parameters_from_context failed");
++ return false;
++ }
++
++ auto [retExtraData, len] = GetPacketExtradata(avpkt, codecPar);
+ if (len > 0)
+ {
+ st->changes++;
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+index 7e6a2e10616d7..bc6b54c87235d 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+@@ -2290,8 +2290,7 @@ void CDVDDemuxFFmpeg::ParsePacket(AVPacket* pkt)
+ parser->second->m_parserCtx->parser &&
+ !st->codecpar->extradata)
+ {
+- auto [retExtraData, i] =
+- GetPacketExtradata(pkt, parser->second->m_parserCtx, parser->second->m_codecCtx);
++ auto [retExtraData, i] = GetPacketExtradata(pkt, st->codecpar);
+ if (i > 0)
+ {
+ st->codecpar->extradata_size = i;
+
+
+From 3ad9588656e30abd421e48147b23aee9fb4b3557 Mon Sep 17 00:00:00 2001
+From: Miguel Borges de Freitas <92enen@gmail.com>
+Date: Sun, 12 Feb 2023 12:08:36 +0000
+Subject: [PATCH] [video] fix crash in avcodec_parameters_from_context
+
+---
+ xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+index 9ca07b9a2dd39..26fa9522eea7a 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
+@@ -162,11 +162,12 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ avpkt->size = pkt->iSize;
+ avpkt->dts = avpkt->pts = AV_NOPTS_VALUE;
+
+- AVCodecParameters* codecPar = nullptr;
++ AVCodecParameters* codecPar = avcodec_parameters_alloc();
+ int ret = avcodec_parameters_from_context(codecPar, stream->m_context);
+ if (ret < 0)
+ {
+ CLog::LogF(LOGERROR, "avcodec_parameters_from_context failed");
++ avcodec_parameters_free(&codecPar);
+ return false;
+ }
+
+@@ -188,7 +189,7 @@ bool CDVDDemuxClient::ParsePacket(DemuxPacket* pkt)
+ avcodec_close(stream->m_context);
+ }
+ }
+-
++ avcodec_parameters_free(&codecPar);
+ av_packet_free(&avpkt);
+ }
+
+
+
+From f30f1e6418ea60bc7cb081c59f5f1d9431d264e6 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Sat, 11 Mar 2023 13:28:38 -0800
+Subject: [PATCH 01/10] CDVDAudioCodecFFmpeg: ifdef use of
+ AV_CODEC_FLAG_TRUNCATED for ffmpeg 6.0
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+index b2849c797dbc4..325bb0b7549ab 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+@@ -73,8 +73,10 @@ bool CDVDAudioCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
+ m_pCodecContext->debug = 0;
+ m_pCodecContext->workaround_bugs = 1;
+
++#if LIBAVCODEC_VERSION_MAJOR < 60
+ if (pCodec->capabilities & AV_CODEC_CAP_TRUNCATED)
+ m_pCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED;
++#endif
+
+ m_matrixEncoding = AV_MATRIX_ENCODING_NONE;
+ m_channels = 0;
+
+From 3b71910ee0bb650816456ecc9a21251aff650c4d Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Sat, 11 Mar 2023 13:29:18 -0800
+Subject: [PATCH 02/10] CDVDAudioCodecFFmpeg: fix setting channel layout mask
+ when opening codec
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ .../DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+index 325bb0b7549ab..d1fb2cfe96afc 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+@@ -80,13 +80,21 @@
+ m_channels = 0;
+ #if LIBAVCODEC_BUILD >= AV_VERSION_INT(59, 37, 100) && \
+ LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 28, 100)
+- av_channel_layout_uninit(&m_pCodecContext->ch_layout);
+- m_pCodecContext->ch_layout.order = AV_CHANNEL_ORDER_NATIVE;
+- m_pCodecContext->ch_layout.nb_channels = hints.channels;
++ if (hints.channels > 0 && hints.channellayout > 0)
++ {
++ m_pCodecContext->ch_layout.order = AV_CHANNEL_ORDER_NATIVE;
++ m_pCodecContext->ch_layout.nb_channels = hints.channels;
++ m_pCodecContext->ch_layout.u.mask = hints.channellayout;
++ }
++ else if (hints.channels > 0)
++ {
++ av_channel_layout_default(&m_pCodecContext->ch_layout, hints.channels);
++ }
++
++ m_hint_layout = m_pCodecContext->ch_layout.u.mask;
+ #else
+ m_pCodecContext->channels = hints.channels;
+ #endif
+- m_hint_layout = hints.channellayout;
+ m_pCodecContext->sample_rate = hints.samplerate;
+ m_pCodecContext->block_align = hints.blockalign;
+ m_pCodecContext->bit_rate = hints.bitrate;
+
+From f4fadb3ba4583c45fb06908a3eb352be8c29f235 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Sat, 11 Mar 2023 13:29:34 -0800
+Subject: [PATCH 03/10] CDVDAudioCodecFFmpeg: drop unneeded use of
+ AVFMT_FLAG_PRIV_OPT
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+index bc6b54c87235d..016d198206716 100644
+--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+@@ -323,7 +323,6 @@ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool
+ }
+ if (result < 0)
+ {
+- m_pFormatContext->flags |= AVFMT_FLAG_PRIV_OPT;
+ if (avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0)
+ {
+ CLog::Log(LOGDEBUG, "Error, could not open file {}", CURL::GetRedacted(strFile));
+@@ -335,7 +334,6 @@ bool CDVDDemuxFFmpeg::Open(const std::shared_ptr<CDVDInputStream>& pInput, bool
+ avformat_close_input(&m_pFormatContext);
+ m_pFormatContext = avformat_alloc_context();
+ m_pFormatContext->interrupt_callback = int_cb;
+- m_pFormatContext->flags &= ~AVFMT_FLAG_PRIV_OPT;
+ AVDictionary* options = GetFFMpegOptionsFromInput();
+ av_dict_set_int(&options, "load_all_variants", 0, AV_OPT_SEARCH_CHILDREN);
+ if (avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0)
+
+From 7d03f33b83e5fb127a7495798a20c3b63ac06795 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Wed, 15 Mar 2023 19:58:56 -0700
+Subject: [PATCH 04/10] CDVDVideoCodecFFmpeg: update filter args to use
+ key/value pairs
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ .../VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+index bb9c20bf9d06e..c080589896ce7 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+@@ -1164,8 +1164,9 @@ int CDVDVideoCodecFFmpeg::FilterOpen(const std::string& filters, bool scale)
+ const AVFilter* outFilter = avfilter_get_by_name("buffersink"); // should be last filter in the graph for now
+
+ std::string args = StringUtils::Format(
+- "{}:{}:{}:{}:{}:{}:{}", m_pCodecContext->width, m_pCodecContext->height,
+- m_pCodecContext->pix_fmt, m_pCodecContext->time_base.num ? m_pCodecContext->time_base.num : 1,
++ "video_size={}x{}:pix_fmt={}:time_base={}/{}:pixel_aspect={}/{}", m_pCodecContext->width,
++ m_pCodecContext->height, m_pCodecContext->pix_fmt,
++ m_pCodecContext->time_base.num ? m_pCodecContext->time_base.num : 1,
+ m_pCodecContext->time_base.num ? m_pCodecContext->time_base.den : 1,
+ m_pCodecContext->sample_aspect_ratio.num != 0 ? m_pCodecContext->sample_aspect_ratio.num : 1,
+ m_pCodecContext->sample_aspect_ratio.num != 0 ? m_pCodecContext->sample_aspect_ratio.den : 1);
+
+From 30bd7912802cf0f608751c452c48fc1a2eb8d91b Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Wed, 15 Mar 2023 19:59:27 -0700
+Subject: [PATCH 05/10] CFFmpegPostproc: update filter args to use key/value
+ pairs
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+index 81b969d119667..6c4f664591a04 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+@@ -2962,10 +2962,11 @@ bool CFFmpegPostproc::Init(EINTERLACEMETHOD method)
+ const AVFilter* srcFilter = avfilter_get_by_name("buffer");
+ const AVFilter* outFilter = avfilter_get_by_name("buffersink");
+
+- std::string args = StringUtils::Format("{}:{}:{}:{}:{}:{}:{}", m_config.vidWidth,
+- m_config.vidHeight, AV_PIX_FMT_NV12, 1, 1,
+- (m_config.aspect.num != 0) ? m_config.aspect.num : 1,
+- (m_config.aspect.num != 0) ? m_config.aspect.den : 1);
++ std::string args =
++ StringUtils::Format("video_size={}x{}:pix_fmt={}:time_base={}/{}:pixel_aspect={}/{}",
++ m_config.vidWidth, m_config.vidHeight, AV_PIX_FMT_NV12, 1, 1,
++ (m_config.aspect.num != 0) ? m_config.aspect.num : 1,
++ (m_config.aspect.num != 0) ? m_config.aspect.den : 1);
+
+ if (avfilter_graph_create_filter(&m_pFilterIn, srcFilter, "src", args.c_str(), NULL, m_pFilterGraph) < 0)
+ {
+
+From 54a21151374a2d40a2a452fae2709205ed8e8836 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Sat, 11 Mar 2023 19:32:09 -0800
+Subject: [PATCH 08/10] DXVA: CDecoder: replace removed av_mallocz_array with
+ av_calloc
+
+ref: https://ffmpeg.org/doxygen/5.0/group__lavu__mem__funcs.html\#ga6627f140c3f70847bc6d9690a2fd001f
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
+index a1bc3761c59d1..c06bd1ac0c7e6 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
+@@ -972,7 +972,8 @@ CDecoder::CDecoder(CProcessInfo& processInfo)
+ m_event.Set();
+ m_avD3D11Context = av_d3d11va_alloc_context();
+ m_avD3D11Context->cfg = reinterpret_cast<D3D11_VIDEO_DECODER_CONFIG*>(av_mallocz(sizeof(D3D11_VIDEO_DECODER_CONFIG)));
+- m_avD3D11Context->surface = reinterpret_cast<ID3D11VideoDecoderOutputView**>(av_mallocz_array(32, sizeof(ID3D11VideoDecoderOutputView*)));
++ m_avD3D11Context->surface = reinterpret_cast<ID3D11VideoDecoderOutputView**>(
++ av_calloc(32, sizeof(ID3D11VideoDecoderOutputView*)));
+ m_bufferPool.reset();
+
+ DX::Windowing()->Register(this);
+
+From be1247d627cee6561174467094f1e8a46357df79 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Sat, 11 Mar 2023 19:45:46 -0800
+Subject: [PATCH 09/10] CFFmpegImage: remove deprecated use of pkt_duration
+
+ref: https://ffmpeg.org/doxygen/6.0/structAVFrame.html\#a91725a40000e348b0607adf7f577e646
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ xbmc/guilib/FFmpegImage.cpp | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/guilib/FFmpegImage.cpp b/xbmc/guilib/FFmpegImage.cpp
+index e71980998b2e5..7171c046a9ce5 100644
+--- a/xbmc/guilib/FFmpegImage.cpp
++++ b/xbmc/guilib/FFmpegImage.cpp
+@@ -294,7 +294,15 @@ AVFrame* CFFmpegImage::ExtractFrame()
+ return nullptr;
+ }
+ //we need milliseconds
+- frame->pkt_duration = av_rescale_q(frame->pkt_duration, m_fctx->streams[0]->time_base, AVRational{ 1, 1000 });
++
++#if LIBAVCODEC_VERSION_MAJOR < 60
++ frame->pkt_duration =
++ av_rescale_q(frame->pkt_duration, m_fctx->streams[0]->time_base, AVRational{1, 1000});
++#else
++ frame->duration =
++ av_rescale_q(frame->duration, m_fctx->streams[0]->time_base, AVRational{1, 1000});
++#endif
++
+ m_height = frame->height;
+ m_width = frame->width;
+ m_originalWidth = m_width;
+@@ -745,7 +753,13 @@ std::shared_ptr<Frame> CFFmpegImage::ReadFrame()
+ if (avframe == nullptr)
+ return nullptr;
+ std::shared_ptr<Frame> frame(new Frame());
++
++#if LIBAVCODEC_VERSION_MAJOR < 60
+ frame->m_delay = (unsigned int)avframe->pkt_duration;
++#else
++ frame->m_delay = (unsigned int)avframe->duration;
++#endif
++
+ frame->m_pitch = avframe->width * 4;
+ frame->m_pImage = (unsigned char*) av_malloc(avframe->height * frame->m_pitch);
+ DecodeFrame(avframe, avframe->width, avframe->height, frame->m_pitch, frame->m_pImage);
+
+From c12af890b0973f7c86316087e823f8a31c6b2ed3 Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+Date: Sat, 11 Mar 2023 19:45:01 -0800
+Subject: [PATCH 10/10] CDVDVideoCodecFFmpeg: remove deprecated use of
+ reordered_opaque
+
+ref: https://ffmpeg.org/doxygen/6.0/structAVFrame.html#a12f572ed19a2cba6be3790393cee76b5
+
+Signed-off-by: Lukas Rusak <lorusak@gmail.com>
+---
+ .../DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp | 11 ++++++++++-
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp | 6 ++++--
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp | 3 +++
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp | 5 ++++-
+ 4 files changed, 21 insertions(+), 4 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+index c080589896ce7..6a53ade4a7351 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
+@@ -370,6 +370,10 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
+ m_pCodecContext->get_format = GetFormat;
+ m_pCodecContext->codec_tag = hints.codec_tag;
+
++#if LIBAVCODEC_VERSION_MAJOR >= 60
++ m_pCodecContext->flags = AV_CODEC_FLAG_COPY_OPAQUE;
++#endif
++
+ // setup threading model
+ if (!(hints.codecOptions & CODEC_FORCE_SOFTWARE))
+ {
+@@ -545,9 +549,10 @@ void CDVDVideoCodecFFmpeg::UpdateName()
+ CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg - Updated codec: {}", m_name);
+ }
+
++#if LIBAVCODEC_VERSION_MAJOR < 60
+ union pts_union
+ {
+- double pts_d;
++ double pts_d;
+ int64_t pts_i;
+ };
+
+@@ -557,6 +562,7 @@ static int64_t pts_dtoi(double pts)
+ u.pts_d = pts;
+ return u.pts_i;
+ }
++#endif
+
+ bool CDVDVideoCodecFFmpeg::AddData(const DemuxPacket &packet)
+ {
+@@ -575,7 +581,10 @@ bool CDVDVideoCodecFFmpeg::AddData(const DemuxPacket &packet)
+ m_started = true;
+
+ m_dts = packet.dts;
++
++#if LIBAVCODEC_VERSION_MAJOR < 60
+ m_pCodecContext->reordered_opaque = pts_dtoi(packet.pts);
++#endif
+
+ AVPacket* avpkt = av_packet_alloc();
+ if (!avpkt)
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
+index c06bd1ac0c7e6..81451995ca1db 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DXVA.cpp
+@@ -1538,8 +1538,6 @@ int CDecoder::GetBuffer(AVCodecContext* avctx, AVFrame* pic)
+ return -1;
+ }
+
+- pic->reordered_opaque = avctx->reordered_opaque;
+-
+ for (unsigned i = 0; i < 4; i++)
+ {
+ pic->data[i] = nullptr;
+@@ -1556,6 +1554,10 @@ int CDecoder::GetBuffer(AVCodecContext* avctx, AVFrame* pic)
+ }
+ pic->buf[0] = buffer;
+
++#if LIBAVCODEC_VERSION_MAJOR < 60
++ pic->reordered_opaque = avctx->reordered_opaque;
++#endif
++
+ Acquire();
+
+ return 0;
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+index 6c4f664591a04..447a13495d4e8 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+@@ -867,7 +867,10 @@ int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags)
+ }
+ pic->buf[0] = buffer;
+
++#if LIBAVCODEC_VERSION_MAJOR < 60
+ pic->reordered_opaque = avctx->reordered_opaque;
++#endif
++
+ va->Acquire();
+ return 0;
+ }
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp
+index ec07af79de819..50e16d492ebc7 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/VDPAU.cpp
+@@ -1041,7 +1041,10 @@ int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags)
+ }
+ pic->buf[0] = buffer;
+
+- pic->reordered_opaque= avctx->reordered_opaque;
++#if LIBAVCODEC_VERSION_MAJOR < 60
++ pic->reordered_opaque = avctx->reordered_opaque;
++#endif
++
+ return 0;
+ }
+
+
+From 928a7e4196046154419727a23c734d904e5e1b6c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Markus=20H=C3=A4rer?= <markus.haerer@gmx.net>
+Date: Sun, 23 Apr 2023 23:29:28 +0200
+Subject: [PATCH] FFmpegImage: Switch back to jpeg_pipe for FFmpeg>=6.0
+
+---
+ xbmc/guilib/FFmpegImage.cpp | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/guilib/FFmpegImage.cpp b/xbmc/guilib/FFmpegImage.cpp
+index 7171c046a9ce5..429037740a7d2 100644
+--- a/xbmc/guilib/FFmpegImage.cpp
++++ b/xbmc/guilib/FFmpegImage.cpp
+@@ -198,9 +198,16 @@ bool CFFmpegImage::Initialize(unsigned char* buffer, size_t bufSize)
+ bool is_png = (bufSize > 3 && buffer[1] == 'P' && buffer[2] == 'N' && buffer[3] == 'G');
+ bool is_tiff = (bufSize > 2 && buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*');
+
++ // See Github #19113
++#if LIBAVCODEC_VERSION_MAJOR < 60
++ constexpr char jpegFormat[] = "image2";
++#else
++ constexpr char jpegFormat[] = "jpeg_pipe";
++#endif
++
+ FFMPEG_FMT_CONST AVInputFormat* inp = nullptr;
+ if (is_jpeg)
+- inp = av_find_input_format("image2");
++ inp = av_find_input_format(jpegFormat);
+ else if (m_strMimeType == "image/apng")
+ inp = av_find_input_format("apng");
+ else if (is_png)
+@@ -213,7 +220,7 @@ bool CFFmpegImage::Initialize(unsigned char* buffer, size_t bufSize)
+ inp = av_find_input_format("webp_pipe");
+ // brute force parse if above check already failed
+ else if (m_strMimeType == "image/jpeg" || m_strMimeType == "image/jpg")
+- inp = av_find_input_format("image2");
++ inp = av_find_input_format(jpegFormat);
+ else if (m_strMimeType == "image/png")
+ inp = av_find_input_format("png_pipe");
+ else if (m_strMimeType == "image/tiff")
+
+
+From 9d7f4dfd00d89d4a5d6d8095ee9b0b746051b30c Mon Sep 17 00:00:00 2001
+From: CrystalP <crystalp@kodi.tv>
+Date: Mon, 1 May 2023 13:26:56 -0400
+Subject: [PATCH] [fix] extern C for ffmpeg includes
+
+---
+ xbmc/cores/RetroPlayer/process/RPProcessInfo.h | 3 +++
+ xbmc/cores/RetroPlayer/rendering/RenderTranslator.h | 3 +++
+ xbmc/cores/VideoPlayer/DVDFileInfo.cpp | 4 ++--
+ xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h | 4 ++++
+ .../VideoPlayer/VideoRenderers/windows/RendererShaders.h | 4 ++++
+ 5 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/cores/RetroPlayer/process/RPProcessInfo.h b/xbmc/cores/RetroPlayer/process/RPProcessInfo.h
+index 9f930e78e9d84..f5ffe670d68aa 100644
+--- a/xbmc/cores/RetroPlayer/process/RPProcessInfo.h
++++ b/xbmc/cores/RetroPlayer/process/RPProcessInfo.h
+@@ -17,7 +17,10 @@
+ #include <string>
+ #include <vector>
+
++extern "C"
++{
+ #include <libavutil/pixfmt.h>
++}
+
+ class CDataCacheCore;
+
+diff --git a/xbmc/cores/RetroPlayer/rendering/RenderTranslator.h b/xbmc/cores/RetroPlayer/rendering/RenderTranslator.h
+index 575ad814fc125..d78e1c25e4070 100644
+--- a/xbmc/cores/RetroPlayer/rendering/RenderTranslator.h
++++ b/xbmc/cores/RetroPlayer/rendering/RenderTranslator.h
+@@ -10,7 +10,10 @@
+
+ #include "cores/GameSettings.h"
+
++extern "C"
++{
+ #include <libavutil/pixfmt.h>
++}
+
+ namespace KODI
+ {
+diff --git a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp
+index 0860b40475b18..c7253bbd5497f 100644
+--- a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp
++++ b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp
+@@ -32,8 +32,6 @@
+ #include "DVDDemuxers/DVDDemuxVobsub.h"
+ #include "Process/ProcessInfo.h"
+
+-#include <libavcodec/avcodec.h>
+-#include <libswscale/swscale.h>
+ #include "filesystem/File.h"
+ #include "cores/FFmpeg.h"
+ #include "TextureCache.h"
+@@ -44,7 +42,9 @@
+ #include <memory>
+
+ extern "C" {
++#include <libavcodec/avcodec.h>
+ #include <libavformat/avformat.h>
++#include <libswscale/swscale.h>
+ }
+
+ bool CDVDFileInfo::GetFileDuration(const std::string &path, int& duration)
+diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h
+index 9412377157f94..0eed9503dc9ac 100644
+--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h
++++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.h
+@@ -13,7 +13,11 @@
+ #include <map>
+
+ #include <d3d11_4.h>
++
++extern "C"
++{
+ #include <libavutil/pixfmt.h>
++}
+
+ enum RenderMethod;
+
+diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.h b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.h
+index 945cadda76841..af4d677aae923 100644
+--- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.h
++++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererShaders.h
+@@ -13,7 +13,11 @@
+ #include <map>
+
+ #include <d3d11_4.h>
++
++extern "C"
++{
+ #include <libavutil/pixfmt.h>
++}
+
+ #define PLANE_Y 0
+ #define PLANE_U 1
diff --git a/debian/patches/workarounds/0005-pcre2.patch b/debian/patches/workarounds/0005-pcre2.patch
new file mode 100644
index 0000000..7dfda8c
--- /dev/null
+++ b/debian/patches/workarounds/0005-pcre2.patch
@@ -0,0 +1,1219 @@
+Description: Port to PCRE2.
+Bug-Debian: https://bugs.debian.org/1000113
+Author: Yavor Doganov <yavor@gnu.org>
+Forwarded: no
+Last-Update: 2024-01-07
+---
+
+--- kodi-20.2+dfsg.orig/cmake/modules/FindPCRE.cmake
++++ kodi-20.2+dfsg/cmake/modules/FindPCRE.cmake
+@@ -77,45 +77,34 @@
+
+ else()
+ # Populate paths for find_package_handle_standard_args
+- find_path(PCRE_INCLUDE_DIR pcre.h)
++ find_path(PCRE_INCLUDE_DIR pcre2.h)
+
+- find_library(PCRECPP_LIBRARY_RELEASE NAMES pcrecpp)
+- find_library(PCRECPP_LIBRARY_DEBUG NAMES pcrecppd)
+-
+- find_library(PCRE_LIBRARY_RELEASE NAMES pcre)
+- find_library(PCRE_LIBRARY_DEBUG NAMES pcred)
++ find_library(PCRE_LIBRARY_RELEASE NAMES pcre2-8)
+ endif()
+ else()
+
+ if(PKG_CONFIG_FOUND)
+- pkg_check_modules(PC_PCRE libpcrecpp QUIET)
++ pkg_check_modules(PC_PCRE libpcre2-8 QUIET)
+ endif()
+
+- find_path(PCRE_INCLUDE_DIR pcrecpp.h
++ find_path(PCRE_INCLUDE_DIR pcre2.h
+ PATHS ${PC_PCRE_INCLUDEDIR})
+- find_library(PCRECPP_LIBRARY_RELEASE NAMES pcrecpp
+- PATHS ${PC_PCRE_LIBDIR})
+- find_library(PCRE_LIBRARY_RELEASE NAMES pcre
++ find_library(PCRE_LIBRARY_RELEASE NAMES pcre2-8
+ PATHS ${PC_PCRE_LIBDIR})
+- find_library(PCRECPP_LIBRARY_DEBUG NAMES pcrecppd
+- PATHS ${PC_PCRE_LIBDIR})
+- find_library(PCRE_LIBRARY_DEBUG NAMES pcred
+- PATHS ${PC_PCRE_LIBDIR})
+ set(PCRE_VERSION ${PC_PCRE_VERSION})
+
+ endif()
+
+ include(SelectLibraryConfigurations)
+- select_library_configurations(PCRECPP)
+ select_library_configurations(PCRE)
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(PCRE
+- REQUIRED_VARS PCRECPP_LIBRARY PCRE_LIBRARY PCRE_INCLUDE_DIR
++ REQUIRED_VARS PCRE_LIBRARY PCRE_INCLUDE_DIR
+ VERSION_VAR PCRE_VERSION)
+
+ if(PCRE_FOUND)
+- set(PCRE_LIBRARIES ${PCRECPP_LIBRARY} ${PCRE_LIBRARY})
++ set(PCRE_LIBRARIES ${PCRE_LIBRARY})
+ set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
+ if(WIN32)
+ set(PCRE_DEFINITIONS -DPCRE_STATIC=1)
+@@ -166,5 +155,5 @@
+ endif()
+ endif()
+
+- mark_as_advanced(PCRE_INCLUDE_DIR PCRECPP_LIBRARY PCRE_LIBRARY)
++ mark_as_advanced(PCRE_INCLUDE_DIR PCRE_LIBRARY)
+ endif()
+--- kodi-20.2+dfsg.orig/xbmc/utils/RegExp.h
++++ kodi-20.2+dfsg/xbmc/utils/RegExp.h
+@@ -13,16 +13,8 @@
+ #include <string>
+ #include <vector>
+
+-/* make sure stdlib.h is included before including pcre.h inside the
+- namespace; this works around stdlib.h definitions also living in
+- the PCRE namespace */
+-#include <stdlib.h>
+-
+-namespace PCRE {
+-struct real_pcre_jit_stack; // forward declaration for PCRE without JIT
+-typedef struct real_pcre_jit_stack pcre_jit_stack;
+-#include <pcre.h>
+-}
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+
+ class CRegExp
+ {
+@@ -143,17 +135,17 @@
+ void Cleanup();
+ inline bool IsValidSubNumber(int iSub) const;
+
+- PCRE::pcre* m_re;
+- PCRE::pcre_extra* m_sd;
++ pcre2_code* m_re;
++ pcre2_match_context* m_ctxt;
+ static const int OVECCOUNT=(m_MaxNumOfBackrefrences + 1) * 3;
+ unsigned int m_offset;
+- int m_iOvector[OVECCOUNT];
++ PCRE2_SIZE* m_iOvector;
+ utf8Mode m_utf8Mode;
+ int m_iMatchCount;
+- int m_iOptions;
++ uint32_t m_iOptions;
+ bool m_jitCompiled;
+ bool m_bMatched;
+- PCRE::pcre_jit_stack* m_jitStack;
++ pcre2_jit_stack* m_jitStack;
+ std::string m_subject;
+ std::string m_pattern;
+ static int m_Utf8Supported;
+--- kodi-20.2+dfsg.orig/xbmc/filesystem/FTPParse.cpp
++++ kodi-20.2+dfsg/xbmc/filesystem/FTPParse.cpp
+@@ -9,8 +9,10 @@
+ #include "FTPParse.h"
+
+ #include <cmath>
++#include <cstring>
+
+-#include <pcrecpp.h>
++#define PCRE2_CODE_UNIT_WIDTH 8
++#include <pcre2.h>
+
+ CFTPParse::CFTPParse()
+ {
+@@ -46,26 +48,31 @@
+
+ void CFTPParse::setTime(const std::string& str)
+ {
++ pcre2_code *re, *unix_re, *multinet_re, *msdos_re;
++ pcre2_match_data *md;
++ PCRE2_SPTR unix_pat, multinet_pat, msdos_pat, str_sptr;
++ PCRE2_SIZE offset;
++ int err;
+ /* Variables used to capture patterns via the regexes */
+- std::string month;
+- std::string day;
+- std::string year;
+- std::string hour;
+- std::string minute;
+- std::string second;
+- std::string am_or_pm;
++ char *month = NULL;
++ PCRE2_UCHAR *day = NULL;
++ PCRE2_UCHAR *year = NULL;
++ PCRE2_UCHAR *hour = NULL;
++ PCRE2_UCHAR *minute = NULL;
++ PCRE2_UCHAR *second = NULL;
++ PCRE2_UCHAR *am_or_pm = NULL;
+
+ /* time struct used to set the time_t variable */
+ struct tm time_struct = {};
+
+ /* Regex to read Unix, NetWare and NetPresenz time format */
+- pcrecpp::RE unix_re("^([A-Za-z]{3})" // month
++ unix_pat = reinterpret_cast<PCRE2_SPTR>("^([A-Za-z]{3})" // month
+ "\\s+(\\d{1,2})" // day of month
+ "\\s+([:\\d]{4,5})$" // time of day or year
+ );
+
+ /* Regex to read MultiNet time format */
+- pcrecpp::RE multinet_re("^(\\d{1,2})" // day of month
++ multinet_pat = reinterpret_cast<PCRE2_SPTR>("^(\\d{1,2})" // day of month
+ "-([A-Za-z]{3})" // month
+ "-(\\d{4})" // year
+ "\\s+(\\d{2})" // hour
+@@ -74,7 +81,7 @@
+ );
+
+ /* Regex to read MSDOS time format */
+- pcrecpp::RE msdos_re("^(\\d{2})" // month
++ msdos_pat = reinterpret_cast<PCRE2_SPTR>("^(\\d{2})" // month
+ "-(\\d{2})" // day of month
+ "-(\\d{2})" // year
+ "\\s+(\\d{2})" // hour
+@@ -82,48 +89,53 @@
+ "([AP]M)$" // AM or PM
+ );
+
+- if (unix_re.FullMatch(str, &month, &day, &year))
+- {
++ unix_re = pcre2_compile(unix_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ multinet_re = pcre2_compile(multinet_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ msdos_re = pcre2_compile(msdos_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ str_sptr = reinterpret_cast<PCRE2_SPTR>(str.c_str());
++ md = pcre2_match_data_create(30, NULL);
++
++ if (pcre2_match(unix_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
++ > 0)
++ {
++ pcre2_substring_get_bynumber(md, 1,
++ reinterpret_cast<PCRE2_UCHAR **>(&month),
++ &offset);
++ pcre2_substring_get_bynumber(md, 2, &day, &offset);
++ pcre2_substring_get_bynumber(md, 3, &year, &offset);
++ re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\d{2}):(\\d{2})"),
++ PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
+ /* set the month */
+- if (pcrecpp::RE("jan",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ if (strcasestr(month, "jan"))
+ time_struct.tm_mon = 0;
+- else if (pcrecpp::RE("feb",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "feb"))
+ time_struct.tm_mon = 1;
+- else if (pcrecpp::RE("mar",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "mar"))
+ time_struct.tm_mon = 2;
+- else if (pcrecpp::RE("apr",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "apr"))
+ time_struct.tm_mon = 3;
+- else if (pcrecpp::RE("may",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "may"))
+ time_struct.tm_mon = 4;
+- else if (pcrecpp::RE("jun",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "jun"))
+ time_struct.tm_mon = 5;
+- else if (pcrecpp::RE("jul",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "jul"))
+ time_struct.tm_mon = 6;
+- else if (pcrecpp::RE("aug",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "aug"))
+ time_struct.tm_mon = 7;
+- else if (pcrecpp::RE("sep",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "sep"))
+ time_struct.tm_mon = 8;
+- else if (pcrecpp::RE("oct",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "oct"))
+ time_struct.tm_mon = 9;
+- else if (pcrecpp::RE("nov",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "nov"))
+ time_struct.tm_mon = 10;
+- else if (pcrecpp::RE("dec",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "dec"))
+ time_struct.tm_mon = 11;
+
+ /* set the day of the month */
+- time_struct.tm_mday = atoi(day.c_str());
++ time_struct.tm_mday = atoi(reinterpret_cast<const char *>(day));
+
+ time_t t = time(NULL);
+ struct tm *current_time;
+@@ -133,11 +145,14 @@
+ #else
+ current_time = localtime(&t);
+ #endif
+- if (pcrecpp::RE("(\\d{2}):(\\d{2})").FullMatch(year, &hour, &minute))
++ if (pcre2_match(re, reinterpret_cast<PCRE2_SPTR>(year),
++ PCRE2_ZERO_TERMINATED, 0, 0, md, NULL) > 0)
+ {
++ pcre2_substring_get_bynumber(md, 1, &hour, &offset);
++ pcre2_substring_get_bynumber(md, 2, &minute, &offset);
+ /* set the hour and minute */
+- time_struct.tm_hour = atoi(hour.c_str());
+- time_struct.tm_min = atoi(minute.c_str());
++ time_struct.tm_hour = atoi(reinterpret_cast<const char *>(hour));
++ time_struct.tm_min = atoi(reinterpret_cast<const char *>(minute));
+
+ /* set the year */
+ if ((current_time->tm_mon - time_struct.tm_mon < 0) ||
+@@ -150,93 +165,99 @@
+ else
+ {
+ /* set the year */
+- time_struct.tm_year = atoi(year.c_str()) - 1900;
++ time_struct.tm_year = atoi(reinterpret_cast<const char *>(year)) - 1900;
+ }
+
+ /* set the day of the week */
+ time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
+ time_struct.tm_mday,
+ time_struct.tm_year + 1900);
++ pcre2_code_free(re);
+ }
+- else if (multinet_re.FullMatch(str, &day, &month, &year,
+- &hour, &minute, (void*)NULL, &second))
++ else if (pcre2_match(multinet_re, str_sptr, PCRE2_ZERO_TERMINATED,
++ 0, 0, md, NULL) > 0)
+ {
++ pcre2_substring_get_bynumber(md, 1, &day, &offset);
++ pcre2_substring_get_bynumber(md, 2,
++ reinterpret_cast<PCRE2_UCHAR **>(&month),
++ &offset);
++ pcre2_substring_get_bynumber(md, 3, &year, &offset);
++ pcre2_substring_get_bynumber(md, 4, &hour, &offset);
++ pcre2_substring_get_bynumber(md, 5, &minute, &offset);
++ pcre2_substring_get_bynumber(md, 7, &second, &offset);
+ /* set the month */
+- if (pcrecpp::RE("jan",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ if (strcasestr(month, "jan"))
+ time_struct.tm_mon = 0;
+- else if (pcrecpp::RE("feb",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "feb"))
+ time_struct.tm_mon = 1;
+- else if (pcrecpp::RE("mar",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "mar"))
+ time_struct.tm_mon = 2;
+- else if (pcrecpp::RE("apr",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "apr"))
+ time_struct.tm_mon = 3;
+- else if (pcrecpp::RE("may",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "may"))
+ time_struct.tm_mon = 4;
+- else if (pcrecpp::RE("jun",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "jun"))
+ time_struct.tm_mon = 5;
+- else if (pcrecpp::RE("jul",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "jul"))
+ time_struct.tm_mon = 6;
+- else if (pcrecpp::RE("aug",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "aug"))
+ time_struct.tm_mon = 7;
+- else if (pcrecpp::RE("sep",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "sep"))
+ time_struct.tm_mon = 8;
+- else if (pcrecpp::RE("oct",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "oct"))
+ time_struct.tm_mon = 9;
+- else if (pcrecpp::RE("nov",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "nov"))
+ time_struct.tm_mon = 10;
+- else if (pcrecpp::RE("dec",
+- pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
++ else if (strcasestr(month, "dec"))
+ time_struct.tm_mon = 11;
+
+ /* set the day of the month and year */
+- time_struct.tm_mday = atoi(day.c_str());
+- time_struct.tm_year = atoi(year.c_str()) - 1900;
++ time_struct.tm_mday = atoi(reinterpret_cast<const char *>(day));
++ time_struct.tm_year = atoi(reinterpret_cast<const char *>(year)) - 1900;
+
+ /* set the hour and minute */
+- time_struct.tm_hour = atoi(hour.c_str());
+- time_struct.tm_min = atoi(minute.c_str());
++ time_struct.tm_hour = atoi(reinterpret_cast<const char *>(hour));
++ time_struct.tm_min = atoi(reinterpret_cast<const char *>(minute));
+
+ /* set the second if given*/
+- if (second.length() > 0)
+- time_struct.tm_sec = atoi(second.c_str());
++ if (strlen(reinterpret_cast<const char *>(second)) > 0)
++ time_struct.tm_sec = atoi(reinterpret_cast<const char *>(second));
+
+ /* set the day of the week */
+ time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
+ time_struct.tm_mday,
+ time_struct.tm_year + 1900);
+ }
+- else if (msdos_re.FullMatch(str, &month, &day,
+- &year, &hour, &minute, &am_or_pm))
++ else if (pcre2_match(msdos_re, str_sptr, PCRE2_ZERO_TERMINATED,
++ 0, 0, md, NULL) > 0)
+ {
++ pcre2_substring_get_bynumber(md, 1,
++ reinterpret_cast<PCRE2_UCHAR **>(&month),
++ &offset);
++ pcre2_substring_get_bynumber(md, 2, &day, &offset);
++ pcre2_substring_get_bynumber(md, 3, &year, &offset);
++ pcre2_substring_get_bynumber(md, 4, &hour, &offset);
++ pcre2_substring_get_bynumber(md, 5, &minute, &offset);
++ pcre2_substring_get_bynumber(md, 6, &am_or_pm, &offset);
++
+ /* set the month and the day of the month */
+- time_struct.tm_mon = atoi(month.c_str()) - 1;
+- time_struct.tm_mday = atoi(day.c_str());
++ time_struct.tm_mon = atoi(month) - 1;
++ time_struct.tm_mday = atoi(reinterpret_cast<const char *>(day));
+
+ /* set the year */
+- time_struct.tm_year = atoi(year.c_str());
++ time_struct.tm_year = atoi(reinterpret_cast<const char *>(year));
+ if (time_struct.tm_year < 70)
+ time_struct.tm_year += 100;
+
+ /* set the hour */
+- time_struct.tm_hour = atoi(hour.c_str());
++ time_struct.tm_hour = atoi(reinterpret_cast<const char *>(hour));
+ if (time_struct.tm_hour == 12)
+ time_struct.tm_hour -= 12;
+- if (pcrecpp::RE("PM").FullMatch(am_or_pm))
++ if (strstr(reinterpret_cast<const char *>(am_or_pm), "PM"))
+ time_struct.tm_hour += 12;
+
+ /* set the minute */
+- time_struct.tm_min = atoi(minute.c_str());
++ time_struct.tm_min = atoi(reinterpret_cast<const char *>(minute));
+
+ /* set the day of the week */
+ time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
+@@ -246,6 +267,18 @@
+
+ /* now set m_time */
+ m_time = mktime(&time_struct);
++
++ pcre2_code_free(unix_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
++ pcre2_match_data_free(md);
++ pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(month));
++ pcre2_substring_free(day);
++ pcre2_substring_free(year);
++ pcre2_substring_free(hour);
++ pcre2_substring_free(minute);
++ pcre2_substring_free(second);
++ pcre2_substring_free(am_or_pm);
+ }
+
+ int CFTPParse::getDayOfWeek(int month, int date, int year)
+@@ -325,22 +358,22 @@
+
+ int CFTPParse::FTPParse(const std::string& str)
+ {
++ pcre2_code *unix_re, *netware_re, *netpresenz_re, *eplf_re, *multinet_re,
++ *msdos_re;
++ pcre2_match_data *md;
++ PCRE2_SPTR unix_pat, netware_pat, netpresenz_pat, eplf_pat, multinet_pat,
++ msdos_pat, str_sptr;
++ PCRE2_SIZE offset;
++ int err;
+ /* Various variable to capture patterns via the regexes */
+- std::string permissions;
+- std::string link_count;
+- std::string owner;
+- std::string group;
+- std::string size;
+- std::string date;
+- std::string name;
+- std::string type;
+- std::string stuff;
+- std::string facts;
+- std::string version;
+- std::string file_id;
++ char *type = NULL;
++ PCRE2_UCHAR *size = NULL;
++ PCRE2_UCHAR *date = NULL;
++ PCRE2_UCHAR *name = NULL;
++ PCRE2_UCHAR *facts;
+
+ /* Regex for standard Unix listing formats */
+- pcrecpp::RE unix_re("^([-bcdlps])" // type
++ unix_pat = reinterpret_cast<PCRE2_SPTR>("^([-bcdlps])" // type
+ "([-rwxXsStT]{9})" // permissions
+ "\\s+(\\d+)" // hard link count
+ "\\s+(\\w+)" // owner
+@@ -352,7 +385,7 @@
+
+ /* Regex for NetWare listing formats */
+ /* See http://www.novell.com/documentation/oes/ftp_enu/data/a3ep22p.html#fbhbaijf */
+- pcrecpp::RE netware_re("^([-d])" // type
++ netware_pat = reinterpret_cast<PCRE2_SPTR>("^([-d])" // type
+ "\\s+(\\[[-SRWCIEMFA]{8}\\])" // rights
+ "\\s+(\\w+)" // owner
+ "\\s+(\\d+)" // size
+@@ -363,7 +396,7 @@
+ /* Regex for NetPresenz */
+ /* See http://files.stairways.com/other/ftp-list-specs-info.txt */
+ /* Here we will capture permissions and size if given */
+- pcrecpp::RE netpresenz_re("^([-dl])" // type
++ netpresenz_pat = reinterpret_cast<PCRE2_SPTR>("^([-dl])" // type
+ "([-rwx]{9}|)" // permissions
+ "\\s+(.*)" // stuff
+ "\\s+(\\d+|)" // size
+@@ -374,7 +407,7 @@
+ /* Regex for EPLF */
+ /* See http://cr.yp.to/ftp/list/eplf.html */
+ /* SAVE: "(/,|r,|s\\d+,|m\\d+,|i[\\d!#@$%^&*()]+(\\.[\\d!#@$%^&*()]+|),)+" */
+- pcrecpp::RE eplf_re("^\\+" // initial "plus" sign
++ eplf_pat = reinterpret_cast<PCRE2_SPTR>("^\\+" // initial "plus" sign
+ "([^\\s]+)" // facts
+ "\\s(.+)$" // name
+ );
+@@ -382,7 +415,7 @@
+ /* Regex for MultiNet */
+ /* Best documentation found was
+ * http://www-sld.slac.stanford.edu/SLDWWW/workbook/vms_files.html */
+- pcrecpp::RE multinet_re("^([^;]+)" // name
++ multinet_pat = reinterpret_cast<PCRE2_SPTR>("^([^;]+)" // name
+ ";(\\d+)" // version
+ "\\s+([\\d/]+)" // file id
+ "\\s+(\\d{1,2}-[A-Za-z]{3}-\\d{4}\\s+\\d{2}:\\d{2}(:\\d{2})?)" // date
+@@ -391,20 +424,42 @@
+ );
+
+ /* Regex for MSDOS */
+- pcrecpp::RE msdos_re("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
++ msdos_pat = reinterpret_cast<PCRE2_SPTR>("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
+ "\\s+(<DIR>|[\\d]+)" // dir or size
+ "\\s+(.+)$" // name
+ );
+
+- if (unix_re.FullMatch(str, &type, &permissions, &link_count, &owner, &group, &size, &date, &name))
+- {
+- m_name = name;
+- m_size = (uint64_t)strtod(size.c_str(), NULL);
+- if (pcrecpp::RE("d").FullMatch(type))
++ unix_re = pcre2_compile(unix_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ netware_re = pcre2_compile(netware_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ netpresenz_re = pcre2_compile(netpresenz_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ eplf_re = pcre2_compile(eplf_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ multinet_re = pcre2_compile(multinet_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ msdos_re = pcre2_compile(msdos_pat, PCRE2_ZERO_TERMINATED,
++ 0, &err, &offset, NULL);
++ md = pcre2_match_data_create(30, NULL);
++ str_sptr = reinterpret_cast<PCRE2_SPTR>(str.c_str());
++
++ if (pcre2_match(unix_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
++ > 0)
++ {
++ pcre2_substring_get_bynumber(md, 1,
++ reinterpret_cast<PCRE2_UCHAR **>(&type),
++ &offset);
++ pcre2_substring_get_bynumber(md, 6, &size, &offset);
++ pcre2_substring_get_bynumber(md, 7, &date, &offset);
++ pcre2_substring_get_bynumber(md, 8, &name, &offset);
++ m_name = reinterpret_cast<const char *>(name);
++ m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
++ if (strstr(type, "d"))
+ m_flagtrycwd = 1;
+- if (pcrecpp::RE("-").FullMatch(type))
++ if (strstr(type, "-"))
+ m_flagtryretr = 1;
+- if (pcrecpp::RE("l").FullMatch(type))
++ if (strstr(type, "l"))
+ {
+ m_flagtrycwd = m_flagtryretr = 1;
+ // handle symlink
+@@ -412,31 +467,67 @@
+ if (found != std::string::npos)
+ m_name = m_name.substr(0, found);
+ }
+- setTime(date);
++ setTime(reinterpret_cast<const char *>(date));
++ pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
++ pcre2_substring_free(size);
++ pcre2_substring_free(date);
++ pcre2_substring_free(name);
++ pcre2_match_data_free(md);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 1;
+ }
+- if (netware_re.FullMatch(str, &type, &permissions, &owner, &size, &date, &name))
++ if (pcre2_match(netware_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
++ > 0)
+ {
+- m_name = name;
+- m_size = (uint64_t)strtod(size.c_str(), NULL);
+- if (pcrecpp::RE("d").FullMatch(type))
++ pcre2_substring_get_bynumber(md, 1,
++ reinterpret_cast<PCRE2_UCHAR **>(&type),
++ &offset);
++ pcre2_substring_get_bynumber(md, 4, &size, &offset);
++ pcre2_substring_get_bynumber(md, 5, &date, &offset);
++ pcre2_substring_get_bynumber(md, 6, &name, &offset);
++ m_name = reinterpret_cast<const char *>(name);
++ m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
++ if (strstr(type, "d"))
+ m_flagtrycwd = 1;
+- if (pcrecpp::RE("-").FullMatch(type))
++ if (strstr(type, "-"))
+ m_flagtryretr = 1;
+- setTime(date);
++ setTime(reinterpret_cast<const char *>(date));
++ pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
++ pcre2_substring_free(size);
++ pcre2_substring_free(date);
++ pcre2_substring_free(name);
++ pcre2_match_data_free(md);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 1;
+ }
+- if (netpresenz_re.FullMatch(str, &type, &permissions, &stuff, &size, &date, &name))
++ if (pcre2_match(netpresenz_re, str_sptr, PCRE2_ZERO_TERMINATED,
++ 0, 0, md, NULL) > 0)
+ {
+- m_name = name;
+- m_size = (uint64_t)strtod(size.c_str(), NULL);
+- if (pcrecpp::RE("d").FullMatch(type))
++ pcre2_substring_get_bynumber(md, 1,
++ reinterpret_cast<PCRE2_UCHAR **>(&type),
++ &offset);
++ pcre2_substring_get_bynumber(md, 4, &size, &offset);
++ pcre2_substring_get_bynumber(md, 5, &date, &offset);
++ pcre2_substring_get_bynumber(md, 6, &name, &offset);
++ m_name = reinterpret_cast<const char *>(name);
++ m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
++ if (strstr(type, "d"))
+ m_flagtrycwd = 1;
+- if (pcrecpp::RE("-").FullMatch(type))
++ if (strstr(type, "-"))
+ m_flagtryretr = 1;
+- if (pcrecpp::RE("l").FullMatch(type))
++ if (strstr(type, "l"))
+ {
+ m_flagtrycwd = m_flagtryretr = 1;
+ // handle symlink
+@@ -444,48 +535,118 @@
+ if (found != std::string::npos)
+ m_name = m_name.substr(0, found);
+ }
+- setTime(date);
++ setTime(reinterpret_cast<const char *>(date));
++ pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
++ pcre2_substring_free(size);
++ pcre2_substring_free(date);
++ pcre2_substring_free(name);
++ pcre2_match_data_free(md);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 1;
+ }
+- if (eplf_re.FullMatch(str, &facts, &name))
++ if (pcre2_match(eplf_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
++ > 0)
+ {
++ pcre2_code *re;
++
++ pcre2_substring_get_bynumber(md, 1, &facts, &offset);
++ pcre2_substring_get_bynumber(md, 2, &name, &offset);
+ /* Get the type, size, and date from the facts */
+- pcrecpp::RE("(\\+|,)(r|/),").PartialMatch(facts, (void*)NULL, &type);
+- pcrecpp::RE("(\\+|,)s(\\d+),").PartialMatch(facts, (void*)NULL, &size);
+- pcrecpp::RE("(\\+|,)m(\\d+),").PartialMatch(facts, (void*)NULL, &date);
+-
+- m_name = name;
+- m_size = (uint64_t)strtod(size.c_str(), NULL);
+- if (pcrecpp::RE("/").FullMatch(type))
++ re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\+|,)(r|/),"),
++ PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
++ if (pcre2_match(re, facts, PCRE2_ZERO_TERMINATED, 0,
++ PCRE2_PARTIAL_SOFT, md, NULL) > 0)
++ pcre2_substring_get_bynumber(md, 2,
++ reinterpret_cast<PCRE2_UCHAR **>(&type),
++ &offset);
++ pcre2_code_free(re);
++ re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\+|,)s(\\d+),"),
++ PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
++ if (pcre2_match(re, facts, PCRE2_ZERO_TERMINATED, 0,
++ PCRE2_PARTIAL_SOFT, md, NULL) > 0)
++ pcre2_substring_get_bynumber(md, 2, &size, &offset);
++ pcre2_code_free(re);
++ re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\+|,)m(\\d+),"),
++ PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
++ if (pcre2_match(re, facts, PCRE2_ZERO_TERMINATED, 0,
++ PCRE2_PARTIAL_SOFT, md, NULL) > 0)
++ pcre2_substring_get_bynumber(md, 2, &date, &offset);
++ pcre2_code_free(re);
++ pcre2_substring_free(facts);
++
++ m_name = reinterpret_cast<const char *>(name);
++ m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
++ if (strstr(type, "/"))
+ m_flagtrycwd = 1;
+- if (pcrecpp::RE("r").FullMatch(type))
++ if (strstr(type, "r"))
+ m_flagtryretr = 1;
+ /* eplf stores the date in time_t format already */
+- m_time = atoi(date.c_str());
++ m_time = atoi(reinterpret_cast<const char *>(date));
++ pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
++ pcre2_substring_free(size);
++ pcre2_substring_free(date);
++ pcre2_substring_free(name);
++ pcre2_match_data_free(md);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 1;
+ }
+- if (multinet_re.FullMatch(str, &name, &version, &file_id, &date, (void*)NULL, &owner, &permissions))
++ if (pcre2_match(multinet_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
++ > 0)
+ {
+- if (pcrecpp::RE("\\.DIR$").PartialMatch(name))
++ pcre2_code *re;
++ std::string tmp;
++
++ pcre2_substring_get_bynumber(md, 1, &name, &offset);
++ pcre2_substring_get_bynumber(md, 4, &date, &offset);
++ re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("\\.DIR$"),
++ PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
++ //if (pcrecpp::RE("\\.DIR$").PartialMatch(name))
++ tmp = reinterpret_cast<const char *>(name);
++ if (pcre2_match(re, name, PCRE2_ZERO_TERMINATED, 0, PCRE2_PARTIAL_SOFT,
++ md, NULL) > 0)
+ {
+- name.resize(name.size() - 4);
++ tmp.resize(tmp.size() - 4);
+ m_flagtrycwd = 1;
+ }
+ else
+ m_flagtryretr = 1;
+- m_name = name;
+- setTime(date);
++ m_name = tmp;
++ setTime(reinterpret_cast<const char *>(date));
+ /* Multinet doesn't provide a size */
+ m_size = 0;
++ pcre2_substring_free(date);
++ pcre2_substring_free(name);
++ pcre2_match_data_free(md);
++ pcre2_code_free(re);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 1;
+ }
+- if (msdos_re.FullMatch(str, &date, &size, &name))
++ if (pcre2_match(msdos_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
++ > 0)
+ {
+- m_name = name;
+- if (pcrecpp::RE("<DIR>").FullMatch(size))
++ pcre2_substring_get_bynumber(md, 1, &date, &offset);
++ pcre2_substring_get_bynumber(md, 2, &size, &offset);
++ pcre2_substring_get_bynumber(md, 3, &name, &offset);
++ m_name = reinterpret_cast<const char *>(name);
++ if (strstr(reinterpret_cast<const char *>(size), "<DIR>"))
+ {
+ m_flagtrycwd = 1;
+ m_size = 0;
+@@ -493,12 +654,29 @@
+ else
+ {
+ m_flagtryretr = 1;
+- m_size = (uint64_t)strtod(size.c_str(), NULL);
++ m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
+ }
+- setTime(date);
++ setTime(reinterpret_cast<const char *>(date));
++ pcre2_substring_free(date);
++ pcre2_substring_free(size);
++ pcre2_substring_free(name);
++ pcre2_match_data_free(md);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 1;
+ }
++ pcre2_match_data_free(md);
++ pcre2_code_free(unix_re);
++ pcre2_code_free(netware_re);
++ pcre2_code_free(netpresenz_re);
++ pcre2_code_free(eplf_re);
++ pcre2_code_free(multinet_re);
++ pcre2_code_free(msdos_re);
+
+ return 0;
+ }
+--- kodi-20.2+dfsg.orig/xbmc/utils/RegExp.cpp
++++ kodi-20.2+dfsg/xbmc/utils/RegExp.cpp
+@@ -16,27 +16,6 @@
+ #include <stdlib.h>
+ #include <string.h>
+
+-using namespace PCRE;
+-
+-#ifndef PCRE_UCP
+-#define PCRE_UCP 0
+-#endif // PCRE_UCP
+-
+-#ifdef PCRE_CONFIG_JIT
+-#define PCRE_HAS_JIT_CODE 1
+-#endif
+-
+-#ifndef PCRE_STUDY_JIT_COMPILE
+-#define PCRE_STUDY_JIT_COMPILE 0
+-#endif
+-#ifndef PCRE_INFO_JIT
+-// some unused number
+-#define PCRE_INFO_JIT 2048
+-#endif
+-#ifndef PCRE_HAS_JIT_CODE
+-#define pcre_free_study(x) pcre_free((x))
+-#endif
+-
+ int CRegExp::m_Utf8Supported = -1;
+ int CRegExp::m_UcpSupported = -1;
+ int CRegExp::m_JitSupported = -1;
+@@ -51,25 +30,24 @@
+ {
+ m_utf8Mode = utf8;
+ m_re = NULL;
+- m_sd = NULL;
+- m_iOptions = PCRE_DOTALL | PCRE_NEWLINE_ANY;
++ m_ctxt = NULL;
++ m_iOptions = PCRE2_DOTALL;
+ if(caseless)
+- m_iOptions |= PCRE_CASELESS;
++ m_iOptions |= PCRE2_CASELESS;
+ if (m_utf8Mode == forceUtf8)
+ {
+ if (IsUtf8Supported())
+- m_iOptions |= PCRE_UTF8;
++ m_iOptions |= PCRE2_UTF;
+ if (AreUnicodePropertiesSupported())
+- m_iOptions |= PCRE_UCP;
++ m_iOptions |= PCRE2_UCP;
+ }
+
+ m_offset = 0;
+ m_jitCompiled = false;
+ m_bMatched = false;
+ m_iMatchCount = 0;
++ m_iOvector = NULL;
+ m_jitStack = NULL;
+-
+- memset(m_iOvector, 0, sizeof(m_iOvector));
+ }
+
+ CRegExp::CRegExp(bool caseless, CRegExp::utf8Mode utf8, const char *re, studyMode study /*= NoStudy*/)
+@@ -225,7 +203,8 @@
+ CRegExp::CRegExp(const CRegExp& re)
+ {
+ m_re = NULL;
+- m_sd = NULL;
++ m_ctxt = NULL;
++ m_iOvector = NULL;
+ m_jitStack = NULL;
+ m_utf8Mode = re.m_utf8Mode;
+ m_iOptions = re.m_iOptions;
+@@ -240,12 +219,13 @@
+ m_pattern = re.m_pattern;
+ if (re.m_re)
+ {
+- if (pcre_fullinfo(re.m_re, NULL, PCRE_INFO_SIZE, &size) >= 0)
++ if (pcre2_pattern_info(re.m_re, PCRE2_INFO_SIZE, &size) >= 0)
+ {
+- if ((m_re = (pcre*)malloc(size)))
++ if ((m_re = pcre2_code_copy(re.m_re)))
+ {
+- memcpy(m_re, re.m_re, size);
+- memcpy(m_iOvector, re.m_iOvector, OVECCOUNT*sizeof(int));
++ if (re.m_ctxt)
++ m_ctxt = pcre2_match_context_copy(re.m_ctxt);
++ m_iOvector = re.m_iOvector;
+ m_offset = re.m_offset;
+ m_iMatchCount = re.m_iMatchCount;
+ m_bMatched = re.m_bMatched;
+@@ -273,18 +253,28 @@
+ m_jitCompiled = false;
+ m_bMatched = false;
+ m_iMatchCount = 0;
+- const char *errMsg = NULL;
+- int errOffset = 0;
+- int options = m_iOptions;
++ pcre2_compile_context *ctxt;
++ int errCode;
++ char errMsg[120];
++ PCRE2_SIZE errOffset;
++ uint32_t options = m_iOptions;
+ if (m_utf8Mode == autoUtf8 && requireUtf8(re))
+- options |= (IsUtf8Supported() ? PCRE_UTF8 : 0) | (AreUnicodePropertiesSupported() ? PCRE_UCP : 0);
++ options |= (IsUtf8Supported() ? PCRE2_UTF : 0) | (AreUnicodePropertiesSupported() ? PCRE2_UCP : 0);
+
+ Cleanup();
+
+- m_re = pcre_compile(re, options, &errMsg, &errOffset, NULL);
++ ctxt = pcre2_compile_context_create(NULL);
++ pcre2_set_newline(ctxt, PCRE2_NEWLINE_ANY);
++ m_re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(re),
++ PCRE2_ZERO_TERMINATED, options,
++ &errCode, &errOffset, ctxt);
++ pcre2_compile_context_free(ctxt);
++
+ if (!m_re)
+ {
+ m_pattern.clear();
++ pcre2_get_error_message(errCode, reinterpret_cast<PCRE2_UCHAR *>(errMsg),
++ sizeof(errMsg));
+ CLog::Log(LOGERROR, "PCRE: {}. Compilation failed at offset {} in expression '{}'", errMsg,
+ errOffset, re);
+ return false;
+@@ -295,23 +285,12 @@
+ if (study)
+ {
+ const bool jitCompile = (study == StudyWithJitComp) && IsJitSupported();
+- const int studyOptions = jitCompile ? PCRE_STUDY_JIT_COMPILE : 0;
+
+- m_sd = pcre_study(m_re, studyOptions, &errMsg);
+- if (errMsg != NULL)
+- {
+- CLog::Log(LOGWARNING, "{}: PCRE error \"{}\" while studying expression", __FUNCTION__,
+- errMsg);
+- if (m_sd != NULL)
+- {
+- pcre_free_study(m_sd);
+- m_sd = NULL;
+- }
+- }
+- else if (jitCompile)
++ if (jitCompile)
+ {
+- int jitPresent = 0;
+- m_jitCompiled = (pcre_fullinfo(m_re, m_sd, PCRE_INFO_JIT, &jitPresent) == 0 && jitPresent == 1);
++ pcre2_jit_compile(m_re, PCRE2_JIT_COMPLETE);
++ size_t jitPresent = 0;
++ m_jitCompiled = (pcre2_pattern_info(m_re, PCRE2_INFO_JITSIZE, &jitPresent) == 0 && jitPresent > 0);
+ }
+ }
+
+@@ -325,6 +304,9 @@
+
+ int CRegExp::PrivateRegFind(size_t bufferLen, const char *str, unsigned int startoffset /* = 0*/, int maxNumberOfCharsToTest /*= -1*/)
+ {
++ pcre2_match_data *md;
++ PCRE2_SIZE offset;
++
+ m_offset = 0;
+ m_bMatched = false;
+ m_iMatchCount = 0;
+@@ -347,37 +329,47 @@
+ return -1;
+ }
+
+-#ifdef PCRE_HAS_JIT_CODE
++ if (!m_ctxt)
++ m_ctxt = pcre2_match_context_create(NULL);
++
+ if (m_jitCompiled && !m_jitStack)
+ {
+- m_jitStack = pcre_jit_stack_alloc(32*1024, 512*1024);
++ m_jitStack = pcre2_jit_stack_create(32*1024, 512*1024, NULL);
+ if (m_jitStack == NULL)
+ CLog::Log(LOGWARNING, "{}: can't allocate address space for JIT stack", __FUNCTION__);
+
+- pcre_assign_jit_stack(m_sd, NULL, m_jitStack);
++ pcre2_jit_stack_assign(m_ctxt, NULL, m_jitStack);
+ }
+-#endif
+
+ if (maxNumberOfCharsToTest >= 0)
+ bufferLen = std::min<size_t>(bufferLen, startoffset + maxNumberOfCharsToTest);
+
+ m_subject.assign(str + startoffset, bufferLen - startoffset);
+- int rc = pcre_exec(m_re, NULL, m_subject.c_str(), m_subject.length(), 0, 0, m_iOvector, OVECCOUNT);
++ md = pcre2_match_data_create(OVECCOUNT, NULL);
++ int rc = pcre2_match(m_re,
++ reinterpret_cast<PCRE2_SPTR>(m_subject.c_str()),
++ m_subject.length(), 0, 0, md, m_ctxt);
++ m_iOvector = pcre2_get_ovector_pointer(md);
++ offset = pcre2_get_startchar(md);
++ pcre2_match_data_free(md);
+
+ if (rc<1)
+ {
+ static const int fragmentLen = 80; // length of excerpt before erroneous char for log
+ switch(rc)
+ {
+- case PCRE_ERROR_NOMATCH:
++ case PCRE2_ERROR_NOMATCH:
+ return -1;
+
+- case PCRE_ERROR_MATCHLIMIT:
++ case PCRE2_ERROR_MATCHLIMIT:
+ CLog::Log(LOGERROR, "PCRE: Match limit reached");
+ return -1;
+
+-#ifdef PCRE_ERROR_SHORTUTF8
+- case PCRE_ERROR_SHORTUTF8:
++ case PCRE2_ERROR_UTF8_ERR1:
++ case PCRE2_ERROR_UTF8_ERR2:
++ case PCRE2_ERROR_UTF8_ERR3:
++ case PCRE2_ERROR_UTF8_ERR4:
++ case PCRE2_ERROR_UTF8_ERR5:
+ {
+ const size_t startPos = (m_subject.length() > fragmentLen) ? CUtf8Utils::RFindValidUtf8Char(m_subject, m_subject.length() - fragmentLen) : 0;
+ if (startPos != std::string::npos)
+@@ -389,22 +381,41 @@
+ CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character at the end of string");
+ return -1;
+ }
+-#endif
+- case PCRE_ERROR_BADUTF8:
++ case PCRE2_ERROR_UTF8_ERR6:
++ case PCRE2_ERROR_UTF8_ERR7:
++ case PCRE2_ERROR_UTF8_ERR8:
++ case PCRE2_ERROR_UTF8_ERR9:
++ case PCRE2_ERROR_UTF8_ERR10:
++ case PCRE2_ERROR_UTF8_ERR11:
++ case PCRE2_ERROR_UTF8_ERR12:
++ case PCRE2_ERROR_UTF8_ERR13:
++ case PCRE2_ERROR_UTF8_ERR14:
++ case PCRE2_ERROR_UTF8_ERR15:
++ case PCRE2_ERROR_UTF8_ERR16:
++ case PCRE2_ERROR_UTF8_ERR17:
++ case PCRE2_ERROR_UTF8_ERR18:
++ case PCRE2_ERROR_UTF8_ERR19:
++ case PCRE2_ERROR_UTF8_ERR20:
++ case PCRE2_ERROR_UTF8_ERR21:
+ {
++ char errbuf[120];
++
++ pcre2_get_error_message(rc,
++ reinterpret_cast<PCRE2_UCHAR *>(errbuf),
++ sizeof(errbuf));
+ const size_t startPos = (m_iOvector[0] > fragmentLen) ? CUtf8Utils::RFindValidUtf8Char(m_subject, m_iOvector[0] - fragmentLen) : 0;
+- if (m_iOvector[0] >= 0 && startPos != std::string::npos)
++ if ((int)m_iOvector[0] >= 0 && startPos != std::string::npos)
+ CLog::Log(LOGERROR,
+ "PCRE: Bad UTF-8 character, error code: {}, position: {}. Text before bad "
+ "char: \"{}\"",
+- m_iOvector[1], m_iOvector[0],
++ errbuf, offset,
+ m_subject.substr(startPos, m_iOvector[0] - startPos + 1));
+ else
+ CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character, error code: {}, position: {}",
+- m_iOvector[1], m_iOvector[0]);
++ errbuf, offset);
+ return -1;
+ }
+- case PCRE_ERROR_BADUTF8_OFFSET:
++ case PCRE2_ERROR_BADUTFOFFSET:
+ CLog::Log(LOGERROR, "PCRE: Offset is pointing to the middle of UTF-8 character");
+ return -1;
+
+@@ -423,7 +434,7 @@
+ {
+ int c = -1;
+ if (m_re)
+- pcre_fullinfo(m_re, NULL, PCRE_INFO_CAPTURECOUNT, &c);
++ pcre2_pattern_info(m_re, PCRE2_INFO_CAPTURECOUNT, &c);
+ return c;
+ }
+
+@@ -524,7 +535,7 @@
+ bool CRegExp::GetNamedSubPattern(const char* strName, std::string& strMatch) const
+ {
+ strMatch.clear();
+- int iSub = pcre_get_stringnumber(m_re, strName);
++ int iSub = pcre2_substring_number_from_name(m_re, reinterpret_cast<PCRE2_SPTR>(strName));
+ if (!IsValidSubNumber(iSub))
+ return false;
+ strMatch = GetMatch(iSub);
+@@ -533,7 +544,7 @@
+
+ int CRegExp::GetNamedSubPatternNumber(const char* strName) const
+ {
+- return pcre_get_stringnumber(m_re, strName);
++ return pcre2_substring_number_from_name(m_re, reinterpret_cast<PCRE2_SPTR>(strName));
+ }
+
+ void CRegExp::DumpOvector(int iLog /* = LOGDEBUG */)
+@@ -558,23 +569,21 @@
+ {
+ if (m_re)
+ {
+- pcre_free(m_re);
++ pcre2_code_free(m_re);
+ m_re = NULL;
+ }
+
+- if (m_sd)
++ if (m_ctxt)
+ {
+- pcre_free_study(m_sd);
+- m_sd = NULL;
++ pcre2_match_context_free(m_ctxt);
++ m_ctxt = NULL;
+ }
+
+-#ifdef PCRE_HAS_JIT_CODE
+ if (m_jitStack)
+ {
+- pcre_jit_stack_free(m_jitStack);
++ pcre2_jit_stack_free(m_jitStack);
+ m_jitStack = NULL;
+ }
+-#endif
+ }
+
+ inline bool CRegExp::IsValidSubNumber(int iSub) const
+@@ -587,7 +596,7 @@
+ {
+ if (m_Utf8Supported == -1)
+ {
+- if (pcre_config(PCRE_CONFIG_UTF8, &m_Utf8Supported) != 0)
++ if (pcre2_config(PCRE2_CONFIG_UNICODE, &m_Utf8Supported) < 0)
+ m_Utf8Supported = 0;
+ }
+
+@@ -596,13 +605,11 @@
+
+ bool CRegExp::AreUnicodePropertiesSupported(void)
+ {
+-#if defined(PCRE_CONFIG_UNICODE_PROPERTIES) && PCRE_UCP != 0
+ if (m_UcpSupported == -1)
+ {
+- if (pcre_config(PCRE_CONFIG_UNICODE_PROPERTIES, &m_UcpSupported) != 0)
++ if (pcre2_config(PCRE2_CONFIG_UNICODE, &m_UcpSupported) < 0)
+ m_UcpSupported = 0;
+ }
+-#endif
+
+ return m_UcpSupported == 1;
+ }
+@@ -625,13 +632,13 @@
+
+ if (!utf8FullSupport)
+ {
++ char ver[24];
++
++ pcre2_config(PCRE2_CONFIG_VERSION, ver);
+ CLog::Log(LOGINFO,
+- "Consider installing PCRE lib version 8.10 or later with enabled Unicode properties "
++ "Consider installing PCRE lib version 10.10 or later with enabled Unicode properties "
+ "and UTF-8 support. Your PCRE lib version: {}",
+- PCRE::pcre_version());
+-#if PCRE_UCP == 0
+- CLog::Log(LOGINFO, "You will need to rebuild XBMC after PCRE lib update.");
+-#endif
++ ver);
+ }
+
+ return utf8FullSupport;
+@@ -641,9 +648,7 @@
+ {
+ if (m_JitSupported == -1)
+ {
+-#ifdef PCRE_HAS_JIT_CODE
+- if (pcre_config(PCRE_CONFIG_JIT, &m_JitSupported) != 0)
+-#endif
++ if (pcre2_config(PCRE2_CONFIG_JIT, &m_JitSupported) < 0)
+ m_JitSupported = 0;
+ }
+
+--- kodi-20.2+dfsg.orig/xbmc/utils/test/TestRegExp.cpp
++++ kodi-20.2+dfsg/xbmc/utils/test/TestRegExp.cpp
+@@ -30,6 +30,29 @@
+ EXPECT_EQ(-1, regex.RegFind("Test string."));
+ }
+
++TEST(TestRegExp, InvalidPattern)
++{
++ CRegExp regex;
++
++ EXPECT_FALSE(regex.RegComp("+"));
++}
++
++TEST(TestRegExp, Unicode)
++{
++ CRegExp regex;
++
++ EXPECT_TRUE(regex.RegComp("Бог!$"));
++ EXPECT_EQ(12, regex.RegFind("С нами Бог!"));
++}
++
++TEST(TestRegExp, JIT)
++{
++ CRegExp regex;
++
++ EXPECT_TRUE(regex.RegComp(".JIT.", CRegExp::StudyWithJitComp));
++ EXPECT_EQ(12, regex.RegFind("Test string, JIT-matched."));
++}
++
+ TEST(TestRegExp, GetReplaceString)
+ {
+ CRegExp regex;
diff --git a/debian/patches/workarounds/0006-loongarch.patch b/debian/patches/workarounds/0006-loongarch.patch
new file mode 100644
index 0000000..e028ae4
--- /dev/null
+++ b/debian/patches/workarounds/0006-loongarch.patch
@@ -0,0 +1,40 @@
+From 1c2c5150eb17f432a5d9ad81a97d999662a4e669 Mon Sep 17 00:00:00 2001
+From: Dandan Zhang <zhangdandan@loongson.cn>
+Date: Tue, 24 Oct 2023 07:59:42 +0000
+Subject: [PATCH] Add LoongArch support to system info
+
+---
+ xbmc/utils/SystemInfo.cpp | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/xbmc/utils/SystemInfo.cpp b/xbmc/utils/SystemInfo.cpp
+index d1e53c41bb95e..92ef021efb158 100644
+--- a/xbmc/utils/SystemInfo.cpp
++++ b/xbmc/utils/SystemInfo.cpp
+@@ -952,7 +952,7 @@ int CSysInfo::GetKernelBitness(void)
+ std::string machine(un.machine);
+ if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" ||
+ machine == "ppc64" || machine == "ppc64el" || machine == "ppc64le" || machine == "ia64" ||
+- machine == "mips64" || machine == "s390x" || machine == "riscv64" ||
++ machine == "loongarch64" || machine == "mips64" || machine == "s390x" || machine == "riscv64" ||
+ machine == "sparc64" || machine == "alpha")
+ kernelBitness = 64;
+ else
+@@ -1080,6 +1080,8 @@ const std::string& CSysInfo::GetKernelCpuFamily(void)
+ std::string machine(un.machine);
+ if (machine.compare(0, 3, "arm", 3) == 0 || machine.compare(0, 7, "aarch64", 7) == 0)
+ kernelCpuFamily = "ARM";
++ else if (machine.compare(0, 9, "loongarch", 9) == 0 || machine.compare(0, 7, "loong64", 7) == 0)
++ kernelCpuFamily = "LoongArch";
+ else if (machine.compare(0, 4, "mips", 4) == 0)
+ kernelCpuFamily = "MIPS";
+ else if (machine.compare(0, 4, "i686", 4) == 0 || machine == "i386" || machine == "amd64" || machine.compare(0, 3, "x86", 3) == 0)
+@@ -1466,6 +1468,8 @@ std::string CSysInfo::GetBuildTargetCpuFamily(void)
+ return "ARM (Thumb)";
+ #elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ return "ARM";
++#elif defined(__loongarch__)
++ return "LoongArch";
+ #elif defined(__mips__) || defined(mips) || defined(__mips)
+ return "MIPS";
+ #elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
diff --git a/debian/patches/workarounds/0007-swig.patch b/debian/patches/workarounds/0007-swig.patch
new file mode 100644
index 0000000..94fbcf9
--- /dev/null
+++ b/debian/patches/workarounds/0007-swig.patch
@@ -0,0 +1,10 @@
+diff --git a/xbmc/interfaces/swig/AddonModuleXbmcaddon.i b/xbmc/interfaces/swig/AddonModuleXbmcaddon.i
+index 6c00a1caa2..d38794c043 100644
+--- a/xbmc/interfaces/swig/AddonModuleXbmcaddon.i
++++ b/xbmc/interfaces/swig/AddonModuleXbmcaddon.i
+@@ -33,5 +33,6 @@ using namespace xbmcaddon;
+ %include "interfaces/legacy/AddonString.h"
+
+ %include "interfaces/legacy/Addon.h"
++%nodefaultctor Settings;
+ %include "interfaces/legacy/Settings.h"