2 * $Logfile: /Freespace2/code/Network/multi_xfer.cpp $
8 * Revision 1.4 2002/06/01 07:12:33 relnev
9 * a few NDEBUG updates.
11 * removed a few warnings.
13 * Revision 1.3 2002/05/26 20:22:48 theoddone33
14 * Most of network/ works
16 * Revision 1.2 2002/05/07 03:16:47 theoddone33
17 * The Great Newline Fix
19 * Revision 1.1.1.1 2002/05/03 03:28:10 root
23 * 11 3/10/99 6:50p Dave
24 * Changed the way we buffer packets for all clients. Optimized turret
25 * fired packets. Did some weapon firing optimizations.
27 * 10 3/09/99 6:24p Dave
28 * More work on object update revamping. Identified several sources of
29 * unnecessary bandwidth.
31 * 9 1/24/99 11:37p Dave
32 * First full rev of beam weapons. Very customizable. Removed some bogus
33 * Int3()'s in low level net code.
35 * 8 12/16/98 11:17a Dave
36 * Fixed potential situation where a send and receive to the same player
37 * with the same sig might get confused with each other.
39 * 7 12/14/98 4:01p Dave
40 * Got multi_data stuff working well with new xfer stuff.
42 * 6 12/14/98 12:13p Dave
43 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
46 * 5 11/19/98 8:03a Dave
47 * Full support for D3-style reliable sockets. Revamped packet lag/loss
48 * system, made it receiver side and at the lowest possible level.
50 * 4 11/17/98 11:12a Dave
51 * Removed player identification by address. Now assign explicit id #'s.
53 * 3 11/05/98 5:55p Dave
54 * Big pass at reducing #includes
56 * 2 10/07/98 10:53a Dave
59 * 1 10/07/98 10:50a Dave
61 * 56 9/11/98 9:31a Dave
62 * Fixed file xfer bug regarding checksums.
64 * 55 8/12/98 4:53p Dave
65 * Put in 32 bit checksumming for PXO missions. No validation on the
66 * actual tracker yet, though.
68 * 54 6/13/98 9:32p Mike
69 * Kill last character in file which caused "Find in Files" to report the
70 * file as "not a text file."
72 * 53 6/13/98 6:01p Hoffoss
73 * Externalized all new (or forgot to be added) strings to all the code.
75 * 52 6/13/98 3:19p Hoffoss
76 * NOX()ed out a bunch of strings that shouldn't be translated.
78 * 51 5/21/98 1:52a Dave
79 * Remove obsolete command line functions. Reduce shield explosion packets
80 * drastically. Tweak PXO screen even more. Fix file xfer system so that
81 * we can guarantee file uniqueness.
83 * 50 4/30/98 4:53p John
84 * Restructured and cleaned up cfile code. Added capability to read off
85 * of CD-ROM drive and out of multiple pack files.
87 * 49 4/23/98 6:18p Dave
88 * Store ETS values between respawns. Put kick feature in the text
89 * messaging system. Fixed text messaging system so that it doesn't
90 * process or trigger ship controls. Other UI fixes.
92 * 48 4/22/98 5:53p Dave
93 * Large reworking of endgame sequencing. Updated multi host options
94 * screen for new artwork. Put in checks for host or team captains leaving
97 * 47 4/21/98 4:44p Dave
98 * Implement Vasudan ships in multiplayer. Added a debug function to bash
99 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
100 * problem in options screen.
102 * 46 4/20/98 6:04p Dave
103 * Implement multidata cache flushing and xferring mission files to
104 * multidata. Make sure observers can't change hud config. Fix pilot image
105 * viewing in popup. Put in game status field. Tweaked multi options.
107 * 45 4/08/98 2:51p Dave
108 * Fixed pilot image xfer once again. Solidify team selection process in
109 * pre-briefing multiplayer.
111 * 44 4/04/98 4:22p Dave
112 * First rev of UDP reliable sockets is done. Seems to work well if not
115 * 43 4/03/98 1:03a Dave
116 * First pass at unreliable guaranteed delivery packets.
118 * 42 4/01/98 11:19p Dave
119 * Put in auto-loading of xferred pilot pic files. Grey out background
120 * behind pinfo popup. Put a chatbox message in when players are kicked.
121 * Moved mission title down in briefing. Other ui fixes.
123 * 41 3/31/98 4:51p Dave
124 * Removed medals screen and multiplayer buttons from demo version. Put in
125 * new pilot popup screen. Make ships in mp team vs. team have proper team
126 * ids. Make mp respawns a permanent option saved in the player file.
128 * 40 3/30/98 8:46a Allender
129 * fix an optimized build compiler warning
131 * 39 3/26/98 6:01p Dave
132 * Put in file checksumming routine in cfile. Made pilot pic xferring more
133 * robust. Cut header size of voice data packets in half. Put in
134 * restricted game host query system.
136 * 38 3/25/98 2:16p Dave
137 * Select random default image for newly created pilots. Fixed several
138 * multi-pause messaging bugs. Begin work on online help for multiplayer
141 * 37 3/24/98 5:00p Dave
142 * Fixed several ui bugs. Put in pre and post voice stream playback sound
143 * fx. Put in error specific popups for clients getting dropped from games
144 * through actions other than their own.
146 * 36 3/23/98 5:42p Dave
147 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
148 * that it can support multiplayer sends/received between client and
149 * server simultaneously.
151 * 35 3/21/98 7:14p Dave
152 * Fixed up standalone player slot switching. Made training missions not
153 * count towards player stats.
155 * 34 3/16/98 11:52p Allender
156 * Put in timestamp updates when processing data on both sender and
159 * 33 2/22/98 2:53p Dave
160 * Put in groundwork for advanced multiplayer campaign options.
162 * 32 2/20/98 4:43p Dave
163 * Finished support for multiplayer player data files. Split off
164 * multiplayer campaign functionality.
166 * 31 2/19/98 6:26p Dave
167 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
168 * initial support for player data uploading.
170 * 30 2/18/98 10:21p Dave
171 * Ripped out old file xfer system. Put in brand new xfer system.
180 #include "multi_xfer.h"
182 #include "multimsgs.h"
185 #include "multi_endgame.h"
188 #include "multiutil.h"
189 #include "multi_log.h"
191 // ------------------------------------------------------------------------------------------
192 // MULTI XFER DEFINES/VARS
195 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
197 #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) )
200 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
201 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
202 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
203 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
204 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
207 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
208 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
209 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
210 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
211 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
212 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
213 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
214 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
215 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
216 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
217 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
219 // packet size for file xfer
220 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
222 // timeout for a given xfer operation
223 #define MULTI_XFER_TIMEOUT 10000
227 // temp filename header for xferring files
228 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
232 // xfer entries/events
233 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
234 typedef struct xfer_entry {
235 int flags; // status flags for this entry
236 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
237 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
238 CFILE *file; // file handle of the current xferring file
239 int file_size; // total size of the file being xferred
240 int file_ptr; // total bytes we're received so far
241 ushort file_chksum; // used for checking successfully xferred files
242 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
243 int xfer_stamp; // timestamp for the current operation
244 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
245 ushort sig; // identifying sig - sender specifies this
247 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
249 // callback function pointer for when we start receiving a file
250 void (*Multi_xfer_recv_notify)(int handle);
252 // lock for the xfer system
253 int Multi_xfer_locked;
255 // force directory type for receives
256 int Multi_xfer_force_dir;
258 // unique file signature - this along with a socket # is enough to identify all xfers
259 ushort Multi_xfer_sig = 0;
262 // ------------------------------------------------------------------------------------------
263 // MULTI XFER FORWARD DECLARATIONS
266 // evaluate the condition of the entry
267 void multi_xfer_eval_entry(xfer_entry *xe);
269 // set an entry to be "failed"
270 void multi_xfer_fail_entry(xfer_entry *xe);
272 // get a valid xfer entry handle
273 int multi_xfer_get_free_handle();
275 // process an ack for this entry
276 void multi_xfer_process_ack(xfer_entry *xe);
278 // process a nak for this entry
279 void multi_xfer_process_nak(xfer_entry *xe);
281 // process a "final" packet
282 void multi_xfer_process_final(xfer_entry *xe);
284 // process a data packet
285 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
288 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
290 // send the next block of outgoing data or a "final" packet if we're done
291 void multi_xfer_send_next(xfer_entry *xe);
293 // send an ack to the sender
294 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
296 // send a nak to the sender
297 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
299 // send a "final" packet
300 void multi_xfer_send_final(xfer_entry *xe);
302 // send the header to begin a file transfer
303 void multi_xfer_send_header(xfer_entry *xe);
305 // convert the filename into the prefixed ex_filename
306 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
308 // get a new xfer sig
309 ushort multi_xfer_get_sig();
311 // ------------------------------------------------------------------------------------------
312 // MULTI XFER FUNCTIONS
315 // initialize all file xfer transaction stuff, call in multi_level_init()
316 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
318 // blast all the entries
319 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
321 // assign the receive callback function pointer
322 Multi_xfer_recv_notify = multi_xfer_recv_callback;
325 Multi_xfer_locked = 0;
327 // no forced directory
328 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
331 // do frame for all file xfers, call in multi_do_frame()
336 // process all valid xfer entries
337 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
338 // if this one is actually in use and has not finished for one reason or another
339 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))){
340 // evaluate the condition of this entry (fail, timeout, etc)
341 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
346 // close down the file xfer system
347 void multi_xfer_close()
351 // go through all active entries and abort them
352 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
353 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
354 multi_xfer_abort(idx);
358 // now blast all the memory free
359 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
362 // reset the xfer system, including shutting down/killing all active xfers
363 void multi_xfer_reset()
367 // shut down all active xfers
368 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
369 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
370 multi_xfer_abort(idx);
374 // blast all the memory clean
375 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
378 // send a file to the specified player, return a handle
379 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
381 xfer_entry temp_entry;
384 // if the system is locked, return -1
385 if(Multi_xfer_locked){
389 // attempt to get a free handle
390 handle = multi_xfer_get_free_handle();
395 // clear the temp entry
396 memset(&temp_entry,0,sizeof(xfer_entry));
399 strcpy(temp_entry.filename,filename);
401 // attempt to open the file
402 temp_entry.file = NULL;
403 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
404 if(temp_entry.file == NULL){
405 #ifdef MULTI_XFER_VERBOSE
406 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
413 temp_entry.file_size = -1;
414 temp_entry.file_size = cfilelength(temp_entry.file);
415 if(temp_entry.file_size == -1){
416 #ifdef MULTI_XFER_VERBOSE
417 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
421 temp_entry.file_ptr = 0;
423 // get the file checksum
424 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
425 #ifdef MULTI_XFER_VERBOSE
426 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
430 #ifdef MULTI_XFER_VERBOSE
431 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
433 // rewind the file pointer to the beginning of the file
434 cfseek(temp_entry.file,0,CF_SEEK_SET);
437 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
438 temp_entry.flags |= flags;
441 temp_entry.file_socket = who;
444 temp_entry.sig = multi_xfer_get_sig();
446 // copy to the global array
447 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
448 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
453 // get the status of the current file xfer
454 int multi_xfer_get_status(int handle)
456 // if this is an invalid or an unused handle, notify as such
457 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
458 return MULTI_XFER_NONE;
461 // if the xfer has timed-out
462 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
463 return MULTI_XFER_TIMEDOUT;
466 // if the xfer has failed for one reason or another (not timeout)
467 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
468 return MULTI_XFER_FAIL;
471 // if the xfer has succeeded
472 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
473 return MULTI_XFER_SUCCESS;
476 // if the xfer is queued
477 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
478 return MULTI_XFER_QUEUED;
481 // otherwise the xfer is still in progress
482 return MULTI_XFER_IN_PROGRESS;
485 // abort a transferring file
486 void multi_xfer_abort(int handle)
490 // don't do anything if this is an invalid handle
491 if(MULTI_XFER_INVALID_HANDLE(handle)){
495 // get e handle to the entry
496 xe = &Multi_xfer_entry[handle];
498 // close any open file and delete it
499 if(xe->file != NULL){
503 // delete it if there isn't some problem with the filename
504 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
505 cf_delete(xe->ex_filename, xe->force_dir);
510 xe->file_socket = INVALID_SOCKET;
513 memset(xe,0,sizeof(xfer_entry));
516 // release an xfer handle
517 void multi_xfer_release_handle(int handle)
521 // don't do anything if this is an invalid handle
522 if(MULTI_XFER_INVALID_HANDLE(handle)){
526 // get e handle to the entry
527 xe = &Multi_xfer_entry[handle];
529 // close any open file and delete it
530 if(xe->file != NULL){
534 // delete it if the file was not successfully received
535 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
536 cf_delete(xe->ex_filename,xe->force_dir);
541 xe->file_socket = INVALID_SOCKET;
544 memset(xe,0,sizeof(xfer_entry));
547 // get the filename of the xfer for the given handle
548 char *multi_xfer_get_filename(int handle)
550 // if this is an invalid handle, return NULL
551 if(MULTI_XFER_INVALID_HANDLE(handle)){
555 // otherwise return the string
556 return Multi_xfer_entry[handle].filename;
559 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
560 void multi_xfer_lock()
562 Multi_xfer_locked = 1;
565 // unlock the xfer system
566 void multi_xfer_unlock()
568 Multi_xfer_locked = 0;
571 // force all receives to go into the specified directory by cfile type
572 void multi_xfer_force_dir(int cf_type)
574 Multi_xfer_force_dir = cf_type;
575 Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
578 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
579 void multi_xfer_handle_force_dir(int handle,int cf_type)
581 // if this is an invalid handle, return NULL
582 if(MULTI_XFER_INVALID_HANDLE(handle)){
586 // force to go to the given directory
587 Multi_xfer_entry[handle].force_dir = cf_type;
588 Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
591 // or the flag on a given entry
592 void multi_xfer_xor_flags(int handle,int flags)
594 // if this is an invalid handle, return NULL
595 if(MULTI_XFER_INVALID_HANDLE(handle)){
600 Multi_xfer_entry[handle].flags ^= flags;
603 // get the flags for a given entry
604 int multi_xfer_get_flags(int handle)
606 // if this is an invalid handle, return NULL
607 if(MULTI_XFER_INVALID_HANDLE(handle)){
612 return Multi_xfer_entry[handle].flags;
615 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
616 int multi_xfer_lookup(char *filename)
620 // if we have an invalid filename, do nothing
621 if((filename == NULL) || (strlen(filename) <= 0)){
625 // otherwise, perform a lookup
626 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
627 // if we found a matching filename
628 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
633 // did not find a match
637 // get the % of completion of the passed file handle, return < 0 if invalid
638 float multi_xfer_pct_complete(int handle)
640 // if this is an invalid handle, return invalid
641 if(MULTI_XFER_INVALID_HANDLE(handle)){
645 // if the file size is 0, return invalid
646 if(Multi_xfer_entry[handle].file_size == 0){
650 // return the pct completion
651 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
654 // get the socket of the file xfer (useful for identifying players)
655 uint multi_xfer_get_sock(int handle)
657 // if this is an invalid handle, return NULL
658 if(MULTI_XFER_INVALID_HANDLE(handle)){
659 return INVALID_SOCKET;
662 return Multi_xfer_entry[handle].file_socket;
665 // get the CF_TYPE of the directory this file is going to
666 int multi_xfer_get_force_dir(int handle)
668 // if this is an invalid handle, return NULL
669 if(MULTI_XFER_INVALID_HANDLE(handle)){
670 return INVALID_SOCKET;
673 return Multi_xfer_entry[handle].force_dir;
677 // ------------------------------------------------------------------------------------------
678 // MULTI XFER FORWARD DECLARATIONS
681 // evaluate the condition of the entry
682 void multi_xfer_eval_entry(xfer_entry *xe)
688 // if the entry is marked as successful, then don't do anything
689 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
693 // if the entry is queued
694 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
695 // if the entry is not current
696 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
697 // see if there are any other queued up xfers to this target. if not, make me current and start sending
699 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
700 xe_c = &Multi_xfer_entry[idx];
702 // if this is a valid entry and is a queued entry and is going to the same target
703 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
704 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
711 // if we found no other entries, make this guy current and pending
713 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
714 xe->flags |= MULTI_XFER_FLAG_PENDING;
716 #ifdef MULTI_XFER_VERBOSE
717 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
720 // otherwise, do nothing for him - he has to still wait
727 // if the entry is marked as pending - send out the header to get the ball rolling
728 if(xe->flags & MULTI_XFER_FLAG_PENDING){
729 // send the header to begin the transfer
730 multi_xfer_send_header(xe);
733 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
735 // unset the pending flag
736 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
738 // set the ack/wait flag
739 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
742 // see if the entry has timed-out for one reason or another
743 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
744 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
746 // if we should be auto-destroying this entry, do so
747 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
748 multi_xfer_fail_entry(xe);
753 // lookup a file xfer entry by player
754 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
758 // look through all valid xfer entries
759 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
760 // if we're looking for sending entries
761 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
764 // if we're looking for recv entries
765 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
769 // if we found a match
770 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
771 return &Multi_xfer_entry[idx];
778 // set an entry to be "failed"
779 void multi_xfer_fail_entry(xfer_entry *xe)
781 // set its flags appropriately
782 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
783 xe->flags |= MULTI_XFER_FLAG_FAIL;
785 // close the file pointer
786 if(xe->file != NULL){
792 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
793 cf_delete(xe->ex_filename,xe->force_dir);
796 // null the timestamp
799 // if we should be auto-destroying this entry, do so
800 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
801 multi_xfer_release_handle(xe - Multi_xfer_entry);
804 // blast the memory clean
805 memset(xe,0,sizeof(xfer_entry));
808 // get a valid xfer entry handle
809 int multi_xfer_get_free_handle()
813 // look for a free entry
814 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
815 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
824 // ------------------------------------------------------------------------------------------
825 // MULTI XFER PACKET HANDLERS
828 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
829 // packet regardless of error conditions
830 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
835 ushort data_size = 0;
837 ushort file_checksum = 0;
839 ubyte xfer_data[600];
843 // read in all packet data
848 case MULTI_XFER_CODE_DATA:
850 memcpy(xfer_data, data + offset, data_size);
856 case MULTI_XFER_CODE_HEADER:
857 GET_STRING(filename);
859 GET_DATA(file_checksum);
864 case MULTI_XFER_CODE_ACK:
865 case MULTI_XFER_CODE_NAK:
869 case MULTI_XFER_CODE_FINAL:
877 // at this point we've read all the data in the packet
879 // at this point, we should process code-specific data
881 if(val != MULTI_XFER_CODE_HEADER){
882 // if the code is not a request or a header, we need to look up the existing xfer_entry
885 xe = multi_xfer_find_entry(who, sig, sender_side);
887 #ifdef MULTI_XFER_VERBOSE
888 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
890 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
892 int np_index = find_player_socket(who);
893 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
894 ml_printf(": sig == %d", sig);
895 ml_printf(": xfer header == %d", val);
897 ml_string(": player == unknown");
899 ml_printf(": player == %s", Net_players[np_index].player->callsign);
902 ml_string(": sending");
904 ml_string(": receiving");
914 // process an ack for this entry
915 case MULTI_XFER_CODE_ACK :
917 multi_xfer_process_ack(xe);
920 // process a nak for this entry
921 case MULTI_XFER_CODE_NAK :
923 multi_xfer_process_nak(xe);
926 // process a "final" packet
927 case MULTI_XFER_CODE_FINAL :
929 multi_xfer_process_final(xe);
932 // process a data packet
933 case MULTI_XFER_CODE_DATA :
935 multi_xfer_process_data(xe, xfer_data, data_size);
939 case MULTI_XFER_CODE_HEADER :
940 // send on my reliable socket
941 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
947 // process an ack for this entry
948 void multi_xfer_process_ack(xfer_entry *xe)
950 // if we are a sender
951 if(xe->flags & MULTI_XFER_FLAG_SEND){
952 // if we are waiting on a final ack, then the transfer has completed successfully
953 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
954 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
955 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
957 #ifdef MULTI_XFER_VERBOSE
958 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
961 // if we should be auto-destroying this entry, do so
962 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
963 multi_xfer_release_handle(xe - Multi_xfer_entry);
966 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
967 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
968 multi_xfer_send_next(xe);
973 // process a nak for this entry
974 void multi_xfer_process_nak(xfer_entry *xe)
976 // if we get an ack at any time we should simply set the xfer to failed
977 multi_xfer_fail_entry(xe);
980 // process a "final" packet
981 void multi_xfer_process_final(xfer_entry *xe)
985 // make sure we skip a line
986 nprintf(("Network","\n"));
989 if(xe->file != NULL){
995 // check to make sure the file checksum is the same
997 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
999 xe->flags |= MULTI_XFER_FLAG_FAIL;
1001 #ifdef MULTI_XFER_VERBOSE
1002 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1006 multi_xfer_abort(xe - Multi_xfer_entry);
1009 // checksums check out, so rename the file and be done with it
1011 #ifdef MULTI_XFER_VERBOSE
1012 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));
1014 // rename the file properly
1015 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1016 // mark the xfer as being successful
1017 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1019 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1021 // send an ack to the sender
1022 multi_xfer_send_ack(xe->file_socket, xe->sig);
1024 // mark it as failing
1025 xe->flags |= MULTI_XFER_FLAG_FAIL;
1026 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1028 // delete the tempfile
1029 cf_delete(xe->ex_filename, xe->force_dir);
1031 // send an nak to the sender
1032 multi_xfer_send_nak(xe->file_socket, xe->sig);
1035 // if we should be auto-destroying this entry, do so
1036 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1037 multi_xfer_release_handle(xe - Multi_xfer_entry);
1042 // process a data packet
1043 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1045 // print out a crude progress indicator
1046 nprintf(("Network","."));
1048 // attempt to write the rest of the data string to the file
1049 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1050 // inform the sender we had a problem
1051 multi_xfer_send_nak(xe->file_socket, xe->sig);
1054 multi_xfer_fail_entry(xe);
1056 xe->file_ptr += data_size;
1060 // increment the file pointer
1061 xe->file_ptr += data_size;
1063 // send an ack to the sender
1064 multi_xfer_send_ack(xe->file_socket, xe->sig);
1067 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1070 // process a header, return bytes processed
1071 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1076 // if the xfer system is locked, send a nak
1077 if(Multi_xfer_locked){
1078 multi_xfer_send_nak(who, sig);
1082 // try and get a free xfer handle
1083 handle = multi_xfer_get_free_handle();
1085 multi_xfer_send_nak(who, sig);
1088 xe = &Multi_xfer_entry[handle];
1089 memset(xe,0,sizeof(xfer_entry));
1092 // set the recv and used flags
1093 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1095 // get the header data
1096 xe->file_size = file_size;
1098 // get the file chksum
1099 xe->file_chksum = file_checksum;
1102 xe->file_socket = who;
1104 // set the timeout timestamp
1105 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1110 // copy the filename and get the prefixed xfer filename
1111 strcpy(xe->filename, filename);
1112 multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1113 #ifdef MULTI_XFER_VERBOSE
1114 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1117 // determine what directory to place the file in
1118 // individual xfer entries take precedence over the global multi xfer force entry
1119 xe->force_dir = Multi_xfer_force_dir;
1121 // call the callback function
1122 Multi_xfer_recv_notify(handle);
1124 // if the notify function invalidated this xfer handle, then cancel the whole thing
1125 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1126 multi_xfer_send_nak(who, sig);
1129 memset(xe, 0, sizeof(xfer_entry));
1133 // delete the old file (if it exists)
1134 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1135 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1137 // attempt to open the file (using the prefixed filename)
1139 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1140 if(xe->file == NULL){
1141 multi_xfer_send_nak(who, sig);
1144 memset(xe, 0, sizeof(xfer_entry));
1148 // set the waiting for data flag
1149 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1151 // send an ack to the server
1152 multi_xfer_send_ack(who, sig);
1154 #ifdef MULTI_XFER_VERBOSE
1155 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1159 // send the next block of outgoing data or a "final" packet if we're done
1160 void multi_xfer_send_next(xfer_entry *xe)
1162 ubyte data[MAX_PACKET_SIZE],code;
1165 int packet_size = 0;
1167 // print out a crude progress indicator
1168 nprintf(("Network", "+"));
1170 // if we've sent all the data, then we should send a "final" packet
1171 if(xe->file_ptr >= xe->file_size){
1172 // mark the entry as unknown
1173 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1176 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1179 multi_xfer_send_final(xe);
1185 BUILD_HEADER(XFER_PACKET);
1187 // length of the added string
1188 flen = strlen(xe->filename) + 4;
1190 // determine how much data we are going to send with this packet and add it in
1191 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1192 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1194 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1196 // increment the file pointer
1197 xe->file_ptr += data_size;
1200 code = MULTI_XFER_CODE_DATA;
1206 // add in the size of the rest of the packet
1207 ADD_DATA(data_size);
1210 if(cfread(data+packet_size,1,(int)data_size,xe->file) == 0){
1211 // send a nack to the receiver
1212 multi_xfer_send_nak(xe->file_socket, xe->sig);
1215 multi_xfer_fail_entry(xe);
1219 // increment the packet size
1220 packet_size += (int)data_size;
1223 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1225 // otherwise send the data
1226 psnet_rel_send(xe->file_socket, data, packet_size);
1229 // send an ack to the sender
1230 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1232 ubyte data[MAX_PACKET_SIZE],code;
1233 int packet_size = 0;
1235 // build the header and add
1236 BUILD_HEADER(XFER_PACKET);
1239 code = MULTI_XFER_CODE_ACK;
1246 psnet_rel_send(socket, data, packet_size);
1249 // send a nak to the sender
1250 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1252 ubyte data[MAX_PACKET_SIZE],code;
1253 int packet_size = 0;
1255 // build the header and add the code
1256 BUILD_HEADER(XFER_PACKET);
1259 code = MULTI_XFER_CODE_NAK;
1266 psnet_rel_send(socket, data, packet_size);
1269 // send a "final" packet
1270 void multi_xfer_send_final(xfer_entry *xe)
1272 ubyte data[MAX_PACKET_SIZE],code;
1273 int packet_size = 0;
1276 BUILD_HEADER(XFER_PACKET);
1279 code = MULTI_XFER_CODE_FINAL;
1286 psnet_rel_send(xe->file_socket, data, packet_size);
1289 // send the header to begin a file transfer
1290 void multi_xfer_send_header(xfer_entry *xe)
1292 ubyte data[MAX_PACKET_SIZE],code;
1293 int packet_size = 0;
1295 // build the header and add the opcode
1296 BUILD_HEADER(XFER_PACKET);
1297 code = MULTI_XFER_CODE_HEADER;
1304 ADD_STRING(xe->filename);
1307 ADD_DATA(xe->file_size);
1309 // add the file checksum
1310 ADD_DATA(xe->file_chksum);
1313 psnet_rel_send(xe->file_socket, data, packet_size);
1316 // convert the filename into the prefixed ex_filename
1317 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1319 char temp[MAX_FILENAME_LEN+50];
1321 // blast the memory clean
1322 memset(temp, 0, MAX_FILENAME_LEN+50);
1324 // copy in the prefix
1325 strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1327 // stick on the original name
1328 strcat(temp, filename);
1330 // copy the whole thing to the outgoing filename
1331 strcpy(ex_filename, temp);
1334 // get a new xfer sig
1335 ushort multi_xfer_get_sig()
1337 ushort ret = Multi_xfer_sig;
1340 if(Multi_xfer_sig == 0xffff){