From b00c7729cc6d18a674ac3cf2747fadae70f17f64 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 3 Dec 2021 19:16:30 -0600 Subject: [PATCH 01/16] Ensure we don't try to get localized values for null objectnames --- Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp b/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp index 40abbff0..a43cc5e8 100644 --- a/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp +++ b/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp @@ -536,7 +536,7 @@ inline plKeyImp* IFindKeyLocalized(const plUoid& uoid, plRegistryPageNode* page) const char* objectName = uoid.GetObjectName(); // If we're running localized, try to find a localized version first - if (plLocalization::IsLocalized()) + if (objectName != nullptr && plLocalization::IsLocalized()) { char localName[256]; if (plLocalization::GetLocalized(objectName, localName)) From 391bbaa4e79321fd59b7b2199fe6b0c1b9be8e8c Mon Sep 17 00:00:00 2001 From: Edmond Mondor Date: Wed, 15 Dec 2021 09:23:54 -0800 Subject: [PATCH 02/16] Update error messages for new variable --- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp | 4 ++-- Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp index 9e384b3a..61556375 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp @@ -606,12 +606,12 @@ PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtWearDefaultClothingType, args, kw, "Param bool broadcast = false; if (!PyArg_ParseTupleAndKeywords(args, kw, "Ol|b", kwlist, &keyObj, &type, &broadcast)) { - PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long"); + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey, an unsigned long, and an optional bool"); PYTHON_RETURN_ERROR; } if (!pyKey::Check(keyObj)) { - PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long"); + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey, an unsigned long, and an optional bool"); PYTHON_RETURN_ERROR; } pyKey* key = pyKey::ConvertFrom(keyObj); diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp index e5e9f772..0a30675c 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp @@ -536,12 +536,12 @@ PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtWearDefaultClothing, args, kw, "Params: k bool broadcast = false; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|b", kwlist, &keyObj, &broadcast)) { - PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey"); + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey and an optional bool"); PYTHON_RETURN_ERROR; } if (!pyKey::Check(keyObj)) { - PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey"); + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey and an optional bool"); PYTHON_RETURN_ERROR; } pyKey* key = pyKey::ConvertFrom(keyObj); From 62089be53f2a6724101605545f66f695aedc933c Mon Sep 17 00:00:00 2001 From: Edmond Mondor Date: Fri, 7 Jan 2022 11:31:11 -0800 Subject: [PATCH 03/16] Changes needed so climbing works correctly with wall --- .../PubUtilLib/plAvatar/plAnimStage.cpp | 2 +- .../PubUtilLib/plAvatar/plAvBrainClimb.cpp | 4 +-- .../PubUtilLib/plAvatar/plAvBrainHuman.cpp | 26 +++++++++++++------ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp index 0b481513..98a82046 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp @@ -313,7 +313,7 @@ hsBool plAnimStage::ISendNotify(UInt32 notifyMask, UInt32 notifyType, plArmature int stageNum = genBrain ? genBrain->GetStageNum(this) : -1; msg->AddMultiStageEvent(stageNum, notifyType, armature->GetTarget(0)->GetKey()); - if (! genBrain->RelayNotifyMsg(msg) ) + if (stageNum < 0 || !genBrain || !genBrain->RelayNotifyMsg(msg) ) { msg->UnRef(); // couldn't send; destroy... } diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp index 872bfca7..9439ebe8 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp @@ -703,8 +703,8 @@ void plAvBrainClimb::ICalcProbeLengths() { // we assume that the up and down climbs go the same distance; // same for the left and right climbs - plAGAnim *up = fAvMod->FindCustomAnim("ClimbUp"); - plAGAnim *left = fAvMod->FindCustomAnim("ClimbLeft"); + plAGAnim *up = fAvMod->FindCustomAnim("WallClimbUp"); + plAGAnim *left = fAvMod->FindCustomAnim("WallClimbLeft"); hsMatrix44 upMove, leftMove; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp index 6ba9da21..8a2856c5 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -75,6 +75,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../plInputCore/plInputDevice.h" #include "../plMath/plRandom.h" #include "../plPipeline/plDebugText.h" +#include "../plNetClient/plNetClientMgr.h" #include "../plNetClient/plNetLinkingMgr.h" #include "../plMessage/plAvatarMsg.h" @@ -494,12 +495,20 @@ hsBool plAvBrainHuman::IHandleClimbMsg(plClimbMsg *msg) bool isStartClimb = msg->fCommand == plClimbMsg::kStartClimbing; if(isStartClimb) { - // let's build a seek task to get us to the attach point - plKey seekTarget = msg->fTarget; - plAvTaskSeek *seekTask = TRACKED_NEW plAvTaskSeek(seekTarget); - QueueTask(seekTask); - - // now a brain task to start the actual climb. + // Warp the player to the Seekpoint + plSceneObject *avatarObj = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + plSceneObject *obj = plSceneObject::ConvertNoRef(msg->fTarget->ObjectIsLoaded()); + plArmatureMod *localAvatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + plArmatureMod *climbAvatar = plArmatureMod::ConvertNoRef(fArmature); + if (climbAvatar == localAvatar) // is it our avatar who has to seek? + { + hsMatrix44 target = obj->GetLocalToWorld(); + plWarpMsg *warp = TRACKED_NEW plWarpMsg(NULL, avatarObj->GetKey(), plWarpMsg::kFlushTransform, target); + warp->SetBCastFlag(plMessage::kNetPropagate); + plgDispatch::MsgSend(warp); + } + + // build the Climb brain plAvBrainClimb::Mode startMode; switch(msg->fDirection) { @@ -515,10 +524,11 @@ hsBool plAvBrainHuman::IHandleClimbMsg(plClimbMsg *msg) case plClimbMsg::kRight: startMode = plAvBrainClimb::kMountingRight; break; + default: + break; } plAvBrainClimb *brain = TRACKED_NEW plAvBrainClimb(startMode); - plAvTaskBrain *brainTask = TRACKED_NEW plAvTaskBrain(brain); - QueueTask(brainTask); + climbAvatar->PushBrain(brain); } // ** potentially controversial: // It's fairly easy for a human brain to hit a climb trigger - like when falling off a wall. From 5f3a8644a708e86a3a9f507fbecccfe854449578 Mon Sep 17 00:00:00 2001 From: Edmond Mondor Date: Mon, 24 Jan 2022 14:22:04 -0800 Subject: [PATCH 04/16] Files for Allowing disabling avatar panic links --- .../FeatureLib/pfCamera/plVirtualCamNeu.cpp | 16 ++++++++++++++++ .../Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h | 2 ++ Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp | 9 +++++++++ Sources/Plasma/FeatureLib/pfPython/cyAvatar.h | 8 ++++++++ .../Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp | 14 ++++++++++++++ .../Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp | 8 ++++---- .../Plasma/PubUtilLib/plAvatar/plArmatureMod.h | 4 +++- 7 files changed, 56 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp b/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp index 85390771..d74605bb 100644 --- a/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp +++ b/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp @@ -499,6 +499,22 @@ void plVirtualCam1::SetCutNextTrans() #endif } +void plVirtualCam1::SetCutNext() +{ + plCameraModifier1* cam = GetCurrentCamera(); + if (cam && cam->GetBrain()) { + cam->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + cam->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + } + + SetFlags(kCutNextTrans); + SetRender(true); + +#ifdef STATUS_LOG + camLog->AddLineF("Set Camera to cut on next frame"); +#endif +} + void plVirtualCam1::SetRender(hsBool render) { fFlags.SetBit(kRender,render); diff --git a/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h b/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h index 0f4ebf07..6649eabc 100644 --- a/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h +++ b/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h @@ -85,6 +85,7 @@ public: enum flags { kSetFOV, + /** Forces the next camera transition to be cut. */ kCutNextTrans, kRender, kRegionIgnore, @@ -145,6 +146,7 @@ public: hsPoint3 GetCameraPOA() { return fOutputPOA; } hsVector3 GetCameraUp() { return fOutputUp; } void SetCutNextTrans(); // used when player warps into a new camera region + void SetCutNext(); const hsMatrix44 GetCurrentMatrix() { return fMatrix; } static plVirtualCam1* Instance() { return fInstance; } diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp index cd5d123e..0d81bbba 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp @@ -1978,3 +1978,12 @@ hsBool cyAvatar::IsCurrentBrainHuman() } return false; } + +void cyAvatar::SetDontPanicLink(bool value) +{ + if (!fRecvr.empty()) { + plArmatureMod* mod = plAvatarMgr::FindAvatar(fRecvr[0]); + if (mod) + mod->SetDontPanicLinkFlag(value); + } +} diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h index e21a8929..168c748b 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h @@ -566,6 +566,14 @@ public: static hsBool IsCurrentBrainHuman(); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SetDontPanicLink + // PARAMETERS : value + // + // PURPOSE : Disables panic linking to Personal Age (warps the avatar back to the start instead) + // + void SetDontPanicLink(bool value); }; #endif // cyAvatar_h diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp index 8a538415..96b39c2c 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp @@ -597,6 +597,18 @@ PYTHON_METHOD_DEFINITION(ptAvatar, playSimpleAnimation, args) PYTHON_RETURN_NONE; } +PYTHON_METHOD_DEFINITION(ptAvatar, setDontPanicLink, args) +{ + bool value; + if (!PyArg_ParseTuple(args, "b", &value)) { + PyErr_SetString(PyExc_TypeError, "setDontPanicLink expects a boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetDontPanicLink(value); + PYTHON_RETURN_NONE; +} + PYTHON_START_METHODS_TABLE(ptAvatar) PYTHON_METHOD(ptAvatar, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" "- This is to be used if your Python program is running on only one client\n" @@ -651,6 +663,8 @@ PYTHON_START_METHODS_TABLE(ptAvatar) PYTHON_METHOD(ptAvatar, unRegisterForBehaviorNotify, "Params: selfKey\nThis will unregister behavior notifications"), PYTHON_METHOD(ptAvatar, playSimpleAnimation, "Params: animName\nPlay simple animation on avatar"), + + PYTHON_METHOD(ptAvatar, setDontPanicLink, "Params: value\nDisables panic linking to Personal Age (warps the avatar back to the start instead)"), PYTHON_END_METHODS_TABLE; PYTHON_GLOBAL_METHOD_DEFINITION(PtSetBehaviorLoopCount, args, "Params: behaviorKey,stage,loopCount,netForce\nThis will set the loop count for a particular stage in a multistage behavior") diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 90fc261c..b3660d6a 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -865,10 +865,10 @@ void plArmatureMod::SpawnAt(int spawnNum, double time) w2l.RemoveScale(); ci->SetTransform(l2w, w2l); ci->FlushTransform(); - - if (plVirtualCam1::Instance()) - plVirtualCam1::Instance()->SetCutNextTrans(); - + + if (IsLocalAvatar() && plVirtualCam1::Instance()) + plVirtualCam1::Instance()->SetCutNext(); + if (GetFollowerParticleSystemSO()) { // Since particles are in world space, if we've got some surrounding us, we've got to translate them to compensate for our warp. diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h index b79f2105..77eecb45 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h @@ -234,7 +234,9 @@ public: virtual void PanicLink(hsBool playLinkOutAnim = true); virtual void PersonalLink(); - virtual bool ToggleDontPanicLinkFlag() { fDontPanicLink = fDontPanicLink ? false : true; return fDontPanicLink; } + bool ToggleDontPanicLinkFlag() { fDontPanicLink = fDontPanicLink ? false : true; return fDontPanicLink; } + + void SetDontPanicLinkFlag(bool value) { fDontPanicLink = value; } int GetBrainCount(); plArmatureBrain *GetNextBrain(plArmatureBrain *brain); From e81af06584e926ad03bd9352d423a1ff907f9ff7 Mon Sep 17 00:00:00 2001 From: Edmond Mondor Date: Mon, 24 Jan 2022 19:06:23 -0800 Subject: [PATCH 05/16] Fix error due to no Empty() --- Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp index 0d81bbba..2c7d61da 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp @@ -1981,7 +1981,7 @@ hsBool cyAvatar::IsCurrentBrainHuman() void cyAvatar::SetDontPanicLink(bool value) { - if (!fRecvr.empty()) { + if ( fRecvr.Count() > 0 ) { plArmatureMod* mod = plAvatarMgr::FindAvatar(fRecvr[0]); if (mod) mod->SetDontPanicLinkFlag(value); From 69dac251b6742ab7f77bed1a676e8de52920bd90 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 18 Feb 2022 18:11:37 -0600 Subject: [PATCH 06/16] Fixes bug where avatar run-lock state can get confused after alt-tabbing --- .../Plasma/PubUtilLib/plInputCore/plInputDevice.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp index 90846201..5e74921f 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp @@ -176,19 +176,16 @@ void plKeyboardDevice::HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDo if (key == KEY_SHIFT) { fShiftKeyDown = bKeyDown; -// return; } if (key == KEY_CTRL) { fCtrlKeyDown = bKeyDown; -// return; } if (key == KEY_CAPSLOCK) { - // Keyboards toggle the light on key-down, so I'm going with that. - if (bKeyDown && !bKeyRepeat) + if (!bKeyRepeat) { - fCapsLockLock = !fCapsLockLock; + fCapsLockLock = (GetKeyState(KEY_CAPSLOCK) & 1) == 1; plAvatarInputInterface::GetInstance()->ForceAlwaysRun(fCapsLockLock); } } @@ -208,7 +205,8 @@ void plKeyboardDevice::HandleWindowActivate(bool bActive, HWND hWnd) { if (bActive) { - fCtrlKeyDown = false; + // Refresh the caps lock state + HandleKeyEvent(KEYDOWN, KEY_CAPSLOCK, nil, false); } else { From 51b6b4750b939f0231ceba8dc7d6e67a6da8bcc5 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 18 Feb 2022 20:19:15 -0600 Subject: [PATCH 07/16] Don't update to visible arrow when cursor should be hidden --- .../PubUtilLib/plInputCore/plInputDevice.cpp | 5 +---- .../plInputCore/plInputInterfaceMgr.cpp | 18 ++++++++---------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp index 90846201..eec78f77 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp @@ -408,7 +408,7 @@ void plMouseDevice::CreateCursor( char* cursor ) fCursor->SetPosition( 0, 0, 0 ); IUpdateCursorSize(); - fCursor->SetVisible( true ); + fCursor->SetVisible(!bCursorHidden); fCursor->SetOpacity( fOpacity ); } @@ -510,9 +510,6 @@ void plMouseDevice::NewCursor(char* cursor) fInstance->CreateCursor(cursor); fInstance->SetCursorX(fInstance->GetCursorX()); fInstance->SetCursorY(fInstance->GetCursorY()); - - if (!plMouseDevice::bCursorHidden) - fInstance->fCursor->SetVisible( true ); } void plMouseDevice::SetCursorOpacity( hsScalar opacity ) diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp index c1077d98..eda837ab 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp @@ -253,15 +253,13 @@ void plInputInterfaceMgr::IUpdateCursor( Int32 newCursor ) { char* mouseCursorResID; - - fCurrentCursor = newCursor; - if( fCurrentCursor == plInputInterface::kCursorHidden ) + if (newCursor == plInputInterface::kCursorHidden) { plMouseDevice::HideCursor(); - else - { - plMouseDevice::ShowCursor(); + } else { + if (fCurrentCursor == plInputInterface::kCursorHidden) + plMouseDevice::ShowCursor(); - switch( fCurrentCursor ) + switch(newCursor) { case plInputInterface::kCursorUp: mouseCursorResID = CURSOR_UP; break; case plInputInterface::kCursorLeft: mouseCursorResID = CURSOR_LEFT; break; @@ -286,12 +284,12 @@ void plInputInterfaceMgr::IUpdateCursor( Int32 newCursor ) case plInputInterface::kCursorHand: mouseCursorResID = CURSOR_HAND; break; case plInputInterface::kCursorUpward: mouseCursorResID = CURSOR_UPWARD; break; default: mouseCursorResID = CURSOR_OPEN; break; - } - - plMouseDevice::NewCursor( mouseCursorResID ); + plMouseDevice::NewCursor(mouseCursorResID); } + + fCurrentCursor = newCursor; } //// IEval /////////////////////////////////////////////////////////////////// From 229883654e1ecb9e7368a4b15dd5f3094fc0949f Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Wed, 23 Feb 2022 21:35:03 -0600 Subject: [PATCH 08/16] Initial pass at getting subtitle support in. Doesn't compile for unknown reasons --- .../plAudioCore/plAudioCore.vcxproj | 2 + .../plAudioCore/plAudioCore.vcxproj.filters | 6 + .../PubUtilLib/plMessage/plMessage.vcxproj | 1 + .../plMessage/plMessage.vcxproj.filters | 3 + Sources/Plasma/Apps/plClient/plClient.cpp | 1 + .../pfConsole/pfConsoleCommands.cpp | 5 + .../FeatureLib/pfPython/plPythonFileMod.cpp | 32 +++ .../FeatureLib/pfPython/plPythonFileMod.h | 1 + .../FeatureLib/pfPython/pyAudioControl.cpp | 16 ++ .../FeatureLib/pfPython/pyAudioControl.h | 5 + .../pfPython/pyAudioControlGlue.cpp | 11 + .../Plasma/NucleusLib/inc/plCreatableIndex.h | 80 ++++---- .../NucleusLib/pnModifier/plLogicModBase.h | 2 +- .../PubUtilLib/plAudio/plAudioSystem.cpp | 6 + .../Plasma/PubUtilLib/plAudio/plAudioSystem.h | 2 + .../PubUtilLib/plAudio/plWin32Sound.cpp | 29 ++- .../PubUtilLib/plAudioCore/plSoundBuffer.cpp | 27 ++- .../PubUtilLib/plAudioCore/plSoundBuffer.h | 6 +- .../plAudioCore/plSrtFileReader.cpp | 192 ++++++++++++++++++ .../PubUtilLib/plAudioCore/plSrtFileReader.h | 105 ++++++++++ .../PubUtilLib/plMessage/plMessageCreatable.h | 4 + .../PubUtilLib/plMessage/plSubtitleMsg.h | 82 ++++++++ 22 files changed, 579 insertions(+), 39 deletions(-) create mode 100644 Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp create mode 100644 Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h create mode 100644 Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h diff --git a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj index ad01030c..4ff0c775 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj +++ b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj @@ -244,6 +244,7 @@ %(PreprocessorDefinitions) %(PreprocessorDefinitions) + Disabled Disabled @@ -315,6 +316,7 @@ + diff --git a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters index 88ab2ab0..27a310a8 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters +++ b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters @@ -32,6 +32,9 @@ Source Files + + Source Files + @@ -61,5 +64,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj index 4b64cea6..21785e59 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj +++ b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj @@ -817,6 +817,7 @@ + diff --git a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters index f0e01926..c3423612 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters +++ b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters @@ -361,5 +361,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 07a2ccb4..9d4d5566 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -2313,6 +2313,7 @@ void plClient::IDetectAudioVideoSettings() WriteInt(stream, "Audio.SetChannelVolume Ambience", 1); WriteInt(stream, "Audio.SetChannelVolume NPCVoice", 1); WriteInt(stream, "Audio.EnableVoiceRecording", 1); + WriteInt(stream, "Audio.EnableSubtitles", false); WriteString(stream, "Audio.SetDeviceName", deviceName ); stream->Close(); delete stream; diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp index 3834f3e0..c2a96a24 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp +++ b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp @@ -3391,6 +3391,11 @@ PF_CONSOLE_CMD( Audio, MuteAll, "bool on", "Mute or unmute all sounds") plgAudioSys::SetMuted( (bool)params[ 0 ] ); } +PF_CONSOLE_CMD(Audio, EnableSubtitles, "bool on", "Enable or disable displaying subtitles for audio files containing speech") +{ + plgAudioSys::SetEnableSubtitles((bool)params[0]); +} + PF_CONSOLE_CMD( Audio, SetDistanceModel, "int type", "Sets the distance model for all 3d sounds") { if(plgAudioSys::Sys()) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index ca1ec5fb..cfc2dc90 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -94,6 +94,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../pfGameMgr/pfGameMgr.h" #include "../plMessage/plAIMsg.h" #include "../plAvatar/plAvBrainCritter.h" +#include "../plMessage/plSubtitleMsg.h" #include "plProfile.h" @@ -189,6 +190,7 @@ char* plPythonFileMod::fFunctionNames[] = { "OnGameMgrMsg" }, // kfunc_OnGameMgrMsg { "OnGameCliMsg" }, // kfunc_OnGameCliMsg { "OnAIMsg" }, // kfunc_OnAIMsg + { "OnSubtitleMsg" }, // kfunc_OnSubtitleMsg { nil } }; @@ -2780,6 +2782,36 @@ hsBool plPythonFileMod::MsgReceive(plMessage* msg) } } + if (fPyFunctionInstances[kfunc_OnSubtitleMsg]) + { + plSubtitleMsg* pSubMsg = plSubtitleMsg::ConvertNoRef(msg); + if (pSubMsg) + { + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_OnSubtitleMsg], fFunctionNames[kfunc_OnSubtitleMsg], + "ss", pSubMsg->GetText(), pSubMsg->GetSpeaker()); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnSubtitleMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + + // we handled this message (I think) + return true; + } + } + return plModifier::MsgReceive(msg); } diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h index fc701a99..b781c717 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h @@ -202,6 +202,7 @@ public: kfunc_OnGameMgrMsg, kfunc_OnGameCliMsg, kfunc_OnAIMsg, + kfunc_OnSubtitleMsg, kfunc_lastone }; // array of matching Python instance where the functions are, if defined diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp index bddad507..623f0106 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp @@ -256,6 +256,22 @@ hsBool pyAudioControl::IsMuted() return plgAudioSys::IsMuted(); } +// Enable or disable displaying speech subtitles +void pyAudioControl::EnableSubtitles() +{ + plgAudioSys::SetEnableSubtitles(true); +} + +void pyAudioControl::DisableSubtitles() +{ + plgAudioSys::SetEnableSubtitles(false); +} + +bool pyAudioControl::AreSubtitlesEnabled() const +{ + return plgAudioSys::AreSubtitlesEnabled(); +} + hsBool pyAudioControl::SupportEAX(const char *deviceName) { return plgAudioSys::SupportsEAX(deviceName); diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h index 1bd2e435..3e20d460 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h @@ -109,6 +109,11 @@ public: virtual void UnmuteAll(); virtual hsBool IsMuted(); + // Enable or disable displaying speech subtitles + void EnableSubtitles(); + void DisableSubtitles(); + bool AreSubtitlesEnabled() const; + virtual void SetAudioSystemMode(int mode); // sets the current mode virtual int GetAudioSystemMode(); // returns the current mode virtual int GetHighestAudioMode(); // returns the highest mode the card is capable of handling diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp index 7a886eb7..c0dfc11f 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp @@ -242,6 +242,14 @@ PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isMuted) PYTHON_RETURN_BOOL(self->fThis->IsMuted()); } +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, enableSubtitles, EnableSubtitles) +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, disableSubtitles, DisableSubtitles) + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, areSubtitlesEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->AreSubtitlesEnabled()); +} + PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, canSetMicLevel) { PYTHON_RETURN_BOOL(self->fThis->CanSetMicLevel()); @@ -484,6 +492,9 @@ PYTHON_START_METHODS_TABLE(ptAudioControl) PYTHON_BASIC_METHOD(ptAudioControl, muteAll, "Mutes all sounds."), PYTHON_BASIC_METHOD(ptAudioControl, unmuteAll, "Unmutes all sounds."), PYTHON_METHOD_NOARGS(ptAudioControl, isMuted, "Are all sounds muted? Returns 1 if true otherwise returns 0."), + PYTHON_BASIC_METHOD(ptAudioControl, enableSubtitles, "Enables audio subtitles."), + PYTHON_BASIC_METHOD(ptAudioControl, disableSubtitles, "Disables audio subtitles."), + PYTHON_METHOD_NOARGS(ptAudioControl, areSubtitlesEnabled, "Are audio subtitles enabled? Returns 1 if true otherwise returns 0."), PYTHON_METHOD_NOARGS(ptAudioControl, canSetMicLevel, "Can the microphone level be set? Returns 1 if true otherwise returns 0."), PYTHON_METHOD(ptAudioControl, setMicLevel, "Params: level\nSets the microphone recording level (0.0 to 1.0)."), PYTHON_METHOD_NOARGS(ptAudioControl, getMicLevel, "Returns the microphone recording level (0.0 to 1.0)."), diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index 75f0e728..a5c5b248 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -34,9 +34,9 @@ work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 *==LICENSE==*/ @@ -46,19 +46,19 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plClassIndexMacros.h" // for CLASS_INDEX macro defn CLASS_INDEX_LIST_START - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- // hsKeyedObjects should appear in the first section of the enum list // And everything else in the next section // Otherwise you will get an Assert - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- CLASS_INDEX(plSceneNode), - CLASS_INDEX(plSceneObject), + CLASS_INDEX(plSceneObject), CLASS_INDEX(hsKeyedObject), CLASS_INDEX(plBitmap), CLASS_INDEX(plMipmap), CLASS_INDEX(plCubicEnvironmap), - CLASS_INDEX(plLayer), - CLASS_INDEX(hsGMaterial), + CLASS_INDEX(plLayer), + CLASS_INDEX(hsGMaterial), CLASS_INDEX(plParticleSystem), CLASS_INDEX(plParticleEffect), CLASS_INDEX(plParticleCollisionEffectBeat), @@ -74,7 +74,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plWinAudible), CLASS_INDEX(plCoordinateInterface), CLASS_INDEX(plDrawInterface), - CLASS_INDEX(plDrawable), + CLASS_INDEX(plDrawable), CLASS_INDEX(plDrawableMesh), CLASS_INDEX(plDrawableIce), CLASS_INDEX(plPhysical), @@ -131,7 +131,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(UNUSED_plDrawablePatchSet), CLASS_INDEX(plInputManager), CLASS_INDEX(plLogicModBase), - CLASS_INDEX(plFogEnvironment), + CLASS_INDEX(plFogEnvironment), CLASS_INDEX(plNetApp), CLASS_INDEX(plNetClientMgr), CLASS_INDEX(pl2WayWinAudible), @@ -243,7 +243,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(pfGUIDynDisplayCtrl), CLASS_INDEX(UNUSED_plLayerProject), CLASS_INDEX(plInputInterfaceMgr), - CLASS_INDEX(plRailCameraMod), + CLASS_INDEX(plRailCameraMod), CLASS_INDEX(plMultistageBehMod), CLASS_INDEX(plCameraBrain1_Circle), CLASS_INDEX(plParticleWindEffect), @@ -368,14 +368,15 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plRidingAnimatedPhysicalDetector), CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration), CLASS_INDEX(plPXSubWorld), -//--------------------------------------------------------- -// Keyed objects above this line, unkeyed (such as messages) below.. -//--------------------------------------------------------- + CLASS_INDEX(pfConfirmationMgr), + //--------------------------------------------------------- + // Keyed objects above this line, unkeyed (such as messages) below.. + //--------------------------------------------------------- CLASS_INDEX_NONKEYED_OBJ_START - CLASS_INDEX(plObjRefMsg), + CLASS_INDEX(plObjRefMsg), CLASS_INDEX(plNodeRefMsg), - CLASS_INDEX(plMessage), + CLASS_INDEX(plMessage), CLASS_INDEX(plRefMsg), CLASS_INDEX(plGenRefMsg), CLASS_INDEX(plTimeMsg), @@ -411,12 +412,12 @@ CLASS_INDEX_LIST_START CLASS_INDEX(hsSfxObjDistFade), CLASS_INDEX(hsSfxObjDistShade), CLASS_INDEX(hsDynamicValue), - CLASS_INDEX(hsDynamicScalar), + CLASS_INDEX(hsDynamicScalar), CLASS_INDEX(hsDynamicColorRGBA), CLASS_INDEX(hsDynamicMatrix33), CLASS_INDEX(hsDynamicMatrix44), CLASS_INDEX(plOmniSqApplicator), - CLASS_INDEX(plPreResourceMsg), + CLASS_INDEX(plPreResourceMsg), CLASS_INDEX(UNUSED_hsDynamicColorRGBA), CLASS_INDEX(UNUSED_hsDynamicMatrix33), CLASS_INDEX(UNUSED_hsDynamicMatrix44), @@ -427,7 +428,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(UNUSED_plPosController), CLASS_INDEX(UNUSED_plScalarController), CLASS_INDEX(UNUSED_plPoint3Controller), - CLASS_INDEX(UNUSED_plScaleValueController), + CLASS_INDEX(UNUSED_plScaleValueController), CLASS_INDEX(UNUSED_plQuatController), CLASS_INDEX(UNUSED_plMatrix33Controller), CLASS_INDEX(UNUSED_plMatrix44Controller), @@ -437,7 +438,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plCompoundRotController), CLASS_INDEX(UNUSED_plSimplePosController), CLASS_INDEX(plCompoundPosController), - CLASS_INDEX(plTMController), + CLASS_INDEX(plTMController), CLASS_INDEX(hsFogControl), CLASS_INDEX(plIntRefMsg), CLASS_INDEX(plCollisionReactor), @@ -577,8 +578,8 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plNetServerMsgPing), CLASS_INDEX(plNetMsgAlive), CLASS_INDEX(plNetMsgTerminated), - CLASS_INDEX(plSDLModifierMsg), - CLASS_INDEX(plNetMsgSDLState), + CLASS_INDEX(plSDLModifierMsg), + CLASS_INDEX(plNetMsgSDLState), CLASS_INDEX(plNetServerMsgSessionReset), CLASS_INDEX(plCCRBanLinkingMsg), CLASS_INDEX(plCCRSilencePlayerMsg), @@ -780,17 +781,17 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plShadowCastMsg), CLASS_INDEX(plBoundsIsect), CLASS_INDEX(plResMgrHelperMsg), - CLASS_INDEX(plNetCommAuthMsg), - CLASS_INDEX(plNetCommFileListMsg), - CLASS_INDEX(plNetCommFileDownloadMsg), - CLASS_INDEX(plNetCommLinkToAgeMsg), - CLASS_INDEX(plNetCommPlayerListMsg), - CLASS_INDEX(plNetCommActivePlayerMsg), - CLASS_INDEX(plNetCommCreatePlayerMsg), - CLASS_INDEX(plNetCommDeletePlayerMsg), - CLASS_INDEX(plNetCommPublicAgeListMsg), - CLASS_INDEX(plNetCommPublicAgeMsg), - CLASS_INDEX(plNetCommRegisterAgeMsg), + CLASS_INDEX(plNetCommAuthMsg), + CLASS_INDEX(plNetCommFileListMsg), + CLASS_INDEX(plNetCommFileDownloadMsg), + CLASS_INDEX(plNetCommLinkToAgeMsg), + CLASS_INDEX(plNetCommPlayerListMsg), + CLASS_INDEX(plNetCommActivePlayerMsg), + CLASS_INDEX(plNetCommCreatePlayerMsg), + CLASS_INDEX(plNetCommDeletePlayerMsg), + CLASS_INDEX(plNetCommPublicAgeListMsg), + CLASS_INDEX(plNetCommPublicAgeMsg), + CLASS_INDEX(plNetCommRegisterAgeMsg), CLASS_INDEX(plVaultAdminInitializationTask), CLASS_INDEX(plMultistageModMsg), CLASS_INDEX(plSoundVolumeApplicator), @@ -948,6 +949,17 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plAngularVelocityMsg), CLASS_INDEX(plRideAnimatedPhysMsg), CLASS_INDEX(plAvBrainRideAnimatedPhysical), + CLASS_INDEX(pfGameScoreMsg), + CLASS_INDEX(pfGameScoreListMsg), + CLASS_INDEX(pfGameScoreTransferMsg), + CLASS_INDEX(pfGameScoreUpdateMsg), + CLASS_INDEX(plLoadClothingMsg), + CLASS_INDEX(plNullPipeline), + CLASS_INDEX(plGLPipeline), + CLASS_INDEX(plSDLModifierStateMsg), + CLASS_INDEX(plConfirmationMsg), + CLASS_INDEX(plLocalizedConfirmationMsg), + CLASS_INDEX(plSubtitleMsg), CLASS_INDEX_LIST_END -#endif // plCreatableIndex_inc +#endif // plCreatableIndex_inc \ No newline at end of file diff --git a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h index 908f5924..dd6f1100 100644 --- a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h +++ b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h @@ -43,7 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef plLogicModBase_inc #define plLogicModBase_inc -#include +#include #include "plSingleModifier.h" #include "../pnNetCommon/plSynchedValue.h" #include "hsTemplates.h" diff --git a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp index c2da6da0..83a1d5b5 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp @@ -994,6 +994,7 @@ hsBool plgAudioSys::fInit = false; hsBool plgAudioSys::fActive = false; hsBool plgAudioSys::fUseHardware = false; hsBool plgAudioSys::fMuted = true; +bool plgAudioSys::fEnableSubtitles = false; hsBool plgAudioSys::fDelayedActivate = false; hsBool plgAudioSys::fEnableEAX = false; hsWindowHndl plgAudioSys::fWnd = nil; @@ -1047,6 +1048,11 @@ void plgAudioSys::SetMuted( hsBool b ) SetGlobalFadeVolume(1.0); } +void plgAudioSys::SetEnableSubtitles(bool b) +{ + fEnableSubtitles = b; +} + void plgAudioSys::SetUseHardware(hsBool b) { fUseHardware = b; diff --git a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h index a376e9c7..43df875e 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h +++ b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h @@ -193,11 +193,13 @@ public: static void SetUseHardware(hsBool b); static void SetActive(hsBool b); static void SetMuted( hsBool b ); + static void SetEnableSubtitles(bool b); static void EnableEAX( hsBool b ); static hsBool Active() { return fInit; } static void Shutdown(); static void Activate(hsBool b); static hsBool IsMuted( void ) { return fMuted; } + static bool AreSubtitlesEnabled() { return fEnableSubtitles; } static hsWindowHndl hWnd() { return fWnd; } static plAudioSystem* Sys() { return fSys; } static void Restart( void ); diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp index a81a31b4..0eebdfbc 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp @@ -53,10 +53,12 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plWavFile.h" #include "../plAudible/plWinAudible.h" +#include "../plAudioCore/plSrtFileReader.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../plMessage/plSubtitleMsg.h" #include "../plNetMessage/plNetMessage.h" #include "../pnNetCommon/plNetApp.h" -#include "../pnMessage/plSoundMsg.h" -#include "../pnMessage/plEventCallbackMsg.h" #include "../plPipeline/plPlates.h" #include "../plStatusLog/plStatusLog.h" @@ -108,6 +110,18 @@ void plWin32Sound::IFreeBuffers( void ) void plWin32Sound::Update() { + plSoundBuffer* buf = GetDataBuffer(); + if (plgAudioSys::AreSubtitlesEnabled() && buf != nullptr) { + plSrtFileReader* srtReader = buf->GetSrtReader(); + if (srtReader != nullptr) { + while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime((uint32_t)(GetActualTimeSec() * 1000.0f))) { + // add a plSubtitleMsg to go... to whoever is listening (probably the KI) + plSubtitleMsg* msg = new plSubtitleMsg(nextEntry->GetSubtitleText(), nextEntry->GetSpeakerName()); + msg->Send(); + } + } + } + plSound::Update(); } @@ -120,6 +134,17 @@ void plWin32Sound::IActuallyPlay( void ) { if (fDSoundBuffer && plgAudioSys::Active() ) { + if (!fReallyPlaying && fSynchedStartTimeSec > 0) { + // advance past any subtitles that would end before the synched start time + // not sure when this actually happens... + plSoundBuffer* buf = GetDataBuffer(); + if (buf != nullptr) { + plSrtFileReader* srtReader = buf->GetSrtReader(); + if (srtReader != nullptr) { + srtReader->AdvanceToTime(fSynchedStartTimeSec * 1000.0); + } + } + } // Sometimes base/derived classes can be annoying IDerivedActuallyPlay(); diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp index f7068053..b7259bc1 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp @@ -48,6 +48,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plgDispatch.h" #include "hsResMgr.h" +#include "plSrtFileReader.h" #include "../pnMessage/plRefMsg.h" #include "../plFile/plFileUtils.h" #include "../plFile/hsFiles.h" @@ -117,20 +118,35 @@ static void LoadCallback(void *) else { plAudioFileReader *reader = nil; + plSrtFileReader* srtReader = nullptr; + while(plSoundBuffer *buffer = templist.Head()) { if(buffer->GetData()) { - reader = CreateReader(true, buffer->GetFileName(), buffer->GetAudioReaderType(), buffer->GetReaderSelect()); + const char* srcFilename = buffer->GetFileName(); + reader = CreateReader(true, srcFilename, buffer->GetAudioReaderType(), buffer->GetReaderSelect()); if( reader ) { unsigned readLen = buffer->GetAsyncLoadLength() ? buffer->GetAsyncLoadLength() : buffer->GetDataLength(); reader->Read( readLen, buffer->GetData() ); buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later + + plSrtFileReader* srtReader = buffer->GetSrtReader(); + if (srtReader != nullptr && srtReader->GetCurrentAudioFileName() == srcFilename) { + // same file we were playing before, so start the SRT feed over instead of deleting and reloading + srtReader->StartOver(); + } else { + std::unique_ptr newSrtFileReader(new plSrtFileReader(srcFilename)); + if (newSrtFileReader->ReadFile()) + buffer->SetSrtReader(newSrtFileReader.release()); + } } else + { buffer->SetError(); + } } templist.Unlink(buffer); @@ -194,6 +210,8 @@ plSoundBuffer::~plSoundBuffer() ASSERT(!link.IsLinked()); delete [] fFileName; UnLoad(); + + delete fSrtReader; } void plSoundBuffer::IInitBuffer() @@ -206,6 +224,7 @@ void plSoundBuffer::IInitBuffer() fFlags = 0; fDataRead = 0; fReader = nil; + fSrtReader = nullptr; fLoaded = 0; fLoading = false; fHeader.fFormatTag = 0; @@ -458,6 +477,12 @@ void plSoundBuffer::SetLoaded(bool loaded) fLoaded = loaded; } +void plSoundBuffer::SetSrtReader(plSrtFileReader* reader) +{ + delete fSrtReader; + fSrtReader = reader; +} + /***************************************************************************** * diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h index 6bb72244..940adf95 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h @@ -61,6 +61,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plUnifiedTime; class plAudioFileReader; +class plSrtFileReader; class plSoundBuffer : public hsKeyedObject { public: @@ -118,6 +119,8 @@ public: plAudioFileReader * GetAudioReader(); // transfers ownership to caller void SetAudioReader(plAudioFileReader *reader); void SetLoaded(bool loaded); + plSrtFileReader* GetSrtReader() const { return fSrtReader; } // does not transfer ownership + void SetSrtReader(plSrtFileReader* reader); plAudioFileReader::StreamType GetAudioReaderType() { return fStreamType; } unsigned GetAsyncLoadLength() { return fAsyncLoadLength ? fAsyncLoadLength : fDataLength; } @@ -148,7 +151,8 @@ protected: bool fLoading; bool fError; - plAudioFileReader * fReader; + plAudioFileReader * fReader; + plSrtFileReader* fSrtReader; UInt8 * fData; plWAVHeader fHeader; UInt32 fDataLength; diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp new file mode 100644 index 00000000..31311d3d --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -0,0 +1,192 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +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 3 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, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plSrtFileReader.h" + +#include "hsStream.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../pnUtils/Private/pnUtPath.h" +#include "../pnUtils/Private/pnUtStr.h" + +#include + +static const std::regex speakerTextRegex("^((([\\w.]+(\\s\\w+)?):) )?(.*)"); +static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> (\\d{2}):(\\d{2}):(\\d{2}),(\\d{3})$"); + +bool plSrtFileReader::ReadFile() +{ + wchar srtPathUnicode[MAX_PATH]; + StrToUnicode(srtPathUnicode, fAudioFileName, arrsize(srtPathUnicode)); + PathSetExtension(srtPathUnicode, srtPathUnicode, L".sub", arrsize(srtPathUnicode)); + + if (PathDoesFileExist(srtPathUnicode)) { + // read sets of SRT data until end of file + hsUNIXStream srtFileStream; + + // if file exists and was opened successfully + if (srtFileStream.Open(srtPathUnicode, L"r")) { + plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", srtPathUnicode); + + uint32_t subtitleNumber = 0; + uint32_t subtitleStartTimeMs = 0; + uint32_t subtitleEndTimeMs = 0; + char* line; + std::string speakerName; + std::string subtitleText; + std::cmatch matches; + + for (unsigned int lnCounter = 0; !srtFileStream.AtEnd(); lnCounter++) { + if (lnCounter % 4 == 0 && srtFileStream.ReadLn(line)) { + subtitleNumber = std::stoul(line); + } + else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) { + if (std::regex_match(line, matches, timingsRegex)) { + if (matches.size() < 9) { + // add error message and ensure this subtitle line won't be added to the entries + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '{}' are formatted incorrectly.", line); + subtitleNumber = 0; + } + else { + // matches[0] is the entire match, we don't do anything with it + // matches[1] is the first group -- the start hour number + subtitleStartTimeMs += std::stoul(matches[1].str()) * 3600000; + // matches[2] is the second group -- the start minute number + subtitleStartTimeMs += std::stoul(matches[2].str()) * 60000; + // matches[3] is the third group -- the start seconds number + subtitleStartTimeMs += std::stoul(matches[3].str()) * 1000; + // matches[4] is the fourth group -- the start milliseconds number + subtitleStartTimeMs += std::stoul(matches[4].str()); + + // matches[5] is the fifth group -- the end hour number + subtitleEndTimeMs += std::stoul(matches[5].str()) * 3600000; + // matches[6] is the sixth group -- the end minute number + subtitleEndTimeMs += std::stoul(matches[6].str()) * 60000; + // matches[7] is the seventh group -- the end seconds number + subtitleEndTimeMs += std::stoul(matches[7].str()) * 1000; + // matches[8] is the eighth group -- the end milliseconds number + subtitleEndTimeMs += std::stoul(matches[8].str()); + } + } + } + else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) { + if (std::regex_match(line, matches, speakerTextRegex)) { + if (matches.size() < 5) { + // add error message and ensure this subtitle line won't be added to the entries + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '{}' are formatted incorrectly.", line); + subtitleNumber = 0; + } + else { + // matches[0] is the entire match, we don't do anything with it + // matches[2] is the second group (the optional subtitle speaker with colon but no space at the end) + speakerName = matches[2]; + + // matches[5] is the fourth group (the subtitle text) + subtitleText = matches[5]; + } + } + } + else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { + // entry is complete, add to the queue and reset our temp variables + if (!speakerName.empty()) + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName))); + else + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText))); + + subtitleNumber = 0; + subtitleStartTimeMs = 0; + subtitleEndTimeMs = 0; + subtitleText.clear(); + speakerName.clear(); + } + } + + if (subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { + // enqueue the last subtitle from the file if we didn't have an extra blank line at the end + if (!speakerName.empty()) + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName))); + else + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText))); + } + + return true; + } + } + + return false; +} + +void plSrtFileReader::AdvanceToTime(uint32_t timeMs) +{ + while (fCurrentEntryIndex < fEntries.size() && fEntries.at(fCurrentEntryIndex).GetEndTimeMs() <= timeMs) { + fCurrentEntryIndex++; + } +} + + +plSrtEntry* plSrtFileReader::GetNextEntryStartingBeforeTime(uint32_t timeMs) +{ + if (fCurrentEntryIndex < fEntries.size()) { + plSrtEntry& nextEntry = fEntries.at(fCurrentEntryIndex); + + if (nextEntry.GetStartTimeMs() <= timeMs) { + fCurrentEntryIndex++; + return &nextEntry; + } + } + + return nullptr; +} + +plSrtEntry* plSrtFileReader::GetNextEntryEndingBeforeTime(uint32_t timeMs) +{ + if (fCurrentEntryIndex < fEntries.size()) { + plSrtEntry& nextEntry = fEntries.at(fCurrentEntryIndex); + + if (nextEntry.GetEndTimeMs() <= timeMs) { + fCurrentEntryIndex++; + return &nextEntry; + } + } + + return nullptr; +} \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h new file mode 100644 index 00000000..f9e57210 --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +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 3 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, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSrtFileReader - Class for reading an SRT format file and // +// storing the read entries for access later // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSrtFileReader_h +#define _plSrtFileReader_h + +#include "hsTypes.h" + +#include +#include + +class plSrtEntry +{ +public: + + plSrtEntry(uint32_t entryNum, uint32_t startTimeMs, uint32_t endTimeMs, std::string subtitleText, std::string speakerName) + : fEntryNum(entryNum), fStartTimeMs(startTimeMs), fEndTimeMs(endTimeMs), fSubtitleText(std::move(subtitleText)), + fSpeakerName(std::move(speakerName)) { } + + plSrtEntry(uint32_t entryNum, uint32_t startTimeMs, uint32_t endTimeMs, std::string subtitleText) + : fEntryNum(entryNum), fStartTimeMs(startTimeMs), fEndTimeMs(endTimeMs), fSubtitleText(std::move(subtitleText)) { } + + std::string GetSubtitleText() const { return fSubtitleText; } + std::string GetSpeakerName() const { return fSpeakerName; } + uint32_t GetStartTimeMs() const { return fStartTimeMs; } + uint32_t GetEndTimeMs() const { return fEndTimeMs; } + +protected: + + uint32_t fEntryNum; + uint32_t fStartTimeMs; + uint32_t fEndTimeMs; + std::string fSubtitleText; + std::string fSpeakerName; + +}; + +class plSrtFileReader +{ +public: + + plSrtFileReader(const char* audioFileName) + : fAudioFileName(std::move(audioFileName)), fEntries(), fCurrentEntryIndex() { } + + bool ReadFile(); + void StartOver() { fCurrentEntryIndex = 0; } + const char* GetCurrentAudioFileName() const { return fAudioFileName; } + void AdvanceToTime(uint32_t timeMs); + plSrtEntry* GetNextEntryStartingBeforeTime(uint32_t timeMs); + plSrtEntry* GetNextEntryEndingBeforeTime(uint32_t timeMs); + +protected: + + const char* fAudioFileName; + std::vector fEntries; + uint32_t fCurrentEntryIndex; + +}; + +#endif //_plSrtFileReader_h \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h index 25dac2e2..cee8bf45 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h @@ -133,6 +133,10 @@ REGISTER_CREATABLE( plSpawnModMsg ); REGISTER_CREATABLE( plSpawnRequestMsg ); +#include "plSubtitleMsg.h" + +REGISTER_CREATABLE(plSubtitleMsg); + #include "plNodeCleanupMsg.h" REGISTER_CREATABLE( plNodeCleanupMsg ); diff --git a/Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h new file mode 100644 index 00000000..e6cdb68d --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +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 3 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, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSubtitleMsg_inc +#define plSubtitleMsg_inc + +#include "../pnMessage/plMessage.h" + +/** Show subtitle text for in-game audio */ +class plSubtitleMsg : public plMessage +{ +protected: + std::string fText; + std::string fSpeaker; + +public: + plSubtitleMsg() : plMessage() + { + SetBCastFlag(plMessage::kBCastByExactType); + } + + plSubtitleMsg(std::string msgText) : plMessage(), fText(std::move(msgText)) + { + SetBCastFlag(plMessage::kBCastByExactType); + } + + plSubtitleMsg(std::string msgText, std::string msgSpeaker) : plMessage(), fText(std::move(msgText)), + fSpeaker(std::move(msgSpeaker)) + { + SetBCastFlag(plMessage::kBCastByExactType); + } + + CLASSNAME_REGISTER(plSubtitleMsg); + GETINTERFACE_ANY(plSubtitleMsg, plMessage); + + void Read(hsStream*, hsResMgr*) override { FATAL("no"); } + void Write(hsStream*, hsResMgr*) override { FATAL("no"); } + + std::string GetText() const { return fText; } + std::string GetSpeaker() const { return fSpeaker; } +}; + +#endif // plSubtitleMsg_inc \ No newline at end of file From c50bb08e8db3caffe769d8b1e3907d7aa71333a8 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Wed, 23 Feb 2022 21:46:02 -0600 Subject: [PATCH 09/16] Fix usage and overflow bug causing plWin32StreamingSound::GetActualTimeSec() to return absurd values. Needed fix for subtitle timings. --- .../PubUtilLib/plAudio/plDSoundBuffer.cpp | 4 ++-- .../plAudio/plWin32StreamingSound.cpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp index 82f6d53d..a3a89af6 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp @@ -710,9 +710,9 @@ hsBool plDSoundBuffer::IsEAXAccelerated( void ) const //// BytePosToMSecs ////////////////////////////////////////////////////////// -UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const +UInt32 plDSoundBuffer::BytePosToMSecs(UInt32 bytePos) const { - return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->fAvgBytesPerSec); + return (UInt32)(bytePos / ((hsScalar)fBufferDesc->fAvgBytesPerSec) / 1000.0f); } //// GetBufferBytePos //////////////////////////////////////////////////////// diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp index e402e771..14beb5e1 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp @@ -455,21 +455,24 @@ void plWin32StreamingSound::IActuallyStop() unsigned plWin32StreamingSound::GetByteOffset() { - if(fDataStream && fDSoundBuffer) - { + if (fDataStream && fDSoundBuffer) + { + UInt32 totalSize = fDataStream->GetDataSize(); + UInt32 bytesRemaining = fDataStream->NumBytesLeft(); unsigned bytesQueued = fDSoundBuffer->BuffersQueued() * STREAM_BUFFER_SIZE; unsigned offset = fDSoundBuffer->GetByteOffset(); - long byteoffset = ((fDataStream->GetDataSize() - fDataStream->NumBytesLeft()) - bytesQueued) + offset; - - return byteoffset < 0 ? fDataStream->GetDataSize() - abs(byteoffset) : byteoffset; + long byteoffset = ((totalSize - bytesRemaining) - bytesQueued) + offset; + + return byteoffset < 0 ? totalSize - std::abs(byteoffset) : byteoffset; } + return 0; } float plWin32StreamingSound::GetActualTimeSec() { - if(fDataStream && fDSoundBuffer) - return fDSoundBuffer->BytePosToMSecs(fDataStream->NumBytesLeft()) / 1000.0f; + if (fDataStream && fDSoundBuffer) + return fDSoundBuffer->BytePosToMSecs(this->GetByteOffset()) / 1000.0f; return 0.0f; } From 1d65c780979d707c89d81b08aa82dd3f86d60bc1 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 25 Feb 2022 01:06:29 -0600 Subject: [PATCH 10/16] Fix bad include, missing static declaration, SRT path directory, and formatting --- .../Plasma/PubUtilLib/plAudio/plAudioSystem.h | 1 + .../plAudioCore/plSrtFileReader.cpp | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h index 43df875e..dbc05db8 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h +++ b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h @@ -260,6 +260,7 @@ private: static hsBool fInit; static hsBool fActive; static hsBool fMuted; + static bool fEnableSubtitles; static hsWindowHndl fWnd; static hsBool fUseHardware; static hsBool fDelayedActivate; diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 31311d3d..19e5f688 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -45,8 +45,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsStream.h" #include "../plStatusLog/plStatusLog.h" -#include "../pnUtils/Private/pnUtPath.h" -#include "../pnUtils/Private/pnUtStr.h" +#include "../pnUtils/pnUtils.h" #include @@ -55,8 +54,15 @@ static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> ( bool plSrtFileReader::ReadFile() { + char path[MAX_PATH]; + + if (strchr(fAudioFileName, '\\') != nil) + strcpy(path, fAudioFileName); + else + sprintf(path, "sfx\\%s", fAudioFileName); + wchar srtPathUnicode[MAX_PATH]; - StrToUnicode(srtPathUnicode, fAudioFileName, arrsize(srtPathUnicode)); + StrToUnicode(srtPathUnicode, path, arrsize(srtPathUnicode)); PathSetExtension(srtPathUnicode, srtPathUnicode, L".sub", arrsize(srtPathUnicode)); if (PathDoesFileExist(srtPathUnicode)) { @@ -70,7 +76,7 @@ bool plSrtFileReader::ReadFile() uint32_t subtitleNumber = 0; uint32_t subtitleStartTimeMs = 0; uint32_t subtitleEndTimeMs = 0; - char* line; + char line[4096]; std::string speakerName; std::string subtitleText; std::cmatch matches; @@ -78,8 +84,7 @@ bool plSrtFileReader::ReadFile() for (unsigned int lnCounter = 0; !srtFileStream.AtEnd(); lnCounter++) { if (lnCounter % 4 == 0 && srtFileStream.ReadLn(line)) { subtitleNumber = std::stoul(line); - } - else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) { + } else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) { if (std::regex_match(line, matches, timingsRegex)) { if (matches.size() < 9) { // add error message and ensure this subtitle line won't be added to the entries @@ -107,8 +112,7 @@ bool plSrtFileReader::ReadFile() subtitleEndTimeMs += std::stoul(matches[8].str()); } } - } - else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) { + } else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) { if (std::regex_match(line, matches, speakerTextRegex)) { if (matches.size() < 5) { // add error message and ensure this subtitle line won't be added to the entries @@ -124,8 +128,7 @@ bool plSrtFileReader::ReadFile() subtitleText = matches[5]; } } - } - else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { + } else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { // entry is complete, add to the queue and reset our temp variables if (!speakerName.empty()) fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName))); From 1012251d9ab1aabab4dad58bce52ccd517999899 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 25 Feb 2022 01:07:40 -0600 Subject: [PATCH 11/16] Add missing message dispatch registration --- Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index cfc2dc90..f32d64d5 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -830,6 +830,8 @@ void plPythonFileMod::AddTarget(plSceneObject* sobj) // the message that is spammed to anyone who will listen plgDispatch::Dispatch()->RegisterForExactType(plAIBrainCreatedMsg::Index(), GetKey()); } + if (fPyFunctionInstances[kfunc_OnSubtitleMsg]) + plgDispatch::Dispatch()->RegisterForExactType(plSubtitleMsg::Index(), GetKey()); // As the last thing... call the OnInit function if they have one if ( fPyFunctionInstances[kfunc_Init] != nil ) From 9a1b37e7f6662d4b2ff1561a04ae709157b89def Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 25 Feb 2022 12:14:44 -0600 Subject: [PATCH 12/16] Turn std::strings into char* before doing PyObject_CallMethod with them --- Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index f32d64d5..5518bd5c 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -2792,7 +2792,7 @@ hsBool plPythonFileMod::MsgReceive(plMessage* msg) plProfile_BeginTiming(PythonUpdate); PyObject* retVal = PyObject_CallMethod( fPyFunctionInstances[kfunc_OnSubtitleMsg], fFunctionNames[kfunc_OnSubtitleMsg], - "ss", pSubMsg->GetText(), pSubMsg->GetSpeaker()); + "ss", pSubMsg->GetText().c_str(), pSubMsg->GetSpeaker().c_str()); if (retVal == nil) { #ifndef PLASMA_EXTERNAL_RELEASE From 8a2534dff6967d8077fdde16c08ba40d886db67d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 25 Feb 2022 14:34:30 -0600 Subject: [PATCH 13/16] Simplify SRT file path construction with suggestion from Hoikas --- .../plAudioCore/plSrtFileReader.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 19e5f688..01b53088 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -54,24 +54,17 @@ static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> ( bool plSrtFileReader::ReadFile() { - char path[MAX_PATH]; - - if (strchr(fAudioFileName, '\\') != nil) - strcpy(path, fAudioFileName); - else - sprintf(path, "sfx\\%s", fAudioFileName); - - wchar srtPathUnicode[MAX_PATH]; - StrToUnicode(srtPathUnicode, path, arrsize(srtPathUnicode)); - PathSetExtension(srtPathUnicode, srtPathUnicode, L".sub", arrsize(srtPathUnicode)); + wchar path[MAX_PATH]; + StrPrintf(path, arrsize(path), L"sfx\\%S", fAudioFileName); + PathSetExtension(path, path, L".sub", arrsize(path)); - if (PathDoesFileExist(srtPathUnicode)) { + if (PathDoesFileExist(path)) { // read sets of SRT data until end of file hsUNIXStream srtFileStream; // if file exists and was opened successfully - if (srtFileStream.Open(srtPathUnicode, L"r")) { - plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", srtPathUnicode); + if (srtFileStream.Open(path, L"r")) { + plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", path); uint32_t subtitleNumber = 0; uint32_t subtitleStartTimeMs = 0; From 64323c6073323dcf8f9ec633707d3e1072052e6b Mon Sep 17 00:00:00 2001 From: rarified Date: Sat, 26 Feb 2022 11:57:56 -0600 Subject: [PATCH 14/16] Apply code suggestions from rarified --- Sources/Plasma/NucleusLib/inc/plCreatableIndex.h | 4 ++-- .../PubUtilLib/plAudioCore/plSoundBuffer.cpp | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index a5c5b248..6c2d20ed 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -949,7 +949,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plAngularVelocityMsg), CLASS_INDEX(plRideAnimatedPhysMsg), CLASS_INDEX(plAvBrainRideAnimatedPhysical), - CLASS_INDEX(pfGameScoreMsg), + CLASS_INDEX(pfGameScoreMsg), // This index and below are unused currently and placed for indexing plSubtitleMsg consistently with H'uru CLASS_INDEX(pfGameScoreListMsg), CLASS_INDEX(pfGameScoreTransferMsg), CLASS_INDEX(pfGameScoreUpdateMsg), @@ -958,7 +958,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plGLPipeline), CLASS_INDEX(plSDLModifierStateMsg), CLASS_INDEX(plConfirmationMsg), - CLASS_INDEX(plLocalizedConfirmationMsg), + CLASS_INDEX(plLocalizedConfirmationMsg), // This index and above are unused currently and placed for indexing plSubtitleMsg consistently with H'uru CLASS_INDEX(plSubtitleMsg), CLASS_INDEX_LIST_END diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp index b7259bc1..177257d8 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp @@ -45,6 +45,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsStream.h" #include "hsUtils.h" +#include #include "plgDispatch.h" #include "hsResMgr.h" @@ -134,13 +135,13 @@ static void LoadCallback(void *) buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later plSrtFileReader* srtReader = buffer->GetSrtReader(); - if (srtReader != nullptr && srtReader->GetCurrentAudioFileName() == srcFilename) { - // same file we were playing before, so start the SRT feed over instead of deleting and reloading - srtReader->StartOver(); - } else { - std::unique_ptr newSrtFileReader(new plSrtFileReader(srcFilename)); - if (newSrtFileReader->ReadFile()) - buffer->SetSrtReader(newSrtFileReader.release()); + if (srtReader != nullptr && (strcmp(srtReader->GetCurrentAudioFileName(), srcFilename) == 0)) { + // same file we were playing before, so start the SRT feed over instead of deleting and reloading + srtReader->StartOver(); + } else { + std::unique_ptr newSrtFileReader(new plSrtFileReader(srcFilename)); + if (newSrtFileReader->ReadFile()) + buffer->SetSrtReader(newSrtFileReader.release()); } } else From 126d7511e0cd041241326df8e06cd448fe4eecaf Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Sat, 26 Feb 2022 19:16:00 -0600 Subject: [PATCH 15/16] Fix parentheses mistake for retrieving streaming audio timings and apply code fix suggestions from Adam --- Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp | 2 +- Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp | 3 ++- Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp | 4 ++-- Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h | 4 +++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp index a3a89af6..27bc2217 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp @@ -712,7 +712,7 @@ hsBool plDSoundBuffer::IsEAXAccelerated( void ) const UInt32 plDSoundBuffer::BytePosToMSecs(UInt32 bytePos) const { - return (UInt32)(bytePos / ((hsScalar)fBufferDesc->fAvgBytesPerSec) / 1000.0f); + return (UInt32)(bytePos / ((float)fBufferDesc->fAvgBytesPerSec / 1000.0f)); } //// GetBufferBytePos //////////////////////////////////////////////////////// diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp index 0eebdfbc..2fcbe712 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp @@ -114,7 +114,8 @@ void plWin32Sound::Update() if (plgAudioSys::AreSubtitlesEnabled() && buf != nullptr) { plSrtFileReader* srtReader = buf->GetSrtReader(); if (srtReader != nullptr) { - while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime((uint32_t)(GetActualTimeSec() * 1000.0f))) { + uint32_t currentTimeMs = (uint32_t)(GetActualTimeSec() * 1000.0f); + while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime(currentTimeMs)) { // add a plSubtitleMsg to go... to whoever is listening (probably the KI) plSubtitleMsg* msg = new plSubtitleMsg(nextEntry->GetSubtitleText(), nextEntry->GetSpeakerName()); msg->Send(); diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 01b53088..3208086c 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -64,7 +64,7 @@ bool plSrtFileReader::ReadFile() // if file exists and was opened successfully if (srtFileStream.Open(path, L"r")) { - plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", path); + plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file %S", path); uint32_t subtitleNumber = 0; uint32_t subtitleStartTimeMs = 0; @@ -81,7 +81,7 @@ bool plSrtFileReader::ReadFile() if (std::regex_match(line, matches, timingsRegex)) { if (matches.size() < 9) { // add error message and ensure this subtitle line won't be added to the entries - plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '{}' are formatted incorrectly.", line); + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '%s' are formatted incorrectly.", line); subtitleNumber = 0; } else { diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h index f9e57210..94604f87 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h @@ -50,6 +50,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define _plSrtFileReader_h #include "hsTypes.h" +#include "../pnUtils/pnUtils.h" #include #include @@ -85,7 +86,8 @@ class plSrtFileReader public: plSrtFileReader(const char* audioFileName) - : fAudioFileName(std::move(audioFileName)), fEntries(), fCurrentEntryIndex() { } + : fAudioFileName(StrDup(audioFileName)), fCurrentEntryIndex(0) { } + virtual ~plSrtFileReader() { delete[] fAudioFileName; } bool ReadFile(); void StartOver() { fCurrentEntryIndex = 0; } From 9e308cbebe53426e12e99960522068806282aa04 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Sat, 26 Feb 2022 20:22:41 -0600 Subject: [PATCH 16/16] Fix another log string formatting issue found by Adam. --- Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 3208086c..2cc63f32 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -109,10 +109,9 @@ bool plSrtFileReader::ReadFile() if (std::regex_match(line, matches, speakerTextRegex)) { if (matches.size() < 5) { // add error message and ensure this subtitle line won't be added to the entries - plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '{}' are formatted incorrectly.", line); + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '%s' are formatted incorrectly.", line); subtitleNumber = 0; - } - else { + } else { // matches[0] is the entire match, we don't do anything with it // matches[2] is the second group (the optional subtitle speaker with colon but no space at the end) speakerName = matches[2];