Battle.net File Downloading by Skywing

The Battle.net file download system has a rich enough set of features for most needs, including support for file resume. I've put together a function to download a file from Battle.net:


// FileDownload Copyright (C) 2001 Skywing
// filename is the file to download from the server.
// ip is the network byte order IP address of the server.
// startposition is the position to begin downloading the file at. You can use this to resume an interrupted file download.
// Note that Battle.net supports file transfers on both port 6112 through connection type 0x02, or on port 116, without a connection select. Simply define PORT_116 if you wish to use it instead of port 6112.
bool downloadfile(const char *filename, DWORD ip, DWORD startposition) {
char databuf[2048] = "";
char servername[256] = "";
HANDLE dataevent = INVALID_HANDLE_VALUE;
HANDLE downloadedfile = INVALID_HANDLE_VALUE;
SOCKADDR_IN remoteaddr = {AF_INET, 0, INADDR_ANY};
SOCKET filesocket = INVALID_SOCKET;
DWORD nwritten = 0;
DWORD totalwritten = 0;
DWORD waitresult = 0;
DWORD filelen = 0;
time_t startsecond = 0;
FILETIME filedate = {0};
int recvlen = 0;
int buflen = 0;
bool receivedheader = false;
remoteaddr.sin_addr.s_addr = ip;
#ifdef PORT_116
remoteaddr.sin_port = htons(116);
#else
remoteaddr.sin_port = htons(6112);
#endif
filesocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(filesocket == INVALID_SOCKET) {
printf("Unable to create socket!\n");
return false;
}
dataevent = CreateEvent(0, 0, 0, 0);
if(!dataevent) {
printf("Unable to create receive event!\n");
closesocket(filesocket);
return false;
}
PacketBuffer downloadbuf;
downloadbuf.insertshort(0); // len
downloadbuf.insertshort(0x0100);
downloadbuf.insert("68XIPXES", 8);
downloadbuf.insert((int)0);
downloadbuf.insert((int)0);
downloadbuf.insert((int)startposition);
downloadbuf.insert((int)1); // filetime
downloadbuf.insert((int)2); // filetime
downloadbuf.insert(filename);
*(unsigned short *)(char *)downloadbuf = (int)downloadbuf;
if(connect(filesocket, (SOCKADDR *)&remoteaddr, sizeof(remoteaddr))) {
printf("Unable to connect to Battle.net server (code %u)!\n", WSAGetLastError());
closesocket(filesocket);
CloseHandle(dataevent);
return false;
} else {
printf("Connected to Battle.net server, requesting file download for %s...\n", filename);
#ifndef PORT_116
send(filesocket, "\x02", 1, 0);
#endif
send(filesocket, (char *)downloadbuf, (int)downloadbuf, 0);
WSAEventSelect(filesocket, dataevent, FD_READ | FD_CLOSE);
}
while(filesocket != INVALID_SOCKET) {
waitresult = WaitForMultipleObjects(1, &dataevent, 0, 180000);
if(waitresult == WAIT_FAILED) {
printf("Error waiting for data from Battle.net!\n");
closesocket(filesocket);
CloseHandle(dataevent);
return false;
} else if(waitresult == WAIT_TIMEOUT) {
printf("Connection to Battle.net timed out!\n");
closesocket(filesocket);
CloseHandle(dataevent);
return false;
} else if(waitresult == WAIT_OBJECT_0) {
recvlen = recv(filesocket, databuf + buflen, 1024, 0);
if(recvlen < 1) {
printf("Connection to Battle.net lost, download failed!\n");
closesocket(filesocket);
CloseHandle(dataevent);
return false;
}
buflen += recvlen;
if(receivedheader == false) {
if(recvlen < 2) {
// printf("waiting for header len\n");
continue;
}
if(buflen < *(unsigned long *)databuf) {
// printf("waiting for full header\n");
continue;
}
downloadbuf.clear();
downloadbuf.insert(databuf + 4, *(unsigned long *)databuf - 4);
downloadbuf.reset();
filelen = downloadbuf.extract();
downloadbuf += 8;
filedate.dwLowDateTime = downloadbuf.extract();
filedate.dwHighDateTime = downloadbuf.extract();
downloadbuf.extract(servername);
receivedheader = true;
// printf("got header, len=%u bytes, servername=%s\n", filelen, servername);
printf("Received confirmation of file request, remote name %s (length: %u bytes)!\n", servername, filelen);
time(&startsecond);
downloadedfile = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if(downloadedfile == INVALID_HANDLE_VALUE) {
printf("Error creating file (code %u)!\n", GetLastError());
closesocket(filesocket);
CloseHandle(dataevent);
return false;
}
if(startposition) {
printf("Starting download at position %u...\n", startposition);
SetFilePointer(downloadedfile, startposition, 0, FILE_BEGIN);
filelen -= startposition;
}
MoveMemory(databuf, databuf + downloadbuf.extractlen() + 4, buflen - downloadbuf.extractlen() + 4);
buflen -= downloadbuf.extractlen() + 4;
}
if(buflen) {
WriteFile(downloadedfile, databuf, buflen, &nwritten, 0);
totalwritten += buflen;
printf("Received %u bytes, %u total, out of %u bytes total in %s.\n", buflen, totalwritten, filelen, filename);
}
if(totalwritten == filelen) {
printf("File successfully downloaded!\n");
int seconds = time(0) - startsecond;
if(seconds) {
int kbytes = totalwritten / 1024;
if(kbytes) {
printf("File transfer rate: %d KBps (%u kilobytes downloaded in %u seconds).\n", kbytes / seconds, kbytes, seconds);
} else {
printf("Zero kilobytes transfered!\n");
}
} else {
printf("Zero download time!\n");
}
CloseHandle(downloadedfile);
closesocket(filesocket);
CloseHandle(dataevent);
return true;
}
buflen = 0;
}
}
return true;
}




Successfully tested this on a large file (the 2MB any SC version -- SC 108B patch):

File successfully downloaded!
File transfer rate: 141 KBps (2683 kilobytes downloaded in 19 seconds)

Skywing[vL] of Valhalla Legends
Battle.net: Clan [vL] - USE-BNCS02.Battle.net
Programmer of ZeroBot, BinaryBotPlugin and SCEnhancements.