-
-
Notifications
You must be signed in to change notification settings - Fork 9
GameMaker Studio Ports
GameMaker engine games can run on Linux ARM/ARM64 (armhf/aarch64) platforms using GMLoader-Next, an open-source translation layer. GMLoader-Next converts Android function calls from GameMaker's libyoyo.so runtime to Linux equivalents, allowing games to operate seamlessly on Linux devices. Users must provide their own legally obtained game files (data.win, game.unx, or APK), which are combined with a .port file to create a minimal, Linux-specific runtime environment. These .port files are not Android APKs and will not run on Android devices.
GMLoader-Next is a community-driven project aimed at expanding GameMaker's reach to Linux, aligning with YoYo Games' support for indie developers and accessibility.
GMLoader-Next, developed by JohnnyOnFlame and contributors, is a successor to droidports, and a free, open-source tool released under GPLv2. It performs the following key functions:
-
Fake Java Virtual Machine (JVM): Creates a JVM environment to emulate Android's JNI-based functionality, allowing GameMaker's
libyoyo.soto run on Linux. -
Library Loading: Loads shared libraries (e.g.,
libm.so,libc++_shared.so,libyoyo.so) from user-provided APKs or.portfiles using SDL2 for graphics, audio, and input. -
Patching and Stubbing: Applies patches to handle platform differences, such as input, gamepad, audio, and missing functions (e.g., Steam API, Windows-only
game_change). - Game Loop Management: Uses SDL2 to manage rendering (OpenGL ES 2.0), input, and audio, ensuring smooth gameplay.
GMLoader-Next includes patches to ensure compatibility:
-
Steamworks API Stubs: Android
libyoyo.soruntimes do not include the Steamworks API, so titles that optionally link against Steamworks would fail to load on platforms without a Steam client. GMLoader-Next provides no-op stubs that return neutral values (for example,0forsteam_is_initialized()), allowing games to continue past these calls or to take their own non-Steam code paths. The stubs implement an ABI compatibility shim only; no DRM or technological protection measure is examined, removed, or bypassed. -
game_change Hook: Some games expect an exclusive
game_changefunction, absent in Android runtimes. GMLoader-Next intercepts these calls with a custom implementation to maintain compatibility, preventing crashes on Linux. - Other Patches: Adjust input, gamepad, mouse, audio (FMOD), and display settings to align with Linux, ensuring games run as expected.
Patches target the public function interfaces required for interoperability on Linux. GMLoader-Next contains no GameMaker or YoYo Games source code.
.port files are zip archives containing lib/arm64-v8a/libyoyo.so or lib/armeabi-v7a/libyoyo.so, tailored to specific GameMaker games. Unlike full Android APKs, they are stripped of Android components and designed solely for Linux compatibility via GMLoader-Next. When launching a port, the patchscript:
- Checks for user-provided game data (
data.win,game.unx, or APK) in a designated folder, as specified in the port's readme. - Applies necessary patches (e.g., xdelta patches, audio compression, and/or texture externalization).
- Merges the game data into the
.portfile, creating a minimal runtime environment for Linux.
Note: .port files are not executable on Android devices and require user-provided, legally obtained game files to function.
Patching progress is shown on the PortMaster's Patcher GUI utility.
The libyoyo.so included in .port archives is produced locally from a blank project exported for Android using GameMaker Studio. All project content is stripped, leaving only the runtime shared object required to load a user-supplied data.win on aarch64 / armhf Linux. No runtime binaries are extracted from third-party games or installers.
A GameMaker port combines three independent pieces:
| Component | Origin | Role |
|---|---|---|
| GMLoader-Next | Original GPLv2 code | ELF loader, JNI shim, and Steamworks API stubs |
| libyoyo.so | Built locally from a blank GameMaker Studio Android export, stripped to the runtime | GameMaker's Android runtime |
| data.win / game assets | Supplied by the user from their own copy | The game itself |
Users must provide their own legally obtained copies of any game they wish to run and comply with the applicable publisher and engine EULAs. RHH-Ports does not provide game data unless given explicit permission which is documented. Redistribution of game data or modification of GMLoader-Next's stubs into a circumvention tool is outside the scope and intent of this project.
The goal is to expand GameMaker's reach to Linux ARM/ARM64, supporting indie developers and players with a more accessible option that isn't limited to Raspberry Pi.
GMToolkit (GameMaker Toolkit) is a squashfs mounted runtime that houses all of the external tools commonly used with Game Maker ports. Since these ports need to run on small devices that are often weaker than a standard desktop computer, GMToolkit is used to perform several "first time setup" functions to make a port "ready to play".
A runtime for these tools was created to prevent duplicating the same tools across the 200+ GameMaker ports that PortMaster offers, thus decreasing storage bloat.
GMTools, developed by Cyril Delétré (aka kotzebuedog / Kdog), is a Python script that reads GameMaker data files and can use oggdec/oggenc to re-encode game audio at a lower bitrate. This reduces file sizes and RAM usage during playback, with minimal perceived loss in quality — an ideal trade-off for small handheld devices that use low-powered speakers and have limited memory.
UndertaleModTool CLI is a command-line version of UndertaleModTool modified to support texture externalization. In the Pizza Tower and Deltarune ports, this process replaces in-game textures inside the GameMaker data file with lightweight placeholders, while exporting the full-quality textures to .pvr format — a GPU-native compressed texture format. The placeholders are loaded into memory, and GMLoader-Next dynamically replaces them at runtime by loading the .pvr textures directly from disk, reducing RAM usage and improving performance on small handheld devices.
UTMT-CLI can dump specific parts of a data file, for example textures or scripts. The below example collects several gml script files into a single $CODEARGS variable and calls dotnet on UndertaleModCli.dll with the dump command. Dump must point to the data file to open, -o provides the output directory (in most cases a temp folder), and then $CODEARGS which expands to --code $entry. The full command result for this example is:
dotnet "$TOOLKIT/utmt-cli/UndertaleModCli.dll" dump "$DATADIR/data.win" -o "$TMPDIR" --code $entry1 --code $entry2 --code $entry3
# Dump game code entries from data.win
dump_code() {
# Purge cache before operations
rm -rf "$TMPDIR/"*
# Specify which code entries to extract
CODE_ENTRIES=(
"gml_Object_code_game_init_Create_0"
"gml_Object_obj_god_gem_slot_controller_Draw_0"
"gml_GlobalScript_localization_functions"
)
CODEARGS=""
for entry in "${CODE_ENTRIES[@]}"; do
CODEARGS="$CODEARGS --code $entry"
done
echo "Dumping GML scripts: ${CODE_ENTRIES[*]}"
dotnet "$TOOLKIT/utmt-cli/UndertaleModCli.dll" \
dump "$DATADIR/data.win" \
-o "$TMPDIR" \
$CODEARGS
if [ $? -ne 0 ]; then
echo "Failed to dump GML scripts."
return 1
fi
echo "GML scripts dumped to $TMPDIR"
}After the dump command extracts gml scripts they may be modified and then repacked. Modifications can be done however the user wishes, however I will stress the importance of not distributing whole gml files that include proprietary code. Replacing gml scripts with modified versions is similar to dumping, see the below example. This replace function loops through all .gml files inside the designated path $TMPDIR/CodeEntries/ and adds each file to $CODEARGS.
# Replace game code entries for data.win
replace_code() {
# Build --code args from all gml files
CODEARGS=""
for file in "$TMPDIR/CodeEntries/"*.gml; do
[ -f "$file" ] || continue
entry=$(basename "$file" .gml)
echo "Will replace gml script: $file"
CODEARGS="$CODEARGS --code $entry=$file"
done
# Begin replace operations
dotnet "$TOOLKIT/utmt-cli/UndertaleModCli.dll" \
replace "$DATADIR/data.win" \
-o "$DATADIR/data2.win" \
$CODEARGS
if [ $? -ne 0 ]; then
echo "Failed to replace gml scripts."
return 1
else
echo "Successfully replaced gml scripts."
rm -f "$DATADIR/data.win"
mv "$DATADIR/data2.win" "$DATADIR/data.win"
[ -d "$TMPDIR" ] && rm -rf "$TMPDIR"/*
fi
}UTMT-CLI has the ability to run .csx scripts. Using scripts this way enhances the power of UTMT-CLI on-device. For example, the below function forces a Game Maker Studio game to stretch to fill a display instead of maintaining a 16:9 aspect ratio. Note the helpful comment which explains the reference used when choosing which flags to set and clear.
# Toggle GeneralInfo flags via envars - see https://github.com/UnderminersTeam/UndertaleModTool/blob/master/UndertaleModLib/Models/UndertaleGeneralInfo.cs for options
set_flags() {
export SET_FLAGS="Fullscreen"
export CLEAR_FLAGS="Scale"
dotnet "$TOOLKIT/utmt-cli/UndertaleModCli.dll" \
load "$DATADIR/data.win" \
-s "$GAMEDIR/tools/toggleflags.csx" \
-o "$DATADIR/data2.win"
if [ $? -ne 0 ]; then
echo "Failed to toggle info flags."
return 1
else
echo "Successfully toggled info flags."
rm -f "$DATADIR/data.win"
mv "$DATADIR/data2.win" "$DATADIR/data.win"
[ -d "$TMPDIR" ] && rm -rf "$TMPDIR"/*
fi
}For the above example you must include toggleflags.csx which is used in a few ports in this repository. You can also use csx scripts to upgrade bytecode. This is useful for GMS games on the cusp of 64-bit support; it can be a hit or miss depending on how much a game relies on older runtime functionality. This function is actively used in the port VA11-HALL-A, which is made playable with gmloadernext thanks to upgrading bytecode to 16.
# Upgrade the game's bytecode to 16
upgrade_bytecode() {
# Purge cache before operations
rm -rf "$TMPDIR/"*
# Run script
dotnet "$TOOLKIT/utmt-cli/UndertaleModCli.dll" \
load "$DATADIR/data.win" \
-s "$GAMEDIR/tools/bytecode.csx" \
-o "$DATADIR/game.droid"
if [ $? -ne 0 ]; then
echo "[DOTNET]: Couldn't apply bytecode upgrade."
return 1
else
rm -f "$DATADIR/data.win"
[ -d "$TMPDIR" ] && rm -rf "$TMPDIR"/*
fi
}