Subversion Repositories Games.Carmageddon

Rev

Rev 18 | 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" // Pierre-Marie Baty -- our port doesn't need that (though it's actually based on dethrace-0.7.1)
  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. #ifdef __APPLE__
  140. #include <pwd.h> // for struct passwd and getpwuid()
  141. #endif /* __APPLE__ */
  142. void Harness_Init(int* argc, char* argv[]) {
  143.     int result;
  144. #ifndef DETHRACE_VERSION
  145. #define DETHRACE_VERSION "0.7.1-pmbaty" // Pierre-Marie Baty -- CMake fix
  146. #endif // !DETHRACE_VERSION
  147.     printf("Dethrace version: %s\n", DETHRACE_VERSION);
  148.  
  149.     memset(&harness_game_info, 0, sizeof(harness_game_info));
  150.  
  151.     // disable the original CD check code
  152.     harness_game_config.enable_cd_check = 0;
  153.     // original physics time step. Lower values seem to work better at 30+ fps
  154.     harness_game_config.physics_step_time = 40;
  155.     // do not limit fps by default
  156.     harness_game_config.fps = 0;
  157.     // do not freeze timer
  158.     harness_game_config.freeze_timer = 0;
  159.     // default demo time out is 240s
  160.     harness_game_config.demo_timeout = 240000;
  161.     // disable developer diagnostics by default
  162.     harness_game_config.enable_diagnostics = 0;
  163.     // no volume multiplier
  164.     harness_game_config.volume_multiplier = 1.0f;
  165.     // start window in windowed mode
  166.     harness_game_config.start_full_screen = 0;
  167.     // Emulate gore check
  168.     harness_game_config.gore_check = 0;
  169.     // Enable Sound Options menu
  170.     harness_game_config.sound_options = 1; // Pierre-Marie Baty -- invert option default value
  171.     // Skip binding socket to allow local network testing
  172.     harness_game_config.no_bind = 0;
  173.  
  174.     // install signal handler by default
  175.     harness_game_config.install_signalhandler = 1;
  176.  
  177.     Harness_ProcessCommandLine(argc, argv);
  178.  
  179.     if (harness_game_config.install_signalhandler) {
  180.         OS_InstallSignalHandler(argv[0]);
  181.     }
  182.  
  183.     char* root_dir = getenv("DETHRACE_ROOT_DIR");
  184.     if (root_dir != NULL) {
  185.         LOG_INFO("DETHRACE_ROOT_DIR is set to '%s'", root_dir);
  186.     } else {
  187.         root_dir = OS_Dirname(argv[0]);
  188. #ifdef __APPLE__
  189.         // Pierre-Marie Baty -- macOS .app fix
  190.         // peek in ~/Library/Application Support/<DOTTED BUNDLE ID>
  191.         // if the directory doesn't exist, create it and copy all the contents of root_dir in it
  192.         // this is necessary to have a read/write copy of the game data to not invalidate the app bundle's code signature
  193.         // disk space
  194.         {
  195.            extern int CFStringGetCString (void *theString, char *buffer, int bufferSize, uint32_t encoding); // avoid importing the whole Cocoa stuff
  196.            extern void *CFBundleGetIdentifier (void *bundle); // avoid importing the whole Cocoa stuff
  197.            extern void *CFBundleGetMainBundle (void); // avoid importing the whole Cocoa stuff
  198.            static char data_path[1024] = "";
  199.            char bundle_id[256];
  200.            char *homedir = getenv ("HOME");
  201.            if (homedir == NULL)
  202.               homedir = getpwuid (getuid ())->pw_dir;
  203.            CFStringGetCString (CFBundleGetIdentifier (CFBundleGetMainBundle ()), bundle_id, sizeof (bundle_id), 0x08000100 /*kCFStringEncodingUTF8*/);
  204.            sprintf (data_path, "%s/Library/Application Support/%s", homedir, bundle_id);
  205.            if (access (data_path, 0) != 0)
  206.            {
  207.               mkdir (data_path, 0755);
  208.               {
  209.                  char cp_cmd[2048];
  210.                  sprintf (cp_cmd, "cp -LR \"%s/../Resources/DATA\" \"%s\"", root_dir, data_path);
  211.                  system (cp_cmd);
  212.               }
  213.            }
  214.            root_dir = data_path;
  215.         }
  216. #endif /* __APPLE__ */
  217.         if (root_dir[0] == 0)
  218.             strcpy(root_dir, "."); // Pierre-Marie Baty -- consistency check
  219.     }
  220.     printf("Using root directory: %s\n", root_dir);
  221.     result = chdir(root_dir);
  222.     if (result != 0) {
  223.         LOG_PANIC("Failed to chdir. Error is %s", strerror(errno));
  224.     }
  225.  
  226.     if (harness_game_info.mode == eGame_none) {
  227.         Harness_DetectGameMode();
  228.     }
  229.  
  230.     if (force_null_platform) {
  231.         Null_Platform_Init(&gHarness_platform);
  232.     } else {
  233.         Harness_Platform_Init(&gHarness_platform);
  234.     }
  235. }
  236.  
  237. // used by unit tests
  238. void Harness_ForceNullPlatform(void) {
  239.     force_null_platform = 1;
  240. }
  241.  
  242. int Harness_ProcessCommandLine(int* argc, char* argv[]) {
  243.     for (int i = 1; i < *argc; i++) {
  244.         int handled = 0;
  245.  
  246.         if (strcasecmp(argv[i], "--cdcheck") == 0) {
  247.             harness_game_config.enable_cd_check = 1;
  248.             handled = 1;
  249.         } else if (strstr(argv[i], "--debug=") != NULL) {
  250.             char* s = strstr(argv[i], "=");
  251.             harness_debug_level = atoi(s + 1);
  252.             LOG_INFO("debug level set to %d", harness_debug_level);
  253.             handled = 1;
  254.         } else if (strstr(argv[i], "--physics-step-time=") != NULL) {
  255.             char* s = strstr(argv[i], "=");
  256.             harness_game_config.physics_step_time = atof(s + 1);
  257.             LOG_INFO("Physics step time set to %f", harness_game_config.physics_step_time);
  258.             handled = 1;
  259.         } else if (strstr(argv[i], "--fps=") != NULL) {
  260.             char* s = strstr(argv[i], "=");
  261.             harness_game_config.fps = atoi(s + 1);
  262.             LOG_INFO("FPS limiter set to %f", harness_game_config.fps);
  263.             handled = 1;
  264.         } else if (strcasecmp(argv[i], "--freeze-timer") == 0) {
  265.             LOG_INFO("Timer frozen");
  266.             harness_game_config.freeze_timer = 1;
  267.             handled = 1;
  268.         } else if (strcasecmp(argv[i], "--no-signal-handler") == 0) {
  269.             LOG_INFO("Don't install the signal handler");
  270.             harness_game_config.install_signalhandler = 0;
  271.             handled = 1;
  272.         } else if (strstr(argv[i], "--demo-timeout=") != NULL) {
  273.             char* s = strstr(argv[i], "=");
  274.             harness_game_config.demo_timeout = atoi(s + 1) * 1000;
  275.             LOG_INFO("Demo timeout set to %d milliseconds", harness_game_config.demo_timeout);
  276.             handled = 1;
  277.         } else if (strcasecmp(argv[i], "--i-am-cheating") == 0) {
  278.             gI_am_cheating = 0xa11ee75d;
  279.             handled = 1;
  280.         } else if (strcasecmp(argv[i], "--enable-diagnostics") == 0) {
  281.             harness_game_config.enable_diagnostics = 1;
  282.             handled = 1;
  283.         } else if (strstr(argv[i], "--volume-multiplier=") != NULL) {
  284.             char* s = strstr(argv[i], "=");
  285.             harness_game_config.volume_multiplier = atof(s + 1);
  286.             LOG_INFO("Volume multiplier set to %f", harness_game_config.volume_multiplier);
  287.             handled = 1;
  288.         } else if (strcasecmp(argv[i], "--full-screen") == 0) {
  289.             harness_game_config.start_full_screen = 1;
  290.             handled = 1;
  291.         } else if (strcasecmp(argv[i], "--gore-check") == 0) {
  292.             harness_game_config.gore_check = 1;
  293.             handled = 1;
  294.         } else if (strcasecmp(argv[i], "--no-sound-options") == 0) { // Pierre-Marie Baty -- invert option default value
  295.             harness_game_config.sound_options = 0; // Pierre-Marie Baty -- invert option default value
  296.             handled = 1;
  297.         } else if (strcasecmp(argv[i], "--no-bind") == 0) {
  298.             harness_game_config.no_bind = 1;
  299.             handled = 1;
  300.         }
  301.  
  302.         if (handled) {
  303.             // shift args downwards
  304.             for (int j = i; j < *argc - 1; j++) {
  305.                 argv[j] = argv[j + 1];
  306.             }
  307.             (*argc)--;
  308.             i--;
  309.         }
  310.     }
  311.  
  312.     return 0;
  313. }
  314.  
  315. // Render 2d back buffer
  316. void Harness_RenderScreen(br_pixelmap* dst, br_pixelmap* src) {
  317.     gHarness_platform.Renderer_FullScreenQuad((uint8_t*)src->pixels);
  318.  
  319.     last_dst = dst;
  320.     last_src = src;
  321. }
  322.  
  323. void Harness_Hook_BrV1dbRendererBegin(br_v1db_state* v1db) {
  324.     renderer_state = NewHarnessBrRenderer();
  325.     v1db->renderer = (br_renderer*)renderer_state;
  326. }
  327.  
  328. static int Harness_CalculateFrameDelay(void) {
  329.     if (harness_game_config.fps == 0) {
  330.         return 0;
  331.     }
  332.  
  333.     unsigned int now = GetTotalTime();
  334.  
  335.     if (last_frame_time != 0) {
  336.         unsigned int frame_time = now - last_frame_time;
  337.         last_frame_time = now;
  338.         if (frame_time < 100) {
  339.             int sleep_time = (1000 / harness_game_config.fps) - frame_time;
  340.             if (sleep_time > 5) {
  341.                 return sleep_time;
  342.             }
  343.         }
  344.     }
  345.     return 0;
  346. }
  347.  
  348. void Harness_Hook_renderActor(br_actor* actor, br_model* model, br_material* material, br_token type) {
  349.     gHarness_platform.Renderer_Model(actor, model, material, type, renderer_state->state.matrix.model_to_view);
  350. }
  351.  
  352. // Called by game to swap buffers at end of frame rendering
  353. void Harness_Hook_BrPixelmapDoubleBuffer(br_pixelmap* dst, br_pixelmap* src) {
  354.  
  355.     // draw the current colour_buffer (2d screen) contents
  356.     Harness_RenderScreen(dst, src);
  357.  
  358.     int delay_ms = Harness_CalculateFrameDelay();
  359.     gHarness_platform.SwapWindow();
  360.     if (delay_ms > 0) {
  361.         gHarness_platform.Sleep(delay_ms);
  362.     }
  363.  
  364.     gHarness_platform.Renderer_ClearBuffers();
  365.     last_frame_time = GetTotalTime();
  366. }
  367.  
  368. void Harness_RenderLastScreen(void) {
  369.     if (last_dst) {
  370.         Harness_RenderScreen(last_dst, last_src);
  371.         gHarness_platform.SwapWindow();
  372.     }
  373. }
  374.  
  375. // Filesystem hooks
  376. FILE* Harness_Hook_fopen(const char* pathname, const char* mode) {
  377.     return OS_fopen(pathname, mode);
  378. }
  379.