Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
53 changes: 52 additions & 1 deletion .github/workflows/build-other.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,55 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: butterscotch-android
path: src/android/frontend/app/build/outputs/apk/debug/app-debug.apk
path: src/android/frontend/app/build/outputs/apk/debug/app-debug.apk

build-webos:
name: Build (webOS)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7

- name: Set up webOS toolchain
run: |
curl -Lo native-toolchain.tar.gz https://github.com/openlgtv/buildroot-nc4/releases/latest/download/arm-webos-linux-gnueabi_sdk-buildroot-x86_64.tar.gz
sudo tar xzf native-toolchain.tar.gz -C /opt/

PLATFORM=linux-x86_64
TAG=$(curl -fsSL https://api.github.com/repos/webosbrew/ares-cli-rs/releases/latest | grep '"tag_name"' | cut -d '"' -f 4)
curl -fsSL "https://github.com/webosbrew/ares-cli-rs/releases/download/$TAG/ares-cli-rs-$TAG-$PLATFORM.tar.gz" | tar xz
sudo install -m 755 "ares-cli-rs-$TAG-$PLATFORM"/ares-* /usr/local/bin/

- name: Install ccache
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends ccache

- uses: actions/cache@v6
with:
path: ~/.cache/ccache
key: ccache-webos-${{ github.sha }}
restore-keys: |
ccache-webos-

- name: Build Butterscotch
run: |
cmake -B build -G Ninja \
-DWERROR=ON -DPLATFORM=webos \
-DCMAKE_TOOLCHAIN_FILE=/opt/arm-webos-linux-gnueabi_sdk-buildroot/share/buildroot/toolchainfile.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build build

- name: Package IPK
run: |
mkdir -p dist/
cp build/butterscotch dist/
cp src/webos/dist/appinfo.json dist/
cp src/webos/dist/icon.png dist/
ares-package dist/ -o dist/

- name: Upload IPK
uses: actions/upload-artifact@v7
with:
name: butterscotch-webos
path: dist/*.ipk
13 changes: 10 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ endif()

set(BACKEND "sdl2")

elseif(PLATFORM STREQUAL "webos")
set(BACKEND "sdl2")
endif()

project(butterscotch C)
Expand Down Expand Up @@ -121,7 +123,7 @@ option(ENABLE_WAD14 "Enable support for WAD Version 14 and below" ON)
option(ENABLE_WAD16 "Enable support for WAD Version 15/16" ON)
option(ENABLE_WAD17 "Enable support for WAD Version 17" ON)
option(ENABLE_LEGACY_GL "Enable the legacy OpenGL renderer" ON)
if(PLATFORM STREQUAL "switch")
if(PLATFORM STREQUAL "switch" OR PLATFORM STREQUAL "webos")
set(ENABLE_LEGACY_GL OFF CACHE BOOL "Enable the legacy OpenGL renderer" FORCE)
endif()
option(ENABLE_MODERN_GL "Enable the modern OpenGL renderer" ON)
Expand Down Expand Up @@ -260,8 +262,13 @@ if(ENABLE_VM_EXCEPTIONS_LOGS)
add_compile_definitions(ENABLE_VM_EXCEPTIONS_LOGS)
endif()

if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "switch")
if(PLATFORM STREQUAL "vita")
if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "switch" OR PLATFORM STREQUAL "webos")
if(PLATFORM STREQUAL "webos")
set(VM_GML_PROFILER_DEFAULT OFF)
set(VM_TRACING_DEFAULT OFF)
set(VM_OPCODE_PROFILER_DEFAULT OFF)
set(VM_STUB_LOGS_DEFAULT OFF)
elseif(PLATFORM STREQUAL "vita")
# Normally, __VITA__ should already be defined, but for some reason,
# in my case, this didn't really work... so just in case, when we're
# building for the Vita, just force-feed these...
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Of course, there are exceptions that break game compatibility altogether:
* PlayStation 3
* PlayStation Vita
* Nintendo Switch
* LG webOS
* ...and maybe more in the future!

Additionally, any platform with reasonably complete C and POSIX conformance should work, the following have been tested.
Expand Down Expand Up @@ -120,7 +121,8 @@ All RISC architecture (ARM, MIPS, PowerPC, RISC-V) builds require hardware float
| Platform | Download | Notes |
| -------- | -------- | ----- |
| WebAssembly | [butterscotch-web.zip](https://nightly.link/ButterscotchRunner/Butterscotch/workflows/build/main/butterscotch-web.zip) | [Play online](https://butterscotch.mrpowergamerbr.com/web/) |
| Android | [butterscotch-android.zip](https://nightly.link/ButterscotchRunner/Butterscotch/workflows/build/main/butterscotch-android.zip) |
| Android | [butterscotch-android.zip](https://nightly.link/ButterscotchRunner/Butterscotch/workflows/build/main/butterscotch-android.zip) |
| webOS | [butterscotch-webos.zip](https://nightly.link/ButterscotchRunner/Butterscotch/workflows/build/main/butterscotch-webos.zip) | After installing the IPK, place the game's `data.win` and other required files in `/media/developer/apps/usr/palm/applications/com.mrpowergamerbr.butterscotch/` |

## Community Ports

Expand Down
8 changes: 7 additions & 1 deletion src/input_recording.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,13 @@ bool InputRecording_save(InputRecording* recording) {
}

const char* output = JsonWriter_getOutput(&w);
fwrite(output, 1, JsonWriter_getLength(&w), file);
size_t len = JsonWriter_getLength(&w);
if (fwrite(output, 1, len, file) != len) {
logWarn("Error: Could not write input recording to '%s'\n", recording->recordFilePath);
JsonWriter_free(&w);
fclose(file);
return false;
}
fputc('\n', file);
fclose(file);

Expand Down
72 changes: 45 additions & 27 deletions src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -1081,25 +1081,34 @@ int loop(CommandLineArgs args, const char *argv0) {
logDebug("Dumping runner state at frame %d\n", runner->frameCount);
char* json = Runner_dumpStateJson(runner);

if (args.dumpJsonFilePattern != nullptr) {
char filename[512];
snprintf(filename, sizeof(filename), args.dumpJsonFilePattern, runner->frameCount);
FILE* f = fopen(filename, "wb");
if (f != nullptr) {
fwrite(json, 1, strlen(json), f);
fputc('\n', f);
fclose(f);
logInfo("JSON dump saved: %s\n", filename);
if (json != nullptr) {
if (args.dumpJsonFilePattern != nullptr) {
char filename[512];
snprintf(filename, sizeof(filename), args.dumpJsonFilePattern, runner->frameCount);
FILE* f = fopen(filename, "wb");

if (f != nullptr) {
size_t len = strlen(json);
if (fwrite(json, 1, len, f) != len) {
logWarn("Error: Could not write JSON dump to '%s'\n", filename);
fclose(f);
} else {
fputc('\n', f);
fclose(f);
logInfo("JSON dump saved: %s\n", filename);
}
} else {
logWarn("Could not write JSON dump to '%s'\n", filename);
}
} else {
logWarn("Could not write JSON dump to '%s'\n", filename);
logInfo("%s\n", json);
}
} else {
logInfo("%s\n", json);
}

free(json);
free(json);
}
}


// Toggle the collision mask debug overlay
if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F2)) {
debugShowCollisionMasks = !debugShowCollisionMasks;
Expand Down Expand Up @@ -1195,22 +1204,31 @@ int loop(CommandLineArgs args, const char *argv0) {
// Dump runner state as JSON if this frame was requested
if (hmget(args.dumpJsonFrames, runner->frameCount)) {
char* json = Runner_dumpStateJson(runner);
if (args.dumpJsonFilePattern != nullptr) {
char filename[512];
snprintf(filename, sizeof(filename), args.dumpJsonFilePattern, runner->frameCount);
FILE* f = fopen(filename, "wb");
if (f != nullptr) {
fwrite(json, 1, strlen(json), f);
fputc('\n', f);
fclose(f);
logInfo("JSON dump saved: %s\n", filename);

if (json != nullptr) {
if (args.dumpJsonFilePattern != nullptr) {
char filename[512];
snprintf(filename, sizeof(filename), args.dumpJsonFilePattern, runner->frameCount);
FILE* f = fopen(filename, "wb");

if (f != nullptr) {
size_t len = strlen(json);
if (fwrite(json, 1, len, f) != len) {
logWarn("Error: Could not write JSON dump to '%s'\n", filename);
fclose(f);
} else {
fputc('\n', f);
fclose(f);
logInfo("JSON dump saved: %s\n", filename);
}
} else {
logWarn("Could not write JSON dump to '%s'\n", filename);
}
} else {
logWarn("Could not write JSON dump to '%s'\n", filename);
logInfo("%s\n", json);
}
} else {
logInfo("%s\n", json);
free(json);
}
free(json);
}

// Query actual framebuffer size
Expand Down
9 changes: 9 additions & 0 deletions src/webos/dist/appinfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "com.mrpowergamerbr.butterscotch",
"version": "1.0.0",
"vendor": "Perfect Dreams",
"type": "native",
"main": "butterscotch",
"title": "Butterscotch",
"icon": "icon.png"
}
Binary file added src/webos/dist/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions src/webos/glibc_compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <sys/types.h>
#include <sys/stat.h>

extern int __xstat(int ver, const char *path, struct stat *buf);
extern int __fxstat(int ver, int fd, struct stat *buf);

int stat(const char *path, struct stat *buf)
{
return __xstat(0, path, buf);
}

int fstat(int fd, struct stat *buf)
{
return __fxstat(0, fd, buf);
}
25 changes: 25 additions & 0 deletions src/webos/log.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <stdio.h>

#include "log.h"

void platformLog(const logType type, const char *format, va_list va)
{
FILE *out = stderr;

switch (type) {
case LOG_TYPE_NORMAL:
out = stdout;
break;
case LOG_TYPE_WARNING:
fputs("Warning: ", out);
break;
case LOG_TYPE_ERROR:
fputs("Error: ", out);
break;
case LOG_TYPE_DEBUG:
fputs("Debug: ", out);
break;
}

vfprintf(out, format, va);
}
36 changes: 36 additions & 0 deletions src/webos/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <loop.h>

#include "platformdefs.h"

int main(int argc, char **argv)
{
(void)argc;

CommandLineArgs args = {0};

args.exitAtFrame = -1;

#ifdef ENABLE_VM_TRACING
args.traceBytecodeAfterFrame = 0;
#endif

args.speedMultiplier = 1.0;
args.fastForwardSpeed = 0.0;

/*
* webOS native apps receive a JSON launch object in argv[1].
* It is not a data.win path, so deliberately ignore it here.
*/
args.osType = OS_WINDOWS;
args.profilerFramesBetween = 0;
args.loadType = DATAWINLOADTYPE_LOAD_IN_MEMORY_AHEAD_OF_TIME;
args.renderer = MODERN_GL;

args.dataWinPath = "./data.win";
args.saveFolder = "./saves";

int ret = loop(args, argv[0]);

freeCommandLineArgs(&args);
return ret;
}
9 changes: 9 additions & 0 deletions src/webos/stb_impl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_STDIO
#include "stb_image.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
#include "stb_image_write.h"
#pragma GCC diagnostic pop
Loading