diff --git a/LEGO1/lego/legoomni/include/infocenter.h b/LEGO1/lego/legoomni/include/infocenter.h index 2839bc88..8c4f8aa9 100644 --- a/LEGO1/lego/legoomni/include/infocenter.h +++ b/LEGO1/lego/legoomni/include/infocenter.h @@ -119,25 +119,6 @@ struct InfocenterMapEntry { // SIZE 0x1d8 class Infocenter : public LegoWorld { public: - enum Cutscene { - e_noIntro = -1, - e_legoMovie, - e_mindscapeMovie, - e_introMovie, - e_outroMovie, - e_badEndMovie, - e_goodEndMovie - }; - - enum Character { - e_noCharacter = 0, - e_pepper, - e_mama, - e_papa, - e_nick, - e_laura - }; - Infocenter(); ~Infocenter() override; @@ -180,7 +161,7 @@ class Infocenter : public LegoWorld { void UpdateFrameHot(MxBool p_display); void Reset(); - void PlayCutscene(Cutscene p_entityId, MxBool p_scale); + void PlayCutscene(IntroScript::Script p_entityId, MxBool p_scale); void StopCutscene(); void UpdateEnabledGlowControl(MxS32 p_x, MxS32 p_y); @@ -198,7 +179,7 @@ class Infocenter : public LegoWorld { MxS16 m_selectedCharacter; // 0xfc InfocenterState* m_infocenterState; // 0x100 LegoGameState::Area m_destLocation; // 0x104 - Cutscene m_currentCutscene; // 0x108 + IntroScript::Script m_currentCutscene; // 0x108 Radio m_radio; // 0x10c MxStillPresenter* m_dragPresenter; // 0x11c InfocenterMapEntry m_glowInfo[7]; // 0x120 diff --git a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp index 7e3d9294..fde06e8f 100644 --- a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp @@ -630,7 +630,6 @@ void MxTransitionManager::SetupCopyRect(LPDDSURFACEDESC p_ddsc) // Setup display surface if ((m_waitIndicator->GetAction()->GetFlags() & MxDSAction::c_bit5) != 0) { MxDisplaySurface* displaySurface = VideoManager()->GetDisplaySurface(); - MxBool und = FALSE; displaySurface->VTable0x2c( p_ddsc, m_waitIndicator->GetBitmap(), @@ -639,8 +638,7 @@ void MxTransitionManager::SetupCopyRect(LPDDSURFACEDESC p_ddsc) m_waitIndicator->GetLocation().GetX(), m_waitIndicator->GetLocation().GetY(), m_waitIndicator->GetWidth(), - m_waitIndicator->GetHeight(), - und + m_waitIndicator->GetHeight() ); } else { diff --git a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp index fe56a353..b0e2a7bf 100644 --- a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp +++ b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp @@ -852,7 +852,7 @@ void LegoVideoManager::SetCursorBitmap(const CursorBitmap* p_cursorBitmap) m_cursorRect.bottom = p_cursorBitmap->height; m_cursorRect.right = p_cursorBitmap->width; - m_cursorSurface = MxDisplaySurface::CreateCursorSurface(p_cursorBitmap); + m_cursorSurface = MxDisplaySurface::CreateCursorSurface(p_cursorBitmap, m_videoParam.GetPalette()); if (m_cursorSurface == NULL) { m_drawCursor = FALSE; diff --git a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp index ce610d82..b51011b5 100644 --- a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp +++ b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp @@ -2,8 +2,10 @@ #include "act3.h" #include "credits_actions.h" +#include "extensions/siloader.h" #include "helicopter.h" #include "infomain_actions.h" +#include "intro_actions.h" #include "jukebox.h" #include "jukebox_actions.h" #include "legoact2.h" @@ -35,6 +37,8 @@ DECOMP_SIZE_ASSERT(Infocenter, 0x1d8) DECOMP_SIZE_ASSERT(InfocenterMapEntry, 0x18) DECOMP_SIZE_ASSERT(InfocenterState, 0x94) +using namespace Extensions; + // GLOBAL: LEGO1 0x100f76a0 const char* g_object2x4red = "2x4red"; @@ -128,13 +132,13 @@ InfomainScript::Script g_bricksterDialogue[2] = { // FUNCTION: LEGO1 0x1006ea20 Infocenter::Infocenter() { - m_selectedCharacter = e_noCharacter; + m_selectedCharacter = LegoActor::c_none; m_dragPresenter = NULL; m_infocenterState = NULL; m_frame = NULL; m_destLocation = LegoGameState::e_undefined; m_currentInfomainScript = InfomainScript::c_noneInfomain; - m_currentCutscene = e_noIntro; + m_currentCutscene = IntroScript::c_noneIntro; memset(&m_glowInfo, 0, sizeof(m_glowInfo)); @@ -312,19 +316,19 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) GameState()->SetActor(m_selectedCharacter); switch (m_selectedCharacter) { - case e_pepper: + case LegoActor::c_pepper: PlayAction(InfomainScript::c_avo901in_RunAnim); break; - case e_mama: + case LegoActor::c_mama: PlayAction(InfomainScript::c_avo902in_RunAnim); break; - case e_papa: + case LegoActor::c_papa: PlayAction(InfomainScript::c_avo903in_RunAnim); break; - case e_nick: + case LegoActor::c_nick: PlayAction(InfomainScript::c_avo904in_RunAnim); break; - case e_laura: + case LegoActor::c_laura: PlayAction(InfomainScript::c_avo905in_RunAnim); break; default: @@ -338,7 +342,8 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) MxLong result = m_radio.Notify(p_param); - if (result || (action->GetAtomId() != m_atomId && action->GetAtomId() != *g_introScript)) { + if (result || (action->GetAtomId() != m_atomId && action->GetAtomId() != *g_introScript && + !Extension::Call(ReplacedIn, *action, m_atomId, *g_introScript).value_or(false))) { return result; } @@ -350,23 +355,23 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) switch (m_infocenterState->m_state) { case InfocenterState::e_playCutscene: switch (m_currentCutscene) { - case e_legoMovie: - PlayCutscene(e_mindscapeMovie, FALSE); + case IntroScript::c_Lego_Movie: + PlayCutscene(IntroScript::c_Mindscape_Movie, FALSE); return 1; - case e_mindscapeMovie: - PlayCutscene(e_introMovie, TRUE); + case IntroScript::c_Mindscape_Movie: + PlayCutscene(IntroScript::c_Intro_Movie, TRUE); return 1; - case e_badEndMovie: + case IntroScript::c_BadEnd_Movie: StopCutscene(); m_infocenterState->m_state = InfocenterState::e_welcomeAnimation; PlayAction(InfomainScript::c_tic092in_RunAnim); - m_currentCutscene = e_noIntro; + m_currentCutscene = IntroScript::c_noneIntro; return 1; - case e_goodEndMovie: + case IntroScript::c_GoodEnd_Movie: StopCutscene(); m_infocenterState->m_state = InfocenterState::e_welcomeAnimation; PlayAction(InfomainScript::c_tic089in_RunAnim); - m_currentCutscene = e_noIntro; + m_currentCutscene = IntroScript::c_noneIntro; return 1; } @@ -374,7 +379,7 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) StopCutscene(); m_infocenterState->m_state = InfocenterState::e_welcomeAnimation; PlayAction(InfomainScript::c_iic001in_RunAnim); - m_currentCutscene = e_noIntro; + m_currentCutscene = IntroScript::c_noneIntro; if (!m_infocenterState->HasRegistered()) { m_bookAnimationTimer = 1; @@ -385,17 +390,17 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) m_infocenterState->m_state = InfocenterState::e_welcomeAnimation; switch (m_currentCutscene) { - case e_badEndMovie: + case IntroScript::c_BadEnd_Movie: PlayAction(InfomainScript::c_tic092in_RunAnim); break; - case e_goodEndMovie: + case IntroScript::c_GoodEnd_Movie: PlayAction(InfomainScript::c_tic089in_RunAnim); break; default: PlayAction(InfomainScript::c_iic001in_RunAnim); } - m_currentCutscene = e_noIntro; + m_currentCutscene = IntroScript::c_noneIntro; return 1; case InfocenterState::e_notRegistered: SetROIVisible(g_object2x4red, FALSE); @@ -412,7 +417,7 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) break; case InfocenterState::e_selectedCharacterAndDestination: if (action->GetObjectId() == m_currentInfomainScript) { - if (GameState()->GetCurrentAct() != LegoGameState::e_act3 && m_selectedCharacter != e_noCharacter) { + if (GameState()->GetCurrentAct() != LegoGameState::e_act3 && m_selectedCharacter != LegoActor::c_none) { GameState()->SetActor(m_selectedCharacter); } TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); @@ -462,7 +467,7 @@ void Infocenter::ReadyWorld() switch (m_infocenterState->m_state) { case InfocenterState::e_newState: - PlayCutscene(e_legoMovie, TRUE); + PlayCutscene(IntroScript::c_Lego_Movie, TRUE); m_infocenterState->m_state = InfocenterState::e_playCutscene; return; case InfocenterState::e_selectedSave: @@ -525,7 +530,7 @@ void Infocenter::ReadyWorld() if (state && state->GetState() == LegoAct2State::c_badEnding) { bg->Enable(TRUE); - PlayCutscene(e_badEndMovie, TRUE); + PlayCutscene(IntroScript::c_BadEnd_Movie, TRUE); m_infocenterState->m_state = InfocenterState::e_playCutscene; return; } @@ -570,14 +575,14 @@ void Infocenter::ReadyWorld() if (state && state->GetState() == Act3State::e_badEnding) { bg->Enable(TRUE); - PlayCutscene(e_badEndMovie, TRUE); + PlayCutscene(IntroScript::c_BadEnd_Movie, TRUE); m_infocenterState->m_state = InfocenterState::e_playCutscene; return; } if (state && state->GetState() == Act3State::e_goodEnding) { bg->Enable(TRUE); - PlayCutscene(e_goodEndMovie, TRUE); + PlayCutscene(IntroScript::c_GoodEnd_Movie, TRUE); m_infocenterState->m_state = InfocenterState::e_playCutscene; return; } @@ -759,19 +764,19 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) switch (m_dragPresenter->GetAction()->GetObjectId()) { case InfomainScript::c_PepperHot_Bitmap: - m_selectedCharacter = e_pepper; + m_selectedCharacter = LegoActor::c_pepper; break; case InfomainScript::c_MamaHot_Bitmap: - m_selectedCharacter = e_mama; + m_selectedCharacter = LegoActor::c_mama; break; case InfomainScript::c_PapaHot_Bitmap: - m_selectedCharacter = e_papa; + m_selectedCharacter = LegoActor::c_papa; break; case InfomainScript::c_NickHot_Bitmap: - m_selectedCharacter = e_nick; + m_selectedCharacter = LegoActor::c_nick; break; case InfomainScript::c_LauraHot_Bitmap: - m_selectedCharacter = e_laura; + m_selectedCharacter = LegoActor::c_laura; break; } @@ -780,7 +785,7 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) switch (control->GetAction()->GetObjectId()) { case InfomainScript::c_Pepper_Ctl: - if (m_selectedCharacter == e_pepper) { + if (m_selectedCharacter == LegoActor::c_pepper) { m_radio.Stop(); BackgroundAudioManager()->Stop(); PlayAction(InfomainScript::c_Pepper_All_Movie); @@ -788,7 +793,7 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) } break; case InfomainScript::c_Mama_Ctl: - if (m_selectedCharacter == e_mama) { + if (m_selectedCharacter == LegoActor::c_mama) { m_radio.Stop(); BackgroundAudioManager()->Stop(); PlayAction(InfomainScript::c_Mama_All_Movie); @@ -796,7 +801,7 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) } break; case InfomainScript::c_Papa_Ctl: - if (m_selectedCharacter == e_papa) { + if (m_selectedCharacter == LegoActor::c_papa) { m_radio.Stop(); BackgroundAudioManager()->Stop(); PlayAction(InfomainScript::c_Papa_All_Movie); @@ -804,7 +809,7 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) } break; case InfomainScript::c_Nick_Ctl: - if (m_selectedCharacter == e_nick) { + if (m_selectedCharacter == LegoActor::c_nick) { m_radio.Stop(); BackgroundAudioManager()->Stop(); PlayAction(InfomainScript::c_Nick_All_Movie); @@ -812,7 +817,7 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) } break; case InfomainScript::c_Laura_Ctl: - if (m_selectedCharacter == e_laura) { + if (m_selectedCharacter == LegoActor::c_laura) { m_radio.Stop(); BackgroundAudioManager()->Stop(); PlayAction(InfomainScript::c_Laura_All_Movie); @@ -830,19 +835,19 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) GameState()->SetActor(m_selectedCharacter); switch (m_selectedCharacter) { - case e_pepper: + case LegoActor::c_pepper: PlayAction(InfomainScript::c_avo901in_RunAnim); break; - case e_mama: + case LegoActor::c_mama: PlayAction(InfomainScript::c_avo902in_RunAnim); break; - case e_papa: + case LegoActor::c_papa: PlayAction(InfomainScript::c_avo903in_RunAnim); break; - case e_nick: + case LegoActor::c_nick: PlayAction(InfomainScript::c_avo904in_RunAnim); break; - case e_laura: + case LegoActor::c_laura: PlayAction(InfomainScript::c_avo905in_RunAnim); break; } @@ -901,23 +906,23 @@ MxU8 Infocenter::HandleButtonUp(MxS32 p_x, MxS32 p_y) } else { switch (m_selectedCharacter) { - case e_pepper: + case LegoActor::c_pepper: dialogueToPlay = InfomainScript::c_avo901in_RunAnim; GameState()->SetActorId(m_selectedCharacter); break; - case e_mama: + case LegoActor::c_mama: dialogueToPlay = InfomainScript::c_avo902in_RunAnim; GameState()->SetActorId(m_selectedCharacter); break; - case e_papa: + case LegoActor::c_papa: dialogueToPlay = InfomainScript::c_avo903in_RunAnim; GameState()->SetActorId(m_selectedCharacter); break; - case e_nick: + case LegoActor::c_nick: dialogueToPlay = InfomainScript::c_avo904in_RunAnim; GameState()->SetActorId(m_selectedCharacter); break; - case e_laura: + case LegoActor::c_laura: dialogueToPlay = InfomainScript::c_avo905in_RunAnim; GameState()->SetActorId(m_selectedCharacter); break; @@ -1262,7 +1267,7 @@ MxResult Infocenter::Tickle() } // FUNCTION: LEGO1 0x10070c20 -void Infocenter::PlayCutscene(Cutscene p_entityId, MxBool p_scale) +void Infocenter::PlayCutscene(IntroScript::Script p_entityId, MxBool p_scale) { m_currentCutscene = p_entityId; @@ -1272,9 +1277,9 @@ void Infocenter::PlayCutscene(Cutscene p_entityId, MxBool p_scale) SetAppCursor(e_cursorNone); VideoManager()->GetDisplaySurface()->ClearScreen(); - if (m_currentCutscene != e_noIntro) { + if (m_currentCutscene != IntroScript::c_noneIntro) { // check if the cutscene is an ending - if (m_currentCutscene >= e_badEndMovie && m_currentCutscene <= e_goodEndMovie) { + if (m_currentCutscene >= IntroScript::c_BadEnd_Movie && m_currentCutscene <= IntroScript::c_GoodEnd_Movie) { Reset(); } @@ -1285,7 +1290,7 @@ void Infocenter::PlayCutscene(Cutscene p_entityId, MxBool p_scale) // FUNCTION: LEGO1 0x10070cb0 void Infocenter::StopCutscene() { - if (m_currentCutscene != e_noIntro) { + if (m_currentCutscene != IntroScript::c_noneIntro) { InvokeAction(Extra::ActionType::e_close, *g_introScript, m_currentCutscene, NULL); } @@ -1402,9 +1407,9 @@ void Infocenter::Reset() GameState()->m_savedPreviousArea = LegoGameState::e_undefined; InitializeBitmaps(); - m_selectedCharacter = e_pepper; + m_selectedCharacter = LegoActor::c_pepper; - GameState()->SetActor(e_pepper); + GameState()->SetActor(LegoActor::c_pepper); HelicopterState* state = (HelicopterState*) GameState()->GetState("HelicopterState"); diff --git a/LEGO1/omni/include/mxdisplaysurface.h b/LEGO1/omni/include/mxdisplaysurface.h index aa30b543..e0d1a240 100644 --- a/LEGO1/omni/include/mxdisplaysurface.h +++ b/LEGO1/omni/include/mxdisplaysurface.h @@ -61,8 +61,7 @@ class MxDisplaySurface : public MxCore { MxS32 p_right, MxS32 p_bottom, MxS32 p_width, - MxS32 p_height, - MxBool p_RLE + MxS32 p_height ); // vtable+0x2c virtual void VTable0x30( MxBitmap* p_bitmap, @@ -71,8 +70,7 @@ class MxDisplaySurface : public MxCore { MxS32 p_right, MxS32 p_bottom, MxS32 p_width, - MxS32 p_height, - MxBool p_RLE + MxS32 p_height ); // vtable+0x30 virtual void Display( MxS32 p_left, @@ -92,23 +90,12 @@ class MxDisplaySurface : public MxCore { ); // vtable+0x44 void ClearScreen(); - static LPDIRECTDRAWSURFACE CreateCursorSurface(const CursorBitmap* p_cursorBitmap); + static LPDIRECTDRAWSURFACE CreateCursorSurface(const CursorBitmap* p_cursorBitmap, MxPalette* p_palette); static LPDIRECTDRAWSURFACE CopySurface(LPDIRECTDRAWSURFACE p_src); LPDIRECTDRAWSURFACE GetDirectDrawSurface1() { return m_ddSurface1; } LPDIRECTDRAWSURFACE GetDirectDrawSurface2() { return m_ddSurface2; } MxVideoParam& GetVideoParam() { return m_videoParam; } - - void DrawTransparentRLE( - MxU8*& p_bitmapData, - MxU8*& p_surfaceData, - MxU32 p_bitmapSize, - MxS32 p_width, - MxS32 p_height, - MxLong p_pitch, - MxU8 p_bpp - ); - LPDIRECTDRAWSURFACE FUN_100bc8b0(MxS32 p_width, MxS32 p_height); private: diff --git a/LEGO1/omni/src/notify/mxnotificationmanager.cpp b/LEGO1/omni/src/notify/mxnotificationmanager.cpp index 064f38f3..373c8aef 100644 --- a/LEGO1/omni/src/notify/mxnotificationmanager.cpp +++ b/LEGO1/omni/src/notify/mxnotificationmanager.cpp @@ -2,6 +2,7 @@ #include "compat.h" #include "decomp.h" +#include "extensions/siloader.h" #include "mxautolock.h" #include "mxmisc.h" #include "mxnotificationparam.h" @@ -12,6 +13,8 @@ DECOMP_SIZE_ASSERT(MxNotification, 0x08); DECOMP_SIZE_ASSERT(MxNotificationManager, 0x40); +using namespace Extensions; + // FUNCTION: LEGO1 0x100ac220 MxNotification::MxNotification(MxCore* p_target, const MxNotificationParam& p_param) { @@ -105,6 +108,11 @@ MxResult MxNotificationManager::Tickle() while (m_sendList->size() != 0) { MxNotification* notif = m_sendList->front(); m_sendList->pop_front(); + + if (notif->GetParam()->GetNotification() == c_notificationEndAction) { + Extension::Call(HandleEndAction, (MxEndActionNotificationParam&) *notif->GetParam()); + } + notif->GetTarget()->Notify(*notif->GetParam()); delete notif; } @@ -159,6 +167,11 @@ void MxNotificationManager::FlushPending(MxCore* p_listener) while (pending.size() != 0) { notif = pending.front(); pending.pop_front(); + + if (notif->GetParam()->GetNotification() == c_notificationEndAction) { + Extension::Call(HandleEndAction, (MxEndActionNotificationParam&) *notif->GetParam()); + } + notif->GetTarget()->Notify(*notif->GetParam()); delete notif; } diff --git a/LEGO1/omni/src/stream/mxstreamer.cpp b/LEGO1/omni/src/stream/mxstreamer.cpp index bb5f1cb5..8693ba6e 100644 --- a/LEGO1/omni/src/stream/mxstreamer.cpp +++ b/LEGO1/omni/src/stream/mxstreamer.cpp @@ -66,7 +66,7 @@ MxStreamController* MxStreamer::Open(const char* p_name, MxU16 p_lookupType) MxStreamController* stream = NULL; - if (GetOpenStream(p_name)) { + if ((stream = GetOpenStream(p_name))) { goto done; } diff --git a/LEGO1/omni/src/video/mxdisplaysurface.cpp b/LEGO1/omni/src/video/mxdisplaysurface.cpp index 71a3808e..387b31b8 100644 --- a/LEGO1/omni/src/video/mxdisplaysurface.cpp +++ b/LEGO1/omni/src/video/mxdisplaysurface.cpp @@ -327,6 +327,7 @@ void MxDisplaySurface::SetPalette(MxPalette* p_palette) } } +#ifndef MINIWIN MxS32 bitCount = m_surfaceDesc.ddpfPixelFormat.dwRGBBitCount; if (bitCount == 8) { return; @@ -378,6 +379,7 @@ void MxDisplaySurface::SetPalette(MxPalette* p_palette) m_32bitPal[i] = red | green | blue | alpha; } } +#endif } // FUNCTION: LEGO1 0x100bacc0 @@ -412,7 +414,13 @@ void MxDisplaySurface::VTable0x28( ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; ddsd.dwWidth = p_width; ddsd.dwHeight = p_height; +#ifdef MINIWIN + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 8; +#else ddsd.ddpfPixelFormat = m_surfaceDesc.ddpfPixelFormat; +#endif ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; LPDIRECTDRAWSURFACE tempSurface = nullptr; @@ -422,6 +430,24 @@ void MxDisplaySurface::VTable0x28( return; } +#ifdef MINIWIN + MxBITMAPINFO* bmi = p_bitmap->GetBitmapInfo(); + if (bmi) { + PALETTEENTRY pe[256]; + for (int i = 0; i < 256; i++) { + pe[i].peRed = bmi->m_bmiColors[i].rgbRed; + pe[i].peGreen = bmi->m_bmiColors[i].rgbGreen; + pe[i].peBlue = bmi->m_bmiColors[i].rgbBlue; + pe[i].peFlags = PC_NONE; + } + + LPDIRECTDRAWPALETTE palette = nullptr; + if (draw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &palette, NULL) == DD_OK && palette) { + tempSurface->SetPalette(palette); + palette->Release(); + } + } +#else if (m_surfaceDesc.ddpfPixelFormat.dwRGBBitCount != 32) { DDCOLORKEY colorKey; if (m_surfaceDesc.ddpfPixelFormat.dwRGBBitCount == 8) { @@ -432,6 +458,7 @@ void MxDisplaySurface::VTable0x28( } tempSurface->SetColorKey(DDCKEY_SRCBLT, &colorKey); } +#endif DDSURFACEDESC tempDesc; memset(&tempDesc, 0, sizeof(tempDesc)); @@ -450,7 +477,7 @@ void MxDisplaySurface::VTable0x28( MxU8* data = p_bitmap->GetStart(p_left, p_top); - MxS32 bytesPerPixel = m_surfaceDesc.ddpfPixelFormat.dwRGBBitCount / 8; + MxS32 bytesPerPixel = tempDesc.ddpfPixelFormat.dwRGBBitCount / 8; MxU8* surface = (MxU8*) tempDesc.lpSurface; MxLong stride = (bytesPerPixel == 1) ? GetAdjustedStride(p_bitmap) : -p_width + GetAdjustedStride(p_bitmap); @@ -502,8 +529,7 @@ void MxDisplaySurface::VTable0x30( MxS32 p_right, MxS32 p_bottom, MxS32 p_width, - MxS32 p_height, - MxBool p_RLE + MxS32 p_height ) { if (!GetRectIntersection( @@ -526,7 +552,13 @@ void MxDisplaySurface::VTable0x30( ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; ddsd.dwWidth = p_width; ddsd.dwHeight = p_height; +#ifdef MINIWIN + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 8; +#else ddsd.ddpfPixelFormat = m_surfaceDesc.ddpfPixelFormat; +#endif ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; LPDIRECTDRAW draw = MVideoManager()->GetDirectDraw(); @@ -535,6 +567,25 @@ void MxDisplaySurface::VTable0x30( return; } +#ifdef MINIWIN + MxBITMAPINFO* bmi = p_bitmap->GetBitmapInfo(); + if (bmi) { + PALETTEENTRY pe[256]; + for (int i = 0; i < 256; i++) { + pe[i].peRed = bmi->m_bmiColors[i].rgbRed; + pe[i].peGreen = bmi->m_bmiColors[i].rgbGreen; + pe[i].peBlue = bmi->m_bmiColors[i].rgbBlue; + pe[i].peFlags = PC_NONE; + } + + LPDIRECTDRAWPALETTE palette = nullptr; + if (draw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &palette, NULL) == DD_OK && palette) { + tempSurface->SetPalette(palette); + palette->Release(); + } + } +#endif + DDCOLORKEY colorKey; colorKey.dwColorSpaceLowValue = colorKey.dwColorSpaceHighValue = 0; tempSurface->SetColorKey(DDCKEY_SRCBLT, &colorKey); @@ -550,38 +601,32 @@ void MxDisplaySurface::VTable0x30( MxU8* data = p_bitmap->GetStart(p_left, p_top); - MxS32 bytesPerPixel = m_surfaceDesc.ddpfPixelFormat.dwRGBBitCount / 8; + MxS32 bytesPerPixel = tempDesc.ddpfPixelFormat.dwRGBBitCount / 8; MxU8* surface = (MxU8*) tempDesc.lpSurface; - if (p_RLE) { - MxS32 size = p_bitmap->GetBmiHeader()->biSizeImage; - DrawTransparentRLE(data, surface, size, p_width, p_height, tempDesc.lPitch, bytesPerPixel * 8); - } - else { - MxLong stride = -p_width + GetAdjustedStride(p_bitmap); - MxLong length = -bytesPerPixel * p_width + tempDesc.lPitch; + MxLong stride = -p_width + GetAdjustedStride(p_bitmap); + MxLong length = -bytesPerPixel * p_width + tempDesc.lPitch; - for (MxS32 i = 0; i < p_height; i++) { - for (MxS32 j = 0; j < p_width; j++) { - if (*data != 0) { - switch (bytesPerPixel) { - case 1: - *surface = *data; - break; - case 2: - *(MxU16*) surface = m_16bitPal[*data]; - break; - default: - *(MxU32*) surface = m_32bitPal[*data]; - break; - } + for (MxS32 i = 0; i < p_height; i++) { + for (MxS32 j = 0; j < p_width; j++) { + if (*data != 0) { + switch (bytesPerPixel) { + case 1: + *surface = *data; + break; + case 2: + *(MxU16*) surface = m_16bitPal[*data]; + break; + default: + *(MxU32*) surface = m_32bitPal[*data]; + break; } - data++; - surface += bytesPerPixel; } - data += stride; - surface += length; + data++; + surface += bytesPerPixel; } + data += stride; + surface += length; } tempSurface->Unlock(NULL); @@ -591,161 +636,6 @@ void MxDisplaySurface::VTable0x30( tempSurface->Release(); } -// FUNCTION: LEGO1 0x100bb500 -// FUNCTION: BETA10 0x10140cd6 -void MxDisplaySurface::DrawTransparentRLE( - MxU8*& p_bitmapData, - MxU8*& p_surfaceData, - MxU32 p_bitmapSize, - MxS32 p_width, - MxS32 p_height, - MxLong p_pitch, - MxU8 p_bpp -) -{ - /* Assumes partial RLE for the bitmap: only the skipped pixels are compressed. - The drawn pixels are uncompressed. The procedure is: - 1. Read 3 bytes from p_bitmapData. Skip this many pixels on the surface. - 2. Read 3 bytes from p_bitmapData. Draw this many pixels on the surface. - 3. Repeat until the end of p_bitmapData is reached. */ - - MxU8* end = p_bitmapData + p_bitmapSize; - MxU8* surfCopy = p_surfaceData; // unused? - - // The total number of pixels drawn or skipped - MxU32 count = 0; - - // Used in both 8 and 16 bit branches - MxU32 skipCount; - MxU32 drawCount; - MxU32 t; - - if (p_bpp == 16) { - // DECOMP: why goto? - goto sixteen_bit; - } - - while (p_bitmapData < end) { - skipCount = *p_bitmapData++; - t = *p_bitmapData++; - skipCount += t << 8; - t = *p_bitmapData++; - skipCount += t << 16; - - MxS32 rowRemainder = p_width - count % p_width; - count += skipCount; - - if (skipCount >= rowRemainder) { - p_surfaceData += rowRemainder; // skip the rest of this row - skipCount -= rowRemainder; - p_surfaceData += p_pitch - p_width; // seek to start of next row - p_surfaceData += p_pitch * (skipCount / p_width); // skip entire rows if any - } - - // skip any pixels at the start of this row - p_surfaceData += skipCount % p_width; - if (p_bitmapData >= end) { - break; - } - - drawCount = *p_bitmapData++; - t = *p_bitmapData++; - drawCount += t << 8; - t = *p_bitmapData++; - drawCount += t << 16; - - rowRemainder = p_width - count % p_width; - count += drawCount; - - if (drawCount >= rowRemainder) { - memcpy(p_surfaceData, p_bitmapData, rowRemainder); - p_surfaceData += rowRemainder; - p_bitmapData += rowRemainder; - - drawCount -= rowRemainder; - - // seek to start of bitmap on this screen row - p_surfaceData += p_pitch - p_width; - MxS32 rows = drawCount / p_width; - - for (MxU32 i = 0; i < rows; i++) { - memcpy(p_surfaceData, p_bitmapData, p_width); - p_bitmapData += p_width; - p_surfaceData += p_pitch; - } - } - - MxS32 tail = drawCount % p_width; - memcpy(p_surfaceData, p_bitmapData, tail); - p_surfaceData += tail; - p_bitmapData += tail; - } - return; - -sixteen_bit: - while (p_bitmapData < end) { - skipCount = *p_bitmapData++; - t = *p_bitmapData++; - skipCount += t << 8; - t = *p_bitmapData++; - skipCount += t << 16; - - MxS32 rowRemainder = p_width - count % p_width; - count += skipCount; - - if (skipCount >= rowRemainder) { - p_surfaceData += 2 * rowRemainder; - skipCount -= rowRemainder; - p_surfaceData += p_pitch - 2 * p_width; - p_surfaceData += p_pitch * (skipCount / p_width); - } - - p_surfaceData += 2 * (skipCount % p_width); - if (p_bitmapData >= end) { - break; - } - - drawCount = *p_bitmapData++; - t = *p_bitmapData++; - drawCount += t << 8; - t = *p_bitmapData++; - drawCount += t << 16; - - rowRemainder = p_width - count % p_width; - count += drawCount; - - if (drawCount >= rowRemainder) { - // memcpy - for (MxU32 j = 0; j < rowRemainder; j++) { - *((MxU16*) p_surfaceData) = m_16bitPal[*p_bitmapData++]; - p_surfaceData += 2; - } - - drawCount -= rowRemainder; - - p_surfaceData += p_pitch - 2 * p_width; - MxS32 rows = drawCount / p_width; - - for (MxU32 i = 0; i < rows; i++) { - // memcpy - for (MxS32 j = 0; j < p_width; j++) { - *((MxU16*) p_surfaceData) = m_16bitPal[*p_bitmapData++]; - p_surfaceData += 2; - } - - p_surfaceData += p_pitch - 2 * p_width; - } - } - - MxS32 tail = drawCount % p_width; - // memcpy - for (MxS32 j = 0; j < tail; j++) { - *((MxU16*) p_surfaceData) = m_16bitPal[*p_bitmapData++]; - p_surfaceData += 2; - } - } -} - // FUNCTION: LEGO1 0x100bba50 void MxDisplaySurface::Display(MxS32 p_left, MxS32 p_top, MxS32 p_left2, MxS32 p_top2, MxS32 p_width, MxS32 p_height) { @@ -829,7 +719,13 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::VTable0x44( ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd.dwWidth = p_bitmap->GetBmiWidth(); ddsd.dwHeight = p_bitmap->GetBmiHeightAbs(); +#ifdef MINIWIN + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 8; +#else ddsd.ddpfPixelFormat = m_surfaceDesc.ddpfPixelFormat; +#endif ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; *p_ret = 0; ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; @@ -852,6 +748,24 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::VTable0x44( } if (surface) { +#ifdef MINIWIN + MxBITMAPINFO* bmi = p_bitmap->GetBitmapInfo(); + if (bmi) { + PALETTEENTRY pe[256]; + for (int i = 0; i < 256; i++) { + pe[i].peRed = bmi->m_bmiColors[i].rgbRed; + pe[i].peGreen = bmi->m_bmiColors[i].rgbGreen; + pe[i].peBlue = bmi->m_bmiColors[i].rgbBlue; + pe[i].peFlags = PC_NONE; + } + LPDIRECTDRAWPALETTE palette = nullptr; + if (draw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &palette, NULL) == DD_OK && palette) { + surface->SetPalette(palette); + palette->Release(); + } + } +#endif + memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); @@ -971,6 +885,21 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::CopySurface(LPDIRECTDRAWSURFACE p_src) return NULL; } +#ifdef MINIWIN + LPDIRECTDRAWPALETTE srcPalette = nullptr; + if (p_src->GetPalette(&srcPalette) == DD_OK && srcPalette) { + PALETTEENTRY pe[256]; + if (srcPalette->GetEntries(0, 0, 256, pe) == DD_OK) { + LPDIRECTDRAWPALETTE newPalette = nullptr; + if (draw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &newPalette, NULL) == DD_OK) { + newSurface->SetPalette(newPalette); + newPalette->Release(); + } + } + srcPalette->Release(); + } +#endif + RECT rect = {0, 0, (LONG) ddsd.dwWidth, (LONG) ddsd.dwHeight}; if (newSurface->BltFast(0, 0, p_src, &rect, DDBLTFAST_WAIT) != DD_OK) { @@ -1084,8 +1013,7 @@ void MxDisplaySurface::VTable0x2c( MxS32 p_right, MxS32 p_bottom, MxS32 p_width, - MxS32 p_height, - MxBool p_RLE + MxS32 p_height ) { // DECOMP: Almost an exact copy of VTable0x28, except that it uses the argument DDSURFACEDESC @@ -1111,38 +1039,25 @@ void MxDisplaySurface::VTable0x2c( MxLong destStride = p_desc->lPitch; MxU8* dest = (MxU8*) p_desc->lpSurface + bytesPerPixel * p_right + (p_bottom * destStride); - if (p_RLE) { - DrawTransparentRLE( - src, - dest, - p_bitmap->GetBmiHeader()->biSizeImage, - p_width, - p_height, - destStride, - m_surfaceDesc.ddpfPixelFormat.dwRGBBitCount - ); - } - else { - MxLong srcStride = GetAdjustedStride(p_bitmap); - MxLong srcSkip = srcStride - p_width; - MxLong destSkip = destStride - bytesPerPixel * p_width; - for (MxS32 i = 0; i < p_height; i++, src += srcSkip, dest += destSkip) { - for (MxS32 j = 0; j < p_width; j++, src++) { - if (*src != 0) { - switch (bytesPerPixel) { - case 1: - *dest = *src; - break; - case 2: - *(MxU16*) dest = m_16bitPal[*src]; - break; - default: - *(MxU32*) dest = m_32bitPal[*src]; - break; - } + MxLong srcStride = GetAdjustedStride(p_bitmap); + MxLong srcSkip = srcStride - p_width; + MxLong destSkip = destStride - bytesPerPixel * p_width; + for (MxS32 i = 0; i < p_height; i++, src += srcSkip, dest += destSkip) { + for (MxS32 j = 0; j < p_width; j++, src++) { + if (*src != 0) { + switch (bytesPerPixel) { + case 1: + *dest = *src; + break; + case 2: + *(MxU16*) dest = m_16bitPal[*src]; + break; + default: + *(MxU32*) dest = m_32bitPal[*src]; + break; } - dest += bytesPerPixel; } + dest += bytesPerPixel; } } } @@ -1183,11 +1098,10 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::FUN_100bc8b0(MxS32 p_width, MxS32 p_height return surface; } -LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface(const CursorBitmap* p_cursorBitmap) +LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface(const CursorBitmap* p_cursorBitmap, MxPalette* p_palette) { LPDIRECTDRAWSURFACE newSurface = NULL; IDirectDraw* draw = MVideoManager()->GetDirectDraw(); - MVideoManager(); DDSURFACEDESC ddsd; memset(&ddsd, 0, sizeof(ddsd)); @@ -1197,14 +1111,19 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface(const CursorBitmap* p_ return NULL; } - MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8; - MxBool isAlphaAvailable = ((ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS) && - (ddsd.ddpfPixelFormat.dwRGBAlphaBitMask != 0); - ddsd.dwWidth = p_cursorBitmap->width; ddsd.dwHeight = p_cursorBitmap->height; ddsd.dwFlags = DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN; +#ifdef MINIWIN + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 8; +#endif + + MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8; + MxBool isAlphaAvailable = ((ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS) && + (ddsd.ddpfPixelFormat.dwRGBAlphaBitMask != 0); if (draw->CreateSurface(&ddsd, &newSurface, NULL) != DD_OK) { ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY; @@ -1215,6 +1134,10 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface(const CursorBitmap* p_ } } +#ifdef MINIWIN + newSurface->SetPalette(p_palette->CreateNativePalette()); +#endif + memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); @@ -1242,6 +1165,8 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface(const CursorBitmap* p_ else { pixel = isBlack ? 0 : 0xff; } + surface[x + y * p_cursorBitmap->width] = pixel; + break; } case 2: { MxU16* surface = (MxU16*) ddsd.lpSurface; @@ -1284,8 +1209,7 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface(const CursorBitmap* p_ switch (bytesPerPixel) { case 1: { DDCOLORKEY colorkey; - colorkey.dwColorSpaceHighValue = 0x10; - colorkey.dwColorSpaceLowValue = 0x10; + colorkey.dwColorSpaceHighValue = colorkey.dwColorSpaceLowValue = 0x10; newSurface->SetColorKey(DDCKEY_SRCBLT, &colorkey); break; } diff --git a/LEGO1/omni/src/video/mxvideopresenter.cpp b/LEGO1/omni/src/video/mxvideopresenter.cpp index cd804de1..ee03ab71 100644 --- a/LEGO1/omni/src/video/mxvideopresenter.cpp +++ b/LEGO1/omni/src/video/mxvideopresenter.cpp @@ -253,8 +253,7 @@ void MxVideoPresenter::PutFrame() rect.GetLeft(), rect.GetTop(), m_frameBitmap->GetBmiWidth(), - m_frameBitmap->GetBmiHeightAbs(), - TRUE + m_frameBitmap->GetBmiHeightAbs() ); } } @@ -280,7 +279,7 @@ void MxVideoPresenter::PutFrame() } } else { - displaySurface->VTable0x30(m_frameBitmap, 0, 0, GetX(), GetY(), GetWidth(), GetHeight(), FALSE); + displaySurface->VTable0x30(m_frameBitmap, 0, 0, GetX(), GetY(), GetWidth(), GetHeight()); } } else if (m_surface) { diff --git a/assets/badend/BadEnd_Smk.smk b/assets/badend/BadEnd_Smk.smk new file mode 100644 index 00000000..401a7e6d Binary files /dev/null and b/assets/badend/BadEnd_Smk.smk differ diff --git a/assets/badend/BadEnd_Wav.wav b/assets/badend/BadEnd_Wav.wav new file mode 100644 index 00000000..33d0d207 Binary files /dev/null and b/assets/badend/BadEnd_Wav.wav differ diff --git a/assets/main.cpp b/assets/main.cpp index d239e013..3557d3e2 100644 --- a/assets/main.cpp +++ b/assets/main.cpp @@ -20,10 +20,16 @@ void CreateWidescreen() struct AssetView { std::string name; std::string extra; + int32_t z; }; const AssetView widescreenBitmaps[] = { {"GaraDoor_Background_Wide", - "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161"} + "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161", + -1}, + {"Police_Background_Wide", + "World:\\lego\\scripts\\police\\police;0, StartWith:\\Lego\\Scripts\\Police\\Police;0, " + "RemoveWith:\\Lego\\Scripts\\Police\\Police;0", + 10} }; si::Interleaf si; @@ -44,9 +50,9 @@ void CreateWidescreen() object->presenter_ = "MxStillPresenter"; object->name_ = asset.name; object->filetype_ = si::MxOb::STL; - object->location_ = si::Vector3(-240.0, 0.0, -1.0); + object->location_ = si::Vector3(-240, 0.0, asset.z); object->direction_ = si::Vector3(0, 0, 0); - object->up_ = si::Vector3(0, 1.0, 0); + object->up_ = si::Vector3(0, 1, 0); if (!object->ReplaceWithFile(file.c_str())) { abort(); @@ -194,6 +200,71 @@ void CreateHDMusic() si.Write(result.c_str()); } +void CreateBadEnd() +{ + std::string result = out + "/badend.si"; + + si::Interleaf si; + mxHd.seek(0, si::MemoryBuffer::SeekStart); + si.Read(&mxHd); + + si::Object* composite = new si::Object; + std::string extra = "Replace:\\lego\\scripts\\intro:4"; + composite->id_ = 0; + composite->type_ = si::MxOb::Presenter; + composite->flags_ = MxDSAction::c_enabled; + composite->duration_ = 0; + composite->loops_ = 1; + composite->extra_ = si::bytearray(extra.c_str(), extra.length() + 1); + composite->presenter_ = "MxCompositeMediaPresenter"; + composite->name_ = "BadEnd_Movie"; + si.AppendChild(composite); + + si::Object* smk = new si::Object; + std::string file = std::string("badend/BadEnd_Smk.smk"); + smk->id_ = 1; + smk->type_ = si::MxOb::Video; + smk->flags_ = MxDSAction::c_enabled; + smk->duration_ = 75100; + smk->loops_ = 1; + smk->presenter_ = "MxSmkPresenter"; + smk->name_ = "BadEnd_Smk"; + smk->filetype_ = si::MxOb::SMK; + smk->location_ = si::Vector3(0, 0, -10000); + smk->direction_ = si::Vector3(0, 0, 1); + smk->up_ = si::Vector3(0, 1, 0); + smk->unknown29_ = 1; // Palette management = yes + if (!smk->ReplaceWithFile(file.c_str())) { + abort(); + } + + composite->AppendChild(smk); + depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl; + + si::Object* wav = new si::Object; + file = std::string("badend/BadEnd_Wav.wav"); + wav->id_ = 2; + wav->type_ = si::MxOb::Sound; + wav->flags_ = MxDSAction::c_enabled; + wav->duration_ = 77800; + wav->loops_ = 1; + wav->presenter_ = "MxWavePresenter"; + wav->name_ = "BadEnd_Wav"; + wav->filetype_ = si::MxOb::WAV; + wav->location_ = si::Vector3(0, 0, 0); + wav->direction_ = si::Vector3(0, 0, 1); + wav->up_ = si::Vector3(0, 1, 0); + wav->volume_ = 79; + if (!wav->ReplaceWithFile(file.c_str())) { + abort(); + } + + composite->AppendChild(wav); + depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl; + + si.Write(result.c_str()); +} + int main(int argc, char* argv[]) { out = argv[1]; @@ -207,5 +278,6 @@ int main(int argc, char* argv[]) CreateWidescreen(); CreateHDMusic(); + CreateBadEnd(); return 0; } diff --git a/assets/widescreen/Police_Background_Wide.bmp b/assets/widescreen/Police_Background_Wide.bmp new file mode 100644 index 00000000..dcbfcc50 --- /dev/null +++ b/assets/widescreen/Police_Background_Wide.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc2cfb5d0522d2cc4cfbaf74d6b1b5f1f2e0512f613e398849a2359d537deff5 +size 538678 diff --git a/extensions/include/extensions/siloader.h b/extensions/include/extensions/siloader.h index ab3e69bc..2a82bd3b 100644 --- a/extensions/include/extensions/siloader.h +++ b/extensions/include/extensions/siloader.h @@ -2,6 +2,7 @@ #include "extensions/extensions.h" #include "legoworld.h" +#include "mxactionnotificationparam.h" #include "mxatom.h" #include @@ -24,31 +25,65 @@ class SiLoader { static std::optional HandleStart(MxDSAction& p_action); static std::optional HandleRemove(StreamObject p_object, LegoWorld* world); static std::optional HandleDelete(MxDSAction& p_action); + static MxBool HandleEndAction(MxEndActionNotificationParam& p_param); + + template + static bool ReplacedIn(MxDSAction& p_action, Args... p_args); static std::map options; static std::vector files; + static std::vector directives; static bool enabled; private: static std::vector> startWith; static std::vector> removeWith; static std::vector> replace; + static std::vector> prepend; + static std::vector fullScreenMovie; + static std::vector disable3d; static bool LoadFile(const char* p_file); - static void ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom = MxAtomId()); + static bool LoadDirective(const char* p_directive); + static MxStreamController* OpenStream(const char* p_file); + static void ParseExtra(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom = MxAtomId()); }; #ifdef EXTENSIONS +template +bool SiLoader::ReplacedIn(MxDSAction& p_action, Args... p_args) +{ + StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + auto checkAtomId = [&p_action, &object](const auto& p_atomId) -> bool { + for (const auto& key : replace) { + if (key.second == object && key.first.first == p_atomId) { + return true; + } + } + + return false; + }; + + return (checkAtomId(p_args) || ...); +} + constexpr auto Load = &SiLoader::Load; constexpr auto HandleFind = &SiLoader::HandleFind; constexpr auto HandleStart = &SiLoader::HandleStart; constexpr auto HandleRemove = &SiLoader::HandleRemove; constexpr auto HandleDelete = &SiLoader::HandleDelete; +constexpr auto HandleEndAction = &SiLoader::HandleEndAction; +constexpr auto ReplacedIn = [](auto&&... args) { return SiLoader::ReplacedIn(std::forward(args)...); }; #else constexpr decltype(&SiLoader::Load) Load = nullptr; constexpr decltype(&SiLoader::HandleFind) HandleFind = nullptr; constexpr decltype(&SiLoader::HandleStart) HandleStart = nullptr; constexpr decltype(&SiLoader::HandleRemove) HandleRemove = nullptr; constexpr decltype(&SiLoader::HandleDelete) HandleDelete = nullptr; +constexpr decltype(&SiLoader::HandleEndAction) HandleEndAction = nullptr; +constexpr auto ReplacedIn = [](auto&&... args) { + ((void) args, ...); + return false; +}; #endif }; // namespace Extensions diff --git a/extensions/src/siloader.cpp b/extensions/src/siloader.cpp index ab23b4a7..3acb667e 100644 --- a/extensions/src/siloader.cpp +++ b/extensions/src/siloader.cpp @@ -9,11 +9,17 @@ using namespace Extensions; +const char prependedMarker[] = ";;prepended;;"; + std::map SiLoader::options; std::vector SiLoader::files; +std::vector SiLoader::directives; std::vector> SiLoader::startWith; std::vector> SiLoader::removeWith; std::vector> SiLoader::replace; +std::vector> SiLoader::prepend; +std::vector SiLoader::fullScreenMovie; +std::vector SiLoader::disable3d; bool SiLoader::enabled = false; void SiLoader::Initialize() @@ -21,11 +27,19 @@ void SiLoader::Initialize() char* files = SDL_strdup(options["si loader:files"].c_str()); char* saveptr; - for (char* file = SDL_strtok_r(files, ",\n", &saveptr); file; file = SDL_strtok_r(NULL, ",\n", &saveptr)) { + for (char* file = SDL_strtok_r(files, ",\n\r ", &saveptr); file; file = SDL_strtok_r(NULL, ",\n\r ", &saveptr)) { SiLoader::files.emplace_back(file); } + char* directives = SDL_strdup(options["si loader:directives"].c_str()); + + for (char* directive = SDL_strtok_r(directives, ",\n\r ", &saveptr); directive; + directive = SDL_strtok_r(NULL, ",\n\r ", &saveptr)) { + SiLoader::directives.emplace_back(directive); + } + SDL_free(files); + SDL_free(directives); } bool SiLoader::Load() @@ -34,6 +48,10 @@ bool SiLoader::Load() LoadFile(file.c_str()); } + for (const auto& directive : directives) { + LoadDirective(directive.c_str()); + } + return true; } @@ -51,34 +69,62 @@ std::optional SiLoader::HandleFind(StreamObject p_object, LegoWorld* wo std::optional SiLoader::HandleStart(MxDSAction& p_action) { StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + auto start = [](const StreamObject& p_object, MxDSAction& p_in, MxDSAction& p_out) -> MxResult { + if (!OpenStream(p_object.first.GetInternal())) { + return FAILURE; + } + + p_out.SetAtomId(p_object.first); + p_out.SetObjectId(p_object.second); + p_out.SetUnknown24(p_in.GetUnknown24()); + p_out.SetNotificationObject(p_in.GetNotificationObject()); + p_out.SetOrigin(p_in.GetOrigin()); + return Start(&p_out); + }; for (const auto& key : startWith) { if (key.first == object) { MxDSAction action; - action.SetAtomId(key.second.first); - action.SetObjectId(key.second.second); - action.SetUnknown24(p_action.GetUnknown24()); - action.SetNotificationObject(p_action.GetNotificationObject()); - action.SetOrigin(p_action.GetOrigin()); - Start(&action); + start(key.second, p_action, action); } } for (const auto& key : replace) { if (key.first == object) { MxDSAction action; - action.SetAtomId(key.second.first); - action.SetObjectId(key.second.second); - action.SetUnknown24(p_action.GetUnknown24()); - action.SetNotificationObject(p_action.GetNotificationObject()); - action.SetOrigin(p_action.GetOrigin()); + MxResult result = start(key.second, p_action, action); + + if (result == SUCCESS) { + p_action.SetUnknown24(action.GetUnknown24()); + } - MxResult result = Start(&action); - p_action.SetUnknown24(action.GetUnknown24()); return result; } } + if (p_action.GetExtraLength() == 0 || !SDL_strstr(p_action.GetExtraData(), prependedMarker)) { + for (const auto& key : prepend) { + if (key.first == object) { + MxDSAction action; + MxResult result = start(key.second, p_action, action); + + if (result == SUCCESS) { + p_action.SetUnknown24(action.GetUnknown24()); + } + + return result; + } + } + } + + if (std::find(fullScreenMovie.begin(), fullScreenMovie.end(), object) != fullScreenMovie.end()) { + VideoManager()->EnableFullScreenMovie(TRUE); + } + + if (std::find(disable3d.begin(), disable3d.end(), object) != disable3d.end()) { + VideoManager()->FUN_1007c520(); + } + return std::nullopt; } @@ -102,29 +148,26 @@ std::optional SiLoader::HandleRemove(StreamObject p_object, LegoWorld* w std::optional SiLoader::HandleDelete(MxDSAction& p_action) { StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + auto deleteObject = [](const StreamObject& p_object, MxDSAction& p_in, MxDSAction& p_out) { + p_out.SetAtomId(p_object.first); + p_out.SetObjectId(p_object.second); + p_out.SetUnknown24(p_in.GetUnknown24()); + p_out.SetNotificationObject(p_in.GetNotificationObject()); + p_out.SetOrigin(p_in.GetOrigin()); + DeleteObject(p_out); + }; for (const auto& key : removeWith) { if (key.first == object) { MxDSAction action; - action.SetAtomId(key.second.first); - action.SetObjectId(key.second.second); - action.SetUnknown24(p_action.GetUnknown24()); - action.SetNotificationObject(p_action.GetNotificationObject()); - action.SetOrigin(p_action.GetOrigin()); - DeleteObject(action); + deleteObject(key.second, p_action, action); } } for (const auto& key : replace) { if (key.first == object) { MxDSAction action; - action.SetAtomId(key.second.first); - action.SetObjectId(key.second.second); - action.SetUnknown24(p_action.GetUnknown24()); - action.SetNotificationObject(p_action.GetNotificationObject()); - action.SetOrigin(p_action.GetOrigin()); - - DeleteObject(action); + deleteObject(key.second, p_action, action); p_action.SetUnknown24(action.GetUnknown24()); return TRUE; } @@ -133,6 +176,39 @@ std::optional SiLoader::HandleDelete(MxDSAction& p_action) return std::nullopt; } +MxBool SiLoader::HandleEndAction(MxEndActionNotificationParam& p_param) +{ + StreamObject object{p_param.GetAction()->GetAtomId(), p_param.GetAction()->GetObjectId()}; + auto start = [](const StreamObject& p_object, MxDSAction& p_in, MxDSAction& p_out) -> MxResult { + if (!OpenStream(p_object.first.GetInternal())) { + return FAILURE; + } + + p_out.SetAtomId(p_object.first); + p_out.SetObjectId(p_object.second); + p_out.SetUnknown24(-1); + p_out.SetNotificationObject(p_in.GetNotificationObject()); + p_out.SetOrigin(p_in.GetOrigin()); + p_out.AppendExtra(sizeof(prependedMarker), prependedMarker); + return Start(&p_out); + }; + + if (std::find(fullScreenMovie.begin(), fullScreenMovie.end(), object) != fullScreenMovie.end()) { + VideoManager()->EnableFullScreenMovie(FALSE); + } + + if (!p_param.GetSender() || !p_param.GetSender()->IsA("MxCompositePresenter")) { + for (const auto& key : prepend) { + if (key.second == object) { + MxDSAction action; + start(key.first, *p_param.GetAction(), action); + } + } + } + + return TRUE; +} + bool SiLoader::LoadFile(const char* p_file) { si::Interleaf si; @@ -150,16 +226,73 @@ bool SiLoader::LoadFile(const char* p_file) } } - if (!(controller = Streamer()->Open(p_file, MxStreamer::e_diskStream))) { - SDL_Log("Could not load SI file %s", p_file); + if (!(controller = OpenStream(p_file))) { return false; } - ParseDirectives(controller->GetAtom(), &si); + ParseExtra(controller->GetAtom(), &si); return true; } -void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom) +bool SiLoader::LoadDirective(const char* p_directive) +{ + char originAtom[256], targetAtom[256]; + uint32_t originId, targetId; + + if (SDL_sscanf( + p_directive, + "StartWith:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", + originAtom, + &originId, + targetAtom, + &targetId + ) == 4) { + startWith.emplace_back( + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}, + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId} + ); + } + else if (SDL_sscanf(p_directive, "RemoveWith:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) { + removeWith.emplace_back( + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}, + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId} + ); + } + else if (SDL_sscanf(p_directive, "Replace:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) { + replace.emplace_back( + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}, + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId} + ); + } + else if (SDL_sscanf(p_directive, "Prepend:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) { + prepend.emplace_back( + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId}, + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId} + ); + } + else if (SDL_sscanf(p_directive, "FullScreenMovie:%255[^:;]%*[:;]%u", originAtom, &originId) == 2) { + fullScreenMovie.emplace_back(StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}); + } + else if (SDL_sscanf(p_directive, "Disable3d:%255[^:;]%*[:;]%u", originAtom, &originId) == 2) { + disable3d.emplace_back(StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}); + } + + return true; +} + +MxStreamController* SiLoader::OpenStream(const char* p_file) +{ + MxStreamController* controller; + + if (!(controller = Streamer()->Open(p_file, MxStreamer::e_diskStream))) { + SDL_Log("Could not load SI file %s", p_file); + return nullptr; + } + + return controller; +} + +void SiLoader::ParseExtra(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom) { for (si::Core* child : p_core->GetChildren()) { MxAtomId replacedAtom = p_parentReplacedAtom; @@ -172,7 +305,7 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI uint32_t id; if ((directive = SDL_strstr(extra.c_str(), "StartWith:"))) { - if (SDL_sscanf(directive, "StartWith:%255[^;];%d", atom, &id) == 2) { + if (SDL_sscanf(directive, "StartWith:%255[^:;]%*[:;]%u", atom, &id) == 2) { startWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, StreamObject{p_atom, object->id_} @@ -181,7 +314,7 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI } if ((directive = SDL_strstr(extra.c_str(), "RemoveWith:"))) { - if (SDL_sscanf(directive, "RemoveWith:%255[^;];%d", atom, &id) == 2) { + if (SDL_sscanf(directive, "RemoveWith:%255[^:;]%*[:;]%u", atom, &id) == 2) { removeWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, StreamObject{p_atom, object->id_} @@ -197,7 +330,7 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI } else { if ((directive = SDL_strstr(extra.c_str(), "Replace:"))) { - if (SDL_sscanf(directive, "Replace:%255[^;];%d", atom, &id) == 2) { + if (SDL_sscanf(directive, "Replace:%255[^:;]%*[:;]%u", atom, &id) == 2) { replace.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, StreamObject{p_atom, object->id_} @@ -206,9 +339,26 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI } } } + + if ((directive = SDL_strstr(extra.c_str(), "Prepend:"))) { + if (SDL_sscanf(directive, "Prepend:%255[^:;]%*[:;]%u", atom, &id) == 2) { + prepend.emplace_back( + StreamObject{p_atom, object->id_}, + StreamObject{MxAtomId{atom, e_lowerCase2}, id} + ); + } + } + + if ((directive = SDL_strstr(extra.c_str(), "FullScreenMovie"))) { + fullScreenMovie.emplace_back(StreamObject{MxAtomId{atom, e_lowerCase2}, id}); + } + + if ((directive = SDL_strstr(extra.c_str(), "Disable3d"))) { + disable3d.emplace_back(StreamObject{MxAtomId{atom, e_lowerCase2}, id}); + } } } - ParseDirectives(p_atom, child, replacedAtom); + ParseExtra(p_atom, child, replacedAtom); } }