Compare commits

...
20 Commits
Author SHA1 Message Date
dkeruza e7d8c5a80a Added libuuid.so.1 to the AppImage. 2025-04-16 10:26:08 -04:00
dkeruza 55fd8955c1 Merge pull request #95 from dkeruza-neo/master
Added libs to AppImage and fixed a bug in lindbergh.c
2025-04-15 19:53:01 -04:00
dkeruza-neo ea35dcdad2 fixed build-deps 2025-04-15 20:37:12 -03:00
dkeruza-neo fb557044d7 Added libs to AppImage and fixed a bug in lindbergh.c 2025-04-15 20:22:37 -03:00
dkeruza 0e8b3b4a59 Merge pull request #92 from dkeruza-neo/master
AppImage support.
2025-04-12 00:41:08 -04:00
dkeruza-neo f17dd93778 Merge remote-tracking branch 'upstream/master' 2025-04-12 00:30:36 -04:00
dkeruza-neo e359cbb0bd Added AppImage, fixed 2Spicy Resolution, again. 2025-04-12 00:28:50 -04:00
dkeruza 72f70ccda1 Merge pull request #90 from dkeruza-neo/master
2Spicy error in the scaling code.
2025-04-06 16:14:12 -04:00
dkeruza-neo 0a29307870 2Spicy error in the scaling code. 2025-04-06 16:12:48 -04:00
dkeruza-neo d202f31717 fixed changelog 2025-04-06 16:12:48 -04:00
dkeruza a68dcbbdcf Merge pull request #88 from dkeruza-neo/master
Resolution patches for 2Spicy and HOD-Ex
2025-04-06 13:28:44 -04:00
dkeruza 75642e285e Merge branch 'master' into master 2025-04-06 13:22:14 -04:00
dkeruza-neo d9d076f120 Added 2Spicy and HOD-Ex resolution patches. 2025-04-06 13:11:58 -04:00
dkeruza a5633bbf1d Fixed an indentation in the Flatpak script. 2025-04-06 12:38:17 -04:00
dkeruza d72ac379ea Fixed Flatpak script.
There was a bug in the script preventing it to launch.
2025-04-06 11:34:49 -04:00
dkeruza-neo c5c44ce395 Fixed flatpak build script. 2025-04-06 11:33:14 -04:00
dkeruza 988a9ee6e3 Merge pull request #86 from dkeruza-neo/master
libCg.so path, better folder redirection.
2025-04-06 04:31:52 -04:00
dkeruza-neo ea19a688cc Recompiled shaders redirectio, and lib path. 2025-04-06 04:30:09 -04:00
dkeruza c5fa5871d1 Merge pull request #85 from dkeruza-neo/master
changelog and guide.
2025-04-05 18:17:04 -04:00
dkeruza-neo 5be9aef4e0 changelog and guide 2025-04-05 18:16:04 -04:00
25 changed files with 1431 additions and 863 deletions
+33
View File
@@ -0,0 +1,33 @@
name: 🐧 Linux AppImage
on:
workflow_call:
workflow_dispatch:
jobs:
linux-appimage:
name: "Build"
runs-on: ubuntu-22.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Packages
run: scripts/appimage/install-packages.sh
- name: Build Deps
run: scripts/appimage/build-deps.sh
- name: Compile Build
shell: bash
run: |
make
scripts/appimage/make-appimage.sh
- name: Upload AppImage
uses: actions/upload-artifact@v4
with:
name: "linux-appimage"
path: "lindbergh-loader.AppImage"
+5 -1
View File
@@ -24,10 +24,13 @@ jobs:
linux-flatpak:
name: 📦 Linux Flatpak
uses: "./.github/workflows/flatpak-build.yml"
linux-appimage:
name: 📦 Linux AppImage
uses: "./.github/workflows/appimage-build.yml"
create-release:
name: 📤 Create Release
needs: [linux-build, linux-flatpak]
needs: [linux-build, linux-flatpak, linux-appimage]
runs-on: ubuntu-22.04
if: github.ref == 'refs/heads/master'
steps:
@@ -55,3 +58,4 @@ jobs:
files: |
./artifacts/linux-flatpak/lindbergh-loader.flatpak
./artifacts/linux-build/build.tar.gz
./artifacts/linux-appimage/lindbergh-loader.AppImage
+5 -6
View File
@@ -12,9 +12,8 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
# Source files for various targets
file(GLOB LIND_OBJS "src/lindbergh/*.c" "src/lindbergh/*.cpp")
list(FILTER LIND_OBJS EXCLUDE REGEX "src/lindbergh/lindbergh.o")
list(FILTER LIND_OBJS EXCLUDE REGEX "src/lindbergh/evdevinput.o")
file(GLOB LIND_OBJS "src/lindbergh/*.c")
list(FILTER LIND_OBJS EXCLUDE REGEX "src/lindbergh/lindbergh.c")
file(GLOB XDIFF_SRCS "src/libxdiff/xdiff/*.c")
@@ -27,12 +26,12 @@ set(LINDBERGH_CXX_FLAGS ${LINDBERGH_C_FLAGS} -std=c++11)
set(LIBXDIFF_C_FLAGS -DHAVE_CONFIG_H -fPIC -m32)
set(LIBSEGAAPI_CXX_FLAGS -m32 -fPIC -shared)
set(LIBSEGAAPI_LINKER_FLAGS -m32 -lFAudio)
set(SHARED_LINKER_FLAGS -m32 -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lSDL2 -lm -lpthread -shared -lstdc++ -nostdlib -lasound)
set(SHARED_LINKER_FLAGS -m32 -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lXcursor -lSDL2 -lm -lpthread -shared -lstdc++ -nostdlib -lasound)
# Build executable for lindbergh
add_executable(lindyloader src/lindbergh/lindbergh.c src/lindbergh/evdevinput.c src/lindbergh/evdevinput.h)
add_executable(lindyloader src/lindbergh/lindbergh.c src/lindbergh/evdevinput.c src/lindbergh/evdevinput.h src/lindbergh/log.c src/lindbergh/log.h src/lindbergh/jvs.c src/lindbergh/jvs.h src/lindbergh/config.h src/lindbergh/config.c)
set_target_properties(lindyloader PROPERTIES OUTPUT_NAME "lindbergh")
set_target_properties(lindyloader PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} LINK_FLAGS "-m32")
set_target_properties(lindyloader PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} LINK_FLAGS "-m32 -lm")
target_compile_options(lindyloader PRIVATE ${LINDBERGH_EXE})
# Build static library libxdiff.a with its own flags
+20 -1
View File
@@ -2,8 +2,27 @@
All notable changes to this project will be documented in this file.
## [2.0.12]
### Added
- AppImage support.
### Fixed
- 2Spicy error in the scaling code, again.
## [2.0.11]
### Added
- Resolution patches for 2Spicy and HOD-Ex
- Code clean up.
### Fixed
- 2Spicy error in the scaling code.
## [2.0.10]
### Added
- Config option to specify a path for the library libCg.so required for some shader compilation.
- Improved folder patching for recompiled shaders (2Spicy, Rambo, Harly and HOD-EX).
- Cleaned a bit of lindbergh elf.
## [2.0.9]
- - lindbergh elf mostly redone to make the loader work as a stand alone mode.
- lindbergh elf mostly redone to make the loader work as a stand alone mode (read guide.md).
## [2.0.8]
### Added
+44
View File
@@ -224,3 +224,47 @@ flatpak install com.github.tchx84.Flatseal
```
Then, run Flatseal and grant Lindbergh Loader access to additional directories.
### Stand alone mode
Now, you have the option to run the games without copying any files to the game's folder.
Your system's 32 bit library folder, your current folder and the game's folder will be added to LD_LIBRARY_PATH env variable,
so you can han the loader libs in any of those locations.
Passing -c option you can specify the location of the .conf file like this:
```
./lindbergh -c /my/custom/folder/myconfig.conf
```
## -g or --gamepath
With this new option, you can specify the location of the game, where the game's ELF resides.
```
./lindbergh -g /my/custom/folder/hod4/elf
```
The loader will search for known elf files in that folder but you can also run it like this:
```
./lindbergh myCustom.elf -g /my/custom/folder/hod4/elf
```
The loader will load `myCustom.elf` inside the passed folder.
## Passing a folder containing an elf
With this option, you pass a full path, including an ELF name like this:
```
./lindbergh /my/custom/folder/hod4/elf/myCustom.elf
```
The loader will load the ELF in that folder.
You can combine all options.
You can have the needed libraries in the folder where `lindbergh` resides, in the system's 32 bit lib folder or in the game's folder.
The loader will try to find the files.
If by mistake, you pass both, a full path including an ELF and a path with -g option, the path passed with -g option will be omitted.
Remember that if the path containg spaces, which is not recommended, pass the path inside double quotes or `\` before the space.
+4
View File
@@ -72,6 +72,10 @@ EMULATE_CARDREADER AUTO
# Define the path to the eeprom.bin file
# EEPROM_PATH eeprom.bin
# If set, the library libCG.so needed for 2Spicy, Harley, Rambo and HOD-Ex shader recompilation
# will be loaded from the specifyied location. (include the name of file in the location)
# LIBCG_PATH /my/file/location/myLibCg.so
# Set the GPU vendor (0 = Autodetect / 1 = NVidia / 2 = AMD / 3 = ATI(AMD-PRO) / 4 = Intel / 5 = Unknown)
# GPU_VENDOR 0
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
APPIMAGEDIRNAME=lindbergh-loader.AppDir
OUTPUT_FOLDER=$(realpath "./$APPIMAGEDIRNAME")
mkdir build-deps
cd build-deps
# Build openal
wget https://github.com/kcat/openal-soft/archive/refs/tags/openal-soft-1.17.2.tar.gz
tar -xzf openal-soft-1.17.2.tar.gz
cd openal-soft-openal-soft-1.17.2/build
CFLAGS="-m32 -mstackrealign" CXXFLAGS="-m32 -std=c++14 -mstackrealign" cmake ../ -DALSOFT_BACKEND_PULSEAUDIO=ON -DALSOFT_BACKEND_ALSA=ON -DALSOFT_BACKEND_JACK=OFF -DALSOFT_BACKEND_SNDIO=ON
make -j4
sudo cp libopenal.so.1.17.2 /usr/lib/i386-linux-gnu/libopenal.so.0
cd ../../
# Build libSSL and libCrypto
wget https://github.com/openssl/openssl/archive/refs/tags/OpenSSL_0_9_7.tar.gz
tar -xzf OpenSSL_0_9_7.tar.gz
cd openssl-OpenSSL_0_9_7
cp ../../scripts/appimage/patches/openssl-0.9.7.patch .
./Configure shared 386 no-asm no-krb5 linux-pentium -m32
patch -p1 < openssl-0.9.7.patch
CFLAGS=-m32 make
sudo cp libcrypto.so.0.9.7 /usr/lib/i386-linux-gnu
sudo cp libssl.so.0.9.7 /usr/lib/i386-linux-gnu
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
echo "Install Packages Running..."
sudo dpkg --add-architecture i386
sudo apt update
sudo apt -y install --no-install-recommends \
build-essential gcc-multilib g++-multilib cmake fuse freeglut3-dev:i386 libsdl2-dev:i386 libfaudio-dev:i386 libvdpau1:i386 \
libstdc++5:i386 libxmu6:i386 libpcsclite1:i386 libncurses5:i386 unzip libsndio-dev:i386
sudo add-apt-repository ppa:kisak/kisak-mesa -y
sudo apt update -y && sudo apt upgrade -y
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env bash
APPIMAGENAME=lindbergh-loader
APPIMAGEDIRNAME=lindbergh-loader.AppDir
declare -a ADDED_LIBS=(
"libOpenGL.so.0"
"libGLdispatch.so.0"
"libGLX.so"
"libGLX_indirect.so.0"
"libGLX_mesa.so.0"
"libGL.so.1"
"libGLU.so.1"
"libglut.so.3"
"libglapi.so.0"
"libICE.so.6"
"libSM.so.6"
"libX11.so.6"
"libX11-xcb.so.1"
"libxcb.so.1"
"libxcb-dri2.so.0"
"libxcb-dri3.so.0"
"libxcb-glx.so.0"
"libXmu.so.6"
"libncurses.so.5"
"libpcsclite.so.1"
"libstdc++.so.5"
"libdrm.so.2"
"libexpat.so.1"
"libm.so.6"
"libopenal.so.0"
"libcrypto.so.0.9.7"
"libssl.so.0.9.7"
"libuuid.so.1"
)
set -e
if [ ! -f "linuxdeploy-x86_64" ]; then
wget -O "linuxdeploy-x86_64" https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
chmod +x "linuxdeploy-x86_64"
fi
if [ ! -f "appimagetool-x86_64" ]; then
wget -O "appimagetool-x86_64" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x "appimagetool-x86_64"
fi
cd build
OUTPUT_FOLDER=$(realpath "./$APPIMAGEDIRNAME")
rm -fr "$OUTPUT_FOLDER"
ADDED_LIBS_ARGS=()
for lib in "${ADDED_LIBS[@]}"; do
ADDED_LIBS_ARGS+=("--library=/usr/lib/i386-linux-gnu/$lib")
done
echo "Copying files into AppDir..."
mkdir -p "$OUTPUT_FOLDER/usr/bin"
mkdir -p "$OUTPUT_FOLDER/usr/lib"
mkdir -p "$OUTPUT_FOLDER/usr/lib32"
# We create the AppRun file
cat > "$OUTPUT_FOLDER/AppRun" << 'EOF'
#!/bin/bash
APP_IMG_ROOT="$(dirname "$(readlink -f "$0")")"
export APP_IMG_ROOT=$APP_IMG_ROOT
export LD_LIBRARY_PATH="$APP_IMG_ROOT/usr/lib32:$LD_LIBRARY_PATH"
exec "$APP_IMG_ROOT/usr/bin/lindbergh" "$@"
EOF
chmod +x "$OUTPUT_FOLDER/AppRun"
cp libkswapapi.so $OUTPUT_FOLDER/usr/lib
cp libsegaapi.so $OUTPUT_FOLDER/usr/lib
cp lindbergh.so $OUTPUT_FOLDER/usr/lib
cp libposixtime.so $OUTPUT_FOLDER/usr/lib
mv lindbergh $OUTPUT_FOLDER/usr/bin
mkdir -p $OUTPUT_FOLDER/usr/lib/dri
cp /usr/lib/i386-linux-gnu/dri/* $OUTPUT_FOLDER/usr/lib/dri
cd ..
echo "Running linuxdeploy to create AppDir..."
./linuxdeploy-x86_64 --appimage-extract-and-run --appdir="$OUTPUT_FOLDER" ${ADDED_LIBS_ARGS[@]} \
--desktop-file="./scripts/lindbergh-loader.desktop" \
--icon-file="./scripts/lindbergh-loader.png"
mv $OUTPUT_FOLDER/usr/lib/libkswapapi.so $OUTPUT_FOLDER/usr/lib32
mv $OUTPUT_FOLDER/usr/lib/libsegaapi.so $OUTPUT_FOLDER/usr/lib32
mv $OUTPUT_FOLDER/usr/lib/lindbergh.so $OUTPUT_FOLDER/usr/lib32
mv $OUTPUT_FOLDER/usr/lib/libposixtime.so $OUTPUT_FOLDER/usr/lib32
mv $OUTPUT_FOLDER/usr/lib/dri $OUTPUT_FOLDER/usr/lib32
unzip libs/Cg-3.1.zip -d $OUTPUT_FOLDER/usr/lib32
cp libs/libCg.so $OUTPUT_FOLDER/usr/lib32/libCg2.so
ln -s -f libposixtime.so $OUTPUT_FOLDER/usr/lib32/libposixtime.so.1
ln -s -f libposixtime.so $OUTPUT_FOLDER/usr/lib32/libposixtime.so.2.4
ln -s -f libkswapapi.so $OUTPUT_FOLDER/usr/lib32/libGLcore.so.1
ln -s -f libkswapapi.so $OUTPUT_FOLDER/usr/lib32/libnvidia-tls.so.1
echo "Generating AppImage..."
rm -f "$APPIMAGENAME.AppImage"
./appimagetool-x86_64 -v "$OUTPUT_FOLDER" "$APPIMAGENAME.AppImage" --appimage-extract-and-run
@@ -0,0 +1,13 @@
diff -ruN openssl-OpenSSL_0_9_7.orig/Makefile.ssl openssl-OpenSSL_0_9_7/Makefile.ssl
--- openssl-OpenSSL_0_9_7.orig/Makefile.ssl 2025-04-14 05:05:11.162340308 +0000
+++ openssl-OpenSSL_0_9_7/Makefile.ssl 2025-04-14 05:06:22.261420144 +0000
@@ -198,7 +198,7 @@
SHARED_SSL=libssl$(SHLIB_EXT)
SHARED_LIBS=$(SHARED_CRYPTO) $(SHARED_SSL)
SHARED_LIBS_LINK_EXTS=.so.$(SHLIB_MAJOR) .so
-SHARED_LDFLAGS=
+SHARED_LDFLAGS=-m32
GENERAL= Makefile
BASENAME= openssl
+54 -103
View File
@@ -1,18 +1,17 @@
app-id: com.github.lindberghloader
runtime: org.freedesktop.Platform
runtime-version: &runtime-version '24.08' # Define runtime version alias
runtime-version: &runtime-version '24.08'
sdk: org.freedesktop.Sdk
# Define reusable aliases
x-gl-versions: &gl-versions '24.08;1.4' # Define GL versions list alias
x-gl-versions: &gl-versions '24.08;1.4'
x-gl-merge-dirs: &gl-merge-dirs vulkan/icd.d;glvnd/egl_vendor.d;egl/egl_external_platform.d;OpenCL/vendors;lib/dri;lib/d3d;lib/gbm;vulkan/explicit_layer.d;vulkan/implicit_layer.d # Define GL merge dirs alias
x-i386-build-options: &i386-build-options # Define 32-bit build options alias
libdir: /app/lib32 # Default library install dir for autotools/meson etc.
x-i386-build-options: &i386-build-options
libdir: /app/lib32
env:
CC: /usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-gcc
CXX: /usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-g++
LD: /usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-ld
LDFLAGS: "-L/app/lib32" # Add custom lib dir to linker search path during build
LDFLAGS: "-L/app/lib32"
command: lindbergh
@@ -21,41 +20,32 @@ sdk-extensions:
- org.freedesktop.Sdk.Extension.toolchain-i386
- org.freedesktop.Sdk.Compat.i386
# Runtime arguments and permissions
finish-args:
# Environment variables needed at runtime
- --env=LD_LIBRARY_PATH=/app/lib32:$LD_LIBRARY_PATH # Make custom 32-bit libs findable
- --env=LD_LIBRARY_PATH=/app/lib32:$LD_LIBRARY_PATH
- --env=PATH=/app/bin:/usr/bin
# Permissions
- --allow=multiarch # Needed for running 32-bit code
- --allow=multiarch
- --share=ipc
- --share=network
- --socket=x11
- --socket=wayland
- --socket=pulseaudio
# WARNING: --device=all grants broad hardware access. Consider using --device=dri and specific devices if possible.
- --device=all
# Filesystem access - adjust path as needed
- --socket=pulseaudio
- --filesystem=~/Games
# Runtime extensions automatically downloaded and installed with the app
# Runtime extensions automatically downloaded and installed with the app (For some reason does not work)
add-extensions:
# 32-bit compatibility libraries from the runtime
org.freedesktop.Platform.Compat.i386:
directory: lib/i386-linux-gnu
version: *runtime-version # Match the main runtime version
# no-autodownload removed (or set to false) to ensure installation
version: *runtime-version
# 32-bit graphics driver extension
org.freedesktop.Platform.GL32:
directory: lib/i386-linux-gnu/GL
versions: *gl-versions # Use defined GL versions list
versions: *gl-versions
subdirectories: true
# no-autodownload removed (or set to false) to ensure installation
autodelete: false
add-ld-path: lib
merge-dirs: *gl-merge-dirs # Use defined merge dirs list
# Conditions for downloading and enabling the GL driver extension
merge-dirs: *gl-merge-dirs
download-if: active-gl-driver
enable-if: active-gl-driver
autoprune-unless: active-gl-driver
@@ -75,12 +65,11 @@ modules:
- name: libGLU
buildsystem: autotools
config-opts:
- --host=i686-unknown-linux-gnu # Specify 32-bit target host
- --prefix=/app # Base install prefix
# --libdir=/app/lib32 should be handled by build-options->libdir
- --host=i686-unknown-linux-gnu
- --prefix=/app
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options on 64-bit host
x86_64: *i386-build-options
sources:
- type: archive
url: https://mesa.freedesktop.org/archive/glu/glu-9.0.2.tar.xz
@@ -91,15 +80,15 @@ modules:
buildsystem: cmake
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/app # Base install prefix
- -DCMAKE_INSTALL_LIBDIR=/app/lib32 # Explicitly set library install dir for CMake
- -DCMAKE_INSTALL_PREFIX=/app
- -DCMAKE_INSTALL_LIBDIR=/app/lib32
sources:
- type: archive
url: https://downloads.sourceforge.net/freeglut/freeglut-3.2.2.tar.gz
sha256: c5944a082df0bba96b5756dddb1f75d0cd72ce27b5395c6c1dde85c2ff297a50
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
# Build 32-bit libFAudio (dynamic)
- name: libFAudio-Dynamic
@@ -107,40 +96,37 @@ modules:
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/app
- -DBUILD_SDL3=off # Assuming SDL3 integration is not needed
- -DCMAKE_INSTALL_LIBDIR=/app/lib32 # Explicitly set library install dir
- -DBUILD_SDL3=off
- -DCMAKE_INSTALL_LIBDIR=/app/lib32
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
env:
# Specific flags needed for FAudio 32-bit build?
CFLAGS: "-m32 -mstackrealign"
CXXFLAGS: "-m32 -mstackrealign"
sources:
- type: git
url: https://github.com/FNA-XNA/FAudio.git
tag: "24.12" # Using a specific tag
tag: "24.12"
# Build 32-bit libXmu
- name: libxmu
buildsystem: autotools
config-opts:
# --libdir=/app/lib32 should be handled by build-options->libdir
- --prefix=/app
- --with-shared # Ensure shared library is built
- --with-shared
sources:
- type: archive
url: https://xorg.freedesktop.org/releases/individual/lib/libXmu-1.2.1.tar.gz
sha256: bf0902583dd1123856c11e0a5085bd3c6e9886fbbd44954464975fd7d52eb599
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options (Standardized)
x86_64: *i386-build-options
# Build 32-bit libmd
- name: libmd
buildsystem: autotools
config-opts:
# --libdir=/app/lib32 should be handled by build-options->libdir
- --prefix=/app
sources:
- type: archive
@@ -148,13 +134,12 @@ modules:
sha256: 1bd6aa42275313af3141c7cf2e5b964e8b1fd488025caf2f971f43b00776b332
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
# Build 32-bit libbsd
- name: libbsd
buildsystem: autotools
config-opts:
# --libdir=/app/lib32 should be handled by build-options->libdir
- --prefix=/app
sources:
- type: archive
@@ -162,14 +147,12 @@ modules:
sha256: b88cc9163d0c652aaf39a99991d974ddba1c3a9711db8f1b5838af2a14731014
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
# Build 32-bit ncurses (very old version)
- name: ncurses
# WARNING: ncurses 5.9 is very old. Consider updating if possible.
buildsystem: autotools
config-opts:
# --libdir=/app/lib32 should be handled by build-options->libdir
- --prefix=/app
- --with-shared
- --without-debug
@@ -178,24 +161,22 @@ modules:
url: https://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz
sha256: 9046298fb440324c9d4135ecea7879ffed8546dd1b58e59430ea07a4633f563b
- type: patch
path: ./assets/ncurses-5.9.patch # Assuming this patch is necessary
path: ./assets/ncurses-5.9.patch
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
env:
CPPFLAGS: "-P" # Often needed for ncurses builds
CXXFLAGS: "-std=c++14" # Is C++14 really needed for ncurses build? Check this.
CPPFLAGS: "-P"
CXXFLAGS: "-std=c++14"
# Install pre-built 32-bit Nvidia Cg Toolkit (old)
- name: nvidia-cg-toolkit
# WARNING: Cg Toolkit is deprecated and old.
buildsystem: simple
build-commands:
- install -Dm755 usr/lib/libCg.so /app/lib32/libCg.so
- install -Dm755 usr/lib/libCgGL.so /app/lib32/libCgGL.so
sources:
- type: archive
# Consider hosting this locally or finding a more reliable source if possible
url: http://developer.download.nvidia.com/cg/Cg_3.1/Cg-3.1_April2012_x86.tgz
sha256: cef3591e436f528852db0e8c145d3842f920e0c89bcfb219c466797cb7b18879
@@ -203,32 +184,27 @@ modules:
- name: libsndio-dynamic
buildsystem: autotools
config-opts:
# --libdir=/app/lib32 should be handled by build-options->libdir
- --prefix=/app
- --enable-dynamic # Build shared library
# - --disable-static # Don't build static library
- --enable-alsa # Enable ALSA backend
- --enable-dynamic
- --enable-alsa
sources:
- type: archive
url: https://www.sndio.org/sndio-1.10.0.tar.gz
sha256: bebd3bfd01c50c9376cf3e7814b9379bed9e17d0393b5113b7eb7a3d0d038c54
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
# Build 32-bit libopenal (old version)
- name: libopenal
# WARNING: OpenAL Soft 1.17.2 is quite old. Consider updating.
buildsystem: cmake
config-opts:
- -DCMAKE_INSTALL_PREFIX=/app
- -DCMAKE_INSTALL_LIBDIR=/app/lib32 # Explicitly set library install dir
# Backend selection
- -DCMAKE_INSTALL_LIBDIR=/app/lib32
- -DALSOFT_BACKEND_PULSEAUDIO=ON
- -DALSOFT_BACKEND_ALSA=ON
- -DALSOFT_BACKEND_JACK=OFF
- -DALSOFT_BACKEND_SNDIO=ON # Depends on libsndio-dynamic
# Point to the previously built sndio library/headers
- -DALSOFT_BACKEND_SNDIO=ON
- -DSOUNDIO_INCLUDE_DIR=/app/include
- -DSOUNDIO_LIBRARY=/app/lib32/libsndio.so
sources:
@@ -237,29 +213,20 @@ modules:
sha256: 11ea176f6cbf9763dbce0953dd77ab72d032b4ad95f07afffb91844abb174033
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
env:
# Specific flags needed for OpenAL 32-bit build?
CFLAGS: "-m32 -mstackrealign"
CXXFLAGS: "-m32 -std=c++14 -mstackrealign" # Needs C++14?
CXXFLAGS: "-m32 -std=c++14 -mstackrealign"
# Build 32-bit OpenSSL (extremely old and insecure version)
- name: openssl
# CRITICAL WARNING: OpenSSL 0.9.7 is extremely old, unsupported, and has known vulnerabilities.
# This should be updated or removed if at all possible. Using it poses a security risk.
buildsystem: simple
build-commands:
# Configure for 32-bit Linux (pentium target), shared libs, install to /app
- ./Configure --prefix=/app --openssldir=/app/etc/ssl shared 386 no-asm no-krb5 linux-pentium
# Build using the 32-bit toolchain
# - make -j$(nproc) CC=/usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-gcc RANLIB=/usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-ranlib
- make CC=/usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-gcc RANLIB=/usr/lib/sdk/toolchain-i386/bin/i686-unknown-linux-gnu-ranlib
# Manually move built libraries to the target lib32 directory
- install -d /app/lib32
- mv libcrypto.so* /app/lib32/
- mv libssl.so* /app/lib32/
# Install headers (needed by other modules?) - will be cleaned later if not
# - make install_hdrs
sources:
- type: archive
url: https://github.com/openssl/openssl/archive/refs/tags/OpenSSL_0_9_7.tar.gz
@@ -269,23 +236,16 @@ modules:
- name: libpcsclite
buildsystem: meson
config-opts:
# --libdir=/app/lib32 should be handled by build-options->libdir
# - -Dprefix=/app
- -Dpolkit=false # Disable polkit integration
- -Dlibsystemd=false # Disable systemd integration
# -Dudevrulesdir=no # Might be needed if it tries to install udev rules
- -Dpolkit=false
- -Dlibsystemd=false
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options
x86_64: *i386-build-options
env:
# Meson cross-compilation setup often requires explicit flags and paths
CFLAGS: "-m32"
CXXFLAGS: "-m32"
LDFLAGS: "-m32 -L/app/lib32" # Ensure LDFLAGS includes -L/app/lib32
# Point pkg-config to custom and SDK 32-bit locations
LDFLAGS: "-m32 -L/app/lib32"
PKG_CONFIG_PATH: "/app/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/sdk/toolchain-i386/lib/pkgconfig"
# LD_LIBRARY_PATH might not be needed here if LDFLAGS is sufficient for linking stage
# LD_LIBRARY_PATH: "/app/lib32:/usr/lib/i386-linux-gnu/pkgconfig:$LD_LIBRARY_PATH"
sources:
- type: archive
url: https://pcsclite.apdu.fr/files/pcsc-lite-2.3.1.tar.xz
@@ -295,59 +255,50 @@ modules:
- name: lindbergh-loader
buildsystem: simple
build-commands:
# Ensure make finds the libraries built in previous steps
- export LD_LIBRARY_PATH=/app/lib32:$LD_LIBRARY_PATH
- make -j$(nproc) # Build the application
# Install application binary and required libraries/symlinks
- make -j$(nproc)
- mkdir -p /app/lib/i386-linux-gnu
- mkdir -p /app/lib/i386-linux-gnu/GL
- install -Dm755 ./build/lindbergh /app/bin/
- install -Dm755 ./build/lindbergh.so /app/lib32/
- install -Dm755 ./build/libsegaapi.so /app/lib32/
- install -Dm755 ./build/libkswapapi.so /app/lib32/
- install -Dm755 ./build/libposixtime.so /app/lib32/
# Install bundled library? Make sure this doesn't conflict with nvidia-cg-toolkit
- install -Dm755 ./libs/libCg.so /app/lib32/libCg2.so
# Create necessary symlinks in the lib32 directory
- ln -sf libposixtime.so /app/lib32/libposixtime.so.1
- ln -sf libposixtime.so /app/lib32/libposixtime.so.2.4
- ln -sf libopenal.so.1 /app/lib32/libopenal.so.0 # Link to correct openal versioned so
- ln -sf libopenal.so.1 /app/lib32/libopenal.so.0
- ln -sf libkswapapi.so /app/lib32/libGLcore.so.1
- ln -sf libkswapapi.so /app/lib32/libnvidia-tls.so.1
- mv /app/lib/libopenal* /app/lib32/
build-options:
arch:
x86_64: *i386-build-options # Apply 32-bit build options (for env vars mainly)
x86_64: *i386-build-options
sources:
- type: dir
path: ../.. # Assumes source code is one level up from manifest directory
path: ../..
# Setup dynamic linker configuration
- name: bundle-setup
buildsystem: simple
build-commands:
# Install ld.so.conf to ensure /app/lib32 is searched by the dynamic linker
- install -Dm644 -t /app/etc ld.so.conf
sources:
- type: file
path: ./assets/ld.so.conf # Assumes this file contains "/app/lib32"
path: ./assets/ld.so.conf
# Cleanup unnecessary files to reduce final flatpak size
- name: cleanup
buildsystem: simple
build-commands:
# Remove static libraries and libtool archives
- find /app/lib32 -name '*.a' -type f -delete
- find /app/lib32 -name '*.la' -type f -delete
# Remove any executables other than the main command
#- find /app/bin -mindepth 1 ! -name "lindbergh" -exec rm -f {} \;
# Remove development/debugging/documentation files
#- rm -rf /app/lib/pkgconfig /app/lib32/pkgconfig /app/share/pkgconfig
#- rm -rf /app/lib32/cmake /app/lib/debug /app/include /app/man /app/share /app/etc/bash_completion.d
# Remove specific libraries if they are not needed at runtime
# (These were in the original, assuming they are safe to remove)
- find /app/bin -mindepth 1 ! -name "lindbergh" -exec rm -f {} \;
- rm -rf /app/lib/pkgconfig /app/lib32/pkgconfig /app/share/pkgconfig
- rm -rf /app/lib32/cmake /app/lib/debug /app/include /app/man /app/share /app/etc/bash_completion.d
- rm -f /app/lib32/libmenu.so*
- rm -f /app/lib32/libform.so*
- rm -f /app/lib32/libpanel.so*
- rm -f /app/lib32/libXmuu.so*
#- rm -f /app/lib32/libbsd.so*
# Remove ncurses terminfo db if not needed
- rm -rf /app/lib/terminfo
- rm -f /app/lib32/libbsd.so*
- rm -rf /app/lib/terminfo
+7
View File
@@ -0,0 +1,7 @@
[Desktop Entry]
Name=Lindbergh Loader
Exec=lindbergh
Icon=lindbergh-loader
Type=Application
Categories=Utility;ConsoleOnly;
Terminal=true
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+183 -182
View File
@@ -29,10 +29,10 @@
typedef struct
{
uint32_t srcAddress;
uint32_t srcSize;
uint32_t destAddress;
uint32_t destSize;
uint32_t srcAddress;
uint32_t srcSize;
uint32_t destAddress;
uint32_t destSize;
} BaseboardCommand;
BaseboardCommand jvsCommand = {0};
@@ -40,16 +40,16 @@ BaseboardCommand serialCommand = {0};
typedef struct
{
uint32_t *data;
uint32_t offset;
uint32_t size;
uint32_t *data;
uint32_t offset;
uint32_t size;
} readData_t;
typedef struct
{
uint32_t offset;
uint32_t *data;
uint32_t size;
uint32_t offset;
uint32_t *data;
uint32_t size;
} writeData_t;
FILE *sram = NULL;
@@ -63,224 +63,225 @@ int jvsPacketSize = -1;
int initBaseboard()
{
char *sramPath = getConfig()->sramPath;
char *sramPath = getConfig()->sramPath;
sram = fopen(sramPath, "a");
sram = fopen(sramPath, "a");
// Create file if it doesn't exist
if (sram == NULL)
{
printf("Error: Cannot open %s\n", sramPath);
return 1;
}
fclose(sram);
sram = fopen(sramPath, "rb+");
fseek(sram, 0, SEEK_SET);
if (getConfig()->emulateJVS == 0 && strcmp(getConfig()->jvsPath, "none") != 0)
{
jvsFileDescriptor = openJVSSerial(getConfig()->jvsPath);
if (jvsFileDescriptor < 0)
// Create file if it doesn't exist
if (sram == NULL)
{
printf("Error: Failed to open %s for JVS\n", getConfig()->jvsPath);
return -1;
printf("Error: Cannot open %s\n", sramPath);
return 1;
}
fclose(sram);
sram = fopen(sramPath, "rb+");
fseek(sram, 0, SEEK_SET);
if (getConfig()->emulateJVS == 0 && strcmp(getConfig()->jvsPath, "none") != 0)
{
jvsFileDescriptor = openJVSSerial(getConfig()->jvsPath);
if (jvsFileDescriptor < 0)
{
printf("Error: Failed to open %s for JVS\n", getConfig()->jvsPath);
return -1;
}
initJVSSerial(jvsFileDescriptor);
startJVSFrameThread(&jvsFileDescriptor);
}
initJVSSerial(jvsFileDescriptor);
startJVSFrameThread(&jvsFileDescriptor);
}
return 0;
return 0;
}
ssize_t baseboardRead(int fd, void *buf, size_t count)
{
memcpy(buf, &sharedMemory[sharedMemoryIndex], count);
return count;
memcpy(buf, &sharedMemory[sharedMemoryIndex], count);
return count;
}
ssize_t baseboardWrite(int fd, const void *buf, size_t count)
{
memcpy(&sharedMemory[sharedMemoryIndex], buf, count);
return count;
memcpy(&sharedMemory[sharedMemoryIndex], buf, count);
return count;
}
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout)
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
struct timeval *restrict timeout)
{
return selectReply;
return selectReply;
}
int baseboardIoctl(int fd, unsigned int request, void *data)
{
switch (request)
{
case BASEBOARD_GET_VERSION:
{
uint8_t versionData[4] = {0x00, 0x19, 0x20, 0x07};
memcpy(data, versionData, 4);
return 0;
}
break;
case BASEBOARD_INIT:
{
// selectReply = -1; Considering adding this in
return 0;
}
break;
case BASEBOARD_READY: // Not sure if this is what it should be called
{
selectReply = 0;
return 0;
}
break;
case BASEBOARD_SEEK_SHM:
{
sharedMemoryIndex = (unsigned int)data;
return 0;
}
break;
case BASEBOARD_READ_SRAM:
{
readData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fread(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_WRITE_SRAM:
{
writeData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fwrite(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_REQUEST:
{
uint32_t *_data = data;
switch (_data[0])
switch (request)
{
case BASEBOARD_GET_SERIAL: // bcCmdSysInfoGetReq
case BASEBOARD_GET_VERSION:
{
serialCommand.destAddress = _data[1];
serialCommand.destSize = _data[2];
uint8_t versionData[4] = {0x00, 0x19, 0x20, 0x07};
memcpy(data, versionData, 4);
return 0;
}
break;
case BASEBOARD_WRITE_FLASH: // bcCmdSysFlashWrite
case BASEBOARD_INIT:
{
log_warn("The game attempted to write to the baseboard flash\n");
// selectReply = -1; Considering adding this in
return 0;
}
break;
case BASEBOARD_PROCESS_JVS:
case BASEBOARD_READY: // Not sure if this is what it should be called
{
jvsCommand.srcAddress = _data[1];
jvsCommand.srcSize = _data[2];
jvsCommand.destAddress = _data[3];
jvsCommand.destSize = _data[4];
memcpy(inputBuffer, &sharedMemory[jvsCommand.srcAddress], jvsCommand.srcSize);
selectReply = 0;
return 0;
}
break;
if (getConfig()->emulateJVS)
{
processPacket(&jvsPacketSize);
}
else if (jvsFileDescriptor >= 0)
{
for (int i = 0; i < jvsCommand.srcSize; i++)
case BASEBOARD_SEEK_SHM:
{
sharedMemoryIndex = (unsigned int)data;
return 0;
}
break;
case BASEBOARD_READ_SRAM:
{
readData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fread(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_WRITE_SRAM:
{
writeData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fwrite(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_REQUEST:
{
uint32_t *_data = data;
switch (_data[0])
{
write(jvsFileDescriptor, &inputBuffer[i], 1);
if (inputBuffer[i] == 0xF0)
{
setSenseLine(3);
}
else if (inputBuffer[i] == 0xF1)
{
setSenseLine(1);
}
case BASEBOARD_GET_SERIAL: // bcCmdSysInfoGetReq
{
serialCommand.destAddress = _data[1];
serialCommand.destSize = _data[2];
}
}
break;
case BASEBOARD_WRITE_FLASH: // bcCmdSysFlashWrite
{
log_warn("The game attempted to write to the baseboard flash\n");
}
break;
case BASEBOARD_PROCESS_JVS:
{
jvsCommand.srcAddress = _data[1];
jvsCommand.srcSize = _data[2];
jvsCommand.destAddress = _data[3];
jvsCommand.destSize = _data[4];
memcpy(inputBuffer, &sharedMemory[jvsCommand.srcAddress], jvsCommand.srcSize);
if (getConfig()->emulateJVS)
{
processPacket(&jvsPacketSize);
}
else if (jvsFileDescriptor >= 0)
{
for (int i = 0; i < jvsCommand.srcSize; i++)
{
write(jvsFileDescriptor, &inputBuffer[i], 1);
if (inputBuffer[i] == 0xF0)
{
setSenseLine(3);
}
else if (inputBuffer[i] == 0xF1)
{
setSenseLine(1);
}
}
}
}
break;
case BASEBOARD_GET_SENSE_LINE:
break;
default:
printf("Error: Unknown baseboard command %X\n", _data[0]);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
return 0;
}
break;
case BASEBOARD_GET_SENSE_LINE:
break;
default:
printf("Error: Unknown baseboard command %X\n", _data[0]);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
return 0;
}
break;
case BASEBOARD_RECEIVE:
{
uint32_t *_data = data;
switch (_data[0] & 0xFFF)
case BASEBOARD_RECEIVE:
{
uint32_t *_data = data;
case BASEBOARD_GET_SERIAL:
{
memcpy(&sharedMemory[serialCommand.destAddress + 96], SERIAL_STRING, strlen(SERIAL_STRING));
_data[1] = 1; // Set the status to success
}
break;
switch (_data[0] & 0xFFF)
{
case BASEBOARD_GET_SENSE_LINE:
{
_data[2] = getSenseLine();
_data[1] = 1; // Set the status to success
}
break;
case BASEBOARD_GET_SERIAL:
{
memcpy(&sharedMemory[serialCommand.destAddress + 96], SERIAL_STRING, strlen(SERIAL_STRING));
_data[1] = 1; // Set the status to success
}
break;
case BASEBOARD_PROCESS_JVS:
{
if (getConfig()->emulateJVS)
{
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, jvsPacketSize);
_data[2] = jvsCommand.destAddress;
_data[3] = jvsPacketSize;
_data[1] = 1; // Set the status to success
}
else if (jvsFileDescriptor >= 0)
{
JVSFrame frame = readJVSFrameFromThread();
memcpy(&sharedMemory[jvsCommand.destAddress], frame.buffer, frame.size);
_data[2] = jvsCommand.destAddress;
_data[3] = frame.size;
_data[1] = frame.ready;
}
case BASEBOARD_GET_SENSE_LINE:
{
_data[2] = getSenseLine();
_data[1] = 1; // Set the status to success
}
break;
case BASEBOARD_PROCESS_JVS:
{
if (getConfig()->emulateJVS)
{
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, jvsPacketSize);
_data[2] = jvsCommand.destAddress;
_data[3] = jvsPacketSize;
_data[1] = 1; // Set the status to success
}
else if (jvsFileDescriptor >= 0)
{
JVSFrame frame = readJVSFrameFromThread();
memcpy(&sharedMemory[jvsCommand.destAddress], frame.buffer, frame.size);
_data[2] = jvsCommand.destAddress;
_data[3] = frame.size;
_data[1] = frame.ready;
}
}
break;
default:
printf("Error: Unknown baseboard receive command %X\n", _data[0] & 0xFFF);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
return 0;
}
break;
default:
printf("Error: Unknown baseboard receive command %X\n", _data[0] & 0xFFF);
printf("Error: Unknown baseboard ioctl %X\n", request);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
return 0;
}
break;
default:
printf("Error: Unknown baseboard ioctl %X\n", request);
}
return 0;
}
+1 -4
View File
@@ -76,10 +76,7 @@ int initCardReader()
int carReaderGetFdIdx(int fd)
{
if (fd == serialFD[0].fd)
return 0;
return 1;
return (fd == serialFD[0].fd) ? 0 : 1;
}
uint8_t cardReaderGenChecksum(int fdIdx)
+1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
+14
View File
@@ -174,6 +174,8 @@ static int detectGame(uint32_t elf_crc)
config.gameStatus = WORKING;
config.jvsIOType = SEGA_TYPE_1;
config.gameType = ABC;
config.width = 640;
config.height = 480;
return 0;
}
break;
@@ -188,6 +190,8 @@ static int detectGame(uint32_t elf_crc)
config.gameStatus = WORKING;
config.jvsIOType = SEGA_TYPE_1;
config.gameType = ABC;
config.width = 640;
config.height = 480;
return 0;
}
break;
@@ -249,6 +253,8 @@ static int detectGame(uint32_t elf_crc)
config.gameNativeResolutions = "";
config.gameType = DRIVING;
config.gameStatus = WORKING;
config.width = 1280;
config.height = 768;
return 0;
}
break;
@@ -262,6 +268,8 @@ static int detectGame(uint32_t elf_crc)
config.gameNativeResolutions = "1280x768";
config.gameType = DRIVING;
config.gameStatus = WORKING;
config.width = 1280;
config.height = 768;
return 0;
}
break;
@@ -275,6 +283,8 @@ static int detectGame(uint32_t elf_crc)
config.gameNativeResolutions = "";
config.gameType = DRIVING;
config.gameStatus = WORKING;
config.width = 1280;
config.height = 768;
return 0;
}
break;
@@ -1113,6 +1123,9 @@ int readConfig(FILE *configFile, EmulatorConfig *config)
else if (strcmp(command, "SRAM_PATH") == 0)
strcpy(config->sramPath, getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "LIBCG_PATH") == 0)
strcpy(config->libCgPath, getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "EMULATE_RIDEBOARD") == 0) {
int defaultValue = config->emulateRideboard;
config->emulateRideboard = getNextIntOrAuto(saveptr, defaultValue);
@@ -1529,6 +1542,7 @@ int initConfig(const char* configFilePath)
config.lindberghColour = YELLOW;
strcpy(config.eepromPath, "eeprom.bin");
strcpy(config.sramPath, "sram.bin");
strcpy(config.libCgPath, "");
strcpy(config.jvsPath, "/dev/ttyUSB0");
strcpy(config.serial1Path, "/dev/ttyS0");
strcpy(config.serial2Path, "/dev/ttyS1");
+1
View File
@@ -232,6 +232,7 @@ typedef struct
int fullscreen;
char eepromPath[MAX_PATH_LENGTH];
char sramPath[MAX_PATH_LENGTH];
char libCgPath[MAX_PATH_LENGTH];
char jvsPath[MAX_PATH_LENGTH];
char serial1Path[MAX_PATH_LENGTH];
char serial2Path[MAX_PATH_LENGTH];
-1
View File
@@ -1,4 +1,3 @@
// #pragma once
int loadNewCursor(const char *cursorFileName, void **cursor, int customWidth, int customHeight);
void loadCursors();
+286 -26
View File
@@ -95,6 +95,12 @@ static int callback(struct dl_phdr_info *info, size_t size, void *data);
uint16_t basePortAddress = 0xFFFF;
/**
* @brief Signal handler for SIGSEGV.
*
* This function handles segmentation faults (SIGSEGV) and attempts to recover
* from certain types of memory access errors, particularly those related to I/O ports.
*/
/**
* Signal handler for the SIGSEGV signal, which is triggered when a process tries to access an illegal memory location.
* @param signal
@@ -177,6 +183,13 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
}
}
/**
* @brief Trims leading and trailing whitespace and quotes from a string.
*
* This function removes leading and trailing whitespace characters, as well as
* single and double quotes, from the input string.
* @param str The string to trim.
*/
char *trimOS_ID(char *str)
{
if (!str)
@@ -198,6 +211,12 @@ char *trimOS_ID(char *str)
return str;
}
/**
* @brief Checks if the operating system is Debian-based.
*
* This function reads the /etc/os-release file to determine if the current
* operating system is Debian or Ubuntu, or a derivative of them.
*/
bool checkOS_ID()
{
FILE *fp = fopen("/etc/os-release", "r");
@@ -249,6 +268,12 @@ bool checkOS_ID()
return found;
}
/**
* @brief Initialization function for the hook library.
*
* This function is called automatically when the library is loaded. It sets up
* signal handlers, initializes configuration, and performs other setup tasks.
*/
void __attribute__((constructor)) hook_init()
{
// Get offsets of the Game's ELF and calculate CRC32.
@@ -360,6 +385,13 @@ void __attribute__((constructor)) hook_init()
}
}
/**
* @brief Hook for the opendir function.
*
* This function intercepts calls to opendir and redirects them to different
* directories based on the game ID.
* @param dirname The name of the directory to open.
*/
DIR *opendir(const char *dirname)
{
DIR *(*_opendir)(const char *dirname) = dlsym(RTLD_NEXT, "opendir");
@@ -395,6 +427,13 @@ DIR *opendir(const char *dirname)
return _opendir(dirname);
}
/**
* @brief Hook for the remove function.
*
* This function intercepts calls to remove and redirects them to different
* paths based on the game ID.
* @param path The path to remove.
*/
int remove(const char *path)
{
int (*_remove)(const char *path) = dlsym(RTLD_NEXT, "remove");
@@ -416,6 +455,13 @@ int remove(const char *path)
return _remove(path);
}
/**
* @brief Hook for the mkdir function.
*
* This function intercepts calls to mkdir and redirects them to different
* paths based on the game ID.
* @param path The path to create.
*/
int mkdir(const char *path, mode_t mode)
{
int (*_mkdir)(const char *path, mode_t mode) = dlsym(RTLD_NEXT, "mkdir");
@@ -437,6 +483,13 @@ int mkdir(const char *path, mode_t mode)
return _mkdir(path, mode);
}
/**
* @brief Hook for the __xstat64 function.
*
* This function intercepts calls to __xstat64 and redirects them to different
* paths based on the game ID.
* @param path The path to stat.
*/
int __xstat64(int ver, const char *path, struct stat64 *stat_buf)
{
int (*___xstat64)(int ver, const char *path, struct stat64 *stat_buf) = dlsym(RTLD_NEXT, "__xstat64");
@@ -448,6 +501,13 @@ int __xstat64(int ver, const char *path, struct stat64 *stat_buf)
return ___xstat64(ver, path, stat_buf);
}
/**
* @brief Hook for the open function.
*
* This function intercepts calls to open and redirects them to different
* files or devices based on the pathname.
* @param pathname The path to open.
*/
int open(const char *pathname, int flags, ...)
{
va_list args;
@@ -534,6 +594,13 @@ int open(const char *pathname, int flags, ...)
return _open(pathname, flags, mode);
}
/**
* @brief Hook for the open64 function.
*
* This function intercepts calls to open64 and redirects them to different
* files or devices based on the pathname.
* @param pathname The path to open.
*/
int open64(const char *pathname, int flags, ...)
{
va_list args;
@@ -544,6 +611,13 @@ int open64(const char *pathname, int flags, ...)
return open(pathname, flags, mode);
}
/**
* @brief Hook for the fopen function.
*
* This function intercepts calls to fopen and redirects them to different
* files based on the pathname.
* @param pathname The path to open.
*/
FILE *fopen(const char *restrict pathname, const char *restrict mode)
{
FILE *(*_fopen)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen");
@@ -757,6 +831,13 @@ FILE *fopen(const char *restrict pathname, const char *restrict mode)
return _fopen(pathname, mode);
}
/**
* @brief Hook for the fopen64 function.
*
* This function intercepts calls to fopen64 and redirects them to different
* files based on the pathname.
* @param pathname The path to open.
*/
FILE *fopen64(const char *pathname, const char *mode)
{
FILE *(*_fopen64)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen64");
@@ -857,6 +938,13 @@ FILE *fopen64(const char *pathname, const char *mode)
return _fopen64(pathname, mode);
}
/**
* @brief Hook for the fclose function.
*
* This function intercepts calls to fclose and performs cleanup operations
* for the file hooks.
* @param stream The file stream to close.
*/
int fclose(FILE *stream)
{
int (*_fclose)(FILE *stream) = dlsym(RTLD_NEXT, "fclose");
@@ -878,6 +966,14 @@ int fclose(FILE *stream)
}
return _fclose(stream);
}
/**
* @brief Hook for the openat function.
*
* This function intercepts calls to openat and redirects them to different
* files or devices based on the pathname.
* @param pathname The path to open.
*/
int openat(int dirfd, const char *pathname, int flags, ...)
{
int (*_openat)(int dirfd, const char *pathname, int flags) = dlsym(RTLD_NEXT, "openat");
@@ -892,6 +988,13 @@ int openat(int dirfd, const char *pathname, int flags, ...)
return _openat(dirfd, pathname, flags);
}
/**
* @brief Hook for the close function.
*
* This function intercepts calls to close and performs cleanup operations
* for the file hooks.
* @param fd The file descriptor to close.
*/
int close(int fd)
{
int (*_close)(int fd) = dlsym(RTLD_NEXT, "close");
@@ -908,6 +1011,13 @@ int close(int fd)
return _close(fd);
}
/**
* @brief Hook for the fgets function.
*
* This function intercepts calls to fgets and redirects them to different
* files based on the stream.
* @param stream The file stream to read from.
*/
char *fgets(char *str, int n, FILE *stream)
{
char *(*_fgets)(char *str, int n, FILE *stream) = dlsym(RTLD_NEXT, "fgets");
@@ -945,6 +1055,13 @@ char *fgets(char *str, int n, FILE *stream)
return _fgets(str, n, stream);
}
/**
* @brief Hook for the read function.
*
* This function intercepts calls to read and redirects them to different
* files or devices based on the file descriptor.
* @param fd The file descriptor to read from.
*/
ssize_t read(int fd, void *buf, size_t count)
{
int (*_read)(int fd, void *buf, size_t count) = dlsym(RTLD_NEXT, "read");
@@ -964,17 +1081,11 @@ ssize_t read(int fd, void *buf, size_t count)
return driveboardRead(fd, buf, count);
}
if (fd == hooks[SERIAL0] && getConfig()->emulateCardreader)
if ((fd == hooks[SERIAL0] || fd == hooks[SERIAL1]) && getConfig()->emulateCardreader)
{
return cardReaderRead(fd, buf, count);
}
if (fd == hooks[SERIAL1] && getConfig()->emulateCardreader)
{
return cardReaderRead(fd, buf, count);
}
// If we don't hook the serial just reply with nothing
if (fd == hooks[SERIAL0] || fd == hooks[SERIAL1])
{
if (gId == PRIMEVAL_HUNT && getConfig()->emulateTouchscreen == 1)
@@ -994,6 +1105,13 @@ ssize_t read(int fd, void *buf, size_t count)
return _read(fd, buf, count);
}
/**
* @brief Hook for the fread function.
*
* This function intercepts calls to fread and redirects them to different
* files based on the stream.
* @param stream The file stream to read from.
*/
size_t fread(void *buf, size_t size, size_t count, FILE *stream)
{
size_t (*_fread)(void *buf, size_t size, size_t count, FILE *stream) = dlsym(RTLD_NEXT, "fread");
@@ -1014,11 +1132,6 @@ size_t fread(void *buf, size_t size, size_t count, FILE *stream)
return freadReplace(buf, size, count, fileRead[FILE_RW2]);
}
// if (stream == fileHooks[FILE_HARLEY])
// {
// return harleyFreadReplace(buf, size, count, fileHooks[FILE_HARLEY]);
// }
if (stream == fileHooks[FILE_FONT_ABC])
{
memcpy(buf, fontABC + fontABCidx, size * count);
@@ -1046,6 +1159,13 @@ size_t fread(void *buf, size_t size, size_t count, FILE *stream)
return _fread(buf, size, count, stream);
}
/**
* @brief Hook for the ftell function.
*
* This function intercepts calls to ftell and redirects them to different
* files based on the stream.
* @param stream The file stream to get the position from.
*/
long int ftell(FILE *stream)
{
long int (*_ftell)(FILE *stream) = dlsym(RTLD_NEXT, "ftell");
@@ -1067,6 +1187,13 @@ long int ftell(FILE *stream)
return _ftell(stream);
}
/**
* @brief Hook for the fseek function.
*
* This function intercepts calls to fseek and redirects them to different
* files based on the stream.
* @param stream The file stream to seek in.
*/
int fseek(FILE *stream, long int offset, int whence)
{
int (*_fseek)(FILE *stream, long int offset, int whence) = dlsym(RTLD_NEXT, "fseek");
@@ -1090,6 +1217,13 @@ int fseek(FILE *stream, long int offset, int whence)
return _fseek(stream, offset, whence);
}
/**
* @brief Hook for the rewind function.
*
* This function intercepts calls to rewind and redirects them to different
* files based on the stream.
* @param stream The file stream to rewind.
*/
void rewind(FILE *stream)
{
void (*_rewind)(FILE *stream) = dlsym(RTLD_NEXT, "rewind");
@@ -1103,6 +1237,13 @@ void rewind(FILE *stream)
_rewind(stream);
}
/**
* @brief Hook for the write function.
*
* This function intercepts calls to write and redirects them to different
* files or devices based on the file descriptor.
* @param fd The file descriptor to write to.
*/
ssize_t write(int fd, const void *buf, size_t count)
{
int (*_write)(int fd, const void *buf, size_t count) = dlsym(RTLD_NEXT, "write");
@@ -1128,12 +1269,7 @@ ssize_t write(int fd, const void *buf, size_t count)
return driveboardWrite(fd, buf, count);
}
if (fd == hooks[SERIAL0] && getConfig()->emulateCardreader)
{
return cardReaderWrite(fd, buf, count);
}
if (fd == hooks[SERIAL1] && getConfig()->emulateCardreader)
if ((fd == hooks[SERIAL0] || fd == hooks[SERIAL1]) && getConfig()->emulateCardreader)
{
return cardReaderWrite(fd, buf, count);
}
@@ -1141,6 +1277,13 @@ ssize_t write(int fd, const void *buf, size_t count)
return _write(fd, buf, count);
}
/**
* @brief Hook for the ioctl function.
*
* This function intercepts calls to ioctl and redirects them to different
* devices based on the file descriptor.
* @param fd The file descriptor to perform the ioctl on.
*/
int ioctl(int fd, unsigned int request, void *data)
{
int (*_ioctl)(int fd, int request, void *data) = dlsym(RTLD_NEXT, "ioctl");
@@ -1170,6 +1313,13 @@ int ioctl(int fd, unsigned int request, void *data)
return _ioctl(fd, request, data);
}
/**
* @brief Hook for the tcgetattr function.
*
* This function intercepts calls to tcgetattr and redirects them to different
* devices based on the file descriptor.
* @param fd The file descriptor to get the attributes from.
*/
int tcgetattr(int fd, struct termios *termios_p)
{
int (*_tcgetattr)(int fd, struct termios *termios_p) = dlsym(RTLD_NEXT, "tcgetattr");
@@ -1180,6 +1330,13 @@ int tcgetattr(int fd, struct termios *termios_p)
return _tcgetattr(fd, termios_p);
}
/**
* @brief Hook for the tcsetattr function.
*
* This function intercepts calls to tcsetattr and redirects them to different
* devices based on the file descriptor.
* @param fd The file descriptor to set the attributes on.
*/
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
{
int (*_tcsetattr)(int fd, int optional_actions, const struct termios *termios_p) = dlsym(RTLD_NEXT, "tcsetattr");
@@ -1190,6 +1347,13 @@ int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
return _tcsetattr(fd, optional_actions, termios_p);
}
/**
* @brief Hook for the cfgetispeed function.
*
* This function intercepts calls to cfgetispeed and redirects them to different
* devices based on the file descriptor.
* @param termios_p The termios structure to get the input speed from.
*/
speed_t cfgetispeed(const struct termios *termios_p)
{
speed_t (*_cfgetispeed)(const struct termios *termios_p) = dlsym(RTLD_NEXT, "cfgetispeed");
@@ -1200,6 +1364,13 @@ speed_t cfgetispeed(const struct termios *termios_p)
return _cfgetispeed(termios_p);
}
/**
* @brief Hook for the cfgetospeed function.
*
* This function intercepts calls to cfgetospeed and redirects them to different
* devices based on the file descriptor.
* @param termios_p The termios structure to get the output speed from.
*/
speed_t cfgetospeed(const struct termios *termios_p)
{
speed_t (*_cfgetospeed)(const struct termios *termios_p) = dlsym(RTLD_NEXT, "cfgetospeed");
@@ -1210,6 +1381,13 @@ speed_t cfgetospeed(const struct termios *termios_p)
return _cfgetospeed(termios_p);
}
/**
* @brief Hook for the cfsetispeed function.
*
* This function intercepts calls to cfsetispeed and redirects them to different
* devices based on the file descriptor.
* @param termios_p The termios structure to set the input speed on.
*/
int cfsetispeed(struct termios *termios_p, speed_t speed)
{
int (*_cfsetispeed)(struct termios *termios_p, speed_t speed) = dlsym(RTLD_NEXT, "cfsetispeed");
@@ -1220,6 +1398,13 @@ int cfsetispeed(struct termios *termios_p, speed_t speed)
return _cfsetispeed(termios_p, speed);
}
/**
* @brief Hook for the cfsetospeed function.
*
* This function intercepts calls to cfsetospeed and redirects them to different
* devices based on the file descriptor.
* @param termios_p The termios structure to set the output speed on.
*/
int cfsetospeed(struct termios *termios_p, speed_t speed)
{
int (*_cfsetospeed)(struct termios *termios_p, speed_t speed) = dlsym(RTLD_NEXT, "cfsetospeed");
@@ -1230,6 +1415,14 @@ int cfsetospeed(struct termios *termios_p, speed_t speed)
return _cfsetospeed(termios_p, speed);
}
/**
* @brief Hook for the select function.
*
* This function intercepts calls to select and redirects them to different
* devices based on the file descriptor.
* @param nfds The highest file descriptor number plus one.
* @param readfds The file descriptors to check for readability.
*/
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
struct timeval *restrict timeout)
{
@@ -1254,6 +1447,13 @@ int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set
return _select(nfds, readfds, writefds, exceptfds, timeout);
}
/**
* @brief Hook for the system function.
*
* This function intercepts calls to system and redirects them to different
* commands based on the command string.
* @param command The command to execute.
*/
int system(const char *command)
{
int (*_system)(const char *command) = dlsym(RTLD_NEXT, "system");
@@ -1291,6 +1491,36 @@ int system(const char *command)
return _system(command);
}
/**
* @brief Hook for the strncpy function.
*
* This function intercepts calls to strncpy and redirects them to different
* strings based on the game ID.
* @param dest The destination string.
*/
char *strncpy(char *dest, const char *src, size_t n)
{
char *(*_strncpy)(char *dest, const char *src, size_t n) = dlsym(RTLD_NEXT, "strncpy");
switch (gId)
{
case HARLEY_DAVIDSON:
case RAMBO:
case THE_HOUSE_OF_THE_DEAD_EX:
case TOO_SPICY:
if (getConfig()->GPUVendor != NVIDIA_GPU && (strstr(src, "../fs/compiledshader") != NULL || strstr(src, "../fs/compiled") != NULL))
return _strncpy(dest, "../fs/compiledmesa", n);
}
return _strncpy(dest, src, n);
}
/**
* @brief Hook for the iopl function.
*
* This function intercepts calls to iopl and always returns 0.
* @param level The I/O privilege level to set.
* @return Always returns 0.
*/
int iopl(int level)
{
return 0;
@@ -1306,10 +1536,11 @@ void kswap_collect(void *p)
}
/**
* Hook for function used by Primevil
* @param base The number to raise to the exponent
* @param exp The exponent to raise the number to
* @return The result of raising the number to the exponent
* @brief Hook for the powf function.
*
* This function intercepts calls to powf and redirects them to pow.
* @param base The base number.
* @param exponent The exponent.
*/
float powf(float base, float exponent)
{
@@ -1324,6 +1555,13 @@ int sem_wait(sem_t *sem)
}
*/
/**
* @brief Gets the machine's IP address.
*
* This function retrieves the machine's IP address and stores it in the
* provided sockaddr_in structure.
* @param addr The sockaddr_in structure to store the IP address in.
*/
int get_machine_ip(struct sockaddr_in *addr)
{
struct ifaddrs *ifaddr, *ifa;
@@ -1396,7 +1634,11 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
}
/**
* Callback function to get the offset and size of the execution program in memory of the ELF we hook to.
* @brief Callback function for dl_iterate_phdr.
*
* This function is called for each program header in the loaded shared
* objects. It is used to calculate the CRC32 of the ELF file.
* @param info Information about the program header.
*/
static int callback(struct dl_phdr_info *info, size_t size, void *data)
{
@@ -1408,7 +1650,11 @@ static int callback(struct dl_phdr_info *info, size_t size, void *data)
}
/**
* Stop the game changing the DISPLAY environment variable
* @brief Hook for the setenv function.
*
* This function intercepts calls to setenv and prevents the game from
* changing the DISPLAY environment variable.
* @param name The name of the environment variable.
*/
int setenv(const char *name, const char *value, int overwrite)
{
@@ -1423,7 +1669,10 @@ int setenv(const char *name, const char *value, int overwrite)
}
/**
* Fake the TEA_DIR environment variable to games that require it to run
* @brief Hook for the getenv function.
*
* This function "Fakes" the TEA_DIR environment variable to games that require it to run
* @param name The name of the environment variable.
*/
char *getenv(const char *name)
{
@@ -1470,7 +1719,11 @@ char *getenv(const char *name)
}
/**
* Stop the game unsetting the DISPLAY environment variable
* @brief Hook for the unsetenv function.
*
* This function intercepts calls to unsetenv and prevents the game from
* unsetting the DISPLAY environment variable.
* @param name The name of the environment variable.
*/
int unsetenv(const char *name)
{
@@ -1497,6 +1750,13 @@ char *__strdup(const char *string)
return ___strdup(string);
}
/**
* @brief Hook for the localtime_r function.
*
* This function intercepts calls to localtime_r and redirects them to different
* times based on the game ID.
* @param timep The time to convert.
*/
struct tm *localtime_r(const time_t *timep, struct tm *result)
{
struct tm *(*_localtime_r)(const time_t *, struct tm *) =
+282 -493
View File
@@ -1,15 +1,14 @@
#include <dirent.h>
#include <libgen.h>
#include <linux/limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/stat.h>
#include <limits.h>
#include "config.h"
#include "evdevinput.h"
#include "version.h"
#include "log.h"
@@ -19,59 +18,15 @@
#define PRELOAD_FILE_NAME "lindbergh.so"
#define TEAM "bobbydilley, retrofan, dkeruza-neo, doozer, francesco, rolel, caviar-x"
#define LINDBERGH_CONFIG_PATH "LINDBERGH_CONFIG_PATH"
#define MAX_PATH_LEN 1024
/**
* @brief Calculates the CRC32 of a single byte.
*
* This function calculates the CRC32 value for a given byte of data.
*
* @param crc The current CRC32 value.
* @param data The byte of data to calculate the CRC32 for.
* @return The updated CRC32 value.
*/
uint32_t calcCrc32(uint32_t crc, uint8_t data)
{
crc ^= data; // No shift needed; working in LSB-first order
for (int i = 0; i < 8; i++)
{
if (crc & 1)
{
crc = (crc >> 1) ^ 0xEDB88320;
}
else
{
crc = (crc >> 1);
}
}
return crc;
}
#define LINDBERGH_LOADER_CURRENT_DIR "LINDBERGH_LOADER_CURRENT_DIR"
uint32_t elf_crc = 0;
// List of all lindbergh executables known, not including the test executables
char *games[] = {"main.exe",
"ramboM.elf",
"vt3_Lindbergh",
"hummer_Master.elf",
"drive.elf",
"chopperM.elf",
"vsg",
"Jennifer",
"dsr",
"abc",
"hod4M.elf",
"lgj_final",
"vt3",
"id4.elf",
"id5.elf",
"lgjsp_app",
"gsevo",
"vf5",
"apacheM.elf",
"hodexRI.elf",
"a.elf",
"END"};
char *games[] = {"a.elf", "abc", "apacheM.elf", "chopperM.elf", "drive.elf", "dsr",
"gsevo", "hod4M.elf", "hodexRI.elf", "hummer_Master.elf", "id4.elf", "id5.elf",
"Jennifer", "lgj_final", "lgjsp_app", "main.exe", "mj4", "ramboM.elf",
"vf5", "vsg", "vt3", "vt3_Lindbergh", "END"};
/**
* An array containin clean games elf's CRC32
@@ -128,6 +83,7 @@ uint32_t cleanElfCRC32[] = {
0xB0A96E34, // DVP-0043 | vf5
0xF99E5635, // DVP-0044 | drive.elf
0x4143F6B4, // DVP-0048A | main.exe
0x65489691, // DVP-0049 | mj4
0x653BC83B, // DVP-0057 | a.elf
0x04D88552, // DVP-0057B | a.elf
0x089D6051, // DVP-0060 | dsr
@@ -138,6 +94,7 @@ uint32_t cleanElfCRC32[] = {
0x2E6732A3, // DVP-0070F | id5.elf
0xF99A3CDB, // DVP-0075 | id5.elf
0x05647A8E, // DVP-0079 | hummer_Master.elf
0x0AD7CF0F, // DVP-0081 | mj4
0x4442EA15, // DVP-0083 | hummer_Master.elf
0x8DF6BBF9, // DVP-0084 | id5.elf
0x2AF8004E, // DVP-0084A | id5.elf
@@ -152,67 +109,30 @@ uint32_t cleanElfCRC32[] = {
int cleanElfCRC32Count = sizeof(cleanElfCRC32) / sizeof(uint32_t);
/**
* @brief Looks up a CRC value in the cleanElfCRC32 table.
*
* This function searches for a given CRC value within the cleanElfCRC32 array.
*
* @param crc The CRC value to look up.
* @return 1 if the CRC is found in the table, 0 otherwise.
*/
int lookupCrcTable(uint32_t crc)
uint32_t calcCrc32(uint32_t crc, uint8_t data)
{
for (int x = 0; x < cleanElfCRC32Count; x++)
crc ^= data;
for (int i = 0; i < 8; i++)
{
if (cleanElfCRC32[x] == crc)
return 1;
}
return 0;
}
/**
* @brief Removes double quotes from a string.
*
* This function iterates through the input string and removes any double quote characters.
* It modifies the string in place.
*
* @param str The string to remove double quotes from.
*/
void removeDoubleQuotes(char *str)
{
if (str == NULL)
{
return;
}
int j = 0;
for (int i = 0; str[i] != '\0'; i++)
{
if (str[i] != '"')
if (crc & 1)
{
str[j] = str[i];
j++;
crc = (crc >> 1) ^ 0xEDB88320;
}
else
{
crc = (crc >> 1);
}
}
str[j] = '\0';
return crc;
}
/**
* @brief Calculates the CRC32 of a file.
*
* This function calculates the CRC32 of the specified file.
*
* @param filename The path to the file.
* @param crc The pointer to the variable that will contain the CRC32.
* @return 0 if the CRC32 was calculated successfully, -1 otherwise.
*/
int calculateFileCRC32(const char *filename, uint32_t *crc)
int calculateCRC32inChunks(const char *filename, uint32_t *crc)
{
FILE *file = fopen(filename, "rb");
if (!file)
if (file == NULL)
{
log_error("Could not open file to calculate the CRC32.");
return -1;
return EXIT_FAILURE;
}
*crc = 0xFFFFFFFF;
@@ -230,19 +150,34 @@ int calculateFileCRC32(const char *filename, uint32_t *crc)
return 0;
}
/**
* @brief Checks if the ELF file is clean based on its CRC32.
*
* This function calculates the CRC32 of the specified ELF file and compares it
* against a list of known clean ELF CRC32 values.
* @param command The command line string containing the path to the ELF file.
*/
int lookupCrcTable(uint32_t crc)
{
for (int x = 0; x < cleanElfCRC32Count; x++)
{
if (cleanElfCRC32[x] == crc)
return 1;
}
return 0;
}
int fileExists(const char *path)
{
struct stat buffer;
return stat(path, &buffer) == 0;
}
int dirExists(const char *path)
{
struct stat buffer;
return stat(path, &buffer) == 0 && S_ISDIR(buffer.st_mode);
}
void isCleanElf(char *command)
{
char *last_space = strrchr(command, ' ');
char *lastSpace = strrchr(command, ' ');
size_t length;
if (last_space != NULL && strcmp(last_space, " -t") == 0)
length = (size_t)(last_space - command);
if (lastSpace != NULL && strcmp(lastSpace, " -t") == 0)
length = (size_t)(lastSpace - command);
else
length = strlen(command);
@@ -250,10 +185,14 @@ void isCleanElf(char *command)
strncpy(elfName, command, length);
elfName[length] = '\0';
removeDoubleQuotes(elfName);
if (dirExists(elfName))
{
log_error("There is a folder named \'%s\' in the current directory instead of a game's ELF.", elfName + 2);
exit(EXIT_FAILURE);
}
uint32_t crc;
if (calculateFileCRC32(elfName, &crc) != 0)
if (calculateCRC32inChunks(elfName, &crc) != 0)
return;
crc = crc ^ 0xFFFFFFFF;
@@ -268,259 +207,146 @@ void isCleanElf(char *command)
}
}
/**
* Tests if the game uses a seperate elf for test mode
* and updates the command line to this elf.
*
* @param name The command path to run
*/
void testModePath(char *name)
void extractPathFromProg(const char *input, char *out_path, char *out_prog)
{
printf("name: %s\n", name);
char *pos;
// Check if a different testmode elf is used
pos = strstr(name, "hod4M.elf");
if (pos != NULL)
{
strcpy(pos, "hod4testM.elf");
return;
}
pos = strstr(name, "hodexRI.elf");
if (pos != NULL)
{
strcpy(pos, "hodextestR.elf");
return;
}
pos = strstr(name, "/Jennifer/\"Jennifer");
if (pos != NULL)
{
strcpy(pos, "/Jennifer/\"../JenTest/JenTest");
return;
}
pos = strstr(name, "/Jennifer");
if (pos != NULL)
{
strcpy(pos, "/../JenTest/JenTest");
return;
}
pos = strstr(name, "apacheM.elf");
if (pos != NULL)
{
strcpy(pos, "apachetestM.elf");
return;
}
pos = strstr(name, "/vt3_Lindbergh/\"vt3_Lindbergh");
if (pos != NULL)
{
strcpy(pos, "/vt3_Lindbergh/\"vt3_testmode");
return;
}
pos = strstr(name, "/vt3_Lindbergh");
if (pos != NULL)
{
strcpy(pos, "/vt3_testmode");
return;
}
// Otherwise add the standard -t to the end
strcat(name, " -t");
char tmp1[MAX_PATH_LENGTH], tmp2[MAX_PATH_LENGTH];
strncpy(tmp1, input, MAX_PATH_LENGTH);
strncpy(tmp2, input, MAX_PATH_LENGTH);
strncpy(out_prog, basename(tmp1), MAX_PATH_LENGTH);
strncpy(out_path, dirname(tmp2), MAX_PATH_LENGTH);
}
/**
* @brief Checks if a given path is a valid directory.
*
* This function uses the stat system call to determine if the provided path
* corresponds to a directory.
* @param path The path to check.
*/
bool isValidDirectory(const char *path)
void testModePath(char *program)
{
struct stat statbuf;
if (stat(path, &statbuf) != 0)
return false;
return S_ISDIR(statbuf.st_mode);
if (strcmp(program, "hod4M.elf") == 0)
strcpy(program, "hod4testM.elf");
else if (strcmp(program, "hodexRI.elf") == 0)
strcpy(program, "hodextestR.elf");
else if (strcmp(program, "apacheM.elf") == 0)
strcpy(program, "apachetestM.elf");
else if (strcmp(program, "vt3_Lindbergh") == 0)
strcpy(program, "vt3_testmode");
else if (strcmp(program, "Jennifer") == 0)
strcpy(program, "../JenTest/JenTest");
else
strcat(program, " -t");
}
/**
* @brief Finds a suitable 32-bit library folder.
*
* This function searches for a directory that is likely to contain 32-bit
* libraries, based on a predefined list of candidate paths.
* @return A dynamically allocated string containing the path to the found
* directory, or NULL if no suitable directory is found.
*/
char *find32bLibFolder(void)
char *findPreloadLibrary(const char *originalDir, const char *gameDir)
{
const char *folderCandidates[] = {"/app/lib32", "/usr/lib/i386-linux-gnu", "/usr/lib/i686-linux-gnu", "/usr/lib32", "/usr/lib"};
int numCandidates = sizeof(folderCandidates) / sizeof(folderCandidates[0]);
static char result[MAX_PATH_LENGTH];
char appImageLib[MAX_PATH_LENGTH];
snprintf(appImageLib, MAX_PATH_LENGTH, "%s/usr/lib32", getenv("APP_IMG_ROOT"));
const char *folderCandidates[] = {"/app/lib32", appImageLib, originalDir, "/usr/lib/i386-linux-gnu", "/usr/lib/i686-linux-gnu",
"/usr/lib32", "/usr/lib", NULL};
char currentPath[PATH_MAX];
struct stat st;
char *resultPath = NULL;
for (int i = 0; i < numCandidates; ++i)
for (int i = 0; i < sizeof(folderCandidates) / sizeof(folderCandidates[0]); i++)
{
int written = snprintf(currentPath, sizeof(currentPath), "%s", folderCandidates[i]);
if (written < 0 || (size_t)written >= sizeof(currentPath))
continue;
if (stat(currentPath, &st) == 0 && S_ISDIR(st.st_mode))
{
size_t pathLen = strlen(currentPath);
resultPath = (char *)malloc(pathLen + 1);
if (resultPath == NULL)
{
log_fatal("Failed to allocate memory for result path.");
return NULL;
}
strcpy(resultPath, currentPath);
return resultPath;
}
snprintf(result, MAX_PATH_LENGTH, "%s/%s", folderCandidates[i], PRELOAD_FILE_NAME);
if (fileExists(result))
return result;
}
if (fileExists(PRELOAD_FILE_NAME))
return PRELOAD_FILE_NAME;
return NULL;
}
/**
* @brief Finds the path to the LD_PRELOAD library.
*
* This function searches for the lindbergh.so library in various locations,
* including the current directory and system library folders.
* @return A dynamically allocated string containing the path to the library, or an empty string if not found.
*/
char *findLDPreloadLibrary()
int pathsDiffer(const char *p1, const char *p2)
{
char *foundPathPtr = NULL;
char *systemLibFolder = find32bLibFolder();
if (systemLibFolder == NULL || systemLibFolder[0] == '\0')
{
log_error("Error: library folder not found.\n");
return "";
}
char fullLibPath[PATH_MAX];
int lenWritten = snprintf(fullLibPath, sizeof(fullLibPath), "%s/%s", systemLibFolder, PRELOAD_FILE_NAME);
if (lenWritten < 0 || (size_t)lenWritten >= sizeof(fullLibPath))
{
log_error("Warning: Constructed path is too long or invalid: %s/%s\n", systemLibFolder, PRELOAD_FILE_NAME);
return "";
}
if (access(PRELOAD_FILE_NAME, F_OK) == 0) // lindbergh.so in current folder
{
char curDir[MAX_PATH_LEN];
if (getcwd(curDir, sizeof(curDir)) == NULL)
return "";
size_t len = strlen(curDir) + strlen(PRELOAD_FILE_NAME) + 1;
foundPathPtr = (char *)malloc(len + 1);
if (foundPathPtr == NULL)
return "";
else
snprintf(foundPathPtr, len + 1, "%s/%s", curDir, PRELOAD_FILE_NAME);
}
else if (access(fullLibPath, F_OK) == 0) // lindbergh.so in system lib folder
{
foundPathPtr = (char *)malloc(strlen(fullLibPath) + 1);
if (foundPathPtr == NULL)
return "";
else
strcpy(foundPathPtr, fullLibPath);
}
else // lindbergh.so in the ELF folder.
{
return PRELOAD_FILE_NAME;
}
return foundPathPtr;
char real1[MAX_PATH_LENGTH], real2[MAX_PATH_LENGTH];
if (!realpath(p1, real1) || !realpath(p2, real2))
return 1;
return strcmp(real1, real2) != 0;
}
/**
* Makes sure the environment variables are set correctly
*
* This function sets the LD_LIBRARY_PATH and LD_PRELOAD environment variables
* to ensure that the game can find the necessary libraries and preload the
* lindbergh.so library.
*
* @param ldLibPath The path to the LD_PRELOAD library.
* @param curDir The current working directory.
* @param gameDir The directory where the game is located.
* @param zink Flag indicating whether to use Zink.
* @param nvidia Flag indicating whether to use NVIDIA GPU.
* to run the game.
*/
void setEnvironmentVariables(char *ldLibPath, char *curDir, char *gameDir, int zink, int nvidia)
bool hasSpaces(const char *path)
{
// Ensure the library path is set correctly
char libraryPath[MAX_PATH_LEN] = {0};
if (strchr(path, ' '))
return true;
const char *currentLibraryPath = getenv(LD_LIBRARY_PATH);
if (currentLibraryPath != NULL)
return false;
}
void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, const char *gameDir, int zink, int nvidia,
const char *confFilePath)
{
setenv("LD_PRELOAD", ldLibPath, 1);
if (hasSpaces(ldLibPath))
{
strcat(libraryPath, currentLibraryPath);
strcat(libraryPath, ":");
log_error("The path \'%s\' where lindbergh.so is located cannot contain spaces.", ldLibPath);
exit(EXIT_FAILURE);
}
strcat(libraryPath, ".:lib:../lib");
char *currentLibraryPath = getenv("LD_LIBRARY_PATH");
char newLdLibPath[MAX_PATH_LENGTH * 3] = "";
if (currentLibraryPath && strlen(currentLibraryPath) > 0)
snprintf(newLdLibPath, sizeof(newLdLibPath), "%s:", currentLibraryPath);
char *tmpLibPath = dirname(strdup(ldLibPath));
if (strcmp(tmpLibPath, ".") != 0)
{
strcat(libraryPath, ":");
strcat(libraryPath, tmpLibPath);
strcat(newLdLibPath, ":");
strcat(newLdLibPath, tmpLibPath);
}
if (curDir != NULL && curDir[0] != '\0' && strcmp(curDir, tmpLibPath) != 0)
if (originalDir != NULL && originalDir[0] != '\0' && pathsDiffer(originalDir, tmpLibPath) != 0)
{
strcat(libraryPath, ":");
strcat(libraryPath, curDir);
strcat(newLdLibPath, ":");
strcat(newLdLibPath, originalDir);
}
if (gameDir != NULL && gameDir[0] != '\0')
if (gameDir && gameDir[0] != '\0' && pathsDiffer(originalDir, gameDir) && pathsDiffer(tmpLibPath, gameDir))
{
strcat(libraryPath, ":");
strcat(libraryPath, gameDir);
strcat(newLdLibPath, ":");
strcat(newLdLibPath, gameDir);
}
setenv(LD_LIBRARY_PATH, libraryPath, 1);
strcat(newLdLibPath, ":.:lib:../lib");
// Ensure the preload path is set correctly
if (strcmp(ldLibPath, "") != 0)
setenv(LD_PRELOAD, ldLibPath, 1);
else
setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1); // Should not reach here
if (newLdLibPath[0] == ':')
memmove(newLdLibPath, newLdLibPath + 1, strlen(newLdLibPath));
setenv("LD_LIBRARY_PATH", newLdLibPath, 1);
if (strlen(confFilePath) > 0)
{
if (hasSpaces(confFilePath))
{
log_error("The path \'%s\' for the config file cannot contain spaces.", confFilePath);
exit(EXIT_FAILURE);
}
setenv(LINDBERGH_CONFIG_PATH, confFilePath, 1);
}
if (zink && nvidia)
{
log_error("Cannot pass both, zink and nvidia options at the same time.");
exit(EXIT_FAILURE);
}
if (zink)
{
setenv("MESA_LOADER_DRIVER_OVERRIDE", "zink", 1);
}
if (nvidia)
{
setenv("__GLX_VENDOR_LIBRARY_NAME", "nvidia", 1);
setenv("__NV_PRIME_RENDER_OFFLOAD", "1", 1);
}
char *appImgRoot = getenv("APP_IMG_ROOT");
if (appImgRoot != NULL)
{
char libglDriPath[MAX_PATH_LENGTH];
sprintf(libglDriPath, "%s/usr/lib32/dri", appImgRoot);
setenv("LIBGL_DRIVERS_PATH", libglDriPath, 1);
}
}
/**
* @brief Prints the usage information for the Lindbergh loader.
*
* This function displays the command-line options and usage instructions for
* the Lindbergh loader.
* @param argv The command-line arguments array.
* Prints the usage for the loader
*/
void printUsage(char *argv[])
{
printf("%s [GAME_PATH] [OPTIONS]\n", argv[0]);
@@ -537,15 +363,12 @@ void printUsage(char *argv[])
printf(" --gamepath | -g Specifies game path without ELF name\n");
}
/**
* @brief Lists available evdev controllers and their inputs.
*
* This function initializes the controller system and then iterates through
* the available controllers, printing their names and the names of their
* inputs.
* @return EXIT_SUCCESS on success, EXIT_FAILURE on failure.
* Lists available evdev controllers and their inputs
*/
void printVersion()
{
printf("Lindbergh Loader v%d.%d\n", MAJOR_VERSION, MINOR_VERSION);
printf("Created by: %s\n", TEAM);
}
int listControllers()
{
Controllers controllers;
@@ -553,7 +376,7 @@ int listControllers()
ControllerStatus status = initControllers(&controllers);
if (status != CONTROLLER_STATUS_SUCCESS)
{
printf("Failed to list controllers\n");
log_error("Failed to list controllers\n");
return EXIT_FAILURE;
}
@@ -570,33 +393,16 @@ int listControllers()
printf(" - %s\n", controller.inputs[i].inputName);
}
}
stopControllers(&controllers);
return EXIT_SUCCESS;
}
/**
* @brief Prints the version information of the Lindbergh loader.
*
* This function displays the version number of the Lindbergh loader and the
* names of the team members.
*/
void printVersion() {
printf("Lindbergh Loader v%d.%d\n", MAJOR_VERSION, MINOR_VERSION);
printf("Created by: %s\n", TEAM);
}
/**
* Small utility to automatically detect the game and run it without
* having to type a long string in.
*/
int main(int argc, char *argv[])
{
char gameELF[128] = {0};
char gameDir[MAX_PATH_LEN] = {0};
if (argc > 1 && strcmp(argv[1], "--version") == 0) {
if (argc > 1 && strcmp(argv[1], "--version") == 0)
{
printVersion();
return EXIT_SUCCESS;
}
@@ -611,16 +417,18 @@ int main(int argc, char *argv[])
{
return listControllers();
}
// Build up the command to start the game
int testMode = false;
int gdb = false;
int zink = false;
int nvidia = false;
int forceGame = false;
int segaboot = false;
char *extConfigPath = NULL;
char *passedGamePath = NULL;
char *forcedGamePath = NULL;
char passedGamePath[MAX_PATH_LENGTH] = "";
char forcedGamePath[MAX_PATH_LENGTH] = "";
char forcedGameDir[MAX_PATH_LENGTH] = "";
char gameELF[MAX_PATH_LENGTH] = "";
char extConfigPath[MAX_PATH_LENGTH] = "";
char originalDir[MAX_PATH_LENGTH] = "";
bool gdb = false;
bool testMode = false;
bool segaboot = false;
bool zink = false;
bool nvidia = false;
for (int i = 1; i < argc; i++)
{
@@ -660,161 +468,158 @@ int main(int argc, char *argv[])
{
break;
}
extConfigPath = strdup(argv[i + 1]);
strncpy(extConfigPath, argv[i + 1], MAX_PATH_LENGTH);
i += 1;
continue;
}
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gamepath") == 0)
if ((strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gamepath") == 0) && i + 1 < argc)
{
if (i + 1 >= argc)
{
break;
}
passedGamePath = strdup(argv[i + 1]);
i += 1;
strncpy(passedGamePath, argv[i + 1], MAX_PATH_LENGTH);
i++;
continue;
}
// Treat the argument as the game name
forcedGamePath = strdup(argv[i]);
forceGame = true;
strncpy(forcedGamePath, argv[i], MAX_PATH_LENGTH);
}
// We save the current dir to return to it later
char curDir[MAX_PATH_LEN];
if (getcwd(curDir, sizeof(curDir)) == NULL)
if (!getcwd(originalDir, sizeof(originalDir)))
{
log_error("Error: could not get the current directory.");
perror("getcwd");
return EXIT_FAILURE;
}
char command[PATH_MAX] = {0};
char *forcedGameDir;
char *forcedGameElf;
if (forceGame)
int useForceCommandPath = strlen(forcedGamePath) > 0;
bool commandOnlyElf = useForceCommandPath && strchr(forcedGamePath, '/') == NULL;
if (useForceCommandPath && !commandOnlyElf)
{
if (forcedGamePath != NULL)
if (!fileExists(forcedGamePath))
{
forcedGameDir = dirname(strdup(forcedGamePath));
forcedGameElf = basename(strdup(forcedGamePath));
log_error("File does not exist: %s\n", forcedGamePath);
return EXIT_FAILURE;
}
if (strcmp(forcedGameDir, ".") == 0) // Only the ELF file name was passed.
{
if (passedGamePath[0] != '\0')
snprintf(gameDir, MAX_PATH_LEN, "%s", passedGamePath);
else
snprintf(gameDir, MAX_PATH_LEN, "./");
}
else
{
if (passedGamePath != NULL) // ELF with path and also -g option pointing to a folder
log_warn("Warning: elf passed with a path, and -g option enabled. Ignoring -g option.");
extractPathFromProg(forcedGamePath, forcedGameDir, gameELF);
if (hasSpaces(forcedGameDir))
{
log_warn("The path contains spaces, this most likely will cause issues.");
log_warn("Please, make sure you don't use spaces in the path.");
}
snprintf(gameDir, MAX_PATH_LEN, "%s", forcedGameDir);
}
if (!dirExists(forcedGameDir))
{
log_error("Directory does not exist: %s\n", forcedGameDir);
return EXIT_FAILURE;
}
if (strcmp(forcedGameElf, ".") != 0)
strcpy(gameELF, forcedGameElf);
// Check if the passed folder is valid
if (!isValidDirectory(gameDir))
chdir(forcedGameDir);
}
else if (strlen(passedGamePath) > 0)
{
if (hasSpaces(passedGamePath))
{
log_warn("The path contains spaces, this most likely will cause issues.");
log_warn("Please, make sure you don't use spaces in the path.");
}
if (!dirExists(passedGamePath))
{
log_error("Directory does not exist: %s\n", passedGamePath);
return EXIT_FAILURE;
}
chdir(passedGamePath);
if (commandOnlyElf)
{
strncpy(gameELF, forcedGamePath, MAX_PATH_LENGTH);
if (!fileExists(gameELF))
{
log_error("%s is not a valid directory here.", gameDir);
log_error("Program '%s' not found in %s\n", gameELF, passedGamePath);
return EXIT_FAILURE;
}
}
else
{
for (int i = 0; games[i] && strcmp(games[i], "END") != 0; i++)
{
if (fileExists(games[i]))
{
strncpy(gameELF, games[i], MAX_PATH_LENGTH);
break;
}
}
if (strlen(gameELF) == 0)
{
log_error("No known game file found in %s\n", passedGamePath);
return EXIT_FAILURE;
}
}
}
else if (commandOnlyElf)
{
strncpy(gameELF, forcedGamePath, MAX_PATH_LENGTH);
if (!fileExists(gameELF))
{
log_error("'%s' not found in current directory\n", gameELF);
return EXIT_FAILURE;
}
}
else
{
// Look for the game ELF's
struct dirent *ent;
DIR *dir;
if (passedGamePath != NULL && passedGamePath[0] != '\0')
for (int i = 0; games[i] && strcmp(games[i], "END") != 0; i++)
{
snprintf(gameDir, MAX_PATH_LEN, "%s", passedGamePath);
// Check if the passed folder is valid
if (!isValidDirectory(gameDir))
if (access(games[i], F_OK) == 0)
{
log_error("%s is not a valid directory.", gameDir);
return EXIT_FAILURE;
strncpy(gameELF, games[i], sizeof(gameELF) - 1);
gameELF[sizeof(gameELF) - 1] = '\0';
printf("Auto-detected ELF: %s\n", gameELF);
break;
}
dir = opendir(gameDir);
}
else
{
snprintf(gameDir, MAX_PATH_LEN, "%s", curDir);
dir = opendir(curDir);
}
if (dir == NULL)
if (gameELF[0] == '\0')
{
log_error("Could not list files in current directory.");
log_error("No game ELF found in current directory.\n");
printUsage(argv);
return EXIT_FAILURE;
}
bool found = false;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type != DT_REG)
continue;
int index = 0;
while (!found)
{
if (strcmp(games[index], "END") == 0)
break;
if (strcmp(ent->d_name, games[index]) == 0)
{
strcpy(gameELF, games[index]);
found = true;
break;
}
index++;
}
}
closedir(dir);
}
if (testMode)
{
testModePath(gameELF);
}
char *targetedGameDir = strlen(forcedGameDir) ? forcedGameDir : passedGamePath;
char *lib_path = findPreloadLibrary(originalDir, targetedGameDir);
if (!lib_path)
{
log_error("Error: %s not found in known locations.\n", PRELOAD_FILE_NAME);
return 1;
}
setEnvironmentVariables(lib_path, originalDir, targetedGameDir, zink, nvidia, extConfigPath);
if (segaboot)
{
strcpy(gameELF, "segaboot");
testMode = true;
strcpy(gameELF, "segaboot -t");
// testMode = true;
}
if (gameELF[0] == '\0')
// Final command
char command[MAX_PATH_LENGTH];
if (strcmp(gameELF, "../JenTest/JenTest") == 0)
{
log_error("No lindbergh game found in this directory.");
printUsage(argv);
return EXIT_FAILURE;
snprintf(command, MAX_PATH_LENGTH, "%s", gameELF);
}
sprintf(command, "\"%s/\"%s", gameDir, gameELF);
char *ldPreloadLibrary = findLDPreloadLibrary();
if (strcmp(ldPreloadLibrary, PRELOAD_FILE_NAME) == 0)
else
{
char lindberghSoPath[MAX_PATH_LEN];
if (gameDir[0] != '\0')
snprintf(lindberghSoPath, strlen(gameDir) + sizeof(PRELOAD_FILE_NAME) + 1, "%s/%s", gameDir, PRELOAD_FILE_NAME);
if (access(lindberghSoPath, F_OK) != 0)
{
log_error("The preload object lindbergh.so was not found.");
return EXIT_FAILURE;
}
ldPreloadLibrary = strdup(lindberghSoPath);
snprintf(command, MAX_PATH_LENGTH, "./%s", gameELF);
}
// Ensure environment variables are set correctly
setEnvironmentVariables(ldPreloadLibrary, curDir, gameDir, zink, nvidia);
if (testMode)
testModePath(command);
isCleanElf(command);
if (gdb)
@@ -825,30 +630,14 @@ int main(int argc, char *argv[])
strcpy(command, temp);
}
if (extConfigPath != NULL && extConfigPath[0] != '\0')
{
setenv(LINDBERGH_CONFIG_PATH, extConfigPath, 1);
}
log_info("Starting $ %s", command);
if (gameDir[0] != '\0')
{
if (chdir(gameDir) != 0)
{
log_error("Could not change to game path.");
return EXIT_FAILURE;
}
}
int sysCmd = system(command);
if (gameDir[0] != '\0')
if (chdir(originalDir) != 0)
{
if (chdir(curDir) != 0)
{
log_error("Could not return to the original directory.");
return EXIT_FAILURE;
}
log_error("Could not return to the original directory.");
return EXIT_FAILURE;
}
return sysCmd;
+10 -6
View File
@@ -212,8 +212,8 @@ void *readJVSFrameThread(void * arg)
* @param fd
* @return 0|1
*/
int startJVSFrameThread(int * fd) {
int fdlocal = *((int *) fd);
int startJVSFrameThread(int *fd)
{
printf("SERIAL: starting thread.\n");
// Clean shared JVS frame buffer
@@ -236,17 +236,21 @@ int startJVSFrameThread(int * fd) {
* Return a JVSFrame structure with empty or full data, no in between
* @return
*/
JVSFrame readJVSFrameFromThread() {
JVSFrame readJVSFrameFromThread()
{
JVSFrame frame;
// Lock while reading/writing to shared frame
pthread_mutex_lock(&jvsBuffer_lock);
// Check if we have a valid frame
if (jvsFrameBuffer.ready == 1) {
if (jvsFrameBuffer.ready == 1)
{
frame = jvsFrameBuffer;
// It has been red, we disable this frame
// It has been read, we disable this frame
jvsFrameBuffer.ready = 0;
} else {
}
else
{
frame.ready = 0;
frame.size = 0;
memset(frame.buffer, 0, JVSBUFFER_SIZE);
+5 -11
View File
@@ -628,11 +628,9 @@ int initPatch()
if (GPUVendor != NVIDIA_GPU)
{
cacheModedShaderFiles();
loadLibCg();
cacheModedShaderFiles();
cacheNnstdshader();
patchMemory(0x082d4437, "9090909090"); // Stop the folder rename
patchMemory(0x088a6127, "66732f636f6d70696c65646d657361"); // Change folder to compiledshdmesa
patchMemory(0x082646b3, "01"); // Compile using CGC
replaceCallAtAddress(0x082d48ac, compileWithCGC);
replaceCallAtAddress(0x082d4b3b, compileWithCGC);
@@ -2059,12 +2057,9 @@ int initPatch()
{
loadLibCg();
cacheNnstdshader();
patchMemory(0x08200c17, "9090909090"); // Stop the folder rename
patchMemory(0x084173e7, "66732f636f6d70696c65646d657361"); // Change folder to compiledshdmesa
patchMemory(0x080b3d71, "01"); // Compile using CGC
replaceCallAtAddress(0x082015bf, compileWithCGC);
replaceCallAtAddress(0x082012c1, compileWithCGC);
// patchMemory(0x080b3d65, "00"); // Force recompile shaders
}
}
break;
@@ -2141,6 +2136,7 @@ int initPatch()
detourFunction(0x084d44fc, amDipswSetLed);
detourFunction(0x084d4485, amDipswGetData);
patchMemory(0x0804edb2, "9090909090"); //__intel_new_proc_init_P
detourFunction(0x08178e6c, stubRetOne); // _ZN7am_ctrl18SetNetworkAddrEth0Ejj.
detourFunction(0x0804e594, gl_XGetProcAddressARB);
srtvElfShaderPatcher();
@@ -2487,9 +2483,8 @@ int initPatch()
// Mesa Patches
if (GPUVendor != NVIDIA_GPU)
{
patchMemory(0x08503bfa, "6d657361"); // Rename compile folder to compmesa
cacheModedShaderFiles();
loadLibCg();
cacheModedShaderFiles();
cacheNnstdshader();
replaceCallAtAddress(0x08370aa7, compileWithCGC);
replaceCallAtAddress(0x08370bb6, compileWithCGC);
@@ -2550,10 +2545,9 @@ int initPatch()
if (GPUVendor != NVIDIA_GPU)
{
loadLibCg();
cacheNnstdshader();
patchMemory(0x081ef9d8, "01"); // Force the game to recompile the shaders using cgc
patchMemory(0x08413f04, "646d657361"); // Changes compiledshader folder to compiledshdmesa
cacheModedShaderFiles();
cacheNnstdshader();
patchMemory(0x081ef9d8, "01"); // Force the game to recompile the shaders using cgc
replaceCallAtAddress(0x08259fe5, compileWithCGC);
replaceCallAtAddress(0x0825a089, compileWithCGC);
}
+233 -5
View File
@@ -1267,6 +1267,182 @@ void glVertex3fHOD4(GLfloat x, GLfloat y, GLfloat z)
_glVertex3f(x, y, z);
}
void tooSpicySC(uint32_t param_1, uint32_t param_2)
{
myEnableScaling = true;
SDL_WarpMouseInWindow(SDLwindow, 1280 / 2, 768 / 2);
((void (*)(uint32_t, uint32_t))0x082fa77c)(param_1, param_2);
}
void tooSpicyPR(uint32_t param_1, uint32_t param_2)
{
myEnableScaling = false;
((void (*)(uint32_t, uint32_t))0x081eb4d4)(param_1, param_2);
}
int tooSpicyVsprintf(char *str, const char *format, va_list arg)
{
int (*_vsprintf)(char *str, const char *format, va_list arg) = dlsym(RTLD_NEXT, "vsprintf");
int res = _vsprintf(str, format, arg);
if (strcmp(str, "Additional 475") == 0)
{
myEnableScaling = false;
}
return res;
}
void glVertex3f2SP(GLfloat x, GLfloat y, GLfloat z)
{
int (*_glVertex3f)(GLfloat x, GLfloat y, GLfloat z) = dlsym(RTLD_NEXT, "glVertex3f");
void *returnAddress = __builtin_return_address(0);
float scaleX = gWidth / 1280.0f;
float scaleY = gHeight / 768.0f;
float TestX = ((((gWidth - 1280.0) / 2.0) * 1.1116317809) / 1280.0) * (1.5 / scaleX);
float TestY = ((((gHeight - 768.0) / 2.0) * 0.666979044) / 768.0) * (1.5 / scaleX);
float OffsetX = TestX; // test
float OffsetY = TestY;
float scaleZ = fabs(z) / 1.510000;
float AdjOffsetX = OffsetX * scaleZ;
float AdjOffsetY = OffsetY * scaleZ;
if (z == -1.000000f || z == -3.000000f)
{
// We skip 3d and videos
;
}
else if (z == -1.010000f)
{
// bd main
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.510000f)
{
// logo - coin/start - 2D
if (curTextureID == 497) // right
{
x -= OffsetX;
y -= OffsetY;
x *= scaleX;
y *= scaleY;
}
else if (curTextureID == 498)
{ // deadend
;
}
else
{
x += OffsetX;
y -= OffsetY;
x *= scaleX;
y *= scaleY;
}
}
else if (z == -1.460000f)
{
// 2D title
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.490000f)
{
// 2D title
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.500000f)
{
// 2D title
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.310000f)
{
// 2D
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.210000f)
{
// 2D
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.5109999180f)
{
// 2D
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z == -1.200000f)
{
// target - select
if (curTextureID == 454 || curTextureID == 461 || curTextureID == 456)
{
// 457 X - 454 461 456 O
if (myEnableScaling)
{
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
}
}
else if (z == -1.710000f)
{
// 2D
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
else if (z > -1.85f)
{
// 2d
x += AdjOffsetX;
y -= AdjOffsetY;
x *= scaleX;
y *= scaleY;
}
_glVertex3f(x, y, z);
}
void glVertex3fHarley(GLfloat x, GLfloat y, GLfloat z)
{
int (*_glVertex3f)(GLfloat x, GLfloat y, GLfloat z) = dlsym(RTLD_NEXT, "glVertex3f");
@@ -1286,11 +1462,6 @@ void glVertex3fHarley(GLfloat x, GLfloat y, GLfloat z)
y *= scaleY;
}
void *addr = __builtin_return_address(0);
// printf("glVertex3f is hooked: x=%f, y=%f, z=%f\n", x, y, z);
// printf("z values: %.10f\n", z);
// if ( z == -3517.2272949219f){ //1360x768
// if ( z == -4271.795898f){ //900p
if (z == -5300.7529296875)
{ // 1080p
printf("glVertex3f hooked: x=%f, y=%f, z=%f, addr: %p\n", x, y, z, addr);
@@ -1301,6 +1472,38 @@ void glVertex3fHarley(GLfloat x, GLfloat y, GLfloat z)
_glVertex3f(x, y, z);
}
void glVertex3fHodEx(GLfloat x, GLfloat y, GLfloat z)
{
int (*_glVertex3f)(GLfloat x, GLfloat y, GLfloat z) = dlsym(RTLD_NEXT, "glVertex3f");
float scaleX = 600.0 / gWidth;
float scaleY = 240.0 / gHeight;
if (z == -1.210000f && (y == 0.0035231018f || y == -0.0152667547f || y == -0.0209428426f || // coin/start
y == -0.0397327021f || y == -0.0454087853f || y == -0.0641986430f || y == -0.0790739134f ||
y == -0.0931663215f || y == -0.0692875385f || y == -0.0833799466f ||
y == 0.0456045270f || y == 0.0268146675f || y == 0.0211385805f || y == 0.0023487248f || y == -0.0033273622f ||
y == -0.0221172161f || y == -0.0787803382f || y == -0.0934598967f ||
y == 0.0524549857f || y == 0.0336651318f || y == 0.0279890448f || y == 0.0091991890f
))
{
x += scaleX;
y -= scaleY;
}
else if (y == 0.0042380672f || y == -0.0169522688f || y == 0.0360235684f || y == 0.0148332343f)
{ // Main bd
x += scaleX;
y -= scaleY;
}
_glVertex3f(x, y, z);
}
int initResolutionPatches()
{
gWidth = getConfig()->width;
@@ -2655,6 +2858,21 @@ int initResolutionPatches()
{
break;
}
setVariable(0x08785b80, gWidth); // main
setVariable(0x08785b84, gHeight);
patchMemory(0x082a3222, "02");
setVariable(0x08785ad8, 1360); // sprite res 1280x768
setVariable(0x08785adc, 768);
setVariable(0x087859b8, 1360); // sprite res 640x480
setVariable(0x087859bc, 768);
patchMemory(0x081775d2, "b801000000"); // target
patchMemory(0x0804e5d2, "909090909090");
setVariable(0x087aa080, gWidth); // render res
patchMemory(0x0804e60c, "909090909090");
patchMemory(0x0828cc2f, "909090909090");
}
break;
case TOO_SPICY:
@@ -2669,6 +2887,16 @@ int initResolutionPatches()
{
break;
}
setVariable(0x08202b22, gWidth); // render res
patchMemory(0x08202b19, "74");
setVariable(0x08664410, gWidth); // 1280x768
setVariable(0x08664414, gHeight);
detourFunction(0x0804d0d0, glVertex3f2SP);
detourFunction(0x0804dda0, myGlBindTexture);
replaceCallAtAddress(0x080d96ff, tooSpicySC);
replaceCallAtAddress(0x080da026, tooSpicyPR);
detourFunction(0x0804ce10, tooSpicyVsprintf);
patchMemory(0x081f96b9, "02"); // 5
}
break;
case TOO_SPICY_TEST:
+85 -24
View File
@@ -22,6 +22,7 @@
#include "../libxdiff/xdiff/xdiff.h"
#include "config.h"
#include "patch.h"
#include "log.h"
#include "shader_work/2spicy.h"
#include "shader_work/abc.h"
@@ -1541,41 +1542,101 @@ int parseCgcArgs(const char *input, const char ***compilerArgs, const char **out
return 0;
}
void loadLibCg()
char *findLibCg()
{
int isFlatpak = 0;
void *handle = dlopen("./libCg.so", RTLD_NOW);
if (!handle)
const char *foundPath = NULL;
FILE *libCgF = NULL;
char appImageLib[MAX_PATH_LENGTH];
snprintf(appImageLib, MAX_PATH_LENGTH, "%s/usr/lib32/libCg2.so", getenv("APP_IMG_ROOT"));
char *pathsToCheck[] = {NULL, "/app/lib32/libCg2.so", appImageLib, "./libCg.so", NULL};
if (strcmp(getConfig()->libCgPath, "") != 0)
{
handle = dlopen("/app/lib32/libCg2.so", RTLD_NOW);
if (!handle)
pathsToCheck[0] = getConfig()->libCgPath;
}
const char *currentDir = getenv("LINDBERGH_LOADER_CURRENT_DIR");
if (currentDir != NULL)
{
size_t pathLen = strlen(currentDir) + strlen("/libCg.so") + 1;
pathsToCheck[3] = malloc(pathLen);
if (pathsToCheck[4] != NULL)
{
fprintf(stderr, "Error: Unable to load library libCg.so %s\n", dlerror());
fprintf(stderr, "Error: libCg.so version 2.0 is needed to compile the shaders in the game's folder.\n");
exit(1);
}
else
{
isFlatpak = 1;
snprintf(pathsToCheck[4], pathLen, "%s/%s", currentDir, "libCg.so");
}
}
char libCgVersion[9];
FILE *libF;
if (isFlatpak)
libF = fopen("/app/lib32/libCg2.so", "r");
else
libF = fopen("./libCg.so", "r");
for (int i = 0; i < 5; ++i)
{
if (pathsToCheck[i] == NULL)
continue;
fseek(libF, 0x226d84, SEEK_SET);
fread(libCgVersion, 9, 1, libF);
fclose(libF);
if (access(pathsToCheck[i], F_OK) == 0)
{
foundPath = pathsToCheck[i];
break;
}
}
if (foundPath != pathsToCheck[4] && pathsToCheck[4] != NULL)
{
free(pathsToCheck[4]);
}
if (foundPath == NULL)
{
log_error("Error: Unable to find library libCg.so\n");
log_error("Error: libCg.so version 2.0 is needed to compile the shaders in the game's folder.\n");
exit(EXIT_FAILURE);
}
char libCgVersion[9] = {0};
libCgF = fopen(foundPath, "rb");
if (libCgF == NULL)
{
log_error("Failed to open: %s\n", foundPath);
exit(EXIT_FAILURE);
}
fseek(libCgF, 0x226d84L, SEEK_SET);
if (fread(libCgVersion, 1, sizeof(libCgVersion) - 1, libCgF) != sizeof(libCgVersion) - 1)
{
log_error("Failed to read version from: %s\n", foundPath);
fclose(libCgF);
exit(EXIT_FAILURE);
}
fclose(libCgF);
if (strcmp(libCgVersion, "2.0.0.12") != 0)
{
printf("ERROR: Wrong libCg.so, version 2.0.0.12 is needed.\n");
exit(1);
log_error("ERROR: Wrong libCg.so version '%s', version 2.0.0.12 is needed.\n", libCgVersion);
exit(EXIT_FAILURE);
}
char *resultPath = strdup(foundPath);
if (resultPath == NULL)
{
log_error("Error allocating memory for result path");
exit(EXIT_FAILURE);
}
return resultPath;
}
void loadLibCg()
{
void *handle;
char *libCgFilePath = findLibCg();
handle = dlopen(libCgFilePath, RTLD_NOW);
if (!handle)
{
log_error("Error: Unable to find library libCg.so\n");
log_error("Error: libCg.so version 2.0 is needed to compile the shaders in the game's folder.\n");
exit(EXIT_FAILURE);
}
free(libCgFilePath);
dlerror();
char *error;