Subversion Repositories Games.Carmageddon

Rev

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

Rev Author Line No. Line
1 pmbaty 1
#include "loadsave.h"
18 pmbaty 2
#include "brender.h"
3
#include "brhton.h"
1 pmbaty 4
#include "cutscene.h"
5
#include "errors.h"
6
#include "flicplay.h"
7
#include "globvars.h"
8
#include "globvrpb.h"
9
#include "grafdata.h"
10
#include "graphics.h"
11
#include "harness/config.h"
12
#include "harness/trace.h"
13
#include "init.h"
14
#include "input.h"
15
#include "intrface.h"
16
#include "loading.h"
17
#include "pd/sys.h"
18
#include "sound.h"
19
#include "structur.h"
20
#include "utility.h"
21
#include "world.h"
22
#include <stdlib.h>
23
#include <string.h>
24
 
25
tSave_game* gSaved_games[8];
26
int gStarted_typing;
27
int gSave_allowed;
28
 
29
#define SAVEGAME_VERSION 6
30
 
18 pmbaty 31
#define SWAP32_BE(V)      \
32
    do {                  \
1 pmbaty 33
        (V) = BrHtoNL(V); \
34
    } while (0)
35
 
36
// IDA: void __usercall CorrectLoadByteOrdering(int pIndex@<EAX>)
37
void CorrectLoadByteOrdering(int pIndex) {
38
    int i;
39
    LOG_TRACE("(%d)", pIndex);
40
 
41
    SWAP32_BE(gSaved_games[pIndex]->version);
42
    SWAP32_BE(gSaved_games[pIndex]->rank);
43
    SWAP32_BE(gSaved_games[pIndex]->credits);
44
    SWAP32_BE(gSaved_games[pIndex]->skill_level);
45
    SWAP32_BE(gSaved_games[pIndex]->frank_or_annitude);
46
    SWAP32_BE(gSaved_games[pIndex]->game_completed);
47
    SWAP32_BE(gSaved_games[pIndex]->current_race_index);
48
    SWAP32_BE(gSaved_games[pIndex]->number_of_cars);
49
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->cars_available); i++) {
50
        SWAP32_BE(gSaved_games[pIndex]->cars_available[i]);
51
    }
52
    SWAP32_BE(gSaved_games[pIndex]->current_car_index);
53
    SWAP32_BE(gSaved_games[pIndex]->redo_race_index);
54
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->power_up_levels); i++) {
55
        SWAP32_BE(gSaved_games[pIndex]->power_up_levels[i]);
56
    }
57
}
58
 
59
// IDA: tU32 __usercall CalcLSChecksum@<EAX>(tSave_game *pSaved_game@<EAX>)
60
tU32 CalcLSChecksum(tSave_game* pSaved_game) {
61
    tU32 checksum;
62
    tU32 checksum2;
63
    int i;
64
    tU8* ptr;
65
    LOG_TRACE("(%p)", pSaved_game);
66
 
67
#ifdef DETHRACE_FIX_BUGS
68
    if (sizeof(tSave_game) - sizeof(tU32) != offsetof(tSave_game, checksum)) {
69
        PDFatalError("checksum must be last element of savegame struct");
70
    }
71
#endif
72
 
73
    checksum = 0;
74
    for (i = 0, ptr = (tU8*)pSaved_game; i < (sizeof(tSave_game) - sizeof(tU32)); i++, ptr++) {
75
        checksum2 = (*ptr ^ 0xbd) + checksum;
76
        checksum = checksum ^ checksum2 << 25 ^ checksum2 >> 7;
77
    }
78
    return checksum;
79
}
80
 
81
// IDA: void __cdecl LoadSavedGames()
82
void LoadSavedGames(void) {
83
    tPath_name the_path;
84
    int i;
85
    FILE* f;
86
    tU32 the_size;
87
    LOG_TRACE("()");
88
 
89
#ifdef DETHRACE_FIX_BUGS
90
    if (sizeof(tSave_game) != 948) {
91
        PDFatalError("Size of tSave_game struct is not correct (should be 948 bytes)");
92
    }
93
#endif
94
 
95
    PathCat(the_path, gApplication_path, "SAVEGAME");
96
    PathCat(the_path, the_path, "SAVEx");
97
    for (i = 0; i < COUNT_OF(gSaved_games); i++) {
98
        the_path[strlen(the_path) - 1] = '0' + i;
99
        f = DRfopen(the_path, "rb");
100
        if (f == NULL) {
101
            continue;
102
        }
103
        the_size = GetFileLength(f);
104
        if (the_size == sizeof(tSave_game)) {
105
            gSaved_games[i] = BrMemCalloc(1, sizeof(tSave_game), kMem_saved_game);
106
            fread(gSaved_games[i], 1, the_size, f);
107
            CorrectLoadByteOrdering(i);
108
            if (CalcLSChecksum(gSaved_games[i]) != gSaved_games[i]->checksum || gSaved_games[i]->version != SAVEGAME_VERSION) {
109
                BrMemFree(gSaved_games[i]);
110
                gSaved_games[i] = NULL;
111
            }
112
        } else {
113
            gSaved_games[i] = NULL;
114
        }
115
        fclose(f);
116
    }
117
}
118
 
119
// IDA: void __cdecl DisposeSavedGames()
120
void DisposeSavedGames(void) {
121
    int i;
122
    LOG_TRACE("()");
123
 
124
    for (i = 0; i < COUNT_OF(gSaved_games); i++) {
125
        if (gSaved_games[i] != NULL) {
126
            BrMemFree(gSaved_games[i]);
127
        }
128
    }
129
}
130
 
131
// IDA: void __usercall LoadTheGame(int pSlot_index@<EAX>)
132
void LoadTheGame(int pSlot_index) {
133
    int i;
134
    //char the_car_name[14]; // Pierre-Marie Baty -- unused variable
135
    LOG_TRACE("(%d)", pSlot_index);
136
 
137
    if (gProgram_state.car_name[0] != '\0') {
138
        DisposeCar(&gProgram_state.current_car, gProgram_state.current_car.index);
139
        ClearOutStorageSpace(&gOur_car_storage_space);
140
    }
141
    strcpy(gProgram_state.player_name[0], gSaved_games[pSlot_index]->player_name[0]);
142
    strcpy(gProgram_state.player_name[1], gSaved_games[pSlot_index]->player_name[1]);
143
    gProgram_state.current_car_index = gSaved_games[pSlot_index]->current_car_index;
144
    gProgram_state.redo_race_index = gSaved_games[pSlot_index]->redo_race_index;
145
    gProgram_state.frank_or_anniness = gSaved_games[pSlot_index]->frank_or_annitude;
146
    gProgram_state.car_name[0] = '\0';
147
    AboutToLoadFirstCar();
148
    SwitchToRealResolution();
149
    LoadCar(gSaved_games[pSlot_index]->car_name, eDriver_local_human,
150
        &gProgram_state.current_car, gProgram_state.current_car_index,
151
        gProgram_state.player_name[gProgram_state.frank_or_anniness],
152
        &gOur_car_storage_space);
153
    SwitchToLoresMode();
154
    SetCarStorageTexturingLevel(&gOur_car_storage_space, GetCarTexturingLevel(), eCTL_full);
155
    gProgram_state.skill_level = gSaved_games[pSlot_index]->skill_level;
156
    gProgram_state.current_race_index = gSaved_games[pSlot_index]->current_race_index;
157
    InitGame(gProgram_state.current_race_index);
158
    gProgram_state.number_of_cars = gSaved_games[pSlot_index]->number_of_cars;
159
    memcpy(gProgram_state.cars_available, gSaved_games[pSlot_index]->cars_available, sizeof(gProgram_state.cars_available));
160
    for (i = 0; i < gNumber_of_races; i++) {
161
        gRace_list[i].been_there_done_that = gSaved_games[pSlot_index]->race_info[i].been_there_done_that;
162
    }
163
    for (i = 0; i < gNumber_of_racers; i++) {
164
        gOpponents[i].dead = gSaved_games[pSlot_index]->opponent_info[i].dead;
165
    }
166
    gProgram_state.credits = gSaved_games[pSlot_index]->credits;
167
    gProgram_state.rank = gSaved_games[pSlot_index]->rank;
168
    for (i = 0; i < COUNT_OF(gProgram_state.current_car.power_up_levels); i++) {
169
        gProgram_state.current_car.power_up_levels[i] = gSaved_games[pSlot_index]->power_up_levels[i];
170
    }
171
    gProgram_state.game_completed = gSaved_games[pSlot_index]->game_completed;
172
    SetSoundVolumes();
173
    gProgram_state.saving = 0;
174
    SelectOpponents(&gCurrent_race);
175
}
176
 
177
// IDA: void __cdecl StartRollingSaveNamesIn()
178
void StartRollingSaveNamesIn(void) {
179
    int i;
180
    int save_slot_height;
181
    LOG_TRACE("()");
182
 
183
    for (i = 0; i < COUNT_OF(gSaved_games); i++) {
184
        save_slot_height = gCurrent_graf_data->save_slot_y_offset + i * gCurrent_graf_data->rolling_letter_y_pitch;
185
        SetSlotXY(i, gCurrent_graf_data->save_slot_x_offset, save_slot_height);
186
        if (gSaved_games[i] != NULL) {
187
            AddRollingString(gSaved_games[i]->slot_name, gCurrent_graf_data->save_slot_x_offset, save_slot_height, eRT_alpha);
188
            AddRollingNumber(gSaved_games[i]->rank, 2, gCurrent_graf_data->save_slot_rank_x_offset, save_slot_height);
189
            AddRollingNumber(gSaved_games[i]->credits, 6, gCurrent_graf_data->save_slot_credits_x_offset, save_slot_height);
190
        } else {
191
            AddRollingString(GetMiscString(kMiscString_Empty), gCurrent_graf_data->save_slot_x_offset, save_slot_height, eRT_alpha);
192
        }
193
    }
194
}
195
 
196
// IDA: void __cdecl LoadStart()
197
void LoadStart(void) {
198
    LOG_TRACE("()");
199
 
200
    StartRollingSaveNamesIn();
201
}
202
 
203
// IDA: int __usercall DoLoadGame@<EAX>(int pSave_allowed@<EAX>)
204
int DoLoadGame(void) {
205
    static tFlicette flicker_on[9] = {
206
        { 74, { 47, 94 }, { 23, 55 } },
207
        { 74, { 47, 94 }, { 44, 110 } },
208
        { 74, { 47, 94 }, { 65, 156 } },
209
        { 74, { 47, 94 }, { 86, 206 } },
210
        { 74, { 47, 94 }, { 107, 257 } },
211
        { 74, { 47, 94 }, { 128, 307 } },
212
        { 74, { 47, 94 }, { 149, 358 } },
213
        { 74, { 47, 94 }, { 170, 408 } },
214
        { 57, { 255, 510 }, { 151, 362 } },
215
    };
216
    static tFlicette flicker_off[9] = {
217
        { 73, { 47, 94 }, { 23, 55 } },
218
        { 73, { 47, 94 }, { 44, 110 } },
219
        { 73, { 47, 94 }, { 65, 156 } },
220
        { 73, { 47, 94 }, { 86, 206 } },
221
        { 73, { 47, 94 }, { 107, 257 } },
222
        { 73, { 47, 94 }, { 128, 307 } },
223
        { 73, { 47, 94 }, { 149, 358 } },
224
        { 73, { 47, 94 }, { 170, 408 } },
225
        { 56, { 255, 510 }, { 151, 362 } },
226
    };
227
    static tFlicette push[9] = {
228
        { 74, { 47, 94 }, { 23, 55 } },
229
        { 74, { 47, 94 }, { 44, 110 } },
230
        { 74, { 47, 94 }, { 65, 156 } },
231
        { 74, { 47, 94 }, { 86, 206 } },
232
        { 74, { 47, 94 }, { 107, 257 } },
233
        { 74, { 47, 94 }, { 128, 307 } },
234
        { 74, { 47, 94 }, { 149, 358 } },
235
        { 74, { 47, 94 }, { 170, 408 } },
236
        { 59, { 255, 510 }, { 151, 362 } },
237
    };
238
    static tMouse_area mouse_areas[9] = {
239
        { { 48, 96 }, { 17, 41 }, { 250, 500 }, { 33, 79 }, 0, 0, 0, NULL },
240
        { { 48, 96 }, { 39, 94 }, { 250, 500 }, { 55, 132 }, 1, 0, 0, NULL },
241
        { { 48, 96 }, { 59, 142 }, { 250, 500 }, { 75, 180 }, 2, 0, 0, NULL },
242
        { { 48, 96 }, { 80, 192 }, { 250, 500 }, { 96, 230 }, 3, 0, 0, NULL },
243
        { { 48, 96 }, { 101, 242 }, { 250, 500 }, { 117, 281 }, 4, 0, 0, NULL },
244
        { { 48, 96 }, { 122, 293 }, { 250, 500 }, { 138, 331 }, 5, 0, 0, NULL },
245
        { { 48, 96 }, { 143, 343 }, { 250, 500 }, { 159, 382 }, 6, 0, 0, NULL },
246
        { { 48, 96 }, { 164, 394 }, { 250, 500 }, { 180, 432 }, 7, 0, 0, NULL },
247
        { { 152, 304 }, { 151, 362 }, { 299, 598 }, { 171, 410 }, 8, 1, 0, NULL },
248
    };
249
    static tRectile recopy_areas[24] = {
250
        {
251
            { 53, 106 },
252
            { 17, 41 },
253
            { 147, 294 },
254
            { 35, 84 },
255
        },
256
        {
257
            { 53, 106 },
258
            { 39, 94 },
259
            { 147, 294 },
260
            { 57, 137 },
261
        },
262
        {
263
            { 53, 106 },
264
            { 59, 142 },
265
            { 147, 294 },
266
            { 77, 185 },
267
        },
268
        {
269
            { 53, 106 },
270
            { 80, 192 },
271
            { 147, 294 },
272
            { 98, 235 },
273
        },
274
        {
275
            { 53, 106 },
276
            { 101, 242 },
277
            { 147, 294 },
278
            { 119, 286 },
279
        },
280
        {
281
            { 53, 106 },
282
            { 122, 293 },
283
            { 147, 294 },
284
            { 140, 336 },
285
        },
286
        {
287
            { 53, 106 },
288
            { 143, 343 },
289
            { 147, 294 },
290
            { 161, 386 },
291
        },
292
        {
293
            { 53, 106 },
294
            { 164, 394 },
295
            { 147, 294 },
296
            { 182, 437 },
297
        },
298
        {
299
            { 166, 332 },
300
            { 17, 41 },
301
            { 180, 360 },
302
            { 35, 84 },
303
        },
304
        {
305
            { 166, 332 },
306
            { 39, 94 },
307
            { 180, 360 },
308
            { 57, 137 },
309
        },
310
        {
311
            { 166, 332 },
312
            { 59, 142 },
313
            { 180, 360 },
314
            { 77, 185 },
315
        },
316
        {
317
            { 166, 332 },
318
            { 80, 192 },
319
            { 180, 360 },
320
            { 98, 235 },
321
        },
322
        {
323
            { 166, 332 },
324
            { 101, 242 },
325
            { 180, 360 },
326
            { 119, 286 },
327
        },
328
        {
329
            { 166, 332 },
330
            { 122, 293 },
331
            { 180, 360 },
332
            { 140, 336 },
333
        },
334
        {
335
            { 166, 332 },
336
            { 143, 343 },
337
            { 180, 360 },
338
            { 161, 386 },
339
        },
340
        {
341
            { 166, 332 },
342
            { 164, 394 },
343
            { 180, 360 },
344
            { 182, 370 },
345
        },
346
        {
347
            { 198, 396 },
348
            { 17, 41 },
349
            { 245, 490 },
350
            { 35, 84 },
351
        },
352
        {
353
            { 198, 396 },
354
            { 39, 94 },
355
            { 245, 490 },
356
            { 57, 137 },
357
        },
358
        {
359
            { 198, 396 },
360
            { 59, 142 },
361
            { 245, 490 },
362
            { 77, 185 },
363
        },
364
        {
365
            { 198, 396 },
366
            { 80, 192 },
367
            { 245, 490 },
368
            { 98, 235 },
369
        },
370
        {
371
            { 198, 396 },
372
            { 101, 242 },
373
            { 245, 490 },
374
            { 119, 286 },
375
        },
376
        {
377
            { 198, 396 },
378
            { 122, 293 },
379
            { 245, 490 },
380
            { 140, 336 },
381
        },
382
        {
383
            { 198, 396 },
384
            { 143, 343 },
385
            { 245, 490 },
386
            { 161, 386 },
387
        },
388
        {
389
            { 198, 396 },
390
            { 164, 394 },
391
            { 245, 490 },
392
            { 182, 437 },
393
        },
394
    };
395
    static tInterface_spec interface_spec = {
396
        0,
397
        71,
398
        70,
399
        0,
400
        0,
401
        0,
402
        3,
403
        { 1, 0 },
404
        { -1, 8 },
405
        { 8, 0 },
406
        { 8, 7 },
407
        { NULL, NULL },
408
        { 1, 0 },
409
        { 1, 0 },
410
        { 8, 0 },
411
        { 8, 7 },
412
        { NULL, NULL },
413
        { -1, -1 },
414
        { -1, 0 },
415
        { 0, 8 },
416
        { 7, 8 },
417
        { NULL, NULL },
418
        { -1, -1 },
419
        { 1, 0 },
420
        { 0, 8 },
421
        { 7, 8 },
422
        { NULL, NULL },
423
        { 1, 1 },
424
        { NULL, NULL },
425
        { 1, 1 },
426
        { NULL, NULL },
427
        NULL,
428
        NULL,
429
        0,
430
        LoadStart,
431
        NULL,
432
        NULL,
433
        1,
434
        { 0, 0 },
435
        NULL,
436
        8,
437
        1,
438
        COUNT_OF(flicker_on),
439
        flicker_on,
440
        flicker_off,
441
        push,
442
        COUNT_OF(mouse_areas),
443
        mouse_areas,
444
        COUNT_OF(recopy_areas),
445
        recopy_areas,
446
    };
447
    int result;
448
    LOG_TRACE("()");
449
 
450
    if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
451
        DoFeatureUnavailableInDemo();
452
        return 0;
453
    }
454
 
455
    if (gNet_mode == eNet_mode_none) {
456
        if (!OriginalCarmaCDinDrive()) {
457
            DoErrorInterface(kMiscString_PLEASE_INSERT_THE_CARMAGEDDON_CD);
458
            return 0;
459
        }
460
        gProgram_state.loading = 1;
461
        LoadSavedGames();
462
        if (gGame_to_load >= 0) {
463
            gProgram_state.last_slot = gGame_to_load;
464
            gProgram_state.loaded = 1;
465
            LoadTheGame(gGame_to_load);
466
            gGame_to_load = -1;
467
            gProgram_state.prog_status = eProg_game_starting;
468
            DisposeSavedGames();
469
            gProgram_state.loading = 0;
470
            return 1;
471
        } else {
472
            result = DoInterfaceScreen(&interface_spec, gFaded_palette, gProgram_state.last_slot);
473
            if (result != 8 && gSaved_games[result] == NULL) {
474
                result = 8;
475
                DRS3StartSound(gEffects_outlet, 3100);
476
            }
477
            if (result != 8) {
478
                FadePaletteDown();
479
                if (gProgram_state.racing) {
480
                    gGame_to_load = result;
481
                    gProgram_state.prog_status = eProg_idling;
482
                } else {
483
                    gProgram_state.last_slot = result;
484
                    gProgram_state.loaded = 1;
485
                    LoadTheGame(result);
486
                }
487
            } else {
488
                if (gProgram_state.racing) {
489
                    FadePaletteDown();
490
                } else {
491
                    RunFlic(72);
492
                }
493
            }
494
            gProgram_state.loading = 0;
495
            DisposeSavedGames();
496
            return result != 8;
497
        }
498
    } else {
499
        SuspendPendingFlic();
500
        DoErrorInterface(kMiscString_CannotSaveGameInNetworkPlay);
501
        return 0;
502
    }
503
}
504
 
505
// IDA: void __usercall CorrectSaveByteOrdering(int pIndex@<EAX>)
506
void CorrectSaveByteOrdering(int pIndex) {
507
    int i;
508
    LOG_TRACE("(%d)", pIndex);
509
 
510
    SWAP32_BE(gSaved_games[pIndex]->version);
511
    SWAP32_BE(gSaved_games[pIndex]->rank);
512
    SWAP32_BE(gSaved_games[pIndex]->credits);
513
    SWAP32_BE(gSaved_games[pIndex]->skill_level);
514
    SWAP32_BE(gSaved_games[pIndex]->frank_or_annitude);
515
    SWAP32_BE(gSaved_games[pIndex]->game_completed);
516
    SWAP32_BE(gSaved_games[pIndex]->current_race_index);
517
    SWAP32_BE(gSaved_games[pIndex]->number_of_cars);
518
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->cars_available); i++) {
519
        SWAP32_BE(gSaved_games[pIndex]->cars_available[i]);
520
    }
521
    SWAP32_BE(gSaved_games[pIndex]->current_car_index);
522
    SWAP32_BE(gSaved_games[pIndex]->redo_race_index);
523
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->power_up_levels); i++) {
524
        SWAP32_BE(gSaved_games[pIndex]->power_up_levels[i]);
525
    }
526
}
527
 
528
// IDA: void __usercall SaveTheGame(int pSlot_number@<EAX>)
529
void SaveTheGame(int pSlot_number) {
530
    tPath_name the_path;
531
    FILE* f;
532
    LOG_TRACE("(%d)", pSlot_number);
533
 
534
    gSaved_games[pSlot_number]->checksum = CalcLSChecksum(gSaved_games[pSlot_number]);
535
    PathCat(the_path, gApplication_path, "SAVEGAME");
536
    PathCat(the_path, the_path, "SAVEx");
537
    the_path[strlen(the_path) - 1] = '0' + pSlot_number;
538
    PDFileUnlock(the_path);
539
    f = DRfopen(the_path, "wb");
540
    if (f != NULL) {
541
        CorrectSaveByteOrdering(pSlot_number);
542
        fwrite(gSaved_games[pSlot_number], 1, sizeof(tSave_game), f);
543
        fclose(f);
544
        CorrectLoadByteOrdering(pSlot_number);
545
    }
546
}
547
 
548
// IDA: int __cdecl ConfirmMidGameSave()
549
int ConfirmMidGameSave(void) {
550
    static tFlicette flicker_on[2] = {
551
        { 43, { 84, 168 }, { 124, 298 } },
552
        { 43, { 181, 362 }, { 124, 298 } },
553
    };
554
    static tFlicette flicker_off[2] = {
555
        { 42, { 84, 168 }, { 124, 298 } },
556
        { 42, { 181, 362 }, { 124, 298 } },
557
    };
558
    static tFlicette push[2] = {
559
        { 44, { 84, 168 }, { 124, 298 } },
560
        { 45, { 181, 362 }, { 124, 298 } },
561
    };
562
    static tMouse_area mouse_areas[2] = {
563
        { { 84, 168 }, { 124, 298 }, { 147, 294 }, { 144, 346 }, 0, 0, 0, NULL },
564
        { { 181, 362 }, { 124, 298 }, { 244, 488 }, { 144, 346 }, 1, 0, 0, NULL },
565
    };
566
    static tInterface_spec interface_spec = {
567
        0, 40, 0, 41, -1, -1, 0,
568
        { -1, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
569
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
570
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
571
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
572
        { 1, 1 }, { NULL, NULL }, { 1, 1 }, { NULL, NULL },
573
        NULL, NULL, 0, NULL, NULL, NULL, 0, { 0, 0 },
574
        NULL, 1, 1,
575
        COUNT_OF(flicker_on), flicker_on, flicker_off, push,
576
        COUNT_OF(mouse_areas), mouse_areas,
577
        0, NULL
578
    };
579
    LOG_TRACE("()");
580
 
581
    return DoInterfaceScreen(&interface_spec, 0, 0) == 0;
582
}
583
 
584
// IDA: void __usercall MakeSavedGame(tSave_game **pSave_record@<EAX>)
585
void MakeSavedGame(tSave_game** pSave_record) {
586
    int i;
587
    LOG_TRACE("(%p)", pSave_record);
588
 
589
    if (*pSave_record == NULL) {
590
        *pSave_record = BrMemCalloc(1, sizeof(tSave_game), kMem_new_save_game);
591
    }
592
    (*pSave_record)->skill_level = gProgram_state.skill_level;
593
    (*pSave_record)->version = SAVEGAME_VERSION;
594
    (*pSave_record)->frank_or_annitude = gProgram_state.frank_or_anniness;
595
    (*pSave_record)->game_completed = gProgram_state.game_completed;
596
    (*pSave_record)->current_race_index = gProgram_state.current_race_index;
597
    for (i = 0; i < gNumber_of_races; i++) {
598
        (*pSave_record)->race_info[i].been_there_done_that = gRace_list[i].been_there_done_that;
599
    }
600
    for (i = 0; i < gNumber_of_racers; i++) {
601
        (*pSave_record)->opponent_info[i].dead = gOpponents[i].dead;
602
    }
603
    (*pSave_record)->credits = gProgram_state.credits;
604
    (*pSave_record)->rank = gProgram_state.rank;
605
    strcpy((*pSave_record)->car_name, gProgram_state.car_name);
606
    strcpy((*pSave_record)->player_name[0], gProgram_state.player_name[0]);
607
    strcpy((*pSave_record)->player_name[1], gProgram_state.player_name[1]);
608
 
609
    (*pSave_record)->number_of_cars = gProgram_state.number_of_cars;
610
    (*pSave_record)->current_car_index = gProgram_state.current_car_index;
611
    (*pSave_record)->redo_race_index = gProgram_state.redo_race_index;
612
    for (i = 0; i < COUNT_OF(gProgram_state.cars_available); i++) {
613
        (*pSave_record)->cars_available[i] = gProgram_state.cars_available[i];
614
    }
615
    for (i = 0; i < COUNT_OF(gProgram_state.current_car.power_up_levels); i++) {
616
        (*pSave_record)->power_up_levels[i] = gProgram_state.current_car.power_up_levels[i];
617
    }
618
}
619
 
620
// IDA: void __cdecl SaveStart()
621
void SaveStart(void) {
622
    LOG_TRACE("()");
623
 
624
    StartRollingSaveNamesIn();
625
}
626
 
627
// IDA: void __usercall GetSaveName(int pStarting_to_type@<EAX>, int pCurrent_choice@<EDX>, char *pString@<EBX>, int *pMax_length@<ECX>)
628
void GetSaveName(int pStarting_to_type, int pCurrent_choice, char* pString, int* pMax_length) {
629
    LOG_TRACE("(%d, %d, \"%s\", %p)", pStarting_to_type, pCurrent_choice, pString, pMax_length);
630
 
631
    if (gSaved_games[pCurrent_choice] != NULL) {
632
        strcpy(pString, gSaved_games[pCurrent_choice]->slot_name);
633
    } else {
634
        if (pStarting_to_type) {
635
            BlankSlot(pCurrent_choice, strlen(GetMiscString(kMiscString_Empty)), 12);
636
            *pString = '\0';
637
        } else {
638
            strcpy(pString, GetMiscString(kMiscString_Empty));
639
        }
640
    }
641
    *pMax_length = 12;
642
}
643
 
644
// IDA: int __usercall SaveDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
645
int SaveDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
646
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
647
 
648
    if (!pEscaped && pCurrent_choice < 8) {
649
        gProgram_state.last_slot = pCurrent_choice;
650
        MakeSavedGame(&gSaved_games[pCurrent_choice]);
651
        if (!gSave_allowed && gPre_race_saved_game != NULL) {
652
            memcpy(gSaved_games[pCurrent_choice], gPre_race_saved_game, sizeof(tSave_game));
653
        }
654
        GetTypedName(gSaved_games[pCurrent_choice]->slot_name, 12);
655
        gSaved_games[pCurrent_choice]->slot_name[12] = '\0';
656
        SaveTheGame(pCurrent_choice);
657
    }
658
    return pCurrent_choice;
659
}
660
 
661
// IDA: int __usercall SaveGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
662
int SaveGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
663
    char s1[256];
664
    char s2[256];
665
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
666
 
667
    if (gTyping_slot != 0) {
668
        sprintf(s1, VARLZEROINT, 2, gProgram_state.rank);
669
        if (gSaved_games[*pCurrent_choice] == NULL) {
670
            s2[0] = '\0';
671
        } else {
672
            sprintf(s2, VARLZEROINT, 2, gSaved_games[*pCurrent_choice]->rank);
673
        }
674
        ChangeTextTo(gCurrent_graf_data->save_slot_rank_x_offset,
675
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
676
            s1, s2);
677
        sprintf(s1, VARLZEROINT, 6, gProgram_state.credits);
678
        if (gSaved_games[*pCurrent_choice] == NULL) {
679
            s2[0] = '\0';
680
        } else {
681
            sprintf(s2, VARLZEROINT, 6, gSaved_games[*pCurrent_choice]->credits);
682
        }
683
        ChangeTextTo(gCurrent_graf_data->save_slot_credits_x_offset,
684
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
685
            s1, s2);
686
        gStarted_typing = 1;
687
    }
688
    return 1;
689
}
690
 
691
// IDA: int __usercall SaveEscape@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
692
int SaveEscape(int* pCurrent_choice, int* pCurrent_mode) {
693
    char s1[256];
694
    char s2[256];
695
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
696
 
697
    if (gStarted_typing != 0) {
698
        sprintf(s1, VARLZEROINT, 2, gProgram_state.rank);
699
        if (gSaved_games[*pCurrent_choice] == NULL) {
700
            s2[0] = '\0';
701
        } else {
702
            sprintf(s2, VARLZEROINT, 2, gSaved_games[*pCurrent_choice]->rank);
703
        }
704
        ChangeTextTo(gCurrent_graf_data->save_slot_rank_x_offset,
705
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
706
            s2, s1);
707
        sprintf(s1, VARLZEROINT, 6, gProgram_state.credits);
708
        if (gSaved_games[*pCurrent_choice] == NULL) {
709
            s2[0] = '\0';
710
        } else {
711
            sprintf(s2, VARLZEROINT, 6, gSaved_games[*pCurrent_choice]->credits);
712
        }
713
        ChangeTextTo(gCurrent_graf_data->save_slot_credits_x_offset,
714
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
715
            s2, s1);
716
        gStarted_typing = 0;
717
    }
718
    return 1;
719
}
720
 
721
// IDA: int __usercall SaveGameInterface@<EAX>(int pDefault_choice@<EAX>)
722
int SaveGameInterface(int pDefault_choice) {
723
    static tFlicette flicker_on[9] = {
724
        { 74, { 47, 94 }, { 23, 55 } },
725
        { 74, { 47, 94 }, { 44, 106 } },
726
        { 74, { 47, 94 }, { 65, 156 } },
727
        { 74, { 47, 94 }, { 86, 206 } },
728
        { 74, { 47, 94 }, { 107, 257 } },
729
        { 74, { 47, 94 }, { 128, 307 } },
730
        { 74, { 47, 94 }, { 149, 358 } },
731
        { 74, { 47, 94 }, { 170, 408 } },
732
        { 57, { 255, 510 }, { 151, 362 } },
733
    };
734
    static tFlicette flicker_off[9] = {
735
        { 73, { 47, 94 }, { 23, 55 } },
736
        { 73, { 47, 94 }, { 44, 106 } },
737
        { 73, { 47, 94 }, { 65, 156 } },
738
        { 73, { 47, 94 }, { 86, 206 } },
739
        { 73, { 47, 94 }, { 107, 257 } },
740
        { 73, { 47, 94 }, { 128, 307 } },
741
        { 73, { 47, 94 }, { 149, 358 } },
742
        { 73, { 47, 94 }, { 170, 408 } },
743
        { 56, { 255, 510 }, { 151, 362 } },
744
    };
745
    static tFlicette push[9] = {
746
        { 74, { 47, 94 }, { 23, 55 } },
747
        { 74, { 47, 94 }, { 44, 106 } },
748
        { 74, { 47, 94 }, { 65, 156 } },
749
        { 74, { 47, 94 }, { 86, 206 } },
750
        { 74, { 47, 94 }, { 107, 257 } },
751
        { 74, { 47, 94 }, { 128, 307 } },
752
        { 74, { 47, 94 }, { 149, 358 } },
753
        { 74, { 47, 94 }, { 170, 408 } },
754
        { 59, { 255, 510 }, { 151, 362 } },
755
    };
756
    static tMouse_area mouse_areas[9] = {
757
        { { 48, 96 }, { 17, 41 }, { 250, 500 }, { 33, 79 }, 0, 0, 0, NULL },
758
        { { 48, 96 }, { 39, 94 }, { 250, 500 }, { 55, 132 }, 1, 0, 0, NULL },
759
        { { 48, 96 }, { 59, 142 }, { 250, 500 }, { 75, 180 }, 2, 0, 0, NULL },
760
        { { 48, 96 }, { 80, 192 }, { 250, 500 }, { 96, 230 }, 3, 0, 0, NULL },
761
        { { 48, 96 }, { 101, 242 }, { 250, 500 }, { 117, 281 }, 4, 0, 0, NULL },
762
        { { 48, 96 }, { 122, 293 }, { 250, 500 }, { 138, 331 }, 5, 0, 0, NULL },
763
        { { 48, 96 }, { 143, 343 }, { 250, 500 }, { 159, 382 }, 6, 0, 0, NULL },
764
        { { 48, 96 }, { 164, 394 }, { 250, 500 }, { 180, 432 }, 7, 0, 0, NULL },
765
        { { 152, 304 }, { 151, 362 }, { 299, 598 }, { 171, 410 }, 8, 1, 0, NULL },
766
    };
767
    static tRectile recopy_areas[24] = {
768
        {
769
            { 53, 106 },
770
            { 17, 41 },
771
            { 147, 294 },
772
            { 35, 84 },
773
        },
774
        {
775
            { 53, 106 },
776
            { 39, 94 },
777
            { 147, 294 },
778
            { 57, 137 },
779
        },
780
        {
781
            { 53, 106 },
782
            { 59, 142 },
783
            { 147, 294 },
784
            { 77, 185 },
785
        },
786
        {
787
            { 53, 106 },
788
            { 80, 192 },
789
            { 147, 294 },
790
            { 98, 235 },
791
        },
792
        {
793
            { 53, 106 },
794
            { 101, 242 },
795
            { 147, 294 },
796
            { 119, 286 },
797
        },
798
        {
799
            { 53, 106 },
800
            { 122, 293 },
801
            { 147, 294 },
802
            { 140, 336 },
803
        },
804
        {
805
            { 53, 106 },
806
            { 143, 343 },
807
            { 147, 294 },
808
            { 161, 386 },
809
        },
810
        {
811
            { 53, 106 },
812
            { 164, 394 },
813
            { 147, 294 },
814
            { 182, 437 },
815
        },
816
        {
817
            { 166, 332 },
818
            { 17, 41 },
819
            { 180, 360 },
820
            { 35, 84 },
821
        },
822
        {
823
            { 166, 332 },
824
            { 39, 94 },
825
            { 180, 360 },
826
            { 57, 137 },
827
        },
828
        {
829
            { 166, 332 },
830
            { 59, 142 },
831
            { 180, 360 },
832
            { 77, 185 },
833
        },
834
        {
835
            { 166, 332 },
836
            { 80, 192 },
837
            { 180, 360 },
838
            { 98, 235 },
839
        },
840
        {
841
            { 166, 332 },
842
            { 101, 242 },
843
            { 180, 360 },
844
            { 119, 286 },
845
        },
846
        {
847
            { 166, 332 },
848
            { 122, 293 },
849
            { 180, 360 },
850
            { 140, 336 },
851
        },
852
        {
853
            { 166, 332 },
854
            { 143, 343 },
855
            { 180, 360 },
856
            { 161, 386 },
857
        },
858
        {
859
            { 166, 332 },
860
            { 164, 394 },
861
            { 180, 360 },
862
            { 182, 437 },
863
        },
864
        {
865
            { 198, 396 },
866
            { 17, 41 },
867
            { 245, 490 },
868
            { 35, 84 },
869
        },
870
        {
871
            { 198, 396 },
872
            { 39, 94 },
873
            { 245, 490 },
874
            { 57, 137 },
875
        },
876
        {
877
            { 198, 396 },
878
            { 59, 142 },
879
            { 245, 490 },
880
            { 77, 185 },
881
        },
882
        {
883
            { 198, 396 },
884
            { 80, 192 },
885
            { 245, 490 },
886
            { 98, 235 },
887
        },
888
        {
889
            { 198, 396 },
890
            { 101, 242 },
891
            { 245, 490 },
892
            { 119, 286 },
893
        },
894
        {
895
            { 198, 396 },
896
            { 122, 293 },
897
            { 245, 490 },
898
            { 140, 336 },
899
        },
900
        {
901
            { 198, 396 },
902
            { 143, 343 },
903
            { 245, 490 },
904
            { 161, 386 },
905
        },
906
        {
907
            { 198, 396 },
908
            { 164, 394 },
909
            { 245, 490 },
910
            { 182, 437 },
911
        },
912
    };
913
    static tInterface_spec interface_spec = {
914
        0, 50, 0, 0, 0, 0, 2,
915
        { 1, 0 }, { -1, 8 }, { 8, 0 }, { 8, 7 }, { NULL, NULL },
916
        { 1, 0 }, { 1, 0 }, { 8, 0 }, { 8, 7 }, { NULL, NULL },
917
        { -1, -1 }, { -1, 0 }, { 0, 8 }, { 7, 8 }, { NULL, NULL },
918
        { -1, -1 }, { 1, 0 }, { 0, 8 }, { 7, 8 }, { NULL, NULL },
919
        { 1, 1 }, { SaveGoAhead, NULL },
920
        { 1, 1 }, { SaveEscape, NULL },
921
        NULL, NULL, 0, SaveStart, NULL, SaveDone, 1,
922
        { 1, 0 }, GetSaveName, 8, 1,
923
        COUNT_OF(flicker_on), flicker_on, flicker_off, push,
924
        COUNT_OF(mouse_areas), mouse_areas,
925
        COUNT_OF(recopy_areas), recopy_areas
926
    };
927
    LOG_TRACE("(%d)", pDefault_choice);
928
 
929
    gStarted_typing = 0;
930
    return DoInterfaceScreen(&interface_spec, 0, pDefault_choice) < 8;
931
}
932
 
933
// IDA: void __usercall DoSaveGame(int pSave_allowed@<EAX>)
934
void DoSaveGame(int pSave_allowed) {
935
    LOG_TRACE("()");
936
 
937
    if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
938
        DoFeatureUnavailableInDemo();
939
        return;
940
    }
941
 
942
    if (gNet_mode == eNet_mode_none) {
943
        DRS3StopOutletSound(gEffects_outlet);
944
        gProgram_state.saving = 1;
945
        gSave_allowed = pSave_allowed;
946
        LoadSavedGames();
11 pmbaty 947
        LoadFont(kFont_TYPEABLE);
1 pmbaty 948
        if (!pSave_allowed && !ConfirmMidGameSave()) {
949
            gProgram_state.saving = 0;
950
            return;
951
        }
952
        SaveGameInterface(gProgram_state.last_slot);
953
        if (pSave_allowed) {
954
            RunFlic(51);
955
        } else {
956
            FadePaletteDown();
957
        }
958
        DisposeSavedGames();
959
        gProgram_state.saving = 0;
960
    } else {
961
        SuspendPendingFlic();
962
        DoErrorInterface(kMiscString_CannotSaveGameInNetworkPlay);
963
    }
964
}