2 * $Logfile: /Freespace2/code/Network/multi_xfer.cpp $
8 * Revision 1.2 2002/05/07 03:16:47 theoddone33
9 * The Great Newline Fix
11 * Revision 1.1.1.1 2002/05/03 03:28:10 root
15 * 11 3/10/99 6:50p Dave
16 * Changed the way we buffer packets for all clients. Optimized turret
17 * fired packets. Did some weapon firing optimizations.
19 * 10 3/09/99 6:24p Dave
20 * More work on object update revamping. Identified several sources of
21 * unnecessary bandwidth.
23 * 9 1/24/99 11:37p Dave
24 * First full rev of beam weapons. Very customizable. Removed some bogus
25 * Int3()'s in low level net code.
27 * 8 12/16/98 11:17a Dave
28 * Fixed potential situation where a send and receive to the same player
29 * with the same sig might get confused with each other.
31 * 7 12/14/98 4:01p Dave
32 * Got multi_data stuff working well with new xfer stuff.
34 * 6 12/14/98 12:13p Dave
35 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
38 * 5 11/19/98 8:03a Dave
39 * Full support for D3-style reliable sockets. Revamped packet lag/loss
40 * system, made it receiver side and at the lowest possible level.
42 * 4 11/17/98 11:12a Dave
43 * Removed player identification by address. Now assign explicit id #'s.
45 * 3 11/05/98 5:55p Dave
46 * Big pass at reducing #includes
48 * 2 10/07/98 10:53a Dave
51 * 1 10/07/98 10:50a Dave
53 * 56 9/11/98 9:31a Dave
54 * Fixed file xfer bug regarding checksums.
56 * 55 8/12/98 4:53p Dave
57 * Put in 32 bit checksumming for PXO missions. No validation on the
58 * actual tracker yet, though.
60 * 54 6/13/98 9:32p Mike
61 * Kill last character in file which caused "Find in Files" to report the
62 * file as "not a text file."
64 * 53 6/13/98 6:01p Hoffoss
65 * Externalized all new (or forgot to be added) strings to all the code.
67 * 52 6/13/98 3:19p Hoffoss
68 * NOX()ed out a bunch of strings that shouldn't be translated.
70 * 51 5/21/98 1:52a Dave
71 * Remove obsolete command line functions. Reduce shield explosion packets
72 * drastically. Tweak PXO screen even more. Fix file xfer system so that
73 * we can guarantee file uniqueness.
75 * 50 4/30/98 4:53p John
76 * Restructured and cleaned up cfile code. Added capability to read off
77 * of CD-ROM drive and out of multiple pack files.
79 * 49 4/23/98 6:18p Dave
80 * Store ETS values between respawns. Put kick feature in the text
81 * messaging system. Fixed text messaging system so that it doesn't
82 * process or trigger ship controls. Other UI fixes.
84 * 48 4/22/98 5:53p Dave
85 * Large reworking of endgame sequencing. Updated multi host options
86 * screen for new artwork. Put in checks for host or team captains leaving
89 * 47 4/21/98 4:44p Dave
90 * Implement Vasudan ships in multiplayer. Added a debug function to bash
91 * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
92 * problem in options screen.
94 * 46 4/20/98 6:04p Dave
95 * Implement multidata cache flushing and xferring mission files to
96 * multidata. Make sure observers can't change hud config. Fix pilot image
97 * viewing in popup. Put in game status field. Tweaked multi options.
99 * 45 4/08/98 2:51p Dave
100 * Fixed pilot image xfer once again. Solidify team selection process in
101 * pre-briefing multiplayer.
103 * 44 4/04/98 4:22p Dave
104 * First rev of UDP reliable sockets is done. Seems to work well if not
107 * 43 4/03/98 1:03a Dave
108 * First pass at unreliable guaranteed delivery packets.
110 * 42 4/01/98 11:19p Dave
111 * Put in auto-loading of xferred pilot pic files. Grey out background
112 * behind pinfo popup. Put a chatbox message in when players are kicked.
113 * Moved mission title down in briefing. Other ui fixes.
115 * 41 3/31/98 4:51p Dave
116 * Removed medals screen and multiplayer buttons from demo version. Put in
117 * new pilot popup screen. Make ships in mp team vs. team have proper team
118 * ids. Make mp respawns a permanent option saved in the player file.
120 * 40 3/30/98 8:46a Allender
121 * fix an optimized build compiler warning
123 * 39 3/26/98 6:01p Dave
124 * Put in file checksumming routine in cfile. Made pilot pic xferring more
125 * robust. Cut header size of voice data packets in half. Put in
126 * restricted game host query system.
128 * 38 3/25/98 2:16p Dave
129 * Select random default image for newly created pilots. Fixed several
130 * multi-pause messaging bugs. Begin work on online help for multiplayer
133 * 37 3/24/98 5:00p Dave
134 * Fixed several ui bugs. Put in pre and post voice stream playback sound
135 * fx. Put in error specific popups for clients getting dropped from games
136 * through actions other than their own.
138 * 36 3/23/98 5:42p Dave
139 * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
140 * that it can support multiplayer sends/received between client and
141 * server simultaneously.
143 * 35 3/21/98 7:14p Dave
144 * Fixed up standalone player slot switching. Made training missions not
145 * count towards player stats.
147 * 34 3/16/98 11:52p Allender
148 * Put in timestamp updates when processing data on both sender and
151 * 33 2/22/98 2:53p Dave
152 * Put in groundwork for advanced multiplayer campaign options.
154 * 32 2/20/98 4:43p Dave
155 * Finished support for multiplayer player data files. Split off
156 * multiplayer campaign functionality.
158 * 31 2/19/98 6:26p Dave
159 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
160 * initial support for player data uploading.
162 * 30 2/18/98 10:21p Dave
163 * Ripped out old file xfer system. Put in brand new xfer system.
170 #include "multi_xfer.h"
172 #include "multimsgs.h"
175 #include "multi_endgame.h"
178 #include "multiutil.h"
179 #include "multi_log.h"
181 // ------------------------------------------------------------------------------------------
182 // MULTI XFER DEFINES/VARS
185 #define MULTI_XFER_VERBOSE // keep this defined for verbose debug output
187 #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) )
190 #define MULTI_XFER_CODE_ACK 0 // simple response to the last request
191 #define MULTI_XFER_CODE_NAK 1 // simple response to the last request
192 #define MULTI_XFER_CODE_HEADER 2 // file xfer header information follows, requires a HEADER_RESPONSE
193 #define MULTI_XFER_CODE_DATA 3 // data block follows, requires an ack
194 #define MULTI_XFER_CODE_FINAL 4 // indication from sender that xfer is complete, requires an ack
197 #define MULTI_XFER_FLAG_USED (1<<0) // this entry is in use
198 #define MULTI_XFER_FLAG_SEND (1<<1) // this entry is sending a file
199 #define MULTI_XFER_FLAG_RECV (1<<2) // this entry is receiving a file
200 #define MULTI_XFER_FLAG_PENDING (1<<3) // this entry is ready to send a header and start the process
201 #define MULTI_XFER_FLAG_WAIT_ACK (1<<4) // waiting for an ack/nak
202 #define MULTI_XFER_FLAG_WAIT_DATA (1<<5) // waiting for another block of data
203 #define MULTI_XFER_FLAG_UNKNOWN (1<<6) // xfer final has been sent, and we are waiting for a response
204 #define MULTI_XFER_FLAG_SUCCESS (1<<7) // finished xfer
205 #define MULTI_XFER_FLAG_FAIL (1<<8) // xfer failed
206 #define MULTI_XFER_FLAG_TIMEOUT (1<<9) // xfer has timed-out
207 #define MULTI_XFER_FLAG_QUEUE_CURRENT (1<<10) // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
209 // packet size for file xfer
210 #define MULTI_XFER_MAX_DATA_SIZE 490 // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
212 // timeout for a given xfer operation
213 #define MULTI_XFER_TIMEOUT 10000
217 // temp filename header for xferring files
218 #define MULTI_XFER_FNAME_PREFIX "_fsx_"
222 // xfer entries/events
223 #define MAX_XFER_ENTRIES 60 // the max allowed file xfer entries
224 typedef struct xfer_entry {
225 int flags; // status flags for this entry
226 char filename[MAX_FILENAME_LEN+1]; // filename of the currently xferring file
227 char ex_filename[MAX_FILENAME_LEN+10]; // filename with xfer prefix tacked on to the front
228 CFILE *file; // file handle of the current xferring file
229 int file_size; // total size of the file being xferred
230 int file_ptr; // total bytes we're received so far
231 ushort file_chksum; // used for checking successfully xferred files
232 PSNET_SOCKET_RELIABLE file_socket; // socket used to xfer the file
233 int xfer_stamp; // timestamp for the current operation
234 int force_dir; // force the file to go to this directory on receive (will override Multi_xfer_force_dir)
235 ushort sig; // identifying sig - sender specifies this
237 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES]; // the file xfer entries themselves
239 // callback function pointer for when we start receiving a file
240 void (*Multi_xfer_recv_notify)(int handle);
242 // lock for the xfer system
243 int Multi_xfer_locked;
245 // force directory type for receives
246 int Multi_xfer_force_dir;
248 // unique file signature - this along with a socket # is enough to identify all xfers
249 ushort Multi_xfer_sig = 0;
252 // ------------------------------------------------------------------------------------------
253 // MULTI XFER FORWARD DECLARATIONS
256 // evaluate the condition of the entry
257 void multi_xfer_eval_entry(xfer_entry *xe);
259 // set an entry to be "failed"
260 void multi_xfer_fail_entry(xfer_entry *xe);
262 // get a valid xfer entry handle
263 int multi_xfer_get_free_handle();
265 // process an ack for this entry
266 void multi_xfer_process_ack(xfer_entry *xe);
268 // process a nak for this entry
269 void multi_xfer_process_nak(xfer_entry *xe);
271 // process a "final" packet
272 void multi_xfer_process_final(xfer_entry *xe);
274 // process a data packet
275 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
278 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);
280 // send the next block of outgoing data or a "final" packet if we're done
281 void multi_xfer_send_next(xfer_entry *xe);
283 // send an ack to the sender
284 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
286 // send a nak to the sender
287 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
289 // send a "final" packet
290 void multi_xfer_send_final(xfer_entry *xe);
292 // send the header to begin a file transfer
293 void multi_xfer_send_header(xfer_entry *xe);
295 // convert the filename into the prefixed ex_filename
296 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
298 // get a new xfer sig
299 ushort multi_xfer_get_sig();
301 // ------------------------------------------------------------------------------------------
302 // MULTI XFER FUNCTIONS
305 // initialize all file xfer transaction stuff, call in multi_level_init()
306 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
308 // blast all the entries
309 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
311 // assign the receive callback function pointer
312 Multi_xfer_recv_notify = multi_xfer_recv_callback;
315 Multi_xfer_locked = 0;
317 // no forced directory
318 Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;
321 // do frame for all file xfers, call in multi_do_frame()
326 // process all valid xfer entries
327 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
328 // if this one is actually in use and has not finished for one reason or another
329 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))){
330 // evaluate the condition of this entry (fail, timeout, etc)
331 multi_xfer_eval_entry(&Multi_xfer_entry[idx]);
336 // close down the file xfer system
337 void multi_xfer_close()
341 // go through all active entries and abort them
342 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
343 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
344 multi_xfer_abort(idx);
348 // now blast all the memory free
349 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
352 // reset the xfer system, including shutting down/killing all active xfers
353 void multi_xfer_reset()
357 // shut down all active xfers
358 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
359 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
360 multi_xfer_abort(idx);
364 // blast all the memory clean
365 memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
368 // send a file to the specified player, return a handle
369 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
371 xfer_entry temp_entry;
374 // if the system is locked, return -1
375 if(Multi_xfer_locked){
379 // attempt to get a free handle
380 handle = multi_xfer_get_free_handle();
385 // clear the temp entry
386 memset(&temp_entry,0,sizeof(xfer_entry));
389 strcpy(temp_entry.filename,filename);
391 // attempt to open the file
392 temp_entry.file = NULL;
393 temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
394 if(temp_entry.file == NULL){
395 #ifdef MULTI_XFER_VERBOSE
396 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
403 temp_entry.file_size = -1;
404 temp_entry.file_size = cfilelength(temp_entry.file);
405 if(temp_entry.file_size == -1){
406 #ifdef MULTI_XFER_VERBOSE
407 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
411 temp_entry.file_ptr = 0;
413 // get the file checksum
414 if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
415 #ifdef MULTI_XFER_VERBOSE
416 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
420 #ifdef MULTI_XFER_VERBOSE
421 nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
423 // rewind the file pointer to the beginning of the file
424 cfseek(temp_entry.file,0,CF_SEEK_SET);
427 temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
428 temp_entry.flags |= flags;
431 temp_entry.file_socket = who;
434 temp_entry.sig = multi_xfer_get_sig();
436 // copy to the global array
437 memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
438 memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
443 // get the status of the current file xfer
444 int multi_xfer_get_status(int handle)
446 // if this is an invalid or an unused handle, notify as such
447 if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
448 return MULTI_XFER_NONE;
451 // if the xfer has timed-out
452 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
453 return MULTI_XFER_TIMEDOUT;
456 // if the xfer has failed for one reason or another (not timeout)
457 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
458 return MULTI_XFER_FAIL;
461 // if the xfer has succeeded
462 if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
463 return MULTI_XFER_SUCCESS;
466 // if the xfer is queued
467 if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
468 return MULTI_XFER_QUEUED;
471 // otherwise the xfer is still in progress
472 return MULTI_XFER_IN_PROGRESS;
475 // abort a transferring file
476 void multi_xfer_abort(int handle)
480 // don't do anything if this is an invalid handle
481 if(MULTI_XFER_INVALID_HANDLE(handle)){
485 // get e handle to the entry
486 xe = &Multi_xfer_entry[handle];
488 // close any open file and delete it
489 if(xe->file != NULL){
493 // delete it if there isn't some problem with the filename
494 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
495 cf_delete(xe->ex_filename, xe->force_dir);
500 xe->file_socket = INVALID_SOCKET;
503 memset(xe,0,sizeof(xfer_entry));
506 // release an xfer handle
507 void multi_xfer_release_handle(int handle)
511 // don't do anything if this is an invalid handle
512 if(MULTI_XFER_INVALID_HANDLE(handle)){
516 // get e handle to the entry
517 xe = &Multi_xfer_entry[handle];
519 // close any open file and delete it
520 if(xe->file != NULL){
524 // delete it if the file was not successfully received
525 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
526 cf_delete(xe->ex_filename,xe->force_dir);
531 xe->file_socket = INVALID_SOCKET;
534 memset(xe,0,sizeof(xfer_entry));
537 // get the filename of the xfer for the given handle
538 char *multi_xfer_get_filename(int handle)
540 // if this is an invalid handle, return NULL
541 if(MULTI_XFER_INVALID_HANDLE(handle)){
545 // otherwise return the string
546 return Multi_xfer_entry[handle].filename;
549 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
550 void multi_xfer_lock()
552 Multi_xfer_locked = 1;
555 // unlock the xfer system
556 void multi_xfer_unlock()
558 Multi_xfer_locked = 0;
561 // force all receives to go into the specified directory by cfile type
562 void multi_xfer_force_dir(int cf_type)
564 Multi_xfer_force_dir = cf_type;
565 Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
568 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
569 void multi_xfer_handle_force_dir(int handle,int cf_type)
571 // if this is an invalid handle, return NULL
572 if(MULTI_XFER_INVALID_HANDLE(handle)){
576 // force to go to the given directory
577 Multi_xfer_entry[handle].force_dir = cf_type;
578 Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
581 // or the flag on a given entry
582 void multi_xfer_xor_flags(int handle,int flags)
584 // if this is an invalid handle, return NULL
585 if(MULTI_XFER_INVALID_HANDLE(handle)){
590 Multi_xfer_entry[handle].flags ^= flags;
593 // get the flags for a given entry
594 int multi_xfer_get_flags(int handle)
596 // if this is an invalid handle, return NULL
597 if(MULTI_XFER_INVALID_HANDLE(handle)){
602 return Multi_xfer_entry[handle].flags;
605 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
606 int multi_xfer_lookup(char *filename)
610 // if we have an invalid filename, do nothing
611 if((filename == NULL) || (strlen(filename) <= 0)){
615 // otherwise, perform a lookup
616 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
617 // if we found a matching filename
618 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
623 // did not find a match
627 // get the % of completion of the passed file handle, return < 0 if invalid
628 float multi_xfer_pct_complete(int handle)
630 // if this is an invalid handle, return invalid
631 if(MULTI_XFER_INVALID_HANDLE(handle)){
635 // if the file size is 0, return invalid
636 if(Multi_xfer_entry[handle].file_size == 0){
640 // return the pct completion
641 return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
644 // get the socket of the file xfer (useful for identifying players)
645 uint multi_xfer_get_sock(int handle)
647 // if this is an invalid handle, return NULL
648 if(MULTI_XFER_INVALID_HANDLE(handle)){
649 return INVALID_SOCKET;
652 return Multi_xfer_entry[handle].file_socket;
655 // get the CF_TYPE of the directory this file is going to
656 int multi_xfer_get_force_dir(int handle)
658 // if this is an invalid handle, return NULL
659 if(MULTI_XFER_INVALID_HANDLE(handle)){
660 return INVALID_SOCKET;
663 return Multi_xfer_entry[handle].force_dir;
667 // ------------------------------------------------------------------------------------------
668 // MULTI XFER FORWARD DECLARATIONS
671 // evaluate the condition of the entry
672 void multi_xfer_eval_entry(xfer_entry *xe)
678 // if the entry is marked as successful, then don't do anything
679 if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
683 // if the entry is queued
684 if(xe->flags & MULTI_XFER_FLAG_QUEUE){
685 // if the entry is not current
686 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
687 // see if there are any other queued up xfers to this target. if not, make me current and start sending
689 for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
690 xe_c = &Multi_xfer_entry[idx];
692 // if this is a valid entry and is a queued entry and is going to the same target
693 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) &&
694 (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
701 // if we found no other entries, make this guy current and pending
703 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
704 xe->flags |= MULTI_XFER_FLAG_PENDING;
706 #ifdef MULTI_XFER_VERBOSE
707 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
710 // otherwise, do nothing for him - he has to still wait
717 // if the entry is marked as pending - send out the header to get the ball rolling
718 if(xe->flags & MULTI_XFER_FLAG_PENDING){
719 // send the header to begin the transfer
720 multi_xfer_send_header(xe);
723 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
725 // unset the pending flag
726 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
728 // set the ack/wait flag
729 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
732 // see if the entry has timed-out for one reason or another
733 if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
734 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;
736 // if we should be auto-destroying this entry, do so
737 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
738 multi_xfer_fail_entry(xe);
743 // lookup a file xfer entry by player
744 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
748 // look through all valid xfer entries
749 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
750 // if we're looking for sending entries
751 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
754 // if we're looking for recv entries
755 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
759 // if we found a match
760 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
761 return &Multi_xfer_entry[idx];
768 // set an entry to be "failed"
769 void multi_xfer_fail_entry(xfer_entry *xe)
771 // set its flags appropriately
772 xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
773 xe->flags |= MULTI_XFER_FLAG_FAIL;
775 // close the file pointer
776 if(xe->file != NULL){
782 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
783 cf_delete(xe->ex_filename,xe->force_dir);
786 // null the timestamp
789 // if we should be auto-destroying this entry, do so
790 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
791 multi_xfer_release_handle(xe - Multi_xfer_entry);
794 // blast the memory clean
795 memset(xe,0,sizeof(xfer_entry));
798 // get a valid xfer entry handle
799 int multi_xfer_get_free_handle()
803 // look for a free entry
804 for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
805 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
814 // ------------------------------------------------------------------------------------------
815 // MULTI XFER PACKET HANDLERS
818 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
819 // packet regardless of error conditions
820 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
825 ushort data_size = 0;
827 ushort file_checksum = 0;
829 ubyte xfer_data[600];
833 // read in all packet data
838 case MULTI_XFER_CODE_DATA:
840 memcpy(xfer_data, data + offset, data_size);
846 case MULTI_XFER_CODE_HEADER:
847 GET_STRING(filename);
849 GET_DATA(file_checksum);
854 case MULTI_XFER_CODE_ACK:
855 case MULTI_XFER_CODE_NAK:
859 case MULTI_XFER_CODE_FINAL:
867 // at this point we've read all the data in the packet
869 // at this point, we should process code-specific data
871 if(val != MULTI_XFER_CODE_HEADER){
872 // if the code is not a request or a header, we need to look up the existing xfer_entry
875 xe = multi_xfer_find_entry(who, sig, sender_side);
877 #ifdef MULTI_XFER_VERBOSE
878 nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
880 // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
882 int np_index = find_player_socket(who);
883 ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
884 ml_printf(": sig == %d", sig);
885 ml_printf(": xfer header == %d", val);
887 ml_string(": player == unknown");
889 ml_printf(": player == %s", Net_players[np_index].player->callsign);
892 ml_string(": sending");
894 ml_string(": receiving");
904 // process an ack for this entry
905 case MULTI_XFER_CODE_ACK :
907 multi_xfer_process_ack(xe);
910 // process a nak for this entry
911 case MULTI_XFER_CODE_NAK :
913 multi_xfer_process_nak(xe);
916 // process a "final" packet
917 case MULTI_XFER_CODE_FINAL :
919 multi_xfer_process_final(xe);
922 // process a data packet
923 case MULTI_XFER_CODE_DATA :
925 multi_xfer_process_data(xe, xfer_data, data_size);
929 case MULTI_XFER_CODE_HEADER :
930 // send on my reliable socket
931 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
937 // process an ack for this entry
938 void multi_xfer_process_ack(xfer_entry *xe)
940 // if we are a sender
941 if(xe->flags & MULTI_XFER_FLAG_SEND){
942 // if we are waiting on a final ack, then the transfer has completed successfully
943 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
944 xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
945 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
947 #ifdef MULTI_XFER_VERBOSE
948 nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
951 // if we should be auto-destroying this entry, do so
952 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
953 multi_xfer_release_handle(xe - Multi_xfer_entry);
956 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
957 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
958 multi_xfer_send_next(xe);
963 // process a nak for this entry
964 void multi_xfer_process_nak(xfer_entry *xe)
966 // if we get an ack at any time we should simply set the xfer to failed
967 multi_xfer_fail_entry(xe);
970 // process a "final" packet
971 void multi_xfer_process_final(xfer_entry *xe)
975 // make sure we skip a line
976 nprintf(("Network","\n"));
979 if(xe->file != NULL){
985 // check to make sure the file checksum is the same
987 if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
989 xe->flags |= MULTI_XFER_FLAG_FAIL;
991 #ifdef MULTI_XFER_VERBOSE
992 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
996 multi_xfer_abort(xe - Multi_xfer_entry);
999 // checksums check out, so rename the file and be done with it
1001 #ifdef MULTI_XFER_VERBOSE
1002 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));
1004 // rename the file properly
1005 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1006 // mark the xfer as being successful
1007 xe->flags |= MULTI_XFER_FLAG_SUCCESS;
1009 nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));
1011 // send an ack to the sender
1012 multi_xfer_send_ack(xe->file_socket, xe->sig);
1014 // mark it as failing
1015 xe->flags |= MULTI_XFER_FLAG_FAIL;
1016 nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1018 // delete the tempfile
1019 cf_delete(xe->ex_filename, xe->force_dir);
1021 // send an nak to the sender
1022 multi_xfer_send_nak(xe->file_socket, xe->sig);
1025 // if we should be auto-destroying this entry, do so
1026 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1027 multi_xfer_release_handle(xe - Multi_xfer_entry);
1032 // process a data packet
1033 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)
1035 // print out a crude progress indicator
1036 nprintf(("Network","."));
1038 // attempt to write the rest of the data string to the file
1039 if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1040 // inform the sender we had a problem
1041 multi_xfer_send_nak(xe->file_socket, xe->sig);
1044 multi_xfer_fail_entry(xe);
1046 xe->file_ptr += data_size;
1050 // increment the file pointer
1051 xe->file_ptr += data_size;
1053 // send an ack to the sender
1054 multi_xfer_send_ack(xe->file_socket, xe->sig);
1057 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1060 // process a header, return bytes processed
1061 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1066 // if the xfer system is locked, send a nak
1067 if(Multi_xfer_locked){
1068 multi_xfer_send_nak(who, sig);
1072 // try and get a free xfer handle
1073 handle = multi_xfer_get_free_handle();
1075 multi_xfer_send_nak(who, sig);
1078 xe = &Multi_xfer_entry[handle];
1079 memset(xe,0,sizeof(xfer_entry));
1082 // set the recv and used flags
1083 xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1085 // get the header data
1086 xe->file_size = file_size;
1088 // get the file chksum
1089 xe->file_chksum = file_checksum;
1092 xe->file_socket = who;
1094 // set the timeout timestamp
1095 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1100 // copy the filename and get the prefixed xfer filename
1101 strcpy(xe->filename, filename);
1102 multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1103 #ifdef MULTI_XFER_VERBOSE
1104 nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1107 // determine what directory to place the file in
1108 // individual xfer entries take precedence over the global multi xfer force entry
1109 xe->force_dir = Multi_xfer_force_dir;
1111 // call the callback function
1112 Multi_xfer_recv_notify(handle);
1114 // if the notify function invalidated this xfer handle, then cancel the whole thing
1115 if(xe->flags & MULTI_XFER_FLAG_REJECT){
1116 multi_xfer_send_nak(who, sig);
1119 memset(xe, 0, sizeof(xfer_entry));
1123 // delete the old file (if it exists)
1124 cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1125 cf_delete( xe->filename, CF_TYPE_MISSIONS );
1127 // attempt to open the file (using the prefixed filename)
1129 xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1130 if(xe->file == NULL){
1131 multi_xfer_send_nak(who, sig);
1134 memset(xe, 0, sizeof(xfer_entry));
1138 // set the waiting for data flag
1139 xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;
1141 // send an ack to the server
1142 multi_xfer_send_ack(who, sig);
1144 #ifdef MULTI_XFER_VERBOSE
1145 nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1149 // send the next block of outgoing data or a "final" packet if we're done
1150 void multi_xfer_send_next(xfer_entry *xe)
1152 ubyte data[MAX_PACKET_SIZE],code;
1155 int packet_size = 0;
1157 // print out a crude progress indicator
1158 nprintf(("Network", "+"));
1160 // if we've sent all the data, then we should send a "final" packet
1161 if(xe->file_ptr >= xe->file_size){
1162 // mark the entry as unknown
1163 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1166 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1169 multi_xfer_send_final(xe);
1175 BUILD_HEADER(XFER_PACKET);
1177 // length of the added string
1178 flen = strlen(xe->filename) + 4;
1180 // determine how much data we are going to send with this packet and add it in
1181 if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1182 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1184 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1186 // increment the file pointer
1187 xe->file_ptr += data_size;
1190 code = MULTI_XFER_CODE_DATA;
1196 // add in the size of the rest of the packet
1197 ADD_DATA(data_size);
1200 if(cfread(data+packet_size,1,(int)data_size,xe->file) == NULL){
1201 // send a nack to the receiver
1202 multi_xfer_send_nak(xe->file_socket, xe->sig);
1205 multi_xfer_fail_entry(xe);
1209 // increment the packet size
1210 packet_size += (int)data_size;
1213 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1215 // otherwise send the data
1216 psnet_rel_send(xe->file_socket, data, packet_size);
1219 // send an ack to the sender
1220 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1222 ubyte data[MAX_PACKET_SIZE],code;
1223 int packet_size = 0;
1225 // build the header and add
1226 BUILD_HEADER(XFER_PACKET);
1229 code = MULTI_XFER_CODE_ACK;
1236 psnet_rel_send(socket, data, packet_size);
1239 // send a nak to the sender
1240 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1242 ubyte data[MAX_PACKET_SIZE],code;
1243 int packet_size = 0;
1245 // build the header and add the code
1246 BUILD_HEADER(XFER_PACKET);
1249 code = MULTI_XFER_CODE_NAK;
1256 psnet_rel_send(socket, data, packet_size);
1259 // send a "final" packet
1260 void multi_xfer_send_final(xfer_entry *xe)
1262 ubyte data[MAX_PACKET_SIZE],code;
1263 int packet_size = 0;
1266 BUILD_HEADER(XFER_PACKET);
1269 code = MULTI_XFER_CODE_FINAL;
1276 psnet_rel_send(xe->file_socket, data, packet_size);
1279 // send the header to begin a file transfer
1280 void multi_xfer_send_header(xfer_entry *xe)
1282 ubyte data[MAX_PACKET_SIZE],code;
1283 int packet_size = 0;
1285 // build the header and add the opcode
1286 BUILD_HEADER(XFER_PACKET);
1287 code = MULTI_XFER_CODE_HEADER;
1294 ADD_STRING(xe->filename);
1297 ADD_DATA(xe->file_size);
1299 // add the file checksum
1300 ADD_DATA(xe->file_chksum);
1303 psnet_rel_send(xe->file_socket, data, packet_size);
1306 // convert the filename into the prefixed ex_filename
1307 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1309 char temp[MAX_FILENAME_LEN+50];
1311 // blast the memory clean
1312 memset(temp, 0, MAX_FILENAME_LEN+50);
1314 // copy in the prefix
1315 strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1317 // stick on the original name
1318 strcat(temp, filename);
1320 // copy the whole thing to the outgoing filename
1321 strcpy(ex_filename, temp);
1324 // get a new xfer sig
1325 ushort multi_xfer_get_sig()
1327 ushort ret = Multi_xfer_sig;
1330 if(Multi_xfer_sig == 0xffff){