FileSystem.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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. static std::string __resourcePath("./");
  60. static std::map<std::string, std::string> __aliases;
  61. FileSystem::FileSystem()
  62. {
  63. }
  64. FileSystem::~FileSystem()
  65. {
  66. }
  67. void FileSystem::setResourcePath(const char* path)
  68. {
  69. __resourcePath = path == NULL ? "" : path;
  70. }
  71. const char* FileSystem::getResourcePath()
  72. {
  73. return __resourcePath.c_str();
  74. }
  75. void FileSystem::loadResourceAliases(const char* aliasFilePath)
  76. {
  77. Properties* properties = Properties::create(aliasFilePath);
  78. if (properties)
  79. {
  80. Properties* aliases;
  81. while ((aliases = properties->getNextNamespace()) != NULL)
  82. {
  83. loadResourceAliases(aliases);
  84. }
  85. }
  86. SAFE_DELETE(properties);
  87. }
  88. void FileSystem::loadResourceAliases(Properties* properties)
  89. {
  90. assert(properties);
  91. const char* name;
  92. while ((name = properties->getNextProperty()) != NULL)
  93. {
  94. __aliases[name] = properties->getString();
  95. }
  96. }
  97. const char* FileSystem::resolvePath(const char* path)
  98. {
  99. GP_ASSERT(path);
  100. size_t len = strlen(path);
  101. if (len > 1 && path[0] == '@')
  102. {
  103. std::string alias(path + 1);
  104. std::map<std::string, std::string>::const_iterator itr = __aliases.find(alias);
  105. if (itr == __aliases.end())
  106. return path; // no matching alias found
  107. return itr->second.c_str();
  108. }
  109. return path;
  110. }
  111. bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
  112. {
  113. // TODO make this method work with absolute and relative paths.
  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. // Win32 doesn't support an asset or bundle definitions.
  184. #ifdef WIN32
  185. if (stat(fullPath.c_str(), &s) != 0)
  186. {
  187. fullPath = __resourcePath;
  188. fullPath += "../../gameplay/";
  189. fullPath += filePath;
  190. return stat(fullPath.c_str(), &s) == 0;
  191. }
  192. return true;
  193. #else
  194. return stat(fullPath.c_str(), &s) == 0;
  195. #endif
  196. }
  197. FILE* FileSystem::openFile(const char* path, const char* mode)
  198. {
  199. GP_ASSERT(path);
  200. GP_ASSERT(mode);
  201. std::string fullPath(__resourcePath);
  202. fullPath += resolvePath(path);
  203. createFileFromAsset(path);
  204. FILE* fp = fopen(fullPath.c_str(), mode);
  205. // Win32 doesn't support an asset or bundle definitions.
  206. #ifdef WIN32
  207. if (fp == NULL)
  208. {
  209. fullPath = __resourcePath;
  210. fullPath += "../../gameplay/";
  211. fullPath += path;
  212. fp = fopen(fullPath.c_str(), mode);
  213. }
  214. #endif
  215. return fp;
  216. }
  217. char* FileSystem::readAll(const char* filePath, int* fileSize)
  218. {
  219. GP_ASSERT(filePath);
  220. // Open file for reading.
  221. FILE* file = openFile(filePath, "rb");
  222. if (file == NULL)
  223. {
  224. GP_ERROR("Failed to load file: %s", filePath);
  225. return NULL;
  226. }
  227. // Obtain file length.
  228. if (fseek(file, 0, SEEK_END) != 0)
  229. {
  230. GP_ERROR("Failed to seek to the end of the file '%s' to obtain the file length.", filePath);
  231. return NULL;
  232. }
  233. int size = (int)ftell(file);
  234. if (fseek(file, 0, SEEK_SET) != 0)
  235. {
  236. GP_ERROR("Failed to seek to beginning of the file '%s' to begin reading in the entire file.", filePath);
  237. return NULL;
  238. }
  239. // Read entire file contents.
  240. char* buffer = new char[size + 1];
  241. int read = (int)fread(buffer, 1, size, file);
  242. if (read != size)
  243. {
  244. GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %d < %d).", filePath, (int)read, (int)size);
  245. SAFE_DELETE_ARRAY(buffer);
  246. return NULL;
  247. }
  248. // Force the character buffer to be NULL-terminated.
  249. buffer[size] = '\0';
  250. // Close file and return.
  251. if (fclose(file) != 0)
  252. {
  253. GP_ERROR("Failed to close file '%s'.", filePath);
  254. }
  255. if (fileSize)
  256. {
  257. *fileSize = size;
  258. }
  259. return buffer;
  260. }
  261. void createFileFromAsset(const char* path)
  262. {
  263. #ifdef __ANDROID__
  264. static std::set<std::string> upToDateAssets;
  265. GP_ASSERT(path);
  266. std::string fullPath(__resourcePath);
  267. std::string resolvedPath = FileSystem::resolvePath(path);
  268. fullPath += resolvedPath;
  269. std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
  270. struct stat s;
  271. if (stat(directoryPath.c_str(), &s) != 0)
  272. makepath(directoryPath.c_str(), 0777);
  273. // To ensure that the files on the file system corresponding to the assets in the APK bundle
  274. // are always up to date (and in sync), we copy them from the APK to the file system once
  275. // for each time the process (game) runs.
  276. if (upToDateAssets.find(fullPath) == upToDateAssets.end())
  277. {
  278. AAsset* asset = AAssetManager_open(__assetManager, resolvedPath.c_str(), AASSET_MODE_RANDOM);
  279. if (asset)
  280. {
  281. const void* data = AAsset_getBuffer(asset);
  282. int length = AAsset_getLength(asset);
  283. FILE* file = fopen(fullPath.c_str(), "wb");
  284. if (file != NULL)
  285. {
  286. int ret = fwrite(data, sizeof(unsigned char), length, file);
  287. if (fclose(file) != 0)
  288. {
  289. GP_ERROR("Failed to close file on file system created from APK asset '%s'.", path);
  290. return;
  291. }
  292. if (ret != length)
  293. {
  294. GP_ERROR("Failed to write all data from APK asset '%s' to file on file system.", path);
  295. return;
  296. }
  297. }
  298. else
  299. {
  300. GP_ERROR("Failed to create file on file system from APK asset '%s'.", path);
  301. return;
  302. }
  303. upToDateAssets.insert(fullPath);
  304. }
  305. }
  306. #endif
  307. }
  308. }