Cemu/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp
2022-08-25 03:45:35 +01:00

1375 lines
40 KiB
C++

#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "nlibcurl.h"
#include "openssl/bn.h"
#include "openssl/x509.h"
#include "openssl/ssl.h"
#include "curl/curl.h"
#include <unordered_map>
#include <atomic>
#include "Cafe/IOSU/legacy/iosu_crypto.h"
#include "Cafe/OS/libs/nsysnet/nsysnet.h"
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
#include "util/helpers/ConcurrentQueue.h"
#include "Cafe/OS/common/PPCConcurrentQueue.h"
#include "Common/filestream.h"
#include "config/ActiveSettings.h"
namespace nlibcurl
{
#define CURL_MULTI_HANDLE (0x000bab1e)
#define NSSL_VERIFY_NONE (0x0)
#define NSSL_VERIFY_PEER (1<<0)
#define NSSL_VERIFY_HOSTNAME (1<<1)
#define NSSL_VERIFY_DATE (1<<2)
enum QueueOrder
{
QueueOrder_None,
QueueOrder_Result,
QueueOrder_CBDone,
QueueOrder_HeaderCB,
QueueOrder_ReadCB,
QueueOrder_WriteCB,
QueueOrder_ProgressCB,
QueueOrder_Perform,
QueueOrder_Pause,
};
struct QueueMsg_t
{
QueueOrder order;
union
{
uint32 result;
struct
{
char* buffer;
uint32 size;
uint32 nitems;
} header_cb;
struct
{
char* buffer;
uint32 size;
uint32 nitems;
} read_cb;
struct
{
char* buffer;
uint32 size;
uint32 nmemb;
} write_cb;
struct
{
uint32 clientp;
double dltotal;
double dlnow;
double ultotal;
double ulnow;
} progress_cb;
struct
{
sint32 bitmask;
} pause;
};
};
struct MEMPTRHash_t
{
size_t operator()(const MEMPTR<void>& p) const
{
return p.GetMPTR();
}
};
struct
{
sint32 initialized;
MEMPTR<void> proxyConfig;
MEMPTR<curl_malloc_callback> malloc;
MEMPTR<curl_free_callback> free;
MEMPTR<curl_realloc_callback> realloc;
MEMPTR<curl_strdup_callback> strdup;
MEMPTR<curl_calloc_callback> calloc;
} g_nlibcurl = {};
#pragma pack(1)
struct CURL_t
{
CURL* curl;
uint32be hNSSL;
uint32be nsslVerifyOptions;
MEMPTR<void> out; // CURLOPT_WRITEDATA
MEMPTR<void> in_set; // CURLOPT_READDATA
MEMPTR<void> writeheader; // CURLOPT_HEADERDATA
MEMPTR<void> fwrite_func; // CURLOPT_WRITEFUNCTION
MEMPTR<void> fwrite_header; // CURLOPT_HEADERFUNCTION
MEMPTR<void> fread_func_set; // CURLOPT_READFUNCTION
MEMPTR<void> progress_client; // CURLOPT_PROGRESSDATA
MEMPTR<void> fprogress; // CURLOPT_PROGRESSFUNCTION
MEMPTR<void> fsockopt; // CURLOPT_SOCKOPTFUNCTION
MEMPTR<void> sockopt_client; // CURLOPT_SOCKOPTDATA
// Cemu specific
OSThread_t* curlThread;
MEMPTR<char> info_redirectUrl; // stores CURLINFO_REDIRECT_URL ptr
MEMPTR<char> info_contentType; // stores CURLINFO_CONTENT_TYPE ptr
// debug
struct
{
uint32 activeRequestIndex{};
uint32 responseRequestIndex{}; // set to activeRequestIndex once perform is called
bool hasDumpedResultInfo{}; // set once the response info has been dumped (reset when next request is performed)
FileStream* file_requestParam{};
FileStream* file_responseHeaders{};
FileStream* file_responseRaw{};
}debug;
};
static_assert(sizeof(CURL_t) <= 0x8698);
typedef MEMPTR<CURL_t> CURLPtr;
typedef struct
{
//uint32be specifier; // 0x00
//uint32be dirty; // 0x04
MEMPTR<void> lockfunc; // 0x08
MEMPTR<void> unlockfunc; // 0x0C
MEMPTR<void> data; // 0x10
//MEMPTR<void> uk14; // 0x14
//MEMPTR<void> uk18; // 0x18
CURLSH* curlsh;
MEMPTR <CURL_t> curl;
uint32 uk1;
}CURLSH_t; // SCURL
static_assert(sizeof(CURLSH_t) <= 0x1C);
typedef MEMPTR<CURLSH_t> CURLSHPtr;
typedef struct
{
CURLM* curlm;
std::vector< MEMPTR<CURL> > curl;
}CURLM_t;
static_assert(sizeof(CURLM_t) <= 0x80, "sizeof(CURLM_t)");
typedef MEMPTR<CURLM_t> CURLMPtr;
struct curl_slist_t
{
MEMPTR<char> data;
MEMPTR<curl_slist_t> next;
};
static_assert(sizeof(curl_slist_t) <= 0x8, "sizeof(curl_slist_t)");
struct CURLMsg_t
{
uint32be msg;
MEMPTR<CURL_t> curl;
uint32be result; // CURLcode
};
static_assert(sizeof(CURLMsg_t) <= 0xC, "sizeof(CURLMsg_t)");
#pragma pack()
#include "nlibcurlDebug.hpp"
size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata);
THREAD_LOCAL PPCConcurrentQueue<QueueMsg_t>* g_callerQueue;
THREAD_LOCAL ConcurrentQueue<QueueMsg_t>* g_threadQueue;
void CurlWorkerThread(CURL_t* curl, PPCConcurrentQueue<QueueMsg_t>* callerQueue, ConcurrentQueue<QueueMsg_t>* threadQueue)
{
g_callerQueue = callerQueue;
g_threadQueue = threadQueue;
const QueueMsg_t msg = threadQueue->pop();
QueueMsg_t resultMsg = {};
resultMsg.order = QueueOrder_Result;
if (msg.order == QueueOrder_Perform)
resultMsg.result = ::curl_easy_perform(curl->curl);
else if(msg.order == QueueOrder_Pause)
resultMsg.result = ::curl_easy_pause(curl->curl, msg.pause.bitmask);
else
assert_dbg();
callerQueue->push(resultMsg, curl->curlThread);
}
uint32 SendOrderToWorker(CURL_t* curl, QueueOrder order, uint32 arg1 = 0)
{
OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance);
curl->curlThread = currentThread;
// forceLogDebug_printf("CURRENTTHREAD: 0x%p -> %d",currentThread, order)
PPCConcurrentQueue<QueueMsg_t> callerQueue;
ConcurrentQueue<QueueMsg_t> threadQueue;
std::thread worker(CurlWorkerThread, curl, &callerQueue, &threadQueue);
worker.detach();
QueueMsg_t orderMsg = {};
orderMsg.order = order;
if (order == QueueOrder_Pause)
orderMsg.pause.bitmask = arg1;
threadQueue.push(orderMsg);
uint32 result;
while (true)
{
const QueueMsg_t msg = callerQueue.pop(currentThread);
if (msg.order == QueueOrder_ProgressCB)
{
QueueMsg_t sendMsg = {};
sendMsg.order = QueueOrder_CBDone;
sendMsg.result = PPCCoreCallback(curl->fprogress.GetMPTR(), curl->progress_client.GetMPTR(), msg.progress_cb.dltotal, msg.progress_cb.dlnow, msg.progress_cb.ultotal, msg.progress_cb.ulnow);
threadQueue.push(sendMsg);
}
else if (msg.order == QueueOrder_HeaderCB)
{
StackAllocator<char> tmp(msg.header_cb.size * msg.header_cb.nitems);
memcpy(tmp.GetPointer(), msg.header_cb.buffer, msg.header_cb.size * msg.header_cb.nitems);
QueueMsg_t sendMsg = {};
sendMsg.order = QueueOrder_CBDone;
sendMsg.result = PPCCoreCallback(curl->fwrite_header.GetMPTR(), tmp.GetMPTR(), msg.header_cb.size, msg.header_cb.nitems, curl->writeheader.GetMPTR());
threadQueue.push(sendMsg);
}
else if (msg.order == QueueOrder_ReadCB)
{
StackAllocator<char> tmp(msg.read_cb.size * msg.read_cb.nitems);
QueueMsg_t sendMsg = {};
sendMsg.order = QueueOrder_CBDone;
forceLogDebug_printf("QueueOrder_ReadCB size: %d nitems: %d", msg.read_cb.size, msg.read_cb.nitems);
sendMsg.result = PPCCoreCallback(curl->fread_func_set.GetMPTR(), tmp.GetMPTR(), msg.read_cb.size, msg.read_cb.nitems, curl->in_set.GetMPTR());
forceLogDebug_printf("readcb size: %d", (sint32)sendMsg.result);
if (sendMsg.result > 0)
memcpy(msg.read_cb.buffer, tmp.GetPointer(), sendMsg.result);
threadQueue.push(sendMsg);
}
else if (msg.order == QueueOrder_WriteCB)
{
StackAllocator<char> tmp(msg.write_cb.size * msg.write_cb.nmemb);
memcpy(tmp.GetPointer(), msg.write_cb.buffer, msg.write_cb.size * msg.write_cb.nmemb);
QueueMsg_t sendMsg = {};
sendMsg.order = QueueOrder_CBDone;
sendMsg.result = PPCCoreCallback(curl->fwrite_func.GetMPTR(), tmp.GetMPTR(), msg.write_cb.size, msg.write_cb.nmemb, curl->out.GetMPTR());
threadQueue.push(sendMsg);
}
else if (msg.order == QueueOrder_Result)
{
result = msg.result;
break;
}
}
return result;
}
void export_malloc(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(size, 0);
MPTR memAddr = PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeap, size);
osLib_returnFromFunction(hCPU, memAddr);
}
void export_calloc(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(count, 0);
ppcDefineParamU32(size, 1);
MPTR memAddr = PPCCoreCallback(gCoreinitData->MEMAllocFromDefaultHeap, count*size);
osLib_returnFromFunction(hCPU, memAddr);
}
void export_free(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(mem, void, 0);
PPCCoreCallback(gCoreinitData->MEMFreeToDefaultHeap, mem.GetMPTR());
osLib_returnFromFunction(hCPU, 0);
}
void export_strdup(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(str, const char, 0);
int len = (int)strlen(str.GetPtr()) + 1;
MEMPTR<char> result = (char*)coreinit::default_MEMAllocFromDefaultHeap(len);
strcpy(result.GetPtr(), str.GetPtr());
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
void export_realloc(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(mem, void, 0);
ppcDefineParamU32(size, 1);
MEMPTR<void> result = coreinit::default_MEMAllocFromDefaultHeap(size);
memcpy(result.GetPtr(), mem.GetPtr(), size);
coreinit::default_MEMFreeToDefaultHeap(mem.GetPtr());
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
CURLcode curl_global_init(uint32 flags)
{
if (g_nlibcurl.initialized++)
{
return CURLE_OK;
}
g_nlibcurl.malloc = PPCInterpreter_makeCallableExportDepr(export_malloc);
g_nlibcurl.calloc = PPCInterpreter_makeCallableExportDepr(export_calloc);
g_nlibcurl.free = PPCInterpreter_makeCallableExportDepr(export_free);
g_nlibcurl.strdup = PPCInterpreter_makeCallableExportDepr(export_strdup);
g_nlibcurl.realloc = PPCInterpreter_makeCallableExportDepr(export_realloc);
// curl_read_default_proxy_config()
return ::curl_global_init(flags);
}
void export_curl_multi_init(PPCInterpreter_t* hCPU)
{
CURLMPtr result{ PPCCoreCallback(g_nlibcurl.calloc, 1, ppcsizeof<CURLM_t>()) };
forceLogDebug_printf("curl_multi_init() -> 0x%08x", result.GetMPTR());
if (result)
{
memset(result.GetPtr(), 0, sizeof(CURLM_t));
*result = {};
result->curlm = curl_multi_init();
}
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
void export_curl_multi_add_handle(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamMEMPTR(curl, CURL_t, 1);
curlDebug_markActiveRequest(curl.GetPtr());
curlDebug_notifySubmitRequest(curl.GetPtr());
CURLMcode result = ::curl_multi_add_handle(curlm->curlm, curl->curl);
if (result == CURLM_OK)
curlm->curl.push_back(curl.GetPtr());
forceLogDebug_printf("curl_multi_add_handle(0x%08x, 0x%08x) -> 0x%x", curlm.GetMPTR(), curl.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_multi_remove_handle(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamMEMPTR(curl, CURL_t, 1);
CURLMcode result = curl_multi_remove_handle(curlm->curlm, curl->curl);
if (result == CURLM_OK)
{
curlm->curl.erase(std::remove(curlm->curl.begin(), curlm->curl.end(), (void*)curl.GetPtr()), curlm->curl.end());
}
forceLogDebug_printf("curl_multi_remove_handle(0x%08x, 0x%08x) -> 0x%x", curlm.GetMPTR(), curl.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_multi_timeout(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamMEMPTR(timeoParam, uint32be, 1);
long timeoutLE = (long)(uint32)*timeoParam;
CURLMcode result = ::curl_multi_timeout(curlm->curlm, &timeoutLE);
*timeoParam = (uint32)timeoutLE;
forceLogDebug_printf("curl_multi_timeout(0x%08x, 0x%08x [%d]) -> 0x%x", curlm.GetMPTR(), timeoParam.GetMPTR(), timeoutLE, result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_multi_cleanup(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
CURLMcode result = ::curl_multi_cleanup(curlm->curlm);
forceLogDebug_printf("curl_multi_cleanup(0x%08x) -> 0x%x", curlm.GetMPTR(), result);
curlm->curl.clear();
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), curlm.GetMPTR());
osLib_returnFromFunction(hCPU, result);
}
void export_curl_multi_perform(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamMEMPTR(runningHandles, uint32be, 1);
//forceLogDebug_printf("curl_multi_perform(0x%08x, 0x%08x)", curlm.GetMPTR(), runningHandles.GetMPTR());
//g_callerQueue = curlm->callerQueue;
//g_threadQueue = curlm->threadQueue;
int tempRunningHandles = 0;
CURLMcode result = curl_multi_perform(curlm->curlm, &tempRunningHandles);
*(runningHandles.GetPtr()) = tempRunningHandles;
forceLogDebug_printf("curl_multi_perform(0x%08x, 0x%08x) -> 0x%x (running handles: %d) LR: 0x%08x", curlm.GetMPTR(), runningHandles.GetMPTR(), result, tempRunningHandles, hCPU->spr.LR);
//const uint32 result = SendOrderToWorker(curlm.GetPtr(), QueueOrder_MultiPerform);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_multi_fdset(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamMEMPTR(readFd, wu_fd_set, 1);
ppcDefineParamMEMPTR(writeFd, wu_fd_set, 2);
ppcDefineParamMEMPTR(exceptionFd, wu_fd_set, 3);
ppcDefineParamU32BEPtr(maxFd, 4);
#if BOOST_OS_LINUX > 0
cemuLog_log(LogType::Force, "curl_multi_fdset(...) - todo");
osLib_returnFromFunction(hCPU, 0);
#else
fd_set h_readFd;
fd_set h_writeFd;
fd_set h_exceptionFd;
FD_ZERO(&h_readFd);
FD_ZERO(&h_writeFd);
FD_ZERO(&h_exceptionFd);
sint32 h_maxFd = 0;
CURLMcode result = curl_multi_fdset(curlm->curlm, &h_readFd, &h_writeFd, &h_exceptionFd, &h_maxFd);
nsysnet::wuResetFD(readFd.GetPtr());
nsysnet::wuResetFD(writeFd.GetPtr());
nsysnet::wuResetFD(exceptionFd.GetPtr());
sint32 c_maxFD = -1;
// fd read set
for (uint32 i = 0; i < h_readFd.fd_count; i++)
{
sint32 wuSocket = nsysnet_getVirtualSocketHandleFromHostHandle(h_readFd.fd_array[i]);
if (wuSocket < 0)
wuSocket = nsysnet_createVirtualSocketFromExistingSocket(h_readFd.fd_array[i]);
if (wuSocket >= 0)
{
c_maxFD = std::max(wuSocket, c_maxFD);
nsysnet::wuSetFD(readFd.GetPtr(), wuSocket);
}
}
// fd write set
for (uint32 i = 0; i < h_writeFd.fd_count; i++)
{
cemu_assert_debug(false);
}
// fd exception set
for (uint32 i = 0; i < h_exceptionFd.fd_count; i++)
{
cemu_assert_debug(false);
}
*maxFd = c_maxFD;
osLib_returnFromFunction(hCPU, result);
#endif
}
void export_curl_multi_setopt(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamU32(option, 1);
ppcDefineParamMEMPTR(parameter, void, 2);
ppcDefineParamU64(parameterU64, 2);
CURLMcode result = CURLM_OK;
switch (option)
{
case CURLMOPT_MAXCONNECTS:
result = ::curl_multi_setopt(curlm->curlm, (CURLMoption)option, parameter.GetMPTR());
break;
default:
forceLogDebug_printf("curl_multi_setopt(0x%08x, %d, 0x%08x) unsupported option", curlm.GetMPTR(), option, parameter.GetMPTR());
}
forceLogDebug_printf("curl_multi_setopt(0x%08x, %d, 0x%08x) -> 0x%x", curlm.GetMPTR(), option, parameter.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_multi_info_read(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlm, CURLM_t, 0);
ppcDefineParamMEMPTR(msgsInQueue, int, 1);
CURLMsg* msg = ::curl_multi_info_read(curlm->curlm, msgsInQueue.GetPtr());
forceLogDebug_printf("curl_multi_info_read - todo");
if (msg)
{
MEMPTR<CURLMsg_t> result{ PPCCoreCallback(g_nlibcurl.malloc.GetMPTR(), ppcsizeof<CURLMsg_t>()) };
result->msg = msg->msg;
result->result = msg->data.result;
if (msg->easy_handle)
{
const auto it = find_if(curlm->curl.cbegin(), curlm->curl.cend(),
[msg](const MEMPTR<void>& curl)
{
const MEMPTR<CURL_t> _curl{ curl };
return _curl->curl = msg->easy_handle;
});
if (it != curlm->curl.cend())
result->curl = (CURL_t*)(*it).GetPtr();
}
else
result->curl = nullptr;
forceLogDebug_printf("curl_multi_info_read(0x%08x, 0x%08x [%d]) -> 0x%08x (0x%x, 0x%08x, 0x%x)", curlm.GetMPTR(), msgsInQueue.GetMPTR(), *msgsInQueue.GetPtr(),
result.GetMPTR(), msg->msg, result->curl.GetMPTR(), msg->data.result);
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
else
{
osLib_returnFromFunction(hCPU, 0);
}
}
void lock_function(CURL* handle, curl_lock_data data, curl_lock_access access, void* userptr)
{
CURLSH_t* share = (CURLSH_t*)userptr;
PPCCoreCallback(share->lockfunc.GetMPTR(), share->curl.GetMPTR(), (uint32)data, (uint32)access, share->data.GetMPTR());
}
void unlock_function(CURL* handle, curl_lock_data data, void* userptr)
{
CURLSH_t* share = (CURLSH_t*)userptr;
PPCCoreCallback(share->unlockfunc.GetMPTR(), share->curl.GetMPTR(), (uint32)data, share->data.GetMPTR());
}
void export_curl_share_setopt(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(share, CURLSH_t, 0);
ppcDefineParamU32(option, 1);
ppcDefineParamMEMPTR(parameter, void, 2);
/*if(share->dirty)
{
osLib_returnFromFunction(hCPU, CURLSHE_IN_USE);
return;
}*/
CURLSH* curlSh = share->curlsh;
CURLSHcode result = CURLSHE_OK;
switch (option)
{
case CURLSHOPT_USERDATA:
share->data = parameter;
result = curl_share_setopt(curlSh, CURLSHOPT_USERDATA, share.GetPtr());
break;
case CURLSHOPT_LOCKFUNC:
share->lockfunc = parameter;
result = curl_share_setopt(curlSh, CURLSHOPT_LOCKFUNC, lock_function);
break;
case CURLSHOPT_UNLOCKFUNC:
share->unlockfunc = parameter;
result = curl_share_setopt(curlSh, CURLSHOPT_UNLOCKFUNC, unlock_function);
break;
case CURLSHOPT_SHARE:
{
const sint32 type = parameter.GetMPTR();
result = curl_share_setopt(curlSh, CURLSHOPT_SHARE, type);
break;
}
case CURLSHOPT_UNSHARE:
{
const sint32 type = parameter.GetMPTR();
result = curl_share_setopt(curlSh, CURLSHOPT_UNSHARE, type);
break;
}
default:
cemu_assert_unimplemented();
}
forceLogDebug_printf("curl_share_setopt(0x%08x, 0x%x, 0x%08x) -> 0x%x", share.GetMPTR(), option, parameter.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_share_init(PPCInterpreter_t* hCPU)
{
CURLSHPtr result{ PPCCoreCallback(g_nlibcurl.calloc.GetMPTR(), (uint32)1, ppcsizeof<CURLSH_t>()) };
forceLogDebug_printf("curl_share_init() -> 0x%08x", result.GetMPTR());
if (result)
{
memset(result.GetPtr(), 0, sizeof(CURLSH_t));
*result = {};
result->curlsh = curl_share_init();
}
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
void export_curl_share_cleanup(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curlsh, CURLSH_t, 0);
uint32 result = ::curl_share_cleanup(curlsh->curlsh);
forceLogDebug_printf("curl_share_cleanup(0x%08x) -> 0x%x", curlsh.GetMPTR(), result);
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), curlsh.GetMPTR());
osLib_returnFromFunction(hCPU, 0);
}
int my_trace(CURL *handle, curl_infotype type, char *ptr, size_t size,
void *userp)
{
FILE* f = (FILE*)userp;
//if (type == CURLINFO_TEXT)
{
char tmp[1024] = {};
sprintf(tmp, "0x%p: ", handle);
fwrite(tmp, 1, strlen(tmp), f);
memcpy(tmp, ptr, std::min(size, (size_t)990));
fwrite(tmp, 1, std::min(size + 1, (size_t)991), f);
fflush(f);
}
return 0;
}
static int curl_closesocket(void *clientp, curl_socket_t item)
{
nsysnet_notifyCloseSharedSocket((SOCKET)item);
closesocket(item);
return 0;
}
void export_curl_easy_init(PPCInterpreter_t* hCPU)
{
if (g_nlibcurl.initialized == 0)
{
if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
{
osLib_returnFromFunction(hCPU, 0);
return;
}
}
// Curl_open
CURLPtr result{ PPCCoreCallback(g_nlibcurl.calloc.GetMPTR(), (uint32)1, ppcsizeof<CURL_t>()) };
forceLogDebug_printf("curl_easy_init() -> 0x%08x", result.GetMPTR());
if (result)
{
memset(result.GetPtr(), 0, sizeof(CURL_t));
*result = {};
result->curl = curl_easy_init();
result->curlThread = coreinitThread_getCurrentThreadDepr(ppcInterpreterCurrentInstance);
result->info_contentType = nullptr;
result->info_redirectUrl = nullptr;
// default parameters
curl_easy_setopt(result->curl, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(result->curl, CURLOPT_HEADERDATA, result.GetPtr());
curl_easy_setopt(result->curl, CURLOPT_CLOSESOCKETFUNCTION, curl_closesocket);
curl_easy_setopt(result->curl, CURLOPT_CLOSESOCKETDATA, nullptr);
if (g_nlibcurl.proxyConfig)
{
// todo
}
}
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
void export_curl_easy_pause(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curl, CURL_t, 0);
ppcDefineParamS32(bitmask, 1);
forceLogDebug_printf("curl_easy_pause(0x%08x, 0x%x)", curl.GetMPTR(), bitmask);
//const CURLcode result = ::curl_easy_pause(curl->curl, bitmask);
const uint32 result = SendOrderToWorker(curl.GetPtr(), QueueOrder_Pause, bitmask);
forceLogDebug_printf("curl_easy_pause(0x%08x, 0x%x) DONE", curl.GetMPTR(), bitmask);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_easy_cleanup(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curl, CURL_t, 0);
forceLogDebug_printf("curl_easy_cleanup(0x%08x)", curl.GetMPTR());
curlDebug_cleanup(curl.GetPtr());
::curl_easy_cleanup(curl->curl);
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), curl.GetMPTR());
if (curl->info_contentType.IsNull() == false)
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), curl->info_contentType.GetMPTR());
if (curl->info_redirectUrl.IsNull() == false)
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), curl->info_redirectUrl.GetMPTR());
osLib_returnFromFunction(hCPU, 0);
}
void export_curl_easy_reset(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curl, CURL_t, 0);
forceLogDebug_printf("curl_easy_reset(0x%08x)", curl.GetMPTR());
// curl_apply_default_proxy_config();
::curl_easy_reset(curl->curl);
osLib_returnFromFunction(hCPU, 0);
}
int ssl_verify_callback(int preverify_ok, X509_STORE_CTX* ctx)
{
if (preverify_ok) return preverify_ok;
int err = X509_STORE_CTX_get_error(ctx);
if(err != 0)
forceLogDebug_printf("ssl_verify_callback: Error %d but allow certificate anyway", err);
X509_STORE_CTX_set_error(ctx, 0);
return 1;
}
CURLcode ssl_ctx_callback(CURL* curl, void* sslctx, void* param)
{
//peterBreak();
CURL_t* ppcCurl = (CURL_t*)param;
nsysnet::NSSLInternalState_t* nssl = nsysnet::GetNSSLContext((uint32)ppcCurl->hNSSL);
for (uint32 i : nssl->serverPKIs)
{
if (iosuCrypto_addCACertificate(sslctx, i) == false)
{
cemu_assert_suspicious();
return CURLE_SSL_CACERT;
}
}
for (auto& customPKI : nssl->serverCustomPKIs)
{
if (iosuCrypto_addCustomCACertificate(sslctx, &customPKI[0], (sint32)customPKI.size()) == false)
{
cemu_assert_suspicious();
return CURLE_SSL_CACERT;
}
}
if (nssl->clientPKI != 0 && iosuCrypto_addClientCertificate(sslctx, nssl->clientPKI) == false)
{
cemu_assert_suspicious();
return CURLE_SSL_CERTPROBLEM;
}
uint32 flags = ppcCurl->nsslVerifyOptions;
if (HAS_FLAG(flags, NSSL_VERIFY_PEER))
{
::SSL_CTX_set_cipher_list((SSL_CTX*)sslctx, "AES256-SHA"); // TLS_RSA_WITH_AES_256_CBC_SHA (in CURL it's called rsa_aes_256_sha)
::SSL_CTX_set_mode((SSL_CTX*)sslctx, SSL_MODE_AUTO_RETRY);
::SSL_CTX_set_verify_depth((SSL_CTX*)sslctx, 2);
::SSL_CTX_set_verify((SSL_CTX*)sslctx, SSL_VERIFY_PEER, ssl_verify_callback);
}
else
{
::SSL_CTX_set_verify((SSL_CTX*)sslctx, SSL_VERIFY_NONE, nullptr);
}
return CURLE_OK;
}
size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata)
{
//peterBreak();
CURL_t* curl = (CURL_t*)userdata;
curlDebug_headerWrite(curl, buffer, size, nitems);
if (curl->fwrite_header.IsNull())
{
return size * nitems;
}
if (g_callerQueue == nullptr || g_threadQueue == nullptr)
{
StackAllocator<char> tmp((uint32)(size * nitems));
memcpy(tmp.GetPointer(), buffer, size * nitems);
return PPCCoreCallback(curl->fwrite_header.GetMPTR(), tmp.GetMPTR(), (uint32)size, (uint32)nitems, curl->writeheader.GetMPTR());
}
QueueMsg_t msg = {};
msg.order = QueueOrder_HeaderCB;
msg.header_cb.buffer = buffer;
msg.header_cb.size = (uint32)size;
msg.header_cb.nitems = (uint32)nitems;
g_callerQueue->push(msg, curl->curlThread);
msg = g_threadQueue->pop();
if (msg.order != QueueOrder_CBDone)
cemu_assert_suspicious();
#ifndef PUBLIC_RELEASE
char debug[500];
cemu_assert_debug((size*nitems) < 500);
memcpy(debug, buffer, size*nitems);
debug[size*nitems] = 0;
forceLogDebug_printf("header_callback(0x%p, 0x%x, 0x%x, 0x%08x) [%s]", (void*)buffer, size, nitems, curl->writeheader.GetMPTR(), debug);
#endif
return msg.result;
}
size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata)
{
CURL_t* curl = (CURL_t*)userdata;
curlDebug_resultWrite(curl, ptr, size, nmemb);
//StackAllocator<char> tmp(size * nmemb);
//memcpy(tmp.GetPointer(), ptr, size * nmemb);
forceLogDebug_printf("write_callback(0x%p 0x%x, 0x%x, 0x%08x)", (void*)ptr, size, nmemb, curl->out.GetMPTR());
if (g_callerQueue == nullptr || g_threadQueue == nullptr)
{
StackAllocator<char> tmp((uint32)(size * nmemb));
memcpy(tmp.GetPointer(), ptr, size * nmemb);
int r = PPCCoreCallback(curl->fwrite_func.GetMPTR(), tmp.GetMPTR(), (uint32)size, (uint32)nmemb, curl->out.GetMPTR());
return r;
}
QueueMsg_t msg = {};
msg.order = QueueOrder_WriteCB;
msg.write_cb.buffer = ptr;
msg.write_cb.size = (uint32)size;
msg.write_cb.nmemb = (uint32)nmemb;
g_callerQueue->push(msg, curl->curlThread);
msg = g_threadQueue->pop();
if (msg.order != QueueOrder_CBDone)
cemu_assert_suspicious();
return msg.result;
}
int sockopt_callback(void* clientp, curl_socket_t curlfd, curlsocktype purpose)
{
CURL_t* curl = (CURL_t*)clientp;
forceLogDebug_printf("sockopt_callback called!");
sint32 r = 0;
return r;
}
size_t read_callback(char* buffer, size_t size, size_t nitems, void* instream)
{
CURL_t* curl = (CURL_t*)instream;
forceLogDebug_printf("read_callback(0x%p, 0x%x, 0x%x, 0x%08x) [func: 0x%x]", (void*)buffer, size, nitems, curl->in_set.GetMPTR(), curl->fread_func_set.GetMPTR());
if (g_callerQueue == nullptr || g_threadQueue == nullptr)
{
StackAllocator<char> tmp((uint32)(size * nitems));
const sint32 result = PPCCoreCallback(curl->fread_func_set.GetMPTR(), tmp.GetMPTR(), (uint32)size, (uint32)nitems, curl->in_set.GetMPTR());
memcpy(buffer, tmp.GetPointer(), result);
return result;
}
QueueMsg_t msg = {};
msg.order = QueueOrder_ReadCB;
msg.read_cb.buffer = buffer;
msg.read_cb.size = (uint32)size;
msg.read_cb.nitems = (uint32)std::min<uint32>(nitems, 0x4000); // limit this to 16KB which is the limit in nlibcurl.rpl (Super Mario Maker crashes on level upload if the size is too big)
forceLogDebug_printf("read_callback(0x%p, 0x%x, 0x%x, 0x%08x) [func: 0x%x] PUSH", (void*)buffer, size, nitems, curl->in_set.GetMPTR(), curl->fread_func_set.GetMPTR());
g_callerQueue->push(msg, curl->curlThread);
msg = g_threadQueue->pop();
if (msg.order != QueueOrder_CBDone)
cemu_assert_suspicious();
forceLogDebug_printf("read_callback(0x%p, 0x%x, 0x%x, 0x%08x) DONE Result: %d", (void*)buffer, size, nitems, curl->in_set.GetMPTR(), msg.result);
return msg.result;
}
int progress_callback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
//peterBreak();
CURL_t* curl = (CURL_t*)clientp;
//int result = PPCCoreCallback(curl->fprogress.GetMPTR(), curl->progress_client.GetMPTR(), dltotal, dlnow, ultotal, ulnow);
if(g_callerQueue == nullptr || g_threadQueue == nullptr)
return PPCCoreCallback(curl->fprogress.GetMPTR(), curl->progress_client.GetMPTR(), dltotal, dlnow, ultotal, ulnow);
QueueMsg_t msg = {};
msg.order = QueueOrder_ProgressCB;
msg.progress_cb.dltotal = dltotal;
msg.progress_cb.dlnow = dlnow;
msg.progress_cb.ultotal = ultotal;
msg.progress_cb.ulnow = ulnow;
g_callerQueue->push(msg, curl->curlThread);
msg = g_threadQueue->pop();
if (msg.order != QueueOrder_CBDone)
cemu_assert_suspicious();
forceLogDebug_printf("progress_callback(%.02lf, %.02lf, %.02lf, %.02lf) -> %d", dltotal, dlnow, ultotal, ulnow, msg.result);
return msg.result;
}
void export_curl_easy_setopt(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curl, CURL_t, 0);
ppcDefineParamU32(option, 1);
ppcDefineParamMEMPTR(parameter, void, 2);
ppcDefineParamU64(parameterU64, 2);
CURL* curlObj = curl->curl;
CURLcode result = CURLE_OK;
switch (option)
{
case CURLOPT_NOSIGNAL:
case CURLOPT_HTTPGET:
case CURLOPT_FOLLOWLOCATION:
case CURLOPT_BUFFERSIZE:
case CURLOPT_TIMEOUT:
case CURLOPT_CONNECTTIMEOUT_MS:
case CURLOPT_POST:
case CURLOPT_INFILESIZE:
case CURLOPT_NOPROGRESS:
case CURLOPT_LOW_SPEED_LIMIT:
case CURLOPT_LOW_SPEED_TIME:
case CURLOPT_CONNECTTIMEOUT:
{
result = ::curl_easy_setopt(curlObj, (CURLoption)option, parameter.GetMPTR());
break;
}
case CURLOPT_URL:
{
curlDebug_logEasySetOptStr(curl.GetPtr(), "CURLOPT_URL", (const char*)parameter.GetPtr());
forceLogDebug_printf("curl_easy_setopt(%d) [%s]", option, parameter.GetPtr());
result = ::curl_easy_setopt(curlObj, (CURLoption)option, parameter.GetPtr());
break;
}
case CURLOPT_PROXY:
case CURLOPT_USERAGENT:
{
forceLogDebug_printf("curl_easy_setopt(%d) [%s]", option, parameter.GetPtr());
result = ::curl_easy_setopt(curlObj, (CURLoption)option, parameter.GetPtr());
break;
}
case CURLOPT_POSTFIELDS:
{
curlDebug_logEasySetOptStr(curl.GetPtr(), "CURLOPT_POSTFIELDS", (const char*)parameter.GetPtr());
forceLogDebug_printf("curl_easy_setopt(%d) [%s]", option, parameter.GetPtr());
result = ::curl_easy_setopt(curlObj, (CURLoption)option, parameter.GetPtr());
break;
}
case CURLOPT_MAX_SEND_SPEED_LARGE:
case CURLOPT_MAX_RECV_SPEED_LARGE:
case CURLOPT_POSTFIELDSIZE_LARGE:
{
result = ::curl_easy_setopt(curlObj, (CURLoption)option, parameterU64);
break;
}
case 211: // verifyOpt
{
uint32 flags = parameter.GetMPTR();
curl->nsslVerifyOptions = flags;
if (HAS_FLAG(flags, NSSL_VERIFY_PEER))
::curl_easy_setopt(curlObj, CURLOPT_SSL_VERIFYPEER, 1);
else
::curl_easy_setopt(curlObj, CURLOPT_SSL_VERIFYPEER, 0);
if (HAS_FLAG(flags, NSSL_VERIFY_HOSTNAME))
::curl_easy_setopt(curlObj, CURLOPT_SSL_VERIFYHOST, 2);
else
::curl_easy_setopt(curlObj, CURLOPT_SSL_VERIFYHOST, 0);
break;
}
case 210: // SSL_CONTEXT -> set context created with NSSLCreateContext before
{
const uint32 nsslIndex = parameter.GetMPTR();
nsysnet::NSSLInternalState_t* nssl = nsysnet::GetNSSLContext(nsslIndex);
if (nssl)
{
curl->hNSSL = nsslIndex;
result = ::curl_easy_setopt(curlObj, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_callback);
::curl_easy_setopt(curlObj, CURLOPT_SSL_CTX_DATA, curl.GetPtr());
if (nssl->sslVersion == 2)
::curl_easy_setopt(curlObj, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
else // auto = highest = 0 || 2 for v3
::curl_easy_setopt(curlObj, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
}
else
cemu_assert_suspicious();
break;
}
case CURLOPT_SHARE:
{
CURLSH* shObj = nullptr;
if (parameter)
{
auto curlSh = (MEMPTR<CURLSH_t>)parameter;
curlSh->curl = curl;
shObj = curlSh->curlsh;
}
result = ::curl_easy_setopt(curlObj, CURLOPT_SHARE, shObj);
break;
}
case CURLOPT_HEADERFUNCTION:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_HEADERFUNCTION", parameter.GetMPTR());
curl->fwrite_header = parameter;
break;
}
case CURLOPT_HEADERDATA:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_HEADERDATA", parameter.GetMPTR());
curl->writeheader = parameter;
break;
}
case CURLOPT_WRITEFUNCTION:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_WRITEFUNCTION", parameter.GetMPTR());
curl->fwrite_func = parameter;
result = ::curl_easy_setopt(curlObj, CURLOPT_WRITEFUNCTION, write_callback);
::curl_easy_setopt(curlObj, CURLOPT_WRITEDATA, curl.GetPtr());
break;
}
case CURLOPT_WRITEDATA: // aka CURLOPT_FILE
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_WRITEDATA", parameter.GetMPTR());
curl->out = parameter;
break;
}
case CURLOPT_HTTPHEADER:
{
struct curl_slist* list = nullptr;
bool isFirst = true;
for (curl_slist_t* ppcList = (curl_slist_t*)parameter.GetPtr(); ppcList; ppcList = ppcList->next.GetPtr())
{
forceLogDebug_printf("curl_slist_append: %s", ppcList->data.GetPtr());
curlDebug_logEasySetOptStr(curl.GetPtr(), isFirst?"CURLOPT_HTTPHEADER" : "CURLOPT_HTTPHEADER(continue)", (const char*)ppcList->data.GetPtr());
list = ::curl_slist_append(list, ppcList->data.GetPtr());
isFirst = false;
}
result = ::curl_easy_setopt(curlObj, CURLOPT_HTTPHEADER, list);
break;
}
case CURLOPT_SOCKOPTFUNCTION:
{
curl->fsockopt = parameter;
result = ::curl_easy_setopt(curlObj, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);
::curl_easy_setopt(curlObj, CURLOPT_SOCKOPTDATA, curl.GetPtr());
break;
}
case CURLOPT_SOCKOPTDATA:
{
curl->sockopt_client = parameter;
break;
}
case CURLOPT_READFUNCTION:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_READFUNCTION", parameter.GetMPTR());
curl->fread_func_set = parameter;
result = ::curl_easy_setopt(curlObj, CURLOPT_READFUNCTION, read_callback);
::curl_easy_setopt(curlObj, CURLOPT_READDATA, curl.GetPtr());
break;
}
case CURLOPT_READDATA:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_READDATA", parameter.GetMPTR());
curl->in_set = parameter;
break;
}
case CURLOPT_PROGRESSFUNCTION:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_PROGRESSFUNCTION", parameter.GetMPTR());
curl->fprogress = parameter;
result = ::curl_easy_setopt(curlObj, CURLOPT_PROGRESSFUNCTION, progress_callback);
::curl_easy_setopt(curlObj, CURLOPT_PROGRESSDATA, curl.GetPtr());
break;
}
case CURLOPT_PROGRESSDATA:
{
curlDebug_logEasySetOptPtr(curl.GetPtr(), "CURLOPT_PROGRESSDATA", parameter.GetMPTR());
curl->progress_client = parameter;
break;
}
default:
forceLogDebug_printf("curl_easy_setopt(0x%08x, %d, 0x%08x) unsupported option", curl.GetMPTR(), option, parameter.GetMPTR());
}
forceLogDebug_printf("curl_easy_setopt(0x%08x, %d, 0x%08x) -> 0x%x", curl.GetMPTR(), option, parameter.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_easy_perform(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curl, CURL_t, 0);
curlDebug_markActiveRequest(curl.GetPtr());
curlDebug_notifySubmitRequest(curl.GetPtr());
forceLogDebug_printf("curl_easy_perform(0x%08x)", curl.GetMPTR());
const uint32 result = SendOrderToWorker(curl.GetPtr(), QueueOrder_Perform);
forceLogDebug_printf("curl_easy_perform(0x%08x) -> 0x%x DONE", curl.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void _updateGuestString(CURL_t* curl, MEMPTR<char>& ppcStr, char* hostStr)
{
// free current string
if (ppcStr.IsNull() == false)
{
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), ppcStr);
ppcStr = nullptr;
}
if (hostStr == nullptr)
return;
sint32 length = (sint32)strlen(hostStr);
ppcStr = PPCCoreCallback(g_nlibcurl.malloc.GetMPTR(), length+1);
memcpy(ppcStr.GetPtr(), hostStr, length + 1);
}
void export_curl_easy_getinfo(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(curl, CURL_t, 0);
ppcDefineParamU32(info, 1);
ppcDefineParamMEMPTR(parameter, void, 2);
CURL* curlObj = curl->curl;
CURLcode result = CURLE_OK;
switch (info)
{
case CURLINFO_SIZE_DOWNLOAD:
case CURLINFO_SPEED_DOWNLOAD:
case CURLINFO_SIZE_UPLOAD:
case CURLINFO_SPEED_UPLOAD:
case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
{
double tempDouble = 0.0;
result = curl_easy_getinfo(curlObj, (CURLINFO)info, &tempDouble);
*(uint64*)parameter.GetPtr() = _swapEndianU64(*(uint64*)&tempDouble);
break;
}
case CURLINFO_RESPONSE_CODE:
case CURLINFO_SSL_VERIFYRESULT:
{
long tempLong = 0;
result = curl_easy_getinfo(curlObj, (CURLINFO)info, &tempLong);
*(uint32*)parameter.GetPtr() = _swapEndianU32((uint32)tempLong);
break;
}
case CURLINFO_CONTENT_TYPE:
{
//forceLogDebug_printf("CURLINFO_CONTENT_TYPE not supported");
//*(uint32*)parameter.GetPtr() = MPTR_NULL;
char* contentType = nullptr;
result = curl_easy_getinfo(curlObj, CURLINFO_REDIRECT_URL, &contentType);
_updateGuestString(curl.GetPtr(), curl->info_contentType, contentType);
*(uint32*)parameter.GetPtr() = curl->info_contentType.GetMPTRBE();
break;
}
case CURLINFO_REDIRECT_URL:
{
char* redirectUrl = nullptr;
result = curl_easy_getinfo(curlObj, CURLINFO_REDIRECT_URL, &redirectUrl);
_updateGuestString(curl.GetPtr(), curl->info_redirectUrl, redirectUrl);
*(uint32*)parameter.GetPtr() = curl->info_redirectUrl.GetMPTRBE();
break;
}
default:
cemu_assert_unimplemented();
result = curl_easy_getinfo(curlObj, (CURLINFO)info, (double*)parameter.GetPtr());
}
forceLogDebug_printf("curl_easy_getinfo(0x%08x, 0x%x, 0x%08x) -> 0x%x", curl.GetMPTR(), info, parameter.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void export_curl_global_init(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(flags, 0);
osLib_returnFromFunction(hCPU, curl_global_init(flags));
}
void export_curl_easy_strerror(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(code, 0);
MEMPTR<char> result;
const char* error = curl_easy_strerror((CURLcode)code);
if (error)
{
forceLogDebug_printf("curl_easy_strerror: %s", error);
int len = (int)strlen(error) + 1;
result = coreinit_allocFromSysArea(len, 4);
memcpy(result.GetPtr(), error, len);
}
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
void export_curl_slist_append(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(list, curl_slist_t, 0);
ppcDefineParamMEMPTR(data, const char, 1);
MEMPTR<char> dupdata{ PPCCoreCallback(g_nlibcurl.strdup.GetMPTR(), data.GetMPTR()) };
if (!dupdata)
{
forceLogDebug_printf("curl_slist_append(0x%08x, 0x%08x [%s]) -> 0x00000000", list.GetMPTR(), data.GetMPTR(), data.GetPtr());
osLib_returnFromFunction(hCPU, 0);
return;
}
MEMPTR<curl_slist_t> result{ PPCCoreCallback(g_nlibcurl.malloc.GetMPTR(), ppcsizeof<curl_slist_t>()) };
if (result)
{
result->data = dupdata;
result->next = nullptr;
// update last obj of list
if (list)
{
MEMPTR<curl_slist_t> tmp = list;
while (tmp->next)
{
tmp = tmp->next;
}
tmp->next = result;
}
}
else
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), dupdata.GetMPTR());
forceLogDebug_printf("curl_slist_append(0x%08x, 0x%08x [%s]) -> 0x%08x", list.GetMPTR(), data.GetMPTR(), data.GetPtr(), result.GetMPTR());
if(list)
osLib_returnFromFunction(hCPU, list.GetMPTR());
else
osLib_returnFromFunction(hCPU, result.GetMPTR());
}
void export_curl_slist_free_all(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(list, curl_slist_t, 0);
forceLogDebug_printf("export_curl_slist_free_all: TODO");
osLib_returnFromFunction(hCPU, 0);
}
void export_curl_global_init_mem(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(flags, 0);
ppcDefineParamMEMPTR(m, curl_malloc_callback, 1);
ppcDefineParamMEMPTR(f, curl_free_callback, 2);
ppcDefineParamMEMPTR(r, curl_realloc_callback, 3);
ppcDefineParamMEMPTR(s, curl_strdup_callback, 4);
ppcDefineParamMEMPTR(c, curl_calloc_callback, 5);
if (!m || !f || !r || !s || !c)
{
osLib_returnFromFunction(hCPU, CURLE_FAILED_INIT);
return;
}
CURLcode result = CURLE_OK;
if (g_nlibcurl.initialized == 0)
{
result = curl_global_init(flags);
if (result == CURLE_OK)
{
g_nlibcurl.malloc = m;
g_nlibcurl.free = f;
g_nlibcurl.realloc = r;
g_nlibcurl.strdup = s;
g_nlibcurl.calloc = c;
}
}
forceLogDebug_printf("curl_global_init_mem(0x%x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) -> 0x%08x", flags, m.GetMPTR(), f.GetMPTR(), r.GetMPTR(), s.GetMPTR(), c.GetMPTR(), result);
osLib_returnFromFunction(hCPU, result);
}
void load()
{
osLib_addFunction("nlibcurl", "curl_global_init_mem", export_curl_global_init_mem);
osLib_addFunction("nlibcurl", "curl_global_init", export_curl_global_init);
osLib_addFunction("nlibcurl", "curl_slist_append", export_curl_slist_append);
osLib_addFunction("nlibcurl", "curl_slist_free_all", export_curl_slist_free_all);
osLib_addFunction("nlibcurl", "curl_easy_strerror", export_curl_easy_strerror);
osLib_addFunction("nlibcurl", "curl_share_init", export_curl_share_init);
osLib_addFunction("nlibcurl", "curl_share_setopt", export_curl_share_setopt);
osLib_addFunction("nlibcurl", "curl_share_cleanup", export_curl_share_cleanup);
osLib_addFunction("nlibcurl", "curl_multi_init", export_curl_multi_init);
osLib_addFunction("nlibcurl", "curl_multi_add_handle", export_curl_multi_add_handle);
osLib_addFunction("nlibcurl", "curl_multi_perform", export_curl_multi_perform);
osLib_addFunction("nlibcurl", "curl_multi_info_read", export_curl_multi_info_read);
osLib_addFunction("nlibcurl", "curl_multi_remove_handle", export_curl_multi_remove_handle);
osLib_addFunction("nlibcurl", "curl_multi_setopt", export_curl_multi_setopt);
osLib_addFunction("nlibcurl", "curl_multi_fdset", export_curl_multi_fdset);
osLib_addFunction("nlibcurl", "curl_multi_cleanup", export_curl_multi_cleanup);
osLib_addFunction("nlibcurl", "curl_multi_timeout", export_curl_multi_timeout);
osLib_addFunction("nlibcurl", "curl_easy_init", export_curl_easy_init);
osLib_addFunction("nlibcurl", "mw_curl_easy_init", export_curl_easy_init);
osLib_addFunction("nlibcurl", "curl_easy_reset", export_curl_easy_reset);
osLib_addFunction("nlibcurl", "curl_easy_setopt", export_curl_easy_setopt);
osLib_addFunction("nlibcurl", "curl_easy_getinfo", export_curl_easy_getinfo);
osLib_addFunction("nlibcurl", "curl_easy_perform", export_curl_easy_perform);
osLib_addFunction("nlibcurl", "curl_easy_cleanup", export_curl_easy_cleanup);
osLib_addFunction("nlibcurl", "curl_easy_pause", export_curl_easy_pause);
}
}