IO.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. #define CC4_CHUNK CC4('C','H','N','K')
  6. /******************************************************************************/
  7. static Str _DataPath;
  8. /******************************************************************************/
  9. Str CurDir()
  10. {
  11. #if WINDOWS
  12. wchar_t path[MAX_LONG_PATH]; path[0]=0; GetCurrentDirectory(Elms(path), path);
  13. return path;
  14. #else
  15. Char8 path[MAX_UTF_PATH]; path[0]=0; getcwd(path, Elms(path));
  16. return FromUTF8(path);
  17. #endif
  18. }
  19. void CurDir(C Str &dir)
  20. {
  21. #if WINDOWS
  22. SetCurrentDirectory(dir);
  23. #else
  24. chdir(UnixPathUTF8(dir));
  25. #endif
  26. }
  27. /******************************************************************************/
  28. C Str& DataPath( ) {return _DataPath;}
  29. void DataPath(C Str &path)
  30. {
  31. Str p=NormalizePath(MakeFullPath(path)).tailSlash(true); // copy first to a temp variable, to avoid using move assignment which would change pointer address of the '_DataPath' string, this is to avoid thread issues, for example if one thread would cast DataPath to CChar* and was processing that, while the other thread changed DataPath and released that CChar* then the first thread would crash
  32. _DataPath=p; // adjust '_DataPath' in only one operation to maximize thread-safety
  33. }
  34. /******************************************************************************/
  35. Str MakeFullPath(C Str &path, FILE_PATH type, Bool keep_empty)
  36. {
  37. if(type==FILE_CUR || type==FILE_DATA) // do not adjust Android Assets because they're always accessed without any paths (just asset name)
  38. if(!keep_empty || path.is())
  39. if(!FullPath(path))return ((type==FILE_CUR) ? CurDir().tailSlash(true) : DataPath())+path; // 'DataPath' already has 'tailSlash'
  40. return path;
  41. }
  42. /******************************************************************************/
  43. // FILE INFO
  44. /******************************************************************************/
  45. FileInfo::FileInfo() {zero();}
  46. FileInfo::FileInfo(C PakFile &pf)
  47. {
  48. type =pf.type();
  49. attrib =0;
  50. size =pf.data_size;
  51. modify_time_utc=pf.modify_time_utc;
  52. }
  53. #if WINDOWS
  54. void FileInfo::from(WIN32_FIND_DATA &fd)
  55. {
  56. if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  57. {
  58. type=FSTD_DIR;
  59. size=0;
  60. }else
  61. {
  62. type=FSTD_FILE;
  63. size=((ULong(fd.nFileSizeHigh)<<32)|fd.nFileSizeLow);
  64. }
  65. SYSTEMTIME sys_time; FileTimeToSystemTime(&fd.ftLastWriteTime, &sys_time);
  66. modify_time_utc.year =sys_time.wYear;
  67. modify_time_utc.month =sys_time.wMonth;
  68. modify_time_utc.day =sys_time.wDay;
  69. modify_time_utc.hour =sys_time.wHour;
  70. modify_time_utc.minute=sys_time.wMinute;
  71. modify_time_utc.second=sys_time.wSecond;
  72. attrib=0;
  73. if(fd.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN )attrib|=FATTRIB_HIDDEN;
  74. if(fd.dwFileAttributes&FILE_ATTRIBUTE_READONLY)attrib|=FATTRIB_READ_ONLY;
  75. }
  76. #endif
  77. Bool FileInfo::getSystem(C Str &name)
  78. {
  79. if(name.is())
  80. {
  81. if(IsDrive(name))
  82. {
  83. Memt<Drive> drives; GetDrives(drives);
  84. REPA(drives)if(EqualPath(drives[i].path, name))
  85. {
  86. zero(); type=FSTD_DRIVE;
  87. return true;
  88. }
  89. }
  90. #if WINDOWS
  91. WIN32_FILE_ATTRIBUTE_DATA fad; if(GetFileAttributesEx(name, GetFileExInfoStandard, &fad))
  92. {
  93. if(fad.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  94. {
  95. type=FSTD_DIR;
  96. size=0;
  97. }else
  98. {
  99. type=FSTD_FILE;
  100. size=((ULong(fad.nFileSizeHigh)<<32)|fad.nFileSizeLow);
  101. }
  102. SYSTEMTIME sys_time; FileTimeToSystemTime(&fad.ftLastWriteTime, &sys_time);
  103. modify_time_utc.year =sys_time.wYear;
  104. modify_time_utc.month =sys_time.wMonth;
  105. modify_time_utc.day =sys_time.wDay;
  106. modify_time_utc.hour =sys_time.wHour;
  107. modify_time_utc.minute=sys_time.wMinute;
  108. modify_time_utc.second=sys_time.wSecond;
  109. attrib=0;
  110. if(fad.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN )attrib|=FATTRIB_HIDDEN;
  111. if(fad.dwFileAttributes&FILE_ATTRIBUTE_READONLY)attrib|=FATTRIB_READ_ONLY;
  112. return true;
  113. }
  114. #else
  115. struct stat stats; if(!lstat(UnixPathUTF8(name), &stats))
  116. {
  117. attrib=0;
  118. #ifdef UF_HIDDEN
  119. if(stats.st_flags&UF_HIDDEN)attrib|=FATTRIB_HIDDEN;else if(CChar *base=_GetBase(name))if(*base=='.')attrib|=FATTRIB_HIDDEN;
  120. #else
  121. if(CChar *base=_GetBase(name))if(*base=='.')attrib|=FATTRIB_HIDDEN;
  122. #endif
  123. if(!(stats.st_mode&S_IWUSR) && !(stats.st_mode&S_IWGRP) && !(stats.st_mode&S_IWOTH))attrib|=FATTRIB_READ_ONLY;
  124. if(S_ISLNK(stats.st_mode))
  125. {
  126. type=FSTD_LINK;
  127. size=stats.st_size;
  128. }else
  129. if(S_ISDIR(stats.st_mode))
  130. {
  131. type=FSTD_DIR;
  132. size=0;
  133. }else
  134. {
  135. type=FSTD_FILE;
  136. size=stats.st_size;
  137. }
  138. #if APPLE
  139. tm gmt; gmtime_r(&stats.st_mtimespec.tv_sec, &gmt);
  140. #else
  141. tm gmt; time_t t=stats.st_mtime; gmtime_r(&t, &gmt);
  142. #endif
  143. modify_time_utc.year =gmt.tm_year+1900;
  144. modify_time_utc.month =gmt.tm_mon+1;
  145. modify_time_utc.day =gmt.tm_mday;
  146. modify_time_utc.hour =gmt.tm_hour;
  147. modify_time_utc.minute=gmt.tm_min;
  148. modify_time_utc.second=gmt.tm_sec;
  149. return true;
  150. }
  151. #endif
  152. }
  153. zero(); return false;
  154. }
  155. Bool FileInfo::get(C Str &name)
  156. {
  157. if(name.is())
  158. {
  159. if(C PaksFile *psf=Paks.find(name)){T=*psf->file; return true;}
  160. if(getSystem(name))return true;
  161. if(DataPath().is() && !FullPath(name)){Char full[MAX_LONG_PATH]; MergePath(full, DataPath(), name); if(getSystem(full))return true;}
  162. }
  163. zero(); return false;
  164. }
  165. Bool operator==(C FileInfo &f0, C FileInfo &f1)
  166. {
  167. if(f0.type==f1.type)switch(f0.type)
  168. {
  169. case FSTD_DRIVE:
  170. case FSTD_DIR : return true;
  171. case FSTD_LINK:
  172. case FSTD_FILE: return f0.size==f1.size && !Compare(f0.modify_time_utc, f1.modify_time_utc, 1);
  173. }
  174. return false;
  175. }
  176. /******************************************************************************/
  177. // FILE FIND
  178. /******************************************************************************/
  179. void FileFind::zero()
  180. {
  181. _state =NONE;
  182. _drive =0;
  183. _drive_type=DRIVE_UNDEFINED;
  184. _handle =PLATFORM(INVALID_HANDLE_VALUE, null);
  185. }
  186. void FileFind::del()
  187. {
  188. #if WINDOWS
  189. if(_handle!=INVALID_HANDLE_VALUE)FindClose(_handle);
  190. #else
  191. if(_handle)closedir(_handle);
  192. #endif
  193. zero();
  194. }
  195. FileFind::~FileFind() {del ();}
  196. FileFind:: FileFind() {zero();}
  197. FileFind:: FileFind(C Str &path, C Str &ext) : FileFind() {find(path, ext);}
  198. /******************************************************************************/
  199. #if WINDOWS
  200. Bool FileFind::findValid(WIN32_FIND_DATA &fd)
  201. {
  202. for(; fd.cFileName[0]; )
  203. {
  204. if(fd.cFileName[0]=='.')if(!fd.cFileName[1] || (fd.cFileName[1]=='.' && !fd.cFileName[2]))goto dot; // "." or ".."
  205. if(!_ext.is() || _ext==_GetExt(WChar(fd.cFileName)))
  206. {
  207. name=fd.cFileName;
  208. super::from(fd);
  209. return true;
  210. }
  211. dot:;
  212. if(!FindNextFile(_handle, &fd))break;
  213. }
  214. return false;
  215. }
  216. #endif
  217. Bool FileFind::findNext()
  218. {
  219. if(_drive)
  220. {
  221. for(;;)
  222. {
  223. Memt<Drive> drives; GetDrives(drives);
  224. Byte i=_drive-1; if(i>=drives.elms())break; _drive++;
  225. name =drives[i].path;
  226. _drive_type=drives[i].type;
  227. type =FSTD_DRIVE;
  228. attrib =0;
  229. size =0;
  230. modify_time_utc.zero();
  231. return true;
  232. }
  233. }else
  234. {
  235. #if WINDOWS
  236. WIN32_FIND_DATA fd;
  237. if(FindNextFile(_handle, &fd))if(findValid(fd))return true;
  238. #else
  239. for(; dirent *file=readdir(_handle); ) // 'readdir' is multi-thread safe, no need to use 'readdir_r' which is now deprecated
  240. {
  241. name=FromUTF8(file->d_name);
  242. #if APPLE // on Apple we need to precompose strings
  243. if(HasUnicode(name))name=NSStringAuto(name);
  244. #endif
  245. if( name.is())if(name[0]=='.')if(!name[1] || (name[1]=='.' && !name[2]))continue;
  246. if(!_ext.is() || Equal(_ext, _GetExt(name)))
  247. {
  248. super::getSystem(pathName());
  249. return true;
  250. }
  251. }
  252. #endif
  253. }
  254. _state=NONE;
  255. return false;
  256. }
  257. void FileFind::find(C Str &path, C Str &ext)
  258. {
  259. del();
  260. T._ext =ext;
  261. T._path=path; _path.tailSlash(true);
  262. #if WINDOWS
  263. WIN32_FIND_DATA fd;
  264. //_handle =FindFirstFile (_path+'*', &fd);
  265. _handle =FindFirstFileEx(_path+'*', FindExInfoBasic, &fd, FindExSearchNameMatch, null, 0); // this is supposed to be faster than 'FindFirstFile', due to 'FindExInfoBasic' not processing 'cAlternateFileName'
  266. if(_handle!=INVALID_HANDLE_VALUE)if(findValid(fd))_state=FILE_WAITING;
  267. #else
  268. if(_handle=opendir(_path.is() ? UnixPathUTF8(_path)() : "."))_state=NEED_CHECK;
  269. #endif
  270. }
  271. void FileFind::findDrives()
  272. {
  273. del();
  274. _drive=1;
  275. _state=NEED_CHECK;
  276. }
  277. Bool FileFind::operator()()
  278. {
  279. switch(_state)
  280. {
  281. case NEED_CHECK : return findNext();
  282. case FILE_WAITING: _state=NEED_CHECK; return true;
  283. default : return false;
  284. }
  285. }
  286. /******************************************************************************/
  287. // BACKGROUND FILE FIND
  288. /******************************************************************************/
  289. static Bool BackgroundFileFindThreadFunc (Thread &thread ) {return ((BackgroundFileFind*)thread.user)->update();}
  290. static Int BackgroundFileFindComparePath(C Str &a, C Str &b) {return ComparePath(b, a);} // return in reversed order because later we're taking last element from paths
  291. /******************************************************************************/
  292. BackgroundFileFind& BackgroundFileFind::del()
  293. {
  294. _thread.del(); // delete the thread first so it will not process data on released members
  295. _files .del();
  296. _paths .del();
  297. _filter=null;
  298. return T;
  299. }
  300. /******************************************************************************/
  301. Bool BackgroundFileFind::getFiles(Memc<File> &files)
  302. {
  303. files.clear();
  304. if(T._files.elms())
  305. {
  306. SyncLocker locker(_lock);
  307. Swap(files, T._files);
  308. }
  309. return files.elms()>0;
  310. }
  311. /******************************************************************************/
  312. void BackgroundFileFind::find(C Str &path, Bool (*filter)(C Str &name))
  313. {
  314. SyncLocker locker(_lock);
  315. _files.clear();
  316. _paths.setNum(1)[0]=path;
  317. _filter=filter;
  318. _find_id++;
  319. if(!_thread.active())_thread.create(BackgroundFileFindThreadFunc, this);
  320. }
  321. /******************************************************************************/
  322. void BackgroundFileFind::clear()
  323. {
  324. SyncLocker locker(_lock);
  325. _files.clear();
  326. _paths.clear();
  327. _filter=null;
  328. _find_id++;
  329. }
  330. /******************************************************************************/
  331. Bool BackgroundFileFind::update()
  332. {
  333. for(; !_thread.wantStop(); )
  334. {
  335. // get path
  336. SyncLockerEx locker(_lock); if(!_paths.elms())return false;
  337. Str path=_paths.pop(); UInt find_id=T._find_id; // get unique id of the 'find' operation
  338. locker.off();
  339. // find files in path
  340. Memc<Str> new_paths;
  341. for(FileFind ff(path); !_thread.wantStop() && ff(); )
  342. {
  343. File file; FileInfo &fi=file; fi=ff; file.name=ff.pathName();
  344. SyncLockerEx locker(_lock);
  345. if(find_id==T._find_id) // check id's in case during this loop new 'find' was called
  346. {
  347. if(!_filter || _filter(file.name))
  348. {
  349. if(file.type==FSTD_DRIVE || file.type==FSTD_DIR)new_paths.add(file.name); // add folder to paths list
  350. Swap(_files.New(), file);
  351. }
  352. }else
  353. {
  354. new_paths.clear();
  355. break;
  356. }
  357. }
  358. // add new paths
  359. if(new_paths.elms())
  360. {
  361. locker.on (); if(find_id!=T._find_id)new_paths.clear();else FREPA(_paths)new_paths.add(_paths[i]);
  362. locker.off(); new_paths.sort(BackgroundFileFindComparePath);
  363. locker.on (); if(find_id==T._find_id)Swap(_paths, new_paths);
  364. locker.off();
  365. }
  366. }
  367. return false;
  368. }
  369. /******************************************************************************/
  370. // MAIN
  371. /******************************************************************************/
  372. static Bool FListEx(C Str &path, FILE_LIST_MODE func(C FileFind &ff, Ptr user), Ptr user)
  373. {
  374. for(FileFind ff(path); ff(); )switch(ff.type)
  375. {
  376. case FSTD_LINK:
  377. case FSTD_FILE: if(func(ff, user)==FILE_LIST_BREAK)return false; break;
  378. case FSTD_DIR:
  379. {
  380. switch(func(ff, user))
  381. {
  382. //case FILE_LIST_SKIP : break; do nothing
  383. case FILE_LIST_BREAK : return false;
  384. case FILE_LIST_CONTINUE: if(!FListEx(ff.pathName(), func, user))return false; break;
  385. }
  386. }break;
  387. }
  388. return true;
  389. }
  390. void FList(C Str &path, FILE_LIST_MODE func(C FileFind &ff, Ptr user), Ptr user) {FListEx(path, func, user);}
  391. /******************************************************************************/
  392. Bool FEqual(C Str &a, C Str &b, Cipher *a_cipher, Cipher *b_cipher)
  393. {
  394. File fa, fb;
  395. if(fa.readTry(a, a_cipher)
  396. && fb.readTry(b, b_cipher))return fa.equal(fb);
  397. return false;
  398. }
  399. Bool FCopy(C Str &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *src_cipher, Cipher *dest_cipher, CChar *safe_overwrite_suffix)
  400. {
  401. if(EqualPath(src, dest))return src_cipher==dest_cipher; Str full_dest=MakeFullPath(dest); // we can't make 'src' to be full path because it can be path from 'DataPath' or 'Pak'
  402. if(EqualPath(src, full_dest))return src_cipher==dest_cipher;
  403. if(overwrite!=FILE_OVERWRITE_NEVER || !FExistSystem(full_dest))
  404. {
  405. File d, s; if(s.readTry(src, src_cipher))
  406. {
  407. // get source info
  408. C PakFile *pf=null;
  409. FileInfo fi;
  410. if(s._pak)pf=s._pak->find(src, false);else fi.get(src);
  411. // check
  412. if(overwrite==FILE_OVERWRITE_DIFFERENT)
  413. {
  414. if(pf)fi=*pf;
  415. if(fi==FileInfoSystem(full_dest))return true;
  416. }
  417. // write
  418. #if APPLE || LINUX
  419. if(s._pak ? (pf && (pf->flag&PF_STD_LINK)) : (fi.type==FSTD_LINK)) // if this is a symbolic link
  420. {
  421. FDelFile(full_dest); // delete first because 'CreateSymLink' will fail if file already exists
  422. if(!CreateSymLink(full_dest, DecodeSymLink(s)))return false;
  423. }else
  424. #endif
  425. if(Is(safe_overwrite_suffix))
  426. {
  427. if(!SafeOverwrite(s, full_dest, pf ? &pf->modify_time_utc : fi.type ? &fi.modify_time_utc : null, dest_cipher, safe_overwrite_suffix))return false;
  428. }else
  429. {
  430. if(!d.writeTry(full_dest, dest_cipher) || !s.copy(d) || !d.flush())return false;
  431. d.del(); // release handle so we can apply file params
  432. if(pf )FTimeUTC(full_dest, pf->modify_time_utc);else
  433. if(fi.type)FTimeUTC(full_dest, fi.modify_time_utc);
  434. }
  435. // apply params
  436. if(fi.type)FAttrib(full_dest, fi.attrib);
  437. return true;
  438. }
  439. }
  440. return false;
  441. }
  442. Bool FCopy(Pak &pak, C PakFile &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *dest_cipher, CChar *safe_overwrite_suffix)
  443. {
  444. Bool ok=true;
  445. if(!(src.flag&PF_REMOVED))
  446. {
  447. if(src.children_num || (src.flag&PF_STD_DIR)) // folder
  448. {
  449. if(ok=(FCreateDir(dest) || FileInfoSystem(dest).type==FSTD_DIR))
  450. if(src.children_num)
  451. {
  452. Str dest_slash=dest; dest_slash.tailSlash(true);
  453. FREP(src.children_num)
  454. {
  455. C PakFile &file=pak.file(src.children_offset+i);
  456. ok&=FCopy(pak, file, dest_slash+file.name, overwrite, dest_cipher, safe_overwrite_suffix);
  457. }
  458. }
  459. }else // file
  460. if(overwrite!=FILE_OVERWRITE_NEVER || !FExistSystem(dest))
  461. {
  462. // check
  463. if(overwrite==FILE_OVERWRITE_DIFFERENT && FileInfo(src)==FileInfoSystem(dest))return true;
  464. File s, d; if(!s.readTry(src, pak))ok=false;else
  465. {
  466. #if APPLE || LINUX
  467. if(src.flag&PF_STD_LINK)
  468. {
  469. FDelFile(dest); // delete first because 'CreateSymLink' will fail if file already exists
  470. if(CreateSymLink(dest, DecodeSymLink(s)))FTimeUTC(dest, src.modify_time_utc);else ok=false;
  471. }else
  472. #endif
  473. if(Is(safe_overwrite_suffix))
  474. {
  475. if(!SafeOverwrite(s, dest, &src.modify_time_utc, dest_cipher, safe_overwrite_suffix))return false;
  476. }else
  477. if(!d.writeTry(dest, dest_cipher) || !s.copy(d) || !d.flush())ok=false;else
  478. {
  479. d.del(); // release handle so we can apply file params
  480. FTimeUTC(dest, src.modify_time_utc);
  481. }
  482. }
  483. }
  484. }
  485. return ok;
  486. }
  487. Bool FCopy(Pak &pak, C Str &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *dest_cipher, CChar *safe_overwrite_suffix)
  488. {
  489. if(C PakFile *pak_file=pak.find(src, false))return FCopy(pak, *pak_file, dest, overwrite, dest_cipher, safe_overwrite_suffix);
  490. return false;
  491. }
  492. Bool FCopy(Pak &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *dest_cipher)
  493. {
  494. Bool ok=true;
  495. FCreateDirs(dest);
  496. Str dest_path=dest; dest_path.tailSlash(true);
  497. FREP(src.rootFiles())
  498. {
  499. C PakFile &file=src.file(i);
  500. ok&=FCopy(src, file, dest_path+file.name, overwrite, dest_cipher);
  501. }
  502. return ok;
  503. }
  504. Bool FCopyDir(C Str &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *src_cipher, Cipher *dest_cipher)
  505. {
  506. Bool ok=true;
  507. if(FExistSystem(dest) || FCreateDirs(dest))for(FileFind ff(src); ff(); )
  508. {
  509. Str name=dest; name.tailSlash(true)+=ff.name;
  510. switch(ff.type)
  511. {
  512. case FSTD_LINK:
  513. case FSTD_FILE: if(overwrite!=FILE_OVERWRITE_DIFFERENT || ff!=FileInfoSystem(name))ok&=FCopy (ff.pathName(), name, overwrite, src_cipher, dest_cipher); break;
  514. case FSTD_DIR : ok&=FCopyDir(ff.pathName(), name, overwrite, src_cipher, dest_cipher); break;
  515. }
  516. }else ok=false;
  517. return ok;
  518. }
  519. /******************************************************************************/
  520. Bool FTimeUTC(C Str &name, C DateTime &time_utc) // Android OS has a bug which makes the 'utimes' and 'File.setLastModified' always fail - https://code.google.com/p/android/issues/detail?id=18624
  521. {
  522. if(name.is())
  523. {
  524. #if WINDOWS
  525. #if WINDOWS_OLD
  526. HANDLE file=CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, null, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, null); // FILE_FLAG_BACKUP_SEMANTICS is needed for opening folders
  527. #else
  528. CREATEFILE2_EXTENDED_PARAMETERS ex; Zero(ex);
  529. ex.dwSize=SIZE(ex);
  530. //ex.dwFileAttributes=0;
  531. ex.dwFileFlags=FILE_FLAG_BACKUP_SEMANTICS; // needed for opening folders
  532. HANDLE file=CreateFile2(name, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, OPEN_EXISTING, &ex);
  533. #endif
  534. if(file!=INVALID_HANDLE_VALUE)
  535. {
  536. FILETIME file_time;
  537. SYSTEMTIME sys_time;
  538. sys_time.wYear =time_utc.year;
  539. sys_time.wMonth =time_utc.month;
  540. sys_time.wDay =time_utc.day;
  541. sys_time.wHour =time_utc.hour;
  542. sys_time.wMinute=time_utc.minute;
  543. sys_time.wSecond=time_utc.second;
  544. sys_time.wDayOfWeek =0;
  545. sys_time.wMilliseconds=0;
  546. SystemTimeToFileTime(&sys_time, &file_time);
  547. Bool ok=(SetFileTime(file, null, null, &file_time)!=0);
  548. CloseHandle(file);
  549. return ok;
  550. }
  551. #else
  552. timeval tv[2];
  553. tv[0].tv_sec =time_utc.seconds1970();
  554. tv[0].tv_usec=0;
  555. tv[1]=tv[0];
  556. #if ANDROID || WEB
  557. if(! utimes(UnixPathUTF8(name), tv))
  558. #else
  559. if(!lutimes(UnixPathUTF8(name), tv)) // this does not follow symbolic links
  560. #endif
  561. {
  562. FlushIO();
  563. return true;
  564. }
  565. #endif
  566. }
  567. return false;
  568. }
  569. Bool FAttrib(C Str &name, UInt attrib)
  570. {
  571. if(name.is())
  572. {
  573. #if WINDOWS
  574. UInt out=0;
  575. if(attrib&FATTRIB_READ_ONLY)out|=FILE_ATTRIBUTE_READONLY;
  576. if(attrib&FATTRIB_HIDDEN )out|=FILE_ATTRIBUTE_HIDDEN ;
  577. return SetFileAttributes(name, out)!=0;
  578. #else
  579. Str8 utf=UnixPathUTF8(name);
  580. struct stat stats; if(!lstat(utf, &stats))
  581. {
  582. UInt mode =(stats.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX));
  583. Bool write=!FlagTest(attrib, FATTRIB_READ_ONLY);
  584. FlagSet(mode, S_IWUSR, write);
  585. FlagSet(mode, S_IWGRP, write);
  586. FlagSet(mode, S_IWOTH, write);
  587. #ifdef UF_HIDDEN
  588. UInt flags=stats.st_flags; FlagSet(flags, UF_HIDDEN, FlagTest(attrib, FATTRIB_HIDDEN));
  589. if(!lchflags(utf, flags) && !lchmod(utf, mode))
  590. #else
  591. if(!chmod(utf, mode))
  592. #endif
  593. {
  594. FlushIO();
  595. return true;
  596. }
  597. }
  598. #endif
  599. }
  600. return false;
  601. }
  602. UInt FAttrib(C Str &name)
  603. {
  604. UInt out=0;
  605. if(name.is())
  606. {
  607. #if WINDOWS_OLD
  608. UInt attrib =GetFileAttributes(name);
  609. if( attrib!=INVALID_FILE_ATTRIBUTES)
  610. {
  611. if(attrib&FILE_ATTRIBUTE_HIDDEN )out|=FATTRIB_HIDDEN ;
  612. if(attrib&FILE_ATTRIBUTE_READONLY)out|=FATTRIB_READ_ONLY;
  613. }
  614. #elif WINDOWS_NEW
  615. WIN32_FILE_ATTRIBUTE_DATA fad; if(GetFileAttributesEx(name, GetFileExInfoStandard, &fad))
  616. {
  617. if(fad.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN )out|=FATTRIB_HIDDEN;
  618. if(fad.dwFileAttributes&FILE_ATTRIBUTE_READONLY)out|=FATTRIB_READ_ONLY;
  619. }
  620. #else
  621. struct stat stats; if(!lstat(UnixPathUTF8(name), &stats))
  622. {
  623. #ifdef UF_HIDDEN
  624. if(stats.st_flags&UF_HIDDEN)out|=FATTRIB_HIDDEN;else if(CChar *base=_GetBase(name))if(*base=='.')out|=FATTRIB_HIDDEN;
  625. #else
  626. if(CChar *base=_GetBase(name))if(*base=='.')out|=FATTRIB_HIDDEN;
  627. #endif
  628. if(!(stats.st_mode&S_IWUSR) && !(stats.st_mode&S_IWGRP) && !(stats.st_mode&S_IWOTH))out|=FATTRIB_READ_ONLY;
  629. }
  630. #endif
  631. }
  632. return out;
  633. }
  634. static Long FSizeDir(C Str &name)
  635. {
  636. Long size=0; for(FileFind ff(name); ff(); )if(ff.type==FSTD_DIR)size+=FSizeDir(ff.pathName());else size+=ff.size;
  637. return size;
  638. }
  639. Long FSize(C Str &name)
  640. {
  641. FileInfoSystem fi(name);
  642. switch(fi.type)
  643. {
  644. case FSTD_NONE: return -1;
  645. case FSTD_DIR : return FSizeDir(name);
  646. default : return fi.size;
  647. }
  648. }
  649. /******************************************************************************/
  650. Bool FExistSystem(C Str &name)
  651. {
  652. if(name.is())
  653. {
  654. #if WINDOWS_OLD
  655. return GetFileAttributes(name)!=INVALID_FILE_ATTRIBUTES;
  656. #elif WINDOWS_NEW
  657. WIN32_FILE_ATTRIBUTE_DATA fad; return GetFileAttributesEx(name, GetFileExInfoStandard, &fad)!=0;
  658. #else
  659. struct stat stats; return !lstat(UnixPathUTF8(name), &stats);
  660. #endif
  661. }
  662. return false;
  663. }
  664. Bool FExist(C Str &name)
  665. {
  666. if(name.is())
  667. {
  668. Bool full_path=FullPath(name);
  669. if( !full_path && Paks.find(name))return true;
  670. if( FExistSystem(name))return true;
  671. if(DataPath().is() && !full_path){Char full[MAX_LONG_PATH]; MergePath(full, DataPath(), name); if(FExistSystem(full))return true;}
  672. }
  673. return false;
  674. }
  675. Str FFirst(C Str &name, C Str &ext)
  676. {
  677. Str file; FREP(10000)
  678. {
  679. file=name; file+=i; if(ext.is()){file+='.'; file+=ext;}
  680. if(!FExistSystem(file))return file;
  681. }
  682. return S;
  683. }
  684. Str FFirstUp(C Str &name)
  685. {
  686. for(Str path=name; path.is(); path=GetPath(path))if(FExistSystem(path))return path;
  687. return S;
  688. }
  689. /******************************************************************************/
  690. Bool FRename(C Str &src, C Str &dest)
  691. {
  692. Str s=MakeFullPath(src), d=MakeFullPath(dest);
  693. if(EqualPath(s, d) && Equal(GetBase(s), GetBase(d), true))return true; // allow changing case
  694. #if WINDOWS
  695. return MoveFileEx(s, d, MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)!=0;
  696. #else
  697. #if MAC
  698. if(FileInfoSystem(s).type!=FSTD_LINK) // links can't be processed by 'FSPathMoveObjectSync' because the target file will be moved instead of the link
  699. {
  700. Str8 dest_base=UnixPathUTF8(GetBase(d));
  701. CFStringRef name=(dest_base.is() ? CFStringCreateWithCString(kCFAllocatorDefault, dest_base, kCFStringEncodingUTF8) : null);
  702. Bool ok=(FSPathMoveObjectSync(UnixPathUTF8(s), UnixPathUTF8(GetPath(d)), name, null, kFSFileOperationOverwrite|kFSFileOperationSkipPreflight)==noErr);
  703. if(name)CFRelease(name);
  704. if(ok)return true; // return only on success, if failed, then try methods below (this can fail for current app's exe file)
  705. }
  706. #endif
  707. if(!rename(UnixPathUTF8(s), UnixPathUTF8(d))){FlushIO(); return true;}
  708. if(errno==EXDEV)if(FCopy(s, d))return FDel(s);
  709. return false;
  710. #endif
  711. }
  712. #if WINDOWS
  713. Bool FDelFile (C Str &name) {return DeleteFile (name )!=0;}
  714. Bool FCreateDir(C Str &name) {return CreateDirectory(name, null)!=0;}
  715. Bool FDelDir (C Str &name) {return RemoveDirectory(name )!=0;}
  716. #else
  717. Bool FDelFile (C Str &name) {if(!unlink(UnixPathUTF8(name) )){FlushIO(); return true;} return false;} // 'remove' will delete both files/dirs
  718. Bool FCreateDir(C Str &name) {if(!mkdir (UnixPathUTF8(name), S_IRWXU|S_IRWXG|S_IRWXO)){FlushIO(); return true;} return false;}
  719. Bool FDelDir (C Str &name) {if(!rmdir (UnixPathUTF8(name) )){FlushIO(); return true;} return false;}
  720. #endif
  721. static Bool FDel(FSTD_TYPE type, C Str &name)
  722. {
  723. switch(type)
  724. {
  725. case FSTD_LINK:
  726. case FSTD_FILE: return FDelFile(name);
  727. case FSTD_DIR : return FDelDirs(name);
  728. }
  729. return false;
  730. }
  731. static inline Bool FDel(C FileFind &ff) {return FDel(ff.type, ff.pathName());}
  732. Bool FCreateDirs(C Str &name)
  733. {
  734. if(!name.is())return true;
  735. Str path=name; path.tailSlash(false);
  736. Char temp[MAX_LONG_PATH];
  737. FREPA(temp)
  738. {
  739. Char c=path[i];
  740. if( !c)return FCreateDir(path);
  741. if(IsSlash(c) && !(i && path[i-1]==L':')){temp[i]=0; FCreateDir(temp);}
  742. temp[i]=c;
  743. }
  744. return false;
  745. }
  746. Bool FDelDirs(C Str &name)
  747. {
  748. return FDelInside(name) && FDelDir(name); // delete dir contents and the dir itself
  749. }
  750. Bool FDelInside(C Str &name)
  751. {
  752. if(name.is())
  753. {
  754. Bool ok=true; for(FileFind ff(name); ff(); )ok&=FDel(ff); // keep removing other files even if one fails
  755. return ok;
  756. }
  757. return false;
  758. }
  759. Bool FDel(C Str &name) {return FDel(FileInfoSystem(name).type, name);}
  760. #if WINDOWS_NEW
  761. struct FileRecycler
  762. {
  763. Bool ok;
  764. FileRecycler(C Str &name)
  765. {
  766. auto get_file=concurrency::create_task(Windows::Storage::StorageFile::GetFileFromPathAsync(ref new Platform::String(WindowsPath(name)))); // 'WindowsPath' must be used or exception will occur when using '/' instead of '\'
  767. if(App.mainThread())
  768. {
  769. ok=false;
  770. get_file.then([this](concurrency::task<Windows::Storage::StorageFile^> get_file)
  771. {
  772. try
  773. {
  774. concurrency::create_task(get_file.get()->DeleteAsync(Windows::Storage::StorageDeleteOption::Default)).then([this]()
  775. {
  776. ok=true;
  777. });
  778. }
  779. catch(...){ok=true;}
  780. });
  781. App.loopUntil(ok);
  782. }else
  783. {
  784. try
  785. {
  786. auto del=concurrency::create_task(get_file.get()->DeleteAsync(Windows::Storage::StorageDeleteOption::Default));
  787. del.wait();
  788. }
  789. catch(...){}
  790. }
  791. ok=!FExistSystem(name);
  792. }
  793. };
  794. struct FolderRecycler
  795. {
  796. Bool ok;
  797. FolderRecycler(C Str &name)
  798. {
  799. auto get_folder=concurrency::create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(ref new Platform::String(WindowsPath(name)))); // 'WindowsPath' must be used or exception will occur when using '/' instead of '\'
  800. if(App.mainThread())
  801. {
  802. ok=false;
  803. get_folder.then([this](concurrency::task<Windows::Storage::StorageFolder^> get_folder)
  804. {
  805. try
  806. {
  807. concurrency::create_task(get_folder.get()->DeleteAsync(Windows::Storage::StorageDeleteOption::Default)).then([this]()
  808. {
  809. ok=true;
  810. });
  811. }
  812. catch(...){ok=true;}
  813. });
  814. App.loopUntil(ok);
  815. }else
  816. {
  817. try
  818. {
  819. auto del=concurrency::create_task(get_folder.get()->DeleteAsync(Windows::Storage::StorageDeleteOption::Default));
  820. del.wait();
  821. }
  822. catch(...){}
  823. }
  824. ok=!FExistSystem(name);
  825. }
  826. };
  827. #endif
  828. Bool FRecycle(C Str &name, Bool hidden)
  829. {
  830. if(name.is())
  831. {
  832. #if WINDOWS_OLD
  833. Str full=MakeFullPath(name); // must be full path
  834. if(HasDrive(full))
  835. {
  836. Char path[MAX_LONG_PATH+1]; path[SetReturnLength(path, full, Elms(path)-1)+1]=0; // must be "double null terminated" - "\0\0"
  837. SHFILEOPSTRUCT sh; Zero(sh);
  838. sh.wFunc = FO_DELETE;
  839. sh.pFrom =WChar(path);
  840. sh.fFlags=(FOF_ALLOWUNDO|(hidden ? FOF_NO_UI : 0));
  841. return !SHFileOperation(&sh);
  842. }
  843. #elif WINDOWS_NEW
  844. switch(FileInfoSystem(name).type)
  845. {
  846. case FSTD_FILE: return FileRecycler(name).ok;
  847. case FSTD_DIR : return FolderRecycler(name).ok;
  848. }
  849. #elif MAC
  850. FSRef fs_ref; if(FSPathMakeRef((UInt8*)(UnixPathUTF8(name)()), &fs_ref, null)==noErr)return FSMoveObjectToTrashSync(&fs_ref, null, 0)==noErr;
  851. #elif LINUX
  852. Str full=MakeFullPath(name); // must be full path
  853. if(HasDrive(full))
  854. {
  855. Str trash=SystemPath(SP_TRASH); if(trash.is())
  856. {
  857. Str base=GetBase(full), first=FFirst(trash+"/files/"+base); if(first.is())
  858. {
  859. FileText f; if(f.write(trash+"/info/"+GetBase(first)+".trashinfo", UTF_8_NAKED)) // spec requires writing to "info" first, UTF is probably not supported
  860. {
  861. DateTime dt; dt.getLocal();
  862. f.putLine("[Trash Info]");
  863. f.putLine(S+"Path="+UnixPath(full));
  864. f.putLine(S+"DeletionDate="+dt.year+'-'+dt.month+'-'+dt.day+'T'+dt.hour+':'+dt.minute+':'+dt.second);
  865. if(f.flushOK())
  866. {
  867. f.del();
  868. if(FRename(full, first))return true;
  869. }
  870. }
  871. }
  872. }
  873. }
  874. return FDel(name);
  875. #else
  876. return FDel(name);
  877. #endif
  878. }
  879. return false;
  880. }
  881. static Bool FMoveDirEx(C Str &src, C Str &dest)
  882. {
  883. Bool ok=true;
  884. FCreateDir(dest);
  885. Str src_t= src; src_t.tailSlash(true);
  886. Str dest_t=dest; dest_t.tailSlash(true);
  887. for(FileFind ff(src); ff(); )switch(ff.type)
  888. {
  889. case FSTD_LINK:
  890. case FSTD_FILE: ok&=FRename (src_t+ff.name, dest_t+ff.name); break;
  891. case FSTD_DIR : ok&=FMoveDirEx(src_t+ff.name, dest_t+ff.name); break;
  892. }
  893. ok&=FDelDir(src);
  894. return ok;
  895. }
  896. Bool FMoveDir(C Str &src, C Str &dest)
  897. {
  898. Str s=MakeFullPath(src ),
  899. d=MakeFullPath(dest);
  900. if(!StartsPath(d, s))
  901. {
  902. if(FExistSystem(s))
  903. {
  904. FCreateDirs( d);
  905. return FMoveDirEx (s, d);
  906. }
  907. return false;
  908. }
  909. return EqualPath(s, d);
  910. }
  911. static Bool FReplaceDirEx(C Str &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *src_cipher, Cipher *dest_cipher) // !! assumes that 'src' and 'dest' have 'tailSlash(true)' !!
  912. {
  913. Bool ok=true;
  914. // first remove all 'dest' elements not present in 'src' to free disk space, also remove elements of different types (for example on 'src' it's a file while on 'dest' it's a folder)
  915. for(FileFind ff(dest); ff(); )if(FileInfoSystem(src+ff.name).type!=ff.type)ok&=FDel(ff);
  916. // create folder
  917. FCreateDir(dest);
  918. // now copy all 'src' elements to 'dest'
  919. for(FileFind ff(src); ff(); )
  920. if(ff.type==FSTD_DIR)ok&=FReplaceDirEx(ff.pathName().tailSlash(true), dest+ff.name+'\\', overwrite, src_cipher, dest_cipher);
  921. else ok&=FCopy (ff.pathName() , dest+ff.name , overwrite, src_cipher, dest_cipher);
  922. return ok;
  923. }
  924. Bool FReplaceDir(C Str &src, C Str &dest, FILE_OVERWRITE_MODE overwrite, Cipher *src_cipher, Cipher *dest_cipher)
  925. {
  926. Str s=MakeFullPath(src), d=MakeFullPath(dest);
  927. if(!StartsPath(d, s))
  928. {
  929. FCreateDirs (d);
  930. return FReplaceDirEx(s.tailSlash(true), d.tailSlash(true), overwrite, src_cipher, dest_cipher);
  931. }
  932. return EqualPath(s, d) && src_cipher==dest_cipher;
  933. }
  934. /******************************************************************************/
  935. void GetDrives(MemPtr<Drive> drives)
  936. {
  937. drives.clear();
  938. #if WINDOWS_OLD
  939. UInt drv=GetLogicalDrives();
  940. FREP(32)if(drv&(1<<i))
  941. {
  942. Drive &d=drives.New();
  943. d.path.reserve(3)+=Char('A'+i); d.path+=":\\";
  944. switch(GetDriveType(d.path))
  945. {
  946. case DRIVE_FIXED : d.type=DRIVE_DISK ; break;
  947. case DRIVE_CDROM : d.type=DRIVE_OPTICAL ; break;
  948. case DRIVE_REMOVABLE: d.type=DRIVE_USB ; break;
  949. default : d.type=DRIVE_UNDEFINED; break;
  950. }
  951. if(i>=2) // skip "A:" and "B:" floppy drives because querying their 'GetVolumeInformation' may be slow (even few seconds), don't remove this, as there can be computers without the floppy drives, but the drivers can still be installed
  952. {
  953. wchar_t name[MAX_LONG_PATH+1]; name[0]=0; GetVolumeInformation(d.path, name, Elms(name), null, null, null, null, 0);
  954. d.name=name;
  955. }
  956. }
  957. #elif WINDOWS_NEW
  958. // TODO: WINDOWS_NEW 'GetDrives'
  959. #elif MAC
  960. FREP(32)
  961. {
  962. FSVolumeRefNum ref;
  963. HFSUniStr255 name;
  964. FSVolumeInfo info;
  965. if(FSGetVolumeInfo(kFSInvalidVolumeRefNum, 1+i, &ref, kFSVolInfoFlags, &info, &name, null)!=noErr)break;
  966. Bool read_only=((info.flags&kFSVolFlagSoftwareLockedMask)!=0);
  967. Char8 url_path[MAX_UTF_PATH];
  968. CFURLRef url; FSCopyURLForVolume(ref, &url); CFURLGetFileSystemRepresentation(url, true, (UInt8*)url_path, Elms(url_path)); if(url)CFRelease(url);
  969. Drive &d=drives.New();
  970. d.type=(read_only ? DRIVE_OPTICAL : DRIVE_DISK);
  971. d.path=Replace(FromUTF8(url_path), '/', '\\'); d.path.tailSlash(true); // make sure slash is added to preserve consistency with Windows version
  972. FREP(name.length)d.name+=(Char)name.unicode[i];
  973. }
  974. #elif ANDROID
  975. {
  976. Drive &d=drives.New();
  977. d.path="\\";
  978. d.name="Drive";
  979. d.type=DRIVE_DISK;
  980. }
  981. if(AndroidSDCardPath.is())
  982. {
  983. Drive &d=drives.New();
  984. d.path=AndroidSDCardPath;
  985. d.name="SD Card";
  986. d.type=DRIVE_SD_CARD;
  987. }
  988. #else
  989. #if 1
  990. Drive &d=drives.New();
  991. d.path="\\";
  992. d.name="Drive";
  993. d.type=DRIVE_DISK;
  994. #else
  995. Memc<struct statfs> st; Int fs=getfsstat(null, 0, MNT_NOWAIT); st.setNum(Max(0, fs)+16); fs=getfsstat(st.data(), st.elms()*st.elmSize(), MNT_NOWAIT);
  996. FREP(fs)if(st[i].f_blocks && st[i].f_bsize)
  997. {
  998. Drive &d=drives.New();
  999. d.path=st[i].f_mntonname; d.path.tailSlash(true);
  1000. d.type=((st[i].f_flags&MNT_RDONLY) ? DRIVE_OPTICAL : DRIVE_DISK);
  1001. }
  1002. #endif
  1003. #endif
  1004. }
  1005. /******************************************************************************/
  1006. Bool GetDriveSize(C Str &path, Long *free, Long *total)
  1007. {
  1008. #if WINDOWS
  1009. ULARGE_INTEGER _free, _total; if(path.is() && GetDiskFreeSpaceEx(path, &_free, &_total, null)){if(free)*free=_free.QuadPart; if(total)*total=_total.QuadPart; return true;}
  1010. #elif IOS && 0 // no need to use this, as 'statvfs' works fine
  1011. if(NSStringAuto _path=UnixPath(path))
  1012. if(NSDictionary *dict=[[NSFileManager defaultManager] attributesOfFileSystemForPath:_path error:nil])
  1013. {
  1014. if(free )*free =[[dict objectForKey:NSFileSystemFreeSize] longLongValue];
  1015. if(total)*total=[[dict objectForKey:NSFileSystemSize ] longLongValue];
  1016. //[dict release]; do not release as it will crash
  1017. return true;
  1018. }
  1019. #elif ANDROID && __ANDROID_API__<21 // on Android, only API 21 and above has 'statvfs', for below we need to use Java
  1020. JNI jni;
  1021. if(jni && ActivityClass)
  1022. if(JMethodID driveSizeFree =jni->GetStaticMethodID(ActivityClass, "driveSizeFree" , "(Ljava/lang/String;)J"))
  1023. if(JMethodID driveSizeTotal=jni->GetStaticMethodID(ActivityClass, "driveSizeTotal", "(Ljava/lang/String;)J"))
  1024. if(JString p=JString(jni, UnixPath(path)))
  1025. {
  1026. Long _free =jni->CallStaticLongMethod(ActivityClass, driveSizeFree , p()),
  1027. _total=jni->CallStaticLongMethod(ActivityClass, driveSizeTotal, p());
  1028. if(_free>=0 && _total>=0)
  1029. {
  1030. if(free )*free =_free;
  1031. if(total)*total=_total;
  1032. return true;
  1033. }
  1034. }
  1035. #else
  1036. struct statvfs stats; if(!statvfs(UnixPathUTF8(path), &stats))
  1037. { // !! need to use 'f_frsize' instead of 'f_bsize' because on Apple it won't return correct results !!
  1038. if(free )*free =ULong(stats.f_frsize)*stats.f_bavail; // use 'f_bavail' instead of 'f_bfree' because that matches what File Managers report about being free on Mac/Linux (on iOS it makes no difference)
  1039. if(total)*total=ULong(stats.f_frsize)*stats.f_blocks;
  1040. return true;
  1041. }
  1042. #endif
  1043. if(free )*free =-1;
  1044. if(total)*total=-1;
  1045. return false;
  1046. }
  1047. /******************************************************************************/
  1048. Bool SafeOverwrite(File &src, C Str &dest, C DateTime *modify_time_utc, Cipher *dest_cipher, C Str &suffix, ReadWriteSync *rws)
  1049. {
  1050. Bool ok=false;
  1051. if(dest.is())
  1052. {
  1053. Bool locked=false;
  1054. Str temp=dest+suffix;
  1055. if(rws && !suffix.is()){rws->enterWrite(); locked=true;} // if there's no suffix then it means we're writing directly to the target, so write lock needs to be enabled at start
  1056. File f; if(f.writeTry(temp, dest_cipher))
  1057. {
  1058. ok=(src.copy(f) && f.flush());
  1059. f.del(); // release file handle so we can set file time/params and rename/remove if needed
  1060. if(ok)
  1061. {
  1062. if(modify_time_utc)FTimeUTC(temp, *modify_time_utc);
  1063. if(suffix.is())
  1064. {
  1065. if(rws){rws->enterWrite(); locked=true;} // lock before renaming
  1066. if(!FRename(temp, dest))
  1067. {
  1068. ok=false;
  1069. UInt attr=FAttrib(dest); if(attr&FATTRIB_READ_ONLY)if(FAttrib(dest, attr&(~FATTRIB_READ_ONLY))) // if failed, then try clearing READ_ONLY attribute
  1070. {
  1071. FAttrib(temp, attr); // keep READ_ONLY on the new file
  1072. ok=FRename(temp, dest);
  1073. }
  1074. }
  1075. }
  1076. }
  1077. if(!ok)FDelFile(temp); // don't leave temp files
  1078. }
  1079. if(locked)rws->leaveWrite();
  1080. }
  1081. return ok;
  1082. }
  1083. Bool SafeOverwrite(FileText &src, C Str &dest, C DateTime *modify_time_utc, Cipher *dest_cipher, C Str &suffix, ReadWriteSync *rws)
  1084. {
  1085. Bool ok=false;
  1086. Long pos=src.pos(); // remember position to restore later
  1087. if(src._f.pos( 0))ok=SafeOverwrite(src._f, dest, modify_time_utc, dest_cipher, suffix, rws);
  1088. src._f.pos(pos); // restore position
  1089. return ok;
  1090. }
  1091. /******************************************************************************/
  1092. Bool CreateSymLink(C Str &name, C Str &target)
  1093. {
  1094. #if APPLE || LINUX
  1095. return !symlink(UnixPathUTF8(target), UnixPathUTF8(name)); // this will fail if 'name' already exists
  1096. #else
  1097. return false;
  1098. #endif
  1099. }
  1100. Str DecodeSymLink(File &f)
  1101. {
  1102. if(f.left()<=MAX_UTF_PATH) // paths should only be in this range
  1103. {
  1104. Char8 s[MAX_UTF_PATH+1]; s[f.getReturnSize(s, MAX_UTF_PATH)]='\0'; return FromUTF8(s);
  1105. }
  1106. return S;
  1107. }
  1108. /******************************************************************************/
  1109. void InitIO()
  1110. {
  1111. #if FILE_MEMB_UNION
  1112. ASSERT(OFFSET(File, _mem)==OFFSET(File, _memb));
  1113. #else
  1114. ASSERT(OFFSET(File, _mem)!=OFFSET(File, _memb));
  1115. #endif
  1116. // Data Path
  1117. _DataPath._d.setNumZero(MAX_LONG_PATH); // allocate string memory up-front, so when changing it later, the CChar* pointer will not be different (to avoid multi-threading issues), use '_d.setNumZero' instead of 'reserve' so that all characters are zero at start
  1118. #if WINDOWS_OLD && DEBUG // automatically set data path when building engine in debug mode
  1119. DataPath(GetPath(_GetPath(_GetPath(GetPath(__FILE__))))+"\\Data"); // set 'DataPath' to the "Engine.pak" "Data" folder which is detected based on relative location of this CPP file
  1120. #endif
  1121. // Current Directory
  1122. #if WEB // WEB has this handled in main
  1123. #elif IOS
  1124. CurDir(App.exe()); // on iOS set path to the application (since on iOS it's a folder, which contains resource files)
  1125. #else
  1126. CurDir(GetPath(App.exe()));
  1127. #endif
  1128. }
  1129. void FlushIO()
  1130. {
  1131. #if WEB
  1132. EM_ASM(FS.syncfs(false, function(err){});); // save data
  1133. #endif
  1134. }
  1135. #if !WINDOWS
  1136. Bool UnixReadFile(CChar8 *file, Char8 *data, Int size)
  1137. {
  1138. Bool ok=false;
  1139. if(data && size>0)
  1140. {
  1141. Int read=0;
  1142. int fd =open(file, O_RDONLY|O_NONBLOCK);
  1143. if( fd>=0)
  1144. {
  1145. read=::read(fd, data, size-1); // leave room for nul character
  1146. if(read>=0 && read<size)ok=true;else read=0;
  1147. close(fd);
  1148. }
  1149. data[read]='\0';
  1150. }
  1151. return ok;
  1152. }
  1153. #endif
  1154. /******************************************************************************/
  1155. }
  1156. /******************************************************************************/