Implement WinINet based HTTP downloads

This commit is contained in:
Tim Angus 2025-07-18 12:33:34 +01:00
parent 94c9be05c9
commit 9814f5f42f
2 changed files with 173 additions and 3 deletions

View File

@ -749,7 +749,9 @@ ifdef MINGW
FREETYPE_CFLAGS = -Ifreetype2
endif
USE_HTTP=0
ifeq ($(USE_HTTP),1)
CLIENT_LIBS += -lwininet
endif
ifeq ($(ARCH),x86)
# build 32bit
@ -1914,8 +1916,6 @@ Q3OBJ = \
$(B)/client/qal.o \
$(B)/client/snd_openal.o \
\
$(B)/client/cl_http_curl.o \
\
$(B)/client/sv_bot.o \
$(B)/client/sv_ccmds.o \
$(B)/client/sv_client.o \
@ -1971,6 +1971,14 @@ Q3OBJ = \
$(B)/client/sys_autoupdater.o \
$(B)/client/sys_main.o
ifdef MINGW
Q3OBJ += \
$(B)/client/cl_http_windows.o
else
Q3OBJ += \
$(B)/client/cl_http_curl.o
endif
ifdef MINGW
Q3OBJ += \
$(B)/client/con_passive.o

View File

@ -0,0 +1,162 @@
/*
===========================================================================
Copyright (C) 2025 Tim Angus (tim@ngus.net)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifdef USE_HTTP
#include "client.h"
#include <windows.h>
#include <wininet.h>
static HINTERNET hInternet = NULL;
static HINTERNET hUrl = NULL;
static void DropIf(qboolean condition, const char *fmt, ...)
{
char buffer[1024];
if (!condition)
return;
va_list argptr;
va_start(argptr, fmt);
Q_vsnprintf(buffer, sizeof(buffer), fmt, argptr);
va_end(argptr);
Com_Error(ERR_DROP, "Download Error: %s URL: %s", buffer, clc.downloadURL);
}
/*
=================
CL_HTTP_Init
=================
*/
qboolean CL_HTTP_Init()
{
OSVERSIONINFO osvi = {sizeof(OSVERSIONINFO)};
const char *windowsVersion = GetVersionEx(&osvi) ? va("Windows %lu.%lu (build %lu)",
osvi.dwMajorVersion,
osvi.dwMinorVersion,
osvi.dwBuildNumber)
: "Windows";
hInternet = InternetOpenA(
va("%s %s", Q3_VERSION, windowsVersion),
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
return hInternet != NULL;
}
/*
=================
CL_HTTP_Available
=================
*/
qboolean CL_HTTP_Available()
{
return hInternet != NULL;
}
/*
=================
CL_HTTP_Shutdown
=================
*/
void CL_HTTP_Shutdown(void)
{
if (hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
}
/*
=================
CL_HTTP_BeginDownload
=================
*/
void CL_HTTP_BeginDownload(const char *remoteURL)
{
DWORD httpCode = 0;
DWORD contentLength = 0;
DWORD len = sizeof(httpCode);
DWORD zero = 0;
BOOL success;
hUrl = InternetOpenUrlA(hInternet, remoteURL,
va("Referer: ioQ3://%s\r\n", NET_AdrToString(clc.serverAddress)), -1,
INTERNET_FLAG_HYPERLINK |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RESYNCHRONIZE |
INTERNET_FLAG_RELOAD,
0);
DropIf(hUrl == NULL, "InternetOpenUrlA failed %lu", GetLastError());
success = HttpQueryInfo(hUrl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &httpCode, &len, &zero);
DropIf(!success, "Get HTTP_QUERY_STATUS_CODE failed %lu", GetLastError());
DropIf(httpCode >= 400, "HTTP code %lu", httpCode);
DropIf(httpCode != 200, "Unhandled HTTP code %lu", httpCode);
success = HttpQueryInfo(hUrl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &contentLength, &len, &zero);
DropIf(!success, "Get HTTP_QUERY_CONTENT_LENGTH failed %lu", GetLastError());
clc.downloadSize = (int)contentLength;
Cvar_SetValue("cl_downloadSize", clc.downloadSize);
}
/*
=================
CL_HTTP_PerformDownload
=================
*/
qboolean CL_HTTP_PerformDownload(void)
{
static BYTE readBuffer[256 * 1024];
DWORD bytesRead = 0;
BOOL success;
DropIf(hUrl == NULL, "hUrl is NULL");
success = InternetReadFile(hUrl, readBuffer, sizeof(readBuffer), &bytesRead);
DropIf(!success, "InternetReadFile failed %lu", GetLastError());
if (bytesRead > 0)
{
clc.downloadCount += bytesRead;
Cvar_SetValue("cl_downloadCount", clc.downloadCount);
int bytesWritten = FS_Write(readBuffer, bytesRead, clc.download);
DropIf(bytesWritten != bytesRead, "bytesWritten != bytesRead");
return qfalse;
}
InternetCloseHandle(hUrl);
return qtrue;
}
#endif /* USE_HTTP */