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.5 2002/06/09 04:41:24 relnev
17 * added copyright header
19 * Revision 1.4 2002/06/01 07:12:33 relnev
20 * a few NDEBUG updates.
22 * removed a few warnings.
24 * Revision 1.3 2002/05/26 20:22:48 theoddone33
25 * Most of network/ works
27 * Revision 1.2 2002/05/07 03:16:47 theoddone33
28 * The Great Newline Fix
30 * Revision 1.1.1.1 2002/05/03 03:28:10 root
34 * 11 3/10/99 6:50p Dave
35 * Changed the way we buffer packets for all clients. Optimized turret
36 * fired packets. Did some weapon firing optimizations.
38 * 10 3/09/99 6:24p Dave
39 * More work on object update revamping. Identified several sources of
40 * unnecessary bandwidth.
42 * 9 1/24/99 11:37p Dave
43 * First full rev of beam weapons. Very customizable. Removed some bogus
44 * Int3()'s in low level net code.
46 * 8 12/16/98 11:17a Dave
47 * Fixed potential situation where a send and receive to the same player
48 * with the same sig might get confused with each other.
50 * 7 12/14/98 4:01p Dave
51 * Got multi_data stuff working well with new xfer stuff.
53 * 6 12/14/98 12:13p Dave
54 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
57 * 5 11/19/98 8:03a Dave
58 * Full support for D3-style reliable sockets. Revamped packet lag/loss
59 * system, made it receiver side and at the lowest possible level.
61 * 4 11/17/98 11:12a Dave
62 * Removed player identification by address. Now assign explicit id #'s.
64 * 3 11/05/98 5:55p Dave
65 * Big pass at reducing #includes
67 * 2 10/07/98 10:53a Dave
70 * 1 10/07/98 10:50a Dave
72 * 56 9/11/98 9:31a Dave
73 * Fixed file xfer bug regarding checksums.
75 * 55 8/12/98 4:53p Dave
76 * Put in 32 bit checksumming for PXO missions. No validation on the
77 * actual tracker yet, though.
79 * 54 6/13/98 9:32p Mike
80 * Kill last character in file which caused "Find in Files" to report the
81 * file as "not a text file."
83 * 53 6/13/98 6:01p Hoffoss
84 * Externalized all new (or forgot to be added) strings to all the code.
86 * 52 6/13/98 3:19p Hoffoss
87 * NOX()ed out a bunch of strings that shouldn't be translated.
89 * 51 5/21/98 1:52a Dave
90 * Remove obsolete command line functions. Reduce shield explosion packets
91 * drastically. Tweak PXO screen even more. Fix file xfer system so that
92 * we can guarantee file uniqueness.
94 * 50 4/30/98 4:53p John
95 * Restructured and cleaned up cfile code. Added capability to read off
96 * of CD-ROM drive and out of multiple pack files.
98 * 49 4/23/98 6:18p Dave
99 * Store ETS values between respawns. Put kick feature in the text
100 * messaging system. Fixed text messaging system so that it doesn't
101 * process or trigger ship controls. Other UI fixes.
103 * 48 4/22/98 5:53p Dave
104 * Large reworking of endgame sequencing. Updated multi host options
105 * screen for new artwork. Put in checks for host or team captains leaving
108 * 47 4/21/98 4:44p Dave
109 * Implement Vasudan ships in multiplayer. Added a debug function to bash
110 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
111 * problem in options screen.
113 * 46 4/20/98 6:04p Dave
114 * Implement multidata cache flushing and xferring mission files to
115 * multidata. Make sure observers can't change hud config. Fix pilot image
116 * viewing in popup. Put in game status field. Tweaked multi options.
118 * 45 4/08/98 2:51p Dave
119 * Fixed pilot image xfer once again. Solidify team selection process in
120 * pre-briefing multiplayer.
122 * 44 4/04/98 4:22p Dave
123 * First rev of UDP reliable sockets is done. Seems to work well if not
126 * 43 4/03/98 1:03a Dave
127 * First pass at unreliable guaranteed delivery packets.
129 * 42 4/01/98 11:19p Dave
130 * Put in auto-loading of xferred pilot pic files. Grey out background
131 * behind pinfo popup. Put a chatbox message in when players are kicked.
132 * Moved mission title down in briefing. Other ui fixes.
134 * 41 3/31/98 4:51p Dave
135 * Removed medals screen and multiplayer buttons from demo version. Put in
136 * new pilot popup screen. Make ships in mp team vs. team have proper team
137 * ids. Make mp respawns a permanent option saved in the player file.
139 * 40 3/30/98 8:46a Allender
140 * fix an optimized build compiler warning
142 * 39 3/26/98 6:01p Dave
143 * Put in file checksumming routine in cfile. Made pilot pic xferring more
144 * robust. Cut header size of voice data packets in half. Put in
145 * restricted game host query system.
147 * 38 3/25/98 2:16p Dave
148 * Select random default image for newly created pilots. Fixed several
149 * multi-pause messaging bugs. Begin work on online help for multiplayer
152 * 37 3/24/98 5:00p Dave
153 * Fixed several ui bugs. Put in pre and post voice stream playback sound
154 * fx. Put in error specific popups for clients getting dropped from games
155 * through actions other than their own.
157 * 36 3/23/98 5:42p Dave
158 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
159 * that it can support multiplayer sends/received between client and
160 * server simultaneously.
162 * 35 3/21/98 7:14p Dave
163 * Fixed up standalone player slot switching. Made training missions not
164 * count towards player stats.
166 * 34 3/16/98 11:52p Allender
167 * Put in timestamp updates when processing data on both sender and
170 * 33 2/22/98 2:53p Dave
171 * Put in groundwork for advanced multiplayer campaign options.
173 * 32 2/20/98 4:43p Dave
174 * Finished support for multiplayer player data files. Split off
175 * multiplayer campaign functionality.
177 * 31 2/19/98 6:26p Dave
178 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
179 * initial support for player data uploading.
181 * 30 2/18/98 10:21p Dave
182 * Ripped out old file xfer system. Put in brand new xfer system.
191 #include "multi_xfer.h"
193 #include "multimsgs.h"
196 #include "multi_endgame.h"
199 #include "multiutil.h"
200 #include "multi_log.h"
202 // ------------------------------------------------------------------------------------------
203 // MULTI XFER DEFINES/VARS
206 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
208 #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) )
211 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
212 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
213 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
214 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
215 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
218 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
219 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
220 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
221 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
222 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
223 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
224 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
225 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
226 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
227 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
228 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
230 // packet size for file xfer
231 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
233 // timeout for a given xfer operation
234 #define MULTI_XFER_TIMEOUT 10000
238 // temp filename header for xferring files
239 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
243 // xfer entries/events
244 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
245 typedef struct xfer_entry {
246 int flags; // status flags for this entry
247 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
248 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
249 CFILE *file; // file handle of the current xferring file
250 int file_size; // total size of the file being xferred
251 int file_ptr; // total bytes we're received so far
252 ushort file_chksum; // used for checking successfully xferred files
253 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
254 int xfer_stamp; // timestamp for the current operation
255 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
256 ushort sig; // identifying sig - sender specifies this
258 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
260 // callback function pointer for when we start receiving a file
261 void (*Multi_xfer_recv_notify)(int handle);
263 // lock for the xfer system
264 int Multi_xfer_locked;
266 // force directory type for receives
267 int Multi_xfer_force_dir;
269 // unique file signature - this along with a socket # is enough to identify all xfers
270 ushort Multi_xfer_sig = 0;
273 // ------------------------------------------------------------------------------------------
274 // MULTI XFER FORWARD DECLARATIONS
277 // evaluate the condition of the entry
278 void multi_xfer_eval_entry(xfer_entry *xe);
280 // set an entry to be "failed"
281 void multi_xfer_fail_entry(xfer_entry *xe);
283 // get a valid xfer entry handle
284 int multi_xfer_get_free_handle();
286 // process an ack for this entry
287 void multi_xfer_process_ack(xfer_entry *xe);
289 // process a nak for this entry
290 void multi_xfer_process_nak(xfer_entry *xe);
292 // process a "final" packet
293 void multi_xfer_process_final(xfer_entry *xe);
295 // process a data packet
296 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
299 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
301 // send the next block of outgoing data or a "final" packet if we're done
302 void multi_xfer_send_next(xfer_entry *xe);
304 // send an ack to the sender
305 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
307 // send a nak to the sender
308 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
310 // send a "final" packet
311 void multi_xfer_send_final(xfer_entry *xe);
313 // send the header to begin a file transfer
314 void multi_xfer_send_header(xfer_entry *xe);
316 // convert the filename into the prefixed ex_filename
317 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
319 // get a new xfer sig
320 ushort multi_xfer_get_sig();
322 // ------------------------------------------------------------------------------------------
323 // MULTI XFER FUNCTIONS
326 // initialize all file xfer transaction stuff, call in multi_level_init()
327 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
329 // blast all the entries
330 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
332 // assign the receive callback function pointer
333 Multi_xfer_recv_notify = multi_xfer_recv_callback;
336 Multi_xfer_locked = 0;
338 // no forced directory
339 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
342 // do frame for all file xfers, call in multi_do_frame()
347 // process all valid xfer entries
348 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
349 // if this one is actually in use and has not finished for one reason or another
350 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))){
351 // evaluate the condition of this entry (fail, timeout, etc)
352 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
357 // close down the file xfer system
358 void multi_xfer_close()
362 // go through all active entries and abort them
363 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
364 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
365 multi_xfer_abort(idx);
369 // now blast all the memory free
370 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
373 // reset the xfer system, including shutting down/killing all active xfers
374 void multi_xfer_reset()
378 // shut down all active xfers
379 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
380 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
381 multi_xfer_abort(idx);
385 // blast all the memory clean
386 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
389 // send a file to the specified player, return a handle
390 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
392 xfer_entry temp_entry;
395 // if the system is locked, return -1
396 if(Multi_xfer_locked){
400 // attempt to get a free handle
401 handle = multi_xfer_get_free_handle();
406 // clear the temp entry
407 memset(&temp_entry,0,sizeof(xfer_entry));
410 strcpy(temp_entry.filename,filename);
412 // attempt to open the file
413 temp_entry.file = NULL;
414 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
415 if(temp_entry.file == NULL){
416 #ifdef MULTI_XFER_VERBOSE
417 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
424 temp_entry.file_size = -1;
425 temp_entry.file_size = cfilelength(temp_entry.file);
426 if(temp_entry.file_size == -1){
427 #ifdef MULTI_XFER_VERBOSE
428 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
432 temp_entry.file_ptr = 0;
434 // get the file checksum
435 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
436 #ifdef MULTI_XFER_VERBOSE
437 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
441 #ifdef MULTI_XFER_VERBOSE
442 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
444 // rewind the file pointer to the beginning of the file
445 cfseek(temp_entry.file,0,CF_SEEK_SET);
448 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
449 temp_entry.flags |= flags;
452 temp_entry.file_socket = who;
455 temp_entry.sig = multi_xfer_get_sig();
457 // copy to the global array
458 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
459 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
464 // get the status of the current file xfer
465 int multi_xfer_get_status(int handle)
467 // if this is an invalid or an unused handle, notify as such
468 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
469 return MULTI_XFER_NONE;
472 // if the xfer has timed-out
473 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
474 return MULTI_XFER_TIMEDOUT;
477 // if the xfer has failed for one reason or another (not timeout)
478 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
479 return MULTI_XFER_FAIL;
482 // if the xfer has succeeded
483 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
484 return MULTI_XFER_SUCCESS;
487 // if the xfer is queued
488 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
489 return MULTI_XFER_QUEUED;
492 // otherwise the xfer is still in progress
493 return MULTI_XFER_IN_PROGRESS;
496 // abort a transferring file
497 void multi_xfer_abort(int handle)
501 // don't do anything if this is an invalid handle
502 if(MULTI_XFER_INVALID_HANDLE(handle)){
506 // get e handle to the entry
507 xe = &Multi_xfer_entry[handle];
509 // close any open file and delete it
510 if(xe->file != NULL){
514 // delete it if there isn't some problem with the filename
515 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
516 cf_delete(xe->ex_filename, xe->force_dir);
521 xe->file_socket = INVALID_SOCKET;
524 memset(xe,0,sizeof(xfer_entry));
527 // release an xfer handle
528 void multi_xfer_release_handle(int handle)
532 // don't do anything if this is an invalid handle
533 if(MULTI_XFER_INVALID_HANDLE(handle)){
537 // get e handle to the entry
538 xe = &Multi_xfer_entry[handle];
540 // close any open file and delete it
541 if(xe->file != NULL){
545 // delete it if the file was not successfully received
546 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
547 cf_delete(xe->ex_filename,xe->force_dir);
552 xe->file_socket = INVALID_SOCKET;
555 memset(xe,0,sizeof(xfer_entry));
558 // get the filename of the xfer for the given handle
559 char *multi_xfer_get_filename(int handle)
561 // if this is an invalid handle, return NULL
562 if(MULTI_XFER_INVALID_HANDLE(handle)){
566 // otherwise return the string
567 return Multi_xfer_entry[handle].filename;
570 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
571 void multi_xfer_lock()
573 Multi_xfer_locked = 1;
576 // unlock the xfer system
577 void multi_xfer_unlock()
579 Multi_xfer_locked = 0;
582 // force all receives to go into the specified directory by cfile type
583 void multi_xfer_force_dir(int cf_type)
585 Multi_xfer_force_dir = cf_type;
586 Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
589 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
590 void multi_xfer_handle_force_dir(int handle,int cf_type)
592 // if this is an invalid handle, return NULL
593 if(MULTI_XFER_INVALID_HANDLE(handle)){
597 // force to go to the given directory
598 Multi_xfer_entry[handle].force_dir = cf_type;
599 Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
602 // or the flag on a given entry
603 void multi_xfer_xor_flags(int handle,int flags)
605 // if this is an invalid handle, return NULL
606 if(MULTI_XFER_INVALID_HANDLE(handle)){
611 Multi_xfer_entry[handle].flags ^= flags;
614 // get the flags for a given entry
615 int multi_xfer_get_flags(int handle)
617 // if this is an invalid handle, return NULL
618 if(MULTI_XFER_INVALID_HANDLE(handle)){
623 return Multi_xfer_entry[handle].flags;
626 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
627 int multi_xfer_lookup(char *filename)
631 // if we have an invalid filename, do nothing
632 if((filename == NULL) || (strlen(filename) <= 0)){
636 // otherwise, perform a lookup
637 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
638 // if we found a matching filename
639 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
644 // did not find a match
648 // get the % of completion of the passed file handle, return < 0 if invalid
649 float multi_xfer_pct_complete(int handle)
651 // if this is an invalid handle, return invalid
652 if(MULTI_XFER_INVALID_HANDLE(handle)){
656 // if the file size is 0, return invalid
657 if(Multi_xfer_entry[handle].file_size == 0){
661 // return the pct completion
662 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
665 // get the socket of the file xfer (useful for identifying players)
666 uint multi_xfer_get_sock(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].file_socket;
676 // get the CF_TYPE of the directory this file is going to
677 int multi_xfer_get_force_dir(int handle)
679 // if this is an invalid handle, return NULL
680 if(MULTI_XFER_INVALID_HANDLE(handle)){
681 return INVALID_SOCKET;
684 return Multi_xfer_entry[handle].force_dir;
688 // ------------------------------------------------------------------------------------------
689 // MULTI XFER FORWARD DECLARATIONS
692 // evaluate the condition of the entry
693 void multi_xfer_eval_entry(xfer_entry *xe)
699 // if the entry is marked as successful, then don't do anything
700 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
704 // if the entry is queued
705 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
706 // if the entry is not current
707 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
708 // see if there are any other queued up xfers to this target. if not, make me current and start sending
710 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
711 xe_c = &Multi_xfer_entry[idx];
713 // if this is a valid entry and is a queued entry and is going to the same target
714 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
715 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
722 // if we found no other entries, make this guy current and pending
724 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
725 xe->flags |= MULTI_XFER_FLAG_PENDING;
727 #ifdef MULTI_XFER_VERBOSE
728 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
731 // otherwise, do nothing for him - he has to still wait
738 // if the entry is marked as pending - send out the header to get the ball rolling
739 if(xe->flags & MULTI_XFER_FLAG_PENDING){
740 // send the header to begin the transfer
741 multi_xfer_send_header(xe);
744 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
746 // unset the pending flag
747 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
749 // set the ack/wait flag
750 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
753 // see if the entry has timed-out for one reason or another
754 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
755 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
757 // if we should be auto-destroying this entry, do so
758 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
759 multi_xfer_fail_entry(xe);
764 // lookup a file xfer entry by player
765 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
769 // look through all valid xfer entries
770 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
771 // if we're looking for sending entries
772 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
775 // if we're looking for recv entries
776 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
780 // if we found a match
781 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
782 return &Multi_xfer_entry[idx];
789 // set an entry to be "failed"
790 void multi_xfer_fail_entry(xfer_entry *xe)
792 // set its flags appropriately
793 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
794 xe->flags |= MULTI_XFER_FLAG_FAIL;
796 // close the file pointer
797 if(xe->file != NULL){
803 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
804 cf_delete(xe->ex_filename,xe->force_dir);
807 // null the timestamp
810 // if we should be auto-destroying this entry, do so
811 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
812 multi_xfer_release_handle(xe - Multi_xfer_entry);
815 // blast the memory clean
816 memset(xe,0,sizeof(xfer_entry));
819 // get a valid xfer entry handle
820 int multi_xfer_get_free_handle()
824 // look for a free entry
825 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
826 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
835 // ------------------------------------------------------------------------------------------
836 // MULTI XFER PACKET HANDLERS
839 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
840 // packet regardless of error conditions
841 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
846 ushort data_size = 0;
848 ushort file_checksum = 0;
850 ubyte xfer_data[600];
854 // read in all packet data
859 case MULTI_XFER_CODE_DATA:
861 memcpy(xfer_data, data + offset, data_size);
867 case MULTI_XFER_CODE_HEADER:
868 GET_STRING(filename);
870 GET_DATA(file_checksum);
875 case MULTI_XFER_CODE_ACK:
876 case MULTI_XFER_CODE_NAK:
880 case MULTI_XFER_CODE_FINAL:
888 // at this point we've read all the data in the packet
890 // at this point, we should process code-specific data
892 if(val != MULTI_XFER_CODE_HEADER){
893 // if the code is not a request or a header, we need to look up the existing xfer_entry
896 xe = multi_xfer_find_entry(who, sig, sender_side);
898 #ifdef MULTI_XFER_VERBOSE
899 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
901 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
903 int np_index = find_player_socket(who);
904 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
905 ml_printf(": sig == %d", sig);
906 ml_printf(": xfer header == %d", val);
908 ml_string(": player == unknown");
910 ml_printf(": player == %s", Net_players[np_index].player->callsign);
913 ml_string(": sending");
915 ml_string(": receiving");
925 // process an ack for this entry
926 case MULTI_XFER_CODE_ACK :
928 multi_xfer_process_ack(xe);
931 // process a nak for this entry
932 case MULTI_XFER_CODE_NAK :
934 multi_xfer_process_nak(xe);
937 // process a "final" packet
938 case MULTI_XFER_CODE_FINAL :
940 multi_xfer_process_final(xe);
943 // process a data packet
944 case MULTI_XFER_CODE_DATA :
946 multi_xfer_process_data(xe, xfer_data, data_size);
950 case MULTI_XFER_CODE_HEADER :
951 // send on my reliable socket
952 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
958 // process an ack for this entry
959 void multi_xfer_process_ack(xfer_entry *xe)
961 // if we are a sender
962 if(xe->flags & MULTI_XFER_FLAG_SEND){
963 // if we are waiting on a final ack, then the transfer has completed successfully
964 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
965 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
966 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
968 #ifdef MULTI_XFER_VERBOSE
969 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
972 // if we should be auto-destroying this entry, do so
973 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
974 multi_xfer_release_handle(xe - Multi_xfer_entry);
977 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
978 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
979 multi_xfer_send_next(xe);
984 // process a nak for this entry
985 void multi_xfer_process_nak(xfer_entry *xe)
987 // if we get an ack at any time we should simply set the xfer to failed
988 multi_xfer_fail_entry(xe);
991 // process a "final" packet
992 void multi_xfer_process_final(xfer_entry *xe)
996 // make sure we skip a line
997 nprintf(("Network","\n"));
1000 if(xe->file != NULL){
1006 // check to make sure the file checksum is the same
1008 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
1010 xe->flags |= MULTI_XFER_FLAG_FAIL;
1012 #ifdef MULTI_XFER_VERBOSE
1013 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1017 multi_xfer_abort(xe - Multi_xfer_entry);
1020 // checksums check out, so rename the file and be done with it
1022 #ifdef MULTI_XFER_VERBOSE
1023 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));
1025 // rename the file properly
1026 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1027 // mark the xfer as being successful
1028 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1030 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1032 // send an ack to the sender
1033 multi_xfer_send_ack(xe->file_socket, xe->sig);
1035 // mark it as failing
1036 xe->flags |= MULTI_XFER_FLAG_FAIL;
1037 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1039 // delete the tempfile
1040 cf_delete(xe->ex_filename, xe->force_dir);
1042 // send an nak to the sender
1043 multi_xfer_send_nak(xe->file_socket, xe->sig);
1046 // if we should be auto-destroying this entry, do so
1047 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1048 multi_xfer_release_handle(xe - Multi_xfer_entry);
1053 // process a data packet
1054 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1056 // print out a crude progress indicator
1057 nprintf(("Network","."));
1059 // attempt to write the rest of the data string to the file
1060 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1061 // inform the sender we had a problem
1062 multi_xfer_send_nak(xe->file_socket, xe->sig);
1065 multi_xfer_fail_entry(xe);
1067 xe->file_ptr += data_size;
1071 // increment the file pointer
1072 xe->file_ptr += data_size;
1074 // send an ack to the sender
1075 multi_xfer_send_ack(xe->file_socket, xe->sig);
1078 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1081 // process a header, return bytes processed
1082 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1087 // if the xfer system is locked, send a nak
1088 if(Multi_xfer_locked){
1089 multi_xfer_send_nak(who, sig);
1093 // try and get a free xfer handle
1094 handle = multi_xfer_get_free_handle();
1096 multi_xfer_send_nak(who, sig);
1099 xe = &Multi_xfer_entry[handle];
1100 memset(xe,0,sizeof(xfer_entry));
1103 // set the recv and used flags
1104 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1106 // get the header data
1107 xe->file_size = file_size;
1109 // get the file chksum
1110 xe->file_chksum = file_checksum;
1113 xe->file_socket = who;
1115 // set the timeout timestamp
1116 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1121 // copy the filename and get the prefixed xfer filename
1122 strcpy(xe->filename, filename);
1123 multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1124 #ifdef MULTI_XFER_VERBOSE
1125 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1128 // determine what directory to place the file in
1129 // individual xfer entries take precedence over the global multi xfer force entry
1130 xe->force_dir = Multi_xfer_force_dir;
1132 // call the callback function
1133 Multi_xfer_recv_notify(handle);
1135 // if the notify function invalidated this xfer handle, then cancel the whole thing
1136 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1137 multi_xfer_send_nak(who, sig);
1140 memset(xe, 0, sizeof(xfer_entry));
1144 // delete the old file (if it exists)
1145 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1146 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1148 // attempt to open the file (using the prefixed filename)
1150 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1151 if(xe->file == NULL){
1152 multi_xfer_send_nak(who, sig);
1155 memset(xe, 0, sizeof(xfer_entry));
1159 // set the waiting for data flag
1160 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1162 // send an ack to the server
1163 multi_xfer_send_ack(who, sig);
1165 #ifdef MULTI_XFER_VERBOSE
1166 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1170 // send the next block of outgoing data or a "final" packet if we're done
1171 void multi_xfer_send_next(xfer_entry *xe)
1173 ubyte data[MAX_PACKET_SIZE],code;
1176 int packet_size = 0;
1178 // print out a crude progress indicator
1179 nprintf(("Network", "+"));
1181 // if we've sent all the data, then we should send a "final" packet
1182 if(xe->file_ptr >= xe->file_size){
1183 // mark the entry as unknown
1184 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1187 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1190 multi_xfer_send_final(xe);
1196 BUILD_HEADER(XFER_PACKET);
1198 // length of the added string
1199 flen = strlen(xe->filename) + 4;
1201 // determine how much data we are going to send with this packet and add it in
1202 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1203 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1205 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1207 // increment the file pointer
1208 xe->file_ptr += data_size;
1211 code = MULTI_XFER_CODE_DATA;
1217 // add in the size of the rest of the packet
1218 ADD_DATA(data_size);
1221 if(cfread(data+packet_size,1,(int)data_size,xe->file) == 0){
1222 // send a nack to the receiver
1223 multi_xfer_send_nak(xe->file_socket, xe->sig);
1226 multi_xfer_fail_entry(xe);
1230 // increment the packet size
1231 packet_size += (int)data_size;
1234 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1236 // otherwise send the data
1237 psnet_rel_send(xe->file_socket, data, packet_size);
1240 // send an ack to the sender
1241 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1243 ubyte data[MAX_PACKET_SIZE],code;
1244 int packet_size = 0;
1246 // build the header and add
1247 BUILD_HEADER(XFER_PACKET);
1250 code = MULTI_XFER_CODE_ACK;
1257 psnet_rel_send(socket, data, packet_size);
1260 // send a nak to the sender
1261 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1263 ubyte data[MAX_PACKET_SIZE],code;
1264 int packet_size = 0;
1266 // build the header and add the code
1267 BUILD_HEADER(XFER_PACKET);
1270 code = MULTI_XFER_CODE_NAK;
1277 psnet_rel_send(socket, data, packet_size);
1280 // send a "final" packet
1281 void multi_xfer_send_final(xfer_entry *xe)
1283 ubyte data[MAX_PACKET_SIZE],code;
1284 int packet_size = 0;
1287 BUILD_HEADER(XFER_PACKET);
1290 code = MULTI_XFER_CODE_FINAL;
1297 psnet_rel_send(xe->file_socket, data, packet_size);
1300 // send the header to begin a file transfer
1301 void multi_xfer_send_header(xfer_entry *xe)
1303 ubyte data[MAX_PACKET_SIZE],code;
1304 int packet_size = 0;
1306 // build the header and add the opcode
1307 BUILD_HEADER(XFER_PACKET);
1308 code = MULTI_XFER_CODE_HEADER;
1315 ADD_STRING(xe->filename);
1318 ADD_DATA(xe->file_size);
1320 // add the file checksum
1321 ADD_DATA(xe->file_chksum);
1324 psnet_rel_send(xe->file_socket, data, packet_size);
1327 // convert the filename into the prefixed ex_filename
1328 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1330 char temp[MAX_FILENAME_LEN+50];
1332 // blast the memory clean
1333 memset(temp, 0, MAX_FILENAME_LEN+50);
1335 // copy in the prefix
1336 strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1338 // stick on the original name
1339 strcat(temp, filename);
1341 // copy the whole thing to the outgoing filename
1342 strcpy(ex_filename, temp);
1345 // get a new xfer sig
1346 ushort multi_xfer_get_sig()
1348 ushort ret = Multi_xfer_sig;
1351 if(Multi_xfer_sig == 0xffff){