FileSystem.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. #include "Base.h"
  2. #include "FileSystem.h"
  3. #include "Properties.h"
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #ifdef WIN32
  7. #include <windows.h>
  8. #include <tchar.h>
  9. #include <stdio.h>
  10. #define gp_stat _stat
  11. #define gp_stat_struct struct stat
  12. #else
  13. #include <dirent.h>
  14. #define gp_stat stat
  15. #define gp_stat_struct struct stat
  16. #endif
  17. #ifdef __ANDROID__
  18. #include <android/asset_manager.h>
  19. extern AAssetManager* __assetManager;
  20. #endif
  21. namespace gameplay
  22. {
  23. // Creates a file on the file system from the specified asset (Android-specific).
  24. static void createFileFromAsset(const char* path);
  25. #ifdef __ANDROID__
  26. #include <unistd.h>
  27. void makepath(std::string path, int mode)
  28. {
  29. std::vector<std::string> dirs;
  30. while (path.length() > 0)
  31. {
  32. int index = path.find('/');
  33. std::string dir = (index == -1 ) ? path : path.substr(0, index);
  34. if (dir.length() > 0)
  35. dirs.push_back(dir);
  36. if (index + 1 >= path.length() || index == -1)
  37. break;
  38. path = path.substr(index + 1);
  39. }
  40. struct stat s;
  41. std::string dirPath;
  42. for (unsigned int i = 0; i < dirs.size(); i++)
  43. {
  44. dirPath += "/";
  45. dirPath += dirs[i];
  46. if (stat(dirPath.c_str(), &s) != 0)
  47. {
  48. // Directory does not exist.
  49. if (mkdir(dirPath.c_str(), 0777) != 0)
  50. {
  51. GP_ERROR("Failed to create directory: '%s'", dirPath.c_str());
  52. return;
  53. }
  54. }
  55. }
  56. return;
  57. }
  58. #endif
  59. /** @script{ignore} */
  60. static std::string __resourcePath("./");
  61. static std::map<std::string, std::string> __aliases;
  62. FileSystem::FileSystem()
  63. {
  64. }
  65. FileSystem::~FileSystem()
  66. {
  67. }
  68. void FileSystem::setResourcePath(const char* path)
  69. {
  70. __resourcePath = path == NULL ? "" : path;
  71. }
  72. const char* FileSystem::getResourcePath()
  73. {
  74. return __resourcePath.c_str();
  75. }
  76. void FileSystem::loadResourceAliases(const char* aliasFilePath)
  77. {
  78. Properties* properties = Properties::create(aliasFilePath);
  79. if (properties)
  80. {
  81. Properties* aliases;
  82. while ((aliases = properties->getNextNamespace()) != NULL)
  83. {
  84. loadResourceAliases(aliases);
  85. }
  86. }
  87. SAFE_DELETE(properties);
  88. }
  89. void FileSystem::loadResourceAliases(Properties* properties)
  90. {
  91. assert(properties);
  92. const char* name;
  93. while ((name = properties->getNextProperty()) != NULL)
  94. {
  95. __aliases[name] = properties->getString();
  96. }
  97. }
  98. const char* FileSystem::resolvePath(const char* path)
  99. {
  100. GP_ASSERT(path);
  101. size_t len = strlen(path);
  102. if (len > 1 && path[0] == '@')
  103. {
  104. std::string alias(path + 1);
  105. std::map<std::string, std::string>::const_iterator itr = __aliases.find(alias);
  106. if (itr == __aliases.end())
  107. return path; // no matching alias found
  108. return itr->second.c_str();
  109. }
  110. return path;
  111. }
  112. bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
  113. {
  114. // TODO make this method work with absolute and relative paths.
  115. #ifdef WIN32
  116. std::string path(FileSystem::getResourcePath());
  117. if (dirPath && strlen(dirPath) > 0)
  118. {
  119. path.append(dirPath);
  120. }
  121. path.append("/*");
  122. // Convert char to wchar
  123. std::basic_string<TCHAR> wPath;
  124. wPath.assign(path.begin(), path.end());
  125. WIN32_FIND_DATA FindFileData;
  126. HANDLE hFind = FindFirstFile(wPath.c_str(), &FindFileData);
  127. if (hFind == INVALID_HANDLE_VALUE)
  128. {
  129. return false;
  130. }
  131. do
  132. {
  133. // Add to the list if this is not a directory
  134. if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  135. {
  136. // Convert wchar to char
  137. std::basic_string<TCHAR> wfilename(FindFileData.cFileName);
  138. std::string filename;
  139. filename.assign(wfilename.begin(), wfilename.end());
  140. files.push_back(filename);
  141. }
  142. } while (FindNextFile(hFind, &FindFileData) != 0);
  143. FindClose(hFind);
  144. return true;
  145. #else
  146. std::string path(FileSystem::getResourcePath());
  147. if (dirPath && strlen(dirPath) > 0)
  148. {
  149. path.append(dirPath);
  150. }
  151. path.append("/.");
  152. struct dirent* dp;
  153. DIR* dir = opendir(path.c_str());
  154. if (!dir)
  155. {
  156. return false;
  157. }
  158. while ((dp = readdir(dir)) != NULL)
  159. {
  160. std::string filepath(path);
  161. filepath.append("/");
  162. filepath.append(dp->d_name);
  163. struct stat buf;
  164. if (!stat(filepath.c_str(), &buf))
  165. {
  166. // Add to the list if this is not a directory
  167. if (!S_ISDIR(buf.st_mode))
  168. {
  169. files.push_back(dp->d_name);
  170. }
  171. }
  172. }
  173. closedir(dir);
  174. return true;
  175. #endif
  176. }
  177. bool FileSystem::fileExists(const char* filePath)
  178. {
  179. GP_ASSERT(filePath);
  180. std::string fullPath(__resourcePath);
  181. fullPath += resolvePath(filePath);
  182. createFileFromAsset(filePath);
  183. gp_stat_struct s;
  184. // Win32 doesn't support an asset or bundle definitions.
  185. #ifdef WIN32
  186. if (stat(fullPath.c_str(), &s) != 0)
  187. {
  188. fullPath = __resourcePath;
  189. fullPath += "../../gameplay/";
  190. fullPath += filePath;
  191. int result = stat(fullPath.c_str(), &s);
  192. if (result != 0)
  193. {
  194. fullPath = __resourcePath;
  195. fullPath += "../gameplay/";
  196. fullPath += filePath;
  197. return stat(fullPath.c_str(), &s) == 0;
  198. }
  199. }
  200. return true;
  201. #else
  202. return stat(fullPath.c_str(), &s) == 0;
  203. #endif
  204. }
  205. FILE* FileSystem::openFile(const char* path, const char* mode)
  206. {
  207. GP_ASSERT(path);
  208. GP_ASSERT(mode);
  209. std::string fullPath(__resourcePath);
  210. fullPath += resolvePath(path);
  211. createFileFromAsset(path);
  212. FILE* fp = fopen(fullPath.c_str(), mode);
  213. // Win32 doesn't support an asset or bundle definitions.
  214. #ifdef WIN32
  215. if (fp == NULL)
  216. {
  217. fullPath = __resourcePath;
  218. fullPath += "../../gameplay/";
  219. fullPath += path;
  220. fp = fopen(fullPath.c_str(), mode);
  221. if (!fp)
  222. {
  223. fullPath = __resourcePath;
  224. fullPath += "../gameplay/";
  225. fullPath += path;
  226. fp = fopen(fullPath.c_str(), mode);
  227. }
  228. }
  229. #endif
  230. return fp;
  231. }
  232. char* FileSystem::readAll(const char* filePath, int* fileSize)
  233. {
  234. GP_ASSERT(filePath);
  235. // Open file for reading.
  236. FILE* file = openFile(filePath, "rb");
  237. if (file == NULL)
  238. {
  239. GP_ERROR("Failed to load file: %s", filePath);
  240. return NULL;
  241. }
  242. // Obtain file length.
  243. if (fseek(file, 0, SEEK_END) != 0)
  244. {
  245. GP_ERROR("Failed to seek to the end of the file '%s' to obtain the file length.", filePath);
  246. return NULL;
  247. }
  248. int size = (int)ftell(file);
  249. if (fseek(file, 0, SEEK_SET) != 0)
  250. {
  251. GP_ERROR("Failed to seek to beginning of the file '%s' to begin reading in the entire file.", filePath);
  252. return NULL;
  253. }
  254. // Read entire file contents.
  255. char* buffer = new char[size + 1];
  256. int read = (int)fread(buffer, 1, size, file);
  257. if (read != size)
  258. {
  259. GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %d < %d).", filePath, (int)read, (int)size);
  260. SAFE_DELETE_ARRAY(buffer);
  261. return NULL;
  262. }
  263. // Force the character buffer to be NULL-terminated.
  264. buffer[size] = '\0';
  265. // Close file and return.
  266. if (fclose(file) != 0)
  267. {
  268. GP_ERROR("Failed to close file '%s'.", filePath);
  269. }
  270. if (fileSize)
  271. {
  272. *fileSize = size;
  273. }
  274. return buffer;
  275. }
  276. bool FileSystem::isAbsolutePath(const char* filePath)
  277. {
  278. if (filePath == 0 || filePath[0] == '\0')
  279. return false;
  280. #ifdef WIN32
  281. if (strlen(filePath) >= 2)
  282. {
  283. char first = filePath[0];
  284. if (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')))
  285. return true;
  286. }
  287. return false;
  288. #else
  289. return filePath[0] == '/';
  290. #endif
  291. }
  292. void createFileFromAsset(const char* path)
  293. {
  294. #ifdef __ANDROID__
  295. static std::set<std::string> upToDateAssets;
  296. GP_ASSERT(path);
  297. std::string fullPath(__resourcePath);
  298. std::string resolvedPath = FileSystem::resolvePath(path);
  299. fullPath += resolvedPath;
  300. std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
  301. struct stat s;
  302. if (stat(directoryPath.c_str(), &s) != 0)
  303. makepath(directoryPath, 0777);
  304. // To ensure that the files on the file system corresponding to the assets in the APK bundle
  305. // are always up to date (and in sync), we copy them from the APK to the file system once
  306. // for each time the process (game) runs.
  307. if (upToDateAssets.find(fullPath) == upToDateAssets.end())
  308. {
  309. AAsset* asset = AAssetManager_open(__assetManager, resolvedPath.c_str(), AASSET_MODE_RANDOM);
  310. if (asset)
  311. {
  312. const void* data = AAsset_getBuffer(asset);
  313. int length = AAsset_getLength(asset);
  314. FILE* file = fopen(fullPath.c_str(), "wb");
  315. if (file != NULL)
  316. {
  317. int ret = fwrite(data, sizeof(unsigned char), length, file);
  318. if (fclose(file) != 0)
  319. {
  320. GP_ERROR("Failed to close file on file system created from APK asset '%s'.", path);
  321. return;
  322. }
  323. if (ret != length)
  324. {
  325. GP_ERROR("Failed to write all data from APK asset '%s' to file on file system.", path);
  326. return;
  327. }
  328. }
  329. else
  330. {
  331. GP_ERROR("Failed to create file on file system from APK asset '%s'.", path);
  332. return;
  333. }
  334. upToDateAssets.insert(fullPath);
  335. }
  336. }
  337. #endif
  338. }
  339. }