asset_loader.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <planet/asset_manager.hpp>

#include <felspar/exceptions.hpp>

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <jni.h>
#include <SDL.h>


namespace {
    jclass jAsset = {};
    jobject jAssetManager = {};
    jmethodID loader = {};

    AAssetManager *native_manager = nullptr;
}


extern "C" JNIEXPORT void JNICALL
        Java_com_blue5alamander_planet_android_Asset_useManager(
                JNIEnv *env, jobject, jclass am) {
    jAsset = reinterpret_cast<jclass>(env->NewGlobalRef(
            env->FindClass("com/blue5alamander/planet/android/Asset")));
    jAssetManager = env->NewGlobalRef(am);
    loader = env->GetStaticMethodID(
            jAsset, "loader",
            "(Landroid/content/res/AssetManager;Ljava/lang/String;)[B");
    native_manager = AAssetManager_fromJava(env, jAssetManager);
}


namespace {


    const struct jassetloader : public planet::asset_loader {
        std::optional<std::vector<std::byte>> try_load(
                std::ostream &log,
                std::filesystem::path const &fn,
                felspar::source_location const &loc) const override {
            JNIEnv *env = (JNIEnv *)SDL_AndroidGetJNIEnv();
            if (not jAsset or not jAssetManager) {
                throw felspar::stdexcept::runtime_error{
                        "useManager has not been called during application "
                        "start up"};
            }
            auto const assetfn = std::filesystem::path{"share"} / fn;
            if (native_manager) {
                AAsset *asset =
                        AAssetManager_open(native_manager, assetfn.c_str(), {});
                if (asset) {
                    std::vector<std::byte> buffer(AAsset_getLength64(asset));
                    std::memcpy(
                            buffer.data(), AAsset_getBuffer(asset),
                            buffer.size());
                    AAsset_close(asset);
                    return buffer;
                } else {
                    log << "Asset could not be loaded from the Android native "
                           "asset manager\n";
                    return {};
                }
            } else {
                jstring asset{reinterpret_cast<jstring>(
                        env->NewLocalRef(env->NewStringUTF(assetfn.c_str())))};
                jobject load_result(env->CallStaticObjectMethod(
                        jAsset, loader, jAssetManager, asset));
                jbyteArray *bytes(reinterpret_cast<jbyteArray *>(&load_result));
                if (*bytes == nullptr) {
                    log << "Asset could not be loaded from the Java asset "
                           "manager\n";
                    return {};
                } else {
                    std::size_t const length = env->GetArrayLength(*bytes);
                    std::vector<std::byte> buffer(length);
                    env->GetByteArrayRegion(
                            *bytes, 0, length,
                            reinterpret_cast<jbyte *>(buffer.data()));
                    return buffer;
                }
            }
        }
    } g_loader;


}