32 std::filesystem::path executable;
34 std::vector<wchar_t> buffer(1024);
35 while (buffer.size() <= 32768) {
37 GetModuleFileNameW(
nullptr, buffer.data(), buffer.size());
39 return absl::InternalError(
"Cannot resolve the running executable");
41 if (length < buffer.size()) {
42 executable = std::wstring(buffer.data(), length);
45 buffer.resize(buffer.size() * 2);
47#elif defined(__APPLE__)
48 uint32_t size = PATH_MAX;
49 std::vector<char> buffer(size);
50 if (_NSGetExecutablePath(buffer.data(), &size) != 0) {
52 if (_NSGetExecutablePath(buffer.data(), &size) != 0) {
53 return absl::InternalError(
"Cannot resolve the running executable");
56 executable = buffer.data();
57#elif defined(__linux__)
58 std::vector<char> buffer(1024);
59 while (buffer.size() <= 1024 * 1024) {
61 readlink(
"/proc/self/exe", buffer.data(), buffer.size());
63 return absl::InternalError(
"Cannot resolve the running executable");
65 if (
static_cast<size_t>(length) < buffer.size()) {
66 executable = std::string(buffer.data(), length);
69 buffer.resize(buffer.size() * 2);
72 return absl::UnimplementedError(
"Native executable identity is unavailable");
74 std::error_code error;
75 const auto canonical = std::filesystem::canonical(executable, error);
76 if (executable.empty() || error) {
77 return absl::FailedPreconditionError(
78 "Cannot canonicalize the running executable path");
87 const char* userprofile = std::getenv(
"USERPROFILE");
88 if (userprofile && *userprofile) {
89 return std::filesystem::path(userprofile);
93 const char* homedrive = std::getenv(
"HOMEDRIVE");
94 const char* homepath = std::getenv(
"HOMEPATH");
95 if (homedrive && homepath) {
96 return std::filesystem::path(std::string(homedrive) +
97 std::string(homepath));
102 auto temp = std::filesystem::temp_directory_path(ec);
106 return std::filesystem::path(
".");
107#elif defined(__EMSCRIPTEN__)
109 return std::filesystem::path(
"/home/web_user");
112 const char* home = std::getenv(
"HOME");
114 return std::filesystem::path(home);
118 struct passwd* pw = getpwuid(getuid());
119 if (pw && pw->pw_dir) {
120 return std::filesystem::path(pw->pw_dir);
125 auto cwd = std::filesystem::current_path(ec);
129 return std::filesystem::path(
".");
133 return std::filesystem::path(
".");
138 if (
const char* override_dir = std::getenv(
"YAZE_APP_DATA_DIR")) {
140 std::filesystem::path app_data(override_dir);
149#if defined(YAZE_IOS) || defined(YAZE_APPLE_MOBILE)
151 if (home.empty() || home ==
".") {
153 auto temp = std::filesystem::temp_directory_path(ec);
159 std::filesystem::path app_data =
160 home /
"Library" /
"Application Support" /
".yaze";
163 app_data = home /
"Documents" /
".yaze";
170#elif defined(__EMSCRIPTEN__)
182 std::filesystem::path app_data(
"/.yaze");
186 std::filesystem::path preferred;
187 if (!home.empty() && home !=
".") {
188 preferred = home /
".yaze";
191 std::vector<std::filesystem::path> legacy_paths;
192 auto add_legacy_path = [&](
const std::filesystem::path& path) {
196 if (std::find(legacy_paths.begin(), legacy_paths.end(), path) ==
197 legacy_paths.end()) {
198 legacy_paths.push_back(path);
203 wchar_t path[MAX_PATH];
204 if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path))) {
205 add_legacy_path(std::filesystem::path(path) /
"yaze");
207 if (
const char* appdata = std::getenv(
"APPDATA")) {
209 add_legacy_path(std::filesystem::path(appdata) /
"yaze");
212#elif defined(__APPLE__)
213 if (!home.empty() && home !=
".") {
214 add_legacy_path(home /
"Library" /
"Application Support" /
"yaze");
215 add_legacy_path(home /
"Library" /
"Application Support" /
"Yaze");
218 if (!home.empty() && home !=
".") {
219 add_legacy_path(home /
".config" /
"yaze");
223 auto try_migrate = [&](
const std::filesystem::path& legacy) -> absl::Status {
224 if (legacy.empty() || preferred.empty()) {
225 return absl::OkStatus();
228 return absl::OkStatus();
232 std::filesystem::rename(legacy, preferred, ec);
234 return absl::OkStatus();
237 std::filesystem::create_directories(preferred, ec);
239 return absl::InternalError(
240 absl::StrCat(
"Failed to create .yaze directory: ", ec.message()));
243 std::filesystem::copy(legacy, preferred,
244 std::filesystem::copy_options::recursive |
245 std::filesystem::copy_options::skip_existing,
248 return absl::InternalError(
249 absl::StrCat(
"Failed to migrate legacy data: ", ec.message()));
255 std::filesystem::remove_all(legacy, ec);
258 return absl::OkStatus();
261 if (!preferred.empty()) {
263 for (
const auto& legacy : legacy_paths) {
265 (void)try_migrate(legacy);
277 for (
const auto& legacy : legacy_paths) {
284 if (preferred.empty()) {
285 std::filesystem::path fallback = std::filesystem::current_path() /
".yaze";
293 return absl::InternalError(
"Failed to resolve app data directory");
313 if (
const char* override_dir = std::getenv(
"YAZE_USER_DOCUMENTS_DIR")) {
315 std::filesystem::path docs_dir(override_dir);
324#if defined(YAZE_IOS) || defined(YAZE_APPLE_MOBILE)
326 std::filesystem::path docs_dir = home /
"Documents" /
"Yaze";
329 docs_dir = home /
"Yaze";
337 wchar_t path[MAX_PATH];
338 if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, path))) {
339 std::filesystem::path docs_dir = std::filesystem::path(path) /
"Yaze";
348 std::filesystem::path docs_dir = home /
"Documents" /
"Yaze";
354#elif defined(__EMSCRIPTEN__)
356 std::filesystem::path docs_dir(
"/.yaze/projects");
361 std::filesystem::path docs_dir = home /
"Documents" /
"Yaze";
365 docs_dir = home /
"Yaze";
469 const std::string& relative_path) {
470 std::vector<std::filesystem::path> search_paths;
474#ifdef YAZE_ASSETS_PATH
476 search_paths.push_back(std::filesystem::path(YAZE_ASSETS_PATH) /
484 static std::filesystem::path cached_cwd;
485 static bool cwd_cached =
false;
489 cached_cwd = std::filesystem::current_path(ec);
493 if (!cached_cwd.empty()) {
495 search_paths.push_back(cached_cwd /
"assets" / relative_path);
502 static std::filesystem::path cached_exe_dir;
503 static bool exe_dir_cached =
false;
505 if (!exe_dir_cached) {
508 char exe_path[PATH_MAX];
509 uint32_t size =
sizeof(exe_path);
510 if (_NSGetExecutablePath(exe_path, &size) == 0) {
511 cached_exe_dir = std::filesystem::path(exe_path).parent_path();
513#elif defined(__linux__)
514 char exe_path[PATH_MAX];
516 readlink(
"/proc/self/exe", exe_path,
sizeof(exe_path) - 1);
518 exe_path[len] =
'\0';
519 cached_exe_dir = std::filesystem::path(exe_path).parent_path();
522 wchar_t exe_path[MAX_PATH];
523 if (GetModuleFileNameW(NULL, exe_path, MAX_PATH) != 0) {
524 cached_exe_dir = std::filesystem::path(exe_path).parent_path();
530 exe_dir_cached =
true;
533 if (!cached_exe_dir.empty()) {
535 search_paths.push_back(cached_exe_dir /
"assets" / relative_path);
537 search_paths.push_back(cached_exe_dir.parent_path() /
"assets" /
539#if defined(__linux__)
542 search_paths.push_back(cached_exe_dir.parent_path() /
"share" /
"yaze" /
543 "assets" / relative_path);
549 auto contents_dir = cached_exe_dir.parent_path();
550 auto bundle_dir = contents_dir.parent_path();
551 auto bundle_parent = bundle_dir.parent_path();
552 search_paths.push_back(contents_dir /
"Resources" /
"assets" /
554 search_paths.push_back(bundle_parent /
"assets" / relative_path);
562 if (!cached_cwd.empty()) {
564 auto parent = cached_cwd.parent_path();
565 if (!parent.empty() && parent != cached_cwd) {
566 search_paths.push_back(parent /
"assets" / relative_path);
567 auto grandparent = parent.parent_path();
568 if (!grandparent.empty() && grandparent != parent) {
569 search_paths.push_back(grandparent /
"assets" / relative_path);
578 static std::filesystem::path cached_home;
579 static bool home_cached =
false;
590 if (!cached_home.empty() && cached_home !=
".") {
592 search_paths.push_back(cached_home /
".yaze" /
"assets" /
603 search_paths.push_back(
604 std::filesystem::path(
"/Applications/yaze.app/Contents/Resources") /
605 "assets" / relative_path);
606 if (!cached_home.empty() && cached_home !=
".") {
607 search_paths.push_back(cached_home /
"Applications" /
"yaze.app" /
608 "Contents" /
"Resources" /
"assets" /
619 search_paths.push_back(
620 std::filesystem::path(
"/usr/local/share/yaze/assets") /
622 search_paths.push_back(std::filesystem::path(
"/usr/share/yaze/assets") /
631 const size_t max_paths_to_check = 20;
634 for (
const auto& candidate : search_paths) {
635 if (++checked > max_paths_to_check) {
641 std::error_code exists_ec;
642 if (std::filesystem::exists(candidate, exists_ec) && !exists_ec) {
644 auto status = std::filesystem::status(candidate, exists_ec);
646 status.type() != std::filesystem::file_type::not_found) {
657 return absl::NotFoundError(
658 absl::StrCat(
"Asset not found: ", relative_path));
660 }
catch (
const std::exception& e) {
661 return absl::InternalError(
662 absl::StrCat(
"Exception while searching for asset: ", e.what()));
664 return absl::InternalError(
"Unknown exception while searching for asset");