Skip to content

Commit

Permalink
Android: Be able to have assets in a zip file
Browse files Browse the repository at this point in the history
  • Loading branch information
project64 committed Feb 6, 2023
1 parent f79a2e9 commit cd7b0b4
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 360 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -96,3 +96,4 @@ Thumbs.db
/Source/Project64-input/Version.h
/Source/Project64-video/Version.h
/Source/RSP/Version.h
/Android/app/src/main/assets/assets.zip
4 changes: 4 additions & 0 deletions Android/Script/buildAssets.cmd
Expand Up @@ -33,6 +33,10 @@ IF NOT EXIST "%base_dir%/Android/assets/project64_data/Config/Enhancements/" mkd
xcopy "%base_dir%/Config/Enhancements" "%base_dir%/Android/assets/project64_data/Config/Enhancements/" /D /I /F /Y /E
IF %ERRORLEVEL% NEQ 0 (exit /B 1)

IF NOT EXIST "%base_dir%/Android/app/src/main/assets/" mkdir "%base_dir%/Android/app/src/main/assets/"
IF EXIST "%base_dir%/Android/app/src/main/assets/assets.zip" del "%base_dir%\Android\app\src\main\assets\assets.zip"
powershell Compress-Archive "%base_dir%/Android/assets/*" "%base_dir%/Android/app/src/main/assets/assets.zip"

goto :end

:End
Expand Down
62 changes: 15 additions & 47 deletions Android/app/src/main/java/emu/project64/SplashActivity.java
@@ -1,25 +1,22 @@
package emu.project64;

import java.io.File;
import java.io.IOException;
import java.util.List;
import emu.project64.R;
import emu.project64.jni.NativeExports;
import emu.project64.jni.SettingsID;
import emu.project64.jni.UISettingID;
import emu.project64.task.ExtractAssetsTask;
import emu.project64.task.ExtractAssetsTask.ExtractAssetsListener;
import emu.project64.task.ExtractAssetZipTask;
import emu.project64.task.ExtractAssetZipTask.ExtractAssetZipListener;
import emu.project64.task.ExtractAssetsTask.Failure;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback;
import androidx.core.content.ContextCompat;
Expand All @@ -30,7 +27,7 @@
import android.view.WindowManager.LayoutParams;
import android.widget.TextView;

public class SplashActivity extends AppCompatActivity implements ExtractAssetsListener, OnRequestPermissionsResultCallback
public class SplashActivity extends AppCompatActivity implements ExtractAssetZipListener, OnRequestPermissionsResultCallback
{
static final int PERMISSION_REQUEST = 177;
static final int NUM_PERMISSIONS = 2;
Expand Down Expand Up @@ -251,58 +248,29 @@ public void run()
}
};

private boolean CountTotalAssetFiles(String path)
{
String [] list;
try
{
list = getAssets().list(path);
if (list.length > 0)
{
for (String file : list)
{
if (!CountTotalAssetFiles(path + "/" + file))
{
return false;
}
else
{
TOTAL_ASSETS += 1;
}
}
}
}
catch (IOException e)
{
return false;
}
return true;
}

private final Runnable extractAssetsTaskLauncher = new Runnable()
{
@Override
public void run()
{
Log.i( "Splash", "extractAssetsTaskLauncher - start");
TOTAL_ASSETS = 0;
CountTotalAssetFiles(SOURCE_DIR);
mAssetsExtracted = 0;
new ExtractAssetsTask( getAssets(), SOURCE_DIR, AndroidDevice.PACKAGE_DIRECTORY, SplashActivity.this ).execute();
new ExtractAssetZipTask( getAssets(), AndroidDevice.PACKAGE_DIRECTORY, SplashActivity.this ).execute();
}
};

@Override
public void onExtractAssetsProgress( String nextFileToExtract )
public void onExtractAssetsProgress( String text )
{
final float percent = ( 100f * mAssetsExtracted ) / (float) TOTAL_ASSETS;
final String text = getString( R.string.assetExtractor_progress, percent, nextFileToExtract );
mTextView.setText(text);
mAssetsExtracted++;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(text);
}
});
};

@Override
public void onExtractAssetsFinished( List<Failure> failures )
public void onExtractAssetsFinished( List<ExtractAssetZipTask.Failure> failures )
{
if( failures.size() == 0 )
{
Expand All @@ -322,12 +290,12 @@ public void onExtractAssetsFinished( List<Failure> failures )
String weblink = getResources().getString( R.string.assetExtractor_uriHelp );
String message = getString( R.string.assetExtractor_failed, weblink );
String textHtml = message.replace( "\n", "<br/>" ) + "<p><small>";
for( Failure failure : failures )
for( ExtractAssetZipTask.Failure failure : failures )
{
textHtml += failure.toString() + "<br/>";
}
textHtml += "</small>";
mTextView.setText( Html.fromHtml( textHtml ) );
}
}
};
}
230 changes: 230 additions & 0 deletions Android/app/src/main/java/emu/project64/task/ExtractAssetZipTask.java
@@ -0,0 +1,230 @@
package emu.project64.task;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;

public class ExtractAssetZipTask extends AsyncTask<Void, String, List<ExtractAssetZipTask.Failure>>
{
public interface ExtractAssetZipListener
{
public void onExtractAssetsProgress( String nextFileToExtract );
public void onExtractAssetsFinished( List<Failure> failures );
}

public ExtractAssetZipTask( AssetManager assetManager, String dstPath, ExtractAssetZipListener listener)
{
if (assetManager == null )
throw new IllegalArgumentException( "Asset manager cannot be null" );
if( TextUtils.isEmpty( dstPath ) )
throw new IllegalArgumentException( "Destination path cannot be null or empty" );

mAssetManager = assetManager;
mDstPath = dstPath;
mListener = listener;
}

private final AssetManager mAssetManager;
private final String mDstPath;
private final ExtractAssetZipListener mListener;

@Override
protected List<Failure> doInBackground( Void... params )
{
return extractAssets( mDstPath );
}

@Override
protected void onProgressUpdate( String... values )
{
mListener.onExtractAssetsProgress( values[0] );
}

@Override
protected void onPostExecute( List<ExtractAssetZipTask.Failure> result )
{
mListener.onExtractAssetsFinished( result );
}

public static final class Failure
{
public enum Reason
{
FILE_UNWRITABLE,
FILE_UNCLOSABLE,
ASSET_UNCLOSABLE,
ASSET_IO_EXCEPTION,
FILE_IO_EXCEPTION,
}

public final String srcPath;
public final String dstPath;
public final Reason reason;
public Failure( String srcPath, String dstPath, Reason reason )
{
this.srcPath = srcPath;
this.dstPath = dstPath;
this.reason = reason;
}

@Override
public String toString()
{
switch( reason )
{
case FILE_UNWRITABLE:
return "Failed to open file " + dstPath;
case FILE_UNCLOSABLE:
return "Failed to close file " + dstPath;
case ASSET_UNCLOSABLE:
return "Failed to close asset " + srcPath;
case ASSET_IO_EXCEPTION:
return "Failed to extract asset " + srcPath + " to file " + dstPath;
case FILE_IO_EXCEPTION:
return "Failed to add file " + srcPath + " to file " + dstPath;
default:
return "Failed using source " + srcPath + " and destination " + dstPath;
}
}
}

private List<Failure> extractAssets( String dstPath )
{
final List<Failure> failures = new ArrayList<Failure>();

// Ensure the parent directories exist
File root = new File(dstPath + "/");
if (!root.exists()) {
root.mkdirs();
}

String dstFile = dstPath + "/assets.zip";
String srcFile = "assets.zip";
// Call the progress listener before extracting
publishProgress( dstPath );

// IO objects, initialize null to eliminate lint error
OutputStream out = null;
InputStream in = null;

Boolean ExtractZip = false;
// Extract the file
try
{
out = new FileOutputStream( dstFile );
in = mAssetManager.open( srcFile);
byte[] buffer = new byte[1024];
int read;

mListener.onExtractAssetsProgress( "copying asset zip file" );
while( ( read = in.read( buffer ) ) != -1 )
{
out.write( buffer, 0, read );
}
mListener.onExtractAssetsProgress( "Finished copying assset zip" );
out.flush();
ExtractZip = true;
}
catch( FileNotFoundException e )
{
Failure failure = new Failure( srcFile, dstPath, Failure.Reason.FILE_UNWRITABLE );
Log.e( "ExtractAssetZipTask", failure.toString() );
failures.add( failure );
}
catch( IOException e )
{
Failure failure = new Failure( srcFile, dstPath, Failure.Reason.ASSET_IO_EXCEPTION );
Log.e( "ExtractAssetZipTask", failure.toString() );
failures.add( failure );
}
finally
{
if( out != null )
{
try
{
out.close();
}
catch( IOException e )
{
Failure failure = new Failure( srcFile, dstPath, Failure.Reason.FILE_UNCLOSABLE );
Log.e( "ExtractAssetZipTask", failure.toString() );
failures.add( failure );
}
}
if( in != null )
{
try
{
in.close();
}
catch( IOException e )
{
Failure failure = new Failure( srcFile, dstPath, Failure.Reason.ASSET_UNCLOSABLE );
Log.e( "ExtractAssetZipTask", failure.toString() );
failures.add( failure );
}
}
}

if (ExtractZip)
{
String zipFilePath = dstPath + "/assets.zip";
String destDir = dstPath + "/";
File dir = new File(destDir);
// create output directory if it doesn't exist
if(!dir.exists()) dir.mkdirs();
FileInputStream fis;
//buffer for read and write data to file
byte[] buffer = new byte[1024];
try {
fis = new FileInputStream(zipFilePath);
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry ze = zis.getNextEntry();
while(ze != null){
String fileName = ze.getName();
File newFile = new File(destDir + File.separator + fileName.replace("\\", "/"));
mListener.onExtractAssetsProgress( "Unzipping "+fileName );

//create directories for sub directories in zip
new File(newFile.getParent()).mkdirs();
if (ze.getSize() != 0) {
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
}
//close this ZipEntry
zis.closeEntry();
ze = zis.getNextEntry();
}
//close last ZipEntry
zis.closeEntry();
zis.close();
fis.close();
} catch (IOException e) {
Failure failure = new Failure( srcFile, dstPath, Failure.Reason.ASSET_IO_EXCEPTION );
Log.e( "ExtractAssetZipTask", failure.toString() );
failures.add( failure );
}
}
return failures;
}
}

0 comments on commit cd7b0b4

Please sign in to comment.