From 004b88e02e50cb703aa7ec8a54f5e2081161cae8 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 6 Jul 2025 07:06:14 +0200 Subject: [PATCH 1/4] Start virtual mouse at center of screen (#532) --- ISLE/isleapp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index cd0a6849..585134a0 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -92,8 +92,8 @@ MxS32 g_reqEnableRMDevice = FALSE; MxFloat g_lastJoystickMouseX = 0; MxFloat g_lastJoystickMouseY = 0; -MxFloat g_lastMouseX = 0; -MxFloat g_lastMouseY = 0; +MxFloat g_lastMouseX = 320; +MxFloat g_lastMouseY = 240; // STRING: ISLE 0x4101dc #define WINDOW_TITLE "LEGO®" From 1b8f6620904ee6a707b501ae18e636cc16b1cf6c Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 6 Jul 2025 07:12:20 +0200 Subject: [PATCH 2/4] Correctly init p_povPosition (#531) --- LEGO1/lego/legoomni/src/input/legoinputmanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp b/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp index eca8078b..b773a63e 100644 --- a/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp +++ b/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp @@ -207,6 +207,7 @@ MxResult LegoInputManager::GetJoystickState(MxU32* p_joystickX, MxU32* p_joystic // normalize values acquired from joystick axes *p_joystickX = ((xPos + 32768) * 100) / 65535; *p_joystickY = ((yPos + 32768) * 100) / 65535; + *p_povPosition = -1; return SUCCESS; } From 58338fecfc39c72426019fb1402a96e0e3f41c91 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 6 Jul 2025 07:17:06 +0200 Subject: [PATCH 3/4] Faster and cleaner mosaic transition (#530) --- .../src/common/mxtransitionmanager.cpp | 152 ++++++++++++------ miniwin/include/miniwin/ddraw.h | 2 + miniwin/src/ddraw/ddsurface.cpp | 18 +++ miniwin/src/ddraw/framebuffer.cpp | 7 +- miniwin/src/internal/framebuffer_impl.h | 1 + 5 files changed, 127 insertions(+), 53 deletions(-) diff --git a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp index 27086eb3..b66b26e4 100644 --- a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp @@ -271,8 +271,15 @@ void MxTransitionManager::DissolveTransition() // FUNCTION: LEGO1 0x1004bed0 void MxTransitionManager::MosaicTransition() { + static LPDIRECTDRAWSURFACE g_transitionSurface = nullptr; + static MxU32 g_colors[64][48]; + if (m_animationTimer == 16) { m_animationTimer = 0; + if (g_transitionSurface) { + g_transitionSurface->Release(); + g_transitionSurface = nullptr; + } EndTransition(TRUE); return; } @@ -297,6 +304,84 @@ void MxTransitionManager::MosaicTransition() for (i = 0; i < 48; i++) { m_randomShift[i] = SDL_rand(64); } + + DDSURFACEDESC srcDesc = {}; + srcDesc.dwSize = sizeof(srcDesc); + HRESULT lockRes = m_ddSurface->Lock(NULL, &srcDesc, DDLOCK_WAIT | DDLOCK_READONLY, NULL); + if (lockRes == DDERR_SURFACELOST) { + m_ddSurface->Restore(); + lockRes = m_ddSurface->Lock(NULL, &srcDesc, DDLOCK_WAIT | DDLOCK_READONLY, NULL); + } + + if (lockRes != DD_OK) { + return; + } + + MxS32 bytesPerPixel = srcDesc.ddpfPixelFormat.dwRGBBitCount / 8; + + for (MxS32 col = 0; col < 64; col++) { + for (MxS32 row = 0; row < 48; row++) { + MxS32 xBlock = (m_randomShift[row] + col) % 64; + MxS32 y = row * 10; + MxS32 x = xBlock * 10; + + MxU8* pixelPtr = (MxU8*) srcDesc.lpSurface + y * srcDesc.lPitch + x * bytesPerPixel; + + MxU32 pixel; + switch (bytesPerPixel) { + case 1: + pixel = *pixelPtr; + break; + case 2: + pixel = *(MxU16*) pixelPtr; + break; + default: + pixel = *(MxU32*) pixelPtr; + break; + } + + g_colors[col][row] = pixel; + } + } + + m_ddSurface->Unlock(srcDesc.lpSurface); + + DDSURFACEDESC mainDesc = {}; + mainDesc.dwSize = sizeof(mainDesc); + if (m_ddSurface->GetSurfaceDesc(&mainDesc) != DD_OK) { + return; + } + + DDSURFACEDESC tempDesc = {}; + tempDesc.dwSize = sizeof(tempDesc); + tempDesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; + tempDesc.dwWidth = 64; + tempDesc.dwHeight = 48; + tempDesc.ddpfPixelFormat = mainDesc.ddpfPixelFormat; + tempDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + + if (MVideoManager()->GetDirectDraw()->CreateSurface(&tempDesc, &g_transitionSurface, nullptr) != DD_OK) { + return; + } + + DWORD fillColor = 0x00000000; + switch (mainDesc.ddpfPixelFormat.dwRGBBitCount) { + case 8: + fillColor = 0x10; + break; + case 16: + fillColor = RGB555_CREATE(0x1f, 0, 0x1f); + break; + } + + DDBLTFX bltFx = {}; + bltFx.dwSize = sizeof(bltFx); + bltFx.dwFillColor = fillColor; + g_transitionSurface->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &bltFx); + + DDCOLORKEY key = {}; + key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = fillColor; + g_transitionSurface->SetColorKey(DDCKEY_SRCBLT, &key); } // Run one tick of the animation @@ -304,15 +389,18 @@ void MxTransitionManager::MosaicTransition() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT res = g_transitionSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (res == DDERR_SURFACELOST) { - m_ddSurface->Restore(); - res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + g_transitionSurface->Restore(); + res = g_transitionSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (res == DD_OK) { SubmitCopyRect(&ddsd); + // Combine xShift with this value to target the correct location in the buffer. + MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8; + for (MxS32 col = 0; col < 64; col++) { // Select 4 columns on each tick if (m_animationTimer * 4 > m_columnOrder[col]) { @@ -324,69 +412,29 @@ void MxTransitionManager::MosaicTransition() } for (MxS32 row = 0; row < 48; row++) { - // To do the mosaic effect, 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. + MxS32 x = (m_randomShift[row] + col) % 64; + MxU8* dest = (MxU8*) ddsd.lpSurface + row * ddsd.lPitch + x * bytesPerPixel; - // First, get the offset of the 10x10 block that we will sample for this row. - MxS32 xShift = 10 * ((m_randomShift[row] + col) % 64); - - // Combine xShift with this value to target the correct location in the buffer. - MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8; - - // Seek to the sample position. - MxU8* source = (MxU8*) ddsd.lpSurface + 10 * row * ddsd.lPitch + bytesPerPixel * xShift; - - // Sample byte or word depending on display mode. - MxU32 sample; + MxU32 source = g_colors[col][row]; switch (bytesPerPixel) { case 1: - sample = *source; + *dest = (MxU8) source; break; case 2: - sample = *(MxU16*) source; + *((MxU16*) dest) = (MxU16) source; break; default: - sample = *(MxU32*) source; + *((MxU32*) dest) = source; break; } - - // For each of the 10 rows in the 10x10 square: - for (MxS32 k = 10 * row; k < 10 * row + 10; k++) { - void* pos = (MxU8*) ddsd.lpSurface + k * ddsd.lPitch + bytesPerPixel * xShift; - - switch (bytesPerPixel) { - case 1: { - // Optimization: If the pixel is only one byte, we can use memset - memset(pos, sample, 10); - break; - } - case 2: { - MxU16* p = (MxU16*) pos; - for (MxS32 tt = 0; tt < 10; tt++) { - p[tt] = (MxU16) sample; - } - break; - } - default: { - MxU32* p = (MxU32*) pos; - for (MxS32 tt = 0; tt < 10; tt++) { - p[tt] = (MxU32) sample; - } - break; - } - } - } } } SetupCopyRect(&ddsd); - m_ddSurface->Unlock(ddsd.lpSurface); + g_transitionSurface->Unlock(ddsd.lpSurface); - if (VideoManager()->GetVideoParam().Flags().GetFlipSurfaces()) { - LPDIRECTDRAWSURFACE surf = VideoManager()->GetDisplaySurface()->GetDirectDrawSurface1(); - surf->BltFast(0, 0, m_ddSurface, &g_fullScreenRect, DDBLTFAST_WAIT); - } + RECT srcRect = {0, 0, 64, 48}; + m_ddSurface->Blt(&g_fullScreenRect, g_transitionSurface, &srcRect, DDBLT_WAIT | DDBLT_KEYSRC, NULL); m_animationTimer++; } diff --git a/miniwin/include/miniwin/ddraw.h b/miniwin/include/miniwin/ddraw.h index 2a8932a2..0250c099 100644 --- a/miniwin/include/miniwin/ddraw.h +++ b/miniwin/include/miniwin/ddraw.h @@ -162,9 +162,11 @@ ENABLE_BITMASK_OPERATORS(DDBltFastFlags) #define DDLOCK_SURFACEMEMORYPTR DDLockFlags::SURFACEMEMORYPTR #define DDLOCK_WAIT DDLockFlags::WAIT #define DDLOCK_WRITEONLY DDLockFlags::WRITEONLY +#define DDLOCK_READONLY DDLockFlags::READONLY enum class DDLockFlags : uint32_t { SURFACEMEMORYPTR = 0, WAIT = 1 << 0, + READONLY = 1 << 4, WRITEONLY = 1 << 5, }; ENABLE_BITMASK_OPERATORS(DDLockFlags) diff --git a/miniwin/src/ddraw/ddsurface.cpp b/miniwin/src/ddraw/ddsurface.cpp index de250dea..056912fd 100644 --- a/miniwin/src/ddraw/ddsurface.cpp +++ b/miniwin/src/ddraw/ddsurface.cpp @@ -52,6 +52,24 @@ HRESULT DirectDrawSurfaceImpl::Blt( LPDDBLTFX lpDDBltFx ) { + if ((dwFlags & DDBLT_COLORFILL) == DDBLT_COLORFILL) { + Uint8 a = (lpDDBltFx->dwFillColor >> 24) & 0xFF; + Uint8 r = (lpDDBltFx->dwFillColor >> 16) & 0xFF; + Uint8 g = (lpDDBltFx->dwFillColor >> 8) & 0xFF; + Uint8 b = lpDDBltFx->dwFillColor & 0xFF; + + const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_surface->format); + Uint32 color = SDL_MapRGBA(details, nullptr, r, g, b, a); + if (lpDestRect) { + SDL_Rect dstRect = ConvertRect(lpDestRect); + SDL_FillSurfaceRect(m_surface, &dstRect, color); + } + else { + SDL_FillSurfaceRect(m_surface, nullptr, color); + } + return DD_OK; + } + auto other = static_cast(lpDDSrcSurface); SDL_Rect srcRect = lpSrcRect ? ConvertRect(lpSrcRect) : SDL_Rect{0, 0, other->m_surface->w, other->m_surface->h}; diff --git a/miniwin/src/ddraw/framebuffer.cpp b/miniwin/src/ddraw/framebuffer.cpp index ea66e96b..13caa434 100644 --- a/miniwin/src/ddraw/framebuffer.cpp +++ b/miniwin/src/ddraw/framebuffer.cpp @@ -171,6 +171,9 @@ HRESULT FrameBufferImpl::Lock(LPRECT lpDestRect, DDSURFACEDESC* lpDDSurfaceDesc, if (!DDRenderer) { return DDERR_GENERIC; } + + m_readOnlyLock = (dwFlags & DDLOCK_READONLY) == DDLOCK_READONLY; + if ((dwFlags & DDLOCK_WRITEONLY) == DDLOCK_WRITEONLY) { const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_transferBuffer->m_surface->format); SDL_Palette* palette = m_palette ? static_cast(m_palette)->m_palette : nullptr; @@ -223,7 +226,9 @@ HRESULT FrameBufferImpl::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) HRESULT FrameBufferImpl::Unlock(LPVOID lpSurfaceData) { m_transferBuffer->Unlock(lpSurfaceData); - BltFast(0, 0, m_transferBuffer, nullptr, DDBLTFAST_WAIT); + if (!m_readOnlyLock) { + BltFast(0, 0, m_transferBuffer, nullptr, DDBLTFAST_WAIT); + } return DD_OK; } diff --git a/miniwin/src/internal/framebuffer_impl.h b/miniwin/src/internal/framebuffer_impl.h index 5e40f7e8..47805e69 100644 --- a/miniwin/src/internal/framebuffer_impl.h +++ b/miniwin/src/internal/framebuffer_impl.h @@ -39,6 +39,7 @@ struct FrameBufferImpl : public IDirectDrawSurface3 { private: uint32_t m_virtualWidth; uint32_t m_virtualHeight; + bool m_readOnlyLock; DirectDrawSurfaceImpl* m_transferBuffer; IDirectDrawPalette* m_palette = nullptr; }; From fd0b6bcacaedb556c129feb21491ce5b0c005a67 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 6 Jul 2025 07:45:27 +0200 Subject: [PATCH 4/4] Some quick dpad mouse control (#533) --- ISLE/isleapp.cpp | 105 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 12 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 585134a0..6d1c0a99 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -95,6 +95,11 @@ MxFloat g_lastJoystickMouseY = 0; MxFloat g_lastMouseX = 320; MxFloat g_lastMouseY = 240; +bool g_dpadUp = false; +bool g_dpadDown = false; +bool g_dpadLeft = false; +bool g_dpadRight = false; + // STRING: ISLE 0x4101dc #define WINDOW_TITLE "LEGO®" @@ -466,17 +471,73 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) break; } case SDL_EVENT_GAMEPAD_BUTTON_DOWN: { - { - if (event->gbutton.button == SDL_GAMEPAD_BUTTON_SOUTH) { - if (InputManager()) { - InputManager()->QueueEvent(c_notificationKeyPress, SDLK_SPACE, 0, 0, SDLK_SPACE); - } + switch (event->gbutton.button) { + case SDL_GAMEPAD_BUTTON_DPAD_UP: + g_dpadUp = true; + break; + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + g_dpadDown = true; + break; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + g_dpadLeft = true; + break; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + g_dpadRight = true; + break; + case SDL_GAMEPAD_BUTTON_EAST: + g_mousedown = TRUE; + if (InputManager()) { + InputManager()->QueueEvent( + c_notificationButtonDown, + LegoEventNotificationParam::c_lButtonState, + g_lastMouseX, + g_lastMouseY, + 0 + ); } - if (event->gbutton.button == SDL_GAMEPAD_BUTTON_START) { - if (InputManager()) { - InputManager()->QueueEvent(c_notificationKeyPress, SDLK_ESCAPE, 0, 0, SDLK_ESCAPE); - } + break; + + case SDL_GAMEPAD_BUTTON_SOUTH: + if (InputManager()) { + InputManager()->QueueEvent(c_notificationKeyPress, SDLK_SPACE, 0, 0, SDLK_SPACE); } + break; + + case SDL_GAMEPAD_BUTTON_START: + if (InputManager()) { + InputManager()->QueueEvent(c_notificationKeyPress, SDLK_ESCAPE, 0, 0, SDLK_ESCAPE); + } + break; + } + break; + } + + case SDL_EVENT_GAMEPAD_BUTTON_UP: { + switch (event->gbutton.button) { + case SDL_GAMEPAD_BUTTON_DPAD_UP: + g_dpadUp = false; + break; + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + g_dpadDown = false; + break; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + g_dpadLeft = false; + break; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + g_dpadRight = false; + break; + case SDL_GAMEPAD_BUTTON_EAST: + g_mousedown = FALSE; + if (InputManager()) { + InputManager()->QueueEvent( + c_notificationButtonUp, + LegoEventNotificationParam::c_lButtonState, + g_lastMouseX, + g_lastMouseY, + 0 + ); + } + break; } break; } @@ -1278,11 +1339,31 @@ IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice() void IsleApp::MoveVirtualMouseViaJoystick() { - if (g_lastJoystickMouseX != 0 || g_lastJoystickMouseY != 0) { + float dpadX = 0.0f; + float dpadY = 0.0f; + + if (g_dpadLeft) { + dpadX -= m_cursorSensitivity; + } + if (g_dpadRight) { + dpadX += m_cursorSensitivity; + } + if (g_dpadUp) { + dpadY -= m_cursorSensitivity; + } + if (g_dpadDown) { + dpadY += m_cursorSensitivity; + } + + // Use joystick axis if non-zero, else fall back to dpad + float moveX = (g_lastJoystickMouseX != 0) ? g_lastJoystickMouseX : dpadX; + float moveY = (g_lastJoystickMouseY != 0) ? g_lastJoystickMouseY : dpadY; + + if (moveX != 0 || moveY != 0) { g_mousemoved = TRUE; - g_lastMouseX = SDL_clamp(g_lastMouseX + g_lastJoystickMouseX, 0, 640); - g_lastMouseY = SDL_clamp(g_lastMouseY + g_lastJoystickMouseY, 0, 480); + g_lastMouseX = SDL_clamp(g_lastMouseX + moveX, 0, 640); + g_lastMouseY = SDL_clamp(g_lastMouseY + moveY, 0, 480); if (InputManager()) { InputManager()->QueueEvent(