#include "loadsave.h"
 
#include "cutscene.h"
 
#include "errors.h"
 
#include "flicplay.h"
 
#include "globvars.h"
 
#include "globvrpb.h"
 
#include "grafdata.h"
 
#include "graphics.h"
 
#include "harness/config.h"
 
#include "harness/trace.h"
 
#include "init.h"
 
#include "input.h"
 
#include "intrface.h"
 
#include "loading.h"
 
#include "pd/sys.h"
 
#include "sound.h"
 
#include "structur.h"
 
#include "utility.h"
 
#include "world.h"
 
#include <brender/brender.h>
 
#include <stdlib.h>
 
#include <string.h>
 
 
 
tSave_game* gSaved_games[8];
 
int gStarted_typing;
 
int gSave_allowed;
 
 
 
#define SAVEGAME_VERSION 6
 
 
 
#define SWAP32_BE(V)       \
 
    do {                   \
 
        (V) = BrHtoNL(V); \
 
    } while (0)
 
 
 
// IDA: void __usercall CorrectLoadByteOrdering(int pIndex@<EAX>)
 
void CorrectLoadByteOrdering(int pIndex) {
 
    int i;
 
    LOG_TRACE("(%d)", pIndex);
 
 
 
    SWAP32_BE(gSaved_games[pIndex]->version);
 
    SWAP32_BE(gSaved_games[pIndex]->rank);
 
    SWAP32_BE(gSaved_games[pIndex]->credits);
 
    SWAP32_BE(gSaved_games[pIndex]->skill_level);
 
    SWAP32_BE(gSaved_games[pIndex]->frank_or_annitude);
 
    SWAP32_BE(gSaved_games[pIndex]->game_completed);
 
    SWAP32_BE(gSaved_games[pIndex]->current_race_index);
 
    SWAP32_BE(gSaved_games[pIndex]->number_of_cars);
 
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->cars_available); i++) {
 
        SWAP32_BE(gSaved_games[pIndex]->cars_available[i]);
 
    }
 
    SWAP32_BE(gSaved_games[pIndex]->current_car_index);
 
    SWAP32_BE(gSaved_games[pIndex]->redo_race_index);
 
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->power_up_levels); i++) {
 
        SWAP32_BE(gSaved_games[pIndex]->power_up_levels[i]);
 
    }
 
}
 
 
 
// IDA: tU32 __usercall CalcLSChecksum@<EAX>(tSave_game *pSaved_game@<EAX>)
 
tU32 CalcLSChecksum(tSave_game* pSaved_game) {
 
    tU32 checksum;
 
    tU32 checksum2;
 
    int i;
 
    tU8* ptr;
 
    LOG_TRACE("(%p)", pSaved_game);
 
 
 
#ifdef DETHRACE_FIX_BUGS
 
    if (sizeof(tSave_game
) - sizeof(tU32
) != offsetof(tSave_game
, checksum
)) {  
        PDFatalError("checksum must be last element of savegame struct");
 
    }
 
#endif
 
 
 
    checksum = 0;
 
    for (i = 0, ptr = (tU8*)pSaved_game; i < (sizeof(tSave_game) - sizeof(tU32)); i++, ptr++) {
 
        checksum2 = (*ptr ^ 0xbd) + checksum;
 
        checksum = checksum ^ checksum2 << 25 ^ checksum2 >> 7;
 
    }
 
    return checksum;
 
}
 
 
 
// IDA: void __cdecl LoadSavedGames()
 
void LoadSavedGames(void) {
 
    tPath_name the_path;
 
    int i;
 
    FILE* f;
 
    tU32 the_size;
 
    LOG_TRACE("()");
 
 
 
#ifdef DETHRACE_FIX_BUGS
 
    if (sizeof(tSave_game) != 948) {
 
        PDFatalError("Size of tSave_game struct is not correct (should be 948 bytes)");
 
    }
 
#endif
 
 
 
    PathCat(the_path, gApplication_path, "SAVEGAME");
 
    PathCat(the_path, the_path, "SAVEx");
 
    for (i = 0; i < COUNT_OF(gSaved_games); i++) {
 
        the_path
[strlen(the_path
) - 1] = '0' + i
; 
        f = DRfopen(the_path, "rb");
 
        if (f == NULL) {
 
            continue;
 
        }
 
        the_size = GetFileLength(f);
 
        if (the_size == sizeof(tSave_game)) {
 
            gSaved_games[i] = BrMemCalloc(1, sizeof(tSave_game), kMem_saved_game);
 
            fread(gSaved_games
[i
], 1, the_size
, f
);  
            CorrectLoadByteOrdering(i);
 
            if (CalcLSChecksum(gSaved_games[i]) != gSaved_games[i]->checksum || gSaved_games[i]->version != SAVEGAME_VERSION) {
 
                BrMemFree(gSaved_games[i]);
 
                gSaved_games[i] = NULL;
 
            }
 
        } else {
 
            gSaved_games[i] = NULL;
 
        }
 
    }
 
}
 
 
 
// IDA: void __cdecl DisposeSavedGames()
 
void DisposeSavedGames(void) {
 
    int i;
 
    LOG_TRACE("()");
 
 
 
    for (i = 0; i < COUNT_OF(gSaved_games); i++) {
 
        if (gSaved_games[i] != NULL) {
 
            BrMemFree(gSaved_games[i]);
 
        }
 
    }
 
}
 
 
 
// IDA: void __usercall LoadTheGame(int pSlot_index@<EAX>)
 
void LoadTheGame(int pSlot_index) {
 
    int i;
 
    //char the_car_name[14]; // Pierre-Marie Baty -- unused variable
 
    LOG_TRACE("(%d)", pSlot_index);
 
 
 
    if (gProgram_state.car_name[0] != '\0') {
 
        DisposeCar(&gProgram_state.current_car, gProgram_state.current_car.index);
 
        ClearOutStorageSpace(&gOur_car_storage_space);
 
    }
 
    strcpy(gProgram_state.
player_name[0], gSaved_games
[pSlot_index
]->player_name
[0]);  
    strcpy(gProgram_state.
player_name[1], gSaved_games
[pSlot_index
]->player_name
[1]);  
    gProgram_state.current_car_index = gSaved_games[pSlot_index]->current_car_index;
 
    gProgram_state.redo_race_index = gSaved_games[pSlot_index]->redo_race_index;
 
    gProgram_state.frank_or_anniness = gSaved_games[pSlot_index]->frank_or_annitude;
 
    gProgram_state.car_name[0] = '\0';
 
    AboutToLoadFirstCar();
 
    SwitchToRealResolution();
 
    LoadCar(gSaved_games[pSlot_index]->car_name, eDriver_local_human,
 
        &gProgram_state.current_car, gProgram_state.current_car_index,
 
        gProgram_state.player_name[gProgram_state.frank_or_anniness],
 
        &gOur_car_storage_space);
 
    SwitchToLoresMode();
 
    SetCarStorageTexturingLevel(&gOur_car_storage_space, GetCarTexturingLevel(), eCTL_full);
 
    gProgram_state.skill_level = gSaved_games[pSlot_index]->skill_level;
 
    gProgram_state.current_race_index = gSaved_games[pSlot_index]->current_race_index;
 
    InitGame(gProgram_state.current_race_index);
 
    gProgram_state.number_of_cars = gSaved_games[pSlot_index]->number_of_cars;
 
    memcpy(gProgram_state.
cars_available, gSaved_games
[pSlot_index
]->cars_available
, sizeof(gProgram_state.
cars_available));  
    for (i = 0; i < gNumber_of_races; i++) {
 
        gRace_list[i].been_there_done_that = gSaved_games[pSlot_index]->race_info[i].been_there_done_that;
 
    }
 
    for (i = 0; i < gNumber_of_racers; i++) {
 
        gOpponents[i].dead = gSaved_games[pSlot_index]->opponent_info[i].dead;
 
    }
 
    gProgram_state.credits = gSaved_games[pSlot_index]->credits;
 
    gProgram_state.rank = gSaved_games[pSlot_index]->rank;
 
    for (i = 0; i < COUNT_OF(gProgram_state.current_car.power_up_levels); i++) {
 
        gProgram_state.current_car.power_up_levels[i] = gSaved_games[pSlot_index]->power_up_levels[i];
 
    }
 
    gProgram_state.game_completed = gSaved_games[pSlot_index]->game_completed;
 
    SetSoundVolumes();
 
    gProgram_state.saving = 0;
 
    SelectOpponents(&gCurrent_race);
 
}
 
 
 
// IDA: void __cdecl StartRollingSaveNamesIn()
 
void StartRollingSaveNamesIn(void) {
 
    int i;
 
    int save_slot_height;
 
    LOG_TRACE("()");
 
 
 
    for (i = 0; i < COUNT_OF(gSaved_games); i++) {
 
        save_slot_height = gCurrent_graf_data->save_slot_y_offset + i * gCurrent_graf_data->rolling_letter_y_pitch;
 
        SetSlotXY(i, gCurrent_graf_data->save_slot_x_offset, save_slot_height);
 
        if (gSaved_games[i] != NULL) {
 
            AddRollingString(gSaved_games[i]->slot_name, gCurrent_graf_data->save_slot_x_offset, save_slot_height, eRT_alpha);
 
            AddRollingNumber(gSaved_games[i]->rank, 2, gCurrent_graf_data->save_slot_rank_x_offset, save_slot_height);
 
            AddRollingNumber(gSaved_games[i]->credits, 6, gCurrent_graf_data->save_slot_credits_x_offset, save_slot_height);
 
        } else {
 
            AddRollingString(GetMiscString(kMiscString_Empty), gCurrent_graf_data->save_slot_x_offset, save_slot_height, eRT_alpha);
 
        }
 
    }
 
}
 
 
 
// IDA: void __cdecl LoadStart()
 
void LoadStart(void) {
 
    LOG_TRACE("()");
 
 
 
    StartRollingSaveNamesIn();
 
}
 
 
 
// IDA: int __usercall DoLoadGame@<EAX>(int pSave_allowed@<EAX>)
 
int DoLoadGame(void) {
 
    static tFlicette flicker_on[9] = {
 
        { 74, { 47, 94 }, { 23, 55 } },
 
        { 74, { 47, 94 }, { 44, 110 } },
 
        { 74, { 47, 94 }, { 65, 156 } },
 
        { 74, { 47, 94 }, { 86, 206 } },
 
        { 74, { 47, 94 }, { 107, 257 } },
 
        { 74, { 47, 94 }, { 128, 307 } },
 
        { 74, { 47, 94 }, { 149, 358 } },
 
        { 74, { 47, 94 }, { 170, 408 } },
 
        { 57, { 255, 510 }, { 151, 362 } },
 
    };
 
    static tFlicette flicker_off[9] = {
 
        { 73, { 47, 94 }, { 23, 55 } },
 
        { 73, { 47, 94 }, { 44, 110 } },
 
        { 73, { 47, 94 }, { 65, 156 } },
 
        { 73, { 47, 94 }, { 86, 206 } },
 
        { 73, { 47, 94 }, { 107, 257 } },
 
        { 73, { 47, 94 }, { 128, 307 } },
 
        { 73, { 47, 94 }, { 149, 358 } },
 
        { 73, { 47, 94 }, { 170, 408 } },
 
        { 56, { 255, 510 }, { 151, 362 } },
 
    };
 
    static tFlicette push[9] = {
 
        { 74, { 47, 94 }, { 23, 55 } },
 
        { 74, { 47, 94 }, { 44, 110 } },
 
        { 74, { 47, 94 }, { 65, 156 } },
 
        { 74, { 47, 94 }, { 86, 206 } },
 
        { 74, { 47, 94 }, { 107, 257 } },
 
        { 74, { 47, 94 }, { 128, 307 } },
 
        { 74, { 47, 94 }, { 149, 358 } },
 
        { 74, { 47, 94 }, { 170, 408 } },
 
        { 59, { 255, 510 }, { 151, 362 } },
 
    };
 
    static tMouse_area mouse_areas[9] = {
 
        { { 48, 96 }, { 17, 41 }, { 250, 500 }, { 33, 79 }, 0, 0, 0, NULL },
 
        { { 48, 96 }, { 39, 94 }, { 250, 500 }, { 55, 132 }, 1, 0, 0, NULL },
 
        { { 48, 96 }, { 59, 142 }, { 250, 500 }, { 75, 180 }, 2, 0, 0, NULL },
 
        { { 48, 96 }, { 80, 192 }, { 250, 500 }, { 96, 230 }, 3, 0, 0, NULL },
 
        { { 48, 96 }, { 101, 242 }, { 250, 500 }, { 117, 281 }, 4, 0, 0, NULL },
 
        { { 48, 96 }, { 122, 293 }, { 250, 500 }, { 138, 331 }, 5, 0, 0, NULL },
 
        { { 48, 96 }, { 143, 343 }, { 250, 500 }, { 159, 382 }, 6, 0, 0, NULL },
 
        { { 48, 96 }, { 164, 394 }, { 250, 500 }, { 180, 432 }, 7, 0, 0, NULL },
 
        { { 152, 304 }, { 151, 362 }, { 299, 598 }, { 171, 410 }, 8, 1, 0, NULL },
 
    };
 
    static tRectile recopy_areas[24] = {
 
        {
 
            { 53, 106 },
 
            { 17, 41 },
 
            { 147, 294 },
 
            { 35, 84 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 39, 94 },
 
            { 147, 294 },
 
            { 57, 137 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 59, 142 },
 
            { 147, 294 },
 
            { 77, 185 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 80, 192 },
 
            { 147, 294 },
 
            { 98, 235 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 101, 242 },
 
            { 147, 294 },
 
            { 119, 286 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 122, 293 },
 
            { 147, 294 },
 
            { 140, 336 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 143, 343 },
 
            { 147, 294 },
 
            { 161, 386 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 164, 394 },
 
            { 147, 294 },
 
            { 182, 437 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 17, 41 },
 
            { 180, 360 },
 
            { 35, 84 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 39, 94 },
 
            { 180, 360 },
 
            { 57, 137 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 59, 142 },
 
            { 180, 360 },
 
            { 77, 185 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 80, 192 },
 
            { 180, 360 },
 
            { 98, 235 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 101, 242 },
 
            { 180, 360 },
 
            { 119, 286 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 122, 293 },
 
            { 180, 360 },
 
            { 140, 336 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 143, 343 },
 
            { 180, 360 },
 
            { 161, 386 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 164, 394 },
 
            { 180, 360 },
 
            { 182, 370 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 17, 41 },
 
            { 245, 490 },
 
            { 35, 84 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 39, 94 },
 
            { 245, 490 },
 
            { 57, 137 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 59, 142 },
 
            { 245, 490 },
 
            { 77, 185 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 80, 192 },
 
            { 245, 490 },
 
            { 98, 235 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 101, 242 },
 
            { 245, 490 },
 
            { 119, 286 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 122, 293 },
 
            { 245, 490 },
 
            { 140, 336 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 143, 343 },
 
            { 245, 490 },
 
            { 161, 386 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 164, 394 },
 
            { 245, 490 },
 
            { 182, 437 },
 
        },
 
    };
 
    static tInterface_spec interface_spec = {
 
        0,
 
        71,
 
        70,
 
        0,
 
        0,
 
        0,
 
        3,
 
        { 1, 0 },
 
        { -1, 8 },
 
        { 8, 0 },
 
        { 8, 7 },
 
        { NULL, NULL },
 
        { 1, 0 },
 
        { 1, 0 },
 
        { 8, 0 },
 
        { 8, 7 },
 
        { NULL, NULL },
 
        { -1, -1 },
 
        { -1, 0 },
 
        { 0, 8 },
 
        { 7, 8 },
 
        { NULL, NULL },
 
        { -1, -1 },
 
        { 1, 0 },
 
        { 0, 8 },
 
        { 7, 8 },
 
        { NULL, NULL },
 
        { 1, 1 },
 
        { NULL, NULL },
 
        { 1, 1 },
 
        { NULL, NULL },
 
        NULL,
 
        NULL,
 
        0,
 
        LoadStart,
 
        NULL,
 
        NULL,
 
        1,
 
        { 0, 0 },
 
        NULL,
 
        8,
 
        1,
 
        COUNT_OF(flicker_on),
 
        flicker_on,
 
        flicker_off,
 
        push,
 
        COUNT_OF(mouse_areas),
 
        mouse_areas,
 
        COUNT_OF(recopy_areas),
 
        recopy_areas,
 
    };
 
    int result;
 
    LOG_TRACE("()");
 
 
 
    if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
 
        DoFeatureUnavailableInDemo();
 
        return 0;
 
    }
 
 
 
    if (gNet_mode == eNet_mode_none) {
 
        if (!OriginalCarmaCDinDrive()) {
 
            DoErrorInterface(kMiscString_PLEASE_INSERT_THE_CARMAGEDDON_CD);
 
            return 0;
 
        }
 
        gProgram_state.loading = 1;
 
        LoadSavedGames();
 
        if (gGame_to_load >= 0) {
 
            gProgram_state.last_slot = gGame_to_load;
 
            gProgram_state.loaded = 1;
 
            LoadTheGame(gGame_to_load);
 
            gGame_to_load = -1;
 
            gProgram_state.prog_status = eProg_game_starting;
 
            DisposeSavedGames();
 
            gProgram_state.loading = 0;
 
            return 1;
 
        } else {
 
            result = DoInterfaceScreen(&interface_spec, gFaded_palette, gProgram_state.last_slot);
 
            if (result != 8 && gSaved_games[result] == NULL) {
 
                result = 8;
 
                DRS3StartSound(gEffects_outlet, 3100);
 
            }
 
            if (result != 8) {
 
                FadePaletteDown();
 
                if (gProgram_state.racing) {
 
                    gGame_to_load = result;
 
                    gProgram_state.prog_status = eProg_idling;
 
                } else {
 
                    gProgram_state.last_slot = result;
 
                    gProgram_state.loaded = 1;
 
                    LoadTheGame(result);
 
                }
 
            } else {
 
                if (gProgram_state.racing) {
 
                    FadePaletteDown();
 
                } else {
 
                    RunFlic(72);
 
                }
 
            }
 
            gProgram_state.loading = 0;
 
            DisposeSavedGames();
 
            return result != 8;
 
        }
 
    } else {
 
        SuspendPendingFlic();
 
        DoErrorInterface(kMiscString_CannotSaveGameInNetworkPlay);
 
        return 0;
 
    }
 
}
 
 
 
// IDA: void __usercall CorrectSaveByteOrdering(int pIndex@<EAX>)
 
void CorrectSaveByteOrdering(int pIndex) {
 
    int i;
 
    LOG_TRACE("(%d)", pIndex);
 
 
 
    SWAP32_BE(gSaved_games[pIndex]->version);
 
    SWAP32_BE(gSaved_games[pIndex]->rank);
 
    SWAP32_BE(gSaved_games[pIndex]->credits);
 
    SWAP32_BE(gSaved_games[pIndex]->skill_level);
 
    SWAP32_BE(gSaved_games[pIndex]->frank_or_annitude);
 
    SWAP32_BE(gSaved_games[pIndex]->game_completed);
 
    SWAP32_BE(gSaved_games[pIndex]->current_race_index);
 
    SWAP32_BE(gSaved_games[pIndex]->number_of_cars);
 
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->cars_available); i++) {
 
        SWAP32_BE(gSaved_games[pIndex]->cars_available[i]);
 
    }
 
    SWAP32_BE(gSaved_games[pIndex]->current_car_index);
 
    SWAP32_BE(gSaved_games[pIndex]->redo_race_index);
 
    for (i = 0; i < COUNT_OF(gSaved_games[pIndex]->power_up_levels); i++) {
 
        SWAP32_BE(gSaved_games[pIndex]->power_up_levels[i]);
 
    }
 
}
 
 
 
// IDA: void __usercall SaveTheGame(int pSlot_number@<EAX>)
 
void SaveTheGame(int pSlot_number) {
 
    tPath_name the_path;
 
    FILE* f;
 
    LOG_TRACE("(%d)", pSlot_number);
 
 
 
    gSaved_games[pSlot_number]->checksum = CalcLSChecksum(gSaved_games[pSlot_number]);
 
    PathCat(the_path, gApplication_path, "SAVEGAME");
 
    PathCat(the_path, the_path, "SAVEx");
 
    the_path
[strlen(the_path
) - 1] = '0' + pSlot_number
; 
    PDFileUnlock(the_path);
 
    f = DRfopen(the_path, "wb");
 
    if (f != NULL) {
 
        CorrectSaveByteOrdering(pSlot_number);
 
        fwrite(gSaved_games
[pSlot_number
], 1, sizeof(tSave_game
), f
);  
        CorrectLoadByteOrdering(pSlot_number);
 
    }
 
}
 
 
 
// IDA: int __cdecl ConfirmMidGameSave()
 
int ConfirmMidGameSave(void) {
 
    static tFlicette flicker_on[2] = {
 
        { 43, { 84, 168 }, { 124, 298 } },
 
        { 43, { 181, 362 }, { 124, 298 } },
 
    };
 
    static tFlicette flicker_off[2] = {
 
        { 42, { 84, 168 }, { 124, 298 } },
 
        { 42, { 181, 362 }, { 124, 298 } },
 
    };
 
    static tFlicette push[2] = {
 
        { 44, { 84, 168 }, { 124, 298 } },
 
        { 45, { 181, 362 }, { 124, 298 } },
 
    };
 
    static tMouse_area mouse_areas[2] = {
 
        { { 84, 168 }, { 124, 298 }, { 147, 294 }, { 144, 346 }, 0, 0, 0, NULL },
 
        { { 181, 362 }, { 124, 298 }, { 244, 488 }, { 144, 346 }, 1, 0, 0, NULL },
 
    };
 
    static tInterface_spec interface_spec = {
 
        0, 40, 0, 41, -1, -1, 0,
 
        { -1, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
 
        { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
 
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
 
        { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
 
        { 1, 1 }, { NULL, NULL }, { 1, 1 }, { NULL, NULL },
 
        NULL, NULL, 0, NULL, NULL, NULL, 0, { 0, 0 },
 
        NULL, 1, 1,
 
        COUNT_OF(flicker_on), flicker_on, flicker_off, push,
 
        COUNT_OF(mouse_areas), mouse_areas,
 
        0, NULL
 
    };
 
    LOG_TRACE("()");
 
 
 
    return DoInterfaceScreen(&interface_spec, 0, 0) == 0;
 
}
 
 
 
// IDA: void __usercall MakeSavedGame(tSave_game **pSave_record@<EAX>)
 
void MakeSavedGame(tSave_game** pSave_record) {
 
    int i;
 
    LOG_TRACE("(%p)", pSave_record);
 
 
 
    if (*pSave_record == NULL) {
 
        *pSave_record = BrMemCalloc(1, sizeof(tSave_game), kMem_new_save_game);
 
    }
 
    (*pSave_record)->skill_level = gProgram_state.skill_level;
 
    (*pSave_record)->version = SAVEGAME_VERSION;
 
    (*pSave_record)->frank_or_annitude = gProgram_state.frank_or_anniness;
 
    (*pSave_record)->game_completed = gProgram_state.game_completed;
 
    (*pSave_record)->current_race_index = gProgram_state.current_race_index;
 
    for (i = 0; i < gNumber_of_races; i++) {
 
        (*pSave_record)->race_info[i].been_there_done_that = gRace_list[i].been_there_done_that;
 
    }
 
    for (i = 0; i < gNumber_of_racers; i++) {
 
        (*pSave_record)->opponent_info[i].dead = gOpponents[i].dead;
 
    }
 
    (*pSave_record)->credits = gProgram_state.credits;
 
    (*pSave_record)->rank = gProgram_state.rank;
 
    strcpy((*pSave_record
)->car_name
, gProgram_state.
car_name);  
    strcpy((*pSave_record
)->player_name
[0], gProgram_state.
player_name[0]);  
    strcpy((*pSave_record
)->player_name
[1], gProgram_state.
player_name[1]);  
 
 
    (*pSave_record)->number_of_cars = gProgram_state.number_of_cars;
 
    (*pSave_record)->current_car_index = gProgram_state.current_car_index;
 
    (*pSave_record)->redo_race_index = gProgram_state.redo_race_index;
 
    for (i = 0; i < COUNT_OF(gProgram_state.cars_available); i++) {
 
        (*pSave_record)->cars_available[i] = gProgram_state.cars_available[i];
 
    }
 
    for (i = 0; i < COUNT_OF(gProgram_state.current_car.power_up_levels); i++) {
 
        (*pSave_record)->power_up_levels[i] = gProgram_state.current_car.power_up_levels[i];
 
    }
 
}
 
 
 
// IDA: void __cdecl SaveStart()
 
void SaveStart(void) {
 
    LOG_TRACE("()");
 
 
 
    StartRollingSaveNamesIn();
 
}
 
 
 
// IDA: void __usercall GetSaveName(int pStarting_to_type@<EAX>, int pCurrent_choice@<EDX>, char *pString@<EBX>, int *pMax_length@<ECX>)
 
void GetSaveName(int pStarting_to_type, int pCurrent_choice, char* pString, int* pMax_length) {
 
    LOG_TRACE("(%d, %d, \"%s\", %p)", pStarting_to_type, pCurrent_choice, pString, pMax_length);
 
 
 
    if (gSaved_games[pCurrent_choice] != NULL) {
 
        strcpy(pString
, gSaved_games
[pCurrent_choice
]->slot_name
);  
    } else {
 
        if (pStarting_to_type) {
 
            BlankSlot
(pCurrent_choice
, strlen(GetMiscString
(kMiscString_Empty
)), 12); 
            *pString = '\0';
 
        } else {
 
            strcpy(pString
, GetMiscString
(kMiscString_Empty
));  
        }
 
    }
 
    *pMax_length = 12;
 
}
 
 
 
// IDA: int __usercall SaveDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
 
int SaveDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
 
    LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
 
 
 
    if (!pEscaped && pCurrent_choice < 8) {
 
        gProgram_state.last_slot = pCurrent_choice;
 
        MakeSavedGame(&gSaved_games[pCurrent_choice]);
 
        if (!gSave_allowed && gPre_race_saved_game != NULL) {
 
            memcpy(gSaved_games
[pCurrent_choice
], gPre_race_saved_game
, sizeof(tSave_game
));  
        }
 
        GetTypedName(gSaved_games[pCurrent_choice]->slot_name, 12);
 
        gSaved_games[pCurrent_choice]->slot_name[12] = '\0';
 
        SaveTheGame(pCurrent_choice);
 
    }
 
    return pCurrent_choice;
 
}
 
 
 
// IDA: int __usercall SaveGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
 
int SaveGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
 
    char s1[256];
 
    char s2[256];
 
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
 
 
 
    if (gTyping_slot != 0) {
 
        sprintf(s1
, VARLZEROINT
, 2, gProgram_state.
rank);  
        if (gSaved_games[*pCurrent_choice] == NULL) {
 
            s2[0] = '\0';
 
        } else {
 
            sprintf(s2
, VARLZEROINT
, 2, gSaved_games
[*pCurrent_choice
]->rank
);  
        }
 
        ChangeTextTo(gCurrent_graf_data->save_slot_rank_x_offset,
 
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
 
            s1, s2);
 
        sprintf(s1
, VARLZEROINT
, 6, gProgram_state.
credits);  
        if (gSaved_games[*pCurrent_choice] == NULL) {
 
            s2[0] = '\0';
 
        } else {
 
            sprintf(s2
, VARLZEROINT
, 6, gSaved_games
[*pCurrent_choice
]->credits
);  
        }
 
        ChangeTextTo(gCurrent_graf_data->save_slot_credits_x_offset,
 
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
 
            s1, s2);
 
        gStarted_typing = 1;
 
    }
 
    return 1;
 
}
 
 
 
// IDA: int __usercall SaveEscape@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
 
int SaveEscape(int* pCurrent_choice, int* pCurrent_mode) {
 
    char s1[256];
 
    char s2[256];
 
    LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
 
 
 
    if (gStarted_typing != 0) {
 
        sprintf(s1
, VARLZEROINT
, 2, gProgram_state.
rank);  
        if (gSaved_games[*pCurrent_choice] == NULL) {
 
            s2[0] = '\0';
 
        } else {
 
            sprintf(s2
, VARLZEROINT
, 2, gSaved_games
[*pCurrent_choice
]->rank
);  
        }
 
        ChangeTextTo(gCurrent_graf_data->save_slot_rank_x_offset,
 
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
 
            s2, s1);
 
        sprintf(s1
, VARLZEROINT
, 6, gProgram_state.
credits);  
        if (gSaved_games[*pCurrent_choice] == NULL) {
 
            s2[0] = '\0';
 
        } else {
 
            sprintf(s2
, VARLZEROINT
, 6, gSaved_games
[*pCurrent_choice
]->credits
);  
        }
 
        ChangeTextTo(gCurrent_graf_data->save_slot_credits_x_offset,
 
            gCurrent_graf_data->save_slot_y_offset + *pCurrent_choice * gCurrent_graf_data->rolling_letter_y_pitch,
 
            s2, s1);
 
        gStarted_typing = 0;
 
    }
 
    return 1;
 
}
 
 
 
// IDA: int __usercall SaveGameInterface@<EAX>(int pDefault_choice@<EAX>)
 
int SaveGameInterface(int pDefault_choice) {
 
    static tFlicette flicker_on[9] = {
 
        { 74, { 47, 94 }, { 23, 55 } },
 
        { 74, { 47, 94 }, { 44, 106 } },
 
        { 74, { 47, 94 }, { 65, 156 } },
 
        { 74, { 47, 94 }, { 86, 206 } },
 
        { 74, { 47, 94 }, { 107, 257 } },
 
        { 74, { 47, 94 }, { 128, 307 } },
 
        { 74, { 47, 94 }, { 149, 358 } },
 
        { 74, { 47, 94 }, { 170, 408 } },
 
        { 57, { 255, 510 }, { 151, 362 } },
 
    };
 
    static tFlicette flicker_off[9] = {
 
        { 73, { 47, 94 }, { 23, 55 } },
 
        { 73, { 47, 94 }, { 44, 106 } },
 
        { 73, { 47, 94 }, { 65, 156 } },
 
        { 73, { 47, 94 }, { 86, 206 } },
 
        { 73, { 47, 94 }, { 107, 257 } },
 
        { 73, { 47, 94 }, { 128, 307 } },
 
        { 73, { 47, 94 }, { 149, 358 } },
 
        { 73, { 47, 94 }, { 170, 408 } },
 
        { 56, { 255, 510 }, { 151, 362 } },
 
    };
 
    static tFlicette push[9] = {
 
        { 74, { 47, 94 }, { 23, 55 } },
 
        { 74, { 47, 94 }, { 44, 106 } },
 
        { 74, { 47, 94 }, { 65, 156 } },
 
        { 74, { 47, 94 }, { 86, 206 } },
 
        { 74, { 47, 94 }, { 107, 257 } },
 
        { 74, { 47, 94 }, { 128, 307 } },
 
        { 74, { 47, 94 }, { 149, 358 } },
 
        { 74, { 47, 94 }, { 170, 408 } },
 
        { 59, { 255, 510 }, { 151, 362 } },
 
    };
 
    static tMouse_area mouse_areas[9] = {
 
        { { 48, 96 }, { 17, 41 }, { 250, 500 }, { 33, 79 }, 0, 0, 0, NULL },
 
        { { 48, 96 }, { 39, 94 }, { 250, 500 }, { 55, 132 }, 1, 0, 0, NULL },
 
        { { 48, 96 }, { 59, 142 }, { 250, 500 }, { 75, 180 }, 2, 0, 0, NULL },
 
        { { 48, 96 }, { 80, 192 }, { 250, 500 }, { 96, 230 }, 3, 0, 0, NULL },
 
        { { 48, 96 }, { 101, 242 }, { 250, 500 }, { 117, 281 }, 4, 0, 0, NULL },
 
        { { 48, 96 }, { 122, 293 }, { 250, 500 }, { 138, 331 }, 5, 0, 0, NULL },
 
        { { 48, 96 }, { 143, 343 }, { 250, 500 }, { 159, 382 }, 6, 0, 0, NULL },
 
        { { 48, 96 }, { 164, 394 }, { 250, 500 }, { 180, 432 }, 7, 0, 0, NULL },
 
        { { 152, 304 }, { 151, 362 }, { 299, 598 }, { 171, 410 }, 8, 1, 0, NULL },
 
    };
 
    static tRectile recopy_areas[24] = {
 
        {
 
            { 53, 106 },
 
            { 17, 41 },
 
            { 147, 294 },
 
            { 35, 84 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 39, 94 },
 
            { 147, 294 },
 
            { 57, 137 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 59, 142 },
 
            { 147, 294 },
 
            { 77, 185 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 80, 192 },
 
            { 147, 294 },
 
            { 98, 235 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 101, 242 },
 
            { 147, 294 },
 
            { 119, 286 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 122, 293 },
 
            { 147, 294 },
 
            { 140, 336 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 143, 343 },
 
            { 147, 294 },
 
            { 161, 386 },
 
        },
 
        {
 
            { 53, 106 },
 
            { 164, 394 },
 
            { 147, 294 },
 
            { 182, 437 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 17, 41 },
 
            { 180, 360 },
 
            { 35, 84 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 39, 94 },
 
            { 180, 360 },
 
            { 57, 137 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 59, 142 },
 
            { 180, 360 },
 
            { 77, 185 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 80, 192 },
 
            { 180, 360 },
 
            { 98, 235 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 101, 242 },
 
            { 180, 360 },
 
            { 119, 286 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 122, 293 },
 
            { 180, 360 },
 
            { 140, 336 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 143, 343 },
 
            { 180, 360 },
 
            { 161, 386 },
 
        },
 
        {
 
            { 166, 332 },
 
            { 164, 394 },
 
            { 180, 360 },
 
            { 182, 437 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 17, 41 },
 
            { 245, 490 },
 
            { 35, 84 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 39, 94 },
 
            { 245, 490 },
 
            { 57, 137 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 59, 142 },
 
            { 245, 490 },
 
            { 77, 185 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 80, 192 },
 
            { 245, 490 },
 
            { 98, 235 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 101, 242 },
 
            { 245, 490 },
 
            { 119, 286 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 122, 293 },
 
            { 245, 490 },
 
            { 140, 336 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 143, 343 },
 
            { 245, 490 },
 
            { 161, 386 },
 
        },
 
        {
 
            { 198, 396 },
 
            { 164, 394 },
 
            { 245, 490 },
 
            { 182, 437 },
 
        },
 
    };
 
    static tInterface_spec interface_spec = {
 
        0, 50, 0, 0, 0, 0, 2,
 
        { 1, 0 }, { -1, 8 }, { 8, 0 }, { 8, 7 }, { NULL, NULL },
 
        { 1, 0 }, { 1, 0 }, { 8, 0 }, { 8, 7 }, { NULL, NULL },
 
        { -1, -1 }, { -1, 0 }, { 0, 8 }, { 7, 8 }, { NULL, NULL },
 
        { -1, -1 }, { 1, 0 }, { 0, 8 }, { 7, 8 }, { NULL, NULL },
 
        { 1, 1 }, { SaveGoAhead, NULL },
 
        { 1, 1 }, { SaveEscape, NULL },
 
        NULL, NULL, 0, SaveStart, NULL, SaveDone, 1,
 
        { 1, 0 }, GetSaveName, 8, 1,
 
        COUNT_OF(flicker_on), flicker_on, flicker_off, push,
 
        COUNT_OF(mouse_areas), mouse_areas,
 
        COUNT_OF(recopy_areas), recopy_areas
 
    };
 
    LOG_TRACE("(%d)", pDefault_choice);
 
 
 
    gStarted_typing = 0;
 
    return DoInterfaceScreen(&interface_spec, 0, pDefault_choice) < 8;
 
}
 
 
 
// IDA: void __usercall DoSaveGame(int pSave_allowed@<EAX>)
 
void DoSaveGame(int pSave_allowed) {
 
    LOG_TRACE("()");
 
 
 
    if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
 
        DoFeatureUnavailableInDemo();
 
        return;
 
    }
 
 
 
    if (gNet_mode == eNet_mode_none) {
 
        DRS3StopOutletSound(gEffects_outlet);
 
        gProgram_state.saving = 1;
 
        gSave_allowed = pSave_allowed;
 
        LoadSavedGames();
 
        LoadFont(kFont_TYPEABLE);
 
        if (!pSave_allowed && !ConfirmMidGameSave()) {
 
            gProgram_state.saving = 0;
 
            return;
 
        }
 
        SaveGameInterface(gProgram_state.last_slot);
 
        if (pSave_allowed) {
 
            RunFlic(51);
 
        } else {
 
            FadePaletteDown();
 
        }
 
        DisposeSavedGames();
 
        gProgram_state.saving = 0;
 
    } else {
 
        SuspendPendingFlic();
 
        DoErrorInterface(kMiscString_CannotSaveGameInNetworkPlay);
 
    }
 
}