Subversion Repositories Games.Carmageddon

Rev

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

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