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.8 2005/10/02 09:30:10 taylor
17 * sync up rest of big-endian network changes. it should at least be as good as what's in FS2_Open now, only better :)
19 * Revision 1.7 2004/06/11 01:46:42 tigital
20 * byte-swapping changes for bigendian systems
22 * Revision 1.6 2003/06/22 12:51:02 taylor
23 * lower case file transfers
25 * Revision 1.5 2002/06/09 04:41:24 relnev
26 * added copyright header
28 * Revision 1.4 2002/06/01 07:12:33 relnev
29 * a few NDEBUG updates.
31 * removed a few warnings.
33 * Revision 1.3 2002/05/26 20:22:48 theoddone33
34 * Most of network/ works
36 * Revision 1.2 2002/05/07 03:16:47 theoddone33
37 * The Great Newline Fix
39 * Revision 1.1.1.1 2002/05/03 03:28:10 root
43 * 11 3/10/99 6:50p Dave
44 * Changed the way we buffer packets for all clients. Optimized turret
45 * fired packets. Did some weapon firing optimizations.
47 * 10 3/09/99 6:24p Dave
48 * More work on object update revamping. Identified several sources of
49 * unnecessary bandwidth.
51 * 9 1/24/99 11:37p Dave
52 * First full rev of beam weapons. Very customizable. Removed some bogus
53 * Int3()'s in low level net code.
55 * 8 12/16/98 11:17a Dave
56 * Fixed potential situation where a send and receive to the same player
57 * with the same sig might get confused with each other.
59 * 7 12/14/98 4:01p Dave
60 * Got multi_data stuff working well with new xfer stuff.
62 * 6 12/14/98 12:13p Dave
63 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
66 * 5 11/19/98 8:03a Dave
67 * Full support for D3-style reliable sockets. Revamped packet lag/loss
68 * system, made it receiver side and at the lowest possible level.
70 * 4 11/17/98 11:12a Dave
71 * Removed player identification by address. Now assign explicit id #'s.
73 * 3 11/05/98 5:55p Dave
74 * Big pass at reducing #includes
76 * 2 10/07/98 10:53a Dave
79 * 1 10/07/98 10:50a Dave
81 * 56 9/11/98 9:31a Dave
82 * Fixed file xfer bug regarding checksums.
84 * 55 8/12/98 4:53p Dave
85 * Put in 32 bit checksumming for PXO missions. No validation on the
86 * actual tracker yet, though.
88 * 54 6/13/98 9:32p Mike
89 * Kill last character in file which caused "Find in Files" to report the
90 * file as "not a text file."
92 * 53 6/13/98 6:01p Hoffoss
93 * Externalized all new (or forgot to be added) strings to all the code.
95 * 52 6/13/98 3:19p Hoffoss
96 * NOX()ed out a bunch of strings that shouldn't be translated.
98 * 51 5/21/98 1:52a Dave
99 * Remove obsolete command line functions. Reduce shield explosion packets
100 * drastically. Tweak PXO screen even more. Fix file xfer system so that
101 * we can guarantee file uniqueness.
103 * 50 4/30/98 4:53p John
104 * Restructured and cleaned up cfile code. Added capability to read off
105 * of CD-ROM drive and out of multiple pack files.
107 * 49 4/23/98 6:18p Dave
108 * Store ETS values between respawns. Put kick feature in the text
109 * messaging system. Fixed text messaging system so that it doesn't
110 * process or trigger ship controls. Other UI fixes.
112 * 48 4/22/98 5:53p Dave
113 * Large reworking of endgame sequencing. Updated multi host options
114 * screen for new artwork. Put in checks for host or team captains leaving
117 * 47 4/21/98 4:44p Dave
118 * Implement Vasudan ships in multiplayer. Added a debug function to bash
119 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
120 * problem in options screen.
122 * 46 4/20/98 6:04p Dave
123 * Implement multidata cache flushing and xferring mission files to
124 * multidata. Make sure observers can't change hud config. Fix pilot image
125 * viewing in popup. Put in game status field. Tweaked multi options.
127 * 45 4/08/98 2:51p Dave
128 * Fixed pilot image xfer once again. Solidify team selection process in
129 * pre-briefing multiplayer.
131 * 44 4/04/98 4:22p Dave
132 * First rev of UDP reliable sockets is done. Seems to work well if not
135 * 43 4/03/98 1:03a Dave
136 * First pass at unreliable guaranteed delivery packets.
138 * 42 4/01/98 11:19p Dave
139 * Put in auto-loading of xferred pilot pic files. Grey out background
140 * behind pinfo popup. Put a chatbox message in when players are kicked.
141 * Moved mission title down in briefing. Other ui fixes.
143 * 41 3/31/98 4:51p Dave
144 * Removed medals screen and multiplayer buttons from demo version. Put in
145 * new pilot popup screen. Make ships in mp team vs. team have proper team
146 * ids. Make mp respawns a permanent option saved in the player file.
148 * 40 3/30/98 8:46a Allender
149 * fix an optimized build compiler warning
151 * 39 3/26/98 6:01p Dave
152 * Put in file checksumming routine in cfile. Made pilot pic xferring more
153 * robust. Cut header size of voice data packets in half. Put in
154 * restricted game host query system.
156 * 38 3/25/98 2:16p Dave
157 * Select random default image for newly created pilots. Fixed several
158 * multi-pause messaging bugs. Begin work on online help for multiplayer
161 * 37 3/24/98 5:00p Dave
162 * Fixed several ui bugs. Put in pre and post voice stream playback sound
163 * fx. Put in error specific popups for clients getting dropped from games
164 * through actions other than their own.
166 * 36 3/23/98 5:42p Dave
167 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
168 * that it can support multiplayer sends/received between client and
169 * server simultaneously.
171 * 35 3/21/98 7:14p Dave
172 * Fixed up standalone player slot switching. Made training missions not
173 * count towards player stats.
175 * 34 3/16/98 11:52p Allender
176 * Put in timestamp updates when processing data on both sender and
179 * 33 2/22/98 2:53p Dave
180 * Put in groundwork for advanced multiplayer campaign options.
182 * 32 2/20/98 4:43p Dave
183 * Finished support for multiplayer player data files. Split off
184 * multiplayer campaign functionality.
186 * 31 2/19/98 6:26p Dave
187 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
188 * initial support for player data uploading.
190 * 30 2/18/98 10:21p Dave
191 * Ripped out old file xfer system. Put in brand new xfer system.
197 #include <winsock2.h>
199 #include "multi_xfer.h"
201 #include "multimsgs.h"
204 #include "multi_endgame.h"
207 #include "multiutil.h"
208 #include "multi_log.h"
210 // ------------------------------------------------------------------------------------------
211 // MULTI XFER DEFINES/VARS
214 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
216 #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) )
219 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
220 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
221 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
222 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
223 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
226 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
227 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
228 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
229 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
230 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
231 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
232 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
233 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
234 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
235 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
236 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
238 // packet size for file xfer
239 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
241 // timeout for a given xfer operation
242 #define MULTI_XFER_TIMEOUT 10000
246 // temp filename header for xferring files
247 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
251 // xfer entries/events
252 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
253 typedef struct xfer_entry {
254 int flags; // status flags for this entry
255 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
256 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
257 CFILE *file; // file handle of the current xferring file
258 int file_size; // total size of the file being xferred
259 int file_ptr; // total bytes we're received so far
260 ushort file_chksum; // used for checking successfully xferred files
261 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
262 int xfer_stamp; // timestamp for the current operation
263 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
264 ushort sig; // identifying sig - sender specifies this
266 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
268 // callback function pointer for when we start receiving a file
269 void (*Multi_xfer_recv_notify)(int handle);
271 // lock for the xfer system
272 int Multi_xfer_locked;
274 // force directory type for receives
275 int Multi_xfer_force_dir;
277 // unique file signature - this along with a socket # is enough to identify all xfers
278 ushort Multi_xfer_sig = 0;
281 // ------------------------------------------------------------------------------------------
282 // MULTI XFER FORWARD DECLARATIONS
285 // evaluate the condition of the entry
286 void multi_xfer_eval_entry(xfer_entry *xe);
288 // set an entry to be "failed"
289 void multi_xfer_fail_entry(xfer_entry *xe);
291 // get a valid xfer entry handle
292 int multi_xfer_get_free_handle();
294 // process an ack for this entry
295 void multi_xfer_process_ack(xfer_entry *xe);
297 // process a nak for this entry
298 void multi_xfer_process_nak(xfer_entry *xe);
300 // process a "final" packet
301 void multi_xfer_process_final(xfer_entry *xe);
303 // process a data packet
304 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
307 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
309 // send the next block of outgoing data or a "final" packet if we're done
310 void multi_xfer_send_next(xfer_entry *xe);
312 // send an ack to the sender
313 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
315 // send a nak to the sender
316 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
318 // send a "final" packet
319 void multi_xfer_send_final(xfer_entry *xe);
321 // send the header to begin a file transfer
322 void multi_xfer_send_header(xfer_entry *xe);
324 // convert the filename into the prefixed ex_filename
325 void multi_xfer_conv_prefix(char *filename, char *ex_filename, const int max_len);
327 // get a new xfer sig
328 ushort multi_xfer_get_sig();
330 // ------------------------------------------------------------------------------------------
331 // MULTI XFER FUNCTIONS
334 // initialize all file xfer transaction stuff, call in multi_level_init()
335 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
337 // blast all the entries
338 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
340 // assign the receive callback function pointer
341 Multi_xfer_recv_notify = multi_xfer_recv_callback;
344 Multi_xfer_locked = 0;
346 // no forced directory
347 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
350 // do frame for all file xfers, call in multi_do_frame()
355 // process all valid xfer entries
356 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
357 // if this one is actually in use and has not finished for one reason or another
358 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))){
359 // evaluate the condition of this entry (fail, timeout, etc)
360 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
365 // close down the file xfer system
366 void multi_xfer_close()
370 // go through all active entries and abort them
371 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
372 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
373 multi_xfer_abort(idx);
377 // now blast all the memory free
378 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
381 // reset the xfer system, including shutting down/killing all active xfers
382 void multi_xfer_reset()
386 // shut down all active xfers
387 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
388 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
389 multi_xfer_abort(idx);
393 // blast all the memory clean
394 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
397 // send a file to the specified player, return a handle
398 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
400 xfer_entry temp_entry;
403 // if the system is locked, return -1
404 if(Multi_xfer_locked){
408 // attempt to get a free handle
409 handle = multi_xfer_get_free_handle();
414 // clear the temp entry
415 memset(&temp_entry,0,sizeof(xfer_entry));
418 SDL_strlcpy(temp_entry.filename, filename, SDL_arraysize(temp_entry.filename));
420 // attempt to open the file
421 temp_entry.file = NULL;
422 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
423 if(temp_entry.file == NULL){
424 #ifdef MULTI_XFER_VERBOSE
425 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
432 temp_entry.file_size = -1;
433 temp_entry.file_size = cfilelength(temp_entry.file);
434 if(temp_entry.file_size == -1){
435 #ifdef MULTI_XFER_VERBOSE
436 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
440 temp_entry.file_ptr = 0;
442 // get the file checksum
443 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
444 #ifdef MULTI_XFER_VERBOSE
445 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
449 #ifdef MULTI_XFER_VERBOSE
450 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
452 // rewind the file pointer to the beginning of the file
453 cfseek(temp_entry.file,0,CF_SEEK_SET);
456 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
457 temp_entry.flags |= flags;
460 temp_entry.file_socket = who;
463 temp_entry.sig = multi_xfer_get_sig();
465 // copy to the global array
466 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
467 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
472 // get the status of the current file xfer
473 int multi_xfer_get_status(int handle)
475 // if this is an invalid or an unused handle, notify as such
476 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
477 return MULTI_XFER_NONE;
480 // if the xfer has timed-out
481 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
482 return MULTI_XFER_TIMEDOUT;
485 // if the xfer has failed for one reason or another (not timeout)
486 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
487 return MULTI_XFER_FAIL;
490 // if the xfer has succeeded
491 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
492 return MULTI_XFER_SUCCESS;
495 // if the xfer is queued
496 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
497 return MULTI_XFER_QUEUED;
500 // otherwise the xfer is still in progress
501 return MULTI_XFER_IN_PROGRESS;
504 // abort a transferring file
505 void multi_xfer_abort(int handle)
509 // don't do anything if this is an invalid handle
510 if(MULTI_XFER_INVALID_HANDLE(handle)){
514 // get e handle to the entry
515 xe = &Multi_xfer_entry[handle];
517 // close any open file and delete it
518 if(xe->file != NULL){
522 // delete it if there isn't some problem with the filename
523 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
524 cf_delete(xe->ex_filename, xe->force_dir);
529 xe->file_socket = INVALID_SOCKET;
532 memset(xe,0,sizeof(xfer_entry));
535 // release an xfer handle
536 void multi_xfer_release_handle(int handle)
540 // don't do anything if this is an invalid handle
541 if(MULTI_XFER_INVALID_HANDLE(handle)){
545 // get e handle to the entry
546 xe = &Multi_xfer_entry[handle];
548 // close any open file and delete it
549 if(xe->file != NULL){
553 // delete it if the file was not successfully received
554 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
555 cf_delete(xe->ex_filename,xe->force_dir);
560 xe->file_socket = INVALID_SOCKET;
563 memset(xe,0,sizeof(xfer_entry));
566 // get the filename of the xfer for the given handle
567 char *multi_xfer_get_filename(int handle)
569 // if this is an invalid handle, return NULL
570 if(MULTI_XFER_INVALID_HANDLE(handle)){
574 // otherwise return the string
575 return Multi_xfer_entry[handle].filename;
578 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
579 void multi_xfer_lock()
581 Multi_xfer_locked = 1;
584 // unlock the xfer system
585 void multi_xfer_unlock()
587 Multi_xfer_locked = 0;
590 // force all receives to go into the specified directory by cfile type
591 void multi_xfer_force_dir(int cf_type)
593 Multi_xfer_force_dir = cf_type;
594 SDL_assert(Multi_xfer_force_dir > CF_TYPE_ANY);
597 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
598 void multi_xfer_handle_force_dir(int handle,int cf_type)
600 // if this is an invalid handle, return NULL
601 if(MULTI_XFER_INVALID_HANDLE(handle)){
605 // force to go to the given directory
606 Multi_xfer_entry[handle].force_dir = cf_type;
607 SDL_assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
610 // or the flag on a given entry
611 void multi_xfer_xor_flags(int handle,int flags)
613 // if this is an invalid handle, return NULL
614 if(MULTI_XFER_INVALID_HANDLE(handle)){
619 Multi_xfer_entry[handle].flags ^= flags;
622 // get the flags for a given entry
623 int multi_xfer_get_flags(int handle)
625 // if this is an invalid handle, return NULL
626 if(MULTI_XFER_INVALID_HANDLE(handle)){
631 return Multi_xfer_entry[handle].flags;
634 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
635 int multi_xfer_lookup(char *filename)
639 // if we have an invalid filename, do nothing
640 if((filename == NULL) || (strlen(filename) <= 0)){
644 // otherwise, perform a lookup
645 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
646 // if we found a matching filename
647 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !SDL_strcasecmp(filename,Multi_xfer_entry[idx].filename)){
652 // did not find a match
656 // get the % of completion of the passed file handle, return < 0 if invalid
657 float multi_xfer_pct_complete(int handle)
659 // if this is an invalid handle, return invalid
660 if(MULTI_XFER_INVALID_HANDLE(handle)){
664 // if the file size is 0, return invalid
665 if(Multi_xfer_entry[handle].file_size == 0){
669 // return the pct completion
670 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
673 // get the socket of the file xfer (useful for identifying players)
674 uint multi_xfer_get_sock(int handle)
676 // if this is an invalid handle, return NULL
677 if(MULTI_XFER_INVALID_HANDLE(handle)){
678 return INVALID_SOCKET;
681 return Multi_xfer_entry[handle].file_socket;
684 // get the CF_TYPE of the directory this file is going to
685 int multi_xfer_get_force_dir(int handle)
687 // if this is an invalid handle, return NULL
688 if(MULTI_XFER_INVALID_HANDLE(handle)){
689 return INVALID_SOCKET;
692 return Multi_xfer_entry[handle].force_dir;
696 // ------------------------------------------------------------------------------------------
697 // MULTI XFER FORWARD DECLARATIONS
700 // evaluate the condition of the entry
701 void multi_xfer_eval_entry(xfer_entry *xe)
707 // if the entry is marked as successful, then don't do anything
708 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
712 // if the entry is queued
713 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
714 // if the entry is not current
715 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
716 // see if there are any other queued up xfers to this target. if not, make me current and start sending
718 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
719 xe_c = &Multi_xfer_entry[idx];
721 // if this is a valid entry and is a queued entry and is going to the same target
722 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
723 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
730 // if we found no other entries, make this guy current and pending
732 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
733 xe->flags |= MULTI_XFER_FLAG_PENDING;
735 #ifdef MULTI_XFER_VERBOSE
736 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
739 // otherwise, do nothing for him - he has to still wait
746 // if the entry is marked as pending - send out the header to get the ball rolling
747 if(xe->flags & MULTI_XFER_FLAG_PENDING){
748 // send the header to begin the transfer
749 multi_xfer_send_header(xe);
752 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
754 // unset the pending flag
755 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
757 // set the ack/wait flag
758 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
761 // see if the entry has timed-out for one reason or another
762 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
763 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
765 // if we should be auto-destroying this entry, do so
766 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
767 multi_xfer_fail_entry(xe);
772 // lookup a file xfer entry by player
773 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
777 // look through all valid xfer entries
778 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
779 // if we're looking for sending entries
780 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
783 // if we're looking for recv entries
784 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
788 // if we found a match
789 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
790 return &Multi_xfer_entry[idx];
797 // set an entry to be "failed"
798 void multi_xfer_fail_entry(xfer_entry *xe)
800 // set its flags appropriately
801 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
802 xe->flags |= MULTI_XFER_FLAG_FAIL;
804 // close the file pointer
805 if(xe->file != NULL){
811 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
812 cf_delete(xe->ex_filename,xe->force_dir);
815 // null the timestamp
818 // if we should be auto-destroying this entry, do so
819 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
820 multi_xfer_release_handle(xe - Multi_xfer_entry);
823 // blast the memory clean
824 memset(xe,0,sizeof(xfer_entry));
827 // get a valid xfer entry handle
828 int multi_xfer_get_free_handle()
832 // look for a free entry
833 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
834 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
843 // ------------------------------------------------------------------------------------------
844 // MULTI XFER PACKET HANDLERS
847 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
848 // packet regardless of error conditions
849 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
854 ushort data_size = 0;
856 ushort file_checksum = 0;
858 ubyte xfer_data[600];
862 // read in all packet data
867 case MULTI_XFER_CODE_DATA:
868 GET_USHORT(data_size);
869 memcpy(xfer_data, data + offset, data_size);
875 case MULTI_XFER_CODE_HEADER:
876 GET_STRING(filename);
878 GET_USHORT(file_checksum);
883 case MULTI_XFER_CODE_ACK:
884 case MULTI_XFER_CODE_NAK:
888 case MULTI_XFER_CODE_FINAL:
896 // at this point we've read all the data in the packet
898 // at this point, we should process code-specific data
900 if(val != MULTI_XFER_CODE_HEADER){
901 // if the code is not a request or a header, we need to look up the existing xfer_entry
904 xe = multi_xfer_find_entry(who, sig, sender_side);
906 #ifdef MULTI_XFER_VERBOSE
907 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
909 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
911 int np_index = find_player_socket(who);
912 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
913 ml_printf(": sig == %d", sig);
914 ml_printf(": xfer header == %d", val);
916 ml_string(": player == unknown");
918 ml_printf(": player == %s", Net_players[np_index].player->callsign);
921 ml_string(": sending");
923 ml_string(": receiving");
933 // process an ack for this entry
934 case MULTI_XFER_CODE_ACK :
935 SDL_assert(xe != NULL);
936 multi_xfer_process_ack(xe);
939 // process a nak for this entry
940 case MULTI_XFER_CODE_NAK :
941 SDL_assert(xe != NULL);
942 multi_xfer_process_nak(xe);
945 // process a "final" packet
946 case MULTI_XFER_CODE_FINAL :
947 SDL_assert(xe != NULL);
948 multi_xfer_process_final(xe);
951 // process a data packet
952 case MULTI_XFER_CODE_DATA :
953 SDL_assert(xe != NULL);
954 multi_xfer_process_data(xe, xfer_data, data_size);
958 case MULTI_XFER_CODE_HEADER :
959 // send on my reliable socket
960 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
966 // process an ack for this entry
967 void multi_xfer_process_ack(xfer_entry *xe)
969 // if we are a sender
970 if(xe->flags & MULTI_XFER_FLAG_SEND){
971 // if we are waiting on a final ack, then the transfer has completed successfully
972 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
973 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
974 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
976 #ifdef MULTI_XFER_VERBOSE
977 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
980 // if we should be auto-destroying this entry, do so
981 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
982 multi_xfer_release_handle(xe - Multi_xfer_entry);
985 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
986 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
987 multi_xfer_send_next(xe);
992 // process a nak for this entry
993 void multi_xfer_process_nak(xfer_entry *xe)
995 // if we get an ack at any time we should simply set the xfer to failed
996 multi_xfer_fail_entry(xe);
999 // process a "final" packet
1000 void multi_xfer_process_final(xfer_entry *xe)
1004 // make sure we skip a line
1005 nprintf(("Network","\n"));
1008 if(xe->file != NULL){
1014 // check to make sure the file checksum is the same
1016 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
1018 xe->flags |= MULTI_XFER_FLAG_FAIL;
1020 #ifdef MULTI_XFER_VERBOSE
1021 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1025 multi_xfer_abort(xe - Multi_xfer_entry);
1028 // checksums check out, so rename the file and be done with it
1030 #ifdef MULTI_XFER_VERBOSE
1031 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));
1033 // rename the file properly
1034 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1035 // mark the xfer as being successful
1036 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1038 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1040 // send an ack to the sender
1041 multi_xfer_send_ack(xe->file_socket, xe->sig);
1043 // mark it as failing
1044 xe->flags |= MULTI_XFER_FLAG_FAIL;
1045 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1047 // delete the tempfile
1048 cf_delete(xe->ex_filename, xe->force_dir);
1050 // send an nak to the sender
1051 multi_xfer_send_nak(xe->file_socket, xe->sig);
1054 // if we should be auto-destroying this entry, do so
1055 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1056 multi_xfer_release_handle(xe - Multi_xfer_entry);
1061 // process a data packet
1062 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1064 // print out a crude progress indicator
1065 nprintf(("Network","."));
1067 // attempt to write the rest of the data string to the file
1068 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1069 // inform the sender we had a problem
1070 multi_xfer_send_nak(xe->file_socket, xe->sig);
1073 multi_xfer_fail_entry(xe);
1075 xe->file_ptr += data_size;
1079 // increment the file pointer
1080 xe->file_ptr += data_size;
1082 // send an ack to the sender
1083 multi_xfer_send_ack(xe->file_socket, xe->sig);
1086 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1089 // process a header, return bytes processed
1090 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1095 // if the xfer system is locked, send a nak
1096 if(Multi_xfer_locked){
1097 multi_xfer_send_nak(who, sig);
1101 // try and get a free xfer handle
1102 handle = multi_xfer_get_free_handle();
1104 multi_xfer_send_nak(who, sig);
1107 xe = &Multi_xfer_entry[handle];
1108 memset(xe,0,sizeof(xfer_entry));
1111 // set the recv and used flags
1112 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1114 // get the header data
1115 xe->file_size = file_size;
1117 // get the file chksum
1118 xe->file_chksum = file_checksum;
1121 xe->file_socket = who;
1123 // set the timeout timestamp
1124 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1129 // copy the filename and get the prefixed xfer filename
1130 SDL_strlcpy(xe->filename, filename, SDL_arraysize(xe->filename));
1131 // lower case all filenames to avoid case issues
1132 SDL_strlwr(xe->filename);
1134 multi_xfer_conv_prefix(xe->filename, xe->ex_filename, SDL_arraysize(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;
1226 ADD_USHORT(xe->sig);
1228 // add in the size of the rest of the packet
1229 ADD_USHORT(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;
1305 ADD_USHORT(xe->sig);
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;
1323 ADD_USHORT(xe->sig);
1326 ADD_STRING(xe->filename);
1329 ADD_INT(xe->file_size);
1331 // add the file checksum
1332 ADD_USHORT(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, const int max_len)
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 SDL_strlcpy(temp, MULTI_XFER_FNAME_PREFIX, SDL_arraysize(temp));
1349 // stick on the original name
1350 SDL_strlcat(temp, filename, SDL_arraysize(temp));
1352 // copy the whole thing to the outgoing filename
1353 SDL_strlcpy(ex_filename, temp, max_len);
1356 // get a new xfer sig
1357 ushort multi_xfer_get_sig()
1359 ushort ret = Multi_xfer_sig;
1362 if(Multi_xfer_sig == 0xffff){