FileSystem.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. #include "Base.h"
  2. #include "FileSystem.h"
  3. #include "Properties.h"
  4. #include "Stream.h"
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #ifdef WIN32
  8. #include <windows.h>
  9. #include <tchar.h>
  10. #include <stdio.h>
  11. #include <direct.h>
  12. #define gp_stat _stat
  13. #define gp_stat_struct struct stat
  14. #else
  15. #define __EXT_POSIX2
  16. #include <libgen.h>
  17. #include <dirent.h>
  18. #define gp_stat stat
  19. #define gp_stat_struct struct stat
  20. #endif
  21. #ifdef __ANDROID__
  22. #include <android/asset_manager.h>
  23. extern AAssetManager* __assetManager;
  24. #endif
  25. namespace gameplay
  26. {
  27. #ifdef __ANDROID__
  28. #include <unistd.h>
  29. static void makepath(std::string path, int mode)
  30. {
  31. std::vector<std::string> dirs;
  32. while (path.length() > 0)
  33. {
  34. int index = path.find('/');
  35. std::string dir = (index == -1 ) ? path : path.substr(0, index);
  36. if (dir.length() > 0)
  37. dirs.push_back(dir);
  38. if (index + 1 >= path.length() || index == -1)
  39. break;
  40. path = path.substr(index + 1);
  41. }
  42. struct stat s;
  43. std::string dirPath;
  44. for (unsigned int i = 0; i < dirs.size(); i++)
  45. {
  46. dirPath += "/";
  47. dirPath += dirs[i];
  48. if (stat(dirPath.c_str(), &s) != 0)
  49. {
  50. // Directory does not exist.
  51. if (mkdir(dirPath.c_str(), 0777) != 0)
  52. {
  53. GP_ERROR("Failed to create directory: '%s'", dirPath.c_str());
  54. return;
  55. }
  56. }
  57. }
  58. return;
  59. }
  60. /**
  61. * Returns true if the file exists in the android read-only asset directory.
  62. */
  63. static bool androidFileExists(const char* filePath)
  64. {
  65. AAsset* asset = AAssetManager_open(__assetManager, filePath, AASSET_MODE_RANDOM);
  66. if (asset)
  67. {
  68. int lenght = AAsset_getLength(asset);
  69. AAsset_close(asset);
  70. return length > 0;
  71. }
  72. return false;
  73. }
  74. #endif
  75. /** @script{ignore} */
  76. static std::string __resourcePath("./");
  77. static std::map<std::string, std::string> __aliases;
  78. /**
  79. * Gets the fully resolved path.
  80. * If the path is relative then it will be prefixed with the resource path.
  81. * Aliases will be converted to a relative path.
  82. *
  83. * @param path The path to resolve.
  84. * @param fullPath The full resolved path. (out param)
  85. */
  86. static void getFullPath(const char* path, std::string& fullPath)
  87. {
  88. if (FileSystem::isAbsolutePath(path))
  89. {
  90. fullPath.assign(path);
  91. }
  92. else
  93. {
  94. fullPath.assign(__resourcePath);
  95. fullPath += FileSystem::resolvePath(path);
  96. }
  97. }
  98. /**
  99. *
  100. * @script{ignore}
  101. */
  102. class FileStream : public Stream
  103. {
  104. public:
  105. friend class FileSystem;
  106. ~FileStream();
  107. virtual bool canRead();
  108. virtual bool canWrite();
  109. virtual bool canSeek();
  110. virtual void close();
  111. virtual size_t read(void* ptr, size_t size, size_t count);
  112. virtual char* readLine(char* str, int num);
  113. virtual size_t write(const void* ptr, size_t size, size_t count);
  114. virtual bool eof();
  115. virtual size_t length();
  116. virtual long int position();
  117. virtual bool seek(long int offset, int origin);
  118. virtual bool rewind();
  119. static FileStream* create(const char* filePath, const char* mode);
  120. private:
  121. FileStream(FILE* file);
  122. private:
  123. FILE* _file;
  124. bool _canRead;
  125. bool _canWrite;
  126. };
  127. #ifdef __ANDROID__
  128. /**
  129. *
  130. * @script{ignore}
  131. */
  132. class FileStreamAndroid : public Stream
  133. {
  134. public:
  135. friend class FileSystem;
  136. ~FileStreamAndroid();
  137. virtual bool canRead();
  138. virtual bool canWrite();
  139. virtual bool canSeek();
  140. virtual void close();
  141. virtual size_t read(void* ptr, size_t size, size_t count);
  142. virtual char* readLine(char* str, int num);
  143. virtual size_t write(const void* ptr, size_t size, size_t count);
  144. virtual bool eof();
  145. virtual size_t length();
  146. virtual long int position();
  147. virtual bool seek(long int offset, int origin);
  148. virtual bool rewind();
  149. static FileStreamAndroid* create(const char* filePath, const char* mode);
  150. private:
  151. FileStreamAndroid(AAsset* asset);
  152. private:
  153. AAsset* _asset;
  154. };
  155. #endif
  156. /////////////////////////////
  157. FileSystem::FileSystem()
  158. {
  159. }
  160. FileSystem::~FileSystem()
  161. {
  162. }
  163. void FileSystem::setResourcePath(const char* path)
  164. {
  165. __resourcePath = path == NULL ? "" : path;
  166. }
  167. const char* FileSystem::getResourcePath()
  168. {
  169. return __resourcePath.c_str();
  170. }
  171. void FileSystem::loadResourceAliases(const char* aliasFilePath)
  172. {
  173. Properties* properties = Properties::create(aliasFilePath);
  174. if (properties)
  175. {
  176. Properties* aliases;
  177. while ((aliases = properties->getNextNamespace()) != NULL)
  178. {
  179. loadResourceAliases(aliases);
  180. }
  181. }
  182. SAFE_DELETE(properties);
  183. }
  184. void FileSystem::loadResourceAliases(Properties* properties)
  185. {
  186. assert(properties);
  187. const char* name;
  188. while ((name = properties->getNextProperty()) != NULL)
  189. {
  190. __aliases[name] = properties->getString();
  191. }
  192. }
  193. const char* FileSystem::resolvePath(const char* path)
  194. {
  195. GP_ASSERT(path);
  196. size_t len = strlen(path);
  197. if (len > 1 && path[0] == '@')
  198. {
  199. std::string alias(path + 1);
  200. std::map<std::string, std::string>::const_iterator itr = __aliases.find(alias);
  201. if (itr == __aliases.end())
  202. return path; // no matching alias found
  203. return itr->second.c_str();
  204. }
  205. return path;
  206. }
  207. bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
  208. {
  209. #ifdef WIN32
  210. std::string path(FileSystem::getResourcePath());
  211. if (dirPath && strlen(dirPath) > 0)
  212. {
  213. path.append(dirPath);
  214. }
  215. path.append("/*");
  216. // Convert char to wchar
  217. std::basic_string<TCHAR> wPath;
  218. wPath.assign(path.begin(), path.end());
  219. WIN32_FIND_DATA FindFileData;
  220. HANDLE hFind = FindFirstFile(wPath.c_str(), &FindFileData);
  221. if (hFind == INVALID_HANDLE_VALUE)
  222. {
  223. return false;
  224. }
  225. do
  226. {
  227. // Add to the list if this is not a directory
  228. if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  229. {
  230. // Convert wchar to char
  231. std::basic_string<TCHAR> wfilename(FindFileData.cFileName);
  232. std::string filename;
  233. filename.assign(wfilename.begin(), wfilename.end());
  234. files.push_back(filename);
  235. }
  236. } while (FindNextFile(hFind, &FindFileData) != 0);
  237. FindClose(hFind);
  238. return true;
  239. #else
  240. std::string path(FileSystem::getResourcePath());
  241. if (dirPath && strlen(dirPath) > 0)
  242. {
  243. path.append(dirPath);
  244. }
  245. path.append("/.");
  246. bool result = false;
  247. struct dirent* dp;
  248. DIR* dir = opendir(path.c_str());
  249. if (dir != NULL)
  250. {
  251. while ((dp = readdir(dir)) != NULL)
  252. {
  253. std::string filepath(path);
  254. filepath.append("/");
  255. filepath.append(dp->d_name);
  256. struct stat buf;
  257. if (!stat(filepath.c_str(), &buf))
  258. {
  259. // Add to the list if this is not a directory
  260. if (!S_ISDIR(buf.st_mode))
  261. {
  262. files.push_back(dp->d_name);
  263. }
  264. }
  265. }
  266. closedir(dir);
  267. result = true;
  268. }
  269. #ifdef __ANDROID__
  270. // List the files that are in the android APK at this path
  271. AAssetDir* assetDir = AAssetManager_openDir(__assetManager, dirPath);
  272. if (assetDir != NULL)
  273. {
  274. AAssetDir_rewind(assetDir);
  275. const char* file = NULL;
  276. while ((file = AAssetDir_getNextFileName(assetDir)) != NULL)
  277. {
  278. std::string filename(file);
  279. // Check if this file was already added to the list because it was copied to the SD card.
  280. if (find(files.begin(), files.end(), filename) == files.end())
  281. {
  282. files.push_back(filename);
  283. }
  284. }
  285. AAssetDir_close(assetDir);
  286. result = true;
  287. }
  288. #endif
  289. return result;
  290. #endif
  291. }
  292. bool FileSystem::fileExists(const char* filePath)
  293. {
  294. GP_ASSERT(filePath);
  295. #ifdef __ANDROID__
  296. if (androidFileExists(filePath))
  297. {
  298. return true;
  299. }
  300. #endif
  301. std::string fullPath;
  302. getFullPath(filePath, fullPath);
  303. gp_stat_struct s;
  304. #ifdef WIN32
  305. if (!isAbsolutePath(filePath) && stat(fullPath.c_str(), &s) != 0)
  306. {
  307. fullPath = __resourcePath;
  308. fullPath += "../../gameplay/";
  309. fullPath += filePath;
  310. int result = stat(fullPath.c_str(), &s);
  311. if (result != 0)
  312. {
  313. fullPath = __resourcePath;
  314. fullPath += "../gameplay/";
  315. fullPath += filePath;
  316. return stat(fullPath.c_str(), &s) == 0;
  317. }
  318. }
  319. return true;
  320. #else
  321. return stat(fullPath.c_str(), &s) == 0;
  322. #endif
  323. }
  324. Stream* FileSystem::open(const char* path, size_t mode)
  325. {
  326. char modeStr[] = "rb";
  327. if ((mode & WRITE) != 0)
  328. modeStr[0] = 'w';
  329. #ifdef __ANDROID__
  330. if ((mode & WRITE) != 0)
  331. {
  332. // Open a file on the SD card
  333. std::string fullPath(__resourcePath);
  334. fullPath += resolvePath(path);
  335. size_t index = fullPath.rfind('/');
  336. if (index != std::string::npos)
  337. {
  338. std::string directoryPath = fullPath.substr(0, index);
  339. struct stat s;
  340. if (stat(directoryPath.c_str(), &s) != 0)
  341. makepath(directoryPath, 0777);
  342. }
  343. return FileStream::create(fullPath.c_str(), modeStr);
  344. }
  345. else
  346. {
  347. // Open a file in the read-only asset directory
  348. return FileStreamAndroid::create(resolvePath(path), modeStr);
  349. }
  350. #else
  351. std::string fullPath;
  352. getFullPath(path, fullPath);
  353. #ifdef WIN32
  354. gp_stat_struct s;
  355. if (!isAbsolutePath(path) && stat(fullPath.c_str(), &s) != 0 && (mode & WRITE) == 0)
  356. {
  357. fullPath = __resourcePath;
  358. fullPath += "../../gameplay/";
  359. fullPath += path;
  360. int result = stat(fullPath.c_str(), &s);
  361. if (result != 0)
  362. {
  363. fullPath = __resourcePath;
  364. fullPath += "../gameplay/";
  365. fullPath += path;
  366. if (stat(fullPath.c_str(), &s) != 0)
  367. {
  368. return NULL;
  369. }
  370. }
  371. }
  372. #endif
  373. FileStream* stream = FileStream::create(fullPath.c_str(), modeStr);
  374. return stream;
  375. #endif
  376. }
  377. FILE* FileSystem::openFile(const char* filePath, const char* mode)
  378. {
  379. GP_ASSERT(filePath);
  380. GP_ASSERT(mode);
  381. std::string fullPath;
  382. getFullPath(filePath, fullPath);
  383. createFileFromAsset(filePath);
  384. FILE* fp = fopen(fullPath.c_str(), mode);
  385. #ifdef WIN32
  386. if (fp == NULL && !isAbsolutePath(filePath))
  387. {
  388. fullPath = __resourcePath;
  389. fullPath += "../../gameplay/";
  390. fullPath += filePath;
  391. fp = fopen(fullPath.c_str(), mode);
  392. if (!fp)
  393. {
  394. fullPath = __resourcePath;
  395. fullPath += "../gameplay/";
  396. fullPath += filePath;
  397. fp = fopen(fullPath.c_str(), mode);
  398. }
  399. }
  400. #endif
  401. return fp;
  402. }
  403. char* FileSystem::readAll(const char* filePath, int* fileSize)
  404. {
  405. GP_ASSERT(filePath);
  406. // Open file for reading.
  407. std::auto_ptr<Stream> stream(open(filePath));
  408. if (stream.get() == NULL)
  409. {
  410. GP_ERROR("Failed to load file: %s", filePath);
  411. return NULL;
  412. }
  413. size_t size = stream->length();
  414. // Read entire file contents.
  415. char* buffer = new char[size + 1];
  416. size_t read = stream->read(buffer, 1, size);
  417. if (read != size)
  418. {
  419. GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %u < %u).", filePath, read, size);
  420. SAFE_DELETE_ARRAY(buffer);
  421. return NULL;
  422. }
  423. // Force the character buffer to be NULL-terminated.
  424. buffer[size] = '\0';
  425. if (fileSize)
  426. {
  427. *fileSize = (int)size;
  428. }
  429. return buffer;
  430. }
  431. bool FileSystem::isAbsolutePath(const char* filePath)
  432. {
  433. if (filePath == 0 || filePath[0] == '\0')
  434. return false;
  435. #ifdef WIN32
  436. if (filePath[1] != '\0')
  437. {
  438. char first = filePath[0];
  439. return (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')));
  440. }
  441. return false;
  442. #else
  443. return filePath[0] == '/';
  444. #endif
  445. }
  446. void FileSystem::createFileFromAsset(const char* path)
  447. {
  448. #ifdef __ANDROID__
  449. static std::set<std::string> upToDateAssets;
  450. GP_ASSERT(path);
  451. std::string fullPath(__resourcePath);
  452. std::string resolvedPath = FileSystem::resolvePath(path);
  453. fullPath += resolvedPath;
  454. std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
  455. struct stat s;
  456. if (stat(directoryPath.c_str(), &s) != 0)
  457. makepath(directoryPath, 0777);
  458. // To ensure that the files on the file system corresponding to the assets in the APK bundle
  459. // are always up to date (and in sync), we copy them from the APK to the file system once
  460. // for each time the process (game) runs.
  461. if (upToDateAssets.find(fullPath) == upToDateAssets.end())
  462. {
  463. AAsset* asset = AAssetManager_open(__assetManager, resolvedPath.c_str(), AASSET_MODE_RANDOM);
  464. if (asset)
  465. {
  466. const void* data = AAsset_getBuffer(asset);
  467. int length = AAsset_getLength(asset);
  468. FILE* file = fopen(fullPath.c_str(), "wb");
  469. if (file != NULL)
  470. {
  471. int ret = fwrite(data, sizeof(unsigned char), length, file);
  472. if (fclose(file) != 0)
  473. {
  474. GP_ERROR("Failed to close file on file system created from APK asset '%s'.", path);
  475. return;
  476. }
  477. if (ret != length)
  478. {
  479. GP_ERROR("Failed to write all data from APK asset '%s' to file on file system.", path);
  480. return;
  481. }
  482. }
  483. else
  484. {
  485. GP_ERROR("Failed to create file on file system from APK asset '%s'.", path);
  486. return;
  487. }
  488. upToDateAssets.insert(fullPath);
  489. }
  490. }
  491. #endif
  492. }
  493. std::string FileSystem::getDirectoryName(const char* path)
  494. {
  495. if (path == NULL || strlen(path) == 0)
  496. {
  497. return "";
  498. }
  499. #ifdef WIN32
  500. char drive[_MAX_DRIVE];
  501. char dir[_MAX_DIR];
  502. _splitpath(path, drive, dir, NULL, NULL);
  503. std::string dirname;
  504. size_t driveLength = strlen(drive);
  505. if (driveLength > 0)
  506. {
  507. dirname.reserve(driveLength + strlen(dir));
  508. dirname.append(drive);
  509. dirname.append(dir);
  510. }
  511. else
  512. {
  513. dirname.assign(dir);
  514. }
  515. std::replace(dirname.begin(), dirname.end(), '\\', '/');
  516. return dirname;
  517. #else
  518. // dirname() modifies the input string so create a temp string
  519. std::string dirname;
  520. char* tempPath = new char[strlen(path) + 1];
  521. strcpy(tempPath, path);
  522. char* dir = ::dirname(tempPath);
  523. if (dir && strlen(dir) > 0)
  524. {
  525. dirname.assign(dir);
  526. // dirname() strips off the trailing '/' so add it back to be consistent with Windows
  527. dirname.append("/");
  528. }
  529. SAFE_DELETE_ARRAY(tempPath);
  530. return dirname;
  531. #endif
  532. }
  533. std::string FileSystem::getExtension(const char* path)
  534. {
  535. const char* str = strrchr(path, '.');
  536. if (str == NULL)
  537. return "";
  538. std::string ext;
  539. size_t len = strlen(str);
  540. for (size_t i = 0; i < len; ++i)
  541. ext += std::toupper(str[i]);
  542. return ext;
  543. }
  544. //////////////////
  545. FileStream::FileStream(FILE* file)
  546. : _file(file), _canRead(false), _canWrite(false)
  547. {
  548. }
  549. FileStream::~FileStream()
  550. {
  551. if (_file)
  552. {
  553. close();
  554. }
  555. }
  556. FileStream* FileStream::create(const char* filePath, const char* mode)
  557. {
  558. FILE* file = fopen(filePath, mode);
  559. if (file)
  560. {
  561. FileStream* stream = new FileStream(file);
  562. const char* s = mode;
  563. while (s != NULL && *s != '\0')
  564. {
  565. if (*s == 'r')
  566. stream->_canRead = true;
  567. else if (*s == 'w')
  568. stream->_canWrite = true;
  569. ++s;
  570. }
  571. return stream;
  572. }
  573. return NULL;
  574. }
  575. bool FileStream::canRead()
  576. {
  577. return _file && _canRead;
  578. }
  579. bool FileStream::canWrite()
  580. {
  581. return _file && _canWrite;
  582. }
  583. bool FileStream::canSeek()
  584. {
  585. return _file != NULL;
  586. }
  587. void FileStream::close()
  588. {
  589. if (_file)
  590. fclose(_file);
  591. _file = NULL;
  592. }
  593. size_t FileStream::read(void* ptr, size_t size, size_t count)
  594. {
  595. if (!_file)
  596. return 0;
  597. return fread(ptr, size, count, _file);
  598. }
  599. char* FileStream::readLine(char* str, int num)
  600. {
  601. if (!_file)
  602. return 0;
  603. return fgets(str, num, _file);
  604. }
  605. size_t FileStream::write(const void* ptr, size_t size, size_t count)
  606. {
  607. if (!_file)
  608. return 0;
  609. return fwrite(ptr, size, count, _file);
  610. }
  611. bool FileStream::eof()
  612. {
  613. if (!_file || feof(_file))
  614. return true;
  615. return ((size_t)position()) >= length();
  616. }
  617. size_t FileStream::length()
  618. {
  619. size_t len = 0;
  620. if (canSeek())
  621. {
  622. long int pos = position();
  623. if (seek(0, SEEK_END))
  624. {
  625. len = position();
  626. }
  627. seek(pos, SEEK_SET);
  628. }
  629. return len;
  630. }
  631. long int FileStream::position()
  632. {
  633. if (!_file)
  634. return -1;
  635. return ftell(_file);
  636. }
  637. bool FileStream::seek(long int offset, int origin)
  638. {
  639. if (!_file)
  640. return false;
  641. return fseek(_file, offset, origin) == 0;
  642. }
  643. bool FileStream::rewind()
  644. {
  645. if (canSeek())
  646. {
  647. ::rewind(_file);
  648. return true;
  649. }
  650. return false;
  651. }
  652. ////////////////////////////////
  653. #ifdef __ANDROID__
  654. FileStreamAndroid::FileStreamAndroid(AAsset* asset)
  655. : _asset(asset)
  656. {
  657. }
  658. FileStreamAndroid::~FileStreamAndroid()
  659. {
  660. if (_asset)
  661. close();
  662. }
  663. FileStreamAndroid* FileStreamAndroid::create(const char* filePath, const char* mode)
  664. {
  665. AAsset* asset = AAssetManager_open(__assetManager, filePath, AASSET_MODE_RANDOM);
  666. if (asset)
  667. {
  668. FileStreamAndroid* stream = new FileStreamAndroid(asset);
  669. return stream;
  670. }
  671. return NULL;
  672. }
  673. bool FileStreamAndroid::canRead()
  674. {
  675. return true;
  676. }
  677. bool FileStreamAndroid::canWrite()
  678. {
  679. return false;
  680. }
  681. bool FileStreamAndroid::canSeek()
  682. {
  683. return true;
  684. }
  685. void FileStreamAndroid::close()
  686. {
  687. if (_asset)
  688. AAsset_close(_asset);
  689. _asset = NULL;
  690. }
  691. size_t FileStreamAndroid::read(void* ptr, size_t size, size_t count)
  692. {
  693. int result = AAsset_read(_asset, ptr, size * count);
  694. return result > 0 ? ((size_t)result) / size : 0;
  695. }
  696. char* FileStreamAndroid::readLine(char* str, int num)
  697. {
  698. if (num <= 0)
  699. return NULL;
  700. char c = 0;
  701. size_t maxCharsToRead = num - 1;
  702. for (size_t i = 0; i < maxCharsToRead; ++i)
  703. {
  704. size_t result = read(&c, 1, 1);
  705. if (result != 1)
  706. {
  707. str[i] = '\0';
  708. break;
  709. }
  710. if (c == '\n')
  711. {
  712. str[i] = c;
  713. str[i + 1] = '\0';
  714. break;
  715. }
  716. else if(c == '\r')
  717. {
  718. str[i] = c;
  719. // next may be '\n'
  720. size_t pos = position();
  721. char nextChar = 0;
  722. if (read(&nextChar, 1, 1) != 1)
  723. {
  724. // no more characters
  725. str[i + 1] = '\0';
  726. break;
  727. }
  728. if (nextChar == '\n')
  729. {
  730. if (i == maxCharsToRead - 1)
  731. {
  732. str[i + 1] = '\0';
  733. break;
  734. }
  735. else
  736. {
  737. str[i + 1] = nextChar;
  738. str[i + 2] = '\0';
  739. break;
  740. }
  741. }
  742. else
  743. {
  744. seek(pos, SEEK_SET);
  745. str[i + 1] = '\0';
  746. break;
  747. }
  748. }
  749. str[i] = c;
  750. }
  751. return str; // what if first read failed?
  752. }
  753. size_t FileStreamAndroid::write(const void* ptr, size_t size, size_t count)
  754. {
  755. return 0;
  756. }
  757. bool FileStreamAndroid::eof()
  758. {
  759. return position() >= length();
  760. }
  761. size_t FileStreamAndroid::length()
  762. {
  763. return (size_t)AAsset_getLength(_asset);
  764. }
  765. long int FileStreamAndroid::position()
  766. {
  767. return AAsset_getLength(_asset) - AAsset_getRemainingLength(_asset);
  768. }
  769. bool FileStreamAndroid::seek(long int offset, int origin)
  770. {
  771. return AAsset_seek(_asset, offset, origin) != -1;
  772. }
  773. bool FileStreamAndroid::rewind()
  774. {
  775. if (canSeek())
  776. {
  777. return AAsset_seek(_asset, 0, SEEK_SET) != -1;
  778. }
  779. return false;
  780. }
  781. #endif
  782. }