Skip to content

GameMaker Studio Ports

Jeod edited this page Apr 13, 2026 · 12 revisions

Running GameMaker Games on Linux ARM/ARM64

Overview

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

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.so to run on Linux.
  • Library Loading: Loads shared libraries (e.g., libm.so, libc++_shared.so, libyoyo.so) from user-provided APKs or .port files 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.

Patches and Stubs

GMLoader-Next includes patches to ensure compatibility:

  • Steamworks API Stubs: Android libyoyo.so runtimes 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, 0 for steam_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_change function, 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

.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:

  1. Checks for user-provided game data (data.win, game.unx, or APK) in a designated folder, as specified in the port's readme.
  2. Applies necessary patches (e.g., xdelta patches, audio compression, and/or texture externalization).
  3. Merges the game data into the .port file, 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.

libyoyo.so provenance

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.

How a port is composed

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

User Responsibility

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.

Community Goals

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.

External Tools

GMToolkit

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

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.

UTMT-CLI

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.

Dump command

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"
}

Modifying and replacing code

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
}

Running scripts

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
}