mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-22 02:11:57 -05:00
Fix emscripten support
This commit is contained in:
parent
752f169acf
commit
63b0106de8
28 changed files with 769 additions and 25 deletions
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -310,3 +310,17 @@ discord-rpc
|
|||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Emscripten
|
||||
src/thirdparty/nlohmann/
|
||||
emscripten/ext/
|
||||
emscripten/www/
|
||||
emscripten/temp/
|
||||
emscripten/Makefile
|
||||
emscripten/CMake*
|
||||
emscripten/cmake*
|
||||
emscripten/*.a
|
||||
emscripten/*.js
|
||||
emscripten/*.wasm
|
||||
emscripten/static/assets*
|
||||
emscripten-output/
|
||||
|
|
40
cmake/FindSpeexDSP.cmake
Normal file
40
cmake/FindSpeexDSP.cmake
Normal file
|
@ -0,0 +1,40 @@
|
|||
#
|
||||
# - Find speexdsp libraries
|
||||
#
|
||||
# SPEEXDSP_INCLUDE_DIRS - where to find speexdsp headers.
|
||||
# SPEEXDSP_LIBRARIES - List of libraries when using speexdsp.
|
||||
# SPEEXDSP_FOUND - True if speexdsp is found.
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(PC_SPEEXDSP QUIET speexdsp)
|
||||
|
||||
find_path(SPEEXDSP_INCLUDE_DIR
|
||||
NAMES
|
||||
speex/speex_resampler.h
|
||||
HINTS
|
||||
${PC_SPEEXDSP_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_library(SPEEXDSP_LIBRARY
|
||||
NAMES
|
||||
speexdsp
|
||||
HINTS
|
||||
${PC_SPEEXDSP_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SpeexDSP
|
||||
REQUIRED_VARS SPEEXDSP_LIBRARY SPEEXDSP_INCLUDE_DIR
|
||||
VERSION_VAR PC_SPEEXDSP_VERSION)
|
||||
|
||||
if(SPEEXDSP_FOUND)
|
||||
set(SPEEXDSP_LIBRARIES ${SPEEXDSP_LIBRARY})
|
||||
set(SPEEXDSP_INCLUDE_DIRS ${SPEEXDSP_INCLUDE_DIR})
|
||||
set(SPEEX_INCLUDE_DIRS ${SPEEXDSP_INCLUDE_DIR})
|
||||
else()
|
||||
set(SPEEXDSP_LIBRARIES)
|
||||
set(SPEEXDSP_INCLUDE_DIRS)
|
||||
set(SPEEX_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(SPEEXDSP_LIBRARIES SPEEXDSP_INCLUDE_DIRS SPEEX_INCLUDE_DIRS)
|
|
@ -3787,3 +3787,5 @@ STR_6709 :Enter Smooth Strength between {COMMA16} and {COMMA16}
|
|||
STR_6710 :Stable sort
|
||||
STR_6711 :Filename:
|
||||
STR_6712 :Save
|
||||
STR_6713 :Export emscripten data
|
||||
STR_6714 :Import emscripten data
|
||||
|
|
27
emscripten/Dockerfile
Normal file
27
emscripten/Dockerfile
Normal file
|
@ -0,0 +1,27 @@
|
|||
FROM docker.io/library/fedora:41 AS builder
|
||||
|
||||
RUN dnf update -y && dnf install -y git cmake make gcc g++ nlohmann-json-devel autoreconf libtool openssl-devel libcurl-devel fontconfig-devel libzip-devel SDL2-devel flac-devel libvorbis-devel zip speexdsp-devel
|
||||
|
||||
WORKDIR /
|
||||
|
||||
RUN git clone https://github.com/emscripten-core/emsdk.git
|
||||
|
||||
WORKDIR /emsdk/
|
||||
|
||||
# Pin version - to prevent sudden breakage of the CI
|
||||
RUN ./emsdk install 3.1.74
|
||||
RUN ./emsdk activate 3.1.74
|
||||
|
||||
WORKDIR /openrct2/
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN rm -rf emscripten/temp/ emscripten/www/ emscripten/ext/
|
||||
|
||||
WORKDIR /emsdk/
|
||||
|
||||
RUN . ./emsdk_env.sh && cd /openrct2/emscripten/ && ./build_emscripten.sh
|
||||
|
||||
FROM scratch AS export
|
||||
|
||||
COPY --from=builder /openrct2/emscripten/www/* .
|
162
emscripten/build_emscripten.sh
Executable file
162
emscripten/build_emscripten.sh
Executable file
|
@ -0,0 +1,162 @@
|
|||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
START_DIR=$(pwd)
|
||||
ICU_ROOT=$(pwd)/ext/icu/icu4c/source
|
||||
JSON_DIR=/usr/include/nlohmann/
|
||||
|
||||
build_ext() {
|
||||
mkdir -p ext/
|
||||
cd ext/
|
||||
# Pin versions - to prevent sudden breakage
|
||||
if [ ! -d "speexdsp" ]; then
|
||||
git clone https://gitlab.xiph.org/xiph/speexdsp.git --depth 1
|
||||
cd speexdsp
|
||||
git fetch --depth=1 origin dbd421d149a9c362ea16150694b75b63d757a521
|
||||
git checkout dbd421d149a9c362ea16150694b75b63d757a521
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "icu" ]; then
|
||||
git clone https://github.com/unicode-org/icu.git --depth 1
|
||||
cd icu
|
||||
git fetch --depth=1 origin ba012a74a11405a502b6890e710bfb58cef7a2c7
|
||||
git checkout ba012a74a11405a502b6890e710bfb58cef7a2c7
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "libzip" ]; then
|
||||
git clone https://github.com/nih-at/libzip.git --depth 1
|
||||
cd libzip
|
||||
git fetch --depth=1 origin 8352d224d458d86949fd9148dd33332f50a25c7f
|
||||
git checkout 8352d224d458d86949fd9148dd33332f50a25c7f
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "zlib" ]; then
|
||||
git clone https://github.com/madler/zlib.git --depth 1
|
||||
cd zlib
|
||||
git fetch --depth=1 origin ef24c4c7502169f016dcd2a26923dbaf3216748c
|
||||
git checkout ef24c4c7502169f016dcd2a26923dbaf3216748c
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "vorbis" ]; then
|
||||
git clone https://gitlab.xiph.org/xiph/vorbis.git --depth 1
|
||||
cd vorbis
|
||||
git fetch --depth=1 origin bb4047de4c05712bf1fd49b9584c360b8e4e0adf
|
||||
git checkout bb4047de4c05712bf1fd49b9584c360b8e4e0adf
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "ogg" ]; then
|
||||
git clone https://gitlab.xiph.org/xiph/ogg.git --depth 1
|
||||
cd ogg
|
||||
git fetch --depth=1 origin 7cf42ea17aef7bc1b7b21af70724840a96c2e7d0
|
||||
git checkout 7cf42ea17aef7bc1b7b21af70724840a96c2e7d0
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "$JSON_DIR" ]; then
|
||||
echo "$JSON_DIR does not exist. Set in build_emscripten.sh or install the nlohmann-json headers!"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf ../../src/thirdparty/nlohmann
|
||||
cp -r $JSON_DIR ../../src/thirdparty/nlohmann
|
||||
|
||||
cd speexdsp
|
||||
emmake ./autogen.sh
|
||||
emmake ./configure --enable-shared --disable-neon
|
||||
emmake make -j$(nproc)
|
||||
cd $START_DIR/ext/
|
||||
|
||||
cd icu/icu4c/source
|
||||
ac_cv_namespace_ok=yes icu_cv_host_frag=mh-linux emmake ./configure \
|
||||
--enable-release \
|
||||
--enable-shared \
|
||||
--disable-icu-config \
|
||||
--disable-extras \
|
||||
--disable-icuio \
|
||||
--disable-layoutex \
|
||||
--disable-tools \
|
||||
--disable-tests \
|
||||
--disable-samples
|
||||
emmake make -j$(nproc)
|
||||
cd $START_DIR/ext/
|
||||
|
||||
cd zlib
|
||||
emcmake cmake ./
|
||||
emmake make zlib -j$(nproc)
|
||||
emmake make install
|
||||
ZLIB_ROOT=$(pwd)
|
||||
cd $START_DIR/ext/
|
||||
|
||||
cd libzip
|
||||
mkdir -p build/
|
||||
cd build/
|
||||
emcmake cmake ../ -DZLIB_INCLUDE_DIR="$ZLIB_ROOT" -DZLIB_LIBRARY="$ZLIB_ROOT/libz.a"
|
||||
emmake make zip -j$(nproc)
|
||||
emmake make install
|
||||
cd $START_DIR/ext/
|
||||
|
||||
cd ogg
|
||||
mkdir -p build/
|
||||
cd build/
|
||||
emcmake cmake ../
|
||||
emmake make -j$(nproc)
|
||||
emmake make install
|
||||
cd $START_DIR/ext/
|
||||
|
||||
cd vorbis
|
||||
mkdir -p build/
|
||||
cd build/
|
||||
emcmake cmake ../
|
||||
emmake make -j$(nproc)
|
||||
emmake make install
|
||||
|
||||
cd $START_DIR
|
||||
}
|
||||
|
||||
build_assets() {
|
||||
mkdir temp/
|
||||
cd temp/
|
||||
cmake ../../ -DMACOS_BUNDLE=off -DDISABLE_NETWORK=on -DDISABLE_GUI=off
|
||||
make openrct2-cli -j$(nproc)
|
||||
make g2 -j$(nproc)
|
||||
DESTDIR=. make install
|
||||
mkdir -p ../static/assets/
|
||||
cp -r usr/local/share/openrct2/* ../static/assets/
|
||||
cd ../static/assets
|
||||
zip -r ../assets.zip *
|
||||
cd ../
|
||||
rm -rf assets/
|
||||
|
||||
cd $START_DIR
|
||||
}
|
||||
|
||||
if [ "$1" != "skip" ] ; then
|
||||
build_ext
|
||||
build_assets
|
||||
fi
|
||||
|
||||
emcmake cmake ../ \
|
||||
-DDISABLE_NETWORK=ON \
|
||||
-DDISABLE_HTTP=ON \
|
||||
-DDISABLE_TTF=ON \
|
||||
-DDISABLE_FLAC=ON \
|
||||
-DDISABLE_DISCORD_RPC=ON \
|
||||
-DCMAKE_SYSTEM_NAME=Emscripten \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSPEEXDSP_INCLUDE_DIR="$(pwd)/ext/speexdsp/include/" \
|
||||
-DSPEEXDSP_LIBRARY="$(pwd)/ext/speexdsp/libspeexdsp/.libs/libspeexdsp.a" \
|
||||
-DICU_INCLUDE_DIR="$ICU_ROOT/common" \
|
||||
-DICU_DATA_LIBRARIES=$ICU_ROOT/lib/libicuuc.so \
|
||||
-DICU_DT_LIBRARY_RELEASE="$ICU_ROOT/stubdata/libicudata.so" \
|
||||
-DLIBZIP_LIBRARIES="$(pwd)/ext/libzip/build/lib/libzip.a" \
|
||||
-DEMSCRIPTEN_FLAGS="-s USE_SDL=2 -s USE_BZIP2=1 -s USE_LIBPNG=1 -pthread -O3" \
|
||||
-DEMSCRIPTEN_LDFLAGS="-Wno-pthreads-mem-growth -s ASYNCIFY -s FULL_ES3 -s SAFE_HEAP=0 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s INITIAL_MEMORY=2GB -s MAX_WEBGL_VERSION=2 -s PTHREAD_POOL_SIZE=120 -pthread -sEXPORTED_RUNTIME_METHODS=FS,callMain,UTF8ToString,stringToNewUTF8 -lidbfs.js --use-preload-plugins -s MODULARIZE=1 -s 'EXPORT_NAME=\"OPENRCT2_WEB\"'"
|
||||
|
||||
emmake make -j$(nproc)
|
||||
|
||||
rm -rf www/
|
||||
mkdir -p www/
|
||||
cd www/
|
||||
cp -r ../openrct2.* ./
|
||||
cp -r ../static/* ./
|
||||
cp -r ../static/.* ./
|
||||
|
||||
echo "finished!"
|
30
emscripten/static/index.html
Normal file
30
emscripten/static/index.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenRCT2</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js" crossorigin=anonymous></script>
|
||||
<script src="openrct2.js"></script>
|
||||
<style>
|
||||
* { padding: 0; margin: 0; }
|
||||
canvas {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<script src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="loadingWebassembly">Please wait... Loading webassembly</p>
|
||||
<div id="beforeLoad" style="display:none;">
|
||||
<p id="statusMsg">Please select your RCT2 assets (zip file):</p>
|
||||
<input type="file" id="selectFile"></input>
|
||||
</div>
|
||||
<canvas id="canvas" style="display:none;"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
271
emscripten/static/index.js
Normal file
271
emscripten/static/index.js
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2025 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
// assets_version should be updated when assets need to be re-downloaded on the client
|
||||
const assets_version = "0.4.17-1";
|
||||
(async () =>
|
||||
{
|
||||
await new Promise(res => window.addEventListener("DOMContentLoaded", res));
|
||||
if (!window.SharedArrayBuffer)
|
||||
{
|
||||
document.getElementById("loadingWebassembly").innerText = "Error! SharedArrayBuffer is not defined. This page required the CORP and COEP response headers.";
|
||||
}
|
||||
|
||||
window.Module = await window.OPENRCT2_WEB(
|
||||
{
|
||||
noInitialRun: true,
|
||||
arguments: [],
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
canvas: document.getElementById("canvas"),
|
||||
print: function(msg)
|
||||
{
|
||||
console.log(msg);
|
||||
},
|
||||
printErr: function(msg)
|
||||
{
|
||||
console.log(msg);
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: () => {},
|
||||
locateFile: function(fileName)
|
||||
{
|
||||
console.log("loading", fileName);
|
||||
return fileName;
|
||||
},
|
||||
funcs: {
|
||||
export: () =>
|
||||
{
|
||||
const zip = zipFolder("/persistant/");
|
||||
zip.generateAsync({type: "blob"}).then(blob => {
|
||||
const a = document.createElement("a");
|
||||
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = "OpenRCT2-emscripten.zip";
|
||||
a.click();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
|
||||
})
|
||||
},
|
||||
import: () =>
|
||||
{
|
||||
if (!confirm("Are you sure? This will wipe all current data.")) return;
|
||||
alert("Select a zip file");
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.addEventListener("change", async (e) => {
|
||||
let zip = new JSZip();
|
||||
try {
|
||||
zip = await zip.loadAsync(e.target.files[0]);
|
||||
} catch(e) {
|
||||
alert("Not a zip file!");
|
||||
return;
|
||||
}
|
||||
await clearDatabase("/persistant/");
|
||||
for (const k in zip.files) {
|
||||
const entry = zip.files[k];
|
||||
if (entry.dir) {
|
||||
try {
|
||||
Module.FS.mkdir("/"+k);
|
||||
} catch(e) {}
|
||||
} else {
|
||||
Module.FS.writeFile("/"+k, await entry.async("uint8array"));
|
||||
}
|
||||
}
|
||||
console.log("Database restored");
|
||||
})
|
||||
input.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Module.FS.mkdir("/persistant");
|
||||
Module.FS.mount(Module.FS.filesystems.IDBFS, {autoPersist: true}, '/persistant');
|
||||
|
||||
Module.FS.mkdir("/RCT");
|
||||
Module.FS.mount(Module.FS.filesystems.IDBFS, {autoPersist: true}, '/RCT');
|
||||
|
||||
Module.FS.mkdir("/OpenRCT2");
|
||||
Module.FS.mount(Module.FS.filesystems.IDBFS, {autoPersist: true}, '/OpenRCT2');
|
||||
|
||||
await new Promise(res => Module.FS.syncfs(true, res));
|
||||
|
||||
let configExists = fileExists("/persistant/config.ini");
|
||||
if (!configExists)
|
||||
{
|
||||
Module.FS.writeFile("/persistant/config.ini", `
|
||||
[general]
|
||||
game_path = "/RCT"
|
||||
uncap_fps = true
|
||||
window_scale = 1.750000
|
||||
`);
|
||||
}
|
||||
|
||||
await updateAssets();
|
||||
|
||||
Module.FS.writeFile("/OpenRCT2/changelog.txt", `EMSCRIPTEN --- README
|
||||
|
||||
Since we're running in the web browser, we don't have direct access to the file system.
|
||||
All save data is saved under the directory /persistant.
|
||||
|
||||
ALWAYS be sure to save to /persistant/saves when saving a game! Otherwise it will be wiped!
|
||||
|
||||
You can import/export the /persistant folder in the options menu.`);
|
||||
document.getElementById("loadingWebassembly").remove();
|
||||
|
||||
let filesFound = fileExists("/RCT/Data/ch.dat");
|
||||
|
||||
if (!filesFound)
|
||||
{
|
||||
document.getElementById("beforeLoad").style.display = "";
|
||||
await new Promise(res =>
|
||||
{
|
||||
document.getElementById("selectFile").addEventListener("change", async (e) =>
|
||||
{
|
||||
if (await extractZip(e.target.files[0], (zip) =>
|
||||
{
|
||||
if (zip !== null)
|
||||
{
|
||||
if (zip.file("Data/ch.dat"))
|
||||
{
|
||||
document.getElementById("beforeLoad").remove();
|
||||
return "/RCT/";
|
||||
}
|
||||
else if (zip.file("RCT/Data/ch.dat"))
|
||||
{
|
||||
document.getElementById("beforeLoad").remove();
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
document.getElementById("statusMsg").innerText = "That doesn't look right. Your file should be a zip file containing Data/ch.dat. Please select your OpenRCT2 contents (zip file):";
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
res();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Module.canvas.style.display = "";
|
||||
Module.callMain(["--user-data-path=/persistant/", "--openrct2-data-path=/OpenRCT2/"]);
|
||||
})();
|
||||
|
||||
async function updateAssets() {
|
||||
let currentVersion = "";
|
||||
try {
|
||||
currentVersion = Module.FS.readFile("/OpenRCT2/version", {encoding: "utf8"});
|
||||
} catch(e) {};
|
||||
console.log("Found asset version", currentVersion);
|
||||
|
||||
if (currentVersion !== assets_version || assets_version === "DEV")
|
||||
{
|
||||
console.log("Updating assets to", assets_version);
|
||||
document.getElementById("loadingWebassembly").innerText = "Asset update found. Downloading...";
|
||||
await clearDatabase("/OpenRCT2/");
|
||||
await extractZip(await (await fetch("assets.zip")).blob(), () =>
|
||||
{
|
||||
return "/OpenRCT2/";
|
||||
});
|
||||
Module.FS.writeFile("/OpenRCT2/version", assets_version.toString());
|
||||
}
|
||||
}
|
||||
|
||||
async function extractZip(data, checkZip) {
|
||||
let zip = new JSZip();
|
||||
let contents;
|
||||
try {
|
||||
contents = await zip.loadAsync(data);
|
||||
} catch(e) {
|
||||
if (typeof checkZip === "function")
|
||||
{
|
||||
checkZip(null);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
let base = "/";
|
||||
if (typeof checkZip === "function")
|
||||
{
|
||||
const cont = checkZip(contents);
|
||||
if (cont === false) return false;
|
||||
base = cont;
|
||||
}
|
||||
for (const k in contents.files) {
|
||||
const entry = contents.files[k];
|
||||
if (entry.dir)
|
||||
{
|
||||
try {
|
||||
Module.FS.mkdir(base+k);
|
||||
} catch(e) {}
|
||||
}
|
||||
else
|
||||
{
|
||||
Module.FS.writeFile(base+k, await entry.async("uint8array"));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
async function clearDatabase(dir) {
|
||||
await new Promise(res => Module.FS.syncfs(false, res));
|
||||
const processFolder = (path) => {
|
||||
let contents;
|
||||
try {
|
||||
contents = Module.FS.readdir(path);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
contents.forEach((entry) => {
|
||||
if ([".", ".."].includes(entry)) return;
|
||||
try {
|
||||
Module.FS.readFile(path + entry);
|
||||
Module.FS.unlink(path + entry);
|
||||
} catch(e) {
|
||||
processFolder(path + entry + "/");
|
||||
}
|
||||
})
|
||||
if (path === dir) return;
|
||||
try {
|
||||
Module.FS.rmdir(path, {recursive: true});
|
||||
} catch(e) {
|
||||
console.log("Could not remove:", path);
|
||||
}
|
||||
}
|
||||
processFolder(dir);
|
||||
await new Promise(res => Module.FS.syncfs(false, res));
|
||||
}
|
||||
function zipFolder(folder) {
|
||||
let zip = new JSZip();
|
||||
const processFolder = (name) => {
|
||||
let contents;
|
||||
try {
|
||||
contents = Module.FS.readdir(name);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
contents.forEach((entry) => {
|
||||
if ([".", ".."].includes(entry)) return;
|
||||
try {
|
||||
Module.FS.readFile(name + entry);
|
||||
processFile(name + entry);
|
||||
} catch(e) {
|
||||
processFolder(name + entry + "/");
|
||||
}
|
||||
})
|
||||
}
|
||||
const processFile = (name) => {
|
||||
zip.file(name, Module.FS.readFile(name));
|
||||
}
|
||||
processFolder(folder);
|
||||
return zip;
|
||||
}
|
||||
function fileExists(path) {
|
||||
try {
|
||||
Module.FS.readFile(path);
|
||||
return true;
|
||||
} catch(e) {};
|
||||
return false;
|
||||
}
|
|
@ -17,5 +17,8 @@ file(GLOB_RECURSE OPENRCT2_CLI_SOURCES
|
|||
add_executable(${PROJECT_NAME} ${OPENRCT2_CLI_SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/..")
|
||||
ipo_set_target_properties(${PROJECT_NAME})
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
target_link_libraries(${PROJECT_NAME} ${ICU_DT_LIBRARY_RELEASE} ${ICU_DATA_LIBRARIES})
|
||||
endif ()
|
||||
target_link_libraries(${PROJECT_NAME} libopenrct2 Threads::Threads)
|
||||
target_link_platform_libraries(${PROJECT_NAME})
|
||||
|
|
|
@ -11,7 +11,18 @@ option(DISABLE_VORBIS "Disable OGG/VORBIS support.")
|
|||
option(DISABLE_OPENGL "Disable OpenGL support.")
|
||||
|
||||
# Third party libraries
|
||||
if (MSVC)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
set(USE_FLAGS "${EMSCRIPTEN_FLAGS}")
|
||||
set(SHARED_FLAGS "-fexceptions")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS} ${SHARED_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS} ${SHARED_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMSCRIPTEN_LDFLAGS} --bind ${SHARED_FLAGS}")
|
||||
find_package(SpeexDSP REQUIRED)
|
||||
if (NOT DISABLE_VORBIS)
|
||||
PKG_CHECK_MODULES(OGG REQUIRED IMPORTED_TARGET ogg)
|
||||
PKG_CHECK_MODULES(VORBISFILE REQUIRED IMPORTED_TARGET vorbisfile vorbisenc vorbis)
|
||||
endif ()
|
||||
elseif (MSVC)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_library(SPEEX_LDFLAGS libspeexdsp)
|
||||
if (NOT DISABLE_FLAC)
|
||||
|
@ -33,7 +44,7 @@ else ()
|
|||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT DISABLE_OPENGL)
|
||||
if (NOT DISABLE_OPENGL AND NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
# GL doesn't work nicely with macOS, while find_package doesn't work with multiarch on Ubuntu.
|
||||
if (APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
@ -61,7 +72,12 @@ SET_CHECK_CXX_FLAGS(${PROJECT_NAME})
|
|||
ipo_set_target_properties(${PROJECT_NAME})
|
||||
|
||||
# mingw builds cannot use the PkgConfig imported targets
|
||||
if (NOT MSVC AND NOT WIN32)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
target_link_libraries(${PROJECT_NAME} "libopenrct2"
|
||||
${SPEEXDSP_LIBRARIES}
|
||||
${ICU_DATA_LIBRARIES}
|
||||
${ICU_DT_LIBRARY_RELEASE})
|
||||
elseif (NOT MSVC AND NOT WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} "libopenrct2"
|
||||
PkgConfig::SDL2
|
||||
PkgConfig::SPEEX)
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/core/UTF8.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#ifdef __MACOSX__
|
||||
// macOS uses COMMAND rather than CTRL for many keyboard shortcuts
|
||||
#define KEYBOARD_PRIMARY_MODIFIER KMOD_GUI
|
||||
|
@ -170,7 +174,21 @@ void TextComposition::HandleMessage(const SDL_Event* e)
|
|||
case SDLK_c:
|
||||
if ((modifier & KEYBOARD_PRIMARY_MODIFIER) && _session.Length)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_SetClipboardText(_session.Buffer->c_str());
|
||||
#else
|
||||
MAIN_THREAD_EM_ASM(
|
||||
{
|
||||
try
|
||||
{
|
||||
navigator.clipboard.writeText(UTF8ToString($0));
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
};
|
||||
},
|
||||
_session.Buffer->c_str());
|
||||
#endif
|
||||
ContextShowError(STR_COPY_INPUT_TO_CLIPBOARD, STR_NONE, {});
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include <openrct2/platform/Platform.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Audio;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
@ -43,6 +47,12 @@ int NormalisedMain(int argc, const char** argv)
|
|||
int main(int argc, const char** argv)
|
||||
#endif
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MAIN_THREAD_EM_ASM({
|
||||
specialHTMLTargets["!canvas"] = Module.canvas;
|
||||
Module.canvas.addEventListener("contextmenu", (e) = > { e.preventDefault(); });
|
||||
});
|
||||
#endif
|
||||
std::unique_ptr<IContext> context;
|
||||
int32_t rc = EXIT_SUCCESS;
|
||||
int runGame = CommandLineRun(argv, argc);
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <stdexcept>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
enum class DIALOG_TYPE
|
||||
|
@ -129,8 +133,12 @@ namespace OpenRCT2::Ui
|
|||
|
||||
void OpenURL(const std::string& url) override
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::string cmd = String::stdFormat("xdg-open %s", url.c_str());
|
||||
Platform::Execute(cmd);
|
||||
#else
|
||||
MAIN_THREAD_EM_ASM({ window.open(UTF8ToString($0)); }, url.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) override
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
#include <openrct2/world/Location.hpp>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
#endif
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
@ -713,7 +718,25 @@ public:
|
|||
|
||||
bool SetClipboardText(const utf8* target) override
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
return (SDL_SetClipboardText(target) == 0);
|
||||
#else
|
||||
return (
|
||||
MAIN_THREAD_EM_ASM_INT(
|
||||
{
|
||||
try
|
||||
{
|
||||
navigator.clipboard.writeText(UTF8ToString($0));
|
||||
return 0;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return -1;
|
||||
};
|
||||
},
|
||||
gVersionInfoFull)
|
||||
== 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
ITitleSequencePlayer* GetTitleSequencePlayer() override
|
||||
|
@ -753,9 +776,19 @@ private:
|
|||
|
||||
void CreateWindow(const ScreenCoordsXY& windowPos)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MAIN_THREAD_EM_ASM({
|
||||
Module.canvas.width = window.innerWidth;
|
||||
Module.canvas.height = window.innerHeight;
|
||||
});
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
emscripten_get_canvas_element_size("!canvas", &width, &height);
|
||||
#else
|
||||
// Get saved window size
|
||||
int32_t width = Config::Get().general.WindowWidth;
|
||||
int32_t height = Config::Get().general.WindowHeight;
|
||||
#endif
|
||||
if (width <= 0)
|
||||
width = 640;
|
||||
if (height <= 0)
|
||||
|
|
|
@ -1115,6 +1115,10 @@ namespace OpenRCT2
|
|||
STR_DRAWING_ENGINE_TIP = 5876,
|
||||
STR_EARLY_COMPLETION_TIP = 6227,
|
||||
STR_EDIT_ASSET_PACKS_BUTTON = 6640,
|
||||
#ifdef __EMSCRIPTEN__
|
||||
STR_EXPORT_EMSCRIPTEN = 6713,
|
||||
STR_IMPORT_EMSCRIPTEN = 6714,
|
||||
#endif
|
||||
STR_EDIT_THEMES_BUTTON = 5153,
|
||||
STR_EDIT_THEMES_BUTTON_TIP = 5837,
|
||||
STR_EFFECTS_GROUP = 6256,
|
||||
|
@ -2282,4 +2286,4 @@ namespace OpenRCT2
|
|||
STR_ADJUST_SMALLER_WATER_TIP = 2380,
|
||||
STR_WATER = 2383,
|
||||
};
|
||||
}
|
||||
} // namespace OpenRCT2
|
||||
|
|
|
@ -42,7 +42,11 @@ OPENGL_PROC(PFNGLATTACHSHADERPROC, glAttachShader)
|
|||
OPENGL_PROC(PFNGLBINDBUFFERPROC, glBindBuffer)
|
||||
OPENGL_PROC(PFNGLBINDFRAGDATALOCATIONPROC, glBindFragDataLocation)
|
||||
OPENGL_PROC(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer)
|
||||
#ifndef FAKE__EMSCRIPTEN__
|
||||
OPENGL_PROC(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray)
|
||||
#else
|
||||
extern "C" void glBindVertexArray(GLuint array);
|
||||
#endif
|
||||
OPENGL_PROC(PFNGLBLITFRAMEBUFFERPROC, glBlitFramebuffer)
|
||||
OPENGL_PROC(PFNGLBUFFERDATAPROC, glBufferData)
|
||||
OPENGL_PROC(PFNGLBUFFERSUBDATAPROC, glBufferSubData)
|
||||
|
@ -55,7 +59,11 @@ OPENGL_PROC(PFNGLDELETEBUFFERSPROC, glDeleteBuffers)
|
|||
OPENGL_PROC(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers)
|
||||
OPENGL_PROC(PFNGLDELETEPROGRAMPROC, glDeleteProgram)
|
||||
OPENGL_PROC(PFNGLDELETESHADERPROC, glDeleteShader)
|
||||
#ifndef FAKE__EMSCRIPTEN__
|
||||
OPENGL_PROC(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays)
|
||||
#else
|
||||
extern "C" void glDeleteVertexArrays(GLsizei n, const GLuint* arrays);
|
||||
#endif
|
||||
OPENGL_PROC(PFNGLDETACHSHADERPROC, glDetachShader)
|
||||
OPENGL_PROC(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray)
|
||||
OPENGL_PROC(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D)
|
||||
|
@ -67,7 +75,11 @@ OPENGL_PROC(PFNGLGETPROGRAMIVPROC, glGetProgramiv)
|
|||
OPENGL_PROC(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog)
|
||||
OPENGL_PROC(PFNGLGETSHADERIVPROC, glGetShaderiv)
|
||||
OPENGL_PROC(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation)
|
||||
#ifndef FAKE__EMSCRIPTEN__
|
||||
OPENGL_PROC(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays)
|
||||
#else
|
||||
extern "C" void glGenVertexArrays(GLsizei n, GLuint* arrays);
|
||||
#endif
|
||||
OPENGL_PROC(PFNGLLINKPROGRAMPROC, glLinkProgram)
|
||||
OPENGL_PROC(PFNGLSHADERSOURCEPROC, glShaderSource)
|
||||
OPENGL_PROC(PFNGLUNIFORM1IPROC, glUniform1i)
|
||||
|
@ -82,6 +94,11 @@ OPENGL_PROC(PFNGLUNIFORM4FVPROC, glUniform4fv)
|
|||
OPENGL_PROC(PFNGLUSEPROGRAMPROC, glUseProgram)
|
||||
OPENGL_PROC(PFNGLVERTEXATTRIBIPOINTERPROC, glVertexAttribIPointer)
|
||||
OPENGL_PROC(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
|
||||
#ifndef FAKE__EMSCRIPTEN__
|
||||
OPENGL_PROC(PFNGLDRAWARRAYSINSTANCEDPROC, glDrawArraysInstanced)
|
||||
OPENGL_PROC(PFNGLVERTEXATTRIBDIVISORPROC, glVertexAttribDivisor)
|
||||
#else
|
||||
extern "C" void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
|
||||
extern "C" void glVertexAttribDivisor(GLuint index, GLuint divisor);
|
||||
#endif
|
||||
OPENGL_PROC(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate)
|
||||
|
|
|
@ -70,7 +70,9 @@ private:
|
|||
|
||||
int32_t _drawCount = 0;
|
||||
|
||||
#ifndef NO_TTF
|
||||
uint32_t _ttfGlId = 0;
|
||||
#endif
|
||||
|
||||
struct
|
||||
{
|
||||
|
|
|
@ -605,6 +605,7 @@ static void InputViewportDragContinue()
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
const CursorState* cursorState = ContextGetCursorState();
|
||||
if (cursorState->touch || Config::Get().general.InvertViewportDrag)
|
||||
{
|
||||
|
@ -614,6 +615,9 @@ static void InputViewportDragContinue()
|
|||
{
|
||||
ContextSetCursorPosition(gInputDragLast);
|
||||
}
|
||||
#else
|
||||
gInputDragLast = newDragCoords;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void InputViewportDragEnd()
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#include <openrct2/sprites.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
static constexpr int32_t WW = 400;
|
||||
|
@ -119,7 +123,22 @@ namespace OpenRCT2::Ui::Windows
|
|||
ContextOpenWindowView(WV_NEW_VERSION_INFO);
|
||||
break;
|
||||
case WIDX_COPY_BUILD_INFO:
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_SetClipboardText(gVersionInfoFull);
|
||||
#else
|
||||
MAIN_THREAD_EM_ASM(
|
||||
{
|
||||
try
|
||||
{
|
||||
navigator.clipboard.writeText(UTF8ToString($0));
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// Ignore
|
||||
};
|
||||
},
|
||||
gVersionInfoFull);
|
||||
#endif
|
||||
break;
|
||||
case WIDX_CONTRIBUTORS_BUTTON:
|
||||
ContextOpenWindowView(WV_CONTRIBUTORS);
|
||||
|
|
|
@ -46,6 +46,10 @@
|
|||
#include <openrct2/sprites.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Audio;
|
||||
|
||||
|
@ -218,6 +222,10 @@ namespace OpenRCT2::Ui::Windows
|
|||
WIDX_PATH_TO_RCT1_BUTTON,
|
||||
WIDX_PATH_TO_RCT1_CLEAR,
|
||||
WIDX_ASSET_PACKS,
|
||||
#ifdef __EMSCRIPTEN__
|
||||
WIDX_EXPORT_EMSCRIPTEN_DATA,
|
||||
WIDX_IMPORT_EMSCRIPTEN_DATA,
|
||||
#endif
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
@ -404,6 +412,10 @@ namespace OpenRCT2::Ui::Windows
|
|||
MakeWidget ({ 24, 160}, {266, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_NONE, STR_STRING_TOOLTIP ), // RCT 1 path button
|
||||
MakeWidget ({289, 160}, { 11, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_CLOSE_X, STR_PATH_TO_RCT1_CLEAR_TIP ), // RCT 1 path clear button
|
||||
MakeWidget ({150, 176}, {150, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_EDIT_ASSET_PACKS_BUTTON, STR_NONE ), // Asset packs
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MakeWidget ({150, 192}, {150, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_EXPORT_EMSCRIPTEN, STR_NONE ), // Emscripten data export
|
||||
MakeWidget ({150, 208}, {150, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_IMPORT_EMSCRIPTEN, STR_NONE ), // Emscripten data import
|
||||
#endif
|
||||
kWidgetsEnd,
|
||||
};
|
||||
|
||||
|
@ -1973,6 +1985,14 @@ namespace OpenRCT2::Ui::Windows
|
|||
case WIDX_ASSET_PACKS:
|
||||
ContextOpenWindow(WindowClass::AssetPacks);
|
||||
break;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
case WIDX_EXPORT_EMSCRIPTEN_DATA:
|
||||
MAIN_THREAD_EM_ASM({ Module.funcs.export(); });
|
||||
break;
|
||||
case WIDX_IMPORT_EMSCRIPTEN_DATA:
|
||||
MAIN_THREAD_EM_ASM({ Module.funcs.import(); });
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,19 @@ if (NOT DISABLE_GOOGLE_BENCHMARK)
|
|||
endif ()
|
||||
|
||||
# Third party libraries
|
||||
if (MSVC)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${ICU_INCLUDE_DIR})
|
||||
set(USE_FLAGS "${EMSCRIPTEN_FLAGS}")
|
||||
set(SHARED_FLAGS "-fexceptions")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS} ${SHARED_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS} ${SHARED_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMSCRIPTEN_LDFLAGS} --bind ${SHARED_FLAGS}")
|
||||
find_package(SpeexDSP REQUIRED)
|
||||
if (NOT DISABLE_VORBIS)
|
||||
PKG_CHECK_MODULES(OGG REQUIRED IMPORTED_TARGET ogg)
|
||||
PKG_CHECK_MODULES(VORBISFILE REQUIRED IMPORTED_TARGET vorbisfile vorbisenc vorbis)
|
||||
endif ()
|
||||
elseif (MSVC)
|
||||
find_package(png 1.6 REQUIRED)
|
||||
find_package(zlib REQUIRED)
|
||||
|
||||
|
@ -142,7 +154,7 @@ if (STATIC)
|
|||
${ZLIB_STATIC_LIBRARIES}
|
||||
${LIBZIP_STATIC_LIBRARIES})
|
||||
else ()
|
||||
if (NOT MSVC)
|
||||
if (NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PkgConfig::PNG
|
||||
PkgConfig::ZLIB
|
||||
|
@ -171,7 +183,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} Threads::Threads)
|
||||
|
||||
if (NOT MINGW AND NOT MSVC)
|
||||
if (NOT MINGW AND NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
if (APPLE AND NOT MACOS_USE_DEPENDENCIES)
|
||||
execute_process(COMMAND brew --prefix icu4c OUTPUT_VARIABLE HOMEBREW_PREFIX_ICU OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
# Needed for linking with non-broken icu on Apple platforms
|
||||
|
@ -249,7 +261,7 @@ if (NOT OPENRCT2_COMMIT_SHA1_SHORT STREQUAL "HEAD" AND NOT OPENRCT2_COMMIT_SHA1_
|
|||
OPENRCT2_COMMIT_SHA1_SHORT="${OPENRCT2_COMMIT_SHA1_SHORT}")
|
||||
endif()
|
||||
|
||||
if((X86 OR X86_64) AND NOT MSVC)
|
||||
if((X86 OR X86_64) AND NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/drawing/SSE41Drawing.cpp PROPERTIES COMPILE_FLAGS -msse4.1)
|
||||
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/drawing/AVX2Drawing.cpp PROPERTIES COMPILE_FLAGS -mavx2)
|
||||
endif()
|
||||
|
|
|
@ -1201,9 +1201,21 @@ namespace OpenRCT2
|
|||
{
|
||||
SwitchToStartUpScene();
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_main_loop_arg(
|
||||
[](void* vctx) {
|
||||
auto ctx = reinterpret_cast<Context*>(vctx);
|
||||
if (ctx->_finished)
|
||||
{
|
||||
emscripten_cancel_main_loop();
|
||||
}
|
||||
ctx->RunFrame();
|
||||
},
|
||||
this, 0, 1);
|
||||
#else
|
||||
_stdInOutConsole.Start();
|
||||
RunGameLoop();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ShouldDraw()
|
||||
|
@ -1229,6 +1241,7 @@ namespace OpenRCT2
|
|||
/**
|
||||
* Run the main game loop until the finished flag is set.
|
||||
*/
|
||||
#ifndef __EMSCRIPTEN__
|
||||
void RunGameLoop()
|
||||
{
|
||||
PROFILED_FUNCTION();
|
||||
|
@ -1236,22 +1249,14 @@ namespace OpenRCT2
|
|||
LOG_VERBOSE("begin openrct2 loop");
|
||||
_finished = false;
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
_variableFrame = ShouldRunVariableFrame();
|
||||
do
|
||||
{
|
||||
RunFrame();
|
||||
} while (!_finished);
|
||||
#else
|
||||
emscripten_set_main_loop_arg(
|
||||
[](void* vctx) -> {
|
||||
auto ctx = reinterpret_cast<Context*>(vctx);
|
||||
ctx->RunFrame();
|
||||
},
|
||||
this, 0, 1);
|
||||
#endif // __EMSCRIPTEN__
|
||||
LOG_VERBOSE("finish openrct2 loop");
|
||||
}
|
||||
#endif // __EMSCRIPTEN__
|
||||
|
||||
void RunFrame()
|
||||
{
|
||||
|
|
|
@ -35,8 +35,10 @@
|
|||
#elif defined(__riscv)
|
||||
#define OPENRCT2_ARCHITECTURE "RISC-V"
|
||||
#endif
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define OPENRCT2_ARCHITECTURE "Emscripten"
|
||||
#ifdef __wasm32__
|
||||
#define OPENRCT2_ARCHITECTURE "wasm32"
|
||||
#elif defined(__wasm64__)
|
||||
#define OPENRCT2_ARCHITECTURE "wasm64"
|
||||
#endif
|
||||
|
||||
#ifndef OPENRCT2_ARCHITECTURE
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
namespace OpenRCT2::Imaging
|
||||
{
|
||||
constexpr auto EXCEPTION_IMAGE_FORMAT_UNKNOWN = "Unknown image format.";
|
||||
|
@ -334,8 +340,27 @@ namespace OpenRCT2::Imaging
|
|||
break;
|
||||
case IMAGE_FORMAT::PNG:
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::ofstream fs(fs::u8path(path), std::ios::binary);
|
||||
WritePng(fs, image);
|
||||
#else
|
||||
std::ostringstream stream(std::ios::binary);
|
||||
WritePng(stream, image);
|
||||
std::string dataStr = stream.str();
|
||||
void* data = reinterpret_cast<void*>(dataStr.data());
|
||||
MAIN_THREAD_EM_ASM(
|
||||
{
|
||||
const a = document.createElement("a");
|
||||
// Blob requires the data must not be shared
|
||||
const data = new Uint8Array(HEAPU8.subarray($0, $0 + $1));
|
||||
a.href = URL.createObjectURL(new Blob([data]));
|
||||
a.download = UTF8ToString($2).split("/").pop();
|
||||
a.click();
|
||||
setTimeout(function(){ URL.revokeObjectURL(a.href) }, 1000);
|
||||
},
|
||||
data, dataStr.size(), std::string(path).c_str());
|
||||
free(data);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -24,7 +24,7 @@ using money64 = fixed64_1dp;
|
|||
// really tries to use a gigantic constant that can't fit in a double, they are
|
||||
// probably going to be breaking other things anyways.
|
||||
// For more details, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=26374
|
||||
constexpr money64 operator"" _GBP(long double money) noexcept
|
||||
constexpr money64 operator""_GBP(long double money) noexcept
|
||||
{
|
||||
return static_cast<double>(money) * 10;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <cstdint>
|
||||
|
||||
// Note: Only valid for 5 decimal places.
|
||||
constexpr int32_t operator"" _mph(long double speedMph)
|
||||
constexpr int32_t operator""_mph(long double speedMph)
|
||||
{
|
||||
uint32_t wholeNumber = speedMph;
|
||||
uint64_t fraction = (speedMph - wholeNumber) * 100000;
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace OpenRCT2::Platform
|
|||
{
|
||||
LOG_FATAL("failed to get process path");
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
#elif defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
|
||||
// There is no way to get the path name of a running executable.
|
||||
// If you are not using the port or package, you may have to change this line!
|
||||
strlcpy(exePath, "/usr/local/bin/", sizeof(exePath));
|
||||
|
|
|
@ -161,7 +161,7 @@ namespace OpenRCT2::Platform
|
|||
// Return exit code
|
||||
return pclose(fpipe);
|
||||
#else
|
||||
LOG_WARNING("Emscripten cannot execute processes. The commandline was '%s'.", command.c_str());
|
||||
LOG_WARNING("Emscripten cannot execute processes. The commandline was '%s'.", std::string(command).c_str());
|
||||
return -1;
|
||||
#endif // __EMSCRIPTEN__
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
constexpr int operator"" _MPH(unsigned long long x) noexcept
|
||||
constexpr int operator""_MPH(unsigned long long x) noexcept
|
||||
{
|
||||
return x * 29127;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue