mirror of
https://github.com/isledecomp/isle-portable.git
synced 2025-08-03 15:47:34 -04:00
Emscripten port (#229)
* Emscripten port * Fix NCC * Update CMakeLists.txt Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com> * Update CMakeLists.txt Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com> * Update CMakeLists.txt Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com> * Update CMakeLists.txt * cmake: set iniparser cmake options in local scope * ci: try adding emscripten to test matrix * cmake: try to make CMake install package more usable * cmake: fix typo * Update CMakeLists.txt Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com> * Add support for .ini loading * Different default full screen behavior for Emscripten * Add comments * Add web platform --------- Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com> Co-authored-by: Anonymous Maarten <anonymous.maarten@gmail.com>
This commit is contained in:
parent
2293b3fe87
commit
5080e372f9
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -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 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: '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: '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:
|
steps:
|
||||||
- name: Setup vcvars
|
- name: Setup vcvars
|
||||||
if: ${{ !!matrix.msvc }}
|
if: ${{ !!matrix.msvc }}
|
||||||
@ -74,6 +75,10 @@ jobs:
|
|||||||
brew install cmake ninja llvm qt6
|
brew install cmake ninja llvm qt6
|
||||||
echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV
|
echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup Emscripten
|
||||||
|
uses: mymindstorm/setup-emsdk@master
|
||||||
|
if: ${{ matrix.emsdk }}
|
||||||
|
|
||||||
- name: Setup ninja
|
- name: Setup ninja
|
||||||
if: ${{ matrix.msvc }}
|
if: ${{ matrix.msvc }}
|
||||||
uses: ashutoshvarma/setup-ninja@master
|
uses: ashutoshvarma/setup-ninja@master
|
||||||
@ -82,7 +87,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Configure (CMake)
|
- name: Configure (CMake)
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -GNinja \
|
${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \
|
||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
|
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
|
||||||
-DISLE_USE_DX5=${{ !!matrix.dx5 }} \
|
-DISLE_USE_DX5=${{ !!matrix.dx5 }} \
|
||||||
-DISLE_BUILD_CONFIG=${{ matrix.config }} \
|
-DISLE_BUILD_CONFIG=${{ matrix.config }} \
|
||||||
|
@ -2,6 +2,12 @@ cmake_minimum_required(VERSION 3.25...4.0 FATAL_ERROR)
|
|||||||
|
|
||||||
project(isle LANGUAGES CXX C VERSION 0.1)
|
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")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
|
|
||||||
include(CheckCXXSourceCompiles)
|
include(CheckCXXSourceCompiles)
|
||||||
@ -34,6 +40,8 @@ option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON)
|
|||||||
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
|
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
|
||||||
option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON)
|
option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll")
|
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 "Isle app: ${ISLE_BUILD_APP}")
|
||||||
message(STATUS "Config app: ${ISLE_BUILD_CONFIG}")
|
message(STATUS "Config app: ${ISLE_BUILD_CONFIG}")
|
||||||
@ -59,9 +67,11 @@ if (DOWNLOAD_DEPENDENCIES)
|
|||||||
GIT_TAG "main"
|
GIT_TAG "main"
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
)
|
)
|
||||||
set(BUILD_DOCS off)
|
block()
|
||||||
set(BUILD_SHARED_LIBS off)
|
set(BUILD_DOCS off)
|
||||||
FetchContent_MakeAvailable(iniparser)
|
set(BUILD_SHARED_LIBS off)
|
||||||
|
FetchContent_MakeAvailable(iniparser)
|
||||||
|
endblock()
|
||||||
else()
|
else()
|
||||||
# find_package looks for already-installed system packages.
|
# find_package looks for already-installed system packages.
|
||||||
# Configure with `-DCMAKE_PREFIX_PATH="/path/to/package1;/path/to/package2"`
|
# 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)
|
add_library(Vec::Vec INTERFACE IMPORTED)
|
||||||
target_include_directories(Vec::Vec INTERFACE "${CMAKE_SOURCE_DIR}/3rdparty/vec")
|
target_include_directories(Vec::Vec INTERFACE "${CMAKE_SOURCE_DIR}/3rdparty/vec")
|
||||||
|
|
||||||
add_library(lego1 SHARED
|
add_library(lego1
|
||||||
LEGO1/main.cpp
|
LEGO1/main.cpp
|
||||||
)
|
)
|
||||||
target_precompile_headers(lego1 PRIVATE "LEGO1/lego1_pch.h")
|
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})
|
target_include_directories(isle PRIVATE ${valgrind_INCLUDE_PATH})
|
||||||
endif()
|
endif()
|
||||||
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()
|
endif()
|
||||||
|
|
||||||
if (ISLE_BUILD_CONFIG)
|
if (ISLE_BUILD_CONFIG)
|
||||||
@ -602,10 +621,18 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "Platform name of the package")
|
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}"
|
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||||
)
|
)
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
install(FILES "$<TARGET_FILE_DIR:isle>/isle.js" "$<TARGET_FILE_DIR:isle>/isle.wasm"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CPACK_PACKAGE_DIRECTORY "dist")
|
set(CPACK_PACKAGE_DIRECTORY "dist")
|
||||||
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
38
ISLE/emscripten/events.cpp
Normal file
38
ISLE/emscripten/events.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "events.h"
|
||||||
|
|
||||||
|
#include "mxdsaction.h"
|
||||||
|
|
||||||
|
#include <emscripten.h>
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
8
ISLE/emscripten/events.h
Normal file
8
ISLE/emscripten/events.h
Normal file
@ -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
|
127
ISLE/emscripten/filesystem.cpp
Normal file
127
ISLE/emscripten/filesystem.cpp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
#include "legogamestate.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "mxomni.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL_filesystem.h>
|
||||||
|
#include <SDL3/SDL_log.h>
|
||||||
|
#include <emscripten/wasmfs.h>
|
||||||
|
|
||||||
|
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 = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
ISLE/emscripten/filesystem.h
Normal file
16
ISLE/emscripten/filesystem.h
Normal file
@ -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
|
14
ISLE/emscripten/messagebox.cpp
Normal file
14
ISLE/emscripten/messagebox.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include "messagebox.h"
|
||||||
|
|
||||||
|
#include <emscripten.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
13
ISLE/emscripten/messagebox.h
Normal file
13
ISLE/emscripten/messagebox.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef EMSCRIPTEN_MESSAGE_BOX_H
|
||||||
|
#define EMSCRIPTEN_MESSAGE_BOX_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL_messagebox.h>
|
||||||
|
|
||||||
|
bool Emscripten_ShowSimpleMessageBox(
|
||||||
|
SDL_MessageBoxFlags flags,
|
||||||
|
const char* title,
|
||||||
|
const char* message,
|
||||||
|
SDL_Window* window
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // EMSCRIPTEN_MESSAGE_BOX_H
|
159
ISLE/isleapp.cpp
159
ISLE/isleapp.cpp
@ -44,6 +44,12 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#include "emscripten/events.h"
|
||||||
|
#include "emscripten/filesystem.h"
|
||||||
|
#include "emscripten/messagebox.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
|
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
|
||||||
|
|
||||||
// GLOBAL: ISLE 0x410030
|
// GLOBAL: ISLE 0x410030
|
||||||
@ -88,7 +94,11 @@ IsleApp::IsleApp()
|
|||||||
m_cdPath = NULL;
|
m_cdPath = NULL;
|
||||||
m_deviceId = NULL;
|
m_deviceId = NULL;
|
||||||
m_savePath = NULL;
|
m_savePath = NULL;
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
m_fullScreen = FALSE;
|
||||||
|
#else
|
||||||
m_fullScreen = TRUE;
|
m_fullScreen = TRUE;
|
||||||
|
#endif
|
||||||
m_flipSurfaces = FALSE;
|
m_flipSurfaces = FALSE;
|
||||||
m_backBuffersInVram = TRUE;
|
m_backBuffersInVram = TRUE;
|
||||||
m_using8bit = FALSE;
|
m_using8bit = FALSE;
|
||||||
@ -100,7 +110,7 @@ IsleApp::IsleApp()
|
|||||||
m_useJoystick = FALSE;
|
m_useJoystick = FALSE;
|
||||||
m_joystickIndex = 0;
|
m_joystickIndex = 0;
|
||||||
m_wideViewAngle = TRUE;
|
m_wideViewAngle = TRUE;
|
||||||
m_islandQuality = 1;
|
m_islandQuality = 2;
|
||||||
m_islandTexture = 1;
|
m_islandTexture = 1;
|
||||||
m_gameStarted = FALSE;
|
m_gameStarted = FALSE;
|
||||||
m_frameDelta = 10;
|
m_frameDelta = 10;
|
||||||
@ -246,6 +256,9 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
|
|||||||
{
|
{
|
||||||
*appstate = NULL;
|
*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)) {
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)) {
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
SDL_snprintf(
|
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",
|
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.\nSDL error: %s",
|
||||||
SDL_GetError()
|
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;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +279,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
|
|||||||
g_isle = new IsleApp();
|
g_isle = new IsleApp();
|
||||||
|
|
||||||
if (g_isle->ParseArguments(argc, argv) != SUCCESS) {
|
if (g_isle->ParseArguments(argc, argv) != SUCCESS) {
|
||||||
SDL_ShowSimpleMessageBox(
|
Any_ShowSimpleMessageBox(
|
||||||
SDL_MESSAGEBOX_ERROR,
|
SDL_MESSAGEBOX_ERROR,
|
||||||
"LEGO® Island Error",
|
"LEGO® Island Error",
|
||||||
"\"LEGO® Island\" failed to start. Invalid CLI arguments.",
|
"\"LEGO® Island\" failed to start. Invalid CLI arguments.",
|
||||||
@ -277,7 +290,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
|
|||||||
|
|
||||||
// Create window
|
// Create window
|
||||||
if (g_isle->SetupWindow() != SUCCESS) {
|
if (g_isle->SetupWindow() != SUCCESS) {
|
||||||
SDL_ShowSimpleMessageBox(
|
Any_ShowSimpleMessageBox(
|
||||||
SDL_MESSAGEBOX_ERROR,
|
SDL_MESSAGEBOX_ERROR,
|
||||||
"LEGO® Island Error",
|
"LEGO® Island Error",
|
||||||
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again.",
|
"\"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
|
// Get reference to window
|
||||||
*appstate = g_isle->GetWindowHandle();
|
*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;
|
return SDL_APP_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +325,7 @@ SDL_AppResult SDL_AppIterate(void* appstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!g_isle->Tick()) {
|
if (!g_isle->Tick()) {
|
||||||
SDL_ShowSimpleMessageBox(
|
Any_ShowSimpleMessageBox(
|
||||||
SDL_MESSAGEBOX_ERROR,
|
SDL_MESSAGEBOX_ERROR,
|
||||||
"LEGO® Island Error",
|
"LEGO® Island Error",
|
||||||
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
|
"\"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_mousedown && g_mousemoved && g_isle) {
|
||||||
if (!g_isle->Tick()) {
|
if (!g_isle->Tick()) {
|
||||||
SDL_ShowSimpleMessageBox(
|
Any_ShowSimpleMessageBox(
|
||||||
SDL_MESSAGEBOX_ERROR,
|
SDL_MESSAGEBOX_ERROR,
|
||||||
"LEGO® Island Error",
|
"LEGO® Island Error",
|
||||||
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
|
"\"LEGO® Island\" failed to start.\nPlease quit all other applications and try again."
|
||||||
@ -357,6 +384,13 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|||||||
// Remaining functionality to be implemented:
|
// Remaining functionality to be implemented:
|
||||||
// WM_TIMER - use SDL_Timer functionality instead
|
// 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) {
|
switch (event->type) {
|
||||||
case SDL_EVENT_MOUSE_MOTION:
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
@ -376,9 +410,12 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||||
if (!IsleDebug_Enabled()) {
|
if (!IsleDebug_Enabled() && g_isle->GetGameStarted()) {
|
||||||
g_isle->SetWindowActive(FALSE);
|
g_isle->SetWindowActive(FALSE);
|
||||||
Lego()->Pause();
|
Lego()->Pause();
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
GameState()->Save(0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||||
@ -400,6 +437,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_EVENT_MOUSE_MOTION:
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
if (detectedTouchEvents) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
g_mousemoved = TRUE;
|
g_mousemoved = TRUE;
|
||||||
|
|
||||||
if (InputManager()) {
|
if (InputManager()) {
|
||||||
@ -416,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));
|
VideoManager()->MoveCursor(Min((MxS32) event->motion.x, 639), Min((MxS32) event->motion.y, 479));
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
if (detectedTouchEvents) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
g_mousedown = TRUE;
|
g_mousedown = TRUE;
|
||||||
|
|
||||||
if (InputManager()) {
|
if (InputManager()) {
|
||||||
@ -429,7 +494,32 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
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;
|
g_mousedown = FALSE;
|
||||||
|
|
||||||
if (InputManager()) {
|
if (InputManager()) {
|
||||||
@ -442,6 +532,20 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case SDL_EVENT_QUIT:
|
||||||
return SDL_APP_SUCCESS;
|
return SDL_APP_SUCCESS;
|
||||||
break;
|
break;
|
||||||
@ -462,6 +566,23 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (event->user.type == g_legoSdlEvents.m_presenterProgress) {
|
||||||
|
MxPresenter* presenter = static_cast<MxPresenter*>(event->user.data1);
|
||||||
|
MxDSAction* action = presenter->GetAction();
|
||||||
|
MxPresenter::TickleState state = static_cast<MxPresenter::TickleState>(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;
|
return SDL_APP_CONTINUE;
|
||||||
}
|
}
|
||||||
@ -565,6 +686,11 @@ MxResult IsleApp::SetupWindow()
|
|||||||
}
|
}
|
||||||
|
|
||||||
GameState()->SetSavePath(m_savePath);
|
GameState()->SetSavePath(m_savePath);
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Emscripten_SetupFilesystem();
|
||||||
|
#endif
|
||||||
|
|
||||||
GameState()->SerializePlayersInfo(LegoStorage::c_read);
|
GameState()->SerializePlayersInfo(LegoStorage::c_read);
|
||||||
GameState()->SerializeScoreHistory(LegoStorage::c_read);
|
GameState()->SerializeScoreHistory(LegoStorage::c_read);
|
||||||
|
|
||||||
@ -631,6 +757,10 @@ bool IsleApp::LoadConfig()
|
|||||||
}
|
}
|
||||||
SDL_Log("Reading configuration from \"%s\"", iniConfig);
|
SDL_Log("Reading configuration from \"%s\"", iniConfig);
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Emscripten_SetupConfig(iniConfig);
|
||||||
|
#endif
|
||||||
|
|
||||||
dictionary* dict = iniparser_load(iniConfig);
|
dictionary* dict = iniparser_load(iniConfig);
|
||||||
|
|
||||||
// [library:config]
|
// [library:config]
|
||||||
@ -687,12 +817,20 @@ bool IsleApp::LoadConfig()
|
|||||||
fclose(iniFP);
|
fclose(iniFP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
const char* hdPath = Emscripten_bundledPath;
|
||||||
|
#else
|
||||||
const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath());
|
const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath());
|
||||||
|
#endif
|
||||||
m_hdPath = new char[strlen(hdPath) + 1];
|
m_hdPath = new char[strlen(hdPath) + 1];
|
||||||
strcpy(m_hdPath, hdPath);
|
strcpy(m_hdPath, hdPath);
|
||||||
MxOmni::SetHD(m_hdPath);
|
MxOmni::SetHD(m_hdPath);
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
const char* cdPath = Emscripten_streamPath;
|
||||||
|
#else
|
||||||
const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD());
|
const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD());
|
||||||
|
#endif
|
||||||
m_cdPath = new char[strlen(cdPath) + 1];
|
m_cdPath = new char[strlen(cdPath) + 1];
|
||||||
strcpy(m_cdPath, cdPath);
|
strcpy(m_cdPath, cdPath);
|
||||||
MxOmni::SetCD(m_cdPath);
|
MxOmni::SetCD(m_cdPath);
|
||||||
@ -739,7 +877,11 @@ bool IsleApp::LoadConfig()
|
|||||||
// [library:config]
|
// [library:config]
|
||||||
// The original game does not save any data if no savepath is given.
|
// 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.
|
// 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);
|
const char* savePath = iniparser_getstring(dict, "isle:savepath", prefPath);
|
||||||
|
#endif
|
||||||
m_savePath = new char[strlen(savePath) + 1];
|
m_savePath = new char[strlen(savePath) + 1];
|
||||||
strcpy(m_savePath, savePath);
|
strcpy(m_savePath, savePath);
|
||||||
|
|
||||||
@ -838,7 +980,6 @@ inline bool IsleApp::Tick()
|
|||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open ISLE.si: Failed to start initial action");
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open ISLE.si: Failed to start initial action");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_gameStarted = TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -881,7 +1022,6 @@ void IsleApp::SetupCursor(Cursor p_cursor)
|
|||||||
|
|
||||||
MxResult IsleApp::ParseArguments(int argc, char** argv)
|
MxResult IsleApp::ParseArguments(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
|
||||||
for (int i = 1, consumed; i < argc; i += consumed) {
|
for (int i = 1, consumed; i < argc; i += consumed) {
|
||||||
consumed = -1;
|
consumed = -1;
|
||||||
|
|
||||||
@ -902,6 +1042,7 @@ MxResult IsleApp::ParseArguments(int argc, char** argv)
|
|||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +49,10 @@ public:
|
|||||||
SDL_Cursor* GetCursorBusy() { return m_cursorBusy; }
|
SDL_Cursor* GetCursorBusy() { return m_cursorBusy; }
|
||||||
SDL_Cursor* GetCursorNo() { return m_cursorNo; }
|
SDL_Cursor* GetCursorNo() { return m_cursorNo; }
|
||||||
MxS32 GetDrawCursor() { return m_drawCursor; }
|
MxS32 GetDrawCursor() { return m_drawCursor; }
|
||||||
|
MxS32 GetGameStarted() { return m_gameStarted; }
|
||||||
|
|
||||||
void SetWindowActive(MxS32 p_windowActive) { m_windowActive = p_windowActive; }
|
void SetWindowActive(MxS32 p_windowActive) { m_windowActive = p_windowActive; }
|
||||||
|
void SetGameStarted(MxS32 p_gameStarted) { m_gameStarted = p_gameStarted; }
|
||||||
|
|
||||||
MxResult ParseArguments(int argc, char** argv);
|
MxResult ParseArguments(int argc, char** argv);
|
||||||
|
|
||||||
|
@ -224,6 +224,8 @@ public:
|
|||||||
void FindLoadedAct();
|
void FindLoadedAct();
|
||||||
void RegisterState(LegoState* p_state);
|
void RegisterState(LegoState* p_state);
|
||||||
|
|
||||||
|
const char* GetSavePath() { return m_savePath; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MxResult WriteVariable(LegoStorage* p_storage, MxVariableTable* p_from, const char* p_variableName);
|
MxResult WriteVariable(LegoStorage* p_storage, MxVariableTable* p_from, const char* p_variableName);
|
||||||
MxResult WriteEndOfVariables(LegoStorage* p_storage);
|
MxResult WriteEndOfVariables(LegoStorage* p_storage);
|
||||||
|
@ -143,6 +143,7 @@ public:
|
|||||||
MxBool FUN_1005cdf0(LegoEventNotificationParam& p_param);
|
MxBool FUN_1005cdf0(LegoEventNotificationParam& p_param);
|
||||||
void GetKeyboardState();
|
void GetKeyboardState();
|
||||||
MxResult GetNavigationKeyStates(MxU32& p_keyFlags);
|
MxResult GetNavigationKeyStates(MxU32& p_keyFlags);
|
||||||
|
MxResult GetNavigationTouchStates(MxU32& p_keyFlags);
|
||||||
|
|
||||||
// SYNTHETIC: LEGO1 0x1005b8d0
|
// SYNTHETIC: LEGO1 0x1005b8d0
|
||||||
// LegoInputManager::`scalar deleting destructor'
|
// LegoInputManager::`scalar deleting destructor'
|
||||||
|
@ -4,10 +4,8 @@
|
|||||||
#include "actionsfwd.h"
|
#include "actionsfwd.h"
|
||||||
#include "decomp.h"
|
#include "decomp.h"
|
||||||
#include "extra.h"
|
#include "extra.h"
|
||||||
#include "lego1_export.h"
|
|
||||||
#include "mxtypes.h"
|
#include "mxtypes.h"
|
||||||
|
|
||||||
#include <SDL3/SDL_stdinc.h>
|
|
||||||
#ifdef MINIWIN
|
#ifdef MINIWIN
|
||||||
#include "miniwin/windows.h"
|
#include "miniwin/windows.h"
|
||||||
#else
|
#else
|
||||||
@ -19,12 +17,6 @@
|
|||||||
// name verified by BETA10 0x100d4054
|
// name verified by BETA10 0x100d4054
|
||||||
#define DS_NOT_A_STREAM -1
|
#define DS_NOT_A_STREAM -1
|
||||||
|
|
||||||
struct LegoSdlEvents {
|
|
||||||
Uint32 m_windowsMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
LEGO1_EXPORT extern LegoSdlEvents g_legoSdlEvents;
|
|
||||||
|
|
||||||
enum Cursor {
|
enum Cursor {
|
||||||
e_cursorArrow = 0,
|
e_cursorArrow = 0,
|
||||||
e_cursorBusy,
|
e_cursorBusy,
|
||||||
@ -70,7 +62,6 @@ void PlayCamAnim(LegoPathActor* p_actor, MxBool p_unused, MxU32 p_location, MxBo
|
|||||||
void FUN_1003eda0();
|
void FUN_1003eda0();
|
||||||
MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id);
|
MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id);
|
||||||
void EnableAnimations(MxBool p_enable);
|
void EnableAnimations(MxBool p_enable);
|
||||||
void InitSdlEvents();
|
|
||||||
void SetAppCursor(Cursor p_cursor);
|
void SetAppCursor(Cursor p_cursor);
|
||||||
MxBool FUN_1003ef60();
|
MxBool FUN_1003ef60();
|
||||||
MxBool RemoveFromWorld(MxAtomId& p_entityAtom, MxS32 p_entityId, MxAtomId& p_worldAtom, MxS32 p_worldEntityId);
|
MxBool RemoveFromWorld(MxAtomId& p_entityAtom, MxS32 p_entityId, MxAtomId& p_worldAtom, MxS32 p_worldEntityId);
|
||||||
|
@ -37,8 +37,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vec.h>
|
#include <vec.h>
|
||||||
|
|
||||||
LegoSdlEvents g_legoSdlEvents;
|
|
||||||
|
|
||||||
// FUNCTION: LEGO1 0x1003dd70
|
// FUNCTION: LEGO1 0x1003dd70
|
||||||
// FUNCTION: BETA10 0x100d3410
|
// FUNCTION: BETA10 0x100d3410
|
||||||
LegoROI* PickROI(MxLong p_x, MxLong p_y)
|
LegoROI* PickROI(MxLong p_x, MxLong p_y)
|
||||||
@ -568,17 +566,6 @@ void EnableAnimations(MxBool p_enable)
|
|||||||
AnimationManager()->FUN_100604d0(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
|
// FUNCTION: LEGO1 0x1003ef40
|
||||||
void SetAppCursor(Cursor p_cursor)
|
void SetAppCursor(Cursor p_cursor)
|
||||||
{
|
{
|
||||||
|
@ -136,6 +136,8 @@ MxResult LegoInputManager::GetNavigationKeyStates(MxU32& p_keyFlags)
|
|||||||
keyFlags |= c_ctrl;
|
keyFlags |= c_ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetNavigationTouchStates(keyFlags);
|
||||||
|
|
||||||
p_keyFlags = keyFlags;
|
p_keyFlags = keyFlags;
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
@ -542,3 +544,45 @@ void LegoInputManager::EnableInputProcessing()
|
|||||||
g_unk0x100f31b0 = -1;
|
g_unk0x100f31b0 = -1;
|
||||||
g_unk0x100f31b4 = NULL;
|
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;
|
||||||
|
}
|
||||||
|
@ -281,8 +281,6 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p_param)
|
|||||||
SetAppCursor(e_cursorBusy);
|
SetAppCursor(e_cursorBusy);
|
||||||
m_gameState->SetCurrentAct(LegoGameState::e_act1);
|
m_gameState->SetCurrentAct(LegoGameState::e_act1);
|
||||||
|
|
||||||
InitSdlEvents();
|
|
||||||
|
|
||||||
result = SUCCESS;
|
result = SUCCESS;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
@ -204,7 +204,7 @@ int LegoDeviceEnumerate::GetBestDevice()
|
|||||||
// FUNCTION: BETA10 0x1011cf54
|
// FUNCTION: BETA10 0x1011cf54
|
||||||
bool LegoDeviceEnumerate::SupportsSIMD()
|
bool LegoDeviceEnumerate::SupportsSIMD()
|
||||||
{
|
{
|
||||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64)
|
#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__EMSCRIPTEN__)
|
||||||
// All x86_64 and 64-bit ARM CPUs support at least SSE2 or NEON
|
// All x86_64 and 64-bit ARM CPUs support at least SSE2 or NEON
|
||||||
return true;
|
return true;
|
||||||
#elif defined(__i386__) || defined(_M_IX86)
|
#elif defined(__i386__) || defined(_M_IX86)
|
||||||
|
@ -43,8 +43,8 @@ public:
|
|||||||
LEGO1_EXPORT static void SetCD(const char* p_cd);
|
LEGO1_EXPORT static void SetCD(const char* p_cd);
|
||||||
LEGO1_EXPORT static void SetHD(const char* p_hd);
|
LEGO1_EXPORT static void SetHD(const char* p_hd);
|
||||||
LEGO1_EXPORT static void SetSound3D(MxBool p_use3dSound);
|
LEGO1_EXPORT static void SetSound3D(MxBool p_use3dSound);
|
||||||
static const vector<MxString>& GetHDFiles() { return g_hdFiles; }
|
static vector<MxString>& GetHDFiles() { return g_hdFiles; }
|
||||||
static const vector<MxString>& GetCDFiles() { return g_cdFiles; }
|
static vector<MxString>& GetCDFiles() { return g_cdFiles; }
|
||||||
|
|
||||||
MxOmni();
|
MxOmni();
|
||||||
~MxOmni() override;
|
~MxOmni() override;
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#include "mxcore.h"
|
#include "mxcore.h"
|
||||||
#include "mxcriticalsection.h"
|
#include "mxcriticalsection.h"
|
||||||
#include "mxgeometry.h"
|
#include "mxgeometry.h"
|
||||||
|
#include "mxutilities.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
|
||||||
class MxCompositePresenter;
|
class MxCompositePresenter;
|
||||||
class MxDSAction;
|
class MxDSAction;
|
||||||
@ -62,6 +65,12 @@ protected:
|
|||||||
{
|
{
|
||||||
m_previousTickleStates |= 1 << (MxU8) m_currentTickleState;
|
m_previousTickleStates |= 1 << (MxU8) m_currentTickleState;
|
||||||
m_currentTickleState = p_tickleState;
|
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:
|
public:
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
#ifndef MXUTILITIES_H
|
#ifndef MXUTILITIES_H
|
||||||
#define MXUTILITIES_H
|
#define MXUTILITIES_H
|
||||||
|
|
||||||
|
#include "lego1_export.h"
|
||||||
#include "mxtypes.h"
|
#include "mxtypes.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL_stdinc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
struct LegoSdlEvents {
|
||||||
|
Uint32 m_windowsMessage;
|
||||||
|
Uint32 m_presenterProgress;
|
||||||
|
};
|
||||||
|
|
||||||
|
LEGO1_EXPORT extern LegoSdlEvents g_legoSdlEvents;
|
||||||
|
|
||||||
class MxDSFile;
|
class MxDSFile;
|
||||||
class MxDSObject;
|
class MxDSObject;
|
||||||
class MxDSAction;
|
class MxDSAction;
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include <SDL3/SDL_stdinc.h>
|
#include <SDL3/SDL_stdinc.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
LegoSdlEvents g_legoSdlEvents;
|
||||||
|
|
||||||
// GLOBAL: LEGO1 0x101020e8
|
// GLOBAL: LEGO1 0x101020e8
|
||||||
void (*g_omniUserMessage)(const char*, MxS32) = NULL;
|
void (*g_omniUserMessage)(const char*, MxS32) = NULL;
|
||||||
|
|
||||||
|
@ -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;
|
result = SUCCESS;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
@ -15,6 +15,7 @@ Please note: this project is dedicated to achieving platform independence withou
|
|||||||
| Windows | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
| Windows | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||||
| Linux | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
| Linux | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||||
| macOS | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
| macOS | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||||
|
| [Web](https://isle.pizza) | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||||
|
|
||||||
### Library substitutions
|
### Library substitutions
|
||||||
|
|
||||||
|
@ -283,6 +283,7 @@ HRESULT DirectDrawImpl::RestoreDisplayMode()
|
|||||||
HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags)
|
HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags)
|
||||||
{
|
{
|
||||||
SDL_Window* sdlWindow = reinterpret_cast<SDL_Window*>(hWnd);
|
SDL_Window* sdlWindow = reinterpret_cast<SDL_Window*>(hWnd);
|
||||||
|
|
||||||
if (sdlWindow) {
|
if (sdlWindow) {
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
if ((dwFlags & DDSCL_NORMAL) == DDSCL_NORMAL) {
|
if ((dwFlags & DDSCL_NORMAL) == DDSCL_NORMAL) {
|
||||||
@ -296,7 +297,9 @@ HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!SDL_SetWindowFullscreen(sdlWindow, fullscreen)) {
|
if (!SDL_SetWindowFullscreen(sdlWindow, fullscreen)) {
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
return DDERR_GENERIC;
|
return DDERR_GENERIC;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
DDWindow = sdlWindow;
|
DDWindow = sdlWindow;
|
||||||
DDRenderer = SDL_CreateRenderer(DDWindow, NULL);
|
DDRenderer = SDL_CreateRenderer(DDWindow, NULL);
|
||||||
|
@ -17,6 +17,14 @@
|
|||||||
#define DDBitDepths DWORD
|
#define DDBitDepths DWORD
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// SDL will not put the message box on the main thread by default.
|
||||||
|
// See: https://github.com/libsdl-org/SDL/issues/12943
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#define Any_ShowSimpleMessageBox Emscripten_ShowSimpleMessageBox
|
||||||
|
#else
|
||||||
|
#define Any_ShowSimpleMessageBox SDL_ShowSimpleMessageBox
|
||||||
|
#endif
|
||||||
|
|
||||||
// Disable "identifier was truncated to '255' characters" warning.
|
// Disable "identifier was truncated to '255' characters" warning.
|
||||||
// Impossible to avoid this if using STL map or set.
|
// Impossible to avoid this if using STL map or set.
|
||||||
// This removes most (but not all) occurrences of the warning.
|
// This removes most (but not all) occurrences of the warning.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user