Move some classes to LegoOmni (#417)

This commit is contained in:
Christian Semmler
2024-01-08 06:45:07 -05:00
committed by GitHub
parent c47206617d
commit 091ecd5935
27 changed files with 245 additions and 229 deletions

View File

@@ -140,15 +140,12 @@ private:
};
__declspec(dllexport) MxBackgroundAudioManager* BackgroundAudioManager();
__declspec(dllexport) MxDSObject* CreateStreamObject(MxDSFile*, MxS16);
__declspec(dllexport) LegoGameState* GameState();
__declspec(dllexport) const char* GetNoCD_SourceName();
__declspec(dllexport) LegoInputManager* InputManager();
__declspec(dllexport) LegoOmni* Lego();
__declspec(dllexport) void MakeSourceName(char*, const char*);
__declspec(dllexport) LegoEntity* PickEntity(MxLong, MxLong);
__declspec(dllexport) LegoROI* PickROI(MxLong, MxLong);
__declspec(dllexport) void SetOmniUserMessage(void (*)(const char*, int));
__declspec(dllexport) LegoSoundManager* SoundManager();
__declspec(dllexport) MxResult Start(MxDSAction*);
__declspec(dllexport) MxTransitionManager* TransitionManager();
@@ -159,26 +156,12 @@ LegoBuildingManager* BuildingManager();
LegoControlManager* ControlManager();
IslePathActor* GetCurrentVehicle();
LegoPlantManager* PlantManager();
MxBool KeyValueStringParse(char*, const char*, const char*);
LegoWorld* GetCurrentWorld();
GifManager* GetGifManager();
void FUN_10015820(MxU32, MxU32);
LegoEntity* FindEntityByAtomIdOrEntityId(const MxAtomId& p_atom, MxS32 p_entityid);
MxDSAction& GetCurrentAction();
MxBool FUN_100b6e10(
MxS32 p_bitmapWidth,
MxS32 p_bitmapHeight,
MxS32 p_videoParamWidth,
MxS32 p_videoParamHeight,
MxS32* p_left,
MxS32* p_top,
MxS32* p_right,
MxS32* p_bottom,
MxS32* p_width,
MxS32* p_height
);
void PlayMusic(MxU32 p_index);
void SetIsWorldActive(MxBool p_isWorldActive);
void RegisterScripts();

View File

@@ -2,58 +2,13 @@
#define LEGOUTIL_H
#include "extra.h"
#include "legoentity.h"
#include "mxatomid.h"
#include "mxtypes.h"
#include "mxutil.h"
#include <windows.h>
template <class T>
inline T Abs(T p_t)
{
return p_t < 0 ? -p_t : p_t;
}
template <class T>
inline T Min(T p_t1, T p_t2)
{
return p_t1 < p_t2 ? p_t1 : p_t2;
}
template <class T>
inline T Max(T p_t1, T p_t2)
{
return p_t1 > p_t2 ? p_t1 : p_t2;
}
template <class T>
inline void GetScalar(MxU8** p_source, T& p_dest)
{
p_dest = *(T*) *p_source;
*p_source += sizeof(T);
}
template <class T>
inline T GetScalar(T** p_source)
{
T val = **p_source;
*p_source += 1;
return val;
}
template <class T>
inline void GetDouble(MxU8** p_source, T& p_dest)
{
p_dest = *(double*) *p_source;
*p_source += sizeof(double);
}
template <class T>
inline void GetString(MxU8** p_source, char** p_dest, T* p_obj, void (T::*p_setter)(const char*))
{
(p_obj->*p_setter)((char*) *p_source);
*p_source += strlen(*p_dest) + 1;
}
class MxAtomId;
class LegoEntity;
ExtraActionType MatchActionString(const char*);
void InvokeAction(ExtraActionType p_actionId, MxAtomId& p_pAtom, int p_targetEntityId, LegoEntity* p_sender);

View File

@@ -0,0 +1,66 @@
#ifndef MXBACKGROUNDAUDIOMANAGER_H
#define MXBACKGROUNDAUDIOMANAGER_H
#include "mxaudiopresenter.h"
#include "mxcore.h"
#include "mxdsaction.h"
#include "mxnotificationmanager.h"
#include "mxpresenter.h"
#include "mxtypes.h"
// VTABLE: LEGO1 0x100d9fe8
// SIZE 0x150
class MxBackgroundAudioManager : public MxCore {
public:
MxBackgroundAudioManager();
virtual ~MxBackgroundAudioManager() override;
virtual MxLong Notify(MxParam& p_param) override; // vtable+0x04
virtual MxResult Tickle() override; // vtable+0x08
// FUNCTION: LEGO1 0x1007eb70
inline virtual const char* ClassName() const override // vtable+0x0c
{
// STRING: LEGO1 0x100f7ac4
return "MxBackgroundAudioManager";
}
// FUNCTION: LEGO1 0x1007eb80
inline virtual MxBool IsA(const char* p_name) const override // vtable+0x10
{
return !strcmp(p_name, MxBackgroundAudioManager::ClassName()) || MxCore::IsA(p_name);
}
void StartAction(MxParam& p_param);
void StopAction(MxParam& p_param);
MxResult PlayMusic(MxDSAction& p_action, undefined4 p_unk0x140, undefined4 p_unk0x13c);
void FUN_1007ee70();
void FUN_1007ef40();
void FadeInOrFadeOut();
__declspec(dllexport) void Enable(MxBool p_enable);
virtual MxResult Create(MxAtomId& p_script, MxU32 p_frequencyMS);
void Stop();
void LowerVolume();
void RaiseVolume();
private:
void Init();
MxResult OpenMusic(MxAtomId& p_script);
void DestroyMusic();
MxBool m_musicEnabled; // 0x8
MxDSAction m_action1; // 0xc
MxAudioPresenter* m_unk0xa0;
MxDSAction m_action2; // 0xa4
MxAudioPresenter* m_unk0x138;
MxS32 m_unk0x13c;
MxS32 m_unk0x140;
MxS32 m_targetVolume;
MxS16 m_unk0x148;
MxAtomId m_script;
};
#endif // MXBACKGROUNDAUDIOMANAGER_H

View File

@@ -0,0 +1,40 @@
#ifndef MXCOMPOSITEMEDIAPRESENTER_H
#define MXCOMPOSITEMEDIAPRESENTER_H
#include "mxcompositepresenter.h"
// VTABLE: LEGO1 0x100d96b0
// SIZE 0x50
class MxCompositeMediaPresenter : public MxCompositePresenter {
public:
MxCompositeMediaPresenter();
virtual ~MxCompositeMediaPresenter() override;
virtual MxResult Tickle() override; // vtable+0x08
// FUNCTION: LEGO1 0x10073f10
inline virtual const char* ClassName() const override // vtable+0x0c
{
// STRING: LEGO1 0x100f02d4
return "MxCompositeMediaPresenter";
}
// FUNCTION: LEGO1 0x10073f20
inline virtual MxBool IsA(const char* p_name) const override // vtable+0x10
{
return !strcmp(p_name, MxCompositeMediaPresenter::ClassName()) || MxCompositePresenter::IsA(p_name);
}
virtual void StartingTickle() override; // vtable+0x1c
virtual MxResult StartAction(MxStreamController*, MxDSAction* p_action) override; // vtable+0x3c
virtual MxResult PutData() override; // vtable+0x4c
private:
MxS16 m_unk0x4c; // 0x4c
MxBool m_unk0x4e; // 0x4e
};
// SYNTHETIC: LEGO1 0x10074000
// MxCompositeMediaPresenter::`scalar deleting destructor'
#endif // MXCOMPOSITEMEDIAPRESENTER_H

View File

@@ -0,0 +1,75 @@
#ifndef MXTRANSITIONMANAGER_H
#define MXTRANSITIONMANAGER_H
#include "legoomni.h"
#include "mxcore.h"
#include "mxvideopresenter.h"
#include <ddraw.h>
// VTABLE: LEGO1 0x100d7ea0
class MxTransitionManager : public MxCore {
public:
MxTransitionManager();
virtual ~MxTransitionManager() override; // vtable+0x0
__declspec(dllexport) void SetWaitIndicator(MxVideoPresenter* p_waitIndicator);
virtual MxResult Tickle(); // vtable+0x8
// FUNCTION: LEGO1 0x1004b950
inline virtual const char* ClassName() const override // vtable+0x0c
{
return "MxTransitionManager";
}
// FUNCTION: LEGO1 0x1004b960
inline virtual MxBool IsA(const char* p_name) const override // vtable+0x10
{
return !strcmp(p_name, MxTransitionManager::ClassName()) || MxCore::IsA(p_name);
}
virtual MxResult GetDDrawSurfaceFromVideoManager(); // vtable+0x14
enum TransitionType {
NOT_TRANSITIONING,
NO_ANIMATION,
DISSOLVE,
PIXELATION,
SCREEN_WIPE,
WINDOWS,
BROKEN // Unknown what this is supposed to be, it locks the game up
};
MxResult StartTransition(TransitionType p_animationType, MxS32 p_speed, MxBool p_doCopy, MxBool p_playMusicInAnim);
inline TransitionType GetTransitionType() { return m_transitionType; }
private:
void EndTransition(MxBool p_notifyWorld);
void TransitionNone();
void TransitionDissolve();
void TransitionPixelation();
void TransitionWipe();
void TransitionWindows();
void TransitionBroken();
void SubmitCopyRect(LPDDSURFACEDESC p_ddsc);
void SetupCopyRect(LPDDSURFACEDESC p_ddsc);
MxVideoPresenter* m_waitIndicator; // 0x08
RECT m_copyRect; // 0x0c
MxU8* m_copyBuffer; // 0x1c
FlagBitfield m_copyFlags; // 0x20
undefined4 m_unk0x24; // 0x24
FlagBitfield m_unk0x28; // 0x28
TransitionType m_transitionType; // 0x2c
LPDIRECTDRAWSURFACE m_ddSurface; // 0x30
MxU16 m_animationTimer; // 0x34
MxU16 m_columnOrder[640]; // 0x36
MxU16 m_randomShift[480]; // 0x536
MxULong m_systemTime; // 0x8f8
MxS32 m_animationSpeed; // 0x8fc
};
#endif // MXTRANSITIONMANAGER_H

View File

@@ -0,0 +1,326 @@
#include "mxbackgroundaudiomanager.h"
#include "legoomni.h"
#include "mxcompositepresenter.h"
#include "mxdssound.h"
#include "mxomni.h"
#include "mxpresenter.h"
#include "mxstreamer.h"
#include "mxticklemanager.h"
DECOMP_SIZE_ASSERT(MxBackgroundAudioManager, 0x150)
// FUNCTION: LEGO1 0x1007ea90
MxBackgroundAudioManager::MxBackgroundAudioManager()
{
NotificationManager()->Register(this);
m_unk0xa0 = 0;
m_unk0x138 = 0;
m_unk0x13c = 0;
m_unk0x140 = 0;
m_targetVolume = 0;
m_unk0x148 = 0;
m_musicEnabled = FALSE;
}
// FUNCTION: LEGO1 0x1007ec20
MxBackgroundAudioManager::~MxBackgroundAudioManager()
{
TickleManager()->UnregisterClient(this);
NotificationManager()->Unregister(this);
DestroyMusic();
}
// FUNCTION: LEGO1 0x1007ece0
MxResult MxBackgroundAudioManager::Create(MxAtomId& p_script, MxU32 p_frequencyMS)
{
MxResult result = OpenMusic(p_script);
if (result == SUCCESS) {
TickleManager()->RegisterClient(this, p_frequencyMS);
m_musicEnabled = TRUE;
}
return result;
}
// FUNCTION: LEGO1 0x1007ed20
MxResult MxBackgroundAudioManager::OpenMusic(MxAtomId& p_script)
{
if (m_script.GetInternal())
DestroyMusic();
MxResult result = FAILURE;
if (Streamer()->Open(p_script.GetInternal(), 0)) {
m_script = p_script;
result = SUCCESS;
}
return result;
}
// FUNCTION: LEGO1 0x1007ed70
void MxBackgroundAudioManager::DestroyMusic()
{
if (m_script.GetInternal()) {
MxDSAction ds;
ds.SetAtomId(m_script);
ds.SetUnknown24(-2);
DeleteObject(ds);
Streamer()->Close(m_script.GetInternal());
m_musicEnabled = FALSE;
}
}
// FUNCTION: LEGO1 0x1007ee40
MxResult MxBackgroundAudioManager::Tickle()
{
switch (m_unk0x13c) {
case MxPresenter::TickleState_Starting:
FadeInOrFadeOut();
return SUCCESS;
case MxPresenter::TickleState_Streaming:
FUN_1007ee70();
return SUCCESS;
case MxPresenter::TickleState_Repeating:
FUN_1007ef40();
return SUCCESS;
default:
return SUCCESS;
}
}
// FUNCTION: LEGO1 0x1007ee70
void MxBackgroundAudioManager::FUN_1007ee70()
{
if (m_unk0xa0 && m_unk0xa0->GetAction()) {
DeleteObject(*m_unk0x138->GetAction());
}
if (m_unk0x138) {
m_unk0xa0 = m_unk0x138;
m_action1 = m_action2;
m_unk0x138 = NULL;
m_action2.SetObjectId(-1);
m_action2.SetAtomId(MxAtomId());
m_unk0x13c = 0;
}
}
// FUNCTION: LEGO1 0x1007ef40
void MxBackgroundAudioManager::FUN_1007ef40()
{
MxU32 compare;
MxU32 volume;
if (m_unk0xa0 == NULL) {
if (m_unk0x138) {
compare = 30;
if (m_unk0x148 == 0) {
compare = m_unk0x148;
}
volume = m_unk0x138->GetVolume();
if (volume < compare) {
if (m_unk0x140 + m_unk0x138->GetVolume() <= compare) {
compare = m_unk0x140 + compare;
}
m_unk0x138->SetVolume(compare);
}
else {
m_unk0x138->SetVolume(compare);
m_unk0xa0 = m_unk0x138;
m_action1 = m_action2;
m_unk0x138 = NULL;
m_action2.SetObjectId(-1);
m_action2.SetAtomId(MxAtomId());
m_unk0x13c = 0;
}
}
}
else if (m_unk0xa0->GetAction() != NULL) {
if (m_unk0xa0->GetVolume() == 0) {
DeleteObject(*m_unk0xa0->GetAction());
}
else {
compare = m_unk0xa0->GetVolume();
volume = 0;
if (compare != m_unk0x140 && -1 < compare - m_unk0x140) {
volume = m_unk0xa0->GetVolume() - m_unk0x140;
}
m_unk0x138->SetVolume(volume);
}
}
}
// FUNCTION: LEGO1 0x1007f0e0
void MxBackgroundAudioManager::FadeInOrFadeOut()
{
// This function probably is the fade in/out routine
if (m_unk0xa0 != NULL) {
undefined4 volume = m_unk0xa0->GetVolume();
MxU32 compare = 30;
if (m_unk0x148 == 0) {
compare = m_targetVolume;
}
if (volume < compare) {
volume = m_unk0x140 + volume;
if (compare <= volume) {
volume = compare;
}
m_unk0xa0->SetVolume(volume);
}
else if (compare < volume) {
volume = volume - m_unk0x140;
if (volume <= compare) {
volume = compare;
}
m_unk0xa0->SetVolume(volume);
}
else {
m_unk0xa0->SetVolume(volume);
m_unk0x13c = 0;
}
}
else {
m_unk0x13c = 0;
}
}
// FUNCTION: LEGO1 0x1007f170
MxLong MxBackgroundAudioManager::Notify(MxParam& p_param)
{
switch (((MxNotificationParam&) p_param).GetNotification()) {
case c_notificationStartAction:
StartAction(p_param);
return 1;
case c_notificationEndAction:
StopAction(p_param);
return 1;
}
return 0;
}
// FUNCTION: LEGO1 0x1007f1b0
void MxBackgroundAudioManager::StartAction(MxParam& p_param)
{
// TODO: the sender is most likely a MxAudioPresenter?
m_unk0x138 = (MxAudioPresenter*) ((MxNotificationParam&) p_param).GetSender();
m_action2.SetAtomId(m_unk0x138->GetAction()->GetAtomId());
m_action2.SetObjectId(m_unk0x138->GetAction()->GetObjectId());
m_targetVolume = ((MxDSSound*) (m_unk0x138->GetAction()))->GetVolume();
m_unk0x138->SetVolume(0);
}
// FUNCTION: LEGO1 0x1007f200
void MxBackgroundAudioManager::StopAction(MxParam& p_param)
{
if (((MxNotificationParam&) p_param).GetSender() == m_unk0xa0) {
m_unk0xa0 = NULL;
m_action1.SetAtomId(MxAtomId());
m_action1.SetObjectId(-1);
}
else if (((MxNotificationParam&) p_param).GetSender() == m_unk0x138) {
m_unk0x138 = NULL;
m_action2.SetAtomId(MxAtomId());
m_action2.SetObjectId(-1);
}
Lego()->HandleActionEnd(p_param);
}
// FUNCTION: LEGO1 0x1007f2f0
MxResult MxBackgroundAudioManager::PlayMusic(MxDSAction& p_action, undefined4 p_unk0x140, undefined4 p_unk0x13c)
{
if (!m_musicEnabled) {
return SUCCESS;
}
if (m_action2.GetObjectId() == -1 && m_action1.GetObjectId() != p_action.GetObjectId()) {
MxDSAction action;
action.SetAtomId(GetCurrentAction().GetAtomId());
action.SetObjectId(GetCurrentAction().GetObjectId());
action.SetUnknown24(GetCurrentAction().GetUnknown24());
m_action2.SetAtomId(p_action.GetAtomId());
m_action2.SetObjectId(p_action.GetObjectId());
m_action2.SetUnknown84(this);
m_action2.SetOrigin(this);
MxResult result = Start(&m_action2);
GetCurrentAction().SetAtomId(action.GetAtomId());
GetCurrentAction().SetObjectId(action.GetObjectId());
GetCurrentAction().SetUnknown24(action.GetUnknown24());
if (result == SUCCESS) {
m_unk0x13c = p_unk0x13c;
m_unk0x140 = p_unk0x140;
}
return result;
}
return FAILURE;
}
// FUNCTION: LEGO1 0x1007f470
void MxBackgroundAudioManager::Stop()
{
if (m_action2.GetObjectId() != -1)
DeleteObject(m_action2);
m_unk0x138 = 0;
m_action2.SetAtomId(MxAtomId());
m_action2.SetObjectId(-1);
if (m_action1.GetObjectId() != -1)
DeleteObject(m_action1);
m_unk0xa0 = 0;
m_action1.SetAtomId(MxAtomId());
m_unk0x148 = 0;
m_action1.SetObjectId(-1);
m_unk0x13c = 0;
}
// FUNCTION: LEGO1 0x1007f570
void MxBackgroundAudioManager::LowerVolume()
{
if (m_unk0x148 == 0) {
if (m_unk0x13c == 0) {
m_unk0x13c = 2;
}
m_unk0x140 = 20;
}
m_unk0x148++;
}
// FUNCTION: LEGO1 0x1007f5b0
void MxBackgroundAudioManager::RaiseVolume()
{
if (m_unk0x148 != 0) {
m_unk0x148--;
if (m_unk0x148 == 0) {
if (m_unk0x13c == 0) {
m_unk0x13c = 2;
}
m_unk0x140 = 10;
}
}
}
// FUNCTION: LEGO1 0x1007f5f0
void MxBackgroundAudioManager::Enable(MxBool p_enable)
{
if (this->m_musicEnabled != p_enable) {
this->m_musicEnabled = p_enable;
if (!p_enable) {
Stop();
}
}
}
// FUNCTION: LEGO1 0x1007f650
void MxBackgroundAudioManager::Init()
{
this->m_unk0xa0 = 0;
this->m_unk0x13c = 0;
}

View File

@@ -0,0 +1,172 @@
#include "mxcompositemediapresenter.h"
#include "legoomni.h"
#include "legosoundmanager.h"
#include "legovideomanager.h"
#include "mxautolocker.h"
#include "mxdsmultiaction.h"
#include "mxmediapresenter.h"
#include "mxobjectfactory.h"
#include "mxtimer.h"
DECOMP_SIZE_ASSERT(MxCompositeMediaPresenter, 0x50)
// FUNCTION: LEGO1 0x10073ea0
MxCompositeMediaPresenter::MxCompositeMediaPresenter()
{
m_unk0x4c = 0;
m_unk0x4e = FALSE;
VideoManager()->AddPresenter(*this);
}
// FUNCTION: LEGO1 0x10074020
MxCompositeMediaPresenter::~MxCompositeMediaPresenter()
{
VideoManager()->RemovePresenter(*this);
}
// FUNCTION: LEGO1 0x10074090
MxResult MxCompositeMediaPresenter::StartAction(MxStreamController* p_controller, MxDSAction* p_action)
{
MxAutoLocker lock(&m_criticalSection);
MxResult result = FAILURE;
MxDSActionList* actions = ((MxDSMultiAction*) p_action)->GetActionList();
MxDSActionListCursor cursor(actions);
MxDSAction* action;
if (MxPresenter::StartAction(p_controller, p_action) == SUCCESS) {
// The usual cursor.Next() loop doesn't match here, even though
// the logic is the same. It does match when "deconstructed" into
// the following Head(), Current() and NextFragment() calls,
// but this seems unlikely to be the original code.
// The alpha debug build also uses Next().
// cursor.Head();
// while (cursor.Current(action)) {
// cursor.NextFragment();
while (cursor.Next(action)) {
MxBool success = FALSE;
action->CopyFlags(m_action->GetFlags());
const char* presenterName = PresenterNameDispatch(*action);
MxPresenter* presenter = (MxPresenter*) ObjectFactory()->Create(presenterName);
if (presenter && presenter->AddToManager() == SUCCESS) {
presenter->SetCompositePresenter(this);
if (presenter->StartAction(p_controller, action) == SUCCESS) {
presenter->SetTickleState(TickleState_Idle);
if (presenter->IsA("MxVideoPresenter"))
VideoManager()->RemovePresenter(*presenter);
else if (presenter->IsA("MxAudioPresenter"))
SoundManager()->RemovePresenter(*presenter);
success = TRUE;
}
}
if (success) {
action->SetOrigin(this);
m_list.push_back(presenter);
}
else if (presenter)
delete presenter;
}
if (!m_compositePresenter) {
SetTickleState(TickleState_Ready);
MxLong time = Timer()->GetTime();
m_action->SetUnknown90(time);
}
result = SUCCESS;
}
return result;
}
// FUNCTION: LEGO1 0x100742e0
void MxCompositeMediaPresenter::StartingTickle()
{
MxAutoLocker lock(&m_criticalSection);
if (!m_unk0x4e) {
for (MxCompositePresenterList::iterator it = m_list.begin(); it != m_list.end(); it++) {
if ((*it)->GetCurrentTickleState() < TickleState_Streaming) {
(*it)->Tickle();
if ((*it)->GetCurrentTickleState() == TickleState_Streaming ||
((*it)->GetAction() && (*it)->GetAction()->GetStartTime()))
m_unk0x4c++;
}
}
if (m_list.size() == m_unk0x4c) {
m_unk0x4e = TRUE;
m_unk0x4c = 0;
for (MxCompositePresenterList::iterator it = m_list.begin(); it != m_list.end(); it++) {
if (!(*it)->GetAction()->GetStartTime())
m_unk0x4c++;
}
}
}
else {
for (MxCompositePresenterList::iterator it = m_list.begin(); it != m_list.end(); it++) {
if (!(*it)->GetAction()->GetStartTime() && ((MxMediaPresenter*) *it)->FUN_100b5650() &&
!((*it)->GetAction()->GetFlags() & MxDSAction::Flag_Bit9)) {
(*it)->Tickle();
(*it)->GetAction()->SetFlags((*it)->GetAction()->GetFlags() | MxDSAction::Flag_Bit9);
m_unk0x4c--;
}
}
if (!m_unk0x4c) {
m_previousTickleStates |= 1 << (unsigned char) m_currentTickleState;
m_currentTickleState = TickleState_Streaming;
MxLong time = Timer()->GetTime();
m_action->SetUnknown90(time);
}
}
}
// FUNCTION: LEGO1 0x10074470
MxResult MxCompositeMediaPresenter::Tickle()
{
MxAutoLocker lock(&m_criticalSection);
switch (m_currentTickleState) {
case TickleState_Ready:
m_previousTickleStates |= 1 << (unsigned char) m_currentTickleState;
m_currentTickleState = TickleState_Starting;
case TickleState_Starting:
StartingTickle();
break;
case TickleState_Streaming:
case TickleState_Repeating:
case TickleState_unk5:
case TickleState_Done: {
for (MxCompositePresenterList::iterator it = m_list.begin(); it != m_list.end(); it++)
(*it)->Tickle();
break;
}
default:
break;
}
return SUCCESS;
}
// FUNCTION: LEGO1 0x10074540
MxResult MxCompositeMediaPresenter::PutData()
{
MxAutoLocker lock(&m_criticalSection);
if (m_currentTickleState >= TickleState_Streaming && m_currentTickleState <= TickleState_Done) {
for (MxCompositePresenterList::iterator it = m_list.begin(); it != m_list.end(); it++)
(*it)->PutData();
}
return SUCCESS;
}

View File

@@ -111,9 +111,6 @@ MxAtomId* g_nocdSourceName = NULL;
// GLOBAL: LEGO1 0x100f6718
const char* g_current = "current";
// GLOBAL: LEGO1 0x101020e8
void (*g_omniUserMessage)(const char*, int);
// GLOBAL: LEGO1 0x100f4c58
MxBool g_isWorldActive = TRUE;
@@ -694,144 +691,3 @@ MxResult Start(MxDSAction* p_dsAction)
{
return MxOmni::GetInstance()->Start(p_dsAction);
}
// Probably should be somewhere else
// FUNCTION: LEGO1 0x100b6e10
MxBool FUN_100b6e10(
MxS32 p_bitmapWidth,
MxS32 p_bitmapHeight,
MxS32 p_videoParamWidth,
MxS32 p_videoParamHeight,
MxS32* p_left,
MxS32* p_top,
MxS32* p_right,
MxS32* p_bottom,
MxS32* p_width,
MxS32* p_height
)
{
MxPoint32 topLeft(*p_left, *p_top);
MxRect32 bitmapRect(MxPoint32(0, 0), MxSize32(p_bitmapWidth, p_bitmapHeight));
MxPoint32 bottomRight(*p_right, *p_bottom);
MxRect32 videoParamRect(MxPoint32(0, 0), MxSize32(p_videoParamWidth, p_videoParamHeight));
MxRect32 rect(0, 0, *p_width, *p_height);
rect.AddPoint(topLeft);
if (!rect.IntersectsWith(bitmapRect))
return FALSE;
rect.Intersect(bitmapRect);
rect.SubtractPoint(topLeft);
rect.AddPoint(bottomRight);
if (!rect.IntersectsWith(videoParamRect))
return FALSE;
rect.Intersect(videoParamRect);
rect.SubtractPoint(bottomRight);
*p_left += rect.GetLeft();
*p_top += rect.GetTop();
*p_right += rect.GetLeft();
*p_bottom += rect.GetTop();
*p_width = rect.GetWidth();
*p_height = rect.GetHeight();
return TRUE;
}
// FUNCTION: LEGO1 0x100b6ff0
void MakeSourceName(char* p_output, const char* p_input)
{
const char* cln = strchr(p_input, ':');
if (cln) {
p_input = cln + 1;
}
strcpy(p_output, p_input);
strlwr(p_output);
char* extLoc = strstr(p_output, ".si");
if (extLoc) {
*extLoc = 0;
}
}
// FUNCTION: LEGO1 0x100b7050
MxBool KeyValueStringParse(char* p_outputValue, const char* p_key, const char* p_source)
{
MxBool didMatch = FALSE;
MxS16 len = strlen(p_source);
char* temp = new char[len + 1];
strcpy(temp, p_source);
char* token = strtok(temp, ", \t\r\n:");
while (token) {
len -= (strlen(token) + 1);
if (strcmpi(token, p_key) == 0) {
if (p_outputValue && len > 0) {
char* cur = &token[strlen(p_key)];
cur++;
while (*cur != ',') {
if (*cur == ' ' || *cur == '\0' || *cur == '\t' || *cur == '\n' || *cur == '\r')
break;
*p_outputValue++ = *cur++;
}
*p_outputValue = '\0';
}
didMatch = TRUE;
break;
}
token = strtok(NULL, ", \t\r\n:");
}
delete[] temp;
return didMatch;
}
// FUNCTION: LEGO1 0x100b7210
void SetOmniUserMessage(void (*p_userMsg)(const char*, int))
{
g_omniUserMessage = p_userMsg;
}
// FUNCTION: LEGO1 0x100c0280
MxDSObject* CreateStreamObject(MxDSFile* p_file, MxS16 p_ofs)
{
MxU8* buf;
_MMCKINFO tmpChunk;
if (p_file->Seek(((MxLong*) p_file->GetBuffer())[p_ofs], 0)) {
return NULL;
}
if (p_file->Read((MxU8*) &tmpChunk.ckid, 8) == 0 && tmpChunk.ckid == FOURCC('M', 'x', 'S', 't')) {
if (p_file->Read((MxU8*) &tmpChunk.ckid, 8) == 0 && tmpChunk.ckid == FOURCC('M', 'x', 'O', 'b')) {
buf = new MxU8[tmpChunk.cksize];
if (!buf) {
return NULL;
}
if (p_file->Read(buf, tmpChunk.cksize) != 0) {
return NULL;
}
// Save a copy so we can clean up properly, because
// this function will alter the pointer value.
MxU8* copy = buf;
MxDSObject* obj = DeserializeDSObjectDispatch(&buf, -1);
delete[] copy;
return obj;
}
return NULL;
}
return NULL;
}

View File

@@ -0,0 +1,607 @@
#include "mxtransitionmanager.h"
#include "legoinputmanager.h"
#include "legoutil.h"
#include "legovideomanager.h"
#include "legoworld.h"
#include "mxbackgroundaudiomanager.h"
#include "mxparam.h"
#include "mxticklemanager.h"
DECOMP_SIZE_ASSERT(MxTransitionManager, 0x900);
// GLOBAL: LEGO1 0x100f4378
RECT g_fullScreenRect = {0, 0, 640, 480};
// FUNCTION: LEGO1 0x1004b8d0
MxTransitionManager::MxTransitionManager()
{
m_animationTimer = 0;
m_transitionType = NOT_TRANSITIONING;
m_ddSurface = NULL;
m_waitIndicator = NULL;
m_copyBuffer = NULL;
m_copyFlags.m_bit0 = FALSE;
m_unk0x28.m_bit0 = FALSE;
m_unk0x24 = 0;
}
// FUNCTION: LEGO1 0x1004ba00
MxTransitionManager::~MxTransitionManager()
{
delete[] m_copyBuffer;
if (m_waitIndicator != NULL) {
delete m_waitIndicator->GetAction();
delete m_waitIndicator;
}
TickleManager()->UnregisterClient(this);
}
// FUNCTION: LEGO1 0x1004baa0
MxResult MxTransitionManager::GetDDrawSurfaceFromVideoManager() // vtable+0x14
{
LegoVideoManager* videoManager = VideoManager();
this->m_ddSurface = videoManager->GetDisplaySurface()->GetDirectDrawSurface2();
return SUCCESS;
}
// FUNCTION: LEGO1 0x1004bac0
MxResult MxTransitionManager::Tickle()
{
if (this->m_animationSpeed + this->m_systemTime > timeGetTime()) {
return SUCCESS;
}
this->m_systemTime = timeGetTime();
switch (this->m_transitionType) {
case NO_ANIMATION:
TransitionNone();
break;
case DISSOLVE:
TransitionDissolve();
break;
case PIXELATION:
TransitionPixelation();
break;
case SCREEN_WIPE:
TransitionWipe();
break;
case WINDOWS:
TransitionWindows();
break;
case BROKEN:
TransitionBroken();
break;
}
return SUCCESS;
}
// FUNCTION: LEGO1 0x1004bb70
MxResult MxTransitionManager::StartTransition(
TransitionType p_animationType,
MxS32 p_speed,
MxBool p_doCopy,
MxBool p_playMusicInAnim
)
{
if (this->m_transitionType == NOT_TRANSITIONING) {
if (!p_playMusicInAnim) {
MxBackgroundAudioManager* backgroundAudioManager = BackgroundAudioManager();
backgroundAudioManager->Stop();
}
this->m_transitionType = p_animationType;
m_copyFlags.m_bit0 = p_doCopy;
if (m_copyFlags.m_bit0 && m_waitIndicator != NULL) {
m_waitIndicator->Enable(TRUE);
MxDSAction* action = m_waitIndicator->GetAction();
action->SetLoopCount(10000);
action->SetFlags(action->GetFlags() | MxDSAction::Flag_Bit10);
}
MxU32 time = timeGetTime();
this->m_systemTime = time;
this->m_animationSpeed = p_speed;
MxTickleManager* tickleManager = TickleManager();
tickleManager->RegisterClient(this, p_speed);
LegoInputManager* inputManager = InputManager();
inputManager->m_unk0x88 = TRUE;
inputManager->m_unk0x336 = FALSE;
LegoVideoManager* videoManager = VideoManager();
videoManager->SetUnkE4(FALSE);
SetAppCursor(1);
return SUCCESS;
}
return FAILURE;
}
// FUNCTION: LEGO1 0x1004bc30
void MxTransitionManager::EndTransition(MxBool p_notifyWorld)
{
if (m_transitionType != NOT_TRANSITIONING) {
m_transitionType = NOT_TRANSITIONING;
m_copyFlags.m_bit0 = FALSE;
TickleManager()->UnregisterClient(this);
if (p_notifyWorld) {
LegoWorld* world = GetCurrentWorld();
if (world) {
#ifdef COMPAT_MODE
{
MxNotificationParam param(MXTRANSITIONMANAGER_TRANSITIONENDED, this);
world->Notify(param);
}
#else
world->Notify(MxNotificationParam(MXTRANSITIONMANAGER_TRANSITIONENDED, this));
#endif
}
}
}
}
// FUNCTION: LEGO1 0x1004bcf0
void MxTransitionManager::TransitionNone()
{
LegoVideoManager* videoManager = VideoManager();
videoManager->GetDisplaySurface()->FUN_100ba640();
EndTransition(TRUE);
}
// FUNCTION: LEGO1 0x1004bd10
void MxTransitionManager::TransitionDissolve()
{
// If the animation is finished
if (m_animationTimer == 40) {
m_animationTimer = 0;
EndTransition(TRUE);
return;
}
// If we are starting the animation
if (m_animationTimer == 0) {
// Generate the list of columns in order...
MxS32 i;
for (i = 0; i < 640; i++) {
m_columnOrder[i] = i;
}
// ...then shuffle the list (to ensure that we hit each column once)
for (i = 0; i < 640; i++) {
MxS32 swap = rand() % 640;
MxU16 t = m_columnOrder[i];
m_columnOrder[i] = m_columnOrder[swap];
m_columnOrder[swap] = t;
}
// For each scanline, pick a random X offset
for (i = 0; i < 480; i++) {
m_randomShift[i] = rand() % 640;
}
}
// Run one tick of the animation
DDSURFACEDESC ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
if (res == DDERR_SURFACELOST) {
m_ddSurface->Restore();
res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
}
if (res == DD_OK) {
SubmitCopyRect(&ddsd);
for (MxS32 col = 0; col < 640; col++) {
// Select 16 columns on each tick
if (m_animationTimer * 16 > m_columnOrder[col])
continue;
if (m_animationTimer * 16 + 15 < m_columnOrder[col])
continue;
for (MxS32 row = 0; row < 480; row++) {
// Shift the chosen column a different amount at each scanline.
// We use the same shift for that scanline each time.
// By the end, every pixel gets hit.
MxS32 xShift = (m_randomShift[row] + col) % 640;
// Set the chosen pixel to black
if (ddsd.ddpfPixelFormat.dwRGBBitCount == 8) {
((MxU8*) ddsd.lpSurface)[row * ddsd.lPitch + xShift] = 0;
}
else {
((MxU16*) ddsd.lpSurface)[row * ddsd.lPitch + xShift] = 0;
}
}
}
SetupCopyRect(&ddsd);
m_ddSurface->Unlock(ddsd.lpSurface);
if (VideoManager()->GetVideoParam().Flags().GetFlipSurfaces()) {
LPDIRECTDRAWSURFACE surf = VideoManager()->GetDisplaySurface()->GetDirectDrawSurface1();
surf->BltFast(0, 0, m_ddSurface, &g_fullScreenRect, DDBLTFAST_WAIT);
}
m_animationTimer++;
}
}
// FUNCTION: LEGO1 0x1004bed0
void MxTransitionManager::TransitionPixelation()
{
if (m_animationTimer == 16) {
m_animationTimer = 0;
EndTransition(TRUE);
return;
}
if (m_animationTimer == 0) {
// Same init/shuffle steps as the dissolve transition, except that
// we are using big blocky pixels and only need 64 columns.
MxS32 i;
for (i = 0; i < 64; i++) {
m_columnOrder[i] = i;
}
for (i = 0; i < 64; i++) {
MxS32 swap = rand() % 64;
MxU16 t = m_columnOrder[i];
m_columnOrder[i] = m_columnOrder[swap];
m_columnOrder[swap] = t;
}
// The same is true here. We only need 48 rows.
for (i = 0; i < 48; i++) {
m_randomShift[i] = rand() % 64;
}
}
// Run one tick of the animation
DDSURFACEDESC ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT res = m_ddSurface->Lock(NULL, &ddsd, 1, NULL);
if (res == DDERR_SURFACELOST) {
m_ddSurface->Restore();
res = m_ddSurface->Lock(NULL, &ddsd, 1, NULL);
}
if (res == DD_OK) {
SubmitCopyRect(&ddsd);
for (MxS32 col = 0; col < 64; col++) {
// Select 4 columns on each tick
if (m_animationTimer * 4 > m_columnOrder[col])
continue;
if (m_animationTimer * 4 + 3 < m_columnOrder[col])
continue;
for (MxS32 row = 0; row < 48; row++) {
MxS32 xShift = 10 * ((m_randomShift[row] + col) % 64);
// To do the pixelation, we subdivide the 640x480 surface into
// 10x10 pixel blocks. At the chosen block, we sample the top-leftmost
// color and set the other 99 pixels to that value.
// Find the pixel to sample
MxS32 sampleOfs = 10 * row * ddsd.lPitch + xShift;
MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8;
// Save this cast from void* to save time.
// Seems to help accuracy doing it this way.
MxU8* surface = (MxU8*) ddsd.lpSurface;
MxU8* source = surface + sampleOfs * bytesPerPixel;
MxU32 sample = bytesPerPixel == 1 ? *source : *(MxU16*) source;
for (MxS32 k = 10 * row; k < 10 * row + 10; k++) {
if (ddsd.ddpfPixelFormat.dwRGBBitCount == 8) {
// TODO: This block and the next don't match, but they are
// hopefully correct in principle.
MxU16 colorWord = MAKEWORD(LOBYTE(sample), LOBYTE(sample));
MxU32 newColor = MAKELONG(colorWord, colorWord);
MxU8* pos = surface + k * ddsd.lPitch + xShift;
MxU32* dest = (MxU32*) pos;
// Sets 10 pixels (10 bytes)
dest[0] = newColor;
dest[1] = newColor;
MxU16* half = (MxU16*) (dest + 2);
*half = newColor;
}
else {
MxU32 newColor = MAKELONG(sample, sample);
// You might expect a cast to MxU16* instead, but lPitch is
// bytes/scanline, not pixels/scanline. Therefore, we just
// need to double the xShift to get to the right spot.
MxU8* pos = surface + k * ddsd.lPitch + 2 * xShift;
MxU32* dest = (MxU32*) pos;
// Sets 10 pixels (20 bytes)
dest[0] = newColor;
dest[1] = newColor;
dest[2] = newColor;
dest[3] = newColor;
dest[4] = newColor;
}
}
}
}
SetupCopyRect(&ddsd);
m_ddSurface->Unlock(ddsd.lpSurface);
if (VideoManager()->GetVideoParam().Flags().GetFlipSurfaces()) {
LPDIRECTDRAWSURFACE surf = VideoManager()->GetDisplaySurface()->GetDirectDrawSurface1();
surf->BltFast(0, 0, m_ddSurface, &g_fullScreenRect, DDBLTFAST_WAIT);
}
m_animationTimer++;
}
}
// FUNCTION: LEGO1 0x1004c170
void MxTransitionManager::TransitionWipe()
{
// If the animation is finished
if (m_animationTimer == 240) {
m_animationTimer = 0;
EndTransition(TRUE);
return;
}
DDSURFACEDESC ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
if (res == DDERR_SURFACELOST) {
m_ddSurface->Restore();
res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
}
if (res == DD_OK) {
SubmitCopyRect(&ddsd);
// For each of the 240 animation ticks, blank out two scanlines
// starting at the top of the screen.
// (dwRGBBitCount / 8) will tell how many bytes are used per pixel.
MxU8* line = (MxU8*) ddsd.lpSurface + 2 * ddsd.lPitch * m_animationTimer;
memset(line, 0, 640 * ddsd.ddpfPixelFormat.dwRGBBitCount / 8);
line += ddsd.lPitch;
memset(line, 0, 640 * ddsd.ddpfPixelFormat.dwRGBBitCount / 8);
SetupCopyRect(&ddsd);
m_ddSurface->Unlock(ddsd.lpSurface);
m_animationTimer++;
}
}
// FUNCTION: LEGO1 0x1004c270
void MxTransitionManager::TransitionWindows()
{
if (m_animationTimer == 240) {
m_animationTimer = 0;
EndTransition(TRUE);
return;
}
DDSURFACEDESC ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
if (res == DDERR_SURFACELOST) {
m_ddSurface->Restore();
res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
}
if (res == DD_OK) {
SubmitCopyRect(&ddsd);
MxU8* line = (MxU8*) ddsd.lpSurface + m_animationTimer * ddsd.lPitch;
MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8;
MxS32 bytesPerLine = bytesPerPixel * 640;
memset(line, 0, bytesPerLine);
for (MxS32 i = m_animationTimer + 1; i < 480 - m_animationTimer; i++) {
line += ddsd.lPitch;
memset(line + m_animationTimer * bytesPerPixel, 0, bytesPerPixel);
memset(line + 640 + (-1 - m_animationTimer) * bytesPerPixel, 0, bytesPerPixel);
}
line += ddsd.lPitch;
memset(line, 0, bytesPerLine);
SetupCopyRect(&ddsd);
m_ddSurface->Unlock(ddsd.lpSurface);
m_animationTimer++;
}
}
// FUNCTION: LEGO1 0x1004c3e0
void MxTransitionManager::TransitionBroken()
{
// This function has no actual animation logic.
// It also never calls EndTransition to
// properly terminate the transition, so
// the game just hangs forever.
DDSURFACEDESC ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
if (res == DDERR_SURFACELOST) {
m_ddSurface->Restore();
res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
}
if (res == DD_OK) {
SubmitCopyRect(&ddsd);
SetupCopyRect(&ddsd);
m_ddSurface->Unlock(ddsd.lpSurface);
}
}
// FUNCTION: LEGO1 0x1004c470
void MxTransitionManager::SetWaitIndicator(MxVideoPresenter* p_waitIndicator)
{
// End current wait indicator
if (m_waitIndicator != NULL) {
m_waitIndicator->GetAction()->SetFlags(m_waitIndicator->GetAction()->GetFlags() & ~MxDSAction::Flag_World);
m_waitIndicator->EndAction();
m_waitIndicator = NULL;
}
// Check if we were given a new wait indicator
if (p_waitIndicator != NULL) {
// Setup the new wait indicator
m_waitIndicator = p_waitIndicator;
LegoVideoManager* videoManager = VideoManager();
videoManager->RemovePresenter(*m_waitIndicator);
if (m_waitIndicator->GetCurrentTickleState() < MxPresenter::TickleState_Streaming) {
m_waitIndicator->Tickle();
}
}
else {
// Disable copy rect
m_copyFlags.m_bit0 = FALSE;
}
}
// FUNCTION: LEGO1 0x1004c4d0
void MxTransitionManager::SubmitCopyRect(LPDDSURFACEDESC p_ddsc)
{
// Check if the copy rect is setup
if (m_copyFlags.m_bit0 == FALSE || m_waitIndicator == NULL || m_copyBuffer == NULL) {
return;
}
// Copy the copy rect onto the surface
MxU8* dst;
MxU32 bytesPerPixel = p_ddsc->ddpfPixelFormat.dwRGBBitCount / 8;
const MxU8* src = (const MxU8*) m_copyBuffer;
MxS32 copyPitch;
copyPitch = ((m_copyRect.right - m_copyRect.left) + 1) * bytesPerPixel;
MxS32 y;
dst = (MxU8*) p_ddsc->lpSurface + (p_ddsc->lPitch * m_copyRect.top) + (bytesPerPixel * m_copyRect.left);
for (y = 0; y < m_copyRect.bottom - m_copyRect.top + 1; ++y) {
memcpy(dst, src, copyPitch);
src += copyPitch;
dst += p_ddsc->lPitch;
}
// Free the copy buffer
delete[] m_copyBuffer;
m_copyBuffer = NULL;
}
// FUNCTION: LEGO1 0x1004c580
void MxTransitionManager::SetupCopyRect(LPDDSURFACEDESC p_ddsc)
{
// Check if the copy rect is setup
if (m_copyFlags.m_bit0 == FALSE || m_waitIndicator == NULL) {
return;
}
// Tickle wait indicator
m_waitIndicator->Tickle();
// Check if wait indicator has started
if (m_waitIndicator->GetCurrentTickleState() >= MxPresenter::TickleState_Streaming) {
// Setup the copy rect
MxU32 copyPitch = (p_ddsc->ddpfPixelFormat.dwRGBBitCount / 8) *
(m_copyRect.right - m_copyRect.left + 1); // This uses m_copyRect, seemingly erroneously
MxU32 bytesPerPixel = p_ddsc->ddpfPixelFormat.dwRGBBitCount / 8;
m_copyRect.left = m_waitIndicator->GetLocation().GetX();
m_copyRect.top = m_waitIndicator->GetLocation().GetY();
MxS32 height = m_waitIndicator->GetHeight();
MxS32 width = m_waitIndicator->GetWidth();
m_copyRect.right = m_copyRect.left + width - 1;
m_copyRect.bottom = m_copyRect.top + height - 1;
// Allocate the copy buffer
const MxU8* src =
(const MxU8*) p_ddsc->lpSurface + m_copyRect.top * p_ddsc->lPitch + bytesPerPixel * m_copyRect.left;
m_copyBuffer = new MxU8[bytesPerPixel * width * height];
if (!m_copyBuffer)
return;
// Copy into the copy buffer
MxU8* dst = m_copyBuffer;
for (MxS32 i = 0; i < (m_copyRect.bottom - m_copyRect.top + 1); i++) {
memcpy(dst, src, copyPitch);
src += p_ddsc->lPitch;
dst += copyPitch;
}
}
// Setup display surface
if ((m_waitIndicator->GetAction()->GetFlags() & MxDSAction::Flag_Bit5) != 0) {
MxDisplaySurface* displaySurface = VideoManager()->GetDisplaySurface();
MxBool und = FALSE;
displaySurface->VTable0x2c(
p_ddsc,
m_waitIndicator->GetBitmap(),
0,
0,
m_waitIndicator->GetLocation().GetX(),
m_waitIndicator->GetLocation().GetY(),
m_waitIndicator->GetWidth(),
m_waitIndicator->GetHeight(),
und
);
}
else {
MxDisplaySurface* displaySurface = VideoManager()->GetDisplaySurface();
displaySurface->VTable0x24(
p_ddsc,
m_waitIndicator->GetBitmap(),
0,
0,
m_waitIndicator->GetLocation().GetX(),
m_waitIndicator->GetLocation().GetY(),
m_waitIndicator->GetWidth(),
m_waitIndicator->GetHeight()
);
}
}