Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Webassembly support #204

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
aa3c5c1
Ensure db_findfirst path is resolved
bit-hack Nov 18, 2023
c101fda
Fixes movie corruption due to a bad return type in getOffset().
dje4321 May 22, 2024
abc9f35
Starting,,, somewhere
Lockl00p Oct 7, 2024
c98aabf
Update Cmakelists and some QOL to prejs and shell
Lockl00p Oct 20, 2024
e5a39aa
Added Mouse movement and some basic mouse clicking.
Lockl00p Oct 20, 2024
e44f914
Better mouse input
Lockl00p Oct 20, 2024
13347d9
Added save functionality.
Lockl00p Oct 20, 2024
e9315cb
Added instructions for WASM in readme
Lockl00p Oct 20, 2024
c9fb060
Turns out, save functionality sucks. Deal with it later
Lockl00p Oct 22, 2024
3b20186
OH MY GOD THE BUTTONS WORK
Lockl00p Oct 28, 2024
7dcc54f
Fix glitch with buttons
Lockl00p Oct 28, 2024
7411eca
Add an alert to stderr. Just in case.
Lockl00p Oct 28, 2024
089dcca
Merge remote-tracking branch 'bithack/fix_opendir'
Lockl00p Oct 28, 2024
2a2f396
Merge remote-tracking branch 'dje4321/movie_corruption'
Lockl00p Oct 28, 2024
7cac938
Fix saving, fix right clicking
Lockl00p Oct 28, 2024
ec360aa
Even BETTER Mouse Input
Lockl00p Oct 31, 2024
bb36ccd
Hold Clicking Down :)
Lockl00p Oct 31, 2024
0ec2c54
Hold Clicking Right click now works!
Lockl00p Oct 31, 2024
df6b0f3
Added Preloading
Lockl00p Oct 31, 2024
4cea386
Added embedding
Lockl00p Oct 31, 2024
f5e4c36
Add delete option
Lockl00p Oct 31, 2024
1cbbde1
Fix PreJS issue
Lockl00p Oct 31, 2024
fcf7d8e
Increase asyncify stack size
Lockl00p Oct 31, 2024
6c47d7f
Update readme
Lockl00p Oct 31, 2024
841ee17
pointer lock works now!
Lockl00p Oct 31, 2024
fce2e08
Give credit to some people for helping
Lockl00p Nov 1, 2024
a47ce05
Remove some unneeded header includes from mouse.cc
Lockl00p Nov 1, 2024
510c041
Increase asyncify stack size
Lockl00p Nov 1, 2024
83d55a1
Update README.md
Lockl00p Nov 1, 2024
fb27bc7
Update README.md
Lockl00p Nov 1, 2024
238c6d6
Update README.md
Lockl00p Nov 1, 2024
e5ddf86
Update README.md
Lockl00p Nov 1, 2024
bbc98c7
Update README.md
Lockl00p Jan 12, 2025
5a7d92f
Update README.md
Lockl00p Jan 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.13)

set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)

set(EXECUTABLE_NAME fallout-ce)
if(NOT DEFINED EXECUTABLE_NAME)
set(EXECUTABLE_NAME fallout-ce)
endif()

if (APPLE)
if(IOS)
Expand All @@ -14,6 +16,8 @@ if (APPLE)
endif()
endif()



project(${EXECUTABLE_NAME})

set(CMAKE_CXX_STANDARD 17)
Expand All @@ -22,11 +26,27 @@ set(CMAKE_CXX_EXTENSIONS NO)

option(ASAN "Enable address sanitizer" OFF)
option(UBSAN "Enable undefined behaviour sanitizer" OFF)
option(HTML "Output standalone HTML (emscripten only obviously)" OFF)

if (ANDROID)
add_library(${EXECUTABLE_NAME} SHARED)
else()

add_executable(${EXECUTABLE_NAME} WIN32 MACOSX_BUNDLE)

endif()

if(EMSCRIPTEN AND DEFINED PRELOAD)
if(HTML)
target_link_options(${EXECUTABLE_NAME} PUBLIC --embed-file ${PRELOAD}@/preload)
else()
target_link_options(${EXECUTABLE_NAME} PUBLIC --preload-file ${PRELOAD}@/preload)
endif()
endif()

if(EMSCRIPTEN AND HTML)
target_link_options(${EXECUTABLE_NAME} PUBLIC "--shell-file=${CMAKE_CURRENT_SOURCE_DIR}/os/wasm/shell.htm;-sSINGLE_FILE")
set(CMAKE_EXECUTABLE_SUFFIX ".html")
endif()

if(ASAN)
Expand All @@ -38,6 +58,11 @@ if(UBSAN)
target_link_options(${EXECUTABLE_NAME} PUBLIC "-fsanitize=undefined")
endif()

if(EMSCRIPTEN)
target_compile_options(${EXECUTABLE_NAME} PUBLIC "--use-port=sdl2")
target_link_options(${EXECUTABLE_NAME} PUBLIC "-sASYNCIFY_STACK_SIZE=10000000;-sALLOW_MEMORY_GROWTH;-lidbfs.js;--use-port=sdl2;-sASYNCIFY;-sFULL_ES2;--pre-js=${CMAKE_CURRENT_SOURCE_DIR}/os/wasm/pre.js;-sEXPORTED_RUNTIME_METHODS=callMain")
endif()

target_include_directories(${EXECUTABLE_NAME} PUBLIC src)

target_sources(${EXECUTABLE_NAME} PUBLIC
Expand Down Expand Up @@ -290,6 +315,12 @@ if(WIN32)
)
endif()

if(EMSCRIPTEN)
target_compile_definitions(${EXECUTABLE_NAME} PUBLIC
__EMSCRIPTEN__
)
endif()

if(WIN32)
target_link_libraries(${EXECUTABLE_NAME}
winmm
Expand All @@ -303,6 +334,7 @@ if (WIN32)
)
endif()


if(APPLE)
if(IOS)
set(RESOURCES
Expand Down Expand Up @@ -347,7 +379,7 @@ if(APPLE)
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.0.0")
endif()

if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD"))
if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten"))
add_subdirectory("third_party/sdl2")
else()
find_package(SDL2)
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
# Fallout Community Edition
# Fallout Community Edition (Webassembly)

Fallout Community Edition is a fully working re-implementation of Fallout, with the same original gameplay, engine bugfixes, and some quality of life improvements, that works (mostly) hassle-free on multiple platforms.

Oh, also, credit where credit is due. I used the pull requests from [dje4321](https://github.com/dje4321) and [bit-hack](https://github.com/bit-hack) to fix some issues. Mainly, bit-hack fixed saving on linux systems, and dje4321 fixed some corruption on the prerendered cutscene.

Video of it working (and instructions on how to do it). With firefox, you can't save... well, you kinda can, but it's weird.
https://youtu.be/EB6Jsm0oMzM

Thank you guys!

There is also [Fallout 2 Community Edition](https://github.com/alexbatalov/fallout2-ce).

## Installation

You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout) or [Steam](https://store.steampowered.com/app/38400). Download latest [release](https://github.com/alexbatalov/fallout1-ce/releases) or build from source. You can also check latest [debug](https://github.com/alexbatalov/fallout1-ce/actions) build intended for testers.

For Webassembly, building from source is roughly the same as with Linux except you must have emsdk {Install from here}(https://emscripten.org/docs/getting_started/downloads.html), and instead of the normal cmake command, you would use `emcmake cmake {source directory}` to output a js file you can use, and `emcmake cmake {source directory} -DHTML="true"` to output a standalone html file! You may also use `emcmake cmake {source directory} -DPRELOAD="${directory to preload}" `, but this will take a lot of memory when actually running the game so beware!

If you opt for the standalone HTML file, you're going to have to upload all of the game files into the game's filesystem. So, it's a sort of tradeoff between memory usage and ease of access.
Also, the audio is... not the best. Oh *also* if you want to click the escape button without unfullscreening, you could fullscreen with f11.

Another thing: The JS files are pretty untested. I only made this in the first place with the intent to put it into a single HTML file. If you have any problems, feel free to make an issue or PR!

### Windows

Download and copy `fallout-ce.exe` to your `Fallout` folder. It serves as a drop-in replacement for `falloutw.exe`.
Expand Down
1 change: 1 addition & 0 deletions _deps/fpattern-src
Submodule fpattern-src added at 96f42d
98 changes: 98 additions & 0 deletions os/wasm/pre.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//current command in ascii decimal
let currentcmd = [0,0,0]
let currentfile = "";
const sleep = ms => new Promise(r => setTimeout(r,ms));
var loaded = false;
Module['print'] = function(text){console.log(text);}
Module['preRun'] = function()
{

function stdin(){return 10};
var stdout = null;
function stderr(text){alert("stderr: " + text)}
FS.init(stdin,stdout,stderr);
FS.mount(IDBFS,{},"/home/web_user/");

}
Module['noInitialRun'] = true

function keyev(ev) {
home = "/home/web_user/fallout1/"
if(ev.key == "`"){
file_selector.click()
}
else if(ev.key == "="){
FS.syncfs(false,function(){alert("save attempted")});
}
else if(ev.key == "\\"){
readfrom = home.concat('',prompt("Read which directory?"))
alert(readfrom)
alert(FS.readdir(readfrom))


} else if(ev.key == "]"){
FS.syncfs(true,function(){
try {
FS.mkdir("/home/web_user/fallout1")
FS.mkdir("/home/web_user/fallout1/DATA")
} catch (error) {

}
alert("Data loaded. You may now make changes.")
loaded = true;
});
}
else if(ev.key == '-'){
del = home.concat('',prompt("Delete which file?"))
FS.unlink(del)
}



}

document.addEventListener('keydown', keyev, true);

document.addEventListener('click', (ev) => {
console.log("event is captured only once.");
args = []
if(!loaded){
FS.syncfs(true,function(){
try {
FS.mkdir("/home/web_user/fallout1/")
FS.mkdir("/home/web_user/fallout1/DATA")
} catch (error) {

}
if(FS.analyzePath("/preload/DATA").exists){
FS.chdir("/preload")
FS.symlink("/home/web_user/fallout1/DATA/SAVEGAME","/preload/DATA/SAVEGAME");
}
else{
FS.chdir("/home/web_user/fallout1");
}


document.removeEventListener("keydown",keyev,true);
document.getElementById("Instructions1").remove();
document.getElementById("Instructions2").remove();
document.getElementById("Instructions3").remove();
document.getElementById("Instructions4").remove();
document.getElementById("Instructions5").remove();
document.getElementById("Instructions6").remove();
Module.callMain(args);
});}
else{
FS.chdir("/home/web_user/fallout1");
document.removeEventListener("keydown",keyev,true);
document.getElementById("Instructions1").remove();
document.getElementById("Instructions2").remove();
document.getElementById("Instructions3").remove();
document.getElementById("Instructions4").remove();
document.getElementById("Instructions5").remove();
document.getElementById("Instructions6").remove();
Module.callMain(args);
}

}, { once: true });

83 changes: 83 additions & 0 deletions os/wasm/shell.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<!-- Thank You Stack Overflow! -->
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8, width=device-width, initial-scale=1">
<title>Fallout1</title>
</head>

<body style="margin:0;padding:0">

<!-- Create the canvas that the C++ code will draw into -->
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<p id="Instructions1">Press ] before starting game and making any changes to allow changes to your saves before you start the game.</p>
<p id="Instructions2"> Press ` before starting the game to upload files</p>
<p id="Instructions3">Press = before starting the game to save files.</p>
<p id="Instructions4">Press \ before starting the game to read a directory</p>
<p id="Instructions5">Press - before starting the game to delete a file</p>
<p id="Instructions6">Click on the webpage to start the game.</p>
<!-- Allow the C++ to access the canvas element -->
<script type='text/javascript'>
var Module = {
canvas: (function() { return document.getElementById('canvas'); })()
};
Module['preRun'] = []
</script>
<script type='text/javascript'>
var currentname = "/home/web_user/fallout1/"
const file_reader = new FileReader();
file_reader.addEventListener("load", readf);
function readf(event){
//also heavily derivative of Riot's code on Stack Overflow cause I sure as hell don't udnerstand it.
const uint8Arr = new Uint8Array(file_reader.result);
console.log(currentname+fls[inc].name);
try {
stream = FS.open(currentname+fls[inc].name,'w');
} catch (error) {
alert(error.toString() + "... Was that not a directory?");
return;
}

FS.write(stream, uint8Arr, 0, uint8Arr.length, 0);
FS.close(stream);
inc += 1;
if(inc == maxinc){inc = 0;maxinc = 0;file_reader.onloadend = "";currentname="/home/web_user/fallout1/"}
else{
file_reader.onloadend = file_reader.readAsArrayBuffer(fls[inc]);
}
alert("File Uploaded");
}

var fname = ""
var fls = []
var inc = 0;
var maxinc = 0
function save_files(){
currentname = currentname.concat(prompt("Upload to?"),'/')
try {
FS.readdir(currentname)
} catch (error) {
FS.mkdirTree(currentname);
}
fls = this.files
maxinc = fls.length
file_reader.readAsArrayBuffer(fls[inc]);

};


var file_selector = document.createElement('input');
file_selector.setAttribute('type', 'file');
file_selector.setAttribute('multiple', '');
file_selector.addEventListener("change", save_files, false);



</script>
<!-- Where the script shall be -->
{{{ SCRIPT }}}

</body>

</html>
5 changes: 5 additions & 0 deletions src/game/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include "platform_compat.h"
#include "plib/db/db.h"
#include "plib/gnw/memory.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

namespace fallout {

Expand Down Expand Up @@ -270,6 +273,7 @@ bool config_load(Config* config, const char* filePath, bool isDb)
}

fclose(stream);

}

// FIXME: This function returns `true` even if the file was not actually
Expand Down Expand Up @@ -328,6 +332,7 @@ bool config_save(Config* config, const char* filePath, bool isDb)
}

fclose(stream);

}

return true;
Expand Down
7 changes: 6 additions & 1 deletion src/game/loadsave.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
#include "plib/gnw/memory.h"
#include "plib/gnw/svga.h"
#include "plib/gnw/text.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

namespace fallout {

Expand Down Expand Up @@ -799,7 +802,9 @@ int SaveGame(int mode)
quick_done = true;
}
}

#ifdef __EMSCRIPTEN__
EM_ASM({FS.syncfs(false,function(){})});
#endif
return rc;
}

Expand Down
4 changes: 2 additions & 2 deletions src/movie_lib.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static void _nfPkDecomp(unsigned char* buf, unsigned char* a2, int a3, int a4, i

static constexpr uint16_t loadUInt16LE(const uint8_t* b);
static constexpr uint32_t loadUInt32LE(const uint8_t* b);
static uint8_t getOffset(uint16_t v);
static int16_t getOffset(uint16_t v);

// 0x51EBD8
static int dword_51EBD8 = 0;
Expand Down Expand Up @@ -2802,7 +2802,7 @@ constexpr uint32_t loadUInt32LE(const uint8_t* b)
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
}

uint8_t getOffset(uint16_t v)
int16_t getOffset(uint16_t v)
{
return static_cast<int8_t>(v & 0xFF) + dword_51F018[v >> 8];
}
Expand Down
5 changes: 4 additions & 1 deletion src/plib/db/db.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#else
#include <dirent.h>
#endif

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include <fpattern.h>

#include "platform_compat.h"
Expand Down Expand Up @@ -2577,6 +2579,7 @@ static int db_findfirst(const char* path, DB_FIND_DATA* findData)
char basePath[COMPAT_MAX_PATH];
compat_makepath(basePath, drive, dir, NULL, NULL);

compat_resolve_path(basePath);
findData->dir = opendir(basePath);
if (findData->dir == NULL) {
return -1;
Expand Down
Loading