Subversion Repositories Games.Carmageddon

Rev

Rev 20 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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"
14 pmbaty 9
//#include "version.h" // Pierre-Marie Baty -- our port doesn't need that (though it's actually based on dethrace-0.7.1)
1 pmbaty 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
 
11 pmbaty 139
#ifdef __APPLE__
140
#include <pwd.h> // for struct passwd and getpwuid()
141
#endif /* __APPLE__ */
1 pmbaty 142
void Harness_Init(int* argc, char* argv[]) {
143
    int result;
144
#ifndef DETHRACE_VERSION
14 pmbaty 145
#define DETHRACE_VERSION "0.7.1-pmbaty" // Pierre-Marie Baty -- CMake fix
1 pmbaty 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;
20 pmbaty 167
    // Emulate gore check
11 pmbaty 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
1 pmbaty 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__
11 pmbaty 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
        }
1 pmbaty 216
#endif /* __APPLE__ */
217
        if (root_dir[0] == 0)
218
            strcpy(root_dir, "."); // Pierre-Marie Baty -- consistency check
219
    }
20 pmbaty 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));
1 pmbaty 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], "=");
20 pmbaty 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);
1 pmbaty 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;
11 pmbaty 291
        } else if (strcasecmp(argv[i], "--gore-check") == 0) {
292
            harness_game_config.gore_check = 1;
1 pmbaty 293
            handled = 1;
11 pmbaty 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;
1 pmbaty 297
        } else if (strcasecmp(argv[i], "--no-bind") == 0) {
298
            harness_game_config.no_bind = 1;
299
            handled = 1;
21 pmbaty 300
        } else if (strcasecmp(argv[i], "--no-music") == 0) {
301
            harness_game_config.no_music = 1;
302
            handled = 1;
1 pmbaty 303
        }
304
 
305
        if (handled) {
306
            // shift args downwards
307
            for (int j = i; j < *argc - 1; j++) {
308
                argv[j] = argv[j + 1];
309
            }
310
            (*argc)--;
311
            i--;
312
        }
313
    }
314
 
315
    return 0;
316
}
317
 
20 pmbaty 318
// Render 2d back buffer
319
void Harness_RenderScreen(br_pixelmap* dst, br_pixelmap* src) {
320
    gHarness_platform.Renderer_FullScreenQuad((uint8_t*)src->pixels);
321
 
322
    last_dst = dst;
323
    last_src = src;
324
}
325
 
326
void Harness_Hook_BrV1dbRendererBegin(br_v1db_state* v1db) {
327
    renderer_state = NewHarnessBrRenderer();
328
    v1db->renderer = (br_renderer*)renderer_state;
329
}
330
 
331
static int Harness_CalculateFrameDelay(void) {
332
    if (harness_game_config.fps == 0) {
333
        return 0;
334
    }
335
 
336
    unsigned int now = GetTotalTime();
337
 
338
    if (last_frame_time != 0) {
339
        unsigned int frame_time = now - last_frame_time;
340
        last_frame_time = now;
341
        if (frame_time < 100) {
342
            int sleep_time = (1000 / harness_game_config.fps) - frame_time;
343
            if (sleep_time > 5) {
344
                return sleep_time;
345
            }
346
        }
347
    }
348
    return 0;
349
}
350
 
351
void Harness_Hook_renderActor(br_actor* actor, br_model* model, br_material* material, br_token type) {
352
    gHarness_platform.Renderer_Model(actor, model, material, type, renderer_state->state.matrix.model_to_view);
353
}
354
 
355
// Called by game to swap buffers at end of frame rendering
356
void Harness_Hook_BrPixelmapDoubleBuffer(br_pixelmap* dst, br_pixelmap* src) {
357
 
358
    // draw the current colour_buffer (2d screen) contents
359
    Harness_RenderScreen(dst, src);
360
 
361
    int delay_ms = Harness_CalculateFrameDelay();
362
    gHarness_platform.SwapWindow();
363
    if (delay_ms > 0) {
364
        gHarness_platform.Sleep(delay_ms);
365
    }
366
 
367
    gHarness_platform.Renderer_ClearBuffers();
368
    last_frame_time = GetTotalTime();
369
}
370
 
371
void Harness_RenderLastScreen(void) {
372
    if (last_dst) {
373
        Harness_RenderScreen(last_dst, last_src);
374
        gHarness_platform.SwapWindow();
375
    }
376
}
377
 
1 pmbaty 378
// Filesystem hooks
379
FILE* Harness_Hook_fopen(const char* pathname, const char* mode) {
380
    return OS_fopen(pathname, mode);
381
}