#include "types.h" #include "stdclass.h" #include "oslib/directory.h" #include "oslib/oslib.h" #include "serialize.h" #include "oslib/storage.h" #include #include #include #include #include #ifdef _WIN32 #include #elif defined(__ANDROID__) #include #endif static std::string user_config_dir; static std::string user_data_dir; static std::vector system_config_dirs; static std::vector system_data_dirs; const std::string defaultWs(" \0", 2); bool file_exists(const std::string& filename) { return (flycast::access(filename.c_str(), R_OK) == 0); } static void appendPathSep(std::string& str) { #ifdef _WIN32 if (str.empty() || (str.back() != '/' && str.back() != '\\')) str += '\\'; #else if (str.empty() || str.back() != '/') str += '/'; #endif } void set_user_config_dir(const std::string& dir) { user_config_dir = dir; appendPathSep(user_config_dir); } void set_user_data_dir(const std::string& dir) { user_data_dir = dir; appendPathSep(user_data_dir); } void add_system_config_dir(const std::string& dir) { std::string s(dir); appendPathSep(s); system_config_dirs.push_back(s); } void add_system_data_dir(const std::string& dir) { std::string s(dir); appendPathSep(s); system_data_dirs.push_back(s); } std::string get_writable_config_path(const std::string& filename) { /* Only stuff in the user_config_dir is supposed to be writable, * so we always return that. */ return user_config_dir + filename; } std::string get_readonly_config_path(const std::string& filename) { std::string user_filepath = get_writable_config_path(filename); if (file_exists(user_filepath)) return user_filepath; for (const auto& config_dir : system_config_dirs) { std::string filepath = config_dir + filename; if (file_exists(filepath)) return filepath; } // Not found, so we return the user variant return user_filepath; } std::string get_writable_data_path(const std::string& filename) { /* Only stuff in the user_data_dir is supposed to be writable, * so we always return that. */ return user_data_dir + filename; } std::string get_readonly_data_path(const std::string& filename) { std::string user_filepath = get_writable_data_path(filename); if (file_exists(user_filepath)) return user_filepath; for (const auto& data_dir : system_data_dirs) { std::string filepath = data_dir + filename; if (file_exists(filepath)) return filepath; } // Try the game directory try { std::string parent = hostfs::storage().getParentPath(settings.content.path); std::string filepath = hostfs::storage().getSubPath(parent, filename); if (hostfs::storage().exists(filepath)) return filepath; } catch (const FlycastException&) { } // Not found, so we return the user variant return user_filepath; } size_t get_last_slash_pos(const std::string& path) { size_t lastindex = path.find_last_of('/'); #ifdef _WIN32 size_t lastindex2 = path.find_last_of('\\'); if (lastindex == std::string::npos) lastindex = lastindex2; else if (lastindex2 != std::string::npos) lastindex = std::max(lastindex, lastindex2); #endif return lastindex; } std::string get_game_save_prefix() { return get_writable_data_path(settings.content.fileName); } bool make_directory(const std::string& path) { return flycast::mkdir(path.c_str(), 0755) == 0; } void cThread::Start() { verify(!thread.joinable()); thread = std::thread([this]() { ThreadName _(name); entry(param); }); } void cThread::WaitToEnd() { if (thread.joinable() && thread.get_id() != std::this_thread::get_id()) thread.join(); } cResetEvent::cResetEvent() : state(false) { } cResetEvent::~cResetEvent() = default; void cResetEvent::Set()//Signal { std::lock_guard lock(mutx); state = true; cond.notify_one(); } void cResetEvent::Reset() { std::lock_guard lock(mutx); state = false; } bool cResetEvent::Wait(u32 msec) { std::unique_lock lock(mutx); if (!state) cond.wait_for(lock, std::chrono::milliseconds(msec)); bool rc = state; state = false; return rc; } void cResetEvent::Wait() { std::unique_lock lock(mutx); if (!state) { cond.wait(lock); } state = false; } #if defined(__ANDROID__) && (HOST_CPU == CPU_ARM64 || HOST_CPU == CPU_X64) const unsigned long PAGE_SIZE = (unsigned long)sysconf(_SC_PAGESIZE); #endif void RamRegion::serialize(Serializer &ser) const { ser.serialize(data, size); } void RamRegion::deserialize(Deserializer &deser) { deser.deserialize(data, size); } void RamRegion::alloc(size_t size) { this->size = size; data = (u8 *)allocAligned(PAGE_SIZE, size); ownsMemory = true; } void RamRegion::free() { this->size = 0; if (ownsMemory) freeAligned(data); data = nullptr; } u64 getTimeMs() { using the_clock = std::chrono::steady_clock; std::chrono::time_point now = the_clock::now(); static std::chrono::time_point start = now; return std::chrono::duration_cast(now - start).count(); } #ifdef _WIN32 static struct tm *localtime_r(const time_t *_clock, struct tm *_result) { return localtime_s(_result, _clock) ? nullptr : _result; } #endif std::string timeToISO8601(time_t time) { tm t; if (localtime_r(&time, &t) == nullptr) return {}; std::string s(32, '\0'); s.resize(snprintf(s.data(), 32, "%04d/%02d/%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)); return s; }