mirror of
https://github.com/Minecraft-Community-Edition/client.git
synced 2026-05-24 10:04:36 +00:00
Merge remote-tracking branch 'origin/main' into kb-mouse-networking-coah
merge main before final push
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
// Windows64 DLC store stuff blahhhh - whisper
|
||||
#ifdef _WINDOWS64
|
||||
#include "Windows64_DLCOffers.h"
|
||||
#include "..\..\User.h"
|
||||
#include "Windows64_Minecraft.h"
|
||||
#include <unordered_map>
|
||||
static std::unordered_map<int,int> s_offerIndexToListPos;
|
||||
#endif
|
||||
@@ -166,10 +168,12 @@ void UIScene_DLCOffersMenu::handleInput(int iPad, int key, bool repeat, bool pre
|
||||
int iIndex = getControlChildFocus();
|
||||
if (iIndex >= 0 && iIndex < Windows64_DLCOffers::Get().GetOfferCount())
|
||||
{
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
|
||||
Windows64_DLCOffers::Get().InstallOffer(iIndex,
|
||||
[](const wchar_t* id, bool ok, void*) {
|
||||
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
|
||||
}, nullptr);
|
||||
}, nullptr, pMinecraft->user->name.c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -249,9 +253,9 @@ void UIScene_DLCOffersMenu::handlePress(F64 controlId, F64 childId)
|
||||
iIndex < Windows64_DLCOffers::Get().GetOfferCount())
|
||||
{
|
||||
Windows64_DLCOffers::Get().InstallOffer(iIndex,
|
||||
[](const wchar_t* id, bool ok, void*) {
|
||||
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
|
||||
}, nullptr);
|
||||
[](const wchar_t* id, bool ok, void*) {
|
||||
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
|
||||
}, nullptr, Minecraft::GetInstance()->user->name.c_str());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -116,6 +116,8 @@ UIScene_MainMenu::UIScene_MainMenu(int iPad, void *initData, UILayer *parentLaye
|
||||
// Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer
|
||||
XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW);
|
||||
#endif
|
||||
|
||||
Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()));
|
||||
}
|
||||
|
||||
UIScene_MainMenu::~UIScene_MainMenu()
|
||||
|
||||
@@ -4,456 +4,482 @@
|
||||
#include <winhttp.h>
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
#include <shlobj.h>
|
||||
#include "../Windows64_Minecraft.h"
|
||||
#include "../User.h"
|
||||
|
||||
static bool ParseUrl(const wchar_t* url,
|
||||
std::wstring& host, INTERNET_PORT& port,
|
||||
std::wstring& path)
|
||||
std::wstring& host, INTERNET_PORT& port,
|
||||
std::wstring& path)
|
||||
{
|
||||
URL_COMPONENTS uc = {};
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
uc.dwHostNameLength = (DWORD)-1;
|
||||
uc.dwUrlPathLength = (DWORD)-1;
|
||||
uc.dwExtraInfoLength = (DWORD)-1;
|
||||
if (!WinHttpCrackUrl(url, 0, 0, &uc)) return false;
|
||||
host.assign(uc.lpszHostName, uc.dwHostNameLength);
|
||||
path.assign(uc.lpszUrlPath, uc.dwUrlPathLength);
|
||||
if (uc.lpszExtraInfo && uc.dwExtraInfoLength)
|
||||
path.append(uc.lpszExtraInfo, uc.dwExtraInfoLength);
|
||||
port = uc.nPort;
|
||||
return true;
|
||||
URL_COMPONENTS uc = {};
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
uc.dwHostNameLength = (DWORD)-1;
|
||||
uc.dwUrlPathLength = (DWORD)-1;
|
||||
uc.dwExtraInfoLength = (DWORD)-1;
|
||||
if (!WinHttpCrackUrl(url, 0, 0, &uc)) return false;
|
||||
host.assign(uc.lpszHostName, uc.dwHostNameLength);
|
||||
path.assign(uc.lpszUrlPath, uc.dwUrlPathLength);
|
||||
if (uc.lpszExtraInfo && uc.dwExtraInfoLength)
|
||||
path.append(uc.lpszExtraInfo, uc.dwExtraInfoLength);
|
||||
port = uc.nPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Windows64_DLCOffers::FetchBytesFromUrl(const wchar_t* url,
|
||||
PBYTE* ppData, DWORD* pdwSize)
|
||||
PBYTE* ppData, DWORD* pdwSize)
|
||||
{
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
|
||||
printf("[DLC] FetchBytesFromUrl: '%ls'\n", url);
|
||||
printf("[DLC] FetchBytesFromUrl: '%ls'\n", url);
|
||||
|
||||
std::wstring host, path;
|
||||
INTERNET_PORT port = 80;
|
||||
if (!ParseUrl(url, host, port, path))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: ParseUrl FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
std::wstring host, path;
|
||||
INTERNET_PORT port = 80;
|
||||
if (!ParseUrl(url, host, port, path))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: ParseUrl FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("[DLC] FetchBytesFromUrl: host='%ls' port=%u path='%ls'\n",
|
||||
host.c_str(), (unsigned)port, path.c_str());
|
||||
printf("[DLC] FetchBytesFromUrl: host='%ls' port=%u path='%ls'\n",
|
||||
host.c_str(), (unsigned)port, path.c_str());
|
||||
|
||||
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0",
|
||||
WINHTTP_ACCESS_TYPE_NO_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hSession)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpen FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0",
|
||||
WINHTTP_ACCESS_TYPE_NO_PROXY,
|
||||
WINHTTP_NO_PROXY_NAME,
|
||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hSession)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpen FAILED (err=%u)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(), port, 0);
|
||||
if (!hConn)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpConnect FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(), port, 0);
|
||||
if (!hConn)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpConnect FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", path.c_str(),
|
||||
nullptr, WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hReq)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpenRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", path.c_str(),
|
||||
nullptr, WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hReq)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpOpenRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpSendRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpSendRequest FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WinHttpReceiveResponse(hReq, nullptr))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpReceiveResponse FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpReceiveResponse(hReq, nullptr))
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: WinHttpReceiveResponse FAILED (err=%u)\n", GetLastError());
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD dwStatusCode = 0;
|
||||
DWORD dwStatusSize = sizeof(dwStatusCode);
|
||||
WinHttpQueryHeaders(hReq,
|
||||
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
&dwStatusCode, &dwStatusSize,
|
||||
WINHTTP_NO_HEADER_INDEX);
|
||||
printf("[DLC] FetchBytesFromUrl: HTTP status = %u\n", dwStatusCode);
|
||||
DWORD dwStatusCode = 0;
|
||||
DWORD dwStatusSize = sizeof(dwStatusCode);
|
||||
WinHttpQueryHeaders(hReq,
|
||||
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX,
|
||||
&dwStatusCode, &dwStatusSize,
|
||||
WINHTTP_NO_HEADER_INDEX);
|
||||
printf("[DLC] FetchBytesFromUrl: HTTP status = %u\n", dwStatusCode);
|
||||
|
||||
if (dwStatusCode != 200)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: non-200 response, aborting\n");
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (dwStatusCode != 200)
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: non-200 response, aborting\n");
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BYTE> buf;
|
||||
DWORD dwRead = 0;
|
||||
BYTE chunk[8192];
|
||||
while (WinHttpReadData(hReq, chunk, sizeof(chunk), &dwRead) && dwRead)
|
||||
buf.insert(buf.end(), chunk, chunk + dwRead);
|
||||
std::vector<BYTE> buf;
|
||||
DWORD dwRead = 0;
|
||||
BYTE chunk[8192];
|
||||
while (WinHttpReadData(hReq, chunk, sizeof(chunk), &dwRead) && dwRead)
|
||||
buf.insert(buf.end(), chunk, chunk + dwRead);
|
||||
|
||||
printf("[DLC] FetchBytesFromUrl: read %zu bytes\n", buf.size());
|
||||
printf("[DLC] FetchBytesFromUrl: read %zu bytes\n", buf.size());
|
||||
|
||||
bool bOk = false;
|
||||
if (!buf.empty())
|
||||
{
|
||||
*ppData = new BYTE[buf.size()];
|
||||
*pdwSize = (DWORD)buf.size();
|
||||
memcpy(*ppData, buf.data(), buf.size());
|
||||
bOk = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: response body was empty\n");
|
||||
}
|
||||
bool bOk = false;
|
||||
if (!buf.empty())
|
||||
{
|
||||
*ppData = new BYTE[buf.size()];
|
||||
*pdwSize = (DWORD)buf.size();
|
||||
memcpy(*ppData, buf.data(), buf.size());
|
||||
bOk = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] FetchBytesFromUrl: response body was empty\n");
|
||||
}
|
||||
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return bOk;
|
||||
WinHttpCloseHandle(hReq);
|
||||
WinHttpCloseHandle(hConn);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return bOk;
|
||||
}
|
||||
|
||||
static bool WinHttpGetUrl(const wchar_t* fullUrl, PBYTE* ppData, DWORD* pdwSize)
|
||||
{
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
*ppData = nullptr;
|
||||
*pdwSize = 0;
|
||||
|
||||
URL_COMPONENTS uc;
|
||||
ZeroMemory(&uc, sizeof(uc));
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
URL_COMPONENTS uc;
|
||||
ZeroMemory(&uc, sizeof(uc));
|
||||
uc.dwStructSize = sizeof(uc);
|
||||
|
||||
wchar_t szHostName[256] = {};
|
||||
wchar_t szUrlPath[1024] = {};
|
||||
uc.lpszHostName = szHostName;
|
||||
uc.dwHostNameLength = _countof(szHostName);
|
||||
uc.lpszUrlPath = szUrlPath;
|
||||
uc.dwUrlPathLength = _countof(szUrlPath);
|
||||
wchar_t szHostName[256] = {};
|
||||
wchar_t szUrlPath[1024] = {};
|
||||
uc.lpszHostName = szHostName;
|
||||
uc.dwHostNameLength = _countof(szHostName);
|
||||
uc.lpszUrlPath = szUrlPath;
|
||||
uc.dwUrlPathLength = _countof(szUrlPath);
|
||||
|
||||
if (!WinHttpCrackUrl(fullUrl, 0, 0, &uc))
|
||||
{
|
||||
printf("[DLC] WinHttpGetUrl: WinHttpCrackUrl failed for '%ls': %u\n", fullUrl, GetLastError());
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpCrackUrl(fullUrl, 0, 0, &uc))
|
||||
{
|
||||
printf("[DLC] WinHttpGetUrl: WinHttpCrackUrl failed for '%ls': %u\n", fullUrl, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return false;
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return false;
|
||||
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, szHostName, uc.nPort, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, szHostName, uc.nPort, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
|
||||
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize];
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize];
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
|
||||
if (body.empty()) return false;
|
||||
if (body.empty()) return false;
|
||||
|
||||
*pdwSize = (DWORD)body.size();
|
||||
*ppData = new BYTE[*pdwSize];
|
||||
memcpy(*ppData, body.data(), *pdwSize);
|
||||
return true;
|
||||
*pdwSize = (DWORD)body.size();
|
||||
*ppData = new BYTE[*pdwSize];
|
||||
memcpy(*ppData, body.data(), *pdwSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Windows64_DLCOffers::FetchImageFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize)
|
||||
{
|
||||
return WinHttpGetUrl(url, ppData, pdwSize);
|
||||
return WinHttpGetUrl(url, ppData, pdwSize);
|
||||
}
|
||||
|
||||
void Windows64_DLCOffers::InstallOffer(int iIndex,
|
||||
W64_INSTALL_CALLBACK pfnCallback,
|
||||
void* pUserData)
|
||||
W64_INSTALL_CALLBACK pfnCallback,
|
||||
void* pUserData, wstring username)
|
||||
{
|
||||
if (iIndex < 0 || iIndex >= (int)m_offers.size()) return;
|
||||
if (iIndex < 0 || iIndex >= (int)m_offers.size()) return;
|
||||
|
||||
InstallCtx* ctx = new InstallCtx();
|
||||
ctx->offer = m_offers[iIndex];
|
||||
ctx->offerIndex = iIndex;
|
||||
ctx->pfnCallback = pfnCallback;
|
||||
ctx->pUserData = pUserData;
|
||||
InstallCtx* ctx = new InstallCtx();
|
||||
ctx->offer = m_offers[iIndex];
|
||||
ctx->offerIndex = iIndex;
|
||||
ctx->pfnCallback = pfnCallback;
|
||||
ctx->pUserData = pUserData;
|
||||
ctx->username = username;
|
||||
|
||||
HANDLE hThread = CreateThread(nullptr, 0, InstallThreadProc, ctx, 0, nullptr);
|
||||
if (hThread) CloseHandle(hThread);
|
||||
else delete ctx;
|
||||
HANDLE hThread = CreateThread(nullptr, 0, InstallThreadProc, ctx, 0, nullptr);
|
||||
if (hThread) CloseHandle(hThread);
|
||||
else delete ctx;
|
||||
}
|
||||
|
||||
DWORD WINAPI Windows64_DLCOffers::InstallThreadProc(LPVOID lpParam)
|
||||
{
|
||||
InstallCtx* ctx = reinterpret_cast<InstallCtx*>(lpParam);
|
||||
bool bSuccess = false;
|
||||
InstallCtx* ctx = reinterpret_cast<InstallCtx*>(lpParam);
|
||||
bool bSuccess = false;
|
||||
|
||||
// <exe dir>\Windows64Media\DLC\<productID>\folder.pck
|
||||
wchar_t wszExeDir[MAX_PATH] = {};
|
||||
GetModuleFileNameW(nullptr, wszExeDir, MAX_PATH);
|
||||
wchar_t* pLastSlash = wcsrchr(wszExeDir, L'\\');
|
||||
if (pLastSlash) *(pLastSlash + 1) = L'\0';
|
||||
// <exe dir>\Windows64Media\DLC\<productID>\folder.pck
|
||||
wchar_t wszExeDir[MAX_PATH] = {};
|
||||
GetModuleFileNameW(nullptr, wszExeDir, MAX_PATH);
|
||||
wchar_t* pLastSlash = wcsrchr(wszExeDir, L'\\');
|
||||
if (pLastSlash) *(pLastSlash + 1) = L'\0';
|
||||
|
||||
wchar_t wszInstallDir[MAX_PATH];
|
||||
_snwprintf_s(wszInstallDir, _countof(wszInstallDir), _TRUNCATE,
|
||||
L"%lsWindows64Media\\DLC\\%ls",
|
||||
wszExeDir, ctx->offer.wszProductID);
|
||||
wchar_t wszInstallDir[MAX_PATH];
|
||||
_snwprintf_s(wszInstallDir, _countof(wszInstallDir), _TRUNCATE,
|
||||
L"%lsWindows64Media\\DLC\\%ls",
|
||||
wszExeDir, ctx->offer.wszProductID);
|
||||
|
||||
CreateDirectoryW(wszInstallDir, nullptr);
|
||||
CreateDirectoryW(wszInstallDir, nullptr);
|
||||
|
||||
wchar_t wszFilePath[MAX_PATH];
|
||||
_snwprintf_s(wszFilePath, _countof(wszFilePath), _TRUNCATE,
|
||||
L"%ls\\folder.pck", wszInstallDir);
|
||||
wchar_t wszFilePath[MAX_PATH];
|
||||
_snwprintf_s(wszFilePath, _countof(wszFilePath), _TRUNCATE,
|
||||
L"%ls\\folder.pck", wszInstallDir);
|
||||
|
||||
wchar_t wszDownloadUrl[512];
|
||||
_snwprintf_s(wszDownloadUrl, _countof(wszDownloadUrl), _TRUNCATE,
|
||||
L"http://127.0.0.1:3000/download/%ls", ctx->offer.wszProductID);
|
||||
wchar_t wszDownloadUrl[512];
|
||||
_snwprintf_s(wszDownloadUrl, _countof(wszDownloadUrl), _TRUNCATE,
|
||||
L"http://127.0.0.1:3000/download/%ls", ctx->offer.wszProductID);
|
||||
|
||||
PBYTE pData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
PBYTE pData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
|
||||
if (FetchBytesFromUrl(wszDownloadUrl, &pData, &dwBytes) && pData)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD dwWritten = 0;
|
||||
WriteFile(hFile, pData, dwBytes, &dwWritten, nullptr);
|
||||
CloseHandle(hFile);
|
||||
bSuccess = (dwWritten == dwBytes);
|
||||
}
|
||||
delete[] pData;
|
||||
}
|
||||
if (FetchBytesFromUrl(wszDownloadUrl, &pData, &dwBytes) && pData)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD dwWritten = 0;
|
||||
WriteFile(hFile, pData, dwBytes, &dwWritten, nullptr);
|
||||
CloseHandle(hFile);
|
||||
bSuccess = (dwWritten == dwBytes);
|
||||
}
|
||||
delete[] pData;
|
||||
}
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
// Mark the offer as owned.
|
||||
Windows64_DLCOffers::Get().SetPurchased(ctx->offerIndex);
|
||||
if (bSuccess)
|
||||
{
|
||||
Windows64_DLCOffers::Get().SetPurchased(ctx->offerIndex);
|
||||
Windows64_DLCOffers::Get().SetPendingLoadPath(ctx->offerIndex, wszFilePath);
|
||||
InterlockedExchange(&Windows64_DLCOffers::Get().m_iLastInstalled, (LONG)ctx->offerIndex);
|
||||
|
||||
// Must be set BEFORE m_iLastInstalled is updated so the game thread always
|
||||
Windows64_DLCOffers::Get().SetPendingLoadPath(ctx->offerIndex, wszFilePath);
|
||||
|
||||
InterlockedExchange(&Windows64_DLCOffers::Get().m_iLastInstalled,
|
||||
(LONG)ctx->offerIndex);
|
||||
// notify server so purchase persists across sessions
|
||||
wchar_t wszPath[256];
|
||||
_snwprintf_s(wszPath, _countof(wszPath), _TRUNCATE,
|
||||
L"/purchase/%ls?user=%ls",
|
||||
ctx->offer.wszProductID, ctx->username.c_str());
|
||||
|
||||
printf("[DLC] Installed '%ls' -> %ls\n",
|
||||
ctx->offer.wszProductID, wszFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Install FAILED for '%ls'\n", ctx->offer.wszProductID);
|
||||
}
|
||||
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, nullptr, nullptr, 0);
|
||||
if (hSession)
|
||||
{
|
||||
HINTERNET hConn = WinHttpConnect(hSession, L"127.0.0.1", 3000, 0);
|
||||
if (hConn)
|
||||
{
|
||||
HINTERNET hReq = WinHttpOpenRequest(hConn, L"POST", wszPath, nullptr,
|
||||
WINHTTP_NO_REFERER,
|
||||
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (hReq)
|
||||
{
|
||||
WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
|
||||
WinHttpReceiveResponse(hReq, nullptr);
|
||||
WinHttpCloseHandle(hReq);
|
||||
}
|
||||
WinHttpCloseHandle(hConn);
|
||||
}
|
||||
WinHttpCloseHandle(hSession);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Install FAILED for '%ls'\n", ctx->offer.wszProductID);
|
||||
}
|
||||
|
||||
if (ctx->pfnCallback)
|
||||
ctx->pfnCallback(ctx->offer.wszProductID, bSuccess, ctx->pUserData);
|
||||
if (ctx->pfnCallback)
|
||||
ctx->pfnCallback(ctx->offer.wszProductID, bSuccess, ctx->pUserData);
|
||||
|
||||
delete ctx;
|
||||
return 0;
|
||||
delete ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Windows64_DLCOffers::FetchFromServer()
|
||||
{
|
||||
printf("[DLC] FetchFromServer called\n");
|
||||
m_offers.clear();
|
||||
m_bReady = false;
|
||||
printf("[DLC] FetchFromServer called\n");
|
||||
m_offers.clear();
|
||||
m_bReady = false;
|
||||
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return;
|
||||
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
|
||||
if (!hSession) return;
|
||||
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, L"localhost", 3000, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return; }
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, L"localhost", 3000, 0);
|
||||
if (!hConnect) { WinHttpCloseHandle(hSession); return; }
|
||||
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/dlc",
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
wchar_t wszDlcPath[128];
|
||||
_snwprintf_s(wszDlcPath, _countof(wszDlcPath), _TRUNCATE, L"/dlc?user=%ls", Minecraft::GetInstance()->user->name.c_str());
|
||||
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", wszDlcPath,
|
||||
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
|
||||
if (!hRequest)
|
||||
{
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize + 1];
|
||||
ZeroMemory(buf, dwSize + 1);
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
|
||||
!WinHttpReceiveResponse(hRequest, NULL))
|
||||
{
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return;
|
||||
}
|
||||
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
std::string body;
|
||||
DWORD dwSize = 0;
|
||||
do {
|
||||
DWORD dwDownloaded = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
|
||||
char* buf = new char[dwSize + 1];
|
||||
ZeroMemory(buf, dwSize + 1);
|
||||
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
|
||||
body.append(buf, dwDownloaded);
|
||||
delete[] buf;
|
||||
} while (dwSize > 0);
|
||||
|
||||
if (body.empty()) { printf("[DLC] Empty /dlc response\n"); return; }
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
|
||||
printf("[DLC] /dlc response (%d bytes): %s\n", (int)body.size(), body.c_str());
|
||||
if (body.empty()) { printf("[DLC] Empty /dlc response\n"); return; }
|
||||
|
||||
auto toWide = [](const std::string& s, wchar_t* dst, int maxChars) {
|
||||
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, dst, maxChars);
|
||||
};
|
||||
printf("[DLC] /dlc response (%d bytes): %s\n", (int)body.size(), body.c_str());
|
||||
|
||||
size_t pos = 0;
|
||||
while ((pos = body.find('{', pos)) != std::string::npos)
|
||||
{
|
||||
W64_OFFER_INFO offer;
|
||||
ZeroMemory(&offer, sizeof(offer));
|
||||
auto toWide = [](const std::string& s, wchar_t* dst, int maxChars) {
|
||||
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, dst, maxChars);
|
||||
};
|
||||
|
||||
auto readField = [&](const char* key) -> std::string {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return "";
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return "";
|
||||
size_t q1 = body.find('"', colon);
|
||||
if (q1 == std::string::npos) return "";
|
||||
size_t q2 = body.find('"', q1 + 1);
|
||||
if (q2 == std::string::npos) return "";
|
||||
return body.substr(q1 + 1, q2 - q1 - 1);
|
||||
};
|
||||
size_t pos = 0;
|
||||
while ((pos = body.find('{', pos)) != std::string::npos)
|
||||
{
|
||||
W64_OFFER_INFO offer;
|
||||
ZeroMemory(&offer, sizeof(offer));
|
||||
|
||||
auto readInt = [&](const char* key) -> int {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return 0;
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return 0;
|
||||
size_t n = colon + 1;
|
||||
while (n < body.size() && (body[n] == ' ' || body[n] == '\t')) n++;
|
||||
return atoi(&body[n]);
|
||||
};
|
||||
auto readField = [&](const char* key) -> std::string {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return "";
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return "";
|
||||
size_t q1 = body.find('"', colon);
|
||||
if (q1 == std::string::npos) return "";
|
||||
size_t q2 = body.find('"', q1 + 1);
|
||||
if (q2 == std::string::npos) return "";
|
||||
return body.substr(q1 + 1, q2 - q1 - 1);
|
||||
};
|
||||
|
||||
toWide(readField("id"), offer.wszProductID, 64);
|
||||
toWide(readField("name"), offer.wszOfferName, 128);
|
||||
toWide(readField("description"), offer.wszSellText, 512);
|
||||
toWide(readField("price"), offer.wszCurrencyPrice, 32);
|
||||
toWide(readField("type"), offer.wszType, 32);
|
||||
toWide(readField("bannerUrl"), offer.wszBannerUrl, 256);
|
||||
offer.fUserHasPurchased = readInt("purchased");
|
||||
offer.pbBannerData = nullptr;
|
||||
offer.dwBannerBytes = 0;
|
||||
auto readInt = [&](const char* key) -> int {
|
||||
std::string search = std::string("\"") + key + "\"";
|
||||
size_t k = body.find(search, pos);
|
||||
if (k == std::string::npos) return 0;
|
||||
size_t colon = body.find(':', k);
|
||||
if (colon == std::string::npos) return 0;
|
||||
size_t n = colon + 1;
|
||||
while (n < body.size() && (body[n] == ' ' || body[n] == '\t')) n++;
|
||||
return atoi(&body[n]);
|
||||
};
|
||||
|
||||
if (offer.wszProductID[0] != L'\0')
|
||||
{
|
||||
printf("[DLC] Parsed: id='%ls' type='%ls' bannerUrl='%ls'\n",
|
||||
offer.wszProductID, offer.wszType, offer.wszBannerUrl);
|
||||
m_offers.push_back(offer);
|
||||
}
|
||||
toWide(readField("id"), offer.wszProductID, 64);
|
||||
toWide(readField("name"), offer.wszOfferName, 128);
|
||||
toWide(readField("description"), offer.wszSellText, 512);
|
||||
toWide(readField("price"), offer.wszCurrencyPrice, 32);
|
||||
toWide(readField("type"), offer.wszType, 32);
|
||||
toWide(readField("bannerUrl"), offer.wszBannerUrl, 256);
|
||||
offer.fUserHasPurchased = readInt("purchased");
|
||||
offer.pbBannerData = nullptr;
|
||||
offer.dwBannerBytes = 0;
|
||||
|
||||
pos = body.find('}', pos);
|
||||
if (pos == std::string::npos) break;
|
||||
pos++;
|
||||
}
|
||||
if (offer.wszProductID[0] != L'\0')
|
||||
{
|
||||
printf("[DLC] Parsed: id='%ls' type='%ls' bannerUrl='%ls'\n",
|
||||
offer.wszProductID, offer.wszType, offer.wszBannerUrl);
|
||||
m_offers.push_back(offer);
|
||||
}
|
||||
|
||||
printf("[DLC] FetchFromServer complete - %d offers\n", (int)m_offers.size());
|
||||
m_bReady = true;
|
||||
pos = body.find('}', pos);
|
||||
if (pos == std::string::npos) break;
|
||||
pos++;
|
||||
}
|
||||
|
||||
printf("[DLC] FetchFromServer complete - %d offers\n", (int)m_offers.size());
|
||||
m_bReady = true;
|
||||
}
|
||||
|
||||
void Windows64_DLCOffers::FetchBanners()
|
||||
{
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
W64_OFFER_INFO& offer = m_offers[i];
|
||||
if (offer.wszBannerUrl[0] == L'\0') continue;
|
||||
if (offer.pbBannerData != nullptr) continue;
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
W64_OFFER_INFO& offer = m_offers[i];
|
||||
if (offer.wszBannerUrl[0] == L'\0') continue;
|
||||
if (offer.pbBannerData != nullptr) continue;
|
||||
|
||||
PBYTE pbData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
PBYTE pbData = nullptr;
|
||||
DWORD dwBytes = 0;
|
||||
|
||||
if (FetchImageFromUrl(offer.wszBannerUrl, &pbData, &dwBytes))
|
||||
{
|
||||
offer.pbBannerData = pbData;
|
||||
offer.dwBannerBytes = dwBytes;
|
||||
printf("[DLC] Banner fetched for '%ls' - %u bytes\n",
|
||||
offer.wszProductID, dwBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Banner fetch FAILED for '%ls' url='%ls'\n",
|
||||
offer.wszProductID, offer.wszBannerUrl);
|
||||
}
|
||||
}
|
||||
if (FetchImageFromUrl(offer.wszBannerUrl, &pbData, &dwBytes))
|
||||
{
|
||||
offer.pbBannerData = pbData;
|
||||
offer.dwBannerBytes = dwBytes;
|
||||
printf("[DLC] Banner fetched for '%ls' - %u bytes\n",
|
||||
offer.wszProductID, dwBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[DLC] Banner fetch FAILED for '%ls' url='%ls'\n",
|
||||
offer.wszProductID, offer.wszBannerUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Register all offers with the app now that banners are populated.
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
const W64_OFFER_INFO& offer = m_offers[i];
|
||||
// Register all offers with the app now that banners are populated.
|
||||
for (int i = 0; i < (int)m_offers.size(); i++)
|
||||
{
|
||||
const W64_OFFER_INFO& offer = m_offers[i];
|
||||
|
||||
eDLCContentType eType = e_DLC_SkinPack;
|
||||
if (wcscmp(offer.wszType, L"MashUp") == 0) eType = e_DLC_MashupPacks;
|
||||
else if (wcscmp(offer.wszType, L"TexturePack") == 0) eType = e_DLC_TexturePacks;
|
||||
else if (wcscmp(offer.wszType, L"SkinPack") == 0) eType = e_DLC_SkinPack;
|
||||
eDLCContentType eType = e_DLC_SkinPack;
|
||||
if (wcscmp(offer.wszType, L"MashUp") == 0) eType = e_DLC_MashupPacks;
|
||||
else if (wcscmp(offer.wszType, L"TexturePack") == 0) eType = e_DLC_TexturePacks;
|
||||
else if (wcscmp(offer.wszType, L"SkinPack") == 0) eType = e_DLC_SkinPack;
|
||||
|
||||
printf("[DLC] Registering W64 DLC: '%ls' type=%d\n",
|
||||
offer.wszProductID, (int)eType);
|
||||
printf("[DLC] Registering W64 DLC: '%ls' type=%d\n",
|
||||
offer.wszProductID, (int)eType);
|
||||
|
||||
app.RegisterW64DLC(
|
||||
eType,
|
||||
offer.wszProductID, // key AND banner texture name
|
||||
offer.wszProductID, // banner texture name (used as lookup key in UpdateDisplay)
|
||||
0, // iConfig
|
||||
(unsigned int)i, // uiSortIndex
|
||||
offer.pbBannerData,
|
||||
offer.dwBannerBytes
|
||||
);
|
||||
}
|
||||
app.RegisterW64DLC(
|
||||
eType,
|
||||
offer.wszProductID, // key AND banner texture name
|
||||
offer.wszProductID, // banner texture name (used as lookup key in UpdateDisplay)
|
||||
0, // iConfig
|
||||
(unsigned int)i, // uiSortIndex
|
||||
offer.pbBannerData,
|
||||
offer.dwBannerBytes
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
|
||||
void InstallOffer(int iIndex,
|
||||
W64_INSTALL_CALLBACK pfnCallback = nullptr,
|
||||
void* pUserData = nullptr);
|
||||
void* pUserData = nullptr, wstring username = L"");
|
||||
|
||||
static bool FetchImageFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize);
|
||||
static bool FetchBytesFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize);
|
||||
@@ -95,6 +95,7 @@ private:
|
||||
int offerIndex;
|
||||
W64_INSTALL_CALLBACK pfnCallback;
|
||||
void* pUserData;
|
||||
wstring username;
|
||||
};
|
||||
|
||||
static DWORD WINAPI InstallThreadProc(LPVOID lpParam);
|
||||
|
||||
Reference in New Issue
Block a user