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.7 2004/06/11 01:46:42 tigital
17 * byte-swapping changes for bigendian systems
19 * Revision 1.6 2003/06/22 12:51:02 taylor
20 * lower case file transfers
22 * Revision 1.5 2002/06/09 04:41:24 relnev
23 * added copyright header
25 * Revision 1.4 2002/06/01 07:12:33 relnev
26 * a few NDEBUG updates.
28 * removed a few warnings.
30 * Revision 1.3 2002/05/26 20:22:48 theoddone33
31 * Most of network/ works
33 * Revision 1.2 2002/05/07 03:16:47 theoddone33
34 * The Great Newline Fix
36 * Revision 1.1.1.1 2002/05/03 03:28:10 root
40 * 11 3/10/99 6:50p Dave
41 * Changed the way we buffer packets for all clients. Optimized turret
42 * fired packets. Did some weapon firing optimizations.
44 * 10 3/09/99 6:24p Dave
45 * More work on object update revamping. Identified several sources of
46 * unnecessary bandwidth.
48 * 9 1/24/99 11:37p Dave
49 * First full rev of beam weapons. Very customizable. Removed some bogus
50 * Int3()'s in low level net code.
52 * 8 12/16/98 11:17a Dave
53 * Fixed potential situation where a send and receive to the same player
54 * with the same sig might get confused with each other.
56 * 7 12/14/98 4:01p Dave
57 * Got multi_data stuff working well with new xfer stuff.
59 * 6 12/14/98 12:13p Dave
60 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
63 * 5 11/19/98 8:03a Dave
64 * Full support for D3-style reliable sockets. Revamped packet lag/loss
65 * system, made it receiver side and at the lowest possible level.
67 * 4 11/17/98 11:12a Dave
68 * Removed player identification by address. Now assign explicit id #'s.
70 * 3 11/05/98 5:55p Dave
71 * Big pass at reducing #includes
73 * 2 10/07/98 10:53a Dave
76 * 1 10/07/98 10:50a Dave
78 * 56 9/11/98 9:31a Dave
79 * Fixed file xfer bug regarding checksums.
81 * 55 8/12/98 4:53p Dave
82 * Put in 32 bit checksumming for PXO missions. No validation on the
83 * actual tracker yet, though.
85 * 54 6/13/98 9:32p Mike
86 * Kill last character in file which caused "Find in Files" to report the
87 * file as "not a text file."
89 * 53 6/13/98 6:01p Hoffoss
90 * Externalized all new (or forgot to be added) strings to all the code.
92 * 52 6/13/98 3:19p Hoffoss
93 * NOX()ed out a bunch of strings that shouldn't be translated.
95 * 51 5/21/98 1:52a Dave
96 * Remove obsolete command line functions. Reduce shield explosion packets
97 * drastically. Tweak PXO screen even more. Fix file xfer system so that
98 * we can guarantee file uniqueness.
100 * 50 4/30/98 4:53p John
101 * Restructured and cleaned up cfile code. Added capability to read off
102 * of CD-ROM drive and out of multiple pack files.
104 * 49 4/23/98 6:18p Dave
105 * Store ETS values between respawns. Put kick feature in the text
106 * messaging system. Fixed text messaging system so that it doesn't
107 * process or trigger ship controls. Other UI fixes.
109 * 48 4/22/98 5:53p Dave
110 * Large reworking of endgame sequencing. Updated multi host options
111 * screen for new artwork. Put in checks for host or team captains leaving
114 * 47 4/21/98 4:44p Dave
115 * Implement Vasudan ships in multiplayer. Added a debug function to bash
116 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
117 * problem in options screen.
119 * 46 4/20/98 6:04p Dave
120 * Implement multidata cache flushing and xferring mission files to
121 * multidata. Make sure observers can't change hud config. Fix pilot image
122 * viewing in popup. Put in game status field. Tweaked multi options.
124 * 45 4/08/98 2:51p Dave
125 * Fixed pilot image xfer once again. Solidify team selection process in
126 * pre-briefing multiplayer.
128 * 44 4/04/98 4:22p Dave
129 * First rev of UDP reliable sockets is done. Seems to work well if not
132 * 43 4/03/98 1:03a Dave
133 * First pass at unreliable guaranteed delivery packets.
135 * 42 4/01/98 11:19p Dave
136 * Put in auto-loading of xferred pilot pic files. Grey out background
137 * behind pinfo popup. Put a chatbox message in when players are kicked.
138 * Moved mission title down in briefing. Other ui fixes.
140 * 41 3/31/98 4:51p Dave
141 * Removed medals screen and multiplayer buttons from demo version. Put in
142 * new pilot popup screen. Make ships in mp team vs. team have proper team
143 * ids. Make mp respawns a permanent option saved in the player file.
145 * 40 3/30/98 8:46a Allender
146 * fix an optimized build compiler warning
148 * 39 3/26/98 6:01p Dave
149 * Put in file checksumming routine in cfile. Made pilot pic xferring more
150 * robust. Cut header size of voice data packets in half. Put in
151 * restricted game host query system.
153 * 38 3/25/98 2:16p Dave
154 * Select random default image for newly created pilots. Fixed several
155 * multi-pause messaging bugs. Begin work on online help for multiplayer
158 * 37 3/24/98 5:00p Dave
159 * Fixed several ui bugs. Put in pre and post voice stream playback sound
160 * fx. Put in error specific popups for clients getting dropped from games
161 * through actions other than their own.
163 * 36 3/23/98 5:42p Dave
164 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
165 * that it can support multiplayer sends/received between client and
166 * server simultaneously.
168 * 35 3/21/98 7:14p Dave
169 * Fixed up standalone player slot switching. Made training missions not
170 * count towards player stats.
172 * 34 3/16/98 11:52p Allender
173 * Put in timestamp updates when processing data on both sender and
176 * 33 2/22/98 2:53p Dave
177 * Put in groundwork for advanced multiplayer campaign options.
179 * 32 2/20/98 4:43p Dave
180 * Finished support for multiplayer player data files. Split off
181 * multiplayer campaign functionality.
183 * 31 2/19/98 6:26p Dave
184 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
185 * initial support for player data uploading.
187 * 30 2/18/98 10:21p Dave
188 * Ripped out old file xfer system. Put in brand new xfer system.
197 #include "multi_xfer.h"
199 #include "multimsgs.h"
202 #include "multi_endgame.h"
205 #include "multiutil.h"
206 #include "multi_log.h"
208 // ------------------------------------------------------------------------------------------
209 // MULTI XFER DEFINES/VARS
212 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
214 #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) )
217 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
218 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
219 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
220 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
221 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
224 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
225 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
226 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
227 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
228 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
229 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
230 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
231 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
232 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
233 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
234 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
236 // packet size for file xfer
237 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
239 // timeout for a given xfer operation
240 #define MULTI_XFER_TIMEOUT 10000
244 // temp filename header for xferring files
245 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
249 // xfer entries/events
250 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
251 typedef struct xfer_entry {
252 int flags; // status flags for this entry
253 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
254 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
255 CFILE *file; // file handle of the current xferring file
256 int file_size; // total size of the file being xferred
257 int file_ptr; // total bytes we're received so far
258 ushort file_chksum; // used for checking successfully xferred files
259 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
260 int xfer_stamp; // timestamp for the current operation
261 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
262 ushort sig; // identifying sig - sender specifies this
264 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
266 // callback function pointer for when we start receiving a file
267 void (*Multi_xfer_recv_notify)(int handle);
269 // lock for the xfer system
270 int Multi_xfer_locked;
272 // force directory type for receives
273 int Multi_xfer_force_dir;
275 // unique file signature - this along with a socket # is enough to identify all xfers
276 ushort Multi_xfer_sig = 0;
279 // ------------------------------------------------------------------------------------------
280 // MULTI XFER FORWARD DECLARATIONS
283 // evaluate the condition of the entry
284 void multi_xfer_eval_entry(xfer_entry *xe);
286 // set an entry to be "failed"
287 void multi_xfer_fail_entry(xfer_entry *xe);
289 // get a valid xfer entry handle
290 int multi_xfer_get_free_handle();
292 // process an ack for this entry
293 void multi_xfer_process_ack(xfer_entry *xe);
295 // process a nak for this entry
296 void multi_xfer_process_nak(xfer_entry *xe);
298 // process a "final" packet
299 void multi_xfer_process_final(xfer_entry *xe);
301 // process a data packet
302 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
305 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
307 // send the next block of outgoing data or a "final" packet if we're done
308 void multi_xfer_send_next(xfer_entry *xe);
310 // send an ack to the sender
311 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
313 // send a nak to the sender
314 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
316 // send a "final" packet
317 void multi_xfer_send_final(xfer_entry *xe);
319 // send the header to begin a file transfer
320 void multi_xfer_send_header(xfer_entry *xe);
322 // convert the filename into the prefixed ex_filename
323 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
325 // get a new xfer sig
326 ushort multi_xfer_get_sig();
328 // ------------------------------------------------------------------------------------------
329 // MULTI XFER FUNCTIONS
332 // initialize all file xfer transaction stuff, call in multi_level_init()
333 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
335 // blast all the entries
336 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
338 // assign the receive callback function pointer
339 Multi_xfer_recv_notify = multi_xfer_recv_callback;
342 Multi_xfer_locked = 0;
344 // no forced directory
345 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
348 // do frame for all file xfers, call in multi_do_frame()
353 // process all valid xfer entries
354 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
355 // if this one is actually in use and has not finished for one reason or another
356 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))){
357 // evaluate the condition of this entry (fail, timeout, etc)
358 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
363 // close down the file xfer system
364 void multi_xfer_close()
368 // go through all active entries and abort them
369 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
370 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
371 multi_xfer_abort(idx);
375 // now blast all the memory free
376 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
379 // reset the xfer system, including shutting down/killing all active xfers
380 void multi_xfer_reset()
384 // shut down all active xfers
385 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
386 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
387 multi_xfer_abort(idx);
391 // blast all the memory clean
392 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
395 // send a file to the specified player, return a handle
396 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
398 xfer_entry temp_entry;
401 // if the system is locked, return -1
402 if(Multi_xfer_locked){
406 // attempt to get a free handle
407 handle = multi_xfer_get_free_handle();
412 // clear the temp entry
413 memset(&temp_entry,0,sizeof(xfer_entry));
416 strcpy(temp_entry.filename,filename);
418 // attempt to open the file
419 temp_entry.file = NULL;
420 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
421 if(temp_entry.file == NULL){
422 #ifdef MULTI_XFER_VERBOSE
423 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
430 temp_entry.file_size = -1;
431 temp_entry.file_size = cfilelength(temp_entry.file);
432 if(temp_entry.file_size == -1){
433 #ifdef MULTI_XFER_VERBOSE
434 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
438 temp_entry.file_ptr = 0;
440 // get the file checksum
441 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
442 #ifdef MULTI_XFER_VERBOSE
443 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
447 #ifdef MULTI_XFER_VERBOSE
448 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
450 // rewind the file pointer to the beginning of the file
451 cfseek(temp_entry.file,0,CF_SEEK_SET);
454 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
455 temp_entry.flags |= flags;
458 temp_entry.file_socket = who;
461 temp_entry.sig = multi_xfer_get_sig();
463 // copy to the global array
464 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
465 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
470 // get the status of the current file xfer
471 int multi_xfer_get_status(int handle)
473 // if this is an invalid or an unused handle, notify as such
474 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
475 return MULTI_XFER_NONE;
478 // if the xfer has timed-out
479 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
480 return MULTI_XFER_TIMEDOUT;
483 // if the xfer has failed for one reason or another (not timeout)
484 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
485 return MULTI_XFER_FAIL;
488 // if the xfer has succeeded
489 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
490 return MULTI_XFER_SUCCESS;
493 // if the xfer is queued
494 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
495 return MULTI_XFER_QUEUED;
498 // otherwise the xfer is still in progress
499 return MULTI_XFER_IN_PROGRESS;
502 // abort a transferring file
503 void multi_xfer_abort(int handle)
507 // don't do anything if this is an invalid handle
508 if(MULTI_XFER_INVALID_HANDLE(handle)){
512 // get e handle to the entry
513 xe = &Multi_xfer_entry[handle];
515 // close any open file and delete it
516 if(xe->file != NULL){
520 // delete it if there isn't some problem with the filename
521 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
522 cf_delete(xe->ex_filename, xe->force_dir);
527 xe->file_socket = INVALID_SOCKET;
530 memset(xe,0,sizeof(xfer_entry));
533 // release an xfer handle
534 void multi_xfer_release_handle(int handle)
538 // don't do anything if this is an invalid handle
539 if(MULTI_XFER_INVALID_HANDLE(handle)){
543 // get e handle to the entry
544 xe = &Multi_xfer_entry[handle];
546 // close any open file and delete it
547 if(xe->file != NULL){
551 // delete it if the file was not successfully received
552 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
553 cf_delete(xe->ex_filename,xe->force_dir);
558 xe->file_socket = INVALID_SOCKET;
561 memset(xe,0,sizeof(xfer_entry));
564 // get the filename of the xfer for the given handle
565 char *multi_xfer_get_filename(int handle)
567 // if this is an invalid handle, return NULL
568 if(MULTI_XFER_INVALID_HANDLE(handle)){
572 // otherwise return the string
573 return Multi_xfer_entry[handle].filename;
576 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
577 void multi_xfer_lock()
579 Multi_xfer_locked = 1;
582 // unlock the xfer system
583 void multi_xfer_unlock()
585 Multi_xfer_locked = 0;
588 // force all receives to go into the specified directory by cfile type
589 void multi_xfer_force_dir(int cf_type)
591 Multi_xfer_force_dir = cf_type;
592 Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
595 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
596 void multi_xfer_handle_force_dir(int handle,int cf_type)
598 // if this is an invalid handle, return NULL
599 if(MULTI_XFER_INVALID_HANDLE(handle)){
603 // force to go to the given directory
604 Multi_xfer_entry[handle].force_dir = cf_type;
605 Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
608 // or the flag on a given entry
609 void multi_xfer_xor_flags(int handle,int flags)
611 // if this is an invalid handle, return NULL
612 if(MULTI_XFER_INVALID_HANDLE(handle)){
617 Multi_xfer_entry[handle].flags ^= flags;
620 // get the flags for a given entry
621 int multi_xfer_get_flags(int handle)
623 // if this is an invalid handle, return NULL
624 if(MULTI_XFER_INVALID_HANDLE(handle)){
629 return Multi_xfer_entry[handle].flags;
632 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
633 int multi_xfer_lookup(char *filename)
637 // if we have an invalid filename, do nothing
638 if((filename == NULL) || (strlen(filename) <= 0)){
642 // otherwise, perform a lookup
643 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
644 // if we found a matching filename
645 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
650 // did not find a match
654 // get the % of completion of the passed file handle, return < 0 if invalid
655 float multi_xfer_pct_complete(int handle)
657 // if this is an invalid handle, return invalid
658 if(MULTI_XFER_INVALID_HANDLE(handle)){
662 // if the file size is 0, return invalid
663 if(Multi_xfer_entry[handle].file_size == 0){
667 // return the pct completion
668 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
671 // get the socket of the file xfer (useful for identifying players)
672 uint multi_xfer_get_sock(int handle)
674 // if this is an invalid handle, return NULL
675 if(MULTI_XFER_INVALID_HANDLE(handle)){
676 return INVALID_SOCKET;
679 return Multi_xfer_entry[handle].file_socket;
682 // get the CF_TYPE of the directory this file is going to
683 int multi_xfer_get_force_dir(int handle)
685 // if this is an invalid handle, return NULL
686 if(MULTI_XFER_INVALID_HANDLE(handle)){
687 return INVALID_SOCKET;
690 return Multi_xfer_entry[handle].force_dir;
694 // ------------------------------------------------------------------------------------------
695 // MULTI XFER FORWARD DECLARATIONS
698 // evaluate the condition of the entry
699 void multi_xfer_eval_entry(xfer_entry *xe)
705 // if the entry is marked as successful, then don't do anything
706 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
710 // if the entry is queued
711 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
712 // if the entry is not current
713 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
714 // see if there are any other queued up xfers to this target. if not, make me current and start sending
716 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
717 xe_c = &Multi_xfer_entry[idx];
719 // if this is a valid entry and is a queued entry and is going to the same target
720 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
721 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
728 // if we found no other entries, make this guy current and pending
730 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
731 xe->flags |= MULTI_XFER_FLAG_PENDING;
733 #ifdef MULTI_XFER_VERBOSE
734 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
737 // otherwise, do nothing for him - he has to still wait
744 // if the entry is marked as pending - send out the header to get the ball rolling
745 if(xe->flags & MULTI_XFER_FLAG_PENDING){
746 // send the header to begin the transfer
747 multi_xfer_send_header(xe);
750 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
752 // unset the pending flag
753 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
755 // set the ack/wait flag
756 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
759 // see if the entry has timed-out for one reason or another
760 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
761 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
763 // if we should be auto-destroying this entry, do so
764 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
765 multi_xfer_fail_entry(xe);
770 // lookup a file xfer entry by player
771 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
775 // look through all valid xfer entries
776 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
777 // if we're looking for sending entries
778 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
781 // if we're looking for recv entries
782 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
786 // if we found a match
787 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
788 return &Multi_xfer_entry[idx];
795 // set an entry to be "failed"
796 void multi_xfer_fail_entry(xfer_entry *xe)
798 // set its flags appropriately
799 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
800 xe->flags |= MULTI_XFER_FLAG_FAIL;
802 // close the file pointer
803 if(xe->file != NULL){
809 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
810 cf_delete(xe->ex_filename,xe->force_dir);
813 // null the timestamp
816 // if we should be auto-destroying this entry, do so
817 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
818 multi_xfer_release_handle(xe - Multi_xfer_entry);
821 // blast the memory clean
822 memset(xe,0,sizeof(xfer_entry));
825 // get a valid xfer entry handle
826 int multi_xfer_get_free_handle()
830 // look for a free entry
831 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
832 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
841 // ------------------------------------------------------------------------------------------
842 // MULTI XFER PACKET HANDLERS
845 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
846 // packet regardless of error conditions
847 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
852 ushort data_size = 0;
854 ushort file_checksum = 0;
856 ubyte xfer_data[600];
860 // read in all packet data
865 case MULTI_XFER_CODE_DATA:
866 GET_DATA_U16(data_size);
867 memcpy(xfer_data, data + offset, data_size);
873 case MULTI_XFER_CODE_HEADER:
874 GET_STRING(filename);
875 GET_DATA_S32(file_size);
876 GET_DATA_U16(file_checksum);
881 case MULTI_XFER_CODE_ACK:
882 case MULTI_XFER_CODE_NAK:
886 case MULTI_XFER_CODE_FINAL:
894 // at this point we've read all the data in the packet
896 // at this point, we should process code-specific data
898 if(val != MULTI_XFER_CODE_HEADER){
899 // if the code is not a request or a header, we need to look up the existing xfer_entry
902 xe = multi_xfer_find_entry(who, sig, sender_side);
904 #ifdef MULTI_XFER_VERBOSE
905 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
907 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
909 int np_index = find_player_socket(who);
910 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
911 ml_printf(": sig == %d", sig);
912 ml_printf(": xfer header == %d", val);
914 ml_string(": player == unknown");
916 ml_printf(": player == %s", Net_players[np_index].player->callsign);
919 ml_string(": sending");
921 ml_string(": receiving");
931 // process an ack for this entry
932 case MULTI_XFER_CODE_ACK :
934 multi_xfer_process_ack(xe);
937 // process a nak for this entry
938 case MULTI_XFER_CODE_NAK :
940 multi_xfer_process_nak(xe);
943 // process a "final" packet
944 case MULTI_XFER_CODE_FINAL :
946 multi_xfer_process_final(xe);
949 // process a data packet
950 case MULTI_XFER_CODE_DATA :
952 multi_xfer_process_data(xe, xfer_data, data_size);
956 case MULTI_XFER_CODE_HEADER :
957 // send on my reliable socket
958 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
964 // process an ack for this entry
965 void multi_xfer_process_ack(xfer_entry *xe)
967 // if we are a sender
968 if(xe->flags & MULTI_XFER_FLAG_SEND){
969 // if we are waiting on a final ack, then the transfer has completed successfully
970 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
971 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
972 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
974 #ifdef MULTI_XFER_VERBOSE
975 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
978 // if we should be auto-destroying this entry, do so
979 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
980 multi_xfer_release_handle(xe - Multi_xfer_entry);
983 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
984 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
985 multi_xfer_send_next(xe);
990 // process a nak for this entry
991 void multi_xfer_process_nak(xfer_entry *xe)
993 // if we get an ack at any time we should simply set the xfer to failed
994 multi_xfer_fail_entry(xe);
997 // process a "final" packet
998 void multi_xfer_process_final(xfer_entry *xe)
1002 // make sure we skip a line
1003 nprintf(("Network","\n"));
1006 if(xe->file != NULL){
1012 // check to make sure the file checksum is the same
1014 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
1016 xe->flags |= MULTI_XFER_FLAG_FAIL;
1018 #ifdef MULTI_XFER_VERBOSE
1019 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1023 multi_xfer_abort(xe - Multi_xfer_entry);
1026 // checksums check out, so rename the file and be done with it
1028 #ifdef MULTI_XFER_VERBOSE
1029 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));
1031 // rename the file properly
1032 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1033 // mark the xfer as being successful
1034 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1036 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1038 // send an ack to the sender
1039 multi_xfer_send_ack(xe->file_socket, xe->sig);
1041 // mark it as failing
1042 xe->flags |= MULTI_XFER_FLAG_FAIL;
1043 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1045 // delete the tempfile
1046 cf_delete(xe->ex_filename, xe->force_dir);
1048 // send an nak to the sender
1049 multi_xfer_send_nak(xe->file_socket, xe->sig);
1052 // if we should be auto-destroying this entry, do so
1053 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1054 multi_xfer_release_handle(xe - Multi_xfer_entry);
1059 // process a data packet
1060 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1062 // print out a crude progress indicator
1063 nprintf(("Network","."));
1065 // attempt to write the rest of the data string to the file
1066 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1067 // inform the sender we had a problem
1068 multi_xfer_send_nak(xe->file_socket, xe->sig);
1071 multi_xfer_fail_entry(xe);
1073 xe->file_ptr += data_size;
1077 // increment the file pointer
1078 xe->file_ptr += data_size;
1080 // send an ack to the sender
1081 multi_xfer_send_ack(xe->file_socket, xe->sig);
1084 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1087 // process a header, return bytes processed
1088 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1093 // if the xfer system is locked, send a nak
1094 if(Multi_xfer_locked){
1095 multi_xfer_send_nak(who, sig);
1099 // try and get a free xfer handle
1100 handle = multi_xfer_get_free_handle();
1102 multi_xfer_send_nak(who, sig);
1105 xe = &Multi_xfer_entry[handle];
1106 memset(xe,0,sizeof(xfer_entry));
1109 // set the recv and used flags
1110 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1112 // get the header data
1113 xe->file_size = file_size;
1115 // get the file chksum
1116 xe->file_chksum = file_checksum;
1119 xe->file_socket = who;
1121 // set the timeout timestamp
1122 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1127 // copy the filename and get the prefixed xfer filename
1129 // lower case all filenames to avoid case issues
1130 char *tmp_filename = filename;
1132 strlwr(tmp_filename);
1133 strcpy(xe->filename, tmp_filename);
1135 strcpy(xe->filename, filename);
1137 multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1138 #ifdef MULTI_XFER_VERBOSE
1139 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1142 // determine what directory to place the file in
1143 // individual xfer entries take precedence over the global multi xfer force entry
1144 xe->force_dir = Multi_xfer_force_dir;
1146 // call the callback function
1147 Multi_xfer_recv_notify(handle);
1149 // if the notify function invalidated this xfer handle, then cancel the whole thing
1150 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1151 multi_xfer_send_nak(who, sig);
1154 memset(xe, 0, sizeof(xfer_entry));
1158 // delete the old file (if it exists)
1159 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1160 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1162 // attempt to open the file (using the prefixed filename)
1164 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1165 if(xe->file == NULL){
1166 multi_xfer_send_nak(who, sig);
1169 memset(xe, 0, sizeof(xfer_entry));
1173 // set the waiting for data flag
1174 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1176 // send an ack to the server
1177 multi_xfer_send_ack(who, sig);
1179 #ifdef MULTI_XFER_VERBOSE
1180 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1184 // send the next block of outgoing data or a "final" packet if we're done
1185 void multi_xfer_send_next(xfer_entry *xe)
1187 ubyte data[MAX_PACKET_SIZE],code;
1190 int packet_size = 0;
1192 // print out a crude progress indicator
1193 nprintf(("Network", "+"));
1195 // if we've sent all the data, then we should send a "final" packet
1196 if(xe->file_ptr >= xe->file_size){
1197 // mark the entry as unknown
1198 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1201 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1204 multi_xfer_send_final(xe);
1210 BUILD_HEADER(XFER_PACKET);
1212 // length of the added string
1213 flen = strlen(xe->filename) + 4;
1215 // determine how much data we are going to send with this packet and add it in
1216 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1217 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1219 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1221 // increment the file pointer
1222 xe->file_ptr += data_size;
1225 code = MULTI_XFER_CODE_DATA;
1229 ADD_DATA_U16(xe->sig);
1231 // add in the size of the rest of the packet
1232 ADD_DATA_U16(data_size);
1235 if(cfread(data+packet_size,1,(int)data_size,xe->file) == 0){
1236 // send a nack to the receiver
1237 multi_xfer_send_nak(xe->file_socket, xe->sig);
1240 multi_xfer_fail_entry(xe);
1244 // increment the packet size
1245 packet_size += (int)data_size;
1248 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1250 // otherwise send the data
1251 psnet_rel_send(xe->file_socket, data, packet_size);
1254 // send an ack to the sender
1255 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1257 ubyte data[MAX_PACKET_SIZE],code;
1258 int packet_size = 0;
1260 // build the header and add
1261 BUILD_HEADER(XFER_PACKET);
1264 code = MULTI_XFER_CODE_ACK;
1271 psnet_rel_send(socket, data, packet_size);
1274 // send a nak to the sender
1275 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1277 ubyte data[MAX_PACKET_SIZE],code;
1278 int packet_size = 0;
1280 // build the header and add the code
1281 BUILD_HEADER(XFER_PACKET);
1284 code = MULTI_XFER_CODE_NAK;
1291 psnet_rel_send(socket, data, packet_size);
1294 // send a "final" packet
1295 void multi_xfer_send_final(xfer_entry *xe)
1297 ubyte data[MAX_PACKET_SIZE],code;
1298 int packet_size = 0;
1301 BUILD_HEADER(XFER_PACKET);
1304 code = MULTI_XFER_CODE_FINAL;
1308 ADD_DATA_U16(xe->sig);
1311 psnet_rel_send(xe->file_socket, data, packet_size);
1314 // send the header to begin a file transfer
1315 void multi_xfer_send_header(xfer_entry *xe)
1317 ubyte data[MAX_PACKET_SIZE],code;
1318 int packet_size = 0;
1320 // build the header and add the opcode
1321 BUILD_HEADER(XFER_PACKET);
1322 code = MULTI_XFER_CODE_HEADER;
1326 ADD_DATA_U16(xe->sig);
1329 ADD_STRING(xe->filename);
1332 ADD_DATA_S32(xe->file_size);
1334 // add the file checksum
1335 ADD_DATA_U16(xe->file_chksum);
1338 psnet_rel_send(xe->file_socket, data, packet_size);
1341 // convert the filename into the prefixed ex_filename
1342 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1344 char temp[MAX_FILENAME_LEN+50];
1346 // blast the memory clean
1347 memset(temp, 0, MAX_FILENAME_LEN+50);
1349 // copy in the prefix
1350 strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1352 // stick on the original name
1353 strcat(temp, filename);
1355 // copy the whole thing to the outgoing filename
1356 strcpy(ex_filename, temp);
1359 // get a new xfer sig
1360 ushort multi_xfer_get_sig()
1362 ushort ret = Multi_xfer_sig;
1365 if(Multi_xfer_sig == 0xffff){