]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_xfer.cpp
clean up Windows #include's and use winsock2
[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 <winsock2.h>
198 #endif
199 #include "multi_xfer.h"
200 #include "cfile.h"
201 #include "multimsgs.h"
202 #include "psnet.h"
203 #include "popup.h"
204 #include "multi_endgame.h"
205 #include "timer.h"
206 #include "multi.h"
207 #include "multiutil.h"
208 #include "multi_log.h"
209
210 // ------------------------------------------------------------------------------------------
211 // MULTI XFER DEFINES/VARS
212 //
213
214 #define MULTI_XFER_VERBOSE                                                                              // keep this defined for verbose debug output
215
216 #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) )
217
218 // packet codes
219 #define MULTI_XFER_CODE_ACK                                     0                               // simple response to the last request
220 #define MULTI_XFER_CODE_NAK                                     1                               // simple response to the last request
221 #define MULTI_XFER_CODE_HEADER                          2                               // file xfer header information follows, requires a HEADER_RESPONSE
222 #define MULTI_XFER_CODE_DATA                                    3                               // data block follows, requires an ack
223 #define MULTI_XFER_CODE_FINAL                                   4                               // indication from sender that xfer is complete, requires an ack
224
225 // entry flags
226 #define MULTI_XFER_FLAG_USED                                    (1<<0)          // this entry is in use 
227 #define MULTI_XFER_FLAG_SEND                                    (1<<1)          // this entry is sending a file
228 #define MULTI_XFER_FLAG_RECV                                    (1<<2)          // this entry is receiving a file
229 #define MULTI_XFER_FLAG_PENDING                         (1<<3)          // this entry is ready to send a header and start the process
230 #define MULTI_XFER_FLAG_WAIT_ACK                                (1<<4)          // waiting for an ack/nak
231 #define MULTI_XFER_FLAG_WAIT_DATA                       (1<<5)          // waiting for another block of data 
232 #define MULTI_XFER_FLAG_UNKNOWN                         (1<<6)          // xfer final has been sent, and we are waiting for a response
233 #define MULTI_XFER_FLAG_SUCCESS                         (1<<7)          // finished xfer
234 #define MULTI_XFER_FLAG_FAIL                                    (1<<8)          // xfer failed
235 #define MULTI_XFER_FLAG_TIMEOUT                         (1<<9)          // xfer has timed-out
236 #define MULTI_XFER_FLAG_QUEUE_CURRENT           (1<<10)         // for a set of XFER_FLAG_QUEUE'd files, this is the current one sending
237
238 // packet size for file xfer
239 #define MULTI_XFER_MAX_DATA_SIZE                                490                     // this will keep us within the MULTI_XFER_MAX_SIZE_LIMIT
240
241 // timeout for a given xfer operation
242 #define MULTI_XFER_TIMEOUT                                              10000           
243
244 //XSTR:OFF
245
246 // temp filename header for xferring files
247 #define MULTI_XFER_FNAME_PREFIX                         "_fsx_"
248
249 //XSTR:ON
250
251 // xfer entries/events
252 #define MAX_XFER_ENTRIES                                                60                              // the max allowed file xfer entries
253 typedef struct xfer_entry {
254         int flags;                                                                                                              // status flags for this entry
255         char filename[MAX_FILENAME_LEN+1];                                              // filename of the currently xferring file
256         char ex_filename[MAX_FILENAME_LEN+10];                                  // filename with xfer prefix tacked on to the front
257         CFILE *file;                                                                                                    // file handle of the current xferring file
258         int file_size;                                                                                                  // total size of the file being xferred
259         int file_ptr;                                                                                                   // total bytes we're received so far
260         ushort file_chksum;                                                                                     // used for checking successfully xferred files
261         PSNET_SOCKET_RELIABLE file_socket;                                              // socket used to xfer the file 
262         int xfer_stamp;                                                                                         // timestamp for the current operation          
263         int force_dir;                                                                                                  // force the file to go to this directory on receive (will override Multi_xfer_force_dir)       
264         ushort sig;                                                                                                             // identifying sig - sender specifies this
265 } xfer_entry;
266 xfer_entry Multi_xfer_entry[MAX_XFER_ENTRIES];                  // the file xfer entries themselves
267
268 // callback function pointer for when we start receiving a file
269 void (*Multi_xfer_recv_notify)(int handle);
270
271 // lock for the xfer system
272 int Multi_xfer_locked;
273
274 // force directory type for receives
275 int Multi_xfer_force_dir; 
276
277 // unique file signature - this along with a socket # is enough to identify all xfers
278 ushort Multi_xfer_sig = 0;
279
280
281 // ------------------------------------------------------------------------------------------
282 // MULTI XFER FORWARD DECLARATIONS
283 //
284
285 // evaluate the condition of the entry
286 void multi_xfer_eval_entry(xfer_entry *xe);
287
288 // set an entry to be "failed"
289 void multi_xfer_fail_entry(xfer_entry *xe);
290
291 // get a valid xfer entry handle
292 int multi_xfer_get_free_handle();
293
294 // process an ack for this entry
295 void multi_xfer_process_ack(xfer_entry *xe);
296
297 // process a nak for this entry
298 void multi_xfer_process_nak(xfer_entry *xe);
299                 
300 // process a "final" packet     
301 void multi_xfer_process_final(xfer_entry *xe);          
302
303 // process a data packet
304 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size);
305         
306 // process a header
307 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum);                
308
309 // send the next block of outgoing data or a "final" packet if we're done
310 void multi_xfer_send_next(xfer_entry *xe);
311
312 // send an ack to the sender
313 void multi_xfer_send_ack(PSNET_SOCKET_RELIABLE socket, ushort sig);
314
315 // send a nak to the sender
316 void multi_xfer_send_nak(PSNET_SOCKET_RELIABLE socket, ushort sig);
317
318 // send a "final" packet
319 void multi_xfer_send_final(xfer_entry *xe);
320
321 // send the header to begin a file transfer
322 void multi_xfer_send_header(xfer_entry *xe);
323
324 // convert the filename into the prefixed ex_filename
325 void multi_xfer_conv_prefix(char *filename, char *ex_filename, const int max_len);
326
327 // get a new xfer sig
328 ushort multi_xfer_get_sig();
329
330 // ------------------------------------------------------------------------------------------
331 // MULTI XFER FUNCTIONS
332 //
333
334 // initialize all file xfer transaction stuff, call in multi_level_init()
335 void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle))
336 {
337         // blast all the entries
338         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
339
340         // assign the receive callback function pointer
341         Multi_xfer_recv_notify = multi_xfer_recv_callback;
342
343         // unlocked
344         Multi_xfer_locked = 0;
345
346         // no forced directory
347         Multi_xfer_force_dir = CF_TYPE_MULTI_CACHE;     
348 }
349
350 // do frame for all file xfers, call in multi_do_frame()
351 void multi_xfer_do()
352 {
353         int idx;
354
355         // process all valid xfer entries
356         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
357                 // if this one is actually in use and has not finished for one reason or another
358                 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))){
359                         // evaluate the condition of this entry (fail, timeout, etc)
360                         multi_xfer_eval_entry(&Multi_xfer_entry[idx]);                  
361                 }
362         }
363 }
364
365 // close down the file xfer system
366 void multi_xfer_close()
367 {
368         int idx;
369
370         // go through all active entries and abort them
371         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
372                 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
373                         multi_xfer_abort(idx);
374                 }
375         }
376
377         // now blast all the memory free
378         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
379 }
380
381 // reset the xfer system, including shutting down/killing all active xfers
382 void multi_xfer_reset()
383 {
384         int idx;
385
386         // shut down all active xfers
387         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
388                 if(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED){
389                         multi_xfer_abort(idx);
390                 }
391         }
392
393         // blast all the memory clean
394         memset(Multi_xfer_entry,0,sizeof(xfer_entry) * MAX_XFER_ENTRIES);
395 }
396
397 // send a file to the specified player, return a handle
398 int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags)
399 {
400         xfer_entry temp_entry;  
401         int handle;
402
403         // if the system is locked, return -1
404         if(Multi_xfer_locked){
405                 return -1;
406         }
407
408         // attempt to get a free handle
409         handle = multi_xfer_get_free_handle();
410         if(handle == -1){
411                 return -1;
412         }
413
414         // clear the temp entry
415         memset(&temp_entry,0,sizeof(xfer_entry));
416
417         // set the filename
418         SDL_strlcpy(temp_entry.filename, filename, SDL_arraysize(temp_entry.filename));
419
420         // attempt to open the file
421         temp_entry.file = NULL;
422         temp_entry.file = cfopen(filename,"rb",CFILE_NORMAL,cfile_flags);
423         if(temp_entry.file == NULL){
424 #ifdef MULTI_XFER_VERBOSE
425                 nprintf(("Network","MULTI XFER : Could not open file %s on xfer send!\n",filename));
426 #endif
427
428                 return -1;
429         }
430
431         // set the file size
432         temp_entry.file_size = -1;
433         temp_entry.file_size = cfilelength(temp_entry.file);
434         if(temp_entry.file_size == -1){
435 #ifdef MULTI_XFER_VERBOSE
436                 nprintf(("Network","MULTI XFER : Could not get file length for file %s on xfer send\n",filename));
437 #endif
438                 return -1;
439         }
440         temp_entry.file_ptr = 0;
441
442         // get the file checksum
443         if(!cf_chksum_short(temp_entry.file,&temp_entry.file_chksum)){
444 #ifdef MULTI_XFER_VERBOSE
445                 nprintf(("Network","MULTI XFER : Could not get file checksum for file %s on xfer send\n",filename));
446 #endif
447                 return -1;
448         } 
449 #ifdef MULTI_XFER_VERBOSE
450         nprintf(("Network","MULTI XFER : Got file %s checksum of %d\n",temp_entry.filename,(int)temp_entry.file_chksum));
451 #endif
452         // rewind the file pointer to the beginning of the file
453         cfseek(temp_entry.file,0,CF_SEEK_SET);
454
455         // set the flags
456         temp_entry.flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_SEND | MULTI_XFER_FLAG_PENDING);
457         temp_entry.flags |= flags;
458
459         // set the socket       
460         temp_entry.file_socket = who;           
461
462         // set the signature
463         temp_entry.sig = multi_xfer_get_sig();
464
465         // copy to the global array
466         memset(&Multi_xfer_entry[handle],0,sizeof(xfer_entry));
467         memcpy(&Multi_xfer_entry[handle],&temp_entry,sizeof(xfer_entry));
468         
469         return handle;
470 }
471
472 // get the status of the current file xfer
473 int multi_xfer_get_status(int handle)
474 {
475         // if this is an invalid or an unused handle, notify as such
476         if((handle < 0) || (handle > (MAX_XFER_ENTRIES-1)) || !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_USED) ){
477                 return MULTI_XFER_NONE;
478         }       
479         
480         // if the xfer has timed-out
481         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_TIMEOUT){
482                 return MULTI_XFER_TIMEDOUT;
483         }
484
485         // if the xfer has failed for one reason or another (not timeout)
486         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_FAIL){
487                 return MULTI_XFER_FAIL;
488         }
489
490         // if the xfer has succeeded
491         if(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_SUCCESS){
492                 return MULTI_XFER_SUCCESS;
493         }
494
495         // if the xfer is queued
496         if((Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE) && !(Multi_xfer_entry[handle].flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
497                 return MULTI_XFER_QUEUED;
498         }
499
500         // otherwise the xfer is still in progress
501         return MULTI_XFER_IN_PROGRESS;
502 }
503
504 // abort a transferring file
505 void multi_xfer_abort(int handle)
506 {
507         xfer_entry *xe;
508
509         // don't do anything if this is an invalid handle
510         if(MULTI_XFER_INVALID_HANDLE(handle)){
511                 return;
512         }
513
514         // get e handle to the entry
515         xe = &Multi_xfer_entry[handle];
516
517         // close any open file and delete it 
518         if(xe->file != NULL){
519                 cfclose(xe->file);
520                 xe->file = NULL;
521
522                 // delete it if there isn't some problem with the filename
523                 if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
524                         cf_delete(xe->ex_filename, xe->force_dir);
525                 }
526         }
527
528         // zero the socket
529         xe->file_socket = INVALID_SOCKET;
530
531         // blast the entry
532         memset(xe,0,sizeof(xfer_entry));
533 }
534
535 // release an xfer handle
536 void multi_xfer_release_handle(int handle)
537 {
538         xfer_entry *xe;
539
540         // don't do anything if this is an invalid handle
541         if(MULTI_XFER_INVALID_HANDLE(handle)){
542                 return;
543         }
544
545         // get e handle to the entry
546         xe = &Multi_xfer_entry[handle];
547
548         // close any open file and delete it 
549         if(xe->file != NULL){
550                 cfclose(xe->file);
551                 xe->file = NULL;
552
553                 // delete it if the file was not successfully received
554                 if(!(xe->flags & MULTI_XFER_FLAG_SUCCESS) && (xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
555                         cf_delete(xe->ex_filename,xe->force_dir);
556                 }
557         }
558
559         // zero the socket
560         xe->file_socket = INVALID_SOCKET;       
561
562         // blast the entry
563         memset(xe,0,sizeof(xfer_entry));
564 }
565
566 // get the filename of the xfer for the given handle
567 char *multi_xfer_get_filename(int handle)
568 {
569         // if this is an invalid handle, return NULL
570         if(MULTI_XFER_INVALID_HANDLE(handle)){
571                 return NULL;
572         }
573
574         // otherwise return the string
575         return Multi_xfer_entry[handle].filename;
576 }
577
578 // lock the xfer system (don't accept incoming files, don't allow outgoing files)
579 void multi_xfer_lock()
580 {
581         Multi_xfer_locked = 1;
582 }
583
584 // unlock the xfer system
585 void multi_xfer_unlock()
586 {
587         Multi_xfer_locked = 0;
588 }
589
590 // force all receives to go into the specified directory by cfile type
591 void multi_xfer_force_dir(int cf_type)
592 {
593         Multi_xfer_force_dir = cf_type;
594         SDL_assert(Multi_xfer_force_dir > CF_TYPE_ANY);
595 }
596
597 // forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function)
598 void multi_xfer_handle_force_dir(int handle,int cf_type)
599 {
600         // if this is an invalid handle, return NULL
601         if(MULTI_XFER_INVALID_HANDLE(handle)){
602                 return;
603         }
604
605         // force to go to the given directory
606         Multi_xfer_entry[handle].force_dir = cf_type;
607         SDL_assert(Multi_xfer_entry[handle].force_dir > CF_TYPE_ANY);
608 }
609
610 // or the flag on a given entry
611 void multi_xfer_xor_flags(int handle,int flags)
612 {
613         // if this is an invalid handle, return NULL
614         if(MULTI_XFER_INVALID_HANDLE(handle)){
615                 return;
616         }
617
618         // xor the flags
619         Multi_xfer_entry[handle].flags ^= flags;
620 }
621
622 // get the flags for a given entry
623 int multi_xfer_get_flags(int handle)
624 {
625         // if this is an invalid handle, return NULL
626         if(MULTI_XFER_INVALID_HANDLE(handle)){
627                 return -1;
628         }
629
630         // return the flags
631         return Multi_xfer_entry[handle].flags;
632 }
633
634 // if the passed filename is being xferred, return the xfer handle, otherwise return -1
635 int multi_xfer_lookup(char *filename)
636 {
637         int idx;
638
639         // if we have an invalid filename, do nothing
640         if((filename == NULL) || (strlen(filename) <= 0)){
641                 return 0;
642         }
643
644         // otherwise, perform a lookup
645         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
646                 // if we found a matching filename
647                 if((Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED) && !SDL_strcasecmp(filename,Multi_xfer_entry[idx].filename)){
648                         return idx;
649                 }
650         }
651
652         // did not find a match
653         return -1;
654 }
655
656 // get the % of completion of the passed file handle, return < 0 if invalid
657 float multi_xfer_pct_complete(int handle)
658 {
659         // if this is an invalid handle, return invalid
660         if(MULTI_XFER_INVALID_HANDLE(handle)){
661                 return -1.0f;
662         }
663
664         // if the file size is 0, return invalid
665         if(Multi_xfer_entry[handle].file_size == 0){
666                 return -1.0f;
667         }
668
669         // return the pct completion
670         return (float)Multi_xfer_entry[handle].file_ptr / (float)Multi_xfer_entry[handle].file_size;
671 }
672
673 // get the socket of the file xfer (useful for identifying players)
674 uint multi_xfer_get_sock(int handle)
675 {
676         // if this is an invalid handle, return NULL
677         if(MULTI_XFER_INVALID_HANDLE(handle)){
678                 return INVALID_SOCKET;
679         }
680
681         return Multi_xfer_entry[handle].file_socket;
682 }
683
684 // get the CF_TYPE of the directory this file is going to
685 int multi_xfer_get_force_dir(int handle)
686 {
687         // if this is an invalid handle, return NULL
688         if(MULTI_XFER_INVALID_HANDLE(handle)){
689                 return INVALID_SOCKET;
690         }
691
692         return Multi_xfer_entry[handle].force_dir;
693 }
694
695
696 // ------------------------------------------------------------------------------------------
697 // MULTI XFER FORWARD DECLARATIONS
698 //
699
700 // evaluate the condition of the entry
701 void multi_xfer_eval_entry(xfer_entry *xe)
702 {       
703         int idx;
704         int found;
705         xfer_entry *xe_c;
706
707         // if the entry is marked as successful, then don't do anything
708         if(xe->flags & MULTI_XFER_FLAG_SUCCESS){
709                 return;
710         }
711
712         // if the entry is queued
713         if(xe->flags & MULTI_XFER_FLAG_QUEUE){
714                 // if the entry is not current
715                 if(!(xe->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
716                         // see if there are any other queued up xfers to this target. if not, make me current and start sending
717                         found = 0;
718                         for(idx=0; idx<MAX_XFER_ENTRIES; idx++){
719                                 xe_c = &Multi_xfer_entry[idx];
720
721                                 // if this is a valid entry and is a queued entry and is going to the same target
722                                 if((xe_c->flags & MULTI_XFER_FLAG_USED) && (xe_c->file_socket == xe->file_socket) && (xe_c->flags & MULTI_XFER_FLAG_SEND) && 
723                                         (xe_c->flags & MULTI_XFER_FLAG_QUEUE) && (xe_c->flags & MULTI_XFER_FLAG_QUEUE_CURRENT)){
724                                         
725                                         found = 1;
726                                         break;
727                                 }                               
728                         }
729
730                         // if we found no other entries, make this guy current and pending
731                         if(!found){
732                                 xe->flags |= MULTI_XFER_FLAG_QUEUE_CURRENT;
733                                 xe->flags |= MULTI_XFER_FLAG_PENDING;
734
735 #ifdef MULTI_XFER_VERBOSE
736                                 nprintf(("Network","MULTI_XFER : Starting xfer send for queued entry %s\n", xe->filename));
737 #endif
738                         } 
739                         // otherwise, do nothing for him - he has to still wait
740                         else {
741                                 return;
742                         }
743                 }
744         }
745
746         // if the entry is marked as pending - send out the header to get the ball rolling
747         if(xe->flags & MULTI_XFER_FLAG_PENDING){                
748                 // send the header to begin the transfer
749                 multi_xfer_send_header(xe);
750
751                 // set the timestamp
752                 xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); 
753
754                 // unset the pending flag
755                 xe->flags &= ~(MULTI_XFER_FLAG_PENDING);
756
757                 // set the ack/wait flag
758                 xe->flags |= MULTI_XFER_FLAG_WAIT_ACK;
759         }
760         
761         // see if the entry has timed-out for one reason or another
762         if((xe->xfer_stamp != -1) && timestamp_elapsed(xe->xfer_stamp)){
763                 xe->flags |= MULTI_XFER_FLAG_TIMEOUT;                   
764
765                 // if we should be auto-destroying this entry, do so
766                 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
767                         multi_xfer_fail_entry(xe);                      
768                 }
769         }               
770 }
771
772 // lookup a file xfer entry by player
773 xfer_entry *multi_xfer_find_entry(PSNET_SOCKET_RELIABLE who, ushort sig, int sender_side)
774 {
775         int idx;
776
777         // look through all valid xfer entries
778         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
779                 // if we're looking for sending entries
780                 if(sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_SEND)){
781                         continue;
782                 }
783                 // if we're looking for recv entries
784                 if(!sender_side && !(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_RECV)){
785                         continue;
786                 }
787
788                 // if we found a match
789                 if((Multi_xfer_entry[idx].file_socket == who) && (Multi_xfer_entry[idx].sig == sig)){
790                         return &Multi_xfer_entry[idx];
791                 }
792         }
793
794         return NULL;
795 }
796
797 // set an entry to be "failed"
798 void multi_xfer_fail_entry(xfer_entry *xe)
799 {
800         // set its flags appropriately
801         xe->flags &= ~(MULTI_XFER_FLAG_WAIT_ACK | MULTI_XFER_FLAG_WAIT_DATA | MULTI_XFER_FLAG_UNKNOWN);
802         xe->flags |= MULTI_XFER_FLAG_FAIL;
803
804         // close the file pointer
805         if(xe->file != NULL){
806                 cfclose(xe->file);
807                 xe->file = NULL;
808         }
809
810         // delete the file
811         if((xe->flags & MULTI_XFER_FLAG_RECV) && (strlen(xe->filename) > 0)){
812                 cf_delete(xe->ex_filename,xe->force_dir);
813         }
814                 
815         // null the timestamp
816         xe->xfer_stamp = -1;
817
818         // if we should be auto-destroying this entry, do so
819         if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
820                 multi_xfer_release_handle(xe - Multi_xfer_entry);
821         }
822
823         // blast the memory clean
824         memset(xe,0,sizeof(xfer_entry));
825 }
826
827 // get a valid xfer entry handle
828 int multi_xfer_get_free_handle()
829 {
830         int idx;
831
832         // look for a free entry
833         for(idx=0;idx<MAX_XFER_ENTRIES;idx++){
834                 if(!(Multi_xfer_entry[idx].flags & MULTI_XFER_FLAG_USED)){
835                         return idx;
836                 }
837         }
838
839         return -1;
840 }
841
842
843 // ------------------------------------------------------------------------------------------
844 // MULTI XFER PACKET HANDLERS
845 //
846
847 // process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire
848 // packet regardless of error conditions
849 int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who)
850 {       
851         ubyte val;      
852         xfer_entry *xe;
853         char filename[255];
854         ushort data_size = 0;
855         int file_size = -1;
856         ushort file_checksum = 0;
857         int offset = 0;
858         ubyte xfer_data[600];
859         ushort sig;
860         int sender_side = 1;
861
862         // read in all packet data
863         GET_DATA(val);  
864         GET_USHORT(sig);
865         switch(val){
866         // RECV side
867         case MULTI_XFER_CODE_DATA:                              
868                 GET_USHORT(data_size);          
869                 memcpy(xfer_data, data + offset, data_size);
870                 offset += data_size;
871                 sender_side = 0;
872                 break;
873
874         // RECV side
875         case MULTI_XFER_CODE_HEADER:            
876                 GET_STRING(filename);
877                 GET_INT(file_size);                                     
878                 GET_USHORT(file_checksum);
879                 sender_side = 0;
880                 break;
881
882         // SEND side
883         case MULTI_XFER_CODE_ACK:
884         case MULTI_XFER_CODE_NAK:
885                 break;
886
887         // RECV side
888         case MULTI_XFER_CODE_FINAL:
889                 sender_side = 0;
890                 break;
891
892         default:
893                 Int3();
894         }                       
895
896         // at this point we've read all the data in the packet
897
898         // at this point, we should process code-specific data  
899         xe = NULL;
900         if(val != MULTI_XFER_CODE_HEADER){              
901                 // if the code is not a request or a header, we need to look up the existing xfer_entry
902                 xe = NULL;
903                         
904                 xe = multi_xfer_find_entry(who, sig, sender_side);
905                 if(xe == NULL){                                         
906 #ifdef MULTI_XFER_VERBOSE
907                         nprintf(("Network","MULTI XFER : Could not find xfer entry for incoming data!\n"));
908
909                         // this is a rare case - I'm not overly concerned about it. But it _does_ happen. So blech
910 #ifndef NDEBUG
911                         int np_index = find_player_socket(who);
912                         ml_string("MULTI XFER : Could not find xfer entry for incoming data :");
913                         ml_printf(": sig == %d", sig);
914                         ml_printf(": xfer header == %d", val);
915                         if(np_index < 0){
916                                 ml_string(": player == unknown");
917                         } else {
918                                 ml_printf(": player == %s", Net_players[np_index].player->callsign);
919                         }
920                         if(sender_side){
921                                 ml_string(": sending");
922                         } else {
923                                 ml_string(": receiving");
924                         }
925 #endif
926 #endif
927                 //      Int3();
928                         return offset;
929                 }
930         }
931
932         switch((int)val){
933         // process an ack for this entry
934         case MULTI_XFER_CODE_ACK :
935                 SDL_assert(xe != NULL);
936                 multi_xfer_process_ack(xe);
937                 break;
938         
939         // process a nak for this entry
940         case MULTI_XFER_CODE_NAK :
941                 SDL_assert(xe != NULL);
942                 multi_xfer_process_nak(xe);
943                 break;
944
945         // process a "final" packet
946         case MULTI_XFER_CODE_FINAL :
947                 SDL_assert(xe != NULL);
948                 multi_xfer_process_final(xe);
949                 break;
950
951         // process a data packet
952         case MULTI_XFER_CODE_DATA :
953                 SDL_assert(xe != NULL);
954                 multi_xfer_process_data(xe, xfer_data, data_size);
955                 break;
956         
957         // process a header
958         case MULTI_XFER_CODE_HEADER :
959                 // send on my reliable socket
960                 multi_xfer_process_header(xfer_data, who, sig, filename, file_size, file_checksum);
961                 break;
962         }               
963         return offset;
964 }
965
966 // process an ack for this entry
967 void multi_xfer_process_ack(xfer_entry *xe)
968 {                       
969         // if we are a sender
970         if(xe->flags & MULTI_XFER_FLAG_SEND){
971                 // if we are waiting on a final ack, then the transfer has completed successfully
972                 if(xe->flags & MULTI_XFER_FLAG_UNKNOWN){
973                         xe->flags &= ~(MULTI_XFER_FLAG_UNKNOWN);
974                         xe->flags |= MULTI_XFER_FLAG_SUCCESS;
975
976 #ifdef MULTI_XFER_VERBOSE
977                         nprintf(("Network", "MULTI XFER : Successfully sent file %s\n", xe->filename));
978 #endif
979
980                         // if we should be auto-destroying this entry, do so
981                         if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
982                                 multi_xfer_release_handle(xe - Multi_xfer_entry);
983                         }
984                 } 
985                 // otherwise if we're waiting for an ack, we should send the next chunk of data or a "final" packet if we're done
986                 else if(xe->flags & MULTI_XFER_FLAG_WAIT_ACK){
987                         multi_xfer_send_next(xe);
988                 }
989         }
990 }
991
992 // process a nak for this entry
993 void multi_xfer_process_nak(xfer_entry *xe)
994 {               
995         // if we get an ack at any time we should simply set the xfer to failed
996         multi_xfer_fail_entry(xe);
997 }
998                 
999 // process a "final" packet     
1000 void multi_xfer_process_final(xfer_entry *xe)
1001 {       
1002         ushort chksum;
1003
1004         // make sure we skip a line
1005         nprintf(("Network","\n"));
1006         
1007         // close the file
1008         if(xe->file != NULL){
1009                 cflush(xe->file);
1010                 cfclose(xe->file);
1011                 xe->file = NULL;
1012         }       
1013
1014         // check to make sure the file checksum is the same
1015         chksum = 0;
1016         if(!cf_chksum_short(xe->ex_filename, &chksum, -1, xe->force_dir) || (chksum != xe->file_chksum)){
1017                 // mark as failed
1018                 xe->flags |= MULTI_XFER_FLAG_FAIL;
1019
1020 #ifdef MULTI_XFER_VERBOSE
1021                 nprintf(("Network","MULTI XFER : file %s failed checksum %d %d!\n",xe->ex_filename, (int)xe->file_chksum, (int)chksum));
1022 #endif
1023
1024                 // abort the xfer
1025                 multi_xfer_abort(xe - Multi_xfer_entry);
1026                 return;
1027         }
1028         // checksums check out, so rename the file and be done with it
1029         else {
1030 #ifdef MULTI_XFER_VERBOSE
1031                 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));
1032 #endif
1033                 // rename the file properly
1034                 if(cf_rename(xe->ex_filename,xe->filename, xe->force_dir) == CF_RENAME_SUCCESS){
1035                         // mark the xfer as being successful
1036                         xe->flags |= MULTI_XFER_FLAG_SUCCESS;   
1037
1038                         nprintf(("Network","MULTI XFER : SUCCESSFULLY TRANSFERRED FILE %s (%d bytes)\n", xe->filename, xe->file_size));         
1039
1040                         // send an ack to the sender
1041                         multi_xfer_send_ack(xe->file_socket, xe->sig);
1042                 } else {
1043                         // mark it as failing
1044                         xe->flags |= MULTI_XFER_FLAG_FAIL;
1045                         nprintf(("Network","FAILED TO TRANSFER FILE (could not rename temp file %s)\n", xe->ex_filename));
1046
1047                         // delete the tempfile
1048                         cf_delete(xe->ex_filename, xe->force_dir);
1049
1050                         // send an nak to the sender
1051                         multi_xfer_send_nak(xe->file_socket, xe->sig);
1052                 }
1053
1054                 // if we should be auto-destroying this entry, do so
1055                 if(xe->flags & MULTI_XFER_FLAG_AUTODESTROY){
1056                         multi_xfer_release_handle(xe - Multi_xfer_entry);
1057                 }
1058         }
1059 }
1060
1061 // process a data packet
1062 void multi_xfer_process_data(xfer_entry *xe, ubyte *data, int data_size)        
1063 {                       
1064         // print out a crude progress indicator
1065         nprintf(("Network","."));               
1066
1067         // attempt to write the rest of the data string to the file
1068         if((xe->file == NULL) || !cfwrite(data, data_size, 1, xe->file)){
1069                 // inform the sender we had a problem
1070                 multi_xfer_send_nak(xe->file_socket, xe->sig);
1071
1072                 // fail this entry
1073                 multi_xfer_fail_entry(xe);
1074
1075                 xe->file_ptr += data_size;              
1076                 return;
1077         }
1078
1079         // increment the file pointer
1080         xe->file_ptr += data_size;
1081
1082         // send an ack to the sender
1083         multi_xfer_send_ack(xe->file_socket, xe->sig);
1084
1085         // set the timestmp
1086         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT); 
1087 }
1088         
1089 // process a header, return bytes processed
1090 void multi_xfer_process_header(ubyte *data, PSNET_SOCKET_RELIABLE who, ushort sig, char *filename, int file_size, ushort file_checksum)
1091 {               
1092         xfer_entry *xe;         
1093         int handle;     
1094
1095         // if the xfer system is locked, send a nak
1096         if(Multi_xfer_locked){          
1097                 multi_xfer_send_nak(who, sig);
1098                 return;
1099         }
1100
1101         // try and get a free xfer handle
1102         handle = multi_xfer_get_free_handle();
1103         if(handle == -1){               
1104                 multi_xfer_send_nak(who, sig);
1105                 return;
1106         } else {
1107                 xe = &Multi_xfer_entry[handle];
1108                 memset(xe,0,sizeof(xfer_entry));
1109         }               
1110
1111         // set the recv and used flags
1112         xe->flags |= (MULTI_XFER_FLAG_USED | MULTI_XFER_FLAG_RECV);
1113
1114         // get the header data  
1115         xe->file_size = file_size;
1116
1117         // get the file chksum
1118         xe->file_chksum = file_checksum;        
1119
1120         // set the socket
1121         xe->file_socket = who;  
1122
1123         // set the timeout timestamp
1124         xe->xfer_stamp = timestamp(MULTI_XFER_TIMEOUT);
1125
1126         // set the sig
1127         xe->sig = sig;
1128
1129         // copy the filename and get the prefixed xfer filename
1130         SDL_strlcpy(xe->filename, filename, SDL_arraysize(xe->filename));
1131         // lower case all filenames to avoid case issues
1132         SDL_strlwr(xe->filename);
1133
1134         multi_xfer_conv_prefix(xe->filename, xe->ex_filename, SDL_arraysize(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_USHORT(xe->sig);
1227
1228         // add in the size of the rest of the packet    
1229         ADD_USHORT(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_USHORT(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_USHORT(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_USHORT(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_USHORT(xe->sig);
1324
1325         // add the filename
1326         ADD_STRING(xe->filename);
1327                 
1328         // add the id #
1329         ADD_INT(xe->file_size);
1330
1331         // add the file checksum
1332         ADD_USHORT(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, const int max_len)
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         SDL_strlcpy(temp, MULTI_XFER_FNAME_PREFIX, SDL_arraysize(temp));
1348
1349         // stick on the original name
1350         SDL_strlcat(temp, filename, SDL_arraysize(temp));
1351
1352         // copy the whole thing to the outgoing filename
1353         SDL_strlcpy(ex_filename, temp, max_len);
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 }