diff --git a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp index 9b9a9b5..3556c22 100644 --- a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp +++ b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp @@ -1,14 +1,19 @@ #define _LARGEFILE64_SOURCE #include "GpFileSystem_Android.h" #include "GpIOStream.h" +#include "HostDirectoryCursor.h" #include "VirtualDirectory.h" +#include "SDL.h" #include "SDL_rwops.h" #include +#include #include #include #include +#include +#include "UTF8.h" @@ -242,37 +247,73 @@ void GpFileStream_Android_File::Flush() fflush(m_f); } -static bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, std::string &resolution, bool &isAsset) +static bool ResolvePath(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths, std::string &resolution, bool &isAsset) { isAsset = false; switch (virtualDirectory) { case PortabilityLayer::VirtualDirectories::kApplicationData: - resolution = std::string("Packaged/") + path; + resolution = std::string("Packaged") ; isAsset = true; - return true; + break; case PortabilityLayer::VirtualDirectories::kGameData: - resolution = std::string("Packaged/Houses/") + path; + resolution = std::string("Packaged/Houses"); isAsset = true; - return true; + break; case PortabilityLayer::VirtualDirectories::kFonts: - resolution = std::string("Resources/") + path; + resolution = std::string("Resources"); isAsset = true; - return true; + break; default: return false; }; + + for (size_t i = 0; i < numPaths; i++) + { + resolution += "/"; + resolution += paths[i]; + } + + return true; } GpFileSystem_Android::GpFileSystem_Android() + : m_activity(nullptr) { } +GpFileSystem_Android::~GpFileSystem_Android() +{ +} + +void GpFileSystem_Android::InitJNI() +{ + JNIEnv *jni = static_cast(SDL_AndroidGetJNIEnv()); + + jobject activityLR = static_cast(SDL_AndroidGetActivity()); + jclass activityClassLR = static_cast(jni->GetObjectClass(activityLR)); + + m_scanAssetDirectoryMID = jni->GetMethodID(activityClassLR, "scanAssetDirectory", "(Ljava/lang/String;)[Ljava/lang/String;"); + + m_activity = jni->NewGlobalRef(activityLR); + + jni->DeleteLocalRef(activityLR); + jni->DeleteLocalRef(activityClassLR); +} + +void GpFileSystem_Android::ShutdownJNI() +{ + JNIEnv *jni = static_cast(SDL_AndroidGetJNIEnv()); + + jni->DeleteGlobalRef(m_activity); + m_activity = nullptr; +} + bool GpFileSystem_Android::FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) { std::string resolvedPath; bool isAsset; - if (!ResolvePath(virtualDirectory, path, resolvedPath, isAsset)) + if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset)) return false; if (isAsset) @@ -281,6 +322,7 @@ bool GpFileSystem_Android::FileExists(PortabilityLayer::VirtualDirectory_t virtu if (!rw) return false; SDL_RWclose(rw); + return true; } struct stat s; @@ -291,21 +333,26 @@ bool GpFileSystem_Android::FileLocked(PortabilityLayer::VirtualDirectory_t virtu { std::string resolvedPath; bool isAsset; - if (!ResolvePath(virtualDirectory, path, resolvedPath, isAsset)) + if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset)) { - *exists = false; + if (exists) + *exists = false; return false; } if (isAsset) + { + if (exists) + *exists = this->FileExists(virtualDirectory, path); return true; + } int permissions = access(resolvedPath.c_str(), W_OK | F_OK); *exists = ((permissions & F_OK) != 0); return ((permissions & W_OK) != 0); } -GpIOStream *GpFileSystem_Android::OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) +GpIOStream *GpFileSystem_Android::OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) { const char *mode = nullptr; bool canWrite = false; @@ -332,7 +379,7 @@ GpIOStream *GpFileSystem_Android::OpenFile(PortabilityLayer::VirtualDirectory_t std::string resolvedPath; bool isAsset; - if (!ResolvePath(virtualDirectory, path, resolvedPath, isAsset)) + if (!ResolvePath(virtualDirectory, subPaths, numSubPaths, resolvedPath, isAsset)) return nullptr; if (isAsset) @@ -386,7 +433,7 @@ bool GpFileSystem_Android::DeleteFile(PortabilityLayer::VirtualDirectory_t virtu { std::string resolvedPath; bool isAsset; - if (!ResolvePath(virtualDirectory, path, resolvedPath, isAsset)) + if (!ResolvePath(virtualDirectory, &path, 1, resolvedPath, isAsset)) { existed = false; return false; @@ -404,8 +451,11 @@ bool GpFileSystem_Android::DeleteFile(PortabilityLayer::VirtualDirectory_t virtu return true; } -PortabilityLayer::HostDirectoryCursor *GpFileSystem_Android::ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) +PortabilityLayer::HostDirectoryCursor *GpFileSystem_Android::ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *const *paths, size_t numPaths) { + if (IsVirtualDirectoryLooseResources(virtualDirectory)) + return ScanAssetDirectory(virtualDirectory, paths, numPaths); + return nullptr; } @@ -455,9 +505,85 @@ bool GpFileSystem_Android::ValidateFilePathUnicodeChar(uint32_t c) const return false; } +bool GpFileSystem_Android::IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const +{ + return virtualDir == PortabilityLayer::VirtualDirectories::kApplicationData || virtualDir == PortabilityLayer::VirtualDirectories::kGameData; +} + GpFileSystem_Android *GpFileSystem_Android::GetInstance() { return &ms_instance; } +class GpDirectoryCursor_StringList final : public PortabilityLayer::HostDirectoryCursor +{ +public: + explicit GpDirectoryCursor_StringList(std::vector &paths); + ~GpDirectoryCursor_StringList(); + + bool GetNext(const char *&outFileName) override; + void Destroy() override; + +private: + std::vector m_paths; + size_t m_index; +}; + +GpDirectoryCursor_StringList::GpDirectoryCursor_StringList(std::vector &paths) + : m_index(0) +{ + std::swap(paths, m_paths); +} + +GpDirectoryCursor_StringList::~GpDirectoryCursor_StringList() +{ +} + +bool GpDirectoryCursor_StringList::GetNext(const char *&outFileName) +{ + if (m_index == m_paths.size()) + return false; + outFileName = m_paths[m_index].c_str(); + m_index++; + return true; +} + +void GpDirectoryCursor_StringList::Destroy() +{ + delete this; +} + +PortabilityLayer::HostDirectoryCursor *GpFileSystem_Android::ScanAssetDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) +{ + std::string resolvedPath; + std::vector subPaths; + bool isAsset = true; + if (!ResolvePath(virtualDirectory, paths, numPaths, resolvedPath, isAsset)) + return nullptr; + + JNIEnv *jni = static_cast(SDL_AndroidGetJNIEnv()); + + jstring directory = jni->NewStringUTF(resolvedPath.c_str()); + + jobjectArray resultArray = static_cast(jni->CallObjectMethod(m_activity, m_scanAssetDirectoryMID, directory)); + jni->DeleteLocalRef(directory); + + size_t arrayLength = jni->GetArrayLength(resultArray); + subPaths.reserve(arrayLength); + for (size_t i = 0; i < arrayLength; i++) + { + jstring pathJStr = static_cast(jni->GetObjectArrayElement(resultArray, i)); + const char *pathStrChars = jni->GetStringUTFChars(pathJStr, nullptr); + + subPaths.push_back(std::string(pathStrChars, static_cast(jni->GetStringUTFLength(pathJStr)))); + + jni->ReleaseStringUTFChars(pathJStr, pathStrChars); + jni->DeleteLocalRef(pathJStr); + } + + jni->DeleteLocalRef(resultArray); + + return new GpDirectoryCursor_StringList(subPaths); +} + GpFileSystem_Android GpFileSystem_Android::ms_instance; diff --git a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h index 3a58b46..fb4f18d 100644 --- a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h +++ b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.h @@ -4,22 +4,35 @@ #include "GpCoreDefs.h" +#include + class GpFileSystem_Android final : public PortabilityLayer::HostFileSystem { public: GpFileSystem_Android(); + ~GpFileSystem_Android(); + + void InitJNI(); + void ShutdownJNI(); bool FileExists(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path) override; bool FileLocked(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool *exists) override; - GpIOStream *OpenFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool writeAccess, GpFileCreationDisposition_t createDisposition) override; + GpIOStream *OpenFileNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* subPaths, size_t numSubPaths, bool writeAccess, GpFileCreationDisposition_t createDisposition) override; bool DeleteFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, const char *path, bool &existed) override; - PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory) override; + PortabilityLayer::HostDirectoryCursor *ScanDirectoryNested(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths) override; - bool ValidateFilePath(const char *path, size_t sz) const override; + bool ValidateFilePath(const char *path, size_t pathLen) const override; bool ValidateFilePathUnicodeChar(uint32_t ch) const override; + bool IsVirtualDirectoryLooseResources(PortabilityLayer::VirtualDirectory_t virtualDir) const override; + static GpFileSystem_Android *GetInstance(); private: + PortabilityLayer::HostDirectoryCursor *ScanAssetDirectory(PortabilityLayer::VirtualDirectory_t virtualDirectory, char const* const* paths, size_t numPaths); + + jobject m_activity; + jmethodID m_scanAssetDirectoryMID; + static GpFileSystem_Android ms_instance; }; diff --git a/AerofoilAndroid/app/jni/main/GpMain_SDL_Android.cpp b/AerofoilAndroid/app/jni/main/GpMain_SDL_Android.cpp index 69ceb8f..2ddfb6b 100644 --- a/AerofoilAndroid/app/jni/main/GpMain_SDL_Android.cpp +++ b/AerofoilAndroid/app/jni/main/GpMain_SDL_Android.cpp @@ -33,7 +33,9 @@ int main(int argc, char* argv[]) if (SDL_Init(SDL_INIT_VIDEO) < 0) return -1; - SDL_GL_LoadLibrary("libGLESv2.so"); + //SDL_GL_LoadLibrary("libGLESv2.so"); + + GpFileSystem_Android::GetInstance()->InitJNI(); GpAppInterface_Get()->PL_HostFileSystem_SetInstance(GpFileSystem_Android::GetInstance()); GpAppInterface_Get()->PL_HostSystemServices_SetInstance(GpSystemServices_Android::GetInstance()); @@ -57,6 +59,8 @@ int main(int argc, char* argv[]) int returnCode = GpMain::Run(); + GpFileSystem_Android::GetInstance()->ShutdownJNI(); + SDL_Quit(); // This doesn't even actually exit, but it does stop this stupid brain-damaged OS from diff --git a/AerofoilAndroid/app/src/main/java/org/glideport/game/GpActivity.java b/AerofoilAndroid/app/src/main/java/org/glideport/game/GpActivity.java index ffaa412..7b654de 100644 --- a/AerofoilAndroid/app/src/main/java/org/glideport/game/GpActivity.java +++ b/AerofoilAndroid/app/src/main/java/org/glideport/game/GpActivity.java @@ -1,7 +1,30 @@ package org.glideport.game; import org.libsdl.app.SDLActivity; +import android.content.res.AssetManager; +import android.os.Bundle; public class GpActivity extends SDLActivity { + private AssetManager assetManager; + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + assetManager = getAssets(); + } + + public String[] scanAssetDirectory(String directory) + { + try + { + return this.assetManager.list(directory); + } + catch (java.io.IOException ex) + { + return new String[0]; + } + } }