00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00149
00150
00151
00152
00153 #include <stdio.h>
00154 #include <stdlib.h>
00155 #include <stdarg.h>
00156 #include <string.h>
00157 #include <time.h>
00158 #include <ctype.h>
00159 #include <sstream>
00160 #include <algorithm>
00161
00162 #ifndef _MSC_VER
00163 #include <unistd.h>
00164 #endif
00165 #ifdef __CYGWIN__
00166 #include <getopt.h>
00167 #endif
00168 #if defined(_MSC_VER) || defined(__MINGW32__)
00169 extern char *optarg;
00170 extern int optind;
00171 extern int getopt(int argc, char *const *argv, const char *opt);
00172 #endif
00173
00174 #ifdef MAC_BUNDLE
00175 #define Button ButtonOSX
00176 #include <Carbon/Carbon.h>
00177 #undef Button
00178 #endif
00179
00180 #include "SDL.h"
00181
00182 #include "stratagus.h"
00183 #include "unit_manager.h"
00184 #include "video.h"
00185 #include "font.h"
00186 #include "cursor.h"
00187 #include "ui.h"
00188 #include "interface.h"
00189 #include "menus.h"
00190 #include "sound_server.h"
00191 #include "sound.h"
00192 #include "settings.h"
00193 #include "script.h"
00194 #include "network.h"
00195 #include "netconnect.h"
00196 #include "ai.h"
00197 #include "missile.h"
00198 #include "replay.h"
00199 #include "results.h"
00200 #include "editor.h"
00201 #include "movie.h"
00202 #include "pathfinder.h"
00203 #include "widgets.h"
00204 #include "trigger.h"
00205 #include "iolib.h"
00206 #include "iocompat.h"
00207 #include "guichan.h"
00208 #include "version.h"
00209 #include "title.h"
00210 #include "map.h"
00211
00212
00213 #if defined(_MSC_VER) && SDL_VERSION_ATLEAST(1, 2, 13)
00214 #define REDIRECT_OUTPUT
00215 #endif
00216
00217 extern void CreateUserDirectories(void);
00218
00219
00220
00221
00222
00223 std::string StratagusLibPath;
00224 std::string LocalPlayerName;
00225
00227 static std::string NameLine =
00228 "Bos Wars V" VERSION ", (c) 1998-2008 by the Bos Wars and Stratagus Project.";
00229
00230 std::string CliMapName;
00231 std::string CompileOptions;
00232
00233 static std::vector<gcn::Container *> Containers;
00234
00235
00236
00237
00238
00239 int SpeedBuild = 1;
00240 int SpeedTrain = 1;
00241
00242
00243
00244
00245
00246 unsigned long GameCycle;
00247 unsigned long FastForwardCycle;
00248
00249
00250
00251
00252
00258 void ShowLoadProgress(const char *fmt, ...)
00259 {
00260 va_list va;
00261 char temp[4096];
00262
00263 va_start(va, fmt);
00264 vsnprintf(temp, sizeof(temp) - 1, fmt, va);
00265 temp[sizeof(temp) - 1] = '\0';
00266 va_end(va);
00267
00268 if (Video.Depth && GameFont && GameFont->IsLoaded()) {
00269
00270 for (char *s = temp; *s; ++s) {
00271 if (*s < 32) {
00272 *s = ' ';
00273 }
00274 }
00275 Video.FillRectangle(ColorBlack, 5, Video.Height - 18, Video.Width - 10, 18);
00276 VideoDrawTextCentered(Video.Width / 2, Video.Height - 16, GameFont, temp);
00277 InvalidateArea(5, Video.Height - 18, Video.Width - 10, 18);
00278 RealizeVideoMemory();
00279 } else {
00280 DebugPrint("!!!!%s\n" _C_ temp);
00281 }
00282 }
00283
00284
00285
00289 void PreMenuSetup(void)
00290 {
00291
00292
00293
00294 SetDefaultTextColors(FontYellow, FontWhite);
00295
00296 LoadFonts();
00297
00298 InitVideoCursors();
00299
00300 LoadCursors();
00301 InitSettings();
00302
00303 InitUserInterface();
00304 UI.Load();
00305 }
00306
00312 static int MenuLoop()
00313 {
00314 char buf[1024];
00315 int status;
00316
00317 initGuichan();
00318 InterfaceState = IfaceStateMenu;
00319
00320 Video.ClearScreen();
00321 Invalidate();
00322
00323 ButtonUnderCursor = -1;
00324 CursorState = CursorStatePoint;
00325 GameCursor = UI.Point.Cursor;
00326
00327
00328 LibraryFileName("scripts/guichan.lua", buf, sizeof(buf));
00329 status = LuaLoadFile(buf);
00330
00331
00332
00333 return status;
00334 }
00335
00343 void CleanGame(void)
00344 {
00345 EndReplayLog();
00346 CleanMessages();
00347
00348 CleanTriggers();
00349 CleanUnits();
00350 CleanAi();
00351 CleanSelections();
00352 CleanGroups();
00353 CleanMissiles();
00354 Map.Clean();
00355 CleanReplayLog();
00356 FreeVisionTable();
00357 FreePathfinder();
00358 CursorBuilding = NULL;
00359 UnitUnderCursor = NULL;
00360 }
00361
00362 static void ExpandPath(std::string &newpath, const std::string &path)
00363 {
00364 if (path[0] == '~') {
00365 newpath = UserDirectory + path.substr(1);
00366 } else {
00367 newpath = StratagusLibPath + "/" + path;
00368 }
00369 }
00370
00371 extern gcn::Gui *Gui;
00372
00373 void StartMap(const std::string &filename, bool clean)
00374 {
00375 std::string nc, rc;
00376
00377 gcn::Widget *oldTop = Gui->getTop();
00378 gcn::Container *container = new gcn::Container();
00379 Containers.push_back(container);
00380 container->setDimension(gcn::Rectangle(0, 0, Video.Width, Video.Height));
00381 container->setOpaque(false);
00382 Gui->setTop(container);
00383
00384 NetConnectRunning = 0;
00385 InterfaceState = IfaceStateNormal;
00386
00387
00388 DebugPrint("Creating game with map: %s\n" _C_ filename.c_str());
00389 if (clean) {
00390 CleanPlayers();
00391 }
00392 GetDefaultTextColors(nc, rc);
00393
00394 CreateGame(filename, &Map);
00395
00396
00397 GameMainLoop();
00398
00399
00400 Video.ClearScreen();
00401 Invalidate();
00402
00403 CleanGame();
00404 InterfaceState = IfaceStateMenu;
00405 SetDefaultTextColors(nc, rc);
00406
00407 Gui->setTop(oldTop);
00408 Containers.erase(std::find(Containers.begin(), Containers.end(), container));
00409 delete container;
00410 }
00411
00412 void StartSavedGame(const std::string &filename)
00413 {
00414 std::string path;
00415
00416 SaveGameLoading = true;
00417 CleanPlayers();
00418 ExpandPath(path, filename);
00419 LoadGame(path);
00420
00421 StartMap(filename, false);
00422 }
00423
00424 void StartReplay(const std::string &filename, bool reveal)
00425 {
00426 std::string replay;
00427
00428 CleanPlayers();
00429 ExpandPath(replay, filename);
00430 LoadReplay(replay);
00431
00432 ReplayRevealMap = reveal;
00433
00434 StartMap(CurrentMapPath, false);
00435 }
00436
00444 int SaveReplay(const std::string &filename)
00445 {
00446 FILE *fd;
00447 char *buf;
00448 std::ostringstream logfile;
00449 std::string destination;
00450 struct stat sb;
00451
00452 if (filename.find_first_of("\\/") != std::string::npos) {
00453 fprintf(stderr, "\\ or / not allowed in SaveReplay filename\n");
00454 return -1;
00455 }
00456
00457 destination = UserDirectory + "logs/" + filename;
00458
00459 logfile << UserDirectory << "logs/log_of_stratagus_" << ThisPlayer->Index << ".log";
00460
00461 if (stat(logfile.str().c_str(), &sb)) {
00462 fprintf(stderr, "stat failed\n");
00463 return -1;
00464 }
00465 buf = new char[sb.st_size];
00466 if (!buf) {
00467 fprintf(stderr, "Out of memory\n");
00468 return -1;
00469 }
00470 fd = fopen(logfile.str().c_str(), "rb");
00471 if (!fd) {
00472 fprintf(stderr, "fopen failed\n");
00473 delete[] buf;
00474 return -1;
00475 }
00476 fread(buf, sb.st_size, 1, fd);
00477 fclose(fd);
00478
00479 fd = fopen(destination.c_str(), "wb");
00480 if (!fd) {
00481 fprintf(stderr, "Can't save to `%s'\n", destination.c_str());
00482 delete[] buf;
00483 return -1;
00484 }
00485 fwrite(buf, sb.st_size, 1, fd);
00486 fclose(fd);
00487
00488 delete[] buf;
00489
00490 return 0;
00491 }
00492
00493
00494
00498 static void PrintHeader(void)
00499 {
00500 fprintf(stdout, "%s\n written by Lutz Sammer, Fabrice Rossi, Vladi Shabanski, Patrice Fortier,\n"
00501 "Jon Gabrielson, Andreas Arens, Nehal Mistry, Jimmy Salmon, Francois Beerten and others.\n"
00502 "\t(http://www.boswars.org)"
00503 "\nCompile options %s", NameLine.c_str(), CompileOptions.c_str());
00504 }
00505
00512 static int main1(int argc, char **argv)
00513 {
00514 PrintHeader();
00515 printf(
00516 "\n"
00517 "\n"
00518 "Bos Wars may be copied only under the terms of the GNU General Public License\n"
00519 "which must be distributed with this program.\n"
00520 "\n"
00521 "DISCLAIMER:\n"
00522 "This software is provided as-is. The author(s) can not be held liable for any\n"
00523 "damage that might arise from the use of this software.\n"
00524 "Use it at your own risk.\n"
00525 "\n");
00526
00527
00528
00529
00530 if (!InitSound()) {
00531 InitMusic();
00532 }
00533
00534 #ifndef DEBUG // For debug it's better not to have:
00535 srand(time(NULL));
00536 #endif
00537
00538
00539
00540
00541 SetDefaultTextColors(FontYellow, FontWhite);
00542 LoadFonts();
00543 SetClipping(0, 0, Video.Width - 1, Video.Height - 1);
00544 Video.ClearScreen();
00545 ShowTitleScreens();
00546
00547
00548 ThisPlayer = NULL;
00549
00550
00551 NumPlayers = 0;
00552
00553 UnitManager.Init();
00554 PreMenuSetup();
00555
00556 MenuLoop();
00557
00558 return 0;
00559 }
00560
00566 void Exit(int err)
00567 {
00568 StopMusic();
00569 QuitSound();
00570 NetworkQuit();
00571
00572 ExitNetwork1();
00573 #ifdef DEBUG
00574 CleanModules();
00575 FreeBurningBuildingFrames();
00576 FreeSounds();
00577 FreeGraphics();
00578 FreePlayerColors();
00579 FreeButtonStyles();
00580 for (size_t i = 0; i < Containers.size(); ++i) {
00581 delete Containers[i];
00582 }
00583 freeGuichan();
00584 DebugPrint("Frames %lu, Slow frames %d = %ld%%\n" _C_
00585 FrameCounter _C_ SlowFrameCounter _C_
00586 (SlowFrameCounter * 100) / (FrameCounter ? FrameCounter : 1));
00587 lua_settop(Lua, 0);
00588 lua_close(Lua);
00589 #endif
00590
00591 fprintf(stdout, _("Thanks for playing Bos Wars.\n"));
00592 exit(err);
00593 }
00594
00601 void ExitFatal(int err)
00602 {
00603 exit(err);
00604 }
00605
00609 static void Usage(void)
00610 {
00611 PrintHeader();
00612 printf(
00613 "\n\nUsage: boswars [OPTIONS]\n\
00614 \t-c file.lua\tconfiguration start file (default boswars.lua)\n\
00615 \t-d datapath\tpath to Bos Wars data\n\
00616 \t-h\t\tHelp shows this page\n\
00617 \t-l\t\tDisable command log\n\
00618 \t-P port\t\tNetwork port to use\n\
00619 \t-n server\tNetwork server host preset\n\
00620 \t-L lag\t\tNetwork lag in # frames (default 10 = 333ms)\n\
00621 \t-U update\tNetwork update rate in # frames (default 5=6x per s)\n\
00622 \t-s sleep\tNumber of frames for the AI to sleep before it starts\n\
00623 \t-v mode\t\tVideo mode (0=default,1=640x480,2=800x600,\n\
00624 \t\t\t\t3=1024x768,4=1280x960,5=1600x1200)\n\
00625 \t-D\t\tVideo mode depth = pixel per point (for Win32/TNT)\n\
00626 \t-F\t\tFull screen video mode\n\
00627 \t-S\t\tSync speed (100 = 30 frames/s)\n\
00628 \t-W\t\tWindowed video mode\n\
00629 ");
00630 }
00631
00632 #ifdef REDIRECT_OUTPUT
00633
00634 static std::string stdoutFile;
00635 static std::string stderrFile;
00636
00637 static void CleanupOutput()
00638 {
00639 fclose(stdout);
00640 fclose(stderr);
00641
00642 struct stat st;
00643 if (stat(stdoutFile.c_str(), &st) == 0 && st.st_size == 0) {
00644 unlink(stdoutFile.c_str());
00645 }
00646 if (stat(stderrFile.c_str(), &st) == 0 && st.st_size == 0) {
00647 unlink(stderrFile.c_str());
00648 }
00649 }
00650
00651 static void RedirectOutput()
00652 {
00653 char path[MAX_PATH];
00654 int pathlen;
00655
00656 pathlen = GetModuleFileName(NULL, path, sizeof(path));
00657 while (pathlen > 0 && path[pathlen] != '\\') {
00658 --pathlen;
00659 }
00660 path[pathlen] = '\0';
00661
00662 stdoutFile = std::string(path) + "\\stdout.txt";
00663 stderrFile = std::string(path) + "\\stderr.txt";
00664
00665 if (!freopen(stdoutFile.c_str(), "w", stdout)) {
00666 printf("freopen stdout failed");
00667 }
00668 if (!freopen(stderrFile.c_str(), "w", stderr)) {
00669 printf("freopen stderr failed");
00670 }
00671 atexit(CleanupOutput);
00672 }
00673 #endif
00674
00681 int main(int argc, char **argv)
00682 {
00683 #ifdef REDIRECT_OUTPUT
00684 RedirectOutput();
00685 #endif
00686
00687 CompileOptions =
00688 #ifdef DEBUG
00689 "DEBUG "
00690 #endif
00691 #ifdef USE_VORBIS
00692 "VORBIS "
00693 #endif
00694 #ifdef USE_THEORA
00695 "THEORA "
00696 #endif
00697 ""
00698 ;
00699
00700
00701
00702
00703 #ifndef MAC_BUNDLE
00704 StratagusLibPath = STRATAGUS_LIB_PATH;
00705 #else
00706 freopen("/tmp/stdout.txt", "w", stdout);
00707 freopen("/tmp/stderr.txt", "w", stderr);
00708
00709
00710 CFURLRef pluginRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
00711 CFSTR(MAC_BUNDLE_DATADIR), NULL, NULL);
00712 CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef,
00713 kCFURLPOSIXPathStyle);
00714 const char *pathPtr = CFStringGetCStringPtr(macPath,
00715 CFStringGetSystemEncoding());
00716 Assert(pathPtr);
00717 StratagusLibPath = pathPtr;
00718 #endif
00719 CclStartFile = "scripts/boswars.lua";
00720 EditorStartFile = "scripts/editor.lua";
00721
00722
00723 LocalPlayerName.clear();
00724 #ifdef USE_WIN32
00725 LocalPlayerName = "Anonymous";
00726 #else
00727 if (getenv("USER")) {
00728 LocalPlayerName = getenv("USER");
00729 } else {
00730 LocalPlayerName = "Anonymous";
00731 }
00732 #endif
00733
00734
00735
00736
00737
00738
00739 for (;;) {
00740 switch (getopt(argc, argv, "c:d:hln:P:s:v:D:N:E:FL:S:U:W?")) {
00741 case 'c':
00742 CclStartFile = optarg;
00743 continue;
00744 case 'd':
00745 {
00746 StratagusLibPath = optarg;
00747 size_t index;
00748 while ((index = StratagusLibPath.find('\\')) != std::string::npos) {
00749 StratagusLibPath[index] = '/';
00750 }
00751 continue;
00752 }
00753 case 'E':
00754 EditorStartFile = optarg;
00755 continue;
00756 case 'l':
00757 CommandLogDisabled = true;
00758 continue;
00759 case 'P':
00760 NetworkPort = atoi(optarg);
00761 continue;
00762 case 'n':
00763 NetworkArg = optarg;
00764 continue;
00765 case 'N':
00766 LocalPlayerName = optarg;
00767 continue;
00768 case 's':
00769 AiSleepCycles = atoi(optarg);
00770 continue;
00771 case 'v':
00772 switch (atoi(optarg)) {
00773 case 0:
00774 continue;
00775 case 1:
00776 Video.Width = 640;
00777 Video.Height = 480;
00778 continue;
00779 case 2:
00780 Video.Width = 800;
00781 Video.Height = 600;
00782 continue;
00783 case 3:
00784 Video.Width = 1024;
00785 Video.Height = 768;
00786 continue;
00787 case 4:
00788 Video.Width = 1280;
00789 Video.Height = 960;
00790 continue;
00791 case 5:
00792 Video.Width = 1600;
00793 Video.Height = 1200;
00794 continue;
00795 default:
00796 Usage();
00797 ExitFatal(-1);
00798 }
00799 continue;
00800
00801 case 'L':
00802 NetworkLag = atoi(optarg);
00803 if (!NetworkLag) {
00804 fprintf(stderr, "FIXME: zero lag not supported\n");
00805 Usage();
00806 ExitFatal(-1);
00807 }
00808 continue;
00809 case 'U':
00810 NetworkUpdates = atoi(optarg);
00811 continue;
00812
00813 case 'F':
00814 VideoForceFullScreen = 1;
00815 Video.FullScreen = 1;
00816 continue;
00817 case 'W':
00818 VideoForceFullScreen = 1;
00819 Video.FullScreen = 0;
00820 continue;
00821 case 'D':
00822 Video.Depth = atoi(optarg);
00823 continue;
00824 case 'S':
00825 VideoSyncSpeed = atoi(optarg);
00826 continue;
00827
00828 case -1:
00829 break;
00830 case '?':
00831 case 'h':
00832 default:
00833 Usage();
00834 ExitFatal(-1);
00835 }
00836 break;
00837 }
00838
00839 if (argc - optind > 1) {
00840 fprintf(stderr, "too many files\n");
00841 Usage();
00842 ExitFatal(-1);
00843 }
00844
00845 if (argc - optind) {
00846 size_t index;
00847 CliMapName = argv[optind];
00848 while ((index = CliMapName.find('\\')) != std::string::npos) {
00849 CliMapName[index] = '/';
00850 }
00851 --argc;
00852 }
00853
00854
00855 InitSyncRand();
00856
00857 CreateUserDirectories();
00858
00859
00860 InitCcl();
00861
00862
00863 InitAiModule();
00864
00865 LoadCcl();
00866
00867 main1(argc, argv);
00868
00869 Exit(0);
00870 return 0;
00871 }
00872