FileSystem.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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. #ifdef WIN32
  115. std::string path(FileSystem::getResourcePath());
  116. if (dirPath && strlen(dirPath) > 0)
  117. {
  118. path.append(dirPath);
  119. }
  120. path.append("/*");
  121. // Convert char to wchar
  122. std::basic_string<TCHAR> wPath;
  123. wPath.assign(path.begin(), path.end());
  124. WIN32_FIND_DATA FindFileData;
  125. HANDLE hFind = FindFirstFile(wPath.c_str(), &FindFileData);
  126. if (hFind == INVALID_HANDLE_VALUE)
  127. {
  128. return false;
  129. }
  130. do
  131. {
  132. // Add to the list if this is not a directory
  133. if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  134. {
  135. // Convert wchar to char
  136. std::basic_string<TCHAR> wfilename(FindFileData.cFileName);
  137. std::string filename;
  138. filename.assign(wfilename.begin(), wfilename.end());
  139. files.push_back(filename);
  140. }
  141. } while (FindNextFile(hFind, &FindFileData) != 0);
  142. FindClose(hFind);
  143. return true;
  144. #else
  145. std::string path(FileSystem::getResourcePath());
  146. if (dirPath && strlen(dirPath) > 0)
  147. {
  148. path.append(dirPath);
  149. }
  150. path.append("/.");
  151. struct dirent* dp;
  152. DIR* dir = opendir(path.c_str());
  153. if (!dir)
  154. {
  155. return false;
  156. }
  157. while ((dp = readdir(dir)) != NULL)
  158. {
  159. std::string filepath(path);
  160. filepath.append("/");
  161. filepath.append(dp->d_name);
  162. struct stat buf;
  163. if (!stat(filepath.c_str(), &buf))
  164. {
  165. // Add to the list if this is not a directory
  166. if (!S_ISDIR(buf.st_mode))
  167. {
  168. files.push_back(dp->d_name);
  169. }
  170. }
  171. }
  172. closedir(dir);
  173. return true;
  174. #endif
  175. }
  176. bool FileSystem::fileExists(const char* filePath)
  177. {
  178. GP_ASSERT(filePath);
  179. std::string fullPath(__resourcePath);
  180. fullPath += resolvePath(filePath);
  181. createFileFromAsset(filePath);
  182. gp_stat_struct s;
  183. #ifdef WIN32
  184. if (stat(fullPath.c_str(), &s) != 0)
  185. {
  186. fullPath = __resourcePath;
  187. fullPath += "../../gameplay/";
  188. fullPath += filePath;
  189. int result = stat(fullPath.c_str(), &s);
  190. if (result != 0)
  191. {
  192. fullPath = __resourcePath;
  193. fullPath += "../gameplay/";
  194. fullPath += filePath;
  195. return stat(fullPath.c_str(), &s) == 0;
  196. }
  197. }
  198. return true;
  199. #else
  200. return stat(fullPath.c_str(), &s) == 0;
  201. #endif
  202. }
  203. FILE* FileSystem::openFile(const char* path, const char* mode)
  204. {
  205. GP_ASSERT(path);
  206. GP_ASSERT(mode);
  207. std::string fullPath(__resourcePath);
  208. fullPath += resolvePath(path);
  209. createFileFromAsset(path);
  210. FILE* fp = fopen(fullPath.c_str(), mode);
  211. #ifdef WIN32
  212. if (fp == NULL)
  213. {
  214. fullPath = __resourcePath;
  215. fullPath += "../../gameplay/";
  216. fullPath += path;
  217. fp = fopen(fullPath.c_str(), mode);
  218. if (!fp)
  219. {
  220. fullPath = __resourcePath;
  221. fullPath += "../gameplay/";
  222. fullPath += path;
  223. fp = fopen(fullPath.c_str(), mode);
  224. }
  225. }
  226. #endif
  227. return fp;
  228. }
  229. char* FileSystem::readAll(const char* filePath, int* fileSize)
  230. {
  231. GP_ASSERT(filePath);
  232. // Open file for reading.
  233. FILE* file = openFile(filePath, "rb");
  234. if (file == NULL)
  235. {
  236. GP_ERROR("Failed to load file: %s", filePath);
  237. return NULL;
  238. }
  239. // Obtain file length.
  240. if (fseek(file, 0, SEEK_END) != 0)
  241. {
  242. GP_ERROR("Failed to seek to the end of the file '%s' to obtain the file length.", filePath);
  243. return NULL;
  244. }
  245. int size = (int)ftell(file);
  246. if (fseek(file, 0, SEEK_SET) != 0)
  247. {
  248. GP_ERROR("Failed to seek to beginning of the file '%s' to begin reading in the entire file.", filePath);
  249. return NULL;
  250. }
  251. // Read entire file contents.
  252. char* buffer = new char[size + 1];
  253. int read = (int)fread(buffer, 1, size, file);
  254. if (read != size)
  255. {
  256. GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %d < %d).", filePath, (int)read, (int)size);
  257. SAFE_DELETE_ARRAY(buffer);
  258. return NULL;
  259. }
  260. // Force the character buffer to be NULL-terminated.
  261. buffer[size] = '\0';
  262. // Close file and return.
  263. if (fclose(file) != 0)
  264. {
  265. GP_ERROR("Failed to close file '%s'.", filePath);
  266. }
  267. if (fileSize)
  268. {
  269. *fileSize = size;
  270. }
  271. return buffer;
  272. }
  273. bool FileSystem::isAbsolutePath(const char* filePath)
  274. {
  275. if (filePath == 0 || filePath[0] == '\0')
  276. return false;
  277. #ifdef WIN32
  278. if (strlen(filePath) >= 2)
  279. {
  280. char first = filePath[0];
  281. if (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')))
  282. return true;
  283. }
  284. return false;
  285. #else
  286. return filePath[0] == '/';
  287. #endif
  288. }
  289. void createFileFromAsset(const char* path)
  290. {
  291. #ifdef __ANDROID__
  292. static std::set<std::string> upToDateAssets;
  293. GP_ASSERT(path);
  294. std::string fullPath(__resourcePath);
  295. std::string resolvedPath = FileSystem::resolvePath(path);
  296. fullPath += resolvedPath;
  297. std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
  298. struct stat s;
  299. if (stat(directoryPath.c_str(), &s) != 0)
  300. makepath(directoryPath, 0777);
  301. // To ensure that the files on the file system corresponding to the assets in the APK bundle
  302. // are always up to date (and in sync), we copy them from the APK to the file system once
  303. // for each time the process (game) runs.
  304. if (upToDateAssets.find(fullPath) == upToDateAssets.end())
  305. {
  306. AAsset* asset = AAssetManager_open(__assetManager, resolvedPath.c_str(), AASSET_MODE_RANDOM);
  307. if (asset)
  308. {
  309. const void* data = AAsset_getBuffer(asset);
  310. int length = AAsset_getLength(asset);
  311. FILE* file = fopen(fullPath.c_str(), "wb");
  312. if (file != NULL)
  313. {
  314. int ret = fwrite(data, sizeof(unsigned char), length, file);
  315. if (fclose(file) != 0)
  316. {
  317. GP_ERROR("Failed to close file on file system created from APK asset '%s'.", path);
  318. return;
  319. }
  320. if (ret != length)
  321. {
  322. GP_ERROR("Failed to write all data from APK asset '%s' to file on file system.", path);
  323. return;
  324. }
  325. }
  326. else
  327. {
  328. GP_ERROR("Failed to create file on file system from APK asset '%s'.", path);
  329. return;
  330. }
  331. upToDateAssets.insert(fullPath);
  332. }
  333. }
  334. #endif
  335. }
  336. }