gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

filesystem.cpp (10300B)


      1 #include "filesystem.h"
      2 
      3 #include <array>
      4 #include <iostream>
      5 
      6 #ifndef _WIN32
      7 // filesystem is only available on macOS Catalina 10.15+
      8 // filesystem causes linker errors in gcc-8 if linked statically
      9 #define USE_DIRENT
     10 #include <cstdlib>
     11 #include <cstring>
     12 #include <pwd.h>
     13 #endif
     14 
     15 #ifdef USE_DIRENT
     16 #include <dirent.h>
     17 #include <unistd.h>
     18 #include <sys/stat.h>
     19 #else
     20 #include <filesystem>
     21 #endif
     22 
     23 #ifdef _WIN32
     24 #define NOMINMAX
     25 #define NOSERVICE
     26 #include <Windows.h>
     27 #include <shlobj_core.h>
     28 #else
     29 #include <dlfcn.h>
     30 #endif
     31 
     32 #ifdef _MSC_VER
     33 #include <cfloat>
     34 #elif defined(HAVE_SSE)
     35 #include <immintrin.h>
     36 #endif
     37 
     38 #ifdef __APPLE__
     39 #include <sys/types.h>
     40 #include <sys/sysctl.h>
     41 #endif
     42 
     43 namespace baseLib::filesystem
     44 {
     45 #ifdef _WIN32
     46 	constexpr char g_nativePathSeparator = '\\';
     47 #else
     48 	constexpr char g_nativePathSeparator = '/';
     49 #endif
     50 	constexpr char g_otherPathSeparator = g_nativePathSeparator == '\\' ? '/' : '\\';
     51 
     52     std::string getCurrentDirectory()
     53     {
     54 #ifdef USE_DIRENT
     55         char temp[1024];
     56         getcwd(temp, sizeof(temp));
     57         return temp;
     58 #else
     59 		return std::filesystem::current_path().string();
     60 #endif
     61     }
     62 
     63     bool createDirectory(const std::string& _dir)
     64     {
     65 #ifdef USE_DIRENT
     66 		constexpr auto dirAttribs = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
     67 		for(size_t i=0; i<_dir.size(); ++i)
     68 		{
     69 			if(_dir[i] == '/' || _dir[i] == '\\')
     70 			{
     71 				const auto d = _dir.substr(0,i);
     72 		        mkdir(d.c_str(), dirAttribs);
     73 			}
     74 		}
     75         return mkdir(_dir.c_str(), dirAttribs) == 0;
     76 #else
     77         return std::filesystem::create_directories(_dir);
     78 #endif
     79     }
     80 
     81     std::string validatePath(std::string _path)
     82     {
     83         if(_path.empty())
     84             return _path;
     85 
     86         for (char& ch : _path)
     87         {
     88 	        if(ch == g_otherPathSeparator)
     89 				ch = g_nativePathSeparator;
     90         }
     91 
     92 		if(_path.back() == g_nativePathSeparator)
     93             return _path;
     94 
     95         _path += g_nativePathSeparator;
     96         return _path;
     97     }
     98 
     99     bool getDirectoryEntries(std::vector<std::string>& _files, const std::string& _folder)
    100     {
    101 #ifdef USE_DIRENT
    102         DIR *dir;
    103         struct dirent *ent;
    104         if ((dir = opendir(_folder.c_str())))
    105         {
    106             while ((ent = readdir(dir)))
    107             {
    108 				std::string f = ent->d_name;
    109 
    110 				if(f == "." || f == "..")
    111 					continue;
    112 
    113                 std::string file = _folder;
    114 
    115             	if(file.back() != '/' && file.back() != '\\')
    116                     file += '/';
    117 
    118             	file += f;
    119 
    120                 _files.push_back(file);
    121             }
    122             closedir(dir);
    123         }
    124         else
    125         {
    126 //          LOG("Failed to open directory " << _folder << ", error " << errno);
    127 			std::cerr << "Failed to open directory " << _folder << ", error " << errno << '\n';
    128             return false;
    129         }
    130 #else
    131     	try
    132         {
    133             const auto u8Path = std::filesystem::u8path(_folder);
    134             for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(u8Path))
    135             {
    136                 const auto &file = entry.path();
    137 
    138                 try
    139                 {
    140 	                _files.push_back(file.u8string());
    141                 }
    142                 catch(std::exception& e)
    143                 {
    144                 	//LOG(e.what());
    145 	                std::cerr << e.what() << '\n';
    146                 }
    147             }
    148         }
    149         catch (std::exception& e)
    150         {
    151             //LOG(e.what());
    152 			std::cerr << e.what() << '\n';
    153             return false;
    154         }
    155 #endif
    156         return !_files.empty();
    157     }
    158 
    159 	bool findFiles(std::vector<std::string>& _files, const std::string& _rootPath, const std::string& _extension, const size_t _minSize, const size_t _maxSize)
    160     {
    161         std::vector<std::string> files;
    162 
    163         getDirectoryEntries(files, _rootPath);
    164 
    165         for (const auto& file : files)
    166         {
    167             if(!hasExtension(file, _extension))
    168                 continue;
    169 
    170             if (!_minSize && !_maxSize)
    171             {
    172                 _files.push_back(file);
    173                 continue;
    174             }
    175 
    176             const auto size = getFileSize(file);
    177 
    178             if (_minSize && size < _minSize)
    179 	            continue;
    180             if (_maxSize && size > _maxSize)
    181 	            continue;
    182 
    183             _files.push_back(file);
    184         }
    185         return !_files.empty();
    186     }
    187 
    188 	std::string findFile(const std::string& _rootPath, const std::string& _extension, const size_t _minSize, const size_t _maxSize)
    189     {
    190         std::vector<std::string> files;
    191         if(!findFiles(files, _rootPath, _extension, _minSize, _maxSize))
    192             return {};
    193         return files.front();
    194     }
    195 
    196 
    197     std::string lowercase(const std::string &_src)
    198     {
    199         std::string str(_src);
    200         for (char& i : str)
    201 	        i = static_cast<char>(tolower(i));
    202         return str;
    203     }
    204 
    205     std::string getExtension(const std::string &_name)
    206     {
    207         const auto pos = _name.find_last_of('.');
    208         if (pos != std::string::npos)
    209             return _name.substr(pos);
    210         return {};
    211     }
    212 
    213     std::string stripExtension(const std::string& _name)
    214     {
    215 		const auto pos = _name.find_last_of('.');
    216 		if (pos != std::string::npos)
    217 			return _name.substr(0, pos);
    218 		return _name;
    219     }
    220 
    221     std::string getFilenameWithoutPath(const std::string& _name)
    222     {
    223         const auto pos = _name.find_last_of("/\\");
    224         if (pos != std::string::npos)
    225             return _name.substr(pos + 1);
    226         return _name;
    227     }
    228 
    229     std::string getPath(const std::string& _filename)
    230     {
    231         const auto pos = _filename.find_last_of("/\\");
    232         if (pos != std::string::npos)
    233             return _filename.substr(0, pos);
    234         return _filename;
    235     }
    236 
    237     size_t getFileSize(const std::string& _file)
    238     {
    239         FILE* hFile = openFile(_file, "rb");
    240         if (!hFile)
    241             return 0;
    242 
    243         fseek(hFile, 0, SEEK_END);
    244         const auto size = static_cast<size_t>(ftell(hFile));
    245         fclose(hFile);
    246         return size;
    247     }
    248 
    249     bool isDirectory(const std::string& _path)
    250     {
    251 #ifdef USE_DIRENT
    252 		struct stat statbuf;
    253 		stat(_path.c_str(), &statbuf);
    254 		if (S_ISDIR(statbuf.st_mode))
    255             return true;
    256         return false;
    257 #else
    258         return std::filesystem::is_directory(_path);
    259 #endif
    260     }
    261     bool hasExtension(const std::string& _filename, const std::string& _extension)
    262     {
    263         if (_extension.empty())
    264             return true;
    265 
    266         return lowercase(getExtension(_filename)) == lowercase(_extension);
    267     }
    268 
    269     bool writeFile(const std::string& _filename, const std::vector<uint8_t>& _data)
    270     {
    271         return writeFile(_filename, _data.data(), _data.size());
    272     }
    273 
    274     bool writeFile(const std::string& _filename, const uint8_t* _data, size_t _size)
    275     {
    276         auto* hFile = openFile(_filename, "wb");
    277         if(!hFile)
    278             return false;
    279         const auto written = fwrite(&_data[0], 1, _size, hFile);
    280         fclose(hFile);
    281         return written == _size;
    282     }
    283 
    284     bool readFile(std::vector<uint8_t>& _data, const std::string& _filename)
    285     {
    286         auto* hFile = openFile(_filename, "rb");
    287         if(!hFile)
    288             return false;
    289 
    290     	fseek(hFile, 0, SEEK_END);
    291         const auto size = ftell(hFile);
    292         fseek(hFile, 0, SEEK_SET);
    293 
    294     	if(!size)
    295         {
    296 	        fclose(hFile);
    297             _data.clear();
    298             return true;
    299         }
    300 
    301     	if(_data.size() != static_cast<size_t>(size))
    302             _data.resize(size);
    303 
    304     	const auto read = fread(_data.data(), 1, _data.size(), hFile);
    305         fclose(hFile);
    306         return read == _data.size();
    307     }
    308 
    309     FILE* openFile(const std::string& _name, const char* _mode)
    310     {
    311 #ifdef _WIN32
    312         // convert filename
    313 		std::wstring nameW;
    314 		nameW.resize(_name.size());
    315 		const int newSize = MultiByteToWideChar(CP_UTF8, 0, _name.c_str(), static_cast<int>(_name.size()), const_cast<wchar_t *>(nameW.c_str()), static_cast<int>(_name.size()));
    316 		nameW.resize(newSize);
    317 
    318         // convert mode
    319         wchar_t mode[32]{0};
    320 		MultiByteToWideChar(CP_UTF8, 0, _mode, static_cast<int>(strlen(_mode)), mode, (int)std::size(mode));
    321 		return _wfopen(nameW.c_str(), mode);
    322 #else
    323 		return fopen(_name.c_str(), _mode);
    324 #endif
    325     }
    326 
    327     std::string getHomeDirectory()
    328     {
    329 #ifdef _WIN32
    330 		std::array<char, MAX_PATH<<1> data;
    331 		if (SHGetSpecialFolderPathA (nullptr, data.data(), CSIDL_PROFILE, FALSE))
    332 			return validatePath(data.data());
    333 
    334 	    const auto* home = getenv("USERPROFILE");
    335 		if (home)
    336 			return home;
    337 
    338 		const auto* drive = getenv("HOMEDRIVE");
    339 		const auto* path = getenv("HOMEPATH");
    340 
    341 		if (drive && path)
    342 			return std::string(drive) + std::string(path);
    343 
    344 		return "C:\\Users\\Default";			// meh, what can we do?
    345 #else
    346 		const char* home = getenv("HOME");
    347 		if (home && strlen(home) > 0)
    348 			return home;
    349         const auto* pw = getpwuid(getuid());
    350 		if(pw)
    351 			return std::string(pw->pw_dir);
    352 		return "/tmp";							// better ideas welcome
    353 #endif
    354     }
    355 
    356     std::string getSpecialFolderPath(const SpecialFolderType _type)
    357     {
    358 #ifdef _WIN32
    359 		std::array<char, MAX_PATH<<1> path;
    360 
    361 		int csidl;
    362 		switch (_type)
    363 		{
    364 		case SpecialFolderType::UserDocuments:
    365 			csidl = CSIDL_PERSONAL;
    366 			break;
    367 		case SpecialFolderType::PrivateAppData:
    368 			csidl = CSIDL_APPDATA;
    369 			break;
    370 		default:
    371 			return {};
    372 		}
    373 		if (SHGetSpecialFolderPathA (nullptr, path.data(), csidl, FALSE))
    374 			return validatePath(path.data());
    375 #else
    376 		const auto h = std::getenv("HOME");
    377 		const std::string home = validatePath(getHomeDirectory());
    378 
    379 #if defined(__APPLE__)
    380 		switch (_type)
    381 		{
    382 		case SpecialFolderType::UserDocuments:
    383 			return home + "Documents/";
    384 		case SpecialFolderType::PrivateAppData:
    385 			return home + "Library/Application Support/";
    386 		default:
    387 			return {};
    388 		}
    389 #else
    390 		// https://specifications.freedesktop.org/basedir-spec/latest/
    391 		switch (_type)
    392 		{
    393 		case SpecialFolderType::UserDocuments:
    394 			{
    395 				const auto* docDir = std::getenv("XDG_DATA_HOME");
    396 				if(docDir && strlen(docDir) > 0)
    397 					return validatePath(docDir);
    398 				return home + ".local/share/";
    399 			}
    400 		case SpecialFolderType::PrivateAppData:
    401 			{
    402 				const auto* confDir = std::getenv("XDG_CONFIG_HOME");
    403 				if(confDir && strlen(confDir) > 0)
    404 					return validatePath(confDir);
    405 				return home + ".config/";
    406 			}
    407 		default:
    408 			return {};
    409 		}
    410 #endif
    411 #endif
    412 		return {};
    413     }
    414 }