Skip to content
Snippets Groups Projects
Select Git revision
  • db606d0111f627c8638b44c00e000a70cb1510cb
  • next default protected
  • next-test
  • classic-netcode-fixes
  • fix-dedi-pthread
  • fix-enemy-target
  • master protected
  • better-distance-math
  • movie
  • softcode-info
  • acs
  • clipmidtex
  • custom-map-names
  • nogravity-trampolines
  • 2214-pre4
  • 2214-pre3
  • just-in-case
  • fix-opengl-parameter-crash
  • 2214-pre2
  • 2214-pre1
  • delfile2
  • SRB2_release_2.2.15
  • SRB2_release_2.2.13
  • SRB2_release_2.2.12
  • SRB2_release_2.2.11
  • SRB2_release_2.2.10
  • SRB2_release_2.2.9
  • SRB2_release_2.2.8
  • SRB2_release_2.2.7
  • SRB2_release_2.2.6
  • SRB2_release_2.2.5
  • SRB2_release_2.2.4
  • SRB2_release_2.2.3
  • SRB2_release_2.2.2
  • SRB2_release_2.2.1
  • SRB2_release_2.2.0
  • SRB2_release_2.1.25
  • SRB2_release_2.1.24
  • SRB2_release_2.1.23
  • SRB2_release_2.1.22
  • SRB2_release_2.1.21
41 results

makefile

Blame
  • d_netfil.c 21.21 KiB
    // SONIC ROBO BLAST 2
    //-----------------------------------------------------------------------------
    // Copyright (C) 1998-2000 by DooM Legacy Team.
    // Copyright (C) 1999-2014 by Sonic Team Junior.
    //
    // 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>
    #ifndef _WIN32_WCE
    #ifdef __OS2__
    #include <sys/types.h>
    #endif // __OS2__
    #include <sys/stat.h>
    #endif
    
    #if !defined (UNDER_CE)
    #include <time.h>
    #endif
    
    #if ((defined (_WIN32) && !defined (_WIN32_WCE)) || defined (__DJGPP__)) && !defined (_XBOX)
    #include <io.h>
    #include <direct.h>
    #elif !defined (_WIN32_WCE) && !(defined (_XBOX) && !defined (__GNUC__))
    #include <sys/types.h>
    #include <dirent.h>
    #include <utime.h>
    #endif
    
    #ifdef __GNUC__
    #include <unistd.h>
    #include <limits.h>
    #elif defined (_WIN32) && !defined (_WIN32_WCE)
    #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"
    
    #include <errno.h>
    
    static void SendFile(INT32 node, const char *filename, UINT8 fileid);
    
    // sender structure
    typedef struct filetx_s
    {
    	INT32 ram;
    	char *filename; // name of the file or ptr of the data in ram
    	UINT32 size;
    	UINT8 fileid;
    	INT32 node; // destination
    	struct filetx_s *next; // a queue
    } filetx_t;
    
    // current transfers (one for each node)
    typedef struct filetran_s
    {
    	filetx_t *txlist;
    	UINT32 position;
    	FILE *currentfile;
    } filetran_t;
    static filetran_t transfer[MAXNETNODES];
    
    // read time of file: stat _stmtime
    // write time of file: utime
    
    // receiver structure
    INT32 fileneedednum;
    fileneeded_t fileneeded[MAX_WADFILES];
    char downloaddir[256] = "DOWNLOAD";
    
    #ifdef CLIENT_LOADINGSCREEN
    // for cl loading screen
    INT32 lastfilenum = 0;
    #endif
    
    /** Fills a serverinfo packet with information about wad files loaded.
      *
      * \todo Give this function a better name since it is in global scope.
      */
    UINT8 *PutFileNeeded(void)
    {
    	size_t i, count = 0;
    	UINT8 *p = netbuffer->u.serverinfo.fileneeded;
    	char wadfilename[MAX_WADPATH] = "";
    	UINT8 filestatus;
    	size_t bytesused = 0;
    
    	for (i = 0; i < numwadfiles; i++)
    	{
    		// if it has only music/sound lumps, mark it as unimportant
    		if (W_VerifyNMUSlumps(wadfiles[i]->filename))
    			filestatus = 0;
    		else
    			filestatus = 1; // important
    
    		// Store in the upper four bits
    		if (!cv_downloading.value)
    			filestatus += (2 << 4); // won't send
    		else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024))
    			filestatus += (0 << 4); // won't send
    		else
    			filestatus += (1 << 4); // will send if requested
    
    		bytesused += (nameonlylength(wadfilename) + 22);
    
    		// Don't write too far...
    		if (bytesused > sizeof(netbuffer->u.serverinfo.fileneeded))
    			I_Error("Too many wad files added to host a game. (%s, stopped on %s)\n", sizeu1(bytesused), wadfilename);
    
    		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;
    }
    
    // parse the serverinfo packet and fill fileneeded table on client
    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++)
    	{
    		fileneeded[i].status = FS_NOTFOUND;
    		filestatus = READUINT8(p);
    		fileneeded[i].important = (UINT8)(filestatus & 3);
    		fileneeded[i].willsend = (UINT8)(filestatus >> 4);
    		fileneeded[i].totalsize = READUINT32(p);
    		fileneeded[i].phandle = NULL;
    		READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
    		READMEM(p, fileneeded[i].md5sum, 16);
    	}
    }
    
    void CL_PrepareDownloadSaveGame(const char *tmpsave)
    {
    	fileneedednum = 1;
    	fileneeded[0].status = FS_REQUESTED;
    	fileneeded[0].totalsize = UINT32_MAX;
    	fileneeded[0].phandle = NULL;
    	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.
      */
    boolean CL_CheckDownloadable(void)
    {
    	UINT8 i,dlstatus = 0;
    
    	for (i = 0; i < fileneedednum; i++)
    		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && fileneeded[i].important)
    		{
    			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++)
    		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && fileneeded[i].important)
    		{
    			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;
    }
    
    /** Send requests for files in the ::fileneeded table with a status of
      * ::FS_NOTFOUND.
      */
    boolean CL_SendRequestFile(void)
    {
    	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
    			&& fileneeded[i].important && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
    		{
    			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++)
    		if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
    			&& fileneeded[i].important)
    		{
    			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
    void Got_RequestFilePak(INT32 node)
    {
    	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);
    		SendFile(node, wad, id);
    	}
    }
    
    // client check if the fileneeded aren't already loaded or on the disk
    INT32 CL_CheckFiles(void)
    {
    	INT32 i, j;
    	char wadfilename[MAX_WADPATH];
    	INT32 ret = 1;
    
    //	if (M_CheckParm("-nofiles"))
    //		return 1;
    
    	// the first is the iwad (the main wad file)
    	// we don't care if it's called srb2.srb or srb2.wad.
    	// 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;)
    		{
    			if (i < fileneedednum && !fileneeded[i].important)
    			{
    				// Eh whatever, don't care
    				++i;
    				continue;
    			}
    			if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename))
    			{
    				// unimportant on our side. still don't care.
    				++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;
    
    			// for the sake of speed, only bother with a md5 check
    			if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
    				return 2;
    
    			// it's accounted for! let's keep going.
    			CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename);
    			fileneeded[i].status = FS_OPEN;
    			++i;
    			++j;
    		}
    		return 1;
    	}
    
    	for (i = 1; i < fileneedednum; i++)
    	{
    		CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
    
    		// check in allready loaded files
    		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;
    			}
    		}
    		if (fileneeded[i].status != FS_NOTFOUND || !fileneeded[i].important)
    			continue;
    
    		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;
    }
    
    // load it now
    void CL_LoadServerFiles(void)
    {
    	INT32 i;
    
    //	if (M_CheckParm("-nofiles"))
    //		return;
    
    	for (i = 1; i < fileneedednum; i++)
    	{
    		if (fileneeded[i].status == FS_OPEN)
    			continue; // already loaded
    		else if (fileneeded[i].status == FS_FOUND)
    		{
    			P_AddWadFile(fileneeded[i].filename, NULL);
    			G_SetGameModified(true);
    			fileneeded[i].status = FS_OPEN;
    		}
    		else if (fileneeded[i].status == FS_MD5SUMBAD)
    		{
    			// If the file is marked important, don't even bother proceeding.
    			if (fileneeded[i].important)
    				I_Error("Wrong version of important file %s", fileneeded[i].filename);
    
    			// If it isn't, no need to worry the user with a console message,
    			// although it can't hurt to put something in the debug file.
    
    			// ...but wait a second. What if the local version is "important"?
    			if (!W_VerifyNMUSlumps(fileneeded[i].filename))
    				I_Error("File %s should only contain music and sound effects!",
    					fileneeded[i].filename);
    
    			// Okay, NOW we know it's safe. Whew.
    			P_AddWadFile(fileneeded[i].filename, NULL);
    			if (fileneeded[i].important)
    				G_SetGameModified(true);
    			fileneeded[i].status = FS_OPEN;
    			DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename));
    		}
    		else if (fileneeded[i].important)
    			I_Error("Try to load file %s with status of %d\n", fileneeded[i].filename,
    				fileneeded[i].status);
    	}
    }
    
    // little optimization to test if there is a file in the queue
    static INT32 filetosend = 0;
    
    static void SendFile(INT32 node, const char *filename, UINT8 fileid)
    {
    	filetx_t **q;
    	filetx_t *p;
    	INT32 i;
    	char wadfilename[MAX_WADPATH];
    
    	q = &transfer[node].txlist;
    	while (*q)
    		q = &((*q)->next);
    	p = *q = (filetx_t *)malloc(sizeof (filetx_t));
    	if (p)
    		memset(p, 0, sizeof (filetx_t));
    	else
    		I_Error("SendFile: No more ram\n");
    	p->filename = (char *)malloc(MAX_WADPATH);
    	if (!p->filename)
    		I_Error("SendFile: No more ram\n");
    
    	// a minimum of security, can get only file in srb2 direcory
    	strlcpy(p->filename, filename, MAX_WADPATH);
    	nameonly(p->filename);
    
    	// check first in wads loaded the majority of case
    	for (i = 0; wadfiles[i]; i++)
    	{
    		strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
    		nameonly(wadfilename);
    		if (!stricmp(wadfilename, p->filename))
    		{
    			// copy filename with full path
    			strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH);
    			break;
    		}
    	}
    
    	if (!wadfiles[i])
    	{
    		DEBFILE(va("%s not found in wadfiles\n", filename));
    		// this formerly checked if (!findfile(p->filename, NULL, true))
    
    		// not found
    		// don't inform client (probably hacker)
    		DEBFILE(va("Client %d request %s: not found\n", node, filename));
    		free(p->filename);
    		free(p);
    		*q = NULL;
    		return;
    	}
    
    	if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)
    	{
    		// too big
    		// don't inform client (client sucks, man)
    		DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
    		free(p->filename);
    		free(p);
    		*q = NULL;
    		return;
    	}
    
    	DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node));
    	p->ram = SF_FILE;
    	p->fileid = fileid;
    	p->next = NULL; // end of list
    	filetosend++;
    }
    
    void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
    {
    	filetx_t **q;
    	filetx_t *p;
    
    	q = &transfer[node].txlist;
    	while (*q)
    		q = &((*q)->next);
    	p = *q = (filetx_t *)malloc(sizeof (filetx_t));
    	if (p)
    		memset(p, 0, sizeof (filetx_t));
    	else
    		I_Error("SendRam: No more ram\n");
    	p->ram = freemethod;
    	p->filename = data;
    	p->size = (UINT32)size;
    	p->fileid = fileid;
    	p->next = NULL; // end of list
    
    	DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->filename,p->size,node,fileid));
    
    	filetosend++;
    }
    
    static void EndSend(INT32 node)
    {
    	filetx_t *p = transfer[node].txlist;
    	switch (p->ram)
    	{
    		case SF_FILE:
    			if (transfer[node].currentfile)
    				fclose(transfer[node].currentfile);
    			free(p->filename);
    			break;
    		case SF_Z_RAM:
    			Z_Free(p->filename);
    			break;
    		case SF_RAM:
    			free(p->filename);
    		case SF_NOFREERAM:
    			break;
    	}
    	transfer[node].txlist = p->next;
    	transfer[node].currentfile = NULL;
    	free(p);
    	filetosend--;
    }
    
    #define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
    
    void FiletxTicker(void)
    {
    	static INT32 currentnode = 0;
    	filetx_pak *p;
    	size_t size;
    	filetx_t *f;
    	INT32 packetsent = PACKETPERTIC, ram, i;
    
    	if (!filetosend)
    		return;
    	if (!packetsent)
    		packetsent++;
    	// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
    	while (packetsent-- && filetosend != 0)
    	{
    		for (i = currentnode, ram = 0; ram < MAXNETNODES;
    			i = (i+1) % MAXNETNODES, ram++)
    		{
    			if (transfer[i].txlist)
    				goto found;
    		}
    		// no transfer to do
    		I_Error("filetosend=%d but no filetosend found\n", filetosend);
    	found:
    		currentnode = (i+1) % MAXNETNODES;
    		f = transfer[i].txlist;
    		ram = f->ram;
    
    		if (!transfer[i].currentfile) // file not already open
    		{
    			if (!ram)
    			{
    				long filesize;
    
    				transfer[i].currentfile =
    					fopen(f->filename, "rb");
    
    				if (!transfer[i].currentfile)
    					I_Error("File %s does not exist",
    						f->filename);
    
    				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)
    					I_Error("filesize of %s is too large", f->filename);
    				if (-1 == filesize)
    					I_Error("Error getting filesize of %s", f->filename);
    
    				f->size = (UINT32)filesize;
    				fseek(transfer[i].currentfile, 0, SEEK_SET);
    			}
    			else
    				transfer[i].currentfile = (FILE *)1;
    			transfer[i].position = 0;
    		}
    
    		p = &netbuffer->u.filetxpak;
    		size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
    		if (f->size-transfer[i].position < size)
    			size = f->size-transfer[i].position;
    		if (ram)
    			M_Memcpy(p->data, &f->filename[transfer[i].position], size);
    		else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
    			I_Error("FiletxTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
    		p->position = LONG(transfer[i].position);
    		// put flag so receiver know the totalsize
    		if (transfer[i].position + size == f->size)
    			p->position |= LONG(0x80000000);
    		p->fileid = f->fileid;
    		p->size = SHORT((UINT16)size);
    		netbuffer->packettype = PT_FILEFRAGMENT;
    		if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND
    		{ // not sent for some odd reason, retry at next call
    			if (!ram)
    				fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET);
    			// exit the while (can't send this one so why should i send the next?)
    			break;
    		}
    		else // success
    		{
    			transfer[i].position = (UINT32)(size+transfer[i].position);
    			if (transfer[i].position == f->size) //  finish ?
    				EndSend(i);
    		}
    	}
    }
    
    void Got_Filetxpak(void)
    {
    	INT32 filenum = netbuffer->u.filetxpak.fileid;
    	static INT32 filetime = 0;
    
    	if (filenum >= fileneedednum)
    	{
    		DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum));
    		return;
    	}
    
    	if (fileneeded[filenum].status == FS_REQUESTED)
    	{
    		if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n");
    			fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb");
    		if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno));
    			CONS_Printf("\r%s...\n",fileneeded[filenum].filename);
    		fileneeded[filenum].currentsize = 0;
    		fileneeded[filenum].status = FS_DOWNLOADING;
    	}
    
    	if (fileneeded[filenum].status == FS_DOWNLOADING)
    	{
    		UINT32 pos = LONG(netbuffer->u.filetxpak.position);
    		UINT16 size = SHORT(netbuffer->u.filetxpak.size);
    		// use a special tric to know when file is finished (not allways used)
    		// WARNING: filepak can arrive out of order so don't stop now !
    		if (pos & 0x80000000)
    		{
    			pos &= ~0x80000000;
    			fileneeded[filenum].totalsize = pos + size;
    		}
    		// we can receive packet in the wrong order, anyway all os support gaped file
    		fseek(fileneeded[filenum].phandle,pos,SEEK_SET);
    		if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1)
    			I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle)));
    		fileneeded[filenum].currentsize += size;
    
    		// finished?
    		if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize)
    		{
    			fclose(fileneeded[filenum].phandle);
    			fileneeded[filenum].phandle = NULL;
    			fileneeded[filenum].status = FS_FOUND;
    			CONS_Printf(M_GetText("Downloading %s...(done)\n"),
    				fileneeded[filenum].filename);
    		}
    	}
    	else
    		I_Error("Received a file not requested\n");
    	// send ack back quickly
    
    	if (++filetime == 3)
    	{
    		Net_SendAcks(servernode);
    		filetime = 0;
    	}
    
    #ifdef CLIENT_LOADINGSCREEN
    	lastfilenum = filenum;
    #endif
    }
    
    void AbortSendFiles(INT32 node)
    {
    	while (transfer[node].txlist)
    		EndSend(node);
    }
    
    void CloseNetFile(void)
    {
    	INT32 i;
    	// is sending?
    	for (i = 0; i < MAXNETNODES; i++)
    		AbortSendFiles(i);
    
    	// receiving a file?
    	for (i = 0; i < MAX_WADFILES; i++)
    		if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].phandle)
    		{
    			fclose(fileneeded[i].phandle);
    			// file is not complete delete it
    			remove(fileneeded[i].filename);
    		}
    
    	// remove FILEFRAGMENT from acknledge list
    	Net_AbortPacketType(PT_FILEFRAGMENT);
    }
    
    // functions cut and pasted from doomatic :)
    
    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);
    			if (false)
    				M_Memcpy(s, ns, len+1);
    			else
    				memmove(s, ns, len+1);
    			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)
    {
    #if defined (NOMD5) || defined (_arch_dreamcast)
    	(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
    }
    
    filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean completepath)
    {
    	filestatus_t homecheck = filesearch(filename, srb2home, wantedmd5sum, false, 10);
    	if (homecheck == FS_FOUND)
    		return filesearch(filename, srb2home, wantedmd5sum, completepath, 10);
    
    	homecheck = filesearch(filename, srb2path, wantedmd5sum, false, 10);
    	if (homecheck == FS_FOUND)
    		return filesearch(filename, srb2path, wantedmd5sum, completepath, 10);
    
    #ifdef _arch_dreamcast
    	return filesearch(filename, "/cd", wantedmd5sum, completepath, 10);
    #else
    	return filesearch(filename, ".", wantedmd5sum, completepath, 10);
    #endif
    }