isle-portable/LEGO1/mxwavepresenter.cpp

376 lines
8.9 KiB
C++
Raw Normal View History

#include "mxwavepresenter.h"
Implement MxObjectFactory::{MxObjectFactory,Create} + match sizes of Mx.*Presenter objects created by it (#59) * Get Isle building on MinGW32 (#63) * Add MxUnknown100dc6b0::~MxUnknown100dc6b0 stub * Declare destructor of MxOmni and MxTransitionManager in class * inline attribute must go first * Stub LegoState::Vtable0x14 for Act3State * MxStreamer::VTable0x14 is not an override * Stub MxEntity::Destroy for LegoState::Destroy * Stub MxUnknown100dc6e0 for MxSoundManager * ::ClassName and ::IsA are const methods * methods in the class body don't need a namespace * MxSoundManager subclasses MxUnknown100dc6e0 * LegoInputManager subclasses MxPresenter * NotificationId is an enum, and does not need __declspec(dllexport) * Fix final #endif of legoomni.h and mxobjectfactory.h * Add const alternative for LegoOmni::Create and MxVideoParam, only available for MinGW * Alternative approach to MinGW compatibility * MinGW on Linux is case sensitve * Don't delete a member variable. C++ automatically destructs member variables --------- Co-authored-by: MattKC <34096995+itsmattkc@users.noreply.github.com> * MxatomId: implement inline operator== * Add decomp.h header, containing macro's only used when matching the original binaries * Add in-line constructor of MxPresenter * MxMediaPresenter: add members to match size * MxVideoPresenter: add members to match size * MxCompositePresenter: add members to match size * MxFlcPresenter: add members to match size * MxSmkPresenter: add members to match size * MxStillPresenter: add members to match size * MxAudioPresenter: add members to match size * MxWavePresenter: add members to match size * MxMIDIPresenter: add members to match size * MxEventPresenter: add members to match size * MxLoopingFlcPresenter: add members to match size * MxLoopingSmkPresenter: add members to match size * MxLoopingMIDIPresenter: add check for size * Implement MxObjectFactory::{MxObjectFactory,Create} Matching of MxObjectFactory::Create is not good, because none of the other objects have been implemented. * Implement a few MxPresenter methods * Fix size of LegoInputManager * Fix name of first padding member of legoinputmanager.h * add DECOMP_SIZE_ASSERT macro * Use DECOMP_SIZE_ASSERT macro + convert to hexadecimal * fixed minor typos --------- Co-authored-by: MattKC <34096995+itsmattkc@users.noreply.github.com> Co-authored-by: itsmattkc <itsmattkc@gmail.com>
2023-07-02 03:00:28 -04:00
#include "decomp.h"
#include "define.h"
#include "legoomni.h"
#include "mxautolocker.h"
#include "mxdssound.h"
#include "mxomni.h"
#include "mxsoundmanager.h"
Implement MxObjectFactory::{MxObjectFactory,Create} + match sizes of Mx.*Presenter objects created by it (#59) * Get Isle building on MinGW32 (#63) * Add MxUnknown100dc6b0::~MxUnknown100dc6b0 stub * Declare destructor of MxOmni and MxTransitionManager in class * inline attribute must go first * Stub LegoState::Vtable0x14 for Act3State * MxStreamer::VTable0x14 is not an override * Stub MxEntity::Destroy for LegoState::Destroy * Stub MxUnknown100dc6e0 for MxSoundManager * ::ClassName and ::IsA are const methods * methods in the class body don't need a namespace * MxSoundManager subclasses MxUnknown100dc6e0 * LegoInputManager subclasses MxPresenter * NotificationId is an enum, and does not need __declspec(dllexport) * Fix final #endif of legoomni.h and mxobjectfactory.h * Add const alternative for LegoOmni::Create and MxVideoParam, only available for MinGW * Alternative approach to MinGW compatibility * MinGW on Linux is case sensitve * Don't delete a member variable. C++ automatically destructs member variables --------- Co-authored-by: MattKC <34096995+itsmattkc@users.noreply.github.com> * MxatomId: implement inline operator== * Add decomp.h header, containing macro's only used when matching the original binaries * Add in-line constructor of MxPresenter * MxMediaPresenter: add members to match size * MxVideoPresenter: add members to match size * MxCompositePresenter: add members to match size * MxFlcPresenter: add members to match size * MxSmkPresenter: add members to match size * MxStillPresenter: add members to match size * MxAudioPresenter: add members to match size * MxWavePresenter: add members to match size * MxMIDIPresenter: add members to match size * MxEventPresenter: add members to match size * MxLoopingFlcPresenter: add members to match size * MxLoopingSmkPresenter: add members to match size * MxLoopingMIDIPresenter: add check for size * Implement MxObjectFactory::{MxObjectFactory,Create} Matching of MxObjectFactory::Create is not good, because none of the other objects have been implemented. * Implement a few MxPresenter methods * Fix size of LegoInputManager * Fix name of first padding member of legoinputmanager.h * add DECOMP_SIZE_ASSERT macro * Use DECOMP_SIZE_ASSERT macro + convert to hexadecimal * fixed minor typos --------- Co-authored-by: MattKC <34096995+itsmattkc@users.noreply.github.com> Co-authored-by: itsmattkc <itsmattkc@gmail.com>
2023-07-02 03:00:28 -04:00
DECOMP_SIZE_ASSERT(MxWavePresenter, 0x6c);
DECOMP_SIZE_ASSERT(MxWavePresenter::WaveFormat, 0x1c);
// OFFSET: LEGO1 0x1000d640
MxWavePresenter::~MxWavePresenter()
{
Destroy(TRUE);
}
// OFFSET: LEGO1 0x1000d6a0
void MxWavePresenter::Destroy()
{
Destroy(FALSE);
}
// OFFSET: LEGO1 0x1000d6b0
MxBool MxWavePresenter::IsPaused()
{
return m_paused;
}
Implement MxObjectFactory::{MxObjectFactory,Create} + match sizes of Mx.*Presenter objects created by it (#59) * Get Isle building on MinGW32 (#63) * Add MxUnknown100dc6b0::~MxUnknown100dc6b0 stub * Declare destructor of MxOmni and MxTransitionManager in class * inline attribute must go first * Stub LegoState::Vtable0x14 for Act3State * MxStreamer::VTable0x14 is not an override * Stub MxEntity::Destroy for LegoState::Destroy * Stub MxUnknown100dc6e0 for MxSoundManager * ::ClassName and ::IsA are const methods * methods in the class body don't need a namespace * MxSoundManager subclasses MxUnknown100dc6e0 * LegoInputManager subclasses MxPresenter * NotificationId is an enum, and does not need __declspec(dllexport) * Fix final #endif of legoomni.h and mxobjectfactory.h * Add const alternative for LegoOmni::Create and MxVideoParam, only available for MinGW * Alternative approach to MinGW compatibility * MinGW on Linux is case sensitve * Don't delete a member variable. C++ automatically destructs member variables --------- Co-authored-by: MattKC <34096995+itsmattkc@users.noreply.github.com> * MxatomId: implement inline operator== * Add decomp.h header, containing macro's only used when matching the original binaries * Add in-line constructor of MxPresenter * MxMediaPresenter: add members to match size * MxVideoPresenter: add members to match size * MxCompositePresenter: add members to match size * MxFlcPresenter: add members to match size * MxSmkPresenter: add members to match size * MxStillPresenter: add members to match size * MxAudioPresenter: add members to match size * MxWavePresenter: add members to match size * MxMIDIPresenter: add members to match size * MxEventPresenter: add members to match size * MxLoopingFlcPresenter: add members to match size * MxLoopingSmkPresenter: add members to match size * MxLoopingMIDIPresenter: add check for size * Implement MxObjectFactory::{MxObjectFactory,Create} Matching of MxObjectFactory::Create is not good, because none of the other objects have been implemented. * Implement a few MxPresenter methods * Fix size of LegoInputManager * Fix name of first padding member of legoinputmanager.h * add DECOMP_SIZE_ASSERT macro * Use DECOMP_SIZE_ASSERT macro + convert to hexadecimal * fixed minor typos --------- Co-authored-by: MattKC <34096995+itsmattkc@users.noreply.github.com> Co-authored-by: itsmattkc <itsmattkc@gmail.com>
2023-07-02 03:00:28 -04:00
// OFFSET: LEGO1 0x100b1ad0
void MxWavePresenter::Init()
{
m_waveFormat = NULL;
m_dsBuffer = NULL;
m_chunkLength = 0;
m_lockSize = 0;
m_writtenChunks = 0;
m_started = FALSE;
m_unk66 = FALSE;
m_paused = FALSE;
}
// OFFSET: LEGO1 0x100b1af0
MxResult MxWavePresenter::AddToManager()
{
MxResult result = MxSoundPresenter::AddToManager();
Init();
return result;
}
// OFFSET: LEGO1 0x100b1b10
void MxWavePresenter::Destroy(MxBool p_fromDestructor)
{
if (m_dsBuffer) {
m_dsBuffer->Stop();
m_dsBuffer->Release();
}
if (m_waveFormat)
delete[] ((MxU8*) m_waveFormat);
Init();
if (!p_fromDestructor)
MxSoundPresenter::Destroy(FALSE);
}
// OFFSET: LEGO1 0x100b1b60
MxS8 MxWavePresenter::GetPlayedChunks()
{
DWORD dwCurrentPlayCursor, dwCurrentWriteCursor;
MxS8 playedChunks = -1;
if (m_dsBuffer->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor) == DS_OK)
playedChunks = dwCurrentPlayCursor / m_chunkLength;
return playedChunks;
}
// OFFSET: LEGO1 0x100b1ba0
MxBool MxWavePresenter::FUN_100b1ba0()
{
return !m_started || GetPlayedChunks() != m_writtenChunks;
}
// OFFSET: LEGO1 0x100b1bd0
void MxWavePresenter::WriteToSoundBuffer(void* p_audioPtr, MxU32 p_length)
{
DWORD dwStatus;
LPVOID pvAudioPtr1;
DWORD dwOffset;
LPVOID pvAudioPtr2;
DWORD dwAudioBytes1;
DWORD dwAudioBytes2;
dwOffset = m_chunkLength * m_writtenChunks;
m_dsBuffer->GetStatus(&dwStatus);
if (dwStatus == DSBSTATUS_BUFFERLOST) {
m_dsBuffer->Restore();
m_dsBuffer->GetStatus(&dwStatus);
}
if (dwStatus != DSBSTATUS_BUFFERLOST) {
if (m_action->GetFlags() & MxDSAction::Flag_Looping) {
m_writtenChunks++;
m_lockSize = p_length;
}
else {
m_writtenChunks = 1 - m_writtenChunks;
m_lockSize = m_chunkLength;
}
if (m_dsBuffer->Lock(dwOffset, m_lockSize, &pvAudioPtr1, &dwAudioBytes1, &pvAudioPtr2, &dwAudioBytes2, 0) ==
DS_OK) {
memcpy(pvAudioPtr1, p_audioPtr, p_length);
if (m_lockSize > p_length && !(m_action->GetFlags() & MxDSAction::Flag_Looping)) {
memset((MxU8*) pvAudioPtr1 + p_length, m_silenceData, m_lockSize - p_length);
}
m_dsBuffer->Unlock(pvAudioPtr1, m_lockSize, pvAudioPtr2, 0);
}
}
}
// OFFSET: LEGO1 0x100b1cf0
void MxWavePresenter::ReadyTickle()
{
MxStreamChunk* chunk = NextChunk();
if (chunk) {
m_waveFormat = (WaveFormat*) new MxU8[chunk->GetLength()];
memcpy(m_waveFormat, chunk->GetData(), chunk->GetLength());
m_subscriber->FUN_100b8390(chunk);
ParseExtra();
m_previousTickleStates |= 1 << (unsigned char) m_currentTickleState;
m_currentTickleState = TickleState_Starting;
}
}
// OFFSET: LEGO1 0x100b1d50
void MxWavePresenter::StartingTickle()
{
MxStreamChunk* chunk = NextChunk();
if (chunk && m_action->GetElapsedTime() >= chunk->GetTime()) {
MxU32 length = chunk->GetLength();
WAVEFORMATEX waveFormatEx;
m_chunkLength = length;
memset(&waveFormatEx, 0, sizeof(waveFormatEx));
waveFormatEx.wFormatTag = m_waveFormat->m_waveFormatEx.wFormatTag;
waveFormatEx.nChannels = m_waveFormat->m_waveFormatEx.nChannels;
waveFormatEx.nSamplesPerSec = m_waveFormat->m_waveFormatEx.nSamplesPerSec;
waveFormatEx.nAvgBytesPerSec = m_waveFormat->m_waveFormatEx.nAvgBytesPerSec;
waveFormatEx.nBlockAlign = m_waveFormat->m_waveFormatEx.nBlockAlign;
waveFormatEx.wBitsPerSample = m_waveFormat->m_waveFormatEx.wBitsPerSample;
if (waveFormatEx.wBitsPerSample == 8)
m_silenceData = 0x7F;
if (waveFormatEx.wBitsPerSample == 16)
m_silenceData = 0;
DSBUFFERDESC desc;
memset(&desc, 0, sizeof(desc));
desc.dwSize = sizeof(desc);
if (m_unk66)
desc.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
else
desc.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
if (m_action->GetFlags() & MxDSAction::Flag_Looping)
desc.dwBufferBytes = m_waveFormat->m_waveFormatEx.nAvgBytesPerSec *
(m_action->GetDuration() / m_action->GetLoopCount()) / 1000;
else
desc.dwBufferBytes = 2 * length;
desc.lpwfxFormat = &waveFormatEx;
if (MSoundManager()->GetDirectSound()->CreateSoundBuffer(&desc, &m_dsBuffer, NULL) != DS_OK) {
EndAction();
}
else {
SetVolume(((MxDSSound*) m_action)->GetVolume());
m_previousTickleStates |= 1 << (unsigned char) m_currentTickleState;
m_currentTickleState = TickleState_Streaming;
}
}
}
// OFFSET: LEGO1 0x100b1ea0
void MxWavePresenter::StreamingTickle()
{
if (!m_currentChunk) {
if (!(m_action->GetFlags() & MxDSAction::Flag_Looping)) {
MxStreamChunk* chunk = FUN_100b5650();
if (chunk && chunk->GetFlags() & MxDSChunk::Flag_Bit2 && !(chunk->GetFlags() & MxDSChunk::Flag_Bit16)) {
chunk->SetFlags(chunk->GetFlags() | MxDSChunk::Flag_Bit16);
m_currentChunk = new MxStreamChunk;
MxU8* data = new MxU8[m_chunkLength];
memset(data, m_silenceData, m_chunkLength);
m_currentChunk->SetLength(m_chunkLength);
m_currentChunk->SetData(data);
m_currentChunk->SetTime(chunk->GetTime() + 1000);
m_currentChunk->SetFlags(MxDSChunk::Flag_Bit1);
}
}
MxMediaPresenter::StreamingTickle();
}
}
// OFFSET: LEGO1 0x100b20c0
void MxWavePresenter::DoneTickle()
{
if (m_dsBuffer) {
DWORD dwCurrentPlayCursor, dwCurrentWriteCursor;
m_dsBuffer->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor);
MxS8 playedChunks = dwCurrentPlayCursor / m_chunkLength;
if (m_action->GetFlags() & MxDSAction::Flag_Bit7 || m_action->GetFlags() & MxDSAction::Flag_Looping ||
m_writtenChunks != playedChunks || m_lockSize + (m_chunkLength * playedChunks) <= dwCurrentPlayCursor)
MxMediaPresenter::DoneTickle();
}
else
MxMediaPresenter::DoneTickle();
}
// OFFSET: LEGO1 0x100b2130
void MxWavePresenter::AppendChunk(MxStreamChunk* p_chunk)
{
WriteToSoundBuffer(p_chunk->GetData(), p_chunk->GetLength());
if (IsEnabled())
m_subscriber->FUN_100b8390(p_chunk);
}
// OFFSET: LEGO1 0x100b2160
undefined4 MxWavePresenter::PutData()
{
MxAutoLocker lock(&m_criticalSection);
if (IsEnabled()) {
switch (m_currentTickleState) {
case TickleState_Streaming:
if (m_currentChunk && FUN_100b1ba0()) {
WriteToSoundBuffer(m_currentChunk->GetData(), m_currentChunk->GetLength());
m_subscriber->FUN_100b8390(m_currentChunk);
m_currentChunk = NULL;
}
if (!m_started) {
m_dsBuffer->SetCurrentPosition(0);
if (m_dsBuffer->Play(0, 0, DSBPLAY_LOOPING) == DS_OK)
m_started = TRUE;
}
break;
case TickleState_Repeating:
if (m_started)
break;
m_dsBuffer->SetCurrentPosition(0);
if (m_dsBuffer->Play(0, 0, m_action->GetLoopCount() > 1) == DS_OK)
m_started = TRUE;
}
}
return 0;
}
// OFFSET: LEGO1 0x100b2280
void MxWavePresenter::EndAction()
{
if (m_action) {
MxAutoLocker lock(&m_criticalSection);
MxMediaPresenter::EndAction();
if (m_dsBuffer)
m_dsBuffer->Stop();
}
}
// OFFSET: LEGO1 0x100b2300
void MxWavePresenter::SetVolume(MxS32 p_volume)
{
m_criticalSection.Enter();
m_volume = p_volume;
if (m_dsBuffer != NULL) {
MxS32 volume = p_volume * MxOmni::GetInstance()->GetSoundManager()->GetVolume() / 100;
MxS32 otherVolume = MxOmni::GetInstance()->GetSoundManager()->FUN_100aecf0(volume);
m_dsBuffer->SetVolume(otherVolume);
}
m_criticalSection.Leave();
}
// OFFSET: LEGO1 0x100b2360
void MxWavePresenter::Enable(MxBool p_enable)
{
if (IsEnabled() != p_enable) {
MxSoundPresenter::Enable(p_enable);
if (p_enable) {
m_writtenChunks = 0;
m_started = FALSE;
}
else if (m_dsBuffer)
m_dsBuffer->Stop();
}
}
// OFFSET: LEGO1 0x100b23a0
void MxWavePresenter::ParseExtra()
{
char extraCopy[512];
MxSoundPresenter::ParseExtra();
*((MxU16*) &extraCopy[0]) = m_action->GetExtraLength();
char* extraData = m_action->GetExtraData();
if (*((MxU16*) &extraCopy[0])) {
MxU16 len = *((MxU16*) &extraCopy[0]);
memcpy(extraCopy, extraData, len);
extraCopy[len] = '\0';
char t_soundValue[512];
if (KeyValueStringParse(t_soundValue, g_strSOUND, extraCopy)) {
if (!strcmpi(t_soundValue, "FALSE"))
Enable(FALSE);
}
}
}
// OFFSET: LEGO1 0x100b2440
void MxWavePresenter::Pause()
{
if (!m_paused && m_started) {
if (m_dsBuffer)
m_dsBuffer->Stop();
m_paused = TRUE;
}
}
// OFFSET: LEGO1 0x100b2470
void MxWavePresenter::Resume()
{
if (m_paused) {
if (m_dsBuffer && m_started) {
switch (m_currentTickleState) {
case TickleState_Streaming:
m_dsBuffer->Play(0, 0, DSBPLAY_LOOPING);
break;
case TickleState_Repeating:
m_dsBuffer->Play(0, 0, m_action->GetLoopCount() > 1);
break;
case TickleState_Done:
m_dsBuffer->Play(0, 0, 0);
}
}
m_paused = FALSE;
}
}