diff --git a/filesystem/android.c b/filesystem/android.c new file mode 100644 index 00000000..f7f0910d --- /dev/null +++ b/filesystem/android.c @@ -0,0 +1,334 @@ +/* +android.c - android support for filesystem +Copyright (C) 2022 Velaron + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "port.h" + +#if XASH_ANDROID + +#include +#include +#include +#include +#include +#include +#include STDINT_H +#include "filesystem_internal.h" +#include "crtlib.h" +#include "xash3d_mathlib.h" +#include "common/com_strings.h" + +#include +#include +#include +#include + +struct android_assets_s +{ + string package_name; + qboolean engine; + AAssetManager *asset_manager; + AAssetDir *dir; +}; + +/* +struct android_saf_s +{ + +}; +*/ + +struct jni_methods_s +{ + JNIEnv *env; + jobject activity; + jclass activity_class; + jmethodID getPackageName; + jmethodID getCallingPackage; + jmethodID getAssetsList; +} jni; + +static void Android_GetAssetManager( android_assets_t *assets ) +{ + jmethodID getAssets; + jobject assetManager; + + getAssets = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssets", "(Z)Landroid/content/res/AssetManager;" ); + assetManager = (*jni.env)->CallObjectMethod( jni.env, jni.activity, getAssets, assets->engine ); + + if( assetManager ) + assets->asset_manager = AAssetManager_fromJava( jni.env, assetManager ); + else if( assets->engine ) + Con_Reportf( S_WARN "Couldn't add engine assets!" ); +} + +static const char *Android_GetPackageName( qboolean engine ) +{ + static string pkg; + jstring resultJNIStr; + const char *resultCStr; + + resultJNIStr = (*jni.env)->CallObjectMethod( jni.env, jni.activity, engine ? jni.getPackageName : jni.getCallingPackage ); + + if( !resultJNIStr ) + return NULL; + + resultCStr = (*jni.env)->GetStringUTFChars( jni.env, resultJNIStr, NULL ); + Q_strncpy( pkg, resultCStr, sizeof( pkg )); + (*jni.env)->ReleaseStringUTFChars( jni.env, resultJNIStr, resultCStr ); + + return pkg; +} + +static void Android_ListDirectory( stringlist_t *list, const char *path, qboolean engine ) +{ + jstring JStr = (*jni.env)->NewStringUTF( jni.env, path ); + jobjectArray JNIArray = (*jni.env)->CallObjectMethod( jni.env, jni.activity, jni.getAssetsList, engine, JStr ); + int JNIArraySize = (*jni.env)->GetArrayLength( jni.env, JNIArray ); + + for( int i = 0; i < JNIArraySize; i++ ) + { + jstring JNIStr = (*jni.env)->GetObjectArrayElement( jni.env, JNIArray, i ); + const char *CStr = (*jni.env)->GetStringUTFChars( jni.env, JNIStr, NULL ); + + stringlistappend( list, (char *)CStr ); + (*jni.env)->ReleaseStringUTFChars( jni.env, JNIStr, CStr ); + } +} + +static void FS_CloseAndroidAssets( android_assets_t *assets ) +{ + if( assets->dir ) + AAssetDir_close( assets->dir ); + + Mem_Free( assets ); +} + +static android_assets_t *FS_LoadAndroidAssets( qboolean engine ) +{ + android_assets_t *assets = (android_assets_t *)Mem_Calloc( fs_mempool, sizeof( android_assets_t )); + memset( assets, 0, sizeof( android_assets_t )); + + assets->engine = engine; + + Android_GetAssetManager( assets ); + if( !assets->asset_manager ) + { + FS_CloseAndroidAssets( assets ); + return NULL; + } + + assets->dir = AAssetManager_openDir( assets->asset_manager, "" ); + if( !assets->dir ) + { + FS_CloseAndroidAssets( assets ); + return NULL; + } + + return assets; +} + +static int FS_FileTime_AndroidAssets( searchpath_t *search, const char *filename ) +{ + static time_t time; + + if( !time ) + { + struct tm file_tm; + + strptime( __DATE__ " "__TIME__, "%b %d %Y %H:%M:%S", &file_tm ); + time = mktime( &file_tm ); + } + + return time; +} + +static int FS_FindFile_AndroidAssets( struct searchpath_s *search, const char *path, char *fixedname, size_t len ) +{ + AAsset *assets = AAssetManager_open( search->assets->asset_manager, path, AASSET_MODE_UNKNOWN ); + + if( assets ) + { + AAsset_close( assets ); + + Q_strncpy( fixedname, path, len ); + return 0; + } + + return -1; +} + +static void FS_PrintInfo_AndroidAssets( searchpath_t *search, char *dst, size_t size ) +{ + Q_snprintf( dst, size, "%s", search->assets->package_name ); +} + +static void FS_Close_AndroidAssets( searchpath_t *search ) +{ + FS_CloseAndroidAssets( search->assets ); +} + +static void FS_Search_AndroidAssets( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) +{ + string temp; + stringlist_t dirlist; + const char *slash, *backslash, *colon, *separator; + int basepathlength, dirlistindex, resultlistindex; + char *basepath; + + slash = Q_strrchr( pattern, '/' ); + backslash = Q_strrchr( pattern, '\\' ); + colon = Q_strrchr( pattern, ':' ); + + separator = Q_max( slash, backslash ); + separator = Q_max( separator, colon ); + + basepathlength = separator ? (separator + 1 - pattern) : 0; + basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); + if( basepathlength ) + memcpy( basepath, pattern, basepathlength ); + basepath[basepathlength] = '\0'; + + stringlistinit( &dirlist ); + Android_ListDirectory( &dirlist, basepath, search->assets->engine ); + + Q_strncpy( temp, basepath, sizeof( temp )); + + for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) + { + Q_strncpy( &temp[basepathlength], dirlist.strings[dirlistindex], sizeof( temp ) - basepathlength ); + + if( matchpattern( temp, (char *)pattern, true )) + { + for( resultlistindex = 0; resultlistindex < list->numstrings; resultlistindex++ ) + { + if( !Q_strcmp( list->strings[resultlistindex], temp )) + break; + } + + if( resultlistindex == list->numstrings ) + stringlistappend( list, temp ); + } + } + + stringlistfreecontents( &dirlist ); + + Mem_Free( basepath ); +} + +static file_t *FS_OpenFile_AndroidAssets( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) +{ + file_t *file = (file_t *)Mem_Calloc( fs_mempool, sizeof( file_t )); + AAsset *assets = AAssetManager_open( search->assets->asset_manager, filename, AASSET_MODE_RANDOM ); + + file->handle = AAsset_openFileDescriptor( assets, &file->offset, &file->real_length ); + + file->position = 0; + file->ungetc = EOF; + + AAsset_close( assets ); + + return file; +} + +static byte *FS_LoadAndroidAssetsFile( searchpath_t *search, const char *path, int pack_ind, fs_offset_t *filesize ) +{ + byte *buf; + off_t size; + AAsset *asset; + + if( filesize ) *filesize = 0; + + asset = AAssetManager_open( search->assets->asset_manager, path, AASSET_MODE_RANDOM ); + if( !asset ) + return NULL; + + size = AAsset_getLength( asset ); + + buf = (byte *)Mem_Malloc( fs_mempool, size + 1 ); + buf[size] = '\0'; + + if( AAsset_read( asset, buf, size ) < 0 ) + { + Mem_Free( buf ); + AAsset_close( asset ); + return NULL; + } + + AAsset_close( asset ); + + if( filesize ) *filesize = size; + + return buf; +} + +searchpath_t *FS_AddAndroidAssets_Fullpath( const char *path, int flags ) +{ + searchpath_t *search; + android_assets_t *assets = NULL; + qboolean engine = true; + + if(( flags & FS_STATIC_PATH ) || ( flags & FS_CUSTOM_PATH )) + return NULL; + + if(( flags & FS_GAMEDIR_PATH ) && Q_stricmp( GI->basedir, GI->gamefolder )) + engine = false; + + assets = FS_LoadAndroidAssets( engine ); + + if( !assets ) + { + Con_Reportf( S_ERROR "%s: unable to load Android assets \"%s\"\n", __FUNCTION__, Android_GetPackageName( engine ) ); + return NULL; + } + + Q_strncpy( assets->package_name, Android_GetPackageName( engine ), sizeof( assets->package_name )); + + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); + memset( search, 0, sizeof( searchpath_t )); + + Q_strncpy( search->filename, assets->package_name, sizeof( search->filename )); + search->assets = assets; + search->type = SEARCHPATH_ANDROID_ASSETS; + search->flags = FS_NOWRITE_PATH | FS_CUSTOM_PATH; + + search->pfnPrintInfo = FS_PrintInfo_AndroidAssets; + search->pfnClose = FS_Close_AndroidAssets; + search->pfnOpenFile = FS_OpenFile_AndroidAssets; + search->pfnFileTime = FS_FileTime_AndroidAssets; + search->pfnFindFile = FS_FindFile_AndroidAssets; + search->pfnSearch = FS_Search_AndroidAssets; + search->pfnLoadFile = FS_LoadAndroidAssetsFile; + + Con_Reportf( "Adding Android assets: %s\n", assets->package_name ); + + return search; +} + +void FS_InitAndroid( void ) +{ + jmethodID getContext; + + jni.env = (JNIEnv *)Sys_GetNativeObject( "JNIEnv" ); + jni.activity_class = Sys_GetNativeObject( "ActivityClass" ); + + getContext = (*jni.env)->GetStaticMethodID( jni.env, jni.activity_class, "getContext", "()Landroid/content/Context;" ); + jni.activity = (*jni.env)->CallStaticObjectMethod( jni.env, jni.activity_class, getContext ); + + jni.getPackageName = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getPackageName", "()Ljava/lang/String;" ); + jni.getCallingPackage = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getCallingPackage", "()Ljava/lang/String;" ); + jni.getAssetsList = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssetsList", "(ZLjava/lang/String;)[Ljava/lang/String;" ); +} + +#endif // XASH_ANDROID diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 7b622350..bb9174e0 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -71,8 +71,10 @@ const fs_archive_t g_archives[] = static const fs_archive_t g_directory_archive = { NULL, SEARCHPATH_PLAIN, FS_AddDir_Fullpath, false }; -// static const fs_archive_t g_android_archive = -// { NULL, SEARCHPATH_ANDROID, FS_AddAndroid_Fullpath, false, false }; +#ifdef XASH_ANDROID +static const fs_archive_t g_android_archive = +{ NULL, SEARCHPATH_ANDROID_ASSETS, FS_AddAndroidAssets_Fullpath, false }; +#endif #ifdef XASH_REDUCE_FD static file_t *fs_last_readfile; @@ -403,6 +405,10 @@ void FS_AddGameDirectory( const char *dir, uint flags ) stringlistfreecontents( &list ); +#ifdef XASH_ANDROID + FS_AddArchive_Fullpath( &g_android_archive, dir, flags ); +#endif + // add the directory to the search path // (unpacked files have the priority over packed files) search = FS_AddArchive_Fullpath( &g_directory_archive, dir, flags ); @@ -1406,6 +1412,10 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c FS_InitMemory(); +#ifdef XASH_ANDROID + FS_InitAndroid(); +#endif + Q_strncpy( fs_rootdir, rootdir, sizeof( fs_rootdir )); Q_strncpy( fs_gamedir, gamedir, sizeof( fs_gamedir )); Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir )); diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index acaa9fa3..fbd8d958 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -22,6 +22,10 @@ GNU General Public License for more details. #include "xash3d_types.h" #include "filesystem.h" +#if XASH_ANDROID +#include +#endif + #ifdef __cplusplus extern "C" { @@ -31,6 +35,10 @@ typedef struct dir_s dir_t; typedef struct zip_s zip_t; typedef struct pack_s pack_t; typedef struct wfile_s wfile_t; +#if XASH_ANDROID +typedef struct android_assets_s android_assets_t; +// typedef struct android_saf_s android_saf_t; +#endif #define FILE_BUFF_SIZE (2048) @@ -58,7 +66,8 @@ enum SEARCHPATH_PAK, SEARCHPATH_WAD, SEARCHPATH_ZIP, - SEARCHPATH_PK3DIR, // it's actually a plain directory but it must behave like a ZIP archive + SEARCHPATH_PK3DIR, // it's actually a plain directory but it must behave like a ZIP archive, + SEARCHPATH_ANDROID_ASSETS }; typedef struct stringlist_s @@ -81,6 +90,9 @@ typedef struct searchpath_s pack_t *pack; wfile_t *wad; zip_t *zip; +#if XASH_ANDROID + android_assets_t *assets; +#endif }; struct searchpath_s *next; @@ -225,6 +237,12 @@ searchpath_t *FS_AddDir_Fullpath( const char *path, int flags ); qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t len, qboolean createpath ); void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int flags ); +// +// android.c +// +void FS_InitAndroid( void ); +searchpath_t *FS_AddAndroidAssets_Fullpath( const char *path, int flags ); + #ifdef __cplusplus } #endif