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