FileSystem.cpp 19 KB

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