mirror of
https://github.com/lindbergh-loader/lindbergh-loader.git
synced 2026-10-14 01:49:12 +03:00
Compare commits
65
Commits
neo-pr-test
...
v2.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
973cfb7058 | ||
|
|
c47a4a6889 | ||
|
|
4d44d95397 | ||
|
|
da0316d5a6 | ||
|
|
9179d0e282 | ||
|
|
d5e370240c | ||
|
|
3b7d10514f | ||
|
|
01c2c5b765 | ||
|
|
a0e504c4c9 | ||
|
|
3ad93675e0 | ||
|
|
d09c6c72e1 | ||
|
|
32759cd01d | ||
|
|
f0e9a22b24 | ||
|
|
cf33403979 | ||
|
|
ab20dd9420 | ||
|
|
96188c69f5 | ||
|
|
73d2131c73 | ||
|
|
813ea1c49d | ||
|
|
6c3c9a103e | ||
|
|
85548fc01e | ||
|
|
aa44280724 | ||
|
|
ae6e4c6e56 | ||
|
|
b3b3e15357 | ||
|
|
ba51502fdb | ||
|
|
aedad4dcdf | ||
|
|
374ecd6e84 | ||
|
|
f5c8c8c0f0 | ||
|
|
feaa6a912e | ||
|
|
b129220822 | ||
|
|
4d07faa251 | ||
|
|
5558718f29 | ||
|
|
e9e62a3bd1 | ||
|
|
293b4e8c25 | ||
|
|
e7d8c5a80a | ||
|
|
92eb48691d | ||
|
|
55fd8955c1 | ||
|
|
ea35dcdad2 | ||
|
|
fb557044d7 | ||
|
|
0e8b3b4a59 | ||
|
|
f17dd93778 | ||
|
|
e359cbb0bd | ||
|
|
72f70ccda1 | ||
|
|
0a29307870 | ||
|
|
d202f31717 | ||
|
|
a68dcbbdcf | ||
|
|
75642e285e | ||
|
|
d9d076f120 | ||
|
|
a5633bbf1d | ||
|
|
d72ac379ea | ||
|
|
c5c44ce395 | ||
|
|
988a9ee6e3 | ||
|
|
ea19a688cc | ||
|
|
c5fa5871d1 | ||
|
|
5be9aef4e0 | ||
|
|
f89f8e41ab | ||
|
|
ad085a1610 | ||
|
|
edba8bac7a | ||
|
|
98c6b740fd | ||
|
|
32d7347282 | ||
|
|
d500a5b80d | ||
|
|
e9390c85fa | ||
|
|
412d19e5f9 | ||
|
|
a374dec081 | ||
|
|
1ade940d2f | ||
|
|
efe34307e3 |
+1
-1
@@ -129,7 +129,7 @@ IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
name: 🐧 Linux AppImage
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
linux-appimage:
|
||||
name: "Build"
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download version.h artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: version-file
|
||||
path: src/lindbergh/
|
||||
|
||||
- name: Cache APT packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /var/cache/apt/archives
|
||||
key: ${{ runner.os }}-apt-${{ hashFiles('**/install-packages.sh') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-apt-
|
||||
|
||||
- name: Install Packages
|
||||
run: scripts/appimage/install-packages.sh
|
||||
|
||||
- name: Pack ID Libs
|
||||
run: scripts/appimage/id-libs.sh
|
||||
|
||||
- name: Install Kisak
|
||||
run: scripts/appimage/install-kisak.sh
|
||||
|
||||
- name: Build Deps
|
||||
run: scripts/appimage/build-deps.sh
|
||||
|
||||
- name: Pack Libs
|
||||
run: scripts/appimage/pack-libs.sh
|
||||
|
||||
- name: Compile Build
|
||||
shell: bash
|
||||
run: |
|
||||
make
|
||||
scripts/appimage/make-appimage.sh
|
||||
|
||||
- name: Compress build directory
|
||||
run: tar -czvf build.tar.gz -C ./build .
|
||||
|
||||
- name: Upload build.tar.gz artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "linux-build"
|
||||
path: "build.tar.gz"
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "linux-appimage"
|
||||
path: "lindbergh-loader.AppImage"
|
||||
|
||||
- name: Upload SDL3 and FAudio
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SDL3-FAudio-ID
|
||||
path: |
|
||||
libs/libs.tar
|
||||
libs/includes.tar
|
||||
libs/lib32ID.tar
|
||||
@@ -14,10 +14,20 @@ jobs:
|
||||
options: --privileged
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download version.h artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
set-safe-directory: ${{ env.GITHUB_WORKSPACE }}
|
||||
name: version-file
|
||||
path: src/lindbergh/
|
||||
|
||||
- name: Download SDL3 libs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: SDL3-FAudio-ID
|
||||
path: libs/
|
||||
|
||||
# Work around container ownership issue
|
||||
- name: Set Safe Directory
|
||||
|
||||
@@ -2,19 +2,26 @@ name: Build and Release Lindbergh
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checkout the repository
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download version.h artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: version-file
|
||||
path: src/lindbergh/
|
||||
|
||||
- name: Download SDL3 libs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: SDL3-FAudio-ID
|
||||
path: libs/
|
||||
|
||||
# Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
+117
-19
@@ -18,19 +18,74 @@ on:
|
||||
- '.docs/changelog'
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
name: 💻 Linux
|
||||
uses: "./.github/workflows/linux-build.yml"
|
||||
prepare-source:
|
||||
name: ⚙️ Prepare Source
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
release_version: ${{ steps.get_version.outputs.RELEASE_VERSION }}
|
||||
release_tag: ${{ steps.get_version.outputs.RELEASE_TAG }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Extract Version and Update version.h
|
||||
id: get_version
|
||||
run: |
|
||||
CHANGELOG_FILE="./docs/changelog"
|
||||
VERSION_FILE="src/lindbergh/version.h"
|
||||
if [[ ! -f "$CHANGELOG_FILE" ]]; then
|
||||
echo "Error: Changelog file '$CHANGELOG_FILE' not found."
|
||||
exit 1
|
||||
fi
|
||||
VERSION=$(awk '/^## \[/ { match($0, /\[([^]]+)\]/, arr); print arr[1]; exit }' "$CHANGELOG_FILE")
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
echo "Error: Could not find version in '$CHANGELOG_FILE'. Expected format like '## [X.Y.Z]' near the top."
|
||||
exit 1
|
||||
fi
|
||||
echo "Found version: $VERSION"
|
||||
MAJOR=$(echo $VERSION | cut -d. -f1)
|
||||
MINOR=$(echo $VERSION | cut -d. -f2)
|
||||
UPDATE=$(echo $VERSION | cut -d. -f3)
|
||||
if [ -z "$UPDATE" ]; then
|
||||
UPDATE=0
|
||||
fi
|
||||
if [[ ! -f "$VERSION_FILE" ]]; then
|
||||
echo "Error: Version file '$VERSION_FILE' not found."
|
||||
exit 1
|
||||
fi
|
||||
sed -i "s/\(#define MAJOR_VERSION \).*/\1$MAJOR/" "$VERSION_FILE"
|
||||
sed -i "s/\(#define MINOR_VERSION \).*/\1$MINOR/" "$VERSION_FILE"
|
||||
sed -i "s/\(#define UPDATE_VERSION \).*/\1$UPDATE/" "$VERSION_FILE"
|
||||
echo "Updated $VERSION_FILE with version $MAJOR.$MINOR.$UPDATE"
|
||||
echo "RELEASE_VERSION=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_TAG=v$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload version.h artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: version-file
|
||||
path: src/lindbergh/version.h
|
||||
|
||||
linux-appimage:
|
||||
name: 📦 Linux AppImage
|
||||
needs: prepare-source
|
||||
uses: "./.github/workflows/appimage-build.yml"
|
||||
|
||||
linux-flatpak:
|
||||
name: 📦 Linux Flatpak
|
||||
needs: [prepare-source, linux-appimage]
|
||||
uses: "./.github/workflows/flatpak-build.yml"
|
||||
|
||||
create-release:
|
||||
name: 📤 Create Release
|
||||
needs: [linux-build, linux-flatpak]
|
||||
name: 📤 Create Release and Notify
|
||||
needs: [linux-flatpak, linux-appimage, prepare-source]
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -39,19 +94,62 @@ jobs:
|
||||
- name: Display Downloaded Artifacts
|
||||
run: find ./artifacts/
|
||||
|
||||
- name: Add 161 to github.run_number
|
||||
- name: Parse Changelog and Save to File
|
||||
id: changelog
|
||||
run: |
|
||||
NEW_RUN_NUMBER=$(( ${{ github.run_number }} + 161 ))
|
||||
echo "NEW_RUN_NUMBER=$NEW_RUN_NUMBER" >> $GITHUB_ENV
|
||||
VERSION='${{ needs.prepare-source.outputs.release_version }}'
|
||||
awk -v version="$VERSION" '
|
||||
$0 ~ "^## \\[" version "\\]" { in_section=1; next }
|
||||
/^## / && in_section { exit }
|
||||
in_section { print }
|
||||
' ./docs/changelog > RELEASENOTES.md
|
||||
echo "notes_file=RELEASENOTES.md" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Preview Release
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: v${{ env.NEW_RUN_NUMBER }}
|
||||
prerelease: false
|
||||
title: "Build v${{ env.NEW_RUN_NUMBER }}"
|
||||
files: |
|
||||
|
||||
- name: Create Release with GitHub CLI
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release create ${{ needs.prepare-source.outputs.release_tag }} \
|
||||
--title "Build ${{ needs.prepare-source.outputs.release_tag }}" \
|
||||
--notes-file ${{ steps.changelog.outputs.notes_file }} \
|
||||
./artifacts/linux-build/build.tar.gz \
|
||||
./artifacts/linux-appimage/lindbergh-loader.AppImage \
|
||||
./artifacts/linux-flatpak/lindbergh-loader.flatpak
|
||||
./artifacts/linux-build/build.tar.gz
|
||||
|
||||
- name: Notify Discord of New Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Fetch the asset details
|
||||
ASSETS_JSON=$(gh release view ${{ needs.prepare-source.outputs.release_tag }} --json assets)
|
||||
|
||||
# Extract each URL
|
||||
FLATPAK_URL=$(echo "$ASSETS_JSON" | jq -r '.assets[] | select(.name=="lindbergh-loader.flatpak") | .url')
|
||||
APPIMAGE_URL=$(echo "$ASSETS_JSON" | jq -r '.assets[] | select(.name=="lindbergh-loader.AppImage") | .url')
|
||||
TAR_URL=$(echo "$ASSETS_JSON" | jq -r '.assets[] | select(.name=="build.tar.gz") | .url')
|
||||
|
||||
# Get the changelog body (no need to escape it for jq)
|
||||
CHANGELOG_BODY=$(cat ${{ steps.changelog.outputs.notes_file }} | head -c 2000)
|
||||
|
||||
# Use jq to build the JSON payload and pipe it to curl
|
||||
jq -n \
|
||||
--arg content "New lindbergh loader build **v${{ needs.prepare-source.outputs.release_version }}** has been released!" \
|
||||
--arg changelog "$CHANGELOG_BODY" \
|
||||
--arg flatpak_url "$FLATPAK_URL" \
|
||||
--arg appimage_url "$APPIMAGE_URL" \
|
||||
--arg tar_url "$TAR_URL" \
|
||||
'{
|
||||
"username": "Build Bot",
|
||||
"content": $content,
|
||||
"embeds": [{
|
||||
"description": $changelog,
|
||||
"color": 5814783,
|
||||
"fields": [
|
||||
{
|
||||
"name": "Downloads",
|
||||
"value": "[Flatpak](\($flatpak_url))\n[AppImage](\($appimage_url))\n[Build Tarball](\($tar_url))"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}' | curl -X POST -H "Content-Type: application/json" -d @- "${{ secrets.DISCORD_WEBHOOK_URL }}"
|
||||
@@ -1,3 +1,4 @@
|
||||
.cache
|
||||
bld
|
||||
build/
|
||||
test/
|
||||
@@ -5,6 +6,7 @@ tmp/
|
||||
*.bin
|
||||
/*.conf
|
||||
src/lindbergh/*.o
|
||||
src/lindbergh/shaderWork/*.o
|
||||
src/libxdiff/xdiff/*.o
|
||||
src/libxdiff/*.a
|
||||
src/libkswapapi/*.o
|
||||
|
||||
+19
-21
@@ -12,28 +12,28 @@ 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" "src/lindbergh/shaderWork/*.c")
|
||||
list(FILTER LIND_OBJS EXCLUDE REGEX "src/lindbergh/lindbergh.c")
|
||||
|
||||
file(GLOB XDIFF_SRCS "src/libxdiff/xdiff/*.c")
|
||||
|
||||
# Custom compiler and linker flags
|
||||
set(LINDBERGH_EXE -m32)
|
||||
set(LINDBERGH_EXE -m32 -DCOMPILING_LINDBERGH_ELF)
|
||||
set(LINDBERGH_C_FLAGS -g -fPIC -m32 -Wall -Werror -Wno-unused-variable -Wno-unused-function -D_GNU_SOURCE)
|
||||
#SET(CMAKE_C_FLAGS_DEBUG -g)
|
||||
SET(CMAKE_C_FLAGS_RELEASE -O3)
|
||||
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(LIBSEGAAPI_C_FLAGS -m32 -fPIC -shared)
|
||||
set(LIBSEGAAPI_LINKER_FLAGS -m32 -Wl,-Bstatic -lFAudio -Wl,-Bdynamic -lm)
|
||||
set(SHARED_LINKER_FLAGS -m32 -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lXcursor -lSDL3 -lSDL3_ttf -lSDL3_image -ludev -lm -lpthread -shared -lstdc++ -nostdlib)
|
||||
|
||||
# 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/iniParser.c src/lindbergh/sdlInput.c src/lindbergh/controlIniGen.c src/lindbergh/log.c src/lindbergh/jvs.c src/lindbergh/config.c src/lindbergh/evdevInput.c src/lindbergh/gameData.c src/lindbergh/configIni.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 -lSDL3")
|
||||
target_compile_options(lindyloader PRIVATE ${LINDBERGH_EXE})
|
||||
# target_link_libraries(lindyloader "-m32 -lm -sdl3")
|
||||
target_link_libraries(lindyloader PRIVATE lindbergh m SDL3)
|
||||
|
||||
# Build static library libxdiff.a with its own flags
|
||||
add_library(xdiff STATIC ${XDIFF_SRCS})
|
||||
@@ -44,12 +44,12 @@ target_compile_options(xdiff PRIVATE ${LIBXDIFF_C_FLAGS})
|
||||
add_library(lindbergh SHARED ${LIND_OBJS})
|
||||
set_target_properties(lindbergh PROPERTIES PREFIX "")
|
||||
target_link_libraries(lindbergh xdiff ${SHARED_LINKER_FLAGS})
|
||||
target_compile_options(lindbergh PRIVATE ${LINDBERGH_EXE})
|
||||
target_compile_options(lindbergh PRIVATE ${LINDBERGH_C_FLAGS})
|
||||
|
||||
# Build shared library libsegaapi.so
|
||||
add_library(segaapi SHARED src/libsegaapi/libsegaapi.c)
|
||||
target_link_libraries(segaapi FAudio ${LIBSEGAAPI_LINKER_FLAGS})
|
||||
target_compile_options(segaapi PRIVATE ${LIBSEGAAPI_CXX_FLAGS})
|
||||
target_link_libraries(segaapi ${LIBSEGAAPI_LINKER_FLAGS})
|
||||
target_compile_options(segaapi PRIVATE ${LIBSEGAAPI_C_FLAGS})
|
||||
|
||||
# Build shared library libkswapapi.so
|
||||
add_library(kswapapi SHARED src/libkswapapi/libkswapapi.c)
|
||||
@@ -64,12 +64,10 @@ add_custom_command(TARGET posixtime POST_BUILD
|
||||
COMMAND ln -s -f libposixtime.so ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libposixtime.so.2.4)
|
||||
target_compile_options(posixtime PRIVATE ${LINDBERGH_C_FLAGS})
|
||||
|
||||
# Custom clean rule
|
||||
add_custom_target(cleanall
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/build
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/src/lindbergh/*.o
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/src/libsegaapi/*.o
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/src/libkswapapi/*.o
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/src/libxdiff/*.a
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/src/libxdiff/xdiff/*.o
|
||||
# Example: List of extra files and directories to remove during 'make clean'
|
||||
set(MY_EXTRA_CLEAN_STUFF
|
||||
"${CMAKE_BINARY_DIR}/build/libposixtime.so.1" # A specific log file
|
||||
"${CMAKE_BINARY_DIR}/build/libposixtime.so.2.4" # A directory with generated reports
|
||||
)
|
||||
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${MY_EXTRA_CLEAN_STUFF}")
|
||||
|
||||
+25
-2
@@ -5,9 +5,27 @@ RUN dpkg --add-architecture i386 \
|
||||
&& apt install -y --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 \
|
||||
libudev-dev:i386 \
|
||||
zlib1g:i386 \
|
||||
libgpg-error0:i386 \
|
||||
libxcursor-dev:i386 \
|
||||
libxfixes-dev:i386 \
|
||||
libxi-dev:i386 \
|
||||
libxrandr-dev:i386 \
|
||||
libxss-dev:i386 \
|
||||
libxxf86vm-dev:i386 \
|
||||
git \
|
||||
&& apt clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -15,5 +33,10 @@ WORKDIR /lindbergh-loader
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN ls
|
||||
|
||||
RUN tar -xf libs/libs.tar -C /usr/lib/i386-linux-gnu
|
||||
RUN tar -xf libs/includes.tar -C /usr/include
|
||||
|
||||
# Explicitly set the output directory
|
||||
RUN make && mkdir -p /output && cp -r ./build /output
|
||||
RUN make && mkdir -p /output && cp -r ./build /output
|
||||
@@ -2,7 +2,7 @@ CC ?= gcc
|
||||
CFLAGS = -g -fPIC -m32 -pthread -Wall -Werror -Wno-misleading-indentation -Wno-unused-but-set-variable -Wno-unused-variable -Wno-unused-function -D_GNU_SOURCE -Wno-char-subscripts
|
||||
CXX ?= g++
|
||||
LD = $(CXX)
|
||||
LDFLAGS = -m32 -Wl,-z,defs -rdynamic -static-libgcc -lc -ldl -lGL -lglut -lX11 -lXcursor -lSDL2 -lm -lpthread -shared -nostdlib -lasound -L./src/libxdiff -lxdiff -L/app/lib32
|
||||
LDFLAGS = -m32 -Wl,-z,defs -rdynamic -static-libgcc -lc -ldl -lGL -lglut -lX11 -lXcursor -lSDL3 -lSDL3_image -lSDL3_ttf -lm -ludev -lpthread -shared -nostdlib -L./src/libxdiff -lxdiff -L/app/lib32
|
||||
|
||||
BUILD = build
|
||||
|
||||
@@ -12,13 +12,16 @@ XDIFF_SRCS = xdiffi.c xprepare.c xpatchi.c xmerge3.c xemit.c xmissing.c xutils.c
|
||||
XDIFF_OBJS = $(patsubst %.c,src/libxdiff/xdiff/%.o,$(XDIFF_SRCS))
|
||||
|
||||
OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/*.c))
|
||||
OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/shaderWork/*.c)) $(OBJS)
|
||||
|
||||
OBJS := $(filter-out src/lindbergh/lindbergh.o, $(OBJS))
|
||||
|
||||
all: lindbergh libxdiff.a lindbergh.so libsegaapi.so libkswapapi.so libposixtime.so
|
||||
|
||||
lindbergh: src/lindbergh/lindbergh.c src/lindbergh/log.c src/lindbergh/log.h src/lindbergh/jvs.c src/lindbergh/jvs.h src/lindbergh/config.h src/lindbergh/config.c src/lindbergh/evdevinput.h src/lindbergh/evdevinput.c
|
||||
lindbergh: src/lindbergh/lindbergh.c src/lindbergh/iniParser.c src/lindbergh/sdlInput.c src/lindbergh/controlIniGen.c src/lindbergh/log.c src/lindbergh/output.c src/lindbergh/jvs.c src/lindbergh/config.c src/lindbergh/evdevInput.c src/lindbergh/gameData.c src/lindbergh/configIni.c
|
||||
mkdir -p $(BUILD)
|
||||
$(CC) -m32 src/lindbergh/lindbergh.c src/lindbergh/log.h src/lindbergh/log.c src/lindbergh/jvs.h src/lindbergh/jvs.c src/lindbergh/config.h src/lindbergh/config.c src/lindbergh/evdevinput.c src/lindbergh/evdevinput.h -o $(BUILD)/lindbergh -lm
|
||||
$(CC) -m32 -DCOMPILING_LINDBERGH_ELF src/lindbergh/lindbergh.c src/lindbergh/iniParser.c src/lindbergh/sdlInput.c src/lindbergh/controlIniGen.c src/lindbergh/log.c src/lindbergh/output.c src/lindbergh/jvs.c src/lindbergh/config.c src/lindbergh/evdevInput.c src/lindbergh/gameData.c src/lindbergh/configIni.c -o $(BUILD)/lindbergh -lm -lSDL3 -L/app/lib32
|
||||
|
||||
|
||||
libxdiff.a: $(XDIFF_OBJS)
|
||||
mkdir -p $(BUILD)
|
||||
@@ -35,7 +38,7 @@ lindbergh.so: $(OBJS)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
libsegaapi.so: src/libsegaapi/libsegaapi.o
|
||||
$(CC) -m32 -O0 -g src/libsegaapi/libsegaapi.c -lFAudio -L/app/lib32 -fPIC -shared -o $(BUILD)/libsegaapi.so
|
||||
$(CC) -m32 -O0 -g src/libsegaapi/libsegaapi.c -Wl,-Bstatic -lFAudio -Wl,-Bdynamic -L/app/lib32 -fPIC -shared -o $(BUILD)/libsegaapi.so
|
||||
|
||||
libkswapapi.so: src/libkswapapi/libkswapapi.o
|
||||
$(CC) -m32 src/libkswapapi/libkswapapi.o -fPIC -shared -o $(BUILD)/libkswapapi.so
|
||||
@@ -51,12 +54,14 @@ clean:
|
||||
rm -f $(BUILD)/libsegaapi.so
|
||||
rm -f $(BUILD)/lindbergh
|
||||
rm -f src/lindbergh/*.o
|
||||
rm -f src/lindbergh/shaderWork/*.o
|
||||
rm -f src/libsegaapi/*.o
|
||||
|
||||
#clean all rule
|
||||
cleanall:
|
||||
rm -rf $(BUILD)
|
||||
rm -f src/lindbergh/*.o
|
||||
rm -f src/lindbergh/shaderWork/*.o
|
||||
rm -f src/libsegaapi/*.o
|
||||
rm -f src/libkswapapi/*.o
|
||||
rm -f src/libxdiff/*.a
|
||||
|
||||
@@ -4,19 +4,29 @@
|
||||
|
||||
This project emulates the SEGA Lindbergh, allowing games to run on modern Linux computers to be used as replacement hardware for broken Lindbergh systems in physical arcade machines. It supports both Intel and AMD CPUs as well as Intel, NVIDIA and AMD GPUs, surround sound audio, networking and JVS pass through.
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
For detailed information on configuration and controls, please see the following guides:
|
||||
|
||||
- [**🚀 General Guide**](docs/guide.md): Information on supported games, known issues, and general configuration.
|
||||
- [**🎮 Controls Guide**](docs/CONTROLS.md): A detailed guide on how to customize your controls using the `controls.ini` file.
|
||||
|
||||
If you'd like to support the development work of this emulator, see early development builds or get support from the authors please consider [becoming a patreon here](https://www.patreon.com/LindberghLoader).
|
||||
|
||||
If you need any help please ask the community in the [arcade community discord](https://arcade.community). Please only submit issues if they are bugs with the software, ask in the arcade community discord if you're not sure if it's a bug or you're not setting something up properly!
|
||||
|
||||
## Building & Running
|
||||
|
||||
In order to build the loader you will need to install the following dependencies in a Linux environment. We recommend Ubuntu 22.04 LTS as default, but it may work in various configurations like WSL2, Debian, etc.
|
||||
In order to build the loader you will need to install the following dependencies in a Linux environment. We recommend Ubuntu 22.04 LTS, but it will run in almost any modern distro using the AppImage or the Flatpak..
|
||||
Please note other dependencies might be required to run games (see the [guide](docs/guide.md)).
|
||||
|
||||
```shell
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update
|
||||
sudo apt install git build-essential gcc-multilib freeglut3-dev:i386 libsdl2-dev:i386 libfaudio-dev:i386
|
||||
sudo apt install build-essential gcc-multilib g++-multilib cmake fuse freeglut3-dev:i386 libvdpau1:i386 libstdc++5:i386 libxmu6:i386 \
|
||||
libpcsclite1:i386 libncurses5:i386 unzip libsndio-dev libsndio-dev:i386 pulseaudio-utils:i386 zlib1g:i386 libgpg-error0:i386 \
|
||||
libasound2 libasound2-dev libasound2:i386 libasound2-dev:i386 libfreetype6-dev:i386 libdbus-1-dev libpulse-dev libdbus-1-dev:i386 \
|
||||
libudev-dev:i386 libxcursor-dev:i386 libxfixes-dev:i386 libxi-dev:i386 libxrandr-dev:i386 libxss-dev:i386 libxxf86vm-dev:i386 git
|
||||
```
|
||||
|
||||
This emulator will need access to the input devices and serial devices on your computer. You should add your user account to the following groups and then _restart your computer_.
|
||||
@@ -30,9 +40,11 @@ Then you should clone the repository, change directory into it and run make.
|
||||
```shell
|
||||
git clone git@github.com:lindbergh-loader/lindbergh-loader.git
|
||||
cd lindbergh-loader
|
||||
./scripts/appimage/build-deps.sh
|
||||
make
|
||||
```
|
||||
|
||||
|
||||
You should then copy the contents of the build directory to your game directory and run `./lindbergh` for the game, or `./lindbergh -t` for test mode. Please note you might need to set the game executable as "executable" using `chmod +x`.
|
||||
|
||||
```shell
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# 🎮 Guide to Customizing controls.ini
|
||||
|
||||
This document explains how to configure your input devices by editing the `controls.ini` file. This file gives you full control over how your keyboard, mouse, joysticks, and gamepads are mapped to in-game actions.
|
||||
|
||||
## 📄 1. Generating the Default File
|
||||
|
||||
Before you start, it's highly recommended to generate a fresh, default `controls.ini` file. This gives you a complete template with all possible actions and recommended starting points for your controls.
|
||||
|
||||
To generate the file, run the application from your terminal with the `--create` flag, followed by the argument `controls`.
|
||||
|
||||
**Basic Command:**
|
||||
This will create a file named `controls.ini` in the current directory.
|
||||
```bash
|
||||
./lindbergh --create controls
|
||||
```
|
||||
|
||||
**Specifying a Path:**
|
||||
You can also specify a path to create the file in a different location. The folder must already exist.
|
||||
|
||||
- **Create in a specific folder:**
|
||||
```bash
|
||||
./lindbergh --create controls ./my/folder/
|
||||
```
|
||||
- **Create with a custom name and location:**
|
||||
```bash
|
||||
./lindbergh --create controls ./my/path/to/mycontrols.ini
|
||||
```
|
||||
|
||||
## 📂 2. Structure of the INI File
|
||||
|
||||
The file is organized into sections, each marked with a name in square brackets (e.g., `[Config]`). Under each section are key-value pairs, separated by an equals sign (`=`).
|
||||
|
||||
```ini
|
||||
[SectionName]
|
||||
Key = Value1, Value2, ...
|
||||
```
|
||||
|
||||
- **Section:** A category of settings (e.g., `[Driving]`, `[Shooting]`). The game automatically loads the `[Common]` section plus one other section that matches the game's type.
|
||||
- **Key:** The name of the logical in-game action you want to map (e.g., `P1_Start`, `Steer`, `Trigger`).
|
||||
- **Value:** One or more physical input strings, separated by commas, that will trigger the action.
|
||||
|
||||
## ⚙️ 3. Configuration Sections
|
||||
|
||||
### [Config]
|
||||
|
||||
This section contains global settings that affect all input devices.
|
||||
|
||||
- **`<ActionName>_DeadZone`**: Sets a custom analog deadzone for a specific logical action. This gives you precise control over actions like steering and pedals. The value is from `0` (no deadzone) to `32767`. If not specified, a sensible default is used (a large deadzone for centering sticks, a small one for non-centering pedals/triggers).
|
||||
- **Example:** `Steer_DeadZone = 7000`
|
||||
- **Example for Player 2:** `P2_Gas_DeadZone = 500`
|
||||
- **`ShakeIncreaseRate` / `ShakeDecayRate`**: Controls the gun shaking effect in games like *House of the Dead 4*.
|
||||
|
||||
### [ControllerGUIDs]
|
||||
|
||||
This section is managed automatically by the application to ensure your controllers are always assigned to the correct player.
|
||||
|
||||
- **`P<N>_GUID`**: Stores the unique hardware ID for the controller assigned to Player `<N>`. You should not need to edit this section manually.
|
||||
- **Swapping Players**: If you have multiple controllers and want to change which one is Player 1 vs. Player 2, you can do so by swapping the GUID strings between the `P1_GUID` and `P2_GUID` keys.
|
||||
- **Resetting Assignments**: If you want to reset all controller assignments, you can delete this entire section. The application will regenerate it on the next launch based on the order the controllers are detected.
|
||||
|
||||
### Game Type Sections
|
||||
|
||||
These sections contain the actual control mappings. The application will load `[Common]` and one other section based on the game being played.
|
||||
|
||||
- **`[Common]`**: For actions shared by all games (Test, Coin, Start, Service).
|
||||
- **`[Digital]`**: For standard arcade games with an 8-way joystick and buttons.
|
||||
- **`[Driving]`**: For racing games with analog steering, gas, and brake.
|
||||
- **`[Shooting]`**: For lightgun games.
|
||||
- **`[ABC]`**: For games with flight-stick style controls like *After Burner Climax*.
|
||||
- **`[Mahjong]`**: For mahjong panel controls.
|
||||
|
||||
## 🕹️ 4. Understanding Controller Mappings (gamecontrollerdb.txt)
|
||||
|
||||
For the application to recognize your gamepad (like an Xbox or PlayStation controller) and allow you to use the simple `GC` bindings (e.g., `GC0_BUTTON_A`), it needs a database of controller mappings.
|
||||
|
||||
- **Built-in Mappings**: The application already includes a large, built-in database of mappings for hundreds of common controllers, so many devices will work out of the box.
|
||||
- **Adding Custom Mappings**: If you have a rare controller or want to override the built-in behavior, you can provide an external mapping file. The file can have any name (though `gamecontrollerdb.txt` is standard) but must follow the SDL mapping format. You can load a custom file by running the application with the `--controllerdb` flag:
|
||||
```bash
|
||||
./lindbergh --controllerdb ./path/to/your_mappings.txt
|
||||
```
|
||||
The mappings from your file will be added to the existing built-in database.
|
||||
- **Where to get mapping files**: You can find the community-sourced `gamecontrollerdb.txt` file, which is a great starting point for custom mappings, at the [official SDL GameControllerDB GitHub repository](https://github.com/gabomdq/SDL_GameControllerDB).
|
||||
|
||||
## ⌨️ 5. Binding String Syntax
|
||||
|
||||
The value for each key is a comma-separated list of physical input strings. Here is the format for each device type:
|
||||
|
||||
### Keyboard
|
||||
|
||||
- **Format**: `KEY_<ScanCodeName>`
|
||||
- **Example**: `KEY_A, KEY_Space, KEY_ArrowUp`
|
||||
- **Note**: The name is the SDL Scancode Name, not necessarily the character on the key.
|
||||
|
||||
### Mouse
|
||||
|
||||
- **Buttons**: `MOUSE_LEFT_BUTTON`, `MOUSE_RIGHT_BUTTON`, `MOUSE_MIDDLE_BUTTON`
|
||||
- **Axes**: `MOUSE_AXIS_X`, `MOUSE_AXIS_Y`
|
||||
|
||||
### Generic Joysticks (JOY) and Standard Gamepads (GC)
|
||||
|
||||
- **Player Index `<N>`**: The number `<N>` after `JOY` or `GC` always refers to the player. The application uses the `[ControllerGUIDs]` section to determine which physical device is assigned to which player. `JOY0_` and `GC0_` will always be the controller assigned to Player 1, `JOY1_` and `GC1_` will be for Player 2, and so on.
|
||||
- **Buttons**:
|
||||
- `JOY<N>_BUTTON_<ButtonNumber>` (e.g., `JOY0_BUTTON_0`)
|
||||
- `GC<N>_BUTTON_<ButtonName>` (e.g., `GC0_BUTTON_A`, `GC0_BUTTON_LEFTSHOULDER`)
|
||||
- **Hats/D-Pads**: `JOY<N>_HAT<HatNumber>_<Direction>` (e.g., `JOY0_HAT0_UP`)
|
||||
- **Axes**:
|
||||
- `JOY<N>_AXIS_<AxisNumber>` (e.g., `JOY0_AXIS_0`)
|
||||
- `GC<N>_AXIS_<AxisName>` (e.g., `GC0_AXIS_LEFTX`, `GC0_AXIS_TRIGGERRIGHT`)
|
||||
|
||||
### Axis Suffixes (for JOY and GC axes)
|
||||
|
||||
You can add suffixes to an axis string to change its behavior:
|
||||
|
||||
- **`_POSITIVE` / `_NEGATIVE`**: Treats one direction of an analog axis like a digital button press.
|
||||
- **Example**: `P1_Right = GC0_AXIS_LEFTX_POSITIVE`
|
||||
- **`_POSITIVE_HALF` / `_NEGATIVE_HALF`**: Used to map two different triggers or pedals to a single combined analog action (like a throttle).
|
||||
- **Example**: `Throttle = JOY0_AXIS_5_POSITIVE_HALF, JOY0_AXIS_2_NEGATIVE_HALF`
|
||||
- **`_INVERTED`**: Reverses the direction of an analog axis.
|
||||
- **Example**: `Throttle = GC0_AXIS_RIGHTY_INVERTED`
|
||||
|
||||
### Example
|
||||
|
||||
Here is a complex mapping for Player 1's "Start" button:
|
||||
|
||||
```ini
|
||||
P1_Start = KEY_1, GC0_BUTTON_START, JOY0_BUTTON_9
|
||||
```
|
||||
|
||||
This means the action will be triggered by pressing the `'1'` key on the keyboard, the `'Start'` button on Player 1's gamepad, OR button number `9` on Player 1's generic joystick.
|
||||
Binary file not shown.
+104
@@ -2,6 +2,110 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
|
||||
## [2.1.3]
|
||||
### Added
|
||||
- MJ4 Rev. A, MJ4 Rev. F and MJ4 Evolution Rev. B support.
|
||||
### Fixed
|
||||
- Fixed a bug in the shader patching code that prevented the loader to compile shaders for non nVidia GPUs.
|
||||
|
||||
## [2.1.2]
|
||||
### Added
|
||||
- Virtual Card file support for ID4 and ID5 (read the documentation).
|
||||
- Show / Hide crosshairs in parts of games like HOD4, HOD-SP and RAMBO should be hidden.
|
||||
- WiiRemote support for lightgun games. (Needs Testing)
|
||||
- Custom crosshairs for 2 players,rendered directly on-screen rather than replacing mouse cursor.
|
||||
- Preliminary Forcefeedback support with game controllers. (No Steering wheels yet)
|
||||
- Touch screen support for MJ4 and AxA games.
|
||||
### Fixed
|
||||
- Patched Outrun ELF at original resolution would not get fixed.
|
||||
- Possible memory leak (Still nothing major, ABC has its own memory leaks).
|
||||
- Fixed a bug in the resolution patch for 2Spicy.
|
||||
|
||||
## [2.1.1]
|
||||
### Added
|
||||
- HOD4 Boss battle targets should be fine now.
|
||||
- Discord Bot.
|
||||
- Zink support in appimage (fixes Zink in the Steam Deck).
|
||||
- Change the Mesa version on the fly to fix ID games rendering issues in the appimage.
|
||||
### Fixed
|
||||
- A typo in the command to load custom gamecontrollerdb (--controllerdb) to match the guide.
|
||||
- Disable SDL input when EvDev is selected.
|
||||
- A fix to make the Maximize window button show.
|
||||
### Missing from las changelog
|
||||
- Harley network link support (up to 4 players)
|
||||
- R-Tuned network link support (up to 4 players)
|
||||
- Hummer network link support (up to 4 players)
|
||||
- 2 Spicy network link support (up to 2 players)
|
||||
- Sega Race TV network link support (up to 4 players)
|
||||
- ID4 and ID5 network link support (up to 2 players)
|
||||
|
||||
## [2.1.0]
|
||||
### Added
|
||||
- Rambo DVP-0078 (China version) support.
|
||||
- ID5 DVP-0070C support.
|
||||
- AxA games added.
|
||||
- An option to set a percentage to reduce the travel in ID games steering wheel.
|
||||
- An option to change the language in ID5 EXP (DVP-0084 and DVP-0084A) to Chinese.
|
||||
- An option to change the way the Guns are shown in Rambo.
|
||||
- New controls guide in pdf format inside docs folder.
|
||||
- Change the Outrun Testmode font to a built in font that scales with the resolution set.
|
||||
- An option in lindberg elf to specify a comunity built game controller database.
|
||||
- An option in lindberg elf to generate a default controls.ini.
|
||||
- An option to in lindberg elf generate a default lindbergh.ini file.
|
||||
- SDL controls configuration using logical names.
|
||||
- SDL input remade with separate configuration file.
|
||||
- Option to make Primeval Hunt test mode load as a single screen.
|
||||
- Blitting to support stretching with fullscreen on the fly. (F11 to switch from / to fullscreen).
|
||||
- Bobby's birthday checking!
|
||||
- Prevent the created windows to resize/maximize in LGJ games and PH (Cause issues).
|
||||
### Code changes
|
||||
- config file format changed to ini (TOML format).
|
||||
- Removed X11 completely and only SDL is used to create windows and handle controls.
|
||||
- Cleaned the shader patches files.
|
||||
- Moved all the network related code to a new file.
|
||||
- Moved all the shader caching to a new file.
|
||||
- Refactored config.c to make it more readable.
|
||||
- Cleaned the code, fixed all the variables, functions and file names to use the same naming convention.
|
||||
- Cleaned unnecessary global variables in resolution.c
|
||||
- Replaced stb_image with SDL_image.
|
||||
- Switched to SDL3
|
||||
- Version in github now matches the version in the changelog.
|
||||
- FPS target to float.
|
||||
- Clear window background when the game is loading to prevent ghosting in SDL
|
||||
### Fixed
|
||||
- a bug in config.c where MJ4_ENABLED_ALL_THE_TIME was using the wrong variable.
|
||||
- Fixed a bug in Let's Go Jungle Special Resolution Patch.
|
||||
- Fixed a bug that prevented the loader to start when the current folder contained spaces and lindbergh.so was there.
|
||||
|
||||
## [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 (read guide.md).
|
||||
|
||||
## [2.0.8]
|
||||
### Added
|
||||
- Changed preprocessor for avoiding multiple inclusions to #pragma once and clean some includes.
|
||||
### Fixed
|
||||
- Test mode in HOD4 SP now stretches.
|
||||
|
||||
## [2.0.7]
|
||||
### Added
|
||||
- Option -g | --gamepath to lindbergh to point to games in a different folder.
|
||||
|
||||
+79
-22
@@ -1,6 +1,6 @@
|
||||
# Guide
|
||||
# 🚀 Guide
|
||||
|
||||
## Supported Games
|
||||
## 🎮 Supported Games
|
||||
|
||||
The follow list of games are supported. It is worth noting that there are multiple releases of these games, and some specific releases may not be supported.
|
||||
|
||||
@@ -31,16 +31,21 @@ The follow list of games are supported. It is worth noting that there are multip
|
||||
| Initial D 5 EXP 2.0 | SBTS | DVP-0084 | ✓ | ✓ | ✓ | |
|
||||
| Initial D 5 EXP 2.0 Rev A | SBQN | DVP-0084A | ✓ | ✓ | ✓ | |
|
||||
| Initial D Arcade Stage 5 Rev A | SBQZ | DVP-0070A | ✓ | ✓ | ✓ | |
|
||||
| Initial D Arcade Stage 5 Rev C | SBQZ | DVP-0070C | ✓ | ✓ | ✓ | |
|
||||
| Initial D Arcade Stage 5 Rev F | SBQZ | DVP-0070F | ✓ | ✓ | ✓ | |
|
||||
| Let's Go Jungle | SBLU | DVP-0011 | ✓ | ✓ | ✓ | |
|
||||
| Let's Go Jungle! Rev A | SBLU | DVP-0011A | ✓ | ✓ | ✓ | |
|
||||
| Let's Go Jungle Special | SBNR | DVP-0036 | ✓ | ✓ | ✓ | |
|
||||
| SEGA Network Taisen Mahjong MJ4 | SBPN | DVP-0049G | ✓ | ✓ | ✓ | ✓ |
|
||||
| SEGA Network Taisen Mahjong MJ4 Rev A | SBPN | DVP-0049A | ✓ | ✓ | ✓ | ✓ |
|
||||
| SEGA Network Taisen Mahjong MJ4 Rev F | SBPN | DVP-0049F | ✓ | ✓ | ✓ | ✓ |
|
||||
| SEGA Network Taisen Mahjong MJ4 Rev G | SBPN | DVP-0049G | ✓ | ✓ | ✓ | ✓ |
|
||||
| SEGA Network Taisen Mahjong MJ4 Evolution | SBTA | DVP-0081 | ✓ | ✓ | ✓ | ✓ |
|
||||
| SEGA Network Taisen Mahjong MJ4 Evolution Rev B | SBTA | DVP-0081B | ✓ | ✓ | ✓ | ✓ |
|
||||
| Outrun 2 SP SDX | SBMB | DVP-0015 | ✓ | ✓ | ✓ | ✓ |
|
||||
| Outrun 2 SP SDX Rev A | SBMB | DVP-0015A | ✓ | ✓ | ✓ | ✓ |
|
||||
| Primeval Hunt | SBPP | DVP-0048 | ✓ | ✓ | ✓ | ✓ |
|
||||
| Rambo | SBQL | DVP-0069 | ✓ | ✓ | ✓ | ✓ |
|
||||
| Rambo (China) | SBSS | DVP-0078 | ✓ | ✓ | ✓ | ✓ |
|
||||
| R-Tuned | SBQW | DVP-0060 | ✓ | ✓ | ✓ | ✓ |
|
||||
| Sega Race TV | SBPF | DVP-0044 | ✓ | ✓ | ✓ | ✓ |
|
||||
| The House of the Dead 4 Rev A | SBLC | DVP-0003A | ✓ | ✓ | ✓ | ✓ |
|
||||
@@ -66,19 +71,19 @@ The follow list of games are supported. It is worth noting that there are multip
|
||||
| Virtua Tennis 3 Rev B | SBKX | DVP-0005B | ✓ | ✓ | ✓ | ✓ |
|
||||
| Virtua Tennis 3 Rev C | SBKX | DVP-0005C | ✓ | ✓ | ✓ | ✓ |
|
||||
|
||||
## Game Issues & Fixes
|
||||
## 🛠️ Game Issues & Fixes
|
||||
|
||||
### All Games: missing dependencies & libraries
|
||||
|
||||
Additional libraries that you might need to install to get all games running are listed here:
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo apt install build-essential freeglut3:i386 freeglut3-dev:i386 libglew-dev xorg-dev libopenal1:i386 libopenal-dev:i386 libxmu6:i386 libstdc++5:i386 libsdl2-dev:i386 libfaudio0:i386 libfaudio-dev:i386 libncurses5:i386 libasound2-dev:i386 alsa-utils:i386 libasound2-plugins:i386
|
||||
```
|
||||
|
||||
On Debian, `libglut` might not be found. Adding a symbolic link will fix it.
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo ln -s /lib/i386-linux-gnu/libglut.so /lib/i386-linux-gnu/libglut.so.3
|
||||
```
|
||||
|
||||
@@ -88,15 +93,15 @@ sudo ln -s /lib/i386-linux-gnu/libglut.so /lib/i386-linux-gnu/libglut.so.3
|
||||
|
||||
### Outrun 2 SP SDX
|
||||
|
||||
- You need to set `SKIP_OUTRUN_CABINET_CHECK` to 1 in `lindbergh.conf` to play in DX, SDX or Standard cabinet types.
|
||||
- You need to set `SKIP_OUTRUN_CABINET_CHECK` to 1 in `lindbergh.ini` to play in DX, SDX or Standard cabinet types.
|
||||
|
||||
### Hummer (all versions)
|
||||
|
||||
The games will ask for libGLcore.so.1 and libnvidia-tls.so.1 you can copy for example libkswapapi.so as libGLcore.so.1 and libnvidia-tls.so.1 and the game will work fine.
|
||||
|
||||
## Configuration File : lindbergh.conf
|
||||
## ⚙️ Configuration File : lindbergh.ini
|
||||
|
||||
All configurable options are set and explained in the [`lindbergh.conf`](lindbergh.conf) file itself, and should be reasonably easy to understand. This chapter further explains some of those setup options.
|
||||
All configurable options are set and explained in the [`lindbergh.ini`](lindbergh.ini) file itself, and should be reasonably easy to understand. This chapter further explains some of those setup options.
|
||||
|
||||
You will see the value `AUTO` by default for some of the config options. This is present when the emulator might set that config value to multiple different things itself. For example rideboard emulation is set to `AUTO` so that the emulator doesn't enable it for all games, and only turns it on for the special games that require it. If it was set to `1` it would always emulate the rideboard on serial port 2, and if it was set to `0` it would never emulate it.
|
||||
|
||||
@@ -118,26 +123,26 @@ In the second input mode, inputs are taken directly from the evdev library in li
|
||||
|
||||
To list the available inputs you should type:
|
||||
|
||||
```
|
||||
```bash
|
||||
./lindbergh --list-controllers
|
||||
```
|
||||
|
||||
From there you will be able to see the controllers and all of the inputs support. Then in the config file you should map an arcade input to a controller input as follows.
|
||||
|
||||
```
|
||||
```ini
|
||||
PLAYER_1_BUTTON_UP XBOX_CONTROLLER_BTN_UP
|
||||
ANALOGUE_1 XBOX_CONTROLLER_ABS_X
|
||||
```
|
||||
|
||||
You can map digital controls to analogue controls. The analogue value will be set to the digital value, such that if the button isn't pressed it will be set to 0, and if the button is pressed it will be set to MAX. This is useful if your controller doesn't have analogue accelerator or break buttons.
|
||||
|
||||
```
|
||||
```ini
|
||||
ANALOGUE_0 XBOX_CONTROLLER_BUTTON_BR
|
||||
```
|
||||
|
||||
You can map analogue controls to digital controls too. For each analogue input, there are 2 more inputs created ending in _MAX and _MIN. These are digital controls that will be triggered when the analogue input is either at the minimum or maximum value. This can be useful if you'd like to use an analogue stick to control a fighting game for example.
|
||||
|
||||
```
|
||||
```ini
|
||||
PLAYER_1_BUTTON_UP XBOX_CONTROLLER_ABS_Y_MAX
|
||||
PLAYER_1_BUTTON_DOWN XBOX_CONTROLLER_ABS_Y_MIN
|
||||
PLAYER_1_BUTTON_LEFT XBOX_CONTROLLER_ABS_X_MIN
|
||||
@@ -146,7 +151,7 @@ PLAYER_1_BUTTON_RIGHT XBOX_CONTROLLER_ABS_X_MAX
|
||||
|
||||
You can set a deadzone for analogue axis. Each anaolgue input has 3 potential deadzones: start, middle, and end. The deadzone is a percentage of the range from the start, middle, or end that should snap to its respective extreme. This is useful for input devices that don't perfectly center, or pedals that don't perfectly return to 0. For instance.
|
||||
|
||||
```
|
||||
```ini
|
||||
ANALOGUE_DEADZONE_1 10 13 10
|
||||
```
|
||||
|
||||
@@ -161,7 +166,7 @@ Will cause input on analogue input 1 to be clamped as follows:
|
||||
There are currently no audio options that you can set. If you have a stereo sound card installed then the audio will be downmixed to stereo. If you have a 5.1 sound card installed and the game supports surround sound, each surround channel should be passed through properly and should play sound as it was originally intended.
|
||||
|
||||
|
||||
## Installing and Running the Flatpak
|
||||
## 📦 Installing and Running the Flatpak
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -171,19 +176,19 @@ Before installing the main package, ensure that Flatpak is set up on your system
|
||||
|
||||
Once downloaded, install Lindbergh Loader by running the following command in your terminal:
|
||||
|
||||
```
|
||||
```bash
|
||||
flatpak install lindbergh-loader-dev.flatpak
|
||||
```
|
||||
|
||||
Additionally, install the required dependencies:
|
||||
|
||||
```
|
||||
```bash
|
||||
flatpak install org.freedesktop.Platform.Compat.i386//24.08
|
||||
flatpak install org.freedesktop.Platform.GL32.default//24.08
|
||||
```
|
||||
Intel GPU Users (VAAPI Support):
|
||||
|
||||
```
|
||||
```bash
|
||||
flatpak install org.freedesktop.Platform.VAAPI.Intel.i386//24.08
|
||||
```
|
||||
|
||||
@@ -192,12 +197,12 @@ NVIDIA GPU Users (Driver Support):
|
||||
If your system uses an NVIDIA GPU, you need to install the matching NVIDIA driver package for your system.
|
||||
To check your NVIDIA driver version, run:
|
||||
|
||||
```
|
||||
```bash
|
||||
nvidia-smi
|
||||
```
|
||||
Then, install the corresponding Flatpak package, replacing 570-86-16 with your actual driver version:
|
||||
|
||||
```
|
||||
```bash
|
||||
flatpak install org.freedesktop.Platform.GL32.nvidia-570-86-16
|
||||
```
|
||||
|
||||
@@ -205,7 +210,7 @@ flatpak install org.freedesktop.Platform.GL32.nvidia-570-86-16
|
||||
|
||||
Once installed, launch the application with:
|
||||
|
||||
```
|
||||
```bash
|
||||
flatpak run com.github.lindberghloader.dev
|
||||
```
|
||||
|
||||
@@ -219,8 +224,60 @@ By default, games should be added to:
|
||||
|
||||
If you need to add more locations, install Flatseal:
|
||||
|
||||
```
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
./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.
|
||||
|
||||
```bash
|
||||
./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:
|
||||
|
||||
```bash
|
||||
./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:
|
||||
|
||||
```bash
|
||||
./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.
|
||||
|
||||
## 📝 Creating lindbergh.ini or controls.ini
|
||||
|
||||
```bash
|
||||
./lindbergh --create --help
|
||||
```
|
||||
|
||||
Will give you instructions on how to use the new commands.
|
||||
@@ -1,224 +0,0 @@
|
||||
# SEGA Lindbergh Emulator Configuration File
|
||||
# By the Lindbergh Development Team 2024-2025
|
||||
|
||||
# Set the requested dip switch width here
|
||||
WIDTH AUTO
|
||||
|
||||
# Set the requested dip switch height here
|
||||
HEIGHT AUTO
|
||||
|
||||
# Boost render resolution in HOD4/2Spicy/Harley/Rambo/HOD-EX/ID4/ID5 and LGJ
|
||||
# BOOST_RENDER_RES 1
|
||||
|
||||
# Set if the emulator should go full screen
|
||||
# FULLSCREEN 0
|
||||
|
||||
# Sets the Input Mode
|
||||
# Mode 0: will use both SDL/X11 and EVDEV inputs (default)
|
||||
# Mode 1: will use SDL/X11 inputs only
|
||||
# Mode 2: will use EVDEV raw inputs only, which should be configured at the bottom of the settings file
|
||||
# INPUT_MODE 0
|
||||
|
||||
# Set to 1 if you want to disable SDL (Fixes SRTV boost bar)
|
||||
# NO_SDL 0
|
||||
|
||||
# Set the Region ( JP/US/EX )
|
||||
# REGION EX
|
||||
|
||||
# Set if you want the game to be Free Play
|
||||
# FREEPLAY 1
|
||||
|
||||
# Set if the emulator should emulate JVS and use the keyboard/mouse for controls.
|
||||
# If this is set to 0, then the emulator will route the traffic to the serial device
|
||||
# defined in JVS_PATH if it has been defined.
|
||||
# EMULATE_JVS 1
|
||||
|
||||
# Set if the emulator should emulate the rideboard used in the special games here
|
||||
# If this is set to 0, then the emulator will route the traffic to one of the serial ports
|
||||
EMULATE_RIDEBOARD AUTO
|
||||
|
||||
# Set if the emulator should emulate the driveboard used in driving games here
|
||||
# If this is set to 0, then the emulator will route the traffic to one of the serial ports
|
||||
EMULATE_DRIVEBOARD AUTO
|
||||
|
||||
# Set if the emulator should emulate the motion board from Outrun 2 SP SDX here
|
||||
# If this is set to 0, then the emulator will route the traffic to one of the serial ports
|
||||
EMULATE_MOTIONBOARD AUTO
|
||||
|
||||
# Set if the loader should emulate the card reader in Virtua Tenis 3
|
||||
EMULATE_CARDREADER AUTO
|
||||
|
||||
# Set to 1 if the loader should emulate the touchscreen with the mouse in PH.
|
||||
# EMULATE_TOUCHSCREEN 1
|
||||
|
||||
# Card File for reader 1
|
||||
# CARDFILE_01 Card_01.crd
|
||||
|
||||
# Card File for reader 2
|
||||
# CARDFILE_02 Card_02.crd
|
||||
|
||||
# Define the path to pass the JVS packets to if JVS is not being emulated.
|
||||
# JVS_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to pass the first serial port to if it's not being emulated.
|
||||
# SERIAL_1_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to pass the second serial port to if it's not being emulated.
|
||||
# SERIAL_2_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to the sram.bin file
|
||||
# SRAM_PATH sram.bin
|
||||
|
||||
# Define the path to the eeprom.bin file
|
||||
# EEPROM_PATH eeprom.bin
|
||||
|
||||
# Set the GPU vendor (0 = Autodetect / 1 = NVidia / 2 = AMD / 3 = ATI(AMD-PRO) / 4 = Intel / 5 = Unknown)
|
||||
# GPU_VENDOR 0
|
||||
|
||||
# Set if you want to see debug messages in the console
|
||||
# DEBUG_MSGS 0
|
||||
|
||||
# Set if you'd like to add a border for optical light gun tracking
|
||||
# BORDER_ENABLED 0
|
||||
|
||||
# Set the thickness of the white border as a percentage of the width of the screen
|
||||
# WHITE_BORDER_PERCENTAGE 2
|
||||
|
||||
# Set the thickness of the black border which sits around the
|
||||
# white border as a percentage of the width of the screen
|
||||
# BLACK_BORDER_PERCENTAGE 0
|
||||
|
||||
# Set to true if you experience flickering in hummer
|
||||
# HUMMER_FLICKER_FIX 0
|
||||
|
||||
# Set to keep the aspect ratio (4:3) in games like Sega Race TV and Primeval Hunt
|
||||
# KEEP_ASPECT_RATIO 0
|
||||
|
||||
# Set to 0 if you want to disable the Glare effect in OutRun
|
||||
# OUTRUN_LENS_GLARE_ENABLED 1
|
||||
|
||||
# Set to 1 to bypass cabinet checks including drive board and tower in Outrun 2 SP SDX
|
||||
# SKIP_OUTRUN_CABINET_CHECK 0
|
||||
|
||||
# Set to 1 if you want to limit the FPS in games that are not limited like OutRun2
|
||||
# FPS_LIMITER_ENABLED 0
|
||||
|
||||
# Set the target FPS (will only work if FPS_LIMITER_ENABLED = 1)
|
||||
# FPS_TARGET 60
|
||||
|
||||
# Set to 1 if you want to render LGJ using the mesa patches instead of nVidia (fixes some glitches)
|
||||
# LGJ_RENDER_WITH_MESA 1
|
||||
|
||||
# Set the Primeval Hunt mode
|
||||
# Mode 0: Default (Side by Side)
|
||||
# Mode 1: No touch screen
|
||||
# Mode 2: Side by Side
|
||||
# Mode 3: 3ds mode 1 (Touch screen to the right)
|
||||
# Mode 4: 3ds mode 2 (Touch screen to the bottom)
|
||||
# PRIMEVAL_HUNT_MODE 1
|
||||
|
||||
# Disble to use the original fonts instead of the built in font in the loader
|
||||
# DISABLE_BUILTIN_FONT 0
|
||||
|
||||
# Disble to use the original logos instead of the built in logos in the loader
|
||||
# DISABLE_BUILTIN_LOGOS 0
|
||||
|
||||
# Set to 0 to enable the mouse pointer/Cursor
|
||||
# HIDE_CURSOR 1
|
||||
|
||||
# Set the custom mouse pointer from a PNG file (Usefull for shooting games)
|
||||
# CUSTOM_CURSOR myCustomCursor.png
|
||||
|
||||
# Set the width of the custom cursor
|
||||
# CUSTOM_CURSOR_WIDTH 32
|
||||
|
||||
# Set the height of the custom cursor
|
||||
# CUSTOM_CURSOR_HEIGHT 32
|
||||
|
||||
# Set a custom cursor for the touch screen in Primeval Hunt
|
||||
# PH_TOUCH_CURSOR myCustomHandCursor.png
|
||||
|
||||
# Set the width of the custom cursor
|
||||
# PH_TOUCH_CURSOR_WIDTH 32
|
||||
|
||||
# Set the height of the custom cursor
|
||||
# PH_TOUCH_CURSOR_HEIGHT 32
|
||||
|
||||
# Hacky way to make MJ4 work at prohibited times?
|
||||
# MJ4_ENABLED_ALL_THE_TIME 1
|
||||
|
||||
# Sets the IP address in the eeprom for Outrun link (you have to put your NIC ip)
|
||||
# OR2_IP 192.168.1.2
|
||||
# OR2_NETMASK 255.255.255.0
|
||||
|
||||
# House of the dead 4 speed fix set the frequency of your CPU in Ghz (The speed reported by the manufacturer)
|
||||
# CPU_FREQ_GHZ 4.0
|
||||
|
||||
# Set the colour of the lindbergh to change the Segaboot logo
|
||||
# Possible colours are: YELLOW, RED, BLUE, SILVER, REDEX
|
||||
# LINDBERGH_COLOUR YELLOW
|
||||
|
||||
# X11 MODE (Input Mode 1)
|
||||
# To find the value pairs for these run `xev` on linux
|
||||
|
||||
TEST_KEY 28 # t
|
||||
PLAYER_1_START_KEY 10 # 1
|
||||
PLAYER_1_SERVICE_KEY 39 # s
|
||||
PLAYER_1_COIN_KEY 14 # 5
|
||||
PLAYER_1_UP_KEY 111 # Up
|
||||
PLAYER_1_DOWN_KEY 116 # Down
|
||||
PLAYER_1_LEFT_KEY 113 # Left
|
||||
PLAYER_1_RIGHT_KEY 114 # Right
|
||||
PLAYER_1_BUTTON_1_KEY 24 # q
|
||||
PLAYER_1_BUTTON_2_KEY 25 # w
|
||||
PLAYER_1_BUTTON_3_KEY 26 # e
|
||||
PLAYER_1_BUTTON_4_KEY 27 # r
|
||||
|
||||
# EVDEV MODE (Input Mode 2)
|
||||
# To find the value pairs for these run ./lindbergh --list-controllers
|
||||
|
||||
# TEST_BUTTON AT_TRANSLATED_SET_2_KEYBOARD_KEY_Z
|
||||
|
||||
# PLAYER_1_BUTTON_START AT_TRANSLATED_SET_2_KEYBOARD_KEY_1
|
||||
# PLAYER_1_BUTTON_SERVICE AT_TRANSLATED_SET_2_KEYBOARD_KEY_S
|
||||
# PLAYER_1_BUTTON_UP AT_TRANSLATED_SET_2_KEYBOARD_KEY_UP
|
||||
# PLAYER_1_BUTTON_DOWN AT_TRANSLATED_SET_2_KEYBOARD_KEY_DOWN
|
||||
# PLAYER_1_BUTTON_LEFT AT_TRANSLATED_SET_2_KEYBOARD_KEY_LEFT
|
||||
# PLAYER_1_BUTTON_RIGHT AT_TRANSLATED_SET_2_KEYBOARD_KEY_RIGHT
|
||||
# PLAYER_1_BUTTON_1 AT_TRANSLATED_SET_2_KEYBOARD_KEY_Q
|
||||
# PLAYER_1_BUTTON_2 AT_TRANSLATED_SET_2_KEYBOARD_KEY_W
|
||||
# PLAYER_1_BUTTON_3 AT_TRANSLATED_SET_2_KEYBOARD_KEY_E
|
||||
# PLAYER_1_BUTTON_4 AT_TRANSLATED_SET_2_KEYBOARD_KEY_R
|
||||
# PLAYER_1_BUTTON_5 AT_TRANSLATED_SET_2_KEYBOARD_KEY_T
|
||||
# PLAYER_1_BUTTON_6 AT_TRANSLATED_SET_2_KEYBOARD_KEY_Y
|
||||
# PLAYER_1_BUTTON_7 AT_TRANSLATED_SET_2_KEYBOARD_KEY_I
|
||||
# PLAYER_1_BUTTON_8 AT_TRANSLATED_SET_2_KEYBOARD_KEY_O
|
||||
|
||||
# PLAYER_2_BUTTON_START AT_TRANSLATED_SET_2_KEYBOARD_KEY_1
|
||||
# PLAYER_2_BUTTON_SERVICE AT_TRANSLATED_SET_2_KEYBOARD_KEY_S
|
||||
# PLAYER_2_BUTTON_UP AT_TRANSLATED_SET_2_KEYBOARD_KEY_UP
|
||||
# PLAYER_2_BUTTON_DOWN AT_TRANSLATED_SET_2_KEYBOARD_KEY_DOWN
|
||||
# PLAYER_2_BUTTON_LEFT AT_TRANSLATED_SET_2_KEYBOARD_KEY_LEFT
|
||||
# PLAYER_2_BUTTON_RIGHT AT_TRANSLATED_SET_2_KEYBOARD_KEY_RIGHT
|
||||
# PLAYER_2_BUTTON_1 AT_TRANSLATED_SET_2_KEYBOARD_KEY_Q
|
||||
# PLAYER_2_BUTTON_2 AT_TRANSLATED_SET_2_KEYBOARD_KEY_W
|
||||
# PLAYER_2_BUTTON_3 AT_TRANSLATED_SET_2_KEYBOARD_KEY_E
|
||||
# PLAYER_2_BUTTON_4 AT_TRANSLATED_SET_2_KEYBOARD_KEY_R
|
||||
# PLAYER_2_BUTTON_5 AT_TRANSLATED_SET_2_KEYBOARD_KEY_T
|
||||
# PLAYER_2_BUTTON_6 AT_TRANSLATED_SET_2_KEYBOARD_KEY_Y
|
||||
# PLAYER_2_BUTTON_7 AT_TRANSLATED_SET_2_KEYBOARD_KEY_I
|
||||
# PLAYER_2_BUTTON_8 AT_TRANSLATED_SET_2_KEYBOARD_KEY_O
|
||||
|
||||
# ANALOGUE_1 SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_X
|
||||
# ANALOGUE_2 SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_Y
|
||||
# ANALOGUE_3 SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_Z
|
||||
# ANALOGUE_4 SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_RZ
|
||||
|
||||
# ANALOGUE_DEADZONE_1 0 0 0
|
||||
# ANALOGUE_DEADZONE_2 0 0 0
|
||||
# ANALOGUE_DEADZONE_3 0 0 0
|
||||
# ANALOGUE_DEADZONE_4 0 0 0
|
||||
# ANALOGUE_DEADZONE_5 0 0 0
|
||||
# ANALOGUE_DEADZONE_6 0 0 0
|
||||
# ANALOGUE_DEADZONE_7 0 0 0
|
||||
# ANALOGUE_DEADZONE_8 0 0 0
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
APPIMAGEDIRNAME=lindbergh-loader.AppDir
|
||||
OUTPUT_FOLDER=$(realpath "./$APPIMAGEDIRNAME")
|
||||
|
||||
mkdir build-deps
|
||||
cd build-deps
|
||||
|
||||
# Build SDL3
|
||||
git clone https://github.com/libsdl-org/SDL.git
|
||||
cd SDL
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ -DCMAKE_C_FLAGS=-m32 -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j4
|
||||
sudo make install
|
||||
cd ../../
|
||||
|
||||
# Build SDL3_ttf
|
||||
git clone https://github.com/libsdl-org/SDL_ttf.git
|
||||
cd SDL_ttf
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ -DCMAKE_C_FLAGS=-m32 -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j4
|
||||
sudo make install
|
||||
cd ../../
|
||||
|
||||
# Build SDL3_image
|
||||
git clone https://github.com/libsdl-org/SDL_image.git
|
||||
cd SDL_image
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ -DCMAKE_C_FLAGS=-m32 -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j4
|
||||
sudo make install
|
||||
cd ../../
|
||||
|
||||
|
||||
# Build libFAudio
|
||||
git clone https://github.com/FNA-XNA/FAudio.git
|
||||
cd FAudio
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../ -DCMAKE_C_FLAGS=-m32 -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_SHARED_LIBS=off
|
||||
make -j4
|
||||
sudo make install
|
||||
cd ../../../
|
||||
|
||||
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
|
||||
PACKAGES=(
|
||||
"libdrm-amdgpu1:i386"
|
||||
"libdrm-intel1:i386"
|
||||
"libdrm-nouveau2:i386"
|
||||
"libdrm-radeon1:i386"
|
||||
"libgl1-mesa-dri:i386"
|
||||
"libglx-mesa0:i386"
|
||||
)
|
||||
|
||||
COMPRESSION_TARGET_PATH="ext/usr/lib/i386-linux-gnu"
|
||||
|
||||
cd libs
|
||||
|
||||
rm -rf deb_files
|
||||
mkdir -p deb_files
|
||||
cd deb_files
|
||||
|
||||
apt-get download "${PACKAGES[@]}"
|
||||
|
||||
mkdir -p ext
|
||||
for deb_file in *.deb; do
|
||||
dpkg-deb -x "$deb_file" ext
|
||||
done
|
||||
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/i915_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/i915_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/iris_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/iris_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/kms_swrast_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/kms_swrast_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/nouveau_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/nouveau_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/r300_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/r300_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/r600_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/r600_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/radeonsi_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/radeonsi_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/swrast_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/swrast_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/virtio_gpu_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/virtio_gpu_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/vmwgfx_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/vmwgfx_dri.so
|
||||
rm ext/usr/lib/i386-linux-gnu/dri/zink_dri.so
|
||||
ln -s -f crocus_dri.so ext/usr/lib/i386-linux-gnu/dri/zink_dri.so
|
||||
|
||||
|
||||
|
||||
|
||||
# cp -d /usr/lib/i386-linux-gnu/libedit.so* ext/usr/lib/i386-linux-gnu
|
||||
# cp -d /usr/lib/i386-linux-gnu/libglapi.so* ext/usr/lib/i386-linux-gnu
|
||||
# cp -d /usr/lib/i386-linux-gnu/libLLVM-15.so* ext/usr/lib/i386-linux-gnu
|
||||
# cp -d /usr/lib/i386-linux-gnu/libsensors.so* ext/usr/lib/i386-linux-gnu
|
||||
|
||||
tar -cf lib32ID.tar -C "$COMPRESSION_TARGET_PATH" .
|
||||
|
||||
mv lib32ID.tar ../
|
||||
|
||||
cd ..
|
||||
|
||||
rm -rf deb_files
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo snap refresh --hold=forever
|
||||
sudo apt-mark hold linux-image-$(uname -r)
|
||||
sudo apt-mark hold linux-headers-$(uname -r)
|
||||
sudo add-apt-repository ppa:kisak/turtle -y
|
||||
sudo apt update -y && sudo apt upgrade -y
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Install Packages Running..."
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update # && sudo apt upgrade -y
|
||||
sudo apt -y install --no-install-recommends \
|
||||
build-essential gcc-multilib g++-multilib cmake fuse freeglut3-dev:i386 libvdpau1:i386 libstdc++5:i386 libxmu6:i386 \
|
||||
libpcsclite1:i386 unzip libsndio-dev libsndio-dev:i386 pulseaudio-utils:i386 zlib1g:i386 libgpg-error0:i386 \
|
||||
libfreetype6-dev:i386 libdbus-1-dev libpulse-dev libdbus-1-dev:i386 \
|
||||
libudev-dev:i386 libxcursor-dev:i386 libxfixes-dev:i386 libxi-dev:i386 libxrandr-dev:i386 libxss-dev:i386 libxxf86vm-dev:i386 git libvulkan1:i386 \
|
||||
mesa-vulkan-drivers:i386 libxtst-dev:i386
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Install Packages Running..."
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update # && sudo apt upgrade -y
|
||||
sudo apt -y install --no-install-recommends \
|
||||
build-essential gcc-multilib g++-multilib cmake fuse freeglut3-dev:i386 libvdpau1:i386 libstdc++5:i386 libxmu6:i386 \
|
||||
libpcsclite1:i386 libncurses5:i386 unzip libsndio-dev libsndio-dev:i386 pulseaudio-utils:i386 zlib1g:i386 libgpg-error0:i386 \
|
||||
libasound2 libasound2-dev libasound2:i386 libasound2-dev:i386 libfreetype6-dev:i386 libdbus-1-dev libpulse-dev libdbus-1-dev:i386 \
|
||||
libudev-dev:i386 libxcursor-dev:i386 libxfixes-dev:i386 libxi-dev:i386 libxrandr-dev:i386 libxss-dev:i386 libxxf86vm-dev:i386 git libvulkan1:i386 \
|
||||
mesa-vulkan-drivers:i386 libxtst-dev:i386
|
||||
Executable
+146
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
APPIMAGENAME=lindbergh-loader
|
||||
APPIMAGEDIRNAME=lindbergh-loader.AppDir
|
||||
|
||||
declare -a ADDED_LIBS=(
|
||||
"libasound.so.2"
|
||||
"libfreetype.so.6"
|
||||
"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"
|
||||
"libXrandr.so.2"
|
||||
"libncurses.so.5"
|
||||
"libpcsclite.so.1"
|
||||
"libstdc++.so.5"
|
||||
"libdrm.so.2"
|
||||
"libexpat.so.1"
|
||||
"libm.so.6"
|
||||
"libz.so.1"
|
||||
"libgpg-error.so.0"
|
||||
"libstdc++.so.6"
|
||||
"libudev.so.1"
|
||||
"libsndio.so.7"
|
||||
"libvulkan.so.1"
|
||||
"libVkLayer_INTEL_nullhw.so"
|
||||
"libVkLayer_MESA_device_select.so"
|
||||
"libVkLayer_MESA_overlay.so"
|
||||
"libvulkan_intel_hasvk.so"
|
||||
"libvulkan_intel.so"
|
||||
"libvulkan_lvp.so"
|
||||
"libvulkan_radeon.so"
|
||||
"libvulkan_virtio.so"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
ADDED_LIBS_ARGS+=("--library=/usr/lib/i386-linux-gnu/pulseaudio/libpulsedsp.so")
|
||||
|
||||
echo "Copying files into AppDir..."
|
||||
mkdir -p "$OUTPUT_FOLDER/usr/bin"
|
||||
mkdir -p "$OUTPUT_FOLDER/usr/lib"
|
||||
mkdir -p "$OUTPUT_FOLDER/usr/lib32/dri"
|
||||
mkdir -p "$OUTPUT_FOLDER/usr/lib32ID"
|
||||
mkdir -p "$OUTPUT_FOLDER/usr/share/vulkan/icd.d"
|
||||
|
||||
|
||||
# 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
|
||||
VK="/$APP_IMG_ROOT/usr/share/vulkan/icd.d"
|
||||
export VK_ICD_FILENAMES="$VK/intel_hasvk_icd.i686.json:$VK/intel_icd.i686.json:$VK/lvp_icd.i686.json:$VK/radeon_icd.i686.json:$VK/virtio_icd.i686.json"
|
||||
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
|
||||
cp lindbergh $OUTPUT_FOLDER/usr/bin
|
||||
mkdir -p $OUTPUT_FOLDER/usr/lib/dri
|
||||
cp /usr/lib/i386-linux-gnu/dri/libdril_dri.so $OUTPUT_FOLDER/usr/lib/dri/libdril_dri.so
|
||||
cp /usr/share/vulkan/icd.d/* $OUTPUT_FOLDER/usr/share/vulkan/icd.d
|
||||
for file in $OUTPUT_FOLDER/usr/share/vulkan/icd.d/*; do
|
||||
sed -i 's|/usr/lib/i386-linux-gnu/||' "$file"
|
||||
done
|
||||
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/
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/crocus_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/i915_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/iris_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/kms_swrast_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/nouveau_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/r300_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/r600_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/radeonsi_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/swrast_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/virtio_gpu_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/vmwgfx_dri.so
|
||||
ln -s -f libdril_dri.so $OUTPUT_FOLDER/usr/lib32/dri/zink_dri.so
|
||||
|
||||
|
||||
unzip libs/Cg-3.1.zip -d $OUTPUT_FOLDER/usr/lib32
|
||||
cp libs/libCg.so $OUTPUT_FOLDER/usr/lib32/libCg2.so
|
||||
cp libs/libopenal.so.0 $OUTPUT_FOLDER/usr/lib32/libopenal.so.0
|
||||
cp libs/libcrypto.so.0.9.7 $OUTPUT_FOLDER/usr/lib32/libcrypto.so.0.9.7
|
||||
cp libs/libssl.so.0.9.7 $OUTPUT_FOLDER/usr/lib32/libssl.so.0.9.7
|
||||
tar -xf libs/lib32ID.tar -C $OUTPUT_FOLDER/usr/lib32ID
|
||||
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
|
||||
ln -s -f libGLX.so $OUTPUT_FOLDER/usr/lib32/libGLX.so.0
|
||||
rm -rf $OUTPUT_FOLDER/usr/share/doc
|
||||
|
||||
echo "Generating AppImage..."
|
||||
rm -f "$APPIMAGENAME.AppImage"
|
||||
./appimagetool-x86_64 -v "$OUTPUT_FOLDER" "$APPIMAGENAME.AppImage" --appimage-extract-and-run
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
cd libs
|
||||
find /usr/lib/i386-linux-gnu -name "libSDL3*" -exec sh -c 'tar -rf libs.tar -C /usr/lib/i386-linux-gnu $(basename {})' \;
|
||||
find /usr/lib/i386-linux-gnu -name "libFAudio*" -exec sh -c 'tar -rf libs.tar -C /usr/lib/i386-linux-gnu $(basename {})' \;
|
||||
tar -cf includes.tar -C /usr/include SDL3 SDL3_image SDL3_ttf FAudio.h FAudioFX.h
|
||||
|
||||
cd ..
|
||||
@@ -37,19 +37,18 @@ add-extensions:
|
||||
org.freedesktop.Platform.Compat.i386:
|
||||
directory: lib/i386-linux-gnu
|
||||
version: *runtime-version
|
||||
no-autodownload: true
|
||||
|
||||
org.freedesktop.Platform.Compat.i386.Debug:
|
||||
directory: lib/debug/lib/i386-linux-gnu
|
||||
version: *runtime-version
|
||||
no-autodownload: true
|
||||
# no-autodownload: true
|
||||
|
||||
org.freedesktop.Platform.GL32:
|
||||
directory: lib/i386-linux-gnu/GL
|
||||
version: *gl-version
|
||||
versions: *gl-versions
|
||||
subdirectories: true
|
||||
no-autodownload: true
|
||||
# no-autodownload: true
|
||||
autodelete: false
|
||||
add-ld-path: lib
|
||||
merge-dirs: *gl-merge-dirs
|
||||
@@ -61,8 +60,8 @@ add-extensions:
|
||||
directory: lib/debug/lib/i386-linux-gnu/GL
|
||||
version: *gl-version
|
||||
versions: *gl-versions
|
||||
subdirectories: true
|
||||
no-autodownload: true
|
||||
# subdirectories: true
|
||||
# no-autodownload: true
|
||||
merge-dirs: *gl-merge-dirs
|
||||
enable-if: active-gl-driver
|
||||
autoprune-unless: active-gl-driver
|
||||
@@ -77,6 +76,16 @@ modules:
|
||||
- type: file
|
||||
path: ./assets/libstdc++.so.5
|
||||
|
||||
- name: SDL3-FAudio-ID
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- tar -xf libs/libs.tar -C /app/lib32
|
||||
- mkdir -p /app/include
|
||||
- tar -xf libs/includes.tar -C /app/include
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../..
|
||||
|
||||
- name: libGLU
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
@@ -95,103 +104,15 @@ modules:
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/app
|
||||
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
|
||||
|
||||
- name: libFAudio-Dynamic
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/app
|
||||
- -DBUILD_SDL3=off
|
||||
- -DCMAKE_INSTALL_LIBDIR=/app/lib32
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
env:
|
||||
CFLAGS: "-m32 -mstackrealign"
|
||||
CXXFLAGS: "-m32 -mstackrealign"
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/FNA-XNA/FAudio.git
|
||||
tag: "24.12"
|
||||
|
||||
- name: libxmu
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --libdir=/app/lib32
|
||||
- --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
|
||||
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
|
||||
libdir: /app/lib32
|
||||
|
||||
- name: libmd
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --libdir=/app/lib32
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://archive.hadrons.org/software/libmd/libmd-1.1.0.tar.xz
|
||||
sha256: 1bd6aa42275313af3141c7cf2e5b964e8b1fd488025caf2f971f43b00776b332
|
||||
url: https://github.com/freeglut/freeglut.git
|
||||
branch: master
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
- name: libbsd
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --libdir=/app/lib32
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://libbsd.freedesktop.org/releases/libbsd-0.12.2.tar.xz
|
||||
sha256: b88cc9163d0c652aaf39a99991d974ddba1c3a9711db8f1b5838af2a14731014
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
- name: ncurses
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --libdir=/app/lib32
|
||||
- --with-shared
|
||||
- --without-debug
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz
|
||||
sha256: 9046298fb440324c9d4135ecea7879ffed8546dd1b58e59430ea07a4633f563b
|
||||
- type: patch
|
||||
path: ./assets/ncurses-5.9.patch
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
env:
|
||||
CPPFLAGS: "-P"
|
||||
CXXFLAGS: "-std=c++14"
|
||||
|
||||
- name: nvidia-cg-toolkit
|
||||
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
|
||||
url: http://developer.download.nvidia.com/cg/Cg_3.1/Cg-3.1_April2012_x86.tgz
|
||||
sha256: cef3591e436f528852db0e8c145d3842f920e0c89bcfb219c466797cb7b18879
|
||||
|
||||
- name: libsndio-dynamic
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
@@ -220,38 +141,52 @@ modules:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
- name: libopenal
|
||||
buildsystem: cmake
|
||||
- name: libxmu
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- -DCMAKE_INSTALL_LIBDIR=/app/lib32
|
||||
- -DALSOFT_BACKEND_PULSEAUDIO=ON
|
||||
- -DALSOFT_BACKEND_ALSA=ON
|
||||
- -DALSOFT_BACKEND_JACK=OFF
|
||||
- -DALSOFT_BACKEND_SNDIO=ON
|
||||
- -DSOUNDIO_INCLUDE_DIR=/app/include
|
||||
- -DSOUNDIO_LIBRARY=/app/lib32/libsndio.so
|
||||
- --libdir=/app/lib32
|
||||
- --with-shared
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/kcat/openal-soft/archive/refs/tags/openal-soft-1.17.2.tar.gz
|
||||
sha256: 11ea176f6cbf9763dbce0953dd77ab72d032b4ad95f07afffb91844abb174033
|
||||
url: https://xorg.freedesktop.org/releases/individual/lib/libXmu-1.2.1.tar.gz
|
||||
sha256: bf0902583dd1123856c11e0a5085bd3c6e9886fbbd44954464975fd7d52eb599
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
env:
|
||||
CFLAGS: "-m32 -mstackrealign"
|
||||
CXXFLAGS: "-m32 -std=c++14 -mstackrealign"
|
||||
|
||||
- name: openssl
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- ./Configure --prefix=/app --openssldir=/app/etc/ssl shared 386 no-asm no-krb5 linux-pentium
|
||||
- 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
|
||||
- mv libcrypto.* /app/lib32
|
||||
- mv libssl.* /app/lib32
|
||||
- name: libmd
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --libdir=/app/lib32
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/openssl/openssl/archive/refs/tags/OpenSSL_0_9_7.tar.gz
|
||||
sha256: 14e3a90f75994689e8c1bef098a97021f225a1a1623a7b8012079bd8b2eca1e8
|
||||
url: https://archive.hadrons.org/software/libmd/libmd-1.1.0.tar.xz
|
||||
sha256: 1bd6aa42275313af3141c7cf2e5b964e8b1fd488025caf2f971f43b00776b332
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
- name: libbsd
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --libdir=/app/lib32
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://libbsd.freedesktop.org/releases/libbsd-0.12.2.tar.xz
|
||||
sha256: b88cc9163d0c652aaf39a99991d974ddba1c3a9711db8f1b5838af2a14731014
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
- name: nvidia-cg-toolkit
|
||||
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
|
||||
url: http://developer.download.nvidia.com/cg/Cg_3.1/Cg-3.1_April2012_x86.tgz
|
||||
sha256: cef3591e436f528852db0e8c145d3842f920e0c89bcfb219c466797cb7b18879
|
||||
|
||||
- name: libpcsclite
|
||||
buildsystem: meson
|
||||
@@ -273,6 +208,24 @@ modules:
|
||||
url: https://pcsclite.apdu.fr/files/pcsc-lite-2.3.1.tar.xz
|
||||
sha256: a641d44d57affe1edd8365dd75307afc307e7eefb4e7ad839f6f146baa41ed56
|
||||
|
||||
# Build 32-bit libFAudio (static)
|
||||
- name: libFAudio-Static
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DBUILD_SHARED_LIBS=off
|
||||
- -DCMAKE_INSTALL_PREFIX=/app
|
||||
- -DCMAKE_INSTALL_LIBDIR=/app/lib32
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
env:
|
||||
CFLAGS: "-m32 -mstackrealign"
|
||||
CXXFLAGS: "-m32 -mstackrealign"
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/FNA-XNA/FAudio.git
|
||||
branch: master
|
||||
|
||||
- name: lindbergh-loader
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
@@ -287,15 +240,19 @@ modules:
|
||||
- install -Dm755 ./build/libkswapapi.so /app/lib32/
|
||||
- install -Dm755 ./build/libposixtime.so /app/lib32/
|
||||
- install -Dm755 ./libs/libCg.so /app/lib32/libCg2.so
|
||||
- install -Dm755 ./libs/libopenal.so.0 /app/lib32/libopenal.so.0
|
||||
- install -Dm755 ./libs/libcrypto.so.0.9.7 /app/lib32/libcrypto.so.0.9.7
|
||||
- install -Dm755 ./libs/libssl.so.0.9.7 /app/lib32/libssl.so.0.9.7
|
||||
- ln -s -f libposixtime.so /app/lib32/libposixtime.so.1
|
||||
- ln -s -f libposixtime.so /app/lib32/libposixtime.so.2.4
|
||||
- ln -s -f libopenal.so /app/lib32/libopenal.so.0
|
||||
- ln -s -f libkswapapi.so /app/lib32/libGLcore.so.1
|
||||
- ln -s -f libkswapapi.so /app/lib32/libnvidia-tls.so.1
|
||||
- mv /app/lib/libopenal* /app/lib32/
|
||||
- ln -s -f /usr/lib/i386-linux-gnu/libncurses.so /app/lib32/libncurses.so.5
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
env:
|
||||
CFLAGS: "-I/app/include"
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../..
|
||||
@@ -314,10 +271,6 @@ modules:
|
||||
- find /app/lib /app/lib32 -name '*.a' -type f -delete
|
||||
- find /app/lib /app/lib32 -name '*.la' -type f -delete
|
||||
- find /app/bin -mindepth 1 ! -name "lindbergh*" -exec rm -f {} \;
|
||||
- rm -d /app/lib/terminfo
|
||||
- rm -rf /app/lib/pkgconfig /app/lib32/pkgconfig /app/share/pkgconfig /app/lib32/cmake /app/lib/debug /app/include /app/man /app/share
|
||||
- rm /app/lib32/libmenu.*
|
||||
- rm /app/lib32/libform.*
|
||||
- rm /app/lib32/libpanel.*
|
||||
- rm /app/lib32/libXmuu.*
|
||||
- rm /app/lib32/libbsd.so
|
||||
|
||||
@@ -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 |
@@ -60,6 +60,7 @@ typedef struct Buffer
|
||||
SynthParams synthParams[MOD_ENV_TO_FILTER_CUTOFF + 1];
|
||||
|
||||
int lastStatus;
|
||||
int ownsData;
|
||||
|
||||
} Buffer;
|
||||
|
||||
@@ -940,17 +941,20 @@ int SEGAAPI_CreateBuffer(BufferConfig *pConfig, BufferCallback pCallback, unsign
|
||||
if (dwFlags & ALLOC_USER_MEM)
|
||||
{
|
||||
buffer->data = (uint8_t *)pConfig->mapData.hBufferHdr;
|
||||
buffer->ownsData = 0;
|
||||
}
|
||||
// Reuse buffer
|
||||
else if (dwFlags & USE_MAPPED_MEM)
|
||||
{
|
||||
buffer->data = (uint8_t *)pConfig->mapData.hBufferHdr;
|
||||
buffer->ownsData = 0;
|
||||
}
|
||||
// Allocate new buffer (caller will fill it later)
|
||||
else
|
||||
{
|
||||
buffer->data = (uint8_t *)malloc(buffer->size);
|
||||
memset(buffer->data, 0, buffer->size);
|
||||
buffer->ownsData = 1;
|
||||
}
|
||||
|
||||
pConfig->mapData.hBufferHdr = buffer->data;
|
||||
@@ -1014,6 +1018,14 @@ int SEGAAPI_DestroyBuffer(void *hHandle)
|
||||
|
||||
FAudioVoice_DestroyVoice(buffer->fAudioSourceVoice);
|
||||
|
||||
if (buffer->fAudioVoiceCallback)
|
||||
free(buffer->fAudioVoiceCallback);
|
||||
|
||||
if (buffer->ownsData && buffer->data)
|
||||
free(buffer->data);
|
||||
|
||||
free(buffer);
|
||||
|
||||
// TODO: hHandle = NULL?
|
||||
debug(1, "SEGAAPI_DestroyBuffer called\n");
|
||||
|
||||
@@ -1155,5 +1167,31 @@ int SEGAAPI_Exit(void)
|
||||
{
|
||||
debug(1, "SEGAAPI_Exit called\n");
|
||||
|
||||
pthread_mutex_lock(&fAudioMutex);
|
||||
|
||||
for (int i = 0; i < OUTPUT_CHANNELS; i++)
|
||||
{
|
||||
if (fAudioSubmixVoices[i])
|
||||
{
|
||||
FAudioVoice_DestroyVoice(fAudioSubmixVoices[i]);
|
||||
fAudioSubmixVoices[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fAudioMasteringVoice)
|
||||
{
|
||||
FAudioVoice_DestroyVoice(fAudioMasteringVoice);
|
||||
fAudioMasteringVoice = NULL;
|
||||
}
|
||||
|
||||
if (fAudio)
|
||||
{
|
||||
FAudio_Release(fAudio);
|
||||
fAudio = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&fAudioMutex);
|
||||
pthread_mutex_destroy(&fAudioMutex);
|
||||
|
||||
return SEGA_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "jvs.h"
|
||||
#include "passthrough.h"
|
||||
#include "log.h"
|
||||
|
||||
#define SERIAL_STRING "FE11-X018012022X"
|
||||
|
||||
#define BASEBOARD_INIT 0x300
|
||||
#define BASEBOARD_GET_VERSION 0x8004BC02
|
||||
#define BASEBOARD_SEEK_SHM 0x400
|
||||
#define BASEBOARD_READ_SRAM 0x601
|
||||
#define BASEBOARD_WRITE_SRAM 0x600
|
||||
#define BASEBOARD_REQUEST 0xC020BC06
|
||||
#define BASEBOARD_RECEIVE 0xC020BC07
|
||||
#define BASEBOARD_GET_SERIAL 0x120
|
||||
#define BASEBOARD_WRITE_FLASH 0x180
|
||||
#define BASEBOARD_GET_SENSE_LINE 0x210
|
||||
#define BASEBOARD_PROCESS_JVS 0x220
|
||||
#define BASEBOARD_READY 0x201
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t srcAddress;
|
||||
uint32_t srcSize;
|
||||
uint32_t destAddress;
|
||||
uint32_t destSize;
|
||||
} BaseboardCommand;
|
||||
|
||||
BaseboardCommand jvsCommand = {0};
|
||||
BaseboardCommand serialCommand = {0};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t *data;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} readData_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t *data;
|
||||
uint32_t size;
|
||||
} writeData_t;
|
||||
|
||||
FILE *sram = NULL;
|
||||
|
||||
unsigned int sharedMemoryIndex = 0;
|
||||
uint8_t sharedMemory[1024 * 32] = {0};
|
||||
|
||||
int selectReply = -1;
|
||||
int jvsFileDescriptor = -1;
|
||||
int jvsPacketSize = -1;
|
||||
|
||||
char serialString[1024] = {0};
|
||||
|
||||
int initBaseboard()
|
||||
{
|
||||
char *sramPath = getConfig()->sramPath;
|
||||
|
||||
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)
|
||||
{
|
||||
printf("Error: Failed to open %s for JVS\n", getConfig()->jvsPath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
initJVSSerial(jvsFileDescriptor);
|
||||
startJVSFrameThread(&jvsFileDescriptor);
|
||||
}
|
||||
|
||||
strcpy(serialString, SERIAL_STRING);
|
||||
|
||||
time_t t = time(NULL); // Get current time
|
||||
struct tm *tm_info = localtime(&t); // Convert to local time structure
|
||||
|
||||
int month = tm_info->tm_mon + 1; // tm_mon is 0-based (0 = Jan, 4 = May)
|
||||
int day = tm_info->tm_mday; // tm_mday is the day of the month
|
||||
|
||||
if (month == 1 && day == 18)
|
||||
{
|
||||
strcpy(serialString, "HAPPY BIRTHDAY!!");
|
||||
}
|
||||
|
||||
if (month == 5 && day == 20)
|
||||
{
|
||||
strcpy(serialString, "HAPPY BIRTHDAY!!");
|
||||
}
|
||||
|
||||
if (month == 5 && day == 21)
|
||||
{
|
||||
strcpy(serialString, "HAPPY BIRTHDAY!!");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t baseboardRead(int fd, void *buf, size_t 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;
|
||||
}
|
||||
|
||||
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
|
||||
struct timeval *restrict timeout)
|
||||
{
|
||||
return selectReply;
|
||||
}
|
||||
|
||||
int baseboardIoctl(int fd, unsigned long 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 = (size_t)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])
|
||||
{
|
||||
|
||||
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_RECEIVE:
|
||||
{
|
||||
uint32_t *_data = data;
|
||||
|
||||
switch (_data[0] & 0xFFF)
|
||||
{
|
||||
|
||||
case BASEBOARD_GET_SERIAL:
|
||||
{
|
||||
memcpy(&sharedMemory[serialCommand.destAddress + 96], serialString, strlen(serialString));
|
||||
_data[1] = 1; // Set the status to success
|
||||
}
|
||||
break;
|
||||
|
||||
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 ioctl %lX\n", request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
int initBaseboard();
|
||||
|
||||
ssize_t baseboardRead(int fd, void *buf, size_t count);
|
||||
ssize_t baseboardWrite(int fd, const void *buf, size_t count);
|
||||
|
||||
int baseboardIoctl(int fd, unsigned int request, void *data);
|
||||
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout);
|
||||
int baseboardIoctl(int fd, unsigned long request, void *data);
|
||||
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
|
||||
struct timeval *restrict timeout);
|
||||
@@ -1,287 +0,0 @@
|
||||
#include <stdio.h> /* Standard input/output definitions */
|
||||
#include <string.h> /* String function definitions */
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <errno.h> /* Error number definitions */
|
||||
#include <stdlib.h> /* Standard library functions like malloc, free, exit, and atoi */
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "baseboard.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "jvs.h"
|
||||
#include "passthrough.h"
|
||||
#include "log.h"
|
||||
|
||||
#define SERIAL_STRING "FE11-X018012022X"
|
||||
|
||||
#define BASEBOARD_INIT 0x300
|
||||
#define BASEBOARD_GET_VERSION 0x8004BC02
|
||||
#define BASEBOARD_SEEK_SHM 0x400
|
||||
#define BASEBOARD_READ_SRAM 0x601
|
||||
#define BASEBOARD_WRITE_SRAM 0x600
|
||||
#define BASEBOARD_REQUEST 0xC020BC06
|
||||
#define BASEBOARD_RECEIVE 0xC020BC07
|
||||
#define BASEBOARD_GET_SERIAL 0x120
|
||||
#define BASEBOARD_WRITE_FLASH 0x180
|
||||
#define BASEBOARD_GET_SENSE_LINE 0x210
|
||||
#define BASEBOARD_PROCESS_JVS 0x220
|
||||
#define BASEBOARD_READY 0x201
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t srcAddress;
|
||||
uint32_t srcSize;
|
||||
uint32_t destAddress;
|
||||
uint32_t destSize;
|
||||
} BaseboardCommand;
|
||||
|
||||
BaseboardCommand jvsCommand = {0};
|
||||
BaseboardCommand serialCommand = {0};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t *data;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} readData_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t *data;
|
||||
uint32_t size;
|
||||
} writeData_t;
|
||||
|
||||
FILE *sram = NULL;
|
||||
|
||||
unsigned int sharedMemoryIndex = 0;
|
||||
uint8_t sharedMemory[1024 * 32] = {0};
|
||||
|
||||
int selectReply = -1;
|
||||
int jvsFileDescriptor = -1;
|
||||
int jvsPacketSize = -1;
|
||||
|
||||
int initBaseboard()
|
||||
{
|
||||
char *sramPath = getConfig()->sramPath;
|
||||
|
||||
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)
|
||||
{
|
||||
printf("Error: Failed to open %s for JVS\n", getConfig()->jvsPath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
initJVSSerial(jvsFileDescriptor);
|
||||
startJVSFrameThread(&jvsFileDescriptor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t baseboardRead(int fd, void *buf, size_t 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;
|
||||
}
|
||||
|
||||
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout)
|
||||
{
|
||||
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])
|
||||
{
|
||||
|
||||
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_RECEIVE:
|
||||
{
|
||||
uint32_t *_data = data;
|
||||
|
||||
switch (_data[0] & 0xFFF)
|
||||
{
|
||||
|
||||
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_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 ioctl %X\n", request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#include <dlfcn.h>
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <GL/glx.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "blitStretching.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "resolution.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern int gWidth;
|
||||
extern int gHeight;
|
||||
extern SDL_Window *sdlWindow;
|
||||
|
||||
int blitWidth = 0;
|
||||
int blitHeight = 0;
|
||||
|
||||
int fboInitialized = false;
|
||||
GLuint fboId = 0;
|
||||
GLuint fboTextureId = 0;
|
||||
|
||||
int drawableW = 1;
|
||||
int drawableH = 1;
|
||||
|
||||
Dest dest;
|
||||
|
||||
void initBlitting()
|
||||
{
|
||||
blitSetWidthandHeightSize();
|
||||
blitInitializeFbo();
|
||||
dest.gameScale = 1.0f;
|
||||
}
|
||||
|
||||
void blitSetWidthandHeightSize()
|
||||
{
|
||||
if (gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP || gGrp == GROUP_ID5)
|
||||
{
|
||||
if (isTestMode())
|
||||
{
|
||||
blitWidth = 1360;
|
||||
blitHeight = 768;
|
||||
}
|
||||
else
|
||||
{
|
||||
blitWidth = gWidth;
|
||||
blitHeight = gHeight;
|
||||
}
|
||||
}
|
||||
else if (gId == GHOST_SQUAD_EVOLUTION || gGrp == GROUP_VT3_TEST)
|
||||
{
|
||||
blitWidth = 640;
|
||||
blitHeight = 480;
|
||||
}
|
||||
else if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE || gGrp == GROUP_MJ4)
|
||||
{
|
||||
blitWidth = 1024;
|
||||
blitHeight = 768;
|
||||
}
|
||||
else
|
||||
{
|
||||
blitWidth = gWidth;
|
||||
blitHeight = gHeight;
|
||||
}
|
||||
}
|
||||
|
||||
int blitInitializeFbo()
|
||||
{
|
||||
glGenFramebuffers(1, &fboId);
|
||||
if (fboId == 0)
|
||||
{
|
||||
log_error("Failed to generate FBO.\n");
|
||||
return 0;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
|
||||
|
||||
glGenTextures(1, &fboTextureId);
|
||||
if (fboTextureId == 0)
|
||||
{
|
||||
log_error("Failed to generate FBO texture.\n");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fboId);
|
||||
fboId = 0;
|
||||
return 0;
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, fboTextureId);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, blitWidth, blitHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTextureId, 0);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
log_error("FBO is not complete! Status: 0x%x\n", status);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteTextures(1, &fboTextureId);
|
||||
glDeleteFramebuffers(1, &fboId);
|
||||
fboTextureId = 0;
|
||||
fboId = 0;
|
||||
return 0;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
fboInitialized = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void blitStretch()
|
||||
{
|
||||
if (!fboInitialized)
|
||||
return;
|
||||
|
||||
if (fboInitialized && fboId > 0 && fboTextureId > 0 && sdlWindow)
|
||||
{
|
||||
SDL_GetWindowSizeInPixels(sdlWindow, &drawableW, &drawableH);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
|
||||
glBlitFramebuffer(0, 0, blitWidth, blitHeight, 0, 0, blitWidth, blitHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
float gameAspect = (float)blitWidth / (float)blitHeight;
|
||||
float windowAspect = (float)drawableW / (float)drawableH;
|
||||
|
||||
dest.W = drawableW;
|
||||
dest.H = drawableH;
|
||||
|
||||
if (windowAspect > gameAspect)
|
||||
{
|
||||
dest.W = (GLsizei)(drawableH * gameAspect);
|
||||
dest.X = (drawableW - dest.W) / 2;
|
||||
}
|
||||
else if (windowAspect < gameAspect)
|
||||
{
|
||||
dest.H = (GLsizei)(drawableW / gameAspect);
|
||||
dest.Y = (drawableH - dest.H) / 2;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
glBlitFramebuffer(0, 0, blitWidth, blitHeight, dest.X, dest.Y, dest.X + dest.W, dest.Y + dest.H, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int W;
|
||||
int H;
|
||||
int X;
|
||||
int Y;
|
||||
float gameScale;
|
||||
} Dest;
|
||||
|
||||
void initBlitting();
|
||||
void blitSetWidthandHeightSize();
|
||||
int blitInitializeFbo();
|
||||
void blitStretch();
|
||||
@@ -1,6 +1,3 @@
|
||||
#ifndef BORDER_H
|
||||
#define BORDER_H
|
||||
#pragma once
|
||||
|
||||
void drawGameBorder(int width, int height, float whiteBorderPercentage, float blackBorderPercentage);
|
||||
|
||||
#endif // BORDER_H
|
||||
@@ -1,16 +1,21 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cardReader.h"
|
||||
#include "config.h"
|
||||
#include "card_reader.h"
|
||||
#include "flowControl.h"
|
||||
#include "log.h"
|
||||
|
||||
#define BLOCKSIZE 8
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
bool cardReaderInitialized = false;
|
||||
|
||||
void (*idWriteFileHeader)(void *) = NULL;
|
||||
|
||||
uint8_t cardHeader[] = {0x08, 0x00, 0x00, 0x00, 0x81, 0x00, 0x59, 0xda, 0x00, 0x00, 0x54, 0x4d, 0x50, 0x06, 0x03, 0x23, 0x10, 0x41, 0x62,
|
||||
0xad, 0x00, 0x2b, 0x0b, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x71, 0x52, 0x70, 0x00, 0x00,
|
||||
@@ -41,29 +46,17 @@ int initCardReader()
|
||||
{
|
||||
EmulatorConfig *config = getConfig();
|
||||
|
||||
switch (gId)
|
||||
if (gGrp != GROUP_VT3 && gGrp != GROUP_VT3_TEST && gId != R_TUNED)
|
||||
{
|
||||
case R_TUNED:
|
||||
case VIRTUA_TENNIS_3:
|
||||
case VIRTUA_TENNIS_3_TEST:
|
||||
case VIRTUA_TENNIS_3_REVA:
|
||||
case VIRTUA_TENNIS_3_REVA_TEST:
|
||||
case VIRTUA_TENNIS_3_REVB:
|
||||
case VIRTUA_TENNIS_3_REVB_TEST:
|
||||
case VIRTUA_TENNIS_3_REVC:
|
||||
case VIRTUA_TENNIS_3_REVC_TEST:
|
||||
// Code for supported games
|
||||
break;
|
||||
default:
|
||||
printf("Warning: This Game does not support Card Reader, I will disable the card reader for you.\n");
|
||||
config->emulateCardreader = 0;
|
||||
log_warn("Warning: This Game does not support Card Reader, I will disable the card reader for you.\n");
|
||||
config->emulateHW210CardReader = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (getConfig()->emulateCardreader && getConfig()->emulateDriveboard && gId != R_TUNED)
|
||||
if (config->emulateHW210CardReader && config->emulateDriveboard && gId != R_TUNED)
|
||||
{
|
||||
printf("Warning: This Game does not support Card Reader and Driver Board emulation enabled at the same time.\n");
|
||||
printf("Please disable Drive Board emulations in the config file.\n");
|
||||
log_warn("Warning: This Game does not support Card Reader and Driver Board emulation enabled at the same time.\n");
|
||||
log_warn("Please disable Drive Board emulations in the config file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -71,15 +64,13 @@ int initCardReader()
|
||||
initReadCmd(1);
|
||||
initWriteCmd(0);
|
||||
initWriteCmd(1);
|
||||
cardReaderInitialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -177,7 +168,7 @@ void buildOutputBuffer(int fdIdx, uint8_t cmd)
|
||||
break;
|
||||
default:
|
||||
{
|
||||
printf("Command not supported.\n");
|
||||
log_error("Command not supported.\n");
|
||||
}
|
||||
}
|
||||
readCmd[fdIdx].cmdLen = packetLen + 5;
|
||||
@@ -186,10 +177,13 @@ void buildOutputBuffer(int fdIdx, uint8_t cmd)
|
||||
|
||||
size_t cardReaderWrite(int fd, const void *buf, size_t count)
|
||||
{
|
||||
if (!cardReaderInitialized)
|
||||
return -1;
|
||||
|
||||
int fdIdx = carReaderGetFdIdx(fd);
|
||||
if (writeCmd[fdIdx].cmdCompleted)
|
||||
{
|
||||
printf("Something went wrong, a write command was issued after the command was completed.\n");
|
||||
log_error("Something went wrong, a write command was issued after the command was completed.\n");
|
||||
}
|
||||
|
||||
if (writeCmd[fdIdx].recBytes == 4)
|
||||
@@ -211,6 +205,9 @@ size_t cardReaderWrite(int fd, const void *buf, size_t count)
|
||||
|
||||
size_t cardReaderRead(int fd, void *buf, size_t count)
|
||||
{
|
||||
if (!cardReaderInitialized)
|
||||
return -1;
|
||||
|
||||
int fdIdx = carReaderGetFdIdx(fd);
|
||||
if (!writeCmd[fdIdx].cmdCompleted || readCmd[fdIdx].cmdCompleted)
|
||||
return -1;
|
||||
@@ -245,6 +242,159 @@ void cardReaderSetFd(int serial, int fd, char *fName)
|
||||
fseek(crdFile, 0x7c7, SEEK_CUR);
|
||||
fwrite(&z, 1, 1, crdFile);
|
||||
fclose(crdFile);
|
||||
printf("Cardfile %s created.\n", fName);
|
||||
log_info("Cardfile %s created.\n", fName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void idPatchEject()
|
||||
{
|
||||
switch (gId)
|
||||
{
|
||||
case INITIALD_4_EXP_REVB:
|
||||
{
|
||||
patchMemoryFromString(0x082939f9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x08293a2b, "85");
|
||||
patchMemoryFromString(0x0819251e, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVC:
|
||||
{
|
||||
patchMemoryFromString(0x08293aa9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x08293adb, "85");
|
||||
patchMemoryFromString(0x081925ce, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVD:
|
||||
{
|
||||
patchMemoryFromString(0x08295479, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x082954ab, "85");
|
||||
patchMemoryFromString(0x0819349e, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVA:
|
||||
{
|
||||
patchMemoryFromString(0x0828149d, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x08281603, "85");
|
||||
patchMemoryFromString(0x0817943e, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVB:
|
||||
{
|
||||
patchMemoryFromString(0x082815c5, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x0828172b, "85");
|
||||
patchMemoryFromString(0x081793ce, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVC:
|
||||
{
|
||||
patchMemoryFromString(0x08281ae9, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x08281c51, "85");
|
||||
patchMemoryFromString(0x0817990c, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVD:
|
||||
{
|
||||
patchMemoryFromString(0x082833dd, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x08283543, "85");
|
||||
patchMemoryFromString(0x08179c1c, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVG:
|
||||
{
|
||||
patchMemoryFromString(0x082a4ae5, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x082a4b17, "85");
|
||||
patchMemoryFromString(0x08190cbe, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP:
|
||||
{
|
||||
patchMemoryFromString(0x083e1eb9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083e1eeb, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP_20:
|
||||
{
|
||||
patchMemoryFromString(0x083ebd1b, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083ebd51, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP_20A:
|
||||
{
|
||||
patchMemoryFromString(0x083ec07b, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083ec0b1, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_JAP_REVA:
|
||||
{
|
||||
patchMemoryFromString(0x083d92bd, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083d92f1, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_JAP_REVF:
|
||||
{
|
||||
patchMemoryFromString(0x083ec6b9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083ec6eb, "85");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idWriteFileHeaderTram(void *param1)
|
||||
{
|
||||
idPatchEject();
|
||||
idWriteFileHeader(param1);
|
||||
}
|
||||
|
||||
bool idCardFileExists(const char *folderPath, long expectedSize, bool twoDigits)
|
||||
{
|
||||
if (folderPath != NULL && strlen(folderPath) > 0)
|
||||
{
|
||||
struct stat buffer;
|
||||
stat(folderPath, &buffer);
|
||||
if (!S_ISDIR(buffer.st_mode))
|
||||
{
|
||||
log_error("ID Card Folder does not exist! Check the config file.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
char fileName[20];
|
||||
char currentFullPath[1024];
|
||||
char *fmt;
|
||||
int maxCards = 999;
|
||||
char *cardFilename = "InidCrd%03d.crd";
|
||||
|
||||
if (twoDigits)
|
||||
{
|
||||
maxCards = 99;
|
||||
cardFilename = "InidCard%02d.crd";
|
||||
}
|
||||
|
||||
for (int i = 0; i <= maxCards; ++i)
|
||||
{
|
||||
fmt = "%s%s";
|
||||
sprintf(fileName, cardFilename, i);
|
||||
if (folderPath != NULL && strlen(folderPath) > 0)
|
||||
{
|
||||
char lastChar = folderPath[strlen(folderPath) - 1];
|
||||
if (lastChar != '/')
|
||||
fmt = "%s/%s";
|
||||
}
|
||||
|
||||
if (snprintf(currentFullPath, sizeof(currentFullPath), fmt, folderPath, fileName) >= sizeof(currentFullPath))
|
||||
{
|
||||
fprintf(stderr, "Error: Constructed path for '%s' in folder '%s' is too long.\n", fileName, folderPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct stat fileStat;
|
||||
if (stat(currentFullPath, &fileStat) == 0)
|
||||
{
|
||||
if (fileStat.st_size == expectedSize)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef CARD_READER_H
|
||||
#define CARD_READER_H
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -32,5 +33,6 @@ int initCardReader();
|
||||
size_t cardReaderWrite(int fd, const void *buf, size_t count);
|
||||
size_t cardReaderRead(int fd, void *buf, size_t count);
|
||||
void cardReaderSetFd(int serial, int fd, char *fName);
|
||||
|
||||
#endif
|
||||
void idPatchEject();
|
||||
void idWriteFileHeaderTram(void *param1);
|
||||
bool idCardFileExists(const char *folderPath, long expectedSize, bool twoDigits);
|
||||
+407
-1492
File diff suppressed because it is too large
Load Diff
+70
-20
@@ -1,6 +1,6 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#define HUMMER 0x4e6d5c64 // DVP-0057B
|
||||
#define HUMMER_SDLX 0xf3778f44 // DVP-0057
|
||||
#define HUMMER_EXTREME 0x7129c32b // DVP-0079
|
||||
#define HUMMER_EXTREME_MDX 0xfeaf8484 // DVP-0084
|
||||
#define HUMMER_EXTREME_MDX 0xfeaf8484 // DVP-0083
|
||||
#define INITIALD_4_REVA 0x361d1cbe // DVP-0019A
|
||||
#define INITIALD_4_REVB 0x606914be // DVP-0019B
|
||||
#define INITIALD_4_REVC 0x50f1c269 // DVP-0019C
|
||||
@@ -29,6 +29,7 @@
|
||||
#define INITIALD_4_EXP_REVC 0x65ea22e3 // DVP-0030C
|
||||
#define INITIALD_4_EXP_REVD 0x62cc73a9 // DVP-0030D
|
||||
#define INITIALD_5_JAP_REVA 0xb3183112 // DVP-0070A
|
||||
#define INITIALD_5_JAP_REVC 0xda04e5e0 // DVP-0070C
|
||||
#define INITIALD_5_JAP_REVF 0xfc3dc85d // DVP-0070F
|
||||
#define INITIALD_5_EXP 0x701b88cf // DVP-0075
|
||||
#define INITIALD_5_EXP_20 0x77c6b58b // DVP-0084
|
||||
@@ -36,16 +37,21 @@
|
||||
#define LETS_GO_JUNGLE 0xb1c8c901 // DVP-0011
|
||||
#define LETS_GO_JUNGLE_REVA 0xc697c4fb // DVP-0011A
|
||||
#define LETS_GO_JUNGLE_SPECIAL 0x240beedc // DVP-0036A
|
||||
#define MJ4_REVA 0x6eb2a533 // DVP-0049A
|
||||
#define MJ4_REVF 0x220ac11 // DVP-0049F
|
||||
#define MJ4_REVG 0x57813a7 // DVP-0049G
|
||||
#define MJ4_EVO 0xb785a0e9 // DVP-0081
|
||||
#define MJ4_EVO_REVB 0x7f11d214 // DVP-0081B
|
||||
#define OUTRUN_2_SP_SDX 0x92c196d5 // DVP-0015
|
||||
#define OUTRUN_2_SP_SDX_REVA 0x4debd5f0 // DVP-0015A
|
||||
#define OUTRUN_2_SP_SDX_REVA_TEST 0x6b2d5c46 // DVP-0015A
|
||||
#define OUTRUN_2_SP_SDX_REVA_TEST2 0x4ee20716 // DVP-0015A
|
||||
#define OUTRUN_2_SP_SDX_TEST 0xf1c89eae // DVP-0015
|
||||
#define PRIMEVAL_HUNT 0x6868215c // DVP-0048A
|
||||
#define QUIZ_AXA 0x434dee4 // DVP-0025H
|
||||
#define QUIZ_AXA_LIVE 0x7103fafc // DVP-0087D
|
||||
#define RAMBO 0x48f49dd // DVP-0069
|
||||
#define RAMBO_CHINA 0x0025 // DVP-????
|
||||
#define RAMBO_CHINA 0xad864fec // DVP-0078
|
||||
#define R_TUNED 0xa68d053d // DVP-0060
|
||||
#define SEGABOOT 0x0027 // DVP-????
|
||||
#define SEGABOOT_2_4 0x38d56318 // DVP-????
|
||||
@@ -113,12 +119,32 @@ typedef enum
|
||||
{
|
||||
SHOOTING,
|
||||
DRIVING,
|
||||
HARLEY,
|
||||
FIGHTING,
|
||||
DIGITAL,
|
||||
ABC,
|
||||
MAHJONG
|
||||
} GameType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GROUP_ABC,
|
||||
GROUP_HOD4,
|
||||
GROUP_HOD4_TEST,
|
||||
GROUP_HOD4_SP,
|
||||
GROUP_HOD4_SP_TEST,
|
||||
GROUP_HUMMER,
|
||||
GROUP_ID4_EXP,
|
||||
GROUP_ID4_JAP,
|
||||
GROUP_ID5,
|
||||
GROUP_LGJ,
|
||||
GROUP_MJ4,
|
||||
GROUP_OUTRUN,
|
||||
GROUP_OUTRUN_TEST,
|
||||
GROUP_RAMBO,
|
||||
GROUP_VF5,
|
||||
GROUP_VT3,
|
||||
GROUP_VT3_TEST
|
||||
} GameGroup;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUTO_DETECT_GPU,
|
||||
@@ -225,14 +251,18 @@ typedef struct
|
||||
int emulateRideboard;
|
||||
int emulateDriveboard;
|
||||
int emulateMotionboard;
|
||||
int emulateCardreader;
|
||||
int emulateHW210CardReader;
|
||||
int emulateIDCardReader;
|
||||
int idCardFileAutoload;
|
||||
int emulateTouchscreen;
|
||||
char cardFile1[MAX_PATH_LENGTH];
|
||||
char cardFile2[MAX_PATH_LENGTH];
|
||||
char idCardFolder[MAX_PATH_LENGTH];
|
||||
int emulateJVS;
|
||||
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];
|
||||
@@ -242,6 +272,7 @@ typedef struct
|
||||
Colour lindberghColour;
|
||||
GameStatus gameStatus;
|
||||
GameType gameType;
|
||||
GameGroup gameGroup;
|
||||
KeyMapping keymap;
|
||||
uint32_t crc32;
|
||||
GameRegion region;
|
||||
@@ -249,6 +280,7 @@ typedef struct
|
||||
int showDebugMessages;
|
||||
char *gameID;
|
||||
char *gameTitle;
|
||||
char *gameShortTitle;
|
||||
char *gameDVP;
|
||||
Colour gameLindberghColour;
|
||||
char *gameReleaseYear;
|
||||
@@ -261,43 +293,61 @@ typedef struct
|
||||
int hummerFlickerFix;
|
||||
int keepAspectRatio;
|
||||
int fpsLimiter;
|
||||
int fpsTarget;
|
||||
float fpsTarget;
|
||||
int lgjRenderWithMesa;
|
||||
int noSDL;
|
||||
int phMode;
|
||||
int ramboGunsSwitch;
|
||||
int id5ChineseLanguage;
|
||||
float idSteeringPercentageReduction;
|
||||
int phScreenMode;
|
||||
int phTestScreenSingle;
|
||||
int disableBuiltinFont;
|
||||
int disableBuiltinLogos;
|
||||
int hideCursor;
|
||||
int customCursorEnabled;
|
||||
char customCursor[MAX_PATH_LENGTH];
|
||||
int customCursorWidth;
|
||||
int customCursorHeight;
|
||||
char phTouchCursor[MAX_PATH_LENGTH];
|
||||
int phTouchCursorWidth;
|
||||
int phTouchCursorHeight;
|
||||
char touchCursor[MAX_PATH_LENGTH];
|
||||
int touchCursorWidth;
|
||||
int touchCursorHeight;
|
||||
int mj4EnabledAtT;
|
||||
int enableNetworkPatches;
|
||||
char idIpSeat1[16];
|
||||
char idIpSeat2[16];
|
||||
char nicName[20];
|
||||
char or2IP[16];
|
||||
char or2Netmask[16];
|
||||
char IpCab1[16];
|
||||
char IpCab2[16];
|
||||
char IpCab3[16];
|
||||
char IpCab4[16];
|
||||
char tooSpicyIpCab1[16];
|
||||
char tooSpicyIpCab2[16];
|
||||
char srtvIP[16];
|
||||
float cpuFreqGhz;
|
||||
ArcadeInputs arcadeInputs;
|
||||
int inputMode; // 0 = both, 1 = SDL/X11 only, 2 = EVDEV only
|
||||
int inputMode; // 0 = both, 1 = SDL, 2 = EVDEV only
|
||||
int skipOutrunCabinetCheck;
|
||||
float whiteBorderPercentage;
|
||||
float blackBorderPercentage;
|
||||
int borderEnabled;
|
||||
int enableCrosshairs;
|
||||
char p1CrossHairPath[MAX_PATH_LENGTH];
|
||||
char p2CrossHairPath[MAX_PATH_LENGTH];
|
||||
int customCrossHairWidth;
|
||||
int customCrossHairHeight;
|
||||
} EmulatorConfig;
|
||||
|
||||
uint32_t get_crc32(const char *s, ssize_t n);
|
||||
KeyMapping getDefaultKeymap();
|
||||
int initConfig(const char* configFilePath);
|
||||
void setDefaultValues(EmulatorConfig *cfg);
|
||||
EmulatorConfig *getConfig();
|
||||
char *getGameName();
|
||||
char *getDVPName();
|
||||
char *getGameID();
|
||||
char *getDvpName();
|
||||
char *getGameId();
|
||||
int getGameLindberghColour();
|
||||
char *getGameReleaseYear();
|
||||
char *getGameNativeResolutions();
|
||||
const char *getLindberghColourString(Colour lindberghColour);
|
||||
const char *getGameRegionString(GameRegion region);
|
||||
const char *getGpuTypeString(GpuType gpuType);
|
||||
|
||||
#endif
|
||||
extern SDL_Keycode getSDLKeycode(const char *input);
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "configIni.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void createDefaultIni(const char *filePath)
|
||||
{
|
||||
FILE *file = fopen(filePath, "w");
|
||||
if (!file)
|
||||
{
|
||||
log_error("Could not create default INI file at %s", filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
EmulatorConfig defaults = {0};
|
||||
setDefaultValues(&defaults);
|
||||
|
||||
fprintf(file, "# SEGA Lindbergh Emulator Configuration File\n");
|
||||
fprintf(file, "# By the Lindbergh Development Team 2024-2025\n\n");
|
||||
|
||||
// [Display]
|
||||
fprintf(file, "[Display]\n");
|
||||
fprintf(file, "# Set the width resolution here\nWIDTH = AUTO\n\n");
|
||||
fprintf(file, "# Set the height resolution here\nHEIGHT = AUTO\n\n");
|
||||
fprintf(file, "# Boost render resolution in HOD4/2Spicy/Harley/Rambo/HOD-EX/ID4/ID5 and LGJ\n");
|
||||
fprintf(file, "BOOST_RENDER_RES = %s\n\n", defaults.boostRenderRes ? "true" : "false");
|
||||
fprintf(file, "# Set to true for full screen\nFULLSCREEN = %s\n\n", defaults.fullscreen ? "true" : "false");
|
||||
fprintf(file, "# Set to true if you\'d like to add a border for optical light gun tracking\n");
|
||||
fprintf(file, "BORDER_ENABLED = %s\n\n", defaults.borderEnabled ? "true" : "false");
|
||||
fprintf(file, "# Set the thickness of the white border as a percentage of the width of the screen\n");
|
||||
fprintf(file, "WHITE_BORDER_PERCENTAGE = %.0f\n\n", defaults.whiteBorderPercentage * 100);
|
||||
fprintf(file, "# Set the thickness of the black border which sits around the\n");
|
||||
fprintf(file, "# white border as a percentage of the width of the screen\n");
|
||||
fprintf(file, "BLACK_BORDER_PERCENTAGE = %.0f\n\n", defaults.blackBorderPercentage * 100);
|
||||
fprintf(file, "# Set to keep the aspect ratio in games like Sega Race TV Primeval Hunt and LGJ-SP\n");
|
||||
fprintf(file, "KEEP_ASPECT_RATIO = %s\n\n", defaults.keepAspectRatio ? "true" : "false");
|
||||
fprintf(file, "# Set to true to enable the mouse pointer/Cursor\nHIDE_CURSOR = %s\n\n", defaults.hideCursor ? "true" : "false");
|
||||
|
||||
// [Input]
|
||||
fprintf(file, "[Input]\n");
|
||||
fprintf(file, "# Sets the Input Mode (1: SDL, 2: EVDEV\n");
|
||||
fprintf(file, "INPUT_MODE = %d\n\n", defaults.inputMode);
|
||||
|
||||
// [Emulation]
|
||||
fprintf(file, "[Emulation]\n");
|
||||
fprintf(file, "# Set the Region (JP/US/EX)\nREGION = EX\n\n");
|
||||
fprintf(file, "# Set to true for Free Play, none to leave as default\nFREEPLAY = none\n\n");
|
||||
fprintf(file, "# Set to true to emulate JVS and use the keyboard/mouse for controls.\n");
|
||||
fprintf(file, "# If this is set to false, the loader will route the traffic to the serial device\n");
|
||||
fprintf(file, "# defined in JVS_PATH if it has been defined.\nEMULATE_JVS = %s\n\n", defaults.emulateJVS ? "true" : "false");
|
||||
fprintf(file, "# Set to true to emulate the rideboard used in the SPECIAL games\n");
|
||||
fprintf(file, "# If this is set to false, then the emulator will route the traffic to one of the serial ports\n");
|
||||
fprintf(file, "EMULATE_RIDEBOARD = AUTO\n\n");
|
||||
fprintf(file, "# Set to true to emulate the driveboard used in driving games\n");
|
||||
fprintf(file, "# If this is set to false, then the emulator will route the traffic to one of the serial ports\n");
|
||||
fprintf(file, "EMULATE_DRIVEBOARD = AUTO\n\n");
|
||||
fprintf(file, "# Set to true to emulate the motion board from Outrun 2 SP SDX\n");
|
||||
fprintf(file, "# If this is set to false, then the emulator will route the traffic to one of the serial ports\n");
|
||||
fprintf(file, "EMULATE_MOTIONBOARD = AUTO\n\n");
|
||||
fprintf(file, "# Set to true to enable card reader emulation in Virtua Tennis 3 or R-Tuned\nEMULATE_HW210_CARDREADER = AUTO\n\n");
|
||||
fprintf(file, "# Set to true to enable card reader emulation in ID4 and ID5 file \nEMULATE_ID_CARD READER = AUTO\n\n");
|
||||
fprintf(file, "# Set to true to enable touchscreen emulation with the mouse\nEMULATE_TOUCHSCREEN = AUTO\n\n");
|
||||
|
||||
// [Cards]
|
||||
fprintf(file, "[Cards]\n");
|
||||
fprintf(file, "# Set to false to use a button to insert a card manually in ID4 or ID5.\n");
|
||||
fprintf(file, "# Or true to make the loader auto load\nID_CARDFILE_AUTOLOAD = %s\n\n", defaults.idCardFileAutoload ? "true" : "false");
|
||||
fprintf(file, "# Card File for reader 1 in VT3 or R-Tuned\nCARDFILE_01 = \"%s\"\n\n", defaults.cardFile1);
|
||||
fprintf(file, "# Card File for reader 2 in VT3 or R-Tuned\nCARDFILE_02 = \"%s\"\n\n", defaults.cardFile2);
|
||||
fprintf(file, "# Folder for ID Card files\nID_CARDFOLDER = \"%s\"\n\n", defaults.idCardFolder);
|
||||
|
||||
// [Paths]
|
||||
fprintf(file, "[Paths]\n");
|
||||
fprintf(file, "# Define the path to pass the JVS packets to if JVS is not being emulated\nJVS_PATH = \"%s\"\n\n", defaults.jvsPath);
|
||||
fprintf(file, "# Define the path to pass the first serial port to\nSERIAL_1_PATH = \"%s\"\n\n", defaults.serial1Path);
|
||||
fprintf(file, "# Define the path to pass the second serial port to\nSERIAL_2_PATH = \"%s\"\n\n", defaults.serial2Path);
|
||||
fprintf(file, "# Define the path to the sram.bin file\nSRAM_PATH = \"%s\"\n\n", defaults.sramPath);
|
||||
fprintf(file, "# Define the path to the eeprom.bin file\nEEPROM_PATH = \"%s\"\n\n", defaults.eepromPath);
|
||||
fprintf(file, "# If set, libCG.so needed for 2Spicy, Harley, Rambo and HOD-Ex shader recompilation\n");
|
||||
fprintf(file, "# will be loaded from the specified location. (include the name of file in the location ");
|
||||
fprintf(file, "For Example: /my/file/location/myLibCg.so)\nLIBCG_PATH = \"%s\"\n\n", defaults.libCgPath);
|
||||
|
||||
// [Graphics]
|
||||
fprintf(file, "[Graphics]\n");
|
||||
fprintf(file, "# Set the GPU vendor (0 = Autodetect / 1 = NVidia / 2 = AMD / 3 = ATI / 4 = Intel)\n");
|
||||
fprintf(file, "GPU_VENDOR = %d\n\n", defaults.GPUVendor);
|
||||
fprintf(file, "# Set to true if you experience flickering in hummer\n");
|
||||
fprintf(file, "HUMMER_FLICKER_FIX = %s\n\n", defaults.hummerFlickerFix ? "true" : "false");
|
||||
fprintf(file, "# Set to false if you don't want to limit the FPS\n");
|
||||
fprintf(file, "FPS_LIMITER_ENABLED = %s\n\n", defaults.fpsLimiter ? "true" : "false");
|
||||
fprintf(file, "# Set the target FPS (will only work if FPS_LIMITER_ENABLED = 1)\nFPS_TARGET = %.1f\n\n", defaults.fpsTarget);
|
||||
fprintf(file, "# Set to true if you want to render LGJ using the mesa patches\n");
|
||||
fprintf(file, "LGJ_RENDER_WITH_MESA = %s\n\n", defaults.lgjRenderWithMesa ? "true" : "false");
|
||||
fprintf(file, "# Disable to use the original fonts instead of the built in font\n");
|
||||
fprintf(file, "DISABLE_BUILTIN_FONT = %s\n\n", defaults.disableBuiltinFont ? "true" : "false");
|
||||
fprintf(file, "# Disable to use the original logos instead of the built in logos\n");
|
||||
fprintf(file, "DISABLE_BUILTIN_LOGOS = %s\n\n", defaults.disableBuiltinLogos ? "true" : "false");
|
||||
|
||||
// [Cursor]
|
||||
fprintf(file, "[Cursor]\n");
|
||||
fprintf(file, "# If true, a custom mouse cursor will be used loaded from a png file set in CUSTOM_CURSOR\n");
|
||||
fprintf(file, "# Ovewrites HIDE_CURSOR\n");
|
||||
fprintf(file, "CUSTOM_CURSOR_ENABLED = %s\n\n", defaults.customCursorEnabled ? "true" : "false");
|
||||
fprintf(file, "# Set the custom mouse pointer from a PNG file (Usefull for shooting games)\n");
|
||||
fprintf(file, "CUSTOM_CURSOR = \"%s\"\n\n", defaults.customCursor);
|
||||
fprintf(file, "# Set the width of the custom cursor\nCUSTOM_CURSOR_WIDTH = %d\n\n", defaults.customCursorWidth);
|
||||
fprintf(file, "# Set the height of the custom cursor\nCUSTOM_CURSOR_HEIGHT = %d\n\n", defaults.customCursorHeight);
|
||||
fprintf(file, "# Set a custom cursor for the touch screen in Primeval Hunt, MJ4 Games and AxA Games\n");
|
||||
fprintf(file, "TOUCH_CURSOR = \"%s\"\n\n", defaults.touchCursor);
|
||||
fprintf(file, "# Set the width of the custom cursor\nTOUCH_CURSOR_WIDTH = %d\n\n", defaults.touchCursorWidth);
|
||||
fprintf(file, "# Set the height of the custom cursor\nTOUCH_CURSOR_HEIGHT = %d\n\n", defaults.touchCursorHeight);
|
||||
|
||||
// [GameSpecific]
|
||||
fprintf(file, "[GameSpecific]\n");
|
||||
fprintf(file, "# Set Primeval Hunt\n# Mode 0: Default (side by side)\n# Mode 1: No Touch screen\n# Mode 2: Side By Side\n");
|
||||
fprintf(file, "# Mode 3: 3ds mode 1 (Touch screen to the right)\n# Mode 4: 3ds mode 2 (Touch screen to the bottom)\n");
|
||||
fprintf(file, "PRIMEVAL_HUNT_SCREEN_MODE = %d\n\n", defaults.phScreenMode);
|
||||
fprintf(file, "# Set Primeval Hunt Test mode screen to single screen\n");
|
||||
fprintf(file, "PRIMEVAL_HUNT_TEST_SCREEN_SINGLE = %s\n\n", defaults.phTestScreenSingle ? "true" : "false");
|
||||
fprintf(file, "# Set to true to bypass cabinet checks including drive board and tower in Outrun 2 SP SDX\n");
|
||||
fprintf(file, "SKIP_OUTRUN_CABINET_CHECK = %s\n\n", defaults.skipOutrunCabinetCheck ? "true" : "false");
|
||||
fprintf(file, "# Set to false if you want to disable the Glare effect in OutRun\n");
|
||||
fprintf(file, "OUTRUN_LENS_GLARE_ENABLED = %s\n\n", defaults.outrunLensGlareEnabled ? "true" : "false");
|
||||
fprintf(file, "# Hacky way to make MJ4 and AxA work at prohibited times\nMJ4_ENABLED_ALL_THE_TIME = %s\n\n",
|
||||
defaults.mj4EnabledAtT ? "true" : "false");
|
||||
fprintf(file, "# House of the dead 4 speed fix, set the frequency of your CPU in Ghz\nCPU_FREQ_GHZ = %.1f\n\n", defaults.cpuFreqGhz);
|
||||
fprintf(file, "# Set to true if you want to change the way the guns are show in Rambo\n");
|
||||
fprintf(file, "RAMBO_GUNS_SWITCH = false\n\n");
|
||||
fprintf(file, "# Set to true to set the language in Chinese for ID5 DVP-0084 and DVP-0084A\n");
|
||||
fprintf(file, "ID5_CHINESE_LANGUAGE = %s\n\n", defaults.id5ChineseLanguage ? "true" : "false");
|
||||
fprintf(file, "# Set the percentage of the steering wheel travel reduction\n");
|
||||
fprintf(file, "ID_STEERING_REDUCTION_PERCENTAGE = %.1f\n\n", defaults.idSteeringPercentageReduction);
|
||||
|
||||
fprintf(file, "[CrossHairs]\n");
|
||||
fprintf(file, "# Set to true to enable Crosshairs even when using GunLights\n");
|
||||
fprintf(file, "ENABLE_CROSSHAIRS = %s\n\n", defaults.enableCrosshairs ? "true" : "false");
|
||||
fprintf(file, "# Set the Crosshair image from a PNG file for Player 1\n");
|
||||
fprintf(file, "P1_CROSSHAIR_PATH = \"%s\"\n\n", defaults.p1CrossHairPath);
|
||||
fprintf(file, "# Set the Crosshair image from a PNG file for Player 2\n");
|
||||
fprintf(file, "P2_CROSSHAIR_PATH = \"%s\"\n\n", defaults.p2CrossHairPath);
|
||||
fprintf(file, "# Set the width of the Crosshair image\n");
|
||||
fprintf(file, "CUSTOM_CROSSHAIRS_WIDTH = %d\n\n", defaults.customCrossHairWidth);
|
||||
fprintf(file, "# Set the height of the Crosshair image\n");
|
||||
fprintf(file, "CUSTOM_CROSSHAIRS_HEIGHT = %d\n\n", defaults.customCrossHairHeight);
|
||||
|
||||
// [System]
|
||||
fprintf(file, "[System]\n");
|
||||
fprintf(file, "# Set to true to see debug messages in the console\n");
|
||||
fprintf(file, "DEBUG_MSGS = %s\n\n", defaults.showDebugMessages ? "true" : "false");
|
||||
fprintf(file, "# Set the colour of the lindbergh (YELLOW, RED, BLUE, SILVER, REDEX)\nLINDBERGH_COLOUR = YELLOW\n\n");
|
||||
|
||||
// [Network]
|
||||
fprintf(file, "[Network]\n");
|
||||
fprintf(file, "# If true, the loader will apply the following network patches depending on the game\n");
|
||||
fprintf(file, "ENABLE_NETWORK_PATCHES = %s\n\n", defaults.enableNetworkPatches ? "true" : "false");
|
||||
fprintf(file, "# Sets the name of the Network Interface Card\nNIC_NAME = \"enp0s1\"\n\n");
|
||||
fprintf(file, "# ID4 and ID5 network configuration per seat\nID_IP_SEAT_1 = \"192.168.1.2\"\nID_IP_SEAT_2 = \"192.168.1.3\"\n\n");
|
||||
fprintf(file, "# Sets the IP address and Netmask for Outrun link (you have to put your NIC ip)\n");
|
||||
fprintf(file, "OR2_IPADDRESS = \"192.168.1.2\"\nOR2_NETMASK = \"255.255.255.0\"\n\n");
|
||||
fprintf(file, "# Harley / Hummer and R-Tuned IP address for each Cabinet\n");
|
||||
fprintf(file, "IP_CAB1 = \"192.168.1.2\"\nIP_CAB2 = \"192.168.1.3\"\nIP_CAB3 = \"192.168.1.4\"\nIP_CAB4 = \"192.168.1.5\"\n\n");
|
||||
fprintf(file, "# Sets the IP address of each cabinet for network play in 2Spicy\n");
|
||||
fprintf(file, "2SPICY_IP_CAB1 = \"192.168.1.2\"\n2SPICY_IP_CAB2 = \"192.168.1.3\"\n\n");
|
||||
fprintf(file, "# Sets the IP address for SRTV\nSRTV_IPADDRESS = \"192.168.1.2\"\n\n");
|
||||
|
||||
// [EVDEV]
|
||||
fprintf(file, "[EVDEV]\n");
|
||||
fprintf(file, "# EVDEV MODE (Input Mode 2)\n# To find the value pairs for these run ./lindbergh --list-controllers\n\n");
|
||||
fprintf(file, "#TEST_BUTTON = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_T\"\n\n");
|
||||
fprintf(file, "#PLAYER_1_COIN = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_5\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_START = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_1\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_SERVICE = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_S\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_UP = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_UP\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_DOWN = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_DOWN\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_LEFT = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_LEFT\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_RIGHT = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_RIGHT\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_1 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_Q\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_2 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_W\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_3 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_E\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_4 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_R\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_5 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_Y\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_6 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_U\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_7 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_I\"\n");
|
||||
fprintf(file, "#PLAYER_1_BUTTON_8 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_O\"\n\n");
|
||||
fprintf(file, "#PLAYER_1_COIN = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_6\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_START = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_1\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_SERVICE = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_S\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_UP = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_UP\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_DOWN = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_DOWN\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_LEFT = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_LEFT\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_RIGHT = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_RIGHT\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_1 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_Q\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_2 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_W\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_3 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_E\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_4 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_R\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_5 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_Y\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_6 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_U\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_7 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_I\"\n");
|
||||
fprintf(file, "#PLAYER_2_BUTTON_8 = \"AT_TRANSLATED_SET_2_KEYBOARD_KEY_O\"\n\n");
|
||||
fprintf(file, "#ANALOGUE_1 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_X\"\n");
|
||||
fprintf(file, "#ANALOGUE_2 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_Y\"\n");
|
||||
fprintf(file, "#ANALOGUE_3 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_Z\"\n");
|
||||
fprintf(file, "#ANALOGUE_4 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_RZ\"\n");
|
||||
fprintf(file, "#ANALOGUE_5 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_X2\"\n");
|
||||
fprintf(file, "#ANALOGUE_6 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_Y2\"\n");
|
||||
fprintf(file, "#ANALOGUE_7 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_Z2\"\n");
|
||||
fprintf(file, "#ANALOGUE_8 = \"SYNPS_2_SYNAPTICS_TOUCHPAD_ABS_RZ2\"\n\n");
|
||||
|
||||
for (int x = 1; x < 9; x++)
|
||||
fprintf(file, "#ANALOGUE_DEADZONE_%d = 0 0 0\n", x);
|
||||
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef CONFIG_INI_H
|
||||
#define CONFIG_INI_H
|
||||
|
||||
void createDefaultIni(const char *filePath);
|
||||
|
||||
#endif // CONFIG_INI_H
|
||||
@@ -1,205 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CONFIG_TEMP_FILE_NAME ".lindbergh.conf.tmp"
|
||||
|
||||
#include "config_file.h"
|
||||
|
||||
char *getConfigErrorString(ConfigStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case CONFIG_STATUS_COMMAND_NOT_FOUND:
|
||||
return "The key you looked for could not be found";
|
||||
case CONFIG_STATUS_FAILURE:
|
||||
return "There was a generic failure";
|
||||
case CONFIG_STATUS_SUCCESS:
|
||||
return "The operation was successful";
|
||||
case CONFIG_STATUS_NOT_ENOUGH_ARGUMENTS:
|
||||
return "The config file did not have enough arguments to complete your request";
|
||||
case CONFIG_STATUS_CANNOT_OPEN_FILE:
|
||||
return "The config file could not be opened";
|
||||
default:
|
||||
return "This config status enum doesn't have an error string associated.";
|
||||
}
|
||||
}
|
||||
|
||||
static char *getNextToken(char *buffer, char *seperator, char **saveptr)
|
||||
{
|
||||
char *token = strtok_r(buffer, seperator, saveptr);
|
||||
if (token == NULL)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < (int)strlen(token); i++)
|
||||
{
|
||||
if ((token[i] == '\n') || (token[i] == '\r'))
|
||||
{
|
||||
token[i] = 0;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
ConfigStatus processConfig(Config *config, char *path)
|
||||
{
|
||||
config->path = path;
|
||||
config->file = fopen(config->path, "r");
|
||||
config->configPairIndex = 0;
|
||||
|
||||
if (config->file == NULL)
|
||||
{
|
||||
return CONFIG_STATUS_CANNOT_OPEN_FILE;
|
||||
}
|
||||
|
||||
char buffer[CONFIG_MAX_LINE_LENGTH];
|
||||
char *saveptr = NULL;
|
||||
|
||||
while (fgets(buffer, CONFIG_MAX_LINE_LENGTH, config->file))
|
||||
{
|
||||
// Ignore any lines that start with a comment
|
||||
if (buffer[0] == '#' || buffer[0] == 0 || buffer[0] == ' ' || buffer[0] == '\r' || buffer[0] == '\n')
|
||||
continue;
|
||||
|
||||
ConfigPair *configPair = &config->configPairs[config->configPairIndex];
|
||||
|
||||
strcpy(configPair->command, getNextToken(buffer, " ", &saveptr));
|
||||
|
||||
configPair->argumentIndex = 0;
|
||||
char *argument = getNextToken(NULL, " ", &saveptr);
|
||||
while (argument != NULL && argument[0] != 0)
|
||||
{
|
||||
strcpy(configPair->argument[configPair->argumentIndex++], argument);
|
||||
argument = getNextToken(NULL, " ", &saveptr);
|
||||
}
|
||||
|
||||
config->configPairIndex++;
|
||||
}
|
||||
|
||||
fclose(config->file);
|
||||
|
||||
return CONFIG_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ConfigStatus getConfigArgumentString(Config *config, char *key, int n, char *value)
|
||||
{
|
||||
for (int i = 0; i < config->configPairIndex; i++)
|
||||
{
|
||||
if (strcmp(config->configPairs[i].command, key) == 0)
|
||||
{
|
||||
if (n >= config->configPairs[i].argumentIndex)
|
||||
return CONFIG_STATUS_NOT_ENOUGH_ARGUMENTS;
|
||||
|
||||
strcpy(value, config->configPairs[i].argument[n]);
|
||||
|
||||
return CONFIG_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return CONFIG_STATUS_COMMAND_NOT_FOUND;
|
||||
}
|
||||
|
||||
ConfigStatus getConfigArgumentInt(Config *config, char *key, int n, int *value)
|
||||
{
|
||||
for (int i = 0; i < config->configPairIndex; i++)
|
||||
{
|
||||
if (strcmp(config->configPairs[i].command, key) == 0)
|
||||
{
|
||||
if (n >= config->configPairs[i].argumentIndex)
|
||||
return CONFIG_STATUS_NOT_ENOUGH_ARGUMENTS;
|
||||
|
||||
*value = atoi(config->configPairs[i].argument[i]);
|
||||
|
||||
return CONFIG_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return CONFIG_STATUS_COMMAND_NOT_FOUND;
|
||||
}
|
||||
|
||||
ConfigStatus getConfigString(Config *config, char *key, char *value)
|
||||
{
|
||||
return getConfigArgumentString(config, key, 0, value);
|
||||
}
|
||||
|
||||
ConfigStatus getConfigInt(Config *config, char *key, int *value)
|
||||
{
|
||||
return getConfigArgumentInt(config, key, 0, value);
|
||||
}
|
||||
|
||||
ConfigStatus writeConfigString(Config *config, char *key, char *value)
|
||||
{
|
||||
config->file = fopen(config->path, "r");
|
||||
|
||||
if (config->file == NULL)
|
||||
{
|
||||
return CONFIG_STATUS_FAILURE;
|
||||
}
|
||||
|
||||
FILE *tempFile = fopen(CONFIG_TEMP_FILE_NAME, "w");
|
||||
if (tempFile == NULL)
|
||||
{
|
||||
return CONFIG_STATUS_FAILURE;
|
||||
}
|
||||
|
||||
char buffer[CONFIG_MAX_LINE_LENGTH];
|
||||
char *saveptr = NULL;
|
||||
char inspectionBuffer[CONFIG_MAX_LINE_LENGTH];
|
||||
|
||||
char writeBuffer[CONFIG_MAX_LINE_LENGTH];
|
||||
strcpy(writeBuffer, key);
|
||||
strcat(writeBuffer, " ");
|
||||
strcat(writeBuffer, value);
|
||||
strcat(writeBuffer, "\n");
|
||||
|
||||
int lineWasReplaced = 0;
|
||||
|
||||
while (fgets(buffer, CONFIG_MAX_LINE_LENGTH, config->file))
|
||||
{
|
||||
strcpy(inspectionBuffer, buffer);
|
||||
char *token = getNextToken(inspectionBuffer, " ", &saveptr);
|
||||
|
||||
if (strcmp(token, key) == 0)
|
||||
{
|
||||
lineWasReplaced = 1;
|
||||
|
||||
// If there is no value just delete the line
|
||||
if (strcmp(value, "") == 0)
|
||||
continue;
|
||||
|
||||
fputs(writeBuffer, tempFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
fputs(buffer, tempFile);
|
||||
}
|
||||
|
||||
// If the file doesn't end with a new line, write one.
|
||||
if (buffer[strlen(buffer) - 1] != '\n')
|
||||
fputs("\n", tempFile);
|
||||
|
||||
// If no line was replaced add it in at the end
|
||||
if (!lineWasReplaced)
|
||||
fputs(writeBuffer, tempFile);
|
||||
|
||||
fclose(config->file);
|
||||
fclose(tempFile);
|
||||
|
||||
// Now re-write the old file with the new temp file name
|
||||
remove(config->path);
|
||||
rename(CONFIG_TEMP_FILE_NAME, config->path);
|
||||
|
||||
return processConfig(config, config->path);
|
||||
}
|
||||
|
||||
ConfigStatus writeConfigInt(Config *config, char *key, int value)
|
||||
{
|
||||
char stringValue[CONFIG_MAX_LINE_LENGTH];
|
||||
sprintf(stringValue, "%d", value);
|
||||
return writeConfigString(config, key, stringValue);
|
||||
}
|
||||
|
||||
ConfigStatus deleteConfig(Config *config, char *key)
|
||||
{
|
||||
return writeConfigString(config, key, "");
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#define CONFIG_MAX_LINE_LENGTH 2048
|
||||
#define MAX_ARGUMENT_AMOUNT 4
|
||||
#define MAX_CONFIG_LINES 100
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONFIG_STATUS_SUCCESS,
|
||||
CONFIG_STATUS_FAILURE,
|
||||
CONFIG_STATUS_COMMAND_NOT_FOUND,
|
||||
CONFIG_STATUS_NOT_ENOUGH_ARGUMENTS,
|
||||
CONFIG_STATUS_CANNOT_OPEN_FILE
|
||||
} ConfigStatus;
|
||||
|
||||
typedef struct {
|
||||
char command[CONFIG_MAX_LINE_LENGTH];
|
||||
char argument[MAX_ARGUMENT_AMOUNT][CONFIG_MAX_LINE_LENGTH];
|
||||
int argumentIndex;
|
||||
} ConfigPair;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FILE *file;
|
||||
char* path;
|
||||
int configPairIndex;
|
||||
ConfigPair configPairs[MAX_CONFIG_LINES];
|
||||
} Config;
|
||||
|
||||
char* getConfigErrorString(ConfigStatus status);
|
||||
|
||||
|
||||
ConfigStatus processConfig(Config *config, char *path);
|
||||
|
||||
ConfigStatus getConfigString(Config *config, char* key, char* value);
|
||||
ConfigStatus getConfigArgumentString(Config *config, char* key, int n, char* value);
|
||||
ConfigStatus getConfigInt(Config *config, char* key, int* value);
|
||||
ConfigStatus getConfigArgumentInt(Config *config, char* key, int n, int* value);
|
||||
|
||||
ConfigStatus writeConfigString(Config *config, char* key, char* value);
|
||||
ConfigStatus writeConfigInt(Config *config, char* key, int value);
|
||||
|
||||
ConfigStatus deleteConfig(Config *config, char* key);
|
||||
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "controlIniGen.h"
|
||||
#include "iniParser.h"
|
||||
#include "sdlInput.h"
|
||||
|
||||
const ControlBinding gDefaultCommonBindings[] = {
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_T, AXIS_MODE_DIGITAL, 0, false, SYSTEM, LA_Test},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_5, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Coin},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_6, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Coin},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_1, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Start},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_2, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Start},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 11, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Start},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_START, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Start},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_S, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Service},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 10, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Service},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_BACK, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Service},
|
||||
};
|
||||
|
||||
const size_t gDefaultCommonBindingsSize = sizeof(gDefaultCommonBindings) / sizeof(gDefaultCommonBindings[0]);
|
||||
|
||||
const ControlBinding gDefaultDigitalBindings[] = {
|
||||
// Keyboard
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_UP, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_DOWN, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Q, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button1},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_W, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button2},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_E, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button3},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F7, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Card1Insert},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F8, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Card2Insert},
|
||||
|
||||
// Controller
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_UP, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_DOWN, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_SOUTH, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button1},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_EAST, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button2},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_WEST, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button3},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTY, AXIS_MODE_DIGITAL, -1, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTY, AXIS_MODE_DIGITAL, 1, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTX, AXIS_MODE_DIGITAL, 1, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTX, AXIS_MODE_DIGITAL, -1, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_GUIDE, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Card1Insert},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 1, SDL_GAMEPAD_BUTTON_GUIDE, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Card2Insert},
|
||||
|
||||
// Joystick
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_UP, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_DOWN, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_LEFT, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_RIGHT, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 0, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button1},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 1, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button2},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 2, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Button3},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 1, AXIS_MODE_DIGITAL, -1, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 1, AXIS_MODE_DIGITAL, 1, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 0, AXIS_MODE_DIGITAL, -1, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 0, AXIS_MODE_DIGITAL, 1, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 11, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Card1Insert},
|
||||
{INPUT_TYPE_JOY_BUTTON, 1, 11, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Card2Insert},
|
||||
};
|
||||
|
||||
const size_t gDefaultDigitalBindingsSize = sizeof(gDefaultDigitalBindings) / sizeof(gDefaultDigitalBindings[0]);
|
||||
|
||||
const ControlBinding gDefaultDrivingBindings[] = {
|
||||
// Keyboard
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Steer_Left},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Steer_Right},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_UP, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Gas_Digital},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_DOWN, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Brake_Digital},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_V, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ViewChange},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Q, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Boost},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_W, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_BoostRight},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_A, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GearUp},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Z, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GearDown},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_M, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_MusicChange},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_I, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_K, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_J, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_L, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F7, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_CardInsert},
|
||||
|
||||
// Controller
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTX, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_Steer},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_Gas},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_Brake},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_SOUTH, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Boost},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_EAST, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_BoostRight},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_NORTH, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ViewChange},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_WEST, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_MusicChange},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GearUp},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GearDown},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_UP, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_DOWN, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_GUIDE, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_CardInsert},
|
||||
|
||||
// Joystick
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 0, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_Steer},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 5, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_Gas},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 2, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_Brake},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 0, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Boost},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 1, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_BoostRight},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 2, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ViewChange},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 5, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GearUp},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 4, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GearDown},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 3, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_MusicChange},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_UP, false, PLAYER_1, LA_Up},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_DOWN, false, PLAYER_1, LA_Down},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_LEFT, false, PLAYER_1, LA_Left},
|
||||
{INPUT_TYPE_JOY_HAT, 0, 0, AXIS_MODE_DIGITAL, SDL_HAT_RIGHT, false, PLAYER_1, LA_Right},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 12, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_CardInsert},
|
||||
};
|
||||
|
||||
const size_t gDefaultDrivingBindingsSize = sizeof(gDefaultDrivingBindings) / sizeof(gDefaultDrivingBindings[0]);
|
||||
|
||||
const ControlBinding gDefaultAbcBindings[] = {
|
||||
// Keyboard
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ABC_Left},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ABC_Right},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_UP, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ABC_Up},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_DOWN, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ABC_Down},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_A, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Throttle_Accelerate},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Z, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Throttle_Slowdown},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Q, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_GunTrigger},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_W, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_MissileTrigger},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_E, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ClimaxSwitch},
|
||||
|
||||
// Controller
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTX, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_ABC_X},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFTY, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_ABC_Y},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_RIGHTY, AXIS_MODE_FULL, 0, true, PLAYER_1, LA_Throttle},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, AXIS_MODE_POSITIVE_HALF, 0, false, PLAYER_1, LA_Throttle},
|
||||
{INPUT_TYPE_GAMEPAD_AXIS, 0, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, AXIS_MODE_NEGATIVE_HALF, 0, false, PLAYER_1, LA_Throttle},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_SOUTH, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_GunTrigger},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_EAST, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_MissileTrigger},
|
||||
{INPUT_TYPE_GAMEPAD_BUTTON, 0, SDL_GAMEPAD_BUTTON_NORTH, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ClimaxSwitch},
|
||||
|
||||
// Joystick
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 0, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_ABC_X},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 1, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_ABC_Y},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 4, AXIS_MODE_FULL, 0, true, PLAYER_1, LA_Throttle},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 5, AXIS_MODE_POSITIVE_HALF, 0, false, PLAYER_1, LA_Throttle},
|
||||
{INPUT_TYPE_JOY_AXIS, 0, 4, AXIS_MODE_NEGATIVE_HALF, 0, false, PLAYER_1, LA_Throttle},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 0, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_GunTrigger},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 1, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_MissileTrigger},
|
||||
{INPUT_TYPE_JOY_BUTTON, 0, 4, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ClimaxSwitch},
|
||||
|
||||
};
|
||||
|
||||
const size_t gDefaultAbcBindingsSize = sizeof(gDefaultAbcBindings) / sizeof(gDefaultAbcBindings[0]);
|
||||
|
||||
const ControlBinding gDefaultShootingBindings[] = {
|
||||
// Mouse / Gun
|
||||
{INPUT_TYPE_MOUSE_AXIS, 0, 0, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_GunX},
|
||||
{INPUT_TYPE_MOUSE_AXIS, 0, 1, AXIS_MODE_FULL, 0, false, PLAYER_1, LA_GunY},
|
||||
{INPUT_TYPE_MOUSE_BUTTON, 0, SDL_BUTTON_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Trigger},
|
||||
{INPUT_TYPE_MOUSE_BUTTON, 0, SDL_BUTTON_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Reload},
|
||||
{INPUT_TYPE_MOUSE_BUTTON, 0, SDL_BUTTON_MIDDLE, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_GunButton},
|
||||
|
||||
// {INPUT_TYPE_MOUSE_AXIS, 0, 0, AXIS_MODE_FULL, 0, false, PLAYER_2, LA_GunX},
|
||||
// {INPUT_TYPE_MOUSE_AXIS, 0, 1, AXIS_MODE_FULL, 0, false, PLAYER_2, LA_GunY},
|
||||
// {INPUT_TYPE_MOUSE_BUTTON, 0, SDL_BUTTON_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Trigger},
|
||||
// {INPUT_TYPE_MOUSE_BUTTON, 0, SDL_BUTTON_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Reload},
|
||||
// {INPUT_TYPE_MOUSE_BUTTON, 0, SDL_BUTTON_MIDDLE, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_GunButton},
|
||||
|
||||
// Keyboard
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Q, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Trigger},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_W, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_Reload},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_E, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_GunButton},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_R, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_ActionButton},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_LEFT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_PedalLeft},
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_RIGHT, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_PedalRight},
|
||||
};
|
||||
|
||||
const size_t gDefaultShootingBindingsSize = sizeof(gDefaultShootingBindings) / sizeof(gDefaultShootingBindings[0]);
|
||||
|
||||
const ControlBinding gDefaultMahjongBindings[] = {
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_Y, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_A}, // A
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_U, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_B}, // B
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_I, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_C}, // C
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_O, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_D}, // D
|
||||
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_G, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_E}, // E
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_H, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_F}, // F
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_J, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_G}, // G
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_K, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_H}, // H
|
||||
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_L, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_I}, // I
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_V, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_J}, // J
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_B, AXIS_MODE_DIGITAL, 0, false, PLAYER_1, LA_K}, // K
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_N, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_L}, // L
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_M, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_M}, // M
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_COMMA, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_N}, // N
|
||||
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F1, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Chi}, // CHI
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F2, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Pon}, // PON
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F3, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Kan}, // KAN
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F4, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Reach}, // REACH
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F5, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Agari}, // AGARI
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F6, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_Cancel}, // CANCEL
|
||||
{INPUT_TYPE_KEY, 0, SDL_SCANCODE_F7, AXIS_MODE_DIGITAL, 0, false, PLAYER_2, LA_CardInsert}, // Cards In
|
||||
};
|
||||
|
||||
const size_t gDefaultMahjongBindingsSize = sizeof(gDefaultMahjongBindings) / sizeof(gDefaultMahjongBindings[0]);
|
||||
|
||||
char *toUpperCase(const char *str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
char *result = (char *)malloc(strlen(str) + 1);
|
||||
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
int i = 0;
|
||||
while (str[i] != '\0')
|
||||
{
|
||||
result[i] = toupper((unsigned char)str[i]);
|
||||
i++;
|
||||
}
|
||||
result[i] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a ControlBinding struct back into an INI string.
|
||||
* This is the reverse of parseSdlSource, used for generating a default controls.ini.
|
||||
* @param binding Pointer to the ControlBinding to convert.
|
||||
* @param buffer Character buffer to write the string into.
|
||||
* @param buffer_size The size of the character buffer.
|
||||
*/
|
||||
static void getBindingString(const ControlBinding *binding, char *buffer, size_t buffer_size)
|
||||
{
|
||||
char temp_buffer[128] = {0};
|
||||
switch (binding->type)
|
||||
{
|
||||
case INPUT_TYPE_KEY:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "KEY_%s", SDL_GetScancodeName(binding->sdlId));
|
||||
break;
|
||||
case INPUT_TYPE_MOUSE_BUTTON:
|
||||
if (binding->sdlId == SDL_BUTTON_LEFT)
|
||||
strcpy(temp_buffer, "MOUSE_LEFT_BUTTON");
|
||||
else if (binding->sdlId == SDL_BUTTON_RIGHT)
|
||||
strcpy(temp_buffer, "MOUSE_RIGHT_BUTTON");
|
||||
else if (binding->sdlId == SDL_BUTTON_MIDDLE)
|
||||
strcpy(temp_buffer, "MOUSE_MIDDLE_BUTTON");
|
||||
else
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "MOUSE_BUTTON_%d", binding->sdlId);
|
||||
break;
|
||||
case INPUT_TYPE_MOUSE_AXIS:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "MOUSE_AXIS_%c", binding->sdlId == 0 ? 'X' : 'Y');
|
||||
break;
|
||||
case INPUT_TYPE_JOY_BUTTON:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_BUTTON_%d", binding->deviceIndex, binding->sdlId);
|
||||
break;
|
||||
case INPUT_TYPE_JOY_HAT:
|
||||
if (binding->axisThreshold == SDL_HAT_UP)
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_HAT%d_UP", binding->deviceIndex, binding->sdlId);
|
||||
else if (binding->axisThreshold == SDL_HAT_DOWN)
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_HAT%d_DOWN", binding->deviceIndex, binding->sdlId);
|
||||
else if (binding->axisThreshold == SDL_HAT_LEFT)
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_HAT%d_LEFT", binding->deviceIndex, binding->sdlId);
|
||||
else if (binding->axisThreshold == SDL_HAT_RIGHT)
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_HAT%d_RIGHT", binding->deviceIndex, binding->sdlId);
|
||||
break;
|
||||
case INPUT_TYPE_GAMEPAD_BUTTON:
|
||||
{
|
||||
char *buttonName = toUpperCase(SDL_GetGamepadStringForButton(binding->sdlId));
|
||||
if (buttonName)
|
||||
{
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "GC%d_BUTTON_%s", binding->deviceIndex, buttonName);
|
||||
free(buttonName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INPUT_TYPE_JOY_AXIS:
|
||||
case INPUT_TYPE_GAMEPAD_AXIS:
|
||||
{
|
||||
char prefix[64];
|
||||
if (binding->type == INPUT_TYPE_JOY_AXIS)
|
||||
snprintf(prefix, sizeof(prefix), "JOY%d_AXIS_%d", binding->deviceIndex, binding->sdlId);
|
||||
else
|
||||
{
|
||||
char *axisName = toUpperCase(SDL_GetGamepadStringForAxis(binding->sdlId));
|
||||
if (axisName)
|
||||
{
|
||||
snprintf(prefix, sizeof(prefix), "GC%d_AXIS_%s", binding->deviceIndex, axisName);
|
||||
free(axisName);
|
||||
}
|
||||
}
|
||||
|
||||
switch (binding->axisMode)
|
||||
{
|
||||
case AXIS_MODE_DIGITAL:
|
||||
if (binding->axisThreshold > 0)
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "%s_POSITIVE", prefix);
|
||||
else
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "%s_NEGATIVE", prefix);
|
||||
break;
|
||||
case AXIS_MODE_POSITIVE_HALF:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "%s_POSITIVE_HALF", prefix);
|
||||
break;
|
||||
case AXIS_MODE_NEGATIVE_HALF:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "%s_NEGATIVE_HALF", prefix);
|
||||
break;
|
||||
case AXIS_MODE_FULL:
|
||||
default:
|
||||
strcpy(temp_buffer, prefix);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (binding->isInverted)
|
||||
{
|
||||
strncat(temp_buffer, "_INVERTED", sizeof(temp_buffer) - strlen(temp_buffer) - 1);
|
||||
}
|
||||
strncpy(buffer, temp_buffer, buffer_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper to add a set of default bindings to an in-memory INI config.
|
||||
*/
|
||||
static void addBindingsToIni(IniConfig *cfg, const char *section_name, const ControlBinding *bindings, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
const ControlBinding *b = &bindings[i];
|
||||
char action_str[128];
|
||||
char binding_str[128];
|
||||
getLogicalActionString(b, action_str, sizeof(action_str), section_name);
|
||||
getBindingString(b, binding_str, sizeof(binding_str));
|
||||
|
||||
IniSection *sec = iniGetSection(cfg, section_name);
|
||||
IniKeyValuePair *pair = NULL;
|
||||
if (sec)
|
||||
{
|
||||
for (int j = 0; j < sec->numPairs; j++)
|
||||
{
|
||||
if (strcmp(sec->pairs[j].key, action_str) == 0)
|
||||
{
|
||||
pair = &sec->pairs[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pair)
|
||||
{ // Key already exists, append to it
|
||||
char new_value[1024];
|
||||
snprintf(new_value, sizeof(new_value), "%s, %s", pair->value, binding_str);
|
||||
iniSetValue(cfg, section_name, action_str, new_value);
|
||||
}
|
||||
else
|
||||
{ // New key, just set it
|
||||
iniSetValue(cfg, section_name, action_str, binding_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates a default controls.ini file based on the hardcoded default binding arrays.
|
||||
*/
|
||||
void createDefaultControlsIni(const char *fileName)
|
||||
{
|
||||
printf("Generating default controls.ini from built-in bindings...\n");
|
||||
|
||||
IniConfig *ini = calloc(1, sizeof(IniConfig));
|
||||
if (!ini)
|
||||
{
|
||||
perror("Failed to allocate memory for INI config");
|
||||
return;
|
||||
}
|
||||
|
||||
iniSetValue(ini, "Config", "Steer_DeadZone", "8000");
|
||||
iniSetValue(ini, "Config", "Gas_DeadZone", "500");
|
||||
iniSetValue(ini, "Config", "Brake_DeadZone", "500");
|
||||
iniSetValue(ini, "Config", "ABC_X_DeadZone", "8000");
|
||||
iniSetValue(ini, "Config", "ABC_Y_DeadZone", "8000");
|
||||
iniSetValue(ini, "Config", "Throttle_DeadZone", "8000");
|
||||
iniSetValue(ini, "Config", "ShakeIncreaseRate", "10.0");
|
||||
iniSetValue(ini, "Config", "ShakeDecayRate", "0.95");
|
||||
|
||||
addBindingsToIni(ini, "Common", gDefaultCommonBindings, gDefaultCommonBindingsSize);
|
||||
addBindingsToIni(ini, "Digital", gDefaultDigitalBindings, gDefaultDigitalBindingsSize);
|
||||
addBindingsToIni(ini, "Driving", gDefaultDrivingBindings, gDefaultDrivingBindingsSize);
|
||||
addBindingsToIni(ini, "ABC", gDefaultAbcBindings, gDefaultAbcBindingsSize);
|
||||
addBindingsToIni(ini, "Shooting", gDefaultShootingBindings, gDefaultShootingBindingsSize);
|
||||
addBindingsToIni(ini, "Mahjong", gDefaultMahjongBindings, gDefaultMahjongBindingsSize);
|
||||
|
||||
if (iniSave(ini, fileName) == 0)
|
||||
{
|
||||
printf("Default %s has been created successfully.\n", fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Failed to create %s.\n", fileName);
|
||||
}
|
||||
|
||||
iniFree(ini);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Generates a default controls.ini file based on the hardcoded default binding arrays.
|
||||
* This ensures the generated INI is always in sync with the application's default behavior.
|
||||
* @param fileName The name of the file to create (e.g., "controls.ini").
|
||||
*/
|
||||
void createDefaultControlsIni(const char *fileName);
|
||||
@@ -0,0 +1,555 @@
|
||||
// #include <SDL3/SDL_timer.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <dlfcn.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <pthread.h>
|
||||
#include <SDL3/SDL_pixels.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "resolution.h"
|
||||
|
||||
#define MAX_PLAYERS 2
|
||||
#define INACTIVITY_TIMEOUT 3
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern int gWidth;
|
||||
extern int gHeight;
|
||||
extern int drawableW;
|
||||
extern int drawableH;
|
||||
|
||||
extern int phX, phY, phW, phH;
|
||||
extern int phX2, phY2, phW2, phH2;
|
||||
|
||||
static Crosshair crossHair[MAX_PLAYERS];
|
||||
static GLuint gShaderProgram = 0;
|
||||
static GLint gUProjectionLoc = -1;
|
||||
static GLint gUTextureLoc = -1;
|
||||
static pthread_t pollingThreadId = 0;
|
||||
|
||||
void (*real_glDrawArrays)(GLenum, GLint, GLsizei) = NULL;
|
||||
|
||||
bool p1CrossHairInitialized = false;
|
||||
bool p2CrossHairInitialized = false;
|
||||
bool testMode = false;
|
||||
|
||||
int textureIdIdxAdjust = 0;
|
||||
|
||||
|
||||
static const char *vertex_shader_source = "#version 120\n"
|
||||
"attribute vec2 a_pos;\n"
|
||||
"attribute vec2 a_tex_coord;\n"
|
||||
"varying vec2 v_tex_coord;\n"
|
||||
"uniform mat4 u_projection;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = u_projection * vec4(a_pos.x, a_pos.y, 0.0, 1.0);\n"
|
||||
" v_tex_coord = a_tex_coord;\n"
|
||||
"}\n";
|
||||
static const char *fragment_shader_source = "#version 120\n"
|
||||
"varying vec2 v_tex_coord;\n"
|
||||
"uniform sampler2D u_texture;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = texture2D(u_texture, v_tex_coord);\n"
|
||||
"}\n";
|
||||
|
||||
GLuint compileShader(GLenum type, const char *source)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, NULL);
|
||||
glCompileShader(shader);
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, NULL, infoLog);
|
||||
fprintf(stderr, "ERROR: Shader compilation failed\n%s\n", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint createShaderProgram()
|
||||
{
|
||||
GLuint vert = compileShader(GL_VERTEX_SHADER, vertex_shader_source);
|
||||
if (vert == 0)
|
||||
return 0;
|
||||
GLuint frag = compileShader(GL_FRAGMENT_SHADER, fragment_shader_source);
|
||||
if (frag == 0)
|
||||
{
|
||||
glDeleteShader(vert);
|
||||
return 0;
|
||||
}
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vert);
|
||||
glAttachShader(program, frag);
|
||||
glBindAttribLocation(program, 0, "a_pos");
|
||||
glBindAttribLocation(program, 1, "a_tex_coord");
|
||||
glLinkProgram(program);
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(program, 512, NULL, infoLog);
|
||||
fprintf(stderr, "ERROR: Shader linking failed\n%s\n", infoLog);
|
||||
}
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
return program;
|
||||
}
|
||||
|
||||
void createCrosshairGeometry(Crosshair *ch)
|
||||
{
|
||||
float w = (float)ch->width;
|
||||
float h = (float)ch->height;
|
||||
float vertices[] = {0.0f, h, 0.0f, 1.0f, w, h, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
w, h, 1.0f, 1.0f, w, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
glGenVertexArrays(1, &ch->vao);
|
||||
glGenBuffers(1, &ch->vbo);
|
||||
glBindVertexArray(ch->vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, ch->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void initCrossHairs()
|
||||
{
|
||||
if (gShaderProgram == 0)
|
||||
{
|
||||
gShaderProgram = createShaderProgram();
|
||||
gUProjectionLoc = glGetUniformLocation(gShaderProgram, "u_projection");
|
||||
gUTextureLoc = glGetUniformLocation(gShaderProgram, "u_texture");
|
||||
}
|
||||
|
||||
if (gShaderProgram == 0)
|
||||
return;
|
||||
|
||||
if (loadCrosshairImage(0, getConfig()->p1CrossHairPath))
|
||||
p1CrossHairInitialized = true;
|
||||
|
||||
if (loadCrosshairImage(1, getConfig()->p2CrossHairPath))
|
||||
p2CrossHairInitialized = true;
|
||||
|
||||
if (isTestMode() || gGrp == GROUP_HOD4_TEST || gGrp == GROUP_HOD4_SP_TEST)
|
||||
testMode = true;
|
||||
|
||||
if (!testMode && gId != PRIMEVAL_HUNT)
|
||||
startPollingThread();
|
||||
|
||||
if (!real_glDrawArrays)
|
||||
real_glDrawArrays = dlsym(RTLD_NEXT, "glDrawArrays");
|
||||
|
||||
textureIdIdxAdjust = p1CrossHairInitialized + p2CrossHairInitialized;
|
||||
}
|
||||
|
||||
int loadCrosshairImage(int player, const char *filepath)
|
||||
{
|
||||
if (player < 0 || player >= MAX_PLAYERS)
|
||||
return 0;
|
||||
|
||||
if (crossHair[player].surface)
|
||||
{
|
||||
SDL_DestroySurface(crossHair[player].surface);
|
||||
crossHair[player].surface = NULL;
|
||||
}
|
||||
if (crossHair[player].texture)
|
||||
{
|
||||
glDeleteTextures(1, &crossHair[player].texture);
|
||||
crossHair[player].texture = 0;
|
||||
}
|
||||
if (crossHair[player].vao)
|
||||
{
|
||||
glDeleteVertexArrays(1, &crossHair[player].vao);
|
||||
crossHair[player].vao = 0;
|
||||
}
|
||||
if (crossHair[player].vbo)
|
||||
{
|
||||
glDeleteBuffers(1, &crossHair[player].vbo);
|
||||
crossHair[player].vbo = 0;
|
||||
}
|
||||
|
||||
SDL_Surface *surface = IMG_Load(filepath);
|
||||
if (!surface)
|
||||
{
|
||||
fprintf(stderr, "Failed to load PNG for player %d: %s\n", player + 1, SDL_GetError());
|
||||
return 0;
|
||||
}
|
||||
crossHair[player].surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
|
||||
SDL_DestroySurface(surface);
|
||||
|
||||
crossHair[player].x = (int)(drawableW / 2.0f);
|
||||
crossHair[player].y = (int)(drawableH / 2.0f);
|
||||
crossHair[player].visible = false;
|
||||
// crossHair[player].lastMovementTime = time(NULL);
|
||||
crossHair[player].texture = 0;
|
||||
crossHair[player].vao = 0;
|
||||
crossHair[player].vbo = 0;
|
||||
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
GLuint tex;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, crossHair[player].surface->w, crossHair[player].surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
crossHair[player].surface->pixels);
|
||||
|
||||
SDL_DestroySurface(crossHair[player].surface);
|
||||
|
||||
crossHair[player].width = getConfig()->customCrossHairWidth;
|
||||
crossHair[player].height = getConfig()->customCrossHairHeight;
|
||||
crossHair[player].texture = tex;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void updateCrosshairPosition(int player, float normX, float normY)
|
||||
{
|
||||
if (player < 0 || player >= MAX_PLAYERS)
|
||||
return;
|
||||
crossHair[player].x = normX * drawableW;
|
||||
crossHair[player].y = normY * drawableH;
|
||||
// crossHair[player].lastMovementTime = time(NULL);
|
||||
// crossHair[player].visible = true;
|
||||
if (testMode || gId == PRIMEVAL_HUNT)
|
||||
crossHair[player].visible = true;
|
||||
}
|
||||
|
||||
void renderCrosshairs(void)
|
||||
{
|
||||
if (gId == PRIMEVAL_HUNT)
|
||||
glViewport(phX, phY, phW, phH);
|
||||
else if (gGrp == GROUP_HOD4_SP)
|
||||
glViewport(0, 0, gWidth, gHeight);
|
||||
|
||||
GLint texFormat = 0;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &texFormat);
|
||||
|
||||
if (texFormat == 0x1908 && gId == GHOST_SQUAD_EVOLUTION)
|
||||
return;
|
||||
|
||||
GLint last_program, last_vao, last_vbo, last_active_texture, last_depth_func;
|
||||
GLboolean last_blend_enabled, last_depth_enabled; //, last_srgb_enabled;
|
||||
GLint last_blend_src, last_blend_dst;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vao);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vbo);
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &last_active_texture);
|
||||
last_blend_enabled = glIsEnabled(GL_BLEND);
|
||||
last_depth_enabled = glIsEnabled(GL_DEPTH_TEST);
|
||||
// last_srgb_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &last_blend_src);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &last_blend_dst);
|
||||
glGetIntegerv(GL_DEPTH_FUNC, &last_depth_func);
|
||||
|
||||
// if (last_srgb_enabled)
|
||||
// glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glUseProgram(gShaderProgram);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(gUTextureLoc, 0);
|
||||
|
||||
time_t currentTime = time(NULL);
|
||||
for (int i = 0; i < MAX_PLAYERS; ++i)
|
||||
{
|
||||
// if (difftime(currentTime, crossHair[i].lastMovementTime) > INACTIVITY_TIMEOUT)
|
||||
// {
|
||||
// crossHair[i].visible = false;
|
||||
// }
|
||||
if (!crossHair[i].visible)
|
||||
continue;
|
||||
|
||||
if (crossHair[i].texture == 0 && crossHair[i].surface != NULL)
|
||||
{
|
||||
glGenTextures(1, &crossHair[i].texture);
|
||||
glBindTexture(GL_TEXTURE_2D, crossHair[i].texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, crossHair[i].surface->w, crossHair[i].surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
crossHair[i].surface->pixels);
|
||||
|
||||
crossHair[i].width = getConfig()->customCrossHairWidth;
|
||||
crossHair[i].height = getConfig()->customCrossHairHeight;
|
||||
createCrosshairGeometry(&crossHair[i]);
|
||||
|
||||
SDL_DestroySurface(crossHair[i].surface);
|
||||
crossHair[i].surface = NULL;
|
||||
}
|
||||
|
||||
if (crossHair[i].texture != 0 && crossHair[i].vao != 0)
|
||||
{
|
||||
float x = crossHair[i].x - (crossHair[i].width / 2.0f);
|
||||
float y = crossHair[i].y - (crossHair[i].height / 2.0f);
|
||||
float projection[16] = {0.0f};
|
||||
projection[0] = 2.0f / drawableW;
|
||||
projection[5] = -2.0f / drawableH;
|
||||
projection[10] = -1.0f;
|
||||
projection[12] = -1.0f + (x * projection[0]);
|
||||
projection[13] = 1.0f + (y * projection[5]);
|
||||
projection[15] = 1.0f;
|
||||
glUniformMatrix4fv(gUProjectionLoc, 1, GL_FALSE, projection);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, crossHair[i].texture);
|
||||
glBindVertexArray(crossHair[i].vao);
|
||||
real_glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(last_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_vbo);
|
||||
glUseProgram(last_program);
|
||||
glActiveTexture(last_active_texture);
|
||||
glBlendFunc(last_blend_src, last_blend_dst);
|
||||
if (last_blend_enabled)
|
||||
glEnable(GL_BLEND);
|
||||
else
|
||||
glDisable(GL_BLEND);
|
||||
if (last_depth_enabled)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
else
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(last_depth_func);
|
||||
// if (last_srgb_enabled)
|
||||
// glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
|
||||
void bindAndPosition(Crosshair *c)
|
||||
{
|
||||
if (!c->texture)
|
||||
return;
|
||||
;
|
||||
|
||||
float x = c->x - c->width / 2.0f;
|
||||
float y = c->y - c->height / 2.0f;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, c->texture);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(x, y);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(x + c->width, y);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(x + c->width, y + c->height);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(x, y + c->height);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void renderGsEvoCrosshairs(void)
|
||||
{
|
||||
GLint texFormat = 0;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &texFormat);
|
||||
|
||||
if (texFormat == 0x1908 && gId == GHOST_SQUAD_EVOLUTION)
|
||||
return;
|
||||
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
glDisable(GL_DEPTH_TEST); // for HOD4
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, drawableW, drawableH, 0, -1, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
if (p1CrossHairInitialized && crossHair[0].visible)
|
||||
bindAndPosition(&crossHair[0]);
|
||||
|
||||
if (p2CrossHairInitialized && crossHair[1].visible)
|
||||
bindAndPosition(&crossHair[1]);
|
||||
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
void destroyCrosshairs(void)
|
||||
{
|
||||
stopPollingThread();
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (crossHair[i].texture)
|
||||
glDeleteTextures(1, &crossHair[i].texture);
|
||||
if (crossHair[i].vao)
|
||||
glDeleteVertexArrays(1, &crossHair[i].vao);
|
||||
if (crossHair[i].vbo)
|
||||
glDeleteBuffers(1, &crossHair[i].vbo);
|
||||
if (crossHair[i].surface)
|
||||
SDL_DestroySurface(crossHair[i].surface);
|
||||
}
|
||||
if (gShaderProgram)
|
||||
glDeleteProgram(gShaderProgram);
|
||||
}
|
||||
|
||||
void glDrawArrays(GLenum mode, GLint first, GLsizei count)
|
||||
{
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
GLint currentFBO = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, ¤tFBO);
|
||||
renderGsEvoCrosshairs();
|
||||
}
|
||||
if (!real_glDrawArrays)
|
||||
real_glDrawArrays = dlsym(RTLD_NEXT, "glDrawArrays");
|
||||
real_glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool keepRunning;
|
||||
} PollingArgs;
|
||||
|
||||
PollingArgs gPollingArgs;
|
||||
|
||||
static void *gsevoPollingThreadFunc(void *arg)
|
||||
{
|
||||
PollingArgs *args = (PollingArgs *)arg;
|
||||
|
||||
while (args->keepRunning)
|
||||
{
|
||||
uint8_t p1Mode = *(uint8_t *)0x086617E8;
|
||||
uint8_t p2Mode = *(uint8_t *)0x08661994;
|
||||
if (p1Mode == 0x2)
|
||||
crossHair[0].visible = true;
|
||||
else
|
||||
crossHair[0].visible = false;
|
||||
|
||||
if (p2Mode == 0x2)
|
||||
crossHair[1].visible = true;
|
||||
else
|
||||
crossHair[1].visible = false;
|
||||
|
||||
SDL_Delay(10);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *pollingThreadFunc(void *arg)
|
||||
{
|
||||
PollingArgs *args = (PollingArgs *)arg;
|
||||
uint32_t *pGunMgr;
|
||||
uint32_t *pPlayerMgr;
|
||||
|
||||
if (gId == THE_HOUSE_OF_THE_DEAD_4_REVA)
|
||||
{
|
||||
pGunMgr = (uint32_t *)0x0a711758;
|
||||
pPlayerMgr = (uint32_t *)0x0a7117a8;
|
||||
}
|
||||
else if (gId == THE_HOUSE_OF_THE_DEAD_4_REVB || gId == THE_HOUSE_OF_THE_DEAD_4_REVC)
|
||||
{
|
||||
pGunMgr = (uint32_t *)0x0a6f27a8;
|
||||
pPlayerMgr = (uint32_t *)0x0a6f27f8;
|
||||
}
|
||||
else if (gId == RAMBO)
|
||||
pPlayerMgr = (uint32_t *)0x0842fe9c;
|
||||
else if (gId == RAMBO_CHINA)
|
||||
pPlayerMgr = (uint32_t *)0x084304fc;
|
||||
else if (gId == THE_HOUSE_OF_THE_DEAD_4_SPECIAL_REVB)
|
||||
pPlayerMgr = (uint32_t *)0x0A69F92C;
|
||||
else
|
||||
args->keepRunning = false;
|
||||
|
||||
while (args->keepRunning)
|
||||
{
|
||||
if (gGrp != GROUP_HOD4 || *pGunMgr != 0x0)
|
||||
{
|
||||
uint8_t *gameMode;
|
||||
if (gGrp == GROUP_HOD4)
|
||||
{
|
||||
uint32_t *gameModeAddress = *(void **)pGunMgr + 0x2c;
|
||||
gameMode = *(uint8_t **)gameModeAddress + 0x38;
|
||||
}
|
||||
if (gGrp != GROUP_HOD4 || *gameMode == 8)
|
||||
{
|
||||
if (*pPlayerMgr != 0x0)
|
||||
{
|
||||
uint32_t *p1ModeAddress = *(void **)pPlayerMgr + 0x34;
|
||||
uint32_t *p2ModeAddress = *(void **)pPlayerMgr + 0x38;
|
||||
|
||||
if (*p1ModeAddress != 0x0)
|
||||
{
|
||||
uint8_t *p1Mode = *(uint8_t **)p1ModeAddress + 0x38;
|
||||
if (*p1Mode == 3 || *p1Mode == 5)
|
||||
crossHair[0].visible = true;
|
||||
else
|
||||
crossHair[0].visible = false;
|
||||
}
|
||||
|
||||
if (*p2ModeAddress != 0x0)
|
||||
{
|
||||
uint8_t *p2Mode = *(uint8_t **)p2ModeAddress + 0x38;
|
||||
if (*p2Mode == 3 || *p2Mode == 5)
|
||||
crossHair[1].visible = true;
|
||||
else
|
||||
crossHair[1].visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void startPollingThread()
|
||||
{
|
||||
if (gPollingArgs.keepRunning)
|
||||
return;
|
||||
|
||||
gPollingArgs.keepRunning = true;
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
pthread_create(&pollingThreadId, NULL, gsevoPollingThreadFunc, &gPollingArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_create(&pollingThreadId, NULL, pollingThreadFunc, &gPollingArgs);
|
||||
}
|
||||
}
|
||||
|
||||
void stopPollingThread()
|
||||
{
|
||||
if (gPollingArgs.keepRunning)
|
||||
{
|
||||
gPollingArgs.keepRunning = false;
|
||||
if (pollingThreadId)
|
||||
{
|
||||
pthread_join(pollingThreadId, NULL);
|
||||
pollingThreadId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include <SDL3/SDL_surface.h>
|
||||
typedef struct
|
||||
{
|
||||
float x, y;
|
||||
int width, height;
|
||||
|
||||
unsigned int texture;
|
||||
unsigned int vao;
|
||||
unsigned int vbo;
|
||||
SDL_Surface *surface;
|
||||
time_t lastMovementTime;
|
||||
bool visible;
|
||||
} Crosshair;
|
||||
|
||||
extern bool p1CrossHairInitialized;
|
||||
extern bool p2CrossHairInitialized;
|
||||
|
||||
void initCrossHairs();
|
||||
int loadCrosshairImage(int player, const char *filepath);
|
||||
void updateCrosshairPosition(int player, float normX, float normY);
|
||||
void renderCrosshairs(void);
|
||||
void destroyCrosshairs(void);
|
||||
|
||||
void startPollingThread();
|
||||
void stopPollingThread();
|
||||
@@ -0,0 +1,171 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <SDL3/SDL_surface.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "customCursor.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
|
||||
SDL_Cursor *customCursor = NULL;
|
||||
SDL_Cursor *touchCursor = NULL;
|
||||
|
||||
uint32_t hod4PrevValue;
|
||||
bool hod4InTutorial = false;
|
||||
void (*CPlayer_MainOri)(int);
|
||||
|
||||
int loadNewCursor(const char *cursorFileName, SDL_Cursor **cursor, int customWidth, int customHeight, int hotX, int hotY)
|
||||
{
|
||||
SDL_Surface *originalSurface = IMG_Load(cursorFileName);
|
||||
if (!originalSurface)
|
||||
{
|
||||
fprintf(stderr, "Failed to load image %s: %s\n", cursorFileName, SDL_GetError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_Surface *scaledSurface = SDL_ScaleSurface(originalSurface, customWidth, customHeight, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
SDL_DestroySurface(originalSurface);
|
||||
|
||||
if (!scaledSurface)
|
||||
{
|
||||
fprintf(stderr, "SDL_ScaleSurface Error: %s\n", SDL_GetError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
*cursor = SDL_CreateColorCursor(scaledSurface, hotX, hotY);
|
||||
if (!*cursor)
|
||||
{
|
||||
fprintf(stderr, "SDL_CreateColorCursor Error: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_DestroySurface(scaledSurface);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void loadCursors()
|
||||
{
|
||||
if (getConfig()->customCursorEnabled && strcmp(getConfig()->customCursor, "") != 0)
|
||||
{
|
||||
if (!loadNewCursor(getConfig()->customCursor, (void *)&customCursor, getConfig()->customCursorWidth,
|
||||
getConfig()->customCursorHeight, getConfig()->customCursorWidth / 2, getConfig()->customCursorHeight / 2))
|
||||
{
|
||||
fprintf(stderr, "Custom cursor could not be loaded!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (getConfig()->customCursorEnabled && strcmp(getConfig()->touchCursor, "") != 0)
|
||||
{
|
||||
if (!loadNewCursor(getConfig()->touchCursor, (void *)&touchCursor, getConfig()->touchCursorWidth, getConfig()->touchCursorHeight, 15,
|
||||
15))
|
||||
|
||||
{
|
||||
fprintf(stderr, "Custom Touch cursor could not be loaded!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setCursor(void *cursor)
|
||||
{
|
||||
if (SDL_GetCursor() != NULL)
|
||||
SDL_SetCursor(cursor);
|
||||
}
|
||||
|
||||
void hideCursor()
|
||||
{
|
||||
SDL_HideCursor();
|
||||
}
|
||||
|
||||
void showCursor()
|
||||
{
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
|
||||
void showPhCursor()
|
||||
{
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
|
||||
int XDefineCursor(Display *display, Window w, Cursor cursor)
|
||||
{
|
||||
int (*_xDefineCursor)(Display *, Window, Cursor) = dlsym(RTLD_NEXT, "XDefineCursor");
|
||||
if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE)
|
||||
return 0;
|
||||
|
||||
return _xDefineCursor(display, w, cursor);
|
||||
}
|
||||
|
||||
// Functions to Show and Hide the cursor in HOD4 while not necessary
|
||||
void CPlayer_Main(int param1)
|
||||
{
|
||||
|
||||
uint32_t value = *(unsigned int *)(uintptr_t)(param1 + 0x38);
|
||||
if (!getConfig()->hideCursor)
|
||||
{
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (hod4PrevValue != 3 || hod4InTutorial)
|
||||
{
|
||||
hideCursor();
|
||||
}
|
||||
hod4PrevValue = 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
if (!hod4InTutorial)
|
||||
{
|
||||
showCursor();
|
||||
hod4PrevValue = 3;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
hod4InTutorial = false;
|
||||
}
|
||||
case 2:
|
||||
case 5:
|
||||
case 11:
|
||||
{
|
||||
hideCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
CPlayer_MainOri(param1);
|
||||
}
|
||||
|
||||
// Functions to Show and Hide the cursor in HOD4 while not necessary
|
||||
int hod4VsPrintf(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, "free : tutorial\n") == 0)
|
||||
{
|
||||
hod4InTutorial = false;
|
||||
}
|
||||
|
||||
if (strcmp(str, "entry : tutorial\n") == 0)
|
||||
{
|
||||
hod4InTutorial = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int loadNewCursor(const char *cursorFileName, SDL_Cursor **cursor, int customWidth, int customHeight, int hotX, int hotY);
|
||||
void loadCursors();
|
||||
void setCursor(void *cursor);
|
||||
void hideCursor();
|
||||
void showCursor();
|
||||
void showPhCursor();
|
||||
void CPlayer_Main(int param1);
|
||||
int hod4VsPrintf(char *str, const char *format, va_list arg);
|
||||
@@ -1,229 +0,0 @@
|
||||
#include <SDL2/SDL_events.h>
|
||||
#include <SDL2/SDL_mouse.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_SUPPORT_ZLIB
|
||||
#define STBI_NO_SIMD
|
||||
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
extern Display *x11Display;
|
||||
extern Window window;
|
||||
|
||||
void *customCursor;
|
||||
void *phTouchCursor;
|
||||
void *blankCursor;
|
||||
|
||||
SDL_Surface *createSurfaceFromStb(unsigned char *data, int width, int height, int channels)
|
||||
{
|
||||
Uint32 rmask, gmask, bmask, amask;
|
||||
if (channels == 4)
|
||||
{
|
||||
rmask = 0x000000FF;
|
||||
gmask = 0x0000FF00;
|
||||
bmask = 0x00FF0000;
|
||||
amask = 0xFF000000;
|
||||
}
|
||||
else if (channels == 3)
|
||||
{
|
||||
rmask = 0x000000FF;
|
||||
gmask = 0x0000FF00;
|
||||
bmask = 0x00FF0000;
|
||||
amask = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unsupported image format with %d channels.\n", channels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SDL_CreateRGBSurfaceFrom(data, width, height, channels * 8, width * channels, rmask, gmask, bmask, amask);
|
||||
}
|
||||
|
||||
int loadNewCursor(const char *cursorFileName, void **cursor, int customWidth, int customHeight)
|
||||
{
|
||||
int width, height, channels;
|
||||
unsigned char *imageData = stbi_load(cursorFileName, &width, &height, &channels, 0);
|
||||
if (!imageData)
|
||||
{
|
||||
fprintf(stderr, "Failed to load image: %s\n", cursorFileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_Surface *originalSurface = createSurfaceFromStb(imageData, width, height, channels);
|
||||
if (!originalSurface)
|
||||
{
|
||||
stbi_image_free(imageData);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_Surface *resizedSurface =
|
||||
SDL_CreateRGBSurface(0, customWidth, customHeight, originalSurface->format->BitsPerPixel, originalSurface->format->Rmask,
|
||||
originalSurface->format->Gmask, originalSurface->format->Bmask, originalSurface->format->Amask);
|
||||
if (!resizedSurface)
|
||||
{
|
||||
fprintf(stderr, "SDL_CreateRGBSurface Error: %s\n", SDL_GetError());
|
||||
SDL_FreeSurface(originalSurface);
|
||||
stbi_image_free(imageData);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_Rect srcRect = {0, 0, originalSurface->w, originalSurface->h};
|
||||
SDL_Rect destRect = {0, 0, customWidth, customHeight};
|
||||
if (SDL_BlitScaled(originalSurface, &srcRect, resizedSurface, &destRect) != 0)
|
||||
{
|
||||
fprintf(stderr, "SDL_BlitScaled Error: %s\n", SDL_GetError());
|
||||
SDL_FreeSurface(resizedSurface);
|
||||
SDL_FreeSurface(originalSurface);
|
||||
stbi_image_free(imageData);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (getConfig()->noSDL == 0)
|
||||
{
|
||||
*cursor = SDL_CreateColorCursor(resizedSurface, customWidth / 2, customHeight / 2);
|
||||
if (!*cursor)
|
||||
{
|
||||
fprintf(stderr, "SDL_CreateColorCursor Error: %s\n", SDL_GetError());
|
||||
SDL_FreeSurface(resizedSurface);
|
||||
SDL_FreeSurface(originalSurface);
|
||||
stbi_image_free(imageData);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_FreeSurface(resizedSurface);
|
||||
SDL_FreeSurface(originalSurface);
|
||||
}
|
||||
else
|
||||
{
|
||||
XcursorImage *xcursor = XcursorImageCreate(resizedSurface->w, resizedSurface->h);
|
||||
if (!xcursor)
|
||||
return None;
|
||||
|
||||
xcursor->xhot = customWidth / 2;
|
||||
xcursor->yhot = customHeight / 2;
|
||||
xcursor->width = resizedSurface->w;
|
||||
xcursor->height = resizedSurface->h;
|
||||
|
||||
// Convert SDL surface to Xcursor format (32-bit ARGB)
|
||||
for (int y = 0; y < resizedSurface->h; ++y)
|
||||
{
|
||||
for (int x = 0; x < resizedSurface->w; ++x)
|
||||
{
|
||||
Uint32 pixel = ((Uint32 *)resizedSurface->pixels)[y * resizedSurface->w + x];
|
||||
Uint8 r, g, b, a;
|
||||
SDL_GetRGBA(pixel, resizedSurface->format, &r, &g, &b, &a);
|
||||
xcursor->pixels[y * resizedSurface->w + x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
*cursor = (void*)XcursorImageLoadCursor(x11Display, xcursor);
|
||||
XcursorImageDestroy(xcursor);
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void loadCursors()
|
||||
{
|
||||
if (strcmp(getConfig()->customCursor, "") != 0)
|
||||
{
|
||||
if (!loadNewCursor(getConfig()->customCursor, (void *)&customCursor, getConfig()->customCursorWidth,
|
||||
getConfig()->customCursorHeight))
|
||||
{
|
||||
fprintf(stderr, "Custom cursor could not be loaded!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(getConfig()->phTouchCursor, "") != 0)
|
||||
{
|
||||
if (!loadNewCursor(getConfig()->phTouchCursor, (void *)&phTouchCursor, getConfig()->phTouchCursorWidth,
|
||||
getConfig()->phTouchCursorHeight))
|
||||
|
||||
{
|
||||
fprintf(stderr, "Custom cursor could not be loaded!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(getConfig()->noSDL)
|
||||
{
|
||||
XColor dummyColor;
|
||||
Pixmap blankPixmap;
|
||||
blankPixmap = XCreatePixmap(x11Display, window, 1, 1, 1);
|
||||
if (!blankPixmap)
|
||||
{
|
||||
fprintf(stderr, "Failed to create blank pixmap\n");
|
||||
}
|
||||
|
||||
blankCursor = (void*)XCreatePixmapCursor(x11Display, blankPixmap, blankPixmap, &dummyColor, &dummyColor, 0, 0);
|
||||
if (!blankCursor)
|
||||
{
|
||||
fprintf(stderr, "Failed to create blank cursor\n");
|
||||
XFreePixmap(x11Display, blankPixmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setCursor(void *cursor)
|
||||
{
|
||||
if (getConfig()->noSDL == 0)
|
||||
{
|
||||
if (SDL_GetCursor() != NULL)
|
||||
SDL_SetCursor(cursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
XDefineCursor(x11Display, window, (Cursor)cursor);
|
||||
XFlush(x11Display);
|
||||
}
|
||||
}
|
||||
|
||||
void hideCursor()
|
||||
{
|
||||
if (getConfig()->noSDL == 0)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
XDefineCursor(x11Display, window, (Cursor)blankCursor);
|
||||
XFlush(x11Display);
|
||||
}
|
||||
}
|
||||
|
||||
void showCursor()
|
||||
{
|
||||
if (getConfig()->noSDL == 0)
|
||||
{
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
XDefineCursor(x11Display, window, (Cursor)customCursor);
|
||||
XFlush(x11Display);
|
||||
}
|
||||
}
|
||||
|
||||
void showPhCursor()
|
||||
{
|
||||
if (getConfig()->noSDL == 0)
|
||||
{
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
XDefineCursor(x11Display, window, (Cursor)phTouchCursor);
|
||||
XFlush(x11Display);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef CUSTOM_CURSORS_H
|
||||
#define CUSTOM_CURSORS_H
|
||||
|
||||
int loadNewCursor(const char *cursorFileName, void **cursor, int customWidth, int customHeight);
|
||||
void loadCursors();
|
||||
void setCursor(void *cursor);
|
||||
void hideCursor();
|
||||
void showCursor();
|
||||
void showPhCursor();
|
||||
#endif
|
||||
@@ -0,0 +1,226 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "driveBoard.h"
|
||||
#include "forceFeedback.h"
|
||||
#include "hook.h"
|
||||
#include "jvs.h"
|
||||
|
||||
#define DRIVEBOARD_READY 0x00
|
||||
#define DRIVEBOARD_NOT_INIT 0x11
|
||||
#define DRIVEBOARD_BUSY 0x44
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern DeviceType hooks[5];
|
||||
|
||||
unsigned char response;
|
||||
int wheelInitialized;
|
||||
|
||||
uint8_t buffer[64];
|
||||
uint8_t bufferIdx = 0;
|
||||
|
||||
int initDriveboard()
|
||||
{
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
wheelInitialized = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enableRead = 0;
|
||||
|
||||
ssize_t driveboardRead(int fd, void *buf, size_t count)
|
||||
{
|
||||
memset(buffer, '\0', 64);
|
||||
if (enableRead)
|
||||
{
|
||||
char *mybuf = (char *)buf;
|
||||
mybuf[0] = response;
|
||||
enableRead = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t driveboardWrite(int fd, const void *buf, size_t count)
|
||||
{
|
||||
const uint8_t *bytes = (const uint8_t *)buf;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
buffer[bufferIdx++] = bytes[i];
|
||||
if (bufferIdx == 4)
|
||||
{
|
||||
// if (buffer[0] != 0xfd)// && buffer[0] != 0x00 && buffer[0] != 0xff && buffer[0] != 0x01 && buffer[0] != 0xfa)
|
||||
// {
|
||||
// printf("Write: fd: %d,count: %zu, bufferIdx: %d\n", fd, count, bufferIdx);
|
||||
// for (int x = 0; x < 4; x++)
|
||||
// {
|
||||
// printf("0x%02x ", buffer[x]);
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
processDrivePacket(buffer, 1);
|
||||
sdlFfbDriveboard(buffer, 4);
|
||||
if (count != 7)
|
||||
bufferIdx = 0;
|
||||
}
|
||||
else if (bufferIdx == 7)
|
||||
{
|
||||
uint8_t player2Buffer[4];
|
||||
player2Buffer[0] = buffer[0];
|
||||
player2Buffer[1] = buffer[4];
|
||||
player2Buffer[2] = buffer[5];
|
||||
player2Buffer[3] = buffer[6];
|
||||
processDrivePacket(player2Buffer, 2);
|
||||
// if (player2Buffer[0] != 0xfd && player2Buffer[0] != 0x00 && player2Buffer[0] != 0xff && player2Buffer[0] != 0x01)
|
||||
// {
|
||||
// printf("Write P2: fd: %d,count: %zu, bufferIdx: %d\n", fd, count, bufferIdx);
|
||||
// for (int x = 0; x < 4; x++)
|
||||
// {
|
||||
// printf("0x%02x ", player2Buffer[x]);
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
bufferIdx = 0;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void processDrivePacket(uint8_t *buf, int player)
|
||||
{
|
||||
static int fcP1 = 0;
|
||||
static int fcP2 = 0;
|
||||
|
||||
switch (buf[0])
|
||||
{
|
||||
case 0xFF:
|
||||
{
|
||||
printf("Driveboard: Drive board reset\n");
|
||||
// response = 0x88;
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
{
|
||||
printf("Driveboard: Drive board reset 2\n");
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFC:
|
||||
{
|
||||
printf("Driveboard: Start wheel bounds testing\n");
|
||||
if (player == 1)
|
||||
{
|
||||
if (fcP1 == 0)
|
||||
{
|
||||
response = DRIVEBOARD_BUSY;
|
||||
fcP1++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (player == 2)
|
||||
{
|
||||
if (fcP2 == 0)
|
||||
{
|
||||
response = DRIVEBOARD_BUSY;
|
||||
fcP2++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
if ((buf[1] == 0x00 && buf[2] == 0x00 && buf[3] == 0x00) || (buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x01) ||
|
||||
(buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x00))
|
||||
wheelInitialized = 1;
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x83:
|
||||
case 0x86:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9e:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
case 0x84:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
|
||||
if ((buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x05) && !wheelInitialized)
|
||||
{
|
||||
if (player == 1)
|
||||
setAnalogue(ANALOGUE_1, (int)(0.5f * 1024.0f));
|
||||
else
|
||||
setAnalogue(ANALOGUE_5, (int)(0.5f * 1024.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t cmd1 = buf[1];
|
||||
if (gGrp == GROUP_OUTRUN_TEST)
|
||||
{
|
||||
if (buf[4] != 0)
|
||||
cmd1 = buf[4];
|
||||
}
|
||||
|
||||
float scaled = 0.0f;
|
||||
if (cmd1 == 1)
|
||||
scaled = (128.0f - (buf[2] - 1)) / 256.0f;
|
||||
|
||||
if (cmd1 == 0)
|
||||
scaled = (256.0f - (buf[2] + 1)) / 256.0f;
|
||||
|
||||
if (!wheelInitialized)
|
||||
{
|
||||
if (player == 1)
|
||||
setAnalogue(ANALOGUE_1, (int)(scaled * 1024.0f));
|
||||
else
|
||||
setAnalogue(ANALOGUE_5, (int)(scaled * 1024.0f));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xF1:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFA:
|
||||
case 0xFD:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// printf("Driveboard: Unknown command received %X\n", buffer[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
enableRead = 1;
|
||||
}
|
||||
|
||||
int driveBoardioctl(int fd, unsigned int request, void *data)
|
||||
{
|
||||
if (enableRead)
|
||||
{
|
||||
uint8_t d = 1;
|
||||
memcpy(data, &d, sizeof(uint8_t));
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int initDriveboard();
|
||||
void processDrivePacket(uint8_t *buf, int player);
|
||||
ssize_t driveboardRead(int fd, void *buf, size_t count);
|
||||
ssize_t driveboardWrite(int fd, const void *buf, size_t count);
|
||||
int driveBoardioctl(int fd, unsigned int request, void *data);
|
||||
@@ -1,131 +0,0 @@
|
||||
#include "string.h"
|
||||
|
||||
#include "driveboard.h"
|
||||
#include "jvs.h"
|
||||
|
||||
#define DRIVEBOARD_READY 0x00
|
||||
#define DRIVEBOARD_NOT_INIT 0x11
|
||||
#define DRIVEBOARD_BUSY 0x44
|
||||
|
||||
int wheelTest = 0;
|
||||
unsigned char response = DRIVEBOARD_READY;
|
||||
|
||||
double steerValue = 0.5;
|
||||
double force = 0.00;
|
||||
|
||||
int initDriveboard()
|
||||
{
|
||||
wheelTest = 0;
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
|
||||
setAnalogue(ANALOGUE_1, steerValue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t driveboardRead(int fd, void *buf, size_t count)
|
||||
{
|
||||
memcpy(buf, &response, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t driveboardWrite(int fd, const void *buf, size_t count)
|
||||
{
|
||||
|
||||
if (count != 4)
|
||||
{
|
||||
//printf("Error: Drive board count not what expected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char *buffer = (unsigned char *)buf;
|
||||
|
||||
switch (buffer[0])
|
||||
{
|
||||
case 0xFF:
|
||||
{
|
||||
//printf("Driveboard: Drive board reset\n");
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
{
|
||||
//printf("Driveboard: Drive board reset 2\n");
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFC:
|
||||
{
|
||||
//printf("Driveboard: Start wheel bounds testing\n");
|
||||
wheelTest = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
if (buffer[1] == 0 && buffer[2] == 0)
|
||||
{
|
||||
force = 0;
|
||||
}
|
||||
|
||||
if (buffer[1] == 1 && buffer[2] == 1)
|
||||
{
|
||||
|
||||
if (steerValue >= 0.9 && force > 0)
|
||||
break;
|
||||
|
||||
if (steerValue <= 0.1 && force < 0)
|
||||
break;
|
||||
|
||||
steerValue += force;
|
||||
setAnalogue(ANALOGUE_1, (int)(steerValue * 1024));
|
||||
}
|
||||
|
||||
//printf("Driveboard move %f %f\n", steerValue, force);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9e:
|
||||
case 0x84:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
|
||||
if (buffer[1] == 1)
|
||||
force = ((-1 * ((double)buffer[2] / 128.0)) * 2) / 100;
|
||||
|
||||
if (buffer[1] == 0)
|
||||
force = ((1 - ((double)buffer[2] / 128.0)) * 2) / 100;
|
||||
|
||||
//printf("Driveboard set force%f %f\n", steerValue, force);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFA:
|
||||
case 0xFD:
|
||||
{
|
||||
//printf("Driveboard: auto turn wheel mode\n");
|
||||
|
||||
if (wheelTest)
|
||||
{
|
||||
// printf("Increment wheel until 0.9 -> %d\n", (int)(steerValue * 255));
|
||||
steerValue += 0.09;
|
||||
setAnalogue(ANALOGUE_1, (int)(steerValue * 1024));
|
||||
response = DRIVEBOARD_BUSY;
|
||||
if (steerValue >= 0.9)
|
||||
{
|
||||
wheelTest = 0;
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//printf("Driveboard: Unknown command received %X\n", buffer[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "eeprom.h"
|
||||
#include "eeprom_settings.h"
|
||||
#include "eepromSettings.h"
|
||||
|
||||
#define I2C_SMBUS_BLOCK_MAX 32
|
||||
#define I2C_GET_FUNCTIONS 0x705
|
||||
@@ -96,7 +92,7 @@ int initEeprom()
|
||||
break;
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
if (strcmp(getConfig()->or2IP, "") != 0)
|
||||
if (getConfig()->enableNetworkPatches && strcmp(getConfig()->or2IP, "") != 0 && strcmp(getConfig()->or2Netmask, "") != 0)
|
||||
{
|
||||
if (setIP(eeprom, getConfig()->or2IP, getConfig()->or2Netmask) != 0)
|
||||
{
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "eeprom_settings.h"
|
||||
#include "eepromSettings.h"
|
||||
|
||||
uint32_t crc32_table[255];
|
||||
uint32_t crc32Table[255];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t offset;
|
||||
uint16_t size;
|
||||
} eepromOffsets;
|
||||
} EepromOffsets;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -21,9 +19,9 @@ typedef enum
|
||||
ETH1,
|
||||
CREDIT,
|
||||
BACKUP
|
||||
} eepromSection;
|
||||
} EepromSection;
|
||||
|
||||
eepromOffsets eepromOffsetTable[] = {
|
||||
EepromOffsets eepromOffsetTable[] = {
|
||||
{0x0000, 0x0020}, // amSysDataRecord_Static
|
||||
{0x00A0, 0x0010}, // amSysDataRecord_NetworkType
|
||||
{0x00B0, 0x0020}, // amSysDataRecord_Eth0
|
||||
@@ -33,7 +31,7 @@ eepromOffsets eepromOffsetTable[] = {
|
||||
|
||||
unsigned char eepromBuffer[512];
|
||||
|
||||
void build_crc32_table()
|
||||
void buildCrc32Table()
|
||||
{
|
||||
for (uint32_t i = 0; i < 256; i++)
|
||||
{
|
||||
@@ -47,23 +45,23 @@ void build_crc32_table()
|
||||
crc = crc ^ 0xEDB88320;
|
||||
ch >>= 1;
|
||||
}
|
||||
crc32_table[i] = crc;
|
||||
crc32Table[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t gen_crc(int section, size_t n)
|
||||
uint32_t genCrc(int section, size_t n)
|
||||
{
|
||||
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset + 4];
|
||||
unsigned long crc = 0xfffffffful;
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
crc = crc32_table[*buff++ ^ (crc & 0xff)] ^ (crc >> 8);
|
||||
crc = crc32Table[*buff++ ^ (crc & 0xff)] ^ (crc >> 8);
|
||||
return (crc);
|
||||
}
|
||||
|
||||
void addCRCtoBuffer(int section)
|
||||
{
|
||||
uint32_t crc = gen_crc(section, eepromOffsetTable[section].size - 4);
|
||||
uint32_t crc = genCrc(section, eepromOffsetTable[section].size - 4);
|
||||
memcpy(&eepromBuffer[eepromOffsetTable[section].offset], &crc, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
@@ -80,7 +78,7 @@ int checkCRCinBuffer(int section)
|
||||
{
|
||||
uint32_t crc;
|
||||
memcpy(&crc, &eepromBuffer[eepromOffsetTable[section].offset], 4);
|
||||
if (crc != gen_crc(section, eepromOffsetTable[section].size - 4))
|
||||
if (crc != genCrc(section, eepromOffsetTable[section].size - 4))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -244,7 +242,7 @@ int setIP(FILE *eeprom, char *ipAddress, char *netMask)
|
||||
|
||||
int eepromSettingsInit( FILE *eeprom)
|
||||
{
|
||||
build_crc32_table();
|
||||
buildCrc32Table();
|
||||
|
||||
eeprom = fopen("eeprom.bin", "r+b");
|
||||
if (eeprom == NULL)
|
||||
@@ -1,19 +1,28 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <linux/input.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "evdevinput.h"
|
||||
#include "evdevInput.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "jvs.h"
|
||||
#include "log.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
|
||||
int jvsBits = 10;
|
||||
|
||||
#define BITS_PER_LONG (sizeof(long) * 8)
|
||||
@@ -23,6 +32,9 @@ int jvsBits = 10;
|
||||
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
|
||||
#define NAME_ELEMENT(element) [element] = #element
|
||||
|
||||
int (*mySelect)(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
|
||||
struct timeval *restrict timeout) = NULL;
|
||||
|
||||
ArcadeInput arcadeInputs[] = {{"TEST_BUTTON", 0, BUTTON_TEST, 1},
|
||||
|
||||
{"PLAYER_1_BUTTON_START", 1, BUTTON_START, 1},
|
||||
@@ -79,9 +91,8 @@ static const char *const events[EV_MAX + 1] = {
|
||||
};
|
||||
|
||||
static const int maxval[EV_MAX + 1] = {
|
||||
[0 ... EV_MAX] = -1, [EV_SYN] = SYN_MAX, [EV_KEY] = KEY_MAX, [EV_REL] = REL_MAX,
|
||||
[EV_ABS] = ABS_MAX, [EV_MSC] = MSC_MAX, [EV_SW] = SW_MAX, [EV_LED] = LED_MAX,
|
||||
[EV_SND] = SND_MAX, [EV_REP] = REP_MAX, [EV_FF] = FF_MAX, [EV_FF_STATUS] = FF_STATUS_MAX,
|
||||
[0 ... EV_MAX] = -1, [EV_SYN] = SYN_MAX, [EV_KEY] = KEY_MAX, [EV_REL] = REL_MAX, [EV_ABS] = ABS_MAX, [EV_MSC] = MSC_MAX,
|
||||
[EV_SW] = SW_MAX, [EV_LED] = LED_MAX, [EV_SND] = SND_MAX, [EV_REP] = REP_MAX, [EV_FF] = FF_MAX, [EV_FF_STATUS] = FF_STATUS_MAX,
|
||||
};
|
||||
|
||||
static const char *const absolutes[ABS_MAX + 1] = {
|
||||
@@ -774,6 +785,7 @@ static const char *const *const names[EV_MAX + 1] = {
|
||||
[0 ... EV_MAX] = NULL,
|
||||
[EV_KEY] = keys,
|
||||
[EV_ABS] = absolutes,
|
||||
// [EV_REL] = relatives,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@@ -808,7 +820,7 @@ static int isEventDevice(const struct dirent *dir)
|
||||
return strncmp("event", dir->d_name, 5) == 0;
|
||||
}
|
||||
|
||||
ControllerStatus stopControllers(Controllers *controllers)
|
||||
ControllerStatus stopEvdevControllers(Controllers *controllers)
|
||||
{
|
||||
controllers->threadsRunning = 0;
|
||||
for (int i = 0; i < controllers->threadIndex; i++)
|
||||
@@ -825,21 +837,146 @@ void normaliseName(char *string)
|
||||
for (size_t j = 0; j < strlen(string); j++)
|
||||
{
|
||||
string[j] = toupper(string[j]);
|
||||
if (string[j] == ' ' || string[j] == '/' || string[j] == '(' || string[j] == ',' || string[j] == '=' ||
|
||||
string[j] == '-' || string[j] == ')')
|
||||
if (string[j] == ' ' || string[j] == '/' || string[j] == '(' || string[j] == ',' || string[j] == '=' || string[j] == '-' ||
|
||||
string[j] == ')')
|
||||
{
|
||||
string[j] = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ControllerStatus listControllers(Controllers *controllers)
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
struct ff_effect effect;
|
||||
} FFBThreadData;
|
||||
|
||||
void *ffbEffectThread(void *arg)
|
||||
{
|
||||
FFBThreadData *data = (FFBThreadData *)arg;
|
||||
int fd = data->fd;
|
||||
struct ff_effect effect = data->effect;
|
||||
|
||||
// Trigger the effect
|
||||
struct input_event ffb_event;
|
||||
ffb_event.type = EV_FF;
|
||||
ffb_event.code = effect.id;
|
||||
ffb_event.value = 1;
|
||||
|
||||
if (write(fd, &ffb_event, sizeof(ffb_event)) == -1)
|
||||
{
|
||||
perror("Failed to trigger FFB effect");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FFB effect triggered with ID %d\n", effect.id);
|
||||
|
||||
usleep(effect.replay.length * 1000);
|
||||
}
|
||||
|
||||
// Erase FFB effect
|
||||
if (ioctl(fd, EVIOCRMFF, effect.id) == -1)
|
||||
{
|
||||
perror("Failed to erase FFB effect");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FFB effect with ID %d erased\n", effect.id);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void initFFB(Controller *controller, uint16_t weak_magnitude, uint16_t strong_magnitude, uint16_t length)
|
||||
{
|
||||
if (!controller->hasFFB)
|
||||
{
|
||||
printf("Controller does not support FFB.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = open(controller->path, O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror("Failed to open FFB device");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ff_effect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
|
||||
// // Configure the FFB effect
|
||||
// effect.type = FF_RUMBLE;
|
||||
// effect.id = -1; // Let the system assign an ID
|
||||
// effect.u.rumble.weak_magnitude = weak_magnitude;
|
||||
// effect.u.rumble.strong_magnitude = strong_magnitude;
|
||||
// effect.replay.length = length; // Duration
|
||||
// effect.replay.delay = 0;
|
||||
|
||||
// Configure as periodic
|
||||
effect.type = FF_PERIODIC;
|
||||
effect.id = -1; // Let the system assign an ID
|
||||
effect.u.periodic.waveform = FF_SINE; // FF_SINE/FF_SQUARE/FF_TRIANGLE
|
||||
effect.u.periodic.magnitude = 0x100; // magnitude
|
||||
effect.u.periodic.offset = 0;
|
||||
effect.u.periodic.phase = 0;
|
||||
effect.u.periodic.period = 100; // period between bumps
|
||||
effect.u.periodic.envelope.attack_length = 100; // attack
|
||||
effect.u.periodic.envelope.attack_level = 0;
|
||||
effect.u.periodic.envelope.fade_length = 100; // fade
|
||||
effect.u.periodic.envelope.fade_level = 0;
|
||||
effect.replay.length = 500; // duration
|
||||
effect.replay.delay = 0; // No delay
|
||||
|
||||
// Upload the effect
|
||||
if (ioctl(fd, EVIOCSFF, &effect) == -1)
|
||||
{
|
||||
perror("Failed to upload FFB effect");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("FFB effect initialized with ID %d\n", effect.id);
|
||||
|
||||
// Prepare thread data
|
||||
FFBThreadData *threadData = malloc(sizeof(FFBThreadData));
|
||||
if (!threadData)
|
||||
{
|
||||
perror("Failed to allocate thread data");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
threadData->fd = fd;
|
||||
threadData->effect = effect;
|
||||
|
||||
// Start the thread
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, ffbEffectThread, threadData) != 0)
|
||||
{
|
||||
perror("Failed to create FFB thread");
|
||||
free(threadData);
|
||||
close(fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
|
||||
ControllerStatus loadEvdevControllers(Controllers *controllers)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
controllers->count = scandir("/dev/input", &namelist, isEventDevice, alphasort);
|
||||
int count = scandir("/dev/input", &namelist, isEventDevice, alphasort);
|
||||
if (count < 0)
|
||||
{
|
||||
return CONTROLLER_STATUS_ERROR;
|
||||
}
|
||||
controllers->count = count;
|
||||
|
||||
controllers->controller = malloc(sizeof(Controller) * controllers->count);
|
||||
if (controllers == NULL)
|
||||
if (controllers->controller == NULL)
|
||||
{
|
||||
for (int i = 0; i < controllers->count; i++)
|
||||
{
|
||||
@@ -851,8 +988,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
|
||||
for (int i = 0; i < controllers->count; i++)
|
||||
{
|
||||
snprintf(controllers->controller[i].path, sizeof(controllers->controller[i].path), "%s/%s", "/dev/input",
|
||||
namelist[i]->d_name);
|
||||
snprintf(controllers->controller[i].path, sizeof(controllers->controller[i].path), "%s/%s", "/dev/input", namelist[i]->d_name);
|
||||
free(namelist[i]);
|
||||
|
||||
controllers->controller[i].enabled = 0;
|
||||
@@ -889,8 +1025,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
}
|
||||
|
||||
// Get the controllers physical location
|
||||
ioctl(controller, EVIOCGPHYS(sizeof(controllers->controller[i].physicalLocation)),
|
||||
controllers->controller[i].physicalLocation);
|
||||
ioctl(controller, EVIOCGPHYS(sizeof(controllers->controller[i].physicalLocation)), controllers->controller[i].physicalLocation);
|
||||
for (size_t j = 0; j < strlen(controllers->controller[i].physicalLocation); j++)
|
||||
{
|
||||
if (controllers->controller[i].physicalLocation[j] == '/')
|
||||
@@ -912,14 +1047,14 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
{
|
||||
if (test_bit(code, bit[EV_KEY]))
|
||||
{
|
||||
if(controllers->controller[i].inputCount >= MAX_INPUTS) { // the number of input is limited
|
||||
fprintf(stderr, "warning, maximum number of inputs reached !\n");
|
||||
break;
|
||||
if (controllers->controller[i].inputCount >= MAX_INPUTS)
|
||||
{ // the number of input is limited
|
||||
fprintf(stderr, "warning, maximum number of inputs reached !\n");
|
||||
break;
|
||||
}
|
||||
|
||||
controllers->controller[i].enabled = 1;
|
||||
ControllerInput *controllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
ControllerInput *controllerInput = &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
controllerInput->evType = EV_KEY;
|
||||
controllerInput->evCode = code;
|
||||
controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
|
||||
@@ -944,8 +1079,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
if (test_bit(code, bit[EV_ABS]))
|
||||
{
|
||||
controllers->controller[i].enabled = 1;
|
||||
ControllerInput *controllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
ControllerInput *controllerInput = &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
controllerInput->evType = EV_ABS;
|
||||
controllerInput->evCode = code;
|
||||
controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
|
||||
@@ -962,8 +1096,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
// hum ok, truncated value
|
||||
}
|
||||
|
||||
ControllerInput *minControllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
ControllerInput *minControllerInput = &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
minControllerInput->evType = EV_ABS;
|
||||
minControllerInput->evCode = code;
|
||||
minControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MIN;
|
||||
@@ -979,8 +1112,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
// hum ok, truncated value
|
||||
}
|
||||
|
||||
ControllerInput *maxControllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
ControllerInput *maxControllerInput = &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
maxControllerInput->evType = EV_ABS;
|
||||
maxControllerInput->evCode = code;
|
||||
maxControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MAX;
|
||||
@@ -1012,10 +1144,31 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
ioctl(controller, EVIOCGABS(code), &absoluteFeatures);
|
||||
controllers->controller[i].absMin[code] = absoluteFeatures.minimum;
|
||||
controllers->controller[i].absMax[code] = absoluteFeatures.maximum;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// Test if Controller support FFB
|
||||
if (test_bit(EV_FF, bit[0]))
|
||||
{
|
||||
controllers->controller[i].hasFFB = 1;
|
||||
printf("FFB is available on controller %s\n", controllers->controller[i].name);
|
||||
|
||||
// FFB effect
|
||||
initFFB(&controllers->controller[i], 0xFFFF, 0xFFFF, 2000);
|
||||
|
||||
if (controllers->controller[i].ffbEffectId >= 0)
|
||||
{
|
||||
printf("FFB effect initialized with ID: %d\n", controllers->controller[i].ffbEffectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Failed to initialize FFB effect on controller %s\n", controllers->controller[i].name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
controllers->controller[i].hasFFB = 0;
|
||||
}
|
||||
|
||||
close(controller);
|
||||
}
|
||||
@@ -1028,8 +1181,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
for (int j = 0; j < controllers->count - 1 - i; j++)
|
||||
{
|
||||
Controller tmp;
|
||||
if (strcmp(controllers->controller[j].physicalLocation, controllers->controller[j + 1].physicalLocation) >
|
||||
0)
|
||||
if (strcmp(controllers->controller[j].physicalLocation, controllers->controller[j + 1].physicalLocation) > 0)
|
||||
{
|
||||
tmp = controllers->controller[j];
|
||||
controllers->controller[j] = controllers->controller[j + 1];
|
||||
@@ -1041,6 +1193,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
return CONTROLLER_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef COMPILING_LINDBERGH_ELF
|
||||
void *controllerThread(void *_args)
|
||||
{
|
||||
ThreadArguments *args = (ThreadArguments *)_args;
|
||||
@@ -1064,7 +1217,8 @@ void *controllerThread(void *_args)
|
||||
|
||||
args->controllers->threadsRunning = 1;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
analogue_deadzones[i].start_max = args->analogue_deadzone_start[i] / 100.0;
|
||||
analogue_deadzones[i].middle_min = 0.5 - (args->analogue_deadzone_middle[i] / 100.0);
|
||||
analogue_deadzones[i].middle_max = 0.5 + (args->analogue_deadzone_middle[i] / 100.0);
|
||||
@@ -1079,7 +1233,7 @@ void *controllerThread(void *_args)
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 2 * 1000;
|
||||
|
||||
if (select(fd + 1, &file_descriptor, NULL, NULL, &tv) < 1)
|
||||
if (mySelect(fd + 1, &file_descriptor, NULL, NULL, &tv) < 1)
|
||||
continue;
|
||||
|
||||
if (read(fd, &event, sizeof(event)) == sizeof(event))
|
||||
@@ -1087,100 +1241,167 @@ void *controllerThread(void *_args)
|
||||
switch (event.type)
|
||||
{
|
||||
|
||||
case EV_KEY:
|
||||
{
|
||||
if (!args->controller->keyTriggers[event.code].enabled)
|
||||
continue;
|
||||
|
||||
if (args->controller->keyTriggers[event.code].isAnalogue)
|
||||
case EV_KEY:
|
||||
{
|
||||
setAnalogue(args->controller->keyTriggers[event.code].channel,
|
||||
event.value == 0 ? 0 : 1 * (pow(2, jvsBits) - 1));
|
||||
continue;
|
||||
}
|
||||
if (!args->controller->keyTriggers[event.code].enabled)
|
||||
continue;
|
||||
|
||||
if(args->controller->keyTriggers[event.code].isCoin == 1) {
|
||||
if(event.value == 1)
|
||||
incrementCoin(args->controller->keyTriggers[event.code].player, 1);
|
||||
} else
|
||||
setSwitch(args->controller->keyTriggers[event.code].player,
|
||||
args->controller->keyTriggers[event.code].channel, event.value == 0 ? 0 : 1);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_ABS:
|
||||
{
|
||||
double scaled =
|
||||
((double)event.value - (double)args->controller->absMin[event.code]) /
|
||||
((double)args->controller->absMax[event.code] - (double)args->controller->absMin[event.code]);
|
||||
|
||||
if(args->controller->absTriggers[event.code].isNeg == 1) {
|
||||
scaled = 1.0 - scaled;
|
||||
}
|
||||
|
||||
if (args->controller->absTriggers[event.code].enabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].channel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue) {
|
||||
// Deadzone handling
|
||||
if (scaled < analogue_deadzones[channel].start_max)
|
||||
scaled = 0.0;
|
||||
if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max)
|
||||
scaled = 0.5;
|
||||
if (scaled > analogue_deadzones[channel].end_min)
|
||||
scaled = 1.0;
|
||||
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
|
||||
} else {
|
||||
setSwitch(args->controller->absTriggers[event.code].player,
|
||||
args->controller->absTriggers[event.code].channel, scaled < 0.8 ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(event.value <= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2)) {
|
||||
if (args->controller->absTriggers[event.code].minEnabled)
|
||||
if (args->controller->keyTriggers[event.code].isAnalogue)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].minChannel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue) {
|
||||
setAnalogue(channel, scaled < 0.2 ? 0.0 : pow(2, jvsBits) * 1.0);
|
||||
} else {
|
||||
setSwitch(args->controller->absTriggers[event.code].minPlayer,
|
||||
args->controller->absTriggers[event.code].minChannel, scaled < 0.2 ? 1 : 0);
|
||||
}
|
||||
setAnalogue(args->controller->keyTriggers[event.code].channel, event.value == 0 ? 0 : 1 * (pow(2, jvsBits) - 1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(event.value >= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2)) {
|
||||
if (args->controller->absTriggers[event.code].maxEnabled)
|
||||
if (args->controller->keyTriggers[event.code].isCoin == 1)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].maxChannel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue) {
|
||||
setAnalogue(channel, scaled > 0.8 ? pow(2, jvsBits) : 0.0);
|
||||
} else {
|
||||
setSwitch(args->controller->absTriggers[event.code].maxPlayer,
|
||||
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8 ? 1 : 0);
|
||||
}
|
||||
if (event.value == 1)
|
||||
incrementCoin(args->controller->keyTriggers[event.code].player, 1);
|
||||
}
|
||||
else
|
||||
setSwitch(args->controller->keyTriggers[event.code].player, args->controller->keyTriggers[event.code].channel,
|
||||
event.value == 0 ? 0 : 1);
|
||||
}
|
||||
if (args->controller->absTriggers[event.code].shakeEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].shakeChannel;
|
||||
if( (scaled > args->controller->absTriggers[event.code].shakePreviousScaled && scaled - args->controller->absTriggers[event.code].shakePreviousScaled > 0.1) ||
|
||||
(scaled < args->controller->absTriggers[event.code].shakePreviousScaled && -scaled + args->controller->absTriggers[event.code].shakePreviousScaled > 0.1)) {
|
||||
setAnalogue(channel, 0);
|
||||
} else {
|
||||
setAnalogue(channel, pow(2, jvsBits) / 2);
|
||||
}
|
||||
args->controller->absTriggers[event.code].shakePreviousScaled = scaled;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
case EV_ABS:
|
||||
{
|
||||
double scaled = ((double)event.value - (double)args->controller->absMin[event.code]) /
|
||||
((double)args->controller->absMax[event.code] - (double)args->controller->absMin[event.code]);
|
||||
|
||||
if (args->controller->absTriggers[event.code].isNeg == 1)
|
||||
{
|
||||
scaled = 1.0 - scaled;
|
||||
}
|
||||
|
||||
if (args->controller->absTriggers[event.code].enabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].channel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue)
|
||||
{
|
||||
// Deadzone handling
|
||||
if (scaled < analogue_deadzones[channel].start_max)
|
||||
scaled = 0.0;
|
||||
if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max)
|
||||
scaled = 0.5;
|
||||
if (scaled > analogue_deadzones[channel].end_min)
|
||||
scaled = 1.0;
|
||||
|
||||
// uint32_t gId = getConfig()->crc32;
|
||||
|
||||
if (channel == ANALOGUE_1 || channel == ANALOGUE_2)
|
||||
{
|
||||
args->controller->lastAnalogueValue[channel] = scaled;
|
||||
|
||||
double x_pos = (channel == ANALOGUE_1) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_1];
|
||||
double y_pos = (channel == ANALOGUE_2) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_2];
|
||||
|
||||
if (x_pos <= 0.01 || x_pos >= 0.99 || y_pos <= 0.01 || y_pos >= 0.99)
|
||||
{
|
||||
// log_info("Off-screen detected - X: %f, Y: %f", x_pos, y_pos);
|
||||
switch (gId)
|
||||
{
|
||||
case GHOST_SQUAD_EVOLUTION:
|
||||
// log_info("GSE: Triggering reload via BUTTON_2");
|
||||
setSwitch(1, BUTTON_2, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setSwitch(1, BUTTON_2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(channel == ANALOGUE_1 && (gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP || gGrp == GROUP_ID5))
|
||||
scaled = (scaled - 0.5f) * 0.35f + 0.5f;
|
||||
|
||||
if (channel == ANALOGUE_1 || channel == ANALOGUE_2)
|
||||
{
|
||||
args->controller->lastAnalogueValue[channel] = scaled;
|
||||
|
||||
double x_pos = (channel == ANALOGUE_1) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_1];
|
||||
double y_pos = (channel == ANALOGUE_2) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_2];
|
||||
|
||||
updateCrosshairPosition(0, x_pos, y_pos);
|
||||
}
|
||||
|
||||
if (channel == ANALOGUE_3 || channel == ANALOGUE_4)
|
||||
{
|
||||
args->controller->lastAnalogueValue[channel] = scaled;
|
||||
|
||||
double x_pos = (channel == ANALOGUE_3) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_3];
|
||||
double y_pos = (channel == ANALOGUE_4) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_4];
|
||||
|
||||
updateCrosshairPosition(1, x_pos, y_pos);
|
||||
}
|
||||
|
||||
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
setSwitch(args->controller->absTriggers[event.code].player, args->controller->absTriggers[event.code].channel,
|
||||
scaled < 0.8 ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.value <= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2))
|
||||
{
|
||||
if (args->controller->absTriggers[event.code].minEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].minChannel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue)
|
||||
{
|
||||
setAnalogue(channel, scaled < 0.2 ? 0.0 : pow(2, jvsBits) * 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSwitch(args->controller->absTriggers[event.code].minPlayer,
|
||||
args->controller->absTriggers[event.code].minChannel, scaled < 0.2 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.value >= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2))
|
||||
{
|
||||
if (args->controller->absTriggers[event.code].maxEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].maxChannel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue)
|
||||
{
|
||||
setAnalogue(channel, scaled > 0.8 ? pow(2, jvsBits) : 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSwitch(args->controller->absTriggers[event.code].maxPlayer,
|
||||
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args->controller->absTriggers[event.code].shakeEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].shakeChannel;
|
||||
if ((scaled > args->controller->absTriggers[event.code].shakePreviousScaled &&
|
||||
scaled - args->controller->absTriggers[event.code].shakePreviousScaled > 0.1) ||
|
||||
(scaled < args->controller->absTriggers[event.code].shakePreviousScaled &&
|
||||
-scaled + args->controller->absTriggers[event.code].shakePreviousScaled > 0.1))
|
||||
{
|
||||
setAnalogue(channel, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAnalogue(channel, pow(2, jvsBits) / 2);
|
||||
}
|
||||
args->controller->absTriggers[event.code].shakePreviousScaled = scaled;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1313,11 +1534,6 @@ ControllerStatus getArcadeInputByName(char *name, ArcadeInput *input)
|
||||
|
||||
ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
{
|
||||
// Don't start EVDEV threads if SDL/X11-only mode is enabled
|
||||
if (getConfig()->inputMode == 1) {
|
||||
return CONTROLLER_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < controllers->count; i++)
|
||||
{
|
||||
if (!controllers->controller[i].enabled)
|
||||
@@ -1330,7 +1546,7 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
controllers->controller[i].absTriggers[j].maxEnabled = 0;
|
||||
controllers->controller[i].absTriggers[j].shakeEnabled = 0;
|
||||
controllers->controller[i].absTriggers[j].isNeg = 0;
|
||||
controllers->controller[i].absTriggers[j].isAnalogue = 1;
|
||||
controllers->controller[i].absTriggers[j].isAnalogue = 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < KEY_MAX; j++)
|
||||
@@ -1345,17 +1561,20 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
char *mapping = getMapping(controllers->controller[i].inputs[j].inputName);
|
||||
int negabs = 0;
|
||||
|
||||
if (mapping == NULL) {
|
||||
// give a 2nd chance with a techninal mapping
|
||||
mapping = getMapping(controllers->controller[i].inputs[j].inputTechName);
|
||||
if (mapping == NULL) {
|
||||
// give a 3rd change with negativ technical mapping
|
||||
mapping = getMapping(controllers->controller[i].inputs[j].inputTechNegName);
|
||||
negabs = 1;
|
||||
if (mapping == NULL) {
|
||||
continue;
|
||||
if (mapping == NULL)
|
||||
{
|
||||
// give a 2nd chance with a techninal mapping
|
||||
mapping = getMapping(controllers->controller[i].inputs[j].inputTechName);
|
||||
if (mapping == NULL)
|
||||
{
|
||||
// give a 3rd change with negativ technical mapping
|
||||
mapping = getMapping(controllers->controller[i].inputs[j].inputTechNegName);
|
||||
negabs = 1;
|
||||
if (mapping == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArcadeInput input = {0};
|
||||
@@ -1368,102 +1587,92 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
|
||||
switch (controllers->controller[i].inputs[j].evType)
|
||||
{
|
||||
case EV_ABS:
|
||||
{
|
||||
switch (controllers->controller[i].inputs[j].specialFunction)
|
||||
case EV_ABS:
|
||||
{
|
||||
case NO_SPECIAL_FUNCTION:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].channel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].player =
|
||||
input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
switch (controllers->controller[i].inputs[j].specialFunction)
|
||||
{
|
||||
case NO_SPECIAL_FUNCTION:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].channel = input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name, input.name,
|
||||
SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].player = input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ANALOGUE_TO_DIGITAL_MAX:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxChannel = input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName, input.name,
|
||||
SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxPlayer = input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ANALOGUE_TO_DIGITAL_MIN:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minChannel = input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName, input.name,
|
||||
SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minPlayer = input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ANALOGUE_SHAKE:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeChannel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakePlayer = input.player;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ANALOGUE_TO_DIGITAL_MAX:
|
||||
case EV_KEY:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxChannel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxPlayer =
|
||||
input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].channel = input.channel;
|
||||
strncpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name, input.name, SIZE);
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].player = input.player;
|
||||
|
||||
case ANALOGUE_TO_DIGITAL_MIN:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minChannel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minPlayer =
|
||||
input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
if (strstr(input.name, "ANALOGUE") != NULL)
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 1;
|
||||
|
||||
case ANALOGUE_SHAKE:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeChannel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakePlayer =
|
||||
input.player;
|
||||
if (strcmp(input.name, "PLAYER_1_COIN") == 0)
|
||||
{
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isCoin = 1;
|
||||
}
|
||||
|
||||
if (strcmp(input.name, "PLAYER_2_COIN") == 0)
|
||||
{
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isCoin = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_KEY:
|
||||
{
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].channel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].player =
|
||||
input.player;
|
||||
|
||||
if (strstr(input.name, "ANALOGUE") != NULL)
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 1;
|
||||
|
||||
if (strcmp(input.name, "PLAYER_1_COIN") == 0)
|
||||
{
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isCoin = 1;
|
||||
printf("Warining: Unknown mapping type\n");
|
||||
}
|
||||
|
||||
if (strcmp(input.name, "PLAYER_2_COIN") == 0)
|
||||
{
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isCoin = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
printf("Warining: Unknown mapping type\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1485,8 +1694,10 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
ThreadArguments *args = malloc(sizeof(ThreadArguments));
|
||||
args->controller = &controllers->controller[i];
|
||||
args->controllers = controllers;
|
||||
memcpy(args->analogue_deadzone_start, config->arcadeInputs.analogue_deadzone_start, sizeof(config->arcadeInputs.analogue_deadzone_start));
|
||||
memcpy(args->analogue_deadzone_middle, config->arcadeInputs.analogue_deadzone_middle, sizeof(config->arcadeInputs.analogue_deadzone_middle));
|
||||
memcpy(args->analogue_deadzone_start, config->arcadeInputs.analogue_deadzone_start,
|
||||
sizeof(config->arcadeInputs.analogue_deadzone_start));
|
||||
memcpy(args->analogue_deadzone_middle, config->arcadeInputs.analogue_deadzone_middle,
|
||||
sizeof(config->arcadeInputs.analogue_deadzone_middle));
|
||||
memcpy(args->analogue_deadzone_end, config->arcadeInputs.analogue_deadzone_end, sizeof(config->arcadeInputs.analogue_deadzone_end));
|
||||
pthread_create(&controllers->thread[controllers->threadIndex++], NULL, controllerThread, args);
|
||||
controllers->controller[i].inUse = 1;
|
||||
@@ -1495,21 +1706,30 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
return CONTROLLER_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ControllerStatus initControllers(Controllers *controllers)
|
||||
#endif
|
||||
ControllerStatus initEvdevControllers(Controllers *controllers)
|
||||
{
|
||||
// Don't start EVDEV if SDL mode is enabled
|
||||
if (getConfig()->inputMode == 1)
|
||||
{
|
||||
return CONTROLLER_STATUS_SUCCESS;
|
||||
}
|
||||
mySelect = dlsym(RTLD_NEXT, "select");
|
||||
|
||||
if (loadEvdevControllers(controllers) != CONTROLLER_STATUS_SUCCESS)
|
||||
{
|
||||
return CONTROLLER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
jvsBits = getJVSIO()->capabilities.analogueInBits;
|
||||
|
||||
controllers->threadIndex = 0;
|
||||
|
||||
if (listControllers(controllers) != CONTROLLER_STATUS_SUCCESS)
|
||||
{
|
||||
return CONTROLLER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
#ifndef COMPILING_LINDBERGH_ELF
|
||||
if (startControllerThreads(controllers) != CONTROLLER_STATUS_SUCCESS)
|
||||
{
|
||||
return CONTROLLER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
return CONTROLLER_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -82,6 +82,9 @@ typedef struct
|
||||
|
||||
int enabled;
|
||||
int inUse;
|
||||
int hasFFB;
|
||||
int ffbEffectId;
|
||||
double lastAnalogueValue[8];
|
||||
|
||||
} Controller;
|
||||
|
||||
@@ -95,6 +98,6 @@ typedef struct
|
||||
int threadsRunning;
|
||||
} Controllers;
|
||||
|
||||
ControllerStatus initControllers(Controllers *controllers);
|
||||
ControllerStatus stopControllers(Controllers *controllers);
|
||||
|
||||
ControllerStatus initEvdevControllers(Controllers *controllers);
|
||||
ControllerStatus stopEvdevControllers(Controllers *controllers);
|
||||
ControllerStatus loadEvdevControllers(Controllers *controllers);
|
||||
@@ -0,0 +1,268 @@
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "flowControl.h"
|
||||
#include "log.h"
|
||||
|
||||
void setVariable(size_t address, size_t value)
|
||||
{
|
||||
int pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
size_t *variable = (size_t *)address;
|
||||
|
||||
void *toModify = (void *)(address - (address % pagesize));
|
||||
|
||||
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
|
||||
if (prot != 0)
|
||||
{
|
||||
log_error("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
|
||||
return;
|
||||
}
|
||||
*variable = value;
|
||||
}
|
||||
|
||||
void patchMemoryFromString(size_t address, char *value)
|
||||
{
|
||||
size_t size = strlen((void *)value);
|
||||
if (size % 2 != 0)
|
||||
{
|
||||
log_error("Patch sting len should be even.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char buf[size / 2];
|
||||
char tmpchr[3];
|
||||
char *p = value;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
memcpy(tmpchr, p, 2);
|
||||
tmpchr[2] = '\0';
|
||||
buf[i] = (int)strtol(tmpchr, NULL, 16);
|
||||
p += 2;
|
||||
}
|
||||
|
||||
int pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
void *toModify = (void *)(address - (address % pagesize));
|
||||
|
||||
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
|
||||
if (prot != 0)
|
||||
{
|
||||
log_error("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy((void *)address, buf, size / 2);
|
||||
}
|
||||
|
||||
void detourFunction(size_t address, void *function)
|
||||
{
|
||||
int pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
void *toModify = (void *)(address - (address % pagesize));
|
||||
|
||||
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
|
||||
if (prot != 0)
|
||||
{
|
||||
log_error("Error: Cannot detour memory region to change variable (%d)\n", prot);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t jumpAddress = (function - (void *)address) - 5;
|
||||
|
||||
// Build the assembly to make the function jump
|
||||
char cave[5] = {0xE9, 0x00, 0x00, 0x00, 0x00};
|
||||
cave[4] = (jumpAddress >> (8 * 3)) & 0xFF;
|
||||
cave[3] = (jumpAddress >> (8 * 2)) & 0xFF;
|
||||
cave[2] = (jumpAddress >> (8 * 1)) & 0xFF;
|
||||
cave[1] = (jumpAddress) & 0xFF;
|
||||
|
||||
memcpy((void *)address, cave, 5);
|
||||
}
|
||||
|
||||
void replaceCallAtAddress(size_t address, void *function)
|
||||
{
|
||||
uint32_t offset = function - (void *)(address)-5;
|
||||
|
||||
int pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
void *toModify = (void *)(address - (address % pagesize));
|
||||
|
||||
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
|
||||
if (prot != 0)
|
||||
{
|
||||
log_error("Error: Cannot detour memory region to change variable (%d)\n", prot);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t callAddress = (function - (void *)address) - 5;
|
||||
|
||||
char cave[5] = {0xE8, 0x00, 0x00, 0x00, 0x00};
|
||||
cave[4] = (callAddress >> (8 * 3)) & 0xFF;
|
||||
cave[3] = (callAddress >> (8 * 2)) & 0xFF;
|
||||
cave[2] = (callAddress >> (8 * 1)) & 0xFF;
|
||||
cave[1] = (callAddress) & 0xFF;
|
||||
|
||||
memcpy((void *)address, cave, 5);
|
||||
}
|
||||
|
||||
int stubRetZero()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stubReturn()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int stubRetOne()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int stubRetMinusOne()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char stubRetZeroChar()
|
||||
{
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static size_t pageAlignDown(size_t addr)
|
||||
{
|
||||
size_t pagesize = sysconf(_SC_PAGESIZE);
|
||||
return addr & ~(pagesize - 1);
|
||||
}
|
||||
|
||||
int patchMemory(void *address, const void *data, size_t size)
|
||||
{
|
||||
size_t page_start = pageAlignDown((size_t)address);
|
||||
size_t page_size = size + ((size_t)address - page_start);
|
||||
|
||||
if (mprotect((void *)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
|
||||
{
|
||||
log_error("Error in mprotect.");
|
||||
return -1;
|
||||
}
|
||||
memcpy(address, data, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *trampolineHook(void *target, void *replacement, size_t saveSize)
|
||||
{
|
||||
void *trampoline = mmap(NULL, saveSize + 5, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (trampoline == MAP_FAILED)
|
||||
{
|
||||
perror("mmap");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uintptr_t src = (uintptr_t)target;
|
||||
uintptr_t dst = (uintptr_t)replacement;
|
||||
|
||||
// Save original bytes
|
||||
memcpy(trampoline, target, saveSize);
|
||||
|
||||
// Append jmp back after saved bytes
|
||||
((uint8_t *)trampoline)[saveSize] = 0xE9;
|
||||
uintptr_t jump_from = (uintptr_t)trampoline + saveSize;
|
||||
uintptr_t jump_to = src + saveSize;
|
||||
*(uint32_t *)((uint8_t *)trampoline + saveSize + 1) = (uint32_t)(jump_to - (jump_from + 5));
|
||||
|
||||
// Patch original
|
||||
uint8_t jmp[5] = {0xE9};
|
||||
*(uint32_t *)&jmp[1] = (uint32_t)(dst - src - 5);
|
||||
if (patchMemory(target, jmp, sizeof(jmp)) != 0)
|
||||
{
|
||||
log_error("Error patching original function at %p\n", target);
|
||||
}
|
||||
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
uintptr_t getBaseAddress()
|
||||
{
|
||||
FILE *fp = fopen("/proc/self/maps", "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
uint32_t base = 0;
|
||||
fscanf(fp, "%x-", &base);
|
||||
fclose(fp);
|
||||
return base;
|
||||
}
|
||||
|
||||
void *findStaticFnAddr(const char *functionName)
|
||||
{
|
||||
int fd = open("/proc/self/exe", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
{
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
if (data == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
Elf32_Ehdr *ehdr = (Elf32_Ehdr *)data;
|
||||
Elf32_Shdr *shdrs = (Elf32_Shdr *)(data + ehdr->e_shoff);
|
||||
const char *shstrtab = (const char *)(data + shdrs[ehdr->e_shstrndx].sh_offset);
|
||||
|
||||
Elf32_Shdr *symtabHdr = NULL, *strtabHdr = NULL;
|
||||
|
||||
// Locate .symtab and .strtab
|
||||
for (int i = 0; i < ehdr->e_shnum; i++)
|
||||
{
|
||||
const char *secname = shstrtab + shdrs[i].sh_name;
|
||||
if (strcmp(secname, ".symtab") == 0)
|
||||
{
|
||||
symtabHdr = &shdrs[i];
|
||||
}
|
||||
else if (strcmp(secname, ".strtab") == 0)
|
||||
{
|
||||
strtabHdr = &shdrs[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!symtabHdr || !strtabHdr)
|
||||
{
|
||||
munmap(data, st.st_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if the binary is PIE (ET_DYN)
|
||||
int isPie = (ehdr->e_type == ET_DYN);
|
||||
uintptr_t base = isPie ? getBaseAddress() : 0;
|
||||
|
||||
Elf32_Sym *symtab = (Elf32_Sym *)(data + symtabHdr->sh_offset);
|
||||
const char *strtab = (const char *)(data + strtabHdr->sh_offset);
|
||||
int numSyms = symtabHdr->sh_size / sizeof(Elf32_Sym);
|
||||
|
||||
for (int i = 0; i < numSyms; i++)
|
||||
{
|
||||
const char *sym_name = strtab + symtab[i].st_name;
|
||||
if (strcmp(sym_name, functionName) == 0)
|
||||
{
|
||||
uintptr_t addr = symtab[i].st_value;
|
||||
munmap(data, st.st_size);
|
||||
return (void *)(base + addr);
|
||||
}
|
||||
}
|
||||
|
||||
munmap(data, st.st_size);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void setVariable(size_t address, size_t value);
|
||||
void patchMemoryFromString(size_t address, char *value);
|
||||
void detourFunction(size_t address, void *function);
|
||||
void replaceCallAtAddress(size_t address, void *function);
|
||||
void stubReturn();
|
||||
int stubRetZero();
|
||||
int stubRetOne();
|
||||
int stubRetMinusOne();
|
||||
char stubRetZeroChar();
|
||||
void *trampolineHook(void *target, void *replacement, size_t saveSize);
|
||||
void *findStaticFnAddr(const char *functionName);
|
||||
@@ -0,0 +1,196 @@
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_haptic.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "forceFeedback.h"
|
||||
#include "sdlInput.h"
|
||||
|
||||
static int effect_id = -1;
|
||||
extern SDLControllers sdlJoysticks;
|
||||
|
||||
void sdlFfbInit(void)
|
||||
{
|
||||
int i, num_haptics;
|
||||
SDL_HapticID *haptics = SDL_GetHaptics(&num_haptics);
|
||||
if (haptics)
|
||||
{
|
||||
for (i = 0; i < num_haptics; ++i)
|
||||
{
|
||||
SDL_HapticID instance_id = haptics[i];
|
||||
SDL_Log("Haptic %" SDL_PRIu32 ": %s", instance_id, SDL_GetHapticNameForID(instance_id));
|
||||
sdlJoysticks.haptics[i] = SDL_OpenHaptic(instance_id);
|
||||
sdlFfbRumble(1.0, 1.0, 200);
|
||||
}
|
||||
|
||||
SDL_free(haptics);
|
||||
}
|
||||
else {
|
||||
SDL_Log("No haptic support!");
|
||||
}
|
||||
|
||||
// int num_joysticks;
|
||||
// SDL_JoystickID *joystics = SDL_GetJoysticks(&num_joysticks);
|
||||
// if (joystics)
|
||||
// {
|
||||
// SDL_JoystickID instance_id;
|
||||
// for (int i = 0; i < num_joysticks; ++i)
|
||||
// {
|
||||
// instance_id = joystics[i];
|
||||
// // sdlJoysticks.haptics[i] = SDL_HapticOpenFromJoystick(sdlJoysticks.joysticks[i]);
|
||||
// // sdlJoysticks.haptics[i] = SDL_OpenHapticFromJoystick(SDL_GetGamepadJoystick(sdlJoysticks.controllers[i]));
|
||||
// sdlJoysticks.haptics[i] = SDL_OpenHaptic(instance_id);
|
||||
// printf("JoyName: %s\n", SDL_GetJoystickNameForID(instance_id));
|
||||
// if (sdlJoysticks.haptics[i] && SDL_GetHapticFeatures(sdlJoysticks.haptics[i]) & SDL_HAPTIC_LEFTRIGHT)
|
||||
// {
|
||||
// printf("Haptic initialized!\n");
|
||||
// sdlFfbRumble(1.0, 1.0, 200);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// printf("No haptic support!\n");
|
||||
// sdlJoysticks.haptics[i] = NULL;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void sdlFfbRumble(float left, float right, int duration_ms)
|
||||
{
|
||||
if (!sdlJoysticks.haptics[0])
|
||||
return;
|
||||
SDL_HapticEffect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||
effect.leftright.length = duration_ms;
|
||||
effect.leftright.large_magnitude = (Uint16)(left * 0xFFFF);
|
||||
effect.leftright.small_magnitude = (Uint16)(right * 0xFFFF);
|
||||
if (effect_id >= 0)
|
||||
SDL_DestroyHapticEffect(sdlJoysticks.haptics[0], effect_id);
|
||||
effect_id = SDL_CreateHapticEffect(sdlJoysticks.haptics[0], &effect);
|
||||
SDL_RunHapticEffect(sdlJoysticks.haptics[0], effect_id, 1);
|
||||
}
|
||||
|
||||
void sdlFfbShutdown(void)
|
||||
{
|
||||
if (sdlJoysticks.haptics[0])
|
||||
{
|
||||
SDL_CloseHaptic(sdlJoysticks.haptics[0]);
|
||||
sdlJoysticks.haptics[0] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void sdlFfbDriveboard(const unsigned char *buffer, size_t count)
|
||||
{
|
||||
|
||||
// printf("FFB driveboard: count=%zu, data:", count);
|
||||
// for (size_t i = 0; i < count; ++i) {
|
||||
// printf(" 0x%02x", buffer[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
|
||||
switch (buffer[0])
|
||||
{
|
||||
case 0x80:
|
||||
{ // power
|
||||
if (buffer[2] == 0x01)
|
||||
{
|
||||
printf("0x80 command: Triggering full-power rumble\n");
|
||||
// ffb_rumble(1.0f, 1.0f, 500);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x85:
|
||||
{ // Rumble/vibrate
|
||||
uint8_t speed = buffer[1];
|
||||
uint8_t power = buffer[2];
|
||||
|
||||
float force = (float)power / 63.0f; // Map power 1-63 to 0.0-1.0
|
||||
|
||||
int duration = 100;
|
||||
printf("Triggering rumble: force=%.2f, duration=%dms\n", force, duration);
|
||||
sdlFfbRumble(force, force, duration);
|
||||
break;
|
||||
}
|
||||
case 0x84:
|
||||
{ // Movement command
|
||||
uint8_t direction = buffer[1];
|
||||
uint8_t value = buffer[2];
|
||||
|
||||
float left = 0.0f, right = 0.0f;
|
||||
int duration = 100; // ms
|
||||
|
||||
if (direction == 0x00)
|
||||
{ // right - value from 0x7F (min) to 0x01 (max)
|
||||
|
||||
right = (127.0f - (float)value) / 127.0f;
|
||||
}
|
||||
else if (direction == 0x01)
|
||||
{ // left - value goes from 0x00 (min) to 0x7F (max)
|
||||
|
||||
left = (float)value / 127.0f;
|
||||
}
|
||||
|
||||
printf("Triggering movement rumble: left=%.2f, right=%.2f, duration=%dms\n", left, right, duration);
|
||||
// sdlFfbRumble(left, right, duration);
|
||||
break;
|
||||
}
|
||||
case 0x86: // Friction
|
||||
printf("Friction:");
|
||||
printf(" Power: %d", buffer[1]);
|
||||
printf(" / Percentage: %d\n", buffer[2]);
|
||||
break;
|
||||
case 0x8:
|
||||
break;
|
||||
case 0x87: // move and set target point
|
||||
printf("Move and set target point\n");
|
||||
break;
|
||||
case 0x88: // move to current target point
|
||||
printf("Move to current target point\n");
|
||||
break;
|
||||
case 0x8B: // centering strength
|
||||
printf("Centering strength:");
|
||||
printf(" param1: %d", buffer[1]);
|
||||
printf(" / param2: %d\n", buffer[2]);
|
||||
break;
|
||||
case 0xFB:
|
||||
{ // Playback sud package
|
||||
|
||||
uint8_t effect = buffer[2];
|
||||
printf("Outrun FFB 0xFB effect: 0x%02x\n", effect);
|
||||
|
||||
if (effect == 0x02)
|
||||
{
|
||||
sdlFfbRumble(1.0f, 1.0f, 120);
|
||||
}
|
||||
else if (effect == 0x10 || effect == 0x0B || effect == 0x04)
|
||||
{
|
||||
sdlFfbRumble(0.0f, 1.0f, 120);
|
||||
}
|
||||
else if (effect == 0x00 || effect == 0x1B || effect == 0x14)
|
||||
{
|
||||
sdlFfbRumble(1.0f, 0.0f, 120);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ffb_output(const unsigned char *buffer, size_t count)
|
||||
{
|
||||
/* printf("FFB output: count=%zu, data:", count);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
printf(" 0x%02x", buffer[i]);
|
||||
printf("\n"); */
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
if (buffer[0] & 0x40)
|
||||
{
|
||||
// printf("GPO: ABC Vibration triggered (bit 6 set)\n");
|
||||
// sdlFfbRumble(1.0f, 1.0f, 120);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef FFB_H
|
||||
#define FFB_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void sdlFfbInit(void);
|
||||
void sdlFfbDhutdown(void);
|
||||
void sdlFfbRumble(float left, float right, int duration_ms);
|
||||
void sdlFfbDriveboard(const unsigned char *buffer, size_t count);
|
||||
void sdlFfbOutput(const unsigned char *buffer, size_t count);
|
||||
|
||||
#endif
|
||||
@@ -1,14 +1,24 @@
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fps_limiter.h"
|
||||
#include "config.h"
|
||||
#include "fpsLimiter.h"
|
||||
|
||||
fps_limit fpsLimit;
|
||||
FpsLimit fpsLimit;
|
||||
double lastTime = 0.0;
|
||||
int frameCount = 0;
|
||||
double fps = 0.0;
|
||||
|
||||
void initFpsLimiter()
|
||||
{
|
||||
if (getConfig()->fpsLimiter == 1)
|
||||
{
|
||||
fpsLimit.targetFrameTime = 1000000 / getConfig()->fpsTarget;
|
||||
fpsLimit.frameEnd = clockNow();
|
||||
}
|
||||
}
|
||||
|
||||
double getTimeInMilliseconds()
|
||||
{
|
||||
struct timeval time;
|
||||
@@ -37,14 +47,21 @@ double calculateFps()
|
||||
return fps;
|
||||
}
|
||||
|
||||
long Clock_now()
|
||||
long clockNow()
|
||||
{
|
||||
struct timeval time_now;
|
||||
gettimeofday(&time_now, NULL);
|
||||
return time_now.tv_sec * 1000000L + time_now.tv_usec;
|
||||
}
|
||||
|
||||
void FpsLimiter(fps_limit *stats)
|
||||
void frameTiming()
|
||||
{
|
||||
fpsLimit.frameStart = clockNow();
|
||||
fpsLimiter(&fpsLimit);
|
||||
fpsLimit.frameEnd = clockNow();
|
||||
}
|
||||
|
||||
void fpsLimiter(FpsLimit *stats)
|
||||
{
|
||||
stats->sleepTime = stats->targetFrameTime - (stats->frameStart - stats->frameEnd);
|
||||
|
||||
@@ -54,7 +71,7 @@ void FpsLimiter(fps_limit *stats)
|
||||
|
||||
usleep(adjustedSleep);
|
||||
|
||||
stats->frameOverhead = (Clock_now() - stats->frameStart) - adjustedSleep;
|
||||
stats->frameOverhead = (clockNow() - stats->frameStart) - adjustedSleep;
|
||||
|
||||
if (stats->frameOverhead > stats->targetFrameTime / 2)
|
||||
{
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef FPS_LIMITER_H
|
||||
#define FPS_LIMITER_H
|
||||
#pragma once
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -8,10 +7,10 @@ typedef struct
|
||||
long frameEnd; // End time of the frame
|
||||
long sleepTime; // Sleep time to maintain the frame rate
|
||||
long frameOverhead; // Overhead time in the frame
|
||||
} fps_limit;
|
||||
} FpsLimit;
|
||||
|
||||
long Clock_now();
|
||||
void FpsLimiter(fps_limit *stats);
|
||||
void initFpsLimiter();
|
||||
long clockNow();
|
||||
void fpsLimiter(FpsLimit *stats);
|
||||
double calculateFps();
|
||||
|
||||
#endif
|
||||
void frameTiming();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
void loadSDLControllerMappings();
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "gameData.h"
|
||||
#include "config.h"
|
||||
|
||||
static const GameData gameData[] = {
|
||||
{AFTER_BURNER_CLIMAX, "After Burner Climax", "abc", "DVP-0009", "SBLR", "2006", "640x480", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{AFTER_BURNER_CLIMAX_REVA, "After Burner Climax Rev A", "abc-rev-a", "DVP-0009A", "SBLR", "2006", "640x480", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{AFTER_BURNER_CLIMAX_REVB, "After Burner Climax Rev B", "abc-rev-b", "DVP-0009B", "SBLR", "2006", "640x480", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{AFTER_BURNER_CLIMAX_SDX, "After Burner Climax SDX", "abc-sdx", "DVP-0018", "SBMN", "2006 ?", "640x480", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{AFTER_BURNER_CLIMAX_SDX_REVA, "After Burner Climax SDX Rev A", "abc-sdx-rev-a", "DVP-0018A", "SBMN", "2006 ?", "640x480", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{AFTER_BURNER_CLIMAX_SE, "After Burner Climax SE", "abc-se", "DVP-0031", "SBLR", "2006 ?", "", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{AFTER_BURNER_CLIMAX_SE_REVA, "After Burner Climax SE Rev A", "abc-se-rev-a", "DVP-0031A", "SBLR", "2006 ?", "", WORKING, SEGA_TYPE_1, ABC, 640, 480, GROUP_ABC, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{GHOST_SQUAD_EVOLUTION, "Ghost Squad Evolution", "gsevo", "DVP-0029A", "SBNJ", "2007", "640x480", WORKING, SEGA_TYPE_1, SHOOTING, 640, 480, -1, 0, 0, 0, 0, 0, 0, RED},
|
||||
{HARLEY_DAVIDSON, "Harley-Davidson: King of the Road", "harley", "DVP-5007", "SBRG", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, -1, 0, 0, 0, 0, 0, 0, RED},
|
||||
{HUMMER, "Hummer", "hummer", "DVP-0057B", "SBQN", "2008", "1280x768", WORKING, SEGA_TYPE_3, DRIVING, 1280, 768, GROUP_HUMMER, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{HUMMER_EXTREME, "Hummer Extreme", "hummer-ext", "DVP-0079", "SBST", "2009", "", WORKING, SEGA_TYPE_3, DRIVING, 1280, 768, GROUP_HUMMER, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{HUMMER_EXTREME_MDX, "Hummer Extreme MDX", "hummer-ext-mdx", "DVP-0083", "SBST", "2009", "1280x768", WORKING, SEGA_TYPE_3, DRIVING, 1280, 768, GROUP_HUMMER, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{HUMMER_SDLX, "Hummer SDLX", "hummer-sdlx", "DVP-0057", "SBST", "2008", "", WORKING, SEGA_TYPE_3, DRIVING, 1280, 768, GROUP_HUMMER, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{INITIALD_4_EXP_REVB, "Initial D Arcade Stage 4 Export Rev B", "id4-exp-rev-b", "DVP-0030B", "SBNK", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_EXP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_EXP_REVC, "Initial D Arcade Stage 4 Export Rev C", "id4-exp-rev-c", "DVP-0030C", "SBNK", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_EXP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_EXP_REVD, "Initial D Arcade Stage 4 Export Rev D", "id4-exp-rev-d", "DVP-0030D", "SBNK", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_EXP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_REVA, "Initial D Arcade Stage 4 Rev A", "id4-rev-a", "DVP-0019A", "SBML", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_JAP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_REVB, "Initial D Arcade Stage 4 Rev B", "id4-rev-b", "DVP-0019B", "SBML", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_JAP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_REVC, "Initial D Arcade Stage 4 Rev C", "id4-rev-c", "DVP-0019C", "SBML", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_JAP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_REVD, "Initial D Arcade Stage 4 Rev D", "id4-rev-d", "DVP-0019D", "SBML", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_JAP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_4_REVG, "Initial D Arcade Stage 4 Rev G", "id4-rev-g", "DVP-0019G", "SBML", "2007", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID4_JAP, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_5_EXP, "Initial D5 EXP", "id5-exp", "DVP-0075", "SBRY", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID5, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_5_EXP_20, "Initial D5 EXP 2.0", "id5-exp-20", "DVP-0084", "SBTS", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID5, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_5_EXP_20A, "Initial D5 5 EXP 2.0", "id5-exp-20a", "DVP-0084A", "SBTS", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID5, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_5_JAP_REVA, "Initial D Arcade Stage 5 Rev A", "id5-jap-rev-a", "DVP-0070A", "SBQZ", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID5, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_5_JAP_REVC, "Initial D Arcade Stage 5 Rev C", "id5-jap-rev-c", "DVP-0070C", "SBQZ", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID5, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{INITIALD_5_JAP_REVF, "Initial D Arcade Stage 5 Rev F", "id5-jap-rev-f", "DVP-0070F", "SBQZ", "2009", "1360x768", WORKING, SEGA_TYPE_3, DRIVING, 1360, 768, GROUP_ID5, 0, 0, 0, 0, 1, 0, YELLOW},
|
||||
{LETS_GO_JUNGLE, "Let's Go Jungle! Lost on the Island of Spice!", "lgj", "DVP-0011", "SBLU", "2006", "1360x768", WORKING, SEGA_TYPE_3, SHOOTING, 1360, 768, GROUP_LGJ, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{LETS_GO_JUNGLE_REVA, "Let's Go Jungle! Lost on the Island of Spice! Rev A", "lgj-rev-a", "DVP-0011A", "SBLU", "2006", "1360x768", WORKING, SEGA_TYPE_3, SHOOTING, 1360, 768, GROUP_LGJ, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{LETS_GO_JUNGLE_SPECIAL, "Let's Go Jungle! Special!", "lgj-sp", "DVP-0036", "SBNR", "2007", "1024x768", WORKING, SEGA_TYPE_3, SHOOTING, 1024, 768, GROUP_LGJ, 1, 0, 0, 0, 0, 0, YELLOW},
|
||||
{MJ4_EVO, "SEGA Network Taisen Mahjong MJ4 Evolution", "mj4-evo", "DVP-0081", "SBTA", "2010", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, GROUP_MJ4, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{MJ4_EVO_REVB, "SEGA Network Taisen Mahjong MJ4 Evolution Rev B", "mj4-evo-rev-b", "DVP-0081B", "SBTA", "2010", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, GROUP_MJ4, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{MJ4_REVA, "SEGA Network Taisen Mahjong MJ4 Rev A", "mj4-rev-a", "DVP-0049A", "SBPN", "2007", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, GROUP_MJ4, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{MJ4_REVF, "SEGA Network Taisen Mahjong MJ4 Rev F", "mj4-rev-f", "DVP-0049F", "SBPN", "2007", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, GROUP_MJ4, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{MJ4_REVG, "SEGA Network Taisen Mahjong MJ4 Rev G", "mj4-rev-g", "DVP-0049G", "SBPN", "2007", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, GROUP_MJ4, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{OUTRUN_2_SP_SDX, "Outrun 2 SP SDX", "outrun2", "DVP-0015", "SBMB", "2006", "800x480", WORKING, SEGA_TYPE_3, DRIVING, 800, 480, GROUP_OUTRUN, 0, 1, 1, 0, 0, 0, YELLOW},
|
||||
{OUTRUN_2_SP_SDX_TEST, "Outrun 2 SP SDX", "outrun2", "DVP-0015", "SBMB", "2006", "800x480", WORKING, SEGA_TYPE_3, DRIVING, 800, 480, GROUP_OUTRUN_TEST, 0, 1, 1, 0, 0, 0, YELLOW},
|
||||
{OUTRUN_2_SP_SDX_REVA, "Outrun 2 SP SDX Rev A", "outrun2-rev-a", "DVP-0015A", "SBMB", "2006", "800x480", WORKING, SEGA_TYPE_3, DRIVING, 800, 480, GROUP_OUTRUN, 0, 1, 1, 0, 0, 0, YELLOW},
|
||||
{OUTRUN_2_SP_SDX_REVA_TEST, "Outrun 2 SP SDX Rev A", "outrun2-rev-a", "DVP-0015A", "SBMB", "2006", "800x480", WORKING, SEGA_TYPE_3, DRIVING, 800, 480, GROUP_OUTRUN_TEST, 0, 1, 1, 0, 0, 0, YELLOW},
|
||||
{OUTRUN_2_SP_SDX_REVA_TEST2, "Outrun 2 SP SDX Rev A", "outrun2-rev-a", "DVP-0015A", "SBMB", "2006", "800x480", WORKING, SEGA_TYPE_3, DRIVING, 800, 480, GROUP_OUTRUN_TEST, 0, 1, 1, 0, 0, 0, YELLOW},
|
||||
{PRIMEVAL_HUNT, "Primeval Hunt", "ph", "DVP-0048A", "SBPP", "2008", "640x480", WORKING, SEGA_TYPE_3, SHOOTING, 640, 480, -1, 0, 0, 0, 0, 0, 0, RED},
|
||||
{QUIZ_AXA, "Network Taisen Quiz Answer x Answer (SBMS 1.30)", "axa", "DVP-0025H", "SBMS", "", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, -1, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{QUIZ_AXA_LIVE, "Network Taisen Quiz Answer x Answer Live! (SBUR 3.530)", "axalive", "DVP-0087D", "SBUR", "", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, -1, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{RAMBO, "Rambo", "rambo", "DVP-0069", "SBQL", "2008", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_RAMBO, 0, 0, 0, 0, 0, 0, REDEX},
|
||||
{RAMBO_CHINA, "Rambo", "rambo-cn", "DVP-0078", "SBSS", "2008", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_RAMBO, 0, 0, 0, 0, 0, 0, REDEX},
|
||||
{R_TUNED, "R-Tuned: Ultimate Street Racing", "r-tuned", "DVP-0060", "SBQW", "2008", "640x480", WORKING, SEGA_TYPE_3, DRIVING, 640, 480, -1, 0, 1, 0, 1, 0, 0, YELLOW},
|
||||
{SEGABOOT_2_4, "Segaboot 2.4", "segaboot-24", "", "", "", "", WORKING, SEGA_TYPE_3, DIGITAL, 0, 0, -1, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{SEGABOOT_2_4_SYM, "Segaboot 2.4 with symbols", "segaboot-24-sym", "", "", "", "", WORKING, SEGA_TYPE_3, DIGITAL, 0, 0, -1, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{SEGA_RACE_TV, "SEGA Race TV", "srtv", "DVP-0044", "SBPF", "2008", "640x480", WORKING, SEGA_TYPE_3, DRIVING, 640, 480, -1, 0, 1, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_REVA, "The House of the Dead 4 Rev A", "hod4-rev-a", "DVP-0003A", "SBLC", "2005", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_HOD4, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_REVA_TEST, "The House of the Dead 4 Rev A", "hod4-rev-a", "DVP-0003A", "SBLC", "2005", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_HOD4_TEST, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_REVB, "The House of the Dead 4 Rev B", "hod4-rev-b", "DVP-0003B", "SBLC", "2005", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_HOD4, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_REVB_TEST, "The House of the Dead 4 Rev B", "hod4-rev-b", "DVP-0003B", "SBLC", "2005", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_HOD4_TEST, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_REVC, "The House of the Dead 4 Rev C", "hod4-rev-c", "DVP-0003C", "SBLC", "2005", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_HOD4, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_REVC_TEST, "The House of the Dead 4 Rev C", "hod4-rev-c", "DVP-0003C", "SBLC", "2005", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, GROUP_HOD4_TEST, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_SPECIAL, "The House of the Dead 4 Special", "hod4-sp", "DVP-0010", "SBLS", "2006", "1024x768", WORKING, SEGA_TYPE_3, SHOOTING, 1024, 768, GROUP_HOD4_SP, 1, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST, "The House of the Dead 4 Special", "hod4-sp", "DVP-0010", "SBLS", "2006", "1024x768", WORKING, SEGA_TYPE_3, SHOOTING, 1024, 768, GROUP_HOD4_SP_TEST, 1, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_SPECIAL_REVB, "The House of the Dead 4 Special Rev B", "hod4-sp-rev-b", "DVP-0010B", "SBLS", "2006", "1024x768", WORKING, SEGA_TYPE_3, SHOOTING, 1024, 768, GROUP_HOD4_SP, 1, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_4_SPECIAL_REVB_TEST, "The House of the Dead 4 Special Rev B", "hod4-sp-rev-b", "DVP-0010B", "SBLS", "2006", "1024x768", WORKING, SEGA_TYPE_3, SHOOTING, 1024, 768, GROUP_HOD4_SP_TEST, 1, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_EX, "Ai Sareruyori Ai Shitai: The House of the Dead EX", "hod-ex", "DVP-0063", "SBRC", "2008", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1360, 768, -1, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{THE_HOUSE_OF_THE_DEAD_EX_TEST, "Ai Sareruyori Ai Shitai: The House of the Dead EX", "hod-ex", "DVP-0063", "SBRC", "2008", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1360, 768, -1, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{TOO_SPICY, "Too Spicy", "2spicy", "DVP-0027A", "SBMV", "2007", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, -1, 0, 0, 0, 0, 0, 0, RED},
|
||||
{TOO_SPICY_TEST, "Too Spicy", "2spicy", "DVP-0027A", "SBMV", "2007", "640x480, 1280x768", WORKING, SEGA_TYPE_3, SHOOTING, 1280, 768, -1, 0, 0, 0, 0, 0, 0, RED},
|
||||
{VIRTUA_FIGHTER_5, "Virtua Fighter 5", "vf5", "DVP-0008", "SBLM", "2005", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_EXPORT, "Virtua Fighter 5 Export", "vf5-exp", "DVP-0043", "SBLM", "2005", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA, "Virtua Fighter 5 Final Showdown REV A", "vf5-fs-rev-a", "DVP-5019A", "SBUV", "2010", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVB, "Virtua Fighter 5 Final Showdown REV B", "vf5-fs-rev-b", "DVP-5020", "SBXX", "2010", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVB_6000, "Virtua Fighter 5 Final Showdown REV B ver 6.0000", "vf5-fs-rev-b-6000", "DVP-5020", "SBXX", "2010", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_R, "Virtua Fighter 5 R", "vf5-r", "DVP-XXXX", "SBQU", "2008", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_R_REVD, "Virtua Fighter 5 R Rev D", "vf5-r-rev-d", "DVP-XXXX", "SBQU", "2008", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_R_REVG, "Virtua Fighter 5 R Rev G", "vf5-r-rev-g", "DVP-XXXX", "SBQU", "2008", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_REVA, "Virtua Fighter 5 Rev A", "vf5-rev-a", "DVP-0008A", "SBLM", "2005", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_REVB, "Virtua Fighter 5 Rev B", "vf5-rev-b", "DVP-0008B", "SBLM", "2005", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_FIGHTER_5_REVE, "Virtua Fighter 5 Rev E (Public Version C)", "vf5-rev-e", "DVP-0008E", "SBLM", "2005", "1280x768", WORKING, SEGA_TYPE_3, DIGITAL, 1280, 768, GROUP_VF5, 0, 0, 0, 0, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3, "Virtua Tennis 3", "vt3", "DVP-0005", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_TEST, "Virtua Tennis 3", "vt3", "DVP-0005", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3_TEST, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_REVA, "Virtua Tennis 3 Rev A", "vt3-rev-a", "DVP-0005A", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_REVA_TEST, "Virtua Tennis 3 Rev A", "vt3-rev-a", "DVP-0005A", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3_TEST, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_REVB, "Virtua Tennis 3 Rev B", "vt3-rev-b", "DVP-0005B", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_REVB_TEST, "Virtua Tennis 3 Rev B", "vt3-rev-b", "DVP-0005B", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3_TEST, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_REVC, "Virtua Tennis 3 Rev C", "vt3-rev-c", "DVP-0005C", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
{VIRTUA_TENNIS_3_REVC_TEST, "Virtua Tennis 3 Rev C", "vt3-rev-c", "DVP-0005C", "SBKX", "2006", "640x480, 1360x768", WORKING, SEGA_TYPE_3, DIGITAL, 1360, 768, GROUP_VT3_TEST, 0, 0, 0, 1, 0, 0, YELLOW},
|
||||
};
|
||||
|
||||
const GameData *getGameData(uint32_t elf_crc)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(gameData) / sizeof(gameData[0]); ++i)
|
||||
{
|
||||
if (gameData[i].crc32 == elf_crc)
|
||||
{
|
||||
return &gameData[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef GAME_DATA_H
|
||||
#define GAME_DATA_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
typedef struct GameData {
|
||||
uint32_t crc32;
|
||||
const char *gameTitle;
|
||||
const char *gameShortTitle;
|
||||
const char *gameDVP;
|
||||
const char *gameID;
|
||||
const char *gameReleaseYear;
|
||||
const char *gameNativeResolutions;
|
||||
GameStatus gameStatus;
|
||||
JVSIOType jvsIOType;
|
||||
GameType gameType;
|
||||
int width;
|
||||
int height;
|
||||
GameGroup gameGroup;
|
||||
int emulateRideboard;
|
||||
int emulateDriveboard;
|
||||
int emulateMotionboard;
|
||||
int emulateHW210CardReader;
|
||||
int emulateIDCardReader;
|
||||
int emulateTouchscreen;
|
||||
Colour gameLindberghColour;
|
||||
} GameData;
|
||||
|
||||
const GameData *getGameData(uint32_t elf_crc);
|
||||
|
||||
#endif // GAME_DATA_H
|
||||
@@ -0,0 +1,383 @@
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/glx.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "blitStretching.h"
|
||||
#include "border.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "fpsLimiter.h"
|
||||
#include "resolution.h"
|
||||
#include "sdlCalls.h"
|
||||
#include "touchScreen.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern int gWidth;
|
||||
extern int gHeight;
|
||||
extern SDL_Window *sdlWindow;
|
||||
extern Display *x11Display;
|
||||
extern char sdlGameTitle[];
|
||||
extern char current_text[256];
|
||||
extern double localFps;
|
||||
bool winHidden = true;
|
||||
|
||||
void *displayFunc = NULL;
|
||||
void *reshapeFunc = NULL;
|
||||
void *visibilityFunc = NULL;
|
||||
void *idleFunc = NULL;
|
||||
|
||||
extern int drawableW;
|
||||
extern int drawableH;
|
||||
extern SDL_Renderer *fontRenderer;
|
||||
extern TTF_Font *font;
|
||||
int penX = 0;
|
||||
int penY = 0;
|
||||
|
||||
char windowTitle[512];
|
||||
|
||||
void FGAPIENTRY glutInit(int *argcp, char **argv)
|
||||
{
|
||||
void FGAPIENTRY (*_glutInit)(int *argcp, char **argv) = dlsym(RTLD_NEXT, "glutInit");
|
||||
startSDL(argcp, argv);
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMainLoop(void)
|
||||
{
|
||||
|
||||
bool quit = false;
|
||||
while (!quit)
|
||||
{
|
||||
pollEvents();
|
||||
int w, h;
|
||||
SDL_GetWindowSize(sdlWindow, &w, &h);
|
||||
if (displayFunc != NULL)
|
||||
(((void *(*)(void))displayFunc)());
|
||||
if (reshapeFunc != NULL)
|
||||
(((void *(*)(int, int))reshapeFunc)(w, h));
|
||||
if (visibilityFunc != NULL)
|
||||
(((void *(*)(int))visibilityFunc)(1));
|
||||
if (idleFunc != NULL)
|
||||
(((void *(*)(void))idleFunc)());
|
||||
}
|
||||
SDL_DestroyWindow(sdlWindow);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMainLoopEvent(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSwapBuffers(void)
|
||||
{
|
||||
EmulatorConfig *config = getConfig();
|
||||
|
||||
switch (gGrp)
|
||||
{
|
||||
case GROUP_OUTRUN:
|
||||
case GROUP_OUTRUN_TEST:
|
||||
{
|
||||
pollEvents();
|
||||
}
|
||||
}
|
||||
|
||||
if ((p1CrossHairInitialized || p2CrossHairInitialized) && gId != GHOST_SQUAD_EVOLUTION)
|
||||
renderCrosshairs();
|
||||
|
||||
blitStretch();
|
||||
|
||||
if (config->borderEnabled)
|
||||
drawGameBorder(drawableW, drawableH, config->whiteBorderPercentage, config->blackBorderPercentage);
|
||||
|
||||
SDL_GL_SwapWindow(sdlWindow);
|
||||
|
||||
if (config->fpsLimiter)
|
||||
frameTiming();
|
||||
|
||||
sprintf(windowTitle, "%s - FPS: %.2f", sdlGameTitle, calculateFps());
|
||||
SDL_SetWindowTitle(sdlWindow, windowTitle);
|
||||
|
||||
if (gGrp == GROUP_MJ4)
|
||||
mj4TouchHolding();
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutGet(GLenum type)
|
||||
{
|
||||
if (type == 0x66)
|
||||
return gWidth;
|
||||
if (type == 0x67)
|
||||
return gHeight;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutInitWindowSize(int width, int height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutInitWindowPosition(int x, int y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutExtensionSupported(const char *extension)
|
||||
{
|
||||
return SDL_GL_ExtensionSupported(extension);
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSetCursor(int glutCursor)
|
||||
{
|
||||
if (strcmp(getConfig()->customCursor, "") == 0 && strcmp(getConfig()->touchCursor, "") == 0)
|
||||
{
|
||||
SDL_Cursor *cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
|
||||
SDL_SetCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutDisplayFunc(void (*callback)(void))
|
||||
{
|
||||
displayFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutReshapeFunc(void (*callback)(int, int))
|
||||
{
|
||||
return;
|
||||
// reshapeFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutVisibilityFunc(void (*callback)(int))
|
||||
{
|
||||
visibilityFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutIdleFunc(void (*callback)(void))
|
||||
{
|
||||
idleFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutInitDisplayMode(unsigned int mode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutGameModeString(const char *string)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutEnterGameMode(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutCreateWindow(const char *title)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutJoystickFunc(void (*callback)(unsigned int, int, int, int), int pollInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutPostRedisplay(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutKeyboardFunc(void (*callback)(unsigned char, int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutKeyboardUpFunc(void (*callback)(unsigned char, int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMouseFunc(void (*callback)(int, int, int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMotionFunc(void (*callback)(int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSpecialFunc(void (*callback)(int, int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSpecialUpFunc(void (*callback)(int, int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutPassiveMotionFunc(void (*callback)(int, int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutEntryFunc(void (*callback)(int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutLeaveGameMode()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidTeapot(double size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireTeapot(double size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireCone(double base, double height, GLint slices, GLint stacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidCone(double base, double height, GLint slices, GLint stacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireCube(double dSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidCube(double dSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutBitmapWidth(void *fontID, int character)
|
||||
{
|
||||
int width = 0;
|
||||
if (fontID == GLUT_BITMAP_9_BY_15)
|
||||
width = 9;
|
||||
return width;
|
||||
}
|
||||
|
||||
void convertSurfaceTo1Bit(SDL_Surface *surface, uint8_t *outBitmap, int pitch)
|
||||
{
|
||||
SDL_LockSurface(surface);
|
||||
for (int y = 0; y < surface->h; ++y)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
int bit = 0;
|
||||
int sdlY = surface->h - 1 - y;
|
||||
|
||||
Uint8 *row = (Uint8 *)surface->pixels + sdlY * surface->pitch;
|
||||
|
||||
for (int x = 0; x < surface->w; ++x)
|
||||
{
|
||||
if (row[x])
|
||||
byte |= (1 << (7 - bit));
|
||||
|
||||
bit++;
|
||||
if (bit == 8 || x == surface->w - 1)
|
||||
{
|
||||
outBitmap[y * pitch + x / 8] = byte;
|
||||
byte = 0;
|
||||
bit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_UnlockSurface(surface);
|
||||
}
|
||||
|
||||
void glutBitmapCharacter(void *fontID, int character)
|
||||
{
|
||||
GLint glColor[4];
|
||||
glGetIntegerv(GL_CURRENT_COLOR, glColor);
|
||||
|
||||
SDL_Color sdlColor;
|
||||
sdlColor.r = (Uint8)(glColor[0] * 255);
|
||||
sdlColor.g = (Uint8)(glColor[1] * 255);
|
||||
sdlColor.b = (Uint8)(glColor[2] * 255);
|
||||
sdlColor.a = (Uint8)(glColor[3] * 255);
|
||||
|
||||
GLint glPos[4];
|
||||
glGetIntegerv(GL_CURRENT_RASTER_POSITION, glPos);
|
||||
|
||||
if (penX != glPos[0])
|
||||
penX = glPos[0];
|
||||
|
||||
penY = glPos[1];
|
||||
|
||||
SDL_Surface *glyph = TTF_RenderGlyph_Solid(font, character, sdlColor);
|
||||
if (!glyph)
|
||||
{
|
||||
printf("Failed to render glyph: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
int pitch = (glyph->w + 7) / 8;
|
||||
uint8_t *bitmap = malloc(pitch * glyph->h);
|
||||
if (!bitmap)
|
||||
{
|
||||
printf("Failed to allocate memory for bitmap\n");
|
||||
SDL_DestroySurface(glyph);
|
||||
return;
|
||||
}
|
||||
convertSurfaceTo1Bit(glyph, bitmap, pitch);
|
||||
|
||||
GLint swbytes, lsbfirst, rowlen, skiprows, skippix, align;
|
||||
|
||||
glGetIntegerv(GL_UNPACK_SWAP_BYTES, &swbytes);
|
||||
glGetIntegerv(GL_UNPACK_LSB_FIRST, &lsbfirst);
|
||||
glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rowlen);
|
||||
glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skiprows);
|
||||
glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skippix);
|
||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &align);
|
||||
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
glBitmap(glyph->w, glyph->h, 0, 0, (float)(glyph->w), 0.0, bitmap);
|
||||
glGetIntegerv(GL_CURRENT_RASTER_POSITION, glPos);
|
||||
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, swbytes);
|
||||
glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlen);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippix);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, align);
|
||||
|
||||
free(bitmap);
|
||||
SDL_DestroySurface(glyph);
|
||||
}
|
||||
@@ -1,504 +0,0 @@
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/freeglut_std.h>
|
||||
#include <GL/glx.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "fps_limiter.h"
|
||||
#include "sdlcalls.h"
|
||||
#include "border.h"
|
||||
#include "resolution.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
extern SDL_Window *SDLwindow;
|
||||
extern char SDLgameTitle[];
|
||||
extern fps_limit fpsLimit;
|
||||
bool GLUTGame = false;
|
||||
|
||||
void *displayFunc = NULL;
|
||||
void *reshapeFunc = NULL;
|
||||
void *visibilityFunc = NULL;
|
||||
void *idleFunc = NULL;
|
||||
|
||||
void FGAPIENTRY glutInit(int *argcp, char **argv)
|
||||
{
|
||||
void FGAPIENTRY (*_glutInit)(int *argcp, char **argv) = dlsym(RTLD_NEXT, "glutInit");
|
||||
|
||||
if (*argcp > 1 && gId == SEGA_RACE_TV)
|
||||
{
|
||||
if (isTestMode())
|
||||
{
|
||||
EmulatorConfig *config = getConfig();
|
||||
config->noSDL = 0;
|
||||
printf("Forced to use SDL to make Test mode render fine.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
_glutInit(argcp, argv);
|
||||
return;
|
||||
}
|
||||
|
||||
GLUTGame = true;
|
||||
initSDL(argcp, argv);
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMainLoop(void)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutMainLoop)(void) = dlsym(RTLD_NEXT, "glutMainLoop");
|
||||
_glutMainLoop();
|
||||
return;
|
||||
}
|
||||
|
||||
bool quit = false;
|
||||
while (!quit)
|
||||
{
|
||||
pollEvents();
|
||||
int w, h;
|
||||
SDL_GetWindowSize(SDLwindow, &w, &h);
|
||||
if (displayFunc != NULL)
|
||||
(((void *(*)(void))displayFunc)());
|
||||
if (reshapeFunc != NULL)
|
||||
(((void *(*)(int, int))reshapeFunc)(w, h));
|
||||
if (visibilityFunc != NULL)
|
||||
(((void *(*)(int))visibilityFunc)(1));
|
||||
if (idleFunc != NULL)
|
||||
(((void *(*)(void))idleFunc)());
|
||||
}
|
||||
SDL_DestroyWindow(SDLwindow);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMainLoopEvent(void)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutMainLoopEvent)(void) = dlsym(RTLD_NEXT, "glutMainLoopEvent");
|
||||
_glutMainLoopEvent();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSwapBuffers(void)
|
||||
{
|
||||
EmulatorConfig *config = getConfig();
|
||||
|
||||
if (config->borderEnabled)
|
||||
drawGameBorder(config->width, config->height, config->whiteBorderPercentage, config->blackBorderPercentage);
|
||||
|
||||
if (config->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSwapBuffers)(void) = dlsym(RTLD_NEXT, "glutSwapBuffers");
|
||||
_glutSwapBuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (gId)
|
||||
{
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST2:
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
pollEvents();
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(SDLwindow);
|
||||
|
||||
if (config->fpsLimiter)
|
||||
{
|
||||
fpsLimit.frameStart = Clock_now();
|
||||
FpsLimiter(&fpsLimit);
|
||||
fpsLimit.frameEnd = Clock_now();
|
||||
}
|
||||
char windowTitle[512];
|
||||
sprintf(windowTitle, "%s - FPS: %.2f", SDLgameTitle, calculateFps());
|
||||
SDL_SetWindowTitle(SDLwindow, windowTitle);
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutGet(GLenum type)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
int FGAPIENTRY (*_glutGet)(GLenum type) = dlsym(RTLD_NEXT, "glutGet");
|
||||
return _glutGet(type);
|
||||
}
|
||||
if (type == 0x66)
|
||||
return getConfig()->width;
|
||||
if (type == 0x67)
|
||||
return getConfig()->height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutInitWindowSize(int width, int height)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutInitWindowSize)(int width, int height) = dlsym(RTLD_NEXT, "glutInitWindowSize");
|
||||
_glutInitWindowSize(width, height);
|
||||
return;
|
||||
}
|
||||
SDL_SetWindowSize(SDLwindow, getConfig()->width, getConfig()->height);
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutInitWindowPosition(int x, int y)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutInitWindowPosition)(int width, int height) = dlsym(RTLD_NEXT, "glutInitWindowPosition");
|
||||
_glutInitWindowPosition(x, y);
|
||||
return;
|
||||
}
|
||||
SDL_SetWindowPosition(SDLwindow, x, y);
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutExtensionSupported(const char *extension)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
int FGAPIENTRY (*_glutExtensionSupported)(const char *extension) = dlsym(RTLD_NEXT, "glutExtensionSupported");
|
||||
return _glutExtensionSupported(extension);
|
||||
}
|
||||
return SDL_GL_ExtensionSupported(extension);
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSetCursor(int glutCursor)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSetCursor)(int glutCursor) = dlsym(RTLD_NEXT, "glutSetCursor");
|
||||
if(strcmp(getConfig()->customCursor, "") == 0)
|
||||
_glutSetCursor(glutCursor);
|
||||
return;
|
||||
}
|
||||
if (strcmp(getConfig()->customCursor, "") == 0)
|
||||
{
|
||||
SDL_Cursor *cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
SDL_SetCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutDisplayFunc(void (*callback)(void))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutDisplayFunc)(void (*callback)(void)) = dlsym(RTLD_NEXT, "glutDisplayFunc");
|
||||
_glutDisplayFunc(callback);
|
||||
return;
|
||||
}
|
||||
displayFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutReshapeFunc(void (*callback)(int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutReshapeFunc)(void (*callback)(int, int)) = dlsym(RTLD_NEXT, "glutReshapeFunc");
|
||||
_glutReshapeFunc(callback);
|
||||
return;
|
||||
}
|
||||
reshapeFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutVisibilityFunc(void (*callback)(int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutVisibilityFunc)(void (*callback)(int)) = dlsym(RTLD_NEXT, "glutVisibilityFunc");
|
||||
_glutVisibilityFunc(callback);
|
||||
return;
|
||||
}
|
||||
visibilityFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutIdleFunc(void (*callback)(void))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutIdleFunc)(void (*callback)(void)) = dlsym(RTLD_NEXT, "glutIdleFunc");
|
||||
_glutIdleFunc(callback);
|
||||
return;
|
||||
}
|
||||
idleFunc = callback;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutInitDisplayMode(unsigned int mode)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutInitDisplayMode)(unsigned int mode) = dlsym(RTLD_NEXT, "glutInitDisplayMode");
|
||||
_glutInitDisplayMode(mode);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutGameModeString(const char *string)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutGameModeString)(const char *string) = dlsym(RTLD_NEXT, "glutGameModeString");
|
||||
_glutGameModeString(string);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutEnterGameMode(void)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
int FGAPIENTRY (*_glutEnterGameMode)(void) = dlsym(RTLD_NEXT, "glutEnterGameMode");
|
||||
char gameTitle[256] = {0};
|
||||
strcat(gameTitle, getGameName());
|
||||
glutCreateWindow(gameTitle);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutCreateWindow(const char *title)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
int FGAPIENTRY (*_glutCreateWindow)(const char *title) = dlsym(RTLD_NEXT, "glutCreateWindow");
|
||||
return _glutCreateWindow(title);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutJoystickFunc(void (*callback)(unsigned int, int, int, int), int pollInterval)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutJoystickFunc)(void (*callback)(unsigned int, int, int, int), int pollInterval) =
|
||||
dlsym(RTLD_NEXT, "glutJoystickFunc");
|
||||
_glutJoystickFunc(callback, pollInterval);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutPostRedisplay(void)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutPostRedisplay)(void) = dlsym(RTLD_NEXT, "glutPostRedisplay");
|
||||
_glutPostRedisplay();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutKeyboardFunc(void (*callback)(unsigned char, int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutKeyboardFunc)(void (*callback)(unsigned char, int, int)) =
|
||||
dlsym(RTLD_NEXT, "glutKeyboardFunc");
|
||||
_glutKeyboardFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutKeyboardUpFunc(void (*callback)(unsigned char, int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutKeyboardUpFunc)(void (*callback)(unsigned char, int, int)) =
|
||||
dlsym(RTLD_NEXT, "glutKeyboardUpFunc");
|
||||
_glutKeyboardUpFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMouseFunc(void (*callback)(int, int, int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutMouseFunc)(void (*callback)(int, int, int, int)) = dlsym(RTLD_NEXT, "glutMouseFunc");
|
||||
_glutMouseFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutMotionFunc(void (*callback)(int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutMotionFunc)(void (*callback)(int, int)) = dlsym(RTLD_NEXT, "glutMotionFunc");
|
||||
_glutMotionFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSpecialFunc(void (*callback)(int, int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSpecialFunc)(void (*callback)(int, int, int)) = dlsym(RTLD_NEXT, "glutSpecialFunc");
|
||||
_glutSpecialFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSpecialUpFunc(void (*callback)(int, int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSpecialUpFunc)(void (*callback)(int, int, int)) = dlsym(RTLD_NEXT, "glutSpecialUpFunc");
|
||||
_glutSpecialUpFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutPassiveMotionFunc(void (*callback)(int, int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutPassiveMotionFunc)(void (*callback)(int, int)) =
|
||||
dlsym(RTLD_NEXT, "glutPassiveMotionFunc");
|
||||
_glutPassiveMotionFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutEntryFunc(void (*callback)(int))
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutEntryFunc)(void (*callback)(int)) = dlsym(RTLD_NEXT, "glutEntryFunc");
|
||||
_glutEntryFunc(callback);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutLeaveGameMode()
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutLeaveGameMode)() = dlsym(RTLD_NEXT, "glutLeaveGameMode");
|
||||
_glutLeaveGameMode();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidTeapot(double size)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSolidTeapot)(double size) = dlsym(RTLD_NEXT, "glutSolidTeapot");
|
||||
_glutSolidTeapot(size);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireTeapot(double size)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutWireTeapot)(double size) = dlsym(RTLD_NEXT, "glutWireTeapot");
|
||||
_glutWireTeapot(size);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidSphere(double radius, GLint slices, GLint stacks)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSolidSphere)(double radius, GLint slices, GLint stacks) =
|
||||
dlsym(RTLD_NEXT, "glutSolidSphere");
|
||||
_glutSolidSphere(radius, slices, stacks);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireSphere(double radius, GLint slices, GLint stacks)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutWireSphere)(double radius, GLint slices, GLint stacks) =
|
||||
dlsym(RTLD_NEXT, "glutWireSphere");
|
||||
_glutWireSphere(radius, slices, stacks);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireCone(double base, double height, GLint slices, GLint stacks)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutWireCone)(double base, double height, GLint slices, GLint stacks) =
|
||||
dlsym(RTLD_NEXT, "glutWireCone");
|
||||
_glutWireCone(base, height, slices, stacks);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidCone(double base, double height, GLint slices, GLint stacks)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSolidCone)(double base, double height, GLint slices, GLint stacks) =
|
||||
dlsym(RTLD_NEXT, "glutSolidCone");
|
||||
_glutSolidCone(base, height, slices, stacks);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutWireCube(double dSize)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutWireCube)(double dSize) = dlsym(RTLD_NEXT, "glutWireCube");
|
||||
_glutWireCube(dSize);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FGAPIENTRY glutSolidCube(double dSize)
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
void FGAPIENTRY (*_glutSolidCube)(double dSize) = dlsym(RTLD_NEXT, "glutSolidCube");
|
||||
_glutSolidCube(dSize);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#ifndef GLUTHOOKS_H
|
||||
#define GLUTHOOKS_H
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,179 @@
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include <GL/glx.h>
|
||||
#include <stdbool.h>
|
||||
#include <dlfcn.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "blitStretching.h"
|
||||
#include "border.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "flowControl.h"
|
||||
#include "fpsLimiter.h"
|
||||
#include "sdlCalls.h"
|
||||
#include "touchScreen.h"
|
||||
|
||||
bool sdlGame = false;
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern SDL_Window *sdlWindow;
|
||||
extern SDL_GLContext sdlContext;
|
||||
extern Display *x11Display;
|
||||
extern char sdlGameTitle[];
|
||||
double localFps = 0.0;
|
||||
|
||||
bool createContextCalled = false;
|
||||
int ctxCnt = 0;
|
||||
|
||||
extern char windowTitle[512];
|
||||
|
||||
void glXSwapBuffers(Display *dpy, GLXDrawable drawable)
|
||||
{
|
||||
void (*_glXSwapBuffers)(Display *dpy, GLXDrawable drawable) = dlsym(RTLD_NEXT, "glXSwapBuffers");
|
||||
|
||||
EmulatorConfig *config = getConfig();
|
||||
|
||||
if (config->borderEnabled)
|
||||
drawGameBorder(config->width, config->height, config->whiteBorderPercentage, config->blackBorderPercentage);
|
||||
|
||||
if (p1CrossHairInitialized || p2CrossHairInitialized)
|
||||
renderCrosshairs();
|
||||
|
||||
blitStretch();
|
||||
|
||||
pollEvents();
|
||||
|
||||
SDL_GL_SwapWindow(sdlWindow);
|
||||
|
||||
if (config->fpsLimiter)
|
||||
frameTiming();
|
||||
|
||||
localFps = calculateFps();
|
||||
sprintf(windowTitle, "%s - FPS: %.2f", sdlGameTitle, localFps);
|
||||
SDL_SetWindowTitle(sdlWindow, windowTitle);
|
||||
|
||||
if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE)
|
||||
mj4TouchHolding();
|
||||
}
|
||||
|
||||
GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
|
||||
{
|
||||
GLXContext (*_glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct) =
|
||||
dlsym(RTLD_NEXT, "glXCreateContext");
|
||||
|
||||
GLXContext ctx;
|
||||
if (ctxCnt == 0)
|
||||
ctx = (GLXContext)sdlContext;
|
||||
else
|
||||
ctx = _glXCreateContext(dpy, vis, shareList, direct);
|
||||
|
||||
ctxCnt++;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attrib_list, int *nelements)
|
||||
{
|
||||
GLXFBConfig *(*_glXChooseFBConfig)(Display *dpy, int screen, const int *attrib_list, int *nelements) =
|
||||
dlsym(RTLD_NEXT, "glXChooseFBConfig");
|
||||
|
||||
char *__GLX_VENDOR_LIBRARY_NAME = getenv("__GLX_VENDOR_LIBRARY_NAME");
|
||||
char *__NV_PRIME_RENDER_OFFLOAD = getenv("__NV_PRIME_RENDER_OFFLOAD");
|
||||
if (__GLX_VENDOR_LIBRARY_NAME == NULL)
|
||||
__GLX_VENDOR_LIBRARY_NAME = " ";
|
||||
if (__NV_PRIME_RENDER_OFFLOAD == NULL)
|
||||
__NV_PRIME_RENDER_OFFLOAD = " ";
|
||||
|
||||
if (gGrp == GROUP_HOD4 || gGrp == GROUP_HOD4_SP || gId == THE_HOUSE_OF_THE_DEAD_EX || gId == TOO_SPICY || gId == QUIZ_AXA ||
|
||||
gId == QUIZ_AXA_LIVE)
|
||||
{
|
||||
if (strcmp(__GLX_VENDOR_LIBRARY_NAME, "nvidia") == 0 && strcmp(__NV_PRIME_RENDER_OFFLOAD, "1") == 0)
|
||||
{
|
||||
for (int i = 0; attrib_list[i] != None; i += 2)
|
||||
{
|
||||
if (attrib_list[i] == GLX_DOUBLEBUFFER || attrib_list[i] == GLX_DRAWABLE_TYPE)
|
||||
{
|
||||
int *ptr = (int *)&attrib_list[i + 1];
|
||||
patchMemoryFromString((size_t)ptr, "01");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return _glXChooseFBConfig(dpy, screen, attrib_list, nelements);
|
||||
}
|
||||
|
||||
GLXPbuffer glXCreateGLXPbufferSGIX(Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list)
|
||||
{
|
||||
int pbufferAttribs[] = {
|
||||
GLX_PBUFFER_WIDTH, width, GLX_PBUFFER_HEIGHT, height, GLX_PRESERVED_CONTENTS, true, GLX_LARGEST_PBUFFER, true, None};
|
||||
return glXCreatePbuffer(dpy, config, pbufferAttribs);
|
||||
}
|
||||
|
||||
void glXDestroyGLXPbufferSGIX(Display *dpy, GLXPbufferSGIX pbuf)
|
||||
{
|
||||
glXDestroyPbuffer(dpy, pbuf);
|
||||
}
|
||||
|
||||
GLXFBConfigSGIX *glXChooseFBConfigSGIX(Display *dpy, int screen, int *attrib_list, int *nelements)
|
||||
{
|
||||
char *__GLX_VENDOR_LIBRARY_NAME = getenv("__GLX_VENDOR_LIBRARY_NAME");
|
||||
char *__NV_PRIME_RENDER_OFFLOAD = getenv("__NV_PRIME_RENDER_OFFLOAD");
|
||||
if (__GLX_VENDOR_LIBRARY_NAME == NULL)
|
||||
__GLX_VENDOR_LIBRARY_NAME = " ";
|
||||
if (__NV_PRIME_RENDER_OFFLOAD == NULL)
|
||||
__NV_PRIME_RENDER_OFFLOAD = " ";
|
||||
|
||||
if (gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP)
|
||||
{
|
||||
if (strcmp(__GLX_VENDOR_LIBRARY_NAME, "nvidia") == 0 && strcmp(__NV_PRIME_RENDER_OFFLOAD, "1") == 0)
|
||||
{
|
||||
for (int i = 0; attrib_list[i] != None; i += 2)
|
||||
{
|
||||
if (attrib_list[i] == GLX_DOUBLEBUFFER)
|
||||
{
|
||||
int *ptr = (int *)&attrib_list[i + 1];
|
||||
patchMemoryFromString((size_t)ptr, "01");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return glXChooseFBConfig(dpy, screen, attrib_list, nelements);
|
||||
}
|
||||
|
||||
int glXGetFBConfigAttribSGIX(Display *dpy, GLXFBConfigSGIX config, int attribute, int *value)
|
||||
{
|
||||
return glXGetFBConfigAttrib(dpy, config, attribute, value);
|
||||
}
|
||||
|
||||
GLXContext glXCreateContextWithConfigSGIX(Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct)
|
||||
{
|
||||
return glXCreateNewContext(dpy, config, render_type, share_list, direct);
|
||||
}
|
||||
|
||||
Display *glXGetCurrentDisplay(void)
|
||||
{
|
||||
return x11Display;
|
||||
}
|
||||
|
||||
int glXGetVideoSyncSGI(uint *count)
|
||||
{
|
||||
static unsigned int frameCount = 0;
|
||||
*count = (frameCount++) / 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int glXGetRefreshRateSGI(unsigned int *rate)
|
||||
{
|
||||
*rate = 60;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int glXSwapIntervalSGI(int interval)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#include <GL/gl.h>
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fps_limiter.h"
|
||||
#include "sdlcalls.h"
|
||||
#include "patch.h"
|
||||
#include "config.h"
|
||||
#include "border.h"
|
||||
|
||||
bool sdlGame = false;
|
||||
extern uint32_t gId;
|
||||
extern SDL_Window *SDLwindow;
|
||||
extern SDL_GLContext SDLcontext;
|
||||
extern char SDLgameTitle[];
|
||||
extern fps_limit fpsLimit;
|
||||
extern Window window;
|
||||
|
||||
void glXSwapBuffers(Display *dpy, GLXDrawable drawable)
|
||||
{
|
||||
void (*_glXSwapBuffers)(Display *dpy, GLXDrawable drawable) = dlsym(RTLD_NEXT, "glXSwapBuffers");
|
||||
|
||||
EmulatorConfig *config = getConfig();
|
||||
|
||||
if (config->borderEnabled)
|
||||
drawGameBorder(config->width, config->height, config->whiteBorderPercentage, config->blackBorderPercentage);
|
||||
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
switch (gId)
|
||||
{
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST2:
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
{
|
||||
XEvent event;
|
||||
while (XPending(dpy))
|
||||
{
|
||||
XNextEvent(dpy, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pollEvents();
|
||||
}
|
||||
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
_glXSwapBuffers(dpy, drawable);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_GL_SwapWindow(SDLwindow);
|
||||
}
|
||||
|
||||
if (getConfig()->fpsLimiter)
|
||||
{
|
||||
fpsLimit.frameStart = Clock_now();
|
||||
FpsLimiter(&fpsLimit);
|
||||
fpsLimit.frameEnd = Clock_now();
|
||||
}
|
||||
|
||||
if (getConfig()->noSDL)
|
||||
{
|
||||
XStoreName(dpy, window, "");
|
||||
return;
|
||||
}
|
||||
char windowTitle[512];
|
||||
sprintf(windowTitle, "%s - FPS: %.2f", SDLgameTitle, calculateFps());
|
||||
SDL_SetWindowTitle(SDLwindow, windowTitle);
|
||||
}
|
||||
|
||||
int glxSDLmyCreateWindow(int *param1)
|
||||
{
|
||||
initSDL(0, 0);
|
||||
char *buf = malloc(512);
|
||||
memset(buf, '\0', 512);
|
||||
// param1[0x17] = (intptr_t)(void *)buf; // window;
|
||||
sdlGame = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nVidiaAttribs[] = {GLX_RGBA,
|
||||
GLX_DOUBLEBUFFER,
|
||||
GLX_RED_SIZE,
|
||||
8,
|
||||
GLX_GREEN_SIZE,
|
||||
8,
|
||||
GLX_BLUE_SIZE,
|
||||
8,
|
||||
GLX_ALPHA_SIZE,
|
||||
8,
|
||||
GLX_DEPTH_SIZE,
|
||||
16,
|
||||
GLX_STENCIL_SIZE,
|
||||
8,
|
||||
None};
|
||||
|
||||
XVisualInfo *glXChooseVisual(Display *dpy, int screen, int *attribList)
|
||||
{
|
||||
XVisualInfo *(*_glXChooseVisual)(Display *dpy, int screen, int *attribList) = dlsym(RTLD_NEXT, "glXChooseVisual");
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
|
||||
if (getConfig()->GPUVendor == NVIDIA_GPU)
|
||||
{
|
||||
return _glXChooseVisual(dpy, screen, nVidiaAttribs);
|
||||
}
|
||||
return _glXChooseVisual(dpy, screen, attribList);
|
||||
}
|
||||
|
||||
void GLAPIENTRY openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar *message, const void *userParam)
|
||||
{
|
||||
if (id == 1099)
|
||||
return;
|
||||
// Print the debug message details
|
||||
printf("OpenGL Debug Message:\n");
|
||||
printf("Source: 0x%x\n", source);
|
||||
printf("Type: 0x%x\n", type);
|
||||
printf("ID: %u\n", id);
|
||||
printf("Severity: 0x%x\n", severity);
|
||||
printf("Message: %s\n", message);
|
||||
|
||||
// You can filter based on severity or other attributes if needed
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH)
|
||||
{
|
||||
printf("This is a high severity error!\n");
|
||||
}
|
||||
}
|
||||
|
||||
GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
|
||||
{
|
||||
GLXContext (*_glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct) =
|
||||
dlsym(RTLD_NEXT, "glXCreateContext");
|
||||
if (sdlGame)
|
||||
{
|
||||
return SDLcontext;
|
||||
}
|
||||
GLXContext ctx = _glXCreateContext(dpy, vis, shareList, direct);
|
||||
if (getConfig()->showDebugMessages)
|
||||
{
|
||||
// Enable OpenGL debug output
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // Make the callback synchronous
|
||||
glDebugMessageCallback(openglDebugCallback, NULL);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
GLXContext glXGetCurrentContext()
|
||||
{
|
||||
GLXContext (*_glXGetCurrentContext)() = dlsym(RTLD_NEXT, "glXGetCurrentContext");
|
||||
if (sdlGame)
|
||||
return SDLcontext;
|
||||
return _glXGetCurrentContext();
|
||||
}
|
||||
|
||||
GLXPbuffer glXCreatePbuffer(Display *dpy, GLXFBConfig config, const int *attrib_list)
|
||||
{
|
||||
GLXPbuffer (*_glXCreatePbuffer)(Display *dpy, GLXFBConfig config, const int *attrib_list) =
|
||||
dlsym(RTLD_NEXT, "glXCreatePbuffer");
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
return _glXCreatePbuffer(dpy, config, attrib_list);
|
||||
}
|
||||
|
||||
void glXDestroyContext(Display *dpy, GLXContext ctx)
|
||||
{
|
||||
void (*_glXDestroyContext)(Display *dpy, GLXContext ctx) = dlsym(RTLD_NEXT, "glXDestroyContext");
|
||||
if (sdlGame)
|
||||
return;
|
||||
_glXDestroyContext(dpy, ctx);
|
||||
}
|
||||
|
||||
void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf)
|
||||
{
|
||||
void (*_glXDestroyPbuffer)(Display *dpy, GLXPbuffer pbuf) = dlsym(RTLD_NEXT, "glXDestroyPbuffer");
|
||||
if (sdlGame)
|
||||
return;
|
||||
_glXDestroyPbuffer(dpy, pbuf);
|
||||
}
|
||||
|
||||
XVisualInfo *glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config)
|
||||
{
|
||||
XVisualInfo *(*_glXGetVisualFromFBConfig)(Display *dpy, GLXFBConfig config) =
|
||||
dlsym(RTLD_NEXT, "glXGetVisualFromFBConfig");
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
return _glXGetVisualFromFBConfig(dpy, config);
|
||||
}
|
||||
|
||||
Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext ctx)
|
||||
{
|
||||
Bool (*_glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx) = dlsym(RTLD_NEXT, "glXMakeCurrent");
|
||||
if (sdlGame)
|
||||
{
|
||||
SDL_GL_MakeCurrent(SDLwindow, SDLcontext);
|
||||
return 1;
|
||||
}
|
||||
return _glXMakeCurrent(dpy, drawable, ctx);
|
||||
}
|
||||
|
||||
Bool glXQueryExtension(Display *dpy, int *errorBase, int *eventBase)
|
||||
{
|
||||
Bool (*_glXQueryExtension)(Display *dpy, int *errorBase, int *eventBase) = dlsym(RTLD_NEXT, "glXQueryExtension");
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
return _glXQueryExtension(dpy, errorBase, eventBase);
|
||||
}
|
||||
|
||||
GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attrib_list, int *nelements)
|
||||
{
|
||||
GLXFBConfig *(*_glXChooseFBConfig)(Display *dpy, int screen, const int *attrib_list, int *nelements) =
|
||||
dlsym(RTLD_NEXT, "glXChooseFBConfig");
|
||||
|
||||
char *__GLX_VENDOR_LIBRARY_NAME = getenv("__GLX_VENDOR_LIBRARY_NAME");
|
||||
char *__NV_PRIME_RENDER_OFFLOAD = getenv("__NV_PRIME_RENDER_OFFLOAD");
|
||||
if (__GLX_VENDOR_LIBRARY_NAME == NULL)
|
||||
__GLX_VENDOR_LIBRARY_NAME = " ";
|
||||
if (__NV_PRIME_RENDER_OFFLOAD == NULL)
|
||||
__NV_PRIME_RENDER_OFFLOAD = " ";
|
||||
|
||||
switch (gId)
|
||||
{
|
||||
case THE_HOUSE_OF_THE_DEAD_4_REVA:
|
||||
case THE_HOUSE_OF_THE_DEAD_4_REVB:
|
||||
case THE_HOUSE_OF_THE_DEAD_4_REVC:
|
||||
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL:
|
||||
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL_REVB:
|
||||
case THE_HOUSE_OF_THE_DEAD_EX:
|
||||
case TOO_SPICY:
|
||||
{
|
||||
if (strcmp(__GLX_VENDOR_LIBRARY_NAME, "nvidia") == 0 && strcmp(__NV_PRIME_RENDER_OFFLOAD, "1") == 0)
|
||||
{
|
||||
for (int i = 0; attrib_list[i] != None; i += 2)
|
||||
{
|
||||
if (attrib_list[i] == GLX_DOUBLEBUFFER)
|
||||
{
|
||||
int *ptr = (int *)&attrib_list[i + 1];
|
||||
patchMemory((intptr_t)ptr, "01");
|
||||
setVariable((intptr_t)ptr, GLX_DONT_CARE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return _glXChooseFBConfig(dpy, screen, attrib_list, nelements);
|
||||
}
|
||||
|
||||
GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
|
||||
{
|
||||
GLXContext (*_glXCreateNewContext)(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list,
|
||||
Bool direct) = dlsym(RTLD_NEXT, "glXCreateNewContext");
|
||||
|
||||
GLXContext ctx = _glXCreateNewContext(dpy, config, render_type, share_list, direct);
|
||||
|
||||
if (getConfig()->showDebugMessages)
|
||||
{
|
||||
// Enable OpenGL debug output
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(openglDebugCallback, NULL);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
GLXPbuffer glXCreateGLXPbufferSGIX(Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height,
|
||||
int *attrib_list)
|
||||
{
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
int pbufferAttribs[] = {GLX_PBUFFER_WIDTH,
|
||||
width,
|
||||
GLX_PBUFFER_HEIGHT,
|
||||
height,
|
||||
GLX_PRESERVED_CONTENTS,
|
||||
true,
|
||||
GLX_LARGEST_PBUFFER,
|
||||
true,
|
||||
None};
|
||||
return glXCreatePbuffer(dpy, config, pbufferAttribs);
|
||||
}
|
||||
|
||||
void glXDestroyGLXPbufferSGIX(Display *dpy, GLXPbufferSGIX pbuf)
|
||||
{
|
||||
if (sdlGame)
|
||||
return;
|
||||
glXDestroyPbuffer(dpy, pbuf);
|
||||
}
|
||||
|
||||
GLXFBConfigSGIX *glXChooseFBConfigSGIX(Display *dpy, int screen, int *attrib_list, int *nelements)
|
||||
{
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
int gId = getConfig()->crc32;
|
||||
char *__GLX_VENDOR_LIBRARY_NAME = getenv("__GLX_VENDOR_LIBRARY_NAME");
|
||||
char *__NV_PRIME_RENDER_OFFLOAD = getenv("__NV_PRIME_RENDER_OFFLOAD");
|
||||
if (__GLX_VENDOR_LIBRARY_NAME == NULL)
|
||||
__GLX_VENDOR_LIBRARY_NAME = " ";
|
||||
if (__NV_PRIME_RENDER_OFFLOAD == NULL)
|
||||
__NV_PRIME_RENDER_OFFLOAD = " ";
|
||||
|
||||
switch (gId)
|
||||
{
|
||||
case INITIALD_4_REVA:
|
||||
case INITIALD_4_REVB:
|
||||
case INITIALD_4_REVC:
|
||||
case INITIALD_4_REVD:
|
||||
case INITIALD_4_REVG:
|
||||
case INITIALD_4_EXP_REVB:
|
||||
case INITIALD_4_EXP_REVC:
|
||||
case INITIALD_4_EXP_REVD:
|
||||
{
|
||||
if (strcmp(__GLX_VENDOR_LIBRARY_NAME, "nvidia") == 0 && strcmp(__NV_PRIME_RENDER_OFFLOAD, "1") == 0)
|
||||
{
|
||||
for (int i = 0; attrib_list[i] != None; i += 2)
|
||||
{
|
||||
if (attrib_list[i] == GLX_DOUBLEBUFFER)
|
||||
{
|
||||
int *ptr = (int *)&attrib_list[i + 1];
|
||||
patchMemory((intptr_t)ptr, "01");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return glXChooseFBConfig(dpy, screen, attrib_list, nelements);
|
||||
}
|
||||
|
||||
int glXGetFBConfigAttribSGIX(Display *dpy, GLXFBConfigSGIX config, int attribute, int *value)
|
||||
{
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
return glXGetFBConfigAttrib(dpy, config, attribute, value);
|
||||
}
|
||||
|
||||
GLXContext glXCreateContextWithConfigSGIX(Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list,
|
||||
Bool direct)
|
||||
{
|
||||
if (sdlGame)
|
||||
return 0;
|
||||
return glXCreateNewContext(dpy, config, render_type, share_list, direct);
|
||||
}
|
||||
|
||||
const char *glXGetClientString(Display *dpy, int name)
|
||||
{
|
||||
const char *(*_glXGetClientString)(Display *dpy, int name) = dlsym(RTLD_NEXT, "glXGetClientString");
|
||||
if (sdlGame)
|
||||
return "SDLGame";
|
||||
return _glXGetClientString(dpy, name);
|
||||
}
|
||||
|
||||
Display *glXGetCurrentDisplay(void)
|
||||
{
|
||||
Display *(*_glXGetCurrentDisplay)(void) = dlsym(RTLD_NEXT, "glXGetCurrentDisplay");
|
||||
if (sdlGame)
|
||||
return (Display *)SDLwindow;
|
||||
return _glXGetCurrentDisplay();
|
||||
}
|
||||
|
||||
const char *glXQueryExtensionsString(Display *dpy, int screen)
|
||||
{
|
||||
const char *(*_glXQueryExtensionsString)(Display *dpy, int screen) = dlsym(RTLD_NEXT, "glXQueryExtensionsString");
|
||||
if (sdlGame)
|
||||
return "";
|
||||
return _glXQueryExtensionsString(dpy, screen);
|
||||
}
|
||||
|
||||
const char *glXQueryServerString(Display *dpy, int screen, int name)
|
||||
{
|
||||
const char *(*_glXQueryServerString)(Display *dpy, int screen, int name) = dlsym(RTLD_NEXT, "glXQueryServerString");
|
||||
if (sdlGame)
|
||||
return "";
|
||||
return _glXQueryServerString(dpy, screen, name);
|
||||
}
|
||||
|
||||
int glXGetVideoSyncSGI(uint *count)
|
||||
{
|
||||
static unsigned int frameCount = 0;
|
||||
*count = (frameCount++) / 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int glXGetRefreshRateSGI(unsigned int *rate)
|
||||
{
|
||||
*rate = 60;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int glXSwapIntervalSGI(int interval)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include <GL/glx.h>
|
||||
|
||||
void glXToSDLSwapBuffers(Display *dpy, GLXDrawable drawable);
|
||||
int glxSDLmyCreateWindow(int *param1);
|
||||
@@ -3,8 +3,10 @@
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <GL/gl.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -15,7 +17,7 @@ int getGPUVendorID()
|
||||
int vendorId;
|
||||
EmulatorConfig *config = getConfig();
|
||||
gettingGPUVendor = true;
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
@@ -23,11 +25,10 @@ int getGPUVendorID()
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;
|
||||
|
||||
SDL_Window *window =
|
||||
SDL_CreateWindow("OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, windowFlags);
|
||||
SDL_Window *window = SDL_CreateWindow("OpenGL", 640, 480, windowFlags);
|
||||
if (!window)
|
||||
{
|
||||
fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
fprintf(stderr, "Here Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -43,10 +44,12 @@ int getGPUVendorID()
|
||||
if (!config->GPUVendorString)
|
||||
{
|
||||
fprintf(stderr, "Error: Could not get renderer string\n");
|
||||
SDL_GL_DestroyContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
return ERROR_GPU;
|
||||
}
|
||||
gettingGPUVendor = false;
|
||||
SDL_GL_DestroyContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
if (strstr(config->GPUVendorString, "NVIDIA") != NULL)
|
||||
+630
-389
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BASEBOARD = 0,
|
||||
EEPROM = 1,
|
||||
SERIAL0 = 2,
|
||||
SERIAL1 = 3,
|
||||
PCI_CARD_000 = 4
|
||||
} DeviceType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CPUINFO = 0,
|
||||
OSRELEASE = 1,
|
||||
PCI_CARD_1F0 = 2,
|
||||
FILE_RW1 = 3,
|
||||
FILE_RW2 = 4,
|
||||
FILE_HARLEY = 5,
|
||||
FILE_FONT_ABC = 6,
|
||||
FILE_FONT_TGA = 7,
|
||||
FILE_LOGO_TGA = 8,
|
||||
ROUTE = 9
|
||||
} FileTypes;
|
||||
@@ -0,0 +1,251 @@
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "iniParser.h"
|
||||
#include "log.h"
|
||||
|
||||
/**
|
||||
* @brief Trims leading and trailing whitespace from a string in-place.
|
||||
* @param str The string to trim.
|
||||
* @return A pointer to the beginning of the trimmed string.
|
||||
*/
|
||||
static char *trimString(char *str)
|
||||
{
|
||||
char *end;
|
||||
while (isspace((unsigned char)*str))
|
||||
str++;
|
||||
if (*str == 0)
|
||||
return str;
|
||||
end = str + strlen(str) - 1;
|
||||
while (end > str && isspace((unsigned char)*end))
|
||||
end--;
|
||||
end[1] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loads and parses an INI file into an IniConfig structure.
|
||||
* This function reads an INI file, parsing its sections and key-value pairs.
|
||||
* It allocates memory for the configuration structure, which must be freed
|
||||
* later by calling iniFree().
|
||||
* @param filename The path to the INI file.
|
||||
* @return A pointer to the newly created IniConfig structure, or NULL on failure.
|
||||
*/
|
||||
IniConfig *iniLoad(const char *filename)
|
||||
{
|
||||
FILE *file = fopen(filename, "r");
|
||||
if (!file)
|
||||
{
|
||||
log_warn("Error opening INI file");
|
||||
return NULL;
|
||||
}
|
||||
IniConfig *config = calloc(1, sizeof(IniConfig));
|
||||
if (!config)
|
||||
{
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
char line[1024];
|
||||
IniSection *currentSection = NULL;
|
||||
while (fgets(line, sizeof(line), file))
|
||||
{
|
||||
char *trimmedLine = trimString(line);
|
||||
if (trimmedLine[0] == ';' || trimmedLine[0] == '#' || trimmedLine[0] == '\0')
|
||||
continue;
|
||||
if (trimmedLine[0] == '[' && trimmedLine[strlen(trimmedLine) - 1] == ']')
|
||||
{
|
||||
IniSection *newSections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
if (!newSections)
|
||||
{
|
||||
iniFree(config);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
config->sections = newSections;
|
||||
currentSection = &config->sections[config->numSections++];
|
||||
currentSection->numPairs = 0;
|
||||
currentSection->pairs = NULL;
|
||||
char *sectionName = trimmedLine + 1;
|
||||
sectionName[strlen(sectionName) - 1] = '\0';
|
||||
currentSection->name = strdup(sectionName);
|
||||
}
|
||||
else if (currentSection && strchr(trimmedLine, '='))
|
||||
{
|
||||
char *key = trimmedLine;
|
||||
char *value = strchr(trimmedLine, '=');
|
||||
*value = '\0';
|
||||
value++;
|
||||
key = trimString(key);
|
||||
value = trimString(value);
|
||||
IniKeyValuePair *newPairs = realloc(currentSection->pairs, (currentSection->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
if (!newPairs)
|
||||
{
|
||||
iniFree(config);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
currentSection->pairs = newPairs;
|
||||
IniKeyValuePair *pair = ¤tSection->pairs[currentSection->numPairs++];
|
||||
pair->key = strdup(key);
|
||||
pair->value = strdup(value);
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves a section from the INI configuration by its name.
|
||||
* @param config Pointer to the INI configuration structure.
|
||||
* @param section_name The name of the section to find.
|
||||
* @return A pointer to the IniSection if found, otherwise NULL.
|
||||
*/
|
||||
IniSection *iniGetSection(const IniConfig *config, const char *sectionName)
|
||||
{
|
||||
if (!config)
|
||||
return NULL;
|
||||
for (int i = 0; i < config->numSections; i++)
|
||||
{
|
||||
if (strcmp(config->sections[i].name, sectionName) == 0)
|
||||
return &config->sections[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Frees all memory associated with an IniConfig structure.
|
||||
* This includes all sections, key-value pairs, and their names.
|
||||
* @param config Pointer to the INI configuration structure to free.
|
||||
*/
|
||||
void iniFree(IniConfig *config)
|
||||
{
|
||||
if (!config)
|
||||
return;
|
||||
for (int i = 0; i < config->numSections; i++)
|
||||
{
|
||||
for (int j = 0; j < config->sections[i].numPairs; j++)
|
||||
{
|
||||
free(config->sections[i].pairs[j].key);
|
||||
free(config->sections[i].pairs[j].value);
|
||||
}
|
||||
free(config->sections[i].pairs);
|
||||
free(config->sections[i].name);
|
||||
}
|
||||
free(config->sections);
|
||||
free(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a key-value pair in a given section.
|
||||
* If the section does not exist, it is created.
|
||||
* If the key exists within the section, its value is updated.
|
||||
* If the key does not exist, it is created.
|
||||
* @param config Pointer to the INI configuration structure.
|
||||
* @param section_name The name of the section.
|
||||
* @param key The key to set.
|
||||
* @param value The value to associate with the key.
|
||||
*/
|
||||
bool iniSetValue(IniConfig *config, const char *sectionName, const char *key, const char *value)
|
||||
{
|
||||
if (!config || !sectionName || !key || !value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the section, or create it if it doesn't exist.
|
||||
IniSection *section = iniGetSection(config, sectionName);
|
||||
if (!section)
|
||||
{
|
||||
IniSection *newSections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
if (!newSections)
|
||||
return false;
|
||||
config->sections = newSections;
|
||||
section = &config->sections[config->numSections++];
|
||||
section->name = strdup(sectionName);
|
||||
section->pairs = NULL;
|
||||
section->numPairs = 0;
|
||||
}
|
||||
|
||||
// Find the key within the section.
|
||||
for (int i = 0; i < section->numPairs; i++)
|
||||
{
|
||||
if (strcmp(section->pairs[i].key, key) == 0)
|
||||
{
|
||||
// Key found, update the value.
|
||||
free(section->pairs[i].value);
|
||||
section->pairs[i].value = strdup(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Key not found, add a new key-value pair.
|
||||
IniKeyValuePair *newPairs = realloc(section->pairs, (section->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
if (!newPairs)
|
||||
return false;
|
||||
section->pairs = newPairs;
|
||||
IniKeyValuePair *pair = §ion->pairs[section->numPairs++];
|
||||
pair->key = strdup(key);
|
||||
pair->value = strdup(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Saves the in-memory INI configuration to a file.
|
||||
* NOTE: This will overwrite the existing file and will lose any comments
|
||||
* or special formatting from the original.
|
||||
* @param config Pointer to the INI configuration to save.
|
||||
* @param filename The path of the file to save to.
|
||||
* @return 0 on success, -1 on failure (e.g., cannot open file for writing).
|
||||
*/
|
||||
int iniSave(const IniConfig *config, const char *filename)
|
||||
{
|
||||
if (!config || !filename)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *file = fopen(filename, "w");
|
||||
if (!file)
|
||||
{
|
||||
perror("Error saving INI file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write each section and its key-value pairs
|
||||
for (int i = 0; i < config->numSections; i++)
|
||||
{
|
||||
IniSection *section = &config->sections[i];
|
||||
fprintf(file, "[%s]\n", section->name);
|
||||
for (int j = 0; j < section->numPairs; j++)
|
||||
{
|
||||
IniKeyValuePair *pair = §ion->pairs[j];
|
||||
fprintf(file, "%s = %s\n", pair->key, pair->value);
|
||||
}
|
||||
// Add a blank line between sections for readability
|
||||
if (i < config->numSections - 1)
|
||||
{
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *iniGetValue(const IniConfig *config, const char *sectionName, const char *key)
|
||||
{
|
||||
IniSection *section = iniGetSection(config, sectionName);
|
||||
if (!section)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < section->numPairs; i++)
|
||||
{
|
||||
if (strcmp(section->pairs[i].key, key) == 0)
|
||||
return section->pairs[i].value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *key;
|
||||
char *value;
|
||||
} IniKeyValuePair;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
IniKeyValuePair *pairs;
|
||||
int numPairs;
|
||||
} IniSection;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IniSection *sections;
|
||||
int numSections;
|
||||
} IniConfig;
|
||||
|
||||
IniConfig *iniLoad(const char *filename);
|
||||
void iniFree(IniConfig *config);
|
||||
const char *iniGetValue(const IniConfig *config, const char *section, const char *key);
|
||||
IniSection *iniGetSection(const IniConfig *config, const char *sectionName);
|
||||
bool iniSetValue(IniConfig *config, const char *section, const char *key, const char *value);
|
||||
int iniSave(const IniConfig *config, const char *filename);
|
||||
@@ -1,977 +0,0 @@
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#include "customcursor.h"
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "jvs.h"
|
||||
#include "config.h"
|
||||
#include "customcursor.h"
|
||||
#include "patch.h"
|
||||
#include "touchscreen.h"
|
||||
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/glx.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/xf86vmode.h>
|
||||
#include <dlfcn.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
GameType gameType = SHOOTING;
|
||||
int jvsAnalogueInBits = 10;
|
||||
extern int phX, phY, phW, phH;
|
||||
extern int phX2, phY2, phW2, phH2;
|
||||
int phIsDragging = 0;
|
||||
|
||||
extern void *customCursor;
|
||||
extern void *phTouchCursor;
|
||||
extern bool phShowCursorInGame;
|
||||
|
||||
int initInput()
|
||||
{
|
||||
gameType = getConfig()->gameType;
|
||||
jvsAnalogueInBits = getJVSIO()->capabilities.analogueInBits;
|
||||
|
||||
/* Center Steering wheel in racing games (need to add a check for when using real analog devices)*/
|
||||
if (gameType == DRIVING)
|
||||
{
|
||||
setAnalogue(ANALOGUE_1, pow(2, jvsAnalogueInBits) * 0.5);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void phTouchScreenCursor(int mX, int mY, int *motX, int *motY)
|
||||
{
|
||||
if (getConfig()->emulateTouchscreen)
|
||||
{
|
||||
switch (getConfig()->phMode)
|
||||
{
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
if ((mX >= phX2 && mX <= (phX2 + phW2) && mY >= phY2 && mY <= (phY2 + phH2)) && getConfig()->hideCursor == 0)
|
||||
{
|
||||
setCursor(phTouchCursor);
|
||||
showPhCursor();
|
||||
}
|
||||
else if (getConfig()->hideCursor == 1 || phShowCursorInGame == false)
|
||||
{
|
||||
hideCursor();
|
||||
}
|
||||
else if (phShowCursorInGame && getConfig()->hideCursor == 0)
|
||||
{
|
||||
setCursor(customCursor);
|
||||
showCursor();
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if ((mX >= phX2 && mX <= (phX2 + phW2) && mY >= (phY2 + phH) && mY <= (phY2 + (phH2 + phH))) && getConfig()->hideCursor == 0)
|
||||
{
|
||||
setCursor(phTouchCursor);
|
||||
showPhCursor();
|
||||
}
|
||||
else if (getConfig()->hideCursor == 1 || phShowCursorInGame == false)
|
||||
{
|
||||
hideCursor();
|
||||
}
|
||||
else if (phShowCursorInGame && getConfig()->hideCursor == 0)
|
||||
{
|
||||
setCursor(customCursor);
|
||||
showCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mX < phX)
|
||||
*motX = phX;
|
||||
if (mX > (phW + phX))
|
||||
*motX = (phW + phX) - 1;
|
||||
if (mY > phH + phY)
|
||||
*motY = (phH + phY) - 1;
|
||||
if (mY < phY)
|
||||
*motY = phY;
|
||||
}
|
||||
|
||||
void phTouchClick(int mX, int mY, int type)
|
||||
{
|
||||
int x, y;
|
||||
bool insideTouchScreen = false;
|
||||
switch (getConfig()->phMode)
|
||||
{
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
if (mX >= phX2 && mX <= (phX2 + phW2) && mY >= phY2 && mY <= (phY2 + phH2))
|
||||
{
|
||||
x = mX - phW;
|
||||
y = mY - phY2;
|
||||
insideTouchScreen = true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (mX >= phX2 && mX <= (phX2 + phW2) && mY >= (phY2 + phH) && mY <= (phY2 + (phH2 + phH)))
|
||||
{
|
||||
x = mX - phX2;
|
||||
y = mY - phH;
|
||||
insideTouchScreen = true;
|
||||
}
|
||||
}
|
||||
if (insideTouchScreen)
|
||||
{
|
||||
if (type == ButtonPress)
|
||||
phCoordinates(x, y, phW2, phH2, ButtonPress);
|
||||
else if (type == SDL_MOUSEBUTTONDOWN)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_MOUSEBUTTONDOWN);
|
||||
else if (type == ButtonRelease)
|
||||
phCoordinates(x, y, phW2, phH2, ButtonRelease);
|
||||
else if (type == SDL_MOUSEBUTTONUP)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_MOUSEBUTTONUP);
|
||||
else if (type == MotionNotify)
|
||||
phCoordinates(x, y, phW2, phH2, MotionNotify);
|
||||
else if (type == SDL_MOUSEMOTION)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_MOUSEMOTION);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getConfig()->noSDL)
|
||||
setSwitch(PLAYER_1, BUTTON_1, type == ButtonPress);
|
||||
else
|
||||
setSwitch(PLAYER_1, BUTTON_1, type == SDL_MOUSEBUTTONDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Button mapping used for driving games
|
||||
*/
|
||||
int XNextEventDriving(Display *display, XEvent *event_return, int returnValue)
|
||||
{
|
||||
KeyMapping keymap = getConfig()->keymap;
|
||||
switch (event_return->type)
|
||||
{
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
{
|
||||
if (event_return->xkey.keycode == keymap.test)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.service)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.coin)
|
||||
incrementCoin(PLAYER_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.up)
|
||||
setAnalogue(ANALOGUE_2, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.down)
|
||||
setAnalogue(ANALOGUE_3, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.left)
|
||||
setAnalogue(ANALOGUE_1, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) * 0.2
|
||||
: pow(2, jvsAnalogueInBits) * 0.5);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.right)
|
||||
setAnalogue(ANALOGUE_1, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) * 0.8
|
||||
: pow(2, jvsAnalogueInBits) * 0.5);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.start)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button1)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button2)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button3)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button4)
|
||||
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button5)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event_return->type == KeyPress); // Hummer - SegaTV view change
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button6)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button7)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button8)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.button1)
|
||||
setSwitch(PLAYER_2, BUTTON_1, event_return->type == KeyPress); // SegaTV boost
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.down)
|
||||
setSwitch(PLAYER_2, BUTTON_DOWN, event_return->type == KeyPress); // Hummer boost
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.up)
|
||||
setSwitch(PLAYER_2, BUTTON_UP, event_return->type == KeyPress);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Button mapping used for Harley
|
||||
*/
|
||||
int XNextEventHarley(Display *display, XEvent *event_return, int returnValue)
|
||||
{
|
||||
KeyMapping keymap = getConfig()->keymap;
|
||||
switch (event_return->type)
|
||||
{
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
{
|
||||
if (event_return->xkey.keycode == keymap.test)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.service)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.coin)
|
||||
incrementCoin(PLAYER_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.up)
|
||||
setAnalogue(ANALOGUE_1, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.down)
|
||||
setAnalogue(ANALOGUE_4, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.left)
|
||||
setAnalogue(ANALOGUE_2, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) * 0.2
|
||||
: pow(2, jvsAnalogueInBits) * 0.5);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.right)
|
||||
setAnalogue(ANALOGUE_2, event_return->type == KeyPress ? pow(2, jvsAnalogueInBits) * 0.8
|
||||
: pow(2, jvsAnalogueInBits) * 0.5);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.start)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button1)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button2)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button3)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button4)
|
||||
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Button mapping used for shooting games
|
||||
*/
|
||||
int XNextEventShooting(Display *display, XEvent *event_return, int returnValue)
|
||||
{
|
||||
KeyMapping keymap = getConfig()->keymap;
|
||||
switch (event_return->type)
|
||||
{
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
{
|
||||
if (event_return->xkey.keycode == keymap.test)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.service)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.coin)
|
||||
incrementCoin(PLAYER_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.start)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button1)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button2)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button3)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button4)
|
||||
{
|
||||
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
|
||||
|
||||
// For The House of the Dead 4's Accelerometer
|
||||
setAnalogue(ANALOGUE_5, 0);
|
||||
setAnalogue(ANALOGUE_6, 0);
|
||||
}
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.button5)
|
||||
setSwitch(PLAYER_1, BUTTON_5, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.up)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.down)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.left)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player1.right)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event_return->type == KeyPress);
|
||||
|
||||
// Player 2 Controls
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.service)
|
||||
setSwitch(PLAYER_2, BUTTON_SERVICE, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.coin)
|
||||
incrementCoin(PLAYER_2, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.start)
|
||||
setSwitch(PLAYER_2, BUTTON_START, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.button1)
|
||||
setSwitch(PLAYER_2, BUTTON_1, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.button2)
|
||||
setSwitch(PLAYER_2, BUTTON_2, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.button3)
|
||||
setSwitch(PLAYER_2, BUTTON_3, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.button4)
|
||||
setSwitch(PLAYER_2, BUTTON_4, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.button5)
|
||||
setSwitch(PLAYER_2, BUTTON_5, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.up)
|
||||
setSwitch(PLAYER_2, BUTTON_UP, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.down)
|
||||
setSwitch(PLAYER_2, BUTTON_DOWN, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.left)
|
||||
setSwitch(PLAYER_2, BUTTON_LEFT, event_return->type == KeyPress);
|
||||
|
||||
else if (event_return->xkey.keycode == keymap.player2.right)
|
||||
setSwitch(PLAYER_2, BUTTON_RIGHT, event_return->type == KeyPress);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
if (getConfig()->crc32 == LETS_GO_JUNGLE || getConfig()->crc32 == LETS_GO_JUNGLE_REVA)
|
||||
{
|
||||
setAnalogue(ANALOGUE_2, (1.0 - ((double)event_return->xmotion.x / (double)getConfig()->width)) *
|
||||
pow(2, jvsAnalogueInBits));
|
||||
setAnalogue(ANALOGUE_1, (1.0 - ((double)event_return->xmotion.y / (double)getConfig()->height)) *
|
||||
pow(2, jvsAnalogueInBits));
|
||||
}
|
||||
else if (getConfig()->crc32 == PRIMEVAL_HUNT)
|
||||
{
|
||||
int mX = event_return->xmotion.x;
|
||||
int mY = event_return->xmotion.y;
|
||||
|
||||
if (phIsDragging)
|
||||
phTouchClick(mX, mY, event_return->type);
|
||||
|
||||
int motX = mX, motY = mY;
|
||||
phTouchScreenCursor(mX, mY, &motX, &motY);
|
||||
|
||||
setAnalogue(ANALOGUE_1, ((double)(motX - phX) / (double)phW) * pow(2, jvsAnalogueInBits));
|
||||
setAnalogue(ANALOGUE_2, ((double)(motY - phY) / (double)phH) * pow(2, jvsAnalogueInBits));
|
||||
}
|
||||
else
|
||||
{
|
||||
setAnalogue(ANALOGUE_1,
|
||||
((double)event_return->xmotion.x / (double)getConfig()->width) * pow(2, jvsAnalogueInBits));
|
||||
setAnalogue(ANALOGUE_2,
|
||||
((double)event_return->xmotion.y / (double)getConfig()->height) * pow(2, jvsAnalogueInBits));
|
||||
}
|
||||
// For The House of the Dead 4's Accelerometer
|
||||
static int lastMouseX = 0;
|
||||
static int lastMouseY = 0;
|
||||
static const int shakeThresholdPixels = 100; // Adjust as needed
|
||||
int currentMouseX = event_return->xmotion.x;
|
||||
int currentMouseY = event_return->xmotion.y;
|
||||
|
||||
// Calculate movement distance
|
||||
int dx = currentMouseX - lastMouseX;
|
||||
int dy = currentMouseY - lastMouseY;
|
||||
|
||||
// Update last position
|
||||
lastMouseX = currentMouseX;
|
||||
lastMouseY = currentMouseY;
|
||||
|
||||
// Check if mouse movement exceeds shake threshold
|
||||
if (abs(dx) > shakeThresholdPixels || abs(dy) > shakeThresholdPixels)
|
||||
{
|
||||
|
||||
setAnalogue(ANALOGUE_5, 0);
|
||||
setAnalogue(ANALOGUE_6, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
setAnalogue(ANALOGUE_5, pow(2, jvsAnalogueInBits) / 2);
|
||||
setAnalogue(ANALOGUE_6, pow(2, jvsAnalogueInBits) / 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
{
|
||||
if (event_return->xbutton.button == 1) // Trigger
|
||||
{
|
||||
if (getConfig()->crc32 == PRIMEVAL_HUNT && getConfig()->emulateTouchscreen)
|
||||
{
|
||||
int mX = event_return->xmotion.x;
|
||||
int mY = event_return->xmotion.y;
|
||||
phTouchClick(mX, mY, event_return->type);
|
||||
phIsDragging = (event_return->type == ButtonPress ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == ButtonPress);
|
||||
}
|
||||
}
|
||||
else if (event_return->xbutton.button == 3) // Reload
|
||||
{
|
||||
if (getConfig()->crc32 == RAMBO || getConfig()->crc32 == TOO_SPICY)
|
||||
{
|
||||
setAnalogue(ANALOGUE_1, -1);
|
||||
setAnalogue(ANALOGUE_2, -1);
|
||||
}
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == ButtonPress);
|
||||
}
|
||||
else if (event_return->xbutton.button == 2) // Extra Gun Button
|
||||
{
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == ButtonPress);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
int XNextEvent(Display *display, XEvent *event_return)
|
||||
{
|
||||
int (*_XNextEvent)(Display *display, XEvent *event_return) = dlsym(RTLD_NEXT, "XNextEvent");
|
||||
int returnValue = _XNextEvent(display, event_return);
|
||||
|
||||
// Return now if we're not emulating JVS or if EVDEV-only mode is enabled
|
||||
if (!getConfig()->emulateJVS || getConfig()->inputMode == 2)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// Select the appropriate input mapping depending on the game
|
||||
switch (gameType)
|
||||
{
|
||||
case DRIVING:
|
||||
return XNextEventDriving(display, event_return, returnValue);
|
||||
break;
|
||||
|
||||
case HARLEY:
|
||||
return XNextEventHarley(display, event_return, returnValue);
|
||||
break;
|
||||
|
||||
case SHOOTING:
|
||||
case FIGHTING:
|
||||
default:
|
||||
return XNextEventShooting(display, event_return, returnValue);
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void sdlEventDriving(SDL_Event *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if (event->key.keysym.sym == SDLK_t)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_s)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_5)
|
||||
incrementCoin(PLAYER_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_1)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_UP)
|
||||
setAnalogue(ANALOGUE_2, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
else if (event->key.keysym.sym == SDLK_DOWN)
|
||||
setAnalogue(ANALOGUE_3, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
else if (event->key.keysym.sym == SDLK_LEFT)
|
||||
setAnalogue(ANALOGUE_1,
|
||||
event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.2 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_RIGHT)
|
||||
setAnalogue(ANALOGUE_1,
|
||||
event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.8 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_q)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_w)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_e)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_r)
|
||||
setSwitch(PLAYER_1, BUTTON_4, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_z)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event->type == SDL_KEYDOWN); // Hummer - SegaTV view change
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_x)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_c)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_v)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_a)
|
||||
setSwitch(PLAYER_2, BUTTON_1, event->type == SDL_KEYDOWN); // SegaTV boost
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_LCTRL)
|
||||
setSwitch(PLAYER_2, BUTTON_DOWN, event->type == SDL_KEYDOWN); // Hummer boost
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_SPACE)
|
||||
setSwitch(PLAYER_2, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sdlEventShooting(SDL_Event *event)
|
||||
{
|
||||
KeyMapping keymap = getConfig()->keymap;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if (event->key.keysym.sym == SDLK_t)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_s)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_5)
|
||||
incrementCoin(PLAYER_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_1)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_q)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_w)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_e)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_r)
|
||||
{
|
||||
setSwitch(PLAYER_1, BUTTON_4, event->type == SDL_KEYDOWN);
|
||||
|
||||
// For The House of the Dead 4's Accelerometer
|
||||
setAnalogue(ANALOGUE_5, 0);
|
||||
setAnalogue(ANALOGUE_6, 0);
|
||||
}
|
||||
else if (event->key.keysym.sym == SDLK_UP)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_DOWN)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_LEFT)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_RIGHT)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_2)
|
||||
setSwitch(PLAYER_2, BUTTON_START, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_z)
|
||||
setSwitch(PLAYER_2, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_x)
|
||||
setSwitch(PLAYER_2, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_c)
|
||||
setSwitch(PLAYER_2, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_v)
|
||||
setSwitch(PLAYER_2, BUTTON_4, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_u)
|
||||
setSwitch(PLAYER_2, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_i)
|
||||
setSwitch(PLAYER_2, BUTTON_DOWN, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_o)
|
||||
setSwitch(PLAYER_2, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_p)
|
||||
setSwitch(PLAYER_2, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
if (getConfig()->crc32 == LETS_GO_JUNGLE || getConfig()->crc32 == LETS_GO_JUNGLE_REVA)
|
||||
{
|
||||
setAnalogue(ANALOGUE_2,
|
||||
(1.0 - ((double)event->motion.x / (double)getConfig()->width)) * pow(2, jvsAnalogueInBits));
|
||||
setAnalogue(ANALOGUE_1,
|
||||
(1.0 - ((double)event->motion.y / (double)getConfig()->height)) * pow(2, jvsAnalogueInBits));
|
||||
}
|
||||
else if (getConfig()->crc32 == PRIMEVAL_HUNT)
|
||||
{
|
||||
int mX = event->motion.x;
|
||||
int mY = event->motion.y;
|
||||
|
||||
if (phIsDragging)
|
||||
phTouchClick(mX, mY, event->type);
|
||||
|
||||
int motX = mX, motY = mY;
|
||||
phTouchScreenCursor(mX, mY, &motX, &motY);
|
||||
|
||||
setAnalogue(ANALOGUE_1, ((double)(motX - phX) / (double)phW) * pow(2, jvsAnalogueInBits));
|
||||
setAnalogue(ANALOGUE_2, ((double)(motY - phY) / (double)phH) * pow(2, jvsAnalogueInBits));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
setAnalogue(ANALOGUE_1, ((double)event->motion.x / (double)getConfig()->width) * pow(2, jvsAnalogueInBits));
|
||||
setAnalogue(ANALOGUE_2,
|
||||
((double)event->motion.y / (double)getConfig()->height) * pow(2, jvsAnalogueInBits));
|
||||
}
|
||||
// For The House of the Dead 4's Accelerometer
|
||||
static int lastMouseX = 0;
|
||||
static int lastMouseY = 0;
|
||||
static const int shakeThresholdPixels = 100; // Adjust as needed
|
||||
int currentMouseX = event->motion.x;
|
||||
int currentMouseY = event->motion.y;
|
||||
|
||||
// Calculate movement distance
|
||||
int dx = currentMouseX - lastMouseX;
|
||||
int dy = currentMouseY - lastMouseY;
|
||||
|
||||
// Update last position
|
||||
lastMouseX = currentMouseX;
|
||||
lastMouseY = currentMouseY;
|
||||
|
||||
// Check if mouse movement exceeds shake threshold
|
||||
if (abs(dx) > shakeThresholdPixels || abs(dy) > shakeThresholdPixels)
|
||||
{
|
||||
|
||||
setAnalogue(ANALOGUE_5, 0);
|
||||
setAnalogue(ANALOGUE_6, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
setAnalogue(ANALOGUE_5, pow(2, jvsAnalogueInBits) / 2);
|
||||
setAnalogue(ANALOGUE_6, pow(2, jvsAnalogueInBits) / 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
if (event->button.button == SDL_BUTTON_LEFT) // Trigger
|
||||
{
|
||||
if (getConfig()->crc32 == PRIMEVAL_HUNT && getConfig()->emulateTouchscreen)
|
||||
{
|
||||
int mX = event->motion.x;
|
||||
int mY = event->motion.y;
|
||||
phTouchClick(mX, mY, event->type);
|
||||
phIsDragging = (event->type == SDL_MOUSEBUTTONDOWN ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSwitch(PLAYER_1, BUTTON_1, event->type == SDL_MOUSEBUTTONDOWN);
|
||||
}
|
||||
}
|
||||
else if (event->button.button == SDL_BUTTON_RIGHT) // Reload
|
||||
{
|
||||
if (getConfig()->crc32 == RAMBO || getConfig()->crc32 == TOO_SPICY)
|
||||
{
|
||||
setAnalogue(ANALOGUE_1, -1);
|
||||
setAnalogue(ANALOGUE_2, -1);
|
||||
}
|
||||
setSwitch(PLAYER_1, BUTTON_2, event->type == SDL_MOUSEBUTTONDOWN);
|
||||
}
|
||||
else if (event->button.button == SDL_BUTTON_MIDDLE) // Extra Gun Button
|
||||
{
|
||||
setSwitch(PLAYER_1, BUTTON_3, event->type == SDL_MOUSEBUTTONDOWN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sdlEventABC(SDL_Event *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if (event->key.keysym.sym == SDLK_t)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_s)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_5)
|
||||
incrementCoin(PLAYER_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_1)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_UP)
|
||||
setAnalogue(ANALOGUE_2,
|
||||
event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.2 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_DOWN)
|
||||
setAnalogue(ANALOGUE_2,
|
||||
event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.8 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_LEFT)
|
||||
setAnalogue(ANALOGUE_1,
|
||||
event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.2 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_RIGHT)
|
||||
setAnalogue(ANALOGUE_1,
|
||||
event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.8 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_q)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_w)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_e)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_f)
|
||||
setAnalogue(ANALOGUE_3, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.2 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_r)
|
||||
setAnalogue(ANALOGUE_3, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.8 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_z)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_x)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_c)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_v)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sdlEventHarley(SDL_Event *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if (event->key.keysym.sym == SDLK_t)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_s)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_5)
|
||||
incrementCoin(PLAYER_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_1)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_UP)
|
||||
setAnalogue(ANALOGUE_1, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
else if (event->key.keysym.sym == SDLK_DOWN)
|
||||
setAnalogue(ANALOGUE_4, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) - 1 : 0);
|
||||
else if (event->key.keysym.sym == SDLK_LEFT)
|
||||
setAnalogue(ANALOGUE_2, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.2 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_RIGHT)
|
||||
setAnalogue(ANALOGUE_2, event->type == SDL_KEYDOWN ? pow(2, jvsAnalogueInBits) * 0.8 : pow(2, jvsAnalogueInBits) * 0.5);
|
||||
else if (event->key.keysym.sym == SDLK_q)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_w)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_e)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_r)
|
||||
setSwitch(PLAYER_1, BUTTON_4, event->type == SDL_KEYDOWN);
|
||||
else if (event->key.keysym.sym == SDLK_z)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event->type == SDL_KEYDOWN); // Hummer - SegaTV view change
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_x)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_c)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_v)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_a)
|
||||
setSwitch(PLAYER_2, BUTTON_1, event->type == SDL_KEYDOWN); // SegaTV boost
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_LCTRL)
|
||||
setSwitch(PLAYER_2, BUTTON_DOWN, event->type == SDL_KEYDOWN); // Hummer boost
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_SPACE)
|
||||
setSwitch(PLAYER_2, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sdlEventMahjong(SDL_Event *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if (event->key.keysym.sym == SDLK_t)
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_s)
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_5)
|
||||
incrementCoin(PLAYER_1, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_1)
|
||||
setSwitch(PLAYER_1, BUTTON_START, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_a)
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_b)
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_c)
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_d)
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_e)
|
||||
setSwitch(PLAYER_1, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_f)
|
||||
setSwitch(PLAYER_1, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_g)
|
||||
setSwitch(PLAYER_1, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_h)
|
||||
setSwitch(PLAYER_1, BUTTON_4, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_i)
|
||||
setSwitch(PLAYER_1, BUTTON_5, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_j)
|
||||
setSwitch(PLAYER_1, BUTTON_6, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_k)
|
||||
setSwitch(PLAYER_1, BUTTON_7, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_l)
|
||||
setSwitch(PLAYER_2, BUTTON_RIGHT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_m)
|
||||
setSwitch(PLAYER_2, BUTTON_LEFT, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_n)
|
||||
setSwitch(PLAYER_2, BUTTON_UP, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_o)
|
||||
setSwitch(PLAYER_2, BUTTON_DOWN, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_p)
|
||||
setSwitch(PLAYER_2, BUTTON_1, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_q)
|
||||
setSwitch(PLAYER_2, BUTTON_2, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_r)
|
||||
setSwitch(PLAYER_2, BUTTON_3, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_u)
|
||||
setSwitch(PLAYER_2, BUTTON_4, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_v)
|
||||
setSwitch(PLAYER_2, BUTTON_5, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_w)
|
||||
setSwitch(PLAYER_2, BUTTON_6, event->type == SDL_KEYDOWN);
|
||||
|
||||
else if (event->key.keysym.sym == SDLK_x)
|
||||
setSwitch(PLAYER_2, BUTTON_7, event->type == SDL_KEYDOWN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSdlEvents(SDL_Event *event)
|
||||
{
|
||||
// Return now if we're not emulating JVS or if EVDEV-only mode is enabled
|
||||
if (!getConfig()->emulateJVS || getConfig()->inputMode == 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the appropriate input mapping depending on the game
|
||||
switch (gameType)
|
||||
{
|
||||
case DRIVING:
|
||||
sdlEventDriving(event);
|
||||
break;
|
||||
case SHOOTING:
|
||||
case FIGHTING:
|
||||
sdlEventShooting(event);
|
||||
break;
|
||||
case ABC:
|
||||
sdlEventABC(event);
|
||||
break;
|
||||
case HARLEY:
|
||||
sdlEventHarley(event);
|
||||
break;
|
||||
case MAHJONG:
|
||||
sdlEventMahjong(event);
|
||||
break;
|
||||
default:
|
||||
// return XNextEventShooting(display, event_return, returnValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
int initInput();
|
||||
void handleSdlEvents(SDL_Event *event);
|
||||
+4
-6
@@ -1,11 +1,9 @@
|
||||
#include "jvs.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h> /* POSIX threads API to create and manage threads in the program */
|
||||
|
||||
#include "jvs.h"
|
||||
#include "config.h"
|
||||
|
||||
/* The in and out packets used to read and write to and from*/
|
||||
JVSPacket inputPacket, outputPacket;
|
||||
|
||||
@@ -490,7 +488,7 @@ JVSStatus processPacket(int *packetSize)
|
||||
////printf("CMD_CONVEY_ID\n");
|
||||
size = 1;
|
||||
outputPacket.data[outputPacket.length++] = REPORT_SUCCESS;
|
||||
char idData[100];
|
||||
char idData[100] = {0};
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
idData[i] = (char)inputPacket.data[index + i];
|
||||
|
||||
+1
-4
@@ -1,5 +1,4 @@
|
||||
#ifndef JVS_H_
|
||||
#define JVS_H_
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -242,5 +241,3 @@ JVSIO* getJVSIO();
|
||||
int setSwitch(JVSPlayer player, JVSInput switchNumber, int value);
|
||||
int incrementCoin(JVSPlayer player, int amount);
|
||||
int setAnalogue(JVSInput channel, int value);
|
||||
|
||||
#endif // JVS_H_
|
||||
|
||||
+641
-392
File diff suppressed because it is too large
Load Diff
+1
-4
@@ -1,5 +1,4 @@
|
||||
#ifndef LINDBERGH_LOADER_LOG_H
|
||||
#define LINDBERGH_LOADER_LOG_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
@@ -46,5 +45,3 @@ LogFormattedMessage logFormatMessage(const char *message, va_list args);
|
||||
FILE* logGetStream();
|
||||
void logInitTimer();
|
||||
void logGetElapsedTime(long *seconds, long *milliseconds);
|
||||
|
||||
#endif //LINDBERGH_LOADER_LOG_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "motionboard.h"
|
||||
#include "motionBoard.h"
|
||||
|
||||
ssize_t motionboardRead(int fd, void *buf, size_t count) {
|
||||
return 0;
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
|
||||
// Function to send a command
|
||||
void send_command(int fd, char command)
|
||||
{
|
||||
// write(fd, &command, 1);
|
||||
}
|
||||
|
||||
void processGPOpacket(unsigned char data)
|
||||
{
|
||||
for (int i = 7; i >= 4; i--)
|
||||
{
|
||||
int bit = (data >> i) & 1;
|
||||
if (bit != 0)
|
||||
{
|
||||
// printf("bit = %d\n", i);
|
||||
}
|
||||
// printf("Bit %d: %d (0x%x)\n", i, bit, bit << i);
|
||||
// if (i == 4)
|
||||
// {
|
||||
// printf("%c - bit: %d\n", bit ? 'a' : 'A', bit);
|
||||
// send_command(fdHW210, bit ? 'a' : 'A');
|
||||
// usleep(500);
|
||||
// }
|
||||
// else if (i == 6)
|
||||
// {
|
||||
// printf("%c - bit: %d\n", bit ? 'b' : 'B', bit);
|
||||
// send_command(fdHW210, bit ? 'b' : 'B');
|
||||
// usleep(500);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
void send_command(int fd, char command);
|
||||
void processGPOpacket(unsigned char data);
|
||||
+29
-17
@@ -11,7 +11,7 @@
|
||||
#include "passthrough.h"
|
||||
|
||||
#define TIMEOUT_SELECT 200
|
||||
#define CTS_ON_RETRY 20
|
||||
#define CTS_ON_RETRY 100
|
||||
|
||||
// Used to read JVS frame in a non-blocking way
|
||||
JVSFrame jvsFrameBuffer;
|
||||
@@ -23,12 +23,14 @@ pthread_mutex_t jvsBuffer_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
* @param jvsPath The serial port path. Ex: "/dev/ttyS3"
|
||||
* @return A file descriptor
|
||||
*/
|
||||
int openJVSSerial(char *jvsPath) {
|
||||
int openJVSSerial(char *jvsPath)
|
||||
{
|
||||
int jvsFileDescriptor = -1;
|
||||
|
||||
// TODO: check O_NOCTTY declaration
|
||||
jvsFileDescriptor = open(jvsPath, O_RDWR | O_NOCTTY);
|
||||
if (jvsFileDescriptor < 0) {
|
||||
if (jvsFileDescriptor < 0)
|
||||
{
|
||||
printf("Failed to open '%s' for JVS.\n", jvsPath);
|
||||
}
|
||||
|
||||
@@ -41,7 +43,8 @@ int openJVSSerial(char *jvsPath) {
|
||||
* @param fd
|
||||
* @return 0|1
|
||||
*/
|
||||
int initJVSSerial(int fd) {
|
||||
int initJVSSerial(int fd)
|
||||
{
|
||||
struct termios options;
|
||||
int status;
|
||||
|
||||
@@ -94,7 +97,7 @@ int initJVSSerial(int fd) {
|
||||
options.c_cc[VMIN] = 0;
|
||||
options.c_cc[VTIME] = 1;
|
||||
|
||||
|
||||
// options.c_cflag |= CREAD; // Turn on READ, let ctrl lines work
|
||||
tcsetattr(fd, TCSANOW, &options);
|
||||
|
||||
/* No use ? Save it for later
|
||||
@@ -108,22 +111,26 @@ int initJVSSerial(int fd) {
|
||||
|
||||
|
||||
/**
|
||||
* The DCD (Data Carrier Detect) status of a serial port indicates whether a carrier is present on the line, meaning that a connection has been established with another device.
|
||||
* The DCD (Data Carrier Detect) status of a serial port indicates whether a carrier is present on the line, meaning that a connection has
|
||||
* been established with another device.
|
||||
* @param fd File descriptor of JVS (serial) port
|
||||
* @return 0|1 According to control line status
|
||||
*/
|
||||
int getDCD(int fd) {
|
||||
int getDCD(int fd)
|
||||
{
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
return (status & TIOCM_CAR) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The DSR (Data Set Ready) status of a serial port indicates whether the device at the other end of the connection is ready to receive data.
|
||||
* The DSR (Data Set Ready) status of a serial port indicates whether the device at the other end of the connection is ready to receive
|
||||
* data.
|
||||
* @param fd File descriptor of JVS (serial) port
|
||||
* @return 0|1 According to control line status
|
||||
*/
|
||||
int getDSR(int fd) {
|
||||
int getDSR(int fd)
|
||||
{
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
return (status & TIOCM_DSR) != 0;
|
||||
@@ -134,7 +141,8 @@ int getDSR(int fd) {
|
||||
* @param fd File descriptor of JVS (serial) port
|
||||
* @return 0|1 According to control line status
|
||||
*/
|
||||
int getCTS(int fd) {
|
||||
int getCTS(int fd)
|
||||
{
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
return (status & TIOCM_CTS) != 0;
|
||||
@@ -164,7 +172,7 @@ void *readJVSFrameThread(void * arg)
|
||||
// printf("SERIAL thread debug: trying to read byte.\n");
|
||||
// Try to read a byte from serial, this call will be blocking if VMIN > 0 and VTIME = 0
|
||||
bytesRead = read(fd, &localBuffer[byteCount], 1);
|
||||
|
||||
|
||||
// If nothing on serial and CTS is ON, we try to read "CTS_ON_RETRY" times
|
||||
ctsRetry = CTS_ON_RETRY;
|
||||
while (bytesRead < 1 && --ctsRetry > 0 && jvsFrameBuffer.ready == 0) {
|
||||
@@ -212,8 +220,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 +244,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);
|
||||
|
||||
+3427
-3062
File diff suppressed because it is too large
Load Diff
+2
-12
@@ -1,14 +1,4 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
int initPatch();
|
||||
void setVariable(size_t address, size_t value);
|
||||
void patchMemory(size_t address, char *value);
|
||||
void detourFunction(size_t address, void *function);
|
||||
void replaceCallAtAddress(size_t address, void *function);
|
||||
void stubReturn();
|
||||
int stubRetOne();
|
||||
int stubRetMinusOne();
|
||||
char stubRetZeroChar();
|
||||
|
||||
int patchedPrintf(char *format,...);
|
||||
int patchedPuts(char *s);
|
||||
void callLoadCard();
|
||||
void callEjectCard();
|
||||
@@ -0,0 +1,217 @@
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "flowControl.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
char *rtunedIPs[4];
|
||||
char *rtunedSortedIPs[4];
|
||||
u_int8_t rtunedLinkId = 0;
|
||||
uint8_t rtunedCabs = 0;
|
||||
|
||||
void hummerSetIPs(size_t firstAddress)
|
||||
{
|
||||
if (!getConfig()->enableNetworkPatches)
|
||||
return;
|
||||
|
||||
if (strcmp(getConfig()->IpCab1, "") != 0)
|
||||
setVariable(firstAddress, (size_t)(void *)getConfig()->IpCab1);
|
||||
if (strcmp(getConfig()->IpCab2, "") != 0)
|
||||
setVariable(firstAddress + 4, (size_t)(void *)getConfig()->IpCab2);
|
||||
if (strcmp(getConfig()->IpCab3, "") != 0)
|
||||
setVariable(firstAddress + 8, (size_t)(void *)getConfig()->IpCab3);
|
||||
if (strcmp(getConfig()->IpCab4, "") != 0)
|
||||
setVariable(firstAddress + 12, (size_t)(void *)getConfig()->IpCab4);
|
||||
}
|
||||
|
||||
int rtunedGetCabs()
|
||||
{
|
||||
return rtunedCabs;
|
||||
}
|
||||
|
||||
void rtunedGetLinkIdandCabs()
|
||||
{
|
||||
FILE *f = fopen(getConfig()->sramPath, "rb");
|
||||
if (f == NULL)
|
||||
return;
|
||||
fseek(f, 0xc011, SEEK_SET);
|
||||
fread(&rtunedLinkId, 1, 1, f);
|
||||
fseek(f, 0xc019, SEEK_SET);
|
||||
fread(&rtunedCabs, 1, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void rtunedRotateIps(char *ori[], char *dst[], int total, int pos)
|
||||
{
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
dst[i] = ori[(pos + i) % total];
|
||||
}
|
||||
dst[total - 1] = ori[pos - 1];
|
||||
}
|
||||
|
||||
void rtunedSetIPs()
|
||||
{
|
||||
if (!getConfig()->enableNetworkPatches)
|
||||
return;
|
||||
|
||||
for (int x = 0; x < 4; x++)
|
||||
{
|
||||
rtunedIPs[x] = "0.0.0.0";
|
||||
rtunedSortedIPs[x] = "0.0.0.0";
|
||||
}
|
||||
|
||||
uint8_t setIps = 0;
|
||||
if (strcmp(getConfig()->IpCab1, "") != 0)
|
||||
{
|
||||
rtunedIPs[0] = getConfig()->IpCab1;
|
||||
setIps++;
|
||||
}
|
||||
if (strcmp(getConfig()->IpCab2, "") != 0)
|
||||
{
|
||||
rtunedIPs[1] = getConfig()->IpCab2;
|
||||
setIps++;
|
||||
}
|
||||
if (strcmp(getConfig()->IpCab3, "") != 0)
|
||||
{
|
||||
rtunedIPs[2] = getConfig()->IpCab3;
|
||||
setIps++;
|
||||
}
|
||||
if (strcmp(getConfig()->IpCab4, "") != 0)
|
||||
{
|
||||
rtunedIPs[3] = getConfig()->IpCab4;
|
||||
setIps++;
|
||||
}
|
||||
if (setIps != rtunedCabs)
|
||||
log_warn("Warning: You have %d IPs set in the config file and %d Cabinets set in the TestMenu.", setIps, rtunedCabs);
|
||||
|
||||
rtunedRotateIps(rtunedIPs, rtunedSortedIPs, rtunedCabs, rtunedLinkId);
|
||||
}
|
||||
|
||||
int rtunedBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
if (rtunedCabs > 1)
|
||||
{
|
||||
int idx1 = 0;
|
||||
int idx2 = 0;
|
||||
for (int i = 0; i < rtunedCabs; i++)
|
||||
{
|
||||
setVariable(0x0851e890 + idx1, inet_addr(rtunedSortedIPs[i]));
|
||||
setVariable(0x0851d7c0 + idx2, inet_addr(rtunedIPs[i]));
|
||||
idx1 += 16;
|
||||
idx2 += 4;
|
||||
}
|
||||
uint32_t ip = inet_addr(rtunedIPs[rtunedLinkId - 1]);
|
||||
uint32_t invIp = ((ip & 0xFF) << 24) | ((ip & 0xFF00) << 8) | ((ip & 0xFF0000) >> 8) | ((ip & 0xFF000000) >> 24);
|
||||
uint32_t invIpBcast = ((ip & 0xFF) << 24) | ((ip & 0xFF00) << 8) | ((ip & 0xFF0000) >> 8) | 0x000000FF;
|
||||
setVariable(0x0851bf90, invIp);
|
||||
setVariable(0x0851bf98, invIpBcast);
|
||||
}
|
||||
return bind(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
int inet_aton(const char *cp, struct in_addr *inp)
|
||||
{
|
||||
int (*_inet_aton)(const char *cp, struct in_addr *inp) = dlsym(RTLD_NEXT, "inet_aton");
|
||||
|
||||
if (gId == HARLEY_DAVIDSON)
|
||||
{
|
||||
if (getConfig()->enableNetworkPatches)
|
||||
{
|
||||
if ((strcmp(cp, "192.168.1.1") == 0 || strcmp(cp, "192.168.1.5") == 0 || strcmp(cp, "192.168.1.9") == 0))
|
||||
{
|
||||
if (strcmp(getConfig()->IpCab1, "") != 0)
|
||||
return _inet_aton(getConfig()->IpCab1, inp);
|
||||
else
|
||||
return _inet_aton("127.0.0.1", inp);
|
||||
}
|
||||
if ((strcmp(cp, "192.168.1.2") == 0 || strcmp(cp, "192.168.1.7") == 0 || strcmp(cp, "192.168.1.10") == 0) &&
|
||||
strcmp(getConfig()->IpCab2, "") != 0)
|
||||
{
|
||||
return _inet_aton(getConfig()->IpCab2, inp);
|
||||
}
|
||||
if ((strcmp(cp, "192.168.1.3") == 0 || strcmp(cp, "192.168.1.7") == 0 || strcmp(cp, "192.168.1.11") == 0) &&
|
||||
strcmp(getConfig()->IpCab3, "") != 0)
|
||||
{
|
||||
return _inet_aton(getConfig()->IpCab4, inp);
|
||||
}
|
||||
if ((strcmp(cp, "192.168.1.4") == 0 || strcmp(cp, "192.168.1.8") == 0 || strcmp(cp, "192.168.1.12") == 0) &&
|
||||
strcmp(getConfig()->IpCab4, "") != 0)
|
||||
{
|
||||
return _inet_aton(getConfig()->IpCab4, inp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _inet_aton("127.0.0.1", inp);
|
||||
}
|
||||
}
|
||||
return _inet_aton(cp, inp);
|
||||
}
|
||||
|
||||
in_addr_t inet_addr(const char *cp)
|
||||
{
|
||||
in_addr_t (*_inet_addr)(const char *cp) = dlsym(RTLD_NEXT, "inet_addr");
|
||||
|
||||
if (getConfig()->enableNetworkPatches)
|
||||
{
|
||||
if (gId == TOO_SPICY)
|
||||
{
|
||||
if (strcmp("10.0.0.1", cp) == 0 && strcmp(getConfig()->tooSpicyIpCab1, "") != 0)
|
||||
{
|
||||
cp = getConfig()->tooSpicyIpCab1;
|
||||
}
|
||||
else if (strcmp("10.0.0.2", cp) == 0 && strcmp(getConfig()->tooSpicyIpCab2, "") != 0)
|
||||
{
|
||||
cp = getConfig()->tooSpicyIpCab2;
|
||||
}
|
||||
}
|
||||
else if (gId == SEGA_RACE_TV)
|
||||
{
|
||||
// Standalone = 4, Main = 0, 2-Sub = 1, 3-Sub = 2 and 4-Sub = 3
|
||||
if (strcmp("192.168.49.1", cp) == 0 || strcmp("192.168.49.2", cp) == 0 || strcmp("192.168.49.3", cp) == 0 ||
|
||||
strcmp("192.168.49.4", cp) == 0)
|
||||
{
|
||||
if (strcmp(getConfig()->srtvIP, "") != 0)
|
||||
cp = getConfig()->srtvIP;
|
||||
else
|
||||
// Prevents the game to crash if the IP does not match the PC's IP.
|
||||
cp = "127.0.0.1";
|
||||
}
|
||||
}
|
||||
}
|
||||
return _inet_addr(cp);
|
||||
}
|
||||
|
||||
char *strstr(const char *haystack, const char *needle)
|
||||
{
|
||||
char *(*_strstr)(const char *__haystack, const char *__needle) = dlsym(RTLD_NEXT, "strstr");
|
||||
|
||||
if (strcmp(needle, "eth0") == 0 && getConfig()->enableNetworkPatches && strcmp(getConfig()->nicName, "") != 0)
|
||||
{
|
||||
return _strstr(haystack, getConfig()->nicName);
|
||||
}
|
||||
return _strstr(haystack, needle);
|
||||
}
|
||||
|
||||
int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop)
|
||||
{
|
||||
int (*_gethostbyname_r)(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop) =
|
||||
dlsym(RTLD_NEXT, "gethostbyname_r");
|
||||
|
||||
void *addr = __builtin_return_address(0);
|
||||
if (gId == QUIZ_AXA && addr == (void *)0x0832609d)
|
||||
{
|
||||
ret = 0;
|
||||
buf[0] = '\0';
|
||||
result = 0;
|
||||
h_errnop = 0;
|
||||
return -1;
|
||||
}
|
||||
return _gethostbyname_r(name, ret, buf, buflen, result, h_errnop);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void hummerSetIPs(size_t firstAddress);
|
||||
int rtunedGetCabs();
|
||||
void rtunedGetLinkIdandCabs();
|
||||
void rtunedSetIPs();
|
||||
int rtunedBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
+2304
-1942
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,4 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/serial.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "rideboard.h"
|
||||
#include "rideBoard.h"
|
||||
|
||||
RideState rideState = {0};
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
#ifndef __i386__
|
||||
#define __i386__
|
||||
#endif
|
||||
#undef __x86_64__
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <dlfcn.h>
|
||||
// #include <fontconfig/fontconfig.h>
|
||||
#include <GL/glx.h>
|
||||
#include <GL/freeglut.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_error.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "blitStretching.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "customCursor.h"
|
||||
#include "sdlInput.h"
|
||||
#include "jvs.h"
|
||||
#include "resources/LiberationMono-Regular.h"
|
||||
#include "log.h"
|
||||
#include "sdlCalls.h"
|
||||
#include "wiimoteEvdev.h"
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern int gWidth;
|
||||
extern int gHeight;
|
||||
extern int drawableW;
|
||||
extern int drawableH;
|
||||
|
||||
bool creatingWindow = false;
|
||||
SDL_Window *sdlWindow = NULL;
|
||||
SDL_GLContext sdlContext = NULL;
|
||||
Display *x11Display = NULL;
|
||||
Window x11Window;
|
||||
char sdlGameTitle[256] = {0};
|
||||
bool sdlInputInitialized = false;
|
||||
|
||||
extern SDL_Cursor *customCursor;
|
||||
extern SDL_Cursor *touchCursor;
|
||||
|
||||
int glutInitialized = 0;
|
||||
|
||||
bool isFullScreen = false;
|
||||
bool sdlFontInit = false;
|
||||
SDL_Renderer *fontRenderer;
|
||||
TTF_Font *font;
|
||||
|
||||
void GLAPIENTRY openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message,
|
||||
const void *userParam)
|
||||
{
|
||||
if (id == 1099)
|
||||
return;
|
||||
printf("OpenGL Debug Message:\n");
|
||||
printf("Source: 0x%x\n", source);
|
||||
printf("Type: 0x%x\n", type);
|
||||
printf("ID: %u\n", id);
|
||||
printf("Severity: 0x%x\n", severity);
|
||||
printf("Message: %s\n", message);
|
||||
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH)
|
||||
{
|
||||
printf("This is a high severity error!\n");
|
||||
}
|
||||
}
|
||||
|
||||
int initSDL()
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD))
|
||||
{
|
||||
log_error("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (gGrp == GROUP_OUTRUN_TEST)
|
||||
{
|
||||
if (!TTF_Init())
|
||||
{
|
||||
log_error("SDL_ttf could not initialize! SDL_ttf Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
SDL_IOStream *rw = SDL_IOFromConstMem(LiberationMonoRegular_ttf, LiberationMonoRegular_ttf_length);
|
||||
float fontSize = 16.0;
|
||||
if (gWidth > 640)
|
||||
fontSize = fontSize * (((float)gWidth / 640.0f) + ((float)gHeight / 480.0f)) / 2.0f;
|
||||
|
||||
font = TTF_OpenFontIO(rw, 1, fontSize);
|
||||
fontRenderer = SDL_CreateRenderer(sdlWindow, "");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void startSDL(int *argcp, char **argv)
|
||||
{
|
||||
creatingWindow = true;
|
||||
int numDisplays;
|
||||
SDL_DisplayID *sdlDisplayId = SDL_GetDisplays(&numDisplays);
|
||||
if (numDisplays > 1)
|
||||
log_warn("More than 1 display detected, will use the first one.\n");
|
||||
|
||||
const SDL_DisplayMode *displayMode = SDL_GetCurrentDisplayMode(sdlDisplayId[0]);
|
||||
SDL_free(sdlDisplayId);
|
||||
if (displayMode == NULL)
|
||||
{
|
||||
log_error("SDL_GetCurrentDisplayMode Error: %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Double buffering
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); // 24-bit depth buffer
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); // Set the alpha size to 8 bits
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
|
||||
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
||||
|
||||
if (gGrp == GROUP_ABC || gGrp == GROUP_VF5 || gId == R_TUNED || gId == GHOST_SQUAD_EVOLUTION || gId == SEGA_RACE_TV ||
|
||||
gGrp == GROUP_MJ4 )
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
|
||||
}
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;
|
||||
|
||||
strcat(sdlGameTitle, getGameName());
|
||||
|
||||
// Hacky way to make AxA games render the characters properly
|
||||
if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE)
|
||||
sdlWindow = SDL_CreateWindow(sdlGameTitle, 1024, 768, windowFlags);
|
||||
else
|
||||
sdlWindow = SDL_CreateWindow(sdlGameTitle, gWidth, gHeight, windowFlags);
|
||||
|
||||
if (!sdlWindow)
|
||||
{
|
||||
SDL_Quit();
|
||||
log_error("Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (gId == GHOST_SQUAD_EVOLUTION || gGrp == GROUP_MJ4)
|
||||
SDL_SetWindowSize(sdlWindow, gWidth, gHeight + 1);
|
||||
|
||||
x11Display = (Display *)SDL_GetPointerProperty(SDL_GetWindowProperties(sdlWindow), SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
|
||||
x11Window = (Window)SDL_GetNumberProperty(SDL_GetWindowProperties(sdlWindow), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
if (!x11Display || !x11Window)
|
||||
{
|
||||
log_error("This program is not running on X11 or failed to get window/display.\n");
|
||||
}
|
||||
|
||||
sdlContext = SDL_GL_CreateContext(sdlWindow);
|
||||
if (!sdlContext)
|
||||
{
|
||||
SDL_DestroyWindow(sdlWindow);
|
||||
SDL_Quit();
|
||||
log_error("OpenGL context could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (getConfig()->showDebugMessages)
|
||||
{
|
||||
// Enable OpenGL debug output
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(openglDebugCallback, NULL);
|
||||
}
|
||||
|
||||
// If games are any of the LGJ or Primeval Hunt, we prevent the window to resize.
|
||||
if ((gGrp == GROUP_LGJ || gId == PRIMEVAL_HUNT) && !getConfig()->fullscreen)
|
||||
SDL_SetWindowMaximumSize(sdlWindow, gWidth, gHeight);
|
||||
else if (gGrp != GROUP_LGJ)
|
||||
SDL_SetWindowMaximumSize(sdlWindow, displayMode->w, displayMode->h);
|
||||
|
||||
SDL_SetWindowMinimumSize(sdlWindow, gWidth, gHeight);
|
||||
|
||||
// Hacky way to make AxA games render the characters properly
|
||||
if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE)
|
||||
SDL_SetWindowPosition(sdlWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||
|
||||
if (getConfig()->fullscreen)
|
||||
{
|
||||
SDL_SetWindowFullscreenMode(sdlWindow, NULL);
|
||||
SDL_SetWindowFullscreen(sdlWindow, true);
|
||||
isFullScreen = true;
|
||||
}
|
||||
|
||||
initBlitting();
|
||||
|
||||
if (gId != PRIMEVAL_HUNT && gGrp != GROUP_LGJ)
|
||||
SDL_SetWindowResizable(sdlWindow, true);
|
||||
|
||||
SDL_ShowWindow(sdlWindow);
|
||||
|
||||
Uint64 startTime = SDL_GetTicks();
|
||||
int running = 1;
|
||||
|
||||
// We clear the window background
|
||||
while (running)
|
||||
{
|
||||
if (SDL_GetTicks() - startTime >= 1000)
|
||||
running = 0;
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
SDL_GL_SwapWindow(sdlWindow);
|
||||
}
|
||||
|
||||
creatingWindow = false;
|
||||
|
||||
printf(" RESOLUTION: %dx%d\n", gWidth, gHeight);
|
||||
|
||||
loadCursors();
|
||||
if (customCursor)
|
||||
setCursor(customCursor);
|
||||
|
||||
if (getConfig()->enableCrosshairs &&
|
||||
(gGrp == GROUP_HOD4 || gGrp == GROUP_HOD4_TEST || gGrp == GROUP_HOD4_SP || gGrp == GROUP_HOD4_SP_TEST || gGrp == GROUP_RAMBO ||
|
||||
gId == GHOST_SQUAD_EVOLUTION || gId == PRIMEVAL_HUNT))
|
||||
initCrossHairs();
|
||||
|
||||
if ((gGrp == GROUP_MJ4 || gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE) && strcmp(getConfig()->touchCursor, "") != 0 &&
|
||||
getConfig()->emulateTouchscreen)
|
||||
setCursor(touchCursor);
|
||||
else if (getConfig()->hideCursor)
|
||||
SDL_HideCursor();
|
||||
}
|
||||
|
||||
void sdlQuit()
|
||||
{
|
||||
setSwitch(SYSTEM, BUTTON_TEST, 1);
|
||||
usleep(50000);
|
||||
setSwitch(SYSTEM, BUTTON_TEST, 0);
|
||||
usleep(50000);
|
||||
|
||||
if (TTF_WasInit())
|
||||
{
|
||||
if (font)
|
||||
{
|
||||
TTF_CloseFont(font);
|
||||
font = NULL;
|
||||
}
|
||||
TTF_Quit();
|
||||
}
|
||||
if (fontRenderer)
|
||||
{
|
||||
SDL_DestroyRenderer(fontRenderer);
|
||||
fontRenderer = NULL;
|
||||
}
|
||||
|
||||
if (sdlContext)
|
||||
{
|
||||
SDL_GL_DestroyContext(sdlContext);
|
||||
sdlContext = NULL;
|
||||
}
|
||||
|
||||
if (sdlWindow)
|
||||
{
|
||||
SDL_DestroyWindow(sdlWindow);
|
||||
sdlWindow = NULL;
|
||||
}
|
||||
SDL_Quit();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void pollEvents()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
if (event.type == SDL_WIIMOTION_EVENT)
|
||||
processSdlEvent(&event);
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
{
|
||||
if (gGrp != GROUP_LGJ && gId != PRIMEVAL_HUNT)
|
||||
{
|
||||
SDL_Keymod mod = SDL_GetModState();
|
||||
if ((event.key.key == SDLK_RETURN && (mod & SDL_KMOD_ALT)) || event.key.key == SDLK_F11)
|
||||
{
|
||||
if (isFullScreen)
|
||||
{
|
||||
if (!SDL_SetWindowFullscreen(sdlWindow, 0))
|
||||
{
|
||||
log_error("Error setting windowed mode: %s\n", SDL_GetError());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((long long)gWidth * 3 == (long long)gHeight * 4)
|
||||
SDL_SetWindowSize(sdlWindow, gWidth + 1, gHeight);
|
||||
else
|
||||
SDL_SetWindowSize(sdlWindow, gWidth, gHeight + 1);
|
||||
isFullScreen = false;
|
||||
if (getConfig()->fullscreen)
|
||||
SDL_SetWindowBordered(sdlWindow, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SDL_SetWindowFullscreen(sdlWindow, true))
|
||||
{
|
||||
log_error("Error setting fullscreen mode: %s\n", SDL_GetError());
|
||||
}
|
||||
else
|
||||
{
|
||||
isFullScreen = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
case SDL_EVENT_KEY_UP:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
||||
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
||||
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
|
||||
case SDL_EVENT_JOYSTICK_HAT_MOTION:
|
||||
processSdlEvent(&event);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
sdlQuit();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
{
|
||||
if (((long long)gWidth * 3 == (long long)gHeight * 4) || gGrp == GROUP_HUMMER)
|
||||
SDL_SetWindowSize(sdlWindow, gWidth + 1, gHeight);
|
||||
else
|
||||
SDL_SetWindowSize(sdlWindow, gWidth, gHeight + 1);
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||
processSdlEvent(&event);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sdlInputInitialized)
|
||||
{
|
||||
if (gGrp == GROUP_HOD4 || gGrp == GROUP_HOD4_TEST)
|
||||
updateGunShake();
|
||||
updateCombinedAxes();
|
||||
processChangedActions();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <GL/gl.h>
|
||||
|
||||
int initSDL();
|
||||
void startSDL(int *argcp, char **argv);
|
||||
void pollEvents();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user