d_netfil.c 42.5 KB
Newer Older
Alam Ed Arias committed
1 2 3
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
James R. committed
4
// Copyright (C) 1999-2020 by Sonic Team Junior.
Alam Ed Arias committed
5 6 7 8 9 10 11 12 13 14 15 16 17
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  d_netfil.c
/// \brief Transfer a file using HSendPacket.

#include <stdio.h>
#include <sys/stat.h>

#include <time.h>

Monster Iestyn committed
18
#if defined (_WIN32) || defined (__DJGPP__)
Alam Ed Arias committed
19 20
#include <io.h>
#include <direct.h>
Monster Iestyn committed
21
#else
Alam Ed Arias committed
22 23 24 25 26 27 28 29
#include <sys/types.h>
#include <dirent.h>
#include <utime.h>
#endif

#ifdef __GNUC__
#include <unistd.h>
#include <limits.h>
Monster Iestyn committed
30
#elif defined (_WIN32)
Alam Ed Arias committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#include <sys/utime.h>
#endif
#ifdef __DJGPP__
#include <dir.h>
#include <utime.h>
#endif

#include "doomdef.h"
#include "doomstat.h"
#include "d_main.h"
#include "g_game.h"
#include "i_net.h"
#include "i_system.h"
#include "m_argv.h"
#include "d_net.h"
#include "w_wad.h"
#include "d_netfil.h"
#include "z_zone.h"
#include "byteptr.h"
#include "p_setup.h"
#include "m_misc.h"
#include "m_menu.h"
#include "md5.h"
#include "filesrch.h"

Alam Ed Arias committed
56 57
#include <errno.h>

58
// Prototypes
59
static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid);
Alam Ed Arias committed
60

61
// Sender structure
Alam Ed Arias committed
62 63 64
typedef struct filetx_s
{
	INT32 ram;
65 66 67 68 69
	union {
		char *filename; // Name of the file
		char *ram; // Pointer to the data in RAM
	} id;
	UINT32 size; // Size of the file
Alam Ed Arias committed
70
	UINT8 fileid;
71 72
	INT32 node; // Destination
	struct filetx_s *next; // Next file in the list
Alam Ed Arias committed
73 74
} filetx_t;

75
// Current transfers (one for each node)
Alam Ed Arias committed
76 77
typedef struct filetran_s
{
78
	filetx_t *txlist; // Linked list of all files for the node
79 80
	UINT8 iteration;
	UINT8 ackediteration;
81
	UINT32 position; // The current position in the file
LJ Sonic committed
82 83
	boolean *ackedfragments;
	UINT32 ackedsize;
84
	FILE *currentfile; // The file currently being sent/received
85
	tic_t dontsenduntil;
Alam Ed Arias committed
86 87 88
} filetran_t;
static filetran_t transfer[MAXNETNODES];

89 90
// Read time of file: stat _stmtime
// Write time of file: utime
Alam Ed Arias committed
91

92 93 94
// Receiver structure
INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
LJ Sonic committed
95
static tic_t lasttimeackpacketsent = 0;
96
char downloaddir[512] = "DOWNLOAD";
Alam Ed Arias committed
97

98 99 100 101 102 103 104 105 106 107 108
// For resuming failed downloads
typedef struct
{
	char filename[MAX_WADPATH];
	UINT8 md5sum[16];
	boolean *receivedfragments;
	UINT32 fragmentsize;
	UINT32 currentsize;
} pauseddownload_t;
static pauseddownload_t *pauseddownload = NULL;

109
#ifndef NONET
Alam Ed Arias committed
110
// for cl loading screen
111
INT32 lastfilenum = -1;
Alam Ed Arias committed
112 113
#endif

LJ Sonic committed
114 115
luafiletransfer_t *luafiletransfers = NULL;
boolean waitingforluafiletransfer = false;
116
boolean waitingforluafilecommand = false;
117
char luafiledir[256 + 16] = "luafiles";
LJ Sonic committed
118 119


Alam Ed Arias committed
120 121 122
/** Fills a serverinfo packet with information about wad files loaded.
  *
  * \todo Give this function a better name since it is in global scope.
123
  * Used to have size limiting built in - now handled via W_LoadWadFile in w_wad.c
124
  *
Alam Ed Arias committed
125 126 127 128 129 130 131 132 133 134
  */
UINT8 *PutFileNeeded(void)
{
	size_t i, count = 0;
	UINT8 *p = netbuffer->u.serverinfo.fileneeded;
	char wadfilename[MAX_WADPATH] = "";
	UINT8 filestatus;

	for (i = 0; i < numwadfiles; i++)
	{
135
		// If it has only music/sound lumps, don't put it in the list
136
		if (!wadfiles[i]->important)
137
			continue;
138 139

		filestatus = 1; // Importance - not really used any more, holds 1 by default for backwards compat with MS
Alam Ed Arias committed
140 141 142

		// Store in the upper four bits
		if (!cv_downloading.value)
143
			filestatus += (2 << 4); // Won't send
144
		else if ((wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024))
145
			filestatus += (1 << 4); // Will send if requested
146 147
		// else
			// filestatus += (0 << 4); -- Won't send, too big
Alam Ed Arias committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161

		WRITEUINT8(p, filestatus);

		count++;
		WRITEUINT32(p, wadfiles[i]->filesize);
		nameonly(strcpy(wadfilename, wadfiles[i]->filename));
		WRITESTRINGN(p, wadfilename, MAX_WADPATH);
		WRITEMEM(p, wadfiles[i]->md5sum, 16);
	}
	netbuffer->u.serverinfo.fileneedednum = (UINT8)count;

	return p;
}

162 163 164 165 166 167
/** Parses the serverinfo packet and fills the fileneeded table on client
  *
  * \param fileneedednum_parm The number of files needed to join the server
  * \param fileneededstr The memory block containing the list of needed files
  *
  */
Alam Ed Arias committed
168 169 170 171 172 173 174 175 176 177
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
{
	INT32 i;
	UINT8 *p;
	UINT8 filestatus;

	fileneedednum = fileneedednum_parm;
	p = (UINT8 *)fileneededstr;
	for (i = 0; i < fileneedednum; i++)
	{
178
		fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
LJ Sonic committed
179
		fileneeded[i].justdownloaded = false;
180
		filestatus = READUINT8(p); // The first byte is the file status
Alam Ed Arias committed
181
		fileneeded[i].willsend = (UINT8)(filestatus >> 4);
182 183 184 185
		fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
		fileneeded[i].file = NULL; // The file isn't open yet
		READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
		READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
Alam Ed Arias committed
186 187 188 189 190
	}
}

void CL_PrepareDownloadSaveGame(const char *tmpsave)
{
191
#ifndef NONET
192 193 194
	lastfilenum = -1;
#endif

Alam Ed Arias committed
195 196
	fileneedednum = 1;
	fileneeded[0].status = FS_REQUESTED;
LJ Sonic committed
197
	fileneeded[0].justdownloaded = false;
Alam Ed Arias committed
198
	fileneeded[0].totalsize = UINT32_MAX;
199
	fileneeded[0].file = NULL;
Alam Ed Arias committed
200 201 202 203 204 205
	memset(fileneeded[0].md5sum, 0, 16);
	strcpy(fileneeded[0].filename, tmpsave);
}

/** Checks the server to see if we CAN download all the files,
  * before starting to create them and requesting.
206 207 208
  *
  * \return True if we can download all the files
  *
Alam Ed Arias committed
209 210 211 212 213 214
  */
boolean CL_CheckDownloadable(void)
{
	UINT8 i,dlstatus = 0;

	for (i = 0; i < fileneedednum; i++)
215
		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)
Alam Ed Arias committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
		{
			if (fileneeded[i].willsend == 1)
				continue;

			if (fileneeded[i].willsend == 0)
				dlstatus = 1;
			else //if (fileneeded[i].willsend == 2)
				dlstatus = 2;
		}

	// Downloading locally disabled
	if (!dlstatus && M_CheckParm("-nodownload"))
		dlstatus = 3;

	if (!dlstatus)
		return true;

	// not downloadable, put reason in console
	CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n"));
	for (i = 0; i < fileneedednum; i++)
236
		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)
Alam Ed Arias committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
		{
			CONS_Printf(" * \"%s\" (%dK)", fileneeded[i].filename, fileneeded[i].totalsize >> 10);

				if (fileneeded[i].status == FS_NOTFOUND)
					CONS_Printf(M_GetText(" not found, md5: "));
				else if (fileneeded[i].status == FS_MD5SUMBAD)
					CONS_Printf(M_GetText(" wrong version, md5: "));

			{
				INT32 j;
				char md5tmp[33];
				for (j = 0; j < 16; j++)
					sprintf(&md5tmp[j*2], "%02x", fileneeded[i].md5sum[j]);
				CONS_Printf("%s", md5tmp);
			}
			CONS_Printf("\n");
		}

	switch (dlstatus)
	{
		case 1:
			CONS_Printf(M_GetText("Some files are larger than the server is willing to send.\n"));
			break;
		case 2:
			CONS_Printf(M_GetText("The server is not allowing download requests.\n"));
			break;
		case 3:
			CONS_Printf(M_GetText("All files downloadable, but you have chosen to disable downloading locally.\n"));
			break;
	}
	return false;
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
/** Returns true if a needed file transfer can be resumed
  *
  * \param file The needed file to resume the transfer for
  * \return True if the transfer can be resumed
  *
  */
static boolean CL_CanResumeDownload(fileneeded_t *file)
{
	return pauseddownload
		&& !strcmp(pauseddownload->filename, file->filename) // Same name
		&& !memcmp(pauseddownload->md5sum, file->md5sum, 16) // Same checksum
		&& pauseddownload->fragmentsize == file->fragmentsize; // Same fragment size
}

void CL_AbortDownloadResume(void)
{
	if (!pauseddownload)
		return;

	free(pauseddownload->receivedfragments);
	remove(pauseddownload->filename);
	free(pauseddownload);
	pauseddownload = NULL;
}

295
/** Sends requests for files in the ::fileneeded table with a status of
Alam Ed Arias committed
296
  * ::FS_NOTFOUND.
297 298 299 300
  *
  * \return True if the packet was successfully sent
  * \note Sends a PT_REQUESTFILE packet
  *
Alam Ed Arias committed
301
  */
302
boolean CL_SendFileRequest(void)
Alam Ed Arias committed
303 304 305 306 307 308 309 310 311 312 313
{
	char *p;
	INT32 i;
	INT64 totalfreespaceneeded = 0, availablefreespace;

#ifdef PARANOIA
	if (M_CheckParm("-nodownload"))
		I_Error("Attempted to download files in -nodownload mode");

	for (i = 0; i < fileneedednum; i++)
		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
314
			&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
Alam Ed Arias committed
315 316 317 318 319 320 321 322
		{
			I_Error("Attempted to download files that were not sendable");
		}
#endif

	netbuffer->packettype = PT_REQUESTFILE;
	p = (char *)netbuffer->u.textcmd;
	for (i = 0; i < fileneedednum; i++)
323
		if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD))
Alam Ed Arias committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
		{
			totalfreespaceneeded += fileneeded[i].totalsize;
			nameonly(fileneeded[i].filename);
			WRITEUINT8(p, i); // fileid
			WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
			// put it in download dir
			strcatbf(fileneeded[i].filename, downloaddir, "/");
			fileneeded[i].status = FS_REQUESTED;
		}
	WRITEUINT8(p, 0xFF);
	I_GetDiskFreeSpace(&availablefreespace);
	if (totalfreespaceneeded > availablefreespace)
		I_Error("To play on this server you must download %s KB,\n"
			"but you have only %s KB free space on this drive\n",
			sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));

	// prepare to download
	I_mkdir(downloaddir, 0755);
	return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd);
}

// get request filepak and put it on the send queue
346
// returns false if a requested file was not found or cannot be sent
347
boolean PT_RequestFile(INT32 node)
Alam Ed Arias committed
348 349 350 351 352 353 354 355 356 357
{
	char wad[MAX_WADPATH+1];
	UINT8 *p = netbuffer->u.textcmd;
	UINT8 id;
	while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
	{
		id = READUINT8(p);
		if (id == 0xFF)
			break;
		READSTRINGN(p, wad, MAX_WADPATH);
358
		if (!AddFileToSendQueue(node, wad, id))
359
		{
360
			SV_AbortSendFiles(node);
361 362
			return false; // don't read the rest of the files
		}
Alam Ed Arias committed
363
	}
364
	return true; // no problems with any files
Alam Ed Arias committed
365 366
}

367 368 369 370 371 372 373
/** Checks if the files needed aren't already loaded or on the disk
  *
  * \return 0 if some files are missing
  *         1 if all files exist
  *         2 if some already loaded files are not requested or are in a different order
  *
  */
Alam Ed Arias committed
374 375 376 377 378
INT32 CL_CheckFiles(void)
{
	INT32 i, j;
	char wadfilename[MAX_WADPATH];
	INT32 ret = 1;
379
	size_t packetsize = 0;
380
	size_t filestoget = 0;
Alam Ed Arias committed
381 382 383 384 385

//	if (M_CheckParm("-nofiles"))
//		return 1;

	// the first is the iwad (the main wad file)
386
	// we don't care if it's called srb2.pk3 or not.
Alam Ed Arias committed
387 388 389 390 391 392 393 394 395 396 397
	// Never download the IWAD, just assume it's there and identical
	fileneeded[0].status = FS_OPEN;

	// Modified game handling -- check for an identical file list
	// must be identical in files loaded AND in order
	// Return 2 on failure -- disconnect from server
	if (modifiedgame)
	{
		CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n");
		for (i = 1, j = 1; i < fileneedednum || j < numwadfiles;)
		{
398
			if (j < numwadfiles && !wadfiles[j]->important)
Alam Ed Arias committed
399
			{
400
				// Unimportant on our side.
Alam Ed Arias committed
401 402 403 404 405 406 407 408 409
				++j;
				continue;
			}

			// If this test is true, we've reached the end of one file list
			// and the other still has a file that's important
			if (i >= fileneedednum || j >= numwadfiles)
				return 2;

410
			// For the sake of speed, only bother with a md5 check
Alam Ed Arias committed
411 412 413
			if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
				return 2;

414
			// It's accounted for! let's keep going.
Alam Ed Arias committed
415 416 417 418 419 420 421 422
			CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename);
			fileneeded[i].status = FS_OPEN;
			++i;
			++j;
		}
		return 1;
	}

423
	// See W_LoadWadFile in w_wad.c
424
	packetsize = packetsizetally;
425

Alam Ed Arias committed
426 427 428 429
	for (i = 1; i < fileneedednum; i++)
	{
		CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);

430
		// Check in already loaded files
Alam Ed Arias committed
431 432 433 434 435 436 437 438 439 440 441
		for (j = 1; wadfiles[j]; j++)
		{
			nameonly(strcpy(wadfilename, wadfiles[j]->filename));
			if (!stricmp(wadfilename, fileneeded[i].filename) &&
				!memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
			{
				CONS_Debug(DBG_NETPLAY, "already loaded\n");
				fileneeded[i].status = FS_OPEN;
				break;
			}
		}
442
		if (fileneeded[i].status != FS_NOTFOUND)
Alam Ed Arias committed
443 444
			continue;

445 446
		packetsize += nameonlylength(fileneeded[i].filename) + 22;

447
		if ((numwadfiles+filestoget >= MAX_WADFILES)
448
		|| (packetsize > MAXFILENEEDED*sizeof(UINT8)))
449 450
			return 3;

451 452
		filestoget++;

Alam Ed Arias committed
453 454 455 456 457 458 459 460
		fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true);
		CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status);
		if (fileneeded[i].status != FS_FOUND)
			ret = 0;
	}
	return ret;
}

461
// Load it now
Alam Ed Arias committed
462 463 464 465 466 467 468 469 470 471
void CL_LoadServerFiles(void)
{
	INT32 i;

//	if (M_CheckParm("-nofiles"))
//		return;

	for (i = 1; i < fileneedednum; i++)
	{
		if (fileneeded[i].status == FS_OPEN)
472
			continue; // Already loaded
Alam Ed Arias committed
473 474
		else if (fileneeded[i].status == FS_FOUND)
		{
475
			P_AddWadFile(fileneeded[i].filename);
Alam Ed Arias committed
476 477 478 479
			G_SetGameModified(true);
			fileneeded[i].status = FS_OPEN;
		}
		else if (fileneeded[i].status == FS_MD5SUMBAD)
480 481
			I_Error("Wrong version of file %s", fileneeded[i].filename);
		else
482
		{
LJ Sonic committed
483
			const char *s;
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
			switch(fileneeded[i].status)
			{
			case FS_NOTFOUND:
				s = "FS_NOTFOUND";
				break;
			case FS_REQUESTED:
				s = "FS_REQUESTED";
				break;
			case FS_DOWNLOADING:
				s = "FS_DOWNLOADING";
				break;
			default:
				s = "unknown";
				break;
			}
			I_Error("Try to load file \"%s\" with status of %d (%s)\n", fileneeded[i].filename,
				fileneeded[i].status, s);
		}
Alam Ed Arias committed
502 503 504
	}
}

LJ Sonic committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
void AddLuaFileTransfer(const char *filename, const char *mode)
{
	luafiletransfer_t **prevnext; // A pointer to the "next" field of the last transfer in the list
	luafiletransfer_t *filetransfer;
	static INT32 id;

	// Find the last transfer in the list and set a pointer to its "next" field
	prevnext = &luafiletransfers;
	while (*prevnext)
		prevnext = &((*prevnext)->next);

	// Allocate file transfer information and append it to the transfer list
	filetransfer = malloc(sizeof(luafiletransfer_t));
	if (!filetransfer)
		I_Error("AddLuaFileTransfer: Out of memory\n");
	*prevnext = filetransfer;
LJ Sonic committed
521
	filetransfer->next = NULL;
LJ Sonic committed
522

LJ Sonic committed
523 524 525
	// Allocate the file name
	filetransfer->filename = strdup(filename);
	if (!filetransfer->filename)
LJ Sonic committed
526 527
		I_Error("AddLuaFileTransfer: Out of memory\n");

LJ Sonic committed
528
	// Create and allocate the real file name
LJ Sonic committed
529 530 531 532
	if (server)
		filetransfer->realfilename = strdup(va("%s" PATHSEP "%s",
												luafiledir, filename));
	else
533
		filetransfer->realfilename = strdup(va("%s" PATHSEP "client" PATHSEP "$$$%d%d.tmp",
LJ Sonic committed
534
												luafiledir, rand(), rand()));
LJ Sonic committed
535
	if (!filetransfer->realfilename)
LJ Sonic committed
536 537 538 539
		I_Error("AddLuaFileTransfer: Out of memory\n");

	strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode));

540 541 542
	// Only if there is no transfer already going on
	if (server && filetransfer == luafiletransfers)
		SV_PrepareSendLuaFile();
543 544
	else
		filetransfer->ongoing = false;
LJ Sonic committed
545 546 547 548 549 550 551 552 553 554 555 556 557

	// Store the callback so it can be called once everyone has the file
	filetransfer->id = id;
	StoreLuaFileCallback(id);
	id++;

	if (waitingforluafiletransfer)
	{
		waitingforluafiletransfer = false;
		CL_PrepareDownloadLuaFile();
	}
}

558
static void SV_PrepareSendLuaFileToNextNode(void)
LJ Sonic committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
{
	INT32 i;
	UINT8 success = 1;

    // Find a client to send the file to
	for (i = 1; i < MAXNETNODES; i++)
		if (nodeingame[i] && luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting
		{
			// Tell the client we're about to send them the file
			netbuffer->packettype = PT_SENDINGLUAFILE;
			if (!HSendPacket(i, true, 0, 0))
				I_Error("Failed to send a PT_SENDINGLUAFILE packet\n"); // !!! Todo: Handle failure a bit better lol

			luafiletransfers->nodestatus[i] = LFTNS_ASKED;

			return;
		}

	// No client found, everyone has the file
	// Send a net command with 1 as its first byte to indicate the file could be opened
	SendNetXCmd(XD_LUAFILE, &success, 1);
}
581 582 583

void SV_PrepareSendLuaFile(void)
{
584
	char *binfilename;
585 586
	INT32 i;

587 588
	luafiletransfers->ongoing = true;

589 590 591 592 593
	// Set status to "waiting" for everyone
	for (i = 0; i < MAXNETNODES; i++)
		luafiletransfers->nodestatus[i] = LFTNS_WAITING;

	if (FIL_ReadFileOK(luafiletransfers->realfilename))
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
	{
		// If opening in text mode, convert all newlines to LF
		if (!strchr(luafiletransfers->mode, 'b'))
		{
			binfilename = strdup(va("%s" PATHSEP "$$$%d%d.tmp",
				luafiledir, rand(), rand()));
			if (!binfilename)
				I_Error("SV_PrepareSendLuaFile: Out of memory\n");

			if (!FIL_ConvertTextFileToBinary(luafiletransfers->realfilename, binfilename))
				I_Error("SV_PrepareSendLuaFile: Failed to convert file newlines\n");

			// Use the temporary file instead
			free(luafiletransfers->realfilename);
			luafiletransfers->realfilename = binfilename;
		}

611
		SV_PrepareSendLuaFileToNextNode();
612
	}
613 614 615 616 617 618 619
	else
	{
		// Send a net command with 0 as its first byte to indicate the file couldn't be opened
		UINT8 success = 0;
		SendNetXCmd(XD_LUAFILE, &success, 1);
	}
}
LJ Sonic committed
620 621 622 623

void SV_HandleLuaFileSent(UINT8 node)
{
	luafiletransfers->nodestatus[node] = LFTNS_SENT;
LJ Sonic committed
624
	SV_PrepareSendLuaFileToNextNode();
LJ Sonic committed
625 626 627 628 629 630
}

void RemoveLuaFileTransfer(void)
{
	luafiletransfer_t *filetransfer = luafiletransfers;

631 632 633 634
	// If it was a temporary file, delete it
	if (server && !strchr(filetransfer->mode, 'b'))
		remove(filetransfer->realfilename);

LJ Sonic committed
635 636 637 638 639 640 641 642 643
	RemoveLuaFileCallback(filetransfer->id);

	luafiletransfers = filetransfer->next;

	free(filetransfer->filename);
	free(filetransfer->realfilename);
	free(filetransfer);
}

LJ Sonic committed
644
void RemoveAllLuaFileTransfers(void)
LJ Sonic committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
{
	while (luafiletransfers)
		RemoveLuaFileTransfer();
}

void SV_AbortLuaFileTransfer(INT32 node)
{
	if (luafiletransfers
	&& (luafiletransfers->nodestatus[node] == LFTNS_ASKED
	||  luafiletransfers->nodestatus[node] == LFTNS_SENDING))
	{
		luafiletransfers->nodestatus[node] = LFTNS_WAITING;
		SV_PrepareSendLuaFileToNextNode();
	}
}

void CL_PrepareDownloadLuaFile(void)
{
	// If there is no transfer in the list, this normally means the server
	// called io.open before us, so we have to wait until we call it too
	if (!luafiletransfers)
	{
		waitingforluafiletransfer = true;
		return;
	}

671 672 673 674 675 676
	if (luafiletransfers->ongoing)
	{
		waitingforluafilecommand = true;
		return;
	}

LJ Sonic committed
677 678 679 680 681 682
	// Tell the server we are ready to receive the file
	netbuffer->packettype = PT_ASKLUAFILE;
	HSendPacket(servernode, true, 0, 0);

	fileneedednum = 1;
	fileneeded[0].status = FS_REQUESTED;
LJ Sonic committed
683
	fileneeded[0].justdownloaded = false;
LJ Sonic committed
684 685 686 687 688 689 690
	fileneeded[0].totalsize = UINT32_MAX;
	fileneeded[0].file = NULL;
	memset(fileneeded[0].md5sum, 0, 16);
	strcpy(fileneeded[0].filename, luafiletransfers->realfilename);

	// Make sure all directories in the file path exist
	MakePathDirs(fileneeded[0].filename);
691 692

	luafiletransfers->ongoing = true;
LJ Sonic committed
693 694
}

695 696 697
// Number of files to send
// Little optimization to quickly test if there is a file in the queue
static INT32 filestosend = 0;
Alam Ed Arias committed
698

699 700 701 702
/** Adds a file to the file list for a node
  *
  * \param node The node to send the file to
  * \param filename The file to send
703
  * \param fileid The index of the file in the list of added files
704 705
  * \sa AddRamToSendQueue
  * \sa AddLuaFileToSendQueue
706 707
  *
  */
708
static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid)
Alam Ed Arias committed
709
{
710 711
	filetx_t **q; // A pointer to the "next" field of the last file in the list
	filetx_t *p; // The new file request
Alam Ed Arias committed
712 713 714
	INT32 i;
	char wadfilename[MAX_WADPATH];

715
	if (cv_noticedownload.value)
716
		CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node));
717

718
	// Find the last file in the list and set a pointer to its "next" field
Alam Ed Arias committed
719 720 721
	q = &transfer[node].txlist;
	while (*q)
		q = &((*q)->next);
722 723

	// Allocate a file request and append it to the file list
Alam Ed Arias committed
724
	p = *q = (filetx_t *)malloc(sizeof (filetx_t));
725
	if (!p)
726
		I_Error("AddFileToSendQueue: No more memory\n");
727 728 729 730 731 732 733

	// Initialise with zeros
	memset(p, 0, sizeof (filetx_t));

	// Allocate the file name
	p->id.filename = (char *)malloc(MAX_WADPATH);
	if (!p->id.filename)
734
		I_Error("AddFileToSendQueue: No more memory\n");
Alam Ed Arias committed
735

736 737 738
	// Set the file name and get rid of the path
	strlcpy(p->id.filename, filename, MAX_WADPATH);
	nameonly(p->id.filename);
Alam Ed Arias committed
739

740
	// Look for the requested file through all loaded files
Alam Ed Arias committed
741 742 743 744
	for (i = 0; wadfiles[i]; i++)
	{
		strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
		nameonly(wadfilename);
745
		if (!stricmp(wadfilename, p->id.filename))
Alam Ed Arias committed
746
		{
747 748
			// Copy file name with full path
			strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH);
Alam Ed Arias committed
749 750 751 752
			break;
		}
	}

753
	// Handle non-loaded file requests
Alam Ed Arias committed
754 755 756
	if (!wadfiles[i])
	{
		DEBFILE(va("%s not found in wadfiles\n", filename));
757
		// This formerly checked if (!findfile(p->id.filename, NULL, true))
Alam Ed Arias committed
758

759 760
		// Not found
		// Don't inform client (probably someone who thought they could leak 2.2 ACZ)
Alam Ed Arias committed
761
		DEBFILE(va("Client %d request %s: not found\n", node, filename));
762
		free(p->id.filename);
Alam Ed Arias committed
763 764
		free(p);
		*q = NULL;
765
		return false; // cancel the rest of the requests
Alam Ed Arias committed
766 767
	}

768
	// Handle huge file requests (i.e. bigger than cv_maxsend.value KB)
Alam Ed Arias committed
769 770
	if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)
	{
771 772
		// Too big
		// Don't inform client (client sucks, man)
Alam Ed Arias committed
773
		DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
774
		free(p->id.filename);
Alam Ed Arias committed
775 776
		free(p);
		*q = NULL;
777
		return false; // cancel the rest of the requests
Alam Ed Arias committed
778 779 780
	}

	DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node));
781
	p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
Alam Ed Arias committed
782
	p->fileid = fileid;
783 784
	p->next = NULL; // End of list
	filestosend++;
785
	return true;
Alam Ed Arias committed
786 787
}

788 789 790 791 792 793
/** Adds a memory block to the file list for a node
  *
  * \param node The node to send the memory block to
  * \param data The memory block to send
  * \param size The size of the block in bytes
  * \param freemethod How to free the block after it has been sent
794
  * \param fileid The index of the file in the list of added files
795 796
  * \sa AddFileToSendQueue
  * \sa AddLuaFileToSendQueue
797 798
  *
  */
799
void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
Alam Ed Arias committed
800
{
801 802
	filetx_t **q; // A pointer to the "next" field of the last file in the list
	filetx_t *p; // The new file request
Alam Ed Arias committed
803

804
	// Find the last file in the list and set a pointer to its "next" field
Alam Ed Arias committed
805 806 807
	q = &transfer[node].txlist;
	while (*q)
		q = &((*q)->next);
808 809

	// Allocate a file request and append it to the file list
Alam Ed Arias committed
810
	p = *q = (filetx_t *)malloc(sizeof (filetx_t));
811
	if (!p)
812
		I_Error("AddRamToSendQueue: No more memory\n");
813 814 815 816 817 818

	// Initialise with zeros
	memset(p, 0, sizeof (filetx_t));

	p->ram = freemethod; // Remember how to free the memory block for when we're done sending it
	p->id.ram = data;
Alam Ed Arias committed
819 820
	p->size = (UINT32)size;
	p->fileid = fileid;
821
	p->next = NULL; // End of list
Alam Ed Arias committed
822

823
	DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->id.ram,p->size,node,fileid));
Alam Ed Arias committed
824

825
	filestosend++;
Alam Ed Arias committed
826 827
}

LJ Sonic committed
828 829 830 831
/** Adds a file requested by Lua to the file list for a node
  *
  * \param node The node to send the file to
  * \param filename The file to send
832 833
  * \sa AddFileToSendQueue
  * \sa AddRamToSendQueue
LJ Sonic committed
834 835
  *
  */
836
boolean AddLuaFileToSendQueue(INT32 node, const char *filename)
LJ Sonic committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
{
	filetx_t **q; // A pointer to the "next" field of the last file in the list
	filetx_t *p; // The new file request
	//INT32 i;
	//char wadfilename[MAX_WADPATH];

	luafiletransfers->nodestatus[node] = LFTNS_SENDING;

	// Find the last file in the list and set a pointer to its "next" field
	q = &transfer[node].txlist;
	while (*q)
		q = &((*q)->next);

	// Allocate a file request and append it to the file list
	p = *q = (filetx_t *)malloc(sizeof (filetx_t));
	if (!p)
853
		I_Error("AddLuaFileToSendQueue: No more memory\n");
LJ Sonic committed
854 855 856 857 858 859 860

	// Initialise with zeros
	memset(p, 0, sizeof (filetx_t));

	// Allocate the file name
	p->id.filename = (char *)malloc(MAX_WADPATH); // !!!
	if (!p->id.filename)
861
		I_Error("AddLuaFileToSendQueue: No more memory\n");
LJ Sonic committed
862 863 864 865 866 867 868 869 870 871 872 873

	// Set the file name and get rid of the path
	strlcpy(p->id.filename, filename, MAX_WADPATH); // !!!
	//nameonly(p->id.filename);

	DEBFILE(va("Sending Lua file %s to %d\n", filename, node));
	p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
	p->next = NULL; // End of list
	filestosend++;
	return true;
}

874 875 876 877 878 879 880
/** Stops sending a file for a node, and removes the file request from the list,
  * either because the file has been fully sent or because the node was disconnected
  *
  * \param node The destination
  *
  */
static void SV_EndFileSend(INT32 node)
Alam Ed Arias committed
881 882
{
	filetx_t *p = transfer[node].txlist;
883

884 885
	// Free the file request according to the freemethod
	// parameter used with AddFileToSendQueue/AddRamToSendQueue
Alam Ed Arias committed
886 887
	switch (p->ram)
	{
888
		case SF_FILE: // It's a file, close it and free its filename
889 890
			if (cv_noticedownload.value)
				CONS_Printf("Ending file transfer for node %d\n", node);
Alam Ed Arias committed
891 892
			if (transfer[node].currentfile)
				fclose(transfer[node].currentfile);
893
			free(p->id.filename);
Alam Ed Arias committed
894
			break;
895 896
		case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free
			Z_Free(p->id.ram);
Alam Ed Arias committed
897
			break;
898 899 900
		case SF_RAM: // It's a memory block allocated with malloc, use free
			free(p->id.ram);
		case SF_NOFREERAM: // Nothing to free
Alam Ed Arias committed
901 902
			break;
	}
903 904

	// Remove the file request from the list
Alam Ed Arias committed
905 906
	transfer[node].txlist = p->next;
	free(p);
907 908 909

	// Indicate that the transmission is over
	transfer[node].currentfile = NULL;
LJ Sonic committed
910 911 912
	if (transfer[node].ackedfragments)
		free(transfer[node].ackedfragments);
	transfer[node].ackedfragments = NULL;
913 914

	filestosend--;
Alam Ed Arias committed
915 916 917
}

#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
LJ Sonic committed
918
#define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE))
Alam Ed Arias committed
919

920 921 922
/** Handles file transmission
  *
  */
923
void FileSendTicker(void)
Alam Ed Arias committed
924 925 926
{
	static INT32 currentnode = 0;
	filetx_pak *p;
LJ Sonic committed
927
	size_t fragmentsize;
Alam Ed Arias committed
928
	filetx_t *f;
929
	INT32 packetsent, ram, i, j;
Alam Ed Arias committed
930

931
	if (!filestosend) // No file to send
Alam Ed Arias committed
932
		return;
933

LJ Sonic committed
934
	if (cv_downloadspeed.value) // New behavior
935 936 937 938 939 940 941 942 943 944
		packetsent = cv_downloadspeed.value;
	else // Old behavior
	{
		packetsent = PACKETPERTIC;
		if (!packetsent)
			packetsent = 1;
	}

	netbuffer->packettype = PT_FILEFRAGMENT;

Alam Ed Arias committed
945
	// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
946
	while (packetsent-- && filestosend != 0)
Alam Ed Arias committed
947
	{
948 949
		for (i = currentnode, j = 0; j < MAXNETNODES;
			i = (i+1) % MAXNETNODES, j++)
Alam Ed Arias committed
950 951
		{
			if (transfer[i].txlist)
LJ Sonic committed
952
				break;
Alam Ed Arias committed
953 954
		}
		// no transfer to do
LJ Sonic committed
955 956 957
		if (j >= MAXNETNODES)
			I_Error("filestosend=%d but no file to send found\n", filestosend);

Alam Ed Arias committed
958 959 960 961
		currentnode = (i+1) % MAXNETNODES;
		f = transfer[i].txlist;
		ram = f->ram;

962 963
		// Open the file if it isn't open yet, or
		if (!transfer[i].currentfile)
Alam Ed Arias committed
964
		{
965
			if (!ram) // Sending a file
Alam Ed Arias committed
966 967 968 969
			{
				long filesize;

				transfer[i].currentfile =
970
					fopen(f->id.filename, "rb");
Alam Ed Arias committed
971 972 973

				if (!transfer[i].currentfile)
					I_Error("File %s does not exist",
974
						f->id.filename);
Alam Ed Arias committed
975 976 977 978 979 980 981

				fseek(transfer[i].currentfile, 0, SEEK_END);
				filesize = ftell(transfer[i].currentfile);

				// Nobody wants to transfer a file bigger
				// than 4GB!
				if (filesize >= LONG_MAX)
982 983 984
					I_Error("filesize of %s is too large", f->id.filename);
				if (filesize == -1)
					I_Error("Error getting filesize of %s", f->id.filename);
Alam Ed Arias committed
985 986 987 988

				f->size = (UINT32)filesize;
				fseek(transfer[i].currentfile, 0, SEEK_SET);
			}
989 990
			else // Sending RAM
				transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
LJ Sonic committed
991

992 993
			transfer[i].iteration = 1;
			transfer[i].ackediteration = 0;
Alam Ed Arias committed
994
			transfer[i].position = 0;
LJ Sonic committed
995 996 997 998 999
			transfer[i].ackedsize = 0;

			transfer[i].ackedfragments = calloc(f->size / FILEFRAGMENTSIZE + 1, sizeof(*transfer[i].ackedfragments));
			if (!transfer[i].ackedfragments)
				I_Error("FileSendTicker: No more memory\n");
1000 1001

			transfer[i].dontsenduntil = 0;
LJ Sonic committed
1002 1003
		}

1004 1005 1006 1007 1008 1009
		// If the client hasn't acknowledged any fragment from the previous iteration,
		// it is most likely because their acks haven't had enough time to reach the server
		// yet, due to latency. In that case, we wait a little to avoid useless resend.
		if (I_GetTime() < transfer[i].dontsenduntil)
			continue;

LJ Sonic committed
1010 1011 1012 1013 1014
		// Find the first non-acknowledged fragment
		while (transfer[i].ackedfragments[transfer[i].position / FILEFRAGMENTSIZE])
		{
			transfer[i].position += FILEFRAGMENTSIZE;
			if (transfer[i].position >= f->size)
1015 1016 1017 1018
			{
				if (transfer[i].ackediteration < transfer[i].iteration)
					transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;

LJ Sonic committed
1019
				transfer[i].position = 0;
1020 1021
				transfer[i].iteration++;
			}
Alam Ed Arias committed
1022 1023
		}

1024
		// Build a packet containing a file fragment
Alam Ed Arias committed
1025
		p = &netbuffer->u.filetxpak;
LJ Sonic committed
1026
		fragmentsize = FILEFRAGMENTSIZE;
LJ Sonic committed
1027 1028
		if (f->size-transfer[i].position < fragmentsize)
			fragmentsize = f->size-transfer[i].position;
Alam Ed Arias committed
1029
		if (ram)
LJ Sonic committed
1030
			M_Memcpy(p->data, &f->id.ram[transfer[i].position], fragmentsize);
LJ Sonic committed
1031 1032
		else
		{
LJ Sonic committed
1033 1034
			fseek(transfer[i].currentfile, transfer[i].position, SEEK_SET);

1035 1036
			if (fread(p->data, 1, fragmentsize, transfer[i].currentfile) != fragmentsize)
				I_Error("FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(fragmentsize), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
LJ Sonic committed
1037
		}
1038
		p->iteration = transfer[i].iteration;
Alam Ed Arias committed
1039 1040
		p->position = LONG(transfer[i].position);
		p->fileid = f->fileid;
LJ Sonic committed
1041 1042
		p->filesize = LONG(f->size);
		p->size = SHORT((UINT16)FILEFRAGMENTSIZE);
1043 1044

		// Send the packet
LJ Sonic committed
1045
		if (HSendPacket(i, false, 0, FILETXHEADER + fragmentsize)) // Don't use the default acknowledgement system
1046
		{ // Success
LJ Sonic committed
1047
			transfer[i].position = (UINT32)(transfer[i].position + fragmentsize);
LJ Sonic committed
1048
			if (transfer[i].position >= f->size)
1049 1050 1051 1052
			{
				if (transfer[i].ackediteration < transfer[i].iteration)
					transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;

LJ Sonic committed
1053
				transfer[i].position = 0;
1054 1055
				transfer[i].iteration++;
			}
1056 1057 1058 1059
		}
		else
		{ // Not sent for some odd reason, retry at next call
			// Exit the while (can't send this one so why should i send the next?)
Alam Ed Arias committed
1060 1061 1062 1063 1064
			break;
		}
	}
}

LJ Sonic committed
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
void PT_FileAck(void)
{
	fileack_pak *packet = &netbuffer->u.fileack;
	INT32 node = doomcom->remotenode;
	filetran_t *trans = &transfer[node];
	INT32 i, j;

	// Wrong file id? Ignore it, it's probably a late packet
	if (!(trans->txlist && packet->fileid == trans->txlist->fileid))
		return;

	if (packet->numsegments * sizeof(*packet->segments) != doomcom->datalength - BASEPACKETSIZE - sizeof(*packet))
	{
		Net_CloseConnection(node);
		return;
	}

1082 1083 1084 1085 1086 1087 1088
	if (packet->iteration > trans->ackediteration)
	{
		trans->ackediteration = packet->iteration;
		if (trans->ackediteration >= trans->iteration - 1)
			trans->dontsenduntil = 0;
	}

LJ Sonic committed
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
	for (i = 0; i < packet->numsegments; i++)
	{
		fileacksegment_t *segment = &packet->segments[i];

		for (j = 0; j < 32; j++)
			if (LONG(segment->acks) & (1 << j))
			{
				if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size)
				{
					Net_CloseConnection(node);
					return;
				}

				if (!trans->ackedfragments[LONG(segment->start) + j])
				{
					trans->ackedfragments[LONG(segment->start) + j] = true;
					trans->ackedsize += FILEFRAGMENTSIZE;

					// If the last missing fragment was acked, finish!
					if (trans->ackedsize == trans->txlist->size)
					{
						SV_EndFileSend(node);
						return;
					}
				}
			}
	}
}

void PT_FileReceived(void)
{
	filetx_t *trans = transfer[doomcom->remotenode].txlist;

	if (trans && netbuffer->u.filereceived == trans->fileid)
		SV_EndFileSend(doomcom->remotenode);
}

static void SendAckPacket(fileack_pak *packet, UINT8 fileid)
{
	size_t packetsize;
	INT32 i;

	packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments);

	// Finalise the packet
	packet->fileid = fileid;
	for (i = 0; i < packet->numsegments; i++)
	{
		packet->segments[i].start = LONG(packet->segments[i].start);
		packet->segments[i].acks = LONG(packet->segments[i].acks);
	}

	// Send the packet
	netbuffer->packettype = PT_FILEACK;
	M_Memcpy(&netbuffer->u.fileack, packet, packetsize);
	HSendPacket(servernode, false, 0, packetsize);

	// Clear the packet
	memset(packet, 0, sizeof(*packet) + 512);
}

1150
static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32 fragmentpos, UINT8 fileid)
LJ Sonic committed
1151 1152 1153
{
	fileacksegment_t *segment = &packet->segments[packet->numsegments - 1];

1154 1155
	packet->iteration = max(packet->iteration, iteration);

LJ Sonic committed
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
    if (packet->numsegments == 0
		|| fragmentpos < segment->start
		|| fragmentpos - segment->start >= 32)
	{
		// If the packet becomes too big, send it
		if ((packet->numsegments + 1) * sizeof(*segment) > 512)
			SendAckPacket(packet, fileid);

		packet->numsegments++;
		segment = &packet->segments[packet->numsegments - 1];
		segment->start = fragmentpos;
	}

	// Set the bit that represents the fragment
	segment->acks |= 1 << (fragmentpos - segment->start);
}

void FileReceiveTicker(void)
{
	INT32 i;

1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
	for (i = 0; i < fileneedednum; i++)
	{
		fileneeded_t *file = &fileneeded[i];

		if (file->status == FS_DOWNLOADING)
		{
			if (lasttimeackpacketsent - I_GetTime() > TICRATE / 2)
				SendAckPacket(file->ackpacket, i);

			// When resuming a tranfer, start with telling
			// the server what parts we already received
			if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING)
LJ Sonic committed
1189
			{
1190 1191 1192 1193 1194
				// Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s
				INT32 j;
				for (j = 0; j < 2048; j++)
				{
					if (file->receivedfragments[file->ackresendposition])
1195
						AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i);
1196 1197 1198 1199 1200 1201 1202 1203

					file->ackresendposition++;
					if (file->ackresendposition * file->fragmentsize >= file->totalsize)
					{
						file->ackresendposition = UINT32_MAX;
						break;
					}
				}
LJ Sonic committed
1204
			}
1205 1206
		}
	}
LJ Sonic committed
1207 1208
}

1209
void PT_FileFragment(void)
Alam Ed Arias committed
1210 1211
{
	INT32 filenum = netbuffer->u.filetxpak.fileid;
1212
	fileneeded_t *file = &fileneeded[filenum];
LJ Sonic committed
1213 1214 1215
	UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position);
	UINT16 fragmentsize = SHORT(netbuffer->u.filetxpak.size);
	UINT16 boundedfragmentsize = doomcom->datalength - BASEPACKETSIZE - sizeof(netbuffer->u.filetxpak);
1216
	char *filename;
Alam Ed Arias committed
1217

1218 1219 1220
	filename = va("%s", file->filename);
	nameonly(filename);

Nev3r committed
1221
	if (!(strcmp(filename, "srb2.pk3")
1222
		&& strcmp(filename, "zones.pk3")
1223
		&& strcmp(filename, "player.dta")
1224
		&& strcmp(filename, "patch.pk3")
1225 1226 1227 1228
		&& strcmp(filename, "music.dta")
		))
		I_Error("Tried to download \"%s\"", filename);

1229 1230
	filename = file->filename;

Alam Ed Arias committed
1231 1232
	if (filenum >= fileneedednum)
	{
1233
		DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum));
1234
		//I_Error("Received an unneeded file fragment (file id received: %d, file id needed: %d)\n", filenum, fileneedednum);
Alam Ed Arias committed
1235 1236 1237
		return;
	}

1238
	if (file->status == FS_REQUESTED)
Alam Ed Arias committed
1239
	{
1240
		if (file->file)
1241
			I_Error("PT_FileFragment: already open file\n");
LJ Sonic committed
1242

1243
		file->status = FS_DOWNLOADING;
1244
		file->fragmentsize = fragmentsize;
1245
		file->iteration = 0;
LJ Sonic committed
1246 1247

		file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512);
1248
		if (!file->ackpacket)
LJ Sonic committed
1249
			I_Error("FileSendTicker: No more memory\n");
1250 1251 1252

		if (CL_CanResumeDownload(file))
		{
1253
			file->file = fopen(filename, "r+b");
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
			if (!file->file)
				I_Error("Can't reopen file %s: %s", filename, strerror(errno));
			CONS_Printf("\r%s...\n", filename);

			CONS_Printf("Resuming download...\n");
			file->currentsize = pauseddownload->currentsize;
			file->receivedfragments = pauseddownload->receivedfragments;
			file->ackresendposition = 0;

			free(pauseddownload);
			pauseddownload = NULL;
		}
		else
		{
			CL_AbortDownloadResume();

1270
			file->file = fopen(filename, "wb");
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
			if (!file->file)
				I_Error("Can't create file %s: %s", filename, strerror(errno));

			CONS_Printf("\r%s...\n",filename);

			file->currentsize = 0;
			file->totalsize = LONG(netbuffer->u.filetxpak.filesize);
			file->ackresendposition = UINT32_MAX; // Only used for resumed downloads

			file->receivedfragments = calloc(file->totalsize / fragmentsize + 1, sizeof(*file->receivedfragments));
			if (!file->receivedfragments)
				I_Error("FileSendTicker: No more memory\n");
		}

LJ Sonic committed
1285
		lasttimeackpacketsent = I_GetTime();
Alam Ed Arias committed
1286 1287
	}

1288
	if (file->status == FS_DOWNLOADING)
Alam Ed Arias committed
1289
	{
LJ Sonic committed
1290 1291 1292
		if (fragmentpos >= file->totalsize)
			I_Error("Invalid file fragment\n");

1293 1294
		file->iteration = max(file->iteration, netbuffer->u.filetxpak.iteration);

LJ Sonic committed
1295
		if (!file->receivedfragments[fragmentpos / fragmentsize]) // Not received yet
Alam Ed Arias committed
1296
		{
LJ Sonic committed
1297 1298 1299 1300 1301 1302 1303 1304
			file->receivedfragments[fragmentpos / fragmentsize] = true;

			// We can receive packets in the wrong order, anyway all OSes support gaped files
			fseek(file->file, fragmentpos, SEEK_SET);
			if (fragmentsize && fwrite(netbuffer->u.filetxpak.data, boundedfragmentsize, 1, file->file) != 1)
				I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
			file->currentsize += boundedfragmentsize;

1305
			AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum);
LJ Sonic committed
1306 1307 1308

			// Finished?
			if (file->currentsize == file->totalsize)
LJ Sonic committed
1309
			{
LJ Sonic committed
1310 1311 1312 1313 1314 1315 1316 1317 1318
				fclose(file->file);
				file->file = NULL;
				free(file->receivedfragments);
				free(file->ackpacket);
				file->status = FS_FOUND;
				file->justdownloaded = true;
				CONS_Printf(M_GetText("Downloading %s...(done)\n"),
					filename);

LJ Sonic committed
1319
				// Tell the server we have received the file
LJ Sonic committed
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
				netbuffer->packettype = PT_FILERECEIVED;
				netbuffer->u.filereceived = filenum;
				HSendPacket(servernode, true, 0, 1);

				if (luafiletransfers)
				{
					// Tell the server we have received the file
					netbuffer->packettype = PT_HASLUAFILE;
					HSendPacket(servernode, true, 0, 0);
				}
LJ Sonic committed
1330
			}
Alam Ed Arias committed
1331
		}
LJ Sonic committed
1332 1333 1334 1335
		else // Already received
		{
			// If they are sending us the fragment again, it's probably because
			// they missed our previous ack, so we must re-acknowledge it
1336
			AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum);
LJ Sonic committed
1337
		}
Alam Ed Arias committed
1338
	}
LJ Sonic committed
1339
	else if (!file->justdownloaded)
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
	{
		const char *s;
		switch(file->status)
		{
		case FS_NOTFOUND:
			s = "FS_NOTFOUND";
			break;
		case FS_FOUND:
			s = "FS_FOUND";
			break;
		case FS_OPEN:
			s = "FS_OPEN";
			break;
		case FS_MD5SUMBAD:
			s = "FS_MD5SUMBAD";
			break;
		default:
			s = "unknown";
			break;
		}
		I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s);
	}
Alam Ed Arias committed
1362

1363
#ifndef NONET
Alam Ed Arias committed
1364 1365 1366 1367
	lastfilenum = filenum;
#endif
}

1368
/** \brief Checks if a node is downloading a file
1369
 *
1370 1371
 * \param node The node to check for
 * \return True if the node is downloading a file
1372 1373
 *
 */
1374
boolean SendingFile(INT32 node)
1375 1376 1377 1378 1379 1380 1381 1382 1383 1384
{
	return transfer[node].txlist != NULL;
}

/** Cancels all file requests for a node
  *
  * \param node The destination
  * \sa SV_EndFileSend
  *
  */
1385
void SV_AbortSendFiles(INT32 node)
Alam Ed Arias committed
1386 1387
{
	while (transfer[node].txlist)
1388
		SV_EndFileSend(node);
Alam Ed Arias committed
1389 1390 1391 1392 1393
}

void CloseNetFile(void)
{
	INT32 i;
1394
	// Is sending?
Alam Ed Arias committed
1395
	for (i = 0; i < MAXNETNODES; i++)
1396
		SV_AbortSendFiles(i);
Alam Ed Arias committed
1397

1398
	// Receiving a file?
Alam Ed Arias committed
1399
	for (i = 0; i < MAX_WADFILES; i++)
1400
		if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
Alam Ed Arias committed
1401
		{
1402
			fclose(fileneeded[i].file);
LJ Sonic committed
1403
			free(fileneeded[i].ackpacket);
Alam Ed Arias committed
1404

1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
			if (!pauseddownload && i != 0) // 0 is either srb2.srb or the gamestate...
			{
				// Don't remove the file, save it for later in case we resume the download
				pauseddownload = malloc(sizeof(*pauseddownload));
				if (!pauseddownload)
					I_Error("CloseNetFile: No more memory\n");

				strcpy(pauseddownload->filename, fileneeded[i].filename);
				memcpy(pauseddownload->md5sum, fileneeded[i].md5sum, 16);
				pauseddownload->currentsize = fileneeded[i].currentsize;
				pauseddownload->receivedfragments = fileneeded[i].receivedfragments;
				pauseddownload->fragmentsize = fileneeded[i].fragmentsize;
			}
			else
			{
				free(fileneeded[i].receivedfragments);
				// File is not complete delete it
				remove(fileneeded[i].filename);
			}
		}
Alam Ed Arias committed
1425 1426
}

1427 1428 1429 1430 1431 1432 1433 1434 1435
void Command_Downloads_f(void)
{
	INT32 node;

	for (node = 0; node < MAXNETNODES; node++)
		if (transfer[node].txlist
		&& transfer[node].txlist->ram == SF_FILE) // Node is downloading a file?
		{
			const char *name = transfer[node].txlist->id.filename;
1436
			UINT32 position = transfer[node].ackedsize;
1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
			UINT32 size = transfer[node].txlist->size;
			char ratecolor;

			// Avoid division by zero errors
			if (!size)
				size = 1;

			name = &name[strlen(name) - nameonlylength(name)];
			switch (4 * (position - 1) / size)
			{
				case 0: ratecolor = '\x85'; break;
				case 1: ratecolor = '\x87'; break;
				case 2: ratecolor = '\x82'; break;
				case 3: ratecolor = '\x83'; break;
				default: ratecolor = '\x80';
			}

			CONS_Printf("%2d  %c%s  ", node, ratecolor, name); // Node and file name
			CONS_Printf("\x80%uK\x84/\x80%uK ", position / 1024, size / 1024); // Progress in kB
			CONS_Printf("\x80(%c%u%%\x80)  ", ratecolor, (UINT32)(100.0 * position / size)); // Progress in %
			CONS_Printf("%s\n", I_GetNodeAddress(node)); // Address and newline
		}
}

1461
// Functions cut and pasted from Doomatic :)
Alam Ed Arias committed
1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472

void nameonly(char *s)
{
	size_t j, len;
	void *ns;

	for (j = strlen(s); j != (size_t)-1; j--)
		if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
		{
			ns = &(s[j+1]);
			len = strlen(ns);
1473 1474 1475 1476 1477
#if 0
			M_Memcpy(s, ns, len+1);
#else
			memmove(s, ns, len+1);
#endif
Alam Ed Arias committed
1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499
			return;
		}
}

// Returns the length in characters of the last element of a path.
size_t nameonlylength(const char *s)
{
	size_t j, len = strlen(s);

	for (j = len; j != (size_t)-1; j--)
		if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
			return len - j - 1;

	return len;
}

#ifndef O_BINARY
#define O_BINARY 0
#endif

filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum)
{
1500
#if defined (NOMD5)
Alam Ed Arias committed
1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524
	(void)wantedmd5sum;
	(void)filename;
#else
	FILE *fhandle;
	UINT8 md5sum[16];

	if (!wantedmd5sum)
		return FS_FOUND;

	fhandle = fopen(filename, "rb");
	if (fhandle)
	{
		md5_stream(fhandle,md5sum);
		fclose(fhandle);
		if (!memcmp(wantedmd5sum, md5sum, 16))
			return FS_FOUND;
		return FS_MD5SUMBAD;
	}

	I_Error("Couldn't open %s for md5 check", filename);
#endif
	return FS_FOUND; // will never happen, but makes the compiler shut up
}

1525 1526 1527
// Rewritten by Monster Iestyn to be less stupid
// Note: if completepath is true, "filename" is modified, but only if FS_FOUND is going to be returned
// (Don't worry about WinCE's version of filesearch, nobody cares about that OS anymore)
Alam Ed Arias committed
1528 1529
filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean completepath)
{
1530 1531
	filestatus_t homecheck; // store result of last file search
	boolean badmd5 = false; // store whether md5 was bad from either of the first two searches (if nothing was found in the third)
Alam Ed Arias committed
1532

1533 1534
	// first, check SRB2's "home" directory
	homecheck = filesearch(filename, srb2home, wantedmd5sum, completepath, 10);
Alam Ed Arias committed
1535

1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557
	if (homecheck == FS_FOUND) // we found the file, so return that we have :)
		return FS_FOUND;
	else if (homecheck == FS_MD5SUMBAD) // file has a bad md5; move on and look for a file with the right md5
		badmd5 = true;
	// if not found at all, just move on without doing anything

	// next, check SRB2's "path" directory
	homecheck = filesearch(filename, srb2path, wantedmd5sum, completepath, 10);

	if (homecheck == FS_FOUND) // we found the file, so return that we have :)
		return FS_FOUND;
	else if (homecheck == FS_MD5SUMBAD) // file has a bad md5; move on and look for a file with the right md5
		badmd5 = true;
	// if not found at all, just move on without doing anything

	// finally check "." directory
	homecheck = filesearch(filename, ".", wantedmd5sum, completepath, 10);

	if (homecheck != FS_NOTFOUND) // if not found this time, fall back on the below return statement
		return homecheck; // otherwise return the result we got

	return (badmd5 ? FS_MD5SUMBAD : FS_NOTFOUND); // md5 sum bad or file not found
Alam Ed Arias committed
1558
}