]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_xfer.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / network / multi_xfer.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/multi_xfer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * $Log$
8  * Revision 1.2  2002/05/07 03:16:47  theoddone33
9  * The Great Newline Fix
10  *
11  * Revision 1.1.1.1  2002/05/03 03:28:10  root
12  * Initial import.
13  *
14  * 
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.
18  * 
19  * 10    3/09/99 6:24p Dave
20  * More work on object update revamping. Identified several sources of
21  * unnecessary bandwidth.
22  * 
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.
26  * 
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.
30  * 
31  * 7     12/14/98 4:01p Dave
32  * Got multi_data stuff working well with new xfer stuff. 
33  * 
34  * 6     12/14/98 12:13p Dave
35  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
36  * Need to test now.
37  * 
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.
41  * 
42  * 4     11/17/98 11:12a Dave
43  * Removed player identification by address. Now assign explicit id #'s.
44  * 
45  * 3     11/05/98 5:55p Dave
46  * Big pass at reducing #includes
47  * 
48  * 2     10/07/98 10:53a Dave
49  * Initial checkin.
50  * 
51  * 1     10/07/98 10:50a Dave
52  * 
53  * 56    9/11/98 9:31a Dave
54  * Fixed file xfer bug regarding checksums.
55  * 
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.
59  * 
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."
63  * 
64  * 53    6/13/98 6:01p Hoffoss
65  * Externalized all new (or forgot to be added) strings to all the code.
66  * 
67  * 52    6/13/98 3:19p Hoffoss
68  * NOX()ed out a bunch of strings that shouldn't be translated.
69  * 
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.
74  * 
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.
78  * 
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.
83  * 
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
87  * midgame.
88  * 
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. 
93  * 
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. 
98  * 
99  * 45    4/08/98 2:51p Dave
100  * Fixed pilot image xfer once again. Solidify team selection process in
101  * pre-briefing multiplayer.
102  * 
103  * 44    4/04/98 4:22p Dave
104  * First rev of UDP reliable sockets is done. Seems to work well if not
105  * overly burdened.
106  * 
107  * 43    4/03/98 1:03a Dave
108  * First pass at unreliable guaranteed delivery packets.
109  * 
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.
114  * 
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.
119  * 
120  * 40    3/30/98 8:46a Allender
121  * fix an optimized build compiler warning
122  * 
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.
127  * 
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
131  * keys.
132  * 
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.
137  * 
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.
142  * 
143  * 35    3/21/98 7:14p Dave
144  * Fixed up standalone player slot switching. Made training missions not
145  * count towards player stats.
146  * 
147  * 34    3/16/98 11:52p Allender
148  * Put in timestamp updates when processing data on both sender and
149  * receiver sides.
150  * 
151  * 33    2/22/98 2:53p Dave
152  * Put in groundwork for advanced multiplayer campaign  options.
153  * 
154  * 32    2/20/98 4:43p Dave
155  * Finished support for multiplayer player data files. Split off
156  * multiplayer campaign functionality.
157  * 
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.
161  * 
162  * 30    2/18/98 10:21p Dave
163  * Ripped out old file xfer system. Put in brand new xfer system.
164  * 
165  * $NoKeywords: $
166  */
167
168 #include <winsock.h>
169 #include <io.h>
170 #include "multi_xfer.h"
171 #include "cfile.h"
172 #include "multimsgs.h"
173 #include "psnet.h"
174 #include "popup.h"
175 #include "multi_endgame.h"
176 #include "timer.h"
177 #include "multi.h"
178 #include "multiutil.h"
179 #include "multi_log.h"
180
181 // ------------------------------------------------------------------------------------------
182 // MULTI XFER DEFINES/VARS
183 //
184
185 #define MULTI_XFER_VERBOSE                                                                              // keep this defined for verbose debug output
186
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) )
188
189 // packet codes
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
195
196 // entry flags
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
208
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
211
212 // timeout for a given xfer operation
213 #define MULTI_XFER_TIMEOUT                                              10000           
214
215 //XSTR:OFF
216
217 // temp filename header for xferring files
218 #define MULTI_XFER_FNAME_PREFIX                         "_fsx_"
219
220 //XSTR:ON
221
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
236 } xfer_entry;
237 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES];                  // the file xfer entries themselves
238
239 // callback function pointer for when we start receiving a file
240 void (*Multi_xfer_recv_notify)(int handle);
241
242 // lock for the xfer system
243 int Multi_xfer_locked;
244
245 // force directory type for receives
246 int Multi_xfer_force_dir; 
247
248 // unique file signature - this along with a socket # is enough to identify all xfers
249 ushort Multi_xfer_sig = 0;
250
251
252 // ------------------------------------------------------------------------------------------
253 // MULTI XFER FORWARD DECLARATIONS
254 //
255
256 // evaluate the condition of the entry
257 void multi_xfer_eval_entry(xfer_entry *xe);
258
259 // set an entry to be "failed"
260 void multi_xfer_fail_entry(xfer_entry *xe);
261
262 // get a valid xfer entry handle
263 int multi_xfer_get_free_handle();
264
265 // process an ack for this entry
266 void multi_xfer_process_ack(xfer_entry *xe);
267
268 // process a nak for this entry
269 void multi_xfer_process_nak(xfer_entry *xe);
270                 
271 // process a "final" packet     
272 void multi_xfer_process_final(xfer_entry *xe);          
273
274 // process a data packet
275 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
276         
277 // process a header
278 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);                
279
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);
282
283 // send an ack to the sender
284 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
285
286 // send a nak to the sender
287 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
288
289 // send a "final" packet
290 void multi_xfer_send_final(xfer_entry *xe);
291
292 // send the header to begin a file transfer
293 void multi_xfer_send_header(xfer_entry *xe);
294
295 // convert the filename into the prefixed ex_filename
296 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
297
298 // get a new xfer sig
299 ushort multi_xfer_get_sig();
300
301 // ------------------------------------------------------------------------------------------
302 // MULTI XFER FUNCTIONS
303 //
304
305 // initialize all file xfer transaction stuff, call in multi_level_init()
306 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
307 {
308         // blast all the entries
309         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
310
311         // assign the receive callback function pointer
312         Multi_xfer_recv_notify = multi_xfer_recv_callback;
313
314         // unlocked
315         Multi_xfer_locked = 0;
316
317         // no forced directory
318         Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;     
319 }
320
321 // do frame for all file xfers, call in multi_do_frame()
322 void multi_xfer_do()
323 {
324         int idx;
325
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]);                  
332                 }
333         }
334 }
335
336 // close down the file xfer system
337 void multi_xfer_close()
338 {
339         int idx;
340
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);
345                 }
346         }
347
348         // now blast all the memory free
349         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
350 }
351
352 // reset the xfer system, including shutting down/killing all active xfers
353 void multi_xfer_reset()
354 {
355         int idx;
356
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);
361                 }
362         }
363
364         // blast all the memory clean
365         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
366 }
367
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)
370 {
371         xfer_entry temp_entry;  
372         int handle;
373
374         // if the system is locked, return -1
375         if(Multi_xfer_locked){
376                 return -1;
377         }
378
379         // attempt to get a free handle
380         handle = multi_xfer_get_free_handle();
381         if(handle == -1){
382                 return -1;
383         }
384
385         // clear the temp entry
386         memset(&temp_entry,0,sizeof(xfer_entry));
387
388         // set the filename
389         strcpy(temp_entry.filename,filename);   
390
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));
397 #endif
398
399                 return -1;
400         }
401
402         // set the file size
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));
408 #endif
409                 return -1;
410         }
411         temp_entry.file_ptr = 0;
412
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));
417 #endif
418                 return -1;
419         } 
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));
422 #endif
423         // rewind the file pointer to the beginning of the file
424         cfseek(temp_entry.file,0,CF_SEEK_SET);
425
426         // set the flags
427         temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
428         temp_entry.flags |= flags;
429
430         // set the socket       
431         temp_entry.file_socket = who;           
432
433         // set the signature
434         temp_entry.sig = multi_xfer_get_sig();
435
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));
439         
440         return handle;
441 }
442
443 // get the status of the current file xfer
444 int multi_xfer_get_status(int handle)
445 {
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;
449         }       
450         
451         // if the xfer has timed-out
452         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
453                 return MULTI_XFER_TIMEDOUT;
454         }
455
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;
459         }
460
461         // if the xfer has succeeded
462         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
463                 return MULTI_XFER_SUCCESS;
464         }
465
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;
469         }
470
471         // otherwise the xfer is still in progress
472         return MULTI_XFER_IN_PROGRESS;
473 }
474
475 // abort a transferring file
476 void multi_xfer_abort(int handle)
477 {
478         xfer_entry *xe;
479
480         // don't do anything if this is an invalid handle
481         if(MULTI_XFER_INVALID_HANDLE(handle)){
482                 return;
483         }
484
485         // get e handle to the entry
486         xe = &Multi_xfer_entry[handle];
487
488         // close any open file and delete it 
489         if(xe->file != NULL){
490                 cfclose(xe->file);
491                 xe->file = NULL;
492
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);
496                 }
497         }
498
499         // zero the socket
500         xe->file_socket = INVALID_SOCKET;
501
502         // blast the entry
503         memset(xe,0,sizeof(xfer_entry));
504 }
505
506 // release an xfer handle
507 void multi_xfer_release_handle(int handle)
508 {
509         xfer_entry *xe;
510
511         // don't do anything if this is an invalid handle
512         if(MULTI_XFER_INVALID_HANDLE(handle)){
513                 return;
514         }
515
516         // get e handle to the entry
517         xe = &Multi_xfer_entry[handle];
518
519         // close any open file and delete it 
520         if(xe->file != NULL){
521                 cfclose(xe->file);
522                 xe->file = NULL;
523
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);
527                 }
528         }
529
530         // zero the socket
531         xe->file_socket = INVALID_SOCKET;       
532
533         // blast the entry
534         memset(xe,0,sizeof(xfer_entry));
535 }
536
537 // get the filename of the xfer for the given handle
538 char *multi_xfer_get_filename(int handle)
539 {
540         // if this is an invalid handle, return NULL
541         if(MULTI_XFER_INVALID_HANDLE(handle)){
542                 return NULL;
543         }
544
545         // otherwise return the string
546         return Multi_xfer_entry[handle].filename;
547 }
548
549 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
550 void multi_xfer_lock()
551 {
552         Multi_xfer_locked = 1;
553 }
554
555 // unlock the xfer system
556 void multi_xfer_unlock()
557 {
558         Multi_xfer_locked = 0;
559 }
560
561 // force all receives to go into the specified directory by cfile type
562 void multi_xfer_force_dir(int cf_type)
563 {
564         Multi_xfer_force_dir = cf_type;
565         Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
566 }
567
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)
570 {
571         // if this is an invalid handle, return NULL
572         if(MULTI_XFER_INVALID_HANDLE(handle)){
573                 return;
574         }
575
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);
579 }
580
581 // or the flag on a given entry
582 void multi_xfer_xor_flags(int handle,int flags)
583 {
584         // if this is an invalid handle, return NULL
585         if(MULTI_XFER_INVALID_HANDLE(handle)){
586                 return;
587         }
588
589         // xor the flags
590         Multi_xfer_entry[handle].flags ^= flags;
591 }
592
593 // get the flags for a given entry
594 int multi_xfer_get_flags(int handle)
595 {
596         // if this is an invalid handle, return NULL
597         if(MULTI_XFER_INVALID_HANDLE(handle)){
598                 return -1;
599         }
600
601         // return the flags
602         return Multi_xfer_entry[handle].flags;
603 }
604
605 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
606 int multi_xfer_lookup(char *filename)
607 {
608         int idx;
609
610         // if we have an invalid filename, do nothing
611         if((filename == NULL) || (strlen(filename) <= 0)){
612                 return 0;
613         }
614
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)){
619                         return idx;
620                 }
621         }
622
623         // did not find a match
624         return -1;
625 }
626
627 // get the % of completion of the passed file handle, return < 0 if invalid
628 float multi_xfer_pct_complete(int handle)
629 {
630         // if this is an invalid handle, return invalid
631         if(MULTI_XFER_INVALID_HANDLE(handle)){
632                 return -1.0f;
633         }
634
635         // if the file size is 0, return invalid
636         if(Multi_xfer_entry[handle].file_size == 0){
637                 return -1.0f;
638         }
639
640         // return the pct completion
641         return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
642 }
643
644 // get the socket of the file xfer (useful for identifying players)
645 uint multi_xfer_get_sock(int handle)
646 {
647         // if this is an invalid handle, return NULL
648         if(MULTI_XFER_INVALID_HANDLE(handle)){
649                 return INVALID_SOCKET;
650         }
651
652         return Multi_xfer_entry[handle].file_socket;
653 }
654
655 // get the CF_TYPE of the directory this file is going to
656 int multi_xfer_get_force_dir(int handle)
657 {
658         // if this is an invalid handle, return NULL
659         if(MULTI_XFER_INVALID_HANDLE(handle)){
660                 return INVALID_SOCKET;
661         }
662
663         return Multi_xfer_entry[handle].force_dir;
664 }
665
666
667 // ------------------------------------------------------------------------------------------
668 // MULTI XFER FORWARD DECLARATIONS
669 //
670
671 // evaluate the condition of the entry
672 void multi_xfer_eval_entry(xfer_entry *xe)
673 {       
674         int idx;
675         int found;
676         xfer_entry *xe_c;
677
678         // if the entry is marked as successful, then don't do anything
679         if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
680                 return;
681         }
682
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
688                         found = 0;
689                         for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
690                                 xe_c = &Multi_xfer_entry[idx];
691
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)){
695                                         
696                                         found = 1;
697                                         break;
698                                 }                               
699                         }
700
701                         // if we found no other entries, make this guy current and pending
702                         if(!found){
703                                 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
704                                 xe->flags |= MULTI_XFER_FLAG_PENDING;
705
706 #ifdef MULTI_XFER_VERBOSE
707                                 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
708 #endif
709                         } 
710                         // otherwise, do nothing for him - he has to still wait
711                         else {
712                                 return;
713                         }
714                 }
715         }
716
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);
721
722                 // set the timestamp
723                 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); 
724
725                 // unset the pending flag
726                 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
727
728                 // set the ack/wait flag
729                 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
730         }
731         
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;                   
735
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);                      
739                 }
740         }               
741 }
742
743 // lookup a file xfer entry by player
744 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
745 {
746         int idx;
747
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)){
752                         continue;
753                 }
754                 // if we're looking for recv entries
755                 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
756                         continue;
757                 }
758
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];
762                 }
763         }
764
765         return NULL;
766 }
767
768 // set an entry to be "failed"
769 void multi_xfer_fail_entry(xfer_entry *xe)
770 {
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;
774
775         // close the file pointer
776         if(xe->file != NULL){
777                 cfclose(xe->file);
778                 xe->file = NULL;
779         }
780
781         // delete the file
782         if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
783                 cf_delete(xe->ex_filename,xe->force_dir);
784         }
785                 
786         // null the timestamp
787         xe->xfer_stamp = -1;
788
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);
792         }
793
794         // blast the memory clean
795         memset(xe,0,sizeof(xfer_entry));
796 }
797
798 // get a valid xfer entry handle
799 int multi_xfer_get_free_handle()
800 {
801         int idx;
802
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)){
806                         return idx;
807                 }
808         }
809
810         return -1;
811 }
812
813
814 // ------------------------------------------------------------------------------------------
815 // MULTI XFER PACKET HANDLERS
816 //
817
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)
821 {       
822         ubyte val;      
823         xfer_entry *xe;
824         char filename[255];
825         ushort data_size = 0;
826         int file_size = -1;
827         ushort file_checksum = 0;
828         int offset = 0;
829         ubyte xfer_data[600];
830         ushort sig;
831         int sender_side = 1;
832
833         // read in all packet data
834         GET_DATA(val);  
835         GET_DATA(sig);
836         switch(val){
837         // RECV side
838         case MULTI_XFER_CODE_DATA:                              
839                 GET_DATA(data_size);            
840                 memcpy(xfer_data, data + offset, data_size);
841                 offset += data_size;
842                 sender_side = 0;
843                 break;
844
845         // RECV side
846         case MULTI_XFER_CODE_HEADER:            
847                 GET_STRING(filename);
848                 GET_DATA(file_size);                                    
849                 GET_DATA(file_checksum);
850                 sender_side = 0;
851                 break;
852
853         // SEND side
854         case MULTI_XFER_CODE_ACK:
855         case MULTI_XFER_CODE_NAK:
856                 break;
857
858         // RECV side
859         case MULTI_XFER_CODE_FINAL:
860                 sender_side = 0;
861                 break;
862
863         default:
864                 Int3();
865         }                       
866
867         // at this point we've read all the data in the packet
868
869         // at this point, we should process code-specific data  
870         xe = NULL;
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
873                 xe = NULL;
874                         
875                 xe = multi_xfer_find_entry(who, sig, sender_side);
876                 if(xe == NULL){                                         
877 #ifdef MULTI_XFER_VERBOSE
878                         nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
879
880                         // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
881 #ifndef NDEBUG
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);
886                         if(np_index < 0){
887                                 ml_string(": player == unknown");
888                         } else {
889                                 ml_printf(": player == %s", Net_players[np_index].player->callsign);
890                         }
891                         if(sender_side){
892                                 ml_string(": sending");
893                         } else {
894                                 ml_string(": receiving");
895                         }
896 #endif
897 #endif
898                 //      Int3();
899                         return offset;
900                 }
901         }
902
903         switch((int)val){
904         // process an ack for this entry
905         case MULTI_XFER_CODE_ACK :
906                 Assert(xe != NULL);
907                 multi_xfer_process_ack(xe);
908                 break;
909         
910         // process a nak for this entry
911         case MULTI_XFER_CODE_NAK :
912                 Assert(xe != NULL);
913                 multi_xfer_process_nak(xe);
914                 break;
915
916         // process a "final" packet
917         case MULTI_XFER_CODE_FINAL :
918                 Assert(xe != NULL);
919                 multi_xfer_process_final(xe);
920                 break;
921
922         // process a data packet
923         case MULTI_XFER_CODE_DATA :
924                 Assert(xe != NULL);
925                 multi_xfer_process_data(xe, xfer_data, data_size);
926                 break;
927         
928         // process a header
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);
932                 break;
933         }               
934         return offset;
935 }
936
937 // process an ack for this entry
938 void multi_xfer_process_ack(xfer_entry *xe)
939 {                       
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;
946
947 #ifdef MULTI_XFER_VERBOSE
948                         nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
949 #endif
950
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);
954                         }
955                 } 
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);
959                 }
960         }
961 }
962
963 // process a nak for this entry
964 void multi_xfer_process_nak(xfer_entry *xe)
965 {               
966         // if we get an ack at any time we should simply set the xfer to failed
967         multi_xfer_fail_entry(xe);
968 }
969                 
970 // process a "final" packet     
971 void multi_xfer_process_final(xfer_entry *xe)
972 {       
973         ushort chksum;
974
975         // make sure we skip a line
976         nprintf(("Network","\n"));
977         
978         // close the file
979         if(xe->file != NULL){
980                 cflush(xe->file);
981                 cfclose(xe->file);
982                 xe->file = NULL;
983         }       
984
985         // check to make sure the file checksum is the same
986         chksum = 0;
987         if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
988                 // mark as failed
989                 xe->flags |= MULTI_XFER_FLAG_FAIL;
990
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));
993 #endif
994
995                 // abort the xfer
996                 multi_xfer_abort(xe - Multi_xfer_entry);
997                 return;
998         }
999         // checksums check out, so rename the file and be done with it
1000         else {
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));
1003 #endif
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;   
1008
1009                         nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));         
1010
1011                         // send an ack to the sender
1012                         multi_xfer_send_ack(xe->file_socket, xe->sig);
1013                 } else {
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));
1017
1018                         // delete the tempfile
1019                         cf_delete(xe->ex_filename, xe->force_dir);
1020
1021                         // send an nak to the sender
1022                         multi_xfer_send_nak(xe->file_socket, xe->sig);
1023                 }
1024
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);
1028                 }
1029         }
1030 }
1031
1032 // process a data packet
1033 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)        
1034 {                       
1035         // print out a crude progress indicator
1036         nprintf(("Network","."));               
1037
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);
1042
1043                 // fail this entry
1044                 multi_xfer_fail_entry(xe);
1045
1046                 xe->file_ptr += data_size;              
1047                 return;
1048         }
1049
1050         // increment the file pointer
1051         xe->file_ptr += data_size;
1052
1053         // send an ack to the sender
1054         multi_xfer_send_ack(xe->file_socket, xe->sig);
1055
1056         // set the timestmp
1057         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); 
1058 }
1059         
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)
1062 {               
1063         xfer_entry *xe;         
1064         int handle;     
1065
1066         // if the xfer system is locked, send a nak
1067         if(Multi_xfer_locked){          
1068                 multi_xfer_send_nak(who, sig);
1069                 return;
1070         }
1071
1072         // try and get a free xfer handle
1073         handle = multi_xfer_get_free_handle();
1074         if(handle == -1){               
1075                 multi_xfer_send_nak(who, sig);
1076                 return;
1077         } else {
1078                 xe = &Multi_xfer_entry[handle];
1079                 memset(xe,0,sizeof(xfer_entry));
1080         }               
1081
1082         // set the recv and used flags
1083         xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1084
1085         // get the header data  
1086         xe->file_size = file_size;
1087
1088         // get the file chksum
1089         xe->file_chksum = file_checksum;        
1090
1091         // set the socket
1092         xe->file_socket = who;  
1093
1094         // set the timeout timestamp
1095         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1096
1097         // set the sig
1098         xe->sig = sig;
1099
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));
1105 #endif
1106
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;   
1110
1111         // call the callback function
1112         Multi_xfer_recv_notify(handle); 
1113
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);
1117                 
1118                 // clear the data
1119                 memset(xe, 0, sizeof(xfer_entry));
1120                 return;
1121         }                       
1122
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 );
1126
1127         // attempt to open the file (using the prefixed filename)
1128         xe->file = NULL;
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);          
1132
1133                 // clear the data
1134                 memset(xe, 0, sizeof(xfer_entry));
1135                 return;
1136         }
1137         
1138         // set the waiting for data flag
1139         xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;         
1140
1141         // send an ack to the server            
1142         multi_xfer_send_ack(who, sig);  
1143
1144 #ifdef MULTI_XFER_VERBOSE
1145         nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1146 #endif  
1147 }
1148
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)
1151 {
1152         ubyte data[MAX_PACKET_SIZE],code;
1153         ushort data_size;
1154         int flen;
1155         int packet_size = 0;    
1156
1157         // print out a crude progress indicator
1158         nprintf(("Network", "+"));              
1159
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;
1164
1165                 // set the timestmp
1166                 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1167
1168                 // send the packet
1169                 multi_xfer_send_final(xe);              
1170
1171                 return;
1172         }
1173
1174         // build the header 
1175         BUILD_HEADER(XFER_PACKET);      
1176
1177         // length of the added string
1178         flen = strlen(xe->filename) + 4;
1179
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);
1183         } else {
1184                 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1185         }
1186         // increment the file pointer
1187         xe->file_ptr += data_size;      
1188
1189         // add the opcode
1190         code = MULTI_XFER_CODE_DATA;
1191         ADD_DATA(code);
1192
1193         // add the sig
1194         ADD_DATA(xe->sig);
1195
1196         // add in the size of the rest of the packet    
1197         ADD_DATA(data_size);
1198         
1199         // copy in the data
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);
1203
1204                 // fail this send
1205                 multi_xfer_fail_entry(xe);              
1206                 return;
1207         }
1208
1209         // increment the packet size
1210         packet_size += (int)data_size;
1211
1212         // set the timestmp
1213         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1214
1215         // otherwise send the data      
1216         psnet_rel_send(xe->file_socket, data, packet_size);
1217 }
1218
1219 // send an ack to the sender
1220 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1221 {
1222         ubyte data[MAX_PACKET_SIZE],code;       
1223         int packet_size = 0;
1224
1225         // build the header and add 
1226         BUILD_HEADER(XFER_PACKET);      
1227
1228         // add the opcode
1229         code = MULTI_XFER_CODE_ACK;
1230         ADD_DATA(code);
1231
1232         // add the sig
1233         ADD_DATA(sig);
1234         
1235         // send the data        
1236         psnet_rel_send(socket, data, packet_size);
1237 }
1238
1239 // send a nak to the sender
1240 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1241 {
1242         ubyte data[MAX_PACKET_SIZE],code;       
1243         int packet_size = 0;
1244
1245         // build the header and add the code
1246         BUILD_HEADER(XFER_PACKET);      
1247
1248         // add the opcode
1249         code = MULTI_XFER_CODE_NAK;
1250         ADD_DATA(code);
1251
1252         // add the sig
1253         ADD_DATA(sig);
1254
1255         // send the data        
1256         psnet_rel_send(socket, data, packet_size);
1257 }
1258
1259 // send a "final" packet
1260 void multi_xfer_send_final(xfer_entry *xe)
1261 {
1262         ubyte data[MAX_PACKET_SIZE],code;       
1263         int packet_size = 0;
1264
1265         // build the header
1266         BUILD_HEADER(XFER_PACKET);      
1267
1268         // add the code
1269         code = MULTI_XFER_CODE_FINAL;
1270         ADD_DATA(code);
1271
1272         // add the sig
1273         ADD_DATA(xe->sig);
1274
1275         // send the data        
1276         psnet_rel_send(xe->file_socket, data, packet_size);
1277 }
1278
1279 // send the header to begin a file transfer
1280 void multi_xfer_send_header(xfer_entry *xe)
1281 {
1282         ubyte data[MAX_PACKET_SIZE],code;       
1283         int packet_size = 0;
1284
1285         // build the header and add the opcode
1286         BUILD_HEADER(XFER_PACKET);      
1287         code = MULTI_XFER_CODE_HEADER;
1288         ADD_DATA(code);
1289
1290         // add the sig
1291         ADD_DATA(xe->sig);
1292
1293         // add the filename
1294         ADD_STRING(xe->filename);
1295                 
1296         // add the id #
1297         ADD_DATA(xe->file_size);
1298
1299         // add the file checksum
1300         ADD_DATA(xe->file_chksum);
1301
1302         // send the packet      
1303         psnet_rel_send(xe->file_socket, data, packet_size);
1304 }
1305
1306 // convert the filename into the prefixed ex_filename
1307 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1308 {
1309         char temp[MAX_FILENAME_LEN+50];
1310         
1311         // blast the memory clean
1312         memset(temp, 0, MAX_FILENAME_LEN+50);
1313
1314         // copy in the prefix
1315         strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1316
1317         // stick on the original name
1318         strcat(temp, filename);
1319
1320         // copy the whole thing to the outgoing filename
1321         strcpy(ex_filename, temp);
1322 }
1323
1324 // get a new xfer sig
1325 ushort multi_xfer_get_sig()
1326 {
1327         ushort ret = Multi_xfer_sig;
1328
1329         // new one
1330         if(Multi_xfer_sig == 0xffff){
1331                 Multi_xfer_sig = 0;
1332         } else {
1333                 Multi_xfer_sig++;
1334         }
1335
1336         return ret;
1337 }