diff --git a/CMakeLists.txt b/CMakeLists.txt index b72e02f..8483da5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,8 @@ src/Filesystem/MemoryFilesystem.cpp src/Filesystem/SubdirFilesystem.cpp src/Filesystem/NullFilesystem.cpp src/Filesystem/MountableFilesystem.cpp +src/Filesystem/FSHelpers.cpp +src/Filesystem/TempFS.cpp src/Crypto/ClientTLSStream.cpp src/Crypto/MbedHelpers.cpp src/Args.cpp @@ -379,3 +381,4 @@ set(CPACK_PACKAGE_VERSION_MINOR "${TessesFramework_VERSION_MINOR}") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libmbedtls-dev (>= 2.28.8)") include(CPack) +add_subdirectory(pkgconfig) \ No newline at end of file diff --git a/Packaging/Linux/version.sh b/Packaging/Linux/version.sh index 293fb1c..05dcf88 100644 --- a/Packaging/Linux/version.sh +++ b/Packaging/Linux/version.sh @@ -1 +1,2 @@ -export DEB_VERSION=1.0.0 \ No newline at end of file +export BUILD=$(($BUILD_NO-163)) +export DEB_VERSION=1.0.0-$BUILD \ No newline at end of file diff --git a/include/TessesFramework/Date/Date.hpp b/include/TessesFramework/Date/Date.hpp index dda015a..ee1075d 100644 --- a/include/TessesFramework/Date/Date.hpp +++ b/include/TessesFramework/Date/Date.hpp @@ -16,23 +16,23 @@ namespace Tesses::Framework::Date int minute=0; int second=0; bool isLocal=false; - int64_t ToEpochNoConvert(); + int64_t ToEpochNoConvert() const; void FromEpochNoConvert(int64_t gmt); public: DateTime(); DateTime(int year, int month, int day, int hour, int minute, int seconds, bool isLocal=true); DateTime(int64_t epoch); - int Year(); - int Month(); - int Day(); - int Hour(); - int Minute(); - int Second(); - int DayOfWeek(); - bool IsLocal(); - int64_t ToEpoch(); - DateTime ToLocal(); - DateTime ToUTC(); + int Year() const; + int Month() const; + int Day() const; + int Hour() const; + int Minute() const; + int Second() const; + int DayOfWeek() const; + bool IsLocal() const; + int64_t ToEpoch() const; + DateTime ToLocal() const; + DateTime ToUTC() const; void SetToLocal(); void SetToUTC(); void SetYear(int y); @@ -52,15 +52,105 @@ namespace Tesses::Framework::Date static DateTime Now(); static DateTime NowUTC(); - std::string ToString(); - std::string ToString(std::string fmt); + std::string ToString() const; + std::string ToString(std::string fmt) const; - std::string ToHttpDate(); + std::string ToHttpDate() const; static bool TryParseHttpDate(std::string txt, DateTime& dt); + operator std::string() const + { + return ToString(); + } - + operator int64_t() const + { + return ToEpoch(); + } + }; + class TimeSpan { + int64_t totalSeconds; + public: + TimeSpan(); + TimeSpan(int64_t totalSeconds); + TimeSpan(int hours, int minutes, int seconds); + TimeSpan(int days,int hours, int minutes, int seconds); + + + void Set(int days, int hours, int minutes, int seconds); + void Set(int hours, int minutes, int seconds); + + void SetDays(int d); + void SetHours(int h); + void SetMinutes(int m); + void SetSeconds(int s); + + + int Days() const; + int Hours() const; + int Minutes() const; + int Seconds() const; + + + int64_t TotalSeconds() const; + int64_t TotalMinutes() const; + int64_t TotalHours() const; + + + void SetTotalSeconds(int64_t totalSeconds); + void SetTotalMinutes(int64_t totalMinutes); + void SetTotalHours(int64_t totalHours); + + void AddSeconds(int64_t seconds); + void AddMinutes(int64_t minutes); + void AddHours(int64_t hours); + void AddDays(int64_t days); + + std::string ToString(bool slim=true) const; + + static bool TryParse(std::string text, TimeSpan& span); + + operator int64_t() const + { + return TotalSeconds(); + } + + operator std::string() + { + return ToString(); + } + }; + DateTime operator+(const DateTime& dt, const TimeSpan& ts) + { + DateTime dt2(dt.ToEpoch() + ts.TotalSeconds()); + if(dt.IsLocal()) + dt2.SetToLocal(); + return dt2; + } + DateTime operator+(const TimeSpan& ts,const DateTime& dt) + { + DateTime dt2(dt.ToEpoch() + ts.TotalSeconds()); + if(dt.IsLocal()) + dt2.SetToLocal(); + return dt2; + } + TimeSpan operator+(const TimeSpan& ts, const TimeSpan& ts2) + { + return (int64_t)ts + (int64_t)ts2; + } + DateTime operator-(const DateTime& dt, const TimeSpan& ts) + { + DateTime dt2(dt.ToEpoch() - ts.TotalSeconds()); + if(dt.IsLocal()) + dt2.SetToLocal(); + return dt2; + } + TimeSpan operator-(const DateTime& dt, const DateTime& dt2) + { + return (int64_t)dt - (int64_t)dt2; + } + }; \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/FSHelpers.hpp b/include/TessesFramework/Filesystem/FSHelpers.hpp new file mode 100644 index 0000000..4b0e0f7 --- /dev/null +++ b/include/TessesFramework/Filesystem/FSHelpers.hpp @@ -0,0 +1,33 @@ +#pragma once +#include "TempFS.hpp" +#include "VFSFix.hpp" + +namespace Tesses::Framework::Filesystem::Helpers +{ + void ReadAllText(std::shared_ptr vfs, VFSPath path, std::string& text); + void ReadAllLines(std::shared_ptr vfs, VFSPath path, std::vector& lines); + void ReadAllBytes(std::shared_ptr vfs, VFSPath path, std::vector& array); + std::string ReadAllText(std::shared_ptr vfs, VFSPath path); + std::vector ReadAllLines(std::shared_ptr vfs, VFSPath path); + std::vector ReadAllBytes(std::shared_ptr vfs, VFSPath path); + void WriteAllText(std::shared_ptr vfs, VFSPath path, const std::string& text); + void WriteAllLines(std::shared_ptr vfs, VFSPath path, const std::vector& parts); + void WriteAllBytes(std::shared_ptr vfs, VFSPath path, const std::vector& bytes); + + void CopyStreamProgress(std::shared_ptr src,std::shared_ptr dest, std::function progress); + + void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite=true); + void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite=true); + void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite=true); + void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite=true); + + inline std::shared_ptr CreateTempFS(bool deleteOnDestroy=true) + { + return std::make_shared(deleteOnDestroy); + } + inline std::shared_ptr CreateTempFS(std::shared_ptr vfs,bool deleteOnDestroy=true) + { + return std::make_shared(vfs,deleteOnDestroy); + } + +} \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/TempFS.hpp b/include/TessesFramework/Filesystem/TempFS.hpp new file mode 100644 index 0000000..7246749 --- /dev/null +++ b/include/TessesFramework/Filesystem/TempFS.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "../Common.hpp" +#include "VFS.hpp" +#include "VFSFix.hpp" +namespace Tesses::Framework::Filesystem +{ + void UniqueString(std::string& text); + + class TempFS : public VFS + { + std::shared_ptr vfs; + bool deleteOnDestroy; + std::shared_ptr parent; + std::string tmp_str; + public: + std::string TempDirectoryName(); + TempFS(bool deleteOnDestroy=true); + TempFS(std::shared_ptr vfs,bool deleteOnDestroy=true); + + std::shared_ptr OpenFile(VFSPath path, std::string mode); + void CreateDirectory(VFSPath path); + void DeleteDirectory(VFSPath path); + bool SpecialFileExists(VFSPath path); + bool FileExists(VFSPath path); + bool RegularFileExists(VFSPath path); + bool SymlinkExists(VFSPath path); + bool CharacterDeviceExists(VFSPath path); + bool BlockDeviceExists(VFSPath path); + bool SocketFileExists(VFSPath path); + bool FIFOFileExists(VFSPath path); + bool DirectoryExists(VFSPath path); + void DeleteFile(VFSPath path); + void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + VFSPathEnumerator EnumeratePaths(VFSPath path); + void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveFile(VFSPath src, VFSPath dest); + void MoveDirectory(VFSPath src, VFSPath dest); + void DeleteDirectoryRecurse(VFSPath path); + VFSPath ReadLink(VFSPath path); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); + + void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess); + void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); + bool StatVFS(VFSPath path, StatVFSData& vfsData); + + void Chmod(VFSPath path, uint32_t mode); + void Close(); + ~TempFS(); + }; +} \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/VFS.hpp b/include/TessesFramework/Filesystem/VFS.hpp index 7d2cd7d..05b3eaf 100644 --- a/include/TessesFramework/Filesystem/VFS.hpp +++ b/include/TessesFramework/Filesystem/VFS.hpp @@ -29,6 +29,8 @@ namespace Tesses::Framework::Filesystem static std::vector SplitPath(std::string path); std::vector path; VFSPath(); + VFSPath(const char* path) : VFSPath((std::string)path) + {} VFSPath(std::vector path); VFSPath(std::string path); VFSPath(VFSPath p, std::string subent); @@ -36,22 +38,27 @@ namespace Tesses::Framework::Filesystem - VFSPath GetParent(); - VFSPath CollapseRelativeParents(); - std::string GetFileName(); - bool HasExtension(); - std::string GetExtension(); + VFSPath GetParent() const; + VFSPath CollapseRelativeParents() const; + std::string GetFileName() const; + bool HasExtension() const; + std::string GetExtension() const; void ChangeExtension(std::string ext); void RemoveExtension(); - std::string ToString(); + std::string ToString() const; + + operator std::string() const + { + return ToString(); + } static VFSPath GetAbsoluteCurrentDirectory(); static void SetAbsoluteCurrentDirectory(VFSPath path); - VFSPath MakeAbsolute(); + VFSPath MakeAbsolute() const; - VFSPath MakeAbsolute(VFSPath curDir); - VFSPath MakeRelative(); - VFSPath MakeRelative(VFSPath toMakeRelativeTo); + VFSPath MakeAbsolute(VFSPath curDir) const; + VFSPath MakeRelative() const; + VFSPath MakeRelative(VFSPath toMakeRelativeTo) const; }; VFSPath operator/(VFSPath p, VFSPath p2); VFSPath operator/(VFSPath p, std::string p2); diff --git a/include/TessesFramework/Platform/Environment.hpp b/include/TessesFramework/Platform/Environment.hpp index 0647faf..6000c96 100644 --- a/include/TessesFramework/Platform/Environment.hpp +++ b/include/TessesFramework/Platform/Environment.hpp @@ -9,6 +9,7 @@ namespace Tesses::Framework::Platform::Environment namespace SpecialFolders { + Tesses::Framework::Filesystem::VFSPath GetTemp(); Tesses::Framework::Filesystem::VFSPath GetHomeFolder(); Tesses::Framework::Filesystem::VFSPath GetDownloads(); Tesses::Framework::Filesystem::VFSPath GetMusic(); diff --git a/include/TessesFramework/TessesFramework.hpp b/include/TessesFramework/TessesFramework.hpp index 7daa587..f522f9b 100644 --- a/include/TessesFramework/TessesFramework.hpp +++ b/include/TessesFramework/TessesFramework.hpp @@ -29,6 +29,7 @@ #include "Filesystem/NullFilesystem.hpp" #include "Filesystem/MountableFilesystem.hpp" #include "Filesystem/MemoryFilesystem.hpp" +#include "Filesystem/FSHelpers.hpp" #include "Crypto/ClientTLSStream.hpp" #include "Crypto/MbedHelpers.hpp" #include "Lazy.hpp" diff --git a/include/TessesFramework/TextStreams/TextReader.hpp b/include/TessesFramework/TextStreams/TextReader.hpp index 96da392..a57c86b 100644 --- a/include/TessesFramework/TextStreams/TextReader.hpp +++ b/include/TessesFramework/TextStreams/TextReader.hpp @@ -11,6 +11,7 @@ namespace Tesses::Framework::TextStreams int32_t ReadChar(); std::string ReadLine(); bool ReadLine(std::string& str); + void ReadAllLines(std::vector& lines); std::string ReadToEnd(); void ReadToEnd(std::string& str); void CopyTo(TextWriter& writer, size_t bufSz=1024); diff --git a/pkgconfig/CMakeLists.txt b/pkgconfig/CMakeLists.txt new file mode 100644 index 0000000..bcb667f --- /dev/null +++ b/pkgconfig/CMakeLists.txt @@ -0,0 +1,21 @@ +set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") + +set(PKGCONFIG_PROJECT_DESCRIPTION "A cross platform wrapper library") +set(PKGCONFIG_PROJECT_HOMEPAGE_URL "https://onedev.site.tesses.net/tesses-framework") + +if(TESSESFRAMEWORK_ENABLE_MBED) +set(PKGCONFIG_DEPS "Requires: mbedtls") +else() +set(PKGCONFIG_DEPS "") +endif() + +configure_file(tessesframework_static.pc.in tessesframework_static.pc @ONLY) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/tessesframework_static.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +configure_file(tessesframework.pc.in tessesframework.pc @ONLY) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/tessesframework.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) \ No newline at end of file diff --git a/pkgconfig/tessesframework.pc.in b/pkgconfig/tessesframework.pc.in new file mode 100644 index 0000000..56850ae --- /dev/null +++ b/pkgconfig/tessesframework.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=@PKGCONFIG_INCLUDEDIR@ +libdir=@PKGCONFIG_LIBDIR@ + +Name: @PROJECT_NAME@ +Description: @PKGCONFIG_PROJECT_DESCRIPTION@ +URL: @PKGCONFIG_PROJECT_HOMEPAGE_URL@ +Version: @PROJECT_VERSION@ + +@PKGCONFIG_DEPS@ + +Cflags: -I"${includedir}" +Libs: -L"${libdir}" -ltessesframework_shared \ No newline at end of file diff --git a/pkgconfig/tessesframework_static.pc.in b/pkgconfig/tessesframework_static.pc.in new file mode 100644 index 0000000..5670f7a --- /dev/null +++ b/pkgconfig/tessesframework_static.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=@PKGCONFIG_INCLUDEDIR@ +libdir=@PKGCONFIG_LIBDIR@ + +Name: @PROJECT_NAME@ +Description: @PKGCONFIG_PROJECT_DESCRIPTION@ +URL: @PKGCONFIG_PROJECT_HOMEPAGE_URL@ +Version: @PROJECT_VERSION@ + +@PKGCONFIG_DEPS@ + +Cflags: -I"${includedir}" +Libs: -L"${libdir}" -ltessesframework \ No newline at end of file diff --git a/src/Date/Date.cpp b/src/Date/Date.cpp index b94f72a..e9943d2 100644 --- a/src/Date/Date.cpp +++ b/src/Date/Date.cpp @@ -105,35 +105,35 @@ namespace Tesses::Framework::Date this->second = seconds; this->isLocal = isLocal; } - int DateTime::Year() + int DateTime::Year() const { return this->year; } - int DateTime::Month() + int DateTime::Month() const { return this->month; } - int DateTime::Day() + int DateTime::Day() const { return this->day; } - int DateTime::Hour() + int DateTime::Hour() const { return this->hour; } - int DateTime::Minute() + int DateTime::Minute() const { return this->minute; } - int DateTime::Second() + int DateTime::Second() const { return this->second; } - bool DateTime::IsLocal() + bool DateTime::IsLocal() const { return this->isLocal; } - int DateTime::DayOfWeek() + int DateTime::DayOfWeek() const { date::year_month_day ymd(date::year(year),date::month((uint32_t)month),date::day((uint32_t)day)); date::sys_days d = ymd; @@ -214,7 +214,7 @@ namespace Tesses::Framework::Date this->isLocal=true; this->FromEpochNoConvert(local); } - DateTime DateTime::ToLocal() + DateTime DateTime::ToLocal() const { DateTime dt = *this; dt.SetToLocal(); @@ -294,13 +294,13 @@ namespace Tesses::Framework::Date this->FromEpochNoConvert(local); } - DateTime DateTime::ToUTC() + DateTime DateTime::ToUTC() const { DateTime dt = *this; dt.SetToUTC(); return dt; } - int64_t DateTime::ToEpoch() + int64_t DateTime::ToEpoch() const { if(this->isLocal) { @@ -309,7 +309,7 @@ namespace Tesses::Framework::Date } return this->ToEpochNoConvert(); } - int64_t DateTime::ToEpochNoConvert() + int64_t DateTime::ToEpochNoConvert() const { date::year y = (date::year)year; date::month m = (date::month)month; @@ -551,7 +551,7 @@ namespace Tesses::Framework::Date dt.second = second; return true; } - std::string DateTime::ToHttpDate() + std::string DateTime::ToHttpDate() const { auto utc=this->ToUTC(); std::string weekday=weekday_short[utc.DayOfWeek()]; @@ -567,12 +567,12 @@ namespace Tesses::Framework::Date return strm.str(); } - std::string DateTime::ToString() + std::string DateTime::ToString() const { return ToString("%Y/%m/%d %H:%M:%S"); } - std::string DateTime::ToString(std::string fmt) + std::string DateTime::ToString(std::string fmt) const { auto weekday = this->DayOfWeek(); @@ -732,4 +732,211 @@ namespace Tesses::Framework::Date } return text; } + TimeSpan::TimeSpan() + { + this->totalSeconds = 0; + } + TimeSpan::TimeSpan(int64_t totalSeconds) + { + this->totalSeconds = totalSeconds; + + } + TimeSpan::TimeSpan(int hours, int minutes, int seconds) : TimeSpan(0,hours,minutes,seconds) + { + + } + TimeSpan::TimeSpan(int days,int hours, int minutes, int seconds) + { + this->totalSeconds = (int64_t)days * 86400; + this->totalSeconds += (int64_t)hours * 3600; + this->totalSeconds += (int64_t)minutes * 60; + this->totalSeconds += (int64_t)seconds; + } + void TimeSpan::Set(int days, int hours, int minutes, int seconds) + { + this->totalSeconds = (int64_t)days * 86400; + this->totalSeconds += (int64_t)hours * 3600; + this->totalSeconds += (int64_t)minutes * 60; + this->totalSeconds += (int64_t)seconds; + } + void TimeSpan::Set(int hours, int minutes, int seconds) + { + this->totalSeconds = (int64_t)hours * 3600; + this->totalSeconds += (int64_t)minutes * 60; + this->totalSeconds += (int64_t)seconds; + } + + void TimeSpan::SetDays(int d) + { + Set(d,this->Hours(),this->Minutes(),this->Seconds()); + } + void TimeSpan::SetHours(int h) + { + Set(this->Days(),h,this->Minutes(), this->Seconds()); + } + void TimeSpan::SetMinutes(int m) + { + Set(this->Days(),this->Hours(),m,this->Seconds()); + } + void TimeSpan::SetSeconds(int s) + { + Set(this->Days(),this->Hours(),this->Minutes(),s); + } + + int TimeSpan::Days() const + { + + return (int)(this->totalSeconds / 86400); + } + int TimeSpan::Hours() const + { + + return (int)((this->totalSeconds / 3600) % 24); + } + int TimeSpan::Minutes() const + { + return (int)((this->totalSeconds / 60) % 60); + } + int TimeSpan::Seconds() const + { + return (int)(this->totalSeconds % 60); + } + + + int64_t TimeSpan::TotalSeconds() const + { + return this->totalSeconds; + } + int64_t TimeSpan::TotalMinutes() const + { + return this->totalSeconds / 60; + } + int64_t TimeSpan::TotalHours() const + { + return this->totalSeconds / 3600; + } + + void TimeSpan::AddSeconds(int64_t seconds) + { + this->totalSeconds += seconds; + } + void TimeSpan::AddMinutes(int64_t minutes) + { + this->totalSeconds += minutes * 60; + } + void TimeSpan::AddHours(int64_t hours) + { + this->totalSeconds += hours * 3600; + } + void TimeSpan::AddDays(int64_t days) + { + this->totalSeconds += days * 86400; + } + + void TimeSpan::SetTotalSeconds(int64_t totalSeconds) + { + this->totalSeconds = totalSeconds; + } + void TimeSpan::SetTotalMinutes(int64_t totalMinutes) + { + this->totalSeconds = totalMinutes * 60; + } + void TimeSpan::SetTotalHours(int64_t totalHours) + { + this->totalSeconds = totalHours * 3600; + } + + std::string TimeSpan::ToString(bool slim) const + { + std::string str={}; + if(this->totalSeconds < 0) + str += "-"; + if(slim && this->totalSeconds > -36000 && this->totalSeconds < 36000) + { + //0:00 + //00:00 + //0:00:00 + if(this->totalSeconds <= -3600 || this->totalSeconds >= 3600) + { + //hours must force multi digit minutes + str += std::to_string(this->Hours()); + str += ":"; + str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()),2,' '); + } + else + { + str += std::to_string(this->Minutes()); + } + + str += ":"; + str += Http::HttpUtils::LeftPad(std::to_string(this->Seconds()),2,' '); + } + else + { + //00:00:00 + //0.00:00:00 + + if(this->totalSeconds <= -86400 || this->totalSeconds >= 86400) + { + str += std::to_string(this->Days()); + str += "."; + } + + str += Http::HttpUtils::LeftPad(std::to_string(this->Hours()),2,' '); + str += ":"; + str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()),2,' '); + str += ":"; + str += Http::HttpUtils::LeftPad(std::to_string(this->Seconds()),2,' '); + + } + return str; + } + + bool TimeSpan::TryParse(std::string text, TimeSpan& span) + { + if(text.empty()) return false; + bool negative = text[0] == '-'; + int64_t totalSeconds = 0; + + + try{ + + + std::string colonPart = text.substr(negative ? 1 : 0); + auto res = Http::HttpUtils::SplitString(colonPart,":"); + + if(res.size() < 2 || res.size() > 3) return false; + std::string hour = res[0]; + size_t index=hour.find('.'); + if(index != std::string::npos) + { + totalSeconds += std::stoll(hour.substr(0,index)) * 86400; + hour = hour.substr(index+1); + } + + + if(res.size() == 2) + { + //mm:ss + + + totalSeconds += std::stoll(hour) * 60; + totalSeconds += std::stoll(res[1]); + } + else if(res.size() == 3) + { + totalSeconds += std::stoll(hour) * 3600; + totalSeconds += std::stoll(res[1]) * 60; + totalSeconds += std::stoll(res[2]); + } + else return false; + + }catch(...) {return false;} + + if(negative) totalSeconds = -totalSeconds; + + span.SetTotalSeconds(totalSeconds); + return true; + + } } diff --git a/src/Filesystem/FSHelpers.cpp b/src/Filesystem/FSHelpers.cpp new file mode 100644 index 0000000..f237fb5 --- /dev/null +++ b/src/Filesystem/FSHelpers.cpp @@ -0,0 +1,184 @@ +#include "TessesFramework/Filesystem/FSHelpers.hpp" +#include "TessesFramework/TextStreams/StreamReader.hpp" +#include "TessesFramework/TextStreams/StreamWriter.hpp" + +namespace Tesses::Framework::Filesystem::Helpers +{ + void ReadAllText(std::shared_ptr vfs, VFSPath path, std::string& text) + { + auto file = vfs->OpenFile(path,"rb"); + if(file->CanRead()) + { + TextStreams::StreamReader reader(file); + reader.ReadToEnd(text); + } + } + void ReadAllLines(std::shared_ptr vfs, VFSPath path, std::vector& lines) + { + auto file = vfs->OpenFile(path,"rb"); + if(file->CanRead()) + { + TextStreams::StreamReader reader(file); + reader.ReadAllLines(lines); + } + } + void ReadAllBytes(std::shared_ptr vfs, VFSPath path, std::vector& array) + { + auto file = vfs->OpenFile(path,"rb"); + if(file->CanRead()) + { + if(file->CanSeek()) + { + size_t length = (size_t)file->GetLength(); + array.resize(length); + file->ReadBlock(array.data(), array.size()); + } + else + { + size_t totalSize = 0; + size_t read = 0; + do { + array.resize(totalSize+1024); + read = file->ReadBlock(array.data()+totalSize,1024); + totalSize += read; + } while(read != 0); + + array.resize(totalSize); + } + } + } + std::string ReadAllText(std::shared_ptr vfs, VFSPath path) + { + std::string text; + ReadAllText(vfs,path,text); + return text; + } + std::vector ReadAllLines(std::shared_ptr vfs, VFSPath path) + { + std::vector lines; + ReadAllLines(vfs,path,lines); + return lines; + } + std::vector ReadAllBytes(std::shared_ptr vfs, VFSPath path) + { + std::vector bytes; + ReadAllBytes(vfs,path,bytes); + return bytes; + } + void WriteAllText(std::shared_ptr vfs, VFSPath path, const std::string& text) + { + auto file = vfs->OpenFile(path,"wb"); + if(file->CanWrite()) + { + TextStreams::StreamWriter writer(file); + writer.Write(text); + } + } + void WriteAllLines(std::shared_ptr vfs, VFSPath path, const std::vector& parts) + { + auto file = vfs->OpenFile(path,"wb"); + if(file->CanWrite()) + { + TextStreams::StreamWriter writer(file); + for(auto& line : parts) + { + writer.WriteLine(line); + } + } + } + void WriteAllBytes(std::shared_ptr vfs, VFSPath path, const std::vector& bytes) + { + auto file = vfs->OpenFile(path,"wb"); + if(file->CanWrite()) + { + file->WriteBlock(bytes.data(),bytes.size()); + } + } + + void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite) + { + if(!overwrite && vfsDest->FileExists(pathDest)) return; + if(!vfsSrc->FileExists(pathSrc)) return; + auto src=vfsSrc->OpenFile(pathSrc,"wb"); + auto dest = vfsDest->OpenFile(pathDest,"wb"); + if(src->CanRead() && dest->CanWrite()) + src->CopyTo(dest); + } + + void CopyStreamProgress(std::shared_ptr src,std::shared_ptr dest, std::function progress) + { + int64_t length=0; + try { + length = src->GetLength(); + } catch(...) { + length=0; + } + if(length == 0) length = (int64_t)1<<62; // a big number so its always 0% if the stream does not have a length + + int64_t offset = 0; + size_t read = 0; + std::vector data(4096); + + + do { + read = src->ReadBlock(data.data(),data.size()); + dest->WriteBlock(data.data(),read); + offset += (int64_t)read; + if(read != 0) + progress(offset,length); + } while(read != 0); + + if(offset > 0) + progress(offset,offset); + } + void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite) + { + if(!overwrite && vfsDest->FileExists(pathDest)) return; + if(!vfsSrc->FileExists(pathSrc)) return; + auto src=vfsSrc->OpenFile(pathSrc,"wb"); + auto dest = vfsDest->OpenFile(pathDest,"wb"); + if(src->CanRead() && dest->CanWrite()) + CopyStreamProgress(src,dest,progress); + } + void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest,bool overwrite) + { + + + if(vfsSrc->DirectoryExists(pathSrc)) + { + vfsDest->CreateDirectory(pathDest); + for(auto& srcPath : vfsSrc->EnumeratePaths(pathSrc)) + { + if(vfsSrc->DirectoryExists(srcPath)) + { + CopyDirectory(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName()); + } + if(vfsSrc->FileExists(srcPath)) + { + CopyFile(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),overwrite); + } + } + } + } + void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite) + { + if(vfsSrc->DirectoryExists(pathSrc)) + { + vfsDest->CreateDirectory(pathDest); + for(auto& srcPath : vfsSrc->EnumeratePaths(pathSrc)) + { + if(vfsSrc->DirectoryExists(srcPath)) + { + CopyDirectory(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),progress,overwrite); + } + if(vfsSrc->FileExists(srcPath)) + { + CopyFile(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),[progress,srcPath](int64_t offset, int64_t length)->void { + progress(offset,length,srcPath); + },overwrite); + } + } + } + } + +} \ No newline at end of file diff --git a/src/Filesystem/TempFS.cpp b/src/Filesystem/TempFS.cpp new file mode 100644 index 0000000..6c99c24 --- /dev/null +++ b/src/Filesystem/TempFS.cpp @@ -0,0 +1,205 @@ +#include "TessesFramework/Threading/Mutex.hpp" +#include "TessesFramework/Filesystem/TempFS.hpp" +#include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/Filesystem/SubdirFilesystem.hpp" +#include "TessesFramework/Platform/Environment.hpp" +namespace Tesses::Framework::Filesystem { + Tesses::Framework::Threading::Mutex umtx; + int64_t uidx=0; + void UniqueString(std::string& text) + { + umtx.Lock(); + text += std::to_string((int64_t)time(NULL)); + text += "_"; + text += std::to_string(uidx); + + uidx++; + + umtx.Unlock(); + } + + TempFS::TempFS(bool deleteOnDestroy) : TempFS(std::make_shared(LocalFS, Platform::Environment::SpecialFolders::GetTemp()), deleteOnDestroy) + { + + } + TempFS::TempFS(std::shared_ptr vfs,bool deleteOnDestroy) + { + this->parent = vfs; + this->deleteOnDestroy=deleteOnDestroy; + this->tmp_str = "tf_tmp_"; + UniqueString(this->tmp_str); + VFSPath p; + p.relative = false; + p.path.push_back(this->tmp_str); + this->parent->CreateDirectory(p); + this->vfs = std::make_shared(this->parent,p); + } + + std::string TempFS::TempDirectoryName() + { + return this->tmp_str; + } + std::shared_ptr TempFS::OpenFile(VFSPath path, std::string mode) + { + if(this->vfs == nullptr) return nullptr; + return this->vfs->OpenFile(path,mode); + } + void TempFS::CreateDirectory(VFSPath path) + { + if(this->vfs == nullptr) return; + this->vfs->CreateDirectory(path); + } + void TempFS::DeleteDirectory(VFSPath path) + { + if(this->vfs == nullptr) return; + this->vfs->DeleteDirectory(path); + } + bool TempFS::SpecialFileExists(VFSPath path) + { + + if(this->vfs == nullptr) return false; + return this->vfs->SpecialFileExists(path); + } + bool TempFS::FileExists(VFSPath path) + { + + if(this->vfs == nullptr) return false; + return this->vfs->FileExists(path); + } + bool TempFS::RegularFileExists(VFSPath path) + { + + if(this->vfs == nullptr) return false; + return this->vfs->RegularFileExists(path); + } + bool TempFS::SymlinkExists(VFSPath path) + { + + if(this->vfs == nullptr) return false; + return this->vfs->SymlinkExists(path); + } + bool TempFS::CharacterDeviceExists(VFSPath path) + { + + if(this->vfs == nullptr) return false; + return this->vfs->CharacterDeviceExists(path); + } + bool TempFS::BlockDeviceExists(VFSPath path) + { + if(this->vfs == nullptr) return false; + return this->vfs->BlockDeviceExists(path); + } + bool TempFS::SocketFileExists(VFSPath path) + { + if(this->vfs == nullptr) return false; + return this->vfs->SocketFileExists(path); + } + bool TempFS::FIFOFileExists(VFSPath path) + { + if(this->vfs == nullptr) return false; + return this->vfs->FIFOFileExists(path); + } + bool TempFS::DirectoryExists(VFSPath path) + { + if(this->vfs == nullptr) return false; + return this->vfs->DirectoryExists(path); + } + void TempFS::DeleteFile(VFSPath path) + { + if(this->vfs == nullptr) return; + this->vfs->DeleteFile(path); + } + void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) + { + if(this->vfs == nullptr) return; + this->vfs->CreateSymlink(existingFile, symlinkFile); + } + VFSPathEnumerator TempFS::EnumeratePaths(VFSPath path) + { + + if(this->vfs == nullptr) return VFSPathEnumerator(); + + return this->vfs->EnumeratePaths(path); + } + void TempFS::CreateHardlink(VFSPath existingFile, VFSPath newName) + { + + if(this->vfs == nullptr) return; + this->vfs->CreateHardlink(existingFile,newName); + } + void TempFS::MoveFile(VFSPath src, VFSPath dest) + { + + if(this->vfs == nullptr) return; + this->vfs->MoveFile(src,dest); + } + void TempFS::MoveDirectory(VFSPath src, VFSPath dest) + { + if(this->vfs == nullptr) return; + this->vfs->MoveDirectory(src,dest); + } + void TempFS::DeleteDirectoryRecurse(VFSPath path) + { + + if(this->vfs == nullptr) return; + this->vfs->DeleteDirectoryRecurse(path); + } + VFSPath TempFS::ReadLink(VFSPath path) + { + + if(this->vfs == nullptr) return VFSPath(); + return this->vfs->ReadLink(path); + } + std::string TempFS::VFSPathToSystem(VFSPath path) + { + + if(this->vfs == nullptr) return ""; + return this->vfs->VFSPathToSystem(path); + } + VFSPath TempFS::SystemToVFSPath(std::string path) + { + + if(this->vfs == nullptr) return VFSPath(); + return this->vfs->SystemToVFSPath(path); + } + + void TempFS::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess) + { + + if(this->vfs == nullptr) return; + this->vfs->GetDate(path,lastWrite,lastAccess); + } + void TempFS::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) + { + + if(this->vfs == nullptr) return; + this->vfs->SetDate(path,lastWrite,lastAccess); + } + bool TempFS::StatVFS(VFSPath path, StatVFSData& vfsData) + { + + if(this->vfs == nullptr) return false; + return this->vfs->StatVFS(path, vfsData); + } + + void TempFS::Chmod(VFSPath path, uint32_t mode) + { + + if(this->vfs == nullptr) return; + this->vfs->Chmod(path,mode); + } + void TempFS::Close() + { + + VFSPath p; + p.relative = false; + p.path.push_back(this->tmp_str); + this->vfs = nullptr; + if(this->deleteOnDestroy && this->parent->DirectoryExists(p)) + this->parent->DeleteDirectoryRecurse(p); + } + TempFS::~TempFS() + { + Close(); + } +} \ No newline at end of file diff --git a/src/Filesystem/VFS.cpp b/src/Filesystem/VFS.cpp index 30fdbf9..64e8852 100644 --- a/src/Filesystem/VFS.cpp +++ b/src/Filesystem/VFS.cpp @@ -202,21 +202,21 @@ namespace Tesses::Framework::Filesystem std::filesystem::path mpath=res; std::filesystem::current_path(mpath); } - VFSPath VFSPath::MakeAbsolute() + VFSPath VFSPath::MakeAbsolute() const { return MakeAbsolute(GetAbsoluteCurrentDirectory()); } - VFSPath VFSPath::MakeAbsolute(VFSPath curDir) + VFSPath VFSPath::MakeAbsolute(VFSPath curDir) const { if (!this->relative) return *this; VFSPath p2 = curDir / *this; return p2.CollapseRelativeParents(); } - VFSPath VFSPath::MakeRelative() + VFSPath VFSPath::MakeRelative() const { return MakeRelative(GetAbsoluteCurrentDirectory()); } - VFSPath VFSPath::MakeRelative(VFSPath toMakeRelativeTo) + VFSPath VFSPath::MakeRelative(VFSPath toMakeRelativeTo) const { if(this->relative) return *this; @@ -250,7 +250,7 @@ namespace Tesses::Framework::Filesystem p2.relative = true; return p2; } - VFSPath VFSPath::CollapseRelativeParents() + VFSPath VFSPath::CollapseRelativeParents() const { std::vector parts; @@ -330,7 +330,7 @@ namespace Tesses::Framework::Filesystem this->path = p; } - bool VFSPath::HasExtension() + bool VFSPath::HasExtension() const { if(this->path.empty()) return false; auto& str = this->path.back(); @@ -338,7 +338,7 @@ namespace Tesses::Framework::Filesystem if(index == std::string::npos) return false; return true; } - std::string VFSPath::GetExtension() + std::string VFSPath::GetExtension() const { if(this->path.empty()) return {}; auto& str = this->path.back(); @@ -395,7 +395,7 @@ namespace Tesses::Framework::Filesystem { } - VFSPath VFSPath::GetParent() + VFSPath VFSPath::GetParent() const { std::vector paths; if(this->path.empty()) return VFSPath(); @@ -407,13 +407,13 @@ namespace Tesses::Framework::Filesystem return res; } - std::string VFSPath::GetFileName() + std::string VFSPath::GetFileName() const { if(this->path.empty()) return ""; return this->path.back(); } - std::string VFSPath::ToString() + std::string VFSPath::ToString() const { if(this->path.empty() && !this->relative) return "/"; if(!this->relative && this->path.size() == 1 && !this->path[0].empty() && this->path[0].back() == ':') return this->path[0] + "/"; diff --git a/src/Platform/Environment.cpp b/src/Platform/Environment.cpp index 1ebe148..9c803ca 100644 --- a/src/Platform/Environment.cpp +++ b/src/Platform/Environment.cpp @@ -25,13 +25,18 @@ namespace Tesses::Framework::Platform::Environment namespace SpecialFolders { - + VFSPath GetTemp() + { + return std::filesystem::temp_directory_path().string(); + } VFSPath GetHomeFolder() { #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) return sago::getHomeDir(); #elif defined(__EMSCRIPTEN__) return (std::string)"/home/web_user"; + #elif defined(__ANDROID__) + return (std::string)"/sdcard/TF_User"; #else return (std::string)"/TF_User"; #endif @@ -40,6 +45,8 @@ namespace Tesses::Framework::Platform::Environment { #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) return sago::getDownloadFolder(); + #elif defined(__ANDROID__) + return (std::string)"/sdcard/Download"; #else return GetHomeFolder() / "Downloads"; #endif @@ -48,6 +55,8 @@ namespace Tesses::Framework::Platform::Environment { #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) return sago::getMusicFolder(); + #elif defined(__ANDROID__) + return (std::string)"/sdcard/Music"; #else return GetHomeFolder() / "Music"; #endif @@ -56,6 +65,8 @@ namespace Tesses::Framework::Platform::Environment { #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) return sago::getPicturesFolder(); + #elif defined(__ANDROID__) + return (std::string)"/sdcard/Pictures"; #else return GetHomeFolder() / "Pictures"; #endif @@ -64,6 +75,8 @@ namespace Tesses::Framework::Platform::Environment { #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) return sago::getVideoFolder(); + #elif defined(__ANDROID__) + return (std::string)"/sdcard/Movies"; #else return GetHomeFolder() / "Videos"; #endif @@ -72,6 +85,8 @@ namespace Tesses::Framework::Platform::Environment { #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) return sago::getDocumentsFolder(); + #elif defined(__ANDROID__) + return (std::string)"/sdcard/Documents"; #else return GetHomeFolder() / "Documents"; #endif @@ -237,6 +252,9 @@ namespace Tesses::Framework::Platform::Environment } std::string GetPlatform() { + #if defined(__ANDROID__) + return "Android"; + #endif #if defined(__SWITCH__) return "Nintendo Switch"; #endif diff --git a/src/Platform/sago/platform_folders.h b/src/Platform/sago/platform_folders.h index a8aba2d..0fa386c 100644 --- a/src/Platform/sago/platform_folders.h +++ b/src/Platform/sago/platform_folders.h @@ -37,7 +37,7 @@ I modified it to return home directory and conditionally compile for systems tha #ifndef SAGO_PLATFORM_FOLDERS_H #define SAGO_PLATFORM_FOLDERS_H -#if defined(GEKKO) || defined(__SWITCH__) || defined(__EMSCRIPTEN__) || defined(__PS2__) +#if defined(GEKKO) || defined(__SWITCH__) || defined(__EMSCRIPTEN__) || defined(__PS2__) || defined(__ANDROID__) #define SAGO_DISABLE #endif diff --git a/src/TextStreams/TextReader.cpp b/src/TextStreams/TextReader.cpp index d9cf42e..2695511 100644 --- a/src/TextStreams/TextReader.cpp +++ b/src/TextStreams/TextReader.cpp @@ -33,6 +33,23 @@ namespace Tesses::Framework::TextStreams } while(r != -1); return ret; } + void TextReader::ReadAllLines(std::vector& lines) + { + int32_t r = -1; + std::string builder; + do { + r = ReadChar(); + if(r == -1) break; + if(r == '\r') continue; + if(r == '\n') { + lines.push_back(builder); + builder.clear(); + continue; + } + builder += (char)r; + + } while(r != -1); + } std::string TextReader::ReadToEnd() {