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.
200 #include "multi_xfer.h"
202 #include "multimsgs.h"
205 #include "multi_endgame.h"
208 #include "multiutil.h"
209 #include "multi_log.h"
211 // ------------------------------------------------------------------------------------------
212 // MULTI XFER DEFINES/VARS
215 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
217 #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) )
220 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
221 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
222 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
223 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
224 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
227 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
228 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
229 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
230 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
231 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
232 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
233 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
234 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
235 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
236 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
237 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
239 // packet size for file xfer
240 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
242 // timeout for a given xfer operation
243 #define MULTI_XFER_TIMEOUT 10000
247 // temp filename header for xferring files
248 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
252 // xfer entries/events
253 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
254 typedef struct xfer_entry {
255 int flags; // status flags for this entry
256 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
257 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
258 CFILE *file; // file handle of the current xferring file
259 int file_size; // total size of the file being xferred
260 int file_ptr; // total bytes we're received so far
261 ushort file_chksum; // used for checking successfully xferred files
262 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
263 int xfer_stamp; // timestamp for the current operation
264 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
265 ushort sig; // identifying sig - sender specifies this
267 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
269 // callback function pointer for when we start receiving a file
270 void (*Multi_xfer_recv_notify)(int handle);
272 // lock for the xfer system
273 int Multi_xfer_locked;
275 // force directory type for receives
276 int Multi_xfer_force_dir;
278 // unique file signature - this along with a socket # is enough to identify all xfers
279 ushort Multi_xfer_sig = 0;
282 // ------------------------------------------------------------------------------------------
283 // MULTI XFER FORWARD DECLARATIONS
286 // evaluate the condition of the entry
287 void multi_xfer_eval_entry(xfer_entry *xe);
289 // set an entry to be "failed"
290 void multi_xfer_fail_entry(xfer_entry *xe);
292 // get a valid xfer entry handle
293 int multi_xfer_get_free_handle();
295 // process an ack for this entry
296 void multi_xfer_process_ack(xfer_entry *xe);
298 // process a nak for this entry
299 void multi_xfer_process_nak(xfer_entry *xe);
301 // process a "final" packet
302 void multi_xfer_process_final(xfer_entry *xe);
304 // process a data packet
305 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
308 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
310 // send the next block of outgoing data or a "final" packet if we're done
311 void multi_xfer_send_next(xfer_entry *xe);
313 // send an ack to the sender
314 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
316 // send a nak to the sender
317 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
319 // send a "final" packet
320 void multi_xfer_send_final(xfer_entry *xe);
322 // send the header to begin a file transfer
323 void multi_xfer_send_header(xfer_entry *xe);
325 // convert the filename into the prefixed ex_filename
326 void multi_xfer_conv_prefix(char *filename, char *ex_filename, const int max_len);
328 // get a new xfer sig
329 ushort multi_xfer_get_sig();
331 // ------------------------------------------------------------------------------------------
332 // MULTI XFER FUNCTIONS
335 // initialize all file xfer transaction stuff, call in multi_level_init()
336 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
338 // blast all the entries
339 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
341 // assign the receive callback function pointer
342 Multi_xfer_recv_notify = multi_xfer_recv_callback;
345 Multi_xfer_locked = 0;
347 // no forced directory
348 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
351 // do frame for all file xfers, call in multi_do_frame()
356 // process all valid xfer entries
357 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
358 // if this one is actually in use and has not finished for one reason or another
359 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))){
360 // evaluate the condition of this entry (fail, timeout, etc)
361 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
366 // close down the file xfer system
367 void multi_xfer_close()
371 // go through all active entries and abort them
372 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
373 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
374 multi_xfer_abort(idx);
378 // now blast all the memory free
379 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
382 // reset the xfer system, including shutting down/killing all active xfers
383 void multi_xfer_reset()
387 // shut down all active xfers
388 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
389 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
390 multi_xfer_abort(idx);
394 // blast all the memory clean
395 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
398 // send a file to the specified player, return a handle
399 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
401 xfer_entry temp_entry;
404 // if the system is locked, return -1
405 if(Multi_xfer_locked){
409 // attempt to get a free handle
410 handle = multi_xfer_get_free_handle();
415 // clear the temp entry
416 memset(&temp_entry,0,sizeof(xfer_entry));
419 SDL_strlcpy(temp_entry.filename, filename, SDL_arraysize(temp_entry.filename));
421 // attempt to open the file
422 temp_entry.file = NULL;
423 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
424 if(temp_entry.file == NULL){
425 #ifdef MULTI_XFER_VERBOSE
426 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
433 temp_entry.file_size = -1;
434 temp_entry.file_size = cfilelength(temp_entry.file);
435 if(temp_entry.file_size == -1){
436 #ifdef MULTI_XFER_VERBOSE
437 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
441 temp_entry.file_ptr = 0;
443 // get the file checksum
444 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
445 #ifdef MULTI_XFER_VERBOSE
446 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
450 #ifdef MULTI_XFER_VERBOSE
451 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
453 // rewind the file pointer to the beginning of the file
454 cfseek(temp_entry.file,0,CF_SEEK_SET);
457 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
458 temp_entry.flags |= flags;
461 temp_entry.file_socket = who;
464 temp_entry.sig = multi_xfer_get_sig();
466 // copy to the global array
467 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
468 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
473 // get the status of the current file xfer
474 int multi_xfer_get_status(int handle)
476 // if this is an invalid or an unused handle, notify as such
477 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
478 return MULTI_XFER_NONE;
481 // if the xfer has timed-out
482 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
483 return MULTI_XFER_TIMEDOUT;
486 // if the xfer has failed for one reason or another (not timeout)
487 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
488 return MULTI_XFER_FAIL;
491 // if the xfer has succeeded
492 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
493 return MULTI_XFER_SUCCESS;
496 // if the xfer is queued
497 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
498 return MULTI_XFER_QUEUED;
501 // otherwise the xfer is still in progress
502 return MULTI_XFER_IN_PROGRESS;
505 // abort a transferring file
506 void multi_xfer_abort(int handle)
510 // don't do anything if this is an invalid handle
511 if(MULTI_XFER_INVALID_HANDLE(handle)){
515 // get e handle to the entry
516 xe = &Multi_xfer_entry[handle];
518 // close any open file and delete it
519 if(xe->file != NULL){
523 // delete it if there isn't some problem with the filename
524 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
525 cf_delete(xe->ex_filename, xe->force_dir);
530 xe->file_socket = INVALID_SOCKET;
533 memset(xe,0,sizeof(xfer_entry));
536 // release an xfer handle
537 void multi_xfer_release_handle(int handle)
541 // don't do anything if this is an invalid handle
542 if(MULTI_XFER_INVALID_HANDLE(handle)){
546 // get e handle to the entry
547 xe = &Multi_xfer_entry[handle];
549 // close any open file and delete it
550 if(xe->file != NULL){
554 // delete it if the file was not successfully received
555 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
556 cf_delete(xe->ex_filename,xe->force_dir);
561 xe->file_socket = INVALID_SOCKET;
564 memset(xe,0,sizeof(xfer_entry));
567 // get the filename of the xfer for the given handle
568 char *multi_xfer_get_filename(int handle)
570 // if this is an invalid handle, return NULL
571 if(MULTI_XFER_INVALID_HANDLE(handle)){
575 // otherwise return the string
576 return Multi_xfer_entry[handle].filename;
579 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
580 void multi_xfer_lock()
582 Multi_xfer_locked = 1;
585 // unlock the xfer system
586 void multi_xfer_unlock()
588 Multi_xfer_locked = 0;
591 // force all receives to go into the specified directory by cfile type
592 void multi_xfer_force_dir(int cf_type)
594 Multi_xfer_force_dir = cf_type;
595 SDL_assert(Multi_xfer_force_dir > CF_TYPE_ANY);
598 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
599 void multi_xfer_handle_force_dir(int handle,int cf_type)
601 // if this is an invalid handle, return NULL
602 if(MULTI_XFER_INVALID_HANDLE(handle)){
606 // force to go to the given directory
607 Multi_xfer_entry[handle].force_dir = cf_type;
608 SDL_assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
611 // or the flag on a given entry
612 void multi_xfer_xor_flags(int handle,int flags)
614 // if this is an invalid handle, return NULL
615 if(MULTI_XFER_INVALID_HANDLE(handle)){
620 Multi_xfer_entry[handle].flags ^= flags;
623 // get the flags for a given entry
624 int multi_xfer_get_flags(int handle)
626 // if this is an invalid handle, return NULL
627 if(MULTI_XFER_INVALID_HANDLE(handle)){
632 return Multi_xfer_entry[handle].flags;
635 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
636 int multi_xfer_lookup(char *filename)
640 // if we have an invalid filename, do nothing
641 if((filename == NULL) || (strlen(filename) <= 0)){
645 // otherwise, perform a lookup
646 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
647 // if we found a matching filename
648 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !SDL_strcasecmp(filename,Multi_xfer_entry[idx].filename)){
653 // did not find a match
657 // get the % of completion of the passed file handle, return < 0 if invalid
658 float multi_xfer_pct_complete(int handle)
660 // if this is an invalid handle, return invalid
661 if(MULTI_XFER_INVALID_HANDLE(handle)){
665 // if the file size is 0, return invalid
666 if(Multi_xfer_entry[handle].file_size == 0){
670 // return the pct completion
671 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
674 // get the socket of the file xfer (useful for identifying players)
675 uint multi_xfer_get_sock(int handle)
677 // if this is an invalid handle, return NULL
678 if(MULTI_XFER_INVALID_HANDLE(handle)){
679 return INVALID_SOCKET;
682 return Multi_xfer_entry[handle].file_socket;
685 // get the CF_TYPE of the directory this file is going to
686 int multi_xfer_get_force_dir(int handle)
688 // if this is an invalid handle, return NULL
689 if(MULTI_XFER_INVALID_HANDLE(handle)){
690 return INVALID_SOCKET;
693 return Multi_xfer_entry[handle].force_dir;
697 // ------------------------------------------------------------------------------------------
698 // MULTI XFER FORWARD DECLARATIONS
701 // evaluate the condition of the entry
702 void multi_xfer_eval_entry(xfer_entry *xe)
708 // if the entry is marked as successful, then don't do anything
709 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
713 // if the entry is queued
714 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
715 // if the entry is not current
716 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
717 // see if there are any other queued up xfers to this target. if not, make me current and start sending
719 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
720 xe_c = &Multi_xfer_entry[idx];
722 // if this is a valid entry and is a queued entry and is going to the same target
723 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
724 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
731 // if we found no other entries, make this guy current and pending
733 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
734 xe->flags |= MULTI_XFER_FLAG_PENDING;
736 #ifdef MULTI_XFER_VERBOSE
737 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
740 // otherwise, do nothing for him - he has to still wait
747 // if the entry is marked as pending - send out the header to get the ball rolling
748 if(xe->flags & MULTI_XFER_FLAG_PENDING){
749 // send the header to begin the transfer
750 multi_xfer_send_header(xe);
753 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
755 // unset the pending flag
756 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
758 // set the ack/wait flag
759 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
762 // see if the entry has timed-out for one reason or another
763 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
764 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
766 // if we should be auto-destroying this entry, do so
767 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
768 multi_xfer_fail_entry(xe);
773 // lookup a file xfer entry by player
774 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
778 // look through all valid xfer entries
779 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
780 // if we're looking for sending entries
781 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
784 // if we're looking for recv entries
785 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
789 // if we found a match
790 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
791 return &Multi_xfer_entry[idx];
798 // set an entry to be "failed"
799 void multi_xfer_fail_entry(xfer_entry *xe)
801 // set its flags appropriately
802 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
803 xe->flags |= MULTI_XFER_FLAG_FAIL;
805 // close the file pointer
806 if(xe->file != NULL){
812 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
813 cf_delete(xe->ex_filename,xe->force_dir);
816 // null the timestamp
819 // if we should be auto-destroying this entry, do so
820 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
821 multi_xfer_release_handle(xe - Multi_xfer_entry);
824 // blast the memory clean
825 memset(xe,0,sizeof(xfer_entry));
828 // get a valid xfer entry handle
829 int multi_xfer_get_free_handle()
833 // look for a free entry
834 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
835 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
844 // ------------------------------------------------------------------------------------------
845 // MULTI XFER PACKET HANDLERS
848 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
849 // packet regardless of error conditions
850 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
855 ushort data_size = 0;
857 ushort file_checksum = 0;
859 ubyte xfer_data[600];
863 // read in all packet data
868 case MULTI_XFER_CODE_DATA:
869 GET_USHORT(data_size);
870 memcpy(xfer_data, data + offset, data_size);
876 case MULTI_XFER_CODE_HEADER:
877 GET_STRING(filename);
879 GET_USHORT(file_checksum);
884 case MULTI_XFER_CODE_ACK:
885 case MULTI_XFER_CODE_NAK:
889 case MULTI_XFER_CODE_FINAL:
897 // at this point we've read all the data in the packet
899 // at this point, we should process code-specific data
901 if(val != MULTI_XFER_CODE_HEADER){
902 // if the code is not a request or a header, we need to look up the existing xfer_entry
905 xe = multi_xfer_find_entry(who, sig, sender_side);
907 #ifdef MULTI_XFER_VERBOSE
908 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
910 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
912 int np_index = find_player_socket(who);
913 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
914 ml_printf(": sig == %d", sig);
915 ml_printf(": xfer header == %d", val);
917 ml_string(": player == unknown");
919 ml_printf(": player == %s", Net_players[np_index].player->callsign);
922 ml_string(": sending");
924 ml_string(": receiving");
934 // process an ack for this entry
935 case MULTI_XFER_CODE_ACK :
936 SDL_assert(xe != NULL);
937 multi_xfer_process_ack(xe);
940 // process a nak for this entry
941 case MULTI_XFER_CODE_NAK :
942 SDL_assert(xe != NULL);
943 multi_xfer_process_nak(xe);
946 // process a "final" packet
947 case MULTI_XFER_CODE_FINAL :
948 SDL_assert(xe != NULL);
949 multi_xfer_process_final(xe);
952 // process a data packet
953 case MULTI_XFER_CODE_DATA :
954 SDL_assert(xe != NULL);
955 multi_xfer_process_data(xe, xfer_data, data_size);
959 case MULTI_XFER_CODE_HEADER :
960 // send on my reliable socket
961 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
967 // process an ack for this entry
968 void multi_xfer_process_ack(xfer_entry *xe)
970 // if we are a sender
971 if(xe->flags & MULTI_XFER_FLAG_SEND){
972 // if we are waiting on a final ack, then the transfer has completed successfully
973 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
974 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
975 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
977 #ifdef MULTI_XFER_VERBOSE
978 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
981 // if we should be auto-destroying this entry, do so
982 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
983 multi_xfer_release_handle(xe - Multi_xfer_entry);
986 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
987 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
988 multi_xfer_send_next(xe);
993 // process a nak for this entry
994 void multi_xfer_process_nak(xfer_entry *xe)
996 // if we get an ack at any time we should simply set the xfer to failed
997 multi_xfer_fail_entry(xe);
1000 // process a "final" packet
1001 void multi_xfer_process_final(xfer_entry *xe)
1005 // make sure we skip a line
1006 nprintf(("Network","\n"));
1009 if(xe->file != NULL){
1015 // check to make sure the file checksum is the same
1017 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
1019 xe->flags |= MULTI_XFER_FLAG_FAIL;
1021 #ifdef MULTI_XFER_VERBOSE
1022 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1026 multi_xfer_abort(xe - Multi_xfer_entry);
1029 // checksums check out, so rename the file and be done with it
1031 #ifdef MULTI_XFER_VERBOSE
1032 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));
1034 // rename the file properly
1035 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1036 // mark the xfer as being successful
1037 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1039 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1041 // send an ack to the sender
1042 multi_xfer_send_ack(xe->file_socket, xe->sig);
1044 // mark it as failing
1045 xe->flags |= MULTI_XFER_FLAG_FAIL;
1046 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1048 // delete the tempfile
1049 cf_delete(xe->ex_filename, xe->force_dir);
1051 // send an nak to the sender
1052 multi_xfer_send_nak(xe->file_socket, xe->sig);
1055 // if we should be auto-destroying this entry, do so
1056 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1057 multi_xfer_release_handle(xe - Multi_xfer_entry);
1062 // process a data packet
1063 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1065 // print out a crude progress indicator
1066 nprintf(("Network","."));
1068 // attempt to write the rest of the data string to the file
1069 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1070 // inform the sender we had a problem
1071 multi_xfer_send_nak(xe->file_socket, xe->sig);
1074 multi_xfer_fail_entry(xe);
1076 xe->file_ptr += data_size;
1080 // increment the file pointer
1081 xe->file_ptr += data_size;
1083 // send an ack to the sender
1084 multi_xfer_send_ack(xe->file_socket, xe->sig);
1087 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1090 // process a header, return bytes processed
1091 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1096 // if the xfer system is locked, send a nak
1097 if(Multi_xfer_locked){
1098 multi_xfer_send_nak(who, sig);
1102 // try and get a free xfer handle
1103 handle = multi_xfer_get_free_handle();
1105 multi_xfer_send_nak(who, sig);
1108 xe = &Multi_xfer_entry[handle];
1109 memset(xe,0,sizeof(xfer_entry));
1112 // set the recv and used flags
1113 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1115 // get the header data
1116 xe->file_size = file_size;
1118 // get the file chksum
1119 xe->file_chksum = file_checksum;
1122 xe->file_socket = who;
1124 // set the timeout timestamp
1125 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1130 // copy the filename and get the prefixed xfer filename
1131 SDL_strlcpy(xe->filename, filename, SDL_arraysize(xe->filename));
1132 // lower case all filenames to avoid case issues
1133 SDL_strlwr(xe->filename);
1135 multi_xfer_conv_prefix(xe->filename, xe->ex_filename, SDL_arraysize(xe->ex_filename));
1136 #ifdef MULTI_XFER_VERBOSE
1137 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1140 // determine what directory to place the file in
1141 // individual xfer entries take precedence over the global multi xfer force entry
1142 xe->force_dir = Multi_xfer_force_dir;
1144 // call the callback function
1145 Multi_xfer_recv_notify(handle);
1147 // if the notify function invalidated this xfer handle, then cancel the whole thing
1148 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1149 multi_xfer_send_nak(who, sig);
1152 memset(xe, 0, sizeof(xfer_entry));
1156 // delete the old file (if it exists)
1157 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1158 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1160 // attempt to open the file (using the prefixed filename)
1162 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1163 if(xe->file == NULL){
1164 multi_xfer_send_nak(who, sig);
1167 memset(xe, 0, sizeof(xfer_entry));
1171 // set the waiting for data flag
1172 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1174 // send an ack to the server
1175 multi_xfer_send_ack(who, sig);
1177 #ifdef MULTI_XFER_VERBOSE
1178 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1182 // send the next block of outgoing data or a "final" packet if we're done
1183 void multi_xfer_send_next(xfer_entry *xe)
1185 ubyte data[MAX_PACKET_SIZE],code;
1188 int packet_size = 0;
1190 // print out a crude progress indicator
1191 nprintf(("Network", "+"));
1193 // if we've sent all the data, then we should send a "final" packet
1194 if(xe->file_ptr >= xe->file_size){
1195 // mark the entry as unknown
1196 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1199 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1202 multi_xfer_send_final(xe);
1208 BUILD_HEADER(XFER_PACKET);
1210 // length of the added string
1211 flen = strlen(xe->filename) + 4;
1213 // determine how much data we are going to send with this packet and add it in
1214 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1215 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1217 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1219 // increment the file pointer
1220 xe->file_ptr += data_size;
1223 code = MULTI_XFER_CODE_DATA;
1227 ADD_USHORT(xe->sig);
1229 // add in the size of the rest of the packet
1230 ADD_USHORT(data_size);
1233 if(cfread(data+packet_size,1,(int)data_size,xe->file) == 0){
1234 // send a nack to the receiver
1235 multi_xfer_send_nak(xe->file_socket, xe->sig);
1238 multi_xfer_fail_entry(xe);
1242 // increment the packet size
1243 packet_size += (int)data_size;
1246 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1248 // otherwise send the data
1249 psnet_rel_send(xe->file_socket, data, packet_size);
1252 // send an ack to the sender
1253 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1255 ubyte data[MAX_PACKET_SIZE],code;
1256 int packet_size = 0;
1258 // build the header and add
1259 BUILD_HEADER(XFER_PACKET);
1262 code = MULTI_XFER_CODE_ACK;
1269 psnet_rel_send(socket, data, packet_size);
1272 // send a nak to the sender
1273 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1275 ubyte data[MAX_PACKET_SIZE],code;
1276 int packet_size = 0;
1278 // build the header and add the code
1279 BUILD_HEADER(XFER_PACKET);
1282 code = MULTI_XFER_CODE_NAK;
1289 psnet_rel_send(socket, data, packet_size);
1292 // send a "final" packet
1293 void multi_xfer_send_final(xfer_entry *xe)
1295 ubyte data[MAX_PACKET_SIZE],code;
1296 int packet_size = 0;
1299 BUILD_HEADER(XFER_PACKET);
1302 code = MULTI_XFER_CODE_FINAL;
1306 ADD_USHORT(xe->sig);
1309 psnet_rel_send(xe->file_socket, data, packet_size);
1312 // send the header to begin a file transfer
1313 void multi_xfer_send_header(xfer_entry *xe)
1315 ubyte data[MAX_PACKET_SIZE],code;
1316 int packet_size = 0;
1318 // build the header and add the opcode
1319 BUILD_HEADER(XFER_PACKET);
1320 code = MULTI_XFER_CODE_HEADER;
1324 ADD_USHORT(xe->sig);
1327 ADD_STRING(xe->filename);
1330 ADD_INT(xe->file_size);
1332 // add the file checksum
1333 ADD_USHORT(xe->file_chksum);
1336 psnet_rel_send(xe->file_socket, data, packet_size);
1339 // convert the filename into the prefixed ex_filename
1340 void multi_xfer_conv_prefix(char *filename, char *ex_filename, const int max_len)
1342 char temp[MAX_FILENAME_LEN+50];
1344 // blast the memory clean
1345 memset(temp, 0, MAX_FILENAME_LEN+50);
1347 // copy in the prefix
1348 SDL_strlcpy(temp, MULTI_XFER_FNAME_PREFIX, SDL_arraysize(temp));
1350 // stick on the original name
1351 SDL_strlcat(temp, filename, SDL_arraysize(temp));
1353 // copy the whole thing to the outgoing filename
1354 SDL_strlcpy(ex_filename, temp, max_len);
1357 // get a new xfer sig
1358 ushort multi_xfer_get_sig()
1360 ushort ret = Multi_xfer_sig;
1363 if(Multi_xfer_sig == 0xffff){