mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-23 05:58:26 +02:00
skin studio updates, save sync CLOSES #1533
This commit is contained in:
@@ -378,6 +378,104 @@ bool httpPost(const char *url, const char *body, int bodyLen, const char *conten
|
||||
return result;
|
||||
}
|
||||
|
||||
bool httpRequest(const char *url, const char *method,
|
||||
const char *const *headerPairs, int headerPairCount,
|
||||
const char *body, int bodyLen, const char *userAgent, std::string &out)
|
||||
{
|
||||
out.clear();
|
||||
if (url == nullptr)
|
||||
return false;
|
||||
if (headerPairCount < 0 || (headerPairCount > 0 && headerPairs == nullptr))
|
||||
return false;
|
||||
|
||||
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||
// Same resolution rule as httpDownload: the activity's own class via
|
||||
// SDL_AndroidGetActivity, never FindClass for an app class -- save sync
|
||||
// runs on a love.thread worker, whose class loader cannot see them.
|
||||
jobject activityObj = (jobject) SDL_AndroidGetActivity();
|
||||
if (activityObj == nullptr)
|
||||
return false;
|
||||
jclass activity = env->GetObjectClass(activityObj);
|
||||
env->DeleteLocalRef(activityObj);
|
||||
|
||||
// Old APK / new liblove skew: report "no transport" instead of aborting
|
||||
// on a missing method (#597).
|
||||
jmethodID method_id = env->GetStaticMethodID(activity, "httpRequest",
|
||||
"(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[BLjava/lang/String;)[B");
|
||||
if (method_id == nullptr)
|
||||
{
|
||||
env->ExceptionClear();
|
||||
env->DeleteLocalRef(activity);
|
||||
return false;
|
||||
}
|
||||
|
||||
jobjectArray jheaders = nullptr;
|
||||
if (headerPairCount > 0)
|
||||
{
|
||||
// java/lang/String, unlike an app class, resolves from any thread.
|
||||
jclass stringClass = env->FindClass("java/lang/String");
|
||||
if (stringClass == nullptr)
|
||||
{
|
||||
env->ExceptionClear();
|
||||
env->DeleteLocalRef(activity);
|
||||
return false;
|
||||
}
|
||||
jheaders = env->NewObjectArray((jsize) headerPairCount, stringClass, nullptr);
|
||||
env->DeleteLocalRef(stringClass);
|
||||
if (jheaders == nullptr)
|
||||
{
|
||||
env->ExceptionClear();
|
||||
env->DeleteLocalRef(activity);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < headerPairCount; i++)
|
||||
{
|
||||
jstring field = env->NewStringUTF(headerPairs[i] != nullptr ? headerPairs[i] : "");
|
||||
env->SetObjectArrayElement(jheaders, (jsize) i, field);
|
||||
if (field != nullptr)
|
||||
env->DeleteLocalRef(field);
|
||||
}
|
||||
}
|
||||
|
||||
jstring jurl = env->NewStringUTF(url);
|
||||
jstring jmethod = env->NewStringUTF(method != nullptr ? method : "GET");
|
||||
// raw bytes across the bridge, as httpPost does: a request body is JSON
|
||||
// carrying a base64 save, and a jstring would run it through modified UTF-8
|
||||
jbyteArray jbody = nullptr;
|
||||
if (body != nullptr && bodyLen >= 0)
|
||||
{
|
||||
jbody = env->NewByteArray((jsize) bodyLen);
|
||||
if (jbody != nullptr && bodyLen > 0)
|
||||
env->SetByteArrayRegion(jbody, 0, (jsize) bodyLen, (const jbyte*) body);
|
||||
}
|
||||
jstring jua = env->NewStringUTF(userAgent != nullptr ? userAgent : "gen1recomp");
|
||||
|
||||
jobject result = env->CallStaticObjectMethod(activity, method_id, jurl, jmethod,
|
||||
jheaders, jbody, jua);
|
||||
|
||||
env->DeleteLocalRef(jurl);
|
||||
env->DeleteLocalRef(jmethod);
|
||||
if (jheaders != nullptr)
|
||||
env->DeleteLocalRef(jheaders);
|
||||
if (jbody != nullptr)
|
||||
env->DeleteLocalRef(jbody);
|
||||
env->DeleteLocalRef(jua);
|
||||
env->DeleteLocalRef(activity);
|
||||
|
||||
if (result == nullptr)
|
||||
return false;
|
||||
|
||||
jbyteArray bytes = (jbyteArray) result;
|
||||
jsize length = env->GetArrayLength(bytes);
|
||||
if (length > 0)
|
||||
{
|
||||
out.resize((size_t) length);
|
||||
env->GetByteArrayRegion(bytes, 0, length, (jbyte*) &out[0]);
|
||||
}
|
||||
env->DeleteLocalRef(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* TLS sockets. Same resolution rule as httpDownload above -- the activity's
|
||||
* own class, never FindClass -- and the same tolerance for an old APK: a
|
||||
|
||||
@@ -106,6 +106,21 @@ bool httpDownload(const char *url, const char *destPath, const char *userAgent,
|
||||
**/
|
||||
bool httpPost(const char *url, const char *body, int bodyLen, const char *contentType, const char *userAgent);
|
||||
|
||||
/**
|
||||
* Blocking HTTPS request with a method, headers and a byte body
|
||||
* (GameActivity.httpRequest). What save sync needs and neither of the two
|
||||
* above can give it: PUT, per-request auth headers, and the response body of
|
||||
* a 4xx as well as a 2xx. headerPairs is a flat name, value array of
|
||||
* headerPairCount entries; body/userAgent may be null. `out` receives the
|
||||
* Java side's envelope -- a head line of "STATUS <code>" or "ERROR <text>",
|
||||
* a newline, then the raw response bytes. False means the platform has no
|
||||
* such bridge at all (an old APK under a newer liblove), which the Lua side
|
||||
* reports as "update the app" rather than as a failed request.
|
||||
**/
|
||||
bool httpRequest(const char *url, const char *method,
|
||||
const char *const *headerPairs, int headerPairCount,
|
||||
const char *body, int bodyLen, const char *userAgent, std::string &out);
|
||||
|
||||
/**
|
||||
* TLS client sockets (GameActivity.tls*, implemented by TlsSocket.java).
|
||||
* LuaSocket, which is what LOVE ships, does TCP only, so wss:// is otherwise
|
||||
|
||||
@@ -274,6 +274,27 @@ bool System::httpPost(const char *url, const char *body, int bodyLen,
|
||||
#endif
|
||||
}
|
||||
|
||||
bool System::httpRequest(const char *url, const char *method,
|
||||
const char *const *headerPairs, int headerPairCount,
|
||||
const char *body, int bodyLen, const char *userAgent,
|
||||
std::string &out) const
|
||||
{
|
||||
#ifdef LOVE_ANDROID
|
||||
return love::android::httpRequest(url, method, headerPairs, headerPairCount,
|
||||
body, bodyLen, userAgent, out);
|
||||
#else
|
||||
LOVE_UNUSED(url);
|
||||
LOVE_UNUSED(method);
|
||||
LOVE_UNUSED(headerPairs);
|
||||
LOVE_UNUSED(headerPairCount);
|
||||
LOVE_UNUSED(body);
|
||||
LOVE_UNUSED(bodyLen);
|
||||
LOVE_UNUSED(userAgent);
|
||||
out.clear();
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int System::tlsOpen(const char *host, int port) const
|
||||
{
|
||||
#ifdef LOVE_ANDROID
|
||||
|
||||
@@ -159,6 +159,18 @@ public:
|
||||
virtual bool httpPost(const char *url, const char *body, int bodyLen,
|
||||
const char *contentType = nullptr, const char *userAgent = nullptr) const;
|
||||
|
||||
/**
|
||||
* Blocking HTTPS request with a method, headers and a byte body (Android
|
||||
* only; false elsewhere). Save sync needs PUT, auth headers and the body
|
||||
* of a 4xx, none of which the two bridges above can express. headerPairs
|
||||
* is a flat name, value array; `out` receives the response envelope
|
||||
* ("STATUS <code>" or "ERROR <text>", a newline, then the raw body).
|
||||
**/
|
||||
virtual bool httpRequest(const char *url, const char *method,
|
||||
const char *const *headerPairs, int headerPairCount,
|
||||
const char *body, int bodyLen, const char *userAgent,
|
||||
std::string &out) const;
|
||||
|
||||
/**
|
||||
* TLS client sockets (Android only; every call fails elsewhere, where
|
||||
* LuaSec or another provider is the answer). Non-blocking by contract:
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include "wrap_System.h"
|
||||
#include "sdl/System.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace system
|
||||
@@ -150,6 +153,57 @@ int w_httpPost(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* love.system.httpRequest(url, method, headers, body, userAgent) -> envelope
|
||||
*
|
||||
* `headers` is a flat array of alternating header name and value strings, so
|
||||
* it maps straight onto the Java bridge's String[] without any parsing here.
|
||||
* The single return is the response envelope -- a head line of
|
||||
* "STATUS <code>" or "ERROR <text>", a newline, then the raw body -- or nil
|
||||
* where the build has no bridge, which src/core/HostShell.lua turns into an
|
||||
* "update the app" notice rather than a failed request.
|
||||
*/
|
||||
int w_httpRequest(lua_State *L)
|
||||
{
|
||||
const char *url = luaL_checkstring(L, 1);
|
||||
const char *method = luaL_optstring(L, 2, "GET");
|
||||
|
||||
std::vector<std::string> fields;
|
||||
if (!lua_isnoneornil(L, 3))
|
||||
{
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
size_t count = luax_objlen(L, 3);
|
||||
for (size_t i = 1; i <= count; i++)
|
||||
{
|
||||
lua_rawgeti(L, 3, (int) i);
|
||||
const char *field = lua_tostring(L, -1);
|
||||
fields.push_back(field != nullptr ? field : "");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
std::vector<const char *> pairs;
|
||||
for (size_t i = 0; i < fields.size(); i++)
|
||||
pairs.push_back(fields[i].c_str());
|
||||
|
||||
size_t bodyLen = 0;
|
||||
const char *body = nullptr;
|
||||
if (!lua_isnoneornil(L, 4))
|
||||
body = luaL_checklstring(L, 4, &bodyLen);
|
||||
const char *ua = luaL_optstring(L, 5, nullptr);
|
||||
|
||||
std::string out;
|
||||
bool ok = instance()->httpRequest(url, method,
|
||||
pairs.empty() ? nullptr : &pairs[0], (int) pairs.size(),
|
||||
body, (int) bodyLen, ua, out);
|
||||
if (!ok)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
lua_pushlstring(L, out.data(), out.size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_hasBackgroundMusic(lua_State *L)
|
||||
{
|
||||
lua_pushboolean(L, instance()->hasBackgroundMusic());
|
||||
@@ -245,6 +299,7 @@ static const luaL_Reg functions[] =
|
||||
{ "restartApp", w_restartApp },
|
||||
{ "httpDownload", w_httpDownload },
|
||||
{ "httpPost", w_httpPost },
|
||||
{ "httpRequest", w_httpRequest },
|
||||
{ "tlsOpen", w_tlsOpen },
|
||||
{ "tlsStatus", w_tlsStatus },
|
||||
{ "tlsSend", w_tlsSend },
|
||||
|
||||
Reference in New Issue
Block a user