Subversion Repositories Games.Carmageddon

Rev

Rev 11 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "harness.h"
  2. #include "ascii_tables.h"
  3. #include "brender_emu/renderer_impl.h"
  4. #include "include/harness/config.h"
  5. #include "include/harness/hooks.h"
  6. #include "include/harness/os.h"
  7. #include "platforms/null.h"
  8. #include "sound/sound.h"
  9. //#include "version.h"
  10.  
  11. #include <errno.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <sys/stat.h>
  15.  
  16. br_pixelmap* palette;
  17. uint32_t* screen_buffer;
  18. harness_br_renderer* renderer_state;
  19.  
  20. br_pixelmap* last_dst = NULL;
  21. br_pixelmap* last_src = NULL;
  22.  
  23. unsigned int last_frame_time = 0;
  24. int force_null_platform = 0;
  25.  
  26. extern unsigned int GetTotalTime(void);
  27.  
  28. extern br_v1db_state v1db;
  29. extern uint32_t gI_am_cheating;
  30.  
  31. // SplatPack or Carmageddon. This is where we represent the code differences between the two. For example, the intro smack file.
  32. tHarness_game_info harness_game_info;
  33.  
  34. // Configuration options
  35. tHarness_game_config harness_game_config;
  36.  
  37. // Platform hooks
  38. tHarness_platform gHarness_platform;
  39.  
  40. extern void Harness_Platform_Init(tHarness_platform* platform);
  41.  
  42. int Harness_ProcessCommandLine(int* argc, char* argv[]);
  43.  
  44. static void Harness_DetectGameMode(void) {
  45.     if (access("DATA/RACES/CASTLE.TXT", F_OK) != -1) {
  46.         // All splatpack edition have the castle track
  47.         if (access("DATA/RACES/CASTLE2.TXT", F_OK) != -1) {
  48.             // Only the full splat release has the castle2 track
  49.             harness_game_info.defines.INTRO_SMK_FILE = "SPLINTRO.SMK";
  50.             harness_game_info.defines.GERMAN_LOADSCRN = "LOADSCRN.PIX";
  51.             harness_game_info.mode = eGame_splatpack;
  52.             printf("Game mode: Splat Pack\n");
  53.         } else if (access("DATA/RACES/TINSEL.TXT", F_OK) != -1) {
  54.             // Only the the splat x-mas demo has the tinsel track
  55.             harness_game_info.defines.INTRO_SMK_FILE = "MIX_INTR.SMK";
  56.             harness_game_info.defines.GERMAN_LOADSCRN = "";
  57.             harness_game_info.mode = eGame_splatpack_xmas_demo;
  58.             printf("Game mode: Splat Pack X-mas demo\n");
  59.         } else {
  60.             // Assume we're using the splatpack demo
  61.             harness_game_info.defines.INTRO_SMK_FILE = "MIX_INTR.SMK";
  62.             harness_game_info.defines.GERMAN_LOADSCRN = "";
  63.             harness_game_info.mode = eGame_splatpack_demo;
  64.             printf("Game mode: Splat Pack demo\n");
  65.         }
  66.     } else if (access("DATA/RACES/CITYB3.TXT", F_OK) != -1) {
  67.         // All non-splatpack edition have the cityb3 track
  68.         if (access("DATA/RACES/CITYA1.TXT", F_OK) == -1) {
  69.             // The demo does not have the citya1 track
  70.             harness_game_info.defines.INTRO_SMK_FILE = "";
  71.             harness_game_info.defines.GERMAN_LOADSCRN = "COWLESS.PIX";
  72.             harness_game_info.mode = eGame_carmageddon_demo;
  73.             printf("Game mode: Carmageddon demo\n");
  74.         } else {
  75.             goto carmageddon;
  76.         }
  77.     } else {
  78.     carmageddon:
  79.         if (access("DATA/CUTSCENE/Mix_intr.smk", F_OK) == -1) {
  80.             harness_game_info.defines.INTRO_SMK_FILE = "Mix_intr.smk";
  81.         } else {
  82.             harness_game_info.defines.INTRO_SMK_FILE = "MIX_INTR.SMK";
  83.         }
  84.         harness_game_info.defines.GERMAN_LOADSCRN = "LOADSCRN.PIX";
  85.         harness_game_info.mode = eGame_carmageddon;
  86.         printf("Game mode: Carmageddon\n");
  87.     }
  88.  
  89.     harness_game_info.localization = eGameLocalization_none;
  90.     if (access("DATA/TRNSLATE.TXT", F_OK) != -1) {
  91.         FILE* f = fopen("DATA/TRNSLATE.TXT", "rb");
  92.         fseek(f, 0, SEEK_END);
  93.         int filesize = ftell(f);
  94.         fseek(f, 0, SEEK_SET);
  95.         char* buffer = malloc(filesize + 1);
  96.         int nb = fread(buffer, 1, filesize, f);
  97.         if (nb != filesize) {
  98.             LOG_PANIC("Unable to read DATA/TRNSLATE.TXT");
  99.         }
  100.         buffer[filesize] = '\0';
  101.         fclose(f);
  102.         if (strstr(buffer, "NEUES SPIEL") != NULL) {
  103.             harness_game_info.localization = eGameLocalization_german;
  104.             LOG_INFO("Language: \"%s\"", "German");
  105.         } else {
  106.             LOG_INFO("Language: unrecognized");
  107.         }
  108.         free(buffer);
  109.     }
  110.  
  111.     switch (harness_game_info.mode) {
  112.     case eGame_carmageddon:
  113.         switch (harness_game_info.localization) {
  114.         case eGameLocalization_german:
  115.             harness_game_info.defines.requires_ascii_table = 1;
  116.             harness_game_info.defines.ascii_table = carmageddon_german_ascii_tables.ascii;
  117.             harness_game_info.defines.ascii_shift_table = carmageddon_german_ascii_tables.ascii_shift;
  118.             break;
  119.         default:
  120.             harness_game_info.defines.ascii_table = carmageddon_ascii_tables.ascii;
  121.             harness_game_info.defines.ascii_shift_table = carmageddon_ascii_tables.ascii_shift;
  122.             break;
  123.         }
  124.         break;
  125.     case eGame_carmageddon_demo:
  126.         harness_game_info.defines.ascii_table = demo_ascii_tables.ascii;
  127.         harness_game_info.defines.ascii_shift_table = demo_ascii_tables.ascii_shift;
  128.         break;
  129.     case eGame_splatpack_demo:
  130.     case eGame_splatpack_xmas_demo:
  131.         harness_game_info.defines.ascii_table = xmas_ascii_tables.ascii;
  132.         harness_game_info.defines.ascii_shift_table = xmas_ascii_tables.ascii_shift;
  133.         break;
  134.     default:
  135.         break;
  136.     }
  137. }
  138.  
  139. void Harness_Init(int* argc, char* argv[]) {
  140.     int result;
  141. #ifndef DETHRACE_VERSION
  142. #define DETHRACE_VERSION "0.6.0-pmbaty" // Pierre-Marie Baty -- CMake fix
  143. #endif // !DETHRACE_VERSION
  144.     printf("Dethrace version: %s\n", DETHRACE_VERSION);
  145.  
  146.     memset(&harness_game_info, 0, sizeof(harness_game_info));
  147.  
  148.     // disable the original CD check code
  149.     harness_game_config.enable_cd_check = 0;
  150.     // original physics time step. Lower values seem to work better at 30+ fps
  151.     harness_game_config.physics_step_time = 40;
  152.     // do not limit fps by default
  153.     harness_game_config.fps = 0;
  154.     // do not freeze timer
  155.     harness_game_config.freeze_timer = 0;
  156.     // default demo time out is 240s
  157.     harness_game_config.demo_timeout = 240000;
  158.     // disable developer diagnostics by default
  159.     harness_game_config.enable_diagnostics = 0;
  160.     // no volume multiplier
  161.     harness_game_config.volume_multiplier = 1.0f;
  162.     // start window in windowed mode
  163.     harness_game_config.start_full_screen = 0;
  164.     // Emulate DOS behavior
  165.     harness_game_config.dos_mode = 0;
  166.     // Skip binding socket to allow local network testing
  167.     harness_game_config.no_bind = 0;
  168.  
  169.     // install signal handler by default
  170.     harness_game_config.install_signalhandler = 1;
  171.  
  172.     Harness_ProcessCommandLine(argc, argv);
  173.  
  174.     if (harness_game_config.install_signalhandler) {
  175.         OS_InstallSignalHandler(argv[0]);
  176.     }
  177.  
  178.     char* root_dir = getenv("DETHRACE_ROOT_DIR");
  179.     if (root_dir != NULL) {
  180.         LOG_INFO("DETHRACE_ROOT_DIR is set to '%s'", root_dir);
  181.     } else {
  182.         root_dir = OS_Dirname(argv[0]);
  183. #ifdef __APPLE__
  184.         strcat (root_dir, "/../Resources"); // Pierre-Marie Baty -- macOS .app fix
  185. #endif /* __APPLE__ */
  186.         if (root_dir[0] == 0)
  187.             strcpy(root_dir, "."); // Pierre-Marie Baty -- consistency check
  188.     }
  189.     printf("Using root directory: %s\n", root_dir);
  190.     result = chdir(root_dir);
  191.     if (result != 0) {
  192.         LOG_PANIC("Failed to chdir. Error is %s", strerror(errno));
  193.     }
  194.  
  195.     if (harness_game_info.mode == eGame_none) {
  196.         Harness_DetectGameMode();
  197.     }
  198.  
  199.     if (force_null_platform) {
  200.         Null_Platform_Init(&gHarness_platform);
  201.     } else {
  202.         Harness_Platform_Init(&gHarness_platform);
  203.     }
  204. }
  205.  
  206. // used by unit tests
  207. void Harness_ForceNullPlatform(void) {
  208.     force_null_platform = 1;
  209. }
  210.  
  211. int Harness_ProcessCommandLine(int* argc, char* argv[]) {
  212.     for (int i = 1; i < *argc; i++) {
  213.         int handled = 0;
  214.  
  215.         if (strcasecmp(argv[i], "--cdcheck") == 0) {
  216.             harness_game_config.enable_cd_check = 1;
  217.             handled = 1;
  218.         } else if (strstr(argv[i], "--debug=") != NULL) {
  219.             char* s = strstr(argv[i], "=");
  220.             harness_debug_level = atoi(s + 1);
  221.             LOG_INFO("debug level set to %d", harness_debug_level);
  222.             handled = 1;
  223.         } else if (strstr(argv[i], "--physics-step-time=") != NULL) {
  224.             char* s = strstr(argv[i], "=");
  225.             harness_game_config.physics_step_time = atof(s + 1);
  226.             LOG_INFO("Physics step time set to %f", harness_game_config.physics_step_time);
  227.             handled = 1;
  228.         } else if (strstr(argv[i], "--fps=") != NULL) {
  229.             char* s = strstr(argv[i], "=");
  230.             harness_game_config.fps = atoi(s + 1);
  231.             LOG_INFO("FPS limiter set to %f", harness_game_config.fps);
  232.             handled = 1;
  233.         } else if (strcasecmp(argv[i], "--freeze-timer") == 0) {
  234.             LOG_INFO("Timer frozen");
  235.             harness_game_config.freeze_timer = 1;
  236.             handled = 1;
  237.         } else if (strcasecmp(argv[i], "--no-signal-handler") == 0) {
  238.             LOG_INFO("Don't install the signal handler");
  239.             harness_game_config.install_signalhandler = 0;
  240.             handled = 1;
  241.         } else if (strstr(argv[i], "--demo-timeout=") != NULL) {
  242.             char* s = strstr(argv[i], "=");
  243.             harness_game_config.demo_timeout = atoi(s + 1) * 1000;
  244.             LOG_INFO("Demo timeout set to %d milliseconds", harness_game_config.demo_timeout);
  245.             handled = 1;
  246.         } else if (strcasecmp(argv[i], "--i-am-cheating") == 0) {
  247.             gI_am_cheating = 0xa11ee75d;
  248.             handled = 1;
  249.         } else if (strcasecmp(argv[i], "--enable-diagnostics") == 0) {
  250.             harness_game_config.enable_diagnostics = 1;
  251.             handled = 1;
  252.         } else if (strstr(argv[i], "--volume-multiplier=") != NULL) {
  253.             char* s = strstr(argv[i], "=");
  254.             harness_game_config.volume_multiplier = atof(s + 1);
  255.             LOG_INFO("Volume multiplier set to %f", harness_game_config.volume_multiplier);
  256.             handled = 1;
  257.         } else if (strcasecmp(argv[i], "--full-screen") == 0) {
  258.             harness_game_config.start_full_screen = 1;
  259.             handled = 1;
  260.         } else if (strcasecmp(argv[i], "--dos-mode") == 0) {
  261.             harness_game_config.dos_mode = 1;
  262.             handled = 1;
  263.         } else if (strcasecmp(argv[i], "--no-bind") == 0) {
  264.             harness_game_config.no_bind = 1;
  265.             handled = 1;
  266.         }
  267.  
  268.         if (handled) {
  269.             // shift args downwards
  270.             for (int j = i; j < *argc - 1; j++) {
  271.                 argv[j] = argv[j + 1];
  272.             }
  273.             (*argc)--;
  274.             i--;
  275.         }
  276.     }
  277.  
  278.     return 0;
  279. }
  280.  
  281. // Render 2d back buffer
  282. void Harness_RenderScreen(br_pixelmap* dst, br_pixelmap* src) {
  283.     gHarness_platform.Renderer_FullScreenQuad((uint8_t*)src->pixels);
  284.  
  285.     last_dst = dst;
  286.     last_src = src;
  287. }
  288.  
  289. void Harness_Hook_BrV1dbRendererBegin(br_v1db_state* v1db) {
  290.     renderer_state = NewHarnessBrRenderer();
  291.     v1db->renderer = (br_renderer*)renderer_state;
  292. }
  293.  
  294. static int Harness_CalculateFrameDelay(void) {
  295.     if (harness_game_config.fps == 0) {
  296.         return 0;
  297.     }
  298.  
  299.     unsigned int now = GetTotalTime();
  300.  
  301.     if (last_frame_time != 0) {
  302.         unsigned int frame_time = now - last_frame_time;
  303.         last_frame_time = now;
  304.         if (frame_time < 100) {
  305.             int sleep_time = (1000 / harness_game_config.fps) - frame_time;
  306.             if (sleep_time > 5) {
  307.                 return sleep_time;
  308.             }
  309.         }
  310.     }
  311.     return 0;
  312. }
  313.  
  314. void Harness_Hook_renderActor(br_actor* actor, br_model* model, br_material* material, br_token type) {
  315.     gHarness_platform.Renderer_Model(actor, model, material, type, renderer_state->state.matrix.model_to_view);
  316. }
  317.  
  318. // Called by game to swap buffers at end of frame rendering
  319. void Harness_Hook_BrPixelmapDoubleBuffer(br_pixelmap* dst, br_pixelmap* src) {
  320.  
  321.     // draw the current colour_buffer (2d screen) contents
  322.     Harness_RenderScreen(dst, src);
  323.  
  324.     int delay_ms = Harness_CalculateFrameDelay();
  325.     gHarness_platform.SwapWindow();
  326.     if (delay_ms > 0) {
  327.         gHarness_platform.Sleep(delay_ms);
  328.     }
  329.  
  330.     gHarness_platform.Renderer_ClearBuffers();
  331.     last_frame_time = GetTotalTime();
  332. }
  333.  
  334. void Harness_RenderLastScreen(void) {
  335.     if (last_dst) {
  336.         Harness_RenderScreen(last_dst, last_src);
  337.         gHarness_platform.SwapWindow();
  338.     }
  339. }
  340.  
  341. // Filesystem hooks
  342. FILE* Harness_Hook_fopen(const char* pathname, const char* mode) {
  343.     return OS_fopen(pathname, mode);
  344. }
  345.