Path: blob/master/platform/android/file_access_filesystem_jandroid.cpp
11351 views
/**************************************************************************/1/* file_access_filesystem_jandroid.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "file_access_filesystem_jandroid.h"3132#include "thread_jandroid.h"3334#include "core/os/os.h"35#include "core/templates/local_vector.h"3637#include <unistd.h>3839jobject FileAccessFilesystemJAndroid::file_access_handler = nullptr;40jclass FileAccessFilesystemJAndroid::cls;4142jmethodID FileAccessFilesystemJAndroid::_file_open = nullptr;43jmethodID FileAccessFilesystemJAndroid::_file_get_size = nullptr;44jmethodID FileAccessFilesystemJAndroid::_file_seek = nullptr;45jmethodID FileAccessFilesystemJAndroid::_file_seek_end = nullptr;46jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;47jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;48jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;49jmethodID FileAccessFilesystemJAndroid::_file_set_eof = nullptr;50jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;51jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;52jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;53jmethodID FileAccessFilesystemJAndroid::_file_exists = nullptr;54jmethodID FileAccessFilesystemJAndroid::_file_last_modified = nullptr;55jmethodID FileAccessFilesystemJAndroid::_file_last_accessed = nullptr;56jmethodID FileAccessFilesystemJAndroid::_file_resize = nullptr;57jmethodID FileAccessFilesystemJAndroid::_file_size = nullptr;5859String FileAccessFilesystemJAndroid::get_path() const {60return path_src;61}6263String FileAccessFilesystemJAndroid::get_path_absolute() const {64return absolute_path;65}6667Error FileAccessFilesystemJAndroid::open_internal(const String &p_path, int p_mode_flags) {68if (is_open()) {69_close();70}7172if (_file_open) {73JNIEnv *env = get_jni_env();74ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);7576String path = fix_path(p_path).simplify_path();77jstring js = env->NewStringUTF(path.utf8().get_data());78int res = env->CallIntMethod(file_access_handler, _file_open, js, p_mode_flags);79env->DeleteLocalRef(js);8081if (res < 0) {82// Errors are passed back as their negative value to differentiate from the positive file id.83return static_cast<Error>(-res);84}8586id = res;87path_src = p_path;88absolute_path = path;89return OK;90} else {91return ERR_UNCONFIGURED;92}93}9495void FileAccessFilesystemJAndroid::_close() {96if (!is_open()) {97return;98}99100if (_file_close) {101JNIEnv *env = get_jni_env();102ERR_FAIL_NULL(env);103env->CallVoidMethod(file_access_handler, _file_close, id);104}105id = 0;106}107108bool FileAccessFilesystemJAndroid::is_open() const {109return id != 0;110}111112void FileAccessFilesystemJAndroid::seek(uint64_t p_position) {113if (_file_seek) {114JNIEnv *env = get_jni_env();115ERR_FAIL_NULL(env);116ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");117env->CallVoidMethod(file_access_handler, _file_seek, id, p_position);118}119}120121void FileAccessFilesystemJAndroid::seek_end(int64_t p_position) {122if (_file_seek_end) {123JNIEnv *env = get_jni_env();124ERR_FAIL_NULL(env);125ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");126env->CallVoidMethod(file_access_handler, _file_seek_end, id, p_position);127}128}129130uint64_t FileAccessFilesystemJAndroid::get_position() const {131if (_file_tell) {132JNIEnv *env = get_jni_env();133ERR_FAIL_NULL_V(env, 0);134ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");135return env->CallLongMethod(file_access_handler, _file_tell, id);136} else {137return 0;138}139}140141uint64_t FileAccessFilesystemJAndroid::get_length() const {142if (_file_get_size) {143JNIEnv *env = get_jni_env();144ERR_FAIL_NULL_V(env, 0);145ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");146return env->CallLongMethod(file_access_handler, _file_get_size, id);147} else {148return 0;149}150}151152bool FileAccessFilesystemJAndroid::eof_reached() const {153if (_file_eof) {154JNIEnv *env = get_jni_env();155ERR_FAIL_NULL_V(env, false);156ERR_FAIL_COND_V_MSG(!is_open(), false, "File must be opened before use.");157return env->CallBooleanMethod(file_access_handler, _file_eof, id);158} else {159return false;160}161}162163void FileAccessFilesystemJAndroid::_set_eof(bool eof) {164if (_file_set_eof) {165ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");166167JNIEnv *env = get_jni_env();168ERR_FAIL_NULL(env);169env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof);170}171}172173String FileAccessFilesystemJAndroid::get_line() const {174ERR_FAIL_COND_V_MSG(!is_open(), String(), "File must be opened before use.");175176const size_t buffer_size_limit = 2048;177const uint64_t file_size = get_length();178const uint64_t start_position = get_position();179180String result;181LocalVector<uint8_t> line_buffer;182size_t current_buffer_size = 0;183uint64_t line_buffer_position = 0;184185while (true) {186size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());187if (line_buffer_size <= 0) {188const_cast<FileAccessFilesystemJAndroid *>(this)->_set_eof(true);189break;190}191192current_buffer_size += line_buffer_size;193line_buffer.resize(current_buffer_size);194195uint64_t bytes_read = get_buffer(&line_buffer[line_buffer_position], current_buffer_size - line_buffer_position);196if (bytes_read <= 0) {197break;198}199200for (; bytes_read > 0; line_buffer_position++, bytes_read--) {201uint8_t elem = line_buffer[line_buffer_position];202if (elem == '\r' || elem == '\n' || elem == '\0') {203// Found the end of the line204const bool is_crlf = elem == '\r' && line_buffer_position + 1 < current_buffer_size && line_buffer[line_buffer_position + 1] == '\n';205const_cast<FileAccessFilesystemJAndroid *>(this)->seek(start_position + line_buffer_position + (is_crlf ? 2 : 1));206if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position)) {207return String();208}209return result;210}211}212}213214if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position)) {215return String();216}217return result;218}219220uint64_t FileAccessFilesystemJAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {221if (_file_read) {222ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");223if (p_length == 0) {224return 0;225}226227JNIEnv *env = get_jni_env();228ERR_FAIL_NULL_V(env, 0);229230jobject j_buffer = env->NewDirectByteBuffer(p_dst, p_length);231int length = env->CallIntMethod(file_access_handler, _file_read, id, j_buffer);232env->DeleteLocalRef(j_buffer);233return length;234} else {235return 0;236}237}238239bool FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {240if (_file_write) {241ERR_FAIL_COND_V_MSG(!is_open(), false, "File must be opened before use.");242ERR_FAIL_COND_V(!p_src && p_length > 0, false);243if (p_length == 0) {244return true;245}246247JNIEnv *env = get_jni_env();248ERR_FAIL_NULL_V(env, false);249250jobject j_buffer = env->NewDirectByteBuffer((void *)p_src, p_length);251bool ok = env->CallBooleanMethod(file_access_handler, _file_write, id, j_buffer);252env->DeleteLocalRef(j_buffer);253return ok;254} else {255return false;256}257}258259Error FileAccessFilesystemJAndroid::get_error() const {260if (eof_reached()) {261return ERR_FILE_EOF;262}263return OK;264}265266Error FileAccessFilesystemJAndroid::resize(int64_t p_length) {267if (_file_resize) {268JNIEnv *env = get_jni_env();269ERR_FAIL_NULL_V(env, FAILED);270ERR_FAIL_COND_V_MSG(!is_open(), FAILED, "File must be opened before use.");271int res = env->CallIntMethod(file_access_handler, _file_resize, id, p_length);272return static_cast<Error>(res);273} else {274return ERR_UNAVAILABLE;275}276}277278void FileAccessFilesystemJAndroid::flush() {279if (_file_flush) {280JNIEnv *env = get_jni_env();281ERR_FAIL_NULL(env);282ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");283env->CallVoidMethod(file_access_handler, _file_flush, id);284}285}286287bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) {288if (_file_exists) {289JNIEnv *env = get_jni_env();290ERR_FAIL_NULL_V(env, false);291292String path = fix_path(p_path).simplify_path();293jstring js = env->NewStringUTF(path.utf8().get_data());294bool result = env->CallBooleanMethod(file_access_handler, _file_exists, js);295env->DeleteLocalRef(js);296return result;297} else {298return false;299}300}301302uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file) {303if (_file_last_modified) {304JNIEnv *env = get_jni_env();305ERR_FAIL_NULL_V(env, 0);306307String path = fix_path(p_file).simplify_path();308jstring js = env->NewStringUTF(path.utf8().get_data());309uint64_t result = env->CallLongMethod(file_access_handler, _file_last_modified, js);310env->DeleteLocalRef(js);311return result;312} else {313return 0;314}315}316317uint64_t FileAccessFilesystemJAndroid::_get_access_time(const String &p_file) {318if (_file_last_accessed) {319JNIEnv *env = get_jni_env();320ERR_FAIL_NULL_V(env, 0);321322String path = fix_path(p_file).simplify_path();323jstring js = env->NewStringUTF(path.utf8().get_data());324uint64_t result = env->CallLongMethod(file_access_handler, _file_last_accessed, js);325env->DeleteLocalRef(js);326return result;327} else {328return 0;329}330}331332int64_t FileAccessFilesystemJAndroid::_get_size(const String &p_file) {333if (_file_size) {334JNIEnv *env = get_jni_env();335ERR_FAIL_NULL_V(env, -1);336337String path = fix_path(p_file).simplify_path();338jstring js = env->NewStringUTF(path.utf8().get_data());339int64_t result = env->CallLongMethod(file_access_handler, _file_size, js);340env->DeleteLocalRef(js);341return result;342} else {343return -1;344}345}346347void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {348JNIEnv *env = get_jni_env();349file_access_handler = env->NewGlobalRef(p_file_access_handler);350351jclass c = env->GetObjectClass(file_access_handler);352cls = (jclass)env->NewGlobalRef(c);353354_file_open = env->GetMethodID(cls, "fileOpen", "(Ljava/lang/String;I)I");355_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");356_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");357_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");358_file_set_eof = env->GetMethodID(cls, "setFileEof", "(IZ)V");359_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");360_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");361_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");362_file_close = env->GetMethodID(cls, "fileClose", "(I)V");363_file_write = env->GetMethodID(cls, "fileWrite", "(ILjava/nio/ByteBuffer;)Z");364_file_flush = env->GetMethodID(cls, "fileFlush", "(I)V");365_file_exists = env->GetMethodID(cls, "fileExists", "(Ljava/lang/String;)Z");366_file_last_modified = env->GetMethodID(cls, "fileLastModified", "(Ljava/lang/String;)J");367_file_last_accessed = env->GetMethodID(cls, "fileLastAccessed", "(Ljava/lang/String;)J");368_file_resize = env->GetMethodID(cls, "fileResize", "(IJ)I");369_file_size = env->GetMethodID(cls, "fileSize", "(Ljava/lang/String;)J");370}371372void FileAccessFilesystemJAndroid::terminate() {373JNIEnv *env = get_jni_env();374ERR_FAIL_NULL(env);375376env->DeleteGlobalRef(cls);377env->DeleteGlobalRef(file_access_handler);378}379380void FileAccessFilesystemJAndroid::close() {381if (is_open()) {382_close();383}384}385386FileAccessFilesystemJAndroid::FileAccessFilesystemJAndroid() {387id = 0;388}389390FileAccessFilesystemJAndroid::~FileAccessFilesystemJAndroid() {391if (is_open()) {392_close();393}394}395396397