Use Linux-like indentation in mptp.c
[swifty.git] / src / libswift / swift.cpp
1 /*
2  *  swift.cpp
3  *  swift the multiparty transport protocol
4  *
5  *  Created by Victor Grishchenko on 2/15/10.
6  *  Copyright 2009-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
7  *
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include "compat.h"
12 #include "swift.h"
13 #include <cfloat>
14
15 using namespace swift;
16
17
18 // Local constants
19 #define RESCAN_DIR_INTERVAL     30 // seconds
20
21
22 // Local prototypes
23 #define quit(...) {fprintf(stderr,__VA_ARGS__); exit(1); }
24 int OpenSwiftFile(const TCHAR* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, uint32_t chunk_size);
25 int OpenSwiftDirectory(const TCHAR* dirname, Address tracker, bool force_check_diskvshash, uint32_t chunk_size);
26
27 void ReportCallback(int fd, short event, void *arg);
28 void EndCallback(int fd, short event, void *arg);
29 void RescanDirCallback(int fd, short event, void *arg);
30 void TimerCallback(int fd, short event, void *arg);
31
32
33 // Gateway stuff
34 bool InstallHTTPGateway(struct event_base *evbase,Address addr,uint32_t chunk_size, double *maxspeed);
35 bool InstallStatsGateway(struct event_base *evbase,Address addr);
36 bool InstallCmdGateway (struct event_base *evbase,Address cmdaddr,Address httpaddr);
37 bool HTTPIsSending();
38 bool StatsQuit();
39 void CmdGwUpdateDLStatesCallback();
40
41
42 // Global variables
43 struct event evreport, evrescan, evend, evtimer;
44 int single_fd = -1;
45 bool file_enable_checkpoint = false;
46 bool file_checkpointed = false;
47 bool report_progress = false;
48 bool quiet=false;
49 bool exitoncomplete=false;
50 bool httpgw_enabled=false,cmdgw_enabled=false;
51 // Gertjan fix
52 bool do_nat_test = false;
53
54 char *scan_dirname = 0;
55 uint32_t chunk_size = SWIFT_DEFAULT_CHUNK_SIZE;
56 Address tracker;
57
58
59 int main (int argc, char** argv)
60 {
61     static struct option long_options[] =
62     {
63         {"hash",    required_argument, 0, 'h'},
64         {"file",    required_argument, 0, 'f'},
65         {"dir",     required_argument, 0, 'd'}, // SEEDDIR reuse
66         {"listen",  required_argument, 0, 'l'},
67         {"tracker", required_argument, 0, 't'},
68         {"debug",   no_argument, 0, 'D'},
69         {"progress",no_argument, 0, 'p'},
70         {"httpgw",  required_argument, 0, 'g'},
71         {"wait",    optional_argument, 0, 'w'},
72         {"nat-test",no_argument, 0, 'N'},
73         {"statsgw", required_argument, 0, 's'}, // SWIFTPROC
74         {"cmdgw",   required_argument, 0, 'c'}, // SWIFTPROC
75         {"destdir", required_argument, 0, 'o'}, // SWIFTPROC
76         {"uprate",  required_argument, 0, 'u'}, // RATELIMIT
77         {"downrate",required_argument, 0, 'y'}, // RATELIMIT
78         {"checkpoint",no_argument, 0, 'H'},
79         {"chunksize",required_argument, 0, 'z'}, // CHUNKSIZE
80         {"printurl",no_argument, 0, 'm'},
81         {0, 0, 0, 0}
82     };
83
84     Sha1Hash root_hash;
85     char* filename = 0;
86     const char *destdir = 0, *trackerargstr = 0; // UNICODE?
87     bool printurl=false;
88     Address bindaddr;
89     Address httpaddr;
90     Address statsaddr;
91     Address cmdaddr;
92     tint wait_time = 0;
93     double maxspeed[2] = {DBL_MAX,DBL_MAX};
94
95     LibraryInit();
96     Channel::evbase = event_base_new();
97
98     int c,n;
99     while ( -1 != (c = getopt_long (argc, argv, ":h:f:d:l:t:D:pg:s:c:o:u:y:z:wBNHm", long_options, 0)) ) {
100         switch (c) {
101             case 'h':
102                 if (strlen(optarg)!=40)
103                     quit("SHA1 hash must be 40 hex symbols\n");
104                 root_hash = Sha1Hash(true,optarg); // FIXME ambiguity
105                 if (root_hash==Sha1Hash::ZERO)
106                     quit("SHA1 hash must be 40 hex symbols\n");
107                 break;
108             case 'f':
109                 filename = strdup(optarg);
110                 break;
111             case 'd':
112                 scan_dirname = strdup(optarg);
113                 break;
114             case 'l':
115                 bindaddr = Address(optarg);
116                 if (bindaddr==Address())
117                     quit("address must be hostname:port, ip:port or just port\n");
118                 wait_time = TINT_NEVER;
119                 break;
120             case 't':
121                 tracker = Address(optarg);
122                 trackerargstr = strdup(optarg); // UNICODE
123                 if (tracker==Address())
124                     quit("address must be hostname:port, ip:port or just port\n");
125                 SetTracker(tracker);
126                 break;
127             case 'D':
128                 Channel::debug_file = optarg ? fopen(optarg,"a") : stderr;
129                 break;
130             // Arno hack: get opt diff Win32 doesn't allow -D without arg
131             case 'B':
132                 fprintf(stderr,"SETTING DEBUG TO STDOUT\n");
133                 Channel::debug_file = stderr;
134                 break;
135             case 'p':
136                 report_progress = true;
137                 break;
138             case 'g':
139                 httpgw_enabled = true;
140                 httpaddr = Address(optarg);
141                                 wait_time = TINT_NEVER; // seed
142                 break;
143             case 'w':
144                 if (optarg) {
145                     char unit = 'u';
146                     if (sscanf(optarg,"%lli%c",&wait_time,&unit)!=2)
147                         quit("time format: 1234[umsMHD], e.g. 1M = one minute\n");
148
149                     switch (unit) {
150                         case 'D': wait_time *= 24;
151                         case 'H': wait_time *= 60;
152                         case 'M': wait_time *= 60;
153                         case 's': wait_time *= 1000;
154                         case 'm': wait_time *= 1000;
155                         case 'u': break;
156                         default:  quit("time format: 1234[umsMHD], e.g. 1D = one day\n");
157                     }
158                 } else
159                     wait_time = TINT_NEVER;
160                 break;
161             case 'N': // Gertjan fix
162                 do_nat_test = true;
163                 break;
164             case 's': // SWIFTPROC
165                 statsaddr = Address(optarg);
166                 if (statsaddr==Address())
167                     quit("address must be hostname:port, ip:port or just port\n");
168                 break;
169             case 'c': // SWIFTPROC
170                 cmdgw_enabled = true;
171                 cmdaddr = Address(optarg);
172                 if (cmdaddr==Address())
173                     quit("address must be hostname:port, ip:port or just port\n");
174                 wait_time = TINT_NEVER; // seed
175                 break;
176             case 'o': // SWIFTPROC
177                 destdir = strdup(optarg); // UNICODE
178                 break;
179             case 'u': // RATELIMIT
180                 n = sscanf(optarg,"%lf",&maxspeed[DDIR_UPLOAD]);
181                 if (n != 1)
182                         quit("uprate must be KiB/s as float\n");
183                 maxspeed[DDIR_UPLOAD] *= 1024.0;
184                 break;
185             case 'y': // RATELIMIT
186                 n = sscanf(optarg,"%lf",&maxspeed[DDIR_DOWNLOAD]);
187                 if (n != 1)
188                         quit("downrate must be KiB/s as float\n");
189                 maxspeed[DDIR_DOWNLOAD] *= 1024.0;
190                 break;
191             case 'H': //CHECKPOINT
192                 file_enable_checkpoint = true;
193                 break;
194             case 'z': // CHUNKSIZE
195                 n = sscanf(optarg,"%i",&chunk_size);
196                 if (n != 1)
197                         quit("chunk size must be bytes as int\n");
198                 break;
199             case 'm': // printurl
200                 printurl = true;
201                 quiet = true;
202                 wait_time = 0;
203                 break;
204         }
205
206     }   // arguments parsed
207
208
209     if (httpgw_enabled)
210     {
211         // Change current directory to a temporary one
212 #ifdef _WIN32
213         if (destdir == 0) {
214                 std::string destdirstr = gettmpdir();
215                 !::SetCurrentDirectory(destdirstr.c_str());
216         }
217         else
218                 !::SetCurrentDirectory(destdir);
219         TCHAR szDirectory[MAX_PATH] = "";
220
221         !::GetCurrentDirectory(sizeof(szDirectory) - 1, szDirectory);
222         fprintf(stderr,"CWD %s\n",szDirectory);
223 #else
224         if (destdir == 0)
225                 chdir(gettmpdir().c_str());
226         else
227                 chdir(destdir);
228 #endif
229     }
230       
231     if (bindaddr!=Address()) { // seeding
232         if (Listen(bindaddr)<=0)
233             quit("cant listen to %s\n",bindaddr.str())
234     } else if (tracker!=Address() || httpgw_enabled || cmdgw_enabled) { // leeching
235         evutil_socket_t sock = INVALID_SOCKET;
236         for (int i=0; i<=10; i++) {
237             bindaddr = Address((uint32_t)INADDR_ANY,0);
238             sock = Listen(bindaddr);
239             if (sock>0)
240                 break;
241             if (i==10)
242                 quit("cant listen on %s\n",bindaddr.str());
243         }
244         if (!quiet)
245                 fprintf(stderr,"swift: My listen port is %d\n", BoundAddress(sock).port() );
246     }
247
248     if (tracker!=Address())
249         SetTracker(tracker);
250
251     if (httpgw_enabled)
252         InstallHTTPGateway(Channel::evbase,httpaddr,chunk_size,maxspeed);
253     if (cmdgw_enabled)
254                 InstallCmdGateway(Channel::evbase,cmdaddr,httpaddr);
255
256     // TRIALM36: Allow browser to retrieve stats via AJAX and as HTML page
257     if (statsaddr != Address())
258         InstallStatsGateway(Channel::evbase,statsaddr);
259
260
261     if (!cmdgw_enabled && !httpgw_enabled)
262     {
263                 int ret = -1;
264                 if (filename || root_hash != Sha1Hash::ZERO) {
265                         // Single file
266                         if (root_hash!=Sha1Hash::ZERO && !filename)
267                                 filename = strdup(root_hash.hex().c_str());
268
269                         single_fd = OpenSwiftFile(filename,root_hash,Address(),false,chunk_size);
270                         if (single_fd < 0)
271                                 quit("cannot open file %s",filename);
272                         if (printurl) {
273                                 if (trackerargstr == NULL)
274                                         trackerargstr = "";
275                                 printf("tswift://%s/%s$%i\n", trackerargstr, RootMerkleHash(single_fd).hex().c_str(), chunk_size);
276
277                                 // Arno, 2012-01-04: LivingLab: Create checkpoint such that content
278                                 // can be copied to scanned dir and quickly loaded
279                                 swift::Checkpoint(single_fd);
280                         }
281                         else
282                                 printf("Root hash: %s\n", RootMerkleHash(single_fd).hex().c_str());
283
284                         // RATELIMIT
285                         FileTransfer *ft = FileTransfer::file(single_fd);
286                         ft->SetMaxSpeed(DDIR_DOWNLOAD,maxspeed[DDIR_DOWNLOAD]);
287                         ft->SetMaxSpeed(DDIR_UPLOAD,maxspeed[DDIR_UPLOAD]);
288
289                         ret = single_fd;
290                 }
291                 else if (scan_dirname != NULL)
292                         ret = OpenSwiftDirectory(scan_dirname,Address(),false,chunk_size);
293                 else
294                         ret = -1;
295
296                 // No file/dir nor HTTP gateway nor CMD gateway, will never know what to swarm
297                 if (ret == -1) {
298                         fprintf(stderr,"Usage:\n");
299                         fprintf(stderr,"  -h, --hash\troot Merkle hash for the transmission\n");
300                         fprintf(stderr,"  -f, --file\tname of file to use (root hash by default)\n");
301                         fprintf(stderr,"  -l, --listen\t[ip:|host:]port to listen to (default: random)\n");
302                         fprintf(stderr,"  -t, --tracker\t[ip:|host:]port of the tracker (default: none)\n");
303                         fprintf(stderr,"  -D, --debug\tfile name for debugging logs (default: stdout)\n");
304                         fprintf(stderr,"  -B\tdebugging logs to stdout (win32 hack)\n");
305                         fprintf(stderr,"  -p, --progress\treport transfer progress\n");
306                         fprintf(stderr,"  -g, --httpgw\t[ip:|host:]port to bind HTTP content gateway to (no default)\n");
307                         fprintf(stderr,"  -s, --statsgw\t[ip:|host:]port to bind HTTP stats listen socket to (no default)\n");
308                         fprintf(stderr,"  -c, --cmdgw\t[ip:|host:]port to bind CMD listen socket to (no default)\n");
309                         fprintf(stderr,"  -o, --destdir\tdirectory for saving data (default: none)\n");
310                         fprintf(stderr,"  -u, --uprate\tupload rate limit in KiB/s (default: unlimited)\n");
311                         fprintf(stderr,"  -y, --downrate\tdownload rate limit in KiB/s (default: unlimited)\n");
312                         fprintf(stderr,"  -w, --wait\tlimit running time, e.g. 1[DHMs] (default: infinite with -l, -g)\n");
313                         fprintf(stderr,"  -H, --checkpoint\tcreate checkpoint of file when complete for fast restart\n");
314                         fprintf(stderr,"  -z, --chunksize\tchunk size in bytes (default: %d)\n", SWIFT_DEFAULT_CHUNK_SIZE);
315                         fprintf(stderr,"  -m, --printurl\tcompose URL from tracker, file and chunksize\n");
316                         return 1;
317                 }
318     }
319
320     // Arno, 2012-01-04: Allow download and quit mode
321     if (single_fd != -1 && root_hash != Sha1Hash::ZERO && wait_time == 0) {
322         wait_time = TINT_NEVER;
323         exitoncomplete = true;
324     }
325
326     // End after wait_time
327     if ((long)wait_time > 0) {
328         evtimer_assign(&evend, Channel::evbase, EndCallback, NULL);
329         evtimer_add(&evend, tint2tv(wait_time));
330     }
331
332         evtimer_assign(&evtimer, Channel::evbase, TimerCallback, NULL);
333         evtimer_add(&evtimer, tint2tv(TIMER_USEC));
334
335     // Enter mainloop, if daemonizing
336     if (wait_time == TINT_NEVER || (long)wait_time > 0) {
337                 // Arno: always, for statsgw, rate control, etc.
338                 evtimer_assign(&evreport, Channel::evbase, ReportCallback, NULL);
339                 evtimer_add(&evreport, tint2tv(TINT_SEC));
340
341
342                 // Arno:
343                 if (scan_dirname != NULL) {
344                         evtimer_assign(&evrescan, Channel::evbase, RescanDirCallback, NULL);
345                         evtimer_add(&evrescan, tint2tv(RESCAN_DIR_INTERVAL*TINT_SEC));
346                 }
347
348
349                 fprintf(stderr,"swift: Mainloop\n");
350                 // Enter libevent mainloop
351                 event_base_dispatch(Channel::evbase);
352
353                 // event_base_loopexit() was called, shutting down
354     }
355
356     // Arno, 2012-01-03: Close all transfers
357         for (int i=0; i<FileTransfer::files.size(); i++) {
358                 if (FileTransfer::files[i] != NULL)
359             Close(FileTransfer::files[i]->fd());
360     }
361
362     if (Channel::debug_file)
363         fclose(Channel::debug_file);
364
365     swift::Shutdown();
366
367     return 0;
368 }
369
370
371
372 int OpenSwiftFile(const TCHAR* filename, const Sha1Hash& hash, Address tracker, bool force_check_diskvshash, uint32_t chunk_size)
373 {
374         std::string bfn;
375         bfn.assign(filename);
376         bfn.append(".mbinmap");
377         const char *binmap_filename = bfn.c_str();
378
379         // Arno, 2012-01-03: Hack to discover root hash of a file on disk, such that
380         // we don't load it twice while rescanning a dir of content.
381         HashTree *ht = new HashTree(true,binmap_filename);
382
383         //      fprintf(stderr,"swift: parsedir: File %s may have hash %s\n", filename, ht->root_hash().hex().c_str() );
384
385         int fd = swift::Find(ht->root_hash());
386         if (fd == -1) {
387                 if (!quiet)
388                         fprintf(stderr,"swift: parsedir: Opening %s\n", filename);
389
390                 fd = swift::Open(filename,hash,tracker,force_check_diskvshash,true,chunk_size);
391         }
392         else if (!quiet)
393                 fprintf(stderr,"swift: parsedir: Ignoring loaded %s\n", filename);
394         return fd;
395 }
396
397
398 int OpenSwiftDirectory(const TCHAR* dirname, Address tracker, bool force_check_diskvshash, uint32_t chunk_size)
399 {
400 #ifdef _WIN32
401         HANDLE hFind;
402         WIN32_FIND_DATA FindFileData;
403
404         TCHAR pathsearch[MAX_PATH];
405         strcpy(pathsearch,dirname);
406         strcat(pathsearch,"\\*.*");
407         hFind = FindFirstFile(pathsearch, &FindFileData);
408         if(hFind != INVALID_HANDLE_VALUE) {
409             do {
410                 //fprintf(stderr,"swift: parsedir: %s\n", FindFileData.cFileName);
411                 if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 && !strstr(FindFileData.cFileName,".mhash") && !strstr(FindFileData.cFileName,".mbinmap") ) {
412                                 TCHAR path[MAX_PATH];
413                                 strcpy(path,dirname);
414                                 strcat(path,"\\");
415                                 strcat(path,FindFileData.cFileName);
416
417                                 int fd = OpenSwiftFile(path,Sha1Hash::ZERO,tracker,force_check_diskvshash,chunk_size);
418                                 if (fd >= 0)
419                                         Checkpoint(fd);
420                 }
421             } while(FindNextFile(hFind, &FindFileData));
422
423             FindClose(hFind);
424             return 1;
425         }
426         else
427                 return -1;
428 #else
429         DIR *dirp = opendir(dirname);
430         if (dirp == NULL)
431                 return -1;
432
433         while(1)
434         {
435                 struct dirent *de = readdir(dirp);
436                 if (de == NULL)
437                         break;
438                 if ((de->d_type & DT_DIR) !=0 || strstr(de->d_name,".mhash") || strstr(de->d_name,".mbinmap"))
439                         continue;
440                 char path[PATH_MAX];
441                 strcpy(path,dirname);
442                 strcat(path,"/");
443                 strcat(path,de->d_name);
444
445                 int fd = OpenSwiftFile(path,Sha1Hash::ZERO,tracker,force_check_diskvshash,chunk_size);
446                 if (fd >= 0)
447                         Checkpoint(fd);
448         }
449         closedir(dirp);
450         return 1;
451 #endif
452 }
453
454
455
456 int CleanSwiftDirectory(const TCHAR* dirname)
457 {
458         std::set<int>   delset;
459         std::vector<FileTransfer*>::iterator iter;
460         for (iter=FileTransfer::files.begin(); iter!=FileTransfer::files.end(); iter++)
461         {
462                 FileTransfer *ft = *iter;
463                 if (ft != NULL) {
464                         std::string filename = ft->file().filename();
465 #ifdef WIN32
466                         struct _stat buf;
467 #else
468                         struct stat buf;
469 #endif
470                         fprintf(stderr,"swift: clean: Checking %s\n", filename.c_str() );
471                         int res = stat( filename.c_str(), &buf );
472                         if( res < 0 && errno == ENOENT) {
473                                 fprintf(stderr,"swift: clean: Missing %s\n", filename.c_str() );
474                                 delset.insert(ft->fd());
475                         }
476                 }
477         }
478
479         std::set<int>::iterator iiter;
480         for (iiter=delset.begin(); iiter!=delset.end(); iiter++)
481         {
482                 int fd = *iiter;
483                 fprintf(stderr,"swift: clean: Deleting transfer %d\n", fd );
484                 swift::Close(fd);
485         }
486
487         return 1;
488 }
489
490
491
492
493
494 void ReportCallback(int fd, short event, void *arg) {
495         // Called every second to print/calc some stats
496
497         if (single_fd  >= 0)
498         {
499                 if (report_progress) {
500                         fprintf(stderr,
501                                 "%s %lli of %lli (seq %lli) %lli dgram %lli bytes up, " \
502                                 "%lli dgram %lli bytes down mptp[send:%lli,%lli;recv:%lli,%lli]\n",
503                                 IsComplete(single_fd ) ? "DONE" : "done",
504                                 Complete(single_fd), Size(single_fd), SeqComplete(single_fd),
505                                 Channel::global_dgrams_up, Channel::global_raw_bytes_up,
506                                 Channel::global_dgrams_down, Channel::global_raw_bytes_down,
507                                 Channel::global_buffers_up, Channel::global_syscalls_up,
508                                 Channel::global_buffers_down, Channel::global_syscalls_down);
509                 }
510
511         FileTransfer *ft = FileTransfer::file(single_fd);
512         if (report_progress) { // TODO: move up
513                 fprintf(stderr,"upload %lf\n",ft->GetCurrentSpeed(DDIR_UPLOAD));
514                 fprintf(stderr,"dwload %lf\n",ft->GetCurrentSpeed(DDIR_DOWNLOAD) );
515                 //fprintf(stderr,"npeers %d\n",ft->GetNumLeechers()+ft->GetNumSeeders() );
516         }
517         // Update speed measurements such that they decrease when DL/UL stops
518         // Always
519         ft->OnRecvData(0);
520         ft->OnSendData(0);
521
522         // CHECKPOINT
523         if (file_enable_checkpoint && !file_checkpointed && IsComplete(single_fd))
524         {
525                 std::string binmap_filename = ft->file().filename();
526                 binmap_filename.append(".mbinmap");
527                 fprintf(stderr,"swift: Complete, checkpointing %s\n", binmap_filename.c_str() );
528                 FILE *fp = fopen(binmap_filename.c_str(),"wb");
529                 if (!fp) {
530                         print_error("cannot open mbinmap for writing");
531                         return;
532                 }
533                 if (ft->file().serialize(fp) < 0)
534                         print_error("writing to mbinmap");
535                 else
536                         file_checkpointed = true;
537                 fclose(fp);
538         }
539
540
541         if (exitoncomplete && IsComplete(single_fd))
542                 // Download and stop mode
543             event_base_loopexit(Channel::evbase, NULL);
544
545         }
546     if (httpgw_enabled)
547     {
548         fprintf(stderr,".");
549
550         // ARNOSMPTODO: Restore fail behaviour when used in SwarmPlayer 3000.
551         if (!HTTPIsSending()) {
552                 // TODO
553                 //event_base_loopexit(Channel::evbase, NULL);
554             return;
555         }
556     }
557     if (StatsQuit())
558     {
559         // SwarmPlayer 3000: User click "Quit" button in webUI.
560         struct timeval tv;
561         tv.tv_sec = 1;
562         int ret = event_base_loopexit(Channel::evbase,&tv);
563     }
564         // SWIFTPROC
565         // ARNOSMPTODO: SCALE: perhaps less than once a second if many swarms
566         CmdGwUpdateDLStatesCallback();
567
568         // Gertjan fix
569         // Arno, 2011-10-04: Temp disable
570     //if (do_nat_test)
571     //     nat_test_update();
572
573         evtimer_add(&evreport, tint2tv(TINT_SEC));
574 }
575
576 void TimerCallback(int fd, short event, void *arg) {
577         Channel::messageQueue.Flush();
578         evtimer_add(&evtimer, tint2tv(TIMER_USEC));
579 }
580
581 void EndCallback(int fd, short event, void *arg) {
582         // Called when wait timer expires == fixed time daemon
583     event_base_loopexit(Channel::evbase, NULL);
584 }
585
586
587 void RescanDirCallback(int fd, short event, void *arg) {
588
589         // SEEDDIR
590         // Rescan dir: CAREFUL: this is blocking, better prepare .m* files first
591         // by running swift separately and then copy content + *.m* to scanned dir,
592         // such that a fast restore from checkpoint is done.
593         //
594         OpenSwiftDirectory(scan_dirname,tracker,false,chunk_size);
595
596         CleanSwiftDirectory(scan_dirname);
597
598         evtimer_add(&evrescan, tint2tv(RESCAN_DIR_INTERVAL*TINT_SEC));
599 }
600
601
602
603 #ifdef _WIN32
604 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
605 {
606     return main(__argc,__argv);
607 }
608 #endif
609