625 行
12 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. #if __ANDROID__
  14. # include <sys/types.h>
  15. # include <android/asset_manager_jni.h>
  16. #endif
  17. #if defined(_WIN32)
  18. # define WIN32_LEAN_AND_MEAN 1
  19. # include <windows.h>
  20. # undef WIN32_LEAN_AND_MEAN
  21. #else
  22. # include <dirent.h>
  23. #endif
  24. #if defined HAVE_UNISTD_H
  25. # include <unistd.h>
  26. #endif
  27. #include <atomic>
  28. #include <string>
  29. #include <algorithm>
  30. #include <sys/stat.h>
  31. namespace lol
  32. {
  33. #if __ANDROID__
  34. extern AAssetManager *g_assets;
  35. #endif
  36. //---------------
  37. class FileData
  38. {
  39. friend class File;
  40. FileData()
  41. : m_refcount(0),
  42. m_type(StreamType::File)
  43. { }
  44. void Open(StreamType stream)
  45. {
  46. if (m_type == StreamType::File ||
  47. m_type == StreamType::FileBinary)
  48. return;
  49. m_type = stream;
  50. switch (stream.ToScalar())
  51. {
  52. #if __ANDROID__
  53. /* FIXME: no modes, no error checking, no nothing */
  54. #elif HAVE_STDIO_H
  55. case StreamType::StdIn: m_fd = stdin; break;
  56. case StreamType::StdOut: m_fd = stdout; break;
  57. case StreamType::StdErr: m_fd = stderr; break;
  58. #endif
  59. default: break;
  60. }
  61. }
  62. void Open(std::string const &file, FileAccess mode, bool force_binary)
  63. {
  64. m_type = (force_binary) ? (StreamType::FileBinary) : (StreamType::File);
  65. #if __ANDROID__
  66. ASSERT(g_assets);
  67. m_asset = AAssetManager_open(g_assets, file.c_str(), AASSET_MODE_UNKNOWN);
  68. #elif HAVE_STDIO_H
  69. /* FIXME: no modes, no error checking, no nothing */
  70. stat(file.c_str(), &m_stat);
  71. std::string access(mode == FileAccess::Write ? "w" : "r");
  72. if (force_binary)
  73. access += "b";
  74. m_fd = fopen(file.c_str(), access.c_str());
  75. #endif
  76. }
  77. inline bool IsValid() const
  78. {
  79. #if __ANDROID__
  80. return !!m_asset;
  81. #elif HAVE_STDIO_H
  82. return !!m_fd;
  83. #else
  84. return false;
  85. #endif
  86. }
  87. void Close()
  88. {
  89. if (m_type != StreamType::File &&
  90. m_type != StreamType::FileBinary)
  91. return;
  92. #if __ANDROID__
  93. if (m_asset)
  94. AAsset_close(m_asset);
  95. m_asset = nullptr;
  96. #elif HAVE_STDIO_H
  97. if (m_fd)
  98. fclose(m_fd);
  99. m_fd = nullptr;
  100. #endif
  101. }
  102. int Read(uint8_t *buf, int count)
  103. {
  104. #if __ANDROID__
  105. return AAsset_read(m_asset, buf, count);
  106. #elif HAVE_STDIO_H
  107. size_t done = fread(buf, 1, count, m_fd);
  108. if (done <= 0)
  109. return -1;
  110. return (int)done;
  111. #else
  112. return 0;
  113. #endif
  114. }
  115. std::string ReadString()
  116. {
  117. array<uint8_t> buf;
  118. buf.resize(BUFSIZ);
  119. std::string ret;
  120. while (IsValid())
  121. {
  122. int done = Read(&buf[0], buf.count());
  123. if (done <= 0)
  124. break;
  125. int oldsize = ret.length();
  126. ret.resize(oldsize + done);
  127. memcpy(&ret[oldsize], &buf[0], done);
  128. buf.resize(buf.count() * 3 / 2);
  129. }
  130. return ret;
  131. }
  132. int Write(void const *buf, int count)
  133. {
  134. #if __ANDROID__
  135. //return AAsset_read(m_asset, buf, count);
  136. return 0;
  137. #elif HAVE_STDIO_H
  138. size_t done = fwrite(buf, 1, count, m_fd);
  139. if (done <= 0)
  140. return -1;
  141. return done;
  142. #else
  143. return 0;
  144. #endif
  145. }
  146. long int GetPosFromStart()
  147. {
  148. #if __ANDROID__
  149. return 0;
  150. #elif HAVE_STDIO_H
  151. return ftell(m_fd);
  152. #else
  153. return 0;
  154. #endif
  155. }
  156. void SetPosFromStart(long int pos)
  157. {
  158. #if __ANDROID__
  159. //NOT IMPLEMENTED
  160. #elif HAVE_STDIO_H
  161. fseek(m_fd, pos, SEEK_SET);
  162. #else
  163. //NOT IMPLEMENTED
  164. #endif
  165. }
  166. long int size()
  167. {
  168. #if __ANDROID__
  169. return 0;
  170. #elif HAVE_STDIO_H
  171. return m_stat.st_size;
  172. #else
  173. return 0;
  174. #endif
  175. }
  176. long int GetModificationTime()
  177. {
  178. #if __ANDROID__
  179. return 0;
  180. #elif HAVE_STDIO_H
  181. return (long int)m_stat.st_mtime;
  182. #else
  183. return 0;
  184. #endif
  185. }
  186. //-----------------------
  187. #if __ANDROID__
  188. AAsset *m_asset;
  189. #elif HAVE_STDIO_H
  190. FILE *m_fd;
  191. #endif
  192. std::atomic<int> m_refcount;
  193. StreamType m_type;
  194. struct stat m_stat;
  195. };
  196. //-- FILE --
  197. File::File()
  198. : m_data(new FileData)
  199. {
  200. ++m_data->m_refcount;
  201. }
  202. //--
  203. File::File(File const &that)
  204. : m_data(that.m_data)
  205. {
  206. ++m_data->m_refcount;
  207. }
  208. //--
  209. File &File::operator =(File const &that)
  210. {
  211. if (this == &that)
  212. return *this;
  213. /* FIXME: this needs auditing */
  214. int refcount = --m_data->m_refcount;
  215. if (refcount == 0)
  216. {
  217. m_data->Close();
  218. delete m_data;
  219. }
  220. m_data = that.m_data;
  221. ++m_data->m_refcount;
  222. return *this;
  223. }
  224. //--
  225. File::~File()
  226. {
  227. int refcount = --m_data->m_refcount;
  228. if (refcount == 0)
  229. {
  230. m_data->Close();
  231. delete m_data;
  232. }
  233. }
  234. //--
  235. void File::Open(StreamType stream)
  236. {
  237. m_data->Open(stream);
  238. }
  239. //--
  240. void File::Open(std::string const &file, FileAccess mode, bool force_binary)
  241. {
  242. m_data->Open(file, mode, force_binary);
  243. }
  244. //--
  245. bool File::IsValid() const
  246. {
  247. return m_data->IsValid();
  248. }
  249. //--
  250. void File::Close()
  251. {
  252. m_data->Close();
  253. }
  254. //--
  255. int File::Read(uint8_t *buf, int count)
  256. {
  257. return m_data->Read(buf, count);
  258. }
  259. //--
  260. std::string File::ReadString()
  261. {
  262. return m_data->ReadString();
  263. }
  264. //--
  265. int File::Write(void const *buf, int count)
  266. {
  267. return m_data->Write(buf, count);
  268. }
  269. //--
  270. int File::Write(std::string const &buf)
  271. {
  272. return m_data->Write(buf.c_str(), buf.length());
  273. }
  274. //--
  275. long int File::GetPosFromStart()
  276. {
  277. return m_data->GetPosFromStart();
  278. }
  279. //--
  280. void File::SetPosFromStart(long int pos)
  281. {
  282. m_data->SetPosFromStart(pos);
  283. }
  284. //--
  285. long int File::size()
  286. {
  287. return m_data->size();
  288. }
  289. //--
  290. long int File::GetModificationTime()
  291. {
  292. return m_data->GetModificationTime();
  293. }
  294. //---------------
  295. class DirectoryData
  296. {
  297. friend class Directory;
  298. DirectoryData() : m_type(StreamType::File)
  299. {
  300. #if __ANDROID__
  301. /* FIXME: not implemented */
  302. #elif defined(_WIN32)
  303. m_handle = INVALID_HANDLE_VALUE;
  304. #elif HAVE_STDIO_H
  305. m_dd = nullptr;
  306. #endif
  307. }
  308. void Open(std::string const &directory, FileAccess mode)
  309. {
  310. UNUSED(mode); /* FIXME */
  311. m_type = StreamType::File;
  312. #if __ANDROID__
  313. /* FIXME: not implemented */
  314. #elif defined(_WIN32)
  315. m_directory = directory;
  316. std::string filter = m_directory + "*";
  317. std::replace(filter.begin(), filter.end(), '/', '\\');
  318. WIN32_FIND_DATA FindFileData;
  319. m_handle = FindFirstFile(filter.c_str(), &FindFileData);
  320. stat(directory.c_str(), &m_stat);
  321. #elif HAVE_STDIO_H
  322. m_dd = opendir(directory.c_str());
  323. stat(directory.c_str(), &m_stat);
  324. #endif
  325. }
  326. void Close()
  327. {
  328. if (m_type != StreamType::File)
  329. return;
  330. if (IsValid())
  331. {
  332. #if __ANDROID__
  333. /* FIXME: not implemented */
  334. #elif defined(_WIN32)
  335. FindClose(m_handle);
  336. #elif HAVE_STDIO_H
  337. closedir(m_dd);
  338. #endif
  339. }
  340. #if __ANDROID__
  341. /* FIXME: not implemented */
  342. #elif defined(_WIN32)
  343. m_handle = INVALID_HANDLE_VALUE;
  344. #elif HAVE_STDIO_H
  345. m_dd = nullptr;
  346. #endif
  347. }
  348. bool GetContentList(array<std::string>* files, array<std::string>* directories)
  349. {
  350. if (!IsValid())
  351. return false;
  352. #if __ANDROID__
  353. /* FIXME: not implemented */
  354. #elif defined(_WIN32)
  355. std::string filter = m_directory + "*";
  356. std::replace(filter.begin(), filter.end(), '/', '\\');
  357. WIN32_FIND_DATA find_data;
  358. HANDLE handle = FindFirstFile(filter.c_str(), &find_data);
  359. bool file_valid = (handle != INVALID_HANDLE_VALUE);
  360. while (file_valid)
  361. {
  362. if (find_data.cFileName[0] != '.')
  363. {
  364. // We have a directory
  365. if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  366. {
  367. if (directories)
  368. *directories << std::string(find_data.cFileName);
  369. }
  370. else
  371. {
  372. if (files)
  373. *files << std::string(find_data.cFileName);
  374. }
  375. }
  376. //Go for next one
  377. file_valid = !!FindNextFile(m_handle, &find_data);
  378. }
  379. #elif HAVE_STDIO_H
  380. /* FIXME: not implemented */
  381. #endif
  382. return ((files && files->count()) || (directories && directories->count()));
  383. }
  384. inline bool IsValid() const
  385. {
  386. #if __ANDROID__
  387. /* FIXME: not implemented */
  388. #elif defined(_WIN32)
  389. return (m_handle != INVALID_HANDLE_VALUE);
  390. #elif HAVE_STDIO_H
  391. return !!m_dd;
  392. #else
  393. return false;
  394. #endif
  395. }
  396. long int GetModificationTime()
  397. {
  398. #if __ANDROID__
  399. return 0;
  400. #elif HAVE_STDIO_H
  401. return (long int)m_stat.st_mtime;
  402. #else
  403. return 0;
  404. #endif
  405. }
  406. #if __ANDROID__
  407. /* FIXME: not implemented */
  408. #elif defined(_WIN32)
  409. HANDLE m_handle;
  410. std::string m_directory;
  411. #elif HAVE_STDIO_H
  412. DIR *m_dd;
  413. #endif
  414. std::atomic<int> m_refcount;
  415. StreamType m_type;
  416. struct stat m_stat;
  417. };
  418. //-- DIRECTORY --
  419. Directory::Directory(std::string const &name)
  420. : m_data(new DirectoryData),
  421. m_name(name + "/")
  422. {
  423. ++m_data->m_refcount;
  424. }
  425. //--
  426. Directory::Directory(Directory const &that)
  427. : m_data(that.m_data),
  428. m_name(that.m_name)
  429. {
  430. ++m_data->m_refcount;
  431. }
  432. //--
  433. Directory &Directory::operator =(Directory const &that)
  434. {
  435. if (this == &that)
  436. return *this;
  437. /* FIXME: this needs auditing */
  438. int refcount = --m_data->m_refcount;
  439. if (refcount == 0)
  440. {
  441. m_data->Close();
  442. delete m_data;
  443. }
  444. m_data = that.m_data;
  445. m_name = that.m_name;
  446. ++m_data->m_refcount;
  447. return *this;
  448. }
  449. //--
  450. Directory::~Directory()
  451. {
  452. int refcount = --m_data->m_refcount;
  453. if (refcount == 0)
  454. {
  455. m_data->Close();
  456. delete m_data;
  457. }
  458. }
  459. //--
  460. void Directory::Open(FileAccess mode)
  461. {
  462. return m_data->Open(m_name, mode);
  463. }
  464. //--
  465. bool Directory::IsValid() const
  466. {
  467. return m_data->IsValid();
  468. }
  469. //--
  470. void Directory::Close()
  471. {
  472. m_data->Close();
  473. }
  474. //--
  475. bool Directory::GetContent(array<std::string>* files, array<Directory>* directories)
  476. {
  477. array<std::string> sfiles, sdirectories;
  478. bool found_some = m_data->GetContentList(&sfiles, &sdirectories);
  479. UNUSED(found_some);
  480. if (directories)
  481. for (int i = 0; i < sdirectories.count(); i++)
  482. directories->push(Directory(m_name + sdirectories[i]));
  483. if (files)
  484. for (int i = 0; i < sfiles.count(); i++)
  485. files->push(m_name + sfiles[i]);
  486. return (files && files->count()) || (directories || directories->count());
  487. }
  488. //--
  489. bool Directory::GetContent(array<std::string>& files, array<Directory>& directories)
  490. {
  491. return GetContent(&files, &directories);
  492. }
  493. //--
  494. bool Directory::GetContent(array<Directory>& directories)
  495. {
  496. return GetContent(nullptr, &directories);
  497. }
  498. //--
  499. bool Directory::GetContent(array<std::string>& files)
  500. {
  501. return GetContent(&files, nullptr);
  502. }
  503. //--
  504. std::string Directory::GetName()
  505. {
  506. return m_name;
  507. }
  508. //--
  509. long int Directory::GetModificationTime()
  510. {
  511. return m_data->GetModificationTime();
  512. }
  513. //--
  514. std::string Directory::GetCurrent()
  515. {
  516. std::string ret;
  517. #if __ANDROID__
  518. /* FIXME: not implemented */
  519. #elif defined(_WIN32)
  520. TCHAR buff[MAX_PATH * 2];
  521. GetCurrentDirectory(MAX_PATH, buff);
  522. ret = buff;
  523. std::replace(ret.begin(), ret.end(), '\\', '/');
  524. #elif HAVE_STDIO_H
  525. /* FIXME: not implemented */
  526. #endif
  527. return ret;
  528. }
  529. //--
  530. bool Directory::SetCurrent(std::string directory)
  531. {
  532. #if __ANDROID__
  533. /* FIXME: not implemented */
  534. #elif defined(_WIN32)
  535. std::string result = directory;
  536. std::replace(result.begin(), result.end(), '/', '\\');
  537. return !!SetCurrentDirectory(result.c_str());
  538. #elif HAVE_UNISTD_H
  539. chdir(directory.c_str());
  540. #endif
  541. return false;
  542. }
  543. } /* namespace lol */