git: 79dc32d40ec0 - main - emulators/duckstation: Update patches following upstream integration

From: Ganael LAPLANCHE <martymac_at_FreeBSD.org>
Date: Wed, 06 Dec 2023 12:03:58 UTC
The branch main has been updated by martymac:

URL: https://cgit.FreeBSD.org/ports/commit/?id=79dc32d40ec08b1fb6326805db934e351e0e524b

commit 79dc32d40ec08b1fb6326805db934e351e0e524b
Author:     Ganael LAPLANCHE <martymac@FreeBSD.org>
AuthorDate: 2023-12-06 11:38:22 +0000
Commit:     Ganael LAPLANCHE <martymac@FreeBSD.org>
CommitDate: 2023-12-06 12:03:25 +0000

    emulators/duckstation: Update patches following upstream integration
---
 emulators/duckstation/Makefile                     |   1 +
 emulators/duckstation/files/patch-407049c.txt      |  89 +++++
 emulators/duckstation/files/patch-5246252.txt      | 369 +++++++++++++++++++++
 ...-src-common-threading.cpp => patch-5486a7a.txt} |  12 +-
 emulators/duckstation/files/patch-af046c8.txt      |  25 ++
 ...{patch-data-directory.txt => patch-b6d6756.txt} |  24 +-
 .../files/patch-src-common-byte_stream.cpp         |  14 -
 .../files/patch-src-common-file_system.cpp         |  70 ----
 8 files changed, 511 insertions(+), 93 deletions(-)

diff --git a/emulators/duckstation/Makefile b/emulators/duckstation/Makefile
index dc7ab1d7fc2f..0a7a09f50cbb 100644
--- a/emulators/duckstation/Makefile
+++ b/emulators/duckstation/Makefile
@@ -1,6 +1,7 @@
 PORTNAME=	duckstation
 PORTVERSION=	20231124
 DISTVERSIONPREFIX=	v
+PORTREVISION=	1
 CATEGORIES=	emulators
 
 MAINTAINER=	martymac@FreeBSD.org
diff --git a/emulators/duckstation/files/patch-407049c.txt b/emulators/duckstation/files/patch-407049c.txt
new file mode 100644
index 000000000000..d07843895516
--- /dev/null
+++ b/emulators/duckstation/files/patch-407049c.txt
@@ -0,0 +1,89 @@
+Backport 407049c from upstream
+
+commit 407049cd91b65c8a917533b87c7264dba3e2adc5
+Author: Stenzek <stenzek@gmail.com>
+Date:   Tue Dec 5 15:21:52 2023 +1000
+
+    Qt: Resolve any symbolic links in AppRoot/DataRoot
+    
+    Should fix incorrect relative path generation on FreeBSD, where /home is
+    a symlink to /usr/home.
+
+diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp
+index 8e2778ed..15ce8ae5 100644
+--- src/duckstation-nogui/nogui_host.cpp
++++ src/duckstation-nogui/nogui_host.cpp
+@@ -147,7 +147,7 @@ bool NoGUIHost::ShouldUsePortableMode()
+ 
+ void NoGUIHost::SetAppRoot()
+ {
+-  std::string program_path(FileSystem::GetProgramPath());
++  const std::string program_path = FileSystem::GetProgramPath();
+   Log_InfoPrintf("Program Path: %s", program_path.c_str());
+ 
+   EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
+@@ -190,7 +190,7 @@ void NoGUIHost::SetDataDirectory()
+   const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
+   if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
+   {
+-    EmuFolders::DataRoot = Path::Combine(xdg_config_home, "duckstation");
++    EmuFolders::DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "duckstation"));
+   }
+   else
+   {
+@@ -203,14 +203,14 @@ void NoGUIHost::SetDataDirectory()
+       const std::string share_dir(Path::Combine(local_dir, "share"));
+       FileSystem::EnsureDirectoryExists(local_dir.c_str(), false);
+       FileSystem::EnsureDirectoryExists(share_dir.c_str(), false);
+-      EmuFolders::DataRoot = Path::Combine(share_dir, "duckstation");
++      EmuFolders::DataRoot = Path::RealPath(Path::Combine(share_dir, "duckstation"));
+     }
+   }
+ #elif defined(__APPLE__)
+   static constexpr char MAC_DATA_DIR[] = "Library/Application Support/DuckStation";
+   const char* home_dir = getenv("HOME");
+   if (home_dir)
+-    EmuFolders::DataRoot = Path::Combine(home_dir, MAC_DATA_DIR);
++    EmuFolders::DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
+ #endif
+ 
+   // make sure it exists
+diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp
+index b7977cb6..9a52689c 100644
+--- src/duckstation-qt/qthost.cpp
++++ src/duckstation-qt/qthost.cpp
+@@ -252,7 +252,7 @@ bool QtHost::ShouldUsePortableMode()
+ 
+ void QtHost::SetAppRoot()
+ {
+-  std::string program_path(FileSystem::GetProgramPath());
++  const std::string program_path = FileSystem::GetProgramPath();
+   Log_InfoPrintf("Program Path: %s", program_path.c_str());
+ 
+   EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
+@@ -295,7 +295,7 @@ void QtHost::SetDataDirectory()
+   const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
+   if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
+   {
+-    EmuFolders::DataRoot = Path::Combine(xdg_config_home, "duckstation");
++    EmuFolders::DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "duckstation"));
+   }
+   else
+   {
+@@ -308,14 +308,14 @@ void QtHost::SetDataDirectory()
+       const std::string share_dir(Path::Combine(local_dir, "share"));
+       FileSystem::EnsureDirectoryExists(local_dir.c_str(), false);
+       FileSystem::EnsureDirectoryExists(share_dir.c_str(), false);
+-      EmuFolders::DataRoot = Path::Combine(share_dir, "duckstation");
++      EmuFolders::DataRoot = Path::RealPath(Path::Combine(share_dir, "duckstation"));
+     }
+   }
+ #elif defined(__APPLE__)
+   static constexpr char MAC_DATA_DIR[] = "Library/Application Support/DuckStation";
+   const char* home_dir = getenv("HOME");
+   if (home_dir)
+-    EmuFolders::DataRoot = Path::Combine(home_dir, MAC_DATA_DIR);
++    EmuFolders::DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
+ #endif
+ 
+   // make sure it exists
diff --git a/emulators/duckstation/files/patch-5246252.txt b/emulators/duckstation/files/patch-5246252.txt
new file mode 100644
index 000000000000..35487f7939fa
--- /dev/null
+++ b/emulators/duckstation/files/patch-5246252.txt
@@ -0,0 +1,369 @@
+Backport 5246252 from upstream
+
+commit 524625269fe5f365a10dbfc2e18c325b6c4fab61
+Author: Stenzek <stenzek@gmail.com>
+Date:   Tue Dec 5 15:21:37 2023 +1000
+
+    Path: Add RealPath()
+
+diff --git a/src/common-tests/path_tests.cpp b/src/common-tests/path_tests.cpp
+index c0e58526..03b307e4 100644
+--- src/common-tests/path_tests.cpp
++++ src/common-tests/path_tests.cpp
+@@ -5,7 +5,7 @@
+ #include "common/types.h"
+ #include <gtest/gtest.h>
+ 
+-TEST(FileSystem, ToNativePath)
++TEST(Path, ToNativePath)
+ {
+   ASSERT_EQ(Path::ToNativePath(""), "");
+ 
+@@ -29,7 +29,7 @@ TEST(FileSystem, ToNativePath)
+ #endif
+ }
+ 
+-TEST(FileSystem, IsAbsolute)
++TEST(Path, IsAbsolute)
+ {
+   ASSERT_FALSE(Path::IsAbsolute(""));
+   ASSERT_FALSE(Path::IsAbsolute("foo"));
+@@ -61,7 +61,7 @@ TEST(FileSystem, IsAbsolute)
+ #endif
+ }
+ 
+-TEST(FileSystem, Canonicalize)
++TEST(Path, Canonicalize)
+ {
+   ASSERT_EQ(Path::Canonicalize(""), Path::ToNativePath(""));
+   ASSERT_EQ(Path::Canonicalize("foo/bar/../baz"), Path::ToNativePath("foo/baz"));
+@@ -72,10 +72,8 @@ TEST(FileSystem, Canonicalize)
+   ASSERT_EQ(Path::Canonicalize("./foo"), Path::ToNativePath("foo"));
+   ASSERT_EQ(Path::Canonicalize("../foo"), Path::ToNativePath("../foo"));
+   ASSERT_EQ(Path::Canonicalize("foo/b🙃ar/../b🙃az/./foo"), Path::ToNativePath("foo/b🙃az/foo"));
+-  ASSERT_EQ(
+-    Path::Canonicalize(
+-      "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az/../foℹ︎o"),
+-    Path::ToNativePath("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/foℹ︎o"));
++  ASSERT_EQ(Path::Canonicalize("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az/../foℹ︎o"),
++            Path::ToNativePath("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/foℹ︎o"));
+ #ifdef _WIN32
+   ASSERT_EQ(Path::Canonicalize("C:\\foo\\bar\\..\\baz\\.\\foo"), "C:\\foo\\baz\\foo");
+   ASSERT_EQ(Path::Canonicalize("C:/foo\\bar\\..\\baz\\.\\foo"), "C:\\foo\\baz\\foo");
+@@ -87,7 +85,7 @@ TEST(FileSystem, Canonicalize)
+ #endif
+ }
+ 
+-TEST(FileSystem, Combine)
++TEST(Path, Combine)
+ {
+   ASSERT_EQ(Path::Combine("", ""), Path::ToNativePath(""));
+   ASSERT_EQ(Path::Combine("foo", "bar"), Path::ToNativePath("foo/bar"));
+@@ -108,7 +106,7 @@ TEST(FileSystem, Combine)
+ #endif
+ }
+ 
+-TEST(FileSystem, AppendDirectory)
++TEST(Path, AppendDirectory)
+ {
+   ASSERT_EQ(Path::AppendDirectory("foo/bar", "baz"), Path::ToNativePath("foo/baz/bar"));
+   ASSERT_EQ(Path::AppendDirectory("", "baz"), Path::ToNativePath("baz"));
+@@ -122,7 +120,7 @@ TEST(FileSystem, AppendDirectory)
+ #endif
+ }
+ 
+-TEST(FileSystem, MakeRelative)
++TEST(Path, MakeRelative)
+ {
+   ASSERT_EQ(Path::MakeRelative("", ""), Path::ToNativePath(""));
+   ASSERT_EQ(Path::MakeRelative("foo", ""), Path::ToNativePath("foo"));
+@@ -141,8 +139,7 @@ TEST(FileSystem, MakeRelative)
+   ASSERT_EQ(Path::MakeRelative(A "foo/b🙃ar", A "foo/b🙃az"), Path::ToNativePath("../b🙃ar"));
+   ASSERT_EQ(Path::MakeRelative(A "f🙃oo/b🙃ar", A "f🙃oo/b🙃az"), Path::ToNativePath("../b🙃ar"));
+   ASSERT_EQ(
+-    Path::MakeRelative(A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃ar",
+-                       A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az"),
++    Path::MakeRelative(A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃ar", A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az"),
+     Path::ToNativePath("../b🙃ar"));
+ 
+ #undef A
+@@ -154,7 +151,7 @@ TEST(FileSystem, MakeRelative)
+ #endif
+ }
+ 
+-TEST(FileSystem, GetExtension)
++TEST(Path, GetExtension)
+ {
+   ASSERT_EQ(Path::GetExtension("foo"), "");
+   ASSERT_EQ(Path::GetExtension("foo.txt"), "txt");
+@@ -164,7 +161,7 @@ TEST(FileSystem, GetExtension)
+   ASSERT_EQ(Path::GetExtension("a/b/foo"), "");
+ }
+ 
+-TEST(FileSystem, GetFileName)
++TEST(Path, GetFileName)
+ {
+   ASSERT_EQ(Path::GetFileName(""), "");
+   ASSERT_EQ(Path::GetFileName("foo"), "foo");
+@@ -179,7 +176,7 @@ TEST(FileSystem, GetFileName)
+ #endif
+ }
+ 
+-TEST(FileSystem, GetFileTitle)
++TEST(Path, GetFileTitle)
+ {
+   ASSERT_EQ(Path::GetFileTitle(""), "");
+   ASSERT_EQ(Path::GetFileTitle("foo"), "foo");
+@@ -193,7 +190,7 @@ TEST(FileSystem, GetFileTitle)
+ #endif
+ }
+ 
+-TEST(FileSystem, GetDirectory)
++TEST(Path, GetDirectory)
+ {
+   ASSERT_EQ(Path::GetDirectory(""), "");
+   ASSERT_EQ(Path::GetDirectory("foo"), "");
+@@ -207,7 +204,7 @@ TEST(FileSystem, GetDirectory)
+ #endif
+ }
+ 
+-TEST(FileSystem, ChangeFileName)
++TEST(Path, ChangeFileName)
+ {
+   ASSERT_EQ(Path::ChangeFileName("", ""), Path::ToNativePath(""));
+   ASSERT_EQ(Path::ChangeFileName("", "bar"), Path::ToNativePath("bar"));
+@@ -227,13 +224,14 @@ TEST(FileSystem, ChangeFileName)
+ #endif
+ }
+ 
+-TEST(FileSystem, SanitizeFileName)
++TEST(Path, SanitizeFileName)
+ {
+   ASSERT_EQ(Path::SanitizeFileName("foo"), "foo");
+   ASSERT_EQ(Path::SanitizeFileName("foo/bar"), "foo_bar");
+   ASSERT_EQ(Path::SanitizeFileName("f🙃o"), "f🙃o");
+   ASSERT_EQ(Path::SanitizeFileName("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"), "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
+-  ASSERT_EQ(Path::SanitizeFileName("abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}"), "abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}");
++  ASSERT_EQ(Path::SanitizeFileName("abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}"),
++            "abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}");
+   ASSERT_EQ(Path::SanitizeFileName("some*path**with*asterisks"), "some_path__with_asterisks");
+ #ifdef _WIN32
+   ASSERT_EQ(Path::SanitizeFileName("foo:"), "foo_");
+@@ -243,4 +241,18 @@ TEST(FileSystem, SanitizeFileName)
+   ASSERT_EQ(Path::SanitizeFileName("foo\\bar", false), "foo\\bar");
+ #endif
+   ASSERT_EQ(Path::SanitizeFileName("foo/bar", false), "foo/bar");
+-}
+\ No newline at end of file
++}
++
++#if 0
++
++// Relies on presence of files.
++TEST(Path, RealPath)
++{
++#ifdef _WIN32
++  ASSERT_EQ(Path::RealPath("C:\\Users\\Me\\Desktop\\foo\\baz"), "C:\\Users\\Me\\Desktop\\foo\\bar\\baz");
++#else
++  ASSERT_EQ(Path::RealPath("/lib/foo/bar"), "/usr/lib/foo/bar");
++#endif
++}
++
++#endif
+\ No newline at end of file
+diff --git a/src/common/file_system.cpp b/src/common/file_system.cpp
+index 4a5fc6b0..4ce4b9ed 100644
+--- src/common/file_system.cpp
++++ src/common/file_system.cpp
+@@ -12,6 +12,7 @@
+ #include <cstdlib>
+ #include <cstring>
+ #include <limits>
++#include <numeric>
+ 
+ #ifdef __APPLE__
+ #include <mach-o/dyld.h>
+@@ -192,6 +193,161 @@ bool Path::IsAbsolute(const std::string_view& path)
+ #endif
+ }
+ 
++std::string Path::RealPath(const std::string_view& path)
++{
++  // Resolve non-absolute paths first.
++  std::vector<std::string_view> components;
++  if (!IsAbsolute(path))
++    components = Path::SplitNativePath(Path::Combine(FileSystem::GetWorkingDirectory(), path));
++  else
++    components = Path::SplitNativePath(path);
++
++  std::string realpath;
++  if (components.empty())
++    return realpath;
++
++  // Different to path because relative.
++  realpath.reserve(std::accumulate(components.begin(), components.end(), static_cast<size_t>(0),
++                                   [](size_t l, const std::string_view& s) { return l + s.length(); }) +
++                   components.size() + 1);
++
++#ifdef _WIN32
++  std::wstring wrealpath;
++  std::vector<WCHAR> symlink_buf;
++  wrealpath.reserve(realpath.size());
++  symlink_buf.resize(path.size() + 1);
++
++  // Check for any symbolic links throughout the path while adding components.
++  bool test_symlink = true;
++  for (const std::string_view& comp : components)
++  {
++    if (!realpath.empty())
++      realpath.push_back(FS_OSPATH_SEPARATOR_CHARACTER);
++    realpath.append(comp);
++    if (test_symlink)
++    {
++      DWORD attribs;
++      if (StringUtil::UTF8StringToWideString(wrealpath, realpath) &&
++          (attribs = GetFileAttributesW(wrealpath.c_str())) != INVALID_FILE_ATTRIBUTES)
++      {
++        // if not a link, go to the next component
++        if (attribs & FILE_ATTRIBUTE_REPARSE_POINT)
++        {
++          const HANDLE hFile =
++            CreateFileW(wrealpath.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
++                        nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
++          if (hFile != INVALID_HANDLE_VALUE)
++          {
++            // is a link! resolve it.
++            DWORD ret = GetFinalPathNameByHandleW(hFile, symlink_buf.data(), static_cast<DWORD>(symlink_buf.size()),
++                                                  FILE_NAME_NORMALIZED);
++            if (ret > symlink_buf.size())
++            {
++              symlink_buf.resize(ret);
++              ret = GetFinalPathNameByHandleW(hFile, symlink_buf.data(), static_cast<DWORD>(symlink_buf.size()),
++                                              FILE_NAME_NORMALIZED);
++            }
++            if (ret != 0)
++              StringUtil::WideStringToUTF8String(realpath, std::wstring_view(symlink_buf.data(), ret));
++            else
++              test_symlink = false;
++
++            CloseHandle(hFile);
++          }
++        }
++      }
++      else
++      {
++        // not a file or link
++        test_symlink = false;
++      }
++    }
++  }
++
++  // GetFinalPathNameByHandleW() adds a \\?\ prefix, so remove it.
++  if (realpath.starts_with("\\\\?\\") && IsAbsolute(std::string_view(realpath.data() + 4, realpath.size() - 4)))
++    realpath.erase(0, 4);
++
++#else
++  // Why this monstrosity instead of calling realpath()? realpath() only works on files that exist.
++  std::string basepath;
++  std::string symlink;
++
++  basepath.reserve(realpath.capacity());
++  symlink.resize(realpath.capacity());
++
++  // Check for any symbolic links throughout the path while adding components.
++  bool test_symlink = true;
++  for (const std::string_view& comp : components)
++  {
++    if (!test_symlink)
++    {
++      realpath.push_back(FS_OSPATH_SEPARATOR_CHARACTER);
++      realpath.append(comp);
++      continue;
++    }
++
++    basepath = realpath;
++    if (realpath.empty() || realpath.back() != FS_OSPATH_SEPARATOR_CHARACTER)
++      realpath.push_back(FS_OSPATH_SEPARATOR_CHARACTER);
++    realpath.append(comp);
++
++    // Check if the last component added is a symlink
++    struct stat sb;
++    if (lstat(realpath.c_str(), &sb) != 0)
++    {
++      // Don't bother checking any further components once we error out.
++      test_symlink = false;
++      continue;
++    }
++    else if (!S_ISLNK(sb.st_mode))
++    {
++      // Nope, keep going.
++      continue;
++    }
++
++    for (;;)
++    {
++      ssize_t sz = readlink(realpath.c_str(), symlink.data(), symlink.size());
++      if (sz < 0)
++      {
++        // shouldn't happen, due to the S_ISLNK check above.
++        test_symlink = false;
++        break;
++      }
++      else if (static_cast<size_t>(sz) == symlink.size())
++      {
++        // need a larger buffer
++        symlink.resize(symlink.size() * 2);
++        continue;
++      }
++      else
++      {
++        // is a link, and we resolved it. gotta check if the symlink itself is relative :(
++        symlink.resize(static_cast<size_t>(sz));
++        if (!Path::IsAbsolute(symlink))
++        {
++          // symlink is relative to the directory of the symlink
++          realpath = basepath;
++          if (realpath.empty() || realpath.back() != FS_OSPATH_SEPARATOR_CHARACTER)
++            realpath.push_back(FS_OSPATH_SEPARATOR_CHARACTER);
++          realpath.append(symlink);
++        }
++        else
++        {
++          // Use the new, symlinked path.
++          realpath = symlink;
++        }
++
++        break;
++      }
++    }
++  }
++#endif
++
++  return realpath;
++}
++
+ std::string Path::ToNativePath(const std::string_view& path)
+ {
+   std::string ret;
+@@ -1382,6 +1538,7 @@ std::string FileSystem::GetProgramPath()
+     break;
+   }
+ 
++  // Windows symlinks don't behave silly like Linux, so no need to RealPath() it.
+   return StringUtil::WideStringToUTF8String(buffer);
+ }
+ 
+diff --git a/src/common/path.h b/src/common/path.h
+index 7c03c1ad..12d86471 100644
+--- src/common/path.h
++++ src/common/path.h
+@@ -31,6 +31,9 @@ void SanitizeFileName(std::string* str, bool strip_slashes = true);
+ /// Returns true if the specified path is an absolute path (C:\Path on Windows or /path on Unix).
+ bool IsAbsolute(const std::string_view& path);
+ 
++/// Resolves any symbolic links in the specified path.
++std::string RealPath(const std::string_view& path);
++
+ /// Makes the specified path relative to another (e.g. /a/b/c, /a/b -> ../c).
+ /// Both paths must be relative, otherwise this function will just return the input path.
+ std::string MakeRelative(const std::string_view& path, const std::string_view& relative_to);
diff --git a/emulators/duckstation/files/patch-src-common-threading.cpp b/emulators/duckstation/files/patch-5486a7a.txt
similarity index 65%
rename from emulators/duckstation/files/patch-src-common-threading.cpp
rename to emulators/duckstation/files/patch-5486a7a.txt
index 6448ee870fc0..f49e3f6f6af7 100644
--- a/emulators/duckstation/files/patch-src-common-threading.cpp
+++ b/emulators/duckstation/files/patch-5486a7a.txt
@@ -1,6 +1,14 @@
-Fix get_thread_time() declaration on FreeBSD
+Backport 5486a7a from upstream
 
---- src/common/threading.cpp.orig	2023-10-16 12:57:57 UTC
+commit 5486a7a467dc0cd502bd8c4e7eb7a3c95a5a3c02
+Author: Ganael Laplanche <ganael.laplanche@martymac.org>
+Date:   Wed Nov 29 21:05:21 2023 +0100
+
+    Fix get_thread_time() declaration on FreeBSD
+
+diff --git a/src/common/threading.cpp b/src/common/threading.cpp
+index ac272ff1..8dacce83 100644
+--- src/common/threading.cpp
 +++ src/common/threading.cpp
 @@ -64,7 +64,7 @@ static u64 getthreadtime(thread_port_t thread)
  }
diff --git a/emulators/duckstation/files/patch-af046c8.txt b/emulators/duckstation/files/patch-af046c8.txt
new file mode 100644
index 000000000000..1830dd4933d5
--- /dev/null
+++ b/emulators/duckstation/files/patch-af046c8.txt
@@ -0,0 +1,25 @@
+Backport af046c8 from upstream
+
+commit af046c8987ec3c580eff8a0c7c438762377fdbca
+Author: Ganael Laplanche <ganael.laplanche@martymac.org>
+Date:   Wed Nov 29 20:54:12 2023 +0100
+
+    Fix build on FreeBSD
+    
+    FreeBSD's alloca(3) only needs <stdlib.h>
+
+diff --git a/src/common/byte_stream.cpp b/src/common/byte_stream.cpp
+index bb26af0f..18609996 100644
+--- src/common/byte_stream.cpp
++++ src/common/byte_stream.cpp
+@@ -28,8 +28,10 @@
+ #ifdef _MSC_VER
+ #include <malloc.h>
+ #else
++#if !defined(__FreeBSD__)
+ #include <alloca.h>
+ #endif
++#endif
+ 
+ Log_SetChannel(ByteStream);
+ 
diff --git a/emulators/duckstation/files/patch-data-directory.txt b/emulators/duckstation/files/patch-b6d6756.txt
similarity index 58%
rename from emulators/duckstation/files/patch-data-directory.txt
rename to emulators/duckstation/files/patch-b6d6756.txt
index 94f00c7aa01d..aa3be9286ef5 100644
--- a/emulators/duckstation/files/patch-data-directory.txt
+++ b/emulators/duckstation/files/patch-b6d6756.txt
@@ -1,8 +1,16 @@
-Fix data directory handling on FreeBSD
+Backport b6d6756 from upstream
 
---- src/duckstation-qt/qthost.cpp.orig	2023-10-16 12:57:57 UTC
-+++ src/duckstation-qt/qthost.cpp
-@@ -289,7 +289,7 @@ void QtHost::SetDataDirectory()
+commit b6d67560e39ca3c7154d97355fec988210a460e5
+Author: Ganael Laplanche <ganael.laplanche@martymac.org>
+Date:   Wed Nov 29 20:38:56 2023 +0100
+
+    Fix data directory handling on FreeBSD
+
+diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp
+index 5543c2c9..8e2778ed 100644
+--- src/duckstation-nogui/nogui_host.cpp
++++ src/duckstation-nogui/nogui_host.cpp
+@@ -185,7 +185,7 @@ void NoGUIHost::SetDataDirectory()
        EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "DuckStation");
      CoTaskMemFree(documents_directory);
    }
@@ -11,9 +19,11 @@ Fix data directory handling on FreeBSD
    // Use $XDG_CONFIG_HOME/duckstation if it exists.
    const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
    if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
---- src/duckstation-nogui/nogui_host.cpp.orig	2023-10-16 12:57:57 UTC
-+++ src/duckstation-nogui/nogui_host.cpp
-@@ -186,7 +186,7 @@ void NoGUIHost::SetDataDirectory()
+diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp
+index a2039160..b7977cb6 100644
+--- src/duckstation-qt/qthost.cpp
++++ src/duckstation-qt/qthost.cpp
+@@ -290,7 +290,7 @@ void QtHost::SetDataDirectory()
        EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "DuckStation");
      CoTaskMemFree(documents_directory);
    }
diff --git a/emulators/duckstation/files/patch-src-common-byte_stream.cpp b/emulators/duckstation/files/patch-src-common-byte_stream.cpp
deleted file mode 100644
index f791bfdd6232..000000000000
--- a/emulators/duckstation/files/patch-src-common-byte_stream.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-FreeBSD's alloca(3) only needs <stdlib.h>
-
---- src/common/byte_stream.cpp.orig	2023-10-16 12:57:57 UTC
-+++ src/common/byte_stream.cpp
-@@ -28,7 +28,9 @@
- #ifdef _MSC_VER
- #include <malloc.h>
- #else
-+#if !defined(__FreeBSD__)
- #include <alloca.h>
-+#endif
- #endif
- 
- Log_SetChannel(ByteStream);
diff --git a/emulators/duckstation/files/patch-src-common-file_system.cpp b/emulators/duckstation/files/patch-src-common-file_system.cpp
deleted file mode 100644
index a88db23f479a..000000000000
--- a/emulators/duckstation/files/patch-src-common-file_system.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-Fix an off-by-one '..' error when computing bios SearchPath
-
-A '..' is missing in returned path when 'path' and 'relative_to' have no common
-components. C++17's std::filesystem::relative() method returns a correct value.
-
-As an alternative to that simple patch, we may want to just replace
-Path::MakeRelative() calls by std::filesystem::relative() calls and remove
-Path::MakeRelative() method.
-
---- src/common/file_system.cpp.orig	2023-10-16 12:57:57 UTC
-+++ src/common/file_system.cpp
-@@ -12,6 +12,7 @@
- #include <cstdlib>
- #include <cstring>
- #include <limits>
-+#include <filesystem>
- 
- #ifdef __APPLE__
- #include <mach-o/dyld.h>
-@@ -250,49 +251,7 @@ std::string Path::MakeRelative(const std::string_view&
- 
- std::string Path::MakeRelative(const std::string_view& path, const std::string_view& relative_to)
- {
--  // simple algorithm, we just work on the components. could probably be better, but it'll do for now.
--  std::vector<std::string_view> path_components(SplitNativePath(path));
--  std::vector<std::string_view> relative_components(SplitNativePath(relative_to));
--  std::vector<std::string_view> new_components;
--
--  // both must be absolute paths
--  if (Path::IsAbsolute(path) && Path::IsAbsolute(relative_to))
--  {
--    // find the number of same components
--    size_t num_same = 0;
--    for (size_t i = 0; i < path_components.size() && i < relative_components.size(); i++)
--    {
--      if (path_components[i] == relative_components[i])
--        num_same++;
--      else
--        break;
--    }
--
--    // we need at least one same component
--    if (num_same > 0)
--    {
--      // from the relative_to directory, back up to the start of the common components
--      const size_t num_ups = relative_components.size() - num_same;
--      for (size_t i = 0; i < num_ups; i++)
--        new_components.emplace_back("..");
--
--      // and add the remainder of the path components
--      for (size_t i = num_same; i < path_components.size(); i++)
--        new_components.push_back(std::move(path_components[i]));
--    }
--    else
--    {
--      // no similarity
--      new_components = std::move(path_components);
--    }
--  }
--  else
--  {
--    // not absolute
--    new_components = std::move(path_components);
--  }
--
--  return JoinNativePath(new_components);
-+  return std::filesystem::relative(path, relative_to);
- }
- 
- std::string_view Path::GetExtension(const std::string_view& path)