/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ /* * $Logfile: /Freespace2/code/Network/multi_xfer.cpp $ * $Revision: 2.9 $ * $Date: 2005-10-10 17:21:07 $ * $Author: taylor $ * * $Log: not supported by cvs2svn $ * Revision 2.8 2005/07/13 03:35:33 Goober5000 * remove PreProcDefine #includes in FS2 * --Goober5000 * * Revision 2.7 2005/03/02 21:18:20 taylor * better support for Inferno builds (in PreProcDefines.h now, no networking support) * make sure NO_NETWORK builds are as friendly on Windows as it is on Linux/OSX * revert a timeout in Client.h back to the original value before Linux merge * * Revision 2.6 2005/02/04 10:12:31 taylor * merge with Linux/OSX tree - p0204 * * Revision 2.5 2004/07/26 20:47:42 Kazan * remove MCD complete * * Revision 2.4 2004/07/12 16:32:57 Kazan * MCD - define _MCD_CHECK to use memory tracking * * Revision 2.3 2004/03/05 09:02:02 Goober5000 * Uber pass at reducing #includes * --Goober5000 * * Revision 2.2 2002/08/01 01:41:08 penguin * The big include file move * * Revision 2.1 2002/07/07 19:55:59 penguin * Back-port to MSVC * * Revision 2.0 2002/06/03 04:02:26 penguin * Warpcore CVS sync * * Revision 1.1 2002/05/02 18:03:11 mharris * Initial checkin - converted filenames and includes to lower case * * * 11 3/10/99 6:50p Dave * Changed the way we buffer packets for all clients. Optimized turret * fired packets. Did some weapon firing optimizations. * * 10 3/09/99 6:24p Dave * More work on object update revamping. Identified several sources of * unnecessary bandwidth. * * 9 1/24/99 11:37p Dave * First full rev of beam weapons. Very customizable. Removed some bogus * Int3()'s in low level net code. * * 8 12/16/98 11:17a Dave * Fixed potential situation where a send and receive to the same player * with the same sig might get confused with each other. * * 7 12/14/98 4:01p Dave * Got multi_data stuff working well with new xfer stuff. * * 6 12/14/98 12:13p Dave * Spiffed up xfer system a bit. Put in support for squad logo file xfer. * Need to test now. * * 5 11/19/98 8:03a Dave * Full support for D3-style reliable sockets. Revamped packet lag/loss * system, made it receiver side and at the lowest possible level. * * 4 11/17/98 11:12a Dave * Removed player identification by address. Now assign explicit id #'s. * * 3 11/05/98 5:55p Dave * Big pass at reducing #includes * * 2 10/07/98 10:53a Dave * Initial checkin. * * 1 10/07/98 10:50a Dave * * 56 9/11/98 9:31a Dave * Fixed file xfer bug regarding checksums. * * 55 8/12/98 4:53p Dave * Put in 32 bit checksumming for PXO missions. No validation on the * actual tracker yet, though. * * 54 6/13/98 9:32p Mike * Kill last character in file which caused "Find in Files" to report the * file as "not a text file." * * 53 6/13/98 6:01p Hoffoss * Externalized all new (or forgot to be added) strings to all the code. * * 52 6/13/98 3:19p Hoffoss * NOX()ed out a bunch of strings that shouldn't be translated. * * 51 5/21/98 1:52a Dave * Remove obsolete command line functions. Reduce shield explosion packets * drastically. Tweak PXO screen even more. Fix file xfer system so that * we can guarantee file uniqueness. * * 50 4/30/98 4:53p John * Restructured and cleaned up cfile code. Added capability to read off * of CD-ROM drive and out of multiple pack files. * * 49 4/23/98 6:18p Dave * Store ETS values between respawns. Put kick feature in the text * messaging system. Fixed text messaging system so that it doesn't * process or trigger ship controls. Other UI fixes. * * 48 4/22/98 5:53p Dave * Large reworking of endgame sequencing. Updated multi host options * screen for new artwork. Put in checks for host or team captains leaving * midgame. * * 47 4/21/98 4:44p Dave * Implement Vasudan ships in multiplayer. Added a debug function to bash * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui * problem in options screen. * * 46 4/20/98 6:04p Dave * Implement multidata cache flushing and xferring mission files to * multidata. Make sure observers can't change hud config. Fix pilot image * viewing in popup. Put in game status field. Tweaked multi options. * * 45 4/08/98 2:51p Dave * Fixed pilot image xfer once again. Solidify team selection process in * pre-briefing multiplayer. * * 44 4/04/98 4:22p Dave * First rev of UDP reliable sockets is done. Seems to work well if not * overly burdened. * * 43 4/03/98 1:03a Dave * First pass at unreliable guaranteed delivery packets. * * 42 4/01/98 11:19p Dave * Put in auto-loading of xferred pilot pic files. Grey out background * behind pinfo popup. Put a chatbox message in when players are kicked. * Moved mission title down in briefing. Other ui fixes. * * 41 3/31/98 4:51p Dave * Removed medals screen and multiplayer buttons from demo version. Put in * new pilot popup screen. Make ships in mp team vs. team have proper team * ids. Make mp respawns a permanent option saved in the player file. * * 40 3/30/98 8:46a Allender * fix an optimized build compiler warning * * 39 3/26/98 6:01p Dave * Put in file checksumming routine in cfile. Made pilot pic xferring more * robust. Cut header size of voice data packets in half. Put in * restricted game host query system. * * 38 3/25/98 2:16p Dave * Select random default image for newly created pilots. Fixed several * multi-pause messaging bugs. Begin work on online help for multiplayer * keys. * * 37 3/24/98 5:00p Dave * Fixed several ui bugs. Put in pre and post voice stream playback sound * fx. Put in error specific popups for clients getting dropped from games * through actions other than their own. * * 36 3/23/98 5:42p Dave * Put in automatic xfer of pilot pic files. Changed multi_xfer system so * that it can support multiplayer sends/received between client and * server simultaneously. * * 35 3/21/98 7:14p Dave * Fixed up standalone player slot switching. Made training missions not * count towards player stats. * * 34 3/16/98 11:52p Allender * Put in timestamp updates when processing data on both sender and * receiver sides. * * 33 2/22/98 2:53p Dave * Put in groundwork for advanced multiplayer campaign options. * * 32 2/20/98 4:43p Dave * Finished support for multiplayer player data files. Split off * multiplayer campaign functionality. * * 31 2/19/98 6:26p Dave * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in * initial support for player data uploading. * * 30 2/18/98 10:21p Dave * Ripped out old file xfer system. Put in brand new xfer system. * * $NoKeywords: $ */ #include "network/multi_xfer.h" #include "network/multi.h" #include "network/multimsgs.h" #include "network/psnet.h" #include "io/timer.h" #include "cfile/cfile.h" #ifndef NDEBUG #include "playerman/player.h" #include "network/multiutil.h" #include "network/multi_log.h" #endif // ------------------------------------------------------------------------------------------ // MULTI XFER DEFINES/VARS // #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output #define MULTI_XFER_INVALID_HANDLE(handle) ( (handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) || (strlen(Multi_xfer_entry[handle].filename) <= 0) ) // packet codes #define MULTI_XFER_CODE_ACK 0 // simple response to the last request #define MULTI_XFER_CODE_NAK 1 // simple response to the last request #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack // entry flags #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending // packet size for file xfer #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT // timeout for a given xfer operation #define MULTI_XFER_TIMEOUT 10000 //XSTR:OFF // temp filename header for xferring files #define MULTI_XFER_FNAME_PREFIX "_fsx_" //XSTR:ON // xfer entries/events #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries typedef struct xfer_entry { int flags; // status flags for this entry char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front CFILE *file; // file handle of the current xferring file int file_size; // total size of the file being xferred int file_ptr; // total bytes we're received so far ushort file_chksum; // used for checking successfully xferred files PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file int xfer_stamp; // timestamp for the current operation int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir) ushort sig; // identifying sig - sender specifies this } xfer_entry; xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves // callback function pointer for when we start receiving a file void (*Multi_xfer_recv_notify)(int handle); // lock for the xfer system int Multi_xfer_locked; // force directory type for receives int Multi_xfer_force_dir; // unique file signature - this along with a socket # is enough to identify all xfers ushort Multi_xfer_sig = 0; // ------------------------------------------------------------------------------------------ // MULTI XFER FORWARD DECLARATIONS // // evaluate the condition of the entry void multi_xfer_eval_entry(xfer_entry *xe); // set an entry to be "failed" void multi_xfer_fail_entry(xfer_entry *xe); // get a valid xfer entry handle int multi_xfer_get_free_handle(); // process an ack for this entry void multi_xfer_process_ack(xfer_entry *xe); // process a nak for this entry void multi_xfer_process_nak(xfer_entry *xe); // process a "final" packet void multi_xfer_process_final(xfer_entry *xe); // process a data packet void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size); // process a header void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum); // send the next block of outgoing data or a "final" packet if we're done void multi_xfer_send_next(xfer_entry *xe); // send an ack to the sender void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig); // send a nak to the sender void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig); // send a "final" packet void multi_xfer_send_final(xfer_entry *xe); // send the header to begin a file transfer void multi_xfer_send_header(xfer_entry *xe); // convert the filename into the prefixed ex_filename void multi_xfer_conv_prefix(char *filename, char *ex_filename); // get a new xfer sig ushort multi_xfer_get_sig(); // ------------------------------------------------------------------------------------------ // MULTI XFER FUNCTIONS // // initialize all file xfer transaction stuff, call in multi_level_init() void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle)) { // blast all the entries memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES); // assign the receive callback function pointer Multi_xfer_recv_notify = multi_xfer_recv_callback; // unlocked Multi_xfer_locked = 0; // no forced directory Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE; } // do frame for all file xfers, call in multi_do_frame() void multi_xfer_do() { int idx; // process all valid xfer entries for(idx=0;idx (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){ return MULTI_XFER_NONE; } // if the xfer has timed-out if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){ return MULTI_XFER_TIMEDOUT; } // if the xfer has failed for one reason or another (not timeout) if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){ return MULTI_XFER_FAIL; } // if the xfer has succeeded if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){ return MULTI_XFER_SUCCESS; } // if the xfer is queued if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){ return MULTI_XFER_QUEUED; } // otherwise the xfer is still in progress return MULTI_XFER_IN_PROGRESS; } // abort a transferring file void multi_xfer_abort(int handle) { xfer_entry *xe; // don't do anything if this is an invalid handle if(MULTI_XFER_INVALID_HANDLE(handle)){ return; } // get e handle to the entry xe = &Multi_xfer_entry[handle]; // close any open file and delete it if(xe->file != NULL){ cfclose(xe->file); xe->file = NULL; // delete it if there isn't some problem with the filename if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){ cf_delete(xe->ex_filename, xe->force_dir); } } // zero the socket xe->file_socket = INVALID_SOCKET; // blast the entry memset(xe,0,sizeof(xfer_entry)); } // release an xfer handle void multi_xfer_release_handle(int handle) { xfer_entry *xe; // don't do anything if this is an invalid handle if(MULTI_XFER_INVALID_HANDLE(handle)){ return; } // get e handle to the entry xe = &Multi_xfer_entry[handle]; // close any open file and delete it if(xe->file != NULL){ cfclose(xe->file); xe->file = NULL; // delete it if the file was not successfully received if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){ cf_delete(xe->ex_filename,xe->force_dir); } } // zero the socket xe->file_socket = INVALID_SOCKET; // blast the entry memset(xe,0,sizeof(xfer_entry)); } // get the filename of the xfer for the given handle char *multi_xfer_get_filename(int handle) { // if this is an invalid handle, return NULL if(MULTI_XFER_INVALID_HANDLE(handle)){ return NULL; } // otherwise return the string return Multi_xfer_entry[handle].filename; } // lock the xfer system (don't accept incoming files, don't allow outgoing files) void multi_xfer_lock() { Multi_xfer_locked = 1; } // unlock the xfer system void multi_xfer_unlock() { Multi_xfer_locked = 0; } // force all receives to go into the specified directory by cfile type void multi_xfer_force_dir(int cf_type) { Multi_xfer_force_dir = cf_type; Assert(Multi_xfer_force_dir > CF_TYPE_ANY); } // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function) void multi_xfer_handle_force_dir(int handle,int cf_type) { // if this is an invalid handle, return NULL if(MULTI_XFER_INVALID_HANDLE(handle)){ return; } // force to go to the given directory Multi_xfer_entry[handle].force_dir = cf_type; Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY); } // or the flag on a given entry void multi_xfer_xor_flags(int handle,int flags) { // if this is an invalid handle, return NULL if(MULTI_XFER_INVALID_HANDLE(handle)){ return; } // xor the flags Multi_xfer_entry[handle].flags ^= flags; } // get the flags for a given entry int multi_xfer_get_flags(int handle) { // if this is an invalid handle, return NULL if(MULTI_XFER_INVALID_HANDLE(handle)){ return -1; } // return the flags return Multi_xfer_entry[handle].flags; } // if the passed filename is being xferred, return the xfer handle, otherwise return -1 int multi_xfer_lookup(char *filename) { int idx; // if we have an invalid filename, do nothing if((filename == NULL) || (strlen(filename) <= 0)){ return 0; } // otherwise, perform a lookup for(idx=0;idxflags & MULTI_XFER_FLAG_SUCCESS){ return; } // if the entry is queued if(xe->flags & MULTI_XFER_FLAG_QUEUE){ // if the entry is not current if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){ // see if there are any other queued up xfers to this target. if not, make me current and start sending found = 0; for(idx=0; idxflags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){ found = 1; break; } } // if we found no other entries, make this guy current and pending if(!found){ xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT; xe->flags |= MULTI_XFER_FLAG_PENDING; #ifdef MULTI_XFER_VERBOSE nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename)); #endif } // otherwise, do nothing for him - he has to still wait else { return; } } } // if the entry is marked as pending - send out the header to get the ball rolling if(xe->flags & MULTI_XFER_FLAG_PENDING){ // send the header to begin the transfer multi_xfer_send_header(xe); // set the timestamp xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); // unset the pending flag xe->flags &= ~(MULTI_XFER_FLAG_PENDING); // set the ack/wait flag xe->flags |= MULTI_XFER_FLAG_WAIT_ACK; } // see if the entry has timed-out for one reason or another if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){ xe->flags |= MULTI_XFER_FLAG_TIMEOUT; // if we should be auto-destroying this entry, do so if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){ multi_xfer_fail_entry(xe); } } } // lookup a file xfer entry by player xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side) { int idx; // look through all valid xfer entries for(idx=0;idxflags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN); xe->flags |= MULTI_XFER_FLAG_FAIL; // close the file pointer if(xe->file != NULL){ cfclose(xe->file); xe->file = NULL; } // delete the file if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){ cf_delete(xe->ex_filename,xe->force_dir); } // null the timestamp xe->xfer_stamp = -1; // if we should be auto-destroying this entry, do so if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){ multi_xfer_release_handle(xe - Multi_xfer_entry); } // blast the memory clean memset(xe,0,sizeof(xfer_entry)); } // get a valid xfer entry handle int multi_xfer_get_free_handle() { int idx; // look for a free entry for(idx=0;idxcallsign); } if(sender_side){ ml_string(": sending"); } else { ml_string(": receiving"); } #endif #endif // Int3(); return offset; } } switch((int)val){ // process an ack for this entry case MULTI_XFER_CODE_ACK : Assert(xe != NULL); multi_xfer_process_ack(xe); break; // process a nak for this entry case MULTI_XFER_CODE_NAK : Assert(xe != NULL); multi_xfer_process_nak(xe); break; // process a "final" packet case MULTI_XFER_CODE_FINAL : Assert(xe != NULL); multi_xfer_process_final(xe); break; // process a data packet case MULTI_XFER_CODE_DATA : Assert(xe != NULL); multi_xfer_process_data(xe, xfer_data, data_size); break; // process a header case MULTI_XFER_CODE_HEADER : // send on my reliable socket multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum); break; } return offset; } // process an ack for this entry void multi_xfer_process_ack(xfer_entry *xe) { // if we are a sender if(xe->flags & MULTI_XFER_FLAG_SEND){ // if we are waiting on a final ack, then the transfer has completed successfully if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){ xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN); xe->flags |= MULTI_XFER_FLAG_SUCCESS; #ifdef MULTI_XFER_VERBOSE nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename)); #endif // if we should be auto-destroying this entry, do so if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){ multi_xfer_release_handle(xe - Multi_xfer_entry); } } // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){ multi_xfer_send_next(xe); } } } // process a nak for this entry void multi_xfer_process_nak(xfer_entry *xe) { // if we get an ack at any time we should simply set the xfer to failed multi_xfer_fail_entry(xe); } // process a "final" packet void multi_xfer_process_final(xfer_entry *xe) { ushort chksum; // make sure we skip a line nprintf(("Network","\n")); // close the file if(xe->file != NULL){ cflush(xe->file); cfclose(xe->file); xe->file = NULL; } // check to make sure the file checksum is the same chksum = 0; if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){ // mark as failed xe->flags |= MULTI_XFER_FLAG_FAIL; #ifdef MULTI_XFER_VERBOSE nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum)); #endif // abort the xfer multi_xfer_abort(xe - Multi_xfer_entry); return; } // checksums check out, so rename the file and be done with it else { #ifdef MULTI_XFER_VERBOSE nprintf(("Network","MULTI XFER : renaming xferred file from %s to %s (chksum %d %d)\n", xe->ex_filename, xe->filename, (int)xe->file_chksum, (int)chksum)); #endif // rename the file properly if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){ // mark the xfer as being successful xe->flags |= MULTI_XFER_FLAG_SUCCESS; nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size)); // send an ack to the sender multi_xfer_send_ack(xe->file_socket, xe->sig); } else { // mark it as failing xe->flags |= MULTI_XFER_FLAG_FAIL; nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename)); // delete the tempfile cf_delete(xe->ex_filename, xe->force_dir); // send an nak to the sender multi_xfer_send_nak(xe->file_socket, xe->sig); } // if we should be auto-destroying this entry, do so if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){ multi_xfer_release_handle(xe - Multi_xfer_entry); } } } // process a data packet void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size) { // print out a crude progress indicator nprintf(("Network",".")); // attempt to write the rest of the data string to the file if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){ // inform the sender we had a problem multi_xfer_send_nak(xe->file_socket, xe->sig); // fail this entry multi_xfer_fail_entry(xe); xe->file_ptr += data_size; return; } // increment the file pointer xe->file_ptr += data_size; // send an ack to the sender multi_xfer_send_ack(xe->file_socket, xe->sig); // set the timestmp xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); } // process a header, return bytes processed void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum) { xfer_entry *xe; int handle; // if the xfer system is locked, send a nak if(Multi_xfer_locked){ multi_xfer_send_nak(who, sig); return; } // try and get a free xfer handle handle = multi_xfer_get_free_handle(); if(handle == -1){ multi_xfer_send_nak(who, sig); return; } else { xe = &Multi_xfer_entry[handle]; memset(xe,0,sizeof(xfer_entry)); } // set the recv and used flags xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV); // get the header data xe->file_size = file_size; // get the file chksum xe->file_chksum = file_checksum; // set the socket xe->file_socket = who; // set the timeout timestamp xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); // set the sig xe->sig = sig; // copy the filename and get the prefixed xfer filename strcpy(xe->filename, filename); multi_xfer_conv_prefix(xe->filename, xe->ex_filename); #ifdef MULTI_XFER_VERBOSE nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename)); #endif // determine what directory to place the file in // individual xfer entries take precedence over the global multi xfer force entry xe->force_dir = Multi_xfer_force_dir; // call the callback function Multi_xfer_recv_notify(handle); // if the notify function invalidated this xfer handle, then cancel the whole thing if(xe->flags & MULTI_XFER_FLAG_REJECT){ multi_xfer_send_nak(who, sig); // clear the data memset(xe, 0, sizeof(xfer_entry)); return; } // delete the old file (if it exists) cf_delete( xe->filename, CF_TYPE_MULTI_CACHE ); cf_delete( xe->filename, CF_TYPE_MISSIONS ); // attempt to open the file (using the prefixed filename) xe->file = NULL; xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir); if(xe->file == NULL){ multi_xfer_send_nak(who, sig); // clear the data memset(xe, 0, sizeof(xfer_entry)); return; } // set the waiting for data flag xe->flags |= MULTI_XFER_FLAG_WAIT_DATA; // send an ack to the server multi_xfer_send_ack(who, sig); #ifdef MULTI_XFER_VERBOSE nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename)); #endif } // send the next block of outgoing data or a "final" packet if we're done void multi_xfer_send_next(xfer_entry *xe) { ubyte data[MAX_PACKET_SIZE],code; ushort data_size; int flen; int packet_size = 0; // print out a crude progress indicator nprintf(("Network", "+")); // if we've sent all the data, then we should send a "final" packet if(xe->file_ptr >= xe->file_size){ // mark the entry as unknown xe->flags |= MULTI_XFER_FLAG_UNKNOWN; // set the timestmp xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); // send the packet multi_xfer_send_final(xe); return; } // build the header BUILD_HEADER(XFER_PACKET); // length of the added string flen = strlen(xe->filename) + 4; // determine how much data we are going to send with this packet and add it in if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){ data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen); } else { data_size = (unsigned short)(xe->file_size - xe->file_ptr); } // increment the file pointer xe->file_ptr += data_size; // add the opcode code = MULTI_XFER_CODE_DATA; ADD_DATA(code); // add the sig ADD_USHORT(xe->sig); // add in the size of the rest of the packet ADD_USHORT(data_size); // copy in the data if(cfread(data+packet_size,1,(int)data_size,xe->file) == 0){ // send a nack to the receiver multi_xfer_send_nak(xe->file_socket, xe->sig); // fail this send multi_xfer_fail_entry(xe); return; } // increment the packet size packet_size += (int)data_size; // set the timestmp xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); // otherwise send the data psnet_rel_send(xe->file_socket, data, packet_size); } // send an ack to the sender void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig) { ubyte data[MAX_PACKET_SIZE],code; int packet_size = 0; // build the header and add BUILD_HEADER(XFER_PACKET); // add the opcode code = MULTI_XFER_CODE_ACK; ADD_DATA(code); // add the sig ADD_USHORT(sig); // send the data psnet_rel_send(socket, data, packet_size); } // send a nak to the sender void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig) { ubyte data[MAX_PACKET_SIZE],code; int packet_size = 0; // build the header and add the code BUILD_HEADER(XFER_PACKET); // add the opcode code = MULTI_XFER_CODE_NAK; ADD_DATA(code); // add the sig ADD_USHORT(sig); // send the data psnet_rel_send(socket, data, packet_size); } // send a "final" packet void multi_xfer_send_final(xfer_entry *xe) { ubyte data[MAX_PACKET_SIZE],code; int packet_size = 0; // build the header BUILD_HEADER(XFER_PACKET); // add the code code = MULTI_XFER_CODE_FINAL; ADD_DATA(code); // add the sig ADD_USHORT(xe->sig); // send the data psnet_rel_send(xe->file_socket, data, packet_size); } // send the header to begin a file transfer void multi_xfer_send_header(xfer_entry *xe) { ubyte data[MAX_PACKET_SIZE],code; int packet_size = 0; // build the header and add the opcode BUILD_HEADER(XFER_PACKET); code = MULTI_XFER_CODE_HEADER; ADD_DATA(code); // add the sig ADD_USHORT(xe->sig); // add the filename ADD_STRING(xe->filename); // add the id # ADD_INT(xe->file_size); // add the file checksum ADD_USHORT(xe->file_chksum); // send the packet psnet_rel_send(xe->file_socket, data, packet_size); } // convert the filename into the prefixed ex_filename void multi_xfer_conv_prefix(char *filename,char *ex_filename) { char temp[MAX_FILENAME_LEN+50]; // blast the memory clean memset(temp, 0, MAX_FILENAME_LEN+50); // copy in the prefix strcpy(temp, MULTI_XFER_FNAME_PREFIX); // stick on the original name strcat(temp, filename); // copy the whole thing to the outgoing filename strcpy(ex_filename, temp); } // get a new xfer sig ushort multi_xfer_get_sig() { ushort ret = Multi_xfer_sig; // new one if(Multi_xfer_sig == 0xffff){ Multi_xfer_sig = 0; } else { Multi_xfer_sig++; } return ret; }