Initial support for sound code in HE games.

Split HE sound functions into separate file.

svn-id: r19909
This commit is contained in:
Travis Howell 2006-01-05 07:06:47 +00:00
parent c2702891ce
commit e01afb0bd4
16 changed files with 766 additions and 274 deletions

View file

@ -1146,9 +1146,14 @@ void Actor::drawActorCostume(bool hitTestMode) {
if (_vm->_heversion >= 80 && _heNoTalkAnimation == 0 && _animProgress == 0) {
if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
// Get sound var 19 of sound 1, if sound code is active.
// Otherwise choose random animation
setTalkCondition(_vm->_rnd.getRandomNumberRng(1, 10));
int talkState = 0;
if (_vm->_sound->isSoundCodeUsed(1))
talkState = _vm->_sound->getSoundVar(1, 19);
if (talkState == 0)
talkState = _vm->_rnd.getRandomNumberRng(1, 10);
setTalkCondition(talkState);
} else {
setTalkCondition(1);
}
@ -1557,8 +1562,7 @@ void Actor::setActorCostume(int c) {
if (_vm->_heversion >= 71 && _vm->getTalkingActor() == _number) {
if (_vm->_heversion <= 95 || (_vm->_heversion >= 98 && _vm->VAR(_vm->VAR_SKIP_RESET_TALK_ACTOR) == 0)) {
// TODO
// _vm->setTalkingActor(0);
_vm->setTalkingActor(0);
}
}
}

View file

@ -1086,7 +1086,8 @@ protected:
const OpcodeEntryV80he *_opcodesV80he;
int32 _heSBNGId;
int32 _heSndResId;
int _curSndId, _sndOffs1, _sndOffs2;
public:
ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
@ -1102,11 +1103,13 @@ protected:
virtual void clearDrawQueues();
void createSound(int snd1id, int snd2id);
void drawLine(int x1, int y1, int x, int unk1, int unk2, int type, int id);
void drawPixel(int x, int y, int flags);
/* HE version 80 script opcodes */
void o80_loadSBNG();
void o80_createSound();
void o80_getFileSize();
void o80_stringToInt();
void o80_getSoundVar();
@ -1300,7 +1303,7 @@ protected:
void o100_resourceRoutines();
void o100_wizImageOps();
void o100_jumpToScript();
void o100_loadSBNG();
void o100_createSound();
void o100_dim2dim2Array();
void o100_paletteOps();
void o100_jumpToScriptUnk();

View file

@ -42,6 +42,7 @@ MODULE_OBJS := \
scumm/script_v6he.o \
scumm/scumm.o \
scumm/sound.o \
scumm/sound_he.o \
scumm/string.o \
scumm/usage_bits.o \
scumm/util.o \

View file

@ -1005,6 +1005,8 @@ bool ScummEngine::isResourceInUse(int type, int i) const {
return _sound->isSoundInUse(i);
case rtCharset:
return _charset->getCurID() == i;
case rtSpoolBuffer:
return _sound->isSoundRunning(10000 + i);
default:
return false;
}
@ -1348,6 +1350,9 @@ void ScummEngine::allocateArrays() {
allocResTypeData(rtImage, MKID('AWIZ'), _numImages, "images", 1);
allocResTypeData(rtTalkie, MKID('TLKE'), _numTalkies, "talkie", 1);
if (_heversion >= 70) {
allocResTypeData(rtSpoolBuffer, MKID('NONE'), 9, "spool buffer", 0);
}
}
void ScummEngine::dumpResource(const char *tag, int idx, const byte *ptr, int length) {
@ -1577,6 +1582,8 @@ const char *resTypeFromId(int id) {
return "Image";
case rtTalkie:
return "Talkie";
case rtSpoolBuffer:
return "SpoolBuffer";
case rtNumTypes:
return "NumTypes";
default:

View file

@ -1797,4 +1797,119 @@ int ScummEngine_v72he::getSoundResourceSize(int id) {
return size;
}
void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
debug(0, "createSound: snd1id %d snd2id %d", snd1id, snd2id);
byte *snd1Ptr, *snd2Ptr;
byte *sbng1Ptr, *sbng2Ptr;
byte *sdat1Ptr, *sdat2Ptr;
byte *src, *dst, *tmp;
int curOffs, len, offs, size;
if (snd2id == -1) {
_sndOffs1 = 0;
_sndOffs2 = 0;
return;
}
if (snd1id != _curSndId) {
_curSndId = snd1id;
_sndOffs1 = 0;
_sndOffs2 = 0;
}
res.lock(rtSound, snd1id);
res.lock(rtSound, snd2id);
snd1Ptr = getResourceAddress(rtSound, snd1id);
snd2Ptr = getResourceAddress(rtSound, snd2id);
int i;
int chan = -1;
for (i = 0; i < ARRAYSIZE(_sound->_heChannel); i++) {
if (_sound->_heChannel[i].sound == snd1id)
chan = i;
}
sbng1Ptr = heFindResource(MKID('SBNG'), snd1Ptr);
sbng2Ptr = heFindResource(MKID('SBNG'), snd2Ptr);
if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
if (chan != -1 && _sound->_heChannel[chan].codeOffs > 0) {
curOffs = _sound->_heChannel[chan].codeOffs;
src = snd1Ptr + curOffs;
dst = sbng1Ptr + 8;
size = READ_BE_UINT32(sbng1Ptr + 4);
len = sbng1Ptr - snd1Ptr + size - curOffs;
memcpy(dst, src, len);
dst = sbng1Ptr + 8;
while ((offs = READ_LE_UINT16(dst)) != 0)
dst += offs;
} else {
dst = sbng1Ptr + 8;
}
_sound->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8;
tmp = sbng2Ptr + 8;
while ((offs = READ_LE_UINT16(tmp)) != 0) {
tmp += offs;
}
src = sbng2Ptr + 8;
len = tmp - sbng2Ptr - 6;
memcpy(dst, src, len);
int time;
while (READ_LE_UINT16(dst) != 0) {
time = READ_LE_UINT32(dst + 2);
time += _sndOffs2;
size = READ_LE_UINT16(dst);
WRITE_LE_UINT32(dst + 2, time);
dst += size;
}
}
int size1, size2;
sdat1Ptr = heFindResource(MKID('SDAT'), snd1Ptr);
assert(sdat1Ptr);
sdat2Ptr = heFindResource(MKID('SDAT'), snd2Ptr);
assert(sdat2Ptr);
size1 = READ_BE_UINT32(sdat1Ptr + 4) - 8 - _sndOffs1;
size2 = READ_BE_UINT32(sdat2Ptr + 4) - 8;
if (size2 < size1) {
src = sdat2Ptr + 8;
dst = sdat1Ptr + 8 + _sndOffs1;
len = size2;
memcpy(dst, src, len);
_sndOffs1 += size2;
_sndOffs2 += size2;
} else {
src = sdat2Ptr + 8;
dst = sdat1Ptr + 8 + _sndOffs1;
len = size1;
memcpy(dst, src, len);
int tmp3 = size2 - size1;
if (tmp3 != 0) {
// TODO: Additional copy
}
_sndOffs1 += tmp3;
_sndOffs2 += size2;
}
res.unlock(rtSound, snd1id);
res.unlock(rtSound, snd2id);
}
} // End of namespace Scumm

View file

@ -68,7 +68,7 @@ void ScummEngine_v100he::setupOpcodes() {
OPCODE(o6_loadRoomWithEgo),
OPCODE(o6_invalid),
OPCODE(o72_setFilePath),
OPCODE(o100_loadSBNG),
OPCODE(o100_createSound),
/* 18 */
OPCODE(o6_cutscene),
OPCODE(o6_pop),
@ -708,28 +708,25 @@ void ScummEngine_v100he::o100_jumpToScript() {
runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args);
}
void ScummEngine_v100he::o100_loadSBNG() {
// Loads SBNG sound resource
void ScummEngine_v100he::o100_createSound() {
byte subOp = fetchScriptByte();
switch (subOp) {
case 0:
_heSBNGId = pop();
_heSndResId = pop();
break;
case 53:
//loadSBNG(_heSBNGId, -1);
createSound(_heSndResId, -1);
break;
case 92:
// dummy case
break;
case 128:
//loadSBNG(_heSBNGId, pop();
pop();
createSound(_heSndResId, pop());
break;
default:
error("o100_loadSBNG: default case %d", subOp);
error("o100_createSound: default case %d", subOp);
}
debug(1,"o100_loadSBNG stub (%d)",subOp);
}
void ScummEngine_v100he::o100_dim2dimArray() {
@ -1679,7 +1676,8 @@ void ScummEngine_v100he::o100_startSound() {
value = pop();
var = pop();
_heSndSoundId = pop();
debug(0,"o100_startSound: case 29 (snd %d, var %d, value %d)", _heSndSoundId, var, value);
_sound->setSoundVar(_heSndSoundId, var, value);
debug(0,"o100_startSound: case 83 (snd %d, var %d, value %d)", _heSndSoundId, var, value);
break;
case 92:
debug(0, "o100_startSound (ID %d, Offset %d, Channel %d, Flags %d)", _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);

View file

@ -868,9 +868,7 @@ void ScummEngine_v72he::o72_getTimer() {
int cmd = fetchScriptByte();
if (cmd == 10 || cmd == 50) {
checkRange(3, 1, timer, "o72_getTimer: Timer %d out of range(%d)");
int diff = _system->getMillis() - _timers[timer];
push(diff);
push(getHETimer(timer));
} else {
push(0);
}
@ -881,8 +879,7 @@ void ScummEngine_v72he::o72_setTimer() {
int cmd = fetchScriptByte();
if (cmd == 158 || cmd == 61) {
checkRange(3, 1, timer, "o72_setTimer: Timer %d out of range(%d)");
_timers[timer] = _system->getMillis();
setHETimer(timer);
} else {
error("TIMER command %d?", cmd);
}
@ -890,8 +887,7 @@ void ScummEngine_v72he::o72_setTimer() {
void ScummEngine_v72he::o72_getSoundPosition() {
int snd = pop();
push(_sound->getSoundElapsedTime(snd) * 10);
debug(1,"o72_getSoundPosition (%d)", snd);
push(_sound->getSoundPos(snd));
}
void ScummEngine_v72he::o72_startScript() {

View file

@ -436,11 +436,13 @@ void ScummEngine_v70he::o70_startSound() {
value = pop();
var = pop();
_heSndSoundId = pop();
_sound->setSoundVar(_heSndSoundId, var, value);
debug(0,"o70_startSound: case 23 (snd %d, var %d, value %d)", _heSndSoundId, var, value);
break;
case 25:
value = pop();
_heSndSoundId = pop();
debug(0, "o70_startSound: case 25 (ID %d, Offset 0, Channel 0, Flags 8)", _heSndSoundId);
_sound->addSoundToQueue(_heSndSoundId, 0, 0, 8);
case 56:
_heSndFlags |= 16;
@ -467,7 +469,7 @@ void ScummEngine_v70he::o70_startSound() {
_heSndFlags |= 1;
break;
case 255:
debug(1, "o70_startSound (ID %d, Offset %d, Channel %d, Flags %d)", _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
debug(0, "o70_startSound (ID %d, Offset %d, Channel %d, Flags %d)", _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
_sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
_heSndFlags = 0;
break;

View file

@ -129,7 +129,7 @@ void ScummEngine_v80he::setupOpcodes() {
OPCODE(o6_writeWordVar),
/* 44 */
OPCODE(o6_invalid),
OPCODE(o80_loadSBNG),
OPCODE(o80_createSound),
OPCODE(o80_getFileSize),
OPCODE(o6_wordArrayWrite),
/* 48 */
@ -376,28 +376,25 @@ const char *ScummEngine_v80he::getOpcodeDesc(byte i) {
return _opcodesV80he[i].desc;
}
void ScummEngine_v80he::o80_loadSBNG() {
// Loads SBNG sound resource
void ScummEngine_v80he::o80_createSound() {
byte subOp = fetchScriptByte();
switch (subOp) {
case 27:
//loadSBNG(_heSBNGId, pop();
pop();
createSound(_heSndResId, pop());
break;
case 217:
//loadSBNG(_heSBNGId, -1);
createSound(_heSndResId, -1);
break;
case 232:
_heSBNGId = pop();
_heSndResId = pop();
break;
case 255:
// dummy case
break;
default:
error("o80_loadSBNG: default case %d", subOp);
error("o80_createSound: default case %d", subOp);
}
debug(1,"o80_loadSBNG stub (%d)",subOp);
}
void ScummEngine_v80he::o80_getFileSize() {
@ -432,14 +429,9 @@ void ScummEngine_v80he::o80_stringToInt() {
}
void ScummEngine_v80he::o80_getSoundVar() {
// Checks sound variable
int var = pop();
int snd = pop();
int rnd = _rnd.getRandomNumberRng(1, 3);
checkRange(27, 0, var, "Illegal sound variable %d");
push (rnd);
debug(1,"o80_getSoundVar stub (snd %d, var %d)", snd, var);
push(_sound->getSoundVar(snd, var));
}
void ScummEngine_v80he::o80_localizeArrayToRoom() {

View file

@ -127,7 +127,7 @@ void ScummEngine_v90he::setupOpcodes() {
OPCODE(o6_writeWordVar),
/* 44 */
OPCODE(o90_getObjectData),
OPCODE(o80_loadSBNG),
OPCODE(o80_createSound),
OPCODE(o80_getFileSize),
OPCODE(o6_wordArrayWrite),
/* 48 */

View file

@ -1210,7 +1210,7 @@ ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameS
_actorClipOverride.right = 640;
_skipDrawObject = 0;
memset(_timers, 0, sizeof(_timers));
memset(_heTimers, 0, sizeof(_heTimers));
memset(_akosQueue, 0, sizeof(_akosQueue));
_akosQueuePos = 0;
@ -1340,6 +1340,8 @@ ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameS
VAR_SKIP_RESET_TALK_ACTOR = 0xFF;
VAR_MUSIC_CHANNEL = 0xFF;
VAR_SOUND_CHANNEL = 0xFF;
VAR_SOUNDCODE_TMR = 0xFF;
VAR_DEFAULT_SOUND_CHANNEL = 0xFF;
VAR_NUM_SCRIPT_CYCLES = 0xFF;
VAR_SCRIPT_CYCLE = 0xFF;
@ -1605,7 +1607,10 @@ ScummEngine_v72he::ScummEngine_v72he(GameDetector *detector, OSystem *syst, cons
ScummEngine_v80he::ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
: ScummEngine_v72he(detector, syst, gs, md5sum, substResFileNameIndex) {
_heSBNGId = 0;
_heSndResId = 0;
_curSndId = 0;
_sndOffs1 = 0;
_sndOffs2 = 0;
}
ScummEngine_v90he::ScummEngine_v90he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
@ -2489,6 +2494,9 @@ load_game:
_fullRedraw = true;
}
if (_heversion >= 80) {
_sound->processSoundCode();
}
runAllScripts();
checkExecVerbs();
checkAndRunSentenceScript();
@ -2609,6 +2617,17 @@ load_game:
#pragma mark --- SCUMM ---
#pragma mark -
int ScummEngine::getHETimer(int timer) {
checkRange(15, 1, timer, "getHETimer: Timer %d out of range(%d)");
int time = _system->getMillis() - _heTimers[timer];
return time;
}
void ScummEngine::setHETimer(int timer) {
checkRange(15, 1, timer, "setHETimer: Timer %d out of range(%d)");
_heTimers[timer] = _system->getMillis();
}
void ScummEngine::pauseGame() {
pauseDialog();
}

View file

@ -311,8 +311,9 @@ enum ResTypes {
rtRoomImage = 18,
rtImage = 19,
rtTalkie = 20,
rtLast = 20,
rtNumTypes = 21
rtSpoolBuffer = 21,
rtLast = 21,
rtNumTypes = 22
};
class ResourceManager {
@ -1074,14 +1075,19 @@ protected:
bool testGfxOtherUsageBits(int strip, int bit);
public:
uint8 *_hePalettes;
byte _HEV7ActorPalette[256];
byte _roomPalette[256];
byte *_shadowPalette;
bool _skipDrawObject;
int _timers[4];
int _voiceMode;
// HE specific
byte _HEV7ActorPalette[256];
uint8 *_hePalettes;
int _heTimers[16];
int getHETimer(int timer);
void setHETimer(int timer);
protected:
int _shadowPaletteSize;
byte _currentPalette[3 * 256];
@ -1310,7 +1316,7 @@ public:
byte VAR_SAVELOAD_SCRIPT; // V6/V7 (not HE)
byte VAR_SAVELOAD_SCRIPT2; // V6/V7 (not HE)
// V6/V7 specific variables (actually, they are only used in FT and Sam, it seems?)
// V6/V7 specific variables (FT & Sam & Max specific)
byte VAR_CHARSET_MASK;
// V6 specific variables
@ -1328,6 +1334,8 @@ public:
byte VAR_SKIP_RESET_TALK_ACTOR;
byte VAR_MUSIC_CHANNEL;
byte VAR_SOUND_CHANNEL;
byte VAR_SOUNDCODE_TMR;
byte VAR_DEFAULT_SOUND_CHANNEL;
byte VAR_SCRIPT_CYCLE;
byte VAR_NUM_SCRIPT_CYCLES;

View file

@ -29,6 +29,7 @@
#include "scumm/util.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "common/timer.h"
#include "common/util.h"
@ -79,6 +80,7 @@ Sound::Sound(ScummEngine *parent)
_sfxMode(0),
_heMusicTracks(0) {
memset(_heChannel, 0, sizeof(_heChannel));
memset(_soundQue, 0, sizeof(_soundQue));
memset(_soundQue2, 0, sizeof(_soundQue2));
memset(_mouthSyncTimes, 0, sizeof(_mouthSyncTimes));
@ -92,9 +94,16 @@ Sound::~Sound() {
void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) {
if (_vm->VAR_LAST_SOUND != 0xFF)
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
if (heFlags & 16) {
playHESound(sound, heOffset, heChannel, heFlags);
return;
}
// HE music resources are in separate file
if (sound <= _vm->_numSounds)
_vm->ensureResourceLoaded(rtSound, sound);
addSoundToQueue2(sound, heOffset, heChannel, heFlags);
}
@ -140,8 +149,12 @@ void Sound::processSoundQueues() {
heOffset = _soundQue2[_soundQue2Pos].offset;
heChannel = _soundQue2[_soundQue2Pos].channel;
heFlags = _soundQue2[_soundQue2Pos].flags;
if (snd)
playSound(snd, heOffset, heChannel, heFlags);
if (snd) {
if (_vm->_heversion>= 60)
playHESound(snd, heOffset, heChannel, heFlags);
else
playSound(snd);
}
}
while (i < _soundQuePos) {
@ -168,64 +181,7 @@ void Sound::processSoundQueues() {
_soundQuePos = 0;
}
void Sound::setOverrideFreq(int freq) {
_overrideFreq = freq;
}
void Sound::setupHEMusicFile() {
int i, total_size;
char buf[32], buf1[128];
Common::File musicFile;
sprintf(buf, "%s.he4", _vm->getGameName());
if (_vm->_substResFileNameIndex > 0) {
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
strcpy(buf, buf1);
}
if (musicFile.open(buf) == true) {
musicFile.seek(4, SEEK_SET);
total_size = musicFile.readUint32BE();
musicFile.seek(16, SEEK_SET);
_heMusicTracks = musicFile.readUint32LE();
debug(0, "Total music tracks %d", _heMusicTracks);
int musicStart = (_vm->_heversion >= 80) ? 56 : 20;
musicFile.seek(musicStart, SEEK_SET);
_heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
for (i = 0; i < _heMusicTracks; i++) {
_heMusic[i].id = musicFile.readUint32LE();
_heMusic[i].offset = musicFile.readUint32LE();
_heMusic[i].size = musicFile.readUint32LE();
if (_vm->_heversion >= 80) {
musicFile.seek(+9, SEEK_CUR);
} else {
musicFile.seek(+13, SEEK_CUR);
}
}
musicFile.close();
}
}
bool Sound::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
int i;
for (i = 0; i < _heMusicTracks; i++) {
if (_heMusic[i].id == id) {
musicOffs = _heMusic[i].offset;
musicSize = _heMusic[i].size;
return 1;
}
}
return 0;
}
void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
debug(5,"playSound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
void Sound::playSound(int soundID) {
byte *mallocedPtr = NULL;
byte *ptr;
char *sound;
@ -233,56 +189,10 @@ void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
int rate;
byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
if (heChannel == -1) {
heChannel = 1;
}
if (_vm->_heversion >= 70 && soundID > _vm->_numSounds) {
debug(1, "playSound #%d", soundID);
if (soundID >= 10000) {
// Special codes, used in pjgames
return;
}
int music_offs;
char buf[32], buf1[128];
Common::File musicFile;
sprintf(buf, "%s.he4", _vm->getGameName());
if (_vm->_substResFileNameIndex > 0) {
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
strcpy(buf, buf1);
}
if (musicFile.open(buf) == false) {
warning("playSound: Can't open music file %s", buf);
return;
}
if (!getHEMusicDetails(soundID, music_offs, size)) {
debug(0, "playSound: musicID %d not found", soundID);
return;
}
musicFile.seek(music_offs, SEEK_SET);
ptr = (byte *)malloc(size);
musicFile.read(ptr, size);
musicFile.close();
_vm->_mixer->stopID(_currentMusic);
_currentMusic = soundID;
if (_vm->_heversion == 70) {
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], ptr, size, 11025, flags, soundID);
return;
}
// This pointer will be freed at the end of the function
mallocedPtr = ptr;
} else {
debugC(DEBUG_SOUND, "playSound #%d (room %d)", soundID,
_vm->getResourceRoomNr(rtSound, soundID));
ptr = _vm->getResourceAddress(rtSound, soundID);
}
if (!ptr) {
return;
@ -304,7 +214,6 @@ void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
memcpy(sound, ptr, size);
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
}
// WORKAROUND bug # 1311447
else if (READ_UINT32(ptr) == MKID(0x460e200d)) {
// This sound resource occurs in the Macintosh version of Monkey Island.
@ -327,91 +236,6 @@ void Sound::playSound(int soundID, int heOffset, int heChannel, int heFlags) {
memcpy(sound, ptr, size);
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
}
// Support for sound in later Backyard sports games
else if (READ_UINT32(ptr) == MKID('RIFF')) {
uint16 type;
int blockAlign;
size = READ_LE_UINT32(ptr + 4);
Common::MemoryReadStream stream(ptr, size);
if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign)) {
error("playSound: Not a valid WAV file");
}
if (type == 17) {
AudioStream *voxStream = new ADPCMInputStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
sound = (char *)malloc(size * 4);
size = voxStream->readBuffer((int16*)sound, size * 2);
size *= 2; // 16bits.
} else {
// Allocate a sound buffer, copy the data into it, and play
sound = (char *)malloc(size);
memcpy(sound, ptr + stream.pos(), size);
}
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
}
// Support for sound in Humongous Entertainment games
else if (READ_UINT32(ptr) == MKID('DIGI') || READ_UINT32(ptr) == MKID('TALK') || READ_UINT32(ptr) == MKID('HSHD')) {
int priority;
if (READ_UINT32(ptr) == MKID('HSHD')) {
priority = READ_LE_UINT16(ptr + 10);
rate = READ_LE_UINT16(ptr + 14);
ptr += READ_BE_UINT32(ptr + 4);
} else {
priority = READ_LE_UINT16(ptr + 18);
rate = READ_LE_UINT16(ptr + 22);
ptr += 8 + READ_BE_UINT32(ptr + 12);
}
if (READ_UINT32(ptr) == MKID('SBNG')) {
ptr += READ_BE_UINT32(ptr + 4);
}
assert(READ_UINT32(ptr) == MKID('SDAT'));
size = READ_BE_UINT32(ptr+4) - 8;
if (heOffset < 0 || heOffset > size) {
// Occurs when making fireworks in puttmoon
debug(0, "playSound: Invalid sound offset (%d) in sound %d", heOffset, soundID);
heOffset = 0;
}
size -= heOffset;
if (_overrideFreq) {
// Used by the piano in Fatty Bear's Birthday Surprise
rate = _overrideFreq;
_overrideFreq = 0;
}
if (heFlags & 1) {
// TODO
// flags |= Audio::Mixer::FLAG_LOOP;
}
// Allocate a sound buffer, copy the data into it, and play
sound = (char *)malloc(size);
memcpy(sound, ptr + heOffset + 8, size);
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
}
// Support for PCM music in 3DO versions of Humongous Entertainment games
else if (READ_UINT32(ptr) == MKID('MRAW')) {
ptr += 8 + READ_BE_UINT32(ptr+12);
if (READ_UINT32(ptr) != MKID('SDAT'))
return;
size = READ_BE_UINT32(ptr+4) - 8;
rate = 22050;
flags = Audio::Mixer::FLAG_AUTOFREE;
// Allocate a sound buffer, copy the data into it, and play
sound = (char *)malloc(size);
memcpy(sound, ptr + 8, size);
_vm->_mixer->stopID(_currentMusic);
_currentMusic = soundID;
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
}
// Support for sampled sound effects in Monkey Island 1 and 2
else if (READ_UINT32(ptr) == MKID('SBL ')) {
debugC(DEBUG_SOUND, "Using SBL sound effect");
@ -696,30 +520,6 @@ static int compareMP3OffsetTable(const void *a, const void *b) {
return ((const MP3OffsetTable *)a)->org_offset - ((const MP3OffsetTable *)b)->org_offset;
}
void Sound::startHETalkSound(uint32 offset) {
byte *ptr;
int32 size;
if (ConfMan.getBool("speech_mute"))
return;
if (!_sfxFile->isOpen()) {
error("startHETalkSound: Speech file is not open");
return;
}
_sfxMode |= 2;
_vm->res.nukeResource(rtSound, 1);
_sfxFile->seek(offset + 4, SEEK_SET);
size = _sfxFile->readUint32BE() - 8;
_vm->res.createResource(rtSound, 1, size);
ptr = _vm->getResourceAddress(rtSound, 1);
_sfxFile->read(ptr, size);
int channel = (_vm->VAR_SOUND_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_SOUND_CHANNEL) : 0;
addSoundToQueue2(1, 0, channel, 0);
}
void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle) {
int num = 0, i;
int size = 0;
@ -1008,7 +808,13 @@ void Sound::stopSound(int sound) {
if (_vm->_heversion >= 70) {
if ( sound >= 10000) {
_vm->_mixer->stopHandle(_heSoundChannels[sound - 10000]);
int chan = sound - 10000;
_vm->_mixer->stopHandle(_heSoundChannels[chan]);
_heChannel[chan].sound = 0;
_heChannel[chan].priority = 0;
_heChannel[chan].sbngBlock = 0;
_heChannel[chan].codeOffs = 0;
memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
}
} else if (_vm->_heversion >= 60) {
if (sound == -2) {
@ -1033,6 +839,16 @@ void Sound::stopSound(int sound) {
if (_vm->_musicEngine)
_vm->_musicEngine->stopSound(sound);
for (i = 0; i < ARRAYSIZE(_heChannel); i++) {
if (_heChannel[i].sound == sound) {
_heChannel[i].sound = 0;
_heChannel[i].priority = 0;
_heChannel[i].sbngBlock = 0;
_heChannel[i].codeOffs = 0;
memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
}
}
for (i = 0; i < ARRAYSIZE(_soundQue2); i++) {
if (_soundQue2[i].sound == sound) {
_soundQue2[i].sound = 0;
@ -1050,6 +866,9 @@ void Sound::stopAllSounds() {
stopCDTimer();
}
// Clear sound channels for HE games
memset(_heChannel, 0, sizeof(_heChannel));
// Clear the (secondary) sound queue
_soundQue2Pos = 0;
memset(_soundQue2, 0, sizeof(_soundQue2));

View file

@ -95,6 +95,16 @@ protected:
HEMusic *_heMusic;
int16 _heMusicTracks;
public: // Used by createSound()
struct {
int sound;
int codeOffs;
int priority;
int sbngBlock;
int soundVars[27];
int timer;
} _heChannel[9];
public:
Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on
Audio::SoundHandle _heSoundChannels[8];
@ -109,9 +119,8 @@ public:
void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0);
void processSound();
void processSoundQueues();
void setOverrideFreq(int freq);
void playSound(int soundID, int heOffset, int heChannel, int heFlags);
void startHETalkSound(uint32 offset);
void playSound(int soundID);
void startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle = NULL);
void stopTalkSound();
bool isMouthSyncOff(uint pos);
@ -134,8 +143,19 @@ public:
void updateCD();
int getCurrentCDSound() const { return _currentCDSound; }
void setupHEMusicFile();
// HE specific
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
int isSoundCodeUsed(int sound);
int getSoundPos(int sound);
int getSoundPriority(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
void playHESound(int soundID, int heOffset, int heChannel, int heFlags);
void processSoundCode();
void processSoundOpcodes(int sound, byte *codePtr, int *soundVars);
void setOverrideFreq(int freq);
void setupHEMusicFile();
void startHETalkSound(uint32 offset);
// Used by the save/load system:
void saveLoadWithSerializer(Serializer *ser);

506
scumm/sound_he.cpp Normal file
View file

@ -0,0 +1,506 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2005 The ScummVM project
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Header$
*
*/
#include "common/stdafx.h"
#include "scumm/actor.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/util.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "common/timer.h"
#include "common/util.h"
#include "sound/adpcm.h"
#include "sound/audiocd.h"
#include "sound/flac.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
#include "sound/mp3.h"
#include "sound/voc.h"
#include "sound/vorbis.h"
#include "sound/wave.h"
namespace Scumm {
int Sound::isSoundCodeUsed(int sound) {
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
if (_heChannel[i].sound == sound)
chan = i;
}
if (chan != -1) {
return _heChannel[chan].sbngBlock;
} else {
return 0;
}
}
int Sound::getSoundPos(int sound) {
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
if (_heChannel[i].sound == sound)
chan = i;
}
if (chan != -1) {
int time = _vm->getHETimer(chan + 4) * 11025 / 1000;
return time;
} else {
return 0;
}
}
int Sound::getSoundPriority(int sound) {
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
if (_heChannel[i].sound == sound)
chan = i;
}
if (chan != -1) {
return _heChannel[chan].priority;
} else {
return 0;
}
}
int Sound::getSoundVar(int sound, int var) {
checkRange(25, 0, var, "Illegal sound variable %d");
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
if (_heChannel[i].sound == sound)
chan = i;
}
if (chan != -1) {
//debug(1, "getSoundVar: sound %d var %d result %d\n", sound, var, _heChannel[chan].soundVars[var]);
return _heChannel[chan].soundVars[var];
} else {
return 0;
}
}
void Sound::setSoundVar(int sound, int var, int val) {
checkRange(25, 0, var, "Illegal sound variable %d");
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
if (_heChannel[i].sound == sound)
chan = i;
}
if (chan != -1) {
_heChannel[chan].soundVars[var] = val;
}
}
void Sound::setOverrideFreq(int freq) {
_overrideFreq = freq;
}
void Sound::setupHEMusicFile() {
int i, total_size;
char buf[32], buf1[128];
Common::File musicFile;
sprintf(buf, "%s.he4", _vm->getGameName());
if (_vm->_substResFileNameIndex > 0) {
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
strcpy(buf, buf1);
}
if (musicFile.open(buf) == true) {
musicFile.seek(4, SEEK_SET);
total_size = musicFile.readUint32BE();
musicFile.seek(16, SEEK_SET);
_heMusicTracks = musicFile.readUint32LE();
debug(0, "Total music tracks %d", _heMusicTracks);
int musicStart = (_vm->_heversion >= 80) ? 56 : 20;
musicFile.seek(musicStart, SEEK_SET);
_heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
for (i = 0; i < _heMusicTracks; i++) {
_heMusic[i].id = musicFile.readUint32LE();
_heMusic[i].offset = musicFile.readUint32LE();
_heMusic[i].size = musicFile.readUint32LE();
if (_vm->_heversion >= 80) {
musicFile.seek(+9, SEEK_CUR);
} else {
musicFile.seek(+13, SEEK_CUR);
}
}
musicFile.close();
}
}
bool Sound::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
int i;
for (i = 0; i < _heMusicTracks; i++) {
if (_heMusic[i].id == id) {
musicOffs = _heMusic[i].offset;
musicSize = _heMusic[i].size;
return 1;
}
}
return 0;
}
void Sound::processSoundCode() {
byte *codePtr;
int chan, tmr, size, time;
for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
if (_heChannel[chan].sound == 0) {
continue;
}
if (_heChannel[chan].codeOffs == -1) {
continue;
}
tmr = _vm->getHETimer(chan + 4) * 11025 / 1000;
tmr += _vm->VAR(_vm->VAR_SOUNDCODE_TMR);
if (tmr < 0)
tmr = 0;
if (_heChannel[chan].sound > _vm->_numSounds) {
codePtr = _vm->getResourceAddress(rtSpoolBuffer, chan);
} else {
codePtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
}
assert(codePtr);
codePtr += _heChannel[chan].codeOffs;
while(1) {
size = READ_LE_UINT16(codePtr);
time = READ_LE_UINT32(codePtr + 2);
if (size == 0) {
_heChannel[chan].codeOffs = -1;
break;
}
debug(1, "Channel %d Timer %d Time %d", chan,tmr, time);
if (time >= tmr)
break;
processSoundOpcodes(_heChannel[chan].sound, codePtr + 6, _heChannel[chan].soundVars);
codePtr += size;
_heChannel[chan].codeOffs += size;
}
}
}
void Sound::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
int edi, opcode, var, val;
while(READ_LE_UINT16(codePtr) != 0) {
codePtr += 2;
opcode = READ_LE_UINT16(codePtr); codePtr += 2;
opcode &= ~0xF000;
opcode /= 16;
edi = opcode;
opcode &= ~3;
edi &= 3;
debug(1, "processSoundOpcodes: sound %d opcode %d", sound, opcode);
switch (opcode) {
case 0: // Continue
break;
case 16: // Set talk state
val = READ_LE_UINT16(codePtr); codePtr += 2;
setSoundVar(sound, 19, val);
break;
case 32: // Set var
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (edi == 2) {
val = getSoundVar(sound, val);
}
setSoundVar(sound, var, val);
break;
case 48: // Add
var = READ_LE_UINT16(codePtr); codePtr += 2;;
val = READ_LE_UINT16(codePtr); codePtr += 2;;
val = getSoundVar(sound, var) + val;
setSoundVar(sound, var, val);
break;
case 56: // Subtract
var = READ_LE_UINT16(codePtr); codePtr += 2;;
val = READ_LE_UINT16(codePtr); codePtr += 2;;
val = getSoundVar(sound, var) - val;
setSoundVar(sound, var, val);
break;
case 64: // Multiple
var = READ_LE_UINT16(codePtr); codePtr += 2;;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (edi == 2) {
val = getSoundVar(sound, val);
}
val = getSoundVar(sound, var) * val;
setSoundVar(sound, var, val);
break;
case 80: // Divide
var = READ_LE_UINT16(codePtr); codePtr += 2;;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (edi == 2) {
val = getSoundVar(sound, val);
}
val = getSoundVar(sound, var) / val;
setSoundVar(sound, var, val);
break;
case 96: // Increment
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = getSoundVar(sound, var) + 1;
setSoundVar(sound, var, val);
break;
case 104: // Decrement
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = getSoundVar(sound, var) - 1;
setSoundVar(sound, var, val);
break;
default:
error("Illegal sound %d opcode %d", sound, opcode);
}
}
}
void Sound::playHESound(int soundID, int heOffset, int heChannel, int heFlags) {
debug(0,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
byte *mallocedPtr = NULL;
byte *ptr, *spoolPtr;
char *sound;
int size = -1;
int rate;
byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
if (heChannel == -1) {
if (_vm->_heversion >= 95 && _vm->VAR(_vm->VAR_DEFAULT_SOUND_CHANNEL) != 0)
heChannel = _vm->VAR(_vm->VAR_DEFAULT_SOUND_CHANNEL);
else
heChannel = 1;
}
if (soundID > _vm->_numSounds) {
if (soundID >= 10000) {
// Special codes, used in pjgames
return;
}
int music_offs;
char buf[32], buf1[128];
Common::File musicFile;
sprintf(buf, "%s.he4", _vm->getGameName());
if (_vm->_substResFileNameIndex > 0) {
_vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
strcpy(buf, buf1);
}
if (musicFile.open(buf) == false) {
warning("playSound: Can't open music file %s", buf);
return;
}
if (!getHEMusicDetails(soundID, music_offs, size)) {
debug(0, "playSound: musicID %d not found", soundID);
return;
}
musicFile.seek(music_offs, SEEK_SET);
if (_vm->_heversion == 70) {
spoolPtr = (byte *)malloc(size);
musicFile.read(spoolPtr, size);
} else {
spoolPtr = _vm->res.createResource(rtSpoolBuffer, heChannel, size);
assert(spoolPtr);
musicFile.read(spoolPtr, size);
}
musicFile.close();
_vm->_mixer->stopID(_currentMusic);
_currentMusic = soundID;
if (_vm->_heversion == 70) {
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], spoolPtr, size, 11025, flags, soundID);
return;
}
// This pointer will be freed at the end of the function
mallocedPtr = spoolPtr;
}
if (soundID > _vm->_numSounds) {
ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel);
} else {
ptr = _vm->getResourceAddress(rtSound, soundID);
}
if (!ptr) {
return;
}
// Support for sound in later Backyard sports games
if (READ_UINT32(ptr) == MKID('RIFF')) {
uint16 type;
int blockAlign;
size = READ_LE_UINT32(ptr + 4);
Common::MemoryReadStream stream(ptr, size);
if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign)) {
error("playSound: Not a valid WAV file");
}
if (type == 17) {
AudioStream *voxStream = new ADPCMInputStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
sound = (char *)malloc(size * 4);
size = voxStream->readBuffer((int16*)sound, size * 2);
size *= 2; // 16bits.
} else {
// Allocate a sound buffer, copy the data into it, and play
sound = (char *)malloc(size);
memcpy(sound, ptr + stream.pos(), size);
}
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
}
// Support for sound in Humongous Entertainment games
else if (READ_UINT32(ptr) == MKID('DIGI') || READ_UINT32(ptr) == MKID('TALK') || READ_UINT32(ptr) == MKID('HSHD')) {
byte *sndPtr = ptr;
int priority;
if (READ_UINT32(ptr) == MKID('HSHD')) {
priority = READ_LE_UINT16(ptr + 10);
rate = READ_LE_UINT16(ptr + 14);
ptr += READ_BE_UINT32(ptr + 4);
} else {
priority = READ_LE_UINT16(ptr + 18);
rate = READ_LE_UINT16(ptr + 22);
ptr += 8 + READ_BE_UINT32(ptr + 12);
}
//if (_vm->_mixer->isSoundHandleActive(_heSoundChannels[heChannel]) && _heChannel[heChannel].priority > priority)
// return;
int codeOffs = -1;
if (READ_UINT32(ptr) == MKID('SBNG')) {
codeOffs = ptr - sndPtr + 8;
ptr += READ_BE_UINT32(ptr + 4);
}
assert(READ_UINT32(ptr) == MKID('SDAT'));
size = READ_BE_UINT32(ptr+4) - 8;
if (heOffset < 0 || heOffset > size) {
// Occurs when making fireworks in puttmoon
debug(0, "playSound: Invalid sound offset (offset %d, size %d) in sound %d", heOffset, size, soundID);
heOffset = 0;
}
size -= heOffset;
if (_overrideFreq) {
// Used by the piano in Fatty Bear's Birthday Surprise
rate = _overrideFreq;
_overrideFreq = 0;
}
if (heFlags & 1) {
flags |= Audio::Mixer::FLAG_LOOP;
}
// Allocate a sound buffer, copy the data into it, and play
sound = (char *)malloc(size);
memcpy(sound, ptr + heOffset + 8, size);
//_vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
_vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
_vm->setHETimer(heChannel + 4);
_heChannel[heChannel].sound = soundID;
_heChannel[heChannel].priority = priority;
_heChannel[heChannel].timer = 0;
_heChannel[heChannel].sbngBlock = (codeOffs != 0) ? 1 : 0;
_heChannel[heChannel].codeOffs = codeOffs;
memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
}
// Support for PCM music in 3DO versions of Humongous Entertainment games
else if (READ_UINT32(ptr) == MKID('MRAW')) {
ptr += 8 + READ_BE_UINT32(ptr+12);
if (READ_UINT32(ptr) != MKID('SDAT'))
return;
size = READ_BE_UINT32(ptr+4) - 8;
rate = 22050;
flags = Audio::Mixer::FLAG_AUTOFREE;
// Allocate a sound buffer, copy the data into it, and play
sound = (char *)malloc(size);
memcpy(sound, ptr + 8, size);
_vm->_mixer->stopID(_currentMusic);
_currentMusic = soundID;
_vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
}
else {
//if (_vm->_musicEngine) {
// _vm->_musicEngine->startSound(soundID);
//}
}
free(mallocedPtr);
}
void Sound::startHETalkSound(uint32 offset) {
byte *ptr;
int32 size;
if (ConfMan.getBool("speech_mute"))
return;
if (!_sfxFile->isOpen()) {
error("startHETalkSound: Speech file is not open");
return;
}
_sfxMode |= 2;
_vm->res.nukeResource(rtSound, 1);
_sfxFile->seek(offset + 4, SEEK_SET);
size = _sfxFile->readUint32BE() - 8;
_vm->res.createResource(rtSound, 1, size);
ptr = _vm->getResourceAddress(rtSound, 1);
_sfxFile->read(ptr, size);
int channel = (_vm->VAR_SOUND_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_SOUND_CHANNEL) : 0;
addSoundToQueue2(1, 0, channel, 0);
}
} // End of namespace Scumm

View file

@ -298,6 +298,7 @@ void ScummEngine_v72he::setupScummVars() {
VAR_PLATFORM = 78; // 1 is PC, 2 is Macintosh
VAR_WINDOWS_VERSION = 79; // 31 is Windows 3.1, 40 is Windows 95+
VAR_CURRENT_CHARSET = 80;
VAR_SOUNDCODE_TMR = 84;
VAR_KEY_STATE = 86;
VAR_NUM_SOUND_CHANNELS = 88;
if (_heversion >= 90) {
@ -310,6 +311,7 @@ void ScummEngine_v72he::setupScummVars() {
VAR_U32_VERSION = 107;
VAR_U32_ARRAY_UNK = 116;
VAR_WIZ_TCOLOR = 117;
VAR_DEFAULT_SOUND_CHANNEL = 120;
}
if (_heversion >= 98) {
VAR_SKIP_RESET_TALK_ACTOR = 125;