File.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <anki/util/File.h>
  6. #include <anki/util/Filesystem.h>
  7. #include <anki/util/Logger.h>
  8. #include <anki/util/Assert.h>
  9. #include <cstring>
  10. #include <cstdarg>
  11. namespace anki
  12. {
  13. #if ANKI_OS_ANDROID
  14. extern android_app* gAndroidApp;
  15. # define ANKI_AFILE reinterpret_cast<AAsset*>(m_file)
  16. #endif
  17. #define ANKI_CFILE reinterpret_cast<FILE*>(m_file)
  18. File& File::operator=(File&& b)
  19. {
  20. close();
  21. if(b.m_file != nullptr)
  22. {
  23. m_file = b.m_file;
  24. m_type = b.m_type;
  25. m_flags = b.m_flags;
  26. m_size = b.m_size;
  27. }
  28. b.zero();
  29. return *this;
  30. }
  31. File::~File()
  32. {
  33. close();
  34. }
  35. Error File::open(const CString& filename, FileOpenFlag flags)
  36. {
  37. ANKI_ASSERT(m_file == nullptr && m_flags == FileOpenFlag::NONE && m_type == Type::NONE);
  38. // Only these flags are accepted
  39. ANKI_ASSERT((flags
  40. & (FileOpenFlag::READ | FileOpenFlag::WRITE | FileOpenFlag::APPEND | FileOpenFlag::BINARY
  41. | FileOpenFlag::ENDIAN_LITTLE | FileOpenFlag::ENDIAN_BIG))
  42. != FileOpenFlag::NONE);
  43. // Cannot be both
  44. ANKI_ASSERT((flags & FileOpenFlag::READ) != (flags & FileOpenFlag::WRITE));
  45. //
  46. // Determine the file type and open it
  47. //
  48. Array<char, 512> archive;
  49. CString filenameInArchive;
  50. Type ft;
  51. Error err = identifyFile(filename, &archive[0], archive.getSize(), filenameInArchive, ft);
  52. if(!err)
  53. {
  54. switch(ft)
  55. {
  56. #if ANKI_OS_ANDROID
  57. case Type::SPECIAL:
  58. err = openAndroidFile(filename.get(), flags);
  59. break;
  60. #endif
  61. case Type::C:
  62. err = openCFile(filename, flags);
  63. break;
  64. default:
  65. ANKI_ASSERT(0);
  66. }
  67. }
  68. //
  69. // Set endianess
  70. //
  71. if(!err)
  72. {
  73. // If the open() DIDN'T provided us the file endianess
  74. if((flags & (FileOpenFlag::ENDIAN_BIG | FileOpenFlag::ENDIAN_LITTLE)) == FileOpenFlag::NONE)
  75. {
  76. // Set the machine's endianness
  77. m_flags = m_flags | getMachineEndianness();
  78. }
  79. else
  80. {
  81. // Else just make sure that only one of the flags is set
  82. ANKI_ASSERT((flags & FileOpenFlag::ENDIAN_BIG) != (flags & FileOpenFlag::ENDIAN_LITTLE));
  83. }
  84. }
  85. return err;
  86. }
  87. Error File::openCFile(const CString& filename, FileOpenFlag flags)
  88. {
  89. Error err = Error::NONE;
  90. const char* openMode;
  91. if((flags & FileOpenFlag::READ) != FileOpenFlag::NONE)
  92. {
  93. openMode = "rb";
  94. }
  95. else if((flags & FileOpenFlag::APPEND) == FileOpenFlag::APPEND)
  96. {
  97. openMode = "a+b";
  98. }
  99. else if((flags & FileOpenFlag::WRITE) != FileOpenFlag::NONE)
  100. {
  101. openMode = "wb";
  102. }
  103. else
  104. {
  105. ANKI_ASSERT(0);
  106. openMode = nullptr;
  107. }
  108. // Open
  109. m_file = fopen(filename.cstr(), openMode);
  110. if(m_file == nullptr)
  111. {
  112. ANKI_UTIL_LOGE("Failed to open file \"%s\", open mode \"%s\"", &filename[0], openMode);
  113. err = Error::FILE_ACCESS;
  114. }
  115. else
  116. {
  117. m_flags = flags;
  118. m_type = Type::C;
  119. }
  120. // Get file size
  121. if(((flags & FileOpenFlag::READ) != FileOpenFlag::NONE) && !err)
  122. {
  123. fseek(ANKI_CFILE, 0, SEEK_END);
  124. I64 size = ftell(ANKI_CFILE);
  125. if(size < 1)
  126. {
  127. ANKI_UTIL_LOGE("ftell() failed");
  128. err = Error::FUNCTION_FAILED;
  129. }
  130. else
  131. {
  132. m_size = U32(size);
  133. rewind(ANKI_CFILE);
  134. }
  135. }
  136. return err;
  137. }
  138. #if ANKI_OS_ANDROID
  139. Error File::openAndroidFile(const CString& filename, FileOpenFlag flags)
  140. {
  141. if((flags & FileOpenFlag::WRITE) != FileOpenFlag::NONE)
  142. {
  143. ANKI_UTIL_LOGE("Cannot write inside archives");
  144. return Error::FILE_ACCESS;
  145. }
  146. if((flags & FileOpenFlag::READ) != FileOpenFlag::NONE)
  147. {
  148. ANKI_UTIL_LOGE("Missing FileOpenFlag::READ flag");
  149. return Error::FILE_ACCESS;
  150. }
  151. // Open file
  152. ANKI_ASSERT(gAndroidApp != nullptr && gAndroidApp->activity && gAndroidApp->activity->assetManager);
  153. m_file = AAssetManager_open(gAndroidApp->activity->assetManager, &filename[0] + 1, AASSET_MODE_STREAMING);
  154. if(m_file == nullptr)
  155. {
  156. ANKI_UTIL_LOGE("AAssetManager_open() failed");
  157. return Error::FILE_ACCESS;
  158. }
  159. m_flags = flags;
  160. m_type = Type::SPECIAL;
  161. return Error::NONE;
  162. }
  163. #endif
  164. void File::close()
  165. {
  166. if(m_file)
  167. {
  168. if(m_type == Type::C)
  169. {
  170. fclose(ANKI_CFILE);
  171. }
  172. #if ANKI_OS_ANDROID
  173. else if(m_type == Type::SPECIAL)
  174. {
  175. AAsset_close(ANKI_AFILE);
  176. }
  177. #endif
  178. else
  179. {
  180. ANKI_ASSERT(0);
  181. }
  182. }
  183. zero();
  184. }
  185. Error File::flush()
  186. {
  187. ANKI_ASSERT(m_file);
  188. Error err = Error::NONE;
  189. if((m_flags & FileOpenFlag::WRITE) != FileOpenFlag::NONE)
  190. {
  191. if(m_type == Type::C)
  192. {
  193. I ierr = fflush(ANKI_CFILE);
  194. if(ierr)
  195. {
  196. ANKI_UTIL_LOGE("fflush() failed");
  197. err = Error::FUNCTION_FAILED;
  198. }
  199. }
  200. #if ANKI_OS_ANDROID
  201. else if(m_type == Type::SPECIAL)
  202. {
  203. ANKI_ASSERT(0 && "Cannot have write these file types");
  204. }
  205. #endif
  206. else
  207. {
  208. ANKI_ASSERT(0);
  209. }
  210. }
  211. return err;
  212. }
  213. Error File::read(void* buff, PtrSize size)
  214. {
  215. ANKI_ASSERT(buff);
  216. ANKI_ASSERT(size > 0);
  217. ANKI_ASSERT(m_file);
  218. ANKI_ASSERT((m_flags & FileOpenFlag::READ) != FileOpenFlag::NONE);
  219. I64 readSize = 0;
  220. if(m_type == Type::C)
  221. {
  222. readSize = fread(buff, 1, size, ANKI_CFILE);
  223. }
  224. #if ANKI_OS_ANDROID
  225. else if(m_type == Type::SPECIAL)
  226. {
  227. readSize = AAsset_read(ANKI_AFILE, buff, size);
  228. }
  229. #endif
  230. else
  231. {
  232. ANKI_ASSERT(0);
  233. }
  234. Error err = Error::NONE;
  235. if(static_cast<I64>(size) != readSize)
  236. {
  237. ANKI_UTIL_LOGE("File read failed");
  238. err = Error::FILE_ACCESS;
  239. }
  240. return err;
  241. }
  242. PtrSize File::getSize() const
  243. {
  244. ANKI_ASSERT(m_file);
  245. ANKI_ASSERT(m_flags != FileOpenFlag::NONE);
  246. PtrSize out = 0;
  247. if(m_type == Type::C)
  248. {
  249. ANKI_ASSERT(m_size != 0);
  250. out = m_size;
  251. }
  252. #if ANKI_OS_ANDROID
  253. else if(m_type == Type::SPECIAL)
  254. {
  255. out = AAsset_getLength(ANKI_AFILE);
  256. }
  257. #endif
  258. else
  259. {
  260. ANKI_ASSERT(0);
  261. }
  262. return out;
  263. }
  264. Error File::readU32(U32& out)
  265. {
  266. ANKI_ASSERT(m_file);
  267. ANKI_ASSERT(m_flags != FileOpenFlag::NONE);
  268. ANKI_ASSERT((m_flags & FileOpenFlag::READ) != FileOpenFlag::NONE);
  269. ANKI_ASSERT((m_flags & FileOpenFlag::BINARY) != FileOpenFlag::NONE && "Should be binary file");
  270. ANKI_ASSERT((m_flags & FileOpenFlag::ENDIAN_BIG) != (m_flags & FileOpenFlag::ENDIAN_LITTLE)
  271. && "One of those 2 should be active");
  272. Error err = read(&out, sizeof(out));
  273. if(!err)
  274. {
  275. // Copy it
  276. FileOpenFlag machineEndianness = getMachineEndianness();
  277. FileOpenFlag fileEndianness = ((m_flags & FileOpenFlag::ENDIAN_BIG) != FileOpenFlag::NONE)
  278. ? FileOpenFlag::ENDIAN_BIG
  279. : FileOpenFlag::ENDIAN_LITTLE;
  280. if(machineEndianness == fileEndianness)
  281. {
  282. // Same endianness between the file and the machine. Do nothing
  283. }
  284. else if(machineEndianness == FileOpenFlag::ENDIAN_BIG && fileEndianness == FileOpenFlag::ENDIAN_LITTLE)
  285. {
  286. U8* c = reinterpret_cast<U8*>(&out);
  287. out = (c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
  288. }
  289. else
  290. {
  291. U8* c = reinterpret_cast<U8*>(&out);
  292. out = ((c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
  293. }
  294. }
  295. return err;
  296. }
  297. Error File::readF32(F32& out)
  298. {
  299. U32 integer = MAX_U32;
  300. Error err = readU32(integer);
  301. if(!err)
  302. {
  303. memcpy(&out, &integer, sizeof(F32));
  304. }
  305. return err;
  306. }
  307. Error File::write(const void* buff, PtrSize size)
  308. {
  309. ANKI_ASSERT(buff);
  310. ANKI_ASSERT(size > 0);
  311. ANKI_ASSERT(m_file);
  312. ANKI_ASSERT((m_flags & FileOpenFlag::WRITE) != FileOpenFlag::NONE);
  313. Error err = Error::NONE;
  314. if(m_type == Type::C)
  315. {
  316. PtrSize writeSize = 0;
  317. writeSize = std::fwrite(buff, 1, size, ANKI_CFILE);
  318. if(writeSize != size)
  319. {
  320. ANKI_UTIL_LOGE("std::fwrite() failed");
  321. err = Error::FILE_ACCESS;
  322. }
  323. }
  324. #if ANKI_OS_ANDROID
  325. else if(m_type == Type::SPECIAL)
  326. {
  327. ANKI_UTIL_LOGE("Writting to special files is not supported");
  328. err = Error::FILE_ACCESS;
  329. }
  330. #endif
  331. else
  332. {
  333. ANKI_ASSERT(0);
  334. }
  335. return err;
  336. }
  337. Error File::writeText(CString format, ...)
  338. {
  339. ANKI_ASSERT(m_file);
  340. ANKI_ASSERT(m_flags != FileOpenFlag::NONE);
  341. ANKI_ASSERT((m_flags & FileOpenFlag::WRITE) != FileOpenFlag::NONE);
  342. ANKI_ASSERT((m_flags & FileOpenFlag::BINARY) == FileOpenFlag::NONE);
  343. Error err = Error::NONE;
  344. va_list args;
  345. va_start(args, format);
  346. if(m_type == Type::C)
  347. {
  348. std::vfprintf(ANKI_CFILE, &format[0], args);
  349. }
  350. #if ANKI_OS_ANDROID
  351. else if(m_type == Type::SPECIAL)
  352. {
  353. ANKI_UTIL_LOGE("Writting to special files is not supported");
  354. err = Error::FILE_ACCESS;
  355. }
  356. #endif
  357. else
  358. {
  359. ANKI_ASSERT(0);
  360. }
  361. va_end(args);
  362. return err;
  363. }
  364. Error File::seek(PtrSize offset, FileSeekOrigin origin)
  365. {
  366. ANKI_ASSERT(m_file);
  367. ANKI_ASSERT(m_flags != FileOpenFlag::NONE);
  368. Error err = Error::NONE;
  369. if(m_type == Type::C)
  370. {
  371. if(fseek(ANKI_CFILE, offset, I32(origin)) != 0)
  372. {
  373. ANKI_UTIL_LOGE("fseek() failed");
  374. err = Error::FUNCTION_FAILED;
  375. }
  376. }
  377. #if ANKI_OS_ANDROID
  378. else if(m_type == Type::SPECIAL)
  379. {
  380. if(AAsset_seek(ANKI_AFILE, offset, origin) == (off_t)-1)
  381. {
  382. ANKI_UTIL_LOGE("AAsset_seek() failed");
  383. err = Error::FUNCTION_FAILED;
  384. }
  385. }
  386. #endif
  387. else
  388. {
  389. ANKI_ASSERT(0);
  390. }
  391. return err;
  392. }
  393. PtrSize File::tell()
  394. {
  395. ANKI_ASSERT(m_file);
  396. ANKI_ASSERT(m_flags != FileOpenFlag::NONE);
  397. if(m_type == Type::C)
  398. {
  399. return ftell(ANKI_CFILE);
  400. }
  401. #if ANKI_OS_ANDROID
  402. else if(m_type == Type::SPECIAL)
  403. {
  404. ANKI_ASSERT(0);
  405. }
  406. #endif
  407. else
  408. {
  409. ANKI_ASSERT(0);
  410. }
  411. return 0;
  412. }
  413. Error File::identifyFile(const CString& filename, char* archiveFilename, PtrSize archiveFilenameLength,
  414. CString& filenameInArchive, Type& type)
  415. {
  416. Error err = Error::NONE;
  417. #if ANKI_OS_ANDROID
  418. if(filename[0] == '$')
  419. {
  420. type = Type::SPECIAL;
  421. }
  422. else
  423. #endif
  424. {
  425. // It's a C file
  426. type = Type::C;
  427. }
  428. return err;
  429. }
  430. FileOpenFlag File::getMachineEndianness()
  431. {
  432. I32 num = 1;
  433. if(*(char*)&num == 1)
  434. {
  435. return FileOpenFlag::ENDIAN_LITTLE;
  436. }
  437. else
  438. {
  439. return FileOpenFlag::ENDIAN_BIG;
  440. }
  441. }
  442. Error File::readAllText(GenericMemoryPoolAllocator<U8> alloc, String& out)
  443. {
  444. Error err = Error::NONE;
  445. PtrSize size = getSize();
  446. if(size != 0)
  447. {
  448. out.create(alloc, '?', size);
  449. err = read(&out[0], size);
  450. }
  451. else
  452. {
  453. err = Error::FUNCTION_FAILED;
  454. }
  455. return err;
  456. }
  457. Error File::readAllText(StringAuto& out)
  458. {
  459. Error err = Error::NONE;
  460. PtrSize size = getSize();
  461. if(size != 0)
  462. {
  463. out.create('?', size);
  464. err = read(&out[0], size);
  465. }
  466. else
  467. {
  468. err = Error::FUNCTION_FAILED;
  469. }
  470. return err;
  471. }
  472. } // end namespace anki