mirror of
https://github.com/love2d/love.git
synced 2026-08-18 03:34:23 +02:00
Android: Implement special path URI for passing file descriptors.
This is to workaround the limitation of content:// URI severely limited.
This commit is contained in:
+120
-60
@@ -19,6 +19,7 @@
|
||||
**/
|
||||
|
||||
#include "android.h"
|
||||
#include "Object.h"
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
|
||||
@@ -122,34 +123,6 @@ bool getSafeArea(int &top, int &left, int &bottom, int &right)
|
||||
return hasSafeArea;
|
||||
}
|
||||
|
||||
const char *getSelectedGameFile()
|
||||
{
|
||||
static const char *path = NULL;
|
||||
|
||||
if (path)
|
||||
{
|
||||
delete path;
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||
jclass activity = env->FindClass("org/love2d/android/GameActivity");
|
||||
|
||||
jmethodID getGamePath = env->GetStaticMethodID(activity, "getGamePath", "()Ljava/lang/String;");
|
||||
jstring gamePath = (jstring) env->CallStaticObjectMethod(activity, getGamePath);
|
||||
const char *utf = env->GetStringUTFChars(gamePath, 0);
|
||||
if (utf)
|
||||
{
|
||||
path = SDL_strdup(utf);
|
||||
env->ReleaseStringUTFChars(gamePath, utf);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(gamePath);
|
||||
env->DeleteLocalRef(activity);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool openURL(const std::string &url)
|
||||
{
|
||||
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||
@@ -192,37 +165,6 @@ void freeGameArchiveMemory(void *ptr)
|
||||
delete[] game_love_data;
|
||||
}
|
||||
|
||||
bool loadGameArchiveToMemory(const char* filename, char **ptr, size_t *size)
|
||||
{
|
||||
SDL_RWops *asset_game_file = SDL_RWFromFile(filename, "rb");
|
||||
if (!asset_game_file) {
|
||||
SDL_Log("Could not find %s", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
Sint64 file_size = asset_game_file->size(asset_game_file);
|
||||
if (file_size <= 0) {
|
||||
SDL_Log("Could not load game from %s. File has invalid file size: %d.", filename, (int) file_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
*ptr = new char[file_size];
|
||||
if (!*ptr) {
|
||||
SDL_Log("Could not allocate memory for in-memory game archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytes_copied = asset_game_file->read(asset_game_file, (void*) *ptr, sizeof(char), (size_t) file_size);
|
||||
if (bytes_copied != file_size) {
|
||||
SDL_Log("Incomplete copy of in-memory game archive!");
|
||||
delete[] *ptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
*size = (size_t) file_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool directoryExists(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
@@ -766,7 +708,7 @@ bool checkFusedGame(void **physfsIO_Out)
|
||||
io = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// If there's no main.lua inside assets/ try game.love
|
||||
asset = AAssetManager_open(assetManager, "game.love", AASSET_MODE_RANDOM);
|
||||
if (asset)
|
||||
@@ -821,6 +763,124 @@ const char *getCRequirePath()
|
||||
return path;
|
||||
}
|
||||
|
||||
int getFDFromLoveProtocol(const char *path)
|
||||
{
|
||||
constexpr const char PROTOCOL[] = "love2d://fd/";
|
||||
constexpr size_t PROTOCOL_LEN = sizeof(PROTOCOL) - 1;
|
||||
|
||||
if (*path == '/')
|
||||
path++;
|
||||
|
||||
if (memcmp(path, PROTOCOL, PROTOCOL_LEN) == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::stoi(path + PROTOCOL_LEN, nullptr, 10);
|
||||
}
|
||||
catch (std::logic_error &)
|
||||
{ }
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
class FileDescriptorTracker: public love::Object
|
||||
{
|
||||
public:
|
||||
FileDescriptorTracker(int fd): Object(), fd(fd) {}
|
||||
~FileDescriptorTracker() { close(fd); }
|
||||
int getFd() { return fd; }
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct FileDescriptorIO
|
||||
{
|
||||
FileDescriptorTracker *fd;
|
||||
off64_t size;
|
||||
off64_t offset;
|
||||
};
|
||||
|
||||
void *getIOFromFD(int fd)
|
||||
{
|
||||
if (fd == -1)
|
||||
return nullptr;
|
||||
|
||||
// Create file descriptor IO structure
|
||||
FileDescriptorIO *fdio = new FileDescriptorIO();
|
||||
fdio->size = lseek64(fd, 0, SEEK_END);
|
||||
fdio->offset = 0;
|
||||
lseek64(fd, 0, SEEK_SET);
|
||||
|
||||
if (fdio->size == -1)
|
||||
{
|
||||
// Cannot get size
|
||||
delete fdio;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fdio->fd = new FileDescriptorTracker(fd);
|
||||
|
||||
PHYSFS_Io *io = new PHYSFS_Io();
|
||||
io->version = 0;
|
||||
io->opaque = fdio;
|
||||
io->read = [](PHYSFS_Io *io, void *buf, PHYSFS_uint64 size)
|
||||
{
|
||||
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
|
||||
ssize_t ret = pread64(fdio->fd->getFd(), buf, (size_t) size, fdio->offset);
|
||||
|
||||
if (ret == -1)
|
||||
PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR);
|
||||
else
|
||||
fdio->offset = std::min(fdio->offset + (off64_t) ret, fdio->size);
|
||||
|
||||
return (PHYSFS_sint64) ret;
|
||||
};
|
||||
io->write = nullptr;
|
||||
io->seek = [](PHYSFS_Io *io, PHYSFS_uint64 offset)
|
||||
{
|
||||
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
|
||||
fdio->offset = std::min(std::max<off64_t>((off64_t) offset, 0), fdio->size);
|
||||
// Always success
|
||||
return 1;
|
||||
};
|
||||
io->tell = [](PHYSFS_Io *io)
|
||||
{
|
||||
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
|
||||
return (PHYSFS_sint64) fdio->offset;
|
||||
};
|
||||
io->length = [](PHYSFS_Io *io)
|
||||
{
|
||||
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
|
||||
return (PHYSFS_sint64) fdio->size;
|
||||
};
|
||||
io->duplicate = [](PHYSFS_Io *io)
|
||||
{
|
||||
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
|
||||
FileDescriptorIO *fdio2 = new FileDescriptorIO();
|
||||
PHYSFS_Io *io2 = new PHYSFS_Io();
|
||||
|
||||
fdio->fd->retain();
|
||||
|
||||
// Copy data
|
||||
*fdio2 = *fdio;
|
||||
*io2 = *io;
|
||||
io2->opaque = fdio2;
|
||||
|
||||
return io2;
|
||||
};
|
||||
io->flush = nullptr;
|
||||
io->destroy = [](PHYSFS_Io *io)
|
||||
{
|
||||
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
|
||||
fdio->fd->release();
|
||||
delete fdio;
|
||||
delete io;
|
||||
};
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
} // android
|
||||
} // love
|
||||
|
||||
|
||||
+14
-7
@@ -50,11 +50,6 @@ double getScreenScale();
|
||||
**/
|
||||
bool getSafeArea(int &top, int &left, int &bottom, int &right);
|
||||
|
||||
/**
|
||||
* Gets the selected love file in the device filesystem.
|
||||
**/
|
||||
const char *getSelectedGameFile();
|
||||
|
||||
bool openURL(const std::string &url);
|
||||
|
||||
void vibrate(double seconds);
|
||||
@@ -64,8 +59,6 @@ void vibrate(double seconds);
|
||||
*/
|
||||
void freeGameArchiveMemory(void *ptr);
|
||||
|
||||
bool loadGameArchiveToMemory(const char *filename, char **ptr, size_t *size);
|
||||
|
||||
bool directoryExists(const char *path);
|
||||
|
||||
bool mkdir(const char *path);
|
||||
@@ -103,6 +96,20 @@ bool checkFusedGame(void **physfsIO_Out);
|
||||
|
||||
const char *getCRequirePath();
|
||||
|
||||
/**
|
||||
* Attempt to parse "(/)love2d://fd/<fd>" from path.
|
||||
* @param path Potentially special path.
|
||||
* @return File descriptor passed if successful, -1 if path is not valid.
|
||||
*/
|
||||
int getFDFromLoveProtocol(const char *path);
|
||||
|
||||
/**
|
||||
* Create PHYSFS_Io from file descriptor.
|
||||
* @param fd File descriptor
|
||||
* @return PHYSFS_Io casted to void*.
|
||||
*/
|
||||
void *getIOFromFD(int fd);
|
||||
|
||||
} // android
|
||||
} // love
|
||||
|
||||
|
||||
Reference in New Issue
Block a user