From 0d5aa705f083e55c9ec7bf4be99767f3a5bd07b9 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 31 Jul 2022 22:52:26 +0300 Subject: [PATCH] Add activity to import game assets on Android Fixes #105 --- README.md | 10 +-- os/android/app/build.gradle | 1 + os/android/app/src/main/AndroidManifest.xml | 4 + .../fallout2ce/ImportActivity.java | 89 +++++++++++++++++++ .../alexbatalov/fallout2ce/MainActivity.java | 39 ++++++-- 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 os/android/app/src/main/java/com/alexbatalov/fallout2ce/ImportActivity.java diff --git a/README.md b/README.md index beaaf8a..be2d960 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,11 @@ $ sudo apt install libsdl2-2.0-0 > **NOTE**: Fallout 2 was designed with mouse in mind. There are many controls that require precise cursor positioning, which is not possible with fingers. When playing on Android you'll use fingers to move mouse cursor, not a character, or a map. Double tap to "click" left mouse button in the current cursor position, triple tap to "click" right mouse button. It might feel awkward at first, but it's super handy - you can play with just a thumb. This is not set in stone and might change in the future. +- Use Windows installation as a base - it contains data assets needed to play. Copy `Fallout2` folder to your device, for example to `Downloads`. You need `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder. + - Download `fallout2-ce.apk` and copy it to your device. Open it with file explorer, follow instructions (install from unknown source). -- Run the game once, it will say `Couldn't find/load text fonts` and create a folder for data assets. - -- Open file explorer, navigate to `Android/data/com.alexbatalov.fallout2ce/files`, delete junk folders inside (they will be named as game files, just delete them). - -- Copy `master.dat`, `critter.dat`, `patch000.dat` and `data` folder from your Windows/macOS installation to the folder above. - -- Run the game again. +- When you run the game for the first time it will immediately present file picker. Select the folder from the first step. Wait until this data is copied. There is no fancy importing interface (yet), just wait for about 30 seconds. The game will start automatically. ## Contributing diff --git a/os/android/app/build.gradle b/os/android/app/build.gradle index e5f4ff0..fa74d14 100644 --- a/os/android/app/build.gradle +++ b/os/android/app/build.gradle @@ -96,4 +96,5 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.documentfile:documentfile:1.0.1' } diff --git a/os/android/app/src/main/AndroidManifest.xml b/os/android/app/src/main/AndroidManifest.xml index d2e1565..30b5ac2 100644 --- a/os/android/app/src/main/AndroidManifest.xml +++ b/os/android/app/src/main/AndroidManifest.xml @@ -92,6 +92,10 @@ --> + + + diff --git a/os/android/app/src/main/java/com/alexbatalov/fallout2ce/ImportActivity.java b/os/android/app/src/main/java/com/alexbatalov/fallout2ce/ImportActivity.java new file mode 100644 index 0000000..9d602e4 --- /dev/null +++ b/os/android/app/src/main/java/com/alexbatalov/fallout2ce/ImportActivity.java @@ -0,0 +1,89 @@ +package com.alexbatalov.fallout2ce; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +import androidx.documentfile.provider.DocumentFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class ImportActivity extends Activity { + private static final int IMPORT_REQUEST_CODE = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + startActivityForResult(intent, IMPORT_REQUEST_CODE); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent resultData) { + if (requestCode == IMPORT_REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + final Uri treeUri = resultData.getData(); + if (treeUri != null) { + final DocumentFile treeDocument = DocumentFile.fromTreeUri(this, treeUri); + if (treeDocument != null) { + copyRecursively(treeDocument, getExternalFilesDir(null)); + + final Intent intent = new Intent(this, MainActivity.class); + startActivity(intent); + } + } + } + + finish(); + } else { + super.onActivityResult(requestCode, resultCode, resultData); + } + } + + private boolean copyRecursively(DocumentFile src, File dest) { + final DocumentFile[] documentFiles = src.listFiles(); + for (final DocumentFile documentFile : documentFiles) { + if (documentFile.isFile()) { + if (!copyFile(documentFile, new File(dest, documentFile.getName()))) { + return false; + } + } else if (documentFile.isDirectory()) { + final File subdirectory = new File(dest, documentFile.getName()); + if (!subdirectory.exists()) { + subdirectory.mkdir(); + } + + if (!copyRecursively(documentFile, subdirectory)) { + return false; + } + } + } + return true; + } + + private boolean copyFile(DocumentFile src, File dest) { + try { + final InputStream inputStream = getContentResolver().openInputStream(src.getUri()); + final OutputStream outputStream = new FileOutputStream(dest); + + final byte[] buffer = new byte[16384]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + inputStream.close(); + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + return true; + } +} diff --git a/os/android/app/src/main/java/com/alexbatalov/fallout2ce/MainActivity.java b/os/android/app/src/main/java/com/alexbatalov/fallout2ce/MainActivity.java index 5ab4ad0..093b940 100644 --- a/os/android/app/src/main/java/com/alexbatalov/fallout2ce/MainActivity.java +++ b/os/android/app/src/main/java/com/alexbatalov/fallout2ce/MainActivity.java @@ -1,18 +1,47 @@ package com.alexbatalov.fallout2ce; +import android.content.Intent; import android.os.Bundle; import org.libsdl.app.SDLActivity; -public class MainActivity extends SDLActivity { - protected void onCreate(Bundle savedInstanceState) { - // Needed to initialize `files` folder (and make it publicly accessible - // for file managers) for user to upload assets. - getExternalFilesDir(null); +import java.io.File; +public class MainActivity extends SDLActivity { + private boolean noExit = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + final File externalFilesDir = getExternalFilesDir(null); + + final File configFile = new File(externalFilesDir, "fallout2.cfg"); + if (!configFile.exists()) { + final File masterDatFile = new File(externalFilesDir, "master.dat"); + final File critterDatFile = new File(externalFilesDir, "critter.dat"); + if (!masterDatFile.exists() || !critterDatFile.exists()) { + final Intent intent = new Intent(this, ImportActivity.class); + startActivity(intent); + + noExit = true; + finish(); + } + } } + @Override + protected void onDestroy() { + super.onDestroy(); + + if (!noExit) { + // Needed to make sure libc calls exit handlers, which releases + // in-game resources. + System.exit(0); + } + } + + @Override protected String[] getLibraries() { return new String[]{ "fallout2-ce",