/
erlang.spawn
/
fallout2-ce
Обзор
Документация
Войти
/
erlang.spawn
/
fallout2-ce
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
continious
src/main.cc
634 строки
17 KB
Mike Klaas
Add original symbol beside all function and symbol offsets (#332)
12 май 2026, 05:49
Не верифицирован
12 май 2026, 05:49
7ae0452
Код
Авторство
О чём код?
#include "main.h" #include <limits.h> #include <stdlib.h> #include <string.h> #include "art.h" #include "autorun.h" #include "character_selector.h" #include "color.h" #include "content_config.h" #include "credits.h" #include "cycle.h" #include "db.h" #include "debug.h" #include "draw.h" #include "endgame.h" #include "game.h" #include "game_mouse.h" #include "game_movie.h" #include "game_sound.h" #include "input.h" #include "kb.h" #include "loadsave.h" #include "mainmenu.h" #include "map.h" #include "mouse.h" #include "object.h" #include "palette.h" #include "platform_compat.h" #include "preferences.h" #include "proto.h" #include "random.h" #include "scripts.h" #include "settings.h" #include "sfall_callbacks.h" #include "sfall_global_scripts.h" #include "svga.h" #include "text_font.h" #include "window.h" #include "window_manager.h" #include "window_manager_private.h" #include "word_wrap.h" #include "worldmap.h" namespace fallout { #define DEATH_WINDOW_WIDTH 640 #define DEATH_WINDOW_HEIGHT 480 static bool falloutInit(int argc, char** argv); static int main_reset_system(); static void main_exit_system(); static int _main_load_new(char* fname); static int main_loadgame_new(); static void main_unload_new(); static void mainParseCommandLineArguments(int argc, char** argv); static bool mainTryParseDevLoadGameSlot(const char* value, int* slotPtr); static void mainLoop(); static void showDeath(); static void _main_death_voiceover_callback(); static int _mainDeathGrabTextFile(const char* fileName, char* dest); static int _mainDeathWordWrap(char* text, int width, short* beginnings, short* count); // 0x5194C8 mainMap static char _mainMap[] = "artemple.map"; // 0x5194D8 main_game_paused static int _main_game_paused = 0; // 0x5194E8 main_show_death_scene static bool _main_show_death_scene = false; // 0x614838 main_death_voiceover_done static bool _main_death_voiceover_done; static int commandLineDevLoadGameSlot = -1; // 0x48099C int falloutMain(int argc, char** argv) { if (!autorunMutexCreate()) { return 1; } if (!falloutInit(argc, argv)) { return 1; } mainParseCommandLineArguments(argc, argv); // SFALL: Allow to skip intro movies int skipOpeningMovies = settings.ui.skip_opening_movies; if (skipOpeningMovies < 1) { gameMoviePlay(MOVIE_IPLOGO, GAME_MOVIE_FADE_IN); gameMoviePlay(MOVIE_INTRO, 0); gameMoviePlay(MOVIE_CREDITS, 0); } else { // If the splash is shown but opening movies are skipped, fade it out // before the main menu starts its normal fade-in. paletteFadeTo(gPaletteBlack); } if (mainMenuWindowInit() == 0) { bool done = false; while (!done) { keyboardReset(); _gsound_background_play_level_music("07desert", GSOUND_LIMIT_BEFORE); mainMenuWindowUnhide(true); mouseShowCursor(); int devLoadGameSlot = commandLineDevLoadGameSlot; int mainMenuRc; if (devLoadGameSlot != -1) { commandLineDevLoadGameSlot = -1; mainMenuRc = MAIN_MENU_LOAD_GAME; } else { mainMenuRc = mainMenuWindowHandleEvents(); } mouseHideCursor(); switch (mainMenuRc) { case MAIN_MENU_INTRO: mainMenuWindowHide(true); gameMoviePlay(MOVIE_INTRO, GAME_MOVIE_STOP_MUSIC); gameMoviePlay(MOVIE_CREDITS, 0); break; case MAIN_MENU_NEW_GAME: mainMenuWindowHide(true); mainMenuWindowFree(); if (characterSelectorOpen() == 2) { gameMoviePlay(MOVIE_ELDER, GAME_MOVIE_STOP_MUSIC); randomSeedPrerandom(-1); // SFALL: Call "before start" event sfallOnBeforeGameStart(); // SFALL: Override starting map. char* mapName = nullptr; configGetString(&gContentConfig, CONTENT_CONFIG_START_SECTION, "map", &mapName, nullptr); char* mapNameCopy = compat_strdup(mapName != nullptr ? mapName : _mainMap); _main_load_new(mapNameCopy); free(mapNameCopy); // SFALL: AfterNewGameStartHook. sfall_gl_scr_exec_start_proc(); // SFALL: Call "after loading" event sfallOnAfterNewGame(); sfallOnAfterGameStarted(); gGameLoaded = true; mainLoop(); paletteFadeTo(gPaletteWhite); // NOTE: Uninline. main_unload_new(); // NOTE: Uninline. main_reset_system(); if (_main_show_death_scene != 0) { showDeath(); _main_show_death_scene = 0; } } mainMenuWindowInit(); break; case MAIN_MENU_LOAD_GAME: if (1) { int win = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_MODAL | WINDOW_MOVE_ON_TOP); mainMenuWindowHide(true); mainMenuWindowFree(); // NOTE: Uninline. main_loadgame_new(); if (devLoadGameSlot != -1) { lsgDevSetLoadGameSlot(devLoadGameSlot); } int loadGameRc = lsgLoadGame(LOAD_SAVE_MODE_FROM_MAIN_MENU); if (loadGameRc == -1) { debugPrint("\n ** Error running LoadGame()! **\n"); } else if (loadGameRc != 0) { windowDestroy(win); win = -1; mainLoop(); paletteFadeTo(gPaletteWhite); } if (win != -1) { windowDestroy(win); } // NOTE: Uninline. main_unload_new(); // NOTE: Uninline. main_reset_system(); if (_main_show_death_scene != 0) { showDeath(); _main_show_death_scene = 0; } mainMenuWindowInit(); } break; case MAIN_MENU_TIMEOUT: debugPrint("Main menu timed-out\n"); // FALLTHROUGH case MAIN_MENU_SCREENSAVER: mainMenuWindowHide(true); gameMoviePlay(MOVIE_INTRO, GAME_MOVIE_PAUSE_MUSIC); break; case MAIN_MENU_OPTIONS: mainMenuWindowHide(true); doPreferences(true); break; case MAIN_MENU_CREDITS: mainMenuWindowHide(true); creditsOpen("credits.txt", -1, false); break; case MAIN_MENU_QUOTES: // NOTE: There is a strange cmp at 0x480C50. Both operands are // zero, set before the loop and do not modify afterwards. For // clarity this condition is omitted. mainMenuWindowHide(true); creditsOpen("quotes.txt", -1, true); break; case MAIN_MENU_EXIT: case -1: done = true; mainMenuWindowHide(true); mainMenuWindowFree(); backgroundSoundDelete(); break; case MAIN_MENU_SELFRUN: break; } } } // NOTE: Uninline. main_exit_system(); autorunMutexClose(); return 0; } // 0x480CC0 static bool falloutInit(int argc, char** argv) { // set flag to 1 to initialize _screen_buffer for WINDOW_TRANSPARENT if (gameInitWithOptions("FALLOUT II", false, 0, WINDOW_MANAGER_INIT_FLAG_BUFFERED, argc, argv) == -1) { return false; } return true; } static void mainParseCommandLineArguments(int argc, char** argv) { const char* devLoadGamePrefix = "--dev-load-game="; size_t devLoadGamePrefixLength = strlen(devLoadGamePrefix); for (int arg = 1; arg < argc; arg += 1) { if (strncmp(argv[arg], devLoadGamePrefix, devLoadGamePrefixLength) == 0) { int slot; if (mainTryParseDevLoadGameSlot(argv[arg] + devLoadGamePrefixLength, &slot)) { commandLineDevLoadGameSlot = slot; } else { debugPrint("MAIN: invalid --dev-load-game value '%s'\n", argv[arg] + devLoadGamePrefixLength); } } } } static bool mainTryParseDevLoadGameSlot(const char* value, int* slotPtr) { if (value == nullptr || slotPtr == nullptr) { return false; } char* end = nullptr; long slotNumber = strtol(value, &end, 10); if (end == value || *end != '\0' || slotNumber < 1 || slotNumber > lsgGetTotalSlotCount()) { return false; } *slotPtr = static_cast<int>(slotNumber - 1); return true; } // NOTE: Inlined. // // 0x480D0C static int main_reset_system() { gameReset(); return 1; } // NOTE: Inlined. // // 0x480D18 static void main_exit_system() { backgroundSoundDelete(); gameExit(); } // 0x480D4C static int _main_load_new(char* mapFileName) { _game_user_wants_to_quit = GAME_QUIT_REQUEST_NONE; _main_show_death_scene = 0; gDude->flags &= ~OBJECT_FLAT; objectShow(gDude, nullptr); mouseHideCursor(); int win = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_MODAL | WINDOW_MOVE_ON_TOP); windowRefresh(win); colorPaletteLoad("color.pal"); paletteFadeTo(_cmap); mapInit(); gameMouseSetCursor(MOUSE_CURSOR_NONE); mouseShowCursor(); mapLoadByName(mapFileName); wmMapMusicStart(); paletteFadeTo(gPaletteWhite); windowDestroy(win); colorPaletteLoad("color.pal"); paletteFadeTo(_cmap); return 0; } // NOTE: Inlined. // // 0x480DF8 static int main_loadgame_new() { _game_user_wants_to_quit = GAME_QUIT_REQUEST_NONE; _main_show_death_scene = 0; gDude->flags &= ~OBJECT_FLAT; objectShow(gDude, nullptr); mouseHideCursor(); mapInit(); gameMouseSetCursor(MOUSE_CURSOR_NONE); mouseShowCursor(); return 0; } // 0x480E34 static void main_unload_new() { objectHide(gDude, nullptr); mapExit(); } // 0x480E48 static void mainLoop() { bool cursorWasHidden = cursorIsHidden(); if (cursorWasHidden) { mouseShowCursor(); } _main_game_paused = 0; scriptsEnable(); while (_game_user_wants_to_quit == GAME_QUIT_REQUEST_NONE) { sharedFpsLimiter.mark(); int keyCode = inputGetInput(); // SFALL: MainLoopHook. sfall_gl_scr_process_main(); gameHandleKey(keyCode, false); scriptsHandleRequests(); mapHandleTransition(); if (_main_game_paused != 0) { _main_game_paused = 0; } if ((gDude->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_OUT)) != 0) { endgameSetupDeathEnding(ENDGAME_DEATH_ENDING_REASON_DEATH); _main_show_death_scene = 1; _game_user_wants_to_quit = GAME_QUIT_REQUEST_MAIN_MENU; } renderPresent(); sharedFpsLimiter.throttle(); } scriptsDisable(); if (cursorWasHidden) { mouseHideCursor(); } } // 0x48118C static void showDeath() { artCacheFlush(); colorCycleDisable(); gameMouseSetCursor(MOUSE_CURSOR_NONE); bool oldCursorIsHidden = cursorIsHidden(); if (oldCursorIsHidden) { mouseShowCursor(); } int deathWindowX = (screenGetWidth() - DEATH_WINDOW_WIDTH) / 2; int deathWindowY = (screenGetHeight() - DEATH_WINDOW_HEIGHT) / 2; int win = windowCreate(deathWindowX, deathWindowY, DEATH_WINDOW_WIDTH, DEATH_WINDOW_HEIGHT, 0, WINDOW_MOVE_ON_TOP); if (win != -1) { do { unsigned char* windowBuffer = windowGetBuffer(win); if (windowBuffer == nullptr) { break; } // DEATH.FRM FrmImage backgroundFrmImage; int fid = buildFid(OBJ_TYPE_INTERFACE, 309, 0, 0, 0); if (!backgroundFrmImage.lock(fid)) { break; } while (mouseGetEvent() != 0) { sharedFpsLimiter.mark(); inputGetInput(); renderPresent(); sharedFpsLimiter.throttle(); } keyboardReset(); inputEventQueueReset(); blitBufferToBuffer(backgroundFrmImage.getData(), 640, 480, 640, windowBuffer, 640); backgroundFrmImage.unlock(); const char* deathFileName = endgameDeathEndingGetFileName(); if (settings.preferences.subtitles) { char text[512]; if (_mainDeathGrabTextFile(deathFileName, text) == 0) { debugPrint("\n((ShowDeath)): %s\n", text); short beginnings[WORD_WRAP_MAX_COUNT]; short count; if (_mainDeathWordWrap(text, 560, beginnings, &count) == 0) { unsigned char* p = windowBuffer + 640 * (480 - fontGetLineHeight() * count - 8); bufferFill(p - 602, 564, fontGetLineHeight() * count + 2, 640, 0); p += 40; for (int index = 0; index < count; index++) { fontDrawText(p, text + beginnings[index], 560, 640, _colorTable[32767]); p += 640 * fontGetLineHeight(); } } } } windowRefresh(win); colorPaletteLoad("art\\intrface\\death.pal"); paletteFadeTo(_cmap); _main_death_voiceover_done = false; speechSetEndCallback(_main_death_voiceover_callback); unsigned int delay; if (speechLoad(deathFileName, GSOUND_LOAD_NO_PLAY, GSOUND_STREAM, GSOUND_NO_LOOP) == -1) { delay = 3000; } else { delay = UINT_MAX; } _gsound_speech_play_preloaded(); // SFALL: Fix the playback of the speech sound file for the death // screen. inputBlockForTocks(100); unsigned int time = getTicks(); int keyCode; do { sharedFpsLimiter.mark(); keyCode = inputGetInput(); renderPresent(); sharedFpsLimiter.throttle(); } while (keyCode == -1 && !_main_death_voiceover_done && getTicksSince(time) < delay); speechSetEndCallback(nullptr); speechDelete(); while (mouseGetEvent() != 0) { sharedFpsLimiter.mark(); inputGetInput(); renderPresent(); sharedFpsLimiter.throttle(); } if (keyCode == -1) { inputPauseForTocks(500); } paletteFadeTo(gPaletteBlack); colorPaletteLoad("color.pal"); } while (0); windowDestroy(win); } if (oldCursorIsHidden) { mouseHideCursor(); } gameMouseSetCursor(MOUSE_CURSOR_ARROW); colorCycleEnable(); } // 0x4814A8 static void _main_death_voiceover_callback() { _main_death_voiceover_done = true; } // Read endgame subtitle. // // 0x4814B4 static int _mainDeathGrabTextFile(const char* fileName, char* dest) { const char* p = strrchr(fileName, '\\'); if (p == nullptr) { return -1; } char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "text\\%s\\cuts\\%s%s", settings.system.language.c_str(), p + 1, ".TXT"); File* stream = fileOpen(path, "rt"); if (stream == nullptr) { return -1; } while (true) { int c = fileReadChar(stream); if (c == -1) { break; } if (c == '\n') { c = ' '; } *dest++ = (c & 0xFF); } fileClose(stream); *dest = '\0'; return 0; } // 0x481598 static int _mainDeathWordWrap(char* text, int width, short* beginnings, short* count) { while (true) { char* sep = strchr(text, ':'); if (sep == nullptr) { break; } if (sep - 1 < text) { break; } sep[0] = ' '; sep[-1] = ' '; } if (wordWrap(text, width, beginnings, count) == -1) { return -1; } // TODO: Probably wrong. *count -= 1; for (int index = 1; index < *count; index++) { char* p = text + beginnings[index]; while (p >= text && *p != ' ') { p--; beginnings[index]--; } if (p != nullptr) { *p = '\0'; beginnings[index]++; } } return 0; } } // namespace fallout