(Android) Added preliminary support for copying assets to the internal data directory

This commit is contained in:
Leon Styhre 2023-12-01 23:00:41 +01:00
parent 27feef1b23
commit a83763c2c3
7 changed files with 97 additions and 10 deletions

View file

@ -413,7 +413,7 @@ add_compile_definitions(GLM_FORCE_XYZW_ONLY)
# For Unix systems, assign the installation prefix. If it's not explicitly set, # For Unix systems, assign the installation prefix. If it's not explicitly set,
# we use /usr on Linux, /usr/pkg on NetBSD and /usr/local on FreeBSD and OpenBSD. # we use /usr on Linux, /usr/pkg on NetBSD and /usr/local on FreeBSD and OpenBSD.
if(NOT WIN32 AND NOT APPLE) if(NOT WIN32 AND NOT APPLE AND NOT ANDROID)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
if(CMAKE_SYSTEM_NAME MATCHES Linux) if(CMAKE_SYSTEM_NAME MATCHES Linux)
set(CMAKE_INSTALL_PREFIX /usr CACHE INTERNAL CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_PREFIX /usr CACHE INTERNAL CMAKE_INSTALL_PREFIX)

View file

@ -719,6 +719,10 @@ int main(int argc, char* argv[])
renderer = Renderer::getInstance(); renderer = Renderer::getInstance();
window = Window::getInstance(); window = Window::getInstance();
#if defined(__ANDROID__)
Utils::Platform::Android::setupResources();
#endif
ViewController::getInstance()->setMenuColors(); ViewController::getInstance()->setMenuColors();
CollectionSystemsManager::getInstance(); CollectionSystemsManager::getInstance();
Screensaver screensaver; Screensaver screensaver;

View file

@ -709,6 +709,8 @@ void ThemeData::populateThemes()
Utils::FileSystem::getExePath() + "/themes", Utils::FileSystem::getExePath() + "/themes",
#if defined(__APPLE__) #if defined(__APPLE__)
Utils::FileSystem::getExePath() + "/../Resources/themes", Utils::FileSystem::getExePath() + "/../Resources/themes",
#elif defined(__ANDROID__)
ResourceManager::getInstance().getDataDirectory() + "/themes",
#elif defined(__unix__) && !defined(APPIMAGE_BUILD) #elif defined(__unix__) && !defined(APPIMAGE_BUILD)
Utils::FileSystem::getProgramDataPath() + "/themes", Utils::FileSystem::getProgramDataPath() + "/themes",
#endif #endif

View file

@ -42,14 +42,17 @@ std::string ResourceManager::getResourcePath(const std::string& path, bool termi
return applePackagePath; return applePackagePath;
} }
#elif defined(__unix__) && !defined(APPIMAGE_BUILD) #elif defined(__unix__) && !defined(APPIMAGE_BUILD)
#if defined(__ANDROID__)
std::string testDataPath {mDataDirectory + "/resources/" + &path[2]};
#else
// Check under the data installation directory (Unix only). // Check under the data installation directory (Unix only).
std::string testDataPath {Utils::FileSystem::getProgramDataPath() + "/resources/" + std::string testDataPath {Utils::FileSystem::getProgramDataPath() + "/resources/" +
&path[2]}; &path[2]};
if (Utils::FileSystem::exists(testDataPath)) {
return testDataPath;
}
#endif #endif
if (Utils::FileSystem::exists(testDataPath))
return testDataPath;
#endif
#if !defined(__ANDROID__)
// Check under the ES executable directory. // Check under the ES executable directory.
std::string testExePath {Utils::FileSystem::getExePath() + "/resources/" + &path[2]}; std::string testExePath {Utils::FileSystem::getExePath() + "/resources/" + &path[2]};
@ -60,6 +63,14 @@ std::string ResourceManager::getResourcePath(const std::string& path, bool termi
// indicate that we have a broken EmulationStation installation. If the argument // indicate that we have a broken EmulationStation installation. If the argument
// terminateOnFailure is set to false though, then skip this step. // terminateOnFailure is set to false though, then skip this step.
else { else {
#else
SDL_RWops* resFile {SDL_RWFromFile(path.substr(2).c_str(), "rb")};
if (resFile != nullptr) {
SDL_RWclose(resFile);
return path.substr(2);
}
else {
#endif
if (terminateOnFailure) { if (terminateOnFailure) {
LOG(LogError) << "Program resource missing: " << path; LOG(LogError) << "Program resource missing: " << path;
LOG(LogError) << "Tried to find the resource in the following locations:"; LOG(LogError) << "Tried to find the resource in the following locations:";
@ -69,7 +80,9 @@ std::string ResourceManager::getResourcePath(const std::string& path, bool termi
#elif defined(__unix__) && !defined(APPIMAGE_BUILD) #elif defined(__unix__) && !defined(APPIMAGE_BUILD)
LOG(LogError) << testDataPath; LOG(LogError) << testDataPath;
#endif #endif
#if !defined(__ANDROID__)
LOG(LogError) << testExePath; LOG(LogError) << testExePath;
#endif
LOG(LogError) << "Has EmulationStation been properly installed?"; LOG(LogError) << "Has EmulationStation been properly installed?";
Utils::Platform::emergencyShutdown(); Utils::Platform::emergencyShutdown();
} }
@ -88,10 +101,19 @@ const ResourceData ResourceManager::getFileData(const std::string& path) const
// Check if its a resource. // Check if its a resource.
const std::string respath {getResourcePath(path)}; const std::string respath {getResourcePath(path)};
#if defined(__ANDROID__)
SDL_RWops* resFile {SDL_RWFromFile(respath.c_str(), "rb")};
if (resFile != nullptr) {
ResourceData data {loadFile(resFile)};
SDL_RWclose(resFile);
return data;
}
#else
if (Utils::FileSystem::exists(respath)) { if (Utils::FileSystem::exists(respath)) {
ResourceData data {loadFile(respath)}; ResourceData data {loadFile(respath)};
return data; return data;
} }
#endif
// If the file doesn't exist, return an "empty" ResourceData. // If the file doesn't exist, return an "empty" ResourceData.
ResourceData data {nullptr, 0}; ResourceData data {nullptr, 0};
@ -107,7 +129,7 @@ ResourceData ResourceManager::loadFile(const std::string& path) const
#endif #endif
stream.seekg(0, stream.end); stream.seekg(0, stream.end);
size_t size {static_cast<size_t>(stream.tellg())}; const size_t size {static_cast<size_t>(stream.tellg())};
stream.seekg(0, stream.beg); stream.seekg(0, stream.beg);
// Supply custom deleter to properly free array. // Supply custom deleter to properly free array.
@ -120,6 +142,17 @@ ResourceData ResourceManager::loadFile(const std::string& path) const
return ret; return ret;
} }
ResourceData ResourceManager::loadFile(SDL_RWops* resFile) const
{
const size_t size {static_cast<size_t>(SDL_RWsize(resFile))};
std::shared_ptr<unsigned char> data {new unsigned char[size],
[](unsigned char* p) { delete[] p; }};
SDL_RWread(resFile, reinterpret_cast<char*>(data.get()), 1, size);
ResourceData ret {data, size};
return ret;
}
bool ResourceManager::fileExists(const std::string& path) const bool ResourceManager::fileExists(const std::string& path) const
{ {
// If it exists as a resource file, return true. // If it exists as a resource file, return true.

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// //
// EmulationStation Desktop Edition // ES-DE
// ResourceManager.h // ResourceManager.h
// //
// Handles the application resources (fonts, graphics, sounds etc.). // Handles the application resources (fonts, graphics, sounds etc.).
@ -14,6 +14,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <SDL2/SDL_rwops.h>
// The ResourceManager exists to: // The ResourceManager exists to:
// Allow loading resources embedded into the executable like an actual file. // Allow loading resources embedded into the executable like an actual file.
// Allow embedded resources to be optionally remapped to actual files for further customization. // Allow embedded resources to be optionally remapped to actual files for further customization.
@ -42,6 +44,9 @@ public:
void unloadAll(); void unloadAll();
void reloadAll(); void reloadAll();
void setDataDirectory(const std::string& dataDirectory) { mDataDirectory = dataDirectory; }
const std::string& getDataDirectory() const { return mDataDirectory; }
std::string getResourcePath(const std::string& path, bool terminateOnFailure = true) const; std::string getResourcePath(const std::string& path, bool terminateOnFailure = true) const;
const ResourceData getFileData(const std::string& path) const; const ResourceData getFileData(const std::string& path) const;
bool fileExists(const std::string& path) const; bool fileExists(const std::string& path) const;
@ -49,9 +54,11 @@ public:
private: private:
ResourceManager() noexcept {} ResourceManager() noexcept {}
std::list<std::weak_ptr<IReloadable>> mReloadables;
ResourceData loadFile(const std::string& path) const; ResourceData loadFile(const std::string& path) const;
ResourceData loadFile(SDL_RWops* resFile) const;
std::list<std::weak_ptr<IReloadable>> mReloadables;
std::string mDataDirectory;
}; };
#endif // ES_CORE_RESOURCES_RESOURCE_MANAGER_H #endif // ES_CORE_RESOURCES_RESOURCE_MANAGER_H

View file

@ -387,13 +387,53 @@ namespace Utils
return result; return result;
} }
bool setupResources()
{
JNIEnv* jniEnv {reinterpret_cast<JNIEnv*>(SDL_AndroidGetJNIEnv())};
{
jclass jniClass {jniEnv->FindClass("org/es_de/frontend/MainActivity")};
jmethodID methodID {jniEnv->GetStaticMethodID(jniClass, "getDataDirectory",
"()Ljava/lang/String;")};
jstring dataDirectory {
static_cast<jstring>(jniEnv->CallStaticObjectMethod(jniClass, methodID))};
const char* dataDirUtf {jniEnv->GetStringUTFChars(dataDirectory, nullptr)};
ResourceManager::getInstance().setDataDirectory(std::string(dataDirUtf));
jniEnv->ReleaseStringUTFChars(dataDirectory, dataDirUtf);
jniEnv->DeleteLocalRef(jniClass);
}
{
jclass jniClass {jniEnv->FindClass("org/es_de/frontend/MainActivity")};
jmethodID methodID {
jniEnv->GetStaticMethodID(jniClass, "setupResources", "()Z")};
const bool returnValue {
static_cast<bool>(jniEnv->CallStaticBooleanMethod(jniClass, methodID))};
jniEnv->DeleteLocalRef(jniClass);
if (returnValue) {
LOG(LogError) << "Couldn't setup application resources on internal storage";
return true;
}
}
{
jclass jniClass {jniEnv->FindClass("org/es_de/frontend/MainActivity")};
jmethodID methodID {jniEnv->GetStaticMethodID(jniClass, "setupThemes", "()Z")};
const bool returnValue {
static_cast<bool>(jniEnv->CallStaticBooleanMethod(jniClass, methodID))};
jniEnv->DeleteLocalRef(jniClass);
if (returnValue) {
LOG(LogError) << "Couldn't setup application themes on internal storage";
return true;
}
}
return false;
}
bool checkEmulatorInstalled(const std::string& packageName, const std::string& activity) bool checkEmulatorInstalled(const std::string& packageName, const std::string& activity)
{ {
JNIEnv* jniEnv {reinterpret_cast<JNIEnv*>(SDL_AndroidGetJNIEnv())}; JNIEnv* jniEnv {reinterpret_cast<JNIEnv*>(SDL_AndroidGetJNIEnv())};
jclass jniClass {jniEnv->FindClass("org/es_de/frontend/MainActivity")}; jclass jniClass {jniEnv->FindClass("org/es_de/frontend/MainActivity")};
jmethodID methodID {jniEnv->GetStaticMethodID( jmethodID methodID {jniEnv->GetStaticMethodID(
jniClass, "checkEmulatorInstalled", "(Ljava/lang/String;Ljava/lang/String;)Z")}; jniClass, "checkEmulatorInstalled", "(Ljava/lang/String;Ljava/lang/String;)Z")};
bool returnValue {static_cast<bool>(jniEnv->CallStaticBooleanMethod( const bool returnValue {static_cast<bool>(jniEnv->CallStaticBooleanMethod(
jniClass, methodID, jniEnv->NewStringUTF(packageName.c_str()), jniClass, methodID, jniEnv->NewStringUTF(packageName.c_str()),
jniEnv->NewStringUTF(activity.c_str())))}; jniEnv->NewStringUTF(activity.c_str())))};
// jniEnv->DeleteLocalRef(jniClass); // jniEnv->DeleteLocalRef(jniClass);

View file

@ -60,6 +60,7 @@ namespace Utils
namespace Android namespace Android
{ {
bool requestStoragePermission(); bool requestStoragePermission();
bool setupResources();
bool checkEmulatorInstalled(const std::string& packageName, bool checkEmulatorInstalled(const std::string& packageName,
const std::string& activity); const std::string& activity);
int launchGame(const std::string& packageName, int launchGame(const std::string& packageName,