summaryrefslogtreecommitdiffstats
path: root/debian
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:25 +0000
commitd2c5a3255ca77b59775a54ecb70fabc86335296a (patch)
tree348ae3ff176c7c9c9ebe9624e45a7e12bae61155 /debian
parentAdding upstream version 2:20.4+dfsg. (diff)
downloadkodi-d2c5a3255ca77b59775a54ecb70fabc86335296a.tar.xz
kodi-d2c5a3255ca77b59775a54ecb70fabc86335296a.zip
Adding debian version 2:20.4+dfsg-1.debian/2%20.4+dfsg-1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'debian')
-rw-r--r--debian/README.Debian24
-rw-r--r--debian/README.source32
-rw-r--r--debian/changelog1241
-rw-r--r--debian/control543
-rw-r--r--debian/copyright1693
-rwxr-xr-xdebian/dh-addon/dh_kodiaddon_depends148
-rw-r--r--debian/dh-addon/kodiaddon.pm7
-rw-r--r--debian/extra/arial.ttfbin0 -> 5924104 bytes
-rw-r--r--debian/from-debian-logo.svg189
-rw-r--r--debian/gbp.conf7
-rw-r--r--debian/gitlab-ci.yml3
-rw-r--r--debian/headers-check.c126
-rw-r--r--debian/kodi-addons-dev-common.README.Debian2
-rw-r--r--debian/kodi-addons-dev-common.install3
-rw-r--r--debian/kodi-addons-dev.install2
-rw-r--r--debian/kodi-addons-dev.manpages1
-rw-r--r--debian/kodi-bin.install3
-rw-r--r--debian/kodi-bin.lintian-overrides20
-rw-r--r--debian/kodi-bin.manpages1
-rw-r--r--debian/kodi-data.install11
-rw-r--r--debian/kodi-data.links7
-rw-r--r--debian/kodi-data.lintian-overrides14
-rw-r--r--debian/kodi-eventclients-common.install1
-rw-r--r--debian/kodi-eventclients-dev-common.examples1
-rw-r--r--debian/kodi-eventclients-dev-common.install1
-rw-r--r--debian/kodi-eventclients-dev.install1
-rw-r--r--debian/kodi-eventclients-kodi-send.install1
-rw-r--r--debian/kodi-eventclients-kodi-send.lintian-overrides2
-rw-r--r--debian/kodi-eventclients-kodi-send.manpages1
-rw-r--r--debian/kodi-eventclients-ps3.install2
-rw-r--r--debian/kodi-eventclients-ps3.lintian-overrides6
-rw-r--r--debian/kodi-eventclients-ps3.manpages1
-rw-r--r--debian/kodi-eventclients-python.install1
-rw-r--r--debian/kodi-eventclients-python.lintian-overrides2
-rw-r--r--debian/kodi-eventclients-wiiremote.install1
-rw-r--r--debian/kodi-eventclients-wiiremote.lintian-overrides2
-rw-r--r--debian/kodi-eventclients-wiiremote.manpages1
-rw-r--r--debian/kodi-eventclients-zeroconf.install1
-rw-r--r--debian/kodi-eventclients-zeroconf.lintian-overrides5
-rw-r--r--debian/kodi-tools-texturepacker.install1
-rw-r--r--debian/kodi-tools-texturepacker.lintian-overrides2
-rw-r--r--debian/kodi-tools-texturepacker.manpages1
-rw-r--r--debian/kodi.install4
-rw-r--r--debian/kodi.lintian-overrides5
-rw-r--r--debian/kodi.manpages2
-rw-r--r--debian/kodi.mime19
-rwxr-xr-xdebian/mergefonts.ff6
-rw-r--r--debian/not-installed7
-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
-rwxr-xr-xdebian/rules294
-rw-r--r--debian/source/format1
-rw-r--r--debian/source/include-binaries8
-rw-r--r--debian/source/options2
-rw-r--r--debian/tests/control3
-rwxr-xr-xdebian/tests/gui23
-rw-r--r--debian/upstream/metadata7
-rw-r--r--debian/upstream/signing-key.asc25
-rw-r--r--debian/watch75
-rw-r--r--debian/webinterface-default/addon.xml145
-rw-r--r--debian/webinterface-default/css/core.css794
-rw-r--r--debian/webinterface-default/css/ipad.css9
-rw-r--r--debian/webinterface-default/favicon.icobin0 -> 370070 bytes
-rw-r--r--debian/webinterface-default/icon.pngbin0 -> 6129 bytes
-rw-r--r--debian/webinterface-default/images/DefaultAlbumCover.pngbin0 -> 46672 bytes
-rw-r--r--debian/webinterface-default/images/DefaultVideo.pngbin0 -> 44464 bytes
-rw-r--r--debian/webinterface-default/images/ajax-loader.gifbin0 -> 4782 bytes
-rw-r--r--debian/webinterface-default/images/close-button.pngbin0 -> 1649 bytes
-rwxr-xr-xdebian/webinterface-default/images/remote.jpgbin0 -> 44656 bytes
-rwxr-xr-xdebian/webinterface-default/index.html73
-rwxr-xr-xdebian/webinterface-default/js/MediaLibrary.js1420
-rwxr-xr-xdebian/webinterface-default/js/NowPlayingManager.js661
-rw-r--r--debian/webinterface-default/js/json2.js492
-rw-r--r--debian/webinterface-default/js/xbmc.core.js90
-rw-r--r--debian/webinterface-default/js/xbmc.init.js27
-rw-r--r--debian/webinterface-default/js/xbmc.launcher.js46
-rw-r--r--debian/webinterface-default/js/xbmc.rpc.js49
104 files changed, 27862 insertions, 0 deletions
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..d5dda4c
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,24 @@
+Kodi from Debian
+----------------
+
+This package is different from the package you can build from the Kodi source
+you can download from https://kodi.tv (vanilla Kodi, from now on).
+
+This package uses external libraries such as libav distributed in Debian, while
+the XBMC project suggests using the libraries embedded in vanilla Kodi's source.
+
+This difference may cause problems not experienced with vanilla Kodi thus you
+are kindly asked not to report bugs directly to https://kodi.tv, but to
+Debian's Bug Tracking System (BTS):
+
+ https://bugs.debian.org/src:kodi
+
+You can get an overview of the package at Debian Tracker:
+
+ https://tracker.debian.org/kodi
+
+or ask your queestions in Debian Multimedia mailing list by e-mail:
+
+ debian-multimedia@lists.debian.org
+
+ -- Vasyl Gello <vasek.gello@gmail.com>, Fri, 13 Nov 2020 10:00:00 +0300
diff --git a/debian/README.source b/debian/README.source
new file mode 100644
index 0000000..70b0b61
--- /dev/null
+++ b/debian/README.source
@@ -0,0 +1,32 @@
+Kodi used to embed dvdread and dvdnav in its source but the developers switched to
+downloading it which is not an option for Debian source packages. The embedded
+copy of both libraries is retrieved from VideoLAN upstream by watchfile and the
+required Debian patches are applied (debian/patches/libdvd{read,nav}-*.patch).
+
+Kodi replaced the default webinterface in 17.0 beta6 but the new interface contains
+many generated files without providing the source for them. The web interface
+is temporarily reverted to the one included in beta5 to restore GPL compliance.
+
+The web interface is placed in debian patch folder (debian/webinterface-default)
+and is copied in autoconf override because quilt patches can not handle git binary
+diffs.
+
+The cdatetime-std-chrono patch set, available as upstream PR:
+
+ https://github.com/xbmc/xbmc/pull/18727
+
+is a refactored date and time conversion logic in Kodi, that fixes the totally
+broken current implementation for 32-bit architectures (i386, mipsel).
+It should have been added to 19.0 but upstream authors decided to postpond it
+until 20.0 development cycle starts. I decided to include it in Debian release
+nonetheless along with embedded date library (embedded-libdate-tz) from Howard
+Hinnant:
+
+ https://github.com/Howardhinnant/date
+
+It does not make sense to package libdate-tz separately because it is partially
+accepted as upcoming C++20 standard and eventually kodi will move to C++20 in
+20.0 or 21.0. Upstream author also expects the library to be embedded in project,
+reasoning of "poor CMake support" (see upstream issues).
+
+ -- Vasyl Gello <vasek.gello@gmail.com>, Tue, 3 Jun 2020 10:30:00 +0300
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..168cc81
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,1241 @@
+kodi (2:20.4+dfsg-1) unstable; urgency=medium
+
+ * New upstream version 20.4+dfsg
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sun, 11 Feb 2024 07:12:03 +0000
+
+kodi (2:20.3+dfsg-1) unstable; urgency=medium
+
+ * New upstream version 20.3+dfsg (Closes: #1061600)
+ * Build with APP_RENDER_SYSTEM=gles for arm platforms (Closes: #1056563)
+ * Add pcre2 patch (Closes: #1000113)
+ * Add workaround for LoongArch (Closes: #1054463)
+ * Drop obsolete font metapackages from deps (Closes: #1050052)
+ * Drop patches merged upstream
+ * Update cdatetime-std-chrono patchset
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sat, 03 Feb 2024 13:51:04 +0000
+
+kodi (2:20.2+dfsg-4) unstable; urgency=medium
+
+ * Add patch to fix crash with ffmpeg6
+ * Fix FTBFS on s390x (Closes: #1043128)
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sun, 06 Aug 2023 08:31:29 +0000
+
+kodi (2:20.2+dfsg-3) unstable; urgency=medium
+
+ * Revert language pack patches for now to land
+ in unstable
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Fri, 04 Aug 2023 23:26:08 +0000
+
+kodi (2:20.2+dfsg-2) unstable; urgency=medium
+
+ * d/gbp.conf: Drop unnecessary upstream-branch
+ * debian: switch to DEP-14 git branch naming scheme
+ * Prepare for language pack separation
+ * Separate language packs (Closes: #1042996)
+ * Fix build against FFmpeg 6.0 (Closes: #1041400)
+ * Add patch for CD/DVD playback without mount support
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Fri, 04 Aug 2023 19:25:39 +0000
+
+kodi (2:20.2+dfsg-1) unstable; urgency=medium
+
+ * New upstream version 20.2+dfsg
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 03 Jul 2023 08:50:21 +0000
+
+kodi (2:20.1+dfsg-1) unstable; urgency=medium
+
+ * New upstream version 20.1+dfsg
+ * Refresh cdatetime-std-chrono patchset
+ * Drop patches merged upstream
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sun, 12 Mar 2023 09:31:18 +0000
+
+kodi (2:20.0+dfsg-2) unstable; urgency=high
+
+ * s/gbp.conf: Switch to versioned upstream branches
+ * Fix CVE-2023-23082 (Closes: #1031048)
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 13 Feb 2023 10:43:13 +0000
+
+kodi (2:20.0+dfsg-1) unstable; urgency=medium
+
+ * New upstream version 20.0+dfsg
+ * d/rules: Add alpha and arm64 overrides
+ * Refresh ports patch
+ * Refresh libdvd patch
+ * Refresh cdatetime-std-chrono patchset
+ * Drop patches merged upstream
+ * Refresh ffmpeg5 patch
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sat, 21 Jan 2023 18:03:44 +0000
+
+kodi (2:20.0~rc2+dfsg-2) unstable; urgency=medium
+
+ * d/rules: Declare ports architectures
+ * Add patch to build on ports architectures
+ * Refresh ffmpeg5 patch
+ * Patch DoS vulnerability awaiting for CVE
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Thu, 05 Jan 2023 07:24:56 +0000
+
+kodi (2:20.0~rc2+dfsg-1) unstable; urgency=medium
+
+ * New upstream version 20.0~rc2+dfsg
+ * d/control: Add B+R on kodi-addons-dev
+ * d/control: Bump standards version (no changes required)
+ * d/copyright: Bump copyright years
+ * Drop patches merged upstream
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 19 Dec 2022 19:06:13 +0000
+
+kodi (2:20.0~rc1+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 20.0~rc1+dfsg1
+ * Drop obsolete build dependency libgl1-mesa-dev
+ * Add Breaks/Replaces to kodi-addons-dev fixing piuparts failure
+ in stable2sid
+ * Add copyright entries for embedded libdvd*
+ * Drop copyright entry for libdate-tz-embedded and several
+ other extinct paths
+ * Fix the missing manpage after move of /usr/bin/dh_kodiaddon_depends
+ * Add patches for bashisms in kodi.sh
+ * Add descriptions to workaround patches
+ * Use +dfsg instead of +dfsg1 in watchfile
+ * Add workaround patches addressing removal of EGL Chromium headerfile
+ in Mesa 22.3.0 anda regression of playlist detection in Kodi 20 RC1
+ * Refresh cdatetime patchset fixing broken test spotted during
+ reproducibility test
+ * Fix placement of zeroconf wrapper
+ * Drop old NEWS entry about systemd integration
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Tue, 13 Dec 2022 15:04:25 +0000
+
+kodi (2:20.0~beta1+dfsg1-2) unstable; urgency=medium
+
+ * d/rules: Add installable dummy file to kodi/system
+ * Add workaround to fix crossbuilds
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Wed, 09 Nov 2022 21:16:07 +0000
+
+kodi (2:20.0~beta1+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 20.0~beta1+dfsg1
+ * Refresh cdatetime patchset
+ * Refresh kodi and libdvdread patchsets
+ * Refresh ffmpeg5 patchset
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Tue, 08 Nov 2022 19:44:52 +0000
+
+kodi (2:20.0~alpha3+git20221015.976c5c8+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 20.0~alpha3+git20221015.976c5c8+dfsg1
+ * Ship firewalld files (Closes: #1015956)
+ * Bump standards version
+ * Fix lintian warnings
+ * Use system howardhinnant-date
+ * d/watch: Switch to git tags instead of webpage scraping
+ * d/watch: Temporary switch libdvd* to master branch
+ * Refresh kodi patchset
+ * Drop libdvdnav patchset
+ * Drop workaround patches merged upstream
+ * Refresh cdatetime-std-chrono patchset
+ * workarounds: Add libdvd* callback support (Closes: #1020452)
+ * workarounds: refresh ffmpeg5 patch (Closes: #1016925)
+ * d/rules: Follow on upstream buildsystem changes
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sat, 15 Oct 2022 16:54:13 +0000
+
+kodi (2:20.0~alpha2+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 20.0~alpha2+dfsg1
+ * d/copyright fixes:
+ + Keep COMPONENT-VERSION files in tools/depends/target
+ + Remove useless versioncheck addon (Closes: #1015163)
+ + Bump copyright years
+ * Remove obsoleted patches and update the rest:
+ + Add patch fixing color conversion test (merged)
+ + Add patch for overriding local tarball checksum (merged)
+ + Add patch for ffmpeg 5 support (not merged) (Closes: #1004612)
+ + Add patch for proper fix of EDL rounding (merged)
+ + Add patch for building against libfmt 9.0.0 (merged) (Closes: #1014542)
+ * d/rules fixes:
+ + Don't rely on versionfile parsing
+ * + Supply SHA256 hashes for libdvd*
+ * Upload to unstable.
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Wed, 03 Aug 2022 14:24:22 +0000
+
+kodi (2:20.0~alpha1+dfsg1-2) experimental; urgency=medium
+
+ * Fix failing tests and restore timezone picker (Closes:‌ #1011241)
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Thu, 19 May 2022 15:08:49 +0000
+
+kodi (2:20.0~alpha1+dfsg1-1) experimental; urgency=medium
+
+ * New upstream version 20.0~alpha1+dfsg1
+ * Leave only language packs in repo-resources-embedded
+ * Branch out experimental
+ * Restrict watchfile to next Debian stable codename
+ * Add pipewire to build-dependencies
+ * Refresh Kodi patches
+ * Add workaround patches
+ * Refresh cdatetime-std-chrono patches
+ * Refresh headers-check.c
+ * Use system RapidJSON
+ * Bump libdate-tz C++ standard to 17 and enable StringView back
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 16 May 2022 06:45:07 +0000
+
+kodi (2:19.4+dfsg2-1) unstable; urgency=medium
+
+ * New upstream version 19.4+dfsg2
+ + Ship language packs for offline use (Closes: #1009278)
+ + Add copyright entry for language pack tarball
+ * Build libdate-tz as external build-dependency of Kodi.
+ * d/rules: Annotate actions and replace direct source patching
+ within rulefile with proper compiler definition
+ * d/rules:‌ Fix improper cleanup of generated embedded tarballs
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Fri, 29 Apr 2022 07:10:17 +0000
+
+kodi (2:19.4+dfsg1-2) unstable; urgency=medium
+
+ * Upload to unstable
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 09 Apr 2022 12:51:13 +0000
+
+kodi (2:19.4+dfsg1-1) experimental; urgency=medium
+
+ * New upstream version 19.4+dfsg1
+ * Drop system/certs and .github from tarball
+ * Refresh patches
+ * Reorder package structure to allow installation on
+ foreign architectures (Closes: #996627)
+ * Move TexturePacker to a separate binary package
+ * Fix Lintian warnings.
+ * Build native prerequisites before configure if cross-building
+ * Version the build-dependency on gtest to >= 1.10.0~
+ * d/watch: Switch libdate-tz to tags
+ * Put KodiConfig.cmake in arch-dep place (Closes: #999482)
+ * Add patches for cross build support
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 21 Mar 2022 17:48:21 +0000
+
+kodi (2:19.3+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 19.3+dfsg1
+ * Refresh patches
+ * Add proper WITH_ARCH for amd64, i386 and riscv64
+ * Disable DebugFission by CMake configuration option instead of patch
+ * Use the system kissft
+ * kodi-bin: Remove unused lintian override
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sun, 24 Oct 2021 14:45:29 +0000
+
+kodi (2:19.2+dfsg1-2) unstable; urgency=medium
+
+ * Fix AppStream warning with metainfo file
+ * Make build reproducible
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Wed, 13 Oct 2021 22:12:35 +0000
+
+kodi (2:19.2+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 19.2+dfsg1
+ * Restrict watchfile to current Debian stable codename
+ * Bump standard version
+ * Refresh patches
+ * Install package metainfo
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Wed, 06 Oct 2021 10:59:56 +0000
+
+kodi (2:19.1+dfsg2-2) unstable; urgency=medium
+
+ * Add runtime locale test and fallback (Closes: #989814)
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Thu, 24 Jun 2021 20:44:30 +0000
+
+kodi (2:19.1+dfsg2-1) unstable; urgency=medium
+
+ * New upstream version 19.1+dfsg2
+ * Merge 'bullseye' branch into master and revert patches
+ related to newer libdvdread and branch isolation.
+ * Drop the patch downgrading ffmpeg to fit buster-backports.
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 07 Jun 2021 16:42:08 +0200
+
+kodi (2:19.0+dfsg1-2) unstable; urgency=medium
+
+ * Add missing font dependency (Closes: #985497)
+ * Fix github ref
+ * Branch out bullseye
+ * Refresh cdatetime-std-chrono patch (Closes: #984682)
+ * libdate-tz: Fix crash for early local times and USE_OS_TZDB
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sun, 30 May 2021 04:57:39 +0000
+
+kodi (2:19.1+dfsg1-1~exp0) experimental; urgency=medium
+
+ * New upstream version 19.1+dfsg1
+ * Add missing font dependency (Closes: #985497)
+ * Fix links to Github releases in watchfile
+ * Refresh patches
+ * Prepare for official backport
+ * Refresh cdatetime-std-chrono patch (Closes: #984682)
+ * libdate-tz: Fix crash for early local times and USE_OS_TZDB
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sat, 15 May 2021 21:15:26 +0000
+
+kodi (2:19.0+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 19.0+dfsg1
+ * Declare proper Debian release splitting kodi-addons-dev (Closes: #980846)
+ * Detect and honor big-endian architectures
+ * Refresh cdatetime-std-chrono patch
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Fri, 19 Feb 2021 00:02:45 +0000
+
+kodi (2:19.0~rc1+git20210119.8c761c4+dfsg1-2) experimental; urgency=medium
+
+ * Team upload.
+ * Re-instate the kodi-addons-dev-common package split.
+
+ -- Mattia Rizzolo <mattia@debian.org> Wed, 20 Jan 2021 01:56:06 +0100
+
+kodi (2:19.0~rc1+git20210119.8c761c4+dfsg1-1) unstable; urgency=medium
+
+ [ Vasyl Gello ]
+ * New upstream version 19.0~rc1+dfsg1 and 19.0~rc1+git20210119.8c761c4+dfsg1.
+ + Fix possible crash on shutdown. (Closes: #979367)
+ + New upstream snapshot fixes non-DFSG free JPEG image
+ which led to FTP master rejection
+ * d/watch: mangle capital letter abbrevs too
+ * Delete patches merged upstream
+ * Refresh cdatateime-std-chrono patch
+ * copyright: Reflect skin.estuary picture replacement
+
+ [ Mattia Rizzolo ]
+ * Temporary revert the kodi-addons-dev-common split to avoid NEW fo
+ this upload (the previous version was rejected from NEW).
+ * Upload to unstable.
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Tue, 19 Jan 2021 20:02:25 +0000
+
+kodi (2:19.0~beta2+dfsg1-6) experimental; urgency=medium
+
+ [ Mattia Rizzolo ]
+ * d/control: define dh-sequence-kodiaddon as a versioned Provides
+
+ [ Vasyl Gello ]
+ * Check the existence of kodi include dir's full path
+ in dh_kodiaddon_depends.
+ * Refresh cdatetime-std-chrono patch. (Closes: #980038)
+ * Introduce a kodi-addons-dev-common package to hold all the arch-indep
+ files.
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Tue, 12 Jan 2021 02:57:10 +0000
+
+kodi (2:19.0~beta2+dfsg1-5) unstable; urgency=medium
+
+ * Team upload.
+ * Fix arch:all build after turning kodi-addons-dev arch:any.
+
+ -- Mattia Rizzolo <mattia@debian.org> Sun, 10 Jan 2021 20:46:04 +0100
+
+kodi (2:19.0~beta2+dfsg1-4) unstable; urgency=medium
+
+ * Team upload.
+
+ [ Vasyl Gello ]
+ * d/watch: Declare bare git snapshot naming scheme since v20.
+
+ [ Mattia Rizzolo ]
+ * Mark kodi-addons-dev as Arch:any.
+ This is to prevent addons from building in architectures where Kodi
+ is not available.
+
+ -- Mattia Rizzolo <mattia@debian.org> Sun, 10 Jan 2021 15:57:30 +0100
+
+kodi (2:19.0~beta2+dfsg1-3) unstable; urgency=medium
+
+ * d/upstream/metadata: Add Bug-Submit URL
+ * Drop libenca-dev build dependency
+ * dh_kodiaddon_depends: Provide a sliding window
+ for ABI bumps to avoid unnecessary addon rebuild
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sun, 03 Jan 2021 08:45:17 +0000
+
+kodi (2:19.0~beta2+dfsg1-2) unstable; urgency=medium
+
+ [ Vasyl Gello ]
+ * Upload to unstable (Closes: #926922)
+ * d/watch: Simplify dversionmangle
+ * Update Vcs-Git/Vcs-Browser for the move to 'kodi-media-center' subgroup
+ * Restore libfreetype6-dev alternative build-dep for buster.
+
+ [ Tobias Frost ]
+ * Add CVE-ids to past changelog entries when they have been fixed,
+ as per security team policy.
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Tue, 22 Dec 2020 10:01:37 +0000
+
+kodi (2:19.0~beta2+dfsg1-1) experimental; urgency=medium
+
+ * New upstream version 19.0~beta2+dfsg1
+ + Fix kodi-ps3remote crash. (Closes: #962929)
+ * Build a new kodi-eventclients-zeroconf package with the zeroconf scripts
+ * Add breaks and replaces to kodi-eventclients-ps3
+ * Backport a patch from Kodi 20 to replace CDateTime with cdate-std-chrono.
+ Fixes test failures on multiple architectures.
+ + Add libdate-tz as component tarball
+ * Update d/copyright
+ * Use the system-wide tzdata.
+ * Drop clang-format from build-deps, not actually needed during the build
+ * Mark kodi-addons-dev as indep
+ * Fix arch-indepedent-only build (Closes: #976303)
+ * Bump Standards-Version to 4.5.1 (no changes required)
+ * Fix lintian warnings
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Sat, 19 Dec 2020 04:55:34 +0000
+
+kodi (2:19.0~alpha3+dfsg1-1) experimental; urgency=medium
+
+ [ Vasyl Gello ]
+ * New upstream version 19.0~alpha3+dfsg1
+ + This version fully builds with Python 3. (Closes: 936805)
+ * Refresh patches
+ * Revert "temporary disable tests parallelism", in 19.0 tests run fine
+ * Bump the required ffmepg version
+ * Don't use clang-format on x32
+ * Drop the build-dep on lib-p8-platform
+ * Update the watchfile
+ * Don't run dwz during the build as it fails on us
+ * Add several Multi-Arch fields, from the M-A hinter
+ * Fix several lintian warnings
+ * Add default webinterface as debian include (this is temporary until the
+ packaging of the new interface is completed)
+ * Retrieve embedded libdvdnav and libdvdread from upstream
+ * debian/header-check.c: Add new and delete removed headers
+ * Refresh copyright information
+ * Provide 'dh-sequence-kodiaddon' virtual package
+ * Drop kodi-x11, kodi-wayland and kodi-gbm packages, now the same binary
+ can handle all window systems
+
+ [ Adrian Bunk ]
+ * Rename SSE patch file (Closes: #961856)
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Tue, 17 Nov 2020 13:04:40 +0100
+
+kodi (2:18.8+dfsg1-2) unstable; urgency=medium
+
+ * temporary disable tests parallelism (Closes: #968339)
+ * add myself to Uploaders
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Thu, 03 Sep 2020 01:48:09 +0300
+
+kodi (2:18.8+dfsg1-1) unstable; urgency=medium
+
+ [ Vasyl Gello ]
+ * Team upload
+ * fix circular dependency hell (Closes: #963865)
+ * Depend on libudfread & shairplay
+ * remove unnecessary dlopen-ed shlibdeps from kodi-bin
+ * remove dummy remnant
+
+ [ Jonas Smedegaard ]
+ * Remove myself from Uploaders
+
+ [ Vasyl Gello ]
+ * drop unnecessary cpluff component
+ * New upstream version 18.8+dfsg1 (Closes: #964591)
+ * Refresh patches
+
+ [ Adrian Bunk ]
+ * Don't enable SSE in i386 builds (Closes: #961856)
+
+ [ Balint Reczey ]
+ * debian/README.source: Clarify that dvdread and dvdnav are patched for Kodi
+ * debian/gbp.conf: Set components
+ * debian/README.source: Gbp now exports components properly
+ * New upstream version 18.7.1+dfsg1
+
+ -- Vasyl Gello <vasek.gello@gmail.com> Mon, 03 Aug 2020 02:20:44 +0000
+
+kodi (2:18.7+dfsg1-1) unstable; urgency=medium
+
+ * Team upload
+ * New upstream version 18.7
+ * Refresh patches
+ * Update additional tarballs to libdvdnav 6.1.0 and libdvdread 6.1.1
+
+ -- Balint Reczey <rbalint@ubuntu.com> Tue, 26 May 2020 16:14:53 +0200
+
+kodi (2:18.6+dfsg1-2) unstable; urgency=medium
+
+ * Team upload
+ * Drop pycryptodome support with Python 2
+ * debian/control: Revert kodi-data to depend on python-pil instead of python3-pil.
+ Kodi runs addons with Python 2 and many addons would break without PIL.
+ * Run kodi-ps3remote with Python 3
+ * Apply suggestion to debian/rules
+ * debian/rules: Ship debug symbols for all kodi binaries and drop -dbg migration
+ (Closes: #955134)
+
+ -- Balint Reczey <rbalint@ubuntu.com> Fri, 03 Apr 2020 13:48:30 +0200
+
+kodi (2:18.6+dfsg1-1) unstable; urgency=medium
+
+ * Upload to unstable
+
+ -- Balint Reczey <rbalint@ubuntu.com> Sat, 21 Mar 2020 16:39:01 +0100
+
+kodi (2:18.6+dfsg1-1~exp1) experimental; urgency=medium
+
+ * test: Disable flaky TestMassEvent.General test, too
+
+ -- Balint Reczey <rbalint@ubuntu.com> Thu, 19 Mar 2020 21:41:10 +0100
+
+kodi (2:18.6+dfsg1-1~exp0) experimental; urgency=medium
+
+ * debian/rules: Set -DWITH_ARCH=... and -DNEON=False explicitly for armel,
+ armhf, ppc64el and s390x. Those Debian architectures are not detected
+ properly by upstream CMake scripts.
+ * Build-depend on python2-dev
+ * Reposition "from debian" on the main screen to adjust to the changed new
+ skin
+ * Don't strip service.xbmc.versioncheck addon from upstream tarball
+ * New upstream version 18.6
+ * Ship versioncheck addon disabled
+ * Disable long-time failing test and fix others instead of ignoring all test
+ results
+ * Drop obsolete patches
+ * Drop 03-privacy.patch and add TODO item about resurrecting it properly
+ * Refresh patches
+ * Run kodi-send with Python 3
+ * test: Disable flaky TestMassEvent.Polling test
+ * Fix ppc64el build
+ * Fix arch detection
+ * test: Skip checking errno against ENOEN to not FTBFS on armhf
+
+ -- Balint Reczey <rbalint@ubuntu.com> Wed, 18 Mar 2020 12:09:23 +0100
+
+kodi (2:18.5+dfsg1-1~exp0) experimental; urgency=medium
+
+ [ Balint Reczey ]
+ * Team upload
+ * debian/tests/gui: Wait for kodi to start, not just a fixed 5 seconds
+ * Refresh patches
+ * Update extra libdvdnav and libdvdread original tarballs
+ * debian/copyright_hints: Update hints with new files
+ * debian/control: Tidy up using cme fix
+ * debian/gitlab-ci.yml: Add Salsa CI configuration
+ * Refresh build dependencies based on upstream's packaging
+ * Ship separate packages for X11, Wayland and GBM
+ * debian/rules:
+ - Rewrite to use cmake and build separate binaries.
+ Thanks to Wolfgang Schupp
+ - Extend override_dh_clean
+ - Pass -lphtread to linker by default
+ - Refactor building Debianized artifacts
+ - Run tests for each build dir
+ - Clean up separate build directories
+ - clean media/Fonts/arial.ttf, too
+ - Call dh with python2
+ - Drop obsolete --parallel dh argument
+ - Adjust to new component names
+ * debian/copyright: Fix open issues and Lintian complaints
+ * Let upstream's build system build the embedded libdvdnav and libread
+ libraries
+ * Install TexturePacker to /usr/bin/
+ * Ignore failing tests for now
+ * Update which files are shipped by which package
+ * Switch hashing implementation from OpenSSL to Libgcrypt
+ * Build without OpenSSL
+ * Depend on unhinted Roboto font (Closes: #922950)
+ * Fix FTBFS of kodi-test by not #include-ing excluded Windows-only header
+ * Fix dh_kodiaddon_depends to get API versions the new way
+ * Refresh file list shipped by kodi-addons-dev
+ * Remove myself from Uploaders
+ * Work around FTBFS caused by fontforge SEGFAULT
+ * New upstream version 18.5+dfsg1 (CVE-2017-5982)
+ * debian/gbp.conf: Drop postexport hook.
+ It is obsoleted by storing all components in git.
+ * Stop marking kodi-bin Multiarch: same since it now ships TexturePacker
+ * Stop marking kodi-gbm, kodi-x11 and kodi-wayland Multiarch: same.
+ They depend on kodi-bin which is not Multiarch: same either.
+ * Build kodi-wayland, too
+
+ [ Rechi ]
+ * debian/copyright: update Files-Excluded
+ - libUPnP was updated
+ - UnrarXLib was split into an addon
+ - Visual Studio files were removed
+ - windows platform specific code location changed
+ * drop unused Build-depends
+ * add new Build-depends
+ * debian/rules: remove unused DEB vars
+ * debian/rules: remove non existent cmake options
+ - egl is a required dependency on linux so there is no ENABLE_EGL switch
+ - x11 isn't an optional dependency for gbm so there is no ENABLE_X11 switch
+ - vdpau is only an optional dependency for x11, so ENABLE_VDPAU switch
+ exists only for x11
+ * debian/rules: update dlopened libs
+ libass, libbluray, libcec, libcurl and libnfs are no longer dlopend
+ * fix libmicrohttpd linking
+ * debian/rules: install kodi-x11 as last package
+ last installed package defines the fallback windowing in bin/kodi
+ * fix depends syntax
+ * fix test target build system dependency
+
+ -- Balint Reczey <rbalint@ubuntu.com> Fri, 24 Jan 2020 08:20:58 +0100
+
+kodi (2:17.6+dfsg1-4) unstable; urgency=medium
+
+ * debian/tests/gui: Print xvfb-run error to stdout
+ * debian/tests/gui: Fix test by starting 24bit screen and waiting for
+ kodi's start
+ * Drop transitional packages
+ * Fix FTBFS when _G_va_list is not defined, like on Ubuntu 19.04
+
+ -- Balint Reczey <rbalint@ubuntu.com> Tue, 20 Nov 2018 12:21:00 +0100
+
+kodi (2:17.6+dfsg1-3) unstable; urgency=medium
+
+ [ Balint Reczey ]
+ * debian/NEWS: Fix language to keep lintian happy
+ * Add simple autopkgtest
+
+ [ Gianfranco Costamagna ]
+ * Upstream fixes for new ffmpeg:
+ debian/patches/19f28e88a5dfed82e9844f69210f7c045f18ca8e.patch
+ debian/patches/c7e2acc1b2ee781af6e14d80da0edd0aa23bdf03.patch
+ (Closes: #888383, #912994)
+
+ -- Balint Reczey <rbalint@ubuntu.com> Fri, 16 Nov 2018 01:42:36 +0100
+
+kodi (2:17.6+dfsg1-2) unstable; urgency=medium
+
+ [ Ondřej Nový ]
+ * d/copyright: Use https protocol in Format field
+ * d/control: Set Vcs-* to salsa.debian.org
+
+ [ Rechi ]
+ * Drop build dependencies on libboost-*
+ Boost isn't required since v17.
+
+ [ Felipe Sateler ]
+ * Change maintainer address to debian-multimedia@lists.debian.org
+
+ [ Balint Reczey ]
+ * Ship only Kodi desktop session without the xbmc.desktop duplicate link
+ * Stop build-depending on versioned GCC, this workaround is not needed
+ anymore (Closes: #892399)
+ Thanks to Adrian Bunk who also proposed a patch and a delayed NMU.
+ * Stop shipping kodi.service
+ Please use a lightweight display manager set up to autologin instead.
+ * Add d/source.lintian-overrides to d/source/lintian-overrides
+ to keep lintian happy
+
+ [ Rolf Leggewie ]
+ * demote hard font dependencies from Depends to Recommends (LP: #1685918)
+
+ -- Balint Reczey <rbalint@ubuntu.com> Sat, 28 Apr 2018 20:50:46 +0200
+
+kodi (2:17.6+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 17.6 (with accumulated changes from 17.4 and 17.5):
+ - https://kodi.tv/article/kodi-v174-final-just-bunch-fixes
+ - https://kodi.tv/article/kodi-v175-final-another-bunch-fixes
+ - https://kodi.tv/article/kodi-v176-final-very-last
+ * Refresh patches
+ * Depend and build-depend on python-pil instead of python-imaging
+ (Closes: #866437)
+ * Apply workaround for scratchy sound with FFmpeg 3.4.
+ Thanks to James Cowgill for triaging and Stefan Hachmann for the patch
+ (Closes: #881536)
+ * Fix starting kodi on arm64 (Closes: #876876)
+ * Change URLs to use HTTPS in README.Debian and drop feed URL from
+ description (Closes: #860484)
+
+ -- Balint Reczey <rbalint@ubuntu.com> Tue, 26 Dec 2017 01:06:49 +0100
+
+kodi (2:17.3+dfsg1-5) unstable; urgency=medium
+
+ * Build-depend on fontforge-nox (not fontforge.
+ * Use rsvg-convert to generate branding logo.
+ Build-depend on (not inkscape).
+ * Fix remove repository feed from kodi-data.
+ Tighten kodi-repository-kodi breaks+replaces on kodi-data.
+
+ -- Jonas Smedegaard <dr@jones.dk> Wed, 11 Oct 2017 02:37:25 +0200
+
+kodi (2:17.3+dfsg1-4) unstable; urgency=medium
+
+ * Update package relations:
+ + Fix have kodi-repository-kodi conflict against and replace older
+ kodi/kodi-data/xbmc.
+ Fixes bug#877960. Thanks to Andreas Beckmann.
+ + Fix have kodi-repository-kodi conflict against (not break) virtual
+ kodi-repository.
+ + Fix have package kodi-repository-kodi depend on ${misc:Depends}.
+ + Stop build-depend on dh-autoreconf autotools-dev: Handled by
+ debhelper.
+ * Rewrite copyright info, with major coverage improvements.
+ * Add maintainer script copyright-check to source.
+ * Update git-buildpackage config:
+ + Add usage comment.
+ + Filter any .git* file.
+ * Override lintian regarding license in License-Reference field.
+ See bug#786450.
+ * Override lintian regarding obsolete-url-in-packaging: License grant
+ must be included verbatim.
+
+ -- Jonas Smedegaard <dr@jones.dk> Tue, 10 Oct 2017 22:46:23 +0200
+
+kodi (2:17.3+dfsg1-3) unstable; urgency=medium
+
+ * Fix set core configure options to all instances.
+ * Set configure options --disable-maintainer-mode
+ --disable-silent-rules (unused now but might be added in future).
+ * Fix avoid custom build rule silencing.
+ * Fix FTBFS: Build-depend on and build with gcc-6 and g++-6 (until
+ bug#841556 is fixed).
+ Closes: Bug#853476. Thanks to Matthias Klose and Bálint Réczey.
+ * Modernize Vcs-* fields:
+ + Use https (not git or http) protocol.
+ + Use git (not gitweb) in path.
+ * Use https in Homepage URL.
+ * Add myself as uploader.
+ * Add patch 18 to support omitting addon repository feed.
+ * Ship repository feed in separate package kodi-repository-kodi.
+ Closes: Bug#877656 (or arguably only lowers severity of it, but
+ since we cannot track that versioned better to close and track the
+ lowered severity separately).
+
+ -- Jonas Smedegaard <dr@jones.dk> Wed, 04 Oct 2017 03:53:10 +0200
+
+kodi (2:17.3+dfsg1-2) unstable; urgency=medium
+
+ * Upload to unstable
+ * Stop shipping kodi.service as an example since it is now shipped
+ as a regular .service file
+
+ -- Balint Reczey <rbalint@ubuntu.com> Thu, 29 Jun 2017 05:15:29 +0200
+
+kodi (2:17.3+dfsg1-1) experimental; urgency=medium
+
+ * New upstream version 17.3
+ See: https://kodi.tv/article/kodi-v173-minor-bug-fix-and-security-release
+ * Refresh patches
+
+ -- Balint Reczey <rbalint@ubuntu.com> Fri, 26 May 2017 18:44:04 +0200
+
+kodi (2:17.1+dfsg1-3) unstable; urgency=medium
+
+ * Fix zip file directory traversal vulnerability (CVE-2017-8314)
+ (Closes: #863230)
+
+ -- Balint Reczey <rbalint@ubuntu.com> Sat, 27 May 2017 00:50:34 +0200
+
+kodi (2:17.1+dfsg1-2) unstable; urgency=medium
+
+ * Upload to unstable
+
+ -- Balint Reczey <rbalint@ubuntu.com> Thu, 13 Apr 2017 23:49:12 +0200
+
+kodi (2:17.1+dfsg1-1) experimental; urgency=medium
+
+ * Depend on fonts-noto-mono package which contains NotoMono-Regular.ttf
+ (Closes: #856668)
+ * Fix FTBFS on alpha by not using Intel assempler code (Closes: #856815)
+ * Imported Upstream version 17.1+dfsg1
+ See: https://kodi.tv/kodi-v17-1-krypton
+ * Update my Uploader email address to my Ubuntu one
+ * Fix extract-components target in d/rules
+
+ -- Balint Reczey <rbalint@ubuntu.com> Sun, 02 Apr 2017 11:01:21 +0200
+
+kodi (2:17.1~rc1+dfsg1-1) experimental; urgency=medium
+
+ * Imported Upstream version 17.1~rc1+dfsg1
+ * Refresh patches
+
+ -- Balint Reczey <balint@balintreczey.hu> Tue, 28 Feb 2017 02:21:54 +0100
+
+kodi (2:17.0+dfsg1-3) unstable; urgency=medium
+
+ * Ship disabled systemd service file (Closes: #854985, #801886)
+ * Use systemd service file recommended on upstream wiki
+
+ -- Balint Reczey <balint@balintreczey.hu> Wed, 22 Feb 2017 01:28:43 +0100
+
+kodi (2:17.0+dfsg1-2) unstable; urgency=medium
+
+ * Apply patches to additional tarballs using quilt series file
+ (Closes: #854463)
+
+ -- Balint Reczey <balint@balintreczey.hu> Thu, 09 Feb 2017 10:25:18 +0100
+
+kodi (2:17.0+dfsg1-1) experimental; urgency=medium
+
+ * Imported Upstream version 17.0+dfsg1
+ See: https://kodi.tv/kodi17/
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 06 Feb 2017 19:33:08 +0100
+
+kodi (2:17.0~rc4+dfsg1-1) experimental; urgency=medium
+
+ * Imported Upstream version 17.0~rc4+dfsg1
+ See: https://kodi.tv/kodi-v17-0-krypton-release-candidate-4/
+ * Refresh patches
+ * Remove spaces around '=' in service file.
+ Also convert service file to use Unix line ending
+
+ -- Balint Reczey <balint@balintreczey.hu> Sun, 05 Feb 2017 01:27:27 +0100
+
+kodi (2:17.0~rc3+dfsg1-2) unstable; urgency=medium
+
+ * Bump debhelper compat level to 10.
+ Remove --parallel and --with autoreconf since those are now defaults
+ * Build-depend on libcec-dev >= 4~ (Closes: #852216)
+ * Fix make clean in debian/rules
+ * Use real dlopen() for opening libdvdcss (Closes: #784578)
+
+ -- Balint Reczey <balint@balintreczey.hu> Wed, 25 Jan 2017 21:23:22 +0100
+
+kodi (2:17.0~rc3+dfsg1-1) unstable; urgency=medium
+
+ * Make default lirc device match lirc package's default (Closes: #850467)
+ * Stop passing ./configure flags which are not supported anymore
+ * Update fonts and font dependencies
+ * Imported Upstream version 17.0~rc3+dfsg1
+ See: https://kodi.tv/kodi-v17-0-krypton-release-candidate-3/
+ * Refresh patches
+ * Fix FTBFS due to missing AEDefines_override.h
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 16 Jan 2017 21:41:03 +0100
+
+kodi (2:17.0~rc2+dfsg1-1) unstable; urgency=medium
+
+ * Imported Upstream version 17.0~beta7+dfsg1
+ See: https://kodi.tv/kodi-v17-krypton-the-last-beta-7/
+ * Refresh patches
+ * Use NotoSans-Regular font from fonts-noto-hinted package
+ * Imported Upstream version 17.0~rc2+dfsg1
+ See: https://kodi.tv/kodi-v17-krypton-release-candidate-1/
+ * Fix positioning of the "from debian" text next to KODI logo
+
+ -- Balint Reczey <balint@balintreczey.hu> Fri, 06 Jan 2017 18:39:14 +0100
+
+kodi (2:17.0~beta6+dfsg1-1) unstable; urgency=medium
+
+ * Drop new built-in webinterface addon from beta 6.
+ The addon contains many generated files without source. It will
+ be packaged separately.
+ * Imported Upstream version 17.0~beta6+dfsg1
+ * Refresh patches
+ * Revert the webinterface.default addon to old web interface from beta5
+ * Update lintian override about MediaLibrary.js to match new packaging
+ * Revert to using libcec 3.1 until libcec4 becomes available in Debian
+
+ -- Balint Reczey <balint@balintreczey.hu> Thu, 22 Dec 2016 02:00:46 +0100
+
+kodi (2:17.0~beta5+dfsg1-4) unstable; urgency=medium
+
+ * Stop using different epoch for transitional xbmc packages
+ * Use 2: epoch for kodi packages (Closes: #844541)
+ * Handle original tarballs properly when epoch is present in the package version
+
+ -- Balint Reczey <balint@balintreczey.hu> Thu, 17 Nov 2016 15:42:51 +0100
+
+kodi (17.0~beta5+dfsg1-3) unstable; urgency=medium
+
+ * Generate addon API versions for architecture-specific builds only
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 14 Nov 2016 16:37:40 +0100
+
+kodi (17.0~beta5+dfsg1-2) unstable; urgency=medium
+
+ [ Balint Reczey ]
+ * Stop providing xbmc-eventclients-j2me.
+ This was a transitional package originally, but Kodi stopped shipping
+ J2ME client. (Closes: #843169)
+ * Transition to depending on fonts-anonymous-pro.
+ Also accept ttf-anonymous-pro instead to support older releases
+ * Generate additional original source tarball names from current version
+ * Use more absolute paths in d/rules
+
+ [ Tobias Grimm ]
+ * Updated tarball versions in `debian/rules extract-components`
+ * Provide virtual packages for addon APIs and install a debhelper addon
+ to enable KODI addon packages to depend on these virtual packages.
+ * Standards-Version: 3.9.8
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 14 Nov 2016 11:32:17 +0100
+
+kodi (17.0~beta5+dfsg1-1) unstable; urgency=medium
+
+ [ Lukas Rechberger ]
+ * Make kodi depend on kodi-data of same version (Closes: #841937)
+
+ [ Balint Reczey ]
+ * Fix Kodi's FFmpeg API usage for allocating and freeing AVFrame
+ (Closes: #831591)
+ * New upstream beta release 17.0 Beta 5
+ See: https://kodi.tv/kodi-v17-krypton-beta-5/
+ * Refresh patches
+ * Run tests only for architecture dependent package builds
+ * Use Anonymous Pro font from ttf-anonymous-pro instead of from texlive-fonts-extra
+ (Closes: #842717)
+
+ -- Balint Reczey <balint@balintreczey.hu> Wed, 02 Nov 2016 21:53:39 +0100
+
+kodi (17.0~beta4+dfsg1-2) experimental; urgency=medium
+
+ * Generate version of xbmc transitional packages dynamically
+ (Closes: #842141)
+ * Check installed headers only during architecture-specific builds
+
+ -- Balint Reczey <balint@balintreczey.hu> Wed, 26 Oct 2016 13:37:42 +0200
+
+kodi (17.0~beta4+dfsg1-1) experimental; urgency=medium
+
+ * Ship additional headers referenced from already shipped ones
+ * Ensure all needed development headers are installed
+ * Use patches from libdvdread package
+ * Build-depend on libssh-gcrypt-dev instead of libssh-dev to avoid
+ transitive linking to libssl
+ * Build-depend on libgcrypt-dev instead of libssl-dev
+ * New upstream beta release 17.0 Beta 4
+ See: https://kodi.tv/kodi-v17-krypton-beta-4/
+ * Refresh patches
+ * Stop building with OpenSSL
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 10 Oct 2016 23:00:38 +0200
+
+kodi (17.0~beta3+dfsg1-1) experimental; urgency=medium
+
+ * New upstream beta release 17.0 Beta 3
+ See: https://kodi.tv/kodi-v17-krypton-beta-3/
+ * Refresh patches
+ * Use OpenGL ES and no VAAPI/VDAPU on arm64.
+ This fixes FTBFS on arm64
+ * Enable Google Tests on armel and mips, too.
+ The test results don't make the build fail anyway
+ * Build-depend on valgrind on amd64 and i386.
+ This makes tests use Valgrind on those popular and fast architectures
+ * Fix FTBFS on armel/armhf
+ * Properly set hardening options
+ * Re-enable TestSortUtils test
+ * Fix building tests
+ * Build-depend on default-libmysqlclient-dev to keep Lintian happy
+ * Adjust "from debian" logo placement
+
+ -- Balint Reczey <balint@balintreczey.hu> Fri, 07 Oct 2016 10:27:11 +0200
+
+kodi (17.0~beta2+dfsg1-1) experimental; urgency=medium
+
+ * New upstream beta release 17.0 Beta 2
+ See: https://kodi.tv/kodi-v17-krypton-beta-2/
+ * Drop build-depenency on libjasper-dev (Closes: 818201)
+ * Adjust packaging to new skin's font requirements.
+ Add dependency on fonts-lato and texlive-fonts-extra, drop
+ dependency on fonts-roboto-hinted, make symlinks to new
+ fonts instead of shipping them.
+ * Add transitional dummy packages from xbmc
+ * Drop obsoleted dependencies and patches
+ * Silence Lintian complaining for missing misc:Depends of transitional
+ packages
+ * Ignore failed clean of components without Lintian noticing
+ * Build-depend on liblcms2-dev for Little CMS support
+
+ -- Balint Reczey <balint@balintreczey.hu> Sat, 24 Sep 2016 14:57:31 +0200
+
+kodi (17.0~alpha3+dfsg1-1) experimental; urgency=medium
+
+ [ Lukas Rechberger ]
+ * Skip timestamps to make build reproducible (Closes: #825285)
+
+ [ Balint Reczey ]
+ * Build depend on groovy instead of groovy2 (Closes: #824187)
+ * Recommend kodi-visualization-spectrum
+ * New upstream alpha release 17.0 Alpha 2
+ See: https://kodi.tv/kodi-v17-krypton-alpha-2/
+ * Stop excluding paths from orig tarball which are already removed
+ * Refresh patches and changed paths in d/rules
+ * Fix FTBFS due to format string warnings
+ * Disable building DVDCSS
+ * Build-depend on libbluray-dev (>= 0.7.0) to match version required by ./configure
+ * Derive embedded libdvdnav and libdvdread from additional original tarballs
+ * Fix building with Google Test
+ * Ignore failing tests but run them with Valgrind when it is available
+ * Update d/copyright
+ * Add information about multiple orig tarballs
+ * New upstream alpha release 17.0 Alpha 3
+ See: https://kodi.tv/kodi-v17-krypton-alpha-3/
+ * Refresh patches
+ * Really revert patches to embedded dvdnav and dvdread copies in clean target
+ * Drop J2ME client support in sync with upstream
+ * Don't crash on missing version checker addon
+ * Position "from Debian" image better on new skin
+ * Drop symlinks to fonts used in the replaced skin
+
+ -- Balint Reczey <balint@balintreczey.hu> Fri, 05 Aug 2016 01:23:53 +0200
+
+kodi (16.1+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 16.1
+ See https://kodi.tv/kodi-16-1-jarvis-mark-xvi
+
+ -- Balint Reczey <balint@balintreczey.hu> Sun, 24 Apr 2016 15:51:42 +0200
+
+kodi (16.1~rc2+dfsg1-1) unstable; urgency=medium
+
+ * New upstream release candidate 16.1 RC2
+ See: https://kodi.tv/kodi-16-1-jarvis-release-candidate-2/
+ * Fix GetKernelBitness() on s390x to fix FTBFS on that arch
+ * Skip tests on armel and mips to prevent FTBFS
+ * Stop excluding files from upstream tarball which are already removed
+ upstream
+ * Don't modify GetVersionShort(), return package version in GetVersion()
+ instead
+
+ -- Balint Reczey <balint@balintreczey.hu> Tue, 29 Mar 2016 10:52:16 +0200
+
+kodi (16.0+dfsg1-3) unstable; urgency=medium
+
+ * Fix system info for s390x and arm64 architectures
+ * Skip tests failing on buildds to fix FTBFS on most ports
+ * Skip sorting test failing in the test code on armhf and armel
+
+ -- Balint Reczey <balint@balintreczey.hu> Fri, 25 Mar 2016 10:49:06 +0100
+
+kodi (16.0+dfsg1-2) unstable; urgency=medium
+
+ * Enable Google Tests at build-time
+ * Fix user agent string and related test
+ * Disable bindnow hardening option because it breaks playing DVDs
+
+ -- Balint Reczey <balint@balintreczey.hu> Tue, 15 Mar 2016 15:08:54 +0100
+
+kodi (16.0+dfsg1-1) unstable; urgency=medium
+
+ * New upstream version 16.0
+ See https://kodi.tv/kodi-16-0-jarvis-mark-xvi/
+ * Refresh patches
+ * Change build-dependency on libcec-platform-dev to libp8-platform-dev.
+ The library has been renamed
+ * Build-depend on libcec-dev (>= 3.1) which is built with p8-platform
+ * Stop shipping menu file
+ * Override false positive lintian warning about missing JavaScript source
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 22 Feb 2016 23:08:18 +0100
+
+kodi (16.0~rc3+dfsg2-1) experimental; urgency=medium
+
+ * New upstream release candidate 16.0 RC3
+ (Closes: #792299) (CVE-2015-3885)
+ See http://kodi.tv/kodi-16-0-jarvis-release-candidate-3/
+ * Refresh patches
+ * Don't use libcadec, wait for the codec to be included in FFmpeg instead
+ * Drop libhdhomerun-dev build dependency
+ * Add libcrossguid-dev and uuid-dev as build-dependencies
+ * Drop workarounds for screensavers dropped by upstream
+ * Use hinted Roboto fonts adapting to their new location in filesystem
+ * Enable PIE
+ * Make C-Pluff build with PIC only enabling PIE-enabled build to succeed
+ * Update debian/copyright for 16.0 RC3 changes
+
+ -- Balint Reczey <balint@balintreczey.hu> Sat, 13 Feb 2016 22:53:08 +0100
+
+kodi (15.2+dfsg1-3) unstable; urgency=medium
+
+ * Disable PIE due to build failure on amd64
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 25 Jan 2016 10:35:02 +0100
+
+kodi (15.2+dfsg1-2) unstable; urgency=medium
+
+ * Fix 03-privacy.patch to disable rss feed
+ * Disable debugging by default (Closes: #777041)
+ * Build-depend on fonts-droid-fallback instead of obsoleted fonts-droid
+ (Closes: #804695)
+ * Enable all hardening options during build
+ * Migrate to libpng-dev build-dependency (Closes: #810186)
+
+ -- Balint Reczey <balint@balintreczey.hu> Sun, 24 Jan 2016 10:55:08 +0100
+
+kodi (15.2+dfsg1-1) unstable; urgency=medium
+
+ [ Balint Reczey ]
+ * Skip handling migration to automatic debug packages when it is not supported by debhelper
+ * Update debian/watch to not skip part of oddly named versions
+ * New upstream release 15.2
+ See http://kodi.tv/kodi-15-2-isengard-final-release/
+
+ [ Andreas Cadhalpun ]
+ * Fix FTBFS with upcoming FFmpeg 2.9 release (Closes: #803831)
+
+ -- Balint Reczey <balint@balintreczey.hu> Fri, 06 Nov 2015 18:42:22 +0400
+
+kodi (15.2~rc3+dfsg1-1) unstable; urgency=medium
+
+ * New upstream release candidate 15.2 RC3
+ See http://kodi.tv/kodi-15-2-isengard-release-candidate-3
+
+ -- Balint Reczey <balint@balintreczey.hu> Tue, 06 Oct 2015 12:06:19 +0200
+
+kodi (15.2~rc1+dfsg1-2) unstable; urgency=medium
+
+ * Make Breaks/Replaces relation to xbmc packages versioned.
+ This lets dummy transition-only xbmc packages coexist with kodi.
+ Also remove Provides/Replaces relations remaining from xbmc packaging.
+ (Closes: #799823)
+ * Split architecture-indepedent data to kodi-data.
+ Only the kodi and kodi-standalone binaries are kept in kodi package which is
+ now Architecture:any since it contains the architecture-specific path to
+ kodi.bin. (Closes: #799979)
+
+ -- Balint Reczey <balint@balintreczey.hu> Sat, 26 Sep 2015 15:13:36 +0200
+
+kodi (15.2~rc1+dfsg1-1) unstable; urgency=medium
+
+ [ Balint Reczey ]
+ * Really fix s390x build
+ * Call ./configure through dh_auto_configure
+ * Convert to package to use multiarch library dirs
+ * Stop shipping kodi-dbg and disable debugging during build.
+ This is a preparation for the automatically generated debug packages
+ and switching to FFmpeg resulted less reported crashes to BTS.
+ Kodi-dbg packages used quite a big space on mirrors and weren't used
+ frequently.
+ * Make kodi-addons-dev Replaces/Breaks: kodi-bin (<= 15.1+dfsg1-3)
+ This is needed to manage moving .cmake files to to kodi-addons-dev.
+ * Migrate from kodi-dbg to ddebs properly.
+ Thanks to Mattia Rizzolo
+ * Make kodi-addons-dev Architecture: any
+ * Simplify debian/rules using exporting DH_OPTIONS
+ * New upstream release candidate 15.2 RC1
+ See http://kodi.tv/kodi-15-2-isengard-release-candidate-2/
+ * Refresh patches
+
+ [ Tobias Grimm ]
+ * Fixed file name of patches/09-use-correct-ftgl.h
+ * Install *.cmake files in kodi-addons-dev
+
+ [ Mattia Rizzolo ]
+ * debian/rules: correct use of `echo`
+
+ -- Balint Reczey <balint@balintreczey.hu> Mon, 21 Sep 2015 10:38:00 +0200
+
+kodi (15.1+dfsg1-3) unstable; urgency=medium
+
+ * Make configure more verbose when building on architecture not supported
+ by upstream
+ * Fix FTBFS on aarch64 (Closes: #796532)
+ * Don't use NEON on arm/armhf. (Closes: #797204)
+ * Fix build on s390x
+
+ -- Balint Reczey <balint@balintreczey.hu> Sun, 30 Aug 2015 10:20:07 +0200
+
+kodi (15.1+dfsg1-2) unstable; urgency=medium
+
+ * Don't stop configuration for unknown architectures
+ * Drop unknown ./configure options
+ * Continue configuration for unknown arches even further (Closes: #790635)
+ * Link with libatomic to fix FTBFS on powerpc and some other architectures
+ * Fix broken symlinks to DejaVuSans font files
+
+ -- Balint Reczey <balint@balintreczey.hu> Tue, 18 Aug 2015 23:51:14 +0200
+
+kodi (15.1+dfsg1-1) unstable; urgency=medium
+
+ * New upstream release 15.0
+ See http://www.kodi.tv/kodi-15-0-isengard-one-release-to-rule-them-all/
+ * Depend on libav.*-dev provided by src:ffmpeg
+ * Recommend udisks2 instead of udisks.
+ Udisks has been removed from unstable but support for udisks2 is not
+ implemented yet.
+ * Ship patch helping Jessie backports
+ * Refresh patches
+ * Eliminate __DATE__ macros from source to make build more reproducible
+ * Build-depend on libsdl2.*-dev instead of libsdl.*-dev
+ * Build-depend on libgif-dev
+ * Drop obsoleted fix of installed header's include paths
+ * Refresh include file list included by kodi-addons-dev
+ * Build depend on libcec (>= 3)
+ * Build-depend on groovy2 instead of groovy
+ * Sort build dependencies
+ * Fix packaging repository URL
+ * Remove removed files from debian/copyright
+ * Fix filenames with spaces in debian/copyright
+ * Ship TexturePacker in kodi-addons-dev in /usr/lib/kodi
+ * Build depend on libcec-platform-dev
+ * Stop using embedded gnulib modules in rsxs screensaver (Closes: #795813)
+ * Add missing copyright paragraphs to d/copyright
+ * Stop marking files as excluded which are removed from upstream tarball
+ already
+ * Bump standards version to 3.9.6
+ * New upstream release 15.1
+ See http://kodi.tv/kodi-15-1-isengard-maintenance-release/
+ * Move TexturePacker to kodi-bin
+ * Stop building TexturePacker statically
+
+ -- Balint Reczey <balint@balintreczey.hu> Tue, 18 Aug 2015 14:16:59 +0200
+
+kodi (14.2+dfsg1-2) unstable; urgency=medium
+
+ * Recommend udisks (Closes: #787293)
+
+ -- Balint Reczey <balint@balintreczey.hu> Thu, 04 Jun 2015 09:39:30 +0200
+
+kodi (14.2+dfsg1-1) experimental; urgency=medium
+
+ * Fix addon shared library naming on MIPS
+ * Drop non-machine-readable parts of debian/copyright
+ * Generate machine-readable debian/copyright for all files
+ * Clean up generated debian/copyright
+ * Remove non-free files in d/copyright
+ * Fix debian/watch file
+ * Update debian/copyright with new removals
+ * Drop get-orig-source target from d/rules.
+ Use uscan for generating repacked original tarball.
+ * Imported Upstream version 14.1+dfsg1
+ http://kodi.tv/kodi-14-1-helix-bugfix-release
+ * Copyright updates
+ * Use avformat_flush(), it is now exported by FFmpeg
+ * Stop using files omitted from DFSG-free tarballs
+ * Sort filenames omitted from original tarball
+ * Omit embedded Boost header from original tarball
+ * Download original tarball from GitHub
+ * Imported Upstream version 14.2+dfsg1
+ http://kodi.tv/kodi-14-2-helix-the-final-translation
+
+ -- Balint Reczey <balint@balintreczey.hu> Sat, 17 Jan 2015 00:30:36 +0100
+
+kodi (14.0+dfsg1-1) experimental; urgency=medium
+
+ * New upstream release 14.0 (CVE-2013-1438)
+ See http://kodi.tv/kodi-14-0-helix-unwinds
+ * Refresh patches
+ * Generate "from Debian" logo from SVG
+
+ -- Balint Reczey <balint@balintreczey.hu> Fri, 02 Jan 2015 13:19:52 +0100
+
+kodi (14.0~beta1+dfsg1-1) experimental; urgency=medium
+
+ * First packaged Kodi version based on xbmc 2:13.2+dfsg1-4
+ See http://kodi.tv/introducing-kodi-14 for the rationale behind the
+ name change (Closes: #767180)
+ * Use packaged FFmpeg
+ * Stop using internal ffmpeg functions
+ * Build-depend on libcec-dev >= 2.2
+ * Add upstream patches for fixing suspend and adding MIPS support
+
+ -- Balint Reczey <balint@balintreczey.hu> Sat, 08 Nov 2014 11:14:40 +0100
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..e6327fe
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,543 @@
+Source: kodi
+Priority: optional
+Section: video
+Maintainer: Debian Multimedia Maintainers <debian-multimedia@lists.debian.org>
+Uploaders: Vasyl Gello <vasek.gello@gmail.com>
+Build-Depends: autoconf,
+ automake,
+ autopoint,
+ cmake,
+ curl,
+ debhelper-compat (= 13),
+ default-jre:native,
+ default-libmysqlclient-dev,
+ dh-python,
+ doxygen,
+ fontforge-nox,
+ fonts-dejavu-core,
+ fonts-droid-fallback,
+ gawk,
+ gettext,
+ gperf,
+ groovy:native,
+ libhowardhinnant-date-dev,
+ libasound2-dev | libasound-dev,
+ libass-dev,
+ libavahi-client-dev,
+ libavahi-common-dev,
+ libavcodec-dev (>= 7:4.2.2),
+ libavfilter-dev (>= 7:4.2.2),
+ libavformat-dev (>= 7:4.2.2),
+ libavutil-dev (>= 7:4.2.2),
+ libbluetooth-dev,
+ libbluray-dev (>= 1:0.7.0),
+ libcap-dev,
+ libcdio-dev,
+ libcec-dev (>= 4~),
+ libcommons-lang-java:native,
+ libcrossguid-dev,
+ libcurl4-gnutls-dev | libcurl-dev,
+ libcwiid-dev [linux-any],
+ libdav1d-dev (>= 0.7.0),
+ libdbus-1-dev,
+ libdrm-dev,
+# libdvdnav-dev, use patched embedded version
+# libdvdread-dev, use patched embedded version
+ libegl1-mesa-dev,
+ libflatbuffers-dev,
+ libfmt-dev (>= 6.1.2),
+ libfontconfig-dev,
+ libfreetype-dev | libfreetype6-dev,
+ libfribidi-dev,
+ libfstrcmp-dev,
+ libgbm-dev,
+ libgcrypt-dev,
+ libgif-dev,
+ libgif-dev:native,
+ libgl-dev,
+ libglew-dev,
+ libglu1-mesa-dev | libglu-dev,
+ libgnutls28-dev,
+ libgpg-error-dev,
+ libgtest-dev (>= 1.10.0~),
+ libinput-dev,
+ libiso9660-dev,
+ libiso9660++-dev,
+ libjpeg-dev,
+ libjpeg-dev:native,
+ libkissfft-dev (>= 131.1.0~),
+ liblcms2-dev,
+ liblirc-dev | liblircclient-dev,
+ libltdl-dev,
+ liblzo2-dev,
+ liblzo2-dev:native,
+ libmicrohttpd-dev,
+ libnfs-dev,
+ libomxil-bellagio-dev [armel],
+ libpcre2-dev,
+ libpipewire-0.3-dev,
+ libplist-dev,
+ libpng-dev,
+ libpng-dev:native,
+ libpostproc-dev (>= 7:4.2.2),
+ libpulse-dev,
+ libpython3-dev,
+ librsvg2-bin,
+ libshairplay-dev (>= 0.9.0~),
+ libsmbclient-dev,
+ libsndio-dev,
+ libspdlog-dev (>= 1:1.5.0),
+ libsqlite3-dev,
+ libswresample-dev (>= 7:4.2.2),
+ libswscale-dev (>= 7:4.2.2),
+ libtag1-dev (>= 1.8),
+ libtinyxml-dev,
+ libtool,
+ libudev-dev,
+ libudfread-dev (>= 1.0.0),
+ libva-dev,
+ libvdpau-dev,
+ libxkbcommon-dev,
+ libxrandr-dev,
+ libxslt1-dev,
+ libxt-dev,
+ nasm [i386],
+ python3-dev:any,
+ rapidjson-dev,
+ swig,
+ unzip,
+ uuid-dev,
+ valgrind [amd64 i386],
+ wayland-protocols,
+ waylandpp-dev,
+ zip,
+ zlib1g-dev
+Standards-Version: 4.6.2
+Rules-Requires-Root: no
+Vcs-Browser: https://salsa.debian.org/multimedia-team/kodi-media-center/kodi
+Vcs-Git: https://salsa.debian.org/multimedia-team/kodi-media-center/kodi.git -b debian/sid
+Homepage: https://kodi.tv/
+
+Package: kodi
+Architecture: any
+Depends: kodi-bin (>= ${source:Version}),
+ kodi-bin (<< ${source:Version}.1~),
+ kodi-data (>= ${source:Version}),
+ ${misc:Depends}
+Recommends: kodi-repository-kodi | kodi-repository,
+ kodi-visualization-spectrum
+Breaks: xbmc (<< 2:13.2+dfsg1-5~), ${kodi:APIBREAKS}
+Provides: ${kodi:APIPROVIDES}
+Replaces: xbmc (<< 2:13.2+dfsg1-5~)
+Description: Open Source Home Theatre (executable binaries)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package contains the kodi binaries.
+
+Package: kodi-data
+Architecture: all
+Multi-Arch: foreign
+Depends: mesa-utils,
+ x11-utils,
+ libjs-jquery,
+ libjs-iscroll,
+ ${misc:Depends}
+Recommends: fonts-noto-core,
+ fonts-noto-mono,
+ fonts-roboto-unhinted,
+Suggests: fonts-noto-ui-core,
+Breaks: xbmc (<< 2:13.2+dfsg1-5~)
+Replaces: xbmc (<< 2:13.2+dfsg1-5~)
+Description: Open Source Home Theatre (arch-independent data package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package contains all the architecture independent data needed to have a
+ working Kodi.
+
+Package: kodi-bin
+Architecture: any
+Depends: python3-pil,
+ python3-pycryptodome,
+ ${python3:Depends},
+ ${dlopenlibs:Depends},
+ ${shlibs:Depends},
+ ${misc:Depends},
+Recommends: ${shlibs:Recommends},
+ udisks2
+Pre-Depends: ${misc:Pre-Depends}
+Breaks: xbmc-bin (<< 2:13.2+dfsg1-5~)
+Conflicts: libgl1-mesa-swx11
+Replaces: xbmc-bin (<< 2:13.2+dfsg1-5~)
+Description: Open Source Home Theatre (architecture-dependent files)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package contains the binaries needed to have a working Kodi.
+
+Package: kodi-eventclients-common
+Architecture: all
+Multi-Arch: foreign
+Depends: ${misc:Depends}
+Description: Open Source Home Theatre (Event Client Common package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package is the common package for Kodi Event Client.
+
+Package: kodi-eventclients-python
+Architecture: any
+Depends: kodi-eventclients-common (= ${source:Version}),
+ python3-bluez,
+ ${python3:Depends},
+ ${misc:Depends}
+Breaks: kodi-eventclients-common (<< 2:19.4+dfsg1-1~)
+Replaces: kodi-eventclients-common (<< 2:19.4+dfsg1-1~)
+Description: Open Source Home Theatre (Event Client Python package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package is the virtual Python package for Kodi Event Client.
+
+Package: kodi-eventclients-dev-common
+Architecture: all
+Multi-Arch: foreign
+Section: libdevel
+Depends: ${misc:Depends}
+Breaks: kodi-eventclients-dev (<< 2:19.4+dfsg1-1~)
+Replaces: kodi-eventclients-dev (<< 2:19.4+dfsg1-1~)
+Description: Open Source Home Theatre (Event Client common dev package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This is the common development package for Kodi Event Client.
+
+Package: kodi-eventclients-dev
+Architecture: any
+Section: libdevel
+Depends: kodi-eventclients-dev-common (= ${source:Version}),
+ kodi-eventclients-python (= ${binary:Version}),
+ ${misc:Depends}
+Description: Open Source Home Theatre (Event Client virtual dev package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This is the virtual development package for Kodi Event Client.
+
+Package: kodi-eventclients-wiiremote
+Architecture: any
+Depends: kodi-eventclients-python (= ${binary:Version}),
+ ${python3:Depends},
+ ${shlibs:Depends},
+ ${misc:Depends}
+Description: Open Source Home Theatre (Event Client WII Remote support package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package is the Wii Remote client package for Kodi.
+
+Package: kodi-eventclients-ps3
+Architecture: any
+Depends: kodi-eventclients-python (= ${binary:Version}),
+ python3-pyudev,
+ python3-usb,
+ ${python3:Depends},
+ ${misc:Depends}
+Breaks: kodi-eventclients-common (<< 2:19.0~alpha3+dfsg1-1~)
+Replaces: kodi-eventclients-common (<< 2:19.0~alpha3+dfsg1-1~)
+Description: Open Source Home Theatre (Event Client PS3 package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package is the PS3 package for Kodi Event Client.
+
+Package: kodi-eventclients-kodi-send
+Architecture: any
+Depends: kodi-eventclients-python (= ${binary:Version}),
+ ${python3:Depends},
+ ${misc:Depends}
+Description: Open Source Home Theatre (Event Client Kodi-SEND package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package is the Kodi-SEND package for Kodi Event Client.
+
+Package: kodi-eventclients-zeroconf
+Architecture: any
+Depends: kodi-eventclients-python (= ${binary:Version}),
+ python3-avahi,
+ python3-dbus,
+ python3-gi,
+ ${python3:Depends},
+ ${misc:Depends}
+Breaks: kodi-eventclients-common (<< 2:19.0~alpha3+dfsg1-1~)
+Replaces: kodi-eventclients-common (<< 2:19.0~alpha3+dfsg1-1~)
+Description: Open Source Home Theatre (Event Client ZeroConf script package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package is the ZeroConf script for Kodi Event Client.
+
+Package: kodi-addons-dev-common
+Architecture: all
+Multi-Arch: foreign
+Section: libdevel
+Depends: ${misc:Depends}
+Breaks: kodi-addons-dev (<< 2:19.4+dfsg1-1~)
+Replaces: kodi-addons-dev (<< 2:19.4+dfsg1-1~)
+Description: Open Source Home Theatre (architecture-independent addon development package)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This is the development package for Kodi Addons.
+ .
+ This package contains independent headers for building addons without the need
+ to keep a whole Kodi source tree.
+
+Package: kodi-addons-dev
+Section: libdevel
+Architecture: any
+Multi-Arch: same
+Provides: dh-sequence-kodiaddon (= ${binary:Version})
+Depends: kodi-addons-dev-common (= ${source:Version}),
+ ${misc:Depends}
+Breaks: kodi-addons-dev-common (<< 2:20.0~rc1+dfsg1-1~)
+Replaces: kodi-addons-dev-common (<< 2:20.0~rc1+dfsg1-1~)
+Description: Open Source Home Theatre (addon development package)
+ This package is a dummy package used in conjunction with kodi-addons-dev
+ to detect if a binary addon should be built for given Debian architecture.
+ .
+ The alternative to introducing this package is marking kodi-addons-dev back
+ as arch:any, but this makes Lintian and multi-arch hinter noisy.
+ .
+ If Kodi upstream starts shipping architecture-specific development libraries
+ again, they will land in this package.
+
+Package: kodi-repository-kodi
+Architecture: all
+Multi-Arch: foreign
+Depends: ${misc:Depends}
+Enhances: kodi
+Breaks: kodi-data (<< 2:17.3+dfsg1-5~),
+ xbmc (<< 2:13.2+dfsg1-5~)
+Conflicts: kodi-repository
+Provides: kodi-repository
+Replaces: kodi-repository,
+ kodi-data (<< 2:17.3+dfsg1-5~),
+ xbmc (<< 2:13.2+dfsg1-5~)
+Description: Open Source Home Theatre (official addons repository feed)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package enables download of addons via the official Kodi
+ repository feed.
+
+Package: kodi-tools-texturepacker
+Architecture: any
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Breaks: kodi-bin (<< 2:19.4+dfsg1-1~)
+Replaces: kodi-bin (<< 2:19.4+dfsg1-1~)
+Description: Open Source Home Theatre (TexturePacker skin development tool)
+ Kodi, formerly known as XBMC is an award winning free and
+ open source software media-player and entertainment hub for all your digital
+ media. Kodi is available for Linux, Mac OS X (Leopard, Tiger and Apple TV)
+ and Microsoft Windows, as well as the original Xbox game console. Created in
+ 2003 by a group of like minded programmers, Kodi is a non-profit project run
+ and developed by volunteers located around the world. More than 50 software
+ developers have contributed to Kodi, and 100-plus translators have worked to
+ expand its reach, making it available in more than 30 languages.
+ .
+ While Kodi functions very well as a standard media player application for
+ your computer, it has been designed to be the perfect companion for your
+ HTPC. Supporting an almost endless range of remote controls, and combined
+ with its beautiful interface and powerful skinning engine, Kodi feels very
+ natural to use from the couch and is the ideal solution for your home
+ theater. Once installed, your computer will become a fully functional
+ multimedia jukebox.
+ .
+ This package contains TexturePacker, the tool for Kodi skin developers.
+ End users don't need this package to run Kodi.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..adf4c04
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,1693 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Kodi
+Source: http://kodi.tv/
+Files-Excluded: .github
+ addons/service.xbmc.versioncheck
+ addons/skin.estuary/fonts/*.ttf
+ addons/skin.estouchy/fonts/*.ttf
+ addons/webinterface.default
+ lib/libUPnP/Neptune/ThirdParty/zlib-*
+ lib/libUPnP/Platinum/Build/Targets/mipsel-psp-linux
+ lib/libUPnP/Platinum/Build/Targets/x86-microsoft-win32-vs2010
+ lib/libUPnP/Platinum/ThirdParty/log4net
+ lib/win32
+ media/Fonts/arial.ttf
+ project/BuildDependencies
+ project/Win32BuildSetup
+ system/certs
+ tools/android
+ tools/codegenerator/groovy
+ tools/darwin
+ tools/depends/target
+ tools/EventClients/Clients/OSXRemote
+ tools/windows
+ xbmc/platform/android
+ xbmc/platform/darwin
+ xbmc/platform/win10
+ xbmc/platform/win32
+Files-Included: tools/depends/target/*-VERSION
+Files-Excluded-repo-resources-embedded: .github
+ *
+Files-Included-repo-resources-embedded: resource.language.*
+
+Files: *
+Copyright:
+ 2005-2023, Team Kodi <http://kodi.tv/>
+ the authors of ffmpeg and xvid
+ the authors of libPlatinum <http://www.plutinosoft.com/blog/category/platinum/>
+ 1997-2009, Sam Lantinga <slouken@libsdl.org>
+ 2000-2004, Convergence (integrated media) GmbH
+ 2001-2009, The world wide DirectFB Open Source Community <http://directfb.org/>
+ 2002, Albert Griscti-Soler (RUNTiME)
+ 2002, Phil Burr (d7o3g4q)
+ 2002,2007, Erwin Beckers (Frodo)
+ 2003, The Joker / Avalaunch team
+ 2003-2017, Team XBMC <http://xbmc.org/>
+ 2003,2006-2007, Ralf Baechle <ralf@linux-mips.org>
+ 2005, Free Software Foundation, Inc.
+ 2006, Joakim Plate (elupus)
+ 2006, Sylvain Rebaud (c0diq)
+ 2007, Jonathan Marshall (jcmarshall)
+ 2007, Roee Vulkan (vulkanr)
+ 2007-2008, Anoop Menon (d4rk)
+ 2007-2008, Eric Steil III
+ 2007,2011, Tobias Arrskog (topfs) <https://github.com/topfs2>
+ 2008, University Heidelberg
+ 2008, Wladimir J. van der Laan
+ 2010, Team Boxee <http://www.boxee.tv/>
+ 2012, Denis Yantarev
+ 2015-2016, Lauri Mylläri
+ 2016-2018, Chris Browet
+ 2016, 2017, Tobias Kortkamp <t@tobik.me>
+ 2018, Tyler Szabo
+License-Grant:
+ This Program 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, or (at your option) any later version.
+License: GPL-2+
+Comment:
+ Copyright holders "Team XboxMediaCenter" and "Team XBMC" assumed
+ to be equal to "Team Kodi".
+
+Files: addons/skin.estouchy/*
+Copyright: Team Kodi <http://kodi.tv/>
+License-Grant:
+ This work is licensed
+ under the Creative Commons Attribution-Share Alike 3.0 United States
+ License.
+License: CC-BY-SA-3.0~us
+Comment:
+ Copyright information missing from source files.
+ Copyright holder assumed to be main project author.
+ License assumed from license grant
+ located in <addons/skin.estouchy/LICENSE.txt>.
+
+Files: addons/skin.estuary/*
+Copyright: phil65 & Piers (Team Kodi)
+License-Grant:
+ Estuary code work is licensed
+ under GNU General Public License (GPL), Version 2.0
+License: GPL-2
+
+Files: addons/skin.estuary/*.jpg
+ addons/skin.estuary/*.png
+Copyright: phil65 & Piers (Team Kodi)
+License-Grant:
+ Estuary Artwork is licensed
+ under a Creative Commons Attribution-ShareAlike 4.0 Unported License.
+License: CC-BY-SA-4.0
+
+Files: addons/skin.estuary/extras/home-images/movie.jpg
+ addons/skin.estuary/extras/home-images/music.jpg
+Copyright: UNKNOWN
+License-Grant:
+ movie.jpg: https://creativecommons.org/publicdomain/zero/1.0/legalcode
+ music.jpg: https://creativecommons.org/publicdomain/zero/1.0/legalcode
+License: CC0-1.0
+Comment:
+ Missing copyright and licensing information in source file.
+ License assumed from license grant
+ located in addons/skin.estuary/extras/home-images/LICENSE.
+
+Files: addons/skin.estuary/media/windows/subtitles/flags/*
+Copyright: 2013, Go Squared Ltd. <http:www.gosquared.com/>
+License: Expat
+Comment:
+ Copyright and license assumed from
+ <addons/skin.estuary/media/windows/subtitles/flags/LICENSE.txt>.
+
+Files: cmake/modules/extra/ECMEnableSanitizers.cmake
+ cmake/modules/FindLibXml2.cmake
+Copyright:
+ 2006, Alexander Neundorf <neundorf@kde.org>
+ 2006-2009, Kitware, Inc.
+ 2014, Mathieu Tarral <mathieu.tarral@gmail.com>
+ 2016, Team Kodi <http://kodi.tv/>
+License-Grant:
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+License: BSD-3-clause
+Comment:
+ License grant ambiguous regarding exact BSD license.
+ License assumed to be the same as clarified
+ in CMake project commit 86578ec:
+ <https://github.com/Kitware/CMake/commit/86578eccf2e82286248796bad1032cd0e3a5e1e2>.
+
+Files: lib/libUPnP/Neptune/*
+Copyright: 2002-2012, Axiomatic Systems, LLC
+License: BSD-3-clause~Axiomatic
+
+Files: lib/libUPnP/Neptune/Source/Apps/*
+ lib/libUPnP/Neptune/Source/System/*
+ lib/libUPnP/Neptune/Source/Tests/*
+Copyright:
+ 2001-2014, Gilles Boccon-Gibod <bok@bok.net>
+ 2005-2010, Axiomatic Systems, LLC.
+License: BSD-3-clause~Axiomatic
+Comment:
+ Licensing information missing from source files.
+ License assumed from <lib/libUPnP/Neptune/LICENSE>,
+ and from Gilles Boccon-Gibod being former president for Axiomatic
+ according to
+ <https://www.corporationwiki.com/California/Los-Altos/gilles-boccon-gibod/43430379.aspx>
+
+Files: lib/libUPnP/Neptune/ThirdParty/axTLS/*
+Copyright: 2007, Cameron Rich
+License: BSD-3-clause~axTLS
+
+Files: lib/libUPnP/Platinum/*
+Copyright: 2004-2012, Plutinosoft, LLC
+License-Grant:
+ This program 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.
+License: GPL-2+
+Comment:
+ Licensing information missing from some source files:
+ <lib/libUPnP/Platinum/Source/Platform/Android/module/platinum/jni/platinum-jni.cpp>
+ <lib/libUPnP/Platinum/Source/Tests/MediaServerCocoaTest/main.mm>
+ License assumed to be same as related files
+ with same copyright holder.
+
+Files: lib/libUPnP/Platinum/Source/Extras/ObjectiveC/PltMediaServerObject.h
+ lib/libUPnP/Platinum/Source/Extras/ObjectiveC/PltMediaServerObject.mm
+ lib/libUPnP/Platinum/Source/Extras/ObjectiveC/PltUPnPObject.h
+ lib/libUPnP/Platinum/Source/Extras/ObjectiveC/PltUPnPObject.mm
+ lib/libUPnP/Platinum/Source/Tests/MediaServerCocoaTest/MediaServerCocoaTestController.h
+ lib/libUPnP/Platinum/Source/Tests/MediaServerCocoaTest/MediaServerCocoaTestController.mm
+Copyright: 2010-2011, Plutinosoft LLC
+License: GPL-2+
+Comment:
+ Licensing information missing from source files.
+ License assumed to be same as generally for same copyright holder.
+
+Files: libdvdnav-embedded/*
+Copyright: 2000-2001, Björn Englund
+ 2006-2007, Diego Pettenò <flameeyes@gmail.com>
+ 1989-1991, Free Software Foundation, Inc
+ 2000-2001, Håkan Hjort
+ 2000-2001, Martin Norbäck
+ 2000-2004, Rich Wareham <richwareham@users.sourceforge.net>
+ 2001-2004, the libdvdnav project
+ 2000-2007, the xine project
+ 2001-2023, The‌ VideoLAN Project
+License: GPL-2+
+
+Files: libdvdread-embedded/*
+Copyright: Apache Software Foundation
+ 2000-2004, Billy Biggs <vektor@dumbterm.net>
+ 2000-2003, Björn Englund <d4bjorn@dtek.chalmers.se>
+ 1999-2001, Christian Wolff <scarabaeus@convergence.de>
+ 2006-2007, Diego Pettenò <flameeyes@gmail.com>
+ 1989-1991, Free Software Foundation, Inc
+ 2000-2003, Håkan Hjort <d95hjort@dtek.chalmers.se>
+ 1997, Kevlin Henney
+ 2001, Rich Wareham <richwareham@users.sourceforge.net>
+ 2001-2002, Samuel Hocevar <sam@zoy.org>
+ 2000-2007, the xine project
+ 2000-2023, The VideoLAN Project
+License: GPL-2+
+
+Files: libdvdread-embedded/src/dvdread/version.h.in
+ libdvdread-embedded/src/md5.c
+ libdvdread-embedded/src/md5.h
+Copyright: 1995-2003, Free Software Foundation, Inc
+ 2004-2015, VLC authors and VideoLAN
+License: LGPL-2.1+
+
+Files: libdvdread-embedded/src/dvdread/nav_types.h
+ libdvdread-embedded/src/nav_print.c
+Copyright: 1998-1999, Eric Smith <eric@brouhaha.com>
+ 2000-2003, Håkan Hjort <d95hjort@dtek.chalmers.se>
+License: GPL-2 or GPL-3
+
+Files: repo-resources-embedded/*
+Copyright: 2005-2023, Team Kodi
+License: GPL-2+
+Comment: Translations are maintained via Kodi project on Weblate.
+
+Files: system/players/VideoPlayer/etc/fonts/conf.avail/65-fonts-persian.conf
+ system/players/VideoPlayer/etc/fonts/conf.d/65-fonts-persian.conf
+Copyright: 2005, Sharif FarsiWeb, Inc. <license@farsiweb.info>
+License: NTP~disclaimer~FarsiWeb
+
+Files: tools/depends/build-aux/config.guess
+ tools/depends/build-aux/config.sub
+Copyright: 1992-2016, Free Software Foundation, Inc.
+License-Grant:
+ This file 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 3 of the License, or (at your option) any later version.
+License: GPL-3+ with Autoconf exception
+ As a special exception to the GNU General Public License,
+ if you distribute this file as part of a program
+ that contains a configuration script generated by Autoconf,
+ you may include it under the same distribution terms
+ that you use for the rest of that program.
+ This Exception is an additional permission
+ under section 7 of the GNU General Public License, version 3 ("GPLv3").
+
+Files: tools/depends/build-aux/install-sh
+Copyright: 1994, X Consortium
+License: X11
+Comment: FSF changes to this file are in the public domain.
+
+Files: tools/depends/native/TexturePacker/src/Win32/dirent.c
+ tools/depends/native/TexturePacker/src/Win32/dirent.h
+Copyright: 1997,2003, Kevlin Henney <kevlin@acm.org>, <kevlin@curbralan.com>
+License: ISC~minimal-disclaimer
+
+Files: tools/depends/native/TexturePacker/src/md5.cpp
+ tools/depends/native/TexturePacker/src/md5.h
+Copyright: None (in the public domain)
+License: public-domain
+ This code was written by Colin Plumb in 1993, no copyright is claimed.
+ This code is in the public domain; do with it what you wish.
+
+Files: tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.cpp
+ tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.h
+Copyright: 2009, Cory Fields
+License-Grant:
+ This program 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 3 of the License, or (at your option) any later version.
+License: GPL-3+
+
+Files: tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/*
+Copyright: 2006-2007, Michael Laforest (para) <thepara@gmail.com>
+License-Grant:
+ This program 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 3 of the License, or (at your option) any later version.
+License: GPL-3+
+
+Files: tools/EventClients/icons/bluetooth.png
+Copyright: 2003, Jakub Steiner (jimmac) <http://jimmac.musichall.cz/>
+License: GPL-2+
+Comment:
+ Licensing information missing from source file.
+ I've asked upstream to clarify the license
+ https://github.com/xbmc/xbmc/issues/17252
+
+Files: xbmc/addons/AudioDecoder.cpp
+ xbmc/addons/AudioDecoder.h
+Copyright: 2013, 2014 Arne Morten Kvarving (cptspiff)
+License-Grant:
+ This Program 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, or (at your option) any later version.
+License: GPL-2+
+
+Files: xbmc/addons/VFSEntry.cpp
+ xbmc/addons/VFSEntry.h
+Copyright: 2013, 2014 Arne Morten Kvarving (cptspiff)
+License-Grant:
+ This Program 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, or (at your option) any later version.
+License: GPL-2+
+
+Files: xbmc/contrib/kissfft/*
+Copyright: 2003-2010, Mark Borgerding
+License: BSD-3-clause
+
+Files: xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.cpp
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VAAPI.h
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VTB.cpp
+ xbmc/cores/VideoPlayer/DVDCodecs/Video/VTB.h
+ xbmc/cores/VideoPlayer/VideoRenderers/RenderFlags.cpp
+ xbmc/guilib/StereoscopicsManager.cpp
+ xbmc/guilib/StereoscopicsManager.h
+ xbmc/platform/linux/sse4/CopyFrame.cpp
+ xbmc/utils/ActorProtocol.cpp
+ xbmc/utils/ActorProtocol.h
+Copyright:
+ 2005-2015, Team Kodi <http://kodi.tv/>
+ 2005-2016, Team XBMC <http://xbmc.org/>
+License-Grant:
+ This library 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.
+License: LGPL-2.1+
+
+Files: xbmc/cores/VideoPlayer/DVDCodecs/Overlay/contrib/cc_decoder.c
+ xbmc/cores/VideoPlayer/DVDCodecs/Overlay/contrib/cc_decoder.h
+Copyright:
+ 2000-2008, the xine project
+ 2001, Christian Vogler <cvogler@gradient.cis.upenn.edu>
+License-Grant:
+ xine 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.
+License: GPL-2+
+
+Files: xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav.h
+ xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvdnav_events.h
+Copyright:
+ 2001, Håkan Hjort <d95hjort@dtek.chalmers.se>
+ 2001, Martin Norbäck
+ 2001-2004, Rich Wareham <richwareham@users.sourceforge.net>
+License-Grant:
+ 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.
+License: GPL-2+
+
+Files: xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_reader.h
+ xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/dvd_types.h
+ xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/ifo_types.h
+Copyright:
+ 2000-2002, Håkan Hjort <d95hjort@dtek.chalmers.se>
+ 2001, Rich Wareham <richwareham@users.sourceforge.net>
+ 2001-2002, Billy Biggs <vektor@dumbterm.net>
+ 2001-2002, Björn Englund <d4bjorn@dtek.chalmers.se>
+License-Grant:
+ 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.
+License: GPL-2+
+Comment:
+ Copyright information missing from one source file:
+ <xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/remap.h>.
+ Copyright assumed to be same as (or a subset of) that of other files
+ with identical license grant.
+
+Files: xbmc/cores/VideoPlayer/DVDInputStreams/dvdnav/nav_types.h
+Copyright:
+ 1998-1999, Eric Smith <eric@brouhaha.com>
+ 2000-2002, Håkan Hjort <d95hjort@dtek.chalmers.se>
+License-Grant:
+ VOBDUMP is free software;
+ you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+ Note that I am not granting permission
+ to redistribute or modify VOBDUMP
+ under the terms of any later version of the General Public License.
+License: GPL-2
+
+Files: xbmc/dbwrappers/dataset.cpp
+ xbmc/dbwrappers/dataset.h
+ xbmc/dbwrappers/qry_dat.cpp
+ xbmc/dbwrappers/qry_dat.h
+ xbmc/dbwrappers/sqlitedataset.cpp
+ xbmc/dbwrappers/sqlitedataset.h
+Copyright: 2002,2004, Leo Seib, Hannover <leoseib@web.de>
+License: Expat
+
+Files: xbmc/filesystem/NptXbmcFile.cpp
+Copyright: 2002-2008, Axiomatic Systems, LLC
+License: BSD-3-clause~Axiomatic
+
+Files: xbmc/input/XBMC_keyboard.h
+ xbmc/input/XBMC_keysym.h
+ xbmc/input/XBMC_vkeys.h
+Copyright: 1997-2009, Sam Lantinga <slouken@libsdl.org>
+License-Grant:
+ This library 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.
+License: LGPL-2.1+
+
+Files: xbmc/network/AirPlayServer.cpp
+ xbmc/network/AirPlayServer.h
+ xbmc/network/AirTunesServer.cpp
+Copyright: 2011-2013, Team XBMC <http://xbmc.org/>
+License-Grant:
+ This Program 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.1, or (at your option) any later version.
+License: GPL-2+
+
+Files: xbmc/utils/BitstreamConverter.cpp
+ xbmc/utils/BitstreamConverter.h
+Copyright:
+ 2005, Michal Benes <michal.benes@itonis.tv>
+ 2006, Baptiste Coudurier <baptiste.coudurier@smartjog.com>
+ 2007, Benoit Fouet <benoit.fouet@free.fr>
+ 2008, Wim Taymans <wim.taymans@gmail.com>
+ 2010-2015, Team XBMC <http://xbmc.org/>
+License: GPL-2+
+
+Files: xbmc/utils/HttpParser.cpp
+ xbmc/utils/HttpParser.h
+Copyright: 2011-2013, Team XBMC <http://xbmc.org/>
+License-Grant:
+ This Program 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.
+License: GPL-2+ and public-domain
+ This code was written by Steve Hanov in 2009, no copyright is claimed.
+ This code is in the public domain.
+
+Files: xbmc/utils/StringUtils.cpp
+Copyright:
+ 2005-2013, Team XBMC <http://xbmc.org/>
+ 2012, Leigh Brasington
+License-Grant:
+ This Program 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, or (at your option) any later version.
+License: GPL-2+
+Comment: Also contains license for included snippet:
+ This code may be used and reproduced without written permission.
+ <http://www.leighb.com/tounicupper.htm>
+
+Files: xbmc-xrandr.c
+Copyright:
+ 2001, Keith Packard, member of The XFree86 Project, Inc.
+ 2002, Hewlett Packard Company, Inc.
+ 2006, Intel Corporation
+License: NTP~disclaimer
+
+Files: debian/*
+Copyright:
+ 2008-2009, Ouattara Oumar Aziz <wattazoum@gmail.com>
+ 2012-2013, Andres Mejia <amejia@debian.org>
+ 2014-2015, Balint Reczey <balint@balintreczey.hu>
+ 2020-2023, Vasyl Gello <vasek.gello@gmail.com>
+License-Grant:
+ This program 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;
+ version 2 dated June, 1991, or (at your option) any later version.
+License: GPL-2+
+
+Files: debian/extra/arial.ttf
+Copyright: Copyright © 2006, 2007, 2008, 2009, 2010 Google Corp.
+ Droid is a trademark of Google Corp.
+ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.
+ Bitstream Vera is a trademark of Bitstream, Inc.
+ DejaVu changes are in public domain.
+License: GPL-3+
+Comment: The file is merged from Apache-2-licensed
+ /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf
+ and /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf , which is licensed
+ under the following license:
+ .
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of the fonts accompanying this license ("Fonts") and associated
+ documentation files (the "Font Software"), to reproduce and distribute the
+ Font Software, including without limitation the rights to use, copy, merge,
+ publish, distribute, and/or sell copies of the Font Software, and to permit
+ persons to whom the Font Software is furnished to do so, subject to the
+ following conditions:
+ .
+ The above copyright and trademark notices and this permission notice shall
+ be included in all copies of one or more of the Font Software typefaces.
+ .
+ The Font Software may be modified, altered, or added to, and in particular
+ the designs of glyphs or characters in the Fonts may be modified and
+ additional glyphs or characters may be added to the Fonts, only if the fonts
+ are renamed to names not containing either the words "Bitstream" or the word
+ "Vera".
+ .
+ This License becomes null and void to the extent applicable to Fonts or Font
+ Software that has been modified and is distributed under the "Bitstream
+ Vera" names.
+ .
+ The Font Software may be sold as part of a larger software package but no
+ copy of one or more of the Font Software typefaces may be sold by itself.
+ .
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+ TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+ FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
+ ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
+ FONT SOFTWARE.
+ .
+ Except as contained in this notice, the names of Gnome, the Gnome
+ Foundation, and Bitstream Inc., shall not be used in advertising or
+ otherwise to promote the sale, use or other dealings in this Font Software
+ without prior written authorization from the Gnome Foundation or Bitstream
+ Inc., respectively. For further information, contact: fonts at gnome dot
+ org.
+ .
+ .
+ I'm adding the result named arial.ttf under GPL-3+ because this is allowed
+ by the licenses of the original fonts.
+
+Files: debian/from-debian-logo.svg
+Copyright:
+ 1999, Software in the Public Interest, Inc.
+ 2015, Balint Reczey <balint@balintreczey.hu>
+License-Grant:
+ This package 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 3 of the License, or (at your option) any later version.
+License: LGPL-3+
+
+License: BSD-3-clause
+ Redistribution and use in source and binary forms,
+ with or without modification,
+ are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain
+ the above copyright notice, this list of conditions
+ and the following disclaimer.
+ * Redistributions in binary form must reproduce
+ the above copyright notice, this list of conditions
+ and the following disclaimer
+ in the documentation and/or other materials
+ provided with the distribution.
+ * Neither the author
+ nor the names of any contributors
+ may be used to endorse or promote
+ products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED
+ BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.
+ IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Files: debian/webinterface-default/*
+Copyright:
+ 2005-2021, Team Kodi <http://kodi.tv/>
+License-Grant:
+ This package 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.
+Comment: The default webinterface was imported from Kodi 17.1 as
+ component tarball and later moved inside the debian patch in 19.0.
+ See debian/README.source for more information.
+License: GPL-2+
+
+License: BSD-3-clause~Axiomatic
+ Redistribution and use in source and binary forms,
+ with or without modification,
+ are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain
+ the above copyright notice, this list of conditions
+ and the following disclaimer.
+ * Redistributions in binary form must reproduce
+ the above copyright notice, this list of conditions
+ and the following disclaimer
+ in the documentation and/or other materials
+ provided with the distribution.
+ * Neither the name of Axiomatic Systems
+ nor the names of its contributors
+ may be used to endorse or promote
+ products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.
+ IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+License: BSD-3-clause~axTLS
+ Redistribution and use in source and binary forms,
+ with or without modification,
+ are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain
+ the above copyright notice, this list of conditions
+ and the following disclaimer.
+ * Redistributions in binary form must reproduce
+ the above copyright notice, this list of conditions
+ and the following disclaimer
+ in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the axTLS project
+ nor the names of its contributors
+ may be used to endorse or promote
+ products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED
+ BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.
+ IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+License: CC0-1.0
+ To the extent possible under law, the author(s) have dedicated all
+ copyright and related and neighboring rights to this software to the public
+ domain worldwide. This software is distributed without any warranty.
+ .
+ On Debian systems, the complete text of the CC0 license, version 1.0,
+ can be found in /usr/share/common-licenses/CC0-1.0.
+
+License: CC-BY-SA-3.0~us
+ THE WORK (AS DEFINED BELOW) IS PROVIDED
+ UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE
+ ("CCPL" OR "LICENSE").
+ THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.
+ ANY USE OF THE WORK OTHER THAN AS AUTHORIZED
+ UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+ .
+ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE,
+ YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
+ TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT,
+ THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE
+ IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+ .
+ 1. Definitions
+ .
+ a. "Collective Work" means a work,
+ such as a periodical issue, anthology or encyclopedia,
+ in which the Work in its entirety in unmodified form,
+ along with one or more other contributions,
+ constituting separate and independent works in themselves,
+ are assembled into a collective whole.
+ A work that constitutes a Collective Work will not be considered
+ a Derivative Work (as defined below)
+ for the purposes of this License.
+ b. "Creative Commons Compatible License" means a license
+ that is listed at <https://creativecommons.org/compatiblelicenses>
+ that has been approved by Creative Commons
+ as being essentially equivalent to this License,
+ including, at a minimum,
+ because that license: (i) contains terms
+ that have the same purpose, meaning and effect
+ as the License Elements of this License;
+ and, (ii) explicitly permits the relicensing
+ of derivatives of works made available under that license
+ under this License or either a Creative Commons unported license
+ or a Creative Commons jurisdiction license
+ with the same License Elements as this License.
+ c. "Derivative Work" means a work
+ based upon the Work or upon the Work and other pre-existing works,
+ such as a translation, musical arrangement, dramatization,
+ fictionalization, motion picture version, sound recording,
+ art reproduction, abridgment, condensation,
+ or any other form
+ in which the Work may be recast, transformed, or adapted,
+ except that a work that constitutes a Collective Work
+ will not be considered a Derivative Work
+ for the purpose of this License.
+ For the avoidance of doubt,
+ where the Work is a musical composition or sound recording,
+ the synchronization of the Work
+ in timed-relation with a moving image ("synching")
+ will be considered a Derivative Work
+ for the purpose of this License.
+ d. "License Elements" means the following
+ high-level license attributes
+ as selected by Licensor
+ and indicated in the title of this License:
+ Attribution, ShareAlike.
+ e. "Licensor" means the individual, individuals, entity or entities
+ that offers the Work under the terms of this License.
+ f. "Original Author" means the individual, individuals,
+ entity or entities
+ who created the Work.
+ g. "Work" means the copyrightable work of authorship
+ offered under the terms of this License.
+ h. "You" means an individual or entity
+ exercising rights under this License
+ who has not previously violated the terms of this License
+ with respect to the Work,
+ or who has received express permission from the Licensor
+ to exercise rights under this License
+ despite a previous violation.
+ .
+ 2. Fair Use Rights.
+ Nothing in this license is intended
+ to reduce, limit, or restrict any rights
+ arising from fair use, first sale or other limitations
+ on the exclusive rights of the copyright owner
+ under copyright law or other applicable laws.
+ .
+ 3. License Grant.
+ Subject to the terms and conditions of this License,
+ Licensor hereby grants You
+ a worldwide, royalty-free, non-exclusive, perpetual
+ (for the duration of the applicable copyright)
+ license to exercise the rights in the Work
+ as stated below:
+ .
+ a. to reproduce the Work,
+ to incorporate the Work into one or more Collective Works,
+ and to reproduce the Work
+ as incorporated in the Collective Works;
+ b. to create and reproduce Derivative Works
+ provided that any such Derivative Work,
+ including any translation in any medium,
+ takes reasonable steps to clearly label, demarcate
+ or otherwise identify
+ that changes were made to the original Work.
+ For example, a translation could be marked
+ "The original work was translated from English to Spanish,"
+ or a modification could indicate
+ "The original work has been modified.";
+ c. to distribute copies or phonorecords of,
+ display publicly, perform publicly,
+ and perform publicly by means of a digital audio transmission
+ the Work
+ including as incorporated in Collective Works;
+ d. to distribute copies or phonorecords of,
+ display publicly, perform publicly,
+ and perform publicly by means of a digital audio transmission
+ Derivative Works.
+ e. For the avoidance of doubt,
+ where the Work is a musical composition:
+ i. Performance Royalties Under Blanket Licenses.
+ Licensor waives the exclusive right to collect,
+ whether individually or,
+ in the event that Licensor is a member
+ of a performance rights society
+ (e.g. ASCAP, BMI, SESAC),
+ via that society,
+ royalties for the public performance
+ or public digital performance (e.g. webcast)
+ of the Work.
+ ii. Mechanical Rights and Statutory Royalties.
+ Licensor waives the exclusive right to collect,
+ whether individually or via a music rights agency
+ or designated agent (e.g. Harry Fox Agency),
+ royalties for any phonorecord You create from the Work
+ ("cover version")
+ and distribute,
+ subject to the compulsory license
+ created by 17 USC Section 115 of the US Copyright Act
+ (or the equivalent in other jurisdictions).
+ f. Webcasting Rights and Statutory Royalties.
+ For the avoidance of doubt,
+ where the Work is a sound recording,
+ Licensor waives the exclusive right to collect,
+ whether individually
+ or via a performance-rights society (e.g. SoundExchange),
+ royalties for the public digital performance (e.g. webcast)
+ of the Work,
+ subject to the compulsory license
+ created by 17 USC Section 114 of the US Copyright Act
+ (or the equivalent in other jurisdictions).
+ .
+ The above rights may be exercised
+ in all media and formats
+ whether now known or hereafter devised.
+ The above rights include
+ the right to make such modifications
+ as are technically necessary to exercise the rights
+ in other media and formats.
+ All rights not expressly granted by Licensor are hereby reserved.
+ .
+ 4. Restrictions.
+ The license granted in Section 3 above is expressly
+ made subject to and limited by the following restrictions:
+ .
+ a. You may distribute, publicly display, publicly perform,
+ or publicly digitally perform the Work
+ only under the terms of this License,
+ and You must include a copy of,
+ or the Uniform Resource Identifier for,
+ this License
+ with every copy or phonorecord of the Work
+ You distribute, publicly display, publicly perform,
+ or publicly digitally perform.
+ You may not offer or impose any terms on the Work
+ that restrict the terms of this License
+ or the ability of a recipient of the Work to exercise
+ of the rights granted to that recipient
+ under the terms of the License.
+ You may not sublicense the Work.
+ You must keep intact all notices
+ that refer to this License
+ and to the disclaimer of warranties.
+ When You distribute, publicly display, publicly perform,
+ or publicly digitally perform the Work,
+ You may not impose any technological measures on the Work
+ that restrict the ability of a recipient of the Work from You
+ to exercise of the rights granted to that recipient
+ under the terms of the License.
+ This Section 4(a) applies to the Work
+ as incorporated in a Collective Work,
+ but this does not require the Collective Work
+ apart from the Work itself
+ to be made subject to the terms of this License.
+ If You create a Collective Work,
+ upon notice from any Licensor
+ You must, to the extent practicable, remove
+ from the Collective Work
+ any credit as required by Section 4(c), as requested.
+ If You create a Derivative Work,
+ upon notice from any Licensor
+ You must, to the extent practicable, remove
+ from the Derivative Work
+ any credit as required by Section 4(c), as requested.
+ b. You may distribute, publicly display, publicly perform,
+ or publicly digitally perform a Derivative Work
+ only under: (i) the terms of this License;
+ (ii) a later version of this License
+ with the same License Elements as this License;
+ (iii) either the Creative Commons (Unported) license
+ or a Creative Commons jurisdiction license
+ (either this or a later license version)
+ that contains the same License Elements as this License
+ (e.g. Attribution-ShareAlike 3.0 (Unported));
+ (iv) a Creative Commons Compatible License.
+ If you license the Derivative Work
+ under one of the licenses mentioned in (iv),
+ you must comply with the terms of that license.
+ If you license the Derivative Work
+ under the terms of any of the licenses
+ mentioned in (i), (ii) or (iii)
+ (the "Applicable License"),
+ you must comply with the terms of the Applicable License
+ generally and with the following provisions:
+ (I) You must include a copy of,
+ or the Uniform Resource Identifier for,
+ the Applicable License
+ with every copy or phonorecord of each Derivative Work
+ You distribute, publicly display, publicly perform,
+ or publicly digitally perform;
+ (II) You may not offer or impose any terms
+ on the Derivative Works
+ that restrict the terms of the Applicable License
+ or the ability of a recipient of the Work
+ to exercise the rights granted to that recipient
+ under the terms of the Applicable License;
+ (III) You must keep intact all notices
+ that refer to the Applicable License
+ and to the disclaimer of warranties;
+ and, (IV) when You distribute, publicly display, publicly perform,
+ or publicly digitally perform the Work,
+ You may not impose any technological measures
+ on the Derivative Work
+ that restrict the ability of a recipient
+ of the Derivative Work from You
+ to exercise the rights granted to that recipient
+ under the terms of the Applicable License.
+ This Section 4(b) applies to the Derivative Work
+ as incorporated in a Collective Work,
+ but this does not require the Collective Work
+ apart from the Derivative Work itself
+ to be made subject
+ to the terms of the Applicable License.
+ c. If You distribute, publicly display, publicly perform,
+ or publicly digitally perform the Work
+ (as defined in Section 1 above)
+ or any Derivative Works
+ (as defined in Section 1 above)
+ or Collective Works
+ (as defined in Section 1 above),
+ You must,
+ unless a request has been made pursuant to Section 4(a),
+ keep intact all copyright notices for the Work
+ and provide,
+ reasonable to the medium or means You are utilizing:
+ (i) the name of the Original Author
+ (or pseudonym, if applicable) if supplied,
+ and/or (ii) if the Original Author and/or Licensor designate
+ another party or parties
+ (e.g. a sponsor institute, publishing entity, journal)
+ for attribution ("Attribution Parties")
+ in Licensor's copyright notice, terms of service
+ or by other reasonable means,
+ the name of such party or parties;
+ the title of the Work if supplied;
+ to the extent reasonably practicable,
+ the Uniform Resource Identifier, if any,
+ that Licensor specifies to be associated with the Work,
+ unless such URI does not refer to the copyright notice
+ or licensing information for the Work;
+ and, consistent with Section 3(b)
+ in the case of a Derivative Work,
+ a credit identifying the use of the Work in the Derivative Work
+ (e.g., "French translation of the Work by Original Author,"
+ or "Screenplay based on original Work by Original Author").
+ The credit required by this Section 4(c) may be implemented
+ in any reasonable manner;
+ provided, however, that
+ in the case of a Derivative Work or Collective Work,
+ at a minimum such credit will appear,
+ if a credit for all contributing authors
+ of the Derivative Work or Collective Work appears,
+ then as part of these credits
+ and in a manner at least as prominent as the credits
+ for the other contributing authors.
+ For the avoidance of doubt,
+ You may only use the credit required by this Section
+ for the purpose of attribution
+ in the manner set out above
+ and, by exercising Your rights under this License,
+ You may not implicitly or explicitly assert or imply
+ any connection with, sponsorship or endorsement
+ by the Original Author, Licensor
+ and/or Attribution Parties, as appropriate,
+ of You or Your use of the Work,
+ without the separate, express prior written permission
+ of the Original Author, Licensor and/or Attribution Parties.
+ .
+ 5. Representations, Warranties and Disclaimer
+ .
+ UNLESS OTHERWISE MUTUALLY AGREED
+ TO BY THE PARTIES IN WRITING,
+ LICENSOR OFFERS THE WORK AS-IS
+ AND ONLY TO THE EXTENT OF ANY RIGHTS
+ HELD IN THE LICENSED WORK BY THE LICENSOR.
+ THE LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES
+ OF ANY KIND CONCERNING THE WORK,
+ EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+ INCLUDING, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MARKETABILITY, MERCHANTIBILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT,
+ OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY,
+ OR THE PRESENCE OF ABSENCE OF ERRORS,
+ WHETHER OR NOT DISCOVERABLE.
+ SOME JURISDICTIONS DO NOT ALLOW
+ THE EXCLUSION OF IMPLIED WARRANTIES,
+ SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+ .
+ 6. Limitation on Liability.
+ EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW,
+ IN NO EVENT WILL LICENSOR BE LIABLE TO YOU
+ ON ANY LEGAL THEORY
+ FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
+ OR EXEMPLARY DAMAGES
+ ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK,
+ EVEN IF LICENSOR HAS BEEN ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGES.
+ .
+ 7. Termination
+ .
+ a. This License and the rights granted hereunder
+ will terminate automatically upon any breach by You
+ of the terms of this License.
+ Individuals or entities
+ who have received Derivative Works or Collective Works
+ from You under this License,
+ however, will not have their licenses terminated
+ provided such individuals or entities remain
+ in full compliance with those licenses.
+ Sections 1, 2, 5, 6, 7, and 8 will survive
+ any termination of this License.
+ b. Subject to the above terms and conditions,
+ the license granted here is perpetual
+ (for the duration of the applicable copyright in the Work).
+ Notwithstanding the above,
+ Licensor reserves the right to release the Work
+ under different license terms
+ or to stop distributing the Work
+ at any time;
+ provided, however that any such election will not serve
+ to withdraw this License
+ (or any other license that has been, or is required to be, granted
+ under the terms of this License),
+ and this License will continue in full force and effect
+ unless terminated as stated above.
+ .
+ 8. Miscellaneous
+ .
+ a. Each time You distribute or publicly digitally perform
+ the Work (as defined in Section 1 above)
+ or a Collective Work (as defined in Section 1 above),
+ the Licensor offers to the recipient a license to the Work
+ on the same terms and conditions
+ as the license granted to You under this License.
+ b. Each time You distribute
+ or publicly digitally perform a Derivative Work,
+ Licensor offers to the recipient
+ a license to the original Work
+ on the same terms and conditions
+ as the license granted to You under this License.
+ c. If any provision of this License is invalid
+ or unenforceable under applicable law,
+ it shall not affect the validity or enforceability
+ of the remainder of the terms of this License,
+ and without further action
+ by the parties to this agreement,
+ such provision shall be reformed
+ to the minimum extent necessary
+ to make such provision valid and enforceable.
+ d. No term or provision of this License shall be deemed waived
+ and no breach consented to
+ unless such waiver or consent shall be in writing
+ and signed by the party
+ to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties
+ with respect to the Work licensed here.
+ There are no understandings, agreements or representations
+ with respect to the Work not specified here.
+ Licensor shall not be bound by any additional provisions
+ that may appear in any communication from You.
+ This License may not be modified
+ without the mutual written agreement of the Licensor and You.
+
+License: CC-BY-SA-4.0
+ Creative Commons Attribution-ShareAlike 4.0 International
+ Public License
+ .
+ By exercising the Licensed Rights (defined below),
+ You accept and agree to be bound by the terms and conditions
+ of this Creative Commons Attribution-ShareAlike 4.0 International
+ Public License ("Public License").
+ To the extent this Public License may be interpreted as a contract,
+ You are granted the Licensed Rights
+ in consideration of Your acceptance of these terms and conditions,
+ and the Licensor grants You such rights
+ in consideration of benefits the Licensor receives
+ from making the Licensed Material available
+ under these terms and conditions.
+ .
+ Section 1 – Definitions.
+ .
+ a. Adapted Material means material
+ subject to Copyright and Similar Rights
+ that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated,
+ altered, arranged, transformed,
+ or otherwise modified in a manner requiring permission
+ under the Copyright and Similar Rights held by the Licensor.
+ For purposes of this Public License,
+ where the Licensed Material is a musical work,
+ performance, or sound recording,
+ Adapted Material is always produced
+ where the Licensed Material is synched
+ in timed relation with a moving image.
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material
+ in accordance with the terms and conditions of this Public License.
+ c. BY-SA Compatible License means a license
+ listed at <https://creativecommons.org/compatiblelicenses>,
+ approved by Creative Commons
+ as essentially the equivalent of this Public License.
+ d. Copyright and Similar Rights means copyright
+ and/or similar rights closely related to copyright
+ including, without limitation,
+ performance, broadcast, sound recording,
+ and Sui Generis Database Rights,
+ without regard to how the rights are labeled or categorized.
+ For purposes of this Public License,
+ the rights specified in Section 2(b)(1)-(2)
+ are not Copyright and Similar Rights.
+ e. Effective Technological Measures means those measures
+ that, in the absence of proper authority, may not be circumvented
+ under laws fulfilling obligations
+ under Article 11 of the WIPO Copyright Treaty
+ adopted on December 20, 1996,
+ and/or similar international agreements.
+ f. Exceptions and Limitations means fair use, fair dealing,
+ and/or any other exception or limitation
+ to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+ g. License Elements means the license attributes
+ listed in the name of a Creative Commons Public License.
+ The License Elements of this Public License are
+ Attribution and ShareAlike.
+ h. Licensed Material means the artistic or literary work,
+ database, or other material
+ to which the Licensor applied this Public License.
+ i. Licensed Rights means the rights granted to You subject
+ to the terms and conditions of this Public License,
+ which are limited to all Copyright and Similar Rights
+ that apply to Your use of the Licensed Material
+ and that the Licensor has authority to license.
+ j. Licensor means the individual(s) or entity(ies)
+ granting rights under this Public License.
+ k. Share means to provide material to the public
+ by any means or process
+ that requires permission under the Licensed Rights,
+ such as reproduction, public display, public performance,
+ distribution, dissemination, communication, or importation,
+ and to make material available to the public
+ including in ways that members of the public may access
+ the material
+ from a place and at a time individually chosen by them.
+ l. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament
+ and of the Council of 11 March 1996
+ on the legal protection of databases,
+ as amended and/or succeeded,
+ as well as other essentially equivalent rights
+ anywhere in the world.
+ m. You means the individual or entity
+ exercising the Licensed Rights under this Public License.
+ Your has a corresponding meaning.
+ .
+ Section 2 – Scope.
+ .
+ a. License grant.
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You
+ a worldwide, royalty-free, non-sublicensable,
+ non-exclusive, irrevocable license
+ to exercise the Licensed Rights in the Licensed Material to:
+ A. reproduce and Share the Licensed Material,
+ in whole or in part; and
+ B. produce, reproduce, and Share Adapted Material.
+ 2. Exceptions and Limitations.
+ For the avoidance of doubt,
+ where Exceptions and Limitations apply to Your use,
+ this Public License does not apply,
+ and You do not need to comply
+ with its terms and conditions.
+ 3. Term.
+ The term of this Public License is specified in Section 6(a).
+ 4. Media and formats; technical modifications allowed.
+ The Licensor authorizes You to exercise the Licensed Rights
+ in all media and formats
+ whether now known or hereafter created,
+ and to make technical modifications necessary to do so.
+ The Licensor waives and/or agrees not to assert
+ any right or authority
+ to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights,
+ including technical modifications necessary
+ to circumvent Effective Technological Measures.
+ For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)(4)
+ never produces Adapted Material.
+ 5. Downstream recipients.
+ A. Offer from the Licensor – Licensed Material.
+ Every recipient of the Licensed Material
+ automatically receives an offer from the Licensor
+ to exercise the Licensed Rights
+ under the terms and conditions of this Public License.
+ B. Additional offer from the Licensor – Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor
+ to exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter’s License You apply.
+ C. No downstream restrictions.
+ You may not offer or impose
+ any additional or different terms or conditions on,
+ or apply any Effective Technological Measures to,
+ the Licensed Material
+ if doing so restricts exercise of the Licensed Rights
+ by any recipient of the Licensed Material.
+ 6. No endorsement.
+ Nothing in this Public License constitutes
+ or may be construed
+ as permission to assert or imply
+ that You are, or that Your use of the Licensed Material is,
+ connected with, or sponsored, endorsed,
+ or granted official status by,
+ the Licensor or others designated to receive attribution
+ as provided in Section 3(a)(1)(A)(i).
+ .
+ b. Other rights.
+ 1. Moral rights, such as the right of integrity,
+ are not licensed under this Public License,
+ nor are publicity, privacy,
+ and/or other similar personality rights;
+ however, to the extent possible,
+ the Licensor waives
+ and/or agrees not to assert
+ any such rights held by the Licensor
+ to the limited extent necessary
+ to allow You to exercise the Licensed Rights,
+ but not otherwise.
+ 2. Patent and trademark rights are not licensed
+ under this Public License.
+ 3. To the extent possible,
+ the Licensor waives any right
+ to collect royalties from You
+ for the exercise of the Licensed Rights,
+ whether directly or through a collecting society
+ under any voluntary or waivable
+ statutory or compulsory licensing scheme.
+ In all other cases
+ the Licensor expressly reserves any right
+ to collect such royalties.
+ .
+ Section 3 – License Conditions.
+ .
+ Your exercise of the Licensed Rights
+ is expressly made subject to the following conditions.
+ .
+ a. Attribution.
+ 1. If You Share the Licensed Material
+ (including in modified form),
+ You must:
+ A. retain the following
+ if it is supplied by the Licensor with the Licensed Material:
+ i. identification of the creator(s) of the Licensed Material
+ and any others designated to receive attribution,
+ in any reasonable manner requested by the Licensor
+ (including by pseudonym if designated);
+ ii. a copyright notice;
+ iii. a notice that refers to this Public License;
+ iv. a notice that refers to the disclaimer of warranties;
+ v. a URI or hyperlink to the Licensed Material
+ to the extent reasonably practicable;
+ B. indicate if You modified the Licensed Material
+ and retain an indication of any previous modifications; and
+ C. indicate the Licensed Material is licensed
+ under this Public License,
+ and include the text of, or the URI or hyperlink to,
+ this Public License.
+ 2. You may satisfy the conditions in Section 3(a)(1)
+ in any reasonable manner
+ based on the medium, means, and context
+ in which You Share the Licensed Material.
+ For example,
+ it may be reasonable to satisfy the conditions
+ by providing a URI or hyperlink to a resource
+ that includes the required information.
+ 3. If requested by the Licensor,
+ You must remove any of the information
+ required by Section 3(a)(1)(A)
+ to the extent reasonably practicable.
+ b. ShareAlike.
+ In addition to the conditions in Section 3(a),
+ if You Share Adapted Material You produce,
+ the following conditions also apply.
+ 1. The Adapter’s License You apply
+ must be a Creative Commons license
+ with the same License Elements,
+ this version or later,
+ or a BY-SA Compatible License.
+ 2. You must include
+ the text of, or the URI or hyperlink to,
+ the Adapter's License You apply.
+ You may satisfy this condition
+ in any reasonable manner
+ based on the medium, means, and context
+ in which You Share Adapted Material.
+ 3. You may not offer or impose
+ any additional or different terms or conditions on,
+ or apply any Effective Technological Measures to,
+ Adapted Material
+ that restrict exercise of the rights granted
+ under the Adapter's License You apply.
+ .
+ Section 4 – Sui Generis Database Rights.
+ .
+ Where the Licensed Rights include Sui Generis Database Rights
+ that apply to Your use of the Licensed Material:
+ .
+ a. for the avoidance of doubt,
+ Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share
+ all or a substantial portion of the contents of the database;
+ b. if You include all or a substantial portion of
+ the database contents in a database
+ in which You have Sui Generis Database Rights,
+ then the database
+ in which You have Sui Generis Database Rights
+ (but not its individual contents)
+ is Adapted Material,
+ including for purposes of Section 3(b); and
+ c. You must comply with the conditions in Section 3(a)
+ if You Share all or a substantial portion
+ of the contents of the database.
+ .
+ For the avoidance of doubt,
+ this Section 4 supplements and does not replace
+ Your obligations under this Public License
+ where the Licensed Rights include other Copyright and Similar Rights.
+ .
+ Section 5 – Disclaimer of Warranties and Limitation of Liability.
+ .
+ a. Unless otherwise separately undertaken by the Licensor,
+ to the extent possible,
+ the Licensor offers the Licensed Material as-is and as-available,
+ and makes no representations or warranties
+ of any kind concerning the Licensed Material,
+ whether express, implied, statutory, or other.
+ This includes, without limitation,
+ warranties of title, merchantability,
+ fitness for a particular purpose, non-infringement,
+ absence of latent or other defects, accuracy,
+ or the presence or absence of errors,
+ whether or not known or discoverable.
+ Where disclaimers of warranties are not allowed
+ in full or in part,
+ this disclaimer may not apply to You.
+ b. To the extent possible,
+ in no event will the Licensor be liable to You
+ on any legal theory
+ (including, without limitation, negligence)
+ or otherwise for any direct, special,
+ indirect, incidental, consequential, punitive, exemplary,
+ or other losses, costs, expenses, or damages
+ arising out of this Public License
+ or use of the Licensed Material,
+ even if the Licensor has been advised
+ of the possibility of such losses, costs,
+ expenses, or damages.
+ Where a limitation of liability is not allowed
+ in full or in part,
+ this limitation may not apply to You.
+ c. The disclaimer of warranties
+ and limitation of liability provided above
+ shall be interpreted in a manner that,
+ to the extent possible,
+ most closely approximates an absolute disclaimer
+ and waiver of all liability.
+ .
+ Section 6 – Term and Termination.
+ .
+ a. This Public License applies
+ for the term of the Copyright
+ and Similar Rights licensed here.
+ However, if You fail to comply with this Public License,
+ then Your rights under this Public License terminate
+ automatically.
+ b. Where Your right to use the Licensed Material has terminated
+ under Section 6(a), it reinstates:
+ 1. automatically as of the date the violation is cured,
+ provided it is cured within 30 days
+ of Your discovery of the violation; or
+ 2. upon express reinstatement by the Licensor.
+ c. For the avoidance of doubt,
+ this Section 6(b) does not affect
+ any right the Licensor may have to seek remedies
+ for Your violations of this Public License.
+ d. For the avoidance of doubt,
+ the Licensor may also offer the Licensed Material
+ under separate terms or conditions
+ or stop distributing the Licensed Material at any time;
+ however, doing so will not terminate this Public License.
+ e. Sections 1, 5, 6, 7, and 8 survive termination
+ of this Public License.
+ .
+ Section 7 – Other Terms and Conditions.
+ .
+ a. The Licensor shall not be bound
+ by any additional or different terms or conditions
+ communicated by You unless expressly agreed.
+ b. Any arrangements, understandings, or agreements
+ regarding the Licensed Material not stated herein
+ are separate from and independent of the terms and conditions
+ of this Public License.
+ .
+ Section 8 – Interpretation.
+ .
+ a. For the avoidance of doubt,
+ this Public License does not, and shall not be interpreted to,
+ reduce, limit, restrict, or impose conditions on
+ any use of the Licensed Material
+ that could lawfully be made without permission
+ under this Public License.
+ b. To the extent possible,
+ if any provision of this Public License is deemed unenforceable,
+ it shall be automatically reformed
+ to the minimum extent necessary to make it enforceable.
+ If the provision cannot be reformed,
+ it shall be severed from this Public License
+ without affecting the enforceability
+ of the remaining terms and conditions.
+ c. No term or condition of this Public License will be waived
+ and no failure to comply consented to
+ unless expressly agreed to by the Licensor.
+ d. Nothing in this Public License constitutes
+ or may be interpreted as a limitation upon, or waiver of,
+ any privileges and immunities that apply to the Licensor or You,
+ including from the legal processes
+ of any jurisdiction or authority.
+
+License: Expat
+ Permission is hereby granted,
+ free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction,
+ including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS",
+ WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+License: GPL-2
+ This package 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; version 2 of the License.
+ .
+ This package 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 this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License can be found in `/usr/share/common-licenses/GPL-2'.
+
+License: GPL-2+
+ This package 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.
+ .
+ This package 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 this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License can be found in `/usr/share/common-licenses/GPL-2'.
+
+License: GPL-3
+ This package 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 3 of the License.
+ .
+ This package 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 this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License can be found in `/usr/share/common-licenses/GPL-3'.
+
+License: GPL-3+
+ This package 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 3 of the License, or
+ (at your option) any later version.
+ .
+ This package 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 this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License can be found in `/usr/share/common-licenses/GPL-3'.
+
+License: ISC~minimal-disclaimer
+ Permission to use, copy, modify, and distribute
+ this software and its documentation
+ for any purpose
+ is hereby granted without fee,
+ provided that this copyright and permissions notice appear
+ in all copies and derivatives.
+ .
+ This software is supplied "as is"
+ without express or implied warranty.
+
+License: LGPL-2.1+
+ This package 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 package 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 package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
+
+License: LGPL-3+
+ This package 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 3 of the License, or (at your option) any later version.
+ .
+ This package 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 package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License can be found in `/usr/share/common-licenses/LGPL-3'.
+
+License: NTP~disclaimer
+ Permission to use, copy, modify, distribute, and sell
+ this software and its documentation
+ for any purpose
+ is hereby granted without fee,
+ provided that the above copyright notice appear
+ in all copies
+ and that both that copyright notice and this permission notice appear
+ in supporting documentation,
+ and that the name of the copyright holders not be used
+ in advertising or publicity pertaining to distribution of the software
+ without specific, written prior permission.
+ The copyright holders make no representations
+ about the suitability of this software for any purpose.
+ It is provided "as is" without express or implied warranty.
+ .
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE,
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
+ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
+ OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+License: NTP~disclaimer~FarsiWeb
+ Permission to use, copy, modify, distribute, and sell
+ this software and its documentation
+ for any purpose
+ is hereby granted without fee,
+ provided that the above copyright notice appear
+ in all copies
+ and that both that copyright notice and this permission notice appear
+ in supporting documentation,
+ and that the name of Sharif FarsiWeb, Inc. not be used
+ in advertising or publicity pertaining to distribution of the software
+ without specific, written prior permission.
+ Sharif FarsiWeb, Inc. makes no representations
+ about the suitability of this software for any purpose.
+ It is provided "as is" without express or implied warranty.
+ .
+ SHARIF FARSIWEB, INC. DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE,
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ IN NO EVENT SHALL KEITH PACKARD BE LIABLE
+ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
+ OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+License: X11
+ Permission is hereby granted,
+ free of charge, to any person obtaining
+ a copy of this software and associated documentation files
+ (the "Software"),
+ to deal in the Software without restriction,
+ including without limitation
+ the rights to use, copy, modify, merge, publish, distribute,
+ sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS",
+ WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO
+ THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH
+ THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ .
+ Except as contained in this notice,
+ the name of the X Consortium shall not be used
+ in advertising or otherwise
+ to promote the sale, use or other dealings in this Software
+ without prior written authorization from the X Consortium.
diff --git a/debian/dh-addon/dh_kodiaddon_depends b/debian/dh-addon/dh_kodiaddon_depends
new file mode 100755
index 0000000..6e469a1
--- /dev/null
+++ b/debian/dh-addon/dh_kodiaddon_depends
@@ -0,0 +1,148 @@
+#! /usr/bin/perl -w
+
+=head1 NAME
+
+dh_kodiaddon_depends - Provides substvars for Kodi addons
+
+=cut
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+
+=head1 SYNOPSIS
+
+B<dh_kodiaddon_depends> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+dh_kodiaddon_depends is a debhelper program, that is responsible for setting substvars
+providing the Kodi addon-API dependencies. Kodi provides a virtual package for each API
+which might be used by an addon. The following substvars will be set to a dependency
+clause which can be used directly in the "Depends"-line of the addons debian/control:
+
+${kodi:AUDIODECODERAPI} (ADDON_INSTANCE_VERSION_AUDIODECODER)
+${kodi:AUDIOENCODERAPI} (ADDON_INSTANCE_VERSION_AUDIOENCODER)
+${kodi:AUDIOENGINEAPI} (ADDON_GLOBAL_VERSION_AUDIOENGINE)
+${kodi:FILESYSTEMAPI} (ADDON_GLOBAL_VERSION_FILESYSTEM)
+${kodi:GAMEAPI} (ADDON_INSTANCE_VERSION_GAME)
+${kodi:GENERALAPI} (ADDON_GLOBAL_VERSION_GENERAL)
+${kodi:GUIAPI} (ADDON_GLOBAL_VERSION_GUI)
+${kodi:IMAGEDECODERAPI} (ADDON_INSTANCE_VERSION_IMAGEDECODER)
+${kodi:INPUTSTREAMAPI} (ADDON_INSTANCE_VERSION_INPUTSTREAM)
+${kodi:MAINAPI} (ADDON_GLOBAL_VERSION_MAIN)
+${kodi:NETWORKAPI} (ADDON_GLOBAL_VERSION_NETWORK)
+${kodi:PERIPHERALAPI} (ADDON_INSTANCE_VERSION_PERIPHERAL)
+${kodi:PVRAPI} (ADDON_INSTANCE_VERSION_PVR)
+${kodi:SCREENSAVERAPI} (ADDON_INSTANCE_VERSION_SCREENSAVER)
+${kodi:VFSAPI} (ADDON_INSTANCE_VERSION_VFS)
+${kodi:VIDEOCODECAPI} (ADDON_INSTANCE_VERSION_VIDEOCODEC)
+${kodi:VISUALIZATIONAPI} (ADDON_INSTANCE_VERSION_VISUALIZATION)
+
+=head1 EXAMPLES
+
+dh_kodiaddon_depends is usually called indirectly in a rules file via the dh command.
+
+ %:
+ dh $@ --with kodiaddon
+
+It can also be called directly, prior to calling dh_gencontrol.
+
+=head1 CONFORMS TO
+
+Debian policy, version 3.8.1
+
+=cut
+
+init ();
+
+no locale;
+
+sub get_api_version {
+ my $headerFileName = $_[0];
+ my $defineName = $_[1];
+ my $apiVersion;
+ my $apiVersionMin;
+ my $apiVersionMax;
+
+ open(HEADERFILE, "<$headerFileName") or die "$!";
+ while (<HEADERFILE>) {
+ if ($_ =~ /^#define $defineName\s+"(.*)"/) {
+ $apiVersion = $1;
+ }
+ if ($_ =~ /^#define ${defineName}_MIN\s+"(.*)"/) {
+ $apiVersionMin = $1;
+ }
+ if ($apiVersion && $apiVersionMin) {
+ last;
+ }
+ }
+ close(HEADERFILE);
+ if ($apiVersion =~ qr/(?:(\d+)\.(\d+)\.(\d+)|(\d+)\.(\d+)|(\d+))?(?:\.\*)?/) {
+ $apiVersionMax = int($2) + 1;
+ $apiVersionMax = "$1.$apiVersionMax.0";
+ }
+ $apiVersion or die "Could not find '$defineName' in $headerFileName";
+ $apiVersionMin or die "Could not find '${defineName}_MIN' in $headerFileName";
+ $apiVersionMax or die "Could not derive maximum API version from '$defineName' in $headerFileName";
+ ($apiVersion, $apiVersionMin, $apiVersionMax)
+}
+
+sub create_substvar {
+ my $substvarName = $_[0];
+ my $substvarValue = $_[1];
+ foreach my $package (@{$dh{DOPACKAGES}}) {
+ delsubstvar($package, $substvarName);
+ addsubstvar($package, $substvarName, $substvarValue);
+ }
+}
+
+my $baseDir = "debian/kodi-addons-dev-common";
+if (! -d "$baseDir/usr/include/kodi") {
+ $baseDir = "debian/tmp";
+ if (! -d "$baseDir/usr/include/kodi") {
+ $baseDir = ""
+ }
+}
+
+my @apis = (
+ ["kodi-api-audiodecoder", "kodi:AUDIODECODERAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_AUDIODECODER"],
+ ["kodi-api-audioencoder", "kodi:AUDIOENCODERAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_AUDIOENCODER"],
+ ["kodi-api-audioengine", "kodi:AUDIOENGINEAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_GLOBAL_VERSION_AUDIOENGINE"],
+ ["kodi-api-filesystem", "kodi:FILESYSTEMAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_GLOBAL_VERSION_FILESYSTEM"],
+ ["kodi-api-game", "kodi:GAMEAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_GAME"],
+ ["kodi-api-general", "kodi:GENERALAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_GLOBAL_VERSION_GENERAL"],
+ ["kodi-api-gui", "kodi:GUIAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_GLOBAL_VERSION_GUI"],
+ ["kodi-api-imagedecoder", "kodi:IMAGEDECODERAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_IMAGEDECODER"],
+ ["kodi-api-inputstream", "kodi:INPUTSTREAMAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_INPUTSTREAM"],
+ ["kodi-api-main", "kodi:MAINAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_GLOBAL_VERSION_MAIN"],
+ ["kodi-api-network", "kodi:NETWORKAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_GLOBAL_VERSION_NETWORK"],
+ ["kodi-api-peripheral", "kodi:PERIPHERALAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_PERIPHERAL"],
+ ["kodi-api-pvr", "kodi:PVRAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_PVR"],
+ ["kodi-api-screensaver", "kodi:SCREENSAVERAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_SCREENSAVER"],
+ ["kodi-api-vfs", "kodi:VFSAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_VFS"],
+ ["kodi-api-videocodec", "kodi:VIDEOCODECAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_VIDEOCODEC"],
+ ["kodi-api-visualization", "kodi:VISUALIZATIONAPI", "$baseDir/usr/include/kodi/versions.h", "ADDON_INSTANCE_VERSION_VISUALIZATION"]
+);
+
+my @provides;
+
+foreach my $api (@apis) {
+ my ($apiName, $apiSubstvar, $apiHeader, $apiDefine) = @$api;
+ my ($apiVersion, $apiVersionMin, $apiVersionMax) = get_api_version($apiHeader, $apiDefine);
+ create_substvar($apiSubstvar, "$apiName (>= $apiVersionMin), $apiName (<< $apiVersionMax)");
+ push @provides, "$apiName (= $apiVersion)";
+}
+
+create_substvar("kodi:APIPROVIDES", join(", ", @provides));
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is not a part of debhelper.
+
+=head1 AUTHOR
+
+Tobias Grimm <tobias.grimm@e-tobi.net>
+
+=cut
diff --git a/debian/dh-addon/kodiaddon.pm b/debian/dh-addon/kodiaddon.pm
new file mode 100644
index 0000000..019793c
--- /dev/null
+++ b/debian/dh-addon/kodiaddon.pm
@@ -0,0 +1,7 @@
+use warnings;
+use strict;
+use Debian::Debhelper::Dh_Lib;
+
+insert_before("dh_gencontrol", "dh_kodiaddon_depends");
+
+1
diff --git a/debian/extra/arial.ttf b/debian/extra/arial.ttf
new file mode 100644
index 0000000..d4b0962
--- /dev/null
+++ b/debian/extra/arial.ttf
Binary files differ
diff --git a/debian/from-debian-logo.svg b/debian/from-debian-logo.svg
new file mode 100644
index 0000000..e0779d9
--- /dev/null
+++ b/debian/from-debian-logo.svg
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 10.0, SVG Export Plug-In . SVG Version: 3.0.0 Build 77) -->
+
+<svg
+ xmlns:ns0="http://ns.adobe.com/SaveForWeb/1.0/"
+ xmlns:ns="http://ns.adobe.com/Variables/1.0/"
+ xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ i:viewOrigin="251 467"
+ i:rulerOrigin="0 0"
+ i:pageBounds="0 792 612 0"
+ width="124.60188"
+ height="41.382854"
+ viewBox="0 0 124.60188 41.382856"
+ overflow="visible"
+ enable-background="new 0 0 108.758 144.133"
+ xml:space="preserve"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="kodi-from-debian-logo.svg"
+ style="overflow:visible"><defs
+ id="defs49"><filter
+ inkscape:label="Drop Shadow"
+ id="filter4007"
+ color-interpolation-filters="sRGB"><feFlood
+ flood-opacity="0.267"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood4009" /><feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="in"
+ result="composite1"
+ id="feComposite4011" /><feGaussianBlur
+ stdDeviation="6"
+ result="blur"
+ id="feGaussianBlur4013" /><feOffset
+ dx="2.77556e-17"
+ dy="2.77556e-17"
+ result="offset"
+ id="feOffset4015" /><feComposite
+ in="SourceGraphic"
+ in2="offset"
+ operator="over"
+ result="composite2"
+ id="feComposite4017" /></filter><filter
+ inkscape:label="Drop Shadow"
+ id="filter4019"
+ color-interpolation-filters="sRGB"><feFlood
+ flood-opacity="0.267"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood4021" /><feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="in"
+ result="composite1"
+ id="feComposite4023" /><feGaussianBlur
+ stdDeviation="6"
+ result="blur"
+ id="feGaussianBlur4025" /><feOffset
+ dx="2.77556e-17"
+ dy="2.77556e-17"
+ result="offset"
+ id="feOffset4027" /><feComposite
+ in="SourceGraphic"
+ in2="offset"
+ operator="over"
+ result="composite2"
+ id="feComposite4029" /></filter><filter
+ inkscape:label="Drop Shadow"
+ id="filter4055"
+ y="-0.5"
+ height="2"
+ color-interpolation-filters="sRGB"><feFlood
+ flood-opacity="0.25"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood4057" /><feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="in"
+ result="composite1"
+ id="feComposite4059" /><feGaussianBlur
+ stdDeviation="6"
+ result="blur"
+ id="feGaussianBlur4061" /><feOffset
+ dx="2.77556e-17"
+ dy="2.77556e-17"
+ result="offset"
+ id="feOffset4063" /><feComposite
+ in="SourceGraphic"
+ in2="offset"
+ operator="over"
+ result="composite2"
+ id="feComposite4065" /></filter></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1438"
+ inkscape:window-height="879"
+ id="namedview47"
+ showgrid="false"
+ inkscape:zoom="2.3156003"
+ inkscape:cx="9.2522673"
+ inkscape:cy="8.9090173"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ showguides="true"
+ inkscape:guide-bbox="true" /><metadata
+ id="metadata4"><ns:variableSets><ns:variableSet
+ varSetName="binding1"
+ locked="none"><ns:variables /><ns:sampleDataSets /></ns:variableSet></ns:variableSets><ns0:sfw><ns0:slices /><ns0:sliceSourceBounds
+ y="322.867"
+ x="251"
+ width="108.758"
+ height="144.133"
+ bottomLeftOrigin="true" /></ns0:sfw><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
+ id="g4043"
+ style="filter:url(#filter4055)"
+ transform="translate(-179.24258,-86.71267)"><g
+ transform="matrix(0.5678998,0,0,0.5678998,231.69733,35.897279)"
+ id="g4105"><path
+ i:knockout="Off"
+ d="m 13.437,125.506 c -0.045,0.047 -0.045,7.506 -0.138,9.453 -0.092,1.574 -0.232,4.957 -3.568,4.957 -3.429,0 -4.263,-3.939 -4.541,-5.652 -0.324,-1.9 -0.324,-3.477 -0.324,-4.17 0,-2.225 0.139,-8.436 5.375,-8.436 1.576,0 2.456,0.465 3.151,0.834 l 0.045,3.014 z M 0,130.975 c 0,13.066 6.951,13.066 7.97,13.066 2.873,0 4.727,-1.576 5.514,-4.309 l 0.093,4.123 c 0.881,-0.047 1.761,-0.139 3.197,-0.139 0.51,0 0.926,0 1.298,0.047 0.371,0 0.741,0.045 1.158,0.092 -0.741,-1.482 -1.297,-4.818 -1.297,-12.049 0,-7.043 0,-18.951 0.602,-22.566 -1.667,0.789 -3.105,1.299 -6.256,1.576 1.251,1.344 1.251,2.039 1.251,8.154 -0.879,-0.277 -1.992,-0.602 -3.892,-0.602 C 1.344,118.369 0,125.598 0,130.975"
+ id="path33"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ i:knockout="Off"
+ d="m 25.13,128.609 c 0.047,-3.846 0.835,-7.275 4.124,-7.275 3.615,0 3.891,3.984 3.799,7.275 H 25.13 z m 12.51,0.465 c 0,-5.422 -1.065,-10.752 -7.923,-10.752 -9.452,0 -9.452,10.475 -9.452,12.697 0,9.406 4.216,13.113 11.306,13.113 3.149,0 4.68,-0.461 5.514,-0.695 -0.046,-1.668 0.185,-2.734 0.465,-4.17 -0.975,0.604 -2.226,1.391 -5.006,1.391 -7.229,0 -7.322,-6.582 -7.322,-8.852 H 37.55 l 0.09,-2.732"
+ id="path35"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ i:knockout="Off"
+ d="m 52.715,131.066 c 0,4.309 -0.787,10.102 -6.162,10.102 -0.742,0 -1.668,-0.141 -2.27,-0.279 -0.093,-1.668 -0.093,-4.541 -0.093,-7.877 0,-3.986 0.416,-6.068 0.742,-7.09 0.972,-3.289 3.15,-3.334 3.566,-3.334 3.522,0 4.217,4.865 4.217,8.478 z m -13.298,5.051 c 0,3.43 0,5.375 -0.556,6.857 1.9,0.742 4.262,1.158 7.09,1.158 1.807,0 7.043,0 9.869,-5.791 1.344,-2.688 1.807,-6.303 1.807,-9.037 0,-1.668 -0.186,-5.328 -1.529,-7.646 -1.296,-2.176 -3.382,-3.289 -5.605,-3.289 -4.449,0 -5.746,3.707 -6.44,5.607 0,-2.363 0.045,-10.611 0.415,-14.828 -3.011,1.391 -4.866,1.621 -6.857,1.807 1.807,0.74 1.807,3.801 1.807,13.764 v 11.398"
+ id="path37"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ i:knockout="Off"
+ d="m 66.535,143.855 c -0.928,-0.139 -1.578,-0.232 -2.922,-0.232 -1.48,0 -2.502,0.094 -3.566,0.232 0.463,-0.881 0.648,-1.299 0.787,-4.309 0.186,-4.125 0.232,-15.154 -0.092,-17.471 -0.232,-1.762 -0.648,-2.039 -1.297,-2.502 3.799,-0.371 4.865,-0.648 6.625,-1.482 -0.369,2.037 -0.418,3.059 -0.418,6.162 -0.091,15.989 -0.138,17.702 0.883,19.602"
+ id="path39"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ i:knockout="Off"
+ d="m 81.373,130.74 c -0.092,2.92 -0.139,4.959 -0.928,6.58 -0.973,2.086 -2.594,2.688 -3.799,2.688 -2.783,0 -3.383,-2.316 -3.383,-4.586 0,-4.355 3.893,-4.682 5.652,-4.682 h 2.458 z m -12.744,5.701 c 0,2.92 0.881,5.838 3.477,7.09 1.158,0.51 2.316,0.51 2.688,0.51 4.264,0 5.699,-3.152 6.58,-5.098 -0.047,2.039 0,3.289 0.139,4.912 0.834,-0.047 1.668,-0.139 3.059,-0.139 0.787,0 1.529,0.092 2.316,0.139 -0.51,-0.787 -0.787,-1.252 -0.928,-3.059 -0.092,-1.76 -0.092,-3.521 -0.092,-5.977 l 0.047,-9.453 c 0,-3.523 -0.928,-6.998 -7.879,-6.998 -4.586,0 -7.273,1.391 -8.617,2.086 0.557,1.02 1.02,1.898 1.436,3.893 1.809,-1.576 4.172,-2.41 6.58,-2.41 3.848,0 3.848,2.549 3.848,6.162 -0.881,-0.045 -1.623,-0.137 -2.875,-0.137 -5.887,10e-4 -9.779,2.268 -9.779,8.479"
+ id="path41"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ i:knockout="Off"
+ d="m 108.063,139.268 c 0.047,1.576 0.047,3.244 0.695,4.588 -1.021,-0.092 -1.623,-0.232 -3.521,-0.232 -1.113,0 -1.715,0.094 -2.596,0.232 0.184,-0.602 0.279,-0.834 0.371,-1.623 0.139,-1.064 0.232,-4.633 0.232,-5.885 v -5.004 c 0,-2.178 0,-5.33 -0.141,-6.441 -0.092,-0.787 -0.322,-2.918 -3.012,-2.918 -2.641,0 -3.521,1.945 -3.846,3.521 -0.369,1.621 -0.369,3.383 -0.369,10.24 0.045,5.932 0.045,6.486 0.508,8.109 -0.787,-0.092 -1.76,-0.184 -3.15,-0.184 -1.113,0 -1.854,0.045 -2.779,0.184 0.324,-0.742 0.51,-1.113 0.602,-3.707 0.094,-2.549 0.279,-15.061 -0.141,-18.025 -0.23,-1.809 -0.695,-2.225 -1.203,-2.688 3.754,-0.186 4.957,-0.789 6.117,-1.389 v 4.91 c 0.555,-1.438 1.713,-4.635 6.348,-4.635 5.793,0 5.838,4.217 5.885,6.996 v 13.951"
+ id="path43"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ i:knockout="Off"
+ d="m 66.926,111.533 -3.838,3.836 -3.836,-3.836 3.836,-3.836 3.838,3.836"
+ id="path45"
+ inkscape:connector-curvature="0"
+ style="fill:#a80030" /></g><text
+ sodipodi:linespacing="125%"
+ id="text4101"
+ y="113.59251"
+ x="189.35947"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
+ xml:space="preserve"><tspan
+ y="113.59251"
+ x="189.35947"
+ id="tspan4103"
+ sodipodi:role="line">from</tspan></text>
+</g></svg> \ No newline at end of file
diff --git a/debian/gbp.conf b/debian/gbp.conf
new file mode 100644
index 0000000..89e7f25
--- /dev/null
+++ b/debian/gbp.conf
@@ -0,0 +1,7 @@
+# Configuration file for git-buildpackage and friends
+
+[DEFAULT]
+filter = */.git*
+components = ["libdvdnav-embedded", "libdvdread-embedded", "repo-resources-embedded"]
+debian-branch = debian/sid
+upstream-branch = upstream/latest
diff --git a/debian/gitlab-ci.yml b/debian/gitlab-ci.yml
new file mode 100644
index 0000000..0c22dc4
--- /dev/null
+++ b/debian/gitlab-ci.yml
@@ -0,0 +1,3 @@
+include:
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
diff --git a/debian/headers-check.c b/debian/headers-check.c
new file mode 100644
index 0000000..b81900b
--- /dev/null
+++ b/debian/headers-check.c
@@ -0,0 +1,126 @@
+#include "kodi/AddonBase.h"
+#include "kodi/AudioEngine.h"
+#include "kodi/Filesystem.h"
+#include "kodi/General.h"
+#include "kodi/Network.h"
+#include "kodi/addon-instance/AudioDecoder.h"
+#include "kodi/addon-instance/AudioEncoder.h"
+#include "kodi/addon-instance/Game.h"
+#include "kodi/addon-instance/ImageDecoder.h"
+#include "kodi/addon-instance/Inputstream.h"
+#include "kodi/addon-instance/PVR.h"
+#include "kodi/addon-instance/Peripheral.h"
+#include "kodi/addon-instance/Screensaver.h"
+#include "kodi/addon-instance/VFS.h"
+#include "kodi/addon-instance/VideoCodec.h"
+#include "kodi/addon-instance/Visualization.h"
+#include "kodi/addon-instance/inputstream/DemuxPacket.h"
+#include "kodi/addon-instance/inputstream/StreamCodec.h"
+#include "kodi/addon-instance/inputstream/StreamConstants.h"
+#include "kodi/addon-instance/inputstream/StreamCrypto.h"
+#include "kodi/addon-instance/inputstream/TimingConstants.h"
+#include "kodi/addon-instance/peripheral/PeripheralUtils.h"
+#include "kodi/addon-instance/pvr/ChannelGroups.h"
+#include "kodi/addon-instance/pvr/Channels.h"
+#include "kodi/addon-instance/pvr/EDL.h"
+#include "kodi/addon-instance/pvr/EPG.h"
+#include "kodi/addon-instance/pvr/General.h"
+#include "kodi/addon-instance/pvr/MenuHook.h"
+#include "kodi/addon-instance/pvr/Providers.h"
+#include "kodi/addon-instance/pvr/Recordings.h"
+#include "kodi/addon-instance/pvr/Stream.h"
+#include "kodi/addon-instance/pvr/Timers.h"
+#include "kodi/c-api/addon-instance/audiodecoder.h"
+#include "kodi/c-api/addon-instance/audioencoder.h"
+#include "kodi/c-api/addon-instance/game.h"
+#include "kodi/c-api/addon-instance/imagedecoder.h"
+#include "kodi/c-api/addon-instance/inputstream.h"
+#include "kodi/c-api/addon-instance/inputstream/demux_packet.h"
+#include "kodi/c-api/addon-instance/inputstream/stream_codec.h"
+#include "kodi/c-api/addon-instance/inputstream/stream_constants.h"
+#include "kodi/c-api/addon-instance/inputstream/stream_crypto.h"
+#include "kodi/c-api/addon-instance/inputstream/timing_constants.h"
+#include "kodi/c-api/addon-instance/peripheral.h"
+#include "kodi/c-api/addon-instance/pvr.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_channels.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_channel_groups.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_defines.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_edl.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_epg.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_general.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_menu_hook.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_providers.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_recordings.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_stream.h"
+#include "kodi/c-api/addon-instance/pvr/pvr_timers.h"
+#include "kodi/c-api/addon-instance/screensaver.h"
+#include "kodi/c-api/addon-instance/vfs.h"
+#include "kodi/c-api/addon-instance/video_codec.h"
+#include "kodi/c-api/addon-instance/visualization.h"
+#include "kodi/c-api/addon_base.h"
+#include "kodi/c-api/audio_engine.h"
+#include "kodi/c-api/filesystem.h"
+#include "kodi/c-api/general.h"
+#include "kodi/c-api/gui/controls/button.h"
+#include "kodi/c-api/gui/controls/edit.h"
+#include "kodi/c-api/gui/controls/fade_label.h"
+#include "kodi/c-api/gui/controls/image.h"
+#include "kodi/c-api/gui/controls/label.h"
+#include "kodi/c-api/gui/controls/progress.h"
+#include "kodi/c-api/gui/controls/radio_button.h"
+#include "kodi/c-api/gui/controls/rendering.h"
+#include "kodi/c-api/gui/controls/settings_slider.h"
+#include "kodi/c-api/gui/controls/slider.h"
+#include "kodi/c-api/gui/controls/spin.h"
+#include "kodi/c-api/gui/controls/text_box.h"
+#include "kodi/c-api/gui/definitions.h"
+#include "kodi/c-api/gui/dialogs/context_menu.h"
+#include "kodi/c-api/gui/dialogs/extended_progress.h"
+#include "kodi/c-api/gui/dialogs/filebrowser.h"
+#include "kodi/c-api/gui/dialogs/keyboard.h"
+#include "kodi/c-api/gui/dialogs/numeric.h"
+#include "kodi/c-api/gui/dialogs/ok.h"
+#include "kodi/c-api/gui/dialogs/progress.h"
+#include "kodi/c-api/gui/dialogs/select.h"
+#include "kodi/c-api/gui/dialogs/text_viewer.h"
+#include "kodi/c-api/gui/dialogs/yes_no.h"
+#include "kodi/c-api/gui/general.h"
+#include "kodi/c-api/gui/input/action_ids.h"
+#include "kodi/c-api/gui/list_item.h"
+#include "kodi/c-api/gui/window.h"
+#include "kodi/c-api/network.h"
+#include "kodi/gui/General.h"
+#include "kodi/gui/ListItem.h"
+#include "kodi/gui/Window.h"
+#include "kodi/gui/controls/Button.h"
+#include "kodi/gui/controls/Edit.h"
+#include "kodi/gui/controls/FadeLabel.h"
+#include "kodi/gui/controls/Image.h"
+#include "kodi/gui/controls/Label.h"
+#include "kodi/gui/controls/Progress.h"
+#include "kodi/gui/controls/RadioButton.h"
+#include "kodi/gui/controls/Rendering.h"
+#include "kodi/gui/controls/SettingsSlider.h"
+#include "kodi/gui/controls/Slider.h"
+#include "kodi/gui/controls/Spin.h"
+#include "kodi/gui/controls/TextBox.h"
+#include "kodi/gui/dialogs/ContextMenu.h"
+#include "kodi/gui/dialogs/ExtendedProgress.h"
+#include "kodi/gui/dialogs/FileBrowser.h"
+#include "kodi/gui/dialogs/Keyboard.h"
+#include "kodi/gui/dialogs/Numeric.h"
+#include "kodi/gui/dialogs/OK.h"
+#include "kodi/gui/dialogs/Progress.h"
+#include "kodi/gui/dialogs/Select.h"
+#include "kodi/gui/dialogs/TextViewer.h"
+#include "kodi/gui/dialogs/YesNo.h"
+#include "kodi/gui/gl/GL.h"
+#include "kodi/gui/gl/Shader.h"
+#include "kodi/gui/input/ActionIDs.h"
+#include "kodi/gui/renderHelper.h"
+#include "kodi/tools/DllHelper.h"
+#include "kodi/tools/EndTime.h"
+#include "kodi/tools/StringUtils.h"
+#include "kodi/tools/Thread.h"
+#include "kodi/tools/Timer.h"
+#include "kodi/versions.h"
diff --git a/debian/kodi-addons-dev-common.README.Debian b/debian/kodi-addons-dev-common.README.Debian
new file mode 100644
index 0000000..9d87821
--- /dev/null
+++ b/debian/kodi-addons-dev-common.README.Debian
@@ -0,0 +1,2 @@
+The current PVR API version can be found in xbmc_pvr_types.h:
+XBMC_PVR_API_VERSION
diff --git a/debian/kodi-addons-dev-common.install b/debian/kodi-addons-dev-common.install
new file mode 100644
index 0000000..476a4e6
--- /dev/null
+++ b/debian/kodi-addons-dev-common.install
@@ -0,0 +1,3 @@
+usr/include/kodi
+usr/share/kodi/cmake/*.cmake
+debian/dh-addon/kodiaddon.pm usr/share/perl5/Debian/Debhelper/Sequence/
diff --git a/debian/kodi-addons-dev.install b/debian/kodi-addons-dev.install
new file mode 100644
index 0000000..de58205
--- /dev/null
+++ b/debian/kodi-addons-dev.install
@@ -0,0 +1,2 @@
+debian/dh-addon/dh_kodiaddon_depends usr/bin/
+usr/lib/*/kodi/cmake
diff --git a/debian/kodi-addons-dev.manpages b/debian/kodi-addons-dev.manpages
new file mode 100644
index 0000000..f83eeca
--- /dev/null
+++ b/debian/kodi-addons-dev.manpages
@@ -0,0 +1 @@
+debian/dh-addon/*.1
diff --git a/debian/kodi-bin.install b/debian/kodi-bin.install
new file mode 100644
index 0000000..72f940b
--- /dev/null
+++ b/debian/kodi-bin.install
@@ -0,0 +1,3 @@
+usr/lib/*/kodi/system
+usr/lib/*/kodi/kodi-xrandr
+usr/lib/*/kodi/kodi.bin
diff --git a/debian/kodi-bin.lintian-overrides b/debian/kodi-bin.lintian-overrides
new file mode 100644
index 0000000..28c9f96
--- /dev/null
+++ b/debian/kodi-bin.lintian-overrides
@@ -0,0 +1,20 @@
+# bindnow breaks wrappers used with libdvdread
+hardening-no-bindnow [usr/lib/*/kodi/kodi.bin]
+hardening-no-bindnow [usr/lib/*/kodi/kodi-xrandr]
+
+# kodi{.bin,-xrandr} are invoked by scripts /usr/bin/kodi*
+executable-in-usr-lib [usr/lib/*/kodi/kodi.bin]
+executable-in-usr-lib [usr/lib/*/kodi/kodi-xrandr]
+
+# Spelling errors in lib/libUPnP that can not be touched by Kodi
+# (https://github.com/xbmc/xbmc/pull/18100#pullrequestreview-436935700)
+spelling-error-in-binary Paramater Parameter [usr/lib/*/kodi/kodi.bin]
+spelling-error-in-binary ment meant [usr/lib/*/kodi/kodi.bin]
+spelling-error-in-binary unexected unexpected [usr/lib/*/kodi/kodi.bin]
+spelling-error-in-binary develope develop [usr/lib/*/kodi/kodi.bin]
+spelling-error-in-binary installe installer [usr/lib/*/kodi/kodi.bin]
+spelling-error-in-binary protocolL protocol [usr/lib/*/kodi/kodi.bin]
+spelling-error-in-binary rovided provided [usr/lib/*/kodi/kodi.bin]
+
+# The library in question really does not depend on external code
+shared-library-lacks-prerequisites [usr/lib/*/kodi/system/libsse4-*.so]
diff --git a/debian/kodi-bin.manpages b/debian/kodi-bin.manpages
new file mode 100644
index 0000000..ae989bf
--- /dev/null
+++ b/debian/kodi-bin.manpages
@@ -0,0 +1 @@
+docs/manpages/kodi.bin.1
diff --git a/debian/kodi-data.install b/debian/kodi-data.install
new file mode 100644
index 0000000..f9a237d
--- /dev/null
+++ b/debian/kodi-data.install
@@ -0,0 +1,11 @@
+usr/lib/firewalld/services/kodi-jsonrpc.xml
+usr/lib/firewalld/services/kodi-http.xml
+usr/lib/firewalld/services/kodi-eventserver.xml
+usr/share/kodi/addons
+usr/share/kodi/media
+usr/share/kodi/privacy-policy.txt
+usr/share/kodi/system
+usr/share/kodi/userdata
+usr/share/icons
+usr/share/kodi/addons/skin.*
+usr/share/xsessions/kodi.desktop
diff --git a/debian/kodi-data.links b/debian/kodi-data.links
new file mode 100644
index 0000000..bed7947
--- /dev/null
+++ b/debian/kodi-data.links
@@ -0,0 +1,7 @@
+usr/share/fonts/truetype/noto/NotoSans-Regular.ttf usr/share/kodi/addons/skin.estouchy/fonts/NotoSans-Regular.ttf
+usr/share/fonts/truetype/noto/NotoMono-Regular.ttf usr/share/kodi/addons/skin.estuary/fonts/NotoMono-Regular.ttf
+usr/share/fonts/truetype/noto/NotoSans-Bold.ttf usr/share/kodi/addons/skin.estuary/fonts/NotoSans-Bold.ttf
+usr/share/fonts/truetype/noto/NotoSans-Regular.ttf usr/share/kodi/addons/skin.estuary/fonts/NotoSans-Regular.ttf
+usr/share/fonts/truetype/roboto/hinted/Roboto-Thin.ttf usr/share/kodi/addons/skin.estuary/fonts/Roboto-Thin.ttf
+usr/share/javascript/jquery/jquery.min.js /usr/share/kodi/addons/webinterface.default/js/jquery-1.8.2.min.js
+usr/share/javascript/iscroll/iscroll-min.js /usr/share/kodi/addons/webinterface.default/js/iscroll-min.js
diff --git a/debian/kodi-data.lintian-overrides b/debian/kodi-data.lintian-overrides
new file mode 100644
index 0000000..1209223
--- /dev/null
+++ b/debian/kodi-data.lintian-overrides
@@ -0,0 +1,14 @@
+# Merged fonts used by Kodi as fallback
+font-in-non-font-package [usr/share/kodi/media/Fonts/arial.ttf]
+font-in-non-font-package [usr/share/kodi/media/Fonts/teletext.ttf]
+font-outside-font-dir [usr/share/kodi/media/Fonts/arial.ttf]
+font-outside-font-dir [usr/share/kodi/media/Fonts/teletext.ttf]
+
+# The majority of files found by this check are either changelog.txt or
+# other text files used by addon code (like versions.txt). The changelog.txt
+# is used by Kodi GUI to display the version history for the installed addons.
+# Despite https://github.com/xbmc/xbmc/pull/9351 merged, big part of community
+# insists on restoring the old behavior and this may happen before 19.0 final
+# release. Please note that this override guards only addons code, so text files
+# and documentation in other parts of Kodi will still be reported if found.
+package-contains-documentation-outside-usr-share-doc [usr/share/kodi/addons/*]
diff --git a/debian/kodi-eventclients-common.install b/debian/kodi-eventclients-common.install
new file mode 100644
index 0000000..291a7e8
--- /dev/null
+++ b/debian/kodi-eventclients-common.install
@@ -0,0 +1 @@
+usr/share/pixmaps/kodi
diff --git a/debian/kodi-eventclients-dev-common.examples b/debian/kodi-eventclients-dev-common.examples
new file mode 100644
index 0000000..6e4cc71
--- /dev/null
+++ b/debian/kodi-eventclients-dev-common.examples
@@ -0,0 +1 @@
+usr/share/doc/kodi/kodi-eventclients-dev/examples/*
diff --git a/debian/kodi-eventclients-dev-common.install b/debian/kodi-eventclients-dev-common.install
new file mode 100644
index 0000000..14e3505
--- /dev/null
+++ b/debian/kodi-eventclients-dev-common.install
@@ -0,0 +1 @@
+usr/include/kodi/xbmcclient.h
diff --git a/debian/kodi-eventclients-dev.install b/debian/kodi-eventclients-dev.install
new file mode 100644
index 0000000..2b7551e
--- /dev/null
+++ b/debian/kodi-eventclients-dev.install
@@ -0,0 +1 @@
+usr/lib/*/kodi/eventclients-dev
diff --git a/debian/kodi-eventclients-kodi-send.install b/debian/kodi-eventclients-kodi-send.install
new file mode 100644
index 0000000..0d4bff9
--- /dev/null
+++ b/debian/kodi-eventclients-kodi-send.install
@@ -0,0 +1 @@
+usr/bin/kodi-send
diff --git a/debian/kodi-eventclients-kodi-send.lintian-overrides b/debian/kodi-eventclients-kodi-send.lintian-overrides
new file mode 100644
index 0000000..7a665c7
--- /dev/null
+++ b/debian/kodi-eventclients-kodi-send.lintian-overrides
@@ -0,0 +1,2 @@
+# It is Python package dropping files in /usr/bin
+package-contains-no-arch-dependent-files
diff --git a/debian/kodi-eventclients-kodi-send.manpages b/debian/kodi-eventclients-kodi-send.manpages
new file mode 100644
index 0000000..18b822e
--- /dev/null
+++ b/debian/kodi-eventclients-kodi-send.manpages
@@ -0,0 +1 @@
+docs/manpages/kodi-send.1
diff --git a/debian/kodi-eventclients-ps3.install b/debian/kodi-eventclients-ps3.install
new file mode 100644
index 0000000..66bc049
--- /dev/null
+++ b/debian/kodi-eventclients-ps3.install
@@ -0,0 +1,2 @@
+usr/bin/kodi-ps3remote
+usr/lib/python*/*/kodi/ps3
diff --git a/debian/kodi-eventclients-ps3.lintian-overrides b/debian/kodi-eventclients-ps3.lintian-overrides
new file mode 100644
index 0000000..e4c91cf
--- /dev/null
+++ b/debian/kodi-eventclients-ps3.lintian-overrides
@@ -0,0 +1,6 @@
+# Python scripts meant to be run by end-user
+executable-in-usr-lib [usr/lib/python3/dist-packages/kodi/ps3/sixpair.py]
+executable-in-usr-lib [usr/lib/python3/dist-packages/kodi/ps3/sixwatch.py]
+
+# It is Python package dropping files in /usr/bin
+package-contains-no-arch-dependent-files
diff --git a/debian/kodi-eventclients-ps3.manpages b/debian/kodi-eventclients-ps3.manpages
new file mode 100644
index 0000000..a78c6b3
--- /dev/null
+++ b/debian/kodi-eventclients-ps3.manpages
@@ -0,0 +1 @@
+docs/manpages/kodi-ps3remote.1
diff --git a/debian/kodi-eventclients-python.install b/debian/kodi-eventclients-python.install
new file mode 100644
index 0000000..7e241a0
--- /dev/null
+++ b/debian/kodi-eventclients-python.install
@@ -0,0 +1 @@
+usr/lib/python*/*/kodi
diff --git a/debian/kodi-eventclients-python.lintian-overrides b/debian/kodi-eventclients-python.lintian-overrides
new file mode 100644
index 0000000..7a665c7
--- /dev/null
+++ b/debian/kodi-eventclients-python.lintian-overrides
@@ -0,0 +1,2 @@
+# It is Python package dropping files in /usr/bin
+package-contains-no-arch-dependent-files
diff --git a/debian/kodi-eventclients-wiiremote.install b/debian/kodi-eventclients-wiiremote.install
new file mode 100644
index 0000000..96ac87a
--- /dev/null
+++ b/debian/kodi-eventclients-wiiremote.install
@@ -0,0 +1 @@
+usr/bin/kodi-wiiremote
diff --git a/debian/kodi-eventclients-wiiremote.lintian-overrides b/debian/kodi-eventclients-wiiremote.lintian-overrides
new file mode 100644
index 0000000..981ca75
--- /dev/null
+++ b/debian/kodi-eventclients-wiiremote.lintian-overrides
@@ -0,0 +1,2 @@
+# bindnow breaks wrappers used with libdvdread
+hardening-no-bindnow [usr/bin/kodi-wiiremote]
diff --git a/debian/kodi-eventclients-wiiremote.manpages b/debian/kodi-eventclients-wiiremote.manpages
new file mode 100644
index 0000000..ca4297b
--- /dev/null
+++ b/debian/kodi-eventclients-wiiremote.manpages
@@ -0,0 +1 @@
+docs/manpages/kodi-wiiremote.1
diff --git a/debian/kodi-eventclients-zeroconf.install b/debian/kodi-eventclients-zeroconf.install
new file mode 100644
index 0000000..3aadf53
--- /dev/null
+++ b/debian/kodi-eventclients-zeroconf.install
@@ -0,0 +1 @@
+usr/lib/python*/*/kodi/zeroconf.py
diff --git a/debian/kodi-eventclients-zeroconf.lintian-overrides b/debian/kodi-eventclients-zeroconf.lintian-overrides
new file mode 100644
index 0000000..9826a0f
--- /dev/null
+++ b/debian/kodi-eventclients-zeroconf.lintian-overrides
@@ -0,0 +1,5 @@
+# It is Python package
+package-contains-no-arch-dependent-files
+
+# and it can be run by end-user
+executable-in-usr-lib [usr/lib/python3/dist-packages/kodi/zeroconf.py]
diff --git a/debian/kodi-tools-texturepacker.install b/debian/kodi-tools-texturepacker.install
new file mode 100644
index 0000000..588194f
--- /dev/null
+++ b/debian/kodi-tools-texturepacker.install
@@ -0,0 +1 @@
+usr/bin/*TexturePacker
diff --git a/debian/kodi-tools-texturepacker.lintian-overrides b/debian/kodi-tools-texturepacker.lintian-overrides
new file mode 100644
index 0000000..8fc7313
--- /dev/null
+++ b/debian/kodi-tools-texturepacker.lintian-overrides
@@ -0,0 +1,2 @@
+# bindnow breaks wrappers used with libdvdread
+hardening-no-bindnow [usr/bin/*TexturePacker]
diff --git a/debian/kodi-tools-texturepacker.manpages b/debian/kodi-tools-texturepacker.manpages
new file mode 100644
index 0000000..df26bba
--- /dev/null
+++ b/debian/kodi-tools-texturepacker.manpages
@@ -0,0 +1 @@
+docs/manpages/*TexturePacker.1
diff --git a/debian/kodi.install b/debian/kodi.install
new file mode 100644
index 0000000..c98127c
--- /dev/null
+++ b/debian/kodi.install
@@ -0,0 +1,4 @@
+usr/bin/kodi
+usr/bin/kodi-standalone
+usr/share/applications
+usr/share/metainfo
diff --git a/debian/kodi.lintian-overrides b/debian/kodi.lintian-overrides
new file mode 100644
index 0000000..5cb2ecb
--- /dev/null
+++ b/debian/kodi.lintian-overrides
@@ -0,0 +1,5 @@
+# /usr/bin/kodi is a script referencing /usr/lib/$(DEB_ARCH)/kodi
+package-contains-no-arch-dependent-files
+
+# Kodi uses metainfo file to store the info as it is translatable
+desktop-entry-lacks-keywords-entry
diff --git a/debian/kodi.manpages b/debian/kodi.manpages
new file mode 100644
index 0000000..f201e5e
--- /dev/null
+++ b/debian/kodi.manpages
@@ -0,0 +1,2 @@
+docs/manpages/kodi.1
+docs/manpages/kodi-standalone.1
diff --git a/debian/kodi.mime b/debian/kodi.mime
new file mode 100644
index 0000000..ed9a199
--- /dev/null
+++ b/debian/kodi.mime
@@ -0,0 +1,19 @@
+video/mpeg; kodi %s; description="MPEG Video";
+video/x-mpeg; kodi %s; description="MPEG Video";
+video/mpeg-system; kodi %s; description="MPEG Video";
+video/x-mpeg-system; kodi %s; description="MPEG Video";
+audio/x-wav; kodi %s; description="WAV Audio"; nametemplate=%s.wav;
+video/mpeg4; kodi %s; description="MPEG-4 Video";
+audio/mpeg; kodi %s; description="MPEG Audio"; nametemplate=%s.mpg;
+audio/mpegurl; kodi %s; description="MPEG Audio URL"; nametemplate=%s.m3u;
+audio/x-mp3; kodi %s; nametemplate=%s.mp3; description="MPEG Audio";
+audio/mpeg4; kodi %s; description="MPEG-4 Audio";
+application/mpeg4-iod; kodi %s; description="MPEG-4 Video";
+application/mpeg4-muxcodetable; kodi %s; description="MPEG-4 Video";
+video/x-msvideo; kodi %s; description="MS Video (AVI)";
+video/quicktime; kodi %s; description="Apple Quicktime Video";
+application/ogg; kodi %s; nametemplate=%s.ogg; description="Ogg stream";
+application/x-ogg; kodi %s; nametemplate=%s.ogg; description="Ogg stream";
+video/ogg; kodi %s; description="Ogg Video";
+application/x-ms-asf-plugin; kodi %s; description="Windows Media Video";
+application/x-mplayer2; kodi %s; description="Windows Media";
diff --git a/debian/mergefonts.ff b/debian/mergefonts.ff
new file mode 100755
index 0000000..c1a43c0
--- /dev/null
+++ b/debian/mergefonts.ff
@@ -0,0 +1,6 @@
+#!/usr/bin/fontforge
+# merge $1 and $2 to $3 fonts
+Open($1);
+ScaleToEm(2048)
+MergeFonts($2);
+Generate($3);
diff --git a/debian/not-installed b/debian/not-installed
new file mode 100644
index 0000000..a3906d4
--- /dev/null
+++ b/debian/not-installed
@@ -0,0 +1,7 @@
+# README and version.txt are superseded by manpages
+usr/share/doc/kodi/LICENSE.md
+usr/share/doc/kodi/README.Linux.md
+usr/share/doc/kodi/version.txt
+
+# JsonSchemaBuilder is not intended for end-user
+usr/bin/JsonSchemaBuilder
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"
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..b11b575
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,294 @@
+#!/usr/bin/make -f
+
+# Avoid custom build rule silencing
+export V=1
+
+# Include /usr/share/dpkg/pkg-info.mk to get DEB_{SOURCE,VERSION}
+include /usr/share/dpkg/pkg-info.mk
+
+# Include /usr/share/dpkg/architecture.mk to get DEB_HOST_{MULTI,}ARCH
+include /usr/share/dpkg/architecture.mk
+
+# bindnow breaks wrappers used in with libdvdread
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all,-bindnow
+
+DVDNAV_COMPONENT = libdvdnav-embedded
+DVDNAV_URL = tools/depends/target/libdvdnav/libdvdnav-embedded.tar.xz
+DVDREAD_COMPONENT = libdvdread-embedded
+DVDREAD_URL = tools/depends/target/libdvdread/libdvdread-embedded.tar.xz
+
+export DEB_CFLAGS_MAINT_STRIP = -O2
+export DEB_CFLAGS_MAINT_APPEND = -O3
+export DEB_CXXFLAGS_MAINT_STRIP = -O2
+export DEB_CXXFLAGS_MAINT_APPEND = -O3
+
+BUILD_WAYLAND := yes
+BUILD_GBM := yes
+
+NUMCPU =
+# Support parallel=<n> in DEB_BUILD_OPTIONS (see #209008)
+ifneq (,$(filter parallel=%,$(subst $(COMMA), ,$(DEB_BUILD_OPTIONS))))
+ NUMCPU := $(subst parallel=,,$(filter parallel=%,$(subst $(COMMA), ,$(DEB_BUILD_OPTIONS))))
+endif
+
+# Various environment variables to set
+export DEB_LDFLAGS_MAINT_APPEND = -latomic -lpthread
+# for embedded libdvd* and Kodi from Debian patch:
+export DEB_CPPFLAGS_MAINT_APPEND = -D_XBMC -DDEB_VERSION=\"$(DEB_VERSION)\"
+
+KODI_PLATFORMS := x11
+
+ifeq ($(BUILD_WAYLAND),yes)
+ KODI_PLATFORMS := $(KODI_PLATFORMS) wayland
+endif
+ifeq ($(BUILD_GBM),yes)
+ KODI_PLATFORMS := $(KODI_PLATFORMS) gbm
+endif
+
+# basilgello: keep these overrides to ensure CMake honors
+# the 32-bit architecture requested by 'dpkg-buildpackage'
+# (like 'i486-linux') on 64-bit machines (like 'amd64').
+ifneq (,$(filter alpha,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=alpha
+else ifneq (,$(filter amd64,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=x86_64-linux
+else ifneq (,$(filter arm64,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=aarch64
+else ifneq (,$(filter armel armhf,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=arm
+else ifneq (,$(filter i386,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=i486-linux
+else ifneq (,$(filter m68k,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=m68k
+else ifneq (,$(filter mipsel,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=mips
+else ifneq (,$(filter mips64el,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=mips64
+else ifneq (,$(filter powerpc,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=ppc
+else ifneq (,$(filter ppc64,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=ppc64
+else ifneq (,$(filter ppc64el,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=ppc64
+else ifneq (,$(filter riscv64,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=riscv64
+else ifneq (,$(filter s390x,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=s390x
+# TODO: Workaround for #1043076 - remove when resolved
+export DEB_CFLAGS_MAINT_APPEND = -O3 -fexcess-precision=fast
+export DEB_CXXFLAGS_MAINT_APPEND = -O3 -fexcess-precision=fast
+else ifneq (,$(filter sh4,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=sh4
+else ifneq (,$(filter sparc64,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DWITH_ARCH=sparc64
+endif
+
+# Build arm* architectures with GLES instead of GL
+# (Closes: 1056563)
+ifneq (,$(filter armel armhf arm64,$(DEB_HOST_ARCH)))
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DAPP_RENDER_SYSTEM=gles
+else
+EXTRA_FLAGS := $(EXTRA_FLAGS) -DAPP_RENDER_SYSTEM=gl
+endif
+
+# basilgello: If cross-compiling, tell Kodi to look for native prerequisites
+# in $(CURDIR)/native-tools, where the override_dh_autoconfigure step builds
+# them for the build architecture (the native architecture of builder machine)
+ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+EXTRA_FLAGS := $(EXTRA_FLAGS) \
+ -DWITH_JSONSCHEMABUILDER=$(CURDIR)/native-tools/bin/JsonSchemaBuilder \
+ -DWITH_TEXTUREPACKER=$(CURDIR)/native-tools/bin/TexturePacker
+endif
+
+# Kodi configuration options
+#
+KODI_OPTS=\
+-DVERBOSE=1 \
+-DUSE_LTO=$(NUMCPU) \
+-DCMAKE_BUILD_TYPE=RelWithDebInfo \
+-DENABLE_AIRTUNES=ON \
+-DENABLE_ALSA=ON \
+-DENABLE_AVAHI=ON \
+-DENABLE_BLURAY=ON \
+-DENABLE_CEC=ON \
+-DENABLE_DBUS=ON \
+-DENABLE_DEBUGFISSION=OFF \
+-DENABLE_DVDCSS=OFF \
+-DENABLE_EVENTCLIENTS=ON \
+-DENABLE_INTERNAL_CROSSGUID=OFF \
+-DENABLE_INTERNAL_DATE=OFF \
+-DENABLE_INTERNAL_FFMPEG=OFF \
+-DENABLE_INTERNAL_KISSFFT=OFF \
+-DENABLE_INTERNAL_LIBDVD=ON \
+-DENABLE_INTERNAL_RapidJSON=OFF \
+-DENABLE_INTERNAL_TZDATA=OFF \
+-DENABLE_MICROHTTPD=ON \
+-DENABLE_MYSQLCLIENT=ON \
+-DENABLE_NFS=ON \
+-DENABLE_OPTICAL=ON \
+-DENABLE_PULSEAUDIO=ON \
+-DENABLE_SMBCLIENT=ON \
+-DENABLE_UDEV=ON \
+-DENABLE_UPNP=ON \
+-DENABLE_VAAPI=ON \
+-DENABLE_VDPAU=ON \
+-DENABLE_XSLT=ON \
+-DLIBDVDREAD_URL=$(DVDREAD_URL) \
+-DLIBDVDNAV_URL=$(DVDNAV_URL) \
+-DENABLE_LIRCCLIENT=ON \
+-DNEON=False \
+-DCORE_PLATFORM_NAME="$(KODI_PLATFORMS)" \
+$(EXTRA_FLAGS)
+
+%:
+ dh $@ --buildsystem cmake --with python3
+
+override_dh_clean:
+ #‌ Clean the optional language packs originating in repo-resources-embedded
+ #‌ We rely on the fact that there are no optional resource.language.* in
+ # system/addon-manifest.xml
+ cd repo-resources-embedded && for i in resource.language.*; do \
+ if grep -q "<addon optional=\"true\">$$i</addon>" \
+ $(CURDIR)/system/addon-manifest.xml; \
+ then \
+ echo "Cleaning language pack $$i"; \
+ rm -rf $(CURDIR)/addons/$$i; \
+ sed -i "/<addon optional=\"true\">$$i</d" \
+ $(CURDIR)/system/addon-manifest.xml; \
+ sed -i "/$$i\//d" \
+ $(CURDIR)/cmake/installdata/common/addons.txt; \
+ fi; \
+ done
+ # ‌Perform the rest of cleaning
+ dh_clean \
+ addons/skin.estuary/media/from-debian-logo.png \
+ addons/webinterface.default/ \
+ debian/dh-addon/*.1 \
+ lib/gtest/ \
+ media/Fonts/arial.ttf \
+ native-tools/ \
+ tools/depends/native/JsonSchemaBuilder/native/ \
+ tools/depends/native/JsonSchemaBuilder/.installed-native \
+ tools/depends/native/TexturePacker/native/ \
+ tools/depends/native/TexturePacker/.installed-native \
+ $(DVDNAV_URL) \
+ $(DVDREAD_URL)
+
+# fall back to old location of DejaVuSans.ttf if needed
+ifneq ("$(wildcard /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf)","")
+DEJAVUSANS=/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
+else
+DEJAVUSANS=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf
+endif
+
+media/Fonts/arial.ttf: debian/mergefonts.ff
+ # work around #948876 until fontforge is fixed by using cached result
+ fontforge -script $< \
+ /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf \
+ $(DEJAVUSANS) \
+ $@ || cp debian/extra/arial.ttf $@
+
+lib/gtest/lib/.libs/libgtest.a:
+ cp -r /usr/src/gtest/ $(CURDIR)/lib/ && mkdir -p $(CURDIR)/lib/gtest/lib/.libs
+
+addons/skin.estuary/media/from-debian-logo.png: debian/from-debian-logo.svg
+ rsvg-convert -w 250 -h 82 -o $@ $<
+
+embedded-libs:
+ #‌ Copy default webinterface addon
+ cp -r $(CURDIR)/debian/webinterface-default $(CURDIR)/addons/webinterface.default
+ #‌ Copy the optional language packs originating in repo-resources-embedded
+ cd repo-resources-embedded && for i in resource.language.*; do \
+ echo "Adding language pack $$i"; \
+ cp -r $$i $(CURDIR)/addons/$$i; \
+ sed -i "/<\/addons>/i\ \ <addon optional=\"true\">$$i<\/addon>" \
+ $(CURDIR)/system/addon-manifest.xml; \
+ echo "addons/$$i/*"‌ \
+ 1>>$(CURDIR)/cmake/installdata/common/addons.txt; \
+ done
+ #‌ Prepare archives from embedded libdvd*
+ tar -cf $(DVDNAV_URL) $(DVDNAV_COMPONENT)
+ tar -cf $(DVDREAD_URL) $(DVDREAD_COMPONENT)
+
+override_dh_auto_configure: addons/skin.estuary/media/from-debian-logo.png embedded-libs lib/gtest/lib/.libs/libgtest.a media/Fonts/arial.ttf
+ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+ # We are cross-compiling!
+ # Produce the JsonSchemaBuilder and TexturePacker
+ # prerequisites for the build architecture
+ cd tools/depends/native/JsonSchemaBuilder && \
+ make NATIVEPREFIX=$(CURDIR)/native-tools
+ cd tools/depends/native/TexturePacker && \
+ make NATIVEPREFIX=$(CURDIR)/native-tools
+endif
+ dh_auto_configure -- $(KODI_OPTS) \
+ -DLIBDVDREAD_HASH="SHA256=$(shell sha256sum $(DVDREAD_URL) | cut -d' ' -f1)" \
+ -DLIBDVDNAV_HASH="SHA256=$(shell sha256sum $(DVDNAV_URL) | cut -d' ' -f1)"
+
+override_dh_auto_build:
+ dh_auto_build
+ for file in $$(ls debian/dh-addon/dh_*); do \
+ pod2man --section=1 --utf8 $$file $$file.1; \
+ done
+
+override_dh_auto_test-arch:
+ dh_auto_build -- kodi-test
+ dh_auto_test -a
+
+# No tests for indep build.
+override_dh_auto_test-indep:
+
+override_dh_install-indep:
+ # Perform arch-independent install
+ dh_install -i -XLICENCE -XLICENSE \
+ -XLicence.txt -XLicense.txt -XLICENSE.txt \
+ -XNOTICE.txt \
+ -Xjquery-1.8.2.min.js -Xiscroll-min.js
+ # Remove kodi-repository-kodi part from kodi-data
+ rm -rf debian/kodi-data/usr/share/kodi/addons/repository.xbmc.org
+ # Remove documentation-outside-usr-share-doc but not from addons
+ rm -f debian/kodi-data/usr/share/kodi/privacy-policy.txt
+ # Install kodi-repository-kodi
+ dh_install -pkodi-repository-kodi \
+ addons/repository.xbmc.org/* usr/share/kodi/addons/repository.xbmc.org
+ # ship xbmcclient.h only in kodi-eventclients-dev
+ rm -f debian/kodi-addons-dev-common/usr/include/kodi/xbmcclient.h
+ # check if all needed headers are included
+ $(CXX) -E -c debian/headers-check.c -DBUILD_KODI_ADDON \
+ $(shell dpkg-buildflags --get CPPFLAGS) \
+ $(shell dpkg-buildflags --get CXXFLAGS) \
+ -Idebian/kodi-addons-dev-common/usr/include \
+ -o /dev/null
+
+override_dh_install-arch:
+ # Prepare kodi-eventclients-dev dummy file
+ # see debian/control for explanation of package purpose
+ touch debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/kodi/eventclients-dev
+ # Prepare kodi/system dummy file
+ # this is necessary to compensate absence of libsse4.so on non-x86
+ mkdir -p debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/kodi/system
+ touch debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/kodi/system/dummy
+ # Perform arch-specific install
+ dh_install -a -XLICENCE -XLICENSE -XLicence.txt -XLicense.txt -XLICENSE.txt -XNOTICE.txt
+ # Drop executable flag from all Python scripts except:
+ # ps3_remote.py
+ # sixpair.py
+ # sixwatch.py
+ # zeroconf.py
+ find debian/kodi-eventclients-*/ \
+ ! \( -name "ps3_remote.py" -o -name "sixpair.py" -o -name "sixwatch.py" -o -name "zeroconf.py" \) \
+ -name "*.py" -exec chmod 0644 '{}' \;
+ # Remove ps3 and zeroconf.py from kodi-eventclients-python
+ rm -rf debian/kodi-eventclients-python/usr/lib/python*/*/kodi/ps3
+ rm -f debian/kodi-eventclients-python/usr/lib/python*/*/kodi/ps3_remote.py
+ rm -f debian/kodi-eventclients-python/usr/lib/python*/*/kodi/zeroconf.py
+
+# dwz fails to create multifile dwarfs with '-gstrip-dwarf'
+override_dh_dwz:
+
+override_dh_shlibdeps:
+ dh_shlibdeps -a \
+ -l$(CURDIR)/debian/kodi-bin/usr/lib/*/kodi/system/players/dvdplayer
+
+override_dh_gencontrol:
+ debian/dh-addon/dh_kodiaddon_depends
+ dh_gencontrol
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/source/include-binaries b/debian/source/include-binaries
new file mode 100644
index 0000000..f37c721
--- /dev/null
+++ b/debian/source/include-binaries
@@ -0,0 +1,8 @@
+debian/extra/arial.ttf
+debian/webinterface-default/icon.png
+debian/webinterface-default/favicon.ico
+debian/webinterface-default/images/remote.jpg
+debian/webinterface-default/images/ajax-loader.gif
+debian/webinterface-default/images/DefaultVideo.png
+debian/webinterface-default/images/DefaultAlbumCover.png
+debian/webinterface-default/images/close-button.png
diff --git a/debian/source/options b/debian/source/options
new file mode 100644
index 0000000..9cdfca9
--- /dev/null
+++ b/debian/source/options
@@ -0,0 +1,2 @@
+unapply-patches
+abort-on-upstream-changes
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..d81c78d
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,3 @@
+Tests: gui
+Depends: imagemagick, kodi, kodi-eventclients-kodi-send, xauth, xvfb
+Restrictions: isolation-container, allow-stderr
diff --git a/debian/tests/gui b/debian/tests/gui
new file mode 100755
index 0000000..785fe74
--- /dev/null
+++ b/debian/tests/gui
@@ -0,0 +1,23 @@
+#!/bin/sh
+# autopkgtest check: Run kodi GUI to see basic functionality working
+# Author: Balint Reczey <balint.reczey@canonical.com>
+# Modified by Vasyl Gello <vasek.gello@gmail.com>
+
+set -e
+
+export HOME=$AUTOPKGTEST_TMP
+KODI_LOG="$HOME/.kodi/temp/kodi.log"
+
+trap 'cat $KODI_LOG' EXIT
+
+[ ! -e $KODI_LOG ] || (echo "ERROR: $KODI_LOG" already exists! ; exit 1)
+
+xvfb-run -e /dev/stdout -s '-screen 0 1920x1080x24 +extension GLX' kodi &
+while test ! -e $KODI_LOG; do
+ sleep 1
+done
+tail -f -n +0 $KODI_LOG | grep -m 1 'Listening on port 9777'
+kodi-send --host=localhost --port=9777 --action="Quit"
+wait
+
+echo "run: OK"
diff --git a/debian/upstream/metadata b/debian/upstream/metadata
new file mode 100644
index 0000000..fc20964
--- /dev/null
+++ b/debian/upstream/metadata
@@ -0,0 +1,7 @@
+Bug-Database: https://github.com/xbmc/xbmc/issues
+Bug-Submit: https://github.com/xbmc/xbmc/issues/new
+Contact: interest@kodi.tv
+FAQ: https://kodi.wiki/view/Main_Page
+Repository: https://github.com/xbmc/xbmc.git
+Repository-Browse: https://github.com/xbmc/xbmc
+Donation: https://kodi.tv/contribute/donate
diff --git a/debian/upstream/signing-key.asc b/debian/upstream/signing-key.asc
new file mode 100644
index 0000000..1091ddf
--- /dev/null
+++ b/debian/upstream/signing-key.asc
@@ -0,0 +1,25 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGiBFD9w2QRBACoEzH9KKirWE4wgiuPPynNnxks+p+t5i1z3CG+1XhagmTHoOf3
+v8i19kKHV6WnVMn2CKJFgwTTLYXOJTrBM/4ABVtu11cHeeueeo+pCSkdoLzYJ5QF
+HbByB6j33QUbwKF0frEs+ge4LxzvYyCDAmNAW560QtOAR9Lk1Fo5B1GXzwCg1kDk
+RkSe7EOZNm1U2rYAQ2VPrfsEAIHr4ooOyUByPR7XpoDOKoaXEG0hjpgh46lbgse+
+dQx8YrxS9vXQLwYokfWLrs55avx9Ys0iVv2TMv7X4Tn5sTVaK5K+NbKhxhLORxGI
+sgKqRn7W5SG5xoO0w/dmQj756ppjITGbxjFuhYE0X5S6NeMhUuFci7sJ42R7F1Ko
+6sYuA/wOMUxCk4XOXeQF16ApyyenjE/UWbBNEhBmjEsZkYAFNc89pAEnEFSnIxK8
+fcuCQioM6ojjaW+aEs/q3/klI0nat9LMLhNSCebjriMHwJDU70NeCn4nPWsfItT1
+eKvbHNcX+3bq3D/i2Wa3PZ5YFFF01C61dHmVC9YGh4sAOXO09LQjVmlkZW9MQU4g
+UmVsZWFzZSBTaWduaW5nIEtleSAoMjAxMymIaAQTEQIAKAIbAwYLCQgHAwIGFQgC
+CQoLBBYCAwECHgECF4AFAlcfgjEFCQgC8jUACgkQcYBxO+WNGtws1wCfdckVSmW/
+7G+0CNufK+4ZKSXW9BoAoLaxPIFa2qpZdmDr5eci5gdXJI1mtCNWaWRlb0xBTiBS
+ZWxlYXNlIFNpZ25pbmcgS2V5ICgyMDE0KYhpBBMRAgApAhsDBwsJCAcDAgEGFQgC
+CQoLBBYCAwECHgECF4AFAlcfgjEFCQgC8jUACgkQcYBxO+WNGtwkYACgmifLHzLQ
+rbevTlGVWnAfn8AyW8MAn1AIh61iO83YycA1fYoP1sOPBCK/tCNWaWRlb0xBTiBS
+ZWxlYXNlIFNpZ25pbmcgS2V5ICgyMDE1KYhqBBMRAgAqAhsDBQsJCAcCBhUICQoL
+AgQWAgMBAh4BAheAAhkBBQJXH4IjBQkIAvI1AAoJEHGAcTvljRrcSjsAoL/BM6qq
+KwWv2DwdqLd+XRpYlrpyAJ0c43AR1uE2UoMhkUu3vsX75fqELbQjVmlkZW9MQU4g
+UmVsZWFzZSBTaWduaW5nIEtleSAoMjAxNimIaAQTEQIAKAIbAwYLCQgHAwIGFQgC
+CQoLBBYCAwECHgECF4AFAlbGNhQFCQeppiwACgkQcYBxO+WNGtwQ3wCfb8IB8Mdt
+ahoyr5OjvOQL6IQFuUcAoMCyREB9Yn5oLU+6OSnarOAHwzvq
+=QHfT
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..856ef01
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,75 @@
+version=4
+
+# Alpha bare git branch
+#
+# Since v20, bare git snapshots must follow the tagged release cycle:
+#
+# pretty=20.0~alphaX+git%cd.%h, \
+# pretty=20.0~betaX+git%cd.%h, \
+# pretty=20.0~rcX+git%cd.%h, \
+#
+# where X starts with zero. The 'pretty' attribute must be changed manually
+# after the correspondent tagged release is published.
+#
+#opts="mode=git, \
+# pgpmode=none, \
+# pretty=20.0~alpha0+git%cd.%h, \
+# repack, \
+# repacksuffix=+dfsg, \
+# compression=xz, \
+# uversionmangle=s/\([\.0-9a-zA-Z]\)-.*$/\1/;s/rc/~rc/;s/a/~alpha/;s/b/~beta/;s/RC/~rc/;s/A/~alpha/;s/B/~beta/, \
+# dversionmangle=auto" \
+#https://github.com/xbmc/xbmc \
+#HEAD \
+#debian
+
+# Release git tags
+# TODO: Change Kodi codename in refs/tags before packaging next Kodi major release
+
+opts="mode=git, \
+ pgpmode=none, \
+ repack, \
+ repacksuffix=+dfsg, \
+ compression=xz, \
+ uversionmangle=s/\([\.0-9a-zA-Z]\)-.*$/\1/;s/rc/~rc/;s/a/~alpha/;s/b/~beta/;s/RC/~rc/;s/A/~alpha/;s/B/~beta/, \
+ dversionmangle=auto" \
+https://github.com/xbmc/xbmc \
+refs/tags/@ANY_VERSION@-Nexus \
+debian
+
+# libdvdread
+
+opts="component=libdvdread-embedded, \
+ mode=git, \
+ pretty=%cd.%h, \
+ repack, \
+ compression=xz, \
+ dversionmangle=auto" \
+https://code.videolan.org/videolan/libdvdread \
+heads/master \
+ignore
+
+# libdvdnav
+
+opts="component=libdvdnav-embedded, \
+ mode=git, \
+ pretty=%cd.%h, \
+ repack, \
+ compression=xz, \
+ dversionmangle=auto" \
+https://code.videolan.org/videolan/libdvdnav \
+heads/master \
+ignore
+
+# repo-resources
+# TODO: Change Kodi codename in heads/ before packaging next Kodi major release
+
+opts="component=repo-resources-embedded, \
+ mode=git, \
+ pretty=%cd.%h, \
+ repack, \
+ compression=xz, \
+ dversionmangle=auto" \
+https://github.com/xbmc/repo-resources \
+heads/nexus \
+ignore
diff --git a/debian/webinterface-default/addon.xml b/debian/webinterface-default/addon.xml
new file mode 100644
index 0000000..e61262a
--- /dev/null
+++ b/debian/webinterface-default/addon.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+ id="webinterface.default"
+ version="2.2.33"
+ name="Default webinterface"
+ provider-name="Team Kodi">
+ <requires>
+ <import addon="xbmc.json" version="6.0.0"/>
+ </requires>
+ <extension
+ point="xbmc.webinterface"/>
+ <extension point="xbmc.addon.metadata">
+ <summary lang="af_ZA">Span Kodi Web Koppelvlak. (Kodi se verstek web koppelvlak)</summary>
+ <summary lang="am_ET">የ Kodi የዌብ ገጽታዎች (የ Kodi ነባር የዌብ ገጽታዎች)</summary>
+ <summary lang="ar_SA">واجهه فريق إكس بي إم سي للشبكة العنكبوتية (واجهه الشبكة العنكبوتية المبدئية لـ إكس بي إم سي)</summary>
+ <summary lang="be_BY">Team Kodi Web Interface. (Kodi's default web interface)</summary>
+ <summary lang="bg_BG">Уеб интерфейс на Team Kodi. (стандартният уеб интерфейс на Kodi)</summary>
+ <summary lang="ca_ES">Interfície web de l'equip Kodi. (la interfície web per defecte del Kodi)</summary>
+ <summary lang="cs_CZ">Webové rozhraní týmu Kodi (Výchozí webové rozhraní Kodi).</summary>
+ <summary lang="cy_GB">Rhyngwyneb Gwe Team Kodi (Rhyngwyneb gwe rhagosodedig Kodi)</summary>
+ <summary lang="da_DK">Team Kodi Web-grænseflade. (Kodi's standard webgrænseflade)</summary>
+ <summary lang="de_DE">Team Kodi Webinterface. (Standardwebinterface von Kodi)</summary>
+ <summary lang="el_GR">Διεπαφή Ιστού της Team Kodi. (Προεπιλεγμένη διεπαφή ιστού του Kodi)</summary>
+ <summary lang="en_AU">Team Kodi Web Interface. (Kodi's default web interface)</summary>
+ <summary lang="en_GB">Team Kodi Web Interface. (Kodi's default web interface)</summary>
+ <summary lang="en_NZ">Team Kodi Web Interface. (Kodi's default web interface)</summary>
+ <summary lang="en_US">Team Kodi Web Interface. (Kodi's default web interface)</summary>
+ <summary lang="es_AR">Interfaz Web del Equipo de Kodi. (Interfaz web por defecto de Kodi)</summary>
+ <summary lang="es_ES">Interfaz web de Kodi. (Interfaz web por defecto de Kodi)</summary>
+ <summary lang="es_MX">Interfaz Web del Equipo de Kodi (Interfaz Web por defecto de Kodi)</summary>
+ <summary lang="et_EE">Kodi meeskonna veebikeskkond. (Vaikimisi keskkond)</summary>
+ <summary lang="eu_ES">Kodi taldean Web Interfazea. (Kodi-ren lehenetsiriko web interfazea)</summary>
+ <summary lang="fa_AF">نمایه وب تیم Kodi.(رابط پیش فرض Kodi)</summary>
+ <summary lang="fa_IR">رابط وب تیم Kodi. (رابط وب استاندارد Kodi)</summary>
+ <summary lang="fi_FI">Team Kodin web-käyttöliittymä. (Kodin oletus-web-käyttöliittymä)</summary>
+ <summary lang="fr_CA">Interface Web de l'équipe Kodi. (Interface Web par défaut de Kodi)</summary>
+ <summary lang="fr_FR">Interface Web de l'Équipe Kodi (interface d'origine de Kodi)</summary>
+ <summary lang="gl_ES">Interface web da equipa de Kodi. (Interface web predefinida de Kodi)</summary>
+ <summary lang="he_IL">ממשק הדפדפן הרשמי של Kodi. (ברירת המחדל)</summary>
+ <summary lang="hr_HR">Tim Kodi Web sučelje. (Kodijevo uobičajeno web sučelje)</summary>
+ <summary lang="hu_HU">Az Kodi csapat Webes kezelőfelülete (Kodi alapértelmezett webes kezelőfelülete)</summary>
+ <summary lang="id_ID">Antar Muka Web Tim Kodi. (Antar muka web bawaan Kodi)</summary>
+ <summary lang="is_IS">Team Kodi Vefviðmót. (Sjálfgefið vefviðmót á Kodi)</summary>
+ <summary lang="it_IT">Interfaccia Web del Team Kodi. (Interfaccia web di default per Kodi)</summary>
+ <summary lang="ja_JP">Team Kodi Web Interface. (Kodi's default web interface)</summary>
+ <summary lang="ko_KR">Team Kodi 웹 인터페이스. (Kodi 기본 웹 인터페이스)</summary>
+ <summary lang="lt_LT">Komandos Kodi Web sąsaja.(Kodi numatytoji žiniatinklio sąsaja)</summary>
+ <summary lang="lv_LV">Team Kodi tīmekļa saskarne. (Kodi noklusētā tīmekļa saskarne)</summary>
+ <summary lang="mk_MK">Веб интерфејс на Kodi тимот. (Kodi's подразбирачки веб интерфејс)</summary>
+ <summary lang="mn_MN">Kodi багийн вэб интерфэйс. (Kodi-ийн үндсэн вэб интерфэйс)</summary>
+ <summary lang="ms_MY">Antaramuka Sesawang Pasukan-Kodi (antaramuka sesawang lalai Kodi) </summary>
+ <summary lang="mt_MT">Web interface ta' Team Kodi. (Il-Web interface normali ta' Kodi)</summary>
+ <summary lang="my_MM">Team Kodi ဝက်ဘ် အသွင်အပြင် (Kodi ၏ မူရင် ဝက်ဘ် အသွင်အပြင်)</summary>
+ <summary lang="nb_NO">Team Kodi nettgrensesnitt. (Kodis standard nettgrensesnitt)</summary>
+ <summary lang="nl_NL">Team Kodi Webinterface. (Kodi's standaard webinterface)</summary>
+ <summary lang="pl_PL">Interfejs webowy Kodi. (Domyślny)</summary>
+ <summary lang="pt_BR">Interface Web da Equipe Kodi. (interface web padrão do Kodi)</summary>
+ <summary lang="pt_PT">Interface da Web da Team Kodi. (Interface da Web predefinida do Kodi)</summary>
+ <summary lang="ro_RO">Interfață web Kodi. (interfață web implicită pentru Kodi)</summary>
+ <summary lang="ru_RU">Веб-интерфейс от разработчиков Kodi (веб-интерфейс Kodi по умолчанию).</summary>
+ <summary lang="sk_SK">Webové rozhranie tímu Kodi. (predvolené webové rozhranie pre Kodi)</summary>
+ <summary lang="sl_SI">Spletni vmesnik ekipe Kodi. (Privzet spletni vmesnik za Kodi)</summary>
+ <summary lang="sr_RS">Web интерфејс Kodi Тима (подразуевани web интерфејс Kodi-jа)</summary>
+ <summary lang="sr_RS@latin">Web interfejs Kodi Tima (podrazuevani web interfejs Kodija)</summary>
+ <summary lang="sv_SE">Team Kodi webbgränsnitt. (Kodi's standardwebbgränssnitt)</summary>
+ <summary lang="szl">Webowy interfejs ôd Team Kodi (wychodny webowy interface we Kodi).</summary>
+ <summary lang="ta_IN">கோடி குழுமத்தின் இணைய இடைமுகம். (கோடியின் நிரந்தர இணைய இடைமுகம்)</summary>
+ <summary lang="te_IN">టీమ్-కోడి వెబ్ ఇంటర్ఫేస్. (కోడి యొక్క డిఫాల్ట్ వెబ్ ఇంటర్ఫేస్)</summary>
+ <summary lang="tg_TJ">Интерфейси веби гурӯҳи кории Kodi Web. (Интерфейси веби пешфарз барои Kodi)</summary>
+ <summary lang="th_TH">ทีมส่วนประสานหน้าเว็บ Kodi (ส่วนประสานหน้าเว็บพื้นฐานของ Kodi)</summary>
+ <summary lang="tr_TR">Kodi Takımı Web Arayüzü. (Kodi'nin varsayılan web arayüzü)</summary>
+ <summary lang="uk_UA">Веб-інтерфейс Kodi (Типовий веб-інтерфейс для Kodi)</summary>
+ <summary lang="uz_UZ">Kodi guruhi veb Interfeysi. (Kodi andoza veb interfeysi)</summary>
+ <summary lang="vi_VN">Đội ngũ giao diện Web Kodi. (Giao diện Web mặc định của Kodi)</summary>
+ <summary lang="zh_CN">Kodi 团队开发的 Web 界面。(Kodi 的默认 web 界面)</summary>
+ <summary lang="zh_TW">Kodi團隊網頁介面。(Kodi的預設網頁介面)</summary>
+ <description lang="af_ZA">Verstek web koppelvlak vir Kodi; Ontwerp vir toestelle van alle resolusies</description>
+ <description lang="am_ET">ነባር የዌብ ገጽታዎች ለ Kodi ፡ የተነደፈው ለሁሉም አካላቶች resolutions ነው </description>
+ <description lang="ar_SA">واجهه الشبكة العنكبوتية المبدئية لـ إكس بي إم سي، مُصمَمة لتناسب كافة قياسات أبعاد الشاشات</description>
+ <description lang="be_BY">Стандартны вэб-інтэрфэйс да Kodi; распрацаваны для прыладаў зь любымі разрознасьцямі</description>
+ <description lang="bg_BG">Стандартния уеб интерфейс на Kodi; Направен е за устройства с най-различни резолюции</description>
+ <description lang="ca_ES">Interfície web per defecte del Kodi; Dissenyada per dispositius de totes les resolucions</description>
+ <description lang="cs_CZ">Výchozí webové rozhraní Kodi navržené pro zařízení s libovolným rozlišením</description>
+ <description lang="cy_GB">Rhyngwyneb gwe rhagosodedig Kodi. Wedi ei gynllunio ar gyfer cydraniad pob dyfais</description>
+ <description lang="da_DK">Standard webgrænseflade til Kodi; designet til enheder i alle opløsninger</description>
+ <description lang="de_DE">Standard Kodi Webinterface - Für alle Geräte und Auflösungen geeignet</description>
+ <description lang="el_GR">Προεπιλεγμένη διεπαφή ιστού για το Kodi. Σχεδιασμένη για συσκευές όλων των αναλύσεων.</description>
+ <description lang="en_AU">Default web interface for Kodi; Designed for devices of all resolutions</description>
+ <description lang="en_GB">Default web interface for Kodi; Designed for devices of all resolutions</description>
+ <description lang="en_NZ">Default web interface for Kodi; Designed for devices of all resolutions</description>
+ <description lang="en_US">Default web interface for Kodi; Designed for devices of all resolutions</description>
+ <description lang="es_AR">Interfaz web por defecto de Kodi. Diseñada para dispositivos de cualquier resolución</description>
+ <description lang="es_ES">Interfaz web por defecto de Kodi. Diseñada para dispositivos de cualquier resolución</description>
+ <description lang="es_MX">Interfaz Web por defecto para Kodi; Diseñado para dispositivos de todas las resoluciones</description>
+ <description lang="et_EE">Kodi vaikimisi veebikeskkond; Disainitud erinevate resolutsioonidega seadmetele.</description>
+ <description lang="eu_ES">Kodi-ren lehenetsiriko web interfazea. Erresoluzio guztietako gailentzat diseinatua</description>
+ <description lang="fa_AF">رابط پیش فرض Kodiوطراحی شده برای هر رزولیشن</description>
+ <description lang="fa_IR">رابط استاندارد وب برای Kodi. قابل استفاده برای همه دستگاه ها و همه رزولوشن ها</description>
+ <description lang="fi_FI">Kodin oletus-web-käyttöliittymä. Suunniteltu käytettäväksi kaiken kokoisilla laitteilla.</description>
+ <description lang="fr_CA">Interface Web par défaut de Kodi - Conçue pour les appareils de toutes résolutions</description>
+ <description lang="fr_FR">Interface Web d'origine de Kodi ; adaptée aux appareils de toutes résolutions</description>
+ <description lang="gl_ES">Interface web predefinida para Kodi, deseñada para dispositivos de todas as resolucións</description>
+ <description lang="he_IL">ממשק דפדפן ברירת המחדל עבור Kodi; מותאם למכשירים בעלי אבחנות שונות</description>
+ <description lang="hr_HR">Uobičajeno web sučelje za Kodi; Dizajnirano za uređaje svih razlučivosti</description>
+ <description lang="hu_HU">A Kodi alapértelmezett webes kezelőfelülete; Minden felbontásra alkalmas</description>
+ <description lang="hy_AM">Նախնական տեսք Kodi-ի համար; Նախատեսված է բոլոր կրիչների համար</description>
+ <description lang="id_ID">Antar muka web bawaan bagi Kodi; Dirancang untuk perangkat dengan semua resolusi</description>
+ <description lang="is_IS">Sjálfgefið vefviðmót fyrir Kodi; Hannað fyrir tæki með allskonar upplausnir</description>
+ <description lang="it_IT">Interfaccia Web di default per Kodi; Realizzata per periferiche con qualunque risoluzione</description>
+ <description lang="ja_JP">Kodi のデフォルト Web インターフェースです。さまざまな解像度のデバイス用にデザインされています。</description>
+ <description lang="ko_KR">Kodi 기본 웹 인터페이스; 모든 해상도의 기기를 위해 디자인 됨</description>
+ <description lang="lt_LT">Numatytoji Kodi Web sąsaja skirta visų rezoliucijų prietaisams</description>
+ <description lang="lv_LV">Noklusētā Kodi tīmekļa sakarne; Piemērota visu izšķirtspēju iekārtām</description>
+ <description lang="mk_MK">Подразбирачки веб интерфејс за Kodi; Дизајниран за уреди со сите резолуции</description>
+ <description lang="mn_MN">Kod-ийн үндсэн вэб интерфэйс; Бүх төрлийн нарийвчлалтай төхөөрөмжид зориулагдсан.</description>
+ <description lang="ms_MY">Antaramuka sesawang lalai untuk Kodi; Direka untuk peranti pelbagai resolusi</description>
+ <description lang="mt_MT">Il-Web interface in-normali ta' Kodi; Iddiżinjata għal apparat ta' kull reżoluzzjoni</description>
+ <description lang="my_MM">Kodi အတွက် မူရင်း ဝက်ဘ် အသွင်ပြင် ၊ စက်များ အားလုံး၏ resolutions အတွက် ရေးဆွဲထားသည်။</description>
+ <description lang="nb_NO">Standard nettgrensesnitt for Kodi; Laget for alle oppløsninger</description>
+ <description lang="nl_NL">Standaard Kodi-webinterface; geschikt voor alle resoluties</description>
+ <description lang="pl_PL">Domyślny interfejs webowy Kodi; Zaprojektowany dla urządzeń o ekranach w dowolnej rozdzielczości</description>
+ <description lang="pt_BR">Interface web padrão para Kodi; Concebida para dispositivos de todas as resoluções</description>
+ <description lang="pt_PT">Interface da Web ppredefinida do Kodi; Criada para dispositivos de todas as resoluções.</description>
+ <description lang="ro_RO">Interfață web implicită pentru Kodi, realizată de echipa Kodi pentru dispozitive cu orice rezoluție.</description>
+ <description lang="ru_RU">Веб-интерфейс Kodi по умолчанию. Разработан для устройств с любыми разрешениями.</description>
+ <description lang="si_LK">Kodi' සඳහා නියත වෙබ් අතුරුමුහුණත; සෑම විභේදනයකම උපකරණ සඳහා නිර්මාණය කර ඇත</description>
+ <description lang="sk_SK">Predvolené webové rozhranie pre Kodi; Navrhnuté pre zariadenia zobrazujúce v akomkoľvek rozlíšení</description>
+ <description lang="sl_SI">Privzet spletni vmesnik za Kodi, ustvarjen za naprave vseh ločljivosti</description>
+ <description lang="sr_RS">Подразумевани web интерфејс Kodi; Намењен уређајима свих резолуција</description>
+ <description lang="sr_RS@latin">Podrazumevani web interfejs Kodi; Namenjen uređajima svih rezolucija</description>
+ <description lang="sv_SE">Standardwebbgränssnitt för Kodi; Utformat för alla enheter oavsett skärmupplösning</description>
+ <description lang="szl">Wychodny webowy interface we Kodi. Zrychtowany dlŏ maszin we wszyjskich rozdziylczościach.</description>
+ <description lang="ta_IN">கோடி இற்கான நிரந்தர இணைய இடைமுகம்; சகல சாதனங்களின் திரைகளுக்கும் வடிவமைக்கப்பட்டது</description>
+ <description lang="te_IN">కోడి డిఫాల్ట్ వెబ్ ఇంటర్ఫేస్; అన్ని వైశాల్యాలు పరికరాల కోసం రూపొందించబడింది</description>
+ <description lang="tg_TJ">Интерфейси веби пешфарз барои Kodi; Барои ҳар гуна дастгоҳ бо возеҳии экранаш гуногун эҷод карда шудааст</description>
+ <description lang="th_TH">ส่วนประสานหน้าเว็บพื้นฐานสำหรับ Kodi ได้ออกแบบรองรับความละเอียดหน้าจอของอุปกรณ์ทั้งหมด</description>
+ <description lang="tr_TR">Kodi için varsayılan web arayüzü; Bütün çözünürlüklerde tüm cihazlar için dizayn edilmiştir.</description>
+ <description lang="uk_UA">Типовий веб-інтерфейс для Kodi; Розроблений для пристроїв з будь-якою роздільною здатністю</description>
+ <description lang="uz_UZ">Kodi uchun andoza veb interfeysi; barcha ekran o'lchamlari bilan uskunalar uchun yaratilgan</description>
+ <description lang="vi_VN">Giao diện Web mặc định cho Kodi; Thiết kế cho các thiết bị ở tất cả độ phân giải khác nhau</description>
+ <description lang="zh_CN">Kodi 的默认 web 界面;为支持所有分辨率的设备而设计</description>
+ <description lang="zh_TW">Kodi的預設網頁介面;針對所有解析度的設備而設計</description>
+ <platform>all</platform>
+ </extension>
+</addon>
diff --git a/debian/webinterface-default/css/core.css b/debian/webinterface-default/css/core.css
new file mode 100644
index 0000000..5371c4a
--- /dev/null
+++ b/debian/webinterface-default/css/core.css
@@ -0,0 +1,794 @@
+body {
+ font-family: Arial, Verdana, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+#header {
+ position: relative;
+ height: 50px;
+ border-bottom: 1px solid #000;
+ z-index: 200;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVIAAAAxCAYAAACIwiGvAAAACXBIWXMAAAsTAAALEwEAmpwYAAA7r2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS41LWMwMjEgNzkuMTU1NzcyLCAyMDE0LzAxLzEzLTE5OjQ0OjAwICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAxNC0xMi0wNVQxNDozMDoyNyswMTowMDwveG1wOkNyZWF0ZURhdGU+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE0LTEyLTA2VDE4OjI2OjUwKzAxOjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0xMi0wNlQxODoyNjo1MCswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgICAgPHBob3Rvc2hvcDpDb2xvck1vZGU+MzwvcGhvdG9zaG9wOkNvbG9yTW9kZT4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+eG1wLmlpZDozZGEwMjM1Zi1iOTdlLTQ3ODMtOTZjMy0xMDBhYzMzYWI5OGQ8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPmFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2YmMwNTFmNC1iY2I0LTExNzctYTRhYy1lNmU3M2I3NjVlMDI8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDo1ZTc3Y2MyYS00OTE2LTQ4ZjctOGJiZC05YjYyM2Q1NTc1YjU8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jcmVhdGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NWU3N2NjMmEtNDkxNi00OGY3LThiYmQtOWI2MjNkNTU3NWI1PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTEyLTA1VDE0OjMwOjI3KzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NDIyYTA5YzQtZjMwZi00OGM3LWFlYTUtMDZmYzk3NmZlMTk3PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTEyLTA1VDE0OjQwOjQ5KzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6M2RhMDIzNWYtYjk3ZS00NzgzLTk2YzMtMTAwYWMzM2FiOThkPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTEyLTA2VDE4OjI2OjUwKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj43MjAwMDAvMTAwMDA8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjcyMDAwMC8xMDAwMDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT42NTUzNTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MzM4PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjQ5PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg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') 1px 1px no-repeat #fff;
+}
+
+#remoteContainer {
+ background: url("../images/remote.jpg") no-repeat;
+ width: 659px;
+ height: 213px;
+ position: relative;
+ margin: auto;
+ margin-top: 50px;
+}
+
+.remote_key {
+ cursor: pointer;
+ position: absolute;
+}
+
+#navigation {
+ float: right;
+}
+
+#spinner {
+ float: right;
+ padding: 11px 10px;
+}
+
+#commsErrorPanel {
+ float: left;
+ line-height: 50px;
+ background: #c00;
+ color: #fff;
+ font-size: 20px;
+ padding-left: 10px;
+ width: 330px;
+}
+
+#navigation ul {
+ list-style-type: none;
+ border-right: 1px solid #969696;
+ margin: 0;
+ padding: 0;
+}
+
+#navigation ul li {
+ float: left;
+ color: #000;
+ cursor: pointer;
+ line-height: 50px;
+ margin: 0;
+ padding: 0 24px;
+ border-left: 1px solid #969696;
+ font-family: Verdana, sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+}
+
+#navigation ul li.selected,
+#navigation ul li:hover {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAzCAIAAADZxfV4AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjgxMkRGQzczQkFDMDExREY5RDE0QzJCMjZBM0JGMURBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjgxMkRGQzc0QkFDMDExREY5RDE0QzJCMjZBM0JGMURBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ODEyREZDNzFCQUMwMTFERjlEMTRDMkIyNkEzQkYxREEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6ODEyREZDNzJCQUMwMTFERjlEMTRDMkIyNkEzQkYxREEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4vOcxrAAAAVUlEQVR42lyMUQ7AMAhCF+9/px5MIFNpl2YfREF8z1orSAaAYAnUnsdzdiR+vRLsuzNZ36bLyLM3p36zuhpWzp/7Zn/c5tA8zc3eeUvXbi/tXHwFGAC2dpagcbC63AAAAABJRU5ErkJggg%3D%3D') repeat-x;
+ color: #fff;
+}
+
+.floatableAlbum {
+ float: left;
+ width: 130px;
+ height: 150px;
+ padding: 10px 10px 15px 10px;
+}
+
+.floatableMovieCover,
+.floatableProfileThumb {
+ float: left;
+ width: 130px;
+ height: 200px;
+ padding: 10px;
+}
+
+.floatableAlbum:hover,
+.floatableTVShowCover:hover,
+.floatableMovieCover:hover,
+.floatableProfileThumb:hover {
+ background: #aeaeae;
+}
+
+.albumView .floatableAlbum:hover {
+ background: transparent;
+}
+
+.tvshowContainer .floatableTVShowCover:hover {
+ background: #fff;
+}
+
+.floatableTVShowCover {
+ float: left;
+ padding: 10px;
+ width: 379px;
+ height: 70px;
+}
+
+.floatableTVShowCoverSeason {
+ width: 500px;
+ height: 70px;
+ margin-top: 30px;
+}
+
+.toggle .activeMode {
+ font-weight: 700;
+}
+
+#tvshowLibraryContainer {
+ padding-top: 40px;
+}
+
+#libraryContainer .floatableAlbum,
+#movieLibraryContainer .floatableMovieCover,
+#profilesContainer .floatableProfileThumb,
+#tvshowLibraryContainer .floatableTVShowCover {
+ cursor: pointer;
+}
+
+.floatableAlbum div.imgWrapper,
+.floatableMovieCover div.imgWrapper,
+.floatableProfileThumb div.imgWrapper,
+.floatableTVShowCover div.imgWrapper {
+ width: 130px;
+ height: 130px;
+ display: table-cell;
+ vertical-align: middle;
+ text-align: center;
+ overflow: hidden;
+}
+
+.floatableTVShowCoverSeason div.imgWrapper {
+ margin: auto;
+ height: 70px;
+ width: 379px;
+ display: block;
+}
+
+div.imgWrapper div.inner {
+ overflow: hidden;
+ width: 130px;
+}
+
+.floatableMovieCover div.imgWrapper,
+.floatableMovieCover div.imgWrapper div.inner,
+.floatableProfileThumb div.imgWrapper,
+.floatableProfileThumb div.imgWrapper div.inner {
+ height: 190px;
+}
+
+#overlay {
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 150px;
+ background: #3f3f3f;
+ position: fixed;
+ opacity: 0.8;
+ z-index: 2000;
+ /* Above contentContainer's */
+}
+
+.floatableTVShowCover div.imgWrapper,
+.floatableTVShowCover img,
+.floatableTVShowCover div.imgWrapper div.inner,
+.floatableTVShowCoverSeason div.imgWrapper,
+.floatableTVShowCoverSeason img,
+.floatableTVShowCoverSeason div.imgWrapper div.inner {
+ height: 70px;
+ width: 379px;
+}
+
+.floatableAlbum img {
+ width: 130px;
+}
+
+.floatableMovieCover img,
+.floatableProfileThumb img {
+ height: 180px;
+}
+
+.floatableAlbum p.album,
+.floatableMovieCover p.album,
+.floatableProfileThumb p.album {
+ font-size: 12px;
+ font-weight: 700;
+ color: #000;
+ text-align: center;
+ margin: 0 -5px;
+ padding: 0;
+ width: 130px;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.floatableAlbum p.artist,
+.floatableMovieCover p.artist,
+.floatableProfileThumb p.artist {
+ font-size: 11px;
+ color: #777;
+ text-align: center;
+ margin: 0;
+ padding: 0;
+}
+
+.contentContainer {
+ overflow-x: hidden;
+ overflow-y: auto;
+ position: absolute;
+ top: 51px;
+ bottom: 1px;
+ left: 0;
+ right: 0;
+ background: #fff;
+ padding-bottom: 149px;
+}
+
+.footerPadding {
+ clear: both;
+}
+
+.albumContainer {
+ top: 74px;
+}
+
+.albumView .trackRow td,
+.seasonView .episodeRow td {
+ cursor: pointer;
+ line-height: 14px;
+ font-size: 14px;
+ padding: 1px 0;
+ padding-left: 4px;
+}
+
+.albumView .tr0,
+.seasonView .tr0 {
+ background-color: #efefef;
+}
+
+.albumView .tr0:hover,
+.albumView .tr1:hover,
+.seasonView .tr0:hover,
+.seasonView .tr1:hover {
+ background-color: blue;
+ color: #fff;
+}
+
+.seasonView .tr0:hover td.info,
+.seasonView .tr1:hover td.info {
+ background-color: cyan;
+}
+
+.albumView,
+.seasonView {
+ width: 100%;
+ height: 100%;
+ border-collapse: collapse;
+}
+
+.seasonView td.episodeThumb {
+ text-align: center;
+}
+
+.seasonView td.episodeNumber {
+ text-align: left;
+}
+
+.seasonView td.info {
+ background: url('data:image/png;base64,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%3D%3D') no-repeat;
+ background-size: 40%;
+ background-position: 50% 50%;
+}
+
+.seasonView td.episodeThumb img {
+ height: 30px;
+}
+
+.albumView td.albumThumb {
+ padding-left: 0px;
+ border-right: 1px solid #aeaeae;
+}
+
+.albumView td.time,
+.albumView th.time {
+ text-align: right;
+ padding-right: 4px;
+}
+
+.albumView tr.headerRow,
+#albumSelector table {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAXCAIAAABF+LJYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjk4NTQ0NDZGQ0U1ODExREZCMjM4ODgyQjJEQTE5ODBBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjk4NTQ0NDcwQ0U1ODExREZCMjM4ODgyQjJEQTE5ODBBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTg1NDQ0NkRDRTU4MTFERkIyMzg4ODJCMkRBMTk4MEEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTg1NDQ0NkVDRTU4MTFERkIyMzg4ODJCMkRBMTk4MEEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7FuFIdAAAAOklEQVR42kyMUQoAMAhC0+j+J+s2ffXTVtDYA1EUpLsjImhmVFU2IsKqGm1uz8zxX7sBeF1zv44AAwDVBhSDNN7oJQAAAABJRU5ErkJggg%3D%3D') repeat-x;
+}
+
+.albumView th,
+.seasonView th {
+ font-size: 11px;
+ text-align: left;
+ border-left: 1px solid #aeaeae;
+ border-bottom: 1px solid #aeaeae;
+ padding-left: 4px;
+ height: 13px;
+ padding-top: 1px;
+}
+
+.albumView tr.headerRow,
+.seasonView tr.headerRow {
+ background-position: 0 -1px;
+}
+
+.seasonView tr.headerRow th.thumbHeader {
+ width: 40px;
+}
+
+.albumView .albumThumb,
+.albumView .albumBG {
+ width: 120px;
+ background: #efefef;
+ border-right: 1px solid #aeaeae;
+ vertical-align: top;
+}
+
+.albumView .fillerTrackRow2 td {
+ height: 100%;
+ line-height: 100%;
+}
+
+/* Album Selector */
+
+#albumSelector {
+ height: 23px;
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+ font-weight: 700;
+}
+
+#albumSelector table {
+ width: 100%;
+ border: 1px solid #aeaeae;
+ border-top: 0px;
+ height: 23px;
+ border-collapse: collapse;
+}
+
+#albumSelector td {
+ padding-top: 2px;
+}
+
+#albumSelector .prevAlbum,
+#albumSelector .allAlbums {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAIAAADJDItPAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkJCOEEyQzlGQ0U1QTExREY4MUI0RjIyREY1QUFBQTlBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkJCOEEyQ0EwQ0U1QTExREY4MUI0RjIyREY1QUFBQTlBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkI4QTJDOURDRTVBMTFERjgxQjRGMjJERjVBQUFBOUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkI4QTJDOUVDRTVBMTFERjgxQjRGMjJERjVBQUFBOUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6Hnv/HAAAA1ElEQVR42mxQOw5EUBSd9/IyCxA2YQlaO1JbgkVIiIpKgR1oNBqdEKERVD7xmzkzk5gYc6r7zn3nnnMvWdf1dkHf923bchxHH2eM49g0je/7qqoOw8AOxbIs6CVJYppmnudgtm1j+75DhGllWdq2HYbhIQDPpmmCjed5ruvO8/wTghVFoWlalmXXgPCiPM8riiLLMiFkPQM/SNd1sEfINE0ty4rj+FDruv5qfx4wxrQoigzDqKoKDIrvYvc3JEkSRTEIAsdxKKUEsf9era5rQRCeAgwAxPWf44ZS0FcAAAAASUVORK5CYII%3D') no-repeat;
+}
+
+/* This must appear before #albumSelector .allAlbums */
+
+#albumSelector .allAlbums {
+ width: 94px;
+ border-right: 1px solid #aeaeae;
+ text-align: center;
+ padding-left: 10px;
+ cursor: pointer;
+ background-position: 10px center;
+}
+
+#albumSelector .activeAlbumTitle {
+ text-align: center;
+}
+
+#albumSelector .nextAlbum {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAALCAIAAADJDItPAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdCMUE3MDBGQ0U1QzExREZBREU0QkQ5NzdFRENBNkQxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjdCMUE3MDEwQ0U1QzExREZBREU0QkQ5NzdFRENBNkQxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6N0IxQTcwMERDRTVDMTFERkFERTRCRDk3N0VEQ0E2RDEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6N0IxQTcwMEVDRTVDMTFERkFERTRCRDk3N0VEQ0E2RDEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz62tw0dAAAA3UlEQVR42myQPQ5EUBSF573414jQUEh0Go1GrRGFKKiESuxCYztWYAMqy6ATEg0NmbkiMWTmlPede75zH5rneZomURR5nn/9CC/LUhRFXdfjOK7r+n4K9X2f5zkYNU1LksQwDJZlSZI8t1HXdVmWXWm2bUdRpKoqsBBCBIz2fb+em6Zp29b3fc/zoBAG3v4UTKqqKssSgo/tbdvubQHsOE4QBJIkEVDvHm6aZhzHuq5zHIcx/rIVRUnT1LIsaE5R1OkmwELTdBiGrusKgsAwzB10HDYMgyzLf3/tI8AAgcVrkw2eqPgAAAAASUVORK5CYII%3D') no-repeat;
+}
+
+#albumSelector .prevAlbum,
+#albumSelector .nextAlbum {
+ width: 28px;
+ border-left: 1px solid #aeaeae;
+ cursor: pointer;
+ background-position: center center;
+}
+
+/* Movie Overlay */
+
+.moviePopoverContainer,
+.episodePopoverContainer {
+ z-index: 3000;
+ /* Above overlay */
+ border: 1px solid #000;
+ padding: 10px;
+ margin: 10px;
+ position: fixed;
+ background: #3f3f3f;
+ top: 50px;
+ bottom: 150px;
+ left: 10%;
+ right: 10%;
+ opacity: 0.9;
+}
+
+.episodePopoverContainer {
+ bottom: none;
+ top: 30%;
+}
+
+.moviePopoverContainer .closeButton,
+.episodePopoverContainer .closeButton {
+ float: right;
+ cursor: pointer;
+}
+
+.moviePopoverContainer .movieCover {
+ height: 100%;
+ padding-right: 20px;
+ float: left;
+ z-index: 3100;
+}
+
+.episodePopoverContainer .episodeCover {
+ width: 40%;
+ padding-right: 20px;
+ float: left;
+ z-index: 3100;
+}
+
+.moviePopoverContainer .movieTitle,
+.episodePopoverContainer .episodeTitle {
+ font-size: 24px;
+ font-weight: 700;
+ color: #fff;
+ margin: 0;
+}
+
+.moviePopoverContainer .runtime,
+.moviePopoverContainer .director,
+.moviePopoverContainer .genre,
+.moviePopoverContainer .plot,
+.episodePopoverContainer .runtime,
+.episodePopoverContainer .director,
+.episodePopoverContainer .genre,
+.episodePopoverContainer .episode,
+.episodePopoverContainer .season,
+.episodePopoverContainer .plot {
+ color: #fff;
+}
+
+.movieTitle .year,
+.episodeTitle .year {
+ font-weight: 400;
+ font-size: 18px;
+}
+
+.playIcon {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAH4AAAB+CAYAAADiI6WIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY3NEJBNzU2QkM0OTExREZBNjg4RTExODNGRTMxNzlDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjY3NEJBNzU3QkM0OTExREZBNjg4RTExODNGRTMxNzlDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Njc0QkE3NTRCQzQ5MTFERkE2ODhFMTE4M0ZFMzE3OUMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6Njc0QkE3NTVCQzQ5MTFERkE2ODhFMTE4M0ZFMzE3OUMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6EY6YgAAAd50lEQVR42uxdCYwc1Zn+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%3D') center center no-repeat;
+ position: absolute;
+ z-index: 3500;
+ cursor: pointer;
+ opacity: 0.8;
+ width: 100px;
+}
+
+
+.playIcon:hover {
+ opacity: 1;
+}
+
+/* Effects */
+
+#topScrollFade {
+ position: fixed;
+ top: 51px;
+ height: 33px;
+ z-index: 101;
+ left: 0;
+ right: 0;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAhCAYAAAAYucG/AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjYwREM0NTA2QkM1MzExREY4OEFDQUU5NTc5MTFCNkUxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjYwREM0NTA3QkM1MzExREY4OEFDQUU5NTc5MTFCNkUxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjBEQzQ1MDRCQzUzMTFERjg4QUNBRTk1NzkxMUI2RTEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NjBEQzQ1MDVCQzUzMTFERjg4QUNBRTk1NzkxMUI2RTEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4NLytwAAAANElEQVR42mL4DwRMDEAAIv6AiN8g4hec+AkifmAhvoOIb6isb3jFvmHRhs3kn6gu+A0QYACIHCJCHDLYgwAAAABJRU5ErkJggg%3D%3D') top left repeat-x;
+}
+
+/* Now Playing */
+
+#footerPopover {
+ position: fixed;
+ height: 150px;
+ bottom: 0;
+ left: 0;
+ right: 16px;
+ z-index: 10000;
+ /* Top most always */
+ background: #333;
+ opacity: 0.98;
+}
+
+#nowPlayingPanel {
+ height: 130px;
+ width: 600px;
+ padding: 10px;
+}
+
+#audioCoverArt img {
+ width: 100px;
+ height: 100px;
+ float: left;
+ padding: 0 10px 10px 0;
+}
+
+#videoCoverArt img {
+ height: 100px;
+ float: left;
+ padding: 0 10px 10px 0;
+}
+
+#audioArtistTitle,
+#videoShowTitle {
+ float: left;
+ padding: 5px 5px 0 0;
+ color: #777;
+}
+
+#audioAlbumTitle:before {
+ content: ' - ';
+}
+
+#audioAlbumTitle {
+ padding-top: 5px;
+ color: #777;
+}
+
+#audioTrackWrap,
+#videoTrackWrap {
+ width: 365px;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+#audioTrackTitle,
+#videoTitle {
+ color: #fff;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 16px;
+ font-weight: 700;
+ padding: 5px 0;
+ width: 365px;
+}
+
+#audioDuration,
+#videoDuration {
+ float: right;
+ color: #808080;
+ font-size: 20px;
+ font-weight: 700;
+ padding: 0 5px 5px 0;
+}
+
+#progressBar {
+ background: url('data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAAAAAAdccqdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABZJREFUCB1jsGfiYOJh4gJCdiY2JlYABfcAiC/XwOEAAAAASUVORK5CYII=') repeat-x scroll 0 0 transparent;
+ border: 1px solid rgba(0, 0, 0, 0.7);
+ float: left;
+ height: 8px;
+ padding: 0;
+ position: relative;
+ width: 365px;
+ -moz-border-radius: 20px 20px 20px 20px;
+ border-radius: 20px 20px 20px 20px;
+ -webkit-border-radius: 20px 20px 20px 20px;
+}
+
+#progressBar .elapsedTime {
+ background: url('data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAICAAAAAAdccqdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABdJREFUCB1j+M/0g+kr02emj0D4mekrAD41B7hJ9Jz5AAAAAElFTkSuQmCC') repeat-x scroll 0 0 transparent;
+ height: 8px;
+ left: 0;
+ min-width: 5px;
+ width: 0;
+ position: relative;
+ -moz-border-radius: 20px 20px 20px 20px;
+ border-radius: 20px 20px 20px 20px;
+ -webkit-border-radius: 20px 20px 20px 20px;
+}
+
+#progressBar .progressIndicator {
+ background: url('data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNp8kD0OgzAMhe0QWAB1RoKbVKrE0L3H4AztSM/AYaoiVepNuABDGPhN/dIODJQnWYmTL8+O2VpLUJZlJ1kKiSN99ZaomqZ5IWGAApVKqWsYhuT7vqPGcaSu62hZlrvAN07TNBfoGccxMTOtBRNjDM3zfNYoBxc4bElrDbCAY+t53oF2JKCBI/V9v8c5V4D1NE0XebUJSTWAD4URoMdhGJzzOnD2m0LlfiZ9lkmS2CiKbBAELrDHGe7A8Grg+Z+B10g+AgwAK9Fl++J3PdcAAAAASUVORK5CYII=') no-repeat scroll 0 0 transparent;
+ height: 10px;
+ left: 0;
+ position: absolute;
+ top: -1px;
+ width: 10px;
+}
+
+#nowPlayingPlaylist,
+#nextTrack {
+ width: 416px;
+ float: left;
+ cursor: pointer;
+ z-index: 1000;
+}
+
+#nowPlayingPlaylist {
+ clear: both;
+ color: #fff;
+ position: relative;
+ top: -16px;
+}
+
+#nowPlayingPlaylist ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+}
+
+#nowPlayingPlaylist li {
+ padding-bottom: 2px;
+ padding-left: 4px;
+}
+
+#nowPlayingPlaylist li:hover {
+ cursor: pointer;
+ background: #004986;
+}
+
+#nowPlayingPlaylist li span.duration,
+#nextTrack span.duration {
+ float: right;
+ height: 14px;
+ padding-right: 4px;
+ font-size: 12px;
+}
+
+#nowPlayingPlaylist li div.trackInfo,
+#nextTrack div.trackInfo {
+ width: 364px;
+ height: 14px;
+ font-size: 12px;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+#nowPlayingPlaylist li.activeItem div.trackInfo {
+ width: 362px;
+ padding-left: 10px;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAOCAYAAAAWo42rAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIZJREFUeNpi/P//PwMxgImBSECyQlWCKkFuBOI9QBwLxBxQPgaGMS4A8R0g7gNiFWwKYVb/gGI/IJ4GxJHoNjNCg+ckEHMA8V8gFgPi10BsiKyQBeZUIGYFYi4gPg7Ek9FNhCnkBJkOxAuBeBYQv8Ll621A7AfETIR8rYBLAQwzUj2uAQIMAPXkkCPo9C41AAAAAElFTkSuQmCC') no-repeat;
+}
+
+#nextTrack,
+#nextText,
+#nowPlayingPlaylist {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAOCAIAAABhIeELAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADhJREFUeNoMxSEOACAMA8CmAdai5ibBwf9fCOJy8DQyE1WFtTfuPZRMW4y/FFSIIwZ7/1ojgCfAAEzrAiPgCWVXAAAAAElFTkSuQmCC') repeat-x #606060;
+ border: 1px solid #222;
+}
+
+#nextTrack {
+ border-left: 0px;
+ color: #ddd;
+ padding-left: 4px;
+}
+
+#nextText {
+ clear: both;
+ float: left;
+ color: #457cbf;
+ font-size: 12px;
+ font-weight: 700;
+ width: 54px;
+ border-right: 0px;
+ height: 14px;
+ padding-left: 4px;
+}
+
+#playbackControls span,
+#playbackControls span:hover {
+ width: 24px;
+ height: 24px;
+ float: left;
+ display: block;
+ -moz-background-size: 24px;
+ background-size: 24px;
+ -webkit-background-size: 24px;
+ -o-background-size: 24px;
+ -khtml-background-size: 24px;
+ cursor: pointer;
+}
+
+#pbPrev {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAxVJREFUeNrsWU1q20AUluxgsEBgCDjUYBAYui7kArlAu4+7T/dt9m0OkAMkB+gFeoFeoNB1wCAwuNhgCLjIUDDuN+W5vEzejEbyWKqKBx4zaKQ33/d+5k/hdrsNmlzCI4EjAc8EwjAspQh6eqhiSBcSQdrCaxtIBllDVhjrseRYfgjg2w6qM8ipAXBeUYSWkDnG/VUZAXyjwA4JuK+iiEwx/uagBChUEsHiamAVEisVItCTCd9GFGIq1HoGHWleaJUmgHeV1fvaY+X6Gb5blohl5cEBpKN1LaBv6pUA3ku0kNkQ8MW+sQPdfSLCPbKE7tQLAQG8Co9JkcRznBBGNINZSRQiIISN0TKeiOjGehZOzgQoYUdVgbeQmPDE5phbOVNlwsOmCvBkxJTCdFcSwvOstCx6hiypVMJOKt4lTGjcgHAMnQlQQnEXznwmrKMX/kzP7NEp4XLywBmf531MlSVJLGidkXBZCTyxfs0bzpkBl0yAZp6/sV9mhfXshSXPBcJn9UDM2rnbXSi8I3ltecfWdw4Z5AzzaMAnEuiy9srBSOckLwRwA0UOzY9CXwz5gOad9K1WVgZ8wYnwMl/K13ssRsrq73WL7ayO6pMDcAlHlEegzeIvKwE8JotfGPquIJcF8yBjq287j8A+W4ALAi9Z/SWq2wJWdyo+CVxKwFn5fIhZquVR188CifhPEvgCeWcBqvoeqiCw0c6wRZLtG6o3kK9C3wNkjOZ9idyKJHwmAplpznUkoe57rtG8kbyBvnvyxo8CarsGfCKBtWnVK0hEhdRbKWzIU2PJU4YSm9amE0Oy7Y6QvT33MWojNkYIXEmeQnVNU29egvdMk4F4pMSzV2zBSOvc0NHVS8I2l99djpQc8CCotwwMuKwE5qzdoXubOqzf1y695k4E6Dj3xAvSce7A4Du69aVjrW0hm2qH6lHFDhhplwrTQisx3RLza5SI7muqsH6ibZtT0631/30zZ1HanLtRC4nm3E5bwikImvJ/QLt2SYIm/qFhCpr7j0xIvOb9pbSEVv3/iZtWjgTqLr8FGAAhgOqLmi3nxQAAAABJRU5ErkJggg%3D%3D') no-repeat;
+}
+
+#pbPrev:hover {
+ background: url('data:image/png;base64,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') no-repeat;
+}
+
+#pbStop {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAp1JREFUeNrsWV9rwkAMb3UIFsaEgmJhUBB82Os+1D7APsc+2l73IAiFQUWh4BAqlBWXg7ClsWnvSv/Y0cBRLXfN75fkcrk7+3K5WH2WkdVzGQh0LXf0j23blT4C82gCjxm0e2jqtyN0jaEl0E7QjqAvqajvD3PmjyEBGOvCw0XgVUQRiUBv1CoBGKOs/YjWrkOUJz5B/7FRAtB3DA8fwyUvPL7QqjF8K80Z66C3HoQwUwQCPrYWAtBPKVzlWF25PzSNZ5w3HoYg98YWvhfXRgDBr6GNWfwGVSciI+KzeaQ8sJFIGBEQwKt4PdSZDkHPHOdVKQltAmidJwK+0DI1kODGUvo+uJcp5rKFbNUWeDSg+vYGdVmoe1VpJQaWHssUQZPgGYmAvHIQiz4BDJ0leXXQzdE1kVC66BxbIiZtD3gsrYUdlDkh6s7DJBPABYfm5rBsYWnICykznIvYSj1AwSemdUrNJCLmBbewGs3ptDdIgWuDou4E4DaaffdkfXDZ3MgSIPUKrU105RXas2bfd2gvmn2PhICDGFMphBwWPonVsSCGRMB4RWDKqstbkVjAeEWAhtT5hgicpbAfNvUDgYFAVr6l2d6xTAWMVwTOUr7tWBwpO44K8u1EKmHbFMQwkdYnfjKXwoCYMJ7x2qNA3kxqIQMO9AgnRoyFxVxECCx0CRgUZ6ayYNhKs1DEwsjtMHxcFj7lBHAjQTt6eRuJFsCP2S4syttYjQq2c79ekLZzDYvHrB9qL2RYwu7Iqzke6LZlfaVrTl7tpNJ+VDApQ5ayfDx4ahq80uGzzBNWLSW2VvaQad0kCeFkbluY/f712WgJiX6cTjMS/bwfYLnZt/p4Q5OT5vp3RyYs9f27pRTiuft74mFPPBAwlx8BBgDogIiBqL/sAAAAAABJRU5ErkJggg%3D%3D') no-repeat;
+}
+
+#pbStop:hover {
+ background: url('data:image/png;base64,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') no-repeat;
+}
+
+#pbPause {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApxJREFUeNrsWY1qgzAQ1rYILQiC0EKhIPQR9lB9mj3A3ml9gUJBEAoTBEFQGEh3GddxvSXROGvqMBD8T74v+XKXO93r9eqMucyckZeJgO2yoBeu63ZqBNZRAAcf6hLqCupc8loNtYRaQS2gr7xjX3fXLr1hQgC+8+CwgRoqADcVQSiD+gH9fg5GAN4XYHcIvK8iiCTQf/1QAiiVSDLiomMhiUJIBNopJd+uUGJCaoGijbhJWp0JwHti1Nfstpj6C3yXddCymMEtVI89SqG9pFcC8E7EJFMj8PSv2oG210iEzkgGbce9EJCAF/I4myy8lgZhjxZMS8KIgEQ2ypHpiQgfrF9y4gRmDQt2MPA4gDFapFtZIw4zT4ymMqKyeTR4RoJasQjxGG0ldmRRiQV7HniHcMZ+HcSxa00AFxTV4aXPBdtyFr7NM7kVIq5WM7Chdr4PU9mRRIp+RoZLS+Bu9C1vNi8KXHICuOJ/tN/Fw/Y8CxldCzKLtGDXPjlvvd2Fht/g8EJuHaHzA3n+zj45wPNjy+ZzMvo+x8UltCTnxZPELIUCn5QAdeXVkxCoFPikBOZEf+UzoGc45lNQPxGYCOgJ1CyGtV4YjrqJQKmzuZbKUoFPSqBSeGWbxdf5poXE692isMCgk1fWEffiB3Z9Mmg70O0OeGoxB83V6DDE5ilss6GDd04Nz48d9R+yzWXeJiamgLeW5bNV4NIS+CDnHuZtbFgf0a+nwKUmgOHc3SyowrkHgvf46KvCWpUjS1hQvR94AvYsqZAYeWLMEtM0ygqTTkOMfsS2zbEuaz3TWA4R+aQsMxANAJ5n5rSR4f/OjWpIjCc7TT4c7/8BlnaJnDH+oSENjPcfmWThje8vpUZa9v8TTzHxRMC8fAkwABzhg7l0cw47AAAAAElFTkSuQmCC') no-repeat;
+}
+
+#pbPause:hover {
+ background: url('data:image/png;base64,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') no-repeat;
+}
+
+#pbPlay {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAv9JREFUeNrsWl1q20AQ9k8RRGASELjEUBAY8tDXXCDXaHuAHqAHaA+Q9r19L31vLpAL9LUPBoOgoJCAwMUgg6hxZ2DaTiea1Wq1trolA4tiodV838zs7Owow91uNwhZRoPA5YFA3/KI/xgOh04vgXUUweUExgQG/h0rj5YwKhhrGCvQVznq+4P5rx8tCcDcBC4JAXcRJFKA3uKgBGAOWvsJWduHoCe+gf7VXgnAs2O4pBQudeHxnaxawru2NXNj8taxEmZIIJNzvRCA51DhvMbq6P68bTzTuplRCEpvLOF9pTcCBP4MxljEb+a6EAWRVKwj9MBCI9GKgAIe4/XOZzoEPVNaV40krAmQdZ4y8EbLeCAhjYX6vkovc8xNG9n8UODJgPjuBekakO65004MLGciU2T7BC9IZOxWTFjsCVDonLJbd7Y52hMJ1MXX2ClhsvbATKS1vIcyJyfddZh0ArTh8NycN20sYv7Ekxe2wnAJYWv0AAdfta1TQF6DomeeSBTCC0lbArcOetEDr4DEe9PiayG31gRYvcJrE1c5h/HRgzdWIiONTR6IRfhUHZVzb0wcw6gSYRSbCByJ6tKXoDc+A4kLx/mlgvEeAX5C23hOi+iBSyBx6eCNjXaK7ONMfNHRG//Eof63N0LuSuB54ovXrsQBBSvON5BhFr4J/NBWu0f5BMDftpxzpGC8R2Cj5VsPckNWdwmbWMuOI0O+jbQS1kGuYTx3AU8YIm1/kp25LUwoGeMTUZe7LNR38N6rDu/gLZySMBqzEK8+H3dQjNZ+0RG8xFDYpNFChFHioPQDAH8Jo9NBiHRHJgK1XQm4l7LStaLOwNZS6QSeXXvoUIypI/KLAPZQM9uuBLdcpB3nlOpx7Wnhz4T1c+udmErYG3ZrSg3dgwjpmvIUrJX2I4Mlc5GyUmo87Rs86khF5slda6GlaDKd7ZOE0plbGkP2v+6NNpAIozstSIT5fUDk5nQQ4heamjQX3jcyZasP7yulEs/9fycOUR7+V6Jv+SnAAJqtxFYrTd5IAAAAAElFTkSuQmCC') no-repeat;
+}
+
+#pbPlay:hover {
+ background: url('data:image/png;base64,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%3D%3D') no-repeat;
+}
+
+#pbNext {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvpJREFUeNrsWV1q20AQtmJjiCA0IAgkUDAUAi0ECr1ALpADuNdo39u+pweI39tcoAfoBQKFQAuGgCGQkIAhxUEGkeB+E0ZhWO9IK3llVcYDg2ytdne++dvZ3WA2m7WaTMEawBqARwBBEJQaBGN08dgGb4Hpd6h8GoMT8AR8h/mSkvP5AYC+ER4RC16GCMgY846XCgB9SNsvWds+iCxxifnvKgWAb9t49NhdbO7xl7UaY6xHS9+QrfVCcTMCMDL7egGA72jCVxatk/mvivozx80eu6BpjQuMF3sDwMLvg9uG/47KBqIBpGfEEVlgqIEoBEARnvz11mc6xDw7HFe5IJwBsHbeCOEzNeMBhKksmu+3aeUiAF6LYKtU+AwQlBD+FAaA9xRgu6LfhWua8wBimxNGSteY+8oZALvOgRjgFm2XyywRIAPFw454dZ66kguAnkhvCfvh45IBtDn+0rRNK/YoFwB3fCvGGhVd6j2CiDjFpvSLFCll3rD0kwtLUpfwrNAxe4BNNicAN/9BxXzjDEDUK7I2yTLxIXhLaXvHmUzr+wF8Aj7KASBlCFlG1QKh4T55ZUIf/J2EtbTRu29o6yt99/mb3Rw3Sgw3CrMAbBrVpQuRACesUdMa9D/V9N4CbhQrMs4B6Ijf04KTuFjjqCSAqSKjNYgXoWdrWHZp9P8T2o61uClDnYoyRz+j7ZAtcu9joo1WdTTJaLv3NUlVAAbgU6XtB/g97eKqAPCgRbsjXZNwSH0DxSIf0fYZPCk47qYi41wMTLV860Ck8YEi3Bn4iyyJC1KoZcdORr7tUlntsJhNWLifSvtXtJ0uuGfuautTx1j1qNKLBWLaWGTtfc/YLWxaH7I7DRd0c3mEE5vVqK2clptrKifO66zkIM+BsMDTYUJeOT023CiqUfjIcJ+50n71dmQrsSdu/KnESpwLrcTJnDLoczrzLLz/s9EcEM04nTZANPN+wMjNvVYTb2gsaa55d2TKUt+8W0rFn+u/J24irQHUTf8EGABePUpJ23F8cgAAAABJRU5ErkJggg%3D%3D') no-repeat;
+}
+
+#pbNext:hover {
+ background: url('data:image/png;base64,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') no-repeat;
+}
+
+.tvshowContainer .seasonPicker {
+ position: absolute;
+ right: 5px;
+ top: 0px;
+}
+
+
+.tvshowContainer .active {
+ font-weight: bold;
+}
+
+.tvshowContainer .episodeListingsContainer {
+ position: absolute;
+ top: 40px;
+ left: 500px;
+ width: 500px;
+ padding-bottom: 149px;
+}
+
+.tvshowContainer .episodeListingsContainer ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+}
+
+.episodeListingsContainer li {
+ clear: both;
+}
+
+.episodeListingsContainer li img {
+ height: 30px;
+ float: left;
+}
+
+.tvshowContainer .showDetails {
+ text-align: center;
+ width: 100%;
+ height: 18px;
+ position: relative;
+ padding-top: 5px;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAXCAIAAABF+LJYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjk4NTQ0NDZGQ0U1ODExREZCMjM4ODgyQjJEQTE5ODBBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjk4NTQ0NDcwQ0U1ODExREZCMjM4ODgyQjJEQTE5ODBBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTg1NDQ0NkRDRTU4MTFERkIyMzg4ODJCMkRBMTk4MEEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTg1NDQ0NkVDRTU4MTFERkIyMzg4ODJCMkRBMTk4MEEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7FuFIdAAAAOklEQVR42kyMUQoAMAhC0+j+J+s2ffXTVtDYA1EUpLsjImhmVFU2IsKqGm1uz8zxX7sBeF1zv44AAwDVBhSDNN7oJQAAAABJRU5ErkJggg%3D%3D') repeat-x;
+}
+
+.contentContainer .toggle {
+ position: absolute;
+ font-size: 12px;
+ font-weight: 500;
+ left: 5px;
+ top: 4px;
+}
+
+.tvshowContainer .showDetails p {
+ margin: 0;
+ padding: 0;
+}
+
+.tvshowContainer .showDetails .showTitle {
+ font-size: 12px;
+ font-weight: 700;
+}
+
+.tvshowContainer .showDetails span.heading {
+ font-weight: 700;
+} \ No newline at end of file
diff --git a/debian/webinterface-default/css/ipad.css b/debian/webinterface-default/css/ipad.css
new file mode 100644
index 0000000..9441022
--- /dev/null
+++ b/debian/webinterface-default/css/ipad.css
@@ -0,0 +1,9 @@
+.contentContainer {
+ overflow-x: hidden;
+ overflow-y: auto;
+ position: absolute;
+ height: auto;
+ bottom: auto;
+ background: #fff;
+ padding-bottom: 150px;
+} \ No newline at end of file
diff --git a/debian/webinterface-default/favicon.ico b/debian/webinterface-default/favicon.ico
new file mode 100644
index 0000000..078637f
--- /dev/null
+++ b/debian/webinterface-default/favicon.ico
Binary files differ
diff --git a/debian/webinterface-default/icon.png b/debian/webinterface-default/icon.png
new file mode 100644
index 0000000..cca2499
--- /dev/null
+++ b/debian/webinterface-default/icon.png
Binary files differ
diff --git a/debian/webinterface-default/images/DefaultAlbumCover.png b/debian/webinterface-default/images/DefaultAlbumCover.png
new file mode 100644
index 0000000..bbfd653
--- /dev/null
+++ b/debian/webinterface-default/images/DefaultAlbumCover.png
Binary files differ
diff --git a/debian/webinterface-default/images/DefaultVideo.png b/debian/webinterface-default/images/DefaultVideo.png
new file mode 100644
index 0000000..1aa81a1
--- /dev/null
+++ b/debian/webinterface-default/images/DefaultVideo.png
Binary files differ
diff --git a/debian/webinterface-default/images/ajax-loader.gif b/debian/webinterface-default/images/ajax-loader.gif
new file mode 100644
index 0000000..4fb7c23
--- /dev/null
+++ b/debian/webinterface-default/images/ajax-loader.gif
Binary files differ
diff --git a/debian/webinterface-default/images/close-button.png b/debian/webinterface-default/images/close-button.png
new file mode 100644
index 0000000..628fbf6
--- /dev/null
+++ b/debian/webinterface-default/images/close-button.png
Binary files differ
diff --git a/debian/webinterface-default/images/remote.jpg b/debian/webinterface-default/images/remote.jpg
new file mode 100755
index 0000000..8e10d59
--- /dev/null
+++ b/debian/webinterface-default/images/remote.jpg
Binary files differ
diff --git a/debian/webinterface-default/index.html b/debian/webinterface-default/index.html
new file mode 100755
index 0000000..40b5303
--- /dev/null
+++ b/debian/webinterface-default/index.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Kodi</title>
+ <meta http-equiv="Content-Language" content="EN" />
+ <meta http-equiv="Content-Type" content="UTF-8" />
+ <!-- <link rel="search" href="provider.xml" type="application/opensearchdescription+xml" title="Kodi Library" /> -->
+ <link rel="icon" href="favicon.ico" type="image/x-icon">
+ <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
+ <!-- <link href="/images/logo.png" rel="image_src" /> -->
+ <meta name="robots" content="NOINDEX, NOFOLLOW">
+ <link href="css/core.css?1.3.57" rel="stylesheet" type="text/css">
+ <link href="css/ipad.css?1.0.5" rel="stylesheet" media="only screen and (max-device-width: 1024px)" type="text/css">
+ </head>
+ <body>
+ <div id="header">
+ <div id="commsErrorPanel" style="display: none;"></div>
+ <div id="navigation">
+ <ul>
+ <li id="profiles">Profiles</li>
+ <li id="remoteControl">Remote</li>
+ <li id="movieLibrary">Movies</li>
+ <li id="tvshowLibrary">TV Shows</li>
+ <li id="musicLibrary">Music</li>
+ </ul>
+ </div>
+ <img src="images/ajax-loader.gif" alt="Loading please wait" id="spinner" style="display: none">
+ </div>
+ <div id="body">
+ <div id="topScrollFade" style="display: none;"></div>
+ <div id="content"></div>
+ <div id="overlay" style="display: none;"></div>
+ </div>
+ <div id="footerPopover">
+ <div id="nowPlayingPanel" style="display: none;">
+ <div id="nowPlayingContent">
+ <div id="audioDescription">
+ <div id="audioCoverArt"></div>
+ <div id="audioTrackWrap">
+ <div id="audioArtistTitle"></div>
+ <div id="audioAlbumTitle"></div>
+ </div>
+ <div id="audioTrackTitle"></div>
+ <div id="audioDuration"></div>
+ </div>
+ <div id="videoDescription">
+ <div id="videoCoverArt"></div>
+ <div id="videoTrackWrap">
+ <div id="videoShowTitle"></div>
+ </div>
+ <div id="videoTitle"></div>
+ <div id="videoDuration"></div>
+ </div>
+ </div>
+ <div id="playbackControls">
+ <span id="pbPrev" title="Previous"></span>
+ <span id="pbPause" title="Pause"></span>
+ <span id="pbPlay" title="Play"></span>
+ <span id="pbStop" title="Stop"></span>
+ <span id="pbNext" title="Next"></span>
+ <div id="progressBar">
+ <div class="elapsedTime" style="width: 0%"></div>
+ <span class="progressIndicator"></span>
+ </div>
+ </div>
+ <span id="nextText">Next:</span>
+ <div id="nextTrack" style="display: none;"></div>
+ <div id="nowPlayingPlaylist" style="display: none;"></div>
+ </div>
+ </div>
+ <script type="text/javascript" src="js/xbmc.launcher.js?v=2.1.0"></script>
+ </body>
+</html>
diff --git a/debian/webinterface-default/js/MediaLibrary.js b/debian/webinterface-default/js/MediaLibrary.js
new file mode 100755
index 0000000..3c1a1bc
--- /dev/null
+++ b/debian/webinterface-default/js/MediaLibrary.js
@@ -0,0 +1,1420 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+var MediaLibrary = function () {
+ this.init();
+};
+MediaLibrary.prototype = {
+ playlists: {},
+
+ init: function () {
+ this.bindControls();
+ this.getPlaylists();
+ },
+ bindControls: function () {
+ $('#musicLibrary').click(jQuery.proxy(this.musicLibraryOpen, this));
+ $('#movieLibrary').click(jQuery.proxy(this.movieLibraryOpen, this));
+ $('#tvshowLibrary').click(jQuery.proxy(this.tvshowLibraryOpen, this));
+ $('#pictureLibrary').click(jQuery.proxy(this.pictureLibraryOpen, this));
+ $('#remoteControl').click(jQuery.proxy(this.remoteControlOpen, this));
+ $('#profiles').click(jQuery.proxy(this.profilesOpen, this));
+ $('#overlay').click(jQuery.proxy(this.hideOverlay, this));
+ $(window).resize(jQuery.proxy(this.updatePlayButtonLocation, this));
+ $(document).on('keydown', jQuery.proxy(this.handleKeyPress, this));
+ $(document).on('contextmenu', jQuery.proxy(this.handleContextMenu, this));
+ },
+ resetPage: function () {
+ $('#musicLibrary').removeClass('selected');
+ $('#movieLibrary').removeClass('selected');
+ $('#tvshowLibrary').removeClass('selected');
+ $('#remoteControl').removeClass('selected');
+ $('#pictureLibrary').removeClass('selected');
+ $('#profiles').removeClass('selected');
+ this.hideOverlay();
+ },
+ replaceAll: function (haystack, needle, thread) {
+ return (haystack || '').split(needle || '').join(thread || '');
+ },
+ getPlaylists: function () {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Playlist.GetPlaylists',
+ 'timeout': 3000,
+ 'success': function (data) {
+ if (data && data.result && data.result.length > 0) {
+ $.each($(data.result), jQuery.proxy(function (i, item) {
+ this.playlists[item.type] = item.playlistid;
+ }, this));
+ }
+ },
+ 'error': function (data, error) {
+ xbmc.core.displayCommunicationError();
+ setTimeout(jQuery.proxy(this.updateState, this), 2000);
+ }
+ });
+ },
+ remoteControlOpen: function (event) {
+ this.resetPage();
+ this.textBuffer = '';
+ $('#remoteControl').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#remoteContainer');
+ if (!libraryContainer || libraryContainer.length === 0) {
+ $('#spinner').show();
+ libraryContainer = $('<div>');
+ libraryContainer.attr('id', 'remoteContainer')
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ var keys = [{
+ name: 'up',
+ width: '40px',
+ height: '30px',
+ top: '28px',
+ left: '58px'
+ },
+ {
+ name: 'down',
+ width: '40px',
+ height: '30px',
+ top: '122px',
+ left: '58px'
+ },
+ {
+ name: 'left',
+ width: '40px',
+ height: '30px',
+ top: '74px',
+ left: '15px'
+ },
+ {
+ name: 'right',
+ width: '40px',
+ height: '30px',
+ top: '74px',
+ left: '104px'
+ },
+ {
+ name: 'ok',
+ width: '40px',
+ height: '30px',
+ top: '74px',
+ left: '58px'
+ },
+ {
+ name: 'back',
+ width: '40px',
+ height: '30px',
+ top: '13px',
+ left: '161px'
+ },
+ {
+ name: 'home',
+ width: '40px',
+ height: '30px',
+ top: '154px',
+ left: '8px'
+ },
+ {
+ name: 'mute',
+ width: '40px',
+ height: '30px',
+ top: '107px',
+ left: '391px'
+ },
+ {
+ name: 'power',
+ width: '30px',
+ height: '30px',
+ top: '-3px',
+ left: '13px'
+ },
+ {
+ name: 'volumeup',
+ width: '30px',
+ height: '30px',
+ top: '49px',
+ left: '422px'
+ },
+ {
+ name: 'volumedown',
+ width: '30px',
+ height: '30px',
+ top: '49px',
+ left: '367px'
+ },
+ {
+ name: 'playpause',
+ width: '32px',
+ height: '23px',
+ top: '62px',
+ left: '260px'
+ },
+ {
+ name: 'stop',
+ width: '32px',
+ height: '23px',
+ top: '62px',
+ left: '211px'
+ },
+ {
+ name: 'next',
+ width: '38px',
+ height: '25px',
+ top: '102px',
+ left: '304px'
+ },
+ {
+ name: 'previous',
+ width: '38px',
+ height: '25px',
+ top: '101px',
+ left: '160px'
+ },
+ {
+ name: 'forward',
+ width: '32px',
+ height: '23px',
+ top: '102px',
+ left: '259px'
+ },
+ {
+ name: 'rewind',
+ width: '32px',
+ height: '23px',
+ top: '101px',
+ left: '211px'
+ },
+ {
+ name: 'cleanlib_a',
+ width: '46px',
+ height: '26px',
+ top: '47px',
+ left: '553px'
+ },
+ {
+ name: 'updatelib_a',
+ width: '46px',
+ height: '26px',
+ top: '47px',
+ left: '492px'
+ },
+ {
+ name: 'cleanlib_v',
+ width: '46px',
+ height: '26px',
+ top: '111px',
+ left: '553px'
+ },
+ {
+ name: 'updatelib_v',
+ width: '46px',
+ height: '26px',
+ top: '111px',
+ left: '492px'
+ }
+ ];
+ for (var akey in keys) {
+ var aremotekey = $('<p>').attr('id', keys[akey]['name']);
+ aremotekey.addClass('remote_key')
+ .css('height', keys[akey]['height'])
+ .css('width', keys[akey]['width'])
+ .css('top', keys[akey]['top'])
+ .css('left', keys[akey]['left'])
+ .bind('click', {
+ key: keys[akey]['name']
+ }, jQuery.proxy(this.pressRemoteKey, this));
+ libraryContainer.append(aremotekey);
+ }
+ } else {
+ libraryContainer.show();
+ libraryContainer.trigger('scroll');
+ }
+
+ $('#spinner').hide();
+ },
+ shouldHandleEvent: function (event) {
+ var inRemoteControl = $('#remoteControl').hasClass('selected');
+ return (!event.ctrlKey && !event.altKey && inRemoteControl);
+ },
+ handleKeyPress: function (event) {
+ if (!this.shouldHandleEvent(event)) {
+ return true;
+ }
+
+ var keys = {
+ 8: 'back', // Back space
+ 13: 'ok', // Enter
+ 27: 'home', // Escape
+ 32: 'playpause', // Space bar
+ 37: 'left', // Left
+ 38: 'up', // Up
+ 39: 'right', // Right
+ 40: 'down', // Down
+ 93: 'contextmenu', // "Right Click"
+ 107: 'volumeup', // + (num keypad)
+ 109: 'volumedown', // - (num keypad)
+ 187: 'volumeup', // + (alnum keypad)
+ 189: 'volumedown' // - (alnum keypad)
+ };
+ var which = event.which;
+ var key = keys[which];
+
+ event.data = {
+ key: key
+ };
+
+ if (!key) {
+ event.data.key = 'text';
+
+ if (event.key && event.key.length === 1) {
+ event.data.text = event.key;
+ } else {
+ // Letters
+ if (which >= 65 && which <= 90) {
+ var offset = event.shiftKey ? 0 : 32;
+ event.data.text = String.fromCharCode(which + offset);
+ }
+
+ // Digits
+ if (which >= 96 && which <= 105) {
+ event.data.text = (which - 96) + "";
+ }
+ }
+ }
+
+ if (event.data.key) {
+ this.pressRemoteKey(event);
+ return false;
+ }
+ },
+ handleContextMenu: function (event) {
+ if (!this.shouldHandleEvent(event)) {
+ return true;
+ }
+ if (
+ (event.target == document) || //Chrome/Opera
+ (event.clientX === event.clientY && event.clientX === 0) //FF/IE
+ ) {
+ return false;
+ } //keyboard event. cancel it.
+ return true;
+ },
+ rpcCall: function (method, params) {
+ var callObj = {
+ 'method': method
+ };
+ if (params) {
+ callObj.params = params;
+ }
+ return xbmc.rpc.request(callObj);
+ },
+ typeRemoteText: function (event) {
+ if (event.data.key === 'text' || ((event.data.key === 'playpause' || event.data.key === 'back') && this
+ .textBuffer.length)) {
+ if (event.data.key === 'back') {
+ this.textBuffer = this.textBuffer.substring(0, this.textBuffer.length - 1);
+ } else if (event.data.key === 'playpause') {
+ this.textBuffer += ' ';
+ } else if (event.data.text && event.data.text.length) {
+ this.textBuffer += event.data.text;
+ }
+ console.log(this.textBuffer);
+ return this.rpcCall('Input.SendText', {
+ 'text': this.textBuffer,
+ 'done': false
+ });
+ } else {
+ this.textBuffer = '';
+ }
+ },
+ pressRemoteKey: function (event) {
+ var player = -1,
+ keyPressed = event.data.key;
+ $('#spinner').show();
+ if (this.typeRemoteText(event)) {
+ return true;
+ }
+
+ switch (keyPressed) {
+ case 'up':
+ return this.rpcCall('Input.Up');
+ case 'down':
+ return this.rpcCall('Input.Down');
+ case 'left':
+ return this.rpcCall('Input.Left');
+ case 'right':
+ return this.rpcCall('Input.Right');
+ case 'ok':
+ return this.rpcCall('Input.Select');
+ case 'cleanlib_a':
+ return this.rpcCall('AudioLibrary.Clean');
+ case 'updatelib_a':
+ return this.rpcCall('AudioLibrary.Scan');
+ case 'cleanlib_v':
+ return this.rpcCall('VideoLibrary.Clean');
+ case 'updatelib_v':
+ return this.rpcCall('VideoLibrary.Scan');
+ case 'back':
+ return this.rpcCall('Input.Back');
+ case 'home':
+ return this.rpcCall('Input.Home');
+ case 'power':
+ return this.rpcCall('System.Shutdown');
+ case 'contextmenu':
+ return this.rpcCall('Input.ContextMenu');
+ case 'mute':
+ return this.rpcCall('Application.SetMute', {
+ 'mute': 'toggle'
+ });
+ case 'volumeup':
+ return this.rpcCall('Application.SetVolume', {
+ 'volume': 'increment'
+ });
+ case 'volumedown':
+ return this.rpcCall('Application.SetVolume', {
+ 'volume': 'decrement'
+ });
+ }
+
+ // TODO: Get active player
+ if ($('#videoDescription').is(':visible')) {
+ player = this.playlists["video"];
+ } else if ($('#audioDescription').is(':visible')) {
+ player = this.playlists["audio"];
+ }
+
+ if (player >= 0) {
+ switch (keyPressed) {
+ case 'playpause':
+ return this.rpcCall('Player.PlayPause', {
+ 'playerid': player
+ });
+ case 'stop':
+ return this.rpcCall('Player.Stop', {
+ 'playerid': player
+ });
+ case 'next':
+ return this.rpcCall('Player.GoTo', {
+ 'playerid': player,
+ 'to': 'next'
+ });
+ case 'previous':
+ return this.rpcCall('Player.GoTo', {
+ 'playerid': player,
+ 'to': 'previous'
+ });
+ case 'forward':
+ return this.rpcCall('Player.SetSpeed', {
+ 'playerid': player,
+ 'speed': 'increment'
+ });
+ case 'rewind':
+ return this.rpcCall('Player.SetSpeed', {
+ 'playerid': player,
+ 'speed': 'decrement'
+ });
+ }
+ }
+ },
+ musicLibraryOpen: function (event) {
+ this.resetPage();
+ $('#musicLibrary').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#libraryContainer');
+ if (!libraryContainer || libraryContainer.length === 0) {
+ $('#spinner').show();
+ libraryContainer = $('<div>');
+ libraryContainer.attr('id', 'libraryContainer')
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'AudioLibrary.GetAlbums',
+ 'params': {
+ 'limits': {
+ 'start': 0
+ },
+ 'properties': [
+ 'description',
+ 'theme',
+ 'mood',
+ 'style',
+ 'type',
+ 'albumlabel',
+ 'artist',
+ 'genre',
+ 'rating',
+ 'title',
+ 'year',
+ 'thumbnail'
+ ],
+ 'sort': {
+ 'method': 'artist'
+ }
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.albums) {
+ this.albumList = data.result.albums;
+ $.each($(this.albumList), jQuery.proxy(function (i, item) {
+ var floatableAlbum = this.generateThumb('album', item
+ .thumbnail, item.title, item.artist);
+ floatableAlbum.bind('click', {
+ album: item
+ }, jQuery.proxy(this.displayAlbumDetails, this));
+ libraryContainer.append(floatableAlbum);
+ }, this));
+ libraryContainer.append($('<div>').addClass('footerPadding'));
+ $('#spinner').hide();
+ libraryContainer.bind('scroll', {
+ activeLibrary: libraryContainer
+ }, jQuery.proxy(this.updateScrollEffects, this));
+ libraryContainer.trigger('scroll');
+ myScroll = new iScroll('libraryContainer');
+ } else {
+ libraryContainer.html('');
+ }
+ }
+ });
+ } else {
+ libraryContainer.show();
+ libraryContainer.trigger('scroll');
+ }
+ },
+ getThumbnailPath: function (thumbnail) {
+ return thumbnail ? ('image/' + encodeURI(thumbnail)) : xbmc.core.DEFAULT_ALBUM_COVER;
+ },
+ generateThumb: function (type, thumbnail, title, artist) {
+ title = title || '';
+ artist = artist || '';
+
+ var showTitle = title,
+ showArtist = artist;
+ var floatableAlbum = $('<div>');
+ var path = this.getThumbnailPath(thumbnail);
+ if (title.length > 21) {
+ showTitle = $.trim(title.substr(0, 18)) + '...';
+ }
+ if (artist.length > 22) {
+ showArtist = $.trim(artist.substr(0, 20)) + '...';
+ }
+ var className = '';
+ var code = '';
+ switch (type) {
+ case 'album':
+ className = 'floatableAlbum';
+ code = '<p class="album" title="' + title + '">' + showTitle + '</p><p class="artist" title="' +
+ artist + '">' + artist + '</p>';
+ break;
+ case 'movie':
+ className = 'floatableMovieCover';
+ code = '<p class="album" title="' + title + '">' + showTitle + '</p>';
+ break;
+ case 'tvshow':
+ className = 'floatableTVShowCover';
+ break;
+ case 'tvshowseason':
+ className = 'floatableTVShowCoverSeason';
+ break;
+ case 'image':
+ case 'directory':
+ className = 'floatableAlbum';
+ code = '<p class="album" title="' + title + '">' + showTitle + '</p>';
+ break;
+ case 'profile':
+ className = 'floatableProfileThumb';
+ code = '<p class="album" title="' + title + '">' + showTitle + '</p>';
+ break;
+ }
+ return floatableAlbum.addClass(className).html('<div class="imgWrapper"><div class="inner"><img src="' +
+ path + '" alt="' + title + '" /></div></div>' + code);
+ },
+ showAlbumSelectorBlock: function (album) {
+ if (album) {
+ var prevAlbum = null,
+ nextAlbum = null;
+ $.each($(this.albumList), jQuery.proxy(function (i, item) {
+ if (item.albumid == album.albumid) {
+ if (this.albumList.length > 1) {
+ prevAlbum = this.albumList[i <= 0 ? this.albumList.length - 1 : i - 1];
+ nextAlbum = this.albumList[i >= this.albumList.length ? 0 : i + 1];
+ }
+ return false; /* .each break */
+ }
+ }, this));
+ var albumSelectorBlock = $('#albumSelector');
+ if (!albumSelectorBlock || albumSelectorBlock.length === 0) {
+ albumSelectorBlock = $('<div>');
+ albumSelectorBlock.attr('id', 'albumSelector')
+ .html(
+ '<table><tr><td class="allAlbums">All Albums</td><td class="activeAlbumTitle"></td>' +
+ '<td class="prevAlbum">&nbsp;</td><td class="nextAlbum">&nbsp;</td></tr></table>'
+ );
+ $('#content').prepend(albumSelectorBlock);
+ $('#albumSelector .allAlbums').bind('click', jQuery.proxy(this.hideAlbumDetails, this));
+ }
+ $('#albumSelector .prevAlbum').unbind();
+ $('#albumSelector .nextAlbum').unbind();
+ if (prevAlbum) {
+ $('#albumSelector .prevAlbum').bind('click', {
+ album: prevAlbum
+ }, jQuery.proxy(this.displayAlbumDetails, this));
+ }
+ if (nextAlbum) {
+ $('#albumSelector .nextAlbum').bind('click', {
+ album: nextAlbum
+ }, jQuery.proxy(this.displayAlbumDetails, this));
+ }
+ $('#albumSelector .activeAlbumTitle').html(album.title || 'Unknown Album');
+ albumSelectorBlock.show();
+ }
+ },
+ hideAlbumDetails: function () {
+ $('.contentContainer').hide();
+ this.musicLibraryOpen();
+ },
+ displayAlbumDetails: function (event) {
+ this.showAlbumSelectorBlock(event.data.album);
+ var albumDetailsContainer = $('#albumDetails' + event.data.album.albumid);
+ $('#topScrollFade').hide();
+ if (!albumDetailsContainer || albumDetailsContainer.length === 0) {
+ $('#spinner').show();
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'AudioLibrary.GetSongs',
+ 'params': {
+ 'properties': [
+ 'title',
+ 'artist',
+ 'genre',
+ 'track',
+ 'duration',
+ 'year',
+ 'rating',
+ 'playcount'
+ ],
+ 'sort': {
+ 'method': 'track'
+ },
+ 'filter': {
+ 'albumid': event.data.album.albumid
+ }
+ },
+ 'success': function (data) {
+ albumDetailsContainer = $('<div>');
+ albumDetailsContainer.attr('id', 'albumDetails' + event.data.album.albumid)
+ .addClass('contentContainer')
+ .addClass('albumContainer')
+ .html(
+ '<table class="albumView"><thead><tr class="headerRow"><th>Artwork</th><th>&nbsp;</th>' +
+ '<th>Name</th><th class="time">Time</th><th>Artist</th><th>Genre</th></tr></thead>' +
+ '<tbody class="resultSet"></tbody></table>'
+ );
+ $('.contentContainer').hide();
+ $('#content').append(albumDetailsContainer);
+ var albumThumbnail = event.data.album.thumbnail;
+ var albumTitle = event.data.album.title || 'Unknown Album';
+ var albumArtist = event.data.album.artist.join(', ') || 'Unknown Artist';
+ var trackCount = data.result.limits.total;
+ $.each($(data.result.songs), jQuery.proxy(function (i, item) {
+ var trackRow, trackNumberTD;
+ if (i === 0) {
+ trackRow = $('<tr>').addClass('trackRow').addClass('tr' +
+ i % 2);
+ trackRow.append($('<td>').attr('rowspan', ++trackCount + 1)
+ .addClass('albumThumb'));
+ for (var a = 0; a < 5; a++) {
+ trackRow.append($('<td>').html('&nbsp').attr('style',
+ 'display: none'));
+ }
+ $('#albumDetails' + event.data.album.albumid +
+ ' .resultSet').append(trackRow);
+ }
+ trackRow = $('<tr>').addClass('trackRow').addClass('tr' + i % 2)
+ .bind('click', {
+ album: event.data.album,
+ itmnbr: i
+ }, jQuery.proxy(this.playTrack, this));
+ trackNumberTD = $('<td>').html(item.track);
+
+ trackRow.append(trackNumberTD);
+ var trackTitleTD = $('<td>').html(item.title);
+
+ trackRow.append(trackTitleTD);
+ var trackDurationTD = $('<td>')
+ .addClass('time')
+ .html(xbmc.core.durationToString(item.duration));
+
+ trackRow.append(trackDurationTD);
+ var trackArtistTD = $('<td>').html(item.artist.join(', '));
+
+ trackRow.append(trackArtistTD);
+ var trackGenreTD = $('<td>').html(item.genre.join(', '));
+
+ trackRow.append(trackGenreTD);
+ $('#albumDetails' + event.data.album.albumid + ' .resultSet')
+ .append(trackRow);
+ }, this));
+ if (trackCount > 0) {
+ var trackRow = $('<tr>').addClass('fillerTrackRow'),
+ i;
+ for (i = 0; i < 5; i++) {
+ trackRow.append($('<td>').html('&nbsp'));
+ }
+ $('#albumDetails' + event.data.album.albumid + ' .resultSet').append(
+ trackRow);
+
+ var trackRow2 = $('<tr>').addClass('fillerTrackRow2');
+ trackRow2.append($('<td>').addClass('albumBG').html('&nbsp'));
+ for (i = 0; i < 5; i++) {
+ trackRow2.append($('<td>').html('&nbsp'));
+ }
+ $('#albumDetails' + event.data.album.albumid + ' .resultSet').append(
+ trackRow2);
+ }
+ $('#albumDetails' + event.data.album.albumid + ' .albumThumb')
+ .append(this.generateThumb('album', albumThumbnail, albumTitle,
+ albumArtist))
+ .append($('<div>').addClass('footerPadding'));
+ $('#spinner').hide();
+ myScroll = new iScroll('albumDetails' + event.data.album.albumid);
+ }
+ });
+ } else {
+ $('.contentContainer').hide();
+ $('#albumDetails' + event.data.album.albumid).show();
+ }
+ },
+ togglePosterView: function (event) {
+ var view = event.data.mode;
+ var wthumblist, hthumblist, hthumbdetails;
+ $("#toggleBanner").removeClass('activeMode');
+ $("#togglePoster").removeClass('activeMode');
+ $("#toggleLandscape").removeClass('activeMode');
+ switch (view) {
+ case 'landscape':
+ xbmc.core.setCookie('TVView', 'landscape');
+ wthumblist = '210px';
+ hthumblist = '118px';
+ hthumbdetails = '213px';
+ $("#toggleLandscape").addClass('activeMode');
+ break;
+ case 'banner':
+ xbmc.core.setCookie('TVView', 'banner');
+ wthumblist = '379px';
+ hthumblist = '70px';
+ hthumbdetails = '70px';
+ $("#toggleBanner").addClass('activeMode');
+ break;
+ default:
+ xbmc.core.setCookie('TVView', 'poster');
+ wthumblist = '135px';
+ hthumblist = '199px';
+ hthumbdetails = '559px';
+ $("#togglePoster").addClass('activeMode');
+ break;
+ }
+ $(".floatableTVShowCover, .floatableTVShowCover div.imgWrapper, .floatableTVShowCover img, " +
+ ".floatableTVShowCover div.imgWrapper div.inner")
+ .css('width', wthumblist).css('height', hthumblist);
+ $(".floatableTVShowCoverSeason div.imgWrapper, .floatableTVShowCoverSeason div.imgWrapper div.inner," +
+ ".floatableTVShowCoverSeason img, .floatableTVShowCoverSeason")
+ .css('height', hthumbdetails);
+ },
+ displayTVShowDetails: function (event) {
+ var tvshowDetailsContainer = $('#tvShowDetails' + event.data.tvshow.tvshowid);
+ $('#topScrollFade').hide();
+ toggle = this.toggle.detach();
+ if (!tvshowDetailsContainer || tvshowDetailsContainer.length === 0) {
+ $('#spinner').show();
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'VideoLibrary.GetSeasons',
+ 'params': {
+ 'properties': [
+ 'season',
+ 'showtitle',
+ 'playcount',
+ 'episode',
+ 'thumbnail',
+ 'fanart'
+ ],
+ 'tvshowid': event.data.tvshow.tvshowid
+ },
+ 'success': function (data) {
+ tvshowDetailsContainer = $('<div>');
+ tvshowDetailsContainer.attr('id', 'tvShowDetails' + event.data.tvshow.tvshowid)
+ .css('display', 'none')
+ .addClass('contentContainer')
+ .addClass('tvshowContainer');
+ var showThumb = this.generateThumb('tvshowseason', event.data.tvshow.thumbnail,
+ event.data.tvshow.title);
+ if (data && data.result && data.result.seasons && data.result.seasons.length >
+ 0) {
+ var showDetails = $('<div>').addClass('showDetails');
+ showDetails.append(toggle);
+ showDetails.append($('<p>').html(data.result.seasons[0].showtitle).addClass(
+ 'showTitle'));
+ var seasonSelectionSelect = $('<select>').addClass('seasonPicker');
+ this.tvActiveShowContainer = tvshowDetailsContainer;
+ $.each($(data.result.seasons), function (i, item) {
+ var season = $('<option>').attr('value', i);
+ season.text(item.label);
+ seasonSelectionSelect.append(season);
+ });
+ seasonSelectionSelect.bind('change', {
+ tvshow: event.data.tvshow.tvshowid,
+ seasons: data.result.seasons,
+ element: seasonSelectionSelect
+ }, jQuery.proxy(this.displaySeasonListings, this));
+ showDetails.append(seasonSelectionSelect);
+ tvshowDetailsContainer.append(showDetails);
+ tvshowDetailsContainer.append(showThumb);
+ seasonSelectionSelect.trigger('change');
+ $('#content').append(tvshowDetailsContainer);
+ if (xbmc.core.getCookie('TVView') !== null &&
+ xbmc.core.getCookie('TVView') !== 'banner'
+ ) {
+ var view = xbmc.core.getCookie('TVView');
+ switch (view) {
+ case 'poster':
+ togglePoster.trigger('click');
+ break;
+ case 'landscape':
+ toggleLandscape.trigger('click');
+ break;
+ }
+ }
+ tvshowDetailsContainer.fadeIn();
+ }
+ $('#spinner').hide();
+ }
+ });
+ } else {
+ $('.contentContainer').hide();
+ $('#tvShowDetails' + event.data.tvshow.tvshowid).show();
+ $('#tvShowDetails' + event.data.tvshow.tvshowid + ' select').trigger('change');
+ }
+ },
+ displaySeasonListings: function (event) {
+ var selectedVal = event.data.element.val();
+ var seasons = event.data.seasons;
+ $('#topScrollFade').hide();
+ var oldListings = $('.episodeListingsContainer', this.tvActiveShowContainer).fadeOut();
+ this.tvActiveSeason = selectedVal;
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'VideoLibrary.GetEpisodes',
+ 'params': {
+ 'properties': [
+ 'title',
+ 'thumbnail',
+ 'episode',
+ 'plot',
+ 'season'
+ ],
+ 'season': seasons[selectedVal].season,
+ 'tvshowid': event.data.tvshow
+ },
+ 'success': function (data) {
+ var episodeListingsContainer = $('<div>').addClass('episodeListingsContainer');
+ var episodeTable = $('<table>').addClass('seasonView').html(
+ '<thead><tr class="headerRow"><th class="thumbHeader">N&deg;</th><th>Title</th>' +
+ '<th class="thumbHeader">Thumb</th><th class="thumbHeader">Details</th></tr></thead>' +
+ '<tbody class="resultSet"></tbody>'
+ );
+ $.each($(data.result.episodes), jQuery.proxy(function (i, item) {
+ var episodeRow = $('<tr>').addClass('episodeRow').addClass('tr' +
+ i % 2);
+ var episodePictureImg = $('<img>').bind('click', {
+ episode: item
+ }, jQuery.proxy(this.playTVShow, this)).css('cursor', 'pointer');
+ episodePictureImg.attr('src', this.getThumbnailPath(item
+ .thumbnail));
+ var episodePicture = $('<td>').addClass('episodeThumb').append(
+ episodePictureImg).bind('click', {
+ episode: item
+ }, jQuery.proxy(this.playTVShow, this));
+ var episodeNumber = $('<td>').addClass('episodeNumber').html(item
+ .episode).bind('click', {
+ episode: item
+ }, jQuery.proxy(this.playTVShow, this));
+ var episodeTitle = $('<td>').html(item.title).bind('click', {
+ episode: item
+ }, jQuery.proxy(this.playTVShow, this));
+ var episodeDetails = $('<td class="info">').html('').bind('click', {
+ episode: item
+ }, jQuery.proxy(this.displayEpisodeDetails, this)).css('cursor',
+ 'pointer');
+ episodeRow.append(episodeNumber).append(episodeTitle).append(
+ episodePicture).append(episodeDetails);
+ episodeTable.append(episodeRow);
+ }, this));
+ episodeListingsContainer.append(episodeTable);
+ $(this.tvActiveShowContainer).append(episodeListingsContainer);
+ }
+ });
+ },
+ displayEpisodeDetails: function (event) {
+ var episodeDetails = $('<div>').attr('id', 'episode-' + event.data.episode.episodeid).addClass(
+ 'episodePopoverContainer');
+ episodeDetails.append($('<img>').attr('src', 'images/close-button.png').addClass('closeButton').bind(
+ 'click', jQuery.proxy(this.hideOverlay, this)));
+ episodeDetails.append($('<img>').attr('src', this.getThumbnailPath(event.data.episode.thumbnail))
+ .addClass('episodeCover'));
+ episodeDetails.append($('<div>').addClass('playIcon').bind('click', {
+ episode: event.data.episode
+ }, jQuery.proxy(this.playTVShow, this)));
+ var episodeTitle = $('<p>').addClass('episodeTitle');
+ var yearText = event.data.episode.year ? ' <span class="year">(' + event.data.episode.year +
+ ')</span>' : '';
+ episodeTitle.html(event.data.episode.title + yearText);
+ episodeDetails.append(episodeTitle);
+ if (event.data.episode.runtime) {
+ episodeDetails.append($('<p>').addClass('runtime').html('<strong>Runtime:</strong> ' + Math.ceil(
+ event.data.episode.runtime / 60) + ' minutes'));
+ }
+ if (event.data.episode.season) {
+ episodeDetails.append($('<p>').addClass('season').html('<strong>Season:</strong> ' + event.data
+ .episode.season));
+ }
+ if (event.data.episode.episode) {
+ episodeDetails.append($('<p>').addClass('episode').html('<strong>Episode:</strong> ' + event.data
+ .episode.episode));
+ }
+ if (event.data.episode.plot) {
+ episodeDetails.append($('<p>').addClass('plot').html('<strong>Plot:</strong> <br/><br/>' + event
+ .data.episode.plot));
+ }
+ if (event.data.episode.genre) {
+ episodeDetails.append($('<p>').addClass('genre').html('<strong>Genre:</strong> ' + event.data
+ .episode.genre));
+ }
+ if (event.data.episode.director) {
+ episodeDetails.append($('<p>').addClass('director').html('<strong>Directed By:</strong> ' + event
+ .data.episode.director));
+ }
+ this.activeCover = episodeDetails;
+ $('body').append(episodeDetails);
+ $('#overlay').show();
+ this.updatePlayButtonLocation();
+ },
+ playTVShow: function (event) {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Player.Open',
+ 'params': {
+ 'item': {
+ 'episodeid': event.data.episode.episodeid
+ }
+ },
+ 'success': function (data) {
+ this.hideOverlay();
+ }
+ });
+ },
+ hideOverlay: function (event) {
+ if (this.activeCover) {
+ $(this.activeCover).remove();
+ this.activeCover = null;
+ }
+ $('#overlay').hide();
+ },
+ updatePlayButtonLocation: function (event) {
+ var movieContainer = $('.movieCover'),
+ playIcon;
+ if (movieContainer.length > 0) {
+ playIcon = $('.playIcon');
+ if (playIcon.length > 0) {
+ var heightpi = $(movieContainer[0]).height();
+ playIcon.width(Math.floor(0.65 * heightpi));
+ playIcon.height(heightpi);
+ }
+ }
+ var episodeContainer = $('.episodeCover');
+ if (episodeContainer.length > 0) {
+ playIcon = $('.playIcon');
+ if (playIcon.length > 0) {
+ var widthpi = $(episodeContainer[0]).width();
+ playIcon.width(widthpi);
+ //assume 16/9 thumb
+ playIcon.height(Math.floor(widthpi * 9 / 16));
+ }
+ }
+ },
+ playMovie: function (event) {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Player.Open',
+ 'params': {
+ 'item': {
+ 'movieid': event.data.movie.movieid
+ }
+ },
+ 'success': function (data) {
+ this.hideOverlay();
+ }
+ });
+ },
+ displayMovieDetails: function (event) {
+ var movieDetails = $('<div>').attr('id', 'movie-' + event.data.movie.movieid).addClass(
+ 'moviePopoverContainer');
+ movieDetails.append($('<img>').attr('src', 'images/close-button.png').addClass('closeButton').bind(
+ 'click', jQuery.proxy(this.hideOverlay, this)));
+ movieDetails.append($('<img>').attr('src', this.getThumbnailPath(event.data.movie.thumbnail)).addClass(
+ 'movieCover'));
+ movieDetails.append($('<div>').addClass('playIcon').bind('click', {
+ movie: event.data.movie
+ }, jQuery.proxy(this.playMovie, this)));
+ var movieTitle = $('<p>').addClass('movieTitle');
+ var yearText = event.data.movie.year ? ' <span class="year">(' + event.data.movie.year + ')</span>' :
+ '';
+ movieTitle.html(event.data.movie.title + yearText);
+ movieDetails.append(movieTitle);
+ if (event.data.movie.runtime) {
+ movieDetails.append($('<p>').addClass('runtime').html('<strong>Runtime:</strong> ' + Math.ceil(event
+ .data.movie.runtime / 60) + ' minutes'));
+ }
+ if (event.data.movie.plot) {
+ movieDetails.append($('<p>').addClass('plot').html(event.data.movie.plot));
+ }
+ if (event.data.movie.genre) {
+ movieDetails.append($('<p>').addClass('genre').html('<strong>Genre:</strong> ' + event.data.movie
+ .genre));
+ }
+ if (event.data.movie.director) {
+ movieDetails.append($('<p>').addClass('director').html('<strong>Directed By:</strong> ' + event.data
+ .movie.director));
+ }
+ this.activeCover = movieDetails;
+ $('body').append(movieDetails);
+ $('#overlay').show();
+ this.updatePlayButtonLocation();
+ },
+ playTrack: function (event) {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Playlist.Clear',
+ 'params': {
+ 'playlistid': this.playlists["audio"]
+ },
+ 'success': function (data) {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Playlist.Add',
+ 'params': {
+ 'playlistid': this.playlists["audio"],
+ 'item': {
+ 'albumid': event.data.album.albumid
+ }
+ },
+ 'success': function (data) {
+ xbmc.rpc.request({
+ 'method': 'Player.Open',
+ 'params': {
+ 'item': {
+ 'playlistid': this.playlists["audio"],
+ 'position': event.data.itmnbr
+ }
+ },
+ 'success': function () {}
+ });
+ }
+ });
+ }
+ });
+ },
+ loadProfile: function (event) {
+ return xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Profiles.LoadProfile',
+ 'params': {
+ 'profile': event.data.profile.label
+ }
+ });
+ },
+ movieLibraryOpen: function () {
+ this.resetPage();
+ $('#movieLibrary').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#movieLibraryContainer');
+ if (!libraryContainer || libraryContainer.length === 0) {
+ $('#spinner').show();
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'VideoLibrary.GetMovies',
+ 'params': {
+ 'limits': {
+ 'start': 0
+ },
+ 'properties': [
+ 'genre',
+ 'director',
+ 'trailer',
+ 'tagline',
+ 'plot',
+ 'plotoutline',
+ 'title',
+ 'originaltitle',
+ 'lastplayed',
+ 'runtime',
+ 'year',
+ 'playcount',
+ 'rating',
+ 'thumbnail',
+ 'file'
+ ],
+ 'sort': {
+ 'method': 'sorttitle',
+ 'ignorearticle': true
+ }
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.movies) {
+ libraryContainer = $('<div>');
+ libraryContainer.attr('id', 'movieLibraryContainer')
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ } else {
+ libraryContainer.html('');
+ }
+ $.each($(data.result.movies), jQuery.proxy(function (i, item) {
+ var floatableMovieCover = this.generateThumb('movie', item
+ .thumbnail, item.title);
+ floatableMovieCover.bind('click', {
+ movie: item
+ }, jQuery.proxy(this.displayMovieDetails, this));
+ libraryContainer.append(floatableMovieCover);
+ }, this));
+ libraryContainer.append($('<div>').addClass('footerPadding'));
+ $('#spinner').hide();
+ libraryContainer.bind('scroll', {
+ activeLibrary: libraryContainer
+ }, jQuery.proxy(this.updateScrollEffects, this));
+ libraryContainer.trigger('scroll');
+ myScroll = new iScroll('movieLibraryContainer');
+ }
+ });
+ } else {
+ libraryContainer.show();
+ libraryContainer.trigger('scroll');
+ }
+ },
+ tvshowLibraryOpen: function () {
+ this.resetPage();
+ $('#tvshowLibrary').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#tvshowLibraryContainer');
+ if (!libraryContainer || libraryContainer.length === 0) {
+ $('#spinner').show();
+ toggle = $('<p>').addClass('toggle');
+ togglePoster = $('<span>Poster</span>');
+ togglePoster.attr('id', 'togglePoster')
+ .css('cursor', 'pointer')
+ .bind('click', {
+ mode: 'poster'
+ }, jQuery.proxy(this.togglePosterView, this));
+ toggleBanner = $('<span>Banner</span>');
+ toggleBanner.attr('id', 'toggleBanner')
+ .css('cursor', 'pointer')
+ .addClass('activeMode')
+ .bind('click', {
+ mode: 'banner'
+ }, jQuery.proxy(this.togglePosterView, this));
+ toggleLandscape = $('<span>Landscape</span>');
+ toggleLandscape.attr('id', 'toggleLandscape')
+ .css('cursor', 'pointer')
+ .bind('click', {
+ mode: 'landscape'
+ }, jQuery.proxy(this.togglePosterView, this));
+ toggle.append(toggleBanner).append(' | ').append(togglePoster).append(' | ').append(
+ toggleLandscape);
+ this.toggle = toggle;
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'VideoLibrary.GetTVShows',
+ 'params': {
+ 'properties': [
+ 'genre',
+ 'plot',
+ 'title',
+ 'lastplayed',
+ 'episode',
+ 'year',
+ 'playcount',
+ 'rating',
+ 'thumbnail',
+ 'studio',
+ 'mpaa',
+ 'premiered'
+ ],
+ 'sort': {
+ 'method': 'sorttitle',
+ 'ignorearticle': true
+ }
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.tvshows) {
+ libraryContainer = $('<div>');
+ libraryContainer.append(toggle);
+ libraryContainer.attr('id', 'tvshowLibraryContainer')
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ } else {
+ libraryContainer.html('');
+ }
+ $.each($(data.result.tvshows), jQuery.proxy(function (i, item) {
+ var floatableTVShowCover = this.generateThumb('tvshow', item
+ .thumbnail, item.title);
+ floatableTVShowCover.bind('click', {
+ tvshow: item
+ }, jQuery.proxy(this.displayTVShowDetails, this));
+ libraryContainer.append(floatableTVShowCover);
+ }, this));
+ libraryContainer.append($('<div>').addClass('footerPadding'));
+ $('#spinner').hide();
+ libraryContainer.bind('scroll', {
+ activeLibrary: libraryContainer
+ }, jQuery.proxy(this.updateScrollEffects, this));
+ libraryContainer.trigger('scroll');
+ myScroll = new iScroll('tvshowLibraryContainer');
+ if (xbmc.core.getCookie('TVView') !== null &&
+ xbmc.core.getCookie('TVView') !== 'banner'
+ ) {
+ var view = xbmc.core.getCookie('TVView');
+ switch (view) {
+ case 'poster':
+ togglePoster.trigger('click');
+ break;
+ case 'landscape':
+ toggleLandscape.trigger('click');
+ break;
+ }
+ }
+ }
+ });
+ } else {
+ libraryContainer.prepend($(".toggle").detach()).show();
+ libraryContainer.trigger('scroll');
+ }
+ },
+ profilesOpen: function () {
+ this.resetPage();
+ $('#profiles').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#profilesContainer');
+ if (!libraryContainer || libraryContainer.length == 0) {
+ $('#spinner').show();
+ var currentProfile = "";
+ xbmc.rpc.request({
+ 'method': 'Profiles.GetCurrentProfile',
+ 'params': {
+ 'properties': [
+ 'lockmode'
+ ]
+ },
+ 'success': function (data) {
+ if (data)
+ if (data.result)
+ currentProfile = data.result.label;
+ }
+ });
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Profiles.GetProfiles',
+ 'params': {
+ 'limits': {
+ 'start': 0
+ },
+ 'properties': [
+ 'thumbnail'
+ ],
+ 'sort': {
+ 'method': 'sorttitle',
+ 'ignorearticle': true
+ }
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.profiles) {
+ libraryContainer = $('<div>');
+ libraryContainer.attr('id', 'profilesContainer')
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ } else {
+ libraryContainer.html('');
+ }
+ $.each($(data.result.profiles), jQuery.proxy(function (i, item) {
+ var itemLabel = item.label;
+ if (currentProfile == itemLabel) {
+ itemLabel = itemLabel + "*";
+ }
+ var floatableProfileThumb = this.generateThumb('profile', item
+ .thumbnail, itemLabel);
+ floatableProfileThumb.bind('click', {
+ profile: item
+ }, jQuery.proxy(this.loadProfile, this));
+ libraryContainer.append(floatableProfileThumb);
+ }, this));
+ libraryContainer.append($('<div>').addClass('footerPadding'));
+ $('#spinner').hide();
+ libraryContainer.bind('scroll', {
+ activeLibrary: libraryContainer
+ }, jQuery.proxy(this.updateScrollEffects, this));
+ libraryContainer.trigger('scroll');
+ myScroll = new iScroll('profilesContainer');
+ }
+ });
+ } else {
+ libraryContainer.show();
+ libraryContainer.trigger('scroll');
+ }
+ },
+ updateScrollEffects: function (event) {
+ if (event.data.activeLibrary && $(event.data.activeLibrary).scrollTop() > 0) {
+ $('#topScrollFade').fadeIn();
+ } else {
+ $('#topScrollFade').fadeOut();
+ }
+ },
+ startSlideshow: function (event) {
+ xbmc.rpc.request({
+ 'method': 'Player.Open',
+ 'params': {
+ 'item': {
+ 'recursive': 'true',
+ 'random': 'true',
+ 'path': this.replaceAll(event.data.directory.file, "\\", "\\\\")
+ }
+ },
+ 'success': function () {}
+ });
+ },
+ showDirectory: function (event) {
+ var directory = event.data.directory.file;
+ var jsonDirectory = this.replaceAll(directory, "\\", "\\\\");
+ this.resetPage();
+ $('#pictureLibrary').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#pictureLibraryDirContainer' + directory);
+ if (!libraryContainer || libraryContainer.length === 0) {
+ $('#spinner').show();
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Files.GetDirectory',
+ 'params': {
+ 'media': 'pictures',
+ 'directory': jsonDirectory
+ },
+ 'success': function (data) {
+ if (data && data.result && (data.result.directories || data.result.files)) {
+ libraryContainer = $('<div>');
+ libraryContainer.attr('id', 'pictureLibraryDirContainer' + directory)
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ var breadcrumb = $('<div>');
+ var seperator = '/';
+ var item = '';
+ var directoryArray = directory.split(seperator);
+ jQuery.each(directoryArray, function (i, v) {
+ if (v !== '') {
+ item += v + seperator;
+ breadcrumb.append($('<div>').text(' > ' + v).css('float',
+ 'left').addClass('breadcrumb'));
+ }
+ });
+ libraryContainer.append(breadcrumb);
+ libraryContainer.append($('<div>').css('clear', 'both'));
+ if (data.result.files) {
+ $.each($(data.result.files), jQuery.proxy(function (i, item) {
+ if (item.filetype == "file") {
+ var floatableImage = this.generateThumb('image',
+ item.file, item.label);
+ libraryContainer.append(floatableImage);
+ } else if (item.filetype == "directory") {
+ var floatableShare = this.generateThumb('directory',
+ item.thumbnail, item.label);
+ floatableShare.bind('click', {
+ directory: item
+ }, jQuery.proxy(this.showDirectory, this));
+ libraryContainer.append(floatableShare);
+ }
+ }, this));
+ }
+ libraryContainer.append($('<div>').addClass('footerPadding'));
+ } else {
+ libraryContainer.html('');
+ }
+ $('#spinner').hide();
+ libraryContainer.bind('scroll', {
+ activeLibrary: libraryContainer
+ }, jQuery.proxy(this.updateScrollEffects, this));
+ libraryContainer.trigger('scroll');
+ myScroll = new iScroll('#pictureLibraryDirContainer' + directory);
+ }
+ });
+ } else {
+ libraryContainer.show();
+ libraryContainer.trigger('scroll');
+ }
+ },
+ pictureLibraryOpen: function () {
+ this.resetPage();
+ $('#pictureLibrary').addClass('selected');
+ $('.contentContainer').hide();
+ var libraryContainer = $('#pictureLibraryContainer');
+ if (!libraryContainer || libraryContainer.length === 0) {
+ $('#spinner').show();
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Files.GetSources',
+ 'params': {
+ 'media': 'pictures'
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.shares) {
+ libraryContainer = $('<div>');
+ libraryContainer.attr('id', 'pictureLibraryContainer')
+ .addClass('contentContainer');
+ $('#content').append(libraryContainer);
+ } else {
+ libraryContainer.html('');
+ }
+ $.each($(data.result.shares), jQuery.proxy(function (i, item) {
+ var floatableShare = this.generateThumb('directory', item
+ .thumbnail, item.label);
+ floatableShare.bind('click', {
+ directory: item
+ }, jQuery.proxy(this.showDirectory, this));
+ libraryContainer.append(floatableShare);
+ }, this));
+ libraryContainer.append($('<div>').addClass('footerPadding'));
+ $('#spinner').hide();
+ libraryContainer.bind('scroll', {
+ activeLibrary: libraryContainer
+ }, jQuery.proxy(this.updateScrollEffects, this));
+ libraryContainer.trigger('scroll');
+ myScroll = new iScroll('#pictureLibraryContainer');
+ }
+ });
+ } else {
+ libraryContainer.show();
+ libraryContainer.trigger('scroll');
+ }
+ }
+};
diff --git a/debian/webinterface-default/js/NowPlayingManager.js b/debian/webinterface-default/js/NowPlayingManager.js
new file mode 100755
index 0000000..fb34f74
--- /dev/null
+++ b/debian/webinterface-default/js/NowPlayingManager.js
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+var NowPlayingManager = function () {
+ this.init();
+ return true;
+}
+
+NowPlayingManager.prototype = {
+ updateCounter: 0,
+ activePlayer: "",
+ activePlayerId: -1,
+ currentItem: -1,
+ playing: false,
+ paused: false,
+ playlistid: -1,
+
+ init: function () {
+ $('#pbPause').hide(); /* Assume we are not playing something */
+ this.bindPlaybackControls();
+ this.updateState();
+ $('#nextTrack').bind('click', jQuery.proxy(this.showPlaylist, this));
+ $('#nowPlayingPlaylist').bind('click', function () {
+ return false;
+ });
+ $(window).bind('click', jQuery.proxy(this.hidePlaylist, this));
+ },
+ updateState: function () {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Player.GetActivePlayers',
+ 'timeout': 3000,
+ 'success': function (data) {
+ if (data && data.result && data.result.length > 0) {
+ if (data.result[0].playerid != this.activePlayerId) {
+ this.activePlayerId = data.result[0].playerid;
+ this.activePlayer = data.result[0].type;
+ if (this.activePlayer == "audio") {
+ this.stopVideoPlaylistUpdate();
+ this.displayAudioNowPlaying();
+ } else if (this.activePlayer == "video") {
+ this.stopAudioPlaylistUpdate();
+ this.displayVideoNowPlaying();
+ } else {
+ this.stopVideoPlaylistUpdate();
+ this.stopAudioPlaylistUpdate();
+ this.activePlayer = "";
+ this.activePlayerId = -1;
+ }
+
+ this.stopRefreshTime();
+ this.updatePlayer();
+ }
+ } else if (!data || !data.result || data.result.length <= 0) {
+ this.stopVideoPlaylistUpdate();
+ this.stopAudioPlaylistUpdate();
+ this.activePlayer = "";
+ this.activePlayerId = -1;
+ }
+
+ if (this.activePlayerId >= 0) {
+ this.showFooter();
+ } else {
+ this.stopRefreshTime();
+ this.hideFooter();
+ }
+
+ setTimeout(jQuery.proxy(this.updateState, this), 1000);
+ },
+ 'error': function (data, error) {
+ xbmc.core.displayCommunicationError();
+ setTimeout(jQuery.proxy(this.updateState, this), 2000);
+ }
+ });
+ },
+ updatePlayer: function () {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Player.GetProperties',
+ 'params': {
+ 'playerid': this.activePlayerId,
+ 'properties': [
+ 'playlistid',
+ 'speed',
+ 'position',
+ 'totaltime',
+ 'time'
+ ]
+ },
+ 'success': function (data) {
+ if (data && data.result) {
+ this.playlistid = data.result.playlistid;
+ this.playing = data.result.speed != 0;
+ this.paused = data.result.speed == 0;
+ this.currentItem = data.result.position;
+ this.trackBaseTime = xbmc.core.timeToDuration(data.result.time);
+ this.trackDurationTime = xbmc.core.timeToDuration(data.result.totaltime);
+ if (!this.autoRefreshAudioData && !this.autoRefreshVideoData && this.playing) {
+ if (this.activePlayer == 'audio') {
+ this.autoRefreshAudioData = true;
+ this.refreshAudioData();
+ } else if (this.activePlayer == 'video') {
+ this.autoRefreshVideoData = true;
+ this.refreshVideoData();
+ }
+ }
+ }
+ if ((this.autoRefreshAudioData || this.autoRefreshVideoData) && !this
+ .activeItemTimer) {
+ this.activeItemTimer = 1;
+ setTimeout(jQuery.proxy(this.updateActiveItemDurationLoop, this), 1000);
+ }
+ }
+ });
+ },
+ bindPlaybackControls: function () {
+ $('#pbNext').bind('click', jQuery.proxy(this.nextTrack, this));
+ $('#pbPrev').bind('click', jQuery.proxy(this.prevTrack, this));
+ $('#pbStop').bind('click', jQuery.proxy(this.stopTrack, this));
+ $('#pbPlay').bind('click', jQuery.proxy(this.playPauseTrack, this));
+ $('#pbPause').bind('click', jQuery.proxy(this.playPauseTrack, this));
+ that = this
+ $(document).keypress(function (event) {
+ switch (event.which) {
+ case 32: //spacebar
+ event.preventDefault()
+ jQuery.proxy(that.playPauseTrack, that)();
+ break;
+ case 120: //x key
+ event.preventDefault()
+ jQuery.proxy(that.stopTrack, that)();
+ break;
+ case 44: //period key
+ event.preventDefault()
+ jQuery.proxy(that.nextTrack, that)();
+ break;
+ case 46: //comma key
+ event.preventDefault()
+ jQuery.proxy(that.prevTrack, that)();
+ break;
+ }
+ });
+ },
+ showPlaylist: function () {
+ $('#nextText').html('Playlist: ');
+ $('#nowPlayingPlaylist').show();
+ return false;
+ },
+ hidePlaylist: function () {
+ $('#nextText').html('Next: ');
+ $('#nowPlayingPlaylist').hide();
+ return false;
+ },
+ hideFooter: function () {
+ $('#footerPopover').hide();
+ $('#overlay').css('bottom', '0px');
+ },
+ showFooter: function () {
+ $('#footerPopover').show();
+ $('#overlay').css('bottom', '150px');
+ },
+ nextTrack: function () {
+ if (this.activePlayer) {
+ xbmc.rpc.request({
+ 'method': 'Player.GoTo',
+ 'params': {
+ 'playerid': this.activePlayerId,
+ 'to': 'next'
+ },
+ 'success': function () {}
+ });
+ }
+ },
+ prevTrack: function () {
+ if (this.activePlayer) {
+ xbmc.rpc.request({
+ 'method': 'Player.GoTo',
+ 'params': {
+ 'playerid': this.activePlayerId,
+ 'to': 'previous'
+ },
+ 'success': function () {}
+ });
+ }
+ },
+ stopTrack: function () {
+ if (this.activePlayer) {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Player.Stop',
+ 'params': {
+ 'playerid': this.activePlayerId
+ },
+ 'success': function (data) {
+ if (data && data.result == 'OK') {
+ this.playing = false;
+ this.paused = false;
+ this.trackBaseTime = 0;
+ this.trackDurationTime = 0;
+ this.showPlayButton();
+ }
+ }
+ });
+ }
+ },
+ playPauseTrack: function () {
+ if (this.activePlayer) {
+ var method = ((this.playing || this.paused) ? 'Player.PlayPause' : 'Playlist.Play');
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': method,
+ 'params': {
+ 'playerid': this.activePlayerId
+ },
+ 'success': function (data) {
+ if (data && data.result) {
+ this.playing = data.result.speed != 0;
+ this.paused = data.result.speed == 0;
+ if (this.playing) {
+ this.showPauseButton();
+ } else {
+ this.showPlayButton();
+ }
+ }
+ }
+ });
+ }
+ },
+ showPauseButton: function () {
+ $('#pbPause').show();
+ $('#pbPlay').hide();
+ },
+ showPlayButton: function () {
+ $('#pbPause').hide();
+ $('#pbPlay').show();
+ },
+ displayAudioNowPlaying: function () {
+ if (!this.autoRefreshAudioPlaylist) {
+ this.autoRefreshAudioPlaylist = true;
+ this.updateAudioPlaylist();
+ }
+ },
+ displayVideoNowPlaying: function () {
+ if (!this.autoRefreshVideoPlaylist) {
+ this.autoRefreshVideoPlaylist = true;
+ this.updateVideoPlaylist();
+ }
+ },
+ playPlaylistItem: function (sender) {
+ var sequenceId = $(sender.currentTarget).attr('seq');
+ if (!this.activePlaylistItem || (this.activePlaylistItem !== undefined && sequenceId != this
+ .activePlaylistItem.seq)) {
+ xbmc.rpc.request({
+ 'method': 'Player.GoTo',
+ 'params': {
+ 'playerid': this.activePlayerId,
+ 'to': sequenceId
+ },
+ 'success': function () {}
+ });
+ }
+ this.hidePlaylist();
+ },
+ playlistChanged: function (newPlaylist) {
+ if (this.activePlaylist && !newPlaylist || !this.activePlaylist && newPlaylist) {
+ return true;
+ }
+ if (!this.activePlaylist && !newPlaylist) {
+ return false;
+ }
+ if (this.activePlaylist.length != newPlaylist.length) {
+ return true;
+ }
+ for (var i = 0; i < newPlaylist.length; i++) {
+ if (!this.comparePlaylistItems(this.activePlaylist[i], newPlaylist[i])) {
+ return true;
+ }
+ }
+ return false;
+ },
+ updateAudioPlaylist: function () {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Playlist.GetItems',
+ 'params': {
+ 'playlistid': this.playlistid,
+ 'properties': [
+ 'title',
+ 'album',
+ 'artist',
+ 'duration',
+ 'thumbnail'
+ ]
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.items && data.result.items.length > 0 && data
+ .result.limits.total > 0) {
+ if (!this.activePlaylistItem || this.playlistChanged(data.result.items) || (this
+ .activePlaylistItem && (this.activePlaylistItem.seq != this.currentItem)
+ )) {
+ var ul = $('<ul>');
+ var activeItem;
+ $.each($(data.result.items), jQuery.proxy(function (i, item) {
+ var li = $('<li>');
+ var code = '<span class="duration">' + xbmc.core
+ .durationToString(item.duration) +
+ '</span><div class="trackInfo" title="' + item.title +
+ ' - ' + item.artist + '"><span class="trackTitle">' +
+ item.title + '</span> - <span class="trackArtist">' +
+ item.artist + '</span></div>';
+ if (i == this.currentItem) {
+ activeItem = item;
+ activeItem.seq = i;
+ li.addClass('activeItem');
+ }
+ if (i == (this.currentItem + 1)) {
+ $('#nextTrack').html(code).show();
+ }
+ li.bind('click', jQuery.proxy(this.playPlaylistItem, this));
+ ul.append(li.attr('seq', i).html(code));
+ }, this));
+ if (data.result.limits.total > 1) {
+ if (activeItem && data.result.limits.total - 1 == activeItem.seq) {
+ $('#nextTrack').html(
+ '<div class="trackInfo">Last track in playlist</div>')
+ .show();
+ }
+ $('#nextText').show();
+ $('#nowPlayingPlaylist').html('').append(ul);
+ } else {
+ $('#nextText').hide();
+ $('#nowPlayingPlaylist').hide();
+ $('#nextTrack').hide();
+ }
+ if (!this.comparePlaylistItems(activeItem, this.activePlaylistItem)) {
+ this.activePlaylistItem = activeItem;
+ if (!this.updateActiveItemDurationRunOnce) {
+ this.updateActiveItemDurationRunOnce = true;
+ this.updatePlayer();
+ }
+ } else if (!activeItem) {
+ this.stopRefreshTime();
+ }
+ this.activePlaylist = data.result.items;
+ $('#videoDescription').hide();
+ $('#audioDescription').show();
+ $('#nowPlayingPanel').show();
+ }
+ } else {
+ this.activePlaylist = null;
+ $('#audioDescription').hide();
+ $('#nowPlayingPanel').hide();
+ }
+ if (this.autoRefreshAudioPlaylist) {
+ setTimeout(jQuery.proxy(this.updateAudioPlaylist, this), 1000);
+ }
+ },
+ 'error': function (data) {
+ xbmc.core.displayCommunicationError();
+ if (this.autoRefreshAudioPlaylist) {
+ setTimeout(jQuery.proxy(this.updateAudioPlaylist, this),
+ 2000); /* Slow down request period */
+ }
+ }
+ });
+ },
+ stopAudioPlaylistUpdate: function () {
+ this.autoRefreshAudioPlaylist = false;
+ this.updateActiveItemDurationRunOnce = false;
+ },
+ stopVideoPlaylistUpdate: function () {
+ this.autoRefreshVideoPlaylist = false;
+ this.updateActiveItemDurationRunOnce = false;
+ },
+ updateActiveItemDurationLoop: function () {
+ this.activeItemTimer = 0;
+ this.updatePlayer();
+ },
+ refreshAudioDataLoop: function () {
+ this.audioRefreshTimer = 0;
+ this.refreshAudioData();
+ },
+ refreshAudioData: function () {
+ if (this.autoRefreshAudioData && !this.audioRefreshTimer) {
+ this.audioRefreshTimer = 1;
+ setTimeout(jQuery.proxy(this.refreshAudioDataLoop, this), 1000);
+ }
+ if (this.playing && !this.paused) {
+ this.trackBaseTime++;
+ }
+ if (this.paused) {
+ this.showPlayButton();
+ } else if (this.playing) {
+ this.showPauseButton();
+ }
+ if (this.activePlaylistItem) {
+ if (this.activePlaylistItem != this.lastPlaylistItem) {
+ this.lastPlaylistItem = this.activePlaylistItem;
+ var imgPath = xbmc.core.DEFAULT_ALBUM_COVER;
+ if (this.activePlaylistItem.thumbnail) {
+ imgPath = 'image/' + encodeURI(this.activePlaylistItem.thumbnail);
+ }
+ $('#audioCoverArt').html('<img src="' + imgPath + '" alt="' + this.activePlaylistItem.album +
+ ' cover art">');
+ $('#audioTrackTitle').html('<span title="' + this.activePlaylistItem.title + '">' + this
+ .activePlaylistItem.title + '</span>');
+ if (this.activePlaylistItem.album) {
+ $('#audioAlbumTitle').html('<span title="' + this.activePlaylistItem.album + '">' + this
+ .activePlaylistItem.album + '</span>')
+ .show();
+ } else {
+ $('#audioAlbumTitle').hide();
+ }
+ $('#audioArtistTitle').html(this.activePlaylistItem.artist);
+ $('#progressBar').attr('style', '');
+ }
+ $('#audioDuration').html(xbmc.core.durationToString(this.trackBaseTime) + ' / ' + xbmc.core
+ .durationToString(this.trackDurationTime));
+ var buttonWidth = $('#progressBar .progressIndicator').width();
+ var progressBarWidth = (this.trackBaseTime / this.trackDurationTime) * 100;
+ var progressSliderPosition = Math.ceil(($('#progressBar').width() / 100) * progressBarWidth) -
+ buttonWidth;
+ if (progressSliderPosition < 0) {
+ progressSliderPosition = 0;
+ }
+ if (progressBarWidth <= 100) {
+ $('#progressBar .elapsedTime').width(progressBarWidth + '%');
+ $('#progressBar .progressIndicator').css('left', progressSliderPosition);
+ }
+ }
+ },
+ refreshVideoDataLoop: function () {
+ this.videoRefreshTimer = 0;
+ this.refreshVideoData();
+ },
+ refreshVideoData: function () {
+ if (this.autoRefreshVideoData && !this.videoRefreshTimer) {
+ this.videoRefreshTimer = 1;
+ setTimeout(jQuery.proxy(this.refreshVideoDataLoop, this), 1500);
+ }
+ if (this.playing && !this.paused) {
+ this.trackBaseTime++;
+ }
+ if (this.paused) {
+ this.showPlayButton();
+ } else if (this.playing) {
+ this.showPauseButton();
+ }
+ if (this.activePlaylistItem) {
+ if (this.activePlaylistItem != this.lastPlaylistItem) {
+ this.lastPlaylistItem = this.activePlaylistItem;
+ var imgPath = xbmc.core.DEFAULT_VIDEO_COVER;
+ if (this.activePlaylistItem.thumbnail) {
+ imgPath = 'image/' + encodeURI(this.activePlaylistItem.thumbnail);
+ }
+ $('#videoCoverArt').html('<img src="' + imgPath + '" alt="' + this.activePlaylistItem.title +
+ ' cover art">');
+ $('#videoShowTitle').html(this.activePlaylistItem.showtitle || '&nbsp;');
+ var extra = '';
+ if (this.activePlaylistItem.season >= 0 && this.activePlaylistItem.episode >= 0) {
+ extra = this.activePlaylistItem.season + 'x' + this.activePlaylistItem.episode + ' ';
+ }
+ $('#videoTitle').html(extra + this.activePlaylistItem.title);
+ }
+ $('#videoDuration').html(xbmc.core.durationToString(this.trackBaseTime) + ' / ' + xbmc.core
+ .durationToString(this.trackDurationTime));
+ var buttonWidth = $('#progressBar .progressIndicator').width();
+ var progressBarWidth = (this.trackBaseTime / this.trackDurationTime) * 100;
+ var progressSliderPosition = Math.ceil(($('#progressBar').width() / 100) * progressBarWidth) -
+ buttonWidth;
+ if (progressSliderPosition < 0) {
+ progressSliderPosition = 0;
+ }
+ if (progressBarWidth <= 100) {
+ $('#progressBar .elapsedTime').width(progressBarWidth + '%');
+ $('#progressBar .progressIndicator').css('left', progressSliderPosition);
+ }
+ }
+ },
+ stopRefreshTime: function () {
+ this.autoRefreshAudioData = false;
+ this.autoRefreshVideoData = false;
+ },
+ comparePlaylistItems: function (item1, item2) {
+ if (!item1 || !item2) {
+ if (!item1 && !item2) {
+ return true;
+ }
+ return false;
+ }
+ if (item1.title != item2.title) {
+ return false;
+ }
+ if (item1.album != item2.album) {
+ return false;
+ }
+ if (item1.artist != item2.artist) {
+ return false;
+ }
+ if (item1.duration != item2.duration) {
+ return false;
+ }
+ if (item1.label != item2.label) {
+ return false;
+ }
+ if (item1.season != item2.season) {
+ return false;
+ }
+ if (item1.episode != item2.episode) {
+ return false;
+ }
+ return true;
+ },
+ updateVideoPlaylist: function () {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Playlist.GetItems',
+ 'params': {
+ 'playlistid': this.playlistid,
+ 'properties': [
+ 'title',
+ 'season',
+ 'episode',
+ 'plot',
+ 'runtime',
+ 'showtitle',
+ 'thumbnail'
+ ]
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.items && data.result.items.length > 0 && data
+ .result.limits.total > 0) {
+ if (this.playlistChanged(data.result.items)) {
+ var ul = $('<ul>');
+ var activeItem;
+ $.each($(data.result.items), jQuery.proxy(function (i, item) {
+ var li = $('<li>');
+ var extra = '';
+ if (item.season >= 0 && item.episode >= 0) {
+ extra = item.season + 'x' + item.episode + ' ';
+ }
+ var code = '<span class="duration">' + xbmc.core
+ .durationToString(item.runtime) +
+ '</span><div class="trackInfo" title="' + extra + item
+ .title + '"><span class="trackTitle">' + extra + item
+ .title + '</span></div>';
+ if (i == this.currentItem) {
+ activeItem = item;
+ activeItem.seq = i;
+ li.addClass('activeItem');
+ }
+ if (i == (this.currentItem + 1)) {
+ $('#nextTrack').html(code).show();
+ }
+ li.bind('click', jQuery.proxy(this.playPlaylistItem, this));
+ ul.append(li.attr('seq', i).html(code));
+ }, this));
+ if (data.result.limits.total > 1) {
+ $('#nextText').show();
+ if (activeItem && data.result.limits.total == activeItem.seq) {
+ $('#nextTrack').html(
+ '<div class="trackInfo">Last track in playlist</div>')
+ .show();
+ }
+ $('#nowPlayingPlaylist').html('').append(ul);
+ } else {
+ $('#nextText').hide();
+ $('#nowPlayingPlaylist').hide();
+ $('#nextTrack').hide();
+ }
+ if (!this.comparePlaylistItems(activeItem, this.activePlaylistItem)) {
+ this.activePlaylistItem = activeItem;
+ if (!this.updateActiveItemDurationRunOnce) {
+ this.updateActiveItemDurationRunOnce = true;
+ this.updatePlayer();
+ }
+ } else if (!activeItem) {
+ this.stopRefreshTime();
+ }
+ this.activePlaylist = data.result.items;
+ $('#videoDescription').show();
+ $('#audioDescription').hide();
+ $('#nowPlayingPanel').show();
+ }
+ } else {
+ xbmc.rpc.request({
+ 'context': this,
+ 'method': 'Player.GetItem',
+ 'params': {
+ 'playerid': this.activePlayerId,
+ 'properties': [
+ 'title',
+ 'season',
+ 'episode',
+ 'plot',
+ 'runtime',
+ 'showtitle',
+ 'thumbnail'
+ ]
+ },
+ 'success': function (data) {
+ if (data && data.result && data.result.item) {
+ this.activePlaylistItem = data.result.item;
+ if (!this.updateActiveItemDurationRunOnce) {
+ this.updateActiveItemDurationRunOnce = true;
+ this.updatePlayer();
+ }
+
+ $('#nextText').hide();
+ $('#nowPlayingPlaylist').hide();
+ $('#nextTrack').hide();
+
+ $('#videoDescription').show();
+ $('#audioDescription').hide();
+ $('#nowPlayingPanel').show();
+ } else {
+ this.activePlaylist = null;
+ $('#videoDescription').hide();
+ $('#nowPlayingPanel').hide();
+ }
+ },
+ 'error': function (data) {
+ xbmc.core.displayCommunicationError();
+ if (this.autoRefreshVideoPlaylist) {
+ setTimeout(jQuery.proxy(this.updateVideoPlaylist, this),
+ 2000); /* Slow down request period */
+ }
+ }
+ });
+ }
+ if (this.autoRefreshVideoPlaylist) {
+ setTimeout(jQuery.proxy(this.updateVideoPlaylist, this), 1000);
+ }
+ },
+ 'error': function (data) {
+ xbmc.core.displayCommunicationError();
+ if (this.autoRefreshVideoPlaylist) {
+ setTimeout(jQuery.proxy(this.updateVideoPlaylist, this),
+ 2000); /* Slow down request period */
+ }
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/debian/webinterface-default/js/json2.js b/debian/webinterface-default/js/json2.js
new file mode 100644
index 0000000..edb7751
--- /dev/null
+++ b/debian/webinterface-default/js/json2.js
@@ -0,0 +1,492 @@
+/*
+ json2.js
+ 2012-10-08
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value any JavaScript value, usually an object or array.
+
+ replacer an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or '&nbsp;'),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the value
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+*/
+
+/*jslint evil: true, regexp: true */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== 'object') {
+ JSON = {};
+}
+
+(function () {
+ 'use strict';
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function (key) {
+
+ return isFinite(this.valueOf()) ?
+ this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z' :
+ null;
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function (key) {
+ return this.valueOf();
+ };
+ }
+
+ var cx =
+ /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable =
+ /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"': '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can safely slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe escape
+ // sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ?
+ c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+ // Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+ // If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+ // If we were called with a replacer function, then call the replacer to
+ // obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+ // What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+ // JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+ // If the value is a boolean or null, convert it to a string. Note:
+ // typeof null does not produce 'null'. The case is included here in
+ // the remote chance that this gets fixed someday.
+
+ return String(value);
+
+ // If the type is 'object', we might be dealing with an object or an array or
+ // null.
+
+ case 'object':
+
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
+ // so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+ // Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+ // Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+ // The value is an array. Stringify every element. Use null as a placeholder
+ // for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+ // Join all of the elements together, separated with commas, and wrap them in
+ // brackets.
+
+ v = partial.length === 0 ?
+ '[]' :
+ gap ?
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+ // If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+ // Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+ // Join all of the member texts together, separated with commas,
+ // and wrap them in braces.
+
+ v = partial.length === 0 ?
+ '{}' :
+ gap ?
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
+ '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+ // If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== 'function') {
+ JSON.stringify = function (value, replacer, space) {
+
+ // The stringify method takes a value and an optional replacer, and an optional
+ // space parameter, and returns a JSON text. The replacer can be a function
+ // that can replace values, or an array of strings that will select the keys.
+ // A default replacer method can be provided. Use of the space parameter can
+ // produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+ // If the space parameter is a number, make an indent string containing that
+ // many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+ // If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+ // If there is a replacer, it must be a function or an array.
+ // Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+ // Make a fake root object containing our value under the key of ''.
+ // Return the result of stringifying the value.
+
+ return str('', {
+ '': value
+ });
+ };
+ }
+
+
+ // If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== 'function') {
+ JSON.parse = function (text, reviver) {
+
+ // The parse method takes a text and an optional reviver function, and returns
+ // a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+ // The walk method is used to recursively walk the resulting structure so
+ // that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+ // Parsing happens in four stages. In the first stage, we replace certain
+ // Unicode characters with escape sequences. JavaScript handles many characters
+ // incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+ // In the second stage, we run the text against regular expressions that look
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
+ // because they can cause invocation, and '=' because it can cause mutation.
+ // But just to be safe, we want to reject all unexpected forms.
+
+ // We split the second stage into 4 regexp operations in order to work around
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+ // replace all simple value tokens with ']' characters. Third, we delete all
+ // open brackets that follow a colon or comma or that begin the text. Finally,
+ // we look to see that the remaining characters are only whitespace or ']' or
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+ // In the third stage we use the eval function to compile the text into a
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
+ // in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+ // In the optional fourth stage, we recursively walk the new structure, passing
+ // each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function' ?
+ walk({
+ '': j
+ }, '') :
+ j;
+ }
+
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+ }
+}()); \ No newline at end of file
diff --git a/debian/webinterface-default/js/xbmc.core.js b/debian/webinterface-default/js/xbmc.core.js
new file mode 100644
index 0000000..2be141c
--- /dev/null
+++ b/debian/webinterface-default/js/xbmc.core.js
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+(function (window) {
+ "use strict";
+
+ var xbmc = window.xbmc || {};
+ xbmc.core = {
+ 'DEFAULT_ALBUM_COVER': 'images/DefaultAlbumCover.png',
+ 'DEFAULT_VIDEO_COVER': 'images/DefaultVideo.png',
+ 'JSON_RPC': 'jsonrpc',
+ 'applyDeviceFixes': function () {
+ window.document.addEventListener('touchmove', function (e) {
+ e.preventDefault();
+ });
+ },
+ 'displayCommunicationError': function (m) {
+ window.clearTimeout(xbmc.core.commsErrorTimeout);
+ var message = m || 'Connection to server lost';
+ $('#commsErrorPanel').html(message).show();
+ xbmc.core.commsErrorTimeout = window.setTimeout('xbmc.core.hideCommunicationError()', 5000);
+ },
+ 'durationToString': function (duration) {
+ var total_seconds = duration || 0,
+ seconds = total_seconds % 60,
+ minutes = Math.floor(total_seconds / 60) % 60,
+ hours = Math.floor(total_seconds / 3600),
+ result = ((hours > 0 && ((hours < 10 ? '0' : '') + hours + ':')) || '');
+ result += (minutes < 10 ? '0' : '') + minutes + ':';
+ result += (seconds < 10 ? '0' : '') + seconds;
+ return result;
+ },
+ 'getCookie': function (name) {
+ var i,
+ match,
+ haystack = window.document.cookie.split(';');
+ for (i = 0; i < haystack.length; i += 1) {
+ match = haystack[i].match(/^\s*[\S\s]*=([\s\S]*)\s*$/);
+ if (match && match.length === 2) {
+ return match[1];
+ }
+ }
+ return null;
+ },
+ 'hideCommunicationError': function () {
+ $('#commsErrorPanel').hide();
+ },
+ 'setCookie': function (name, value, days) {
+ var date,
+ expires;
+ if (name) {
+ if (days) {
+ date = new Date();
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
+ expires = "; expires=" + date.toGMTString();
+ } else {
+ expires = '';
+ }
+ window.document.cookie = name + "=" + value + expires + "; path=/";
+ }
+ },
+ 'timeToDuration': function (time) {
+ var duration;
+ time = time || {};
+ duration = ((time.hours || 0) * 3600);
+ duration += ((time.minutes || 0) * 60);
+ duration += (time.seconds || 0);
+ return duration;
+ }
+ };
+
+ window.xbmc = xbmc;
+}(window)); \ No newline at end of file
diff --git a/debian/webinterface-default/js/xbmc.init.js b/debian/webinterface-default/js/xbmc.init.js
new file mode 100644
index 0000000..c61a81f
--- /dev/null
+++ b/debian/webinterface-default/js/xbmc.init.js
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+(function () {
+ "use strict"
+
+ var mediaLibrary = new MediaLibrary(),
+ nowPlayingManager = new NowPlayingManager();
+ xbmc.core.applyDeviceFixes();
+}()); \ No newline at end of file
diff --git a/debian/webinterface-default/js/xbmc.launcher.js b/debian/webinterface-default/js/xbmc.launcher.js
new file mode 100644
index 0000000..4f039d6
--- /dev/null
+++ b/debian/webinterface-default/js/xbmc.launcher.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+(function (document) {
+ "use strict";
+
+ var i,
+ script,
+ debug = false,
+ /* Set to true to disable cached javascript */
+ version = (debug ? Math.random() : '2.1.0'),
+ scripts = [
+ "js/jquery-1.8.2.min.js",
+ "js/json2.js",
+ "js/iscroll-min.js",
+ "js/xbmc.core.js",
+ "js/xbmc.rpc.js",
+ "js/MediaLibrary.js",
+ "js/NowPlayingManager.js",
+ "js/xbmc.init.js"
+ ];
+
+ for (i = 0; i < scripts.length; i += 1) {
+ script = '<script type="text/javascript" src="';
+ script += scripts[i] + '?' + version;
+ script += '"><\/script>';
+ document.write(script);
+ }
+}(window.document)); \ No newline at end of file
diff --git a/debian/webinterface-default/js/xbmc.rpc.js b/debian/webinterface-default/js/xbmc.rpc.js
new file mode 100644
index 0000000..60b1d8b
--- /dev/null
+++ b/debian/webinterface-default/js/xbmc.rpc.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+(function (window) {
+
+ var xbmc = window.xbmc || {};
+
+ xbmc.rpc = {
+ 'default_options': {
+ 'contentType': 'application/json',
+ 'dataType': 'json',
+ 'type': 'POST',
+ 'success': function () {
+ $('#spinner').hide();
+ }
+ },
+ 'request': function (options) {
+ var request_options = jQuery.extend({}, this.default_options, options);
+ request_options.url = xbmc.core.JSON_RPC + '?' + options.method;
+ request_options.data = JSON.stringify({
+ 'jsonrpc': '2.0',
+ 'method': options.method,
+ 'id': 1,
+ 'params': request_options.params
+ });
+ return jQuery.ajax(request_options)
+ }
+ };
+
+ window.xbmc = xbmc;
+
+}(window)); \ No newline at end of file