From 957330d722db21a389063084a3f7cb059c4d19b6 Mon Sep 17 00:00:00 2001 From: cigumo Date: Thu, 16 Nov 2017 19:53:41 -0300 Subject: [PATCH 1/2] Changes Filesystem::setSource mount order on Android Simplifies the way setSource is handled on Android. It tries to mount first and if that fails it will copy to memory and mount. The /sdcard/lovegame fallback is moved to GameActivity.java. Also a flag is added (mustCacheArchive) that will copy the game.love file to the cache dir, so it can be mounted directly and not loaded to memory, which does not work for large .love files. The Filesystem.cpp change was also submitted to rude/love repo in this push request: https://bitbucket.org/rude/love/pull-requests/99 --- .../modules/filesystem/physfs/Filesystem.cpp | 62 ++++--------- .../java/org/love2d/android/GameActivity.java | 88 +++++++++++++++++-- 2 files changed, 98 insertions(+), 52 deletions(-) diff --git a/love/src/jni/love/src/modules/filesystem/physfs/Filesystem.cpp b/love/src/jni/love/src/modules/filesystem/physfs/Filesystem.cpp index 4d19d58f..3da360e0 100644 --- a/love/src/jni/love/src/modules/filesystem/physfs/Filesystem.cpp +++ b/love/src/jni/love/src/modules/filesystem/physfs/Filesystem.cpp @@ -233,57 +233,29 @@ bool Filesystem::setSource(const char *source) if (!love::android::createStorageDirectories()) SDL_Log("Error creating storage directories!"); - char* game_archive_ptr = NULL; - size_t game_archive_size = 0; - bool archive_loaded = false; + new_search_path = love::android::getSelectedGameFile(); - // try to load the game that was sent to LÖVE via a Intent - archive_loaded = love::android::loadGameArchiveToMemory(love::android::getSelectedGameFile(), &game_archive_ptr, &game_archive_size); - - if (!archive_loaded) + // try mounting first, if that fails, load to memory and mount + if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) { - // try to load the game in the assets/ folder - archive_loaded = love::android::loadGameArchiveToMemory("game.love", &game_archive_ptr, &game_archive_size); - } - - if (archive_loaded) - { - if (!PHYSFS_mountMemory(game_archive_ptr, game_archive_size, love::android::freeGameArchiveMemory, "archive.zip", "/", 0)) + // PHYSFS cannot yet mount a zip file inside an .apk + SDL_Log("Mounting %s did not work. Loading to memory.", + new_search_path.c_str()); + char* game_archive_ptr = NULL; + size_t game_archive_size = 0; + if (!love::android::loadGameArchiveToMemory( + new_search_path.c_str(), &game_archive_ptr, + &game_archive_size)) { - SDL_Log("Mounting of in-memory game archive failed!"); - love::android::freeGameArchiveMemory(game_archive_ptr); + SDL_Log("Failure memory loading archive %s", new_search_path.c_str()); return false; } - } - else - { - // try to load the game in the directory that was sent to LÖVE via an - // Intent ... - std::string game_path = std::string(love::android::getSelectedGameFile()); - - if (game_path == "") + if (!PHYSFS_mountMemory( + game_archive_ptr, game_archive_size, + love::android::freeGameArchiveMemory, "archive.zip", "/", 0)) { - // ... or fall back to the game at /sdcard/lovegame - game_path = "/sdcard/lovegame/"; - } - - SDL_RWops *sdcard_main = SDL_RWFromFile(std::string(game_path + "main.lua").c_str(), "rb"); - - if (sdcard_main) - { - new_search_path = game_path; - sdcard_main->close(sdcard_main); - - if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) - { - SDL_Log("mounting of %s failed", new_search_path.c_str()); - return false; - } - } - else - { - // Neither assets/game.love or /sdcard/lovegame was mounted - // sucessfully, therefore simply fail. + SDL_Log("Failure mounting in-memory archive."); + love::android::freeGameArchiveMemory(game_archive_ptr); return false; } } diff --git a/love/src/main/java/org/love2d/android/GameActivity.java b/love/src/main/java/org/love2d/android/GameActivity.java index 215fe830..3d8e5884 100644 --- a/love/src/main/java/org/love2d/android/GameActivity.java +++ b/love/src/main/java/org/love2d/android/GameActivity.java @@ -2,9 +2,11 @@ package org.love2d.android; import org.libsdl.app.SDLActivity; +import java.util.Arrays; import java.util.List; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -39,7 +41,7 @@ public class GameActivity extends SDLActivity { private static Context context; private static Vibrator vibrator = null; private static boolean immersiveActive = false; - + private static boolean mustCacheArchive = false; @Override protected String[] getLibraries() { return new String[] { @@ -76,14 +78,13 @@ public class GameActivity extends SDLActivity { handleIntent (intent); resetNative(); startNative(); - }; + } protected void handleIntent (Intent intent) { Uri game = intent.getData(); if (game != null) { if (game.getScheme().equals ("file")) { Log.d("GameActivity", "Received intent with path: " + game.getPath()); - // If we were given the path of a main.lua then use its // directory. Otherwise use full path. List path_segments = game.getPathSegments(); @@ -93,12 +94,36 @@ public class GameActivity extends SDLActivity { gamePath = game.getPath(); } } else { - copyGameToCache (game); + // if archive exists, copy it to the cache + copyGameToCache (game); + return; } - Log.d("GameActivity", "new gamePath: " + gamePath); + + } else { + boolean archiveExists = false; + try { + List assets = Arrays.asList(getAssets().list("")); + archiveExists = assets.contains("game.love"); + } catch (Exception e) { + Log.d("GameActivity", "could not list application assets:" + e.getMessage()); + } + if (archiveExists) { + String df = this.getCacheDir().getPath()+"/game.love"; + if (mustCacheArchive && copyAssetFile("game.love",df)) + gamePath = df; + else + gamePath = "game.love"; + } else { + File ext = Environment.getExternalStorageDirectory(); + if ((new File(ext,"/lovegame/main.lua")).exists()) { + gamePath = ext.getPath() + "/lovegame/"; + } + } + + Log.d("GameActivity", "new gamePath: " + gamePath); } - }; + } @Override protected void onDestroy() { @@ -198,7 +223,56 @@ public class GameActivity extends SDLActivity { i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); } - + + boolean copyAssetFile(String fileName, String destF) + { + boolean success = false; + + // open streams + BufferedOutputStream bos = null; + try { + bos = new BufferedOutputStream(new FileOutputStream(destF, false)); + } catch (IOException e) { + Log.d ("GameActivity", "Could not open destination file:" + e.getMessage()); + } + InputStream ins = null; + try { + ins = getAssets().open(fileName); + } catch (IOException e) { + Log.d("GameActivity", "Could not open game file:" + e.getMessage()); + } + + // copy + int bytes_written = 0; + if (ins != null && bos != null) { + int chunk_read = 0; + try { + byte[] buf = new byte[8192]; + chunk_read = ins.read(buf); + do { + bos.write(buf, 0, chunk_read); + bytes_written += chunk_read; + chunk_read = ins.read(buf); + } while(chunk_read != -1); + } catch (IOException e) { + Log.d ("GameActivity", "Copying failed:" + e.getMessage()); + } + } + + // close streams + try { + if (ins != null) ins.close(); + if (bos != null) bos.close(); + success = true; + } catch (IOException e) { + Log.d ("GameActivity", "Copying failed: " + e.getMessage()); + } + + Log.d("GameActivity", "Copied " + fileName + " to " + destF + + ". Wrote:" + bytes_written + " bytes"); + return success; + } + void copyGameToCache (Uri sourceuri) { String destinationFilename = this.getCacheDir().getPath()+"/downloaded.love"; From 0afdf271a24db1cd18ebe988616e643bb5789671 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Mon, 22 Jan 2018 22:52:16 +0100 Subject: [PATCH 2/2] Cleaned up logic of loading games on the Java side of the App --- .../java/org/love2d/android/GameActivity.java | 173 ++++++++---------- 1 file changed, 75 insertions(+), 98 deletions(-) diff --git a/love/src/main/java/org/love2d/android/GameActivity.java b/love/src/main/java/org/love2d/android/GameActivity.java index 3d8e5884..b74c9120 100644 --- a/love/src/main/java/org/love2d/android/GameActivity.java +++ b/love/src/main/java/org/love2d/android/GameActivity.java @@ -15,6 +15,7 @@ import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; +import android.app.AlertDialog; import android.app.DownloadManager; import android.app.ProgressDialog; import android.content.Context; @@ -82,7 +83,12 @@ public class GameActivity extends SDLActivity { protected void handleIntent (Intent intent) { Uri game = intent.getData(); + if (game != null) { + // If we have a game via the intent data we we try to figure out how we have to load it. We + // support the following variations: + // * a main.lua file: set gamePath to the directory containing main.lua + // * otherwise: set gamePath to the file if (game.getScheme().equals ("file")) { Log.d("GameActivity", "Received intent with path: " + game.getPath()); // If we were given the path of a main.lua then use its @@ -94,35 +100,51 @@ public class GameActivity extends SDLActivity { gamePath = game.getPath(); } } else { - // if archive exists, copy it to the cache - copyGameToCache (game); - return; - } - Log.d("GameActivity", "new gamePath: " + gamePath); + Log.e("GameActivity", "Unsupported scheme: '" + game.getScheme() + "'."); - } else { - boolean archiveExists = false; + AlertDialog.Builder alert_dialog = new AlertDialog.Builder(this); + alert_dialog.setMessage("Could not load LÖVE game '" + game.getPath() + + "' as it uses unsupported scheme '" + game.getScheme() + + "'. Please contact the developer."); + alert_dialog.setTitle("LÖVE for Android Error"); + alert_dialog.setPositiveButton("Exit", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + finish(); + } + }); + alert_dialog.setCancelable(false); + alert_dialog.create().show(); + } + } else { + // No game specified via the intent data -> check whether we have a game.love in our assets. + boolean game_love_in_assets = false; try { List assets = Arrays.asList(getAssets().list("")); - archiveExists = assets.contains("game.love"); + game_love_in_assets = assets.contains("game.love"); } catch (Exception e) { Log.d("GameActivity", "could not list application assets:" + e.getMessage()); } - if (archiveExists) { - String df = this.getCacheDir().getPath()+"/game.love"; - if (mustCacheArchive && copyAssetFile("game.love",df)) - gamePath = df; + + if (game_love_in_assets) { + // If we have a game.love in our assets folder copy it to the cache folder + // so that we can load it from native LÖVE code + String destination_file = this.getCacheDir().getPath()+"/game.love"; + if (mustCacheArchive && copyAssetFile("game.love", destination_file)) + gamePath = destination_file; else gamePath = "game.love"; } else { + // If no game.love was found fall back to the game in /lovegame File ext = Environment.getExternalStorageDirectory(); if ((new File(ext,"/lovegame/main.lua")).exists()) { gamePath = ext.getPath() + "/lovegame/"; } } - - Log.d("GameActivity", "new gamePath: " + gamePath); } + + Log.d("GameActivity", "new gamePath: " + gamePath); } @Override @@ -224,104 +246,59 @@ public class GameActivity extends SDLActivity { context.startActivity(i); } - boolean copyAssetFile(String fileName, String destF) + /** + * Copies a given file from the assets folder to the destination. + * @return true if successful + */ + boolean copyAssetFile(String fileName, String destinationFileName) { boolean success = false; - // open streams - BufferedOutputStream bos = null; + // open source and destination streams + InputStream source_stream = null; try { - bos = new BufferedOutputStream(new FileOutputStream(destF, false)); + source_stream = getAssets().open(fileName); } catch (IOException e) { - Log.d ("GameActivity", "Could not open destination file:" + e.getMessage()); - } - InputStream ins = null; - try { - ins = getAssets().open(fileName); - } catch (IOException e) { - Log.d("GameActivity", "Could not open game file:" + e.getMessage()); + Log.d("GameActivity", "Could not open game.love from assets: " + e.getMessage()); } - // copy - int bytes_written = 0; - if (ins != null && bos != null) { - int chunk_read = 0; - try { - byte[] buf = new byte[8192]; - chunk_read = ins.read(buf); - do { - bos.write(buf, 0, chunk_read); - bytes_written += chunk_read; - chunk_read = ins.read(buf); - } while(chunk_read != -1); - } catch (IOException e) { - Log.d ("GameActivity", "Copying failed:" + e.getMessage()); - } + BufferedOutputStream destination_stream = null; + try { + destination_stream = new BufferedOutputStream(new FileOutputStream(destinationFileName, false)); + } catch (IOException e) { + Log.d ("GameActivity", "Could not open destination file: " + e.getMessage()); } - + + // perform the copying + int chunk_read = 0; + int bytes_written = 0; + + assert (source_stream != null && destination_stream != null); + + try { + byte[] buf = new byte[1024]; + chunk_read = source_stream.read(buf); + do { + destination_stream.write(buf, 0, chunk_read); + bytes_written += chunk_read; + chunk_read = source_stream.read(buf); + } while (chunk_read != -1); + } catch (IOException e) { + Log.d("GameActivity", "Copying failed:" + e.getMessage()); + } + // close streams try { - if (ins != null) ins.close(); - if (bos != null) bos.close(); + if (source_stream != null) source_stream.close(); + if (destination_stream != null) destination_stream.close(); success = true; } catch (IOException e) { Log.d ("GameActivity", "Copying failed: " + e.getMessage()); } - Log.d("GameActivity", "Copied " + fileName + " to " + destF - + ". Wrote:" + bytes_written + " bytes"); + Log.d("GameActivity", "Successfully copied " + fileName + + " to " + destinationFileName + + " (" + bytes_written + " bytes written)."); return success; } - - void copyGameToCache (Uri sourceuri) - { - String destinationFilename = this.getCacheDir().getPath()+"/downloaded.love"; - gamePath = destinationFilename; - - BufferedOutputStream bos = null; - try { - bos = new BufferedOutputStream(new FileOutputStream(destinationFilename, false)); - } catch (IOException e) { - Log.d ("GameActivity", "Could not open destination file:" + e.getMessage()); - } - - int chunk_read = 0; - int bytes_written = 0; - - BufferedInputStream bis = null; - if (sourceuri.getScheme().equals("content")) { - try { - bis = new BufferedInputStream(getContentResolver().openInputStream(sourceuri)); - } catch (IOException e) { - Log.d ("GameActivity", "Could not open game file:" + e.getMessage()); - } - } else { - Log.d ("GameActivity", "Unsupported scheme: " + sourceuri.getScheme()); - } - - if (bis != null) { - // actual copying - try { - byte[] buf = new byte[1024]; - chunk_read = bis.read(buf); - do { - bos.write(buf, 0, chunk_read); - bytes_written += chunk_read; - chunk_read = bis.read(buf); - } while(chunk_read != -1); - } catch (IOException e) { - Log.d ("GameActivity", "Copying failed:" + e.getMessage()); - } - } - - // close streams - try { - if (bis != null) bis.close(); - if (bos != null) bos.close(); - } catch (IOException e) { - Log.d ("GameActivity", "Copying failed: " + e.getMessage()); - } - - Log.d("GameActivity", "Copied " + bytes_written + " bytes"); - } -} +} \ No newline at end of file