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