2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/multi_xfer.cpp $
16 * Revision 1.6 2003/06/22 12:51:02 taylor
17 * lower case file transfers
19 * Revision 1.5 2002/06/09 04:41:24 relnev
20 * added copyright header
22 * Revision 1.4 2002/06/01 07:12:33 relnev
23 * a few NDEBUG updates.
25 * removed a few warnings.
27 * Revision 1.3 2002/05/26 20:22:48 theoddone33
28 * Most of network/ works
30 * Revision 1.2 2002/05/07 03:16:47 theoddone33
31 * The Great Newline Fix
33 * Revision 1.1.1.1 2002/05/03 03:28:10 root
37 * 11 3/10/99 6:50p Dave
38 * Changed the way we buffer packets for all clients. Optimized turret
39 * fired packets. Did some weapon firing optimizations.
41 * 10 3/09/99 6:24p Dave
42 * More work on object update revamping. Identified several sources of
43 * unnecessary bandwidth.
45 * 9 1/24/99 11:37p Dave
46 * First full rev of beam weapons. Very customizable. Removed some bogus
47 * Int3()'s in low level net code.
49 * 8 12/16/98 11:17a Dave
50 * Fixed potential situation where a send and receive to the same player
51 * with the same sig might get confused with each other.
53 * 7 12/14/98 4:01p Dave
54 * Got multi_data stuff working well with new xfer stuff.
56 * 6 12/14/98 12:13p Dave
57 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
60 * 5 11/19/98 8:03a Dave
61 * Full support for D3-style reliable sockets. Revamped packet lag/loss
62 * system, made it receiver side and at the lowest possible level.
64 * 4 11/17/98 11:12a Dave
65 * Removed player identification by address. Now assign explicit id #'s.
67 * 3 11/05/98 5:55p Dave
68 * Big pass at reducing #includes
70 * 2 10/07/98 10:53a Dave
73 * 1 10/07/98 10:50a Dave
75 * 56 9/11/98 9:31a Dave
76 * Fixed file xfer bug regarding checksums.
78 * 55 8/12/98 4:53p Dave
79 * Put in 32 bit checksumming for PXO missions. No validation on the
80 * actual tracker yet, though.
82 * 54 6/13/98 9:32p Mike
83 * Kill last character in file which caused "Find in Files" to report the
84 * file as "not a text file."
86 * 53 6/13/98 6:01p Hoffoss
87 * Externalized all new (or forgot to be added) strings to all the code.
89 * 52 6/13/98 3:19p Hoffoss
90 * NOX()ed out a bunch of strings that shouldn't be translated.
92 * 51 5/21/98 1:52a Dave
93 * Remove obsolete command line functions. Reduce shield explosion packets
94 * drastically. Tweak PXO screen even more. Fix file xfer system so that
95 * we can guarantee file uniqueness.
97 * 50 4/30/98 4:53p John
98 * Restructured and cleaned up cfile code. Added capability to read off
99 * of CD-ROM drive and out of multiple pack files.
101 * 49 4/23/98 6:18p Dave
102 * Store ETS values between respawns. Put kick feature in the text
103 * messaging system. Fixed text messaging system so that it doesn't
104 * process or trigger ship controls. Other UI fixes.
106 * 48 4/22/98 5:53p Dave
107 * Large reworking of endgame sequencing. Updated multi host options
108 * screen for new artwork. Put in checks for host or team captains leaving
111 * 47 4/21/98 4:44p Dave
112 * Implement Vasudan ships in multiplayer. Added a debug function to bash
113 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
114 * problem in options screen.
116 * 46 4/20/98 6:04p Dave
117 * Implement multidata cache flushing and xferring mission files to
118 * multidata. Make sure observers can't change hud config. Fix pilot image
119 * viewing in popup. Put in game status field. Tweaked multi options.
121 * 45 4/08/98 2:51p Dave
122 * Fixed pilot image xfer once again. Solidify team selection process in
123 * pre-briefing multiplayer.
125 * 44 4/04/98 4:22p Dave
126 * First rev of UDP reliable sockets is done. Seems to work well if not
129 * 43 4/03/98 1:03a Dave
130 * First pass at unreliable guaranteed delivery packets.
132 * 42 4/01/98 11:19p Dave
133 * Put in auto-loading of xferred pilot pic files. Grey out background
134 * behind pinfo popup. Put a chatbox message in when players are kicked.
135 * Moved mission title down in briefing. Other ui fixes.
137 * 41 3/31/98 4:51p Dave
138 * Removed medals screen and multiplayer buttons from demo version. Put in
139 * new pilot popup screen. Make ships in mp team vs. team have proper team
140 * ids. Make mp respawns a permanent option saved in the player file.
142 * 40 3/30/98 8:46a Allender
143 * fix an optimized build compiler warning
145 * 39 3/26/98 6:01p Dave
146 * Put in file checksumming routine in cfile. Made pilot pic xferring more
147 * robust. Cut header size of voice data packets in half. Put in
148 * restricted game host query system.
150 * 38 3/25/98 2:16p Dave
151 * Select random default image for newly created pilots. Fixed several
152 * multi-pause messaging bugs. Begin work on online help for multiplayer
155 * 37 3/24/98 5:00p Dave
156 * Fixed several ui bugs. Put in pre and post voice stream playback sound
157 * fx. Put in error specific popups for clients getting dropped from games
158 * through actions other than their own.
160 * 36 3/23/98 5:42p Dave
161 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
162 * that it can support multiplayer sends/received between client and
163 * server simultaneously.
165 * 35 3/21/98 7:14p Dave
166 * Fixed up standalone player slot switching. Made training missions not
167 * count towards player stats.
169 * 34 3/16/98 11:52p Allender
170 * Put in timestamp updates when processing data on both sender and
173 * 33 2/22/98 2:53p Dave
174 * Put in groundwork for advanced multiplayer campaign options.
176 * 32 2/20/98 4:43p Dave
177 * Finished support for multiplayer player data files. Split off
178 * multiplayer campaign functionality.
180 * 31 2/19/98 6:26p Dave
181 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
182 * initial support for player data uploading.
184 * 30 2/18/98 10:21p Dave
185 * Ripped out old file xfer system. Put in brand new xfer system.
194 #include "multi_xfer.h"
196 #include "multimsgs.h"
199 #include "multi_endgame.h"
202 #include "multiutil.h"
203 #include "multi_log.h"
205 // ------------------------------------------------------------------------------------------
206 // MULTI XFER DEFINES/VARS
209 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
211 #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) )
214 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
215 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
216 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
217 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
218 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
221 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
222 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
223 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
224 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
225 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
226 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
227 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
228 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
229 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
230 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
231 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
233 // packet size for file xfer
234 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
236 // timeout for a given xfer operation
237 #define MULTI_XFER_TIMEOUT 10000
241 // temp filename header for xferring files
242 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
246 // xfer entries/events
247 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
248 typedef struct xfer_entry {
249 int flags; // status flags for this entry
250 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
251 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
252 CFILE *file; // file handle of the current xferring file
253 int file_size; // total size of the file being xferred
254 int file_ptr; // total bytes we're received so far
255 ushort file_chksum; // used for checking successfully xferred files
256 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
257 int xfer_stamp; // timestamp for the current operation
258 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
259 ushort sig; // identifying sig - sender specifies this
261 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
263 // callback function pointer for when we start receiving a file
264 void (*Multi_xfer_recv_notify)(int handle);
266 // lock for the xfer system
267 int Multi_xfer_locked;
269 // force directory type for receives
270 int Multi_xfer_force_dir;
272 // unique file signature - this along with a socket # is enough to identify all xfers
273 ushort Multi_xfer_sig = 0;
276 // ------------------------------------------------------------------------------------------
277 // MULTI XFER FORWARD DECLARATIONS
280 // evaluate the condition of the entry
281 void multi_xfer_eval_entry(xfer_entry *xe);
283 // set an entry to be "failed"
284 void multi_xfer_fail_entry(xfer_entry *xe);
286 // get a valid xfer entry handle
287 int multi_xfer_get_free_handle();
289 // process an ack for this entry
290 void multi_xfer_process_ack(xfer_entry *xe);
292 // process a nak for this entry
293 void multi_xfer_process_nak(xfer_entry *xe);
295 // process a "final" packet
296 void multi_xfer_process_final(xfer_entry *xe);
298 // process a data packet
299 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
302 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
304 // send the next block of outgoing data or a "final" packet if we're done
305 void multi_xfer_send_next(xfer_entry *xe);
307 // send an ack to the sender
308 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
310 // send a nak to the sender
311 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
313 // send a "final" packet
314 void multi_xfer_send_final(xfer_entry *xe);
316 // send the header to begin a file transfer
317 void multi_xfer_send_header(xfer_entry *xe);
319 // convert the filename into the prefixed ex_filename
320 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
322 // get a new xfer sig
323 ushort multi_xfer_get_sig();
325 // ------------------------------------------------------------------------------------------
326 // MULTI XFER FUNCTIONS
329 // initialize all file xfer transaction stuff, call in multi_level_init()
330 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
332 // blast all the entries
333 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
335 // assign the receive callback function pointer
336 Multi_xfer_recv_notify = multi_xfer_recv_callback;
339 Multi_xfer_locked = 0;
341 // no forced directory
342 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
345 // do frame for all file xfers, call in multi_do_frame()
350 // process all valid xfer entries
351 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
352 // if this one is actually in use and has not finished for one reason or another
353 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))){
354 // evaluate the condition of this entry (fail, timeout, etc)
355 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
360 // close down the file xfer system
361 void multi_xfer_close()
365 // go through all active entries and abort them
366 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
367 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
368 multi_xfer_abort(idx);
372 // now blast all the memory free
373 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
376 // reset the xfer system, including shutting down/killing all active xfers
377 void multi_xfer_reset()
381 // shut down all active xfers
382 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
383 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
384 multi_xfer_abort(idx);
388 // blast all the memory clean
389 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
392 // send a file to the specified player, return a handle
393 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
395 xfer_entry temp_entry;
398 // if the system is locked, return -1
399 if(Multi_xfer_locked){
403 // attempt to get a free handle
404 handle = multi_xfer_get_free_handle();
409 // clear the temp entry
410 memset(&temp_entry,0,sizeof(xfer_entry));
413 strcpy(temp_entry.filename,filename);
415 // attempt to open the file
416 temp_entry.file = NULL;
417 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
418 if(temp_entry.file == NULL){
419 #ifdef MULTI_XFER_VERBOSE
420 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
427 temp_entry.file_size = -1;
428 temp_entry.file_size = cfilelength(temp_entry.file);
429 if(temp_entry.file_size == -1){
430 #ifdef MULTI_XFER_VERBOSE
431 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
435 temp_entry.file_ptr = 0;
437 // get the file checksum
438 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
439 #ifdef MULTI_XFER_VERBOSE
440 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
444 #ifdef MULTI_XFER_VERBOSE
445 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
447 // rewind the file pointer to the beginning of the file
448 cfseek(temp_entry.file,0,CF_SEEK_SET);
451 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
452 temp_entry.flags |= flags;
455 temp_entry.file_socket = who;
458 temp_entry.sig = multi_xfer_get_sig();
460 // copy to the global array
461 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
462 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
467 // get the status of the current file xfer
468 int multi_xfer_get_status(int handle)
470 // if this is an invalid or an unused handle, notify as such
471 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
472 return MULTI_XFER_NONE;
475 // if the xfer has timed-out
476 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
477 return MULTI_XFER_TIMEDOUT;
480 // if the xfer has failed for one reason or another (not timeout)
481 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
482 return MULTI_XFER_FAIL;
485 // if the xfer has succeeded
486 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
487 return MULTI_XFER_SUCCESS;
490 // if the xfer is queued
491 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
492 return MULTI_XFER_QUEUED;
495 // otherwise the xfer is still in progress
496 return MULTI_XFER_IN_PROGRESS;
499 // abort a transferring file
500 void multi_xfer_abort(int handle)
504 // don't do anything if this is an invalid handle
505 if(MULTI_XFER_INVALID_HANDLE(handle)){
509 // get e handle to the entry
510 xe = &Multi_xfer_entry[handle];
512 // close any open file and delete it
513 if(xe->file != NULL){
517 // delete it if there isn't some problem with the filename
518 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
519 cf_delete(xe->ex_filename, xe->force_dir);
524 xe->file_socket = INVALID_SOCKET;
527 memset(xe,0,sizeof(xfer_entry));
530 // release an xfer handle
531 void multi_xfer_release_handle(int handle)
535 // don't do anything if this is an invalid handle
536 if(MULTI_XFER_INVALID_HANDLE(handle)){
540 // get e handle to the entry
541 xe = &Multi_xfer_entry[handle];
543 // close any open file and delete it
544 if(xe->file != NULL){
548 // delete it if the file was not successfully received
549 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
550 cf_delete(xe->ex_filename,xe->force_dir);
555 xe->file_socket = INVALID_SOCKET;
558 memset(xe,0,sizeof(xfer_entry));
561 // get the filename of the xfer for the given handle
562 char *multi_xfer_get_filename(int handle)
564 // if this is an invalid handle, return NULL
565 if(MULTI_XFER_INVALID_HANDLE(handle)){
569 // otherwise return the string
570 return Multi_xfer_entry[handle].filename;
573 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
574 void multi_xfer_lock()
576 Multi_xfer_locked = 1;
579 // unlock the xfer system
580 void multi_xfer_unlock()
582 Multi_xfer_locked = 0;
585 // force all receives to go into the specified directory by cfile type
586 void multi_xfer_force_dir(int cf_type)
588 Multi_xfer_force_dir = cf_type;
589 Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
592 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
593 void multi_xfer_handle_force_dir(int handle,int cf_type)
595 // if this is an invalid handle, return NULL
596 if(MULTI_XFER_INVALID_HANDLE(handle)){
600 // force to go to the given directory
601 Multi_xfer_entry[handle].force_dir = cf_type;
602 Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
605 // or the flag on a given entry
606 void multi_xfer_xor_flags(int handle,int flags)
608 // if this is an invalid handle, return NULL
609 if(MULTI_XFER_INVALID_HANDLE(handle)){
614 Multi_xfer_entry[handle].flags ^= flags;
617 // get the flags for a given entry
618 int multi_xfer_get_flags(int handle)
620 // if this is an invalid handle, return NULL
621 if(MULTI_XFER_INVALID_HANDLE(handle)){
626 return Multi_xfer_entry[handle].flags;
629 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
630 int multi_xfer_lookup(char *filename)
634 // if we have an invalid filename, do nothing
635 if((filename == NULL) || (strlen(filename) <= 0)){
639 // otherwise, perform a lookup
640 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
641 // if we found a matching filename
642 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
647 // did not find a match
651 // get the % of completion of the passed file handle, return < 0 if invalid
652 float multi_xfer_pct_complete(int handle)
654 // if this is an invalid handle, return invalid
655 if(MULTI_XFER_INVALID_HANDLE(handle)){
659 // if the file size is 0, return invalid
660 if(Multi_xfer_entry[handle].file_size == 0){
664 // return the pct completion
665 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
668 // get the socket of the file xfer (useful for identifying players)
669 uint multi_xfer_get_sock(int handle)
671 // if this is an invalid handle, return NULL
672 if(MULTI_XFER_INVALID_HANDLE(handle)){
673 return INVALID_SOCKET;
676 return Multi_xfer_entry[handle].file_socket;
679 // get the CF_TYPE of the directory this file is going to
680 int multi_xfer_get_force_dir(int handle)
682 // if this is an invalid handle, return NULL
683 if(MULTI_XFER_INVALID_HANDLE(handle)){
684 return INVALID_SOCKET;
687 return Multi_xfer_entry[handle].force_dir;
691 // ------------------------------------------------------------------------------------------
692 // MULTI XFER FORWARD DECLARATIONS
695 // evaluate the condition of the entry
696 void multi_xfer_eval_entry(xfer_entry *xe)
702 // if the entry is marked as successful, then don't do anything
703 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
707 // if the entry is queued
708 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
709 // if the entry is not current
710 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
711 // see if there are any other queued up xfers to this target. if not, make me current and start sending
713 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
714 xe_c = &Multi_xfer_entry[idx];
716 // if this is a valid entry and is a queued entry and is going to the same target
717 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
718 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
725 // if we found no other entries, make this guy current and pending
727 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
728 xe->flags |= MULTI_XFER_FLAG_PENDING;
730 #ifdef MULTI_XFER_VERBOSE
731 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
734 // otherwise, do nothing for him - he has to still wait
741 // if the entry is marked as pending - send out the header to get the ball rolling
742 if(xe->flags & MULTI_XFER_FLAG_PENDING){
743 // send the header to begin the transfer
744 multi_xfer_send_header(xe);
747 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
749 // unset the pending flag
750 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
752 // set the ack/wait flag
753 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
756 // see if the entry has timed-out for one reason or another
757 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
758 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
760 // if we should be auto-destroying this entry, do so
761 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
762 multi_xfer_fail_entry(xe);
767 // lookup a file xfer entry by player
768 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
772 // look through all valid xfer entries
773 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
774 // if we're looking for sending entries
775 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
778 // if we're looking for recv entries
779 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
783 // if we found a match
784 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
785 return &Multi_xfer_entry[idx];
792 // set an entry to be "failed"
793 void multi_xfer_fail_entry(xfer_entry *xe)
795 // set its flags appropriately
796 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
797 xe->flags |= MULTI_XFER_FLAG_FAIL;
799 // close the file pointer
800 if(xe->file != NULL){
806 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
807 cf_delete(xe->ex_filename,xe->force_dir);
810 // null the timestamp
813 // if we should be auto-destroying this entry, do so
814 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
815 multi_xfer_release_handle(xe - Multi_xfer_entry);
818 // blast the memory clean
819 memset(xe,0,sizeof(xfer_entry));
822 // get a valid xfer entry handle
823 int multi_xfer_get_free_handle()
827 // look for a free entry
828 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
829 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
838 // ------------------------------------------------------------------------------------------
839 // MULTI XFER PACKET HANDLERS
842 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
843 // packet regardless of error conditions
844 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
849 ushort data_size = 0;
851 ushort file_checksum = 0;
853 ubyte xfer_data[600];
857 // read in all packet data
862 case MULTI_XFER_CODE_DATA:
864 memcpy(xfer_data, data + offset, data_size);
870 case MULTI_XFER_CODE_HEADER:
871 GET_STRING(filename);
873 GET_DATA(file_checksum);
878 case MULTI_XFER_CODE_ACK:
879 case MULTI_XFER_CODE_NAK:
883 case MULTI_XFER_CODE_FINAL:
891 // at this point we've read all the data in the packet
893 // at this point, we should process code-specific data
895 if(val != MULTI_XFER_CODE_HEADER){
896 // if the code is not a request or a header, we need to look up the existing xfer_entry
899 xe = multi_xfer_find_entry(who, sig, sender_side);
901 #ifdef MULTI_XFER_VERBOSE
902 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
904 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
906 int np_index = find_player_socket(who);
907 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
908 ml_printf(": sig == %d", sig);
909 ml_printf(": xfer header == %d", val);
911 ml_string(": player == unknown");
913 ml_printf(": player == %s", Net_players[np_index].player->callsign);
916 ml_string(": sending");
918 ml_string(": receiving");
928 // process an ack for this entry
929 case MULTI_XFER_CODE_ACK :
931 multi_xfer_process_ack(xe);
934 // process a nak for this entry
935 case MULTI_XFER_CODE_NAK :
937 multi_xfer_process_nak(xe);
940 // process a "final" packet
941 case MULTI_XFER_CODE_FINAL :
943 multi_xfer_process_final(xe);
946 // process a data packet
947 case MULTI_XFER_CODE_DATA :
949 multi_xfer_process_data(xe, xfer_data, data_size);
953 case MULTI_XFER_CODE_HEADER :
954 // send on my reliable socket
955 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
961 // process an ack for this entry
962 void multi_xfer_process_ack(xfer_entry *xe)
964 // if we are a sender
965 if(xe->flags & MULTI_XFER_FLAG_SEND){
966 // if we are waiting on a final ack, then the transfer has completed successfully
967 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
968 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
969 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
971 #ifdef MULTI_XFER_VERBOSE
972 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
975 // if we should be auto-destroying this entry, do so
976 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
977 multi_xfer_release_handle(xe - Multi_xfer_entry);
980 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
981 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
982 multi_xfer_send_next(xe);
987 // process a nak for this entry
988 void multi_xfer_process_nak(xfer_entry *xe)
990 // if we get an ack at any time we should simply set the xfer to failed
991 multi_xfer_fail_entry(xe);
994 // process a "final" packet
995 void multi_xfer_process_final(xfer_entry *xe)
999 // make sure we skip a line
1000 nprintf(("Network","\n"));
1003 if(xe->file != NULL){
1009 // check to make sure the file checksum is the same
1011 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
1013 xe->flags |= MULTI_XFER_FLAG_FAIL;
1015 #ifdef MULTI_XFER_VERBOSE
1016 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1020 multi_xfer_abort(xe - Multi_xfer_entry);
1023 // checksums check out, so rename the file and be done with it
1025 #ifdef MULTI_XFER_VERBOSE
1026 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));
1028 // rename the file properly
1029 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1030 // mark the xfer as being successful
1031 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1033 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1035 // send an ack to the sender
1036 multi_xfer_send_ack(xe->file_socket, xe->sig);
1038 // mark it as failing
1039 xe->flags |= MULTI_XFER_FLAG_FAIL;
1040 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1042 // delete the tempfile
1043 cf_delete(xe->ex_filename, xe->force_dir);
1045 // send an nak to the sender
1046 multi_xfer_send_nak(xe->file_socket, xe->sig);
1049 // if we should be auto-destroying this entry, do so
1050 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1051 multi_xfer_release_handle(xe - Multi_xfer_entry);
1056 // process a data packet
1057 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1059 // print out a crude progress indicator
1060 nprintf(("Network","."));
1062 // attempt to write the rest of the data string to the file
1063 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1064 // inform the sender we had a problem
1065 multi_xfer_send_nak(xe->file_socket, xe->sig);
1068 multi_xfer_fail_entry(xe);
1070 xe->file_ptr += data_size;
1074 // increment the file pointer
1075 xe->file_ptr += data_size;
1077 // send an ack to the sender
1078 multi_xfer_send_ack(xe->file_socket, xe->sig);
1081 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1084 // process a header, return bytes processed
1085 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1090 // if the xfer system is locked, send a nak
1091 if(Multi_xfer_locked){
1092 multi_xfer_send_nak(who, sig);
1096 // try and get a free xfer handle
1097 handle = multi_xfer_get_free_handle();
1099 multi_xfer_send_nak(who, sig);
1102 xe = &Multi_xfer_entry[handle];
1103 memset(xe,0,sizeof(xfer_entry));
1106 // set the recv and used flags
1107 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1109 // get the header data
1110 xe->file_size = file_size;
1112 // get the file chksum
1113 xe->file_chksum = file_checksum;
1116 xe->file_socket = who;
1118 // set the timeout timestamp
1119 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1124 // copy the filename and get the prefixed xfer filename
1126 // lower case all filenames to avoid case issues
1127 char *tmp_filename = filename;
1129 strlwr(tmp_filename);
1130 strcpy(xe->filename, tmp_filename);
1132 strcpy(xe->filename, filename);
1134 multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1135 #ifdef MULTI_XFER_VERBOSE
1136 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1139 // determine what directory to place the file in
1140 // individual xfer entries take precedence over the global multi xfer force entry
1141 xe->force_dir = Multi_xfer_force_dir;
1143 // call the callback function
1144 Multi_xfer_recv_notify(handle);
1146 // if the notify function invalidated this xfer handle, then cancel the whole thing
1147 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1148 multi_xfer_send_nak(who, sig);
1151 memset(xe, 0, sizeof(xfer_entry));
1155 // delete the old file (if it exists)
1156 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1157 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1159 // attempt to open the file (using the prefixed filename)
1161 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1162 if(xe->file == NULL){
1163 multi_xfer_send_nak(who, sig);
1166 memset(xe, 0, sizeof(xfer_entry));
1170 // set the waiting for data flag
1171 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1173 // send an ack to the server
1174 multi_xfer_send_ack(who, sig);
1176 #ifdef MULTI_XFER_VERBOSE
1177 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1181 // send the next block of outgoing data or a "final" packet if we're done
1182 void multi_xfer_send_next(xfer_entry *xe)
1184 ubyte data[MAX_PACKET_SIZE],code;
1187 int packet_size = 0;
1189 // print out a crude progress indicator
1190 nprintf(("Network", "+"));
1192 // if we've sent all the data, then we should send a "final" packet
1193 if(xe->file_ptr >= xe->file_size){
1194 // mark the entry as unknown
1195 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1198 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1201 multi_xfer_send_final(xe);
1207 BUILD_HEADER(XFER_PACKET);
1209 // length of the added string
1210 flen = strlen(xe->filename) + 4;
1212 // determine how much data we are going to send with this packet and add it in
1213 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1214 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1216 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1218 // increment the file pointer
1219 xe->file_ptr += data_size;
1222 code = MULTI_XFER_CODE_DATA;
1228 // add in the size of the rest of the packet
1229 ADD_DATA(data_size);
1232 if(cfread(data+packet_size,1,(int)data_size,xe->file) == 0){
1233 // send a nack to the receiver
1234 multi_xfer_send_nak(xe->file_socket, xe->sig);
1237 multi_xfer_fail_entry(xe);
1241 // increment the packet size
1242 packet_size += (int)data_size;
1245 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1247 // otherwise send the data
1248 psnet_rel_send(xe->file_socket, data, packet_size);
1251 // send an ack to the sender
1252 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1254 ubyte data[MAX_PACKET_SIZE],code;
1255 int packet_size = 0;
1257 // build the header and add
1258 BUILD_HEADER(XFER_PACKET);
1261 code = MULTI_XFER_CODE_ACK;
1268 psnet_rel_send(socket, data, packet_size);
1271 // send a nak to the sender
1272 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1274 ubyte data[MAX_PACKET_SIZE],code;
1275 int packet_size = 0;
1277 // build the header and add the code
1278 BUILD_HEADER(XFER_PACKET);
1281 code = MULTI_XFER_CODE_NAK;
1288 psnet_rel_send(socket, data, packet_size);
1291 // send a "final" packet
1292 void multi_xfer_send_final(xfer_entry *xe)
1294 ubyte data[MAX_PACKET_SIZE],code;
1295 int packet_size = 0;
1298 BUILD_HEADER(XFER_PACKET);
1301 code = MULTI_XFER_CODE_FINAL;
1308 psnet_rel_send(xe->file_socket, data, packet_size);
1311 // send the header to begin a file transfer
1312 void multi_xfer_send_header(xfer_entry *xe)
1314 ubyte data[MAX_PACKET_SIZE],code;
1315 int packet_size = 0;
1317 // build the header and add the opcode
1318 BUILD_HEADER(XFER_PACKET);
1319 code = MULTI_XFER_CODE_HEADER;
1326 ADD_STRING(xe->filename);
1329 ADD_DATA(xe->file_size);
1331 // add the file checksum
1332 ADD_DATA(xe->file_chksum);
1335 psnet_rel_send(xe->file_socket, data, packet_size);
1338 // convert the filename into the prefixed ex_filename
1339 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1341 char temp[MAX_FILENAME_LEN+50];
1343 // blast the memory clean
1344 memset(temp, 0, MAX_FILENAME_LEN+50);
1346 // copy in the prefix
1347 strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1349 // stick on the original name
1350 strcat(temp, filename);
1352 // copy the whole thing to the outgoing filename
1353 strcpy(ex_filename, temp);
1356 // get a new xfer sig
1357 ushort multi_xfer_get_sig()
1359 ushort ret = Multi_xfer_sig;
1362 if(Multi_xfer_sig == 0xffff){