Subversion Repositories Games.Carmageddon

Rev

Rev 11 | Go to most recent revision | Details | 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"
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
}