/
erlang.spawn
/
fallout2-ce
Обзор
Документация
Войти
/
erlang.spawn
/
fallout2-ce
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/lips.cc
475 строк
13 KB
Mike Klaas
Add original symbol beside all function and symbol offsets (#332)
12 май 2026, 05:49
Не верифицирован
12 май 2026, 05:49
7ae0452
Код
Авторство
О чём код?
#include "lips.h" #include <stdio.h> #include <string.h> #include "audio.h" #include "db.h" #include "debug.h" #include "game_sound.h" #include "input.h" #include "memory.h" #include "platform_compat.h" #include "sound.h" #include "svga.h" namespace fallout { static char* lips_fix_string(const char* fileName, size_t length); static int lipsReadV1(LipsData* lipsData, File* stream); static int _lips_make_speech(); // 0x519240 head_phoneme_current unsigned char gLipsCurrentPhoneme = 0; // 0x519241 head_phoneme_drawn static unsigned char gLipsPreviousPhoneme = 0; // 0x519244 head_marker_current static int _head_marker_current = 0; // 0x519248 lips_draw_head bool gLipsPhonemeChanged = true; // 0x51924C lip_info LipsData gLipsData = { 2, 22528, 0, nullptr, -1, nullptr, nullptr, 0, 0, 0, 0, 0, nullptr, 0, 0, 50, 100, 0, 0, 0, "TEST", "VOC", "TXT", "LIP", }; // 0x5193B4 speechStartTime static int _speechStartTime = 0; // 0x613CA0 lips_subdir_name static char _lips_subdir_name[14]; // 0x47AAC0 lips_fix_string static char* lips_fix_string(const char* fileName, size_t length) { // 0x613CAE static char tmp_str[50]; strncpy(tmp_str, fileName, length); tmp_str[length] = '\0'; return tmp_str; } // 0x47AAD8 lips_bkg_proc void lipsTicker() { int v0; SpeechMarker* speech_marker; int v5; v0 = _head_marker_current; if ((gLipsData.flags & LIPS_FLAG_0x02) != 0) { int v1 = _soundGetPosition(gLipsData.sound); speech_marker = &(gLipsData.markers[v0]); while (v1 > speech_marker->position) { gLipsCurrentPhoneme = gLipsData.phonemes[v0]; v0++; if (v0 >= gLipsData.field_2C) { v0 = 0; gLipsCurrentPhoneme = gLipsData.phonemes[0]; if ((gLipsData.flags & LIPS_FLAG_0x01) == 0) { _head_marker_current = 0; soundStop(gLipsData.sound); v0 = _head_marker_current; gLipsData.flags &= ~(LIPS_FLAG_0x01 | LIPS_FLAG_0x02); } break; } speech_marker = &(gLipsData.markers[v0]); } if (v0 >= gLipsData.field_2C - 1) { _head_marker_current = v0; v5 = 0; if (gLipsData.field_2C <= 5) { debugPrint("Error: Too few markers to stop speech!"); } else { v5 = 3; } speech_marker = &(gLipsData.markers[v5]); if (v1 < speech_marker->position) { v0 = 0; gLipsCurrentPhoneme = gLipsData.phonemes[0]; if ((gLipsData.flags & LIPS_FLAG_0x01) == 0) { _head_marker_current = 0; soundStop(gLipsData.sound); v0 = _head_marker_current; gLipsData.flags &= ~(LIPS_FLAG_0x01 | LIPS_FLAG_0x02); } } } } if (gLipsPreviousPhoneme != gLipsCurrentPhoneme) { gLipsPreviousPhoneme = gLipsCurrentPhoneme; gLipsPhonemeChanged = true; } _head_marker_current = v0; soundContinueAll(); } // 0x47AC2C lips_play_speech int lipsStart() { gLipsData.flags |= LIPS_FLAG_0x02; _head_marker_current = 0; if (_soundSetPosition(gLipsData.sound, gLipsData.field_20) != 0) { debugPrint("Failed set of start_offset!\n"); } int v2 = _head_marker_current; while (1) { _head_marker_current = v2; SpeechMarker* speechEntry = &(gLipsData.markers[v2]); if (gLipsData.field_20 <= speechEntry->position) { break; } v2++; gLipsCurrentPhoneme = gLipsData.phonemes[v2]; } int speechVolume = speechGetVolume(); soundSetVolume(gLipsData.sound, (int)(speechVolume * 0.69)); _speechStartTime = getTicks(); if (soundPlay(gLipsData.sound) != 0) { debugPrint("Failed play!\n"); _head_marker_current = 0; soundStop(gLipsData.sound); gLipsData.flags |= ~(LIPS_FLAG_0x01 | LIPS_FLAG_0x02); } return 0; } // 0x47AD98 lips_read_lipsynch_info static int lipsReadV1(LipsData* lipsData, File* stream) { int sound; int field_14; int phonemes; int markers; if (fileReadInt32(stream, &(lipsData->version)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_4)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->flags)) == -1) return -1; if (fileReadInt32(stream, &(sound)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_10)) == -1) return -1; if (fileReadInt32(stream, &(field_14)) == -1) return -1; if (fileReadInt32(stream, &(phonemes)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_1C)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_20)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_24)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_28)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_2C)) == -1) return -1; if (fileReadInt32(stream, &(markers)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_34)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_38)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_3C)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_40)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_44)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_48)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_4C)) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->file_name, 8) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_58, 4) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_5C, 4) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_60, 4) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_64, 260) == -1) return -1; // NOTE: Original code is different. For unknown reason it assigns values // from file (integers) and treat them as pointers, which is obviously wrong // is in this case. lipsData->sound = nullptr; lipsData->field_14 = nullptr; lipsData->phonemes = nullptr; lipsData->markers = nullptr; return 0; } // lips_load_file // 0x47AFAC lips_load_file int lipsLoad(const char* audioFileName, const char* headFileName) { char* sep; int i; char audioBaseName[16]; SpeechMarker* speech_marker; SpeechMarker* prev_speech_marker; char path[260]; strcpy(path, "SOUND\\SPEECH\\"); strcpy(_lips_subdir_name, headFileName); strcat(path, _lips_subdir_name); strcat(path, "\\"); sep = strchr(path, '.'); if (sep != nullptr) { *sep = '\0'; } strcpy(audioBaseName, audioFileName); sep = strchr(audioBaseName, '.'); if (sep != nullptr) { *sep = '\0'; } strncpy(gLipsData.file_name, audioBaseName, sizeof(gLipsData.file_name)); strcat(path, lips_fix_string(gLipsData.file_name, sizeof(gLipsData.file_name))); strcat(path, "."); strcat(path, gLipsData.field_60); lipsFree(); // FIXME: stream is not closed if any error is encountered during reading. File* stream = fileOpen(path, "rb"); if (stream != nullptr) { if (fileReadInt32(stream, &(gLipsData.version)) == -1) { return -1; } if (gLipsData.version == 1) { debugPrint("\nLoading old save-file version (1)"); if (fileSeek(stream, 0, SEEK_SET) != 0) { return -1; } if (lipsReadV1(&gLipsData, stream) != 0) { return -1; } } else if (gLipsData.version == 2) { debugPrint("\nLoading current save-file version (2)"); if (fileReadInt32(stream, &(gLipsData.field_4)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.flags)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_10)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_1C)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_24)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_28)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_2C)) == -1) return -1; if (fileReadFixedLengthString(stream, gLipsData.file_name, 8) == -1) return -1; if (fileReadFixedLengthString(stream, gLipsData.field_58, 4) == -1) return -1; } else { debugPrint("\nError: Lips file WRONG version: %s!", path); } } gLipsData.phonemes = (unsigned char*)internal_malloc(gLipsData.field_24); if (gLipsData.phonemes == nullptr) { debugPrint("Out of memory in lips_load_file.'\n"); return -1; } if (stream != nullptr) { for (i = 0; i < gLipsData.field_24; i++) { if (fileReadUInt8(stream, &(gLipsData.phonemes[i])) == -1) { debugPrint("lips_load_file: Error reading phoneme type.\n"); return -1; } } for (i = 0; i < gLipsData.field_24; i++) { unsigned char phoneme = gLipsData.phonemes[i]; if (phoneme >= PHONEME_COUNT) { debugPrint("\nLoad error: Speech phoneme %d is invalid (%d)!", i, phoneme); } } } gLipsData.markers = (SpeechMarker*)internal_malloc(sizeof(*speech_marker) * gLipsData.field_2C); if (gLipsData.markers == nullptr) { debugPrint("Out of memory in lips_load_file.'\n"); return -1; } if (stream != nullptr) { for (i = 0; i < gLipsData.field_2C; i++) { speech_marker = &(gLipsData.markers[i]); if (fileReadInt32(stream, &(speech_marker->marker)) == -1) break; if (fileReadInt32(stream, &(speech_marker->position)) == -1) break; } if (i != gLipsData.field_2C) { debugPrint("lips_load_file: Error reading marker type."); return -1; } speech_marker = &(gLipsData.markers[0]); if (speech_marker->marker != 1 && speech_marker->marker != 0) { debugPrint("\nLoad error: Speech marker 0 is invalid (%d)!", speech_marker->marker); } if (speech_marker->position != 0) { debugPrint("Load error: Speech marker 0 has invalid position(%d)!", speech_marker->position); } for (i = 1; i < gLipsData.field_2C; i++) { speech_marker = &(gLipsData.markers[i]); prev_speech_marker = &(gLipsData.markers[i - 1]); if (speech_marker->marker != 1 && speech_marker->marker != 0) { debugPrint("\nLoad error: Speech marker %d is invalid (%d)!", i, speech_marker->marker); } if (speech_marker->position < prev_speech_marker->position) { debugPrint("Load error: Speech marker %d has invalid position(%d)!", i, speech_marker->position); } } } if (stream != nullptr) { fileClose(stream); } gLipsData.field_38 = 0; gLipsData.field_34 = 0; gLipsData.field_48 = 0; gLipsData.field_20 = 0; gLipsData.field_3C = 50; gLipsData.field_40 = 100; if (gLipsData.version == 1) { gLipsData.field_4 = 22528; } strcpy(gLipsData.field_58, "ACM"); strcpy(gLipsData.field_5C, "TXT"); strcpy(gLipsData.field_60, "LIP"); _lips_make_speech(); _head_marker_current = 0; gLipsCurrentPhoneme = gLipsData.phonemes[0]; return 0; } // lips_make_speech // 0x47B5D0 lips_make_speech static int _lips_make_speech() { if (gLipsData.field_14 != nullptr) { internal_free(gLipsData.field_14); gLipsData.field_14 = nullptr; } char path[COMPAT_MAX_PATH]; char* v1 = lips_fix_string(gLipsData.file_name, sizeof(gLipsData.file_name)); snprintf(path, sizeof(path), "%s%s\\%s.%s", "SOUND\\SPEECH\\", _lips_subdir_name, v1, "ACM"); if (gLipsData.sound != nullptr) { soundDelete(gLipsData.sound); gLipsData.sound = nullptr; } gLipsData.sound = soundAllocate(SOUND_TYPE_MEMORY, SOUND_16BIT); if (gLipsData.sound == nullptr) { debugPrint("\nsoundAllocate falied in lips_make_speech!"); return -1; } if (soundSetFileIO(gLipsData.sound, audioOpen, audioClose, audioRead, nullptr, audioSeek, nullptr, audioGetSize)) { debugPrint("Ack!"); debugPrint("Error!"); } if (soundLoad(gLipsData.sound, path)) { soundDelete(gLipsData.sound); gLipsData.sound = nullptr; debugPrint("lips_make_speech: soundLoad failed with path "); debugPrint("%s -- file probably doesn't exist.\n", path); return -1; } gLipsData.field_34 = 8 * (gLipsData.field_1C / gLipsData.field_2C); return 0; } // 0x47B730 lips_free_speech int lipsFree() { if (gLipsData.field_14 != nullptr) { internal_free(gLipsData.field_14); gLipsData.field_14 = nullptr; } if (gLipsData.sound != nullptr) { _head_marker_current = 0; soundStop(gLipsData.sound); gLipsData.flags &= ~0x03; soundDelete(gLipsData.sound); gLipsData.sound = nullptr; } if (gLipsData.phonemes != nullptr) { internal_free(gLipsData.phonemes); gLipsData.phonemes = nullptr; } if (gLipsData.markers != nullptr) { internal_free(gLipsData.markers); gLipsData.markers = nullptr; } return 0; } } // namespace fallout