diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a91eb19..8a65ae81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ jobs: - { name: 'msys2 mingw32', os: 'windows-latest', dx5: false, config: false, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw32', msys-env: 'mingw-w64-i686', shell: 'msys2 {0}' } - { name: 'msys2 mingw64', os: 'windows-latest', dx5: false, config: true, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' } - { name: 'macOS', os: 'macos-latest', dx5: false, config: true, build-type: 'Debug', brew: true, werror: true, clang-tidy: false } + - { name: 'Emscripten', os: 'ubuntu-latest', dx5: false, config: false, build-type: 'Debug', emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' } steps: - name: Setup vcvars if: ${{ !!matrix.msvc }} @@ -74,6 +75,10 @@ jobs: brew install cmake ninja llvm qt6 echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV + - name: Setup Emscripten + uses: mymindstorm/setup-emsdk@master + if: ${{ matrix.emsdk }} + - name: Setup ninja if: ${{ matrix.msvc }} uses: ashutoshvarma/setup-ninja@master @@ -82,7 +87,7 @@ jobs: - name: Configure (CMake) run: | - cmake -S . -B build -GNinja \ + ${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \ -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DISLE_USE_DX5=${{ !!matrix.dx5 }} \ -DISLE_BUILD_CONFIG=${{ matrix.config }} \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ac7a25f..55007410 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,12 @@ cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR) project(isle LANGUAGES CXX C VERSION 0.1) +if (EMSCRIPTEN) + add_compile_options(-pthread) + add_link_options(-sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1) + set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE) +endif() + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") include(CheckCXXSourceCompiles) @@ -34,6 +40,8 @@ option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON) option(ENABLE_CLANG_TIDY "Enable clang-tidy") option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll") +set(ISLE_EMSCRIPTEN_HOST "" CACHE STRING "Host URL for Emscripten streaming (e.g., https://test.com)") +cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN" OFF) message(STATUS "Isle app: ${ISLE_BUILD_APP}") message(STATUS "Config app: ${ISLE_BUILD_CONFIG}") @@ -59,9 +67,11 @@ if (DOWNLOAD_DEPENDENCIES) GIT_TAG "main" EXCLUDE_FROM_ALL ) - set(BUILD_DOCS off) - set(BUILD_SHARED_LIBS off) - FetchContent_MakeAvailable(iniparser) + block() + set(BUILD_DOCS off) + set(BUILD_SHARED_LIBS off) + FetchContent_MakeAvailable(iniparser) + endblock() else() # find_package looks for already-installed system packages. # Configure with `-DCMAKE_PREFIX_PATH="/path/to/package1;/path/to/package2"` @@ -129,7 +139,7 @@ target_link_directories(DirectX5::DirectX5 INTERFACE "${CMAKE_SOURCE_DIR}/3rdpar add_library(Vec::Vec INTERFACE IMPORTED) target_include_directories(Vec::Vec INTERFACE "${CMAKE_SOURCE_DIR}/3rdparty/vec") -add_library(lego1 SHARED +add_library(lego1 LEGO1/main.cpp ) target_precompile_headers(lego1 PRIVATE "LEGO1/lego1_pch.h") @@ -497,6 +507,15 @@ if (ISLE_BUILD_APP) target_include_directories(isle PRIVATE ${valgrind_INCLUDE_PATH}) endif() endif() + if(EMSCRIPTEN) + target_sources(isle PRIVATE + ISLE/emscripten/events.cpp + ISLE/emscripten/filesystem.cpp + ISLE/emscripten/messagebox.cpp + ) + target_compile_definitions(isle PRIVATE "ISLE_EMSCRIPTEN_HOST=\"${ISLE_EMSCRIPTEN_HOST}\"") + set_property(TARGET isle PROPERTY SUFFIX ".html") + endif() endif() if (ISLE_BUILD_CONFIG) @@ -602,10 +621,18 @@ else() endif() set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "Platform name of the package") -install(TARGETS isle lego1 ${install_extra_targets} +if(BUILD_SHARED_LIBS) + list(APPEND install_extra_targets lego1) +endif() +install(TARGETS isle ${install_extra_targets} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) +if(EMSCRIPTEN) + install(FILES "$/isle.js" "$/isle.wasm" + DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) +endif() set(CPACK_PACKAGE_DIRECTORY "dist") set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}") diff --git a/ISLE/emscripten/events.cpp b/ISLE/emscripten/events.cpp new file mode 100644 index 00000000..67139dc6 --- /dev/null +++ b/ISLE/emscripten/events.cpp @@ -0,0 +1,38 @@ +#include "events.h" + +#include "mxdsaction.h" + +#include + +// clang-format off +void Emscripten_SendEvent(const char* p_event, const char* p_json) +{ + MAIN_THREAD_EM_ASM({ + const eventName = UTF8ToString($0); + let eventDetail = {}; + + if ($1 && UTF8ToString($1).length > 0) { + eventDetail = JSON.parse(UTF8ToString($1)); + } + + const targetElement = Module.canvas || window; + const event = new CustomEvent(eventName, {detail : eventDetail}); + targetElement.dispatchEvent(event); + }, p_event, p_json); +} +// clang-format on + +void Emscripten_SendPresenterProgress(MxPresenter* p_presenter, MxPresenter::TickleState p_tickleState) +{ + char buf[128]; + SDL_snprintf( + buf, + sizeof(buf), + "{\"objectId\": %d, \"objectName\": \"%s\", \"tickleState\": %d}", + p_presenter->GetAction() ? p_presenter->GetAction()->GetObjectId() : 0, + p_presenter->GetAction() ? p_presenter->GetAction()->GetObjectName() : "", + p_tickleState + ); + + Emscripten_SendEvent("presenterProgress", buf); +} diff --git a/ISLE/emscripten/events.h b/ISLE/emscripten/events.h new file mode 100644 index 00000000..cf0b2ed6 --- /dev/null +++ b/ISLE/emscripten/events.h @@ -0,0 +1,8 @@ +#ifndef EMSCRIPTEN_EVENTS_H +#define EMSCRIPTEN_EVENTS_H + +#include "mxpresenter.h" + +void Emscripten_SendPresenterProgress(MxPresenter* p_presenter, MxPresenter::TickleState p_tickleState); + +#endif // EMSCRIPTEN_EVENTS_H diff --git a/ISLE/emscripten/filesystem.cpp b/ISLE/emscripten/filesystem.cpp new file mode 100644 index 00000000..03b5b582 --- /dev/null +++ b/ISLE/emscripten/filesystem.cpp @@ -0,0 +1,127 @@ +#include "filesystem.h" + +#include "legogamestate.h" +#include "misc.h" +#include "mxomni.h" + +#include +#include +#include + +static backend_t opfs = nullptr; +static backend_t fetchfs = nullptr; + +void Emscripten_SetupConfig(const char* p_iniConfig) +{ + if (!p_iniConfig || !*p_iniConfig) { + return; + } + + opfs = wasmfs_create_opfs_backend(); + MxString iniConfig = p_iniConfig; + + char* parse = iniConfig.GetData(); + while ((parse = SDL_strchr(++parse, '/'))) { + *parse = '\0'; + wasmfs_create_directory(iniConfig.GetData(), 0644, opfs); + *parse = '/'; + } +} + +void Emscripten_SetupFilesystem() +{ + fetchfs = wasmfs_create_fetch_backend((MxString(Emscripten_streamHost) + MxString("/LEGO")).GetData(), 512 * 1024); + + wasmfs_create_directory("/LEGO", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Act2", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Act3", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Build", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Garage", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Hospital", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Infocntr", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Isle", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Police", 0644, fetchfs); + wasmfs_create_directory("/LEGO/Scripts/Race", 0644, fetchfs); + wasmfs_create_directory("/LEGO/data", 0644, fetchfs); + + const auto registerFile = [](const char* p_path) { + MxString path = MxString(Emscripten_bundledPath) + MxString(p_path); + path.MapPathToFilesystem(); + + if (SDL_GetPathInfo(path.GetData(), NULL)) { + SDL_Log("File %s is bundled and won't be streamed", p_path); + } + else { + wasmfs_create_file(p_path, 0644, fetchfs); + MxOmni::GetCDFiles().emplace_back(p_path); + + SDL_Log("File %s set up for streaming", p_path); + } + }; + + registerFile("/LEGO/Scripts/CREDITS.SI"); + registerFile("/LEGO/Scripts/INTRO.SI"); + registerFile("/LEGO/Scripts/NOCD.SI"); + registerFile("/LEGO/Scripts/SNDANIM.SI"); + registerFile("/LEGO/Scripts/Act2/ACT2MAIN.SI"); + registerFile("/LEGO/Scripts/Act3/ACT3.SI"); + registerFile("/LEGO/Scripts/Build/COPTER.SI"); + registerFile("/LEGO/Scripts/Build/DUNECAR.SI"); + registerFile("/LEGO/Scripts/Build/JETSKI.SI"); + registerFile("/LEGO/Scripts/Build/RACECAR.SI"); + registerFile("/LEGO/Scripts/Garage/GARAGE.SI"); + registerFile("/LEGO/Scripts/Hospital/HOSPITAL.SI"); + registerFile("/LEGO/Scripts/Infocntr/ELEVBOTT.SI"); + registerFile("/LEGO/Scripts/Infocntr/HISTBOOK.SI"); + registerFile("/LEGO/Scripts/Infocntr/INFODOOR.SI"); + registerFile("/LEGO/Scripts/Infocntr/INFOMAIN.SI"); + registerFile("/LEGO/Scripts/Infocntr/INFOSCOR.SI"); + registerFile("/LEGO/Scripts/Infocntr/REGBOOK.SI"); + registerFile("/LEGO/Scripts/Isle/ISLE.SI"); + registerFile("/LEGO/Scripts/Isle/JUKEBOX.SI"); + registerFile("/LEGO/Scripts/Isle/JUKEBOXW.SI"); + registerFile("/LEGO/Scripts/Police/POLICE.SI"); + registerFile("/LEGO/Scripts/Race/CARRACE.SI"); + registerFile("/LEGO/Scripts/Race/CARRACER.SI"); + registerFile("/LEGO/Scripts/Race/JETRACE.SI"); + registerFile("/LEGO/Scripts/Race/JETRACER.SI"); + registerFile("/LEGO/data/ACT1INF.DTA"); + registerFile("/LEGO/data/ACT2INF.DTA"); + registerFile("/LEGO/data/ACT3INF.DTA"); + registerFile("/LEGO/data/BLDDINF.DTA"); + registerFile("/LEGO/data/BLDHINF.DTA"); + registerFile("/LEGO/data/BLDJINF.DTA"); + registerFile("/LEGO/data/BLDRINF.DTA"); + registerFile("/LEGO/data/GMAININF.DTA"); + registerFile("/LEGO/data/HOSPINF.DTA"); + registerFile("/LEGO/data/ICUBEINF.DTA"); + registerFile("/LEGO/data/IELEVINF.DTA"); + registerFile("/LEGO/data/IISLEINF.DTA"); + registerFile("/LEGO/data/IMAININF.DTA"); + registerFile("/LEGO/data/IREGINF.DTA"); + registerFile("/LEGO/data/OBSTINF.DTA"); + registerFile("/LEGO/data/PMAININF.DTA"); + registerFile("/LEGO/data/RACCINF.DTA"); + registerFile("/LEGO/data/RACJINF.DTA"); + registerFile("/LEGO/data/WORLD.WDB"); + registerFile("/LEGO/data/testinf.dta"); + + if (GameState()->GetSavePath() && *GameState()->GetSavePath()) { + if (!opfs) { + opfs = wasmfs_create_opfs_backend(); + } + + MxString savePath = GameState()->GetSavePath(); + if (savePath.GetData()[savePath.GetLength() - 1] != '/') { + savePath += "/"; + } + + char* parse = savePath.GetData(); + while ((parse = SDL_strchr(++parse, '/'))) { + *parse = '\0'; + wasmfs_create_directory(savePath.GetData(), 0644, opfs); + *parse = '/'; + } + } +} diff --git a/ISLE/emscripten/filesystem.h b/ISLE/emscripten/filesystem.h new file mode 100644 index 00000000..131df1c8 --- /dev/null +++ b/ISLE/emscripten/filesystem.h @@ -0,0 +1,16 @@ +#ifndef EMSCRIPTEN_FILESYSTEM_H +#define EMSCRIPTEN_FILESYSTEM_H + +#ifndef ISLE_EMSCRIPTEN_HOST +#define ISLE_EMSCRIPTEN_HOST "" +#endif + +inline static const char* Emscripten_bundledPath = "/bundled"; +inline static const char* Emscripten_savePath = "/save"; +inline static const char* Emscripten_streamPath = "/"; +inline static const char* Emscripten_streamHost = ISLE_EMSCRIPTEN_HOST; + +void Emscripten_SetupConfig(const char* p_iniConfig); +void Emscripten_SetupFilesystem(); + +#endif // EMSCRIPTEN_FILESYSTEM_H diff --git a/ISLE/emscripten/messagebox.cpp b/ISLE/emscripten/messagebox.cpp new file mode 100644 index 00000000..4977554c --- /dev/null +++ b/ISLE/emscripten/messagebox.cpp @@ -0,0 +1,14 @@ +#include "messagebox.h" + +#include + +bool Emscripten_ShowSimpleMessageBox( + SDL_MessageBoxFlags flags, + const char* title, + const char* message, + SDL_Window* window +) +{ + MAIN_THREAD_EM_ASM({alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1))}, title, message); + return true; +} diff --git a/ISLE/emscripten/messagebox.h b/ISLE/emscripten/messagebox.h new file mode 100644 index 00000000..a944c64f --- /dev/null +++ b/ISLE/emscripten/messagebox.h @@ -0,0 +1,13 @@ +#ifndef EMSCRIPTEN_MESSAGE_BOX_H +#define EMSCRIPTEN_MESSAGE_BOX_H + +#include + +bool Emscripten_ShowSimpleMessageBox( + SDL_MessageBoxFlags flags, + const char* title, + const char* message, + SDL_Window* window +); + +#endif // EMSCRIPTEN_MESSAGE_BOX_H diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 7377e94e..be4842e2 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -44,6 +44,12 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include "emscripten/events.h" +#include "emscripten/filesystem.h" +#include "emscripten/messagebox.h" +#endif + DECOMP_SIZE_ASSERT(IsleApp, 0x8c) // GLOBAL: ISLE 0x410030 @@ -88,7 +94,11 @@ IsleApp::IsleApp() m_cdPath = NULL; m_deviceId = NULL; m_savePath = NULL; +#ifdef __EMSCRIPTEN__ m_fullScreen = FALSE; +#else + m_fullScreen = TRUE; +#endif m_flipSurfaces = FALSE; m_backBuffersInVram = TRUE; m_using8bit = FALSE; @@ -100,7 +110,7 @@ IsleApp::IsleApp() m_useJoystick = FALSE; m_joystickIndex = 0; m_wideViewAngle = TRUE; - m_islandQuality = 1; + m_islandQuality = 2; m_islandTexture = 1; m_gameStarted = FALSE; m_frameDelta = 10; @@ -246,6 +256,9 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { *appstate = NULL; + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)) { char buffer[256]; SDL_snprintf( @@ -254,7 +267,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) "\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.\nSDL error: %s", SDL_GetError() ); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", buffer, NULL); + Any_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", buffer, NULL); return SDL_APP_FAILURE; } @@ -266,7 +279,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) g_isle = new IsleApp(); if (g_isle->ParseArguments(argc, argv) != SUCCESS) { - SDL_ShowSimpleMessageBox( + Any_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", "\"LEGO® Island\" failed to start. Invalid CLI arguments.", @@ -277,7 +290,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) // Create window if (g_isle->SetupWindow() != SUCCESS) { - SDL_ShowSimpleMessageBox( + Any_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", "\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.", @@ -288,6 +301,20 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) // Get reference to window *appstate = g_isle->GetWindowHandle(); + +#ifdef __EMSCRIPTEN__ + SDL_AddEventWatch( + [](void* userdata, SDL_Event* event) -> bool { + if (event->type == SDL_EVENT_TERMINATING && g_isle && g_isle->GetGameStarted()) { + GameState()->Save(0); + return false; + } + + return true; + }, + NULL + ); +#endif return SDL_APP_CONTINUE; } @@ -298,7 +325,7 @@ SDL_AppResult SDL_AppIterate(void* appstate) } if (!g_isle->Tick()) { - SDL_ShowSimpleMessageBox( + Any_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", "\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again." @@ -324,7 +351,7 @@ SDL_AppResult SDL_AppIterate(void* appstate) if (g_mousedown && g_mousemoved && g_isle) { if (!g_isle->Tick()) { - SDL_ShowSimpleMessageBox( + Any_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "LEGO® Island Error", "\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again." @@ -355,9 +382,26 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) // [library:window] // Remaining functionality to be implemented: - // Full screen - crashes when minimizing/maximizing, but this will probably be fixed once DirectDraw is replaced // WM_TIMER - use SDL_Timer functionality instead +#ifdef __EMSCRIPTEN__ + // Workaround for the fact we are getting both mouse & touch events on mobile devices running Emscripten. + // On desktops, we are only getting mouse events, but a touch device (pen_input) may also be present... + // See: https://github.com/libsdl-org/SDL/issues/13161 + static bool detectedTouchEvents = false; +#endif + + switch (event->type) { + case SDL_EVENT_MOUSE_MOTION: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + IDirect3DRMMiniwinDevice* device = GetD3DRMMiniwinDevice(); + if (device && !device->ConvertEventToRenderCoordinates(event)) { + SDL_Log("Failed to convert event coordinates: %s", SDL_GetError()); + } + break; + } + switch (event->type) { case SDL_EVENT_WINDOW_FOCUS_GAINED: if (!IsleDebug_Enabled()) { @@ -366,9 +410,12 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) } break; case SDL_EVENT_WINDOW_FOCUS_LOST: - if (!IsleDebug_Enabled()) { + if (!IsleDebug_Enabled() && g_isle->GetGameStarted()) { g_isle->SetWindowActive(FALSE); Lego()->Pause(); +#ifdef __EMSCRIPTEN__ + GameState()->Save(0); +#endif } break; case SDL_EVENT_WINDOW_CLOSE_REQUESTED: @@ -390,6 +437,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) break; } case SDL_EVENT_MOUSE_MOTION: +#ifdef __EMSCRIPTEN__ + if (detectedTouchEvents) { + break; + } +#endif g_mousemoved = TRUE; if (InputManager()) { @@ -406,7 +458,30 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) VideoManager()->MoveCursor(Min((MxS32) event->motion.x, 639), Min((MxS32) event->motion.y, 479)); } break; + case SDL_EVENT_FINGER_MOTION: { +#ifdef __EMSCRIPTEN__ + detectedTouchEvents = true; +#endif + g_mousemoved = TRUE; + + float x = SDL_clamp(event->tfinger.x, 0, 1) * 640; + float y = SDL_clamp(event->tfinger.y, 0, 1) * 480; + + if (InputManager()) { + InputManager()->QueueEvent(c_notificationMouseMove, LegoEventNotificationParam::c_lButtonState, x, y, 0); + } + + if (g_isle->GetDrawCursor()) { + VideoManager()->MoveCursor(Min((MxS32) x, 639), Min((MxS32) y, 479)); + } + break; + } case SDL_EVENT_MOUSE_BUTTON_DOWN: +#ifdef __EMSCRIPTEN__ + if (detectedTouchEvents) { + break; + } +#endif g_mousedown = TRUE; if (InputManager()) { @@ -419,7 +494,32 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) ); } break; + case SDL_EVENT_FINGER_DOWN: { +#ifdef __EMSCRIPTEN__ + detectedTouchEvents = true; +#endif + g_mousedown = TRUE; + + float x = SDL_clamp(event->tfinger.x, 0, 1) * 640; + float y = SDL_clamp(event->tfinger.y, 0, 1) * 480; + + if (InputManager()) { + InputManager()->QueueEvent(c_notificationButtonDown, LegoEventNotificationParam::c_lButtonState, x, y, 0); + } + break; + } case SDL_EVENT_MOUSE_BUTTON_UP: +#ifdef __EMSCRIPTEN__ + if (detectedTouchEvents) { + // Abusing the fact (bug?) that we are always getting mouse events on Emscripten. + // This functionality should be enabled in a more general way with touch events, + // but SDL touch event's don't have a "double tap" indicator right now. + if (event->button.clicks == 2) { + InputManager()->QueueEvent(c_notificationKeyPress, SDLK_SPACE, 0, 0, SDLK_SPACE); + } + break; + } +#endif g_mousedown = FALSE; if (InputManager()) { @@ -432,6 +532,20 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) ); } break; + case SDL_EVENT_FINGER_UP: { +#ifdef __EMSCRIPTEN__ + detectedTouchEvents = true; +#endif + g_mousedown = FALSE; + + float x = SDL_clamp(event->tfinger.x, 0, 1) * 640; + float y = SDL_clamp(event->tfinger.y, 0, 1) * 480; + + if (InputManager()) { + InputManager()->QueueEvent(c_notificationButtonUp, 0, x, y, 0); + } + break; + } case SDL_EVENT_QUIT: return SDL_APP_SUCCESS; break; @@ -452,6 +566,23 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) break; } } + else if (event->user.type == g_legoSdlEvents.m_presenterProgress) { + MxPresenter* presenter = static_cast(event->user.data1); + MxDSAction* action = presenter->GetAction(); + MxPresenter::TickleState state = static_cast(event->user.code); + +#ifdef __EMSCRIPTEN__ + if (!g_isle->GetGameStarted()) { + Emscripten_SendPresenterProgress(presenter, state); + } +#endif + + if (!g_isle->GetGameStarted() && action && state == MxPresenter::e_ready && + !SDL_strncmp(action->GetObjectName(), "Lego_Smk", 8)) { + g_isle->SetGameStarted(TRUE); + SDL_Log("Game started"); + } + } return SDL_APP_CONTINUE; } @@ -555,6 +686,11 @@ MxResult IsleApp::SetupWindow() } GameState()->SetSavePath(m_savePath); + +#ifdef __EMSCRIPTEN__ + Emscripten_SetupFilesystem(); +#endif + GameState()->SerializePlayersInfo(LegoStorage::c_read); GameState()->SerializeScoreHistory(LegoStorage::c_read); @@ -621,6 +757,10 @@ bool IsleApp::LoadConfig() } SDL_Log("Reading configuration from \"%s\"", iniConfig); +#ifdef __EMSCRIPTEN__ + Emscripten_SetupConfig(iniConfig); +#endif + dictionary* dict = iniparser_load(iniConfig); // [library:config] @@ -677,12 +817,20 @@ bool IsleApp::LoadConfig() fclose(iniFP); } +#ifdef __EMSCRIPTEN__ + const char* hdPath = Emscripten_bundledPath; +#else const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath()); +#endif m_hdPath = new char[strlen(hdPath) + 1]; strcpy(m_hdPath, hdPath); MxOmni::SetHD(m_hdPath); +#ifdef __EMSCRIPTEN__ + const char* cdPath = Emscripten_streamPath; +#else const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD()); +#endif m_cdPath = new char[strlen(cdPath) + 1]; strcpy(m_cdPath, cdPath); MxOmni::SetCD(m_cdPath); @@ -729,7 +877,11 @@ bool IsleApp::LoadConfig() // [library:config] // The original game does not save any data if no savepath is given. // Instead, we use SDLs prefPath as a default fallback and always save data. +#ifdef __EMSCRIPTEN__ + const char* savePath = Emscripten_savePath; +#else const char* savePath = iniparser_getstring(dict, "isle:savepath", prefPath); +#endif m_savePath = new char[strlen(savePath) + 1]; strcpy(m_savePath, savePath); @@ -828,7 +980,6 @@ inline bool IsleApp::Tick() SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open ISLE.si: Failed to start initial action"); return false; } - m_gameStarted = TRUE; } return true; @@ -871,7 +1022,6 @@ void IsleApp::SetupCursor(Cursor p_cursor) MxResult IsleApp::ParseArguments(int argc, char** argv) { - for (int i = 1, consumed; i < argc; i += consumed) { consumed = -1; @@ -892,6 +1042,7 @@ MxResult IsleApp::ParseArguments(int argc, char** argv) return FAILURE; } } + return SUCCESS; } diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index e35d56a6..b0b7805e 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -49,8 +49,10 @@ class IsleApp { SDL_Cursor* GetCursorBusy() { return m_cursorBusy; } SDL_Cursor* GetCursorNo() { return m_cursorNo; } MxS32 GetDrawCursor() { return m_drawCursor; } + MxS32 GetGameStarted() { return m_gameStarted; } void SetWindowActive(MxS32 p_windowActive) { m_windowActive = p_windowActive; } + void SetGameStarted(MxS32 p_gameStarted) { m_gameStarted = p_gameStarted; } MxResult ParseArguments(int argc, char** argv); diff --git a/LEGO1/lego/legoomni/include/legocarbuild.h b/LEGO1/lego/legoomni/include/legocarbuild.h index 1baef66d..916a5937 100644 --- a/LEGO1/lego/legoomni/include/legocarbuild.h +++ b/LEGO1/lego/legoomni/include/legocarbuild.h @@ -163,7 +163,7 @@ class LegoCarBuild : public LegoWorld { MxS32 FUN_10025ee0(undefined4 p_param1); // FUNCTION: BETA10 0x100735b0 - void SetUnknown0x258(LegoCarBuildAnimPresenter* p_unk0x258) { m_unk0x258 = p_unk0x258; } + void SetCarBuildAnimPresenter(LegoCarBuildAnimPresenter* p_animPresenter) { m_animPresenter = p_animPresenter; } // SYNTHETIC: LEGO1 0x10022a60 // LegoCarBuild::`scalar deleting destructor' @@ -195,8 +195,8 @@ class LegoCarBuild : public LegoWorld { // This is likely a location in pixel space MxS32 m_unk0x250[2]; // 0x250 - LegoCarBuildAnimPresenter* m_unk0x258; // 0x258 - MxQuaternionTransformer m_unk0x25c; // 0x25c + LegoCarBuildAnimPresenter* m_animPresenter; // 0x258 + MxQuaternionTransformer m_unk0x25c; // 0x25c // These two are likely locations in pixel space MxS32 m_unk0x290[2]; // 0x290 diff --git a/LEGO1/lego/legoomni/include/legocarbuildpresenter.h b/LEGO1/lego/legoomni/include/legocarbuildpresenter.h index f09515bf..a13c1adf 100644 --- a/LEGO1/lego/legoomni/include/legocarbuildpresenter.h +++ b/LEGO1/lego/legoomni/include/legocarbuildpresenter.h @@ -13,6 +13,13 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { c_bit1 = 0x01 }; + enum ShelfState { + e_undefined = -1, + e_selected = 0, + e_stopped = 1, + e_moving = 2 + }; + // SIZE 0x0c struct UnknownListEntry { // FUNCTION: LEGO1 0x100795c0 @@ -69,7 +76,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { void FUN_10079050(MxS16 p_index); void SwapNodesByName(LegoChar* p_param1, LegoChar* p_param2); - void FUN_10079160(); + void InitBuildPlatform(); void FUN_100795d0(LegoChar* p_param); void FUN_10079680(LegoChar* p_param); LegoAnimNodeData* FindNodeDataByName(LegoTreeNode* p_treeNode, const LegoChar* p_name); @@ -78,7 +85,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { void RotateAroundYAxis(MxFloat p_angle); MxBool FUN_10079c30(const LegoChar* p_name); MxBool PartIsPlaced(const LegoChar* p_name); - void FUN_10079a90(); + void MoveShelfForward(); MxBool StringEqualsPlatform(const LegoChar* p_string); MxBool StringEqualsShelf(const LegoChar* p_string); MxBool StringEndsOnY(const LegoChar* p_string); @@ -87,10 +94,10 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { void SetPartObjectIdByName(const LegoChar* p_name, MxS16 p_objectId); // FUNCTION: BETA10 0x10070180 - void SetUnknown0xbc(undefined2 p_unk0xbc) { m_unk0xbc = p_unk0xbc; } + void SetShelfState(MxU16 p_shelfState) { m_shelfState = p_shelfState; } // FUNCTION: BETA10 0x100703b0 - Matrix4& GetUnknown0xe0() { return m_unk0xe0; } + Matrix4& GetBuildViewMatrix() { return m_buildViewMatrix; } MxBool StringEndsOnW(LegoChar* p_param); MxBool StringEndsOnYOrN(const LegoChar* p_string); @@ -116,7 +123,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { private: void Beta10Inline0x100733d0(); - MxU16 m_unk0xbc; // 0xbc + MxU16 m_shelfState; // 0xbc // variable name verified by BETA10 0x1007184f MxS16 m_numberOfParts; // 0xbe @@ -124,21 +131,21 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { // name derived from LegoVehicleBuildState, field 0x4f MxS16 m_placedPartCount; // 0xc0 - LegoAnimNodeData* m_unk0xc4; // 0xc4 - LegoAnim m_unk0xc8; // 0xc8 - MxMatrix m_unk0xe0; // 0xe0 + LegoAnimNodeData* m_platformAnimNodeData; // 0xc4 + LegoAnim m_platformAnim; // 0xc8 + MxMatrix m_buildViewMatrix; // 0xe0 // variable name verified by BETA10 0x100719f0 UnknownListEntry* m_parts; // 0x128 - MxFloat m_unk0x12c; // 0x12c - MxFloat m_unk0x130; // 0x130 - MxFloat m_unk0x134; // 0x134 - MxFloat m_unk0x138; // 0x138 - MxULong m_unk0x13c; // 0x13c - LegoEntity* m_unk0x140; // 0x140 - MxS32 m_unk0x144; // 0x144 - MxS32 m_unk0x148; // 0x148 + MxFloat m_shelfFrameBuffer; // 0x12c + MxFloat m_shelfFrame; // 0x130 + MxFloat m_shelfFrameMax; // 0x134 + MxFloat m_shelfFrameInterval; // 0x138 + MxULong m_unk0x13c; // 0x13c + LegoEntity* m_carBuildEntity; // 0x140 + MxS32 m_unk0x144; // 0x144 + MxS32 m_unk0x148; // 0x148 // name verified by BETA10 0x10070d63 LegoChar* m_mainSourceId; // 0x14c diff --git a/LEGO1/lego/legoomni/include/legogamestate.h b/LEGO1/lego/legoomni/include/legogamestate.h index 1191a867..850a8055 100644 --- a/LEGO1/lego/legoomni/include/legogamestate.h +++ b/LEGO1/lego/legoomni/include/legogamestate.h @@ -224,6 +224,8 @@ class LegoGameState { void FindLoadedAct(); void RegisterState(LegoState* p_state); + const char* GetSavePath() { return m_savePath; } + private: MxResult WriteVariable(LegoStorage* p_storage, MxVariableTable* p_from, const char* p_variableName); MxResult WriteEndOfVariables(LegoStorage* p_storage); diff --git a/LEGO1/lego/legoomni/include/legoinputmanager.h b/LEGO1/lego/legoomni/include/legoinputmanager.h index 2c847cab..7ccad404 100644 --- a/LEGO1/lego/legoomni/include/legoinputmanager.h +++ b/LEGO1/lego/legoomni/include/legoinputmanager.h @@ -143,6 +143,7 @@ class LegoInputManager : public MxPresenter { MxBool FUN_1005cdf0(LegoEventNotificationParam& p_param); void GetKeyboardState(); MxResult GetNavigationKeyStates(MxU32& p_keyFlags); + MxResult GetNavigationTouchStates(MxU32& p_keyFlags); // SYNTHETIC: LEGO1 0x1005b8d0 // LegoInputManager::`scalar deleting destructor' diff --git a/LEGO1/lego/legoomni/include/legophoneme.h b/LEGO1/lego/legoomni/include/legophoneme.h index f88595d9..88f85e01 100644 --- a/LEGO1/lego/legoomni/include/legophoneme.h +++ b/LEGO1/lego/legoomni/include/legophoneme.h @@ -10,32 +10,32 @@ class LegoTextureInfo; // SIZE 0x20 class LegoPhoneme { public: - LegoPhoneme(const char* p_name, undefined4 p_unk0x14) + LegoPhoneme(const char* p_name, MxU32 p_count) { m_name = p_name; m_name.ToUpperCase(); Init(); - m_unk0x14 = p_unk0x14; + m_count = p_count; } ~LegoPhoneme(); - virtual undefined4 VTable0x00(); // vtable+0x00 - virtual void VTable0x04(undefined4 p_unk0x14); // vtable+0x04 - virtual LegoTextureInfo* VTable0x08(); // vtable+0x08 - virtual void VTable0x0c(LegoTextureInfo* p_unk0x18); // vtable+0x0c - virtual LegoTextureInfo* VTable0x10(); // vtable+0x10 - virtual void VTable0x14(LegoTextureInfo* p_unk0x1c); // vtable+0x14 - virtual void VTable0x18(); // vtable+0x18 - virtual void Init(); // vtable+0x1c - virtual void VTable0x20(undefined4); // vtable+0x20 + virtual MxU32 GetCount(); // vtable+0x00 + virtual void SetCount(MxU32 p_count); // vtable+0x04 + virtual LegoTextureInfo* GetTextureInfo(); // vtable+0x08 + virtual void SetTextureInfo(LegoTextureInfo* p_textureInfo); // vtable+0x0c + virtual LegoTextureInfo* GetCachedTextureInfo(); // vtable+0x10 + virtual void SetCachedTextureInfo(LegoTextureInfo* p_cachedTextureInfo); // vtable+0x14 + virtual void VTable0x18(); // vtable+0x18 + virtual void Init(); // vtable+0x1c + virtual void VTable0x20(undefined4); // vtable+0x20 MxString& GetName() { return m_name; } private: - MxString m_name; // 0x04 - undefined4 m_unk0x14; // 0x14 - LegoTextureInfo* m_unk0x18; // 0x18 - LegoTextureInfo* m_unk0x1c; // 0x1c + MxString m_name; // 0x04 + MxU32 m_count; // 0x14 + LegoTextureInfo* m_textureInfo; // 0x18 + LegoTextureInfo* m_cachedTextureInfo; // 0x1c }; #endif // LEGOPHONEME_H diff --git a/LEGO1/lego/legoomni/include/legoplantmanager.h b/LEGO1/lego/legoomni/include/legoplantmanager.h index d5007770..1e17d6f4 100644 --- a/LEGO1/lego/legoomni/include/legoplantmanager.h +++ b/LEGO1/lego/legoomni/include/legoplantmanager.h @@ -52,11 +52,11 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore { MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state); LegoPlantInfo* GetInfoArray(MxS32& p_length); LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId); - MxBool FUN_10026c50(LegoEntity* p_entity); + MxBool DecrementCounter(LegoEntity* p_entity); void ScheduleAnimation(LegoEntity* p_entity, MxLong p_length); MxResult FUN_10026410(); - void FUN_10027120(); - void FUN_10027200(); + void ClearCounters(); + void SetInitialCounters(); static void SetCustomizeAnimFile(const char* p_value); @@ -68,10 +68,10 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore { private: void RemovePlant(MxS32 p_index, LegoOmni::World p_worldId); - void FUN_10026860(MxS32 p_index); + void AdjustHeight(MxS32 p_index); LegoPlantInfo* GetInfo(LegoEntity* p_entity); - MxBool FUN_10026c80(MxS32 p_index); - void FUN_100271b0(LegoEntity* p_entity, MxS32 p_adjust); + MxBool DecrementCounter(MxS32 p_index); + void AdjustCounter(LegoEntity* p_entity, MxS32 p_adjust); static char* g_customizeAnimFile; static MxS32 g_maxMove[4]; diff --git a/LEGO1/lego/legoomni/include/legoplants.h b/LEGO1/lego/legoomni/include/legoplants.h index 1b26b0e8..9d20d1a9 100644 --- a/LEGO1/lego/legoomni/include/legoplants.h +++ b/LEGO1/lego/legoomni/include/legoplants.h @@ -41,8 +41,8 @@ struct LegoPlantInfo { MxU32 m_move; // 0x10 MxU8 m_mood; // 0x14 MxU8 m_color; // 0x15 - see enum for possible values - MxS8 m_unk0x16; // 0x16 - MxS8 m_initialUnk0x16; // 0x17 - initial value loaded to m_unk0x16 + MxS8 m_counter; // 0x16 + MxS8 m_initialCounter; // 0x17 - initial value loaded to m_counter const char* m_name; // 0x18 undefined4 m_unk0x1c; // 0x1c float m_x; // 0x20 diff --git a/LEGO1/lego/legoomni/include/legoutils.h b/LEGO1/lego/legoomni/include/legoutils.h index 0bc229cd..3862465f 100644 --- a/LEGO1/lego/legoomni/include/legoutils.h +++ b/LEGO1/lego/legoomni/include/legoutils.h @@ -4,10 +4,8 @@ #include "actionsfwd.h" #include "decomp.h" #include "extra.h" -#include "lego1_export.h" #include "mxtypes.h" -#include #ifdef MINIWIN #include "miniwin/windows.h" #else @@ -19,12 +17,6 @@ // name verified by BETA10 0x100d4054 #define DS_NOT_A_STREAM -1 -struct LegoSdlEvents { - Uint32 m_windowsMessage; -}; - -LEGO1_EXPORT extern LegoSdlEvents g_legoSdlEvents; - enum Cursor { e_cursorArrow = 0, e_cursorBusy, @@ -70,7 +62,6 @@ void PlayCamAnim(LegoPathActor* p_actor, MxBool p_unused, MxU32 p_location, MxBo void FUN_1003eda0(); MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id); void EnableAnimations(MxBool p_enable); -void InitSdlEvents(); void SetAppCursor(Cursor p_cursor); MxBool FUN_1003ef60(); MxBool RemoveFromWorld(MxAtomId& p_entityAtom, MxS32 p_entityId, MxAtomId& p_worldAtom, MxS32 p_worldEntityId); diff --git a/LEGO1/lego/legoomni/src/actors/act2actor.cpp b/LEGO1/lego/legoomni/src/actors/act2actor.cpp index 463302a0..e8f1d851 100644 --- a/LEGO1/lego/legoomni/src/actors/act2actor.cpp +++ b/LEGO1/lego/legoomni/src/actors/act2actor.cpp @@ -728,7 +728,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f0f30[i] != -1; i++) { - if (plantInfo[g_unk0x100f0f30[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f0f30[i]].m_counter) { result = plantInfo[g_unk0x100f0f30[i]].m_entity; break; } @@ -742,7 +742,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f0f50[i] != -1; i++) { - if (plantInfo[g_unk0x100f0f50[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f0f50[i]].m_counter) { result = plantInfo[g_unk0x100f0f50[i]].m_entity; break; } @@ -760,7 +760,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f0f90[i] != -1; i++) { - if (plantInfo[g_unk0x100f0f90[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f0f90[i]].m_counter) { result = plantInfo[g_unk0x100f0f90[i]].m_entity; break; } @@ -782,7 +782,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f0fa8[i] != -1; i++) { - if (plantInfo[g_unk0x100f0fa8[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f0fa8[i]].m_counter) { result = plantInfo[g_unk0x100f0fa8[i]].m_entity; break; } @@ -800,7 +800,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f0fb8[i] != -1; i++) { - if (plantInfo[g_unk0x100f0fb8[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f0fb8[i]].m_counter) { result = plantInfo[g_unk0x100f0fb8[i]].m_entity; break; } @@ -814,7 +814,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f0fe8[i] != -1; i++) { - if (plantInfo[g_unk0x100f0fe8[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f0fe8[i]].m_counter) { result = plantInfo[g_unk0x100f0fe8[i]].m_entity; break; } @@ -828,7 +828,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f1000[i] != -1; i++) { - if (plantInfo[g_unk0x100f1000[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f1000[i]].m_counter) { result = plantInfo[g_unk0x100f1000[i]].m_entity; break; } @@ -842,7 +842,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) } else { for (i = 0; g_unk0x100f1018[i] != -1; i++) { - if (plantInfo[g_unk0x100f1018[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f1018[i]].m_counter) { result = plantInfo[g_unk0x100f1018[i]].m_entity; break; } @@ -851,7 +851,7 @@ LegoEntity* Act2Actor::FUN_10019b90(MxBool* p_param) break; case 8: for (i = 0; g_unk0x100f1030[i] != -1; i++) { - if (plantInfo[g_unk0x100f1030[i]].m_unk0x16) { + if (plantInfo[g_unk0x100f1030[i]].m_counter) { result = plantInfo[g_unk0x100f1030[i]].m_entity; break; } diff --git a/LEGO1/lego/legoomni/src/actors/act3actors.cpp b/LEGO1/lego/legoomni/src/actors/act3actors.cpp index 42451d80..19d9aeb0 100644 --- a/LEGO1/lego/legoomni/src/actors/act3actors.cpp +++ b/LEGO1/lego/legoomni/src/actors/act3actors.cpp @@ -608,8 +608,8 @@ void Act3Brickster::Animate(float p_time) assert(m_shootAnim && m_pInfo); if (m_unk0x50 < p_time) { - while (m_pInfo->m_unk0x16) { - PlantManager()->FUN_10026c50(m_pInfo->m_entity); + while (m_pInfo->m_counter) { + PlantManager()->DecrementCounter(m_pInfo->m_entity); } assert(SoundManager()->GetCacheSoundManager()); diff --git a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp index bc731fee..73678370 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp @@ -99,7 +99,7 @@ LegoCarBuild::LegoCarBuild() m_unk0x110 = 0; m_unk0xf8 = c_unknownminusone; m_unk0x2d4 = FALSE; - m_unk0x258 = 0; + m_animPresenter = NULL; m_ColorBook_Bitmap = NULL; m_Yellow_Ctl = NULL; m_Red_Ctl = NULL; @@ -140,10 +140,10 @@ LegoCarBuild::~LegoCarBuild() m_unk0x100 = 0; m_unk0x110 = NULL; - if (m_unk0x258) { - m_unk0x258->SetUnknown0xbc(0); - m_unk0x258->SetTickleState(MxPresenter::e_idle); - m_unk0x258 = NULL; + if (m_animPresenter) { + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); + m_animPresenter->SetTickleState(MxPresenter::e_idle); + m_animPresenter = NULL; } ControlManager()->Unregister(this); @@ -293,7 +293,7 @@ void LegoCarBuild::FUN_10022f00() { if (m_unk0x110) { VTable0x6c(); - m_unk0x258->SetUnknown0xbc(0); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); m_unk0x100 = 5; } } @@ -306,13 +306,13 @@ void LegoCarBuild::FUN_10022f30() FUN_10024f70(FALSE); FUN_100250e0(FALSE); - if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) { + if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) { m_PlaceBrick_Sound->Enable(FALSE); m_PlaceBrick_Sound->Enable(TRUE); } - m_unk0x258->SetUnknown0xbc(1); - m_unk0x258->PutFrame(); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_stopped); + m_animPresenter->PutFrame(); m_unk0x110 = NULL; m_unk0x100 = 0; } @@ -483,12 +483,12 @@ void LegoCarBuild::FUN_100236d0() FUN_10024f70(FALSE); FUN_100250e0(FALSE); - m_unk0x258->FUN_10079790(m_unk0x110->GetName()); - m_unk0x258->SetUnknown0xbc(1); + m_animPresenter->FUN_10079790(m_unk0x110->GetName()); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_stopped); m_unk0x110 = NULL; m_unk0x100 = 0; - if (m_unk0x258->AllPartsPlaced()) { + if (m_animPresenter->AllPartsPlaced()) { // Note the code duplication with LEGO1 0x10025ee0 switch (m_carId) { case 1: @@ -545,7 +545,7 @@ MxResult LegoCarBuild::Tickle() } if (m_unk0x110) { - if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) { + if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) { FUN_10022f30(); } } @@ -827,7 +827,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y) LegoROI* roi = PickROI(p_x, p_y); - if (!roi || !m_unk0x258->StringEndsOnYOrN(roi->GetName())) { + if (!roi || !m_animPresenter->StringEndsOnYOrN(roi->GetName())) { return 0; } @@ -838,7 +838,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y) FUN_100250e0(TRUE); } - if (m_unk0x100 == 5 && m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) { + if (m_unk0x100 == 5 && m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) { m_unk0x2d4 = TRUE; } else { @@ -847,7 +847,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y) FUN_10025450(); VTable0x70(); - if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) { + if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) { if (m_unk0x100 != 5) { m_unk0x250[0] += m_unk0x290[0] - m_unk0x298[0]; m_unk0x250[1] += m_unk0x290[1] - m_unk0x298[1]; @@ -858,8 +858,8 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y) } } else { - if (m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) { - m_unk0x114 = m_unk0x258->FUN_10079e20(); + if (m_animPresenter->FUN_10079c30(m_unk0x110->GetName())) { + m_unk0x114 = m_animPresenter->FUN_10079e20(); } } @@ -875,7 +875,7 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y) m_GetBrick_Sound->Enable(FALSE); m_GetBrick_Sound->Enable(TRUE); - m_unk0x258->SetUnknown0xbc(0); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); return 1; } @@ -895,7 +895,7 @@ undefined4 LegoCarBuild::FUN_100246e0(MxLong p_x, MxLong p_y) result = 1; break; case 6: - if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName()) && + if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName()) && SpheresIntersect(m_unk0x114, m_unk0x110->GetWorldBoundingSphere())) { FUN_10024f70(FALSE); FUN_100250e0(FALSE); @@ -903,9 +903,9 @@ undefined4 LegoCarBuild::FUN_100246e0(MxLong p_x, MxLong p_y) m_unk0x110 = NULL; m_PlaceBrick_Sound->Enable(FALSE); m_PlaceBrick_Sound->Enable(TRUE); - m_unk0x258->SetUnknown0xbc(1); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_stopped); } - else if (m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) { + else if (m_animPresenter->FUN_10079c30(m_unk0x110->GetName())) { if (SpheresIntersect(m_unk0x114, m_unk0x110->GetWorldBoundingSphere())) { m_PlaceBrick_Sound->Enable(FALSE); m_PlaceBrick_Sound->Enable(TRUE); @@ -968,7 +968,7 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param) DeleteObjects(&m_atomId, 500, 510); } - m_unk0x258->SetUnknown0xbc(0); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); m_destLocation = LegoGameState::e_infomain; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); result = 1; @@ -982,12 +982,12 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param) DeleteObjects(&m_atomId, 500, 510); } - m_unk0x258->SetUnknown0xbc(0); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); if (GameState()->GetCurrentAct() == LegoGameState::e_act2) { FUN_100243a0(); } - else if (m_unk0x258->AllPartsPlaced() || m_buildState->m_unk0x4d) { + else if (m_animPresenter->AllPartsPlaced() || m_buildState->m_unk0x4d) { m_buildState->m_unk0x4d = TRUE; InvokeAction(Extra::e_start, m_atomId, m_carId, NULL); @@ -1024,7 +1024,7 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param) (m_Decals_Ctl5 && m_Decals_Ctl5->GetAction()->GetObjectId() == param->m_clickedObjectId) || (m_Decals_Ctl6 && m_Decals_Ctl6->GetAction()->GetObjectId() == param->m_clickedObjectId) || (m_Decals_Ctl7 && m_Decals_Ctl7->GetAction()->GetObjectId() == param->m_clickedObjectId)) { - m_unk0x258->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId); + m_animPresenter->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId); m_Decal_Sound->Enable(FALSE); m_Decal_Sound->Enable(TRUE); } @@ -1057,16 +1057,16 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param) if (param->m_unk0x28) { switch (param->m_clickedObjectId) { case CopterScript::c_Info_Ctl: - m_unk0x258->SetUnknown0xbc(0); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); m_destLocation = LegoGameState::e_infomain; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); result = 1; break; case CopterScript::c_Exit_Ctl: if (m_buildState->m_animationState != LegoVehicleBuildState::e_exiting) { - m_unk0x258->SetUnknown0xbc(0); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_selected); - if (m_unk0x258->AllPartsPlaced() || m_buildState->m_unk0x4d) { + if (m_animPresenter->AllPartsPlaced() || m_buildState->m_unk0x4d) { m_buildState->m_unk0x4d = TRUE; // GameState()->GetCurrentAct() returns an MxS16 in BETA10 @@ -1128,7 +1128,7 @@ undefined4 LegoCarBuild::FUN_10024890(MxParam* p_param) (m_Decals_Ctl5 && m_Decals_Ctl5->GetAction()->GetObjectId() == param->m_clickedObjectId) || (m_Decals_Ctl6 && m_Decals_Ctl6->GetAction()->GetObjectId() == param->m_clickedObjectId) || (m_Decals_Ctl7 && m_Decals_Ctl7->GetAction()->GetObjectId() == param->m_clickedObjectId)) { - m_unk0x258->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId); + m_animPresenter->SetPartObjectIdByName(m_unk0x110->GetName(), param->m_clickedObjectId); m_Decal_Sound->Enable(FALSE); m_Decal_Sound->Enable(TRUE); } @@ -1205,7 +1205,7 @@ undefined4 LegoCarBuild::FUN_10024c20(MxNotificationParam* p_param) assert(destWorld); m_buildState->m_animationState = LegoVehicleBuildState::e_exiting; - if (!m_unk0x258->AllPartsPlaced()) { + if (!m_animPresenter->AllPartsPlaced()) { FUN_100243a0(); } else { @@ -1264,7 +1264,7 @@ void LegoCarBuild::FUN_10024ef0() void LegoCarBuild::FUN_10024f30() { FUN_10022f30(); - m_unk0x258->SetUnknown0xbc(2); + m_animPresenter->SetShelfState(LegoCarBuildAnimPresenter::e_moving); } // FUNCTION: LEGO1 0x10024f50 @@ -1272,14 +1272,14 @@ void LegoCarBuild::FUN_10024f30() void LegoCarBuild::FUN_10024f50() { m_unk0x2d4 = FALSE; - m_unk0x258->RotateAroundYAxis(g_rotationAngleStepYAxis); + m_animPresenter->RotateAroundYAxis(g_rotationAngleStepYAxis); } // FUNCTION: LEGO1 0x10024f70 // FUNCTION: BETA10 0x1006e002 void LegoCarBuild::FUN_10024f70(MxBool p_enabled) { - if (m_unk0x258->StringEndsOnY(m_unk0x110->GetName())) { + if (m_animPresenter->StringEndsOnY(m_unk0x110->GetName())) { SetPresentersEnabled(p_enabled); } } @@ -1314,7 +1314,7 @@ void LegoCarBuild::TogglePresentersEnabled() // FUNCTION: BETA10 0x1006e124 void LegoCarBuild::FUN_100250e0(MxBool p_enabled) { - if (m_unk0x258->StringDoesNotEndOnZero(m_unk0x110->GetName()) && m_Decals_Ctl) { + if (m_animPresenter->StringDoesNotEndOnZero(m_unk0x110->GetName()) && m_Decals_Ctl) { if (SDL_strncasecmp(m_unk0x110->GetName(), "JSFRNT", strlen("JSFRNT")) == 0) { m_Decal_Bitmap->Enable(p_enabled); m_Decals_Ctl->Enable(p_enabled); @@ -1395,7 +1395,7 @@ void LegoCarBuild::FUN_10025450() m_unk0x1c0 = m_unk0x12c; Vector3 lastColumnOfUnk0x1c0(m_unk0x1c0[3]); - lastColumnOfUnk0x1c0 = Vector3(m_unk0x258->GetUnknown0xe0()[3]); + lastColumnOfUnk0x1c0 = Vector3(m_animPresenter->GetBuildViewMatrix()[3]); // This looks odd, but it improves the LEGO1 match while breaking the BETA10 match. // I don't know whether this is due to compiler entropy. @@ -1404,7 +1404,7 @@ void LegoCarBuild::FUN_10025450() MxMatrix* unk0x178 = &m_unk0x178; *unk0x178 = m_unk0x12c; - if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) { + if (m_animPresenter->PartIsPlaced(m_unk0x110->GetName())) { m_unk0x2a4 = Vector4(m_unk0x110->GetWorldPosition()); if (!m_unk0x2d4) { @@ -1421,11 +1421,11 @@ void LegoCarBuild::FUN_10025450() else { const LegoChar* wiredName; - if (!m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) { - wiredName = m_unk0x258->GetWiredNameByPartName(m_unk0x110->GetName()); + if (!m_animPresenter->FUN_10079c30(m_unk0x110->GetName())) { + wiredName = m_animPresenter->GetWiredNameByPartName(m_unk0x110->GetName()); } else { - wiredName = m_unk0x258->GetWiredNameOfLastPlacedPart(); + wiredName = m_animPresenter->GetWiredNameOfLastPlacedPart(); } LegoROI* parentROI = (LegoROI*) m_unk0x110->GetParentROI(); diff --git a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp index cf9f647e..c1d42a90 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp @@ -25,17 +25,17 @@ DECOMP_SIZE_ASSERT(LegoCarBuildAnimPresenter, 0x150) // FUNCTION: BETA10 0x100707c0 LegoCarBuildAnimPresenter::LegoCarBuildAnimPresenter() { - m_unk0xbc = 0; + m_shelfState = e_selected; m_numberOfParts = 0; m_placedPartCount = 0; m_parts = NULL; - m_unk0xc4 = NULL; - m_unk0x130 = 0; - m_unk0x12c = 0; - m_unk0x134 = 0; - m_unk0x138 = 0; + m_platformAnimNodeData = NULL; + m_shelfFrame = 0; + m_shelfFrameBuffer = 0; + m_shelfFrameMax = 0; + m_shelfFrameInterval = 0; m_unk0x13c = 0; - m_unk0x140 = NULL; + m_carBuildEntity = NULL; m_unk0x144 = -1; m_unk0x148 = -1; m_mainSourceId = NULL; @@ -53,8 +53,8 @@ LegoCarBuildAnimPresenter::~LegoCarBuildAnimPresenter() delete[] m_parts; } - m_unk0xc8.GetRoot()->SetNumChildren(0); - *m_unk0xc8.GetRoot()->GetChildren() = NULL; + m_platformAnim.GetRoot()->SetNumChildren(0); + *m_platformAnim.GetRoot()->GetChildren() = NULL; if (m_mainSourceId) { delete[] m_mainSourceId; @@ -116,14 +116,14 @@ inline void LegoCarBuildAnimPresenter::Beta10Inline0x100733d0() // FUNCTION: BETA10 0x10070ab1 void LegoCarBuildAnimPresenter::PutFrame() { - switch (m_unk0xbc) { - case 0: + switch (m_shelfState) { + case e_selected: break; - case 2: - FUN_10079a90(); - case 1: - if (m_unk0x140->GetROI()) { - FUN_1006b9a0(m_anim, m_unk0x12c, NULL); + case e_moving: + MoveShelfForward(); + case e_stopped: + if (m_carBuildEntity->GetROI()) { + FUN_1006b9a0(m_anim, m_shelfFrameBuffer, NULL); } default: break; @@ -152,24 +152,24 @@ void LegoCarBuildAnimPresenter::ReadyTickle() #endif } - m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "Dunebld"); + m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "Dunebld"); - if (!m_unk0x140) { - m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "Chptrbld"); + if (!m_carBuildEntity) { + m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "Chptrbld"); } - if (!m_unk0x140) { - m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "Jetbld"); + if (!m_carBuildEntity) { + m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "Jetbld"); } - if (!m_unk0x140) { - m_unk0x140 = (LegoEntity*) m_currentWorld->Find("MxEntity", "bldrace"); + if (!m_carBuildEntity) { + m_carBuildEntity = (LegoEntity*) m_currentWorld->Find("MxEntity", "bldrace"); } - if (m_unk0x140) { - ((LegoCarBuild*) m_currentWorld)->SetUnknown0x258(this); + if (m_carBuildEntity) { + ((LegoCarBuild*) m_currentWorld)->SetCarBuildAnimPresenter(this); m_placedPartCount = ((LegoCarBuild*) m_currentWorld)->GetPlacedPartCount(); - SetUnknown0xbc(1); + SetShelfState(e_stopped); m_previousTickleStates |= 1 << m_currentTickleState; m_currentTickleState = e_starting; m_compositePresenter->SendToCompositePresenter(Lego()); @@ -184,7 +184,7 @@ void LegoCarBuildAnimPresenter::ReadyTickle() // FUNCTION: BETA10 0x10070cdd void LegoCarBuildAnimPresenter::StreamingTickle() { - if (!m_unk0x140->GetROI()) { + if (!m_carBuildEntity->GetROI()) { return; } @@ -194,7 +194,7 @@ void LegoCarBuildAnimPresenter::StreamingTickle() strcpy(m_mainSourceId, m_action->GetAtomId().GetInternal()); m_mainSourceId[strlen(m_mainSourceId) - 1] = 'M'; - FUN_10079160(); + InitBuildPlatform(); if (GameState()->GetCurrentAct() == LegoGameState::e_act2) { m_placedPartCount = 10; @@ -234,7 +234,7 @@ void LegoCarBuildAnimPresenter::StreamingTickle() Lego3DView* lego3dview = videoManager->Get3DManager()->GetLego3DView(); LegoROI* videoManagerROI = videoManager->GetViewROI(); - LegoROI* local60 = m_unk0x140->GetROI(); + LegoROI* local60 = m_carBuildEntity->GetROI(); LegoROI* camera = NULL; MxFloat fov; @@ -273,7 +273,7 @@ void LegoCarBuildAnimPresenter::StreamingTickle() lego3dview->Moved(*videoManagerROI); videoManager->Get3DManager()->SetFrustrum(fov, 0.1, 250.0); - m_unk0xe0 = local60->FindChildROI("VIEW", local60)->GetLocal2World(); + m_buildViewMatrix = local60->FindChildROI("VIEW", local60)->GetLocal2World(); m_previousTickleStates |= 1 << m_currentTickleState; m_currentTickleState = e_repeating; @@ -286,7 +286,7 @@ void LegoCarBuildAnimPresenter::EndAction() if (m_action) { AUTOLOCK(m_criticalSection); MxVideoPresenter::EndAction(); - m_unk0xbc = 0; + m_shelfState = e_selected; } } @@ -296,7 +296,7 @@ MxResult LegoCarBuildAnimPresenter::Serialize(LegoStorage* p_storage) { if (p_storage->IsReadMode()) { p_storage->ReadS16(m_placedPartCount); - p_storage->ReadFloat(m_unk0x130); + p_storage->ReadFloat(m_shelfFrame); for (MxS16 i = 0; i < m_numberOfParts; i++) { p_storage->ReadString(m_parts[i].m_name); p_storage->ReadString(m_parts[i].m_wiredName); @@ -305,7 +305,7 @@ MxResult LegoCarBuildAnimPresenter::Serialize(LegoStorage* p_storage) } else if (p_storage->IsWriteMode()) { p_storage->WriteS16(m_placedPartCount); - p_storage->WriteFloat(m_unk0x130); + p_storage->WriteFloat(m_shelfFrame); for (MxS16 i = 0; i < m_numberOfParts; i++) { p_storage->WriteString(m_parts[i].m_name); p_storage->WriteString(m_parts[i].m_wiredName); @@ -346,7 +346,7 @@ void LegoCarBuildAnimPresenter::SwapNodesByName(LegoChar* p_name1, LegoChar* p_n // FUNCTION: LEGO1 0x10079160 // FUNCTION: BETA10 0x1007165d -void LegoCarBuildAnimPresenter::FUN_10079160() +void LegoCarBuildAnimPresenter::InitBuildPlatform() { LegoTreeNode* root; LegoAnimNodeData* data2; @@ -357,16 +357,17 @@ void LegoCarBuildAnimPresenter::FUN_10079160() LegoAnimNodeData* destData; LegoTreeNode** children; + // Get Platform data, Shelf Frame data, and number of build parts for (i = 0; i < totalNodes; i++) { LegoAnimNodeData* data = (LegoAnimNodeData*) GetTreeNode(m_anim->GetRoot(), i)->GetData(); name = data->GetName(); if (StringEqualsPlatform(name)) { - m_unk0xc4 = data; - if (m_unk0xc4->GetNumRotationKeys() == 0) { + m_platformAnimNodeData = data; + if (m_platformAnimNodeData->GetNumRotationKeys() == 0) { LegoRotationKey* key = new LegoRotationKey[1]; - m_unk0xc4->SetNumRotationKeys(1); - m_unk0xc4->SetRotationKeys(key); + m_platformAnimNodeData->SetNumRotationKeys(1); + m_platformAnimNodeData->SetRotationKeys(key); } } else { @@ -374,9 +375,9 @@ void LegoCarBuildAnimPresenter::FUN_10079160() m_numberOfParts++; } else { - if (m_unk0x134 == 0.0f && StringEqualsShelf(name)) { - m_unk0x134 = m_anim->GetDuration(); - m_unk0x138 = m_unk0x134 / (data->GetNumTranslationKeys() - 1); + if (m_shelfFrameMax == 0.0f && StringEqualsShelf(name)) { + m_shelfFrameMax = m_anim->GetDuration(); + m_shelfFrameInterval = m_shelfFrameMax / (data->GetNumTranslationKeys() - 1); } } } @@ -386,6 +387,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160() m_parts = new UnknownListEntry[m_numberOfParts]; assert(m_parts); + // Go through and add the wired name of each part for (i = 0; i < totalNodes; i++) { name = ((LegoAnimNodeData*) GetTreeNode(m_anim->GetRoot(), i)->GetData())->GetName(); @@ -404,6 +406,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160() MxS16 counter = 0; + // Go through and add the normal name of each part for (i = 0; i < totalNodes; i++) { name = ((LegoAnimNodeData*) GetTreeNode(m_anim->GetRoot(), i)->GetData())->GetName(); if (StringEndsOnYOrN(name)) { @@ -422,6 +425,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160() } } + // Set Platform root node destNode = new LegoTreeNode(); assert(destNode); destData = new LegoAnimNodeData(); @@ -438,7 +442,7 @@ void LegoCarBuildAnimPresenter::FUN_10079160() *children = FindNodeByName(m_anim->GetRoot(), "PLATFORM"); destNode->SetChildren(children); - m_unk0xc8.SetRoot(destNode); + m_platformAnim.SetRoot(destNode); } // FUNCTION: LEGO1 0x100795d0 @@ -563,8 +567,8 @@ void LegoCarBuildAnimPresenter::FUN_10079790(const LegoChar* p_name) // FUNCTION: BETA10 0x1007225d void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle) { - if (m_unk0xc4) { - LegoRotationKey* rotationKey = m_unk0xc4->GetRotationKey(0); + if (m_platformAnimNodeData) { + LegoRotationKey* rotationKey = m_platformAnimNodeData->GetRotationKey(0); Mx4DPointFloat currentRotation(rotationKey->GetX(), rotationKey->GetY(), rotationKey->GetZ(), rotationKey->GetAngle()); @@ -581,33 +585,33 @@ void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle) rotationKey->FUN_100739a0(FALSE); } - m_unk0xc4->GetRotationKey(0)->SetX(newRotation[0]); - m_unk0xc4->GetRotationKey(0)->SetY(newRotation[1]); - m_unk0xc4->GetRotationKey(0)->SetZ(newRotation[2]); - m_unk0xc4->GetRotationKey(0)->SetAngle(newRotation[3]); + m_platformAnimNodeData->GetRotationKey(0)->SetX(newRotation[0]); + m_platformAnimNodeData->GetRotationKey(0)->SetY(newRotation[1]); + m_platformAnimNodeData->GetRotationKey(0)->SetZ(newRotation[2]); + m_platformAnimNodeData->GetRotationKey(0)->SetAngle(newRotation[3]); - if (m_unk0x140->GetROI()) { - FUN_1006b9a0(&m_unk0xc8, m_unk0x12c, NULL); + if (m_carBuildEntity->GetROI()) { + FUN_1006b9a0(&m_platformAnim, m_shelfFrameBuffer, NULL); } } } // FUNCTION: LEGO1 0x10079a90 // FUNCTION: BETA10 0x10072412 -void LegoCarBuildAnimPresenter::FUN_10079a90() +void LegoCarBuildAnimPresenter::MoveShelfForward() { - if (m_unk0x12c >= m_unk0x134) { - m_unk0x130 = 0.0; - m_unk0x12c = m_unk0x130; - m_unk0xbc = 1; + if (m_shelfFrameBuffer >= m_shelfFrameMax) { + m_shelfFrame = 0.0; + m_shelfFrameBuffer = m_shelfFrame; + m_shelfState = e_stopped; } - else if (m_unk0x12c >= m_unk0x138 + m_unk0x130) { - m_unk0x130 = m_unk0x138 + m_unk0x130; - m_unk0x12c = m_unk0x130; - m_unk0xbc = 1; + else if (m_shelfFrameBuffer >= m_shelfFrameInterval + m_shelfFrame) { + m_shelfFrame = m_shelfFrameInterval + m_shelfFrame; + m_shelfFrameBuffer = m_shelfFrame; + m_shelfState = e_stopped; } else { - m_unk0x12c = m_unk0x138 / 10.0f + m_unk0x12c; + m_shelfFrameBuffer = m_shelfFrameInterval / 10.0f + m_shelfFrameBuffer; } } @@ -708,6 +712,6 @@ void LegoCarBuildAnimPresenter::SetPartObjectIdByName(const LegoChar* p_name, Mx // FUNCTION: BETA10 0x10072959 const BoundingSphere& LegoCarBuildAnimPresenter::FUN_10079e20() { - LegoROI* roi = m_unk0x140->GetROI(); + LegoROI* roi = m_carBuildEntity->GetROI(); return roi->FindChildROI(m_parts[m_placedPartCount].m_wiredName, roi)->GetWorldBoundingSphere(); } diff --git a/LEGO1/lego/legoomni/src/common/legophoneme.cpp b/LEGO1/lego/legoomni/src/common/legophoneme.cpp index 55da20c9..9f64e043 100644 --- a/LEGO1/lego/legoomni/src/common/legophoneme.cpp +++ b/LEGO1/lego/legoomni/src/common/legophoneme.cpp @@ -8,39 +8,39 @@ LegoPhoneme::~LegoPhoneme() } // FUNCTION: LEGO1 0x10044eb0 -undefined4 LegoPhoneme::VTable0x00() +MxU32 LegoPhoneme::GetCount() { - return m_unk0x14; + return m_count; } // FUNCTION: LEGO1 0x10044ec0 -void LegoPhoneme::VTable0x04(undefined4 p_unk0x14) +void LegoPhoneme::SetCount(MxU32 p_count) { - m_unk0x14 = p_unk0x14; + m_count = p_count; } // FUNCTION: LEGO1 0x10044ed0 -LegoTextureInfo* LegoPhoneme::VTable0x08() +LegoTextureInfo* LegoPhoneme::GetTextureInfo() { - return m_unk0x18; + return m_textureInfo; } // FUNCTION: LEGO1 0x10044ee0 -void LegoPhoneme::VTable0x0c(LegoTextureInfo* p_unk0x18) +void LegoPhoneme::SetTextureInfo(LegoTextureInfo* p_textureInfo) { - m_unk0x18 = p_unk0x18; + m_textureInfo = p_textureInfo; } // FUNCTION: LEGO1 0x10044ef0 -LegoTextureInfo* LegoPhoneme::VTable0x10() +LegoTextureInfo* LegoPhoneme::GetCachedTextureInfo() { - return m_unk0x1c; + return m_cachedTextureInfo; } // FUNCTION: LEGO1 0x10044f00 -void LegoPhoneme::VTable0x14(LegoTextureInfo* p_unk0x1c) +void LegoPhoneme::SetCachedTextureInfo(LegoTextureInfo* p_cachedTextureInfo) { - m_unk0x1c = p_unk0x1c; + m_cachedTextureInfo = p_cachedTextureInfo; } // FUNCTION: LEGO1 0x10044f10 @@ -51,9 +51,9 @@ void LegoPhoneme::VTable0x18() // FUNCTION: LEGO1 0x10044f20 void LegoPhoneme::Init() { - m_unk0x14 = 0; - m_unk0x18 = NULL; - m_unk0x1c = NULL; + m_count = 0; + m_textureInfo = NULL; + m_cachedTextureInfo = NULL; } // FUNCTION: LEGO1 0x10044f30 diff --git a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp index 586179d6..bcb628c0 100644 --- a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp @@ -31,10 +31,10 @@ const char* g_plantLodNames[4][5] = { }; // GLOBAL: LEGO1 0x100f16b0 -float g_unk0x100f16b0[] = {0.1f, 0.7f, 0.5f, 0.9f}; +float g_heightPerCount[] = {0.1f, 0.7f, 0.5f, 0.9f}; // GLOBAL: LEGO1 0x100f16c0 -MxU8 g_unk0x100f16c0[] = {1, 2, 2, 3}; +MxU8 g_counters[] = {1, 2, 2, 3}; // GLOBAL: LEGO1 0x100f315c MxU32 LegoPlantManager::g_maxSound = 8; @@ -217,7 +217,7 @@ LegoEntity* LegoPlantManager::CreatePlant(MxS32 p_index, LegoWorld* p_world, Leg if (p_index < sizeOfArray(g_plantInfo)) { MxU32 world = 1 << (MxU8) p_worldId; - if (g_plantInfo[p_index].m_worlds & world && g_plantInfo[p_index].m_unk0x16 != 0) { + if (g_plantInfo[p_index].m_worlds & world && g_plantInfo[p_index].m_counter != 0) { if (g_plantInfo[p_index].m_entity == NULL) { char name[256]; char lodName[256]; @@ -286,7 +286,7 @@ MxResult LegoPlantManager::Write(LegoStorage* p_storage) if (p_storage->Write(&info->m_color, sizeof(info->m_color)) != SUCCESS) { goto done; } - if (p_storage->Write(&info->m_initialUnk0x16, sizeof(info->m_initialUnk0x16)) != SUCCESS) { + if (p_storage->Write(&info->m_initialCounter, sizeof(info->m_initialCounter)) != SUCCESS) { goto done; } } @@ -321,12 +321,12 @@ MxResult LegoPlantManager::Read(LegoStorage* p_storage) if (p_storage->Read(&info->m_color, sizeof(MxU8)) != SUCCESS) { goto done; } - if (p_storage->Read(&info->m_unk0x16, sizeof(MxS8)) != SUCCESS) { + if (p_storage->Read(&info->m_counter, sizeof(MxS8)) != SUCCESS) { goto done; } - info->m_initialUnk0x16 = info->m_unk0x16; - FUN_10026860(i); + info->m_initialCounter = info->m_counter; + AdjustHeight(i); } result = SUCCESS; @@ -337,13 +337,13 @@ MxResult LegoPlantManager::Read(LegoStorage* p_storage) // FUNCTION: LEGO1 0x10026860 // FUNCTION: BETA10 0x100c5be0 -void LegoPlantManager::FUN_10026860(MxS32 p_index) +void LegoPlantManager::AdjustHeight(MxS32 p_index) { MxU8 variant = g_plantInfo[p_index].m_variant; - if (g_plantInfo[p_index].m_unk0x16 >= 0) { - float value = g_unk0x100f16c0[variant] - g_plantInfo[p_index].m_unk0x16; - g_plantInfo[p_index].m_position[1] = g_plantInfoInit[p_index].m_position[1] - value * g_unk0x100f16b0[variant]; + if (g_plantInfo[p_index].m_counter >= 0) { + float value = g_counters[variant] - g_plantInfo[p_index].m_counter; + g_plantInfo[p_index].m_position[1] = g_plantInfoInit[p_index].m_position[1] - value * g_heightPerCount[variant]; } else { g_plantInfo[p_index].m_position[1] = g_plantInfoInit[p_index].m_position[1]; @@ -411,7 +411,7 @@ MxBool LegoPlantManager::SwitchVariant(LegoEntity* p_entity) { LegoPlantInfo* info = GetInfo(p_entity); - if (info == NULL || info->m_unk0x16 != -1) { + if (info == NULL || info->m_counter != -1) { return FALSE; } @@ -551,7 +551,7 @@ void LegoPlantManager::SetCustomizeAnimFile(const char* p_value) // FUNCTION: LEGO1 0x10026c50 // FUNCTION: BETA10 0x100c6349 -MxBool LegoPlantManager::FUN_10026c50(LegoEntity* p_entity) +MxBool LegoPlantManager::DecrementCounter(LegoEntity* p_entity) { LegoPlantInfo* info = GetInfo(p_entity); @@ -559,12 +559,12 @@ MxBool LegoPlantManager::FUN_10026c50(LegoEntity* p_entity) return FALSE; } - return FUN_10026c80(info - g_plantInfo); + return DecrementCounter(info - g_plantInfo); } // FUNCTION: LEGO1 0x10026c80 // FUNCTION: BETA10 0x100c63eb -MxBool LegoPlantManager::FUN_10026c80(MxS32 p_index) +MxBool LegoPlantManager::DecrementCounter(MxS32 p_index) { if (p_index >= sizeOfArray(g_plantInfo)) { return FALSE; @@ -578,23 +578,23 @@ MxBool LegoPlantManager::FUN_10026c80(MxS32 p_index) MxBool result = TRUE; - if (info->m_unk0x16 < 0) { - info->m_unk0x16 = g_unk0x100f16c0[info->m_variant]; + if (info->m_counter < 0) { + info->m_counter = g_counters[info->m_variant]; } - if (info->m_unk0x16 > 0) { + if (info->m_counter > 0) { LegoROI* roi = info->m_entity->GetROI(); - info->m_unk0x16--; + info->m_counter--; - if (info->m_unk0x16 == 1) { - info->m_unk0x16 = 0; + if (info->m_counter == 1) { + info->m_counter = 0; } - if (info->m_unk0x16 == 0) { + if (info->m_counter == 0) { roi->SetVisibility(FALSE); } else { - FUN_10026860(info - g_plantInfo); + AdjustHeight(info - g_plantInfo); info->m_entity->SetLocation(info->m_position, info->m_direction, info->m_up, FALSE); } } @@ -624,7 +624,7 @@ void LegoPlantManager::ScheduleAnimation(LegoEntity* p_entity, MxLong p_length) time += p_length; entry->m_time = time + 1000; - FUN_100271b0(p_entity, -1); + AdjustCounter(p_entity, -1); } // FUNCTION: LEGO1 0x10026e00 @@ -673,11 +673,11 @@ MxResult LegoPlantManager::Tickle() if (entry->m_time < time) { LegoPlantInfo* info = GetInfo(entry->m_entity); - if (info->m_unk0x16 == 0) { + if (info->m_counter == 0) { entry->m_roi->SetVisibility(FALSE); } else { - FUN_10026860(info - g_plantInfo); + AdjustHeight(info - g_plantInfo); info->m_entity->SetLocation(info->m_position, info->m_direction, info->m_up, FALSE); } @@ -700,14 +700,14 @@ MxResult LegoPlantManager::Tickle() } // FUNCTION: LEGO1 0x10027120 -void LegoPlantManager::FUN_10027120() +void LegoPlantManager::ClearCounters() { LegoWorld* world = CurrentWorld(); for (MxS32 i = 0; i < sizeOfArray(g_plantInfo); i++) { - g_plantInfo[i].m_unk0x16 = -1; - g_plantInfo[i].m_initialUnk0x16 = -1; - FUN_10026860(i); + g_plantInfo[i].m_counter = -1; + g_plantInfo[i].m_initialCounter = -1; + AdjustHeight(i); if (g_plantInfo[i].m_entity != NULL) { g_plantInfo[i].m_entity->SetLocation( @@ -721,28 +721,28 @@ void LegoPlantManager::FUN_10027120() } // FUNCTION: LEGO1 0x100271b0 -void LegoPlantManager::FUN_100271b0(LegoEntity* p_entity, MxS32 p_adjust) +void LegoPlantManager::AdjustCounter(LegoEntity* p_entity, MxS32 p_adjust) { LegoPlantInfo* info = GetInfo(p_entity); if (info != NULL) { - if (info->m_unk0x16 < 0) { - info->m_unk0x16 = g_unk0x100f16c0[info->m_variant]; + if (info->m_counter < 0) { + info->m_counter = g_counters[info->m_variant]; } - if (info->m_unk0x16 > 0) { - info->m_unk0x16 += p_adjust; - if (info->m_unk0x16 <= 1 && p_adjust < 0) { - info->m_unk0x16 = 0; + if (info->m_counter > 0) { + info->m_counter += p_adjust; + if (info->m_counter <= 1 && p_adjust < 0) { + info->m_counter = 0; } } } } // FUNCTION: LEGO1 0x10027200 -void LegoPlantManager::FUN_10027200() +void LegoPlantManager::SetInitialCounters() { for (MxU32 i = 0; i < sizeOfArray(g_plantInfo); i++) { - g_plantInfo[i].m_initialUnk0x16 = g_plantInfo[i].m_unk0x16; + g_plantInfo[i].m_initialCounter = g_plantInfo[i].m_counter; } } diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp index c07a8cb3..26948049 100644 --- a/LEGO1/lego/legoomni/src/common/legoutils.cpp +++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp @@ -37,8 +37,6 @@ #include #include -LegoSdlEvents g_legoSdlEvents; - // FUNCTION: LEGO1 0x1003dd70 // FUNCTION: BETA10 0x100d3410 LegoROI* PickROI(MxLong p_x, MxLong p_y) @@ -568,17 +566,6 @@ void EnableAnimations(MxBool p_enable) AnimationManager()->FUN_100604d0(p_enable); } -void InitSdlEvents() -{ - static bool g_initialized = false; - - if (!g_initialized) { - g_initialized = true; - Uint32 event = SDL_RegisterEvents(1); - g_legoSdlEvents.m_windowsMessage = event + 0; - } -} - // FUNCTION: LEGO1 0x1003ef40 void SetAppCursor(Cursor p_cursor) { diff --git a/LEGO1/lego/legoomni/src/entity/legoentity.cpp b/LEGO1/lego/legoomni/src/entity/legoentity.cpp index 127971f5..acb0848c 100644 --- a/LEGO1/lego/legoomni/src/entity/legoentity.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoentity.cpp @@ -503,7 +503,7 @@ MxLong LegoEntity::Notify(MxParam& p_param) case e_unk1: break; case e_plant: - PlantManager()->FUN_10026c50(this); + PlantManager()->DecrementCounter(this); break; case e_building: BuildingManager()->FUN_10030000(this); diff --git a/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp b/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp index 84edb919..4a852c6f 100644 --- a/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp +++ b/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp @@ -136,6 +136,8 @@ MxResult LegoInputManager::GetNavigationKeyStates(MxU32& p_keyFlags) keyFlags |= c_ctrl; } + GetNavigationTouchStates(keyFlags); + p_keyFlags = keyFlags; return SUCCESS; @@ -542,3 +544,45 @@ void LegoInputManager::EnableInputProcessing() g_unk0x100f31b0 = -1; g_unk0x100f31b4 = NULL; } + +MxResult LegoInputManager::GetNavigationTouchStates(MxU32& p_keyStates) +{ + int count; + SDL_TouchID* touchDevices = SDL_GetTouchDevices(&count); + + if (touchDevices) { + auto applyFingerNavigation = [&p_keyStates](SDL_TouchID p_touchId) { + int count; + SDL_Finger** fingers = SDL_GetTouchFingers(p_touchId, &count); + + if (fingers) { + for (int i = 0; i < count; i++) { + if (fingers[i]->y > 3.0 / 4.0) { + if (fingers[i]->x < 1.0 / 3.0) { + p_keyStates |= c_left; + } + else if (fingers[i]->x > 2.0 / 3.0) { + p_keyStates |= c_right; + } + else { + p_keyStates |= c_down; + } + } + else { + p_keyStates |= c_up; + } + } + + SDL_free(fingers); + } + }; + + for (int i = 0; i < count; i++) { + applyFingerNavigation(touchDevices[i]); + } + + SDL_free(touchDevices); + } + + return SUCCESS; +} diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp index 4ccfabb8..3b99f0b5 100644 --- a/LEGO1/lego/legoomni/src/main/legomain.cpp +++ b/LEGO1/lego/legoomni/src/main/legomain.cpp @@ -281,8 +281,6 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param) SetAppCursor(e_cursorBusy); m_gameState->SetCurrentAct(LegoGameState::e_act1); - InitSdlEvents(); - result = SUCCESS; done: diff --git a/LEGO1/lego/legoomni/src/paths/legopathcontroller.cpp b/LEGO1/lego/legoomni/src/paths/legopathcontroller.cpp index 056f7270..9a3ac922 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathcontroller.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathcontroller.cpp @@ -566,27 +566,32 @@ MxResult LegoPathController::ReadEdges(LegoStorage* p_storage) if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numN); edge.m_pointA = &m_nodes[s]; if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numN); edge.m_pointB = &m_nodes[s]; if (edge.m_flags & LegoOrientedEdge::c_hasFaceA) { if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numL); edge.m_faceA = &m_boundaries[s]; if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numE); edge.m_ccwA = &m_edges[s]; if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numE); edge.m_cwA = &m_edges[s]; } @@ -594,16 +599,19 @@ MxResult LegoPathController::ReadEdges(LegoStorage* p_storage) if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numL); edge.m_faceB = &m_boundaries[s]; if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numE); edge.m_ccwB = &m_edges[s]; if (p_storage->Read(&s, sizeof(MxU16)) != SUCCESS) { return FAILURE; } + assert(s < m_numE); edge.m_cwB = &m_edges[s]; } @@ -624,6 +632,9 @@ MxResult LegoPathController::ReadEdges(LegoStorage* p_storage) MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage) { for (MxS32 i = 0; i < m_numL; i++) { +#ifdef BETA10 + Mx4DPointFloat unused; +#endif LegoPathBoundary& boundary = m_boundaries[i]; MxU8 numE; MxU16 s; @@ -633,6 +644,8 @@ MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage) return FAILURE; } + assert(numE > 2); + boundary.m_edgeNormals = new Mx4DPointFloat[numE]; LegoOrientedEdge** edges = new LegoOrientedEdge*[numE]; @@ -643,6 +656,8 @@ MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage) return FAILURE; } + assert(s < m_numE); + edges[j] = &m_edges[s]; } @@ -700,6 +715,8 @@ MxResult LegoPathController::ReadBoundaries(LegoStorage* p_storage) return FAILURE; } + assert(s < m_numT); + boundary.m_pathTrigger[j].m_pathStruct = &m_structs[s]; if (p_storage->Read(&boundary.m_pathTrigger[j].m_data, sizeof(boundary.m_pathTrigger[j].m_data)) != diff --git a/LEGO1/lego/legoomni/src/video/legophonemepresenter.cpp b/LEGO1/lego/legoomni/src/video/legophonemepresenter.cpp index d1cee327..353480fc 100644 --- a/LEGO1/lego/legoomni/src/video/legophonemepresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legophonemepresenter.cpp @@ -68,8 +68,8 @@ void LegoPhonemePresenter::StartingTickle() CharacterManager()->SetHeadTexture(entityROI, textureInfo); - phoneme->VTable0x0c(m_textureInfo); - phoneme->VTable0x14(textureInfo); + phoneme->SetTextureInfo(m_textureInfo); + phoneme->SetCachedTextureInfo(textureInfo); phonemeList->Append(phoneme); m_textureInfo = textureInfo; } @@ -78,7 +78,7 @@ void LegoPhonemePresenter::StartingTickle() cursor.Current(phoneme); delete newPhoneme; - phoneme->VTable0x04(phoneme->VTable0x00() + 1); + phoneme->SetCount(phoneme->GetCount() + 1); cursor.SetValue(phoneme); m_unk0x70 = TRUE; @@ -136,7 +136,7 @@ void LegoPhonemePresenter::EndAction() cursor.Current(phoneme); delete newPhoneme; - if (phoneme->VTable0x00() == 1) { + if (phoneme->GetCount() == 1) { LegoROI* roi; if (m_unk0x84) { @@ -154,12 +154,12 @@ void LegoPhonemePresenter::EndAction() CharacterManager()->ReleaseActor(m_roiName.GetData()); } - TextureContainer()->EraseCached(phoneme->VTable0x10()); - TextureContainer()->EraseCached(phoneme->VTable0x08()); + TextureContainer()->EraseCached(phoneme->GetCachedTextureInfo()); + TextureContainer()->EraseCached(phoneme->GetTextureInfo()); cursor.Destroy(); } else { - phoneme->VTable0x04(phoneme->VTable0x00() - 1); + phoneme->SetCount(phoneme->GetCount() - 1); cursor.SetValue(phoneme); } diff --git a/LEGO1/lego/legoomni/src/worlds/act3.cpp b/LEGO1/lego/legoomni/src/worlds/act3.cpp index e9a7c0d8..549ca6b8 100644 --- a/LEGO1/lego/legoomni/src/worlds/act3.cpp +++ b/LEGO1/lego/legoomni/src/worlds/act3.cpp @@ -665,7 +665,7 @@ MxLong Act3::HandleTransitionEnd() // FUNCTION: LEGO1 0x10073270 void Act3::ReadyWorld() { - PlantManager()->FUN_10027200(); + PlantManager()->SetInitialCounters(); BuildingManager()->FUN_10030800(); AnimationManager()->FUN_1005f6d0(FALSE); VideoManager()->Get3DManager()->SetFrustrum(90.0f, 0.1f, 125.0f); diff --git a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp index 50515f9f..b552e388 100644 --- a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp +++ b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp @@ -1389,7 +1389,7 @@ void Infocenter::Reset() break; } - PlantManager()->FUN_10027120(); + PlantManager()->ClearCounters(); BuildingManager()->FUN_10030590(); AnimationManager()->Reset(FALSE); CharacterManager()->ReleaseAllActors(); diff --git a/LEGO1/lego/sources/geom/legoweedge.h b/LEGO1/lego/sources/geom/legoweedge.h index 4516f938..7e0e9bae 100644 --- a/LEGO1/lego/sources/geom/legoweedge.h +++ b/LEGO1/lego/sources/geom/legoweedge.h @@ -25,6 +25,7 @@ class LegoWEEdge { // FUNCTION: BETA10 0x100373f0 LegoU32 IsEqual(LegoWEEdge* p_other) { return this == p_other; } + // FUNCTION: BETA10 0x100bd410 void SetEdges(LegoOrientedEdge** p_edges, LegoU8 p_numEdges) { m_edges = p_edges; diff --git a/LEGO1/mxdirectx/legodxinfo.cpp b/LEGO1/mxdirectx/legodxinfo.cpp index f1e9bca7..bb89275a 100644 --- a/LEGO1/mxdirectx/legodxinfo.cpp +++ b/LEGO1/mxdirectx/legodxinfo.cpp @@ -215,11 +215,6 @@ int LegoDeviceEnumerate::FUN_1009d210() } for (list::iterator it = m_list.begin(); it != m_list.end();) { - if (!DriverSupportsRequiredDisplayMode(*it)) { - m_list.erase(it++); - continue; - } - MxDriver& driver = *it; for (list::iterator it2 = driver.m_devices.begin(); it2 != driver.m_devices.end();) { @@ -246,23 +241,6 @@ int LegoDeviceEnumerate::FUN_1009d210() return 0; } -// FUNCTION: CONFIG 0x00402b00 -// FUNCTION: LEGO1 0x1009d370 -// FUNCTION: BETA10 0x1011d176 -unsigned char LegoDeviceEnumerate::DriverSupportsRequiredDisplayMode(MxDriver& p_driver) -{ - for (list::iterator it = p_driver.m_displayModes.begin(); it != p_driver.m_displayModes.end(); - it++) { - if ((*it).m_width == 640 && (*it).m_height == 480) { - if ((*it).m_bitsPerPixel == 8 || (*it).m_bitsPerPixel == 16) { - return TRUE; - } - } - } - - return FALSE; -} - // FUNCTION: CONFIG 0x00402b60 // FUNCTION: LEGO1 0x1009d3d0 // FUNCTION: BETA10 0x1011d235 diff --git a/LEGO1/mxdirectx/legodxinfo.h b/LEGO1/mxdirectx/legodxinfo.h index d3a2bc7a..c9289870 100644 --- a/LEGO1/mxdirectx/legodxinfo.h +++ b/LEGO1/mxdirectx/legodxinfo.h @@ -17,7 +17,6 @@ class LegoDeviceEnumerate : public MxDeviceEnumerate { int GetBestDevice(); static bool SupportsSIMD(); int FUN_1009d210(); - unsigned char DriverSupportsRequiredDisplayMode(MxDriver& p_driver); unsigned char FUN_1009d3d0(Direct3DDeviceInfo& p_device); // SYNTHETIC: BETA10 0x100d8d10 diff --git a/LEGO1/mxdirectx/mxdirect3d.cpp b/LEGO1/mxdirectx/mxdirect3d.cpp index d0e0f94d..f9e0f74d 100644 --- a/LEGO1/mxdirectx/mxdirect3d.cpp +++ b/LEGO1/mxdirectx/mxdirect3d.cpp @@ -167,14 +167,6 @@ BOOL MxDirect3D::D3DSetMode() } DeviceModesInfo::Mode mode = *CurrentMode(); - - if (IsFullScreen()) { - if (!IsSupportedMode(mode.width, mode.height, mode.bitsPerPixel)) { - Error("This device cannot support the current display mode", DDERR_GENERIC); - return FALSE; - } - } - LPDIRECTDRAWSURFACE frontBuffer = FrontBuffer(); LPDIRECTDRAWSURFACE backBuffer = BackBuffer(); @@ -277,21 +269,6 @@ BOOL MxDirect3D::SetDevice(MxDeviceEnumerate& p_deviceEnumerate, MxDriver* p_dri *d->m_deviceInfo->m_guid = *driver.m_guid; } - d->m_deviceInfo->m_count = driver.m_displayModes.size(); - - if (d->m_deviceInfo->m_count > 0) { - int j = 0; - d->m_deviceInfo->m_modeArray = new DeviceModesInfo::Mode[d->m_deviceInfo->m_count]; - - for (list::iterator it2 = driver.m_displayModes.begin(); - it2 != driver.m_displayModes.end(); - it2++, j++) { - d->m_deviceInfo->m_modeArray[j].width = (*it2).m_width; - d->m_deviceInfo->m_modeArray[j].height = (*it2).m_height; - d->m_deviceInfo->m_modeArray[j].bitsPerPixel = (*it2).m_bitsPerPixel; - } - } - d->m_deviceInfo->m_ddcaps = driver.m_ddCaps; if (i == 0) { diff --git a/LEGO1/mxdirectx/mxdirectdraw.cpp b/LEGO1/mxdirectx/mxdirectdraw.cpp index 40c353ed..6f3dfd78 100644 --- a/LEGO1/mxdirectx/mxdirectdraw.cpp +++ b/LEGO1/mxdirectx/mxdirectdraw.cpp @@ -283,23 +283,6 @@ BOOL MxDirectDraw::DDInit(BOOL fullscreen) return TRUE; } -// FUNCTION: LEGO1 0x1009d9d0 -// FUNCTION: BETA10 0x10120e45 -BOOL MxDirectDraw::IsSupportedMode(int width, int height, int bpp) -{ - DeviceModesInfo::Mode mode = {width, height, bpp}; - - assert(m_currentDevInfo); - - for (int i = 0; i < m_currentDevInfo->m_count; i++) { - if (m_currentDevInfo->m_modeArray[i] == mode) { - return TRUE; - } - } - - return FALSE; -} - // FUNCTION: LEGO1 0x1009da20 // FUNCTION: BETA10 0x10120efb void EnableResizing(HWND p_hwnd, BOOL p_flag) @@ -339,12 +322,6 @@ BOOL MxDirectDraw::DDSetMode(int width, int height, int bpp) } #endif - if (!IsSupportedMode(width, height, bpp)) { - width = m_currentDevInfo->m_modeArray[0].width; - height = m_currentDevInfo->m_modeArray[0].height; - bpp = m_currentDevInfo->m_modeArray[0].bitsPerPixel; - } - m_bIgnoreWMSIZE = TRUE; result = m_pDirectDraw->SetDisplayMode(width, height, bpp); m_bIgnoreWMSIZE = FALSE; diff --git a/LEGO1/mxdirectx/mxdirectdraw.h b/LEGO1/mxdirectx/mxdirectdraw.h index b430751c..574f8e54 100644 --- a/LEGO1/mxdirectx/mxdirectdraw.h +++ b/LEGO1/mxdirectx/mxdirectdraw.h @@ -55,8 +55,6 @@ class MxDirectDraw { // FUNCTION: BETA10 0x1011c170 BOOL IsFullScreen() { return m_bFullScreen; } - BOOL IsSupportedMode(int width, int height, int bpp); - int Pause(BOOL); BOOL RestoreSurfaces(); diff --git a/LEGO1/mxdirectx/mxdirectxinfo.cpp b/LEGO1/mxdirectx/mxdirectxinfo.cpp index f2d32e9c..c5100031 100644 --- a/LEGO1/mxdirectx/mxdirectxinfo.cpp +++ b/LEGO1/mxdirectx/mxdirectxinfo.cpp @@ -216,7 +216,6 @@ BOOL MxDeviceEnumerate::EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc BuildErrorString("DirectDraw Create failed: %s\n", EnumerateErrorToString(result)); } else { - lpDD->EnumDisplayModes(0, NULL, this, DisplayModesEnumerateCallback); newDevice.m_ddCaps.dwSize = sizeof(newDevice.m_ddCaps); result = lpDD->GetCaps(&newDevice.m_ddCaps, NULL); @@ -267,18 +266,6 @@ void MxDeviceEnumerate::BuildErrorString(const char* p_format, ...) va_end(args); } -// FUNCTION: CONFIG 0x00401bf0 -// FUNCTION: LEGO1 0x1009c4f0 -// FUNCTION: BETA10 0x1011e1dd -HRESULT CALLBACK MxDeviceEnumerate::DisplayModesEnumerateCallback(LPDDSURFACEDESC p_ddsd, LPVOID p_context) -{ - if (p_context == NULL) { - assert(0); - } - - return ((MxDeviceEnumerate*) p_context)->EnumDisplayModesCallback(p_ddsd); -} - // FUNCTION: CONFIG 0x00401c10 // FUNCTION: LEGO1 0x1009c510 // FUNCTION: BETA10 0x1011e226 @@ -299,20 +286,6 @@ HRESULT CALLBACK MxDeviceEnumerate::DevicesEnumerateCallback( ->EnumDevicesCallback(p_guid, p_deviceDesc, p_deviceName, p_HWDesc, p_HELDesc); } -// FUNCTION: CONFIG 0x00401c40 -// FUNCTION: LEGO1 0x1009c540 -// FUNCTION: BETA10 0x1011e27f -HRESULT MxDeviceEnumerate::EnumDisplayModesCallback(LPDDSURFACEDESC p_ddsd) -{ - assert(m_list.size() > 0); - assert(p_ddsd); - - // TODO: compat_mode? - MxDisplayMode displayMode(p_ddsd->dwWidth, p_ddsd->dwHeight, p_ddsd->ddpfPixelFormat.dwRGBBitCount); - m_list.back().m_displayModes.push_back(displayMode); - return DDENUMRET_OK; -} - // FUNCTION: CONFIG 0x00401cd0 // FUNCTION: LEGO1 0x1009c5d0 // FUNCTION: BETA10 0x1011e32f @@ -582,8 +555,4 @@ DeviceModesInfo::~DeviceModesInfo() if (m_guid != NULL) { delete m_guid; } - - if (m_modeArray != NULL) { - delete[] m_modeArray; - } } diff --git a/LEGO1/mxdirectx/mxdirectxinfo.h b/LEGO1/mxdirectx/mxdirectxinfo.h index 3b3c0600..a4ebef91 100644 --- a/LEGO1/mxdirectx/mxdirectxinfo.h +++ b/LEGO1/mxdirectx/mxdirectxinfo.h @@ -28,11 +28,10 @@ struct DeviceModesInfo { DeviceModesInfo(); ~DeviceModesInfo(); - GUID* m_guid; // 0x00 - Mode* m_modeArray; // 0x04 - int m_count; // 0x08 - DDCAPS m_ddcaps; // 0x0c - void* m_unk0x178; // 0x178 + GUID* m_guid; // 0x00 + int m_count; // 0x08 + DDCAPS m_ddcaps; // 0x0c + void* m_unk0x178; // 0x178 // SYNTHETIC: BETA10 0x1011c650 // DeviceModesInfo::`scalar deleting destructor' @@ -128,7 +127,6 @@ struct MxDriver { char* m_driverName; // 0x08 DDCAPS m_ddCaps; // 0x0c list m_devices; // 0x178 - list m_displayModes; // 0x184 int operator==(MxDriver) const { return 0; } int operator<(MxDriver) const { return 0; } @@ -199,7 +197,6 @@ class MxDeviceEnumerate { virtual int DoEnumerate(); // vtable+0x00 BOOL EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc, LPSTR p_driverName); - HRESULT EnumDisplayModesCallback(LPDDSURFACEDESC p_ddsd); HRESULT EnumDevicesCallback( LPGUID p_guid, LPCSTR p_deviceDesc, @@ -211,7 +208,6 @@ class MxDeviceEnumerate { static void BuildErrorString(const char*, ...); static BOOL CALLBACK DirectDrawEnumerateCallback(LPGUID p_guid, LPSTR p_driverDesc, LPSTR p_driverName, LPVOID p_context); - static HRESULT CALLBACK DisplayModesEnumerateCallback(LPDDSURFACEDESC p_ddsd, LPVOID p_context); static HRESULT CALLBACK DevicesEnumerateCallback( LPGUID p_guid, LPSTR p_deviceDesc, diff --git a/LEGO1/omni/include/mxomni.h b/LEGO1/omni/include/mxomni.h index 38a077f8..0e6bd07c 100644 --- a/LEGO1/omni/include/mxomni.h +++ b/LEGO1/omni/include/mxomni.h @@ -43,8 +43,8 @@ class MxOmni : public MxCore { LEGO1_EXPORT static void SetCD(const char* p_cd); LEGO1_EXPORT static void SetHD(const char* p_hd); LEGO1_EXPORT static void SetSound3D(MxBool p_use3dSound); - static const vector& GetHDFiles() { return g_hdFiles; } - static const vector& GetCDFiles() { return g_cdFiles; } + static vector& GetHDFiles() { return g_hdFiles; } + static vector& GetCDFiles() { return g_cdFiles; } MxOmni(); ~MxOmni() override; diff --git a/LEGO1/omni/include/mxpresenter.h b/LEGO1/omni/include/mxpresenter.h index e257f3c4..0c24881e 100644 --- a/LEGO1/omni/include/mxpresenter.h +++ b/LEGO1/omni/include/mxpresenter.h @@ -5,6 +5,9 @@ #include "mxcore.h" #include "mxcriticalsection.h" #include "mxgeometry.h" +#include "mxutilities.h" + +#include class MxCompositePresenter; class MxDSAction; @@ -62,6 +65,12 @@ class MxPresenter : public MxCore { { m_previousTickleStates |= 1 << (MxU8) m_currentTickleState; m_currentTickleState = p_tickleState; + + SDL_Event event; + event.user.type = g_legoSdlEvents.m_presenterProgress; + event.user.code = m_currentTickleState; + event.user.data1 = (void*) this; + SDL_PushEvent(&event); } public: diff --git a/LEGO1/omni/include/mxutilities.h b/LEGO1/omni/include/mxutilities.h index b345ea79..c68d4201 100644 --- a/LEGO1/omni/include/mxutilities.h +++ b/LEGO1/omni/include/mxutilities.h @@ -1,10 +1,19 @@ #ifndef MXUTILITIES_H #define MXUTILITIES_H +#include "lego1_export.h" #include "mxtypes.h" +#include #include +struct LegoSdlEvents { + Uint32 m_windowsMessage; + Uint32 m_presenterProgress; +}; + +LEGO1_EXPORT extern LegoSdlEvents g_legoSdlEvents; + class MxDSFile; class MxDSObject; class MxDSAction; diff --git a/LEGO1/omni/src/common/mxutilities.cpp b/LEGO1/omni/src/common/mxutilities.cpp index 4d71062d..c17e41e5 100644 --- a/LEGO1/omni/src/common/mxutilities.cpp +++ b/LEGO1/omni/src/common/mxutilities.cpp @@ -12,6 +12,8 @@ #include #include +LegoSdlEvents g_legoSdlEvents; + // GLOBAL: LEGO1 0x101020e8 void (*g_omniUserMessage)(const char*, MxS32) = NULL; diff --git a/LEGO1/omni/src/main/mxomni.cpp b/LEGO1/omni/src/main/mxomni.cpp index 7c701c49..d7b76847 100644 --- a/LEGO1/omni/src/main/mxomni.cpp +++ b/LEGO1/omni/src/main/mxomni.cpp @@ -162,6 +162,12 @@ MxResult MxOmni::Create(MxOmniCreateParam& p_param) } } + { + Uint32 event = SDL_RegisterEvents(2); + g_legoSdlEvents.m_windowsMessage = event + 0; + g_legoSdlEvents.m_presenterProgress = event + 1; + } + result = SUCCESS; done: diff --git a/README.md b/README.md index c7d1608a..fa1a8f4a 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Please note: this project is dedicated to achieving platform independence withou | Windows | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | Linux | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | macOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | +| [Web](https://isle.pizza) | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | ### Library substitutions @@ -22,7 +23,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl | Library/subsystem | Substitution | Status | | | - | - | - | - | -| Window, Events | [SDL3](https://www.libsdl.org/) | 🚧 | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Awindow%5D%22&type=code) | +| Window, Events | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Awindow%5D%22&type=code) | | Windows Registry (Configuration) | [libiniparser](https://gitlab.com/iniparser/iniparser) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aconfig%5D%22&type=code) | | Filesystem | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Afilesystem%5D%22&type=code) | | Threads, Mutexes (Synchronization) | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Asynchronization%5D%22&type=code) | @@ -32,7 +33,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl | DirectDraw (2D video) | [SDL3](https://www.libsdl.org/) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A2d%5D%22&type=code) | | [Smacker](https://github.com/isledecomp/isle/tree/master/3rdparty/smacker) | [libsmacker](https://github.com/foxtacles/libsmacker) | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable%20%22%2F%2F%20%5Blibrary%3Alibsmacker%5D%22&type=code) | | Direct3D (3D video) | [SDL3](https://www.libsdl.org/), OpenGL, Software | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A3d%5D%22&type=code) | -| Direct3D Retained Mode | Custom re-implementation | 🚧 | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aretained%5D%22&type=code) | +| Direct3D Retained Mode | Custom re-implementation | ✅ | [Remarks](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aretained%5D%22&type=code) | | [SmartHeap](https://github.com/isledecomp/isle/tree/master/3rdparty/smartheap) | Default memory allocator | - | - | ## Building diff --git a/miniwin/include/miniwin/miniwindevice.h b/miniwin/include/miniwin/miniwindevice.h index 032e776d..2434175f 100644 --- a/miniwin/include/miniwin/miniwindevice.h +++ b/miniwin/include/miniwin/miniwindevice.h @@ -3,6 +3,7 @@ DEFINE_GUID(IID_IDirect3DRMMiniwinDevice, 0x6eb09673, 0x8d30, 0x4d8a, 0x8d, 0x81, 0x34, 0xea, 0x69, 0x30, 0x12, 0x01); struct IDirect3DRMMiniwinDevice : virtual public IUnknown { + virtual bool ConvertEventToRenderCoordinates(SDL_Event* event) = 0; virtual float GetShininessFactor() = 0; virtual HRESULT SetShininessFactor(float factor) = 0; }; diff --git a/miniwin/src/d3drm/d3drmdevice.cpp b/miniwin/src/d3drm/d3drmdevice.cpp index 2000cf56..58ff7c2c 100644 --- a/miniwin/src/d3drm/d3drmdevice.cpp +++ b/miniwin/src/d3drm/d3drmdevice.cpp @@ -144,6 +144,11 @@ float Direct3DRMDevice2Impl::GetShininessFactor() return m_renderer->GetShininessFactor(); } +bool Direct3DRMDevice2Impl::ConvertEventToRenderCoordinates(SDL_Event* event) +{ + return m_renderer->ConvertEventToRenderCoordinates(event); +} + HRESULT Direct3DRMDevice2Impl::SetShininessFactor(float factor) { return m_renderer->SetShininessFactor(factor); diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 8ac249f1..fc81ddd3 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -17,6 +17,9 @@ SDL_Window* DDWindow; SDL_Surface* DDBackBuffer; +SDL_Texture* HWBackBuffer; +SDL_PixelFormat HWBackBufferFormat; +SDL_Renderer* DDRenderer; HRESULT DirectDrawImpl::QueryInterface(const GUID& riid, void** ppvObject) { @@ -280,6 +283,7 @@ HRESULT DirectDrawImpl::RestoreDisplayMode() HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags) { SDL_Window* sdlWindow = reinterpret_cast(hWnd); + if (sdlWindow) { bool fullscreen; if ((dwFlags & DDSCL_NORMAL) == DDSCL_NORMAL) { @@ -293,9 +297,13 @@ HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags) } if (!SDL_SetWindowFullscreen(sdlWindow, fullscreen)) { +#ifndef __EMSCRIPTEN__ return DDERR_GENERIC; +#endif } DDWindow = sdlWindow; + DDRenderer = SDL_CreateRenderer(DDWindow, NULL); + SDL_SetRenderLogicalPresentation(DDRenderer, 640, 480, SDL_LOGICAL_PRESENTATION_LETTERBOX); } return DD_OK; } diff --git a/miniwin/src/ddraw/ddsurface.cpp b/miniwin/src/ddraw/ddsurface.cpp index 5c8ee6db..7a767e87 100644 --- a/miniwin/src/ddraw/ddsurface.cpp +++ b/miniwin/src/ddraw/ddsurface.cpp @@ -55,6 +55,31 @@ static SDL_Rect ConvertRect(const RECT* r) return {r->left, r->top, r->right - r->left, r->bottom - r->top}; } +bool SetupHWBackBuffer() +{ + HWBackBuffer = SDL_CreateTextureFromSurface(DDRenderer, DDBackBuffer); + if (!HWBackBuffer) { + return false; + } + + SDL_PropertiesID props = SDL_GetTextureProperties(HWBackBuffer); + if (!props) { + SDL_DestroyTexture(HWBackBuffer); + HWBackBuffer = nullptr; + return false; + } + + HWBackBufferFormat = + (SDL_PixelFormat) SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, SDL_PIXELFORMAT_UNKNOWN); + if (HWBackBufferFormat == SDL_PIXELFORMAT_UNKNOWN) { + SDL_DestroyTexture(HWBackBuffer); + HWBackBuffer = nullptr; + return false; + } + + return true; +} + HRESULT DirectDrawSurfaceImpl::Blt( LPRECT lpDestRect, LPDIRECTDRAWSURFACE lpDDSrcSurface, @@ -69,6 +94,9 @@ HRESULT DirectDrawSurfaceImpl::Blt( } if (m_autoFlip) { DDBackBuffer = srcSurface->m_surface; + if (!HWBackBuffer && !SetupHWBackBuffer()) { + return DDERR_GENERIC; + } return Flip(nullptr, DDFLIP_WAIT); } @@ -126,18 +154,15 @@ HRESULT DirectDrawSurfaceImpl::BltFast( HRESULT DirectDrawSurfaceImpl::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DDFlipFlags dwFlags) { - if (!DDBackBuffer) { + if (!DDBackBuffer || !DDRenderer) { return DDERR_GENERIC; } - SDL_Surface* windowSurface = SDL_GetWindowSurface(DDWindow); - if (!windowSurface) { - return DDERR_GENERIC; - } - SDL_Rect srcRect{0, 0, DDBackBuffer->w, DDBackBuffer->h}; - SDL_Surface* copy = SDL_ConvertSurface(DDBackBuffer, windowSurface->format); - SDL_BlitSurface(copy, &srcRect, windowSurface, &srcRect); + + SDL_Surface* copy = SDL_ConvertSurface(DDBackBuffer, HWBackBufferFormat); + SDL_UpdateTexture(HWBackBuffer, nullptr, copy->pixels, copy->pitch); SDL_DestroySurface(copy); - SDL_UpdateWindowSurface(DDWindow); + SDL_RenderTexture(DDRenderer, HWBackBuffer, nullptr, nullptr); + SDL_RenderPresent(DDRenderer); return DD_OK; } @@ -147,6 +172,9 @@ HRESULT DirectDrawSurfaceImpl::GetAttachedSurface(LPDDSCAPS lpDDSCaps, LPDIRECTD return DDERR_INVALIDPARAMS; } DDBackBuffer = m_surface; + if (!SetupHWBackBuffer()) { + return DDERR_GENERIC; + } *lplpDDAttachedSurface = static_cast(this); return DD_OK; } diff --git a/miniwin/src/internal/d3drmdevice_impl.h b/miniwin/src/internal/d3drmdevice_impl.h index e96bcd43..5b902ef6 100644 --- a/miniwin/src/internal/d3drmdevice_impl.h +++ b/miniwin/src/internal/d3drmdevice_impl.h @@ -33,6 +33,7 @@ struct Direct3DRMDevice2Impl : public Direct3DRMObjectBaseImpl