From cd6d59b00e36df851c00e0b2f37651615c65617f Mon Sep 17 00:00:00 2001
From: Andrew Udvare <audvare@gmail.com>
Date: Wed, 11 Feb 2026 12:09:11 -0500
Subject: [PATCH 02/23] Write xenia.log to XDG state dir (~/.local/state or
 XDG_STATE_HOME)

Add GetStateFolder() and use it for the default log path on Linux:
- Use XDG_STATE_HOME if set, else ~/.local/state
- Create parent directory if needed
- Other platforms unchanged (executable folder)

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit e53d38ee4a546f3bf4f2a7ea5564c3033df51f0e)
---
 src/xenia/base/filesystem.h        |  3 +++
 src/xenia/base/filesystem_posix.cc | 17 +++++++++++++++++
 src/xenia/base/filesystem_win.cc   |  4 ++++
 src/xenia/base/logging.cc          |  9 +++++++--
 4 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/src/xenia/base/filesystem.h b/src/xenia/base/filesystem.h
index e3e206b3e..42f416777 100644
--- a/src/xenia/base/filesystem.h
+++ b/src/xenia/base/filesystem.h
@@ -40,6 +40,9 @@ std::filesystem::path GetExecutableFolder();
 // Get user folder.
 std::filesystem::path GetUserFolder();
 
+// Get user state folder (XDG_STATE_HOME or ~/.local/state on Linux).
+std::filesystem::path GetStateFolder();
+
 // Creates the parent folder of the specified path if needed.
 // This can be used to ensure the destination path for a new file exists before
 // attempting to create it.
diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc
index 0a759f1f4..81e7fc592 100644
--- a/src/xenia/base/filesystem_posix.cc
+++ b/src/xenia/base/filesystem_posix.cc
@@ -96,6 +96,23 @@ std::filesystem::path GetUserFolder() {
   return std::filesystem::path(home) / ".local" / "share";
 }
 
+std::filesystem::path GetStateFolder() {
+  char* state_home = std::getenv("XDG_STATE_HOME");
+  if (state_home && state_home[0]) {
+    return std::string(state_home);
+  }
+  char* home = std::getenv("HOME");
+  if (!home) {
+    struct passwd pw1;
+    struct passwd* pw;
+    char buf[4096];
+    getpwuid_r(getuid(), &pw1, buf, sizeof(buf), &pw);
+    assert(&pw1 == pw);
+    home = pw->pw_dir;
+  }
+  return std::filesystem::path(home) / ".local" / "state";
+}
+
 FILE* OpenFile(const std::filesystem::path& path, const std::string_view mode) {
   return fopen(path.c_str(), std::string(mode).c_str());
 }
diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc
index 7a50d8b12..415d4f646 100644
--- a/src/xenia/base/filesystem_win.cc
+++ b/src/xenia/base/filesystem_win.cc
@@ -60,6 +60,10 @@ std::filesystem::path GetUserFolder() {
   return result;
 }
 
+std::filesystem::path GetStateFolder() {
+  return GetUserFolder();
+}
+
 bool CreateEmptyFile(const std::filesystem::path& path) {
   auto handle = CreateFileW(path.c_str(), 0, 0, nullptr, CREATE_ALWAYS,
                             FILE_ATTRIBUTE_NORMAL, nullptr);
diff --git a/src/xenia/base/logging.cc b/src/xenia/base/logging.cc
index d2dc834d4..e40b29319 100644
--- a/src/xenia/base/logging.cc
+++ b/src/xenia/base/logging.cc
@@ -445,9 +445,14 @@ void InitializeLogging(const std::string_view app_name) {
 #else
   FILE* log_file = nullptr;
   if (cvars::log_file.empty()) {
-    // Default to app name.
     auto file_name = fmt::format("{}.log", app_name);
-    auto file_path = xe::filesystem::GetExecutableFolder() / file_name;
+    std::filesystem::path file_path;
+#if XE_PLATFORM_LINUX && !XE_PLATFORM_ANDROID
+    file_path = xe::filesystem::GetStateFolder() / file_name;
+    xe::filesystem::CreateParentFolder(file_path);
+#else
+    file_path = xe::filesystem::GetExecutableFolder() / file_name;
+#endif
     log_file = xe::filesystem::OpenFile(file_path, "wt");
   } else {
     xe::filesystem::CreateParentFolder(cvars::log_file);
-- 
2.54.0

