35 const char* userprofile = std::getenv(
"USERPROFILE");
36 if (userprofile && *userprofile) {
37 return std::filesystem::path(userprofile);
41 const char* homedrive = std::getenv(
"HOMEDRIVE");
42 const char* homepath = std::getenv(
"HOMEPATH");
43 if (homedrive && homepath) {
44 return std::filesystem::path(std::string(homedrive) +
45 std::string(homepath));
50 auto temp = std::filesystem::temp_directory_path(ec);
54 return std::filesystem::path(
".");
55#elif defined(__EMSCRIPTEN__)
57 return std::filesystem::path(
"/home/web_user");
60 const char* home = std::getenv(
"HOME");
62 return std::filesystem::path(home);
66 struct passwd* pw = getpwuid(getuid());
67 if (pw && pw->pw_dir) {
68 return std::filesystem::path(pw->pw_dir);
73 auto cwd = std::filesystem::current_path(ec);
77 return std::filesystem::path(
".");
81 return std::filesystem::path(
".");
86 if (
const char* override_dir = std::getenv(
"YAZE_APP_DATA_DIR")) {
88 std::filesystem::path app_data(override_dir);
97#if defined(YAZE_IOS) || defined(YAZE_APPLE_MOBILE)
99 if (home.empty() || home ==
".") {
101 auto temp = std::filesystem::temp_directory_path(ec);
107 std::filesystem::path app_data =
108 home /
"Library" /
"Application Support" /
".yaze";
111 app_data = home /
"Documents" /
".yaze";
118#elif defined(__EMSCRIPTEN__)
130 std::filesystem::path app_data(
"/.yaze");
134 std::filesystem::path preferred;
135 if (!home.empty() && home !=
".") {
136 preferred = home /
".yaze";
139 std::vector<std::filesystem::path> legacy_paths;
140 auto add_legacy_path = [&](
const std::filesystem::path& path) {
144 if (std::find(legacy_paths.begin(), legacy_paths.end(), path) ==
145 legacy_paths.end()) {
146 legacy_paths.push_back(path);
151 wchar_t path[MAX_PATH];
152 if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path))) {
153 add_legacy_path(std::filesystem::path(path) /
"yaze");
155 if (
const char* appdata = std::getenv(
"APPDATA")) {
157 add_legacy_path(std::filesystem::path(appdata) /
"yaze");
160#elif defined(__APPLE__)
161 if (!home.empty() && home !=
".") {
162 add_legacy_path(home /
"Library" /
"Application Support" /
"yaze");
163 add_legacy_path(home /
"Library" /
"Application Support" /
"Yaze");
166 if (!home.empty() && home !=
".") {
167 add_legacy_path(home /
".config" /
"yaze");
171 auto try_migrate = [&](
const std::filesystem::path& legacy) -> absl::Status {
172 if (legacy.empty() || preferred.empty()) {
173 return absl::OkStatus();
176 return absl::OkStatus();
180 std::filesystem::rename(legacy, preferred, ec);
182 return absl::OkStatus();
185 std::filesystem::create_directories(preferred, ec);
187 return absl::InternalError(
188 absl::StrCat(
"Failed to create .yaze directory: ", ec.message()));
191 std::filesystem::copy(legacy, preferred,
192 std::filesystem::copy_options::recursive |
193 std::filesystem::copy_options::skip_existing,
196 return absl::InternalError(
197 absl::StrCat(
"Failed to migrate legacy data: ", ec.message()));
203 std::filesystem::remove_all(legacy, ec);
206 return absl::OkStatus();
209 if (!preferred.empty()) {
211 for (
const auto& legacy : legacy_paths) {
213 (void)try_migrate(legacy);
225 for (
const auto& legacy : legacy_paths) {
232 if (preferred.empty()) {
233 std::filesystem::path fallback = std::filesystem::current_path() /
".yaze";
241 return absl::InternalError(
"Failed to resolve app data directory");
261 if (
const char* override_dir = std::getenv(
"YAZE_USER_DOCUMENTS_DIR")) {
263 std::filesystem::path docs_dir(override_dir);
272#if defined(YAZE_IOS) || defined(YAZE_APPLE_MOBILE)
274 std::filesystem::path docs_dir = home /
"Documents" /
"Yaze";
277 docs_dir = home /
"Yaze";
285 wchar_t path[MAX_PATH];
286 if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, path))) {
287 std::filesystem::path docs_dir = std::filesystem::path(path) /
"Yaze";
296 std::filesystem::path docs_dir = home /
"Documents" /
"Yaze";
302#elif defined(__EMSCRIPTEN__)
304 std::filesystem::path docs_dir(
"/.yaze/projects");
309 std::filesystem::path docs_dir = home /
"Documents" /
"Yaze";
313 docs_dir = home /
"Yaze";
417 const std::string& relative_path) {
418 std::vector<std::filesystem::path> search_paths;
422#ifdef YAZE_ASSETS_PATH
424 search_paths.push_back(std::filesystem::path(YAZE_ASSETS_PATH) /
432 static std::filesystem::path cached_cwd;
433 static bool cwd_cached =
false;
437 cached_cwd = std::filesystem::current_path(ec);
441 if (!cached_cwd.empty()) {
443 search_paths.push_back(cached_cwd /
"assets" / relative_path);
450 static std::filesystem::path cached_exe_dir;
451 static bool exe_dir_cached =
false;
453 if (!exe_dir_cached) {
456 char exe_path[PATH_MAX];
457 uint32_t size =
sizeof(exe_path);
458 if (_NSGetExecutablePath(exe_path, &size) == 0) {
459 cached_exe_dir = std::filesystem::path(exe_path).parent_path();
461#elif defined(__linux__)
462 char exe_path[PATH_MAX];
464 readlink(
"/proc/self/exe", exe_path,
sizeof(exe_path) - 1);
466 exe_path[len] =
'\0';
467 cached_exe_dir = std::filesystem::path(exe_path).parent_path();
470 wchar_t exe_path[MAX_PATH];
471 if (GetModuleFileNameW(NULL, exe_path, MAX_PATH) != 0) {
472 cached_exe_dir = std::filesystem::path(exe_path).parent_path();
478 exe_dir_cached =
true;
481 if (!cached_exe_dir.empty()) {
483 search_paths.push_back(cached_exe_dir /
"assets" / relative_path);
485 search_paths.push_back(cached_exe_dir.parent_path() /
"assets" /
487#if defined(__linux__)
490 search_paths.push_back(cached_exe_dir.parent_path() /
"share" /
"yaze" /
491 "assets" / relative_path);
497 auto contents_dir = cached_exe_dir.parent_path();
498 auto bundle_dir = contents_dir.parent_path();
499 auto bundle_parent = bundle_dir.parent_path();
500 search_paths.push_back(contents_dir /
"Resources" /
"assets" /
502 search_paths.push_back(bundle_parent /
"assets" / relative_path);
510 if (!cached_cwd.empty()) {
512 auto parent = cached_cwd.parent_path();
513 if (!parent.empty() && parent != cached_cwd) {
514 search_paths.push_back(parent /
"assets" / relative_path);
515 auto grandparent = parent.parent_path();
516 if (!grandparent.empty() && grandparent != parent) {
517 search_paths.push_back(grandparent /
"assets" / relative_path);
526 static std::filesystem::path cached_home;
527 static bool home_cached =
false;
538 if (!cached_home.empty() && cached_home !=
".") {
540 search_paths.push_back(cached_home /
".yaze" /
"assets" /
550 search_paths.push_back(
551 std::filesystem::path(
"/usr/local/share/yaze/assets") /
553 search_paths.push_back(std::filesystem::path(
"/usr/share/yaze/assets") /
562 const size_t max_paths_to_check = 20;
565 for (
const auto& candidate : search_paths) {
566 if (++checked > max_paths_to_check) {
572 std::error_code exists_ec;
573 if (std::filesystem::exists(candidate, exists_ec) && !exists_ec) {
575 auto status = std::filesystem::status(candidate, exists_ec);
577 status.type() != std::filesystem::file_type::not_found) {
588 return absl::NotFoundError(
589 absl::StrCat(
"Asset not found: ", relative_path));
591 }
catch (
const std::exception& e) {
592 return absl::InternalError(
593 absl::StrCat(
"Exception while searching for asset: ", e.what()));
595 return absl::InternalError(
"Unknown exception while searching for asset");