Skip to content

Commit df1cb0d

Browse files
committed
Implement WinINet based HTTP downloads
1 parent a7b3684 commit df1cb0d

2 files changed

Lines changed: 172 additions & 3 deletions

File tree

Makefile

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,9 @@ ifdef MINGW
749749
FREETYPE_CFLAGS = -Ifreetype2
750750
endif
751751

752-
USE_HTTP=0
752+
ifeq ($(USE_HTTP),1)
753+
CLIENT_LIBS += -lwininet
754+
endif
753755

754756
ifeq ($(ARCH),x86)
755757
# build 32bit
@@ -1914,8 +1916,6 @@ Q3OBJ = \
19141916
$(B)/client/qal.o \
19151917
$(B)/client/snd_openal.o \
19161918
\
1917-
$(B)/client/cl_http_curl.o \
1918-
\
19191919
$(B)/client/sv_bot.o \
19201920
$(B)/client/sv_ccmds.o \
19211921
$(B)/client/sv_client.o \
@@ -1971,6 +1971,14 @@ Q3OBJ = \
19711971
$(B)/client/sys_autoupdater.o \
19721972
$(B)/client/sys_main.o
19731973

1974+
ifdef MINGW
1975+
Q3OBJ += \
1976+
$(B)/client/cl_http_windows.o
1977+
else
1978+
Q3OBJ += \
1979+
$(B)/client/cl_http_curl.o
1980+
endif
1981+
19741982
ifdef MINGW
19751983
Q3OBJ += \
19761984
$(B)/client/con_passive.o

code/client/cl_http_windows.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
===========================================================================
3+
Copyright (C) 2025 Tim Angus (tim@ngus.net)
4+
5+
This file is part of Quake III Arena source code.
6+
7+
Quake III Arena source code is free software; you can redistribute it
8+
and/or modify it under the terms of the GNU General Public License as
9+
published by the Free Software Foundation; either version 2 of the License,
10+
or (at your option) any later version.
11+
12+
Quake III Arena source code is distributed in the hope that it will be
13+
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
GNU General Public License for more details.
16+
17+
You should have received a copy of the GNU General Public License
18+
along with Quake III Arena source code; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
===========================================================================
21+
*/
22+
23+
#ifdef USE_HTTP
24+
25+
#include "client.h"
26+
27+
#include <windows.h>
28+
#include <wininet.h>
29+
30+
static HINTERNET hInternet = NULL;
31+
static HINTERNET hUrl = NULL;
32+
33+
static void DropIf(qboolean condition, const char *fmt, ...)
34+
{
35+
char buffer[1024];
36+
37+
if (!condition)
38+
return;
39+
40+
va_list argptr;
41+
va_start(argptr, fmt);
42+
Q_vsnprintf(buffer, sizeof(buffer), fmt, argptr);
43+
va_end(argptr);
44+
45+
Com_Error(ERR_DROP, "Download Error: %s URL: %s", buffer, clc.downloadURL);
46+
}
47+
48+
/*
49+
=================
50+
CL_HTTP_Init
51+
=================
52+
*/
53+
qboolean CL_HTTP_Init()
54+
{
55+
OSVERSIONINFO osvi = {sizeof(OSVERSIONINFO)};
56+
const char *windowsVersion = GetVersionEx(&osvi) ? va("Windows %lu.%lu (build %lu)",
57+
osvi.dwMajorVersion,
58+
osvi.dwMinorVersion,
59+
osvi.dwBuildNumber)
60+
: "Windows";
61+
62+
hInternet = InternetOpenA(
63+
va("%s %s", Q3_VERSION, windowsVersion),
64+
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
65+
66+
return hInternet != NULL;
67+
}
68+
69+
/*
70+
=================
71+
CL_HTTP_Available
72+
=================
73+
*/
74+
qboolean CL_HTTP_Available()
75+
{
76+
return hInternet != NULL;
77+
}
78+
79+
/*
80+
=================
81+
CL_HTTP_Shutdown
82+
=================
83+
*/
84+
void CL_HTTP_Shutdown(void)
85+
{
86+
if (hInternet)
87+
{
88+
InternetCloseHandle(hInternet);
89+
hInternet = NULL;
90+
}
91+
}
92+
93+
/*
94+
=================
95+
CL_HTTP_BeginDownload
96+
=================
97+
*/
98+
void CL_HTTP_BeginDownload(const char *remoteURL)
99+
{
100+
DWORD httpCode = 0;
101+
DWORD contentLength = 0;
102+
DWORD len = sizeof(httpCode);
103+
DWORD zero = 0;
104+
BOOL success;
105+
106+
hUrl = InternetOpenUrlA(hInternet, remoteURL, NULL, 0,
107+
INTERNET_FLAG_HYPERLINK |
108+
INTERNET_FLAG_NO_CACHE_WRITE |
109+
INTERNET_FLAG_NO_COOKIES |
110+
INTERNET_FLAG_NO_UI |
111+
INTERNET_FLAG_RESYNCHRONIZE |
112+
INTERNET_FLAG_RELOAD,
113+
0);
114+
115+
DropIf(hUrl == NULL, "InternetOpenUrlA failed %lu", GetLastError());
116+
117+
success = HttpQueryInfo(hUrl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &httpCode, &len, &zero);
118+
DropIf(!success, "Get HTTP_QUERY_STATUS_CODE failed %lu", GetLastError());
119+
120+
DropIf(httpCode >= 400, "HTTP code %lu", httpCode);
121+
DropIf(httpCode != 200, "Unhandled HTTP code %lu", httpCode);
122+
123+
success = HttpQueryInfo(hUrl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &contentLength, &len, &zero);
124+
DropIf(!success, "Get HTTP_QUERY_CONTENT_LENGTH failed %lu", GetLastError());
125+
126+
clc.downloadSize = (int)contentLength;
127+
Cvar_SetValue("cl_downloadSize", clc.downloadSize);
128+
}
129+
130+
/*
131+
=================
132+
CL_HTTP_PerformDownload
133+
=================
134+
*/
135+
qboolean CL_HTTP_PerformDownload(void)
136+
{
137+
static BYTE readBuffer[256 * 1024];
138+
DWORD bytesRead = 0;
139+
BOOL success;
140+
141+
DropIf(hUrl == NULL, "hUrl is NULL");
142+
143+
success = InternetReadFile(hUrl, readBuffer, sizeof(readBuffer), &bytesRead);
144+
DropIf(!success, "InternetReadFile failed %lu", GetLastError());
145+
146+
if (bytesRead > 0)
147+
{
148+
clc.downloadCount += bytesRead;
149+
Cvar_SetValue("cl_downloadCount", clc.downloadCount);
150+
151+
int bytesWritten = FS_Write(readBuffer, bytesRead, clc.download);
152+
DropIf(bytesWritten != bytesRead, "bytesWritten != bytesRead");
153+
154+
return qfalse;
155+
}
156+
157+
InternetCloseHandle(hUrl);
158+
return qtrue;
159+
}
160+
161+
#endif /* USE_HTTP */

0 commit comments

Comments
 (0)