android.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. /**
  2. * Copyright (c) 2006-2022 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include "android.h"
  21. #include "Object.h"
  22. #ifdef LOVE_ANDROID
  23. #include <cerrno>
  24. #include <unordered_map>
  25. #include <SDL.h>
  26. #include <jni.h>
  27. #include <android/asset_manager.h>
  28. #include <android/asset_manager_jni.h>
  29. #include <sys/stat.h>
  30. #include <sys/types.h>
  31. #include <unistd.h>
  32. #include "libraries/physfs/physfs.h"
  33. namespace love
  34. {
  35. namespace android
  36. {
  37. void setImmersive(bool immersive_active)
  38. {
  39. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  40. jobject activity = (jobject) SDL_AndroidGetActivity();
  41. jclass clazz = env->GetObjectClass(activity);
  42. static jmethodID setImmersiveMethod = env->GetMethodID(clazz, "setImmersiveMode", "(Z)V");
  43. env->CallVoidMethod(activity, setImmersiveMethod, immersive_active);
  44. env->DeleteLocalRef(activity);
  45. env->DeleteLocalRef(clazz);
  46. }
  47. bool getImmersive()
  48. {
  49. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  50. jobject activity = (jobject) SDL_AndroidGetActivity();
  51. jclass clazz = env->GetObjectClass(activity);
  52. static jmethodID getImmersiveMethod = env->GetMethodID(clazz, "getImmersiveMode", "()Z");
  53. jboolean immersiveActive = env->CallBooleanMethod(activity, getImmersiveMethod);
  54. env->DeleteLocalRef(activity);
  55. env->DeleteLocalRef(clazz);
  56. return immersiveActive;
  57. }
  58. double getScreenScale()
  59. {
  60. static double result = -1.;
  61. if (result == -1.)
  62. {
  63. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  64. jobject activity = (jobject) SDL_AndroidGetActivity();
  65. jclass clazz = env->GetObjectClass(activity);
  66. jmethodID getDPIMethod = env->GetMethodID(clazz, "getDPIScale", "()F");
  67. result = (double) env->CallFloatMethod(activity, getDPIMethod);
  68. env->DeleteLocalRef(clazz);
  69. env->DeleteLocalRef(activity);
  70. }
  71. return result;
  72. }
  73. bool getSafeArea(int &top, int &left, int &bottom, int &right)
  74. {
  75. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  76. jobject activity = (jobject) SDL_AndroidGetActivity();
  77. jclass clazz = env->GetObjectClass(activity);
  78. static jmethodID methodID = env->GetMethodID(clazz, "getSafeArea", "()Z");
  79. bool hasSafeArea = false;
  80. if (methodID == nullptr)
  81. // NoSuchMethodException is thrown in case methodID is null
  82. env->ExceptionClear();
  83. else if ((hasSafeArea = env->CallBooleanMethod(activity, methodID)))
  84. {
  85. top = env->GetIntField(activity, env->GetFieldID(clazz, "safeAreaTop", "I"));
  86. left = env->GetIntField(activity, env->GetFieldID(clazz, "safeAreaLeft", "I"));
  87. bottom = env->GetIntField(activity, env->GetFieldID(clazz, "safeAreaBottom", "I"));
  88. right = env->GetIntField(activity, env->GetFieldID(clazz, "safeAreaRight", "I"));
  89. }
  90. env->DeleteLocalRef(clazz);
  91. env->DeleteLocalRef(activity);
  92. return hasSafeArea;
  93. }
  94. bool openURL(const std::string &url)
  95. {
  96. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  97. jobject activity = (jobject) SDL_AndroidGetActivity();
  98. jclass clazz = env->GetObjectClass(activity);
  99. static jmethodID openURL = env->GetMethodID(clazz, "openURLFromLOVE", "(Ljava/lang/String;)Z");
  100. jstring jstringURL = env->NewStringUTF(url.c_str());
  101. jboolean result = env->CallBooleanMethod(clazz, openURL, jstringURL);
  102. env->DeleteLocalRef(jstringURL);
  103. env->DeleteLocalRef(clazz);
  104. env->DeleteLocalRef(activity);
  105. return (bool) result;
  106. }
  107. void vibrate(double seconds)
  108. {
  109. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  110. jobject activity = (jobject) SDL_AndroidGetActivity();
  111. jclass clazz = env->GetObjectClass(activity);
  112. static jmethodID vibrateMethod = env->GetMethodID(clazz, "vibrate", "(D)V");
  113. env->CallVoidMethod(activity, vibrateMethod, seconds);
  114. env->DeleteLocalRef(clazz);
  115. env->DeleteLocalRef(activity);
  116. }
  117. /*
  118. * Helper functions for the filesystem module
  119. */
  120. void freeGameArchiveMemory(void *ptr)
  121. {
  122. char *game_love_data = static_cast<char*>(ptr);
  123. delete[] game_love_data;
  124. }
  125. bool directoryExists(const char *path)
  126. {
  127. struct stat s {};
  128. int err = stat(path, &s);
  129. if (err == -1)
  130. {
  131. if (errno != ENOENT)
  132. SDL_Log("Error checking for directory %s errno = %d: %s", path, errno, strerror(errno));
  133. return false;
  134. }
  135. return S_ISDIR(s.st_mode);
  136. }
  137. bool mkdir(const char *path)
  138. {
  139. int err = ::mkdir(path, 0770);
  140. if (err == -1)
  141. {
  142. SDL_Log("Error: Could not create directory %s", path);
  143. return false;
  144. }
  145. return true;
  146. }
  147. bool createStorageDirectories()
  148. {
  149. std::string internal_storage_path = SDL_AndroidGetInternalStoragePath();
  150. std::string save_directory = internal_storage_path + "/save";
  151. if (!directoryExists(save_directory.c_str()) && !mkdir(save_directory.c_str()))
  152. return false;
  153. std::string game_directory = internal_storage_path + "/game";
  154. if (!directoryExists (game_directory.c_str()) && !mkdir(game_directory.c_str()))
  155. return false;
  156. return true;
  157. }
  158. bool hasBackgroundMusic()
  159. {
  160. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  161. jobject activity = (jobject) SDL_AndroidGetActivity();
  162. jclass clazz(env->GetObjectClass(activity));
  163. jmethodID method_id = env->GetMethodID(clazz, "hasBackgroundMusic", "()Z");
  164. jboolean result = env->CallBooleanMethod(activity, method_id);
  165. env->DeleteLocalRef(activity);
  166. env->DeleteLocalRef(clazz);
  167. return result;
  168. }
  169. bool hasRecordingPermission()
  170. {
  171. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  172. jobject activity = (jobject) SDL_AndroidGetActivity();
  173. jclass clazz = env->GetObjectClass(activity);
  174. static jmethodID methodID = env->GetMethodID(clazz, "hasRecordAudioPermission", "()Z");
  175. jboolean result = false;
  176. if (methodID == nullptr)
  177. env->ExceptionClear();
  178. else
  179. result = env->CallBooleanMethod(activity, methodID);
  180. env->DeleteLocalRef(activity);
  181. env->DeleteLocalRef(clazz);
  182. return result;
  183. }
  184. void requestRecordingPermission()
  185. {
  186. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  187. jobject activity = (jobject) SDL_AndroidGetActivity();
  188. jclass clazz(env->GetObjectClass(activity));
  189. jmethodID methodID = env->GetMethodID(clazz, "requestRecordAudioPermission", "()V");
  190. if (methodID == nullptr)
  191. env->ExceptionClear();
  192. else
  193. env->CallVoidMethod(activity, methodID);
  194. env->DeleteLocalRef(clazz);
  195. env->DeleteLocalRef(activity);
  196. }
  197. void showRecordingPermissionMissingDialog()
  198. {
  199. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  200. jobject activity = (jobject) SDL_AndroidGetActivity();
  201. jclass clazz(env->GetObjectClass(activity));
  202. jmethodID methodID = env->GetMethodID(clazz, "showRecordingAudioPermissionMissingDialog", "()V");
  203. if (methodID == nullptr)
  204. env->ExceptionClear();
  205. else
  206. env->CallVoidMethod(activity, methodID);
  207. env->DeleteLocalRef(clazz);
  208. env->DeleteLocalRef(activity);
  209. }
  210. /* A container for AssetManager Java object */
  211. class AssetManagerObject
  212. {
  213. public:
  214. AssetManagerObject()
  215. {
  216. JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
  217. jobject am = getLocalAssetManager(env);
  218. assetManager = env->NewGlobalRef(am);
  219. env->DeleteLocalRef(am);
  220. }
  221. ~AssetManagerObject()
  222. {
  223. JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
  224. env->DeleteGlobalRef(assetManager);
  225. }
  226. static jobject getLocalAssetManager(JNIEnv *env) {
  227. jobject self = (jobject) SDL_AndroidGetActivity();
  228. jclass activity = env->GetObjectClass(self);
  229. jmethodID method = env->GetMethodID(activity, "getAssets", "()Landroid/content/res/AssetManager;");
  230. jobject am = env->CallObjectMethod(self, method);
  231. env->DeleteLocalRef(self);
  232. env->DeleteLocalRef(activity);
  233. return am;
  234. }
  235. explicit operator jobject()
  236. {
  237. return assetManager;
  238. };
  239. private:
  240. jobject assetManager;
  241. };
  242. /*
  243. * Helper functions to aid new fusing method
  244. */
  245. // This returns *global* reference, no need to free it.
  246. static jobject getJavaAssetManager()
  247. {
  248. static AssetManagerObject assetManager;
  249. return (jobject) assetManager;
  250. }
  251. static AAssetManager *getAssetManager()
  252. {
  253. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  254. return AAssetManager_fromJava(env, (jobject) getJavaAssetManager());
  255. }
  256. namespace aasset
  257. {
  258. namespace io
  259. {
  260. struct AssetInfo
  261. {
  262. AAssetManager *assetManager;
  263. AAsset *asset;
  264. char *filename;
  265. size_t size;
  266. };
  267. static std::unordered_map<std::string, PHYSFS_FileType> fileTree;
  268. PHYSFS_sint64 read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
  269. {
  270. AAsset *asset = ((AssetInfo *) io->opaque)->asset;
  271. int readed = AAsset_read(asset, buf, (size_t) len);
  272. PHYSFS_setErrorCode(readed < 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK);
  273. return (PHYSFS_sint64) readed;
  274. }
  275. PHYSFS_sint64 write(PHYSFS_Io *io, const void *buf, PHYSFS_uint64 len)
  276. {
  277. LOVE_UNUSED(io);
  278. LOVE_UNUSED(buf);
  279. LOVE_UNUSED(len);
  280. PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
  281. return -1;
  282. }
  283. int seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
  284. {
  285. AAsset *asset = ((AssetInfo *) io->opaque)->asset;
  286. int success = AAsset_seek64(asset, (off64_t) offset, SEEK_SET) != -1;
  287. PHYSFS_setErrorCode(success ? PHYSFS_ERR_OK : PHYSFS_ERR_OS_ERROR);
  288. return success;
  289. }
  290. PHYSFS_sint64 tell(PHYSFS_Io *io)
  291. {
  292. AAsset *asset = ((AssetInfo *) io->opaque)->asset;
  293. off64_t len = AAsset_getLength64(asset);
  294. off64_t remain = AAsset_getRemainingLength64(asset);
  295. return len - remain;
  296. }
  297. PHYSFS_sint64 length(PHYSFS_Io *io)
  298. {
  299. AAsset *asset = ((AssetInfo *) io->opaque)->asset;
  300. return AAsset_getLength64(asset);
  301. }
  302. // Forward declaration
  303. PHYSFS_Io *fromAAsset(AAssetManager *assetManager, const char *filename, AAsset *asset);
  304. PHYSFS_Io *duplicate(PHYSFS_Io *io)
  305. {
  306. AssetInfo *assetInfo = (AssetInfo *) io->opaque;
  307. AAsset *asset = AAssetManager_open(assetInfo->assetManager, assetInfo->filename, AASSET_MODE_RANDOM);
  308. if (asset == nullptr)
  309. {
  310. PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
  311. return nullptr;
  312. }
  313. AAsset_seek64(asset, tell(io), SEEK_SET);
  314. return fromAAsset(assetInfo->assetManager, assetInfo->filename, asset);
  315. }
  316. void destroy(PHYSFS_Io *io)
  317. {
  318. AssetInfo *assetInfo = (AssetInfo *) io->opaque;
  319. AAsset_close(assetInfo->asset);
  320. delete[] assetInfo->filename;
  321. delete assetInfo;
  322. delete io;
  323. }
  324. PHYSFS_Io *fromAAsset(AAssetManager *assetManager, const char *filename, AAsset *asset)
  325. {
  326. // Create AssetInfo
  327. AssetInfo *assetInfo = new (std::nothrow) AssetInfo();
  328. assetInfo->assetManager = assetManager;
  329. assetInfo->asset = asset;
  330. assetInfo->size = strlen(filename) + 1;
  331. assetInfo->filename = new (std::nothrow) char[assetInfo->size];
  332. memcpy(assetInfo->filename, filename, assetInfo->size);
  333. // Create PHYSFS_Io
  334. PHYSFS_Io *io = new (std::nothrow) PHYSFS_Io();
  335. io->version = 0;
  336. io->opaque = assetInfo;
  337. io->read = read;
  338. io->write = write;
  339. io->seek = seek;
  340. io->tell = tell;
  341. io->length = length;
  342. io->duplicate = duplicate;
  343. io->flush = nullptr;
  344. io->destroy = destroy;
  345. return io;
  346. }
  347. }
  348. void *openArchive(PHYSFS_Io *io, const char *name, int forWrite, int *claimed)
  349. {
  350. if (io->opaque == nullptr || memcmp(io->opaque, "ASET", 4) != 0)
  351. return nullptr;
  352. // It's our archive
  353. *claimed = 1;
  354. AAssetManager *assetManager = getAssetManager();
  355. if (io::fileTree.empty())
  356. {
  357. // AAssetDir_getNextFileName intentionally excludes directories, so
  358. // we have to use JNI that calls AssetManager.list() recursively.
  359. JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
  360. jobject activity = (jobject) SDL_AndroidGetActivity();
  361. jclass clazz = env->GetObjectClass(activity);
  362. jmethodID method = env->GetMethodID(clazz, "buildFileTree", "()[Ljava/lang/String;");
  363. jobjectArray list = (jobjectArray) env->CallObjectMethod(activity, method);
  364. for (jsize i = 0; i < env->GetArrayLength(list); i++)
  365. {
  366. jstring jstr = (jstring) env->GetObjectArrayElement(list, i);
  367. const char *str = env->GetStringUTFChars(jstr, nullptr);
  368. io::fileTree[str + 1] = str[0] == 'd' ? PHYSFS_FILETYPE_DIRECTORY : PHYSFS_FILETYPE_REGULAR;
  369. env->ReleaseStringUTFChars(jstr, str);
  370. env->DeleteLocalRef(jstr);
  371. }
  372. env->DeleteLocalRef(list);
  373. env->DeleteLocalRef(clazz);
  374. env->DeleteLocalRef(activity);
  375. }
  376. return assetManager;
  377. }
  378. PHYSFS_EnumerateCallbackResult enumerate(
  379. void *opaque,
  380. const char *dirname,
  381. PHYSFS_EnumerateCallback cb,
  382. const char *origdir,
  383. void *callbackdata
  384. )
  385. {
  386. using FileTreeIterator = std::unordered_map<std::string, PHYSFS_FileType>::iterator;
  387. LOVE_UNUSED(opaque);
  388. const char *path = dirname;
  389. if (path == nullptr || (path[0] == '/' && path[1] == 0))
  390. path = "";
  391. if (path[0] != 0)
  392. {
  393. FileTreeIterator result = io::fileTree.find(path);
  394. if (result == io::fileTree.end() || result->second != PHYSFS_FILETYPE_DIRECTORY)
  395. {
  396. PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
  397. return PHYSFS_ENUM_ERROR;
  398. }
  399. }
  400. JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
  401. jobject assetManager = getJavaAssetManager();
  402. jclass clazz = env->GetObjectClass(assetManager);
  403. jmethodID method = env->GetMethodID(clazz, "list", "(Ljava/lang/String;)[Ljava/lang/String;");
  404. jstring jstringDir = env->NewStringUTF(path);
  405. jobjectArray dir = (jobjectArray) env->CallObjectMethod(assetManager, method, jstringDir);
  406. PHYSFS_EnumerateCallbackResult ret = PHYSFS_ENUM_OK;
  407. if (env->ExceptionCheck())
  408. {
  409. // IOException occured
  410. ret = PHYSFS_ENUM_ERROR;
  411. env->ExceptionClear();
  412. }
  413. else
  414. {
  415. jsize i = 0;
  416. jsize len = env->GetArrayLength(dir);
  417. while (ret == PHYSFS_ENUM_OK && i < len) {
  418. jstring jstr = (jstring) env->GetObjectArrayElement(dir, i++);
  419. const char *name = env->GetStringUTFChars(jstr, nullptr);
  420. ret = cb(callbackdata, origdir, name);
  421. env->ReleaseStringUTFChars(jstr, name);
  422. env->DeleteLocalRef(jstr);
  423. }
  424. env->DeleteLocalRef(dir);
  425. }
  426. env->DeleteLocalRef(jstringDir);
  427. env->DeleteLocalRef(clazz);
  428. return ret;
  429. }
  430. PHYSFS_Io *openRead(void *opaque, const char *name)
  431. {
  432. AAssetManager *assetManager = (AAssetManager *) opaque;
  433. AAsset *file = AAssetManager_open(assetManager, name, AASSET_MODE_UNKNOWN);
  434. if (file == nullptr)
  435. {
  436. PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
  437. return nullptr;
  438. }
  439. PHYSFS_setErrorCode(PHYSFS_ERR_OK);
  440. return io::fromAAsset(assetManager, name, file);
  441. }
  442. PHYSFS_Io *openWriteAppend(void *opaque, const char *name)
  443. {
  444. LOVE_UNUSED(opaque);
  445. LOVE_UNUSED(name);
  446. // AAsset doesn't support modification
  447. PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
  448. return nullptr;
  449. }
  450. int removeMkdir(void *opaque, const char *name)
  451. {
  452. LOVE_UNUSED(opaque);
  453. LOVE_UNUSED(name);
  454. // AAsset doesn't support modification
  455. PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
  456. return 0;
  457. }
  458. int stat(void *opaque, const char *name, PHYSFS_Stat *out)
  459. {
  460. LOVE_UNUSED(opaque);
  461. auto result = io::fileTree.find(name);
  462. if (result != io::fileTree.end())
  463. {
  464. out->filetype = result->second;
  465. out->modtime = -1;
  466. out->createtime = -1;
  467. out->accesstime = -1;
  468. out->readonly = 1;
  469. PHYSFS_setErrorCode(PHYSFS_ERR_OK);
  470. return 1;
  471. }
  472. else
  473. {
  474. PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
  475. return 0;
  476. }
  477. }
  478. void closeArchive(void *opaque)
  479. {
  480. // Do nothing
  481. LOVE_UNUSED(opaque);
  482. PHYSFS_setErrorCode(PHYSFS_ERR_OK);
  483. }
  484. static PHYSFS_Archiver g_AAssetArchiver = {
  485. 0,
  486. {
  487. "AASSET",
  488. "Android AAsset Wrapper",
  489. "LOVE Development Team",
  490. "https://developer.android.com/ndk/reference/group/asset",
  491. 0
  492. },
  493. openArchive,
  494. enumerate,
  495. openRead,
  496. openWriteAppend,
  497. openWriteAppend,
  498. removeMkdir,
  499. removeMkdir,
  500. stat,
  501. closeArchive
  502. };
  503. static PHYSFS_sint64 dummyReturn0(PHYSFS_Io *io)
  504. {
  505. LOVE_UNUSED(io);
  506. PHYSFS_setErrorCode(PHYSFS_ERR_OK);
  507. return 0;
  508. }
  509. static PHYSFS_Io *getDummyIO(PHYSFS_Io *io);
  510. static char dummyOpaque[] = "ASET";
  511. static PHYSFS_Io dummyIo = {
  512. 0,
  513. dummyOpaque,
  514. nullptr,
  515. nullptr,
  516. [](PHYSFS_Io *io, PHYSFS_uint64 offset) -> int
  517. {
  518. PHYSFS_setErrorCode(offset == 0 ? PHYSFS_ERR_OK : PHYSFS_ERR_PAST_EOF);
  519. return offset == 0;
  520. },
  521. dummyReturn0,
  522. dummyReturn0,
  523. getDummyIO,
  524. nullptr,
  525. [](PHYSFS_Io *io) { LOVE_UNUSED(io); }
  526. };
  527. static PHYSFS_Io *getDummyIO(PHYSFS_Io *io)
  528. {
  529. return &dummyIo;
  530. }
  531. }
  532. static bool isVirtualArchiveInitialized = false;
  533. bool initializeVirtualArchive()
  534. {
  535. if (isVirtualArchiveInitialized)
  536. return true;
  537. if (!PHYSFS_registerArchiver(&aasset::g_AAssetArchiver))
  538. return false;
  539. if (!PHYSFS_mountIo(&aasset::dummyIo, "ASET.AASSET", nullptr, 0))
  540. {
  541. PHYSFS_deregisterArchiver(aasset::g_AAssetArchiver.info.extension);
  542. return false;
  543. }
  544. isVirtualArchiveInitialized = true;
  545. return true;
  546. }
  547. void deinitializeVirtualArchive()
  548. {
  549. if (isVirtualArchiveInitialized)
  550. {
  551. PHYSFS_deregisterArchiver(aasset::g_AAssetArchiver.info.extension);
  552. isVirtualArchiveInitialized = false;
  553. }
  554. }
  555. bool checkFusedGame(void **physfsIO_Out)
  556. {
  557. PHYSFS_Io *&io = *(PHYSFS_Io **) physfsIO_Out;
  558. AAssetManager *assetManager = getAssetManager();
  559. // Prefer main.lua inside assets/ folder
  560. AAsset *asset = AAssetManager_open(assetManager, "main.lua", AASSET_MODE_STREAMING);
  561. if (asset)
  562. {
  563. AAsset_close(asset);
  564. io = nullptr;
  565. return true;
  566. }
  567. // If there's no main.lua inside assets/ try game.love
  568. asset = AAssetManager_open(assetManager, "game.love", AASSET_MODE_RANDOM);
  569. if (asset)
  570. {
  571. io = aasset::io::fromAAsset(assetManager, "game.love", asset);
  572. return true;
  573. }
  574. // Not found
  575. return false;
  576. }
  577. const char *getCRequirePath()
  578. {
  579. static bool initialized = false;
  580. static std::string path;
  581. if (!initialized)
  582. {
  583. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  584. jobject activity = (jobject) SDL_AndroidGetActivity();
  585. jclass clazz = env->GetObjectClass(activity);
  586. static jmethodID getCRequireMethod = env->GetMethodID(clazz, "getCRequirePath", "()Ljava/lang/String;");
  587. jstring cpath = (jstring) env->CallObjectMethod(activity, getCRequireMethod);
  588. const char *utf = env->GetStringUTFChars(cpath, nullptr);
  589. if (utf)
  590. {
  591. path = utf;
  592. env->ReleaseStringUTFChars(cpath, utf);
  593. }
  594. env->DeleteLocalRef(cpath);
  595. env->DeleteLocalRef(activity);
  596. env->DeleteLocalRef(clazz);
  597. }
  598. return path.c_str();
  599. }
  600. int getFDFromContentProtocol(const char *path)
  601. {
  602. int fd = -1;
  603. if (strstr(path, "content://") == path)
  604. {
  605. JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
  606. jobject activity = (jobject) SDL_AndroidGetActivity();
  607. jclass clazz = env->GetObjectClass(activity);
  608. static jmethodID converter = env->GetMethodID(clazz, "convertToFileDescriptor", "(Ljava/lang/String;)I");
  609. jstring uri = env->NewStringUTF(path);
  610. fd = (int) env->CallIntMethod(activity, converter, uri);
  611. env->DeleteLocalRef(uri);
  612. env->DeleteLocalRef(clazz);
  613. env->DeleteLocalRef(activity);
  614. }
  615. return fd;
  616. }
  617. int getFDFromLoveProtocol(const char *path)
  618. {
  619. constexpr const char PROTOCOL[] = "love2d://fd/";
  620. constexpr size_t PROTOCOL_LEN = sizeof(PROTOCOL) - 1;
  621. if (*path == '/')
  622. path++;
  623. if (memcmp(path, PROTOCOL, PROTOCOL_LEN) == 0)
  624. {
  625. try
  626. {
  627. return std::stoi(path + PROTOCOL_LEN, nullptr, 10);
  628. }
  629. catch (std::logic_error &)
  630. { }
  631. }
  632. return -1;
  633. }
  634. class FileDescriptorTracker: public love::Object
  635. {
  636. public:
  637. explicit FileDescriptorTracker(int fd): Object(), fd(fd) {}
  638. ~FileDescriptorTracker() override { close(fd); }
  639. int getFd() const { return fd; }
  640. private:
  641. int fd;
  642. };
  643. struct FileDescriptorIO
  644. {
  645. FileDescriptorTracker *fd;
  646. off64_t size;
  647. off64_t offset;
  648. };
  649. void *getIOFromFD(int fd)
  650. {
  651. if (fd == -1)
  652. return nullptr;
  653. // Create file descriptor IO structure
  654. FileDescriptorIO *fdio = new FileDescriptorIO();
  655. fdio->size = lseek64(fd, 0, SEEK_END);
  656. fdio->offset = 0;
  657. lseek64(fd, 0, SEEK_SET);
  658. if (fdio->size == -1)
  659. {
  660. // Cannot get size
  661. delete fdio;
  662. return nullptr;
  663. }
  664. fdio->fd = new FileDescriptorTracker(fd);
  665. PHYSFS_Io *io = new PHYSFS_Io();
  666. io->version = 0;
  667. io->opaque = fdio;
  668. io->read = [](PHYSFS_Io *io, void *buf, PHYSFS_uint64 size)
  669. {
  670. FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
  671. ssize_t ret = pread64(fdio->fd->getFd(), buf, (size_t) size, fdio->offset);
  672. if (ret == -1)
  673. PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR);
  674. else
  675. fdio->offset = std::min(fdio->offset + (off64_t) ret, fdio->size);
  676. return (PHYSFS_sint64) ret;
  677. };
  678. io->write = nullptr;
  679. io->seek = [](PHYSFS_Io *io, PHYSFS_uint64 offset)
  680. {
  681. FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
  682. fdio->offset = std::min(std::max<off64_t>((off64_t) offset, 0), fdio->size);
  683. // Always success
  684. return 1;
  685. };
  686. io->tell = [](PHYSFS_Io *io)
  687. {
  688. FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
  689. return (PHYSFS_sint64) fdio->offset;
  690. };
  691. io->length = [](PHYSFS_Io *io)
  692. {
  693. FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
  694. return (PHYSFS_sint64) fdio->size;
  695. };
  696. io->duplicate = [](PHYSFS_Io *io)
  697. {
  698. FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
  699. FileDescriptorIO *fdio2 = new FileDescriptorIO();
  700. PHYSFS_Io *io2 = new PHYSFS_Io();
  701. fdio->fd->retain();
  702. // Copy data
  703. *fdio2 = *fdio;
  704. *io2 = *io;
  705. io2->opaque = fdio2;
  706. return io2;
  707. };
  708. io->flush = nullptr;
  709. io->destroy = [](PHYSFS_Io *io)
  710. {
  711. FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
  712. fdio->fd->release();
  713. delete fdio;
  714. delete io;
  715. };
  716. return io;
  717. }
  718. } // android
  719. } // love
  720. #endif // LOVE_ANDROID