2 * $Logfile: /Freespace2/code/Network/multi_xfer.cpp $
8 * Revision 1.1 2002/05/03 03:28:10 root
12 * 11 3/10/99 6:50p Dave
13 * Changed the way we buffer packets for all clients. Optimized turret
14 * fired packets. Did some weapon firing optimizations.
16 * 10 3/09/99 6:24p Dave
17 * More work on object update revamping. Identified several sources of
18 * unnecessary bandwidth.
20 * 9 1/24/99 11:37p Dave
21 * First full rev of beam weapons. Very customizable. Removed some bogus
22 * Int3()'s in low level net code.
24 * 8 12/16/98 11:17a Dave
25 * Fixed potential situation where a send and receive to the same player
26 * with the same sig might get confused with each other.
28 * 7 12/14/98 4:01p Dave
29 * Got multi_data stuff working well with new xfer stuff.
31 * 6 12/14/98 12:13p Dave
32 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
35 * 5 11/19/98 8:03a Dave
36 * Full support for D3-style reliable sockets. Revamped packet lag/loss
37 * system, made it receiver side and at the lowest possible level.
39 * 4 11/17/98 11:12a Dave
40 * Removed player identification by address. Now assign explicit id #'s.
42 * 3 11/05/98 5:55p Dave
43 * Big pass at reducing #includes
45 * 2 10/07/98 10:53a Dave
48 * 1 10/07/98 10:50a Dave
50 * 56 9/11/98 9:31a Dave
51 * Fixed file xfer bug regarding checksums.
53 * 55 8/12/98 4:53p Dave
54 * Put in 32 bit checksumming for PXO missions. No validation on the
55 * actual tracker yet, though.
57 * 54 6/13/98 9:32p Mike
58 * Kill last character in file which caused "Find in Files" to report the
59 * file as "not a text file."
61 * 53 6/13/98 6:01p Hoffoss
62 * Externalized all new (or forgot to be added) strings to all the code.
64 * 52 6/13/98 3:19p Hoffoss
65 * NOX()ed out a bunch of strings that shouldn't be translated.
67 * 51 5/21/98 1:52a Dave
68 * Remove obsolete command line functions. Reduce shield explosion packets
69 * drastically. Tweak PXO screen even more. Fix file xfer system so that
70 * we can guarantee file uniqueness.
72 * 50 4/30/98 4:53p John
73 * Restructured and cleaned up cfile code. Added capability to read off
74 * of CD-ROM drive and out of multiple pack files.
76 * 49 4/23/98 6:18p Dave
77 * Store ETS values between respawns. Put kick feature in the text
78 * messaging system. Fixed text messaging system so that it doesn't
79 * process or trigger ship controls. Other UI fixes.
81 * 48 4/22/98 5:53p Dave
82 * Large reworking of endgame sequencing. Updated multi host options
83 * screen for new artwork. Put in checks for host or team captains leaving
86 * 47 4/21/98 4:44p Dave
87 * Implement Vasudan ships in multiplayer. Added a debug function to bash
88 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
89 * problem in options screen.
91 * 46 4/20/98 6:04p Dave
92 * Implement multidata cache flushing and xferring mission files to
93 * multidata. Make sure observers can't change hud config. Fix pilot image
94 * viewing in popup. Put in game status field. Tweaked multi options.
96 * 45 4/08/98 2:51p Dave
97 * Fixed pilot image xfer once again. Solidify team selection process in
98 * pre-briefing multiplayer.
100 * 44 4/04/98 4:22p Dave
101 * First rev of UDP reliable sockets is done. Seems to work well if not
104 * 43 4/03/98 1:03a Dave
105 * First pass at unreliable guaranteed delivery packets.
107 * 42 4/01/98 11:19p Dave
108 * Put in auto-loading of xferred pilot pic files. Grey out background
109 * behind pinfo popup. Put a chatbox message in when players are kicked.
110 * Moved mission title down in briefing. Other ui fixes.
112 * 41 3/31/98 4:51p Dave
113 * Removed medals screen and multiplayer buttons from demo version. Put in
114 * new pilot popup screen. Make ships in mp team vs. team have proper team
115 * ids. Make mp respawns a permanent option saved in the player file.
117 * 40 3/30/98 8:46a Allender
118 * fix an optimized build compiler warning
120 * 39 3/26/98 6:01p Dave
121 * Put in file checksumming routine in cfile. Made pilot pic xferring more
122 * robust. Cut header size of voice data packets in half. Put in
123 * restricted game host query system.
125 * 38 3/25/98 2:16p Dave
126 * Select random default image for newly created pilots. Fixed several
127 * multi-pause messaging bugs. Begin work on online help for multiplayer
130 * 37 3/24/98 5:00p Dave
131 * Fixed several ui bugs. Put in pre and post voice stream playback sound
132 * fx. Put in error specific popups for clients getting dropped from games
133 * through actions other than their own.
135 * 36 3/23/98 5:42p Dave
136 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
137 * that it can support multiplayer sends/received between client and
138 * server simultaneously.
140 * 35 3/21/98 7:14p Dave
141 * Fixed up standalone player slot switching. Made training missions not
142 * count towards player stats.
144 * 34 3/16/98 11:52p Allender
145 * Put in timestamp updates when processing data on both sender and
148 * 33 2/22/98 2:53p Dave
149 * Put in groundwork for advanced multiplayer campaign options.
151 * 32 2/20/98 4:43p Dave
152 * Finished support for multiplayer player data files. Split off
153 * multiplayer campaign functionality.
155 * 31 2/19/98 6:26p Dave
156 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
157 * initial support for player data uploading.
159 * 30 2/18/98 10:21p Dave
160 * Ripped out old file xfer system. Put in brand new xfer system.
167 #include "multi_xfer.h"
169 #include "multimsgs.h"
172 #include "multi_endgame.h"
175 #include "multiutil.h"
176 #include "multi_log.h"
178 // ------------------------------------------------------------------------------------------
179 // MULTI XFER DEFINES/VARS
182 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
184 #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) )
187 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
188 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
189 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
190 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
191 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
194 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
195 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
196 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
197 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
198 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
199 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
200 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
201 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
202 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
203 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
204 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
206 // packet size for file xfer
207 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
209 // timeout for a given xfer operation
210 #define MULTI_XFER_TIMEOUT 10000
214 // temp filename header for xferring files
215 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
219 // xfer entries/events
220 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
221 typedef struct xfer_entry {
222 int flags; // status flags for this entry
223 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
224 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
225 CFILE *file; // file handle of the current xferring file
226 int file_size; // total size of the file being xferred
227 int file_ptr; // total bytes we're received so far
228 ushort file_chksum; // used for checking successfully xferred files
229 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
230 int xfer_stamp; // timestamp for the current operation
231 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
232 ushort sig; // identifying sig - sender specifies this
234 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
236 // callback function pointer for when we start receiving a file
237 void (*Multi_xfer_recv_notify)(int handle);
239 // lock for the xfer system
240 int Multi_xfer_locked;
242 // force directory type for receives
243 int Multi_xfer_force_dir;
245 // unique file signature - this along with a socket # is enough to identify all xfers
246 ushort Multi_xfer_sig = 0;
249 // ------------------------------------------------------------------------------------------
250 // MULTI XFER FORWARD DECLARATIONS
253 // evaluate the condition of the entry
254 void multi_xfer_eval_entry(xfer_entry *xe);
256 // set an entry to be "failed"
257 void multi_xfer_fail_entry(xfer_entry *xe);
259 // get a valid xfer entry handle
260 int multi_xfer_get_free_handle();
262 // process an ack for this entry
263 void multi_xfer_process_ack(xfer_entry *xe);
265 // process a nak for this entry
266 void multi_xfer_process_nak(xfer_entry *xe);
268 // process a "final" packet
269 void multi_xfer_process_final(xfer_entry *xe);
271 // process a data packet
272 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
275 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
277 // send the next block of outgoing data or a "final" packet if we're done
278 void multi_xfer_send_next(xfer_entry *xe);
280 // send an ack to the sender
281 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
283 // send a nak to the sender
284 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
286 // send a "final" packet
287 void multi_xfer_send_final(xfer_entry *xe);
289 // send the header to begin a file transfer
290 void multi_xfer_send_header(xfer_entry *xe);
292 // convert the filename into the prefixed ex_filename
293 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
295 // get a new xfer sig
296 ushort multi_xfer_get_sig();
298 // ------------------------------------------------------------------------------------------
299 // MULTI XFER FUNCTIONS
302 // initialize all file xfer transaction stuff, call in multi_level_init()
303 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
305 // blast all the entries
306 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
308 // assign the receive callback function pointer
309 Multi_xfer_recv_notify = multi_xfer_recv_callback;
312 Multi_xfer_locked = 0;
314 // no forced directory
315 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
318 // do frame for all file xfers, call in multi_do_frame()
323 // process all valid xfer entries
324 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
325 // if this one is actually in use and has not finished for one reason or another
326 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !(Multi_xfer_entry[idx].flags & (MULTI_XFER_FLAG_SUCCESS | MULTI_XFER_FLAG_FAIL | MULTI_XFER_FLAG_TIMEOUT))){
327 // evaluate the condition of this entry (fail, timeout, etc)
328 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
333 // close down the file xfer system
334 void multi_xfer_close()
338 // go through all active entries and abort them
339 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
340 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
341 multi_xfer_abort(idx);
345 // now blast all the memory free
346 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
349 // reset the xfer system, including shutting down/killing all active xfers
350 void multi_xfer_reset()
354 // shut down all active xfers
355 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
356 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
357 multi_xfer_abort(idx);
361 // blast all the memory clean
362 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
365 // send a file to the specified player, return a handle
366 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
368 xfer_entry temp_entry;
371 // if the system is locked, return -1
372 if(Multi_xfer_locked){
376 // attempt to get a free handle
377 handle = multi_xfer_get_free_handle();
382 // clear the temp entry
383 memset(&temp_entry,0,sizeof(xfer_entry));
386 strcpy(temp_entry.filename,filename);
388 // attempt to open the file
389 temp_entry.file = NULL;
390 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
391 if(temp_entry.file == NULL){
392 #ifdef MULTI_XFER_VERBOSE
393 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
400 temp_entry.file_size = -1;
401 temp_entry.file_size = cfilelength(temp_entry.file);
402 if(temp_entry.file_size == -1){
403 #ifdef MULTI_XFER_VERBOSE
404 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
408 temp_entry.file_ptr = 0;
410 // get the file checksum
411 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
412 #ifdef MULTI_XFER_VERBOSE
413 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
417 #ifdef MULTI_XFER_VERBOSE
418 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
420 // rewind the file pointer to the beginning of the file
421 cfseek(temp_entry.file,0,CF_SEEK_SET);
424 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
425 temp_entry.flags |= flags;
428 temp_entry.file_socket = who;
431 temp_entry.sig = multi_xfer_get_sig();
433 // copy to the global array
434 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
435 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
440 // get the status of the current file xfer
441 int multi_xfer_get_status(int handle)
443 // if this is an invalid or an unused handle, notify as such
444 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
445 return MULTI_XFER_NONE;
448 // if the xfer has timed-out
449 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
450 return MULTI_XFER_TIMEDOUT;
453 // if the xfer has failed for one reason or another (not timeout)
454 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
455 return MULTI_XFER_FAIL;
458 // if the xfer has succeeded
459 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
460 return MULTI_XFER_SUCCESS;
463 // if the xfer is queued
464 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
465 return MULTI_XFER_QUEUED;
468 // otherwise the xfer is still in progress
469 return MULTI_XFER_IN_PROGRESS;
472 // abort a transferring file
473 void multi_xfer_abort(int handle)
477 // don't do anything if this is an invalid handle
478 if(MULTI_XFER_INVALID_HANDLE(handle)){
482 // get e handle to the entry
483 xe = &Multi_xfer_entry[handle];
485 // close any open file and delete it
486 if(xe->file != NULL){
490 // delete it if there isn't some problem with the filename
491 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
492 cf_delete(xe->ex_filename, xe->force_dir);
497 xe->file_socket = INVALID_SOCKET;
500 memset(xe,0,sizeof(xfer_entry));
503 // release an xfer handle
504 void multi_xfer_release_handle(int handle)
508 // don't do anything if this is an invalid handle
509 if(MULTI_XFER_INVALID_HANDLE(handle)){
513 // get e handle to the entry
514 xe = &Multi_xfer_entry[handle];
516 // close any open file and delete it
517 if(xe->file != NULL){
521 // delete it if the file was not successfully received
522 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
523 cf_delete(xe->ex_filename,xe->force_dir);
528 xe->file_socket = INVALID_SOCKET;
531 memset(xe,0,sizeof(xfer_entry));
534 // get the filename of the xfer for the given handle
535 char *multi_xfer_get_filename(int handle)
537 // if this is an invalid handle, return NULL
538 if(MULTI_XFER_INVALID_HANDLE(handle)){
542 // otherwise return the string
543 return Multi_xfer_entry[handle].filename;
546 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
547 void multi_xfer_lock()
549 Multi_xfer_locked = 1;
552 // unlock the xfer system
553 void multi_xfer_unlock()
555 Multi_xfer_locked = 0;
558 // force all receives to go into the specified directory by cfile type
559 void multi_xfer_force_dir(int cf_type)
561 Multi_xfer_force_dir = cf_type;
562 Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
565 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
566 void multi_xfer_handle_force_dir(int handle,int cf_type)
568 // if this is an invalid handle, return NULL
569 if(MULTI_XFER_INVALID_HANDLE(handle)){
573 // force to go to the given directory
574 Multi_xfer_entry[handle].force_dir = cf_type;
575 Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
578 // or the flag on a given entry
579 void multi_xfer_xor_flags(int handle,int flags)
581 // if this is an invalid handle, return NULL
582 if(MULTI_XFER_INVALID_HANDLE(handle)){
587 Multi_xfer_entry[handle].flags ^= flags;
590 // get the flags for a given entry
591 int multi_xfer_get_flags(int handle)
593 // if this is an invalid handle, return NULL
594 if(MULTI_XFER_INVALID_HANDLE(handle)){
599 return Multi_xfer_entry[handle].flags;
602 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
603 int multi_xfer_lookup(char *filename)
607 // if we have an invalid filename, do nothing
608 if((filename == NULL) || (strlen(filename) <= 0)){
612 // otherwise, perform a lookup
613 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
614 // if we found a matching filename
615 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
620 // did not find a match
624 // get the % of completion of the passed file handle, return < 0 if invalid
625 float multi_xfer_pct_complete(int handle)
627 // if this is an invalid handle, return invalid
628 if(MULTI_XFER_INVALID_HANDLE(handle)){
632 // if the file size is 0, return invalid
633 if(Multi_xfer_entry[handle].file_size == 0){
637 // return the pct completion
638 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
641 // get the socket of the file xfer (useful for identifying players)
642 uint multi_xfer_get_sock(int handle)
644 // if this is an invalid handle, return NULL
645 if(MULTI_XFER_INVALID_HANDLE(handle)){
646 return INVALID_SOCKET;
649 return Multi_xfer_entry[handle].file_socket;
652 // get the CF_TYPE of the directory this file is going to
653 int multi_xfer_get_force_dir(int handle)
655 // if this is an invalid handle, return NULL
656 if(MULTI_XFER_INVALID_HANDLE(handle)){
657 return INVALID_SOCKET;
660 return Multi_xfer_entry[handle].force_dir;
664 // ------------------------------------------------------------------------------------------
665 // MULTI XFER FORWARD DECLARATIONS
668 // evaluate the condition of the entry
669 void multi_xfer_eval_entry(xfer_entry *xe)
675 // if the entry is marked as successful, then don't do anything
676 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
680 // if the entry is queued
681 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
682 // if the entry is not current
683 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
684 // see if there are any other queued up xfers to this target. if not, make me current and start sending
686 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
687 xe_c = &Multi_xfer_entry[idx];
689 // if this is a valid entry and is a queued entry and is going to the same target
690 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
691 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
698 // if we found no other entries, make this guy current and pending
700 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
701 xe->flags |= MULTI_XFER_FLAG_PENDING;
703 #ifdef MULTI_XFER_VERBOSE
704 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
707 // otherwise, do nothing for him - he has to still wait
714 // if the entry is marked as pending - send out the header to get the ball rolling
715 if(xe->flags & MULTI_XFER_FLAG_PENDING){
716 // send the header to begin the transfer
717 multi_xfer_send_header(xe);
720 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
722 // unset the pending flag
723 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
725 // set the ack/wait flag
726 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
729 // see if the entry has timed-out for one reason or another
730 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
731 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
733 // if we should be auto-destroying this entry, do so
734 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
735 multi_xfer_fail_entry(xe);
740 // lookup a file xfer entry by player
741 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
745 // look through all valid xfer entries
746 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
747 // if we're looking for sending entries
748 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
751 // if we're looking for recv entries
752 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
756 // if we found a match
757 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
758 return &Multi_xfer_entry[idx];
765 // set an entry to be "failed"
766 void multi_xfer_fail_entry(xfer_entry *xe)
768 // set its flags appropriately
769 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
770 xe->flags |= MULTI_XFER_FLAG_FAIL;
772 // close the file pointer
773 if(xe->file != NULL){
779 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
780 cf_delete(xe->ex_filename,xe->force_dir);
783 // null the timestamp
786 // if we should be auto-destroying this entry, do so
787 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
788 multi_xfer_release_handle(xe - Multi_xfer_entry);
791 // blast the memory clean
792 memset(xe,0,sizeof(xfer_entry));
795 // get a valid xfer entry handle
796 int multi_xfer_get_free_handle()
800 // look for a free entry
801 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
802 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
811 // ------------------------------------------------------------------------------------------
812 // MULTI XFER PACKET HANDLERS
815 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
816 // packet regardless of error conditions
817 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
822 ushort data_size = 0;
824 ushort file_checksum = 0;
826 ubyte xfer_data[600];
830 // read in all packet data
835 case MULTI_XFER_CODE_DATA:
837 memcpy(xfer_data, data + offset, data_size);
843 case MULTI_XFER_CODE_HEADER:
844 GET_STRING(filename);
846 GET_DATA(file_checksum);
851 case MULTI_XFER_CODE_ACK:
852 case MULTI_XFER_CODE_NAK:
856 case MULTI_XFER_CODE_FINAL:
864 // at this point we've read all the data in the packet
866 // at this point, we should process code-specific data
868 if(val != MULTI_XFER_CODE_HEADER){
869 // if the code is not a request or a header, we need to look up the existing xfer_entry
872 xe = multi_xfer_find_entry(who, sig, sender_side);
874 #ifdef MULTI_XFER_VERBOSE
875 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
877 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
879 int np_index = find_player_socket(who);
880 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
881 ml_printf(": sig == %d", sig);
882 ml_printf(": xfer header == %d", val);
884 ml_string(": player == unknown");
886 ml_printf(": player == %s", Net_players[np_index].player->callsign);
889 ml_string(": sending");
891 ml_string(": receiving");
901 // process an ack for this entry
902 case MULTI_XFER_CODE_ACK :
904 multi_xfer_process_ack(xe);
907 // process a nak for this entry
908 case MULTI_XFER_CODE_NAK :
910 multi_xfer_process_nak(xe);
913 // process a "final" packet
914 case MULTI_XFER_CODE_FINAL :
916 multi_xfer_process_final(xe);
919 // process a data packet
920 case MULTI_XFER_CODE_DATA :
922 multi_xfer_process_data(xe, xfer_data, data_size);
926 case MULTI_XFER_CODE_HEADER :
927 // send on my reliable socket
928 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
934 // process an ack for this entry
935 void multi_xfer_process_ack(xfer_entry *xe)
937 // if we are a sender
938 if(xe->flags & MULTI_XFER_FLAG_SEND){
939 // if we are waiting on a final ack, then the transfer has completed successfully
940 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
941 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
942 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
944 #ifdef MULTI_XFER_VERBOSE
945 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
948 // if we should be auto-destroying this entry, do so
949 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
950 multi_xfer_release_handle(xe - Multi_xfer_entry);
953 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
954 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
955 multi_xfer_send_next(xe);
960 // process a nak for this entry
961 void multi_xfer_process_nak(xfer_entry *xe)
963 // if we get an ack at any time we should simply set the xfer to failed
964 multi_xfer_fail_entry(xe);
967 // process a "final" packet
968 void multi_xfer_process_final(xfer_entry *xe)
972 // make sure we skip a line
973 nprintf(("Network","\n"));
976 if(xe->file != NULL){
982 // check to make sure the file checksum is the same
984 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
986 xe->flags |= MULTI_XFER_FLAG_FAIL;
988 #ifdef MULTI_XFER_VERBOSE
989 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
993 multi_xfer_abort(xe - Multi_xfer_entry);
996 // checksums check out, so rename the file and be done with it
998 #ifdef MULTI_XFER_VERBOSE
999 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));
1001 // rename the file properly
1002 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1003 // mark the xfer as being successful
1004 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1006 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1008 // send an ack to the sender
1009 multi_xfer_send_ack(xe->file_socket, xe->sig);
1011 // mark it as failing
1012 xe->flags |= MULTI_XFER_FLAG_FAIL;
1013 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1015 // delete the tempfile
1016 cf_delete(xe->ex_filename, xe->force_dir);
1018 // send an nak to the sender
1019 multi_xfer_send_nak(xe->file_socket, xe->sig);
1022 // if we should be auto-destroying this entry, do so
1023 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1024 multi_xfer_release_handle(xe - Multi_xfer_entry);
1029 // process a data packet
1030 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1032 // print out a crude progress indicator
1033 nprintf(("Network","."));
1035 // attempt to write the rest of the data string to the file
1036 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1037 // inform the sender we had a problem
1038 multi_xfer_send_nak(xe->file_socket, xe->sig);
1041 multi_xfer_fail_entry(xe);
1043 xe->file_ptr += data_size;
1047 // increment the file pointer
1048 xe->file_ptr += data_size;
1050 // send an ack to the sender
1051 multi_xfer_send_ack(xe->file_socket, xe->sig);
1054 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1057 // process a header, return bytes processed
1058 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1063 // if the xfer system is locked, send a nak
1064 if(Multi_xfer_locked){
1065 multi_xfer_send_nak(who, sig);
1069 // try and get a free xfer handle
1070 handle = multi_xfer_get_free_handle();
1072 multi_xfer_send_nak(who, sig);
1075 xe = &Multi_xfer_entry[handle];
1076 memset(xe,0,sizeof(xfer_entry));
1079 // set the recv and used flags
1080 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1082 // get the header data
1083 xe->file_size = file_size;
1085 // get the file chksum
1086 xe->file_chksum = file_checksum;
1089 xe->file_socket = who;
1091 // set the timeout timestamp
1092 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1097 // copy the filename and get the prefixed xfer filename
1098 strcpy(xe->filename, filename);
1099 multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1100 #ifdef MULTI_XFER_VERBOSE
1101 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1104 // determine what directory to place the file in
1105 // individual xfer entries take precedence over the global multi xfer force entry
1106 xe->force_dir = Multi_xfer_force_dir;
1108 // call the callback function
1109 Multi_xfer_recv_notify(handle);
1111 // if the notify function invalidated this xfer handle, then cancel the whole thing
1112 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1113 multi_xfer_send_nak(who, sig);
1116 memset(xe, 0, sizeof(xfer_entry));
1120 // delete the old file (if it exists)
1121 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1122 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1124 // attempt to open the file (using the prefixed filename)
1126 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1127 if(xe->file == NULL){
1128 multi_xfer_send_nak(who, sig);
1131 memset(xe, 0, sizeof(xfer_entry));
1135 // set the waiting for data flag
1136 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1138 // send an ack to the server
1139 multi_xfer_send_ack(who, sig);
1141 #ifdef MULTI_XFER_VERBOSE
1142 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1146 // send the next block of outgoing data or a "final" packet if we're done
1147 void multi_xfer_send_next(xfer_entry *xe)
1149 ubyte data[MAX_PACKET_SIZE],code;
1152 int packet_size = 0;
1154 // print out a crude progress indicator
1155 nprintf(("Network", "+"));
1157 // if we've sent all the data, then we should send a "final" packet
1158 if(xe->file_ptr >= xe->file_size){
1159 // mark the entry as unknown
1160 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1163 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1166 multi_xfer_send_final(xe);
1172 BUILD_HEADER(XFER_PACKET);
1174 // length of the added string
1175 flen = strlen(xe->filename) + 4;
1177 // determine how much data we are going to send with this packet and add it in
1178 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1179 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1181 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1183 // increment the file pointer
1184 xe->file_ptr += data_size;
1187 code = MULTI_XFER_CODE_DATA;
1193 // add in the size of the rest of the packet
1194 ADD_DATA(data_size);
1197 if(cfread(data+packet_size,1,(int)data_size,xe->file) == NULL){
1198 // send a nack to the receiver
1199 multi_xfer_send_nak(xe->file_socket, xe->sig);
1202 multi_xfer_fail_entry(xe);
1206 // increment the packet size
1207 packet_size += (int)data_size;
1210 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1212 // otherwise send the data
1213 psnet_rel_send(xe->file_socket, data, packet_size);
1216 // send an ack to the sender
1217 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1219 ubyte data[MAX_PACKET_SIZE],code;
1220 int packet_size = 0;
1222 // build the header and add
1223 BUILD_HEADER(XFER_PACKET);
1226 code = MULTI_XFER_CODE_ACK;
1233 psnet_rel_send(socket, data, packet_size);
1236 // send a nak to the sender
1237 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1239 ubyte data[MAX_PACKET_SIZE],code;
1240 int packet_size = 0;
1242 // build the header and add the code
1243 BUILD_HEADER(XFER_PACKET);
1246 code = MULTI_XFER_CODE_NAK;
1253 psnet_rel_send(socket, data, packet_size);
1256 // send a "final" packet
1257 void multi_xfer_send_final(xfer_entry *xe)
1259 ubyte data[MAX_PACKET_SIZE],code;
1260 int packet_size = 0;
1263 BUILD_HEADER(XFER_PACKET);
1266 code = MULTI_XFER_CODE_FINAL;
1273 psnet_rel_send(xe->file_socket, data, packet_size);
1276 // send the header to begin a file transfer
1277 void multi_xfer_send_header(xfer_entry *xe)
1279 ubyte data[MAX_PACKET_SIZE],code;
1280 int packet_size = 0;
1282 // build the header and add the opcode
1283 BUILD_HEADER(XFER_PACKET);
1284 code = MULTI_XFER_CODE_HEADER;
1291 ADD_STRING(xe->filename);
1294 ADD_DATA(xe->file_size);
1296 // add the file checksum
1297 ADD_DATA(xe->file_chksum);
1300 psnet_rel_send(xe->file_socket, data, packet_size);
1303 // convert the filename into the prefixed ex_filename
1304 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1306 char temp[MAX_FILENAME_LEN+50];
1308 // blast the memory clean
1309 memset(temp, 0, MAX_FILENAME_LEN+50);
1311 // copy in the prefix
1312 strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1314 // stick on the original name
1315 strcat(temp, filename);
1317 // copy the whole thing to the outgoing filename
1318 strcpy(ex_filename, temp);
1321 // get a new xfer sig
1322 ushort multi_xfer_get_sig()
1324 ushort ret = Multi_xfer_sig;
1327 if(Multi_xfer_sig == 0xffff){