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