nlibcurl: Detect invalid header combo + refactoring

Fixes error 106-0526 when opening course world on Super Mario Maker

Manually attaching Content-Length header for POST requests is undefined behavior on recent libcurl.
To detect the bad case some refactoring was necessary. In general we should try to move away from directly forwarding curl_easy_setopt() to the underlying instance as the behavior is diverging in modern libcurl. Much more refactoring work is required in the future to fix all of this.
This commit is contained in:
Exzap 2024-04-13 10:38:10 +02:00
parent 84cad8b280
commit d5a8530246

View File

@ -1,4 +1,5 @@
#include "Cafe/OS/common/OSCommon.h" #include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/common/OSUtil.h"
#include "Cafe/HW/Espresso/PPCCallback.h" #include "Cafe/HW/Espresso/PPCCallback.h"
#include "nlibcurl.h" #include "nlibcurl.h"
@ -6,6 +7,8 @@
#include "openssl/x509.h" #include "openssl/x509.h"
#include "openssl/ssl.h" #include "openssl/ssl.h"
#define CURL_STRICTER
#include "curl/curl.h" #include "curl/curl.h"
#include <unordered_map> #include <unordered_map>
#include <atomic> #include <atomic>
@ -98,6 +101,17 @@ struct MEMPTRHash_t
} }
}; };
struct WU_curl_slist
{
MEMPTR<char> data;
MEMPTR<WU_curl_slist> next;
};
enum class WU_CURLcode
{
placeholder = 0,
};
struct struct
{ {
sint32 initialized; sint32 initialized;
@ -110,8 +124,53 @@ struct
MEMPTR<curl_calloc_callback> calloc; MEMPTR<curl_calloc_callback> calloc;
} g_nlibcurl = {}; } g_nlibcurl = {};
using WU_CURL_off_t = uint64be;
#pragma pack(1) enum class WU_HTTPREQ : uint32
{
HTTPREQ_GET = 0x1,
HTTPREQ_POST = 0x2,
UKN_3 = 0x3,
};
struct WU_UserDefined
{
// starting at 0xD8 (probably) in CURL_t
/* 0x0D8 / +0x00 */ uint32be ukn0D8;
/* 0x0DC / +0x04 */ uint32be ukn0DC;
/* 0x0E0 / +0x08 */ MEMPTR<WU_curl_slist> headers;
/* 0x0E4 / +0x0C */ uint32be ukn0E4;
/* 0x0E8 / +0x10 */ uint32be ukn0E8;
/* 0x0EC / +0x14 */ uint32be ukn0EC;
/* 0x0F0 / +0x18 */ uint32be ukn0F0[4];
/* 0x100 / +0x28 */ uint32be ukn100[4];
/* 0x110 / +0x38 */ uint32be ukn110[4]; // +0x40 -> WU_CURL_off_t postfieldsize ?
/* 0x120 / +0x48 */ uint32be ukn120[4];
/* 0x130 / +0x58 */ uint32be ukn130[4];
/* 0x140 / +0x68 */ uint32be ukn140[4];
/* 0x150 / +0x78 */ uint32be ukn150[4];
/* 0x160 / +0x88 */ uint32be ukn160[4];
/* 0x170 / +0x98 */ uint32be ukn170[4];
/* 0x180 / +0xA8 */ uint32be ukn180[4];
/* 0x190 / +0xB0 */ sint64be infilesize_190{0};
/* 0x198 / +0xB8 */ uint32be ukn198;
/* 0x19C / +0xBC */ uint32be ukn19C;
/* 0x1A0 / +0xC8 */ uint32be ukn1A0[4];
/* 0x1B0 / +0xD8 */ uint32be ukn1B0[4];
/* 0x1C0 / +0xE8 */ uint32be ukn1C0[4];
/* 0x1D0 / +0xF8 */ uint32be ukn1D0[4];
/* 0x1E0 / +0x108 */ uint32be ukn1E0;
/* 0x1E4 / +0x108 */ uint32be ukn1E4;
/* 0x1E8 / +0x108 */ uint32be ukn1E8;
/* 0x1EC / +0x108 */ betype<WU_HTTPREQ> httpreq_1EC;
/* 0x1F0 / +0x118 */ uint32be ukn1F0[4];
void SetToDefault()
{
memset(this, 0, sizeof(WU_UserDefined));
httpreq_1EC = WU_HTTPREQ::HTTPREQ_GET;
}
};
struct CURL_t struct CURL_t
{ {
@ -137,6 +196,7 @@ struct CURL_t
OSThread_t* curlThread; OSThread_t* curlThread;
MEMPTR<char> info_redirectUrl; // stores CURLINFO_REDIRECT_URL ptr MEMPTR<char> info_redirectUrl; // stores CURLINFO_REDIRECT_URL ptr
MEMPTR<char> info_contentType; // stores CURLINFO_CONTENT_TYPE ptr MEMPTR<char> info_contentType; // stores CURLINFO_CONTENT_TYPE ptr
bool isDirty{true};
// debug // debug
struct struct
@ -149,10 +209,44 @@ struct CURL_t
FileStream* file_responseRaw{}; FileStream* file_responseRaw{};
}debug; }debug;
// fields below match the actual memory layout, above still needs refactoring
/* 0x78 */ uint32be ukn078;
/* 0x7C */ uint32be ukn07C;
/* 0x80 */ uint32be ukn080;
/* 0x84 */ uint32be ukn084;
/* 0x88 */ uint32be ukn088;
/* 0x8C */ uint32be ukn08C;
/* 0x90 */ uint32be ukn090[4];
/* 0xA0 */ uint32be ukn0A0[4];
/* 0xB0 */ uint32be ukn0B0[4];
/* 0xC0 */ uint32be ukn0C0[4];
/* 0xD0 */ uint32be ukn0D0;
/* 0xD4 */ uint32be ukn0D4;
/* 0xD8 */ WU_UserDefined set;
/* 0x200 */ uint32be ukn200[4];
/* 0x210 */ uint32be ukn210[4];
/* 0x220 */ uint32be ukn220[4];
/* 0x230 */ uint32be ukn230[4];
/* 0x240 */ uint32be ukn240[4];
/* 0x250 */ uint32be ukn250[4];
/* 0x260 */ uint32be ukn260[4];
/* 0x270 */ uint32be ukn270[4];
/* 0x280 */ uint8be ukn280;
/* 0x281 */ uint8be opt_no_body_281;
/* 0x282 */ uint8be ukn282;
/* 0x283 */ uint8be upload_283;
}; };
static_assert(sizeof(CURL_t) <= 0x8698); static_assert(sizeof(CURL_t) <= 0x8698);
static_assert(offsetof(CURL_t, ukn078) == 0x78);
static_assert(offsetof(CURL_t, set) == 0xD8);
static_assert(offsetof(CURL_t, set) + offsetof(WU_UserDefined, headers) == 0xE0);
static_assert(offsetof(CURL_t, set) + offsetof(WU_UserDefined, infilesize_190) == 0x190);
static_assert(offsetof(CURL_t, set) + offsetof(WU_UserDefined, httpreq_1EC) == 0x1EC);
static_assert(offsetof(CURL_t, opt_no_body_281) == 0x281);
typedef MEMPTR<CURL_t> CURLPtr; typedef MEMPTR<CURL_t> CURLPtr;
#pragma pack(1) // may affect structs below, we can probably remove this but lets keep it for now as the code below is fragile
typedef struct typedef struct
{ {
//uint32be specifier; // 0x00 //uint32be specifier; // 0x00
@ -173,18 +267,12 @@ typedef MEMPTR<CURLSH_t> CURLSHPtr;
typedef struct typedef struct
{ {
CURLM* curlm; CURLM* curlm;
std::vector< MEMPTR<CURL> > curl; std::vector<MEMPTR<CURL_t>> curl;
}CURLM_t; }CURLM_t;
static_assert(sizeof(CURLM_t) <= 0x80, "sizeof(CURLM_t)"); static_assert(sizeof(CURLM_t) <= 0x80, "sizeof(CURLM_t)");
typedef MEMPTR<CURLM_t> CURLMPtr; typedef MEMPTR<CURLM_t> CURLMPtr;
struct curl_slist_t static_assert(sizeof(WU_curl_slist) <= 0x8, "sizeof(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 struct CURLMsg_t
{ {
@ -298,6 +386,89 @@ uint32 SendOrderToWorker(CURL_t* curl, QueueOrder order, uint32 arg1 = 0)
return result; return result;
} }
int curl_closesocket(void *clientp, curl_socket_t item);
void _curl_set_default_parameters(CURL_t* curl)
{
curl->set.SetToDefault();
// default parameters
curl_easy_setopt(curl->curl, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(curl->curl, CURLOPT_HEADERDATA, curl);
curl_easy_setopt(curl->curl, CURLOPT_CLOSESOCKETFUNCTION, curl_closesocket);
curl_easy_setopt(curl->curl, CURLOPT_CLOSESOCKETDATA, nullptr);
}
void _curl_sync_parameters(CURL_t* curl)
{
// sync ppc curl to actual curl state
// not all parameters are covered yet, many are still set directly in easy_setopt
bool isPost = curl->set.httpreq_1EC == WU_HTTPREQ::HTTPREQ_POST;
// http request type
if(curl->set.httpreq_1EC == WU_HTTPREQ::HTTPREQ_GET)
{
::curl_easy_setopt(curl->curl, CURLOPT_HTTPGET, 1);
cemu_assert_debug(curl->opt_no_body_281 == 0);
cemu_assert_debug(curl->upload_283 == 0);
}
else if(curl->set.httpreq_1EC == WU_HTTPREQ::HTTPREQ_POST)
{
::curl_easy_setopt(curl->curl, CURLOPT_POST, 1);
cemu_assert_debug(curl->upload_283 == 0);
::curl_easy_setopt(curl->curl, CURLOPT_NOBODY, curl->opt_no_body_281 ? 1 : 0);
}
else
{
cemu_assert_unimplemented();
}
// CURLOPT_HTTPHEADER
std::optional<uint64> manualHeaderContentLength;
if (curl->set.headers)
{
struct curl_slist* list = nullptr;
WU_curl_slist* ppcList = curl->set.headers;
while(ppcList)
{
if(isPost)
{
// for recent libcurl manually adding Content-Length header is undefined behavior. Instead CURLOPT_INFILESIZE(_LARGE) should be set
// here we remove Content-Length and instead substitute it with CURLOPT_INFILESIZE (NEX DataStore in Super Mario Maker requires this)
if(strncmp(ppcList->data.GetPtr(), "Content-Length:", 15) == 0)
{
manualHeaderContentLength = std::stoull(ppcList->data.GetPtr() + 15);
ppcList = ppcList->next;
continue;
}
}
cemuLog_logDebug(LogType::Force, "curl_slist_append: {}", ppcList->data.GetPtr());
curlDebug_logEasySetOptStr(curl, "CURLOPT_HTTPHEADER", (const char*)ppcList->data.GetPtr());
list = ::curl_slist_append(list, ppcList->data.GetPtr());
ppcList = ppcList->next;
}
::curl_easy_setopt(curl->curl, CURLOPT_HTTPHEADER, list);
// todo - prevent leaking of list (maybe store in host curl object, similar to how our zlib implementation does stuff)
}
else
::curl_easy_setopt(curl->curl, CURLOPT_HTTPHEADER, nullptr);
// infile size (post data size)
if (curl->set.infilesize_190)
{
cemu_assert_debug(manualHeaderContentLength == 0); // should not have both?
::curl_easy_setopt(curl->curl, CURLOPT_INFILESIZE_LARGE, curl->set.infilesize_190);
}
else
{
if(isPost && manualHeaderContentLength > 0)
::curl_easy_setopt(curl->curl, CURLOPT_INFILESIZE_LARGE, manualHeaderContentLength);
else
::curl_easy_setopt(curl->curl, CURLOPT_INFILESIZE_LARGE, 0);
}
}
void export_malloc(PPCInterpreter_t* hCPU) void export_malloc(PPCInterpreter_t* hCPU)
{ {
ppcDefineParamU32(size, 0); ppcDefineParamU32(size, 0);
@ -340,7 +511,6 @@ void export_realloc(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, result.GetMPTR()); osLib_returnFromFunction(hCPU, result.GetMPTR());
} }
CURLcode curl_global_init(uint32 flags) CURLcode curl_global_init(uint32 flags)
{ {
if (g_nlibcurl.initialized++) if (g_nlibcurl.initialized++)
@ -436,6 +606,18 @@ void export_curl_multi_perform(PPCInterpreter_t* hCPU)
//cemuLog_logDebug(LogType::Force, "curl_multi_perform(0x{:08x}, 0x{:08x})", curlm.GetMPTR(), runningHandles.GetMPTR()); //cemuLog_logDebug(LogType::Force, "curl_multi_perform(0x{:08x}, 0x{:08x})", curlm.GetMPTR(), runningHandles.GetMPTR());
//curl_multi_get_handles(curlm->curlm);
for(auto _curl : curlm->curl)
{
CURL_t* curl = (CURL_t*)_curl.GetPtr();
if(curl->isDirty)
{
curl->isDirty = false;
_curl_sync_parameters(curl);
}
}
//g_callerQueue = curlm->callerQueue; //g_callerQueue = curlm->callerQueue;
//g_threadQueue = curlm->threadQueue; //g_threadQueue = curlm->threadQueue;
int tempRunningHandles = 0; int tempRunningHandles = 0;
@ -555,7 +737,7 @@ void export_curl_multi_info_read(PPCInterpreter_t* hCPU)
if (msg->easy_handle) if (msg->easy_handle)
{ {
const auto it = find_if(curlm->curl.cbegin(), curlm->curl.cend(), const auto it = find_if(curlm->curl.cbegin(), curlm->curl.cend(),
[msg](const MEMPTR<void>& curl) [msg](const MEMPTR<CURL_t>& curl)
{ {
const MEMPTR<CURL_t> _curl{ curl }; const MEMPTR<CURL_t> _curl{ curl };
return _curl->curl = msg->easy_handle; return _curl->curl = msg->easy_handle;
@ -661,26 +843,6 @@ void export_curl_share_cleanup(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 0); 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) static int curl_closesocket(void *clientp, curl_socket_t item)
{ {
nsysnet_notifyCloseSharedSocket((SOCKET)item); nsysnet_notifyCloseSharedSocket((SOCKET)item);
@ -688,36 +850,30 @@ static int curl_closesocket(void *clientp, curl_socket_t item)
return 0; return 0;
} }
void export_curl_easy_init(PPCInterpreter_t* hCPU) CURL_t* curl_easy_init()
{ {
if (g_nlibcurl.initialized == 0) if (g_nlibcurl.initialized == 0)
{ {
if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
{ {
osLib_returnFromFunction(hCPU, 0); return nullptr;
return;
} }
} }
// Curl_open // Curl_open
CURLPtr result{ PPCCoreCallback(g_nlibcurl.calloc.GetMPTR(), (uint32)1, ppcsizeof<CURL_t>()) }; MEMPTR<CURL_t> result{ PPCCoreCallback(g_nlibcurl.calloc.GetMPTR(), (uint32)1, ppcsizeof<CURL_t>()) };
cemuLog_logDebug(LogType::Force, "curl_easy_init() -> 0x{:08x}", result.GetMPTR()); cemuLog_logDebug(LogType::Force, "curl_easy_init() -> 0x{:08x}", result.GetMPTR());
if (result) if (result)
{ {
memset(result.GetPtr(), 0, sizeof(CURL_t)); memset(result.GetPtr(), 0, sizeof(CURL_t));
*result = {}; *result = {};
result->curl = curl_easy_init(); result->curl = ::curl_easy_init();
result->curlThread = coreinit::OSGetCurrentThread(); result->curlThread = coreinit::OSGetCurrentThread();
result->info_contentType = nullptr; result->info_contentType = nullptr;
result->info_redirectUrl = nullptr; result->info_redirectUrl = nullptr;
// default parameters _curl_set_default_parameters(result.GetPtr());
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) if (g_nlibcurl.proxyConfig)
{ {
@ -725,7 +881,12 @@ void export_curl_easy_init(PPCInterpreter_t* hCPU)
} }
} }
osLib_returnFromFunction(hCPU, result.GetMPTR()); return result;
}
CURL_t* mw_curl_easy_init()
{
return curl_easy_init();
} }
void export_curl_easy_pause(PPCInterpreter_t* hCPU) void export_curl_easy_pause(PPCInterpreter_t* hCPU)
@ -971,18 +1132,47 @@ void export_curl_easy_setopt(PPCInterpreter_t* hCPU)
ppcDefineParamU64(parameterU64, 2); ppcDefineParamU64(parameterU64, 2);
CURL* curlObj = curl->curl; CURL* curlObj = curl->curl;
curl->isDirty = true;
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
switch (option) switch (option)
{ {
case CURLOPT_NOSIGNAL: case CURLOPT_POST:
{
if(parameter)
{
curl->set.httpreq_1EC = WU_HTTPREQ::HTTPREQ_POST;
curl->opt_no_body_281 = 0;
}
else
curl->set.httpreq_1EC = WU_HTTPREQ::HTTPREQ_GET;
break;
}
case CURLOPT_HTTPGET: case CURLOPT_HTTPGET:
{
if (parameter)
{
curl->set.httpreq_1EC = WU_HTTPREQ::HTTPREQ_GET;
curl->opt_no_body_281 = 0;
curl->upload_283 = 0;
}
break;
}
case CURLOPT_INFILESIZE:
{
curl->set.infilesize_190 = (sint64)(sint32)(uint32)parameter.GetBEValue();
break;
}
case CURLOPT_INFILESIZE_LARGE:
{
curl->set.infilesize_190 = (sint64)(uint64)parameterU64;
break;
}
case CURLOPT_NOSIGNAL:
case CURLOPT_FOLLOWLOCATION: case CURLOPT_FOLLOWLOCATION:
case CURLOPT_BUFFERSIZE: case CURLOPT_BUFFERSIZE:
case CURLOPT_TIMEOUT: case CURLOPT_TIMEOUT:
case CURLOPT_CONNECTTIMEOUT_MS: case CURLOPT_CONNECTTIMEOUT_MS:
case CURLOPT_POST:
case CURLOPT_INFILESIZE:
case CURLOPT_NOPROGRESS: case CURLOPT_NOPROGRESS:
case CURLOPT_LOW_SPEED_LIMIT: case CURLOPT_LOW_SPEED_LIMIT:
case CURLOPT_LOW_SPEED_TIME: case CURLOPT_LOW_SPEED_TIME:
@ -1068,8 +1258,6 @@ void export_curl_easy_setopt(PPCInterpreter_t* hCPU)
curlSh->curl = curl; curlSh->curl = curl;
shObj = curlSh->curlsh; shObj = curlSh->curlsh;
} }
result = ::curl_easy_setopt(curlObj, CURLOPT_SHARE, shObj); result = ::curl_easy_setopt(curlObj, CURLOPT_SHARE, shObj);
break; break;
} }
@ -1101,17 +1289,8 @@ void export_curl_easy_setopt(PPCInterpreter_t* hCPU)
} }
case CURLOPT_HTTPHEADER: case CURLOPT_HTTPHEADER:
{ {
struct curl_slist* list = nullptr; curl->set.headers = (WU_curl_slist*)parameter.GetPtr();
bool isFirst = true; result = CURLE_OK;
for (curl_slist_t* ppcList = (curl_slist_t*)parameter.GetPtr(); ppcList; ppcList = ppcList->next.GetPtr())
{
cemuLog_logDebug(LogType::Force, "curl_slist_append: {}", 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; break;
} }
case CURLOPT_SOCKOPTFUNCTION: case CURLOPT_SOCKOPTFUNCTION:
@ -1163,15 +1342,18 @@ void export_curl_easy_setopt(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, result); osLib_returnFromFunction(hCPU, result);
} }
void export_curl_easy_perform(PPCInterpreter_t* hCPU) WU_CURLcode curl_easy_perform(CURL_t* curl)
{ {
ppcDefineParamMEMPTR(curl, CURL_t, 0); curlDebug_markActiveRequest(curl);
curlDebug_markActiveRequest(curl.GetPtr()); curlDebug_notifySubmitRequest(curl);
curlDebug_notifySubmitRequest(curl.GetPtr());
cemuLog_logDebug(LogType::Force, "curl_easy_perform(0x{:08x})", curl.GetMPTR()); if(curl->isDirty)
const uint32 result = SendOrderToWorker(curl.GetPtr(), QueueOrder_Perform); {
cemuLog_logDebug(LogType::Force, "curl_easy_perform(0x{:08x}) -> 0x{:x} DONE", curl.GetMPTR(), result); curl->isDirty = false;
osLib_returnFromFunction(hCPU, result); _curl_sync_parameters(curl);
}
const uint32 result = SendOrderToWorker(curl, QueueOrder_Perform);
return static_cast<WU_CURLcode>(result);
} }
void _updateGuestString(CURL_t* curl, MEMPTR<char>& ppcStr, char* hostStr) void _updateGuestString(CURL_t* curl, MEMPTR<char>& ppcStr, char* hostStr)
@ -1246,14 +1428,6 @@ void export_curl_easy_getinfo(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 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) void export_curl_easy_strerror(PPCInterpreter_t* hCPU)
{ {
ppcDefineParamU32(code, 0); ppcDefineParamU32(code, 0);
@ -1270,21 +1444,16 @@ void export_curl_easy_strerror(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, result.GetMPTR()); osLib_returnFromFunction(hCPU, result.GetMPTR());
} }
void export_curl_slist_append(PPCInterpreter_t* hCPU) WU_curl_slist* curl_slist_append(WU_curl_slist* list, const char* data)
{ {
ppcDefineParamMEMPTR(list, curl_slist_t, 0); MEMPTR<char> dupdata{ PPCCoreCallback(g_nlibcurl.strdup.GetMPTR(), data) };
ppcDefineParamMEMPTR(data, const char, 1);
MEMPTR<char> dupdata{ PPCCoreCallback(g_nlibcurl.strdup.GetMPTR(), data.GetMPTR()) };
if (!dupdata) if (!dupdata)
{ {
cemuLog_logDebug(LogType::Force, "curl_slist_append(0x{:08x}, 0x{:08x} [{}]) -> 0x00000000", list.GetMPTR(), data.GetMPTR(), data.GetPtr()); cemuLog_logDebug(LogType::Force, "curl_slist_append(): Failed to duplicate string");
osLib_returnFromFunction(hCPU, 0); return nullptr;
return;
} }
MEMPTR<curl_slist_t> result{ PPCCoreCallback(g_nlibcurl.malloc.GetMPTR(), ppcsizeof<curl_slist_t>()) }; MEMPTR<WU_curl_slist> result{ PPCCoreCallback(g_nlibcurl.malloc.GetMPTR(), ppcsizeof<WU_curl_slist>()) };
if (result) if (result)
{ {
result->data = dupdata; result->data = dupdata;
@ -1293,7 +1462,7 @@ void export_curl_slist_append(PPCInterpreter_t* hCPU)
// update last obj of list // update last obj of list
if (list) if (list)
{ {
MEMPTR<curl_slist_t> tmp = list; MEMPTR<WU_curl_slist> tmp = list;
while (tmp->next) while (tmp->next)
{ {
tmp = tmp->next; tmp = tmp->next;
@ -1303,38 +1472,24 @@ void export_curl_slist_append(PPCInterpreter_t* hCPU)
} }
} }
else else
{
cemuLog_logDebug(LogType::Force, "curl_slist_append(): Failed to allocate memory");
PPCCoreCallback(g_nlibcurl.free.GetMPTR(), dupdata.GetMPTR()); PPCCoreCallback(g_nlibcurl.free.GetMPTR(), dupdata.GetMPTR());
}
cemuLog_logDebug(LogType::Force, "curl_slist_append(0x{:08x}, 0x{:08x} [{}]) -> 0x{:08x}", list.GetMPTR(), data.GetMPTR(), data.GetPtr(), result.GetMPTR());
if(list) if(list)
osLib_returnFromFunction(hCPU, list.GetMPTR()); return list;
else return result;
osLib_returnFromFunction(hCPU, result.GetMPTR());
} }
void export_curl_slist_free_all(PPCInterpreter_t* hCPU) void curl_slist_free_all(WU_curl_slist* list)
{ {
ppcDefineParamMEMPTR(list, curl_slist_t, 0);
cemuLog_logDebug(LogType::Force, "export_curl_slist_free_all: TODO"); cemuLog_logDebug(LogType::Force, "export_curl_slist_free_all: TODO");
osLib_returnFromFunction(hCPU, 0);
} }
void export_curl_global_init_mem(PPCInterpreter_t* hCPU) CURLcode curl_global_init_mem(uint32 flags, MEMPTR<curl_malloc_callback> malloc_callback, MEMPTR<curl_free_callback> free_callback, MEMPTR<curl_realloc_callback> realloc_callback, MEMPTR<curl_strdup_callback> strdup_callback, MEMPTR<curl_calloc_callback> calloc_callback)
{ {
ppcDefineParamU32(flags, 0); if(!malloc_callback || !free_callback || !realloc_callback || !strdup_callback || !calloc_callback)
ppcDefineParamMEMPTR(m, curl_malloc_callback, 1); return CURLE_FAILED_INIT;
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; CURLcode result = CURLE_OK;
if (g_nlibcurl.initialized == 0) if (g_nlibcurl.initialized == 0)
@ -1342,31 +1497,30 @@ void export_curl_global_init_mem(PPCInterpreter_t* hCPU)
result = curl_global_init(flags); result = curl_global_init(flags);
if (result == CURLE_OK) if (result == CURLE_OK)
{ {
g_nlibcurl.malloc = m; g_nlibcurl.malloc = malloc_callback;
g_nlibcurl.free = f; g_nlibcurl.free = free_callback;
g_nlibcurl.realloc = r; g_nlibcurl.realloc = realloc_callback;
g_nlibcurl.strdup = s; g_nlibcurl.strdup = strdup_callback;
g_nlibcurl.calloc = c; g_nlibcurl.calloc = calloc_callback;
} }
} }
return result;
cemuLog_logDebug(LogType::Force, "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() void load()
{ {
osLib_addFunction("nlibcurl", "curl_global_init_mem", export_curl_global_init_mem); cafeExportRegister("nlibcurl", curl_global_init_mem, LogType::Force);
osLib_addFunction("nlibcurl", "curl_global_init", export_curl_global_init); cafeExportRegister("nlibcurl", curl_global_init, LogType::Force);
osLib_addFunction("nlibcurl", "curl_slist_append", export_curl_slist_append); cafeExportRegister("nlibcurl", curl_slist_append, LogType::Force);
osLib_addFunction("nlibcurl", "curl_slist_free_all", export_curl_slist_free_all); cafeExportRegister("nlibcurl", curl_slist_free_all, LogType::Force);
osLib_addFunction("nlibcurl", "curl_easy_strerror", export_curl_easy_strerror); 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_init", export_curl_share_init);
osLib_addFunction("nlibcurl", "curl_share_setopt", export_curl_share_setopt); osLib_addFunction("nlibcurl", "curl_share_setopt", export_curl_share_setopt);
osLib_addFunction("nlibcurl", "curl_share_cleanup", export_curl_share_cleanup); osLib_addFunction("nlibcurl", "curl_share_cleanup", export_curl_share_cleanup);
cafeExportRegister("nlibcurl", mw_curl_easy_init, LogType::Force);
osLib_addFunction("nlibcurl", "curl_multi_init", export_curl_multi_init); 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_add_handle", export_curl_multi_add_handle);
osLib_addFunction("nlibcurl", "curl_multi_perform", export_curl_multi_perform); osLib_addFunction("nlibcurl", "curl_multi_perform", export_curl_multi_perform);
@ -1377,12 +1531,14 @@ void load()
osLib_addFunction("nlibcurl", "curl_multi_cleanup", export_curl_multi_cleanup); osLib_addFunction("nlibcurl", "curl_multi_cleanup", export_curl_multi_cleanup);
osLib_addFunction("nlibcurl", "curl_multi_timeout", export_curl_multi_timeout); osLib_addFunction("nlibcurl", "curl_multi_timeout", export_curl_multi_timeout);
osLib_addFunction("nlibcurl", "curl_easy_init", export_curl_easy_init); cafeExportRegister("nlibcurl", curl_easy_init, LogType::Force);
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_reset", export_curl_easy_reset);
osLib_addFunction("nlibcurl", "curl_easy_setopt", export_curl_easy_setopt); 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_getinfo", export_curl_easy_getinfo);
osLib_addFunction("nlibcurl", "curl_easy_perform", export_curl_easy_perform); cafeExportRegister("nlibcurl", curl_easy_perform, LogType::Force);
osLib_addFunction("nlibcurl", "curl_easy_cleanup", export_curl_easy_cleanup); osLib_addFunction("nlibcurl", "curl_easy_cleanup", export_curl_easy_cleanup);
osLib_addFunction("nlibcurl", "curl_easy_pause", export_curl_easy_pause); osLib_addFunction("nlibcurl", "curl_easy_pause", export_curl_easy_pause);
} }