]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_xfer.cpp
Initial revision
[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.1  2002/05/03 03:28:10  root
9  * Initial revision
10  *
11  * 
12  * 11    3/10/99 6:50p Dave
13  * Changed the way we buffer packets for all clients. Optimized turret
14  * fired packets. Did some weapon firing optimizations.
15  * 
16  * 10    3/09/99 6:24p Dave
17  * More work on object update revamping. Identified several sources of
18  * unnecessary bandwidth.
19  * 
20  * 9     1/24/99 11:37p Dave
21  * First full rev of beam weapons. Very customizable. Removed some bogus
22  * Int3()'s in low level net code.
23  * 
24  * 8     12/16/98 11:17a Dave
25  * Fixed potential situation where a send and receive to the same player
26  * with the same sig might get confused with each other.
27  * 
28  * 7     12/14/98 4:01p Dave
29  * Got multi_data stuff working well with new xfer stuff. 
30  * 
31  * 6     12/14/98 12:13p Dave
32  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
33  * Need to test now.
34  * 
35  * 5     11/19/98 8:03a Dave
36  * Full support for D3-style reliable sockets. Revamped packet lag/loss
37  * system, made it receiver side and at the lowest possible level.
38  * 
39  * 4     11/17/98 11:12a Dave
40  * Removed player identification by address. Now assign explicit id #'s.
41  * 
42  * 3     11/05/98 5:55p Dave
43  * Big pass at reducing #includes
44  * 
45  * 2     10/07/98 10:53a Dave
46  * Initial checkin.
47  * 
48  * 1     10/07/98 10:50a Dave
49  * 
50  * 56    9/11/98 9:31a Dave
51  * Fixed file xfer bug regarding checksums.
52  * 
53  * 55    8/12/98 4:53p Dave
54  * Put in 32 bit checksumming for PXO missions. No validation on the
55  * actual tracker yet, though.
56  * 
57  * 54    6/13/98 9:32p Mike
58  * Kill last character in file which caused "Find in Files" to report the
59  * file as "not a text file."
60  * 
61  * 53    6/13/98 6:01p Hoffoss
62  * Externalized all new (or forgot to be added) strings to all the code.
63  * 
64  * 52    6/13/98 3:19p Hoffoss
65  * NOX()ed out a bunch of strings that shouldn't be translated.
66  * 
67  * 51    5/21/98 1:52a Dave
68  * Remove obsolete command line functions. Reduce shield explosion packets
69  * drastically. Tweak PXO screen even more. Fix file xfer system so that
70  * we can guarantee file uniqueness.
71  * 
72  * 50    4/30/98 4:53p John
73  * Restructured and cleaned up cfile code.  Added capability to read off
74  * of CD-ROM drive and out of multiple pack files.
75  * 
76  * 49    4/23/98 6:18p Dave
77  * Store ETS values between respawns. Put kick feature in the text
78  * messaging system. Fixed text messaging system so that it doesn't
79  * process or trigger ship controls. Other UI fixes.
80  * 
81  * 48    4/22/98 5:53p Dave
82  * Large reworking of endgame sequencing. Updated multi host options
83  * screen for new artwork. Put in checks for host or team captains leaving
84  * midgame.
85  * 
86  * 47    4/21/98 4:44p Dave
87  * Implement Vasudan ships in multiplayer. Added a debug function to bash
88  * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
89  * problem in options screen. 
90  * 
91  * 46    4/20/98 6:04p Dave
92  * Implement multidata cache flushing and xferring mission files to
93  * multidata. Make sure observers can't change hud config. Fix pilot image
94  * viewing in popup. Put in game status field. Tweaked multi options. 
95  * 
96  * 45    4/08/98 2:51p Dave
97  * Fixed pilot image xfer once again. Solidify team selection process in
98  * pre-briefing multiplayer.
99  * 
100  * 44    4/04/98 4:22p Dave
101  * First rev of UDP reliable sockets is done. Seems to work well if not
102  * overly burdened.
103  * 
104  * 43    4/03/98 1:03a Dave
105  * First pass at unreliable guaranteed delivery packets.
106  * 
107  * 42    4/01/98 11:19p Dave
108  * Put in auto-loading of xferred pilot pic files. Grey out background
109  * behind pinfo popup. Put a chatbox message in when players are kicked.
110  * Moved mission title down in briefing. Other ui fixes.
111  * 
112  * 41    3/31/98 4:51p Dave
113  * Removed medals screen and multiplayer buttons from demo version. Put in
114  * new pilot popup screen. Make ships in mp team vs. team have proper team
115  * ids. Make mp respawns a permanent option saved in the player file.
116  * 
117  * 40    3/30/98 8:46a Allender
118  * fix an optimized build compiler warning
119  * 
120  * 39    3/26/98 6:01p Dave
121  * Put in file checksumming routine in cfile. Made pilot pic xferring more
122  * robust. Cut header size of voice data packets in half. Put in
123  * restricted game host query system.
124  * 
125  * 38    3/25/98 2:16p Dave
126  * Select random default image for newly created pilots. Fixed several
127  * multi-pause messaging bugs. Begin work on online help for multiplayer
128  * keys.
129  * 
130  * 37    3/24/98 5:00p Dave
131  * Fixed several ui bugs. Put in pre and post voice stream playback sound
132  * fx. Put in error specific popups for clients getting dropped from games
133  * through actions other than their own.
134  * 
135  * 36    3/23/98 5:42p Dave
136  * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
137  * that it can support multiplayer sends/received between client and
138  * server simultaneously.
139  * 
140  * 35    3/21/98 7:14p Dave
141  * Fixed up standalone player slot switching. Made training missions not
142  * count towards player stats.
143  * 
144  * 34    3/16/98 11:52p Allender
145  * Put in timestamp updates when processing data on both sender and
146  * receiver sides.
147  * 
148  * 33    2/22/98 2:53p Dave
149  * Put in groundwork for advanced multiplayer campaign  options.
150  * 
151  * 32    2/20/98 4:43p Dave
152  * Finished support for multiplayer player data files. Split off
153  * multiplayer campaign functionality.
154  * 
155  * 31    2/19/98 6:26p Dave
156  * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
157  * initial support for player data uploading.
158  * 
159  * 30    2/18/98 10:21p Dave
160  * Ripped out old file xfer system. Put in brand new xfer system.
161  * 
162  * $NoKeywords: $
163  */
164
165 #include <winsock.h>
166 #include <io.h>
167 #include "multi_xfer.h"
168 #include "cfile.h"
169 #include "multimsgs.h"
170 #include "psnet.h"
171 #include "popup.h"
172 #include "multi_endgame.h"
173 #include "timer.h"
174 #include "multi.h"
175 #include "multiutil.h"
176 #include "multi_log.h"
177
178 // ------------------------------------------------------------------------------------------
179 // MULTI XFER DEFINES/VARS
180 //
181
182 #define MULTI_XFER_VERBOSE                                                                              // keep this defined for verbose debug output
183
184 #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) )
185
186 // packet codes
187 #define MULTI_XFER_CODE_ACK                                     0                               // simple response to the last request
188 #define MULTI_XFER_CODE_NAK                                     1                               // simple response to the last request
189 #define MULTI_XFER_CODE_HEADER                          2                               // file xfer header information follows, requires a HEADER_RESPONSE
190 #define MULTI_XFER_CODE_DATA                                    3                               // data block follows, requires an ack
191 #define MULTI_XFER_CODE_FINAL                                   4                               // indication from sender that xfer is complete, requires an ack
192
193 // entry flags
194 #define MULTI_XFER_FLAG_USED                                    (1<<0)          // this entry is in use 
195 #define MULTI_XFER_FLAG_SEND                                    (1<<1)          // this entry is sending a file
196 #define MULTI_XFER_FLAG_RECV                                    (1<<2)          // this entry is receiving a file
197 #define MULTI_XFER_FLAG_PENDING                         (1<<3)          // this entry is ready to send a header and start the process
198 #define MULTI_XFER_FLAG_WAIT_ACK                                (1<<4)          // waiting for an ack/nak
199 #define MULTI_XFER_FLAG_WAIT_DATA                       (1<<5)          // waiting for another block of data 
200 #define MULTI_XFER_FLAG_UNKNOWN                         (1<<6)          // xfer final has been sent, and we are waiting for a response
201 #define MULTI_XFER_FLAG_SUCCESS                         (1<<7)          // finished xfer
202 #define MULTI_XFER_FLAG_FAIL                                    (1<<8)          // xfer failed
203 #define MULTI_XFER_FLAG_TIMEOUT                         (1<<9)          // xfer has timed-out
204 #define MULTI_XFER_FLAG_QUEUE_CURRENT           (1<<10)         // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
205
206 // packet size for file xfer
207 #define MULTI_XFER_MAX_DATA_SIZE                                490                     // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
208
209 // timeout for a given xfer operation
210 #define MULTI_XFER_TIMEOUT                                              10000           
211
212 //XSTR:OFF
213
214 // temp filename header for xferring files
215 #define MULTI_XFER_FNAME_PREFIX                         "_fsx_"
216
217 //XSTR:ON
218
219 // xfer entries/events
220 #define MAX_XFER_ENTRIES                                                60                              // the max allowed file xfer entries
221 typedef struct xfer_entry {
222         int flags;                                                                                                              // status flags for this entry
223         char filename[MAX_FILENAME_LEN+1];                                              // filename of the currently xferring file
224         char ex_filename[MAX_FILENAME_LEN+10];                                  // filename with xfer prefix tacked on to the front
225         CFILE *file;                                                                                                    // file handle of the current xferring file
226         int file_size;                                                                                                  // total size of the file being xferred
227         int file_ptr;                                                                                                   // total bytes we're received so far
228         ushort file_chksum;                                                                                     // used for checking successfully xferred files
229         PSNET_SOCKET_RELIABLE file_socket;                                              // socket used to xfer the file 
230         int xfer_stamp;                                                                                         // timestamp for the current operation          
231         int force_dir;                                                                                                  // force the file to go to this directory on receive (will override Multi_xfer_force_dir)       
232         ushort sig;                                                                                                             // identifying sig - sender specifies this
233 } xfer_entry;
234 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES];                  // the file xfer entries themselves
235
236 // callback function pointer for when we start receiving a file
237 void (*Multi_xfer_recv_notify)(int handle);
238
239 // lock for the xfer system
240 int Multi_xfer_locked;
241
242 // force directory type for receives
243 int Multi_xfer_force_dir; 
244
245 // unique file signature - this along with a socket # is enough to identify all xfers
246 ushort Multi_xfer_sig = 0;
247
248
249 // ------------------------------------------------------------------------------------------
250 // MULTI XFER FORWARD DECLARATIONS
251 //
252
253 // evaluate the condition of the entry
254 void multi_xfer_eval_entry(xfer_entry *xe);
255
256 // set an entry to be "failed"
257 void multi_xfer_fail_entry(xfer_entry *xe);
258
259 // get a valid xfer entry handle
260 int multi_xfer_get_free_handle();
261
262 // process an ack for this entry
263 void multi_xfer_process_ack(xfer_entry *xe);
264
265 // process a nak for this entry
266 void multi_xfer_process_nak(xfer_entry *xe);
267                 
268 // process a "final" packet     
269 void multi_xfer_process_final(xfer_entry *xe);          
270
271 // process a data packet
272 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
273         
274 // process a header
275 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);                
276
277 // send the next block of outgoing data or a "final" packet if we're done
278 void multi_xfer_send_next(xfer_entry *xe);
279
280 // send an ack to the sender
281 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
282
283 // send a nak to the sender
284 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
285
286 // send a "final" packet
287 void multi_xfer_send_final(xfer_entry *xe);
288
289 // send the header to begin a file transfer
290 void multi_xfer_send_header(xfer_entry *xe);
291
292 // convert the filename into the prefixed ex_filename
293 void multi_xfer_conv_prefix(char *filename, char *ex_filename);
294
295 // get a new xfer sig
296 ushort multi_xfer_get_sig();
297
298 // ------------------------------------------------------------------------------------------
299 // MULTI XFER FUNCTIONS
300 //
301
302 // initialize all file xfer transaction stuff, call in multi_level_init()
303 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
304 {
305         // blast all the entries
306         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
307
308         // assign the receive callback function pointer
309         Multi_xfer_recv_notify = multi_xfer_recv_callback;
310
311         // unlocked
312         Multi_xfer_locked = 0;
313
314         // no forced directory
315         Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;     
316 }
317
318 // do frame for all file xfers, call in multi_do_frame()
319 void multi_xfer_do()
320 {
321         int idx;
322
323         // process all valid xfer entries
324         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
325                 // if this one is actually in use and has not finished for one reason or another
326                 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))){
327                         // evaluate the condition of this entry (fail, timeout, etc)
328                         multi_xfer_eval_entry(&Multi_xfer_entry[idx]);                  
329                 }
330         }
331 }
332
333 // close down the file xfer system
334 void multi_xfer_close()
335 {
336         int idx;
337
338         // go through all active entries and abort them
339         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
340                 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
341                         multi_xfer_abort(idx);
342                 }
343         }
344
345         // now blast all the memory free
346         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
347 }
348
349 // reset the xfer system, including shutting down/killing all active xfers
350 void multi_xfer_reset()
351 {
352         int idx;
353
354         // shut down all active xfers
355         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
356                 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
357                         multi_xfer_abort(idx);
358                 }
359         }
360
361         // blast all the memory clean
362         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
363 }
364
365 // send a file to the specified player, return a handle
366 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
367 {
368         xfer_entry temp_entry;  
369         int handle;
370
371         // if the system is locked, return -1
372         if(Multi_xfer_locked){
373                 return -1;
374         }
375
376         // attempt to get a free handle
377         handle = multi_xfer_get_free_handle();
378         if(handle == -1){
379                 return -1;
380         }
381
382         // clear the temp entry
383         memset(&temp_entry,0,sizeof(xfer_entry));
384
385         // set the filename
386         strcpy(temp_entry.filename,filename);   
387
388         // attempt to open the file
389         temp_entry.file = NULL;
390         temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
391         if(temp_entry.file == NULL){
392 #ifdef MULTI_XFER_VERBOSE
393                 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
394 #endif
395
396                 return -1;
397         }
398
399         // set the file size
400         temp_entry.file_size = -1;
401         temp_entry.file_size = cfilelength(temp_entry.file);
402         if(temp_entry.file_size == -1){
403 #ifdef MULTI_XFER_VERBOSE
404                 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
405 #endif
406                 return -1;
407         }
408         temp_entry.file_ptr = 0;
409
410         // get the file checksum
411         if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
412 #ifdef MULTI_XFER_VERBOSE
413                 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
414 #endif
415                 return -1;
416         } 
417 #ifdef MULTI_XFER_VERBOSE
418         nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
419 #endif
420         // rewind the file pointer to the beginning of the file
421         cfseek(temp_entry.file,0,CF_SEEK_SET);
422
423         // set the flags
424         temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
425         temp_entry.flags |= flags;
426
427         // set the socket       
428         temp_entry.file_socket = who;           
429
430         // set the signature
431         temp_entry.sig = multi_xfer_get_sig();
432
433         // copy to the global array
434         memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
435         memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
436         
437         return handle;
438 }
439
440 // get the status of the current file xfer
441 int multi_xfer_get_status(int handle)
442 {
443         // if this is an invalid or an unused handle, notify as such
444         if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
445                 return MULTI_XFER_NONE;
446         }       
447         
448         // if the xfer has timed-out
449         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
450                 return MULTI_XFER_TIMEDOUT;
451         }
452
453         // if the xfer has failed for one reason or another (not timeout)
454         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
455                 return MULTI_XFER_FAIL;
456         }
457
458         // if the xfer has succeeded
459         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
460                 return MULTI_XFER_SUCCESS;
461         }
462
463         // if the xfer is queued
464         if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
465                 return MULTI_XFER_QUEUED;
466         }
467
468         // otherwise the xfer is still in progress
469         return MULTI_XFER_IN_PROGRESS;
470 }
471
472 // abort a transferring file
473 void multi_xfer_abort(int handle)
474 {
475         xfer_entry *xe;
476
477         // don't do anything if this is an invalid handle
478         if(MULTI_XFER_INVALID_HANDLE(handle)){
479                 return;
480         }
481
482         // get e handle to the entry
483         xe = &Multi_xfer_entry[handle];
484
485         // close any open file and delete it 
486         if(xe->file != NULL){
487                 cfclose(xe->file);
488                 xe->file = NULL;
489
490                 // delete it if there isn't some problem with the filename
491                 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
492                         cf_delete(xe->ex_filename, xe->force_dir);
493                 }
494         }
495
496         // zero the socket
497         xe->file_socket = INVALID_SOCKET;
498
499         // blast the entry
500         memset(xe,0,sizeof(xfer_entry));
501 }
502
503 // release an xfer handle
504 void multi_xfer_release_handle(int handle)
505 {
506         xfer_entry *xe;
507
508         // don't do anything if this is an invalid handle
509         if(MULTI_XFER_INVALID_HANDLE(handle)){
510                 return;
511         }
512
513         // get e handle to the entry
514         xe = &Multi_xfer_entry[handle];
515
516         // close any open file and delete it 
517         if(xe->file != NULL){
518                 cfclose(xe->file);
519                 xe->file = NULL;
520
521                 // delete it if the file was not successfully received
522                 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
523                         cf_delete(xe->ex_filename,xe->force_dir);
524                 }
525         }
526
527         // zero the socket
528         xe->file_socket = INVALID_SOCKET;       
529
530         // blast the entry
531         memset(xe,0,sizeof(xfer_entry));
532 }
533
534 // get the filename of the xfer for the given handle
535 char *multi_xfer_get_filename(int handle)
536 {
537         // if this is an invalid handle, return NULL
538         if(MULTI_XFER_INVALID_HANDLE(handle)){
539                 return NULL;
540         }
541
542         // otherwise return the string
543         return Multi_xfer_entry[handle].filename;
544 }
545
546 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
547 void multi_xfer_lock()
548 {
549         Multi_xfer_locked = 1;
550 }
551
552 // unlock the xfer system
553 void multi_xfer_unlock()
554 {
555         Multi_xfer_locked = 0;
556 }
557
558 // force all receives to go into the specified directory by cfile type
559 void multi_xfer_force_dir(int cf_type)
560 {
561         Multi_xfer_force_dir = cf_type;
562         Assert(Multi_xfer_force_dir > CF_TYPE_ANY);
563 }
564
565 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
566 void multi_xfer_handle_force_dir(int handle,int cf_type)
567 {
568         // if this is an invalid handle, return NULL
569         if(MULTI_XFER_INVALID_HANDLE(handle)){
570                 return;
571         }
572
573         // force to go to the given directory
574         Multi_xfer_entry[handle].force_dir = cf_type;
575         Assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
576 }
577
578 // or the flag on a given entry
579 void multi_xfer_xor_flags(int handle,int flags)
580 {
581         // if this is an invalid handle, return NULL
582         if(MULTI_XFER_INVALID_HANDLE(handle)){
583                 return;
584         }
585
586         // xor the flags
587         Multi_xfer_entry[handle].flags ^= flags;
588 }
589
590 // get the flags for a given entry
591 int multi_xfer_get_flags(int handle)
592 {
593         // if this is an invalid handle, return NULL
594         if(MULTI_XFER_INVALID_HANDLE(handle)){
595                 return -1;
596         }
597
598         // return the flags
599         return Multi_xfer_entry[handle].flags;
600 }
601
602 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
603 int multi_xfer_lookup(char *filename)
604 {
605         int idx;
606
607         // if we have an invalid filename, do nothing
608         if((filename == NULL) || (strlen(filename) <= 0)){
609                 return 0;
610         }
611
612         // otherwise, perform a lookup
613         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
614                 // if we found a matching filename
615                 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !stricmp(filename,Multi_xfer_entry[idx].filename)){
616                         return idx;
617                 }
618         }
619
620         // did not find a match
621         return -1;
622 }
623
624 // get the % of completion of the passed file handle, return < 0 if invalid
625 float multi_xfer_pct_complete(int handle)
626 {
627         // if this is an invalid handle, return invalid
628         if(MULTI_XFER_INVALID_HANDLE(handle)){
629                 return -1.0f;
630         }
631
632         // if the file size is 0, return invalid
633         if(Multi_xfer_entry[handle].file_size == 0){
634                 return -1.0f;
635         }
636
637         // return the pct completion
638         return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
639 }
640
641 // get the socket of the file xfer (useful for identifying players)
642 uint multi_xfer_get_sock(int handle)
643 {
644         // if this is an invalid handle, return NULL
645         if(MULTI_XFER_INVALID_HANDLE(handle)){
646                 return INVALID_SOCKET;
647         }
648
649         return Multi_xfer_entry[handle].file_socket;
650 }
651
652 // get the CF_TYPE of the directory this file is going to
653 int multi_xfer_get_force_dir(int handle)
654 {
655         // if this is an invalid handle, return NULL
656         if(MULTI_XFER_INVALID_HANDLE(handle)){
657                 return INVALID_SOCKET;
658         }
659
660         return Multi_xfer_entry[handle].force_dir;
661 }
662
663
664 // ------------------------------------------------------------------------------------------
665 // MULTI XFER FORWARD DECLARATIONS
666 //
667
668 // evaluate the condition of the entry
669 void multi_xfer_eval_entry(xfer_entry *xe)
670 {       
671         int idx;
672         int found;
673         xfer_entry *xe_c;
674
675         // if the entry is marked as successful, then don't do anything
676         if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
677                 return;
678         }
679
680         // if the entry is queued
681         if(xe->flags & MULTI_XFER_FLAG_QUEUE){
682                 // if the entry is not current
683                 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
684                         // see if there are any other queued up xfers to this target. if not, make me current and start sending
685                         found = 0;
686                         for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
687                                 xe_c = &Multi_xfer_entry[idx];
688
689                                 // if this is a valid entry and is a queued entry and is going to the same target
690                                 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) && 
691                                         (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
692                                         
693                                         found = 1;
694                                         break;
695                                 }                               
696                         }
697
698                         // if we found no other entries, make this guy current and pending
699                         if(!found){
700                                 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
701                                 xe->flags |= MULTI_XFER_FLAG_PENDING;
702
703 #ifdef MULTI_XFER_VERBOSE
704                                 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
705 #endif
706                         } 
707                         // otherwise, do nothing for him - he has to still wait
708                         else {
709                                 return;
710                         }
711                 }
712         }
713
714         // if the entry is marked as pending - send out the header to get the ball rolling
715         if(xe->flags & MULTI_XFER_FLAG_PENDING){                
716                 // send the header to begin the transfer
717                 multi_xfer_send_header(xe);
718
719                 // set the timestamp
720                 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); 
721
722                 // unset the pending flag
723                 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
724
725                 // set the ack/wait flag
726                 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
727         }
728         
729         // see if the entry has timed-out for one reason or another
730         if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
731                 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;                   
732
733                 // if we should be auto-destroying this entry, do so
734                 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
735                         multi_xfer_fail_entry(xe);                      
736                 }
737         }               
738 }
739
740 // lookup a file xfer entry by player
741 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
742 {
743         int idx;
744
745         // look through all valid xfer entries
746         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
747                 // if we're looking for sending entries
748                 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
749                         continue;
750                 }
751                 // if we're looking for recv entries
752                 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
753                         continue;
754                 }
755
756                 // if we found a match
757                 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
758                         return &Multi_xfer_entry[idx];
759                 }
760         }
761
762         return NULL;
763 }
764
765 // set an entry to be "failed"
766 void multi_xfer_fail_entry(xfer_entry *xe)
767 {
768         // set its flags appropriately
769         xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
770         xe->flags |= MULTI_XFER_FLAG_FAIL;
771
772         // close the file pointer
773         if(xe->file != NULL){
774                 cfclose(xe->file);
775                 xe->file = NULL;
776         }
777
778         // delete the file
779         if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
780                 cf_delete(xe->ex_filename,xe->force_dir);
781         }
782                 
783         // null the timestamp
784         xe->xfer_stamp = -1;
785
786         // if we should be auto-destroying this entry, do so
787         if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
788                 multi_xfer_release_handle(xe - Multi_xfer_entry);
789         }
790
791         // blast the memory clean
792         memset(xe,0,sizeof(xfer_entry));
793 }
794
795 // get a valid xfer entry handle
796 int multi_xfer_get_free_handle()
797 {
798         int idx;
799
800         // look for a free entry
801         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
802                 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
803                         return idx;
804                 }
805         }
806
807         return -1;
808 }
809
810
811 // ------------------------------------------------------------------------------------------
812 // MULTI XFER PACKET HANDLERS
813 //
814
815 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
816 // packet regardless of error conditions
817 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
818 {       
819         ubyte val;      
820         xfer_entry *xe;
821         char filename[255];
822         ushort data_size = 0;
823         int file_size = -1;
824         ushort file_checksum = 0;
825         int offset = 0;
826         ubyte xfer_data[600];
827         ushort sig;
828         int sender_side = 1;
829
830         // read in all packet data
831         GET_DATA(val);  
832         GET_DATA(sig);
833         switch(val){
834         // RECV side
835         case MULTI_XFER_CODE_DATA:                              
836                 GET_DATA(data_size);            
837                 memcpy(xfer_data, data + offset, data_size);
838                 offset += data_size;
839                 sender_side = 0;
840                 break;
841
842         // RECV side
843         case MULTI_XFER_CODE_HEADER:            
844                 GET_STRING(filename);
845                 GET_DATA(file_size);                                    
846                 GET_DATA(file_checksum);
847                 sender_side = 0;
848                 break;
849
850         // SEND side
851         case MULTI_XFER_CODE_ACK:
852         case MULTI_XFER_CODE_NAK:
853                 break;
854
855         // RECV side
856         case MULTI_XFER_CODE_FINAL:
857                 sender_side = 0;
858                 break;
859
860         default:
861                 Int3();
862         }                       
863
864         // at this point we've read all the data in the packet
865
866         // at this point, we should process code-specific data  
867         xe = NULL;
868         if(val != MULTI_XFER_CODE_HEADER){              
869                 // if the code is not a request or a header, we need to look up the existing xfer_entry
870                 xe = NULL;
871                         
872                 xe = multi_xfer_find_entry(who, sig, sender_side);
873                 if(xe == NULL){                                         
874 #ifdef MULTI_XFER_VERBOSE
875                         nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
876
877                         // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
878 #ifndef NDEBUG
879                         int np_index = find_player_socket(who);
880                         ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
881                         ml_printf(": sig == %d", sig);
882                         ml_printf(": xfer header == %d", val);
883                         if(np_index < 0){
884                                 ml_string(": player == unknown");
885                         } else {
886                                 ml_printf(": player == %s", Net_players[np_index].player->callsign);
887                         }
888                         if(sender_side){
889                                 ml_string(": sending");
890                         } else {
891                                 ml_string(": receiving");
892                         }
893 #endif
894 #endif
895                 //      Int3();
896                         return offset;
897                 }
898         }
899
900         switch((int)val){
901         // process an ack for this entry
902         case MULTI_XFER_CODE_ACK :
903                 Assert(xe != NULL);
904                 multi_xfer_process_ack(xe);
905                 break;
906         
907         // process a nak for this entry
908         case MULTI_XFER_CODE_NAK :
909                 Assert(xe != NULL);
910                 multi_xfer_process_nak(xe);
911                 break;
912
913         // process a "final" packet
914         case MULTI_XFER_CODE_FINAL :
915                 Assert(xe != NULL);
916                 multi_xfer_process_final(xe);
917                 break;
918
919         // process a data packet
920         case MULTI_XFER_CODE_DATA :
921                 Assert(xe != NULL);
922                 multi_xfer_process_data(xe, xfer_data, data_size);
923                 break;
924         
925         // process a header
926         case MULTI_XFER_CODE_HEADER :
927                 // send on my reliable socket
928                 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
929                 break;
930         }               
931         return offset;
932 }
933
934 // process an ack for this entry
935 void multi_xfer_process_ack(xfer_entry *xe)
936 {                       
937         // if we are a sender
938         if(xe->flags & MULTI_XFER_FLAG_SEND){
939                 // if we are waiting on a final ack, then the transfer has completed successfully
940                 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
941                         xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
942                         xe->flags |= MULTI_XFER_FLAG_SUCCESS;
943
944 #ifdef MULTI_XFER_VERBOSE
945                         nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
946 #endif
947
948                         // if we should be auto-destroying this entry, do so
949                         if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
950                                 multi_xfer_release_handle(xe - Multi_xfer_entry);
951                         }
952                 } 
953                 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
954                 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
955                         multi_xfer_send_next(xe);
956                 }
957         }
958 }
959
960 // process a nak for this entry
961 void multi_xfer_process_nak(xfer_entry *xe)
962 {               
963         // if we get an ack at any time we should simply set the xfer to failed
964         multi_xfer_fail_entry(xe);
965 }
966                 
967 // process a "final" packet     
968 void multi_xfer_process_final(xfer_entry *xe)
969 {       
970         ushort chksum;
971
972         // make sure we skip a line
973         nprintf(("Network","\n"));
974         
975         // close the file
976         if(xe->file != NULL){
977                 cflush(xe->file);
978                 cfclose(xe->file);
979                 xe->file = NULL;
980         }       
981
982         // check to make sure the file checksum is the same
983         chksum = 0;
984         if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
985                 // mark as failed
986                 xe->flags |= MULTI_XFER_FLAG_FAIL;
987
988 #ifdef MULTI_XFER_VERBOSE
989                 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
990 #endif
991
992                 // abort the xfer
993                 multi_xfer_abort(xe - Multi_xfer_entry);
994                 return;
995         }
996         // checksums check out, so rename the file and be done with it
997         else {
998 #ifdef MULTI_XFER_VERBOSE
999                 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));
1000 #endif
1001                 // rename the file properly
1002                 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1003                         // mark the xfer as being successful
1004                         xe->flags |= MULTI_XFER_FLAG_SUCCESS;   
1005
1006                         nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));         
1007
1008                         // send an ack to the sender
1009                         multi_xfer_send_ack(xe->file_socket, xe->sig);
1010                 } else {
1011                         // mark it as failing
1012                         xe->flags |= MULTI_XFER_FLAG_FAIL;
1013                         nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1014
1015                         // delete the tempfile
1016                         cf_delete(xe->ex_filename, xe->force_dir);
1017
1018                         // send an nak to the sender
1019                         multi_xfer_send_nak(xe->file_socket, xe->sig);
1020                 }
1021
1022                 // if we should be auto-destroying this entry, do so
1023                 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1024                         multi_xfer_release_handle(xe - Multi_xfer_entry);
1025                 }
1026         }
1027 }
1028
1029 // process a data packet
1030 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)        
1031 {                       
1032         // print out a crude progress indicator
1033         nprintf(("Network","."));               
1034
1035         // attempt to write the rest of the data string to the file
1036         if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1037                 // inform the sender we had a problem
1038                 multi_xfer_send_nak(xe->file_socket, xe->sig);
1039
1040                 // fail this entry
1041                 multi_xfer_fail_entry(xe);
1042
1043                 xe->file_ptr += data_size;              
1044                 return;
1045         }
1046
1047         // increment the file pointer
1048         xe->file_ptr += data_size;
1049
1050         // send an ack to the sender
1051         multi_xfer_send_ack(xe->file_socket, xe->sig);
1052
1053         // set the timestmp
1054         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); 
1055 }
1056         
1057 // process a header, return bytes processed
1058 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1059 {               
1060         xfer_entry *xe;         
1061         int handle;     
1062
1063         // if the xfer system is locked, send a nak
1064         if(Multi_xfer_locked){          
1065                 multi_xfer_send_nak(who, sig);
1066                 return;
1067         }
1068
1069         // try and get a free xfer handle
1070         handle = multi_xfer_get_free_handle();
1071         if(handle == -1){               
1072                 multi_xfer_send_nak(who, sig);
1073                 return;
1074         } else {
1075                 xe = &Multi_xfer_entry[handle];
1076                 memset(xe,0,sizeof(xfer_entry));
1077         }               
1078
1079         // set the recv and used flags
1080         xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1081
1082         // get the header data  
1083         xe->file_size = file_size;
1084
1085         // get the file chksum
1086         xe->file_chksum = file_checksum;        
1087
1088         // set the socket
1089         xe->file_socket = who;  
1090
1091         // set the timeout timestamp
1092         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1093
1094         // set the sig
1095         xe->sig = sig;
1096
1097         // copy the filename and get the prefixed xfer filename
1098         strcpy(xe->filename, filename);
1099         multi_xfer_conv_prefix(xe->filename, xe->ex_filename);
1100 #ifdef MULTI_XFER_VERBOSE
1101         nprintf(("Network","MULTI XFER : converted filename %s to %s\n",xe->filename, xe->ex_filename));
1102 #endif
1103
1104         // determine what directory to place the file in
1105         // individual xfer entries take precedence over the global multi xfer force entry       
1106         xe->force_dir = Multi_xfer_force_dir;   
1107
1108         // call the callback function
1109         Multi_xfer_recv_notify(handle); 
1110
1111         // if the notify function invalidated this xfer handle, then cancel the whole thing
1112         if(xe->flags & MULTI_XFER_FLAG_REJECT){         
1113                 multi_xfer_send_nak(who, sig);
1114                 
1115                 // clear the data
1116                 memset(xe, 0, sizeof(xfer_entry));
1117                 return;
1118         }                       
1119
1120         // delete the old file (if it exists)
1121         cf_delete( xe->filename, CF_TYPE_MULTI_CACHE );
1122         cf_delete( xe->filename, CF_TYPE_MISSIONS );
1123
1124         // attempt to open the file (using the prefixed filename)
1125         xe->file = NULL;
1126         xe->file = cfopen(xe->ex_filename, "wb", CFILE_NORMAL, xe->force_dir);
1127         if(xe->file == NULL){           
1128                 multi_xfer_send_nak(who, sig);          
1129
1130                 // clear the data
1131                 memset(xe, 0, sizeof(xfer_entry));
1132                 return;
1133         }
1134         
1135         // set the waiting for data flag
1136         xe->flags |= MULTI_XFER_FLAG_WAIT_DATA;         
1137
1138         // send an ack to the server            
1139         multi_xfer_send_ack(who, sig);  
1140
1141 #ifdef MULTI_XFER_VERBOSE
1142         nprintf(("Network","MULTI XFER : AFTER HEADER %s\n",xe->filename));
1143 #endif  
1144 }
1145
1146 // send the next block of outgoing data or a "final" packet if we're done
1147 void multi_xfer_send_next(xfer_entry *xe)
1148 {
1149         ubyte data[MAX_PACKET_SIZE],code;
1150         ushort data_size;
1151         int flen;
1152         int packet_size = 0;    
1153
1154         // print out a crude progress indicator
1155         nprintf(("Network", "+"));              
1156
1157         // if we've sent all the data, then we should send a "final" packet
1158         if(xe->file_ptr >= xe->file_size){
1159                 // mark the entry as unknown 
1160                 xe->flags |= MULTI_XFER_FLAG_UNKNOWN;
1161
1162                 // set the timestmp
1163                 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1164
1165                 // send the packet
1166                 multi_xfer_send_final(xe);              
1167
1168                 return;
1169         }
1170
1171         // build the header 
1172         BUILD_HEADER(XFER_PACKET);      
1173
1174         // length of the added string
1175         flen = strlen(xe->filename) + 4;
1176
1177         // determine how much data we are going to send with this packet and add it in
1178         if((xe->file_size - xe->file_ptr) >= (MULTI_XFER_MAX_DATA_SIZE - flen)){
1179                 data_size = (ushort)(MULTI_XFER_MAX_DATA_SIZE - flen);
1180         } else {
1181                 data_size = (unsigned short)(xe->file_size - xe->file_ptr);
1182         }
1183         // increment the file pointer
1184         xe->file_ptr += data_size;      
1185
1186         // add the opcode
1187         code = MULTI_XFER_CODE_DATA;
1188         ADD_DATA(code);
1189
1190         // add the sig
1191         ADD_DATA(xe->sig);
1192
1193         // add in the size of the rest of the packet    
1194         ADD_DATA(data_size);
1195         
1196         // copy in the data
1197         if(cfread(data+packet_size,1,(int)data_size,xe->file) == NULL){
1198                 // send a nack to the receiver
1199                 multi_xfer_send_nak(xe->file_socket, xe->sig);
1200
1201                 // fail this send
1202                 multi_xfer_fail_entry(xe);              
1203                 return;
1204         }
1205
1206         // increment the packet size
1207         packet_size += (int)data_size;
1208
1209         // set the timestmp
1210         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1211
1212         // otherwise send the data      
1213         psnet_rel_send(xe->file_socket, data, packet_size);
1214 }
1215
1216 // send an ack to the sender
1217 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig)
1218 {
1219         ubyte data[MAX_PACKET_SIZE],code;       
1220         int packet_size = 0;
1221
1222         // build the header and add 
1223         BUILD_HEADER(XFER_PACKET);      
1224
1225         // add the opcode
1226         code = MULTI_XFER_CODE_ACK;
1227         ADD_DATA(code);
1228
1229         // add the sig
1230         ADD_DATA(sig);
1231         
1232         // send the data        
1233         psnet_rel_send(socket, data, packet_size);
1234 }
1235
1236 // send a nak to the sender
1237 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig)
1238 {
1239         ubyte data[MAX_PACKET_SIZE],code;       
1240         int packet_size = 0;
1241
1242         // build the header and add the code
1243         BUILD_HEADER(XFER_PACKET);      
1244
1245         // add the opcode
1246         code = MULTI_XFER_CODE_NAK;
1247         ADD_DATA(code);
1248
1249         // add the sig
1250         ADD_DATA(sig);
1251
1252         // send the data        
1253         psnet_rel_send(socket, data, packet_size);
1254 }
1255
1256 // send a "final" packet
1257 void multi_xfer_send_final(xfer_entry *xe)
1258 {
1259         ubyte data[MAX_PACKET_SIZE],code;       
1260         int packet_size = 0;
1261
1262         // build the header
1263         BUILD_HEADER(XFER_PACKET);      
1264
1265         // add the code
1266         code = MULTI_XFER_CODE_FINAL;
1267         ADD_DATA(code);
1268
1269         // add the sig
1270         ADD_DATA(xe->sig);
1271
1272         // send the data        
1273         psnet_rel_send(xe->file_socket, data, packet_size);
1274 }
1275
1276 // send the header to begin a file transfer
1277 void multi_xfer_send_header(xfer_entry *xe)
1278 {
1279         ubyte data[MAX_PACKET_SIZE],code;       
1280         int packet_size = 0;
1281
1282         // build the header and add the opcode
1283         BUILD_HEADER(XFER_PACKET);      
1284         code = MULTI_XFER_CODE_HEADER;
1285         ADD_DATA(code);
1286
1287         // add the sig
1288         ADD_DATA(xe->sig);
1289
1290         // add the filename
1291         ADD_STRING(xe->filename);
1292                 
1293         // add the id #
1294         ADD_DATA(xe->file_size);
1295
1296         // add the file checksum
1297         ADD_DATA(xe->file_chksum);
1298
1299         // send the packet      
1300         psnet_rel_send(xe->file_socket, data, packet_size);
1301 }
1302
1303 // convert the filename into the prefixed ex_filename
1304 void multi_xfer_conv_prefix(char *filename,char *ex_filename)
1305 {
1306         char temp[MAX_FILENAME_LEN+50];
1307         
1308         // blast the memory clean
1309         memset(temp, 0, MAX_FILENAME_LEN+50);
1310
1311         // copy in the prefix
1312         strcpy(temp, MULTI_XFER_FNAME_PREFIX);
1313
1314         // stick on the original name
1315         strcat(temp, filename);
1316
1317         // copy the whole thing to the outgoing filename
1318         strcpy(ex_filename, temp);
1319 }
1320
1321 // get a new xfer sig
1322 ushort multi_xfer_get_sig()
1323 {
1324         ushort ret = Multi_xfer_sig;
1325
1326         // new one
1327         if(Multi_xfer_sig == 0xffff){
1328                 Multi_xfer_sig = 0;
1329         } else {
1330                 Multi_xfer_sig++;
1331         }
1332
1333         return ret;
1334 }