FileSystem.cpp 20 KB

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