mirror of
https://github.com/lindbergh-loader/lindbergh-loader.git
synced 2026-10-13 17:48:27 +03:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4d31b6522 | ||
|
|
88e1628382 | ||
|
|
6918990746 | ||
|
|
87dccf51a4 | ||
|
|
81d9185227 | ||
|
|
510d13398e | ||
|
|
973cfb7058 | ||
|
|
c47a4a6889 | ||
|
|
4d44d95397 | ||
|
|
da0316d5a6 | ||
|
|
9179d0e282 | ||
|
|
d5e370240c | ||
|
|
3b7d10514f | ||
|
|
01c2c5b765 | ||
|
|
a0e504c4c9 | ||
|
|
3ad93675e0 | ||
|
|
d09c6c72e1 | ||
|
|
32759cd01d | ||
|
|
f0e9a22b24 | ||
|
|
cf33403979 | ||
|
|
ab20dd9420 | ||
|
|
96188c69f5 | ||
|
|
73d2131c73 | ||
|
|
813ea1c49d | ||
|
|
6c3c9a103e | ||
|
|
85548fc01e | ||
|
|
aa44280724 |
+1
-1
@@ -129,7 +129,7 @@ IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
|
||||
@@ -18,9 +18,23 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -33,6 +47,15 @@ jobs:
|
||||
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:
|
||||
@@ -42,7 +65,8 @@ jobs:
|
||||
- name: Upload SDL3 and FAudio
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SDL3-FAudio
|
||||
name: SDL3-FAudio-ID
|
||||
path: |
|
||||
libs/libs.tar
|
||||
libs/includes.tar
|
||||
libs/lib32ID.tar
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Download SDL3 libs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: SDL3-FAudio
|
||||
name: SDL3-FAudio-ID
|
||||
path: libs/
|
||||
|
||||
# Work around container ownership issue
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Download SDL3 libs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: SDL3-FAudio
|
||||
name: SDL3-FAudio-ID
|
||||
path: libs/
|
||||
|
||||
# Set up Docker Buildx
|
||||
|
||||
+63
-32
@@ -4,14 +4,14 @@ on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/ISSUE_TEMPLATE/*'
|
||||
- '.docs/changelog'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/ISSUE_TEMPLATE/*'
|
||||
@@ -33,41 +33,30 @@ jobs:
|
||||
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")
|
||||
|
||||
# Check if a version was actually extracted
|
||||
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
|
||||
|
||||
@@ -82,21 +71,15 @@ jobs:
|
||||
needs: prepare-source
|
||||
uses: "./.github/workflows/appimage-build.yml"
|
||||
|
||||
linux-build:
|
||||
name: 💻 Linux
|
||||
needs: [prepare-source, linux-appimage]
|
||||
uses: "./.github/workflows/linux-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, linux-appimage, prepare-source]
|
||||
name: 📤 Create Release and Notify
|
||||
needs: [linux-flatpak, linux-appimage, prepare-source]
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -107,18 +90,66 @@ jobs:
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts/
|
||||
|
||||
|
||||
- name: Display Downloaded Artifacts
|
||||
run: find ./artifacts/
|
||||
|
||||
- name: Create Release
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: ${{ needs.prepare-source.outputs.release_tag }}
|
||||
prerelease: false
|
||||
title: "Build ${{ needs.prepare-source.outputs.release_tag }}"
|
||||
files: |
|
||||
- name: Parse Changelog and Save to File
|
||||
id: changelog
|
||||
run: |
|
||||
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 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
|
||||
./artifacts/linux-appimage/lindbergh-loader.AppImage
|
||||
|
||||
- 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 }}"
|
||||
@@ -18,9 +18,10 @@ 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/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
|
||||
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 -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/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
|
||||
$(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)
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
## 📚 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/c/LinuxLoader).
|
||||
|
||||
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!
|
||||
|
||||
|
||||
@@ -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.
|
||||
+44
-1
@@ -2,6 +2,49 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
|
||||
## [2.1.4]
|
||||
### Added
|
||||
- Force Outrun to render properly in 4:3 when resolution is set to 640x480
|
||||
to match the chihiro version of the game for use on CRT cabinets.
|
||||
|
||||
## [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.
|
||||
@@ -138,4 +181,4 @@ All notable changes to this project will be documented in this file.
|
||||
- Dynamic resolution patching allowing most games to scale properly in HD
|
||||
- Original cabinet support with pass through of JVS and FFB
|
||||
- Network play
|
||||
- Both simple X11 and advanced evdev input system allowing for ready-to-play keyboard and mouse support or mapping of any controller
|
||||
- Both simple X11 and advanced evdev input system allowing for ready-to-play keyboard and mouse support or mapping of any controller
|
||||
|
||||
+32
-31
@@ -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.
|
||||
|
||||
@@ -36,8 +36,11 @@ The follow list of games are supported. It is worth noting that there are multip
|
||||
| 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 | ✓ | ✓ | ✓ | ✓ |
|
||||
@@ -68,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
|
||||
```
|
||||
|
||||
@@ -96,7 +99,7 @@ sudo ln -s /lib/i386-linux-gnu/libglut.so /lib/i386-linux-gnu/libglut.so.3
|
||||
|
||||
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.ini
|
||||
## ⚙️ Configuration File : lindbergh.ini
|
||||
|
||||
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.
|
||||
|
||||
@@ -120,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
|
||||
@@ -148,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
|
||||
```
|
||||
|
||||
@@ -163,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
|
||||
|
||||
@@ -173,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
|
||||
```
|
||||
|
||||
@@ -194,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
|
||||
```
|
||||
|
||||
@@ -207,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
|
||||
```
|
||||
|
||||
@@ -221,7 +224,7 @@ By default, games should be added to:
|
||||
|
||||
If you need to add more locations, install Flatseal:
|
||||
|
||||
```
|
||||
```bash
|
||||
flatpak install com.github.tchx84.Flatseal
|
||||
```
|
||||
|
||||
@@ -235,29 +238,29 @@ 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
|
||||
## 📂 -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
|
||||
## 📁 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
|
||||
```
|
||||
|
||||
@@ -271,12 +274,10 @@ 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
|
||||
## 📝 Creating lindbergh.ini or controls.ini
|
||||
|
||||
```
|
||||
```bash
|
||||
./lindbergh --create --help
|
||||
```
|
||||
|
||||
Will give you instructions on how to use the new commands.
|
||||
|
||||
|
||||
Will give you instructions on how to use the new commands.
|
||||
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
|
||||
@@ -2,12 +2,10 @@
|
||||
|
||||
echo "Install Packages Running..."
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update
|
||||
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
|
||||
|
||||
sudo add-apt-repository ppa:kisak/turtle -y
|
||||
sudo apt update -y && sudo apt upgrade -y
|
||||
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
|
||||
|
||||
@@ -4,6 +4,7 @@ APPIMAGENAME=lindbergh-loader
|
||||
APPIMAGEDIRNAME=lindbergh-loader.AppDir
|
||||
|
||||
declare -a ADDED_LIBS=(
|
||||
"libasound.so.2"
|
||||
"libfreetype.so.6"
|
||||
"libOpenGL.so.0"
|
||||
"libGLdispatch.so.0"
|
||||
@@ -35,6 +36,15 @@ declare -a ADDED_LIBS=(
|
||||
"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
|
||||
@@ -63,13 +73,18 @@ 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"
|
||||
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
|
||||
@@ -82,7 +97,11 @@ 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/* $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..."
|
||||
@@ -94,18 +113,33 @@ 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
|
||||
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"
|
||||
|
||||
@@ -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,7 +76,7 @@ modules:
|
||||
- type: file
|
||||
path: ./assets/libstdc++.so.5
|
||||
|
||||
- name: SDL3-FAudio
|
||||
- name: SDL3-FAudio-ID
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- tar -xf libs/libs.tar -C /app/lib32
|
||||
@@ -105,10 +104,11 @@ modules:
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/app
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://downloads.sourceforge.net/freeglut/freeglut-3.2.2.tar.gz
|
||||
sha256: c5944a082df0bba96b5756dddb1f75d0cd72ce27b5395c6c1dde85c2ff297a50
|
||||
- type: git
|
||||
url: https://github.com/freeglut/freeglut.git
|
||||
branch: master
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
@@ -141,27 +141,6 @@ modules:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
# - name: libFAudio-Static
|
||||
# buildsystem: cmake-ninja
|
||||
# config-opts:
|
||||
# - -DCMAKE_BUILD_TYPE=Release
|
||||
# - -DCMAKE_INSTALL_PREFIX=/app
|
||||
# - -DBUILD_SHARED_LIBS=off
|
||||
# - -DCMAKE_INSTALL_LIBDIR=/app/lib32
|
||||
# build-options:
|
||||
# arch:
|
||||
# x86_64: *i386-build-options
|
||||
# env:
|
||||
# CFLAGS: "-m32 -mstackrealign"
|
||||
# CXXFLAGS: "-m32 -mstackrealign"
|
||||
# LDFLAGS: "-m32 -mstackrealign"
|
||||
# PKG_CONFIG_PATH: "/app/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig"
|
||||
# LD_LIBRARY_PATH: "/app/lib32:/usr/lib/i386-linux-gnu/pkgconfig:$LD_LIBRARY_PATH"
|
||||
# sources:
|
||||
# - type: git
|
||||
# url: https://github.com/FNA-XNA/FAudio.git
|
||||
# branch: "master"
|
||||
|
||||
- name: libxmu
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
@@ -172,11 +151,8 @@ modules:
|
||||
url: https://xorg.freedesktop.org/releases/individual/lib/libXmu-1.2.1.tar.gz
|
||||
sha256: bf0902583dd1123856c11e0a5085bd3c6e9886fbbd44954464975fd7d52eb599
|
||||
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
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
|
||||
- name: libmd
|
||||
buildsystem: autotools
|
||||
@@ -202,25 +178,6 @@ modules:
|
||||
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:
|
||||
@@ -251,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:
|
||||
@@ -272,6 +247,7 @@ modules:
|
||||
- ln -s -f libposixtime.so /app/lib32/libposixtime.so.2.4
|
||||
- ln -s -f libkswapapi.so /app/lib32/libGLcore.so.1
|
||||
- ln -s -f libkswapapi.so /app/lib32/libnvidia-tls.so.1
|
||||
- ln -s -f /usr/lib/i386-linux-gnu/libncurses.so /app/lib32/libncurses.so.5
|
||||
build-options:
|
||||
arch:
|
||||
x86_64: *i386-build-options
|
||||
@@ -295,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+144
-141
@@ -88,21 +88,24 @@ int initBaseboard()
|
||||
|
||||
strcpy(serialString, SERIAL_STRING);
|
||||
|
||||
time_t t = time(NULL); // Get current time
|
||||
struct tm *tm_info = localtime(&t); // Convert to local time structure
|
||||
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) {
|
||||
if (month == 1 && day == 18)
|
||||
{
|
||||
strcpy(serialString, "HAPPY BIRTHDAY!!");
|
||||
}
|
||||
|
||||
if (month == 5 && day == 20) {
|
||||
if (month == 5 && day == 20)
|
||||
{
|
||||
strcpy(serialString, "HAPPY BIRTHDAY!!");
|
||||
}
|
||||
|
||||
if (month == 5 && day == 21) {
|
||||
if (month == 5 && day == 21)
|
||||
{
|
||||
strcpy(serialString, "HAPPY BIRTHDAY!!");
|
||||
}
|
||||
|
||||
@@ -132,171 +135,171 @@ 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_VERSION:
|
||||
{
|
||||
|
||||
case BASEBOARD_GET_SERIAL: // bcCmdSysInfoGetReq
|
||||
{
|
||||
serialCommand.destAddress = _data[1];
|
||||
serialCommand.destSize = _data[2];
|
||||
uint8_t versionData[4] = {0x00, 0x19, 0x20, 0x07};
|
||||
memcpy(data, versionData, 4);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case BASEBOARD_WRITE_FLASH: // bcCmdSysFlashWrite
|
||||
case BASEBOARD_INIT:
|
||||
{
|
||||
log_warn("The game attempted to write to the baseboard flash\n");
|
||||
// selectReply = -1; Considering adding this in
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case BASEBOARD_PROCESS_JVS:
|
||||
case BASEBOARD_READY: // Not sure if this is what it should be called
|
||||
{
|
||||
jvsCommand.srcAddress = _data[1];
|
||||
jvsCommand.srcSize = _data[2];
|
||||
jvsCommand.destAddress = _data[3];
|
||||
jvsCommand.destSize = _data[4];
|
||||
memcpy(inputBuffer, &sharedMemory[jvsCommand.srcAddress], jvsCommand.srcSize);
|
||||
selectReply = 0;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
if (getConfig()->emulateJVS)
|
||||
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])
|
||||
{
|
||||
processPacket(&jvsPacketSize);
|
||||
}
|
||||
else if (jvsFileDescriptor >= 0)
|
||||
{
|
||||
for (int i = 0; i < jvsCommand.srcSize; i++)
|
||||
|
||||
case BASEBOARD_GET_SERIAL: // bcCmdSysInfoGetReq
|
||||
{
|
||||
write(jvsFileDescriptor, &inputBuffer[i], 1);
|
||||
if (inputBuffer[i] == 0xF0)
|
||||
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)
|
||||
{
|
||||
setSenseLine(3);
|
||||
processPacket(&jvsPacketSize);
|
||||
}
|
||||
else if (inputBuffer[i] == 0xF1)
|
||||
else if (jvsFileDescriptor >= 0)
|
||||
{
|
||||
setSenseLine(1);
|
||||
for (int i = 0; i < jvsCommand.srcSize; i++)
|
||||
{
|
||||
write(jvsFileDescriptor, &inputBuffer[i], 1);
|
||||
if (inputBuffer[i] == 0xF0)
|
||||
{
|
||||
setSenseLine(3);
|
||||
}
|
||||
else if (inputBuffer[i] == 0xF1)
|
||||
{
|
||||
setSenseLine(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BASEBOARD_GET_SENSE_LINE:
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Error: Unknown baseboard command %X\n", _data[0]);
|
||||
}
|
||||
|
||||
// Acknowledge the command
|
||||
_data[0] |= 0xF0000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case BASEBOARD_GET_SENSE_LINE:
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Error: Unknown baseboard command %X\n", _data[0]);
|
||||
}
|
||||
|
||||
// Acknowledge the command
|
||||
_data[0] |= 0xF0000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case BASEBOARD_RECEIVE:
|
||||
{
|
||||
uint32_t *_data = data;
|
||||
|
||||
switch (_data[0] & 0xFFF)
|
||||
case BASEBOARD_RECEIVE:
|
||||
{
|
||||
uint32_t *_data = data;
|
||||
|
||||
case BASEBOARD_GET_SERIAL:
|
||||
{
|
||||
memcpy(&sharedMemory[serialCommand.destAddress + 96], 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)
|
||||
switch (_data[0] & 0xFFF)
|
||||
{
|
||||
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, jvsPacketSize);
|
||||
_data[2] = jvsCommand.destAddress;
|
||||
_data[3] = jvsPacketSize;
|
||||
_data[1] = 1; // Set the status to success
|
||||
}
|
||||
else if (jvsFileDescriptor >= 0)
|
||||
{
|
||||
JVSFrame frame = readJVSFrameFromThread();
|
||||
memcpy(&sharedMemory[jvsCommand.destAddress], frame.buffer, frame.size);
|
||||
_data[2] = jvsCommand.destAddress;
|
||||
_data[3] = frame.size;
|
||||
_data[1] = frame.ready;
|
||||
|
||||
case BASEBOARD_GET_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 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);
|
||||
printf("Error: Unknown baseboard ioctl %lX\n", request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -22,6 +22,8 @@ extern SDL_Window *sdlWindow;
|
||||
int blitWidth = 0;
|
||||
int blitHeight = 0;
|
||||
|
||||
int gameIsOutrunChihiroMode = 0;
|
||||
|
||||
int fboInitialized = false;
|
||||
GLuint fboId = 0;
|
||||
GLuint fboTextureId = 0;
|
||||
@@ -36,6 +38,30 @@ void initBlitting()
|
||||
blitSetWidthandHeightSize();
|
||||
blitInitializeFbo();
|
||||
dest.gameScale = 1.0f;
|
||||
|
||||
if(gGrp == GROUP_OUTRUN && getConfig()->width == 640)
|
||||
gameIsOutrunChihiroMode = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the game is outrun in 640x480 mode we force the game to write to out temp framebuffer directly
|
||||
* to avoid the framebuffer of the window being too small and the edges getting clipped off. So we
|
||||
* hook glBindFramebuffer and if the game tries to bind the default framebuffer, we bind our FBO instead and
|
||||
* set the viewport to 800x480 so the game renders to our FBO. Then in blitStretch we copy from our FBO to the
|
||||
* screen with the correct aspect ratio.
|
||||
*/
|
||||
void glBindFramebufferEXT(GLenum target, GLuint framebuffer) {
|
||||
void (*_glBindFramebufferEXT)(GLenum target, GLuint framebuffer) = dlsym(RTLD_NEXT, "glBindFramebufferEXT");
|
||||
|
||||
if(target == GL_FRAMEBUFFER_EXT && framebuffer == 0 && fboInitialized && gameIsOutrunChihiroMode)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
|
||||
glViewport(0, 0, 800, 480);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
return;
|
||||
}
|
||||
|
||||
_glBindFramebufferEXT(target, framebuffer);
|
||||
}
|
||||
|
||||
void blitSetWidthandHeightSize()
|
||||
@@ -58,7 +84,7 @@ void blitSetWidthandHeightSize()
|
||||
blitWidth = 640;
|
||||
blitHeight = 480;
|
||||
}
|
||||
else if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE || gId == MJ4_REVG || gId == MJ4_EVO)
|
||||
else if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE || gGrp == GROUP_MJ4)
|
||||
{
|
||||
blitWidth = 1024;
|
||||
blitHeight = 768;
|
||||
@@ -121,14 +147,18 @@ void blitStretch()
|
||||
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;
|
||||
|
||||
float gameAspect = 4.0 / 3.0;
|
||||
|
||||
if(!gameIsOutrunChihiroMode)
|
||||
{
|
||||
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);
|
||||
gameAspect = (float)blitWidth / (float)blitHeight;
|
||||
}
|
||||
|
||||
dest.W = drawableW;
|
||||
dest.H = drawableH;
|
||||
|
||||
@@ -142,7 +172,7 @@ void blitStretch()
|
||||
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);
|
||||
@@ -151,7 +181,14 @@ void blitStretch()
|
||||
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);
|
||||
if(gameIsOutrunChihiroMode)
|
||||
{
|
||||
glBlitFramebuffer(80, 0, blitWidth - 80, blitHeight, dest.X, dest.Y, dest.X + dest.W, dest.Y + dest.H, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,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,
|
||||
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xd2};
|
||||
@@ -243,3 +245,156 @@ void cardReaderSetFd(int serial, int fd, char *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;
|
||||
}
|
||||
@@ -33,3 +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);
|
||||
void idPatchEject();
|
||||
void idWriteFileHeaderTram(void *param1);
|
||||
bool idCardFileExists(const char *folderPath, long expectedSize, bool twoDigits);
|
||||
+19
-2
@@ -14,8 +14,6 @@ EmulatorConfig config = {0};
|
||||
|
||||
extern uint32_t elf_crc;
|
||||
|
||||
FILE *configFile = NULL;
|
||||
|
||||
#define CONFIG_PATH "lindbergh.ini"
|
||||
#define MAX_LINE_LENGTH 1024
|
||||
|
||||
@@ -47,6 +45,7 @@ static int detectGame(uint32_t elf_crc)
|
||||
config.emulateDriveboard = gameData->emulateDriveboard;
|
||||
config.emulateMotionboard = gameData->emulateMotionboard;
|
||||
config.emulateHW210CardReader = gameData->emulateHW210CardReader;
|
||||
config.emulateIDCardReader = gameData->emulateIDCardReader;
|
||||
config.emulateTouchscreen = gameData->emulateTouchscreen;
|
||||
config.gameLindberghColour = gameData->gameLindberghColour;
|
||||
return 0;
|
||||
@@ -116,9 +115,12 @@ void setDefaultValues(EmulatorConfig *cfg)
|
||||
cfg->emulateDriveboard = 0;
|
||||
cfg->emulateMotionboard = 0;
|
||||
cfg->emulateHW210CardReader = 0;
|
||||
cfg->emulateIDCardReader = 0;
|
||||
cfg->idCardFileAutoload = 1;
|
||||
cfg->emulateTouchscreen = 0;
|
||||
strcpy(cfg->cardFile1, "Card_01.crd");
|
||||
strcpy(cfg->cardFile2, "Card_02.crd");
|
||||
strcpy(cfg->idCardFolder, "");
|
||||
cfg->emulateJVS = 1;
|
||||
cfg->fullscreen = 0;
|
||||
cfg->lindberghColour = YELLOW;
|
||||
@@ -187,6 +189,11 @@ void setDefaultValues(EmulatorConfig *cfg)
|
||||
cfg->whiteBorderPercentage = 0.02f;
|
||||
cfg->blackBorderPercentage = 0.0f;
|
||||
cfg->inputMode = 1;
|
||||
cfg->enableCrosshairs = 0;
|
||||
strcpy(cfg->p1CrossHairPath, "");
|
||||
strcpy(cfg->p2CrossHairPath, "");
|
||||
cfg->customCrossHairWidth = 64;
|
||||
cfg->customCrossHairHeight = 64;
|
||||
}
|
||||
|
||||
static const char *getValue(const IniConfig *ini, const char *sectionName, const char *key)
|
||||
@@ -326,11 +333,14 @@ void applyIniConfig(EmulatorConfig *config, const IniConfig *ini)
|
||||
config->emulateDriveboard = getInt(ini, "Emulation", "EMULATE_DRIVEBOARD", config->emulateDriveboard);
|
||||
config->emulateMotionboard = getInt(ini, "Emulation", "EMULATE_MOTIONBOARD", config->emulateMotionboard);
|
||||
config->emulateHW210CardReader = getInt(ini, "Emulation", "EMULATE_HW210_CARDREADER", config->emulateHW210CardReader);
|
||||
config->emulateIDCardReader = getInt(ini, "Emulation", "EMULATE_ID_CARDREADER", config->emulateIDCardReader);
|
||||
config->emulateTouchscreen = getInt(ini, "Emulation", "EMULATE_TOUCHSCREEN", config->emulateTouchscreen);
|
||||
|
||||
// [Cards]
|
||||
config->idCardFileAutoload = getInt(ini, "Cards", "ID_CARDFILE_AUTOLOAD", config->idCardFileAutoload);
|
||||
getString(ini, "Cards", "CARDFILE_01", config->cardFile1, MAX_PATH_LENGTH);
|
||||
getString(ini, "Cards", "CARDFILE_02", config->cardFile2, MAX_PATH_LENGTH);
|
||||
getString(ini, "Cards", "ID_CARDFOLDER", config->idCardFolder, MAX_PATH_LENGTH);
|
||||
|
||||
// [Paths]
|
||||
getString(ini, "Paths", "JVS_PATH", config->jvsPath, MAX_PATH_LENGTH);
|
||||
@@ -369,6 +379,13 @@ void applyIniConfig(EmulatorConfig *config, const IniConfig *ini)
|
||||
config->id5ChineseLanguage = getInt(ini, "GameSpecific", "ID5_CHINESE_LANGUAGE", config->id5ChineseLanguage);
|
||||
config->idSteeringPercentageReduction = getFloat(ini, "GameSpecific", "ID_STEERING_REDUCTION_PERCENTAGE", config->idSteeringPercentageReduction);
|
||||
|
||||
// [CrossHairs]
|
||||
config->enableCrosshairs = getInt(ini, "CrossHairs", "ENABLE_CROSSHAIRS", config->enableCrosshairs);
|
||||
getString(ini, "CrossHairs", "P1_CROSSHAIR_PATH", config->p1CrossHairPath, MAX_PATH_LENGTH);
|
||||
getString(ini, "CrossHairs", "P2_CROSSHAIR_PATH", config->p2CrossHairPath, MAX_PATH_LENGTH);
|
||||
config->customCrossHairWidth = getInt(ini, "CrossHairs", "CUSTOM_CROSSHAIRS_WIDTH", config->customCrossHairWidth);
|
||||
config->customCrossHairHeight = getInt(ini, "CrossHairs", "CUSTOM_CROSSHAIRS_HEIGHT", config->customCrossHairHeight);
|
||||
|
||||
config->showDebugMessages = getInt(ini, "System", "DEBUG_MSGS", config->showDebugMessages);
|
||||
const char *colourRaw = getValue(ini, "System", "LINDBERGH_COLOUR");
|
||||
if (colourRaw)
|
||||
|
||||
@@ -37,8 +37,11 @@
|
||||
#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
|
||||
@@ -133,6 +136,7 @@ typedef enum
|
||||
GROUP_ID4_JAP,
|
||||
GROUP_ID5,
|
||||
GROUP_LGJ,
|
||||
GROUP_MJ4,
|
||||
GROUP_OUTRUN,
|
||||
GROUP_OUTRUN_TEST,
|
||||
GROUP_RAMBO,
|
||||
@@ -248,9 +252,12 @@ typedef struct
|
||||
int emulateDriveboard;
|
||||
int emulateMotionboard;
|
||||
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];
|
||||
@@ -324,6 +331,11 @@ typedef struct
|
||||
float whiteBorderPercentage;
|
||||
float blackBorderPercentage;
|
||||
int borderEnabled;
|
||||
int enableCrosshairs;
|
||||
char p1CrossHairPath[MAX_PATH_LENGTH];
|
||||
char p2CrossHairPath[MAX_PATH_LENGTH];
|
||||
int customCrossHairWidth;
|
||||
int customCrossHairHeight;
|
||||
} EmulatorConfig;
|
||||
|
||||
int initConfig(const char* configFilePath);
|
||||
|
||||
@@ -12,7 +12,7 @@ void createDefaultIni(const char *filePath)
|
||||
return;
|
||||
}
|
||||
|
||||
EmulatorConfig defaults;
|
||||
EmulatorConfig defaults = {0};
|
||||
setDefaultValues(&defaults);
|
||||
|
||||
fprintf(file, "# SEGA Lindbergh Emulator Configuration File\n");
|
||||
@@ -58,12 +58,16 @@ void createDefaultIni(const char *filePath)
|
||||
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");
|
||||
@@ -101,7 +105,7 @@ void createDefaultIni(const char *filePath)
|
||||
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\n");
|
||||
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);
|
||||
@@ -120,13 +124,25 @@ void createDefaultIni(const char *filePath)
|
||||
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 chnge the way the guns are show in Rambo\n");
|
||||
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");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "controlIniGen.h"
|
||||
#include "iniParser.h"
|
||||
@@ -211,6 +212,9 @@ const size_t gDefaultMahjongBindingsSize = sizeof(gDefaultMahjongBindings) / siz
|
||||
|
||||
char *toUpperCase(const char *str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
char *result = (char *)malloc(strlen(str) + 1);
|
||||
|
||||
if (result == NULL)
|
||||
@@ -269,9 +273,15 @@ static void getBindingString(const ControlBinding *binding, char *buffer, size_t
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_HAT%d_RIGHT", binding->deviceIndex, binding->sdlId);
|
||||
break;
|
||||
case INPUT_TYPE_GAMEPAD_BUTTON:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "GC%d_BUTTON_%s", binding->deviceIndex,
|
||||
toUpperCase(SDL_GetGamepadStringForButton(binding->sdlId)));
|
||||
{
|
||||
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:
|
||||
{
|
||||
@@ -279,8 +289,14 @@ static void getBindingString(const ControlBinding *binding, char *buffer, size_t
|
||||
if (binding->type == INPUT_TYPE_JOY_AXIS)
|
||||
snprintf(prefix, sizeof(prefix), "JOY%d_AXIS_%d", binding->deviceIndex, binding->sdlId);
|
||||
else
|
||||
snprintf(prefix, sizeof(prefix), "GC%d_AXIS_%s", binding->deviceIndex,
|
||||
toUpperCase(SDL_GetGamepadStringForAxis(binding->sdlId)));
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
+187
-92
@@ -1,131 +1,226 @@
|
||||
#include "string.h"
|
||||
|
||||
#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
|
||||
|
||||
int wheelTest = 0;
|
||||
unsigned char response = DRIVEBOARD_READY;
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern DeviceType hooks[5];
|
||||
|
||||
double steerValue = 0.5;
|
||||
double force = 0.00;
|
||||
unsigned char response;
|
||||
int wheelInitialized;
|
||||
|
||||
uint8_t buffer[64];
|
||||
uint8_t bufferIdx = 0;
|
||||
|
||||
int initDriveboard()
|
||||
{
|
||||
wheelTest = 0;
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
|
||||
setAnalogue(ANALOGUE_1, steerValue);
|
||||
|
||||
wheelInitialized = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enableRead = 0;
|
||||
|
||||
ssize_t driveboardRead(int fd, void *buf, size_t count)
|
||||
{
|
||||
memcpy(buf, &response, 1);
|
||||
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)
|
||||
{
|
||||
|
||||
if (count != 4)
|
||||
const uint8_t *bytes = (const uint8_t *)buf;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
//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)
|
||||
buffer[bufferIdx++] = bytes[i];
|
||||
if (bufferIdx == 4)
|
||||
{
|
||||
force = 0;
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (buffer[1] == 1 && buffer[2] == 1)
|
||||
else if (bufferIdx == 7)
|
||||
{
|
||||
|
||||
if (steerValue >= 0.9 && force > 0)
|
||||
break;
|
||||
|
||||
if (steerValue <= 0.1 && force < 0)
|
||||
break;
|
||||
|
||||
steerValue += force;
|
||||
setAnalogue(ANALOGUE_1, (int)(steerValue * 1024));
|
||||
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;
|
||||
}
|
||||
|
||||
//printf("Driveboard move %f %f\n", steerValue, force);
|
||||
}
|
||||
break;
|
||||
return count;
|
||||
}
|
||||
|
||||
case 0x9e:
|
||||
case 0x84:
|
||||
void processDrivePacket(uint8_t *buf, int player)
|
||||
{
|
||||
static int fcP1 = 0;
|
||||
static int fcP2 = 0;
|
||||
|
||||
switch (buf[0])
|
||||
{
|
||||
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)
|
||||
case 0xFF:
|
||||
{
|
||||
// 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)
|
||||
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)
|
||||
{
|
||||
wheelTest = 0;
|
||||
response = DRIVEBOARD_READY;
|
||||
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;
|
||||
|
||||
default:
|
||||
//printf("Driveboard: Unknown command received %X\n", buffer[0]);
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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);
|
||||
+171
-3
@@ -9,12 +9,14 @@
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "evdevInput.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "jvs.h"
|
||||
#include "log.h"
|
||||
|
||||
@@ -843,13 +845,138 @@ void normaliseName(char *string)
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
@@ -1020,6 +1147,28 @@ ControllerStatus loadEvdevControllers(Controllers *controllers)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
@@ -1111,7 +1260,6 @@ void *controllerThread(void *_args)
|
||||
else
|
||||
setSwitch(args->controller->keyTriggers[event.code].player, args->controller->keyTriggers[event.code].channel,
|
||||
event.value == 0 ? 0 : 1);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1168,6 +1316,26 @@ void *controllerThread(void *_args)
|
||||
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
|
||||
|
||||
@@ -82,6 +82,8 @@ typedef struct
|
||||
|
||||
int enabled;
|
||||
int inUse;
|
||||
int hasFFB;
|
||||
int ffbEffectId;
|
||||
double lastAnalogueValue[8];
|
||||
|
||||
} Controller;
|
||||
|
||||
@@ -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
|
||||
@@ -32,8 +32,11 @@ static const GameData gameData[] = {
|
||||
{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", "2008", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, -1, 0, 0, 0, 0, 0, 1, YELLOW},
|
||||
{MJ4_REVG, "SEGA Network Taisen Mahjong MJ4", "mj4-rev-g", "DVP-0049G", "SBPN", "2007", "1024x768", WORKING, SEGA_TYPE_3, MAHJONG, 1024, 768, -1, 0, 0, 0, 0, 0, 1, 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},
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "blitStretching.h"
|
||||
#include "border.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "fpsLimiter.h"
|
||||
#include "resolution.h"
|
||||
#include "sdlCalls.h"
|
||||
@@ -26,7 +27,7 @@ extern SDL_Window *sdlWindow;
|
||||
extern Display *x11Display;
|
||||
extern char sdlGameTitle[];
|
||||
extern char current_text[256];
|
||||
extern double local_fps;
|
||||
extern double localFps;
|
||||
bool winHidden = true;
|
||||
|
||||
void *displayFunc = NULL;
|
||||
@@ -41,6 +42,8 @@ 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");
|
||||
@@ -87,6 +90,9 @@ void FGAPIENTRY glutSwapBuffers(void)
|
||||
}
|
||||
}
|
||||
|
||||
if ((p1CrossHairInitialized || p2CrossHairInitialized) && gId != GHOST_SQUAD_EVOLUTION)
|
||||
renderCrosshairs();
|
||||
|
||||
blitStretch();
|
||||
|
||||
if (config->borderEnabled)
|
||||
@@ -97,9 +103,11 @@ void FGAPIENTRY glutSwapBuffers(void)
|
||||
if (config->fpsLimiter)
|
||||
frameTiming();
|
||||
|
||||
char windowTitle[512];
|
||||
sprintf(windowTitle, "%s - FPS: %.2f", sdlGameTitle, calculateFps());
|
||||
SDL_SetWindowTitle(sdlWindow, windowTitle);
|
||||
|
||||
if (gGrp == GROUP_MJ4)
|
||||
mj4TouchHolding();
|
||||
}
|
||||
|
||||
int FGAPIENTRY glutGet(GLenum type)
|
||||
@@ -336,6 +344,12 @@ void glutBitmapCharacter(void *fontID, int character)
|
||||
|
||||
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;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "blitStretching.h"
|
||||
#include "border.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "flowControl.h"
|
||||
#include "fpsLimiter.h"
|
||||
#include "sdlCalls.h"
|
||||
@@ -25,11 +26,13 @@ extern SDL_Window *sdlWindow;
|
||||
extern SDL_GLContext sdlContext;
|
||||
extern Display *x11Display;
|
||||
extern char sdlGameTitle[];
|
||||
double local_fps = 0.0;
|
||||
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");
|
||||
@@ -39,6 +42,9 @@ void glXSwapBuffers(Display *dpy, GLXDrawable drawable)
|
||||
if (config->borderEnabled)
|
||||
drawGameBorder(config->width, config->height, config->whiteBorderPercentage, config->blackBorderPercentage);
|
||||
|
||||
if (p1CrossHairInitialized || p2CrossHairInitialized)
|
||||
renderCrosshairs();
|
||||
|
||||
blitStretch();
|
||||
|
||||
pollEvents();
|
||||
@@ -48,10 +54,12 @@ void glXSwapBuffers(Display *dpy, GLXDrawable drawable)
|
||||
if (config->fpsLimiter)
|
||||
frameTiming();
|
||||
|
||||
char windowTitle[512];
|
||||
local_fps = calculateFps();
|
||||
sprintf(windowTitle, "%s - FPS: %.2f", sdlGameTitle, local_fps);
|
||||
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)
|
||||
@@ -81,7 +89,8 @@ GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attrib_list,
|
||||
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 (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)
|
||||
{
|
||||
@@ -112,7 +121,6 @@ void glXDestroyGLXPbufferSGIX(Display *dpy, GLXPbufferSGIX pbuf)
|
||||
|
||||
GLXFBConfigSGIX *glXChooseFBConfigSGIX(Display *dpy, int screen, int *attrib_list, int *nelements)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#include <GL/glx.h>
|
||||
|
||||
void glXToSDLSwapBuffers(Display *dpy, GLXDrawable drawable);
|
||||
int glxSDLmyCreateWindow(int *param1);
|
||||
@@ -44,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)
|
||||
|
||||
+100
-10
@@ -64,6 +64,8 @@ extern bool cachedShaderFilesLoaded;
|
||||
extern char vf5StageNameAbbr[5];
|
||||
bool phShowCursorInGame = false;
|
||||
|
||||
extern bool mj4ResponseReady;
|
||||
|
||||
extern FpsLimit fpsLimit;
|
||||
Controllers controllers = {0};
|
||||
extern SDLControllers sdlJoysticks;
|
||||
@@ -181,6 +183,31 @@ uint32_t getCrc32(const char *s, ssize_t n)
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
int setConfigFolder()
|
||||
{
|
||||
char *home = getenv("HOME");
|
||||
if (!home)
|
||||
{
|
||||
fprintf(stderr, "HOME environment variable not set.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char config_path[PATH_MAX];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/lindbergh-loader", home);
|
||||
configFolder = strdup(config_path);
|
||||
|
||||
char config_base[PATH_MAX];
|
||||
snprintf(config_base, sizeof(config_base), "%s/.config", home);
|
||||
mkdir(config_base, 0755);
|
||||
|
||||
if (mkdir(config_path, 0755) == -1)
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Trims leading and trailing whitespace and quotes from a string.
|
||||
*
|
||||
@@ -282,10 +309,13 @@ void __attribute__((constructor)) hook_init()
|
||||
|
||||
// Implement SIGSEGV handler
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = handleSegfault;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &act, NULL);
|
||||
|
||||
// setConfigFolder();
|
||||
|
||||
char *envPath = getenv("LINDBERGH_CONFIG_PATH");
|
||||
initConfig(envPath);
|
||||
|
||||
@@ -343,6 +373,11 @@ void __attribute__((constructor)) hook_init()
|
||||
}
|
||||
|
||||
envPath = getenv("LINDBERGH_CONTROLS_PATH");
|
||||
if (envPath == NULL)
|
||||
{
|
||||
envPath = "";
|
||||
}
|
||||
|
||||
if (initSdlInput(envPath) != 0)
|
||||
exit(1);
|
||||
|
||||
@@ -409,6 +444,20 @@ DIR *opendir(const char *dirname)
|
||||
return _opendir(dirname + 1);
|
||||
}
|
||||
|
||||
if (strcmp(getConfig()->idCardFolder, "") != 0 && strcmp(dirname, ".") == 0)
|
||||
{
|
||||
if (gGrp == GROUP_ID5 || gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP)
|
||||
{
|
||||
char *newDirName = getConfig()->idCardFolder;
|
||||
char lastChar = newDirName[strlen(newDirName) - 1];
|
||||
if (lastChar == '/')
|
||||
{
|
||||
newDirName[strlen(newDirName) - 1] = '\0';
|
||||
}
|
||||
return _opendir(newDirName);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for Outrun high scores
|
||||
if (strcmp(dirname, "/home/disk1/rankingdata") == 0 && (gGrp == GROUP_OUTRUN || gGrp == GROUP_OUTRUN_TEST))
|
||||
{
|
||||
@@ -763,6 +812,28 @@ FILE *fopen(const char *restrict pathname, const char *restrict mode)
|
||||
return fopen(pathname + 1, mode);
|
||||
}
|
||||
|
||||
if (strcmp(getConfig()->idCardFolder, "") != 0)
|
||||
{
|
||||
if (gGrp == GROUP_ID5 || gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP)
|
||||
{
|
||||
if (strncmp(pathname, "InidCrd", 7) == 0 || strncmp(pathname, "InidCard", 8) == 0)
|
||||
{
|
||||
char *fmt = "%s%s";
|
||||
char newPathname[MAX_PATH_LENGTH];
|
||||
int newPathnameLen = strlen(getConfig()->idCardFolder) + strlen(pathname) + 1;
|
||||
char lastChar = getConfig()->idCardFolder[strlen(getConfig()->idCardFolder) - 1];
|
||||
if (lastChar != '/')
|
||||
{
|
||||
fmt = "%s/%s";
|
||||
newPathnameLen++;
|
||||
}
|
||||
|
||||
snprintf(newPathname, newPathnameLen, fmt, getConfig()->idCardFolder, pathname);
|
||||
return fopen(newPathname, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gId == PRIMEVAL_HUNT)
|
||||
{
|
||||
if (strstr(pathname, "/data/lua/texture/start_stage") != NULL)
|
||||
@@ -1041,6 +1112,11 @@ ssize_t read(int fd, void *buf, size_t count)
|
||||
phRead(fd, buf, count);
|
||||
return 1;
|
||||
}
|
||||
if ((gGrp == GROUP_MJ4 || gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE) && getConfig()->emulateTouchscreen == 1 &&
|
||||
mj4ResponseReady)
|
||||
{
|
||||
return mj4ReadTouchPacket(buf, count);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1217,6 +1293,11 @@ ssize_t write(int fd, const void *buf, size_t count)
|
||||
return driveboardWrite(fd, buf, count);
|
||||
}
|
||||
|
||||
if (fd == hooks[SERIAL1] && (gGrp == GROUP_MJ4 || gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE))
|
||||
{
|
||||
return mj4WriteTouchPacket(buf, count);
|
||||
}
|
||||
|
||||
if ((fd == hooks[SERIAL0] || fd == hooks[SERIAL1]) && getConfig()->emulateHW210CardReader)
|
||||
{
|
||||
return cardReaderWrite(fd, buf, count);
|
||||
@@ -1255,16 +1336,20 @@ int ioctl(int fd, unsigned long int request, ...)
|
||||
|
||||
if (fd == hooks[SERIAL0] || fd == hooks[SERIAL1])
|
||||
{
|
||||
if (request == 0x541b && (gId == R_TUNED) && fd == hooks[SERIAL1])
|
||||
if (request == 0x541b && (gId == R_TUNED || gGrp == GROUP_MJ4) && fd == hooks[SERIAL1])
|
||||
{
|
||||
uint8_t d = 1;
|
||||
memcpy(argp, &d, sizeof(uint8_t));
|
||||
}
|
||||
else if (getConfig()->emulateDriveboard)
|
||||
{
|
||||
return driveBoardioctl(fd, request, argp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Replace "eth0" with your interface name set in the config file.
|
||||
if ((gGrp == GROUP_ID4_EXP || gId == INITIALD_5_EXP_20 || gId == INITIALD_5_EXP_20A ) &&
|
||||
if ((gGrp == GROUP_ID4_EXP || gId == INITIALD_5_EXP_20 || gId == INITIALD_5_EXP_20A) &&
|
||||
(request == SIOCGIFFLAGS || request == SIOCGIFADDR) && getConfig()->enableNetworkPatches && strcmp(getConfig()->nicName, "") != 0)
|
||||
{
|
||||
struct ifreq *ifr = (struct ifreq *)argp;
|
||||
@@ -1389,14 +1474,19 @@ int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set
|
||||
int (*_select)(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
|
||||
struct timeval *restrict timeout) = dlsym(RTLD_NEXT, "select");
|
||||
|
||||
if (readfds != NULL && FD_ISSET(hooks[BASEBOARD], readfds))
|
||||
{
|
||||
return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout);
|
||||
}
|
||||
int baseboardFd = hooks[BASEBOARD];
|
||||
|
||||
if (writefds != NULL && FD_ISSET(hooks[BASEBOARD], writefds))
|
||||
if (baseboardFd >= 0 && baseboardFd < nfds)
|
||||
{
|
||||
return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout);
|
||||
if (readfds != NULL && FD_ISSET(hooks[BASEBOARD], readfds))
|
||||
{
|
||||
return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout);
|
||||
}
|
||||
|
||||
if (writefds != NULL && FD_ISSET(hooks[BASEBOARD], writefds))
|
||||
{
|
||||
return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
if ((getConfig()->emulateHW210CardReader == 1 || getConfig()->emulateDriveboard == 1) &&
|
||||
@@ -1640,7 +1730,7 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
|
||||
{
|
||||
struct tm *(*_gmtime_r)(const time_t *, struct tm *) = (struct tm * (*)(const time_t *, struct tm *)) dlsym(RTLD_NEXT, "gmtime_r");
|
||||
|
||||
if ((gId == MJ4_REVG || gId == MJ4_EVO) && getConfig()->mj4EnabledAtT == 1)
|
||||
if ((gGrp == GROUP_MJ4) && getConfig()->mj4EnabledAtT == 1)
|
||||
{
|
||||
time_t target_time = 1735286445;
|
||||
struct tm *res = _gmtime_r(&target_time, result);
|
||||
@@ -1660,4 +1750,4 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result)
|
||||
return res;
|
||||
}
|
||||
return _gmtime_r(timep, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ IniConfig *iniLoad(const char *filename)
|
||||
}
|
||||
IniConfig *config = calloc(1, sizeof(IniConfig));
|
||||
if (!config)
|
||||
{
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
char line[1024];
|
||||
IniSection *currentSection = NULL;
|
||||
while (fgets(line, sizeof(line), file))
|
||||
@@ -54,7 +57,14 @@ IniConfig *iniLoad(const char *filename)
|
||||
continue;
|
||||
if (trimmedLine[0] == '[' && trimmedLine[strlen(trimmedLine) - 1] == ']')
|
||||
{
|
||||
config->sections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
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;
|
||||
@@ -70,7 +80,14 @@ IniConfig *iniLoad(const char *filename)
|
||||
value++;
|
||||
key = trimString(key);
|
||||
value = trimString(value);
|
||||
currentSection->pairs = realloc(currentSection->pairs, (currentSection->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
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);
|
||||
@@ -142,7 +159,10 @@ bool iniSetValue(IniConfig *config, const char *sectionName, const char *key, co
|
||||
IniSection *section = iniGetSection(config, sectionName);
|
||||
if (!section)
|
||||
{
|
||||
config->sections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
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;
|
||||
@@ -162,7 +182,10 @@ bool iniSetValue(IniConfig *config, const char *sectionName, const char *key, co
|
||||
}
|
||||
|
||||
// Key not found, add a new key-value pair.
|
||||
section->pairs = realloc(section->pairs, (section->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
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);
|
||||
|
||||
+1
-1
@@ -488,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];
|
||||
|
||||
+83
-27
@@ -8,6 +8,7 @@
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "configIni.h"
|
||||
#include "evdevInput.h"
|
||||
#include "log.h"
|
||||
@@ -93,7 +94,9 @@ uint32_t cleanElfCRC32[] = {
|
||||
0xB0A96E34, // DVP-0043 | vf5
|
||||
0xF99E5635, // DVP-0044 | drive.elf
|
||||
0x4143F6B4, // DVP-0048A | main.exe
|
||||
0x65489691, // DVP-0049 | mj4
|
||||
0x93210094, // DVP-0049A | mj4
|
||||
0x561DC942, // DVP-0049F | mj4
|
||||
0x65489691, // DVP-0049G | mj4
|
||||
0x653BC83B, // DVP-0057 | a.elf
|
||||
0x04D88552, // DVP-0057B | a.elf
|
||||
0x089D6051, // DVP-0060 | dsr
|
||||
@@ -107,6 +110,7 @@ uint32_t cleanElfCRC32[] = {
|
||||
0xDCC1f8E7, // DVP-0078 | ramboM.elf
|
||||
0x05647A8E, // DVP-0079 | hummer_Master.elf
|
||||
0x0AD7CF0F, // DVP-0081 | mj4
|
||||
0xEE2D5861, // DVP-0081B | mj4
|
||||
0x4442EA15, // DVP-0083 | hummer_Master.elf
|
||||
0x8DF6BBF9, // DVP-0084 | id5.elf
|
||||
0x2AF8004E, // DVP-0084A | id5.elf
|
||||
@@ -122,6 +126,34 @@ uint32_t cleanElfCRC32[] = {
|
||||
|
||||
int cleanElfCRC32Count = sizeof(cleanElfCRC32) / sizeof(uint32_t);
|
||||
|
||||
bool isIDGame()
|
||||
{
|
||||
switch (elf_crc)
|
||||
{
|
||||
case 0x22905D60: // DVP-0019A | id4.elf
|
||||
case 0x43582D48: // DVP-0019B | id4.elf
|
||||
case 0x2D2A18C1: // DVP-0019C | id4.elf
|
||||
case 0x9BFD0D98: // DVP-0019D | id4.elf
|
||||
case 0x9CF9BBCC: // DVP-0019G | id4.elf
|
||||
case 0xC345E213: // DVP-0030B | id4.elf
|
||||
case 0x98E6A516: // DVP-0030C | id4.elf
|
||||
case 0xF67365C9: // DVP-0030D | id4.elf
|
||||
case 0xE4F202BB: // DVP-0070A | id5.elf
|
||||
case 0x400C09CD: // DVP-0070C | id5.elf
|
||||
case 0x2E6732A3: // DVP-0070F | id5.elf
|
||||
case 0xF99A3CDB: // DVP-0075 | id5.elf
|
||||
case 0x8DF6BBF9: // DVP-0084 | id5.elf
|
||||
case 0x2AF8004E: // DVP-0084A | id5.elf
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t calcCrc32(uint32_t crc, uint8_t data)
|
||||
{
|
||||
crc ^= data;
|
||||
@@ -204,12 +236,11 @@ void isCleanElf(char *command)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
uint32_t crc;
|
||||
if (calculateCRC32inChunks(elfName, &crc) != 0)
|
||||
if (calculateCRC32inChunks(elfName, &elf_crc) != 0)
|
||||
return;
|
||||
|
||||
crc = crc ^ 0xFFFFFFFF;
|
||||
if (!lookupCrcTable(crc))
|
||||
elf_crc = elf_crc ^ 0xFFFFFFFF;
|
||||
if (!lookupCrcTable(elf_crc))
|
||||
{
|
||||
printf("\033[1;31m");
|
||||
printf("Warning: The ELF you are running is not Clean and might cause unwanted behavior.\n");
|
||||
@@ -305,11 +336,20 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
|
||||
char *currentLibraryPath = getenv("LD_LIBRARY_PATH");
|
||||
char newLdLibPath[MAX_PATH_LENGTH * 3] = "";
|
||||
|
||||
if (currentLibraryPath && strlen(currentLibraryPath) > 0)
|
||||
snprintf(newLdLibPath, sizeof(newLdLibPath), "%s:", currentLibraryPath);
|
||||
|
||||
char *tmpLibPath = dirname(strdup(ldLibPath));
|
||||
char *appImgRoot = getenv("APP_IMG_ROOT");
|
||||
if (getenv("APP_IMG_ROOT") && isIDGame()) // If appimage and ID game.
|
||||
{
|
||||
strcat(newLdLibPath, appImgRoot);
|
||||
strcat(newLdLibPath, "/usr/lib32ID:");
|
||||
strcat(newLdLibPath, currentLibraryPath);
|
||||
}
|
||||
else if (currentLibraryPath && strlen(currentLibraryPath) > 0)
|
||||
{
|
||||
snprintf(newLdLibPath, sizeof(newLdLibPath), "%s", currentLibraryPath);
|
||||
}
|
||||
|
||||
char *ldLibPathCopy = strdup(ldLibPath);
|
||||
char *tmpLibPath = dirname(ldLibPathCopy);
|
||||
if (strcmp(tmpLibPath, ".") != 0)
|
||||
{
|
||||
strcat(newLdLibPath, ":");
|
||||
@@ -327,6 +367,8 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
|
||||
strcat(newLdLibPath, ":");
|
||||
strcat(newLdLibPath, gameDir);
|
||||
}
|
||||
|
||||
free(ldLibPathCopy);
|
||||
|
||||
strcat(newLdLibPath, ":.:lib:../lib");
|
||||
|
||||
@@ -343,7 +385,7 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
|
||||
log_error("The loader will use the default configuration.");
|
||||
confFilePath = "";
|
||||
}
|
||||
else if(!fileExists(confFilePath))
|
||||
else if (!fileExists(confFilePath))
|
||||
{
|
||||
log_error("The file \'%s\' does not exist, will be using default config.", confFilePath);
|
||||
confFilePath = "";
|
||||
@@ -395,6 +437,7 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
|
||||
|
||||
if (zink)
|
||||
{
|
||||
setenv("LIBGL_KOPPER_DRI2", "1", 1);
|
||||
setenv("MESA_LOADER_DRIVER_OVERRIDE", "zink", 1);
|
||||
}
|
||||
|
||||
@@ -404,13 +447,17 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
|
||||
setenv("__NV_PRIME_RENDER_OFFLOAD", "1", 1);
|
||||
}
|
||||
|
||||
char *appImgRoot = getenv("APP_IMG_ROOT");
|
||||
char libglDriPath[MAX_PATH_LENGTH] = {0};
|
||||
if (appImgRoot != NULL)
|
||||
{
|
||||
char libglDriPath[MAX_PATH_LENGTH];
|
||||
sprintf(libglDriPath, "%s/usr/lib32/dri", appImgRoot);
|
||||
setenv("LIBGL_DRIVERS_PATH", libglDriPath, 1);
|
||||
if (isIDGame())
|
||||
snprintf(libglDriPath, sizeof(libglDriPath), "%s/usr/lib32ID/dri", appImgRoot);
|
||||
else
|
||||
snprintf(libglDriPath, sizeof(libglDriPath), "%s/usr/lib32/dri", appImgRoot);
|
||||
}
|
||||
|
||||
if (libglDriPath[0] != '\0')
|
||||
setenv("LIBGL_DRIVERS_PATH", libglDriPath, 1);
|
||||
}
|
||||
|
||||
void printUsage(char *programName)
|
||||
@@ -428,7 +475,7 @@ void printUsage(char *programName)
|
||||
printf(" --help | -h Displays this usage text\n");
|
||||
printf(" --config | -c Specifies configuration ini file path\n");
|
||||
printf(" --controls | -o Specifies controls ini file path\n");
|
||||
printf(" --controlsdb | -d Specifies gamecontrollerdb.txt file path\n");
|
||||
printf(" --controllerdb | -d Specifies gamecontrollerdb.txt file path\n");
|
||||
printf(" --gamepath | -g Specifies game path without ELF name\n");
|
||||
printf(" --create | -C Creates a default config or controls file. Use '--create --help' for more info.\n");
|
||||
}
|
||||
@@ -514,7 +561,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
return listSdlControllers();
|
||||
}
|
||||
|
||||
|
||||
if (argc > 1 && (strcmp(argv[1], "--create") == 0 || strcmp(argv[1], "-C") == 0))
|
||||
{
|
||||
if (argc < 3)
|
||||
@@ -536,7 +583,6 @@ int main(int argc, char *argv[])
|
||||
char *argCopyForDirname = NULL;
|
||||
char *argCopyForBasename = NULL;
|
||||
|
||||
|
||||
if (strcmp(subOption, "config") != 0 && strcmp(subOption, "controls") != 0)
|
||||
{
|
||||
log_error("Invalid sub-option for --create: '%s'. Must be 'config' or 'controls'.\n", subOption);
|
||||
@@ -576,8 +622,10 @@ int main(int argc, char *argv[])
|
||||
if (!dot || strcmp(dot, ".ini") != 0)
|
||||
{
|
||||
log_error("Invalid filename: '%s'. Must have .ini extension.\n", filename);
|
||||
if(argCopyForDirname) free(argCopyForDirname);
|
||||
if(argCopyForBasename) free(argCopyForBasename);
|
||||
if (argCopyForDirname)
|
||||
free(argCopyForDirname);
|
||||
if (argCopyForBasename)
|
||||
free(argCopyForBasename);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -585,8 +633,10 @@ int main(int argc, char *argv[])
|
||||
if (!dirExists(path))
|
||||
{
|
||||
log_error("Output directory does not exist: %s\n", path);
|
||||
if(argCopyForDirname) free(argCopyForDirname);
|
||||
if(argCopyForBasename) free(argCopyForBasename);
|
||||
if (argCopyForDirname)
|
||||
free(argCopyForDirname);
|
||||
if (argCopyForBasename)
|
||||
free(argCopyForBasename);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -613,8 +663,10 @@ int main(int argc, char *argv[])
|
||||
log_info("Successfully created controls file at %s\n", finalPath);
|
||||
}
|
||||
|
||||
if(argCopyForDirname) free(argCopyForDirname);
|
||||
if(argCopyForBasename) free(argCopyForBasename);
|
||||
if (argCopyForDirname)
|
||||
free(argCopyForDirname);
|
||||
if (argCopyForBasename)
|
||||
free(argCopyForBasename);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -687,7 +739,7 @@ int main(int argc, char *argv[])
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--controlsdb") == 0)
|
||||
if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--controllerdb") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
@@ -823,6 +875,7 @@ int main(int argc, char *argv[])
|
||||
if (strcmp(libPath, "") == 0)
|
||||
{
|
||||
log_error("Error: %s not found in known locations.\n", PRELOAD_FILE_NAME);
|
||||
free(libPath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -835,9 +888,6 @@ int main(int argc, char *argv[])
|
||||
printf("You might not get sound because libopenal.so.0 was not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
setEnvironmentVariables(libPath, originalDir, targetedGameDir, zink, nvidia, extConfigPath, extControlsPath, extControlsDbPath,
|
||||
libOpenal);
|
||||
|
||||
if (segaboot)
|
||||
{
|
||||
@@ -857,6 +907,12 @@ int main(int argc, char *argv[])
|
||||
|
||||
isCleanElf(command);
|
||||
|
||||
setEnvironmentVariables(libPath, originalDir, targetedGameDir, zink, nvidia, extConfigPath, extControlsPath, extControlsDbPath,
|
||||
libOpenal);
|
||||
|
||||
free(libPath);
|
||||
if (libOpenal) free(libOpenal);
|
||||
|
||||
if (gdb)
|
||||
{
|
||||
char temp[128];
|
||||
|
||||
@@ -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);
|
||||
+19
-11
@@ -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) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <GL/glext.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "cardReader.h"
|
||||
#include "config.h"
|
||||
#include "resolution.h"
|
||||
#include "securityBoard.h"
|
||||
@@ -31,6 +32,8 @@ extern void *customCursor;
|
||||
extern void *touchCursor;
|
||||
extern void *blankCursor;
|
||||
|
||||
extern void (*idWriteFileHeader)(void *);
|
||||
|
||||
int amDongleInit()
|
||||
{
|
||||
return 0;
|
||||
@@ -212,6 +215,21 @@ int patchedPuts(char *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern bool gTriggerInsertKey;
|
||||
int idCardSize = 0;
|
||||
bool idTwoDigitsCardCount = false;
|
||||
|
||||
bool checkTrgOn(int param_1, long param_2)
|
||||
{
|
||||
if (param_2 == 0x1000 && (gTriggerInsertKey || getConfig()->idCardFileAutoload))
|
||||
{
|
||||
if (idCardFileExists(getConfig()->idCardFolder, idCardSize, idTwoDigitsCardCount))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int initPatch()
|
||||
{
|
||||
EmulatorConfig *config = getConfig();
|
||||
@@ -576,6 +594,10 @@ int initPatch()
|
||||
|
||||
// Set IPs
|
||||
hummerSetIPs(0x09c2fe90);
|
||||
|
||||
// Forces FFB command output
|
||||
if (getConfig()->emulateDriveboard)
|
||||
detourFunction(0x080cffb4, stubRetZero);
|
||||
}
|
||||
break;
|
||||
case HUMMER_EXTREME_MDX: // DVP-0083
|
||||
@@ -641,6 +663,10 @@ int initPatch()
|
||||
|
||||
// Set IPs
|
||||
hummerSetIPs(0x09c435b0);
|
||||
|
||||
// Forces FFB command output
|
||||
if (getConfig()->emulateDriveboard)
|
||||
detourFunction(0x080d1348, stubRetZero);
|
||||
}
|
||||
break;
|
||||
case HUMMER_SDLX: // DVP-0057
|
||||
@@ -699,6 +725,10 @@ int initPatch()
|
||||
|
||||
// Set IPs
|
||||
hummerSetIPs(0x09b30258);
|
||||
|
||||
// Forces FFB command output
|
||||
if (getConfig()->emulateDriveboard)
|
||||
detourFunction(0x082cdd1a, stubRetZero);
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVB:
|
||||
@@ -767,6 +797,15 @@ int initPatch()
|
||||
setVariable(0x08235a5d, ipSeat1);
|
||||
setVariable(0x08235a55, ipSeat2);
|
||||
}
|
||||
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0820716e, "48d479"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVC:
|
||||
@@ -835,6 +874,16 @@ int initPatch()
|
||||
setVariable(0x08235b2d, ipSeat1);
|
||||
setVariable(0x08235b25, ipSeat2);
|
||||
}
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0820722e, "c8d179"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVD:
|
||||
@@ -903,6 +952,16 @@ int initPatch()
|
||||
setVariable(0x08236c5d, ipSeat1);
|
||||
setVariable(0x08236c55, ipSeat2);
|
||||
}
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x082082e8, "88fa79"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVA:
|
||||
@@ -959,6 +1018,16 @@ int initPatch()
|
||||
}
|
||||
patchMemoryFromString(0x08548cb3, "31C090"); // cgCreateProgram args argument to 0;
|
||||
detourFunction(0x0825637a, stubRetOne); // isExistNewerSource (forces shader recompilation)
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x081f1e27, "28c777"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVB:
|
||||
@@ -1015,6 +1084,16 @@ int initPatch()
|
||||
}
|
||||
patchMemoryFromString(0x08548ef3, "31C090"); // cgCreateProgram args argument to 0;
|
||||
detourFunction(0x0825653a, stubRetOne); // isExistNewerSource (forces shader recompilation)
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x081f1f67, "e8c977"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVC:
|
||||
@@ -1071,6 +1150,16 @@ int initPatch()
|
||||
}
|
||||
patchMemoryFromString(0x08551763, "31C090"); // cgCreateProgram args argument to 0;
|
||||
detourFunction(0x08256ff4, stubRetOne); // isExistNewerSource (forces shader recompilation)
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x081f27be, "485278"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVD:
|
||||
@@ -1127,6 +1216,16 @@ int initPatch()
|
||||
}
|
||||
patchMemoryFromString(0x0854ee03, "31C090"); // cgCreateProgram args argument to 0;
|
||||
detourFunction(0x08257470, stubRetOne); // isExistNewerSource (forces shader recompilation)
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x081f2c2e, "e82878"); // RealCard --> FileCard
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1824;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVG:
|
||||
@@ -1183,6 +1282,17 @@ int initPatch()
|
||||
}
|
||||
patchMemoryFromString(0x08588f73, "31C090"); // cgCreateProgram args argument to 0;
|
||||
detourFunction(0x08271cec, stubRetOne); // isExistNewerSource (forces shader recompilation)
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0820a911, "a8a07c"); // RealCard --> FileCard
|
||||
patchMemoryFromString(0x081909a3, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1828;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP:
|
||||
@@ -1241,6 +1351,17 @@ int initPatch()
|
||||
detourFunction(0x08389544, stubRetOne); // isExistNewerSource
|
||||
detourFunction(0x0807b370, gl_XGetProcAddressARB);
|
||||
patchMemoryFromString(0x08744f2e, "00"); // Fix cutscenes
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0828573a, "489c9b"); // RealCard --> FileCard
|
||||
patchMemoryFromString(0x081cf8c3, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1354;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP_20:
|
||||
@@ -1319,6 +1440,17 @@ int initPatch()
|
||||
setVariable(0x0833e2e3, ipSeat1);
|
||||
setVariable(0x0833e2db, ipSeat2);
|
||||
}
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x082923ad, "48519e"); // RealCard --> FileCard 089e5144
|
||||
patchMemoryFromString(0x081d6ae3, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1354;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
if (getConfig()->id5ChineseLanguage)
|
||||
{
|
||||
securityBoardSetDipSwitch(1, 1);
|
||||
@@ -1402,6 +1534,17 @@ int initPatch()
|
||||
setVariable(0x0833e473, ipSeat1);
|
||||
setVariable(0x0833e46b, ipSeat2);
|
||||
}
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0829254d, "88539e"); // RealCard --> FileCard 089e5144
|
||||
patchMemoryFromString(0x081d6c83, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1354;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
if (getConfig()->id5ChineseLanguage)
|
||||
{
|
||||
securityBoardSetDipSwitch(1, 1);
|
||||
@@ -1465,6 +1608,17 @@ int initPatch()
|
||||
detourFunction(0x08388cb4, stubRetOne); // isExistNewerSource
|
||||
detourFunction(0x0807b370, gl_XGetProcAddressARB);
|
||||
patchMemoryFromString(0x0874433e, "00"); // Fix cutscenes
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0828471a, "889b9b"); // RealCard --> FileCard 089e5144
|
||||
patchMemoryFromString(0x081ce9a3, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1354;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_JAP_REVC: // ID5 - DVP-0070C
|
||||
@@ -1523,6 +1677,17 @@ int initPatch()
|
||||
detourFunction(0x08389594, stubRetOne); // isExistNewerSource
|
||||
detourFunction(0x0807b370, gl_XGetProcAddressARB);
|
||||
patchMemoryFromString(0x08744d5e, "00"); // Fix cutscenes
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x0828575a, "a8a5"); // RealCard --> FileCard 089e5144
|
||||
patchMemoryFromString(0x081cf903, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1354;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_JAP_REVF: // ID5 - DVP-0070F
|
||||
@@ -1588,6 +1753,17 @@ int initPatch()
|
||||
detourFunction(0x08397aa4, stubRetOne); // isExistNewerSource
|
||||
detourFunction(0x0807b3bc, gl_XGetProcAddressARB);
|
||||
patchMemoryFromString(0x0875e1ae, "00"); // Fix cutscenes
|
||||
|
||||
// Patch for Card Reader to use files instead
|
||||
if (getConfig()->emulateIDCardReader)
|
||||
{
|
||||
patchMemoryFromString(0x08292860, "28f29d"); // RealCard --> FileCard 089e5144
|
||||
patchMemoryFromString(0x081d7023, "eb"); // Fixes crash in findCardFiles
|
||||
patchMemoryFromString((size_t)findStaticFnAddr("_ZN11cFileCardIF9emulationEv") + 0x21, "74");
|
||||
detourFunction((size_t)findStaticFnAddr("_ZN11_sInterface10checkTrgOnENS_11PORT_NUMBEREm"), checkTrgOn);
|
||||
idCardSize = 1354;
|
||||
idTwoDigitsCardCount = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LETS_GO_JUNGLE:
|
||||
@@ -1774,6 +1950,134 @@ int initPatch()
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MJ4_EVO_REVB:
|
||||
{
|
||||
detourFunction(0x08841fbd, amDongleInit);
|
||||
detourFunction(0x08840849, amDongleIsAvailable);
|
||||
detourFunction(0x08840866, amDongleIsDevelop);
|
||||
detourFunction(0x0884136a, amDongleUpdate);
|
||||
|
||||
// Fixes and patches to bypss network check
|
||||
amDipswContextAddr = (void *)0x0af9513c; // Address of amDipswContext
|
||||
detourFunction(0x088405dc, amDipswInit);
|
||||
detourFunction(0x08840660, amDipswExit);
|
||||
detourFunction(0x088406d5, amDipswGetData);
|
||||
detourFunction(0x0884074b, amDipswSetLed);
|
||||
|
||||
patchMemoryFromString(0x080ea0f5, "909090909090");
|
||||
detourFunction(0x080e327c, stubRetZero); // skip checks
|
||||
detourFunction(0x080ee51e, stubRetZero);
|
||||
patchMemoryFromString(0x080ea0e3, "75");
|
||||
patchMemoryFromString(0x08c065b8, "00");
|
||||
|
||||
patchMemoryFromString(0x080eac5c, "01");
|
||||
patchMemoryFromString(0x08c64e00, "ffffffff");
|
||||
patchMemoryFromString(0x080e9346, "90909090909090909090");
|
||||
patchMemoryFromString(0x080eaca6, "90909090909090909090");
|
||||
|
||||
patchMemoryFromString(0x0885d08d, "2e2f72616d2f746f2e74787400"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0885d0a4, "2e2f72616d2f66726f6d2e74787400"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0885d0bd, "2e2f72616d2f2e746d7000"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0885d10d, "2e2f72616d00"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0885d11d, "2e2f666f6f3100"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0885d12e, "2e2f666f6f3200"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x08860885, "2e2f72616d2f746d7000"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x08863fef, "2e2f00"); // Patch /home/disk2 folder
|
||||
|
||||
if (GPUVendor != NVIDIA_GPU)
|
||||
{
|
||||
detourFunction(0x08051314, gl_ProgramStringARB);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MJ4_REVA:
|
||||
{
|
||||
setVariable(0x0a250914, 2);
|
||||
setVariable(0x0a250910, 2);
|
||||
setVariable(0x0a25090c, 2);
|
||||
setVariable(0x0a250908, 2);
|
||||
setVariable(0x0a250900, 2);
|
||||
setVariable(0x0a2508fc, 2);
|
||||
|
||||
// Security
|
||||
detourFunction(0x085c9d64, amDongleInit);
|
||||
detourFunction(0x085c881d, amDongleIsAvailable);
|
||||
detourFunction(0x085c883a, amDongleIsDevelop);
|
||||
detourFunction(0x085c9213, amDongleUpdate);
|
||||
|
||||
// Fixes and patches to bypss network check
|
||||
amDipswContextAddr = (void *)0x0a27a81c; // Address of amDipswContext
|
||||
detourFunction(0x085c85b0, amDipswInit);
|
||||
detourFunction(0x085c8634, amDipswExit);
|
||||
detourFunction(0x085c86a9, amDipswGetData);
|
||||
detourFunction(0x085c8720, amDipswSetLed);
|
||||
|
||||
detourFunction(0x080e209c, stubRetZero); // skip checks
|
||||
detourFunction(0x080ed218, stubRetZero);
|
||||
patchMemoryFromString(0x080e8e81, "75");
|
||||
patchMemoryFromString(0x0887ce98, "00");
|
||||
patchMemoryFromString(0x080e8e93, "909090909090"); // test skip countdown
|
||||
|
||||
patchMemoryFromString(0x085e43ad, "2e2f72616d2f746f2e74787400"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085e43c4, "2e2f72616d2f66726f6d2e74787400"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085e43dd, "2e2f72616d2f2e746d7000"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085e442d, "2e2f72616d00"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085e443d, "2e2f666f6f3100"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085e444e, "2e2f666f6f3200"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085e7b03, "2e2f72616d2f746d7000"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x085eb1c7, "2e2f00"); // Patch /home/disk2 folder
|
||||
|
||||
// Mesa Patches
|
||||
if (GPUVendor != NVIDIA_GPU)
|
||||
{
|
||||
detourFunction(0x08050b44, gl_ProgramStringARB);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MJ4_REVF:
|
||||
{
|
||||
setVariable(0x0a2d3654, 2);
|
||||
setVariable(0x0a2d3650, 2);
|
||||
setVariable(0x0a2d364c, 2);
|
||||
setVariable(0x0a2d3648, 2);
|
||||
setVariable(0x0a2d3640, 2);
|
||||
setVariable(0x0a2d363c, 2);
|
||||
|
||||
// Security
|
||||
detourFunction(0x0860533a, amDongleInit);
|
||||
detourFunction(0x08603df5, amDongleIsAvailable);
|
||||
detourFunction(0x08603e12, amDongleIsDevelop);
|
||||
detourFunction(0x086047eb, amDongleUpdate);
|
||||
|
||||
// Fixes and patches to bypss network check
|
||||
amDipswContextAddr = (void *)0x0a2fd57c; // Address of amDipswContext
|
||||
detourFunction(0x08603b88, amDipswInit);
|
||||
detourFunction(0x08603c0c, amDipswExit);
|
||||
detourFunction(0x08603c81, amDipswGetData);
|
||||
detourFunction(0x08603cf7, amDipswSetLed);
|
||||
|
||||
detourFunction(0x080e24fc, stubRetZero); // skip checks
|
||||
detourFunction(0x080ed74c, stubRetZero);
|
||||
patchMemoryFromString(0x080e9339, "75");
|
||||
patchMemoryFromString(0x088d2f38, "00");
|
||||
patchMemoryFromString(0x080e934b, "909090909090"); // test skip countdown
|
||||
|
||||
patchMemoryFromString(0x0862036d, "2e2f72616d2f746f2e74787400"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x08620384, "2e2f72616d2f66726f6d2e74787400"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0862039d, "2e2f72616d2f2e746d7000"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x086203ed, "2e2f72616d00"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x086203fd, "2e2f666f6f3100"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x0862040e, "2e2f666f6f3200"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x08623ae3, "2e2f72616d2f746d7000"); // Patch /home/disk2 folder
|
||||
patchMemoryFromString(0x086275b3, "2e2f00"); // Patch /home/disk2 folder
|
||||
|
||||
// Mesa Patches
|
||||
if (GPUVendor != NVIDIA_GPU)
|
||||
{
|
||||
detourFunction(0x08050d50, gl_ProgramStringARB);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MJ4_REVG:
|
||||
{
|
||||
setVariable(0x0acf85d4, 2);
|
||||
|
||||
+152
-30
@@ -5,6 +5,7 @@
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
@@ -68,6 +69,8 @@ static void (*idDrawBallonOri)(void *, void *, float, float, float, float, float
|
||||
|
||||
float newHeightLGJ, newWidth2LGJ, newWidthHLGJ, newWidthSLGJ;
|
||||
|
||||
extern int textureIdIdxAdjust;
|
||||
|
||||
bool myEnableScaling = false;
|
||||
|
||||
int hummerRespatch()
|
||||
@@ -188,6 +191,15 @@ void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
|
||||
height = gHeight;
|
||||
}
|
||||
}
|
||||
// Patches the Resolution even tho is not needed to fix patched ELFs.
|
||||
else if (gWidth == 800 && gHeight == 480)
|
||||
{
|
||||
if (width == 1280 && height == 768)
|
||||
{
|
||||
width = 800;
|
||||
height = 480;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gId == SEGA_RACE_TV)
|
||||
{
|
||||
@@ -284,9 +296,9 @@ void glTexImage2D(unsigned int target, int level, int internalformat, int width,
|
||||
void (*_glTexImage2D)(unsigned int target, int level, int internalformat, int width, int height, int border, unsigned int format,
|
||||
unsigned int type, const void *pixels) = dlsym(RTLD_NEXT, "glTexImage2D");
|
||||
|
||||
if (gWidth != 800 || gHeight != 480)
|
||||
if (gGrp == GROUP_OUTRUN)
|
||||
{
|
||||
if (gGrp == GROUP_OUTRUN)
|
||||
if (gWidth != 800 || gHeight != 480)
|
||||
{
|
||||
void *addr = __builtin_return_address(0);
|
||||
if ((width >= 800) && (width != 1024) && (addr != (void *)0x80d78d5) && (addr != (void *)0x080d7941))
|
||||
@@ -295,6 +307,15 @@ void glTexImage2D(unsigned int target, int level, int internalformat, int width,
|
||||
height = gHeight;
|
||||
}
|
||||
}
|
||||
// Patches the Resolution even tho is not needed to fix patched ELFs.
|
||||
else if (gWidth == 800 && gHeight == 480)
|
||||
{
|
||||
if (width == 1280 && height == 768)
|
||||
{
|
||||
width = 800;
|
||||
height = 480;
|
||||
}
|
||||
}
|
||||
}
|
||||
_glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
@@ -347,7 +368,9 @@ void myGlBindTexture(GLenum target, GLuint texture)
|
||||
{
|
||||
curTarget = target;
|
||||
curTextureID = texture;
|
||||
curTextureID--;
|
||||
curTextureID--; // We adjust for the added blitting texture
|
||||
|
||||
curTextureID -= textureIdIdxAdjust; // We also adjust for the crosshair textures.
|
||||
|
||||
void (*_glBindTexture)(GLenum, GLuint) = dlsym(RTLD_NEXT, "glBindTexture");
|
||||
_glBindTexture(target, texture);
|
||||
@@ -1127,7 +1150,7 @@ void glVertex3fHOD4(GLfloat x, GLfloat y, GLfloat z)
|
||||
else if (z == -1.510000f)
|
||||
{ // logo 2d elemtents target
|
||||
|
||||
if (curTextureID != 262) // target boss fight // Added 2 because of the crosshairs.
|
||||
if (curTextureID != 260) // target boss fight
|
||||
{
|
||||
x += OffsetX;
|
||||
y -= OffsetY;
|
||||
@@ -1218,7 +1241,6 @@ void glVertex3fHOD4(GLfloat x, GLfloat y, GLfloat z)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (curTextureID != 26)
|
||||
{ // gun effect
|
||||
if (z > -1.8f && z < -1.0f)
|
||||
@@ -1279,6 +1301,77 @@ void glVertex3f2SP(GLfloat x, GLfloat y, GLfloat z)
|
||||
|
||||
if (z == -1.000000f)
|
||||
{ // 3d
|
||||
// printf("All x values: %.10f\n", x);
|
||||
// printf("All y values: %.10f\n", y);
|
||||
}
|
||||
|
||||
else if (z == -1.3099999428f)
|
||||
{
|
||||
|
||||
if ((x == 0.1838379353f && y == -0.0376717076f) || (x == -0.1838379353f && y == 0.0376717076f) ||
|
||||
(x == 0.1838379353f && y == 0.0376717076f) || (x == -0.1838379353f && y == -0.0376717076f) ||
|
||||
(x == -0.1819543540f && y == -0.0380484276f) || (x == 0.1819543540f && y == -0.0380484276f) ||
|
||||
(x == 0.1819543540f && y == -0.1126384139f) || (x == -0.1819543540f && y == -0.1126384139f))
|
||||
{
|
||||
// if (curTextureID == 499 || curTextureID == 500){ //500 reload - 499 hide and reload
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += AdjOffsetX;
|
||||
y -= AdjOffsetY;
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
}
|
||||
|
||||
else if (z == -1.5099999905f)
|
||||
{
|
||||
|
||||
if ((curTextureID >= 479 && curTextureID < 489) || curTextureID == 504 || curTextureID == 505 || curTextureID == 506)
|
||||
{ // Enemy portraits in the HUD
|
||||
if (x < 0.0f)
|
||||
{
|
||||
x += AdjOffsetX;
|
||||
y -= AdjOffsetY;
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
else
|
||||
{
|
||||
x -= OffsetX;
|
||||
y -= OffsetY;
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
}
|
||||
else if (curTextureID == 497) // right
|
||||
{
|
||||
x -= OffsetX;
|
||||
y -= OffsetY;
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
else if (curTextureID == 498)
|
||||
{ // deadend
|
||||
}
|
||||
else
|
||||
{
|
||||
x += AdjOffsetX;
|
||||
y -= AdjOffsetY;
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
}
|
||||
|
||||
else if (z == -1.2100000381f)
|
||||
{
|
||||
|
||||
x += AdjOffsetX;
|
||||
y -= AdjOffsetY;
|
||||
x *= scaleX;
|
||||
y *= scaleY;
|
||||
}
|
||||
else if (z == -3.000000f)
|
||||
{ // videos
|
||||
@@ -1292,7 +1385,7 @@ void glVertex3f2SP(GLfloat x, GLfloat y, GLfloat z)
|
||||
y *= scaleY;
|
||||
}
|
||||
else if (z == -1.510000f)
|
||||
{ // logo - coin/start - 2D
|
||||
{ // logo - coin/start - 2D
|
||||
if (curTextureID == 497) // right
|
||||
{
|
||||
x -= OffsetX;
|
||||
@@ -2439,6 +2532,20 @@ int initResolutionPatches()
|
||||
setVariable(addressC + 0x8, (size_t)newWidthSPtrLGJ);
|
||||
}
|
||||
break;
|
||||
case MJ4_REVA:
|
||||
{
|
||||
patchMemoryFromString(0x080520b1, "01"); // Enable Anti Alias
|
||||
patchMemoryFromString(0x080520aa, "03"); // Force 1024x768
|
||||
patchMemoryFromString(0x080531fa, "b803000000"); // Skips resolution set by the Dip Switches.
|
||||
}
|
||||
break;
|
||||
case MJ4_REVF:
|
||||
{
|
||||
patchMemoryFromString(0x080522e1, "01"); // Enable Anti Alias
|
||||
patchMemoryFromString(0x080522da, "03"); // Force 1024x768
|
||||
patchMemoryFromString(0x0805346a, "b803000000"); // Skips resolution set by the Dip Switches.
|
||||
}
|
||||
break;
|
||||
case MJ4_REVG:
|
||||
{
|
||||
patchMemoryFromString(0x080524a1, "01"); // Enable Anti Alias
|
||||
@@ -2453,6 +2560,13 @@ int initResolutionPatches()
|
||||
patchMemoryFromString(0x08053b24, "b803000000"); // Skips resolution set by the Dip Switches.
|
||||
}
|
||||
break;
|
||||
case MJ4_EVO_REVB:
|
||||
{
|
||||
patchMemoryFromString(0x08052961, "01"); // Enable Anti Alias
|
||||
patchMemoryFromString(0x0805295a, "03"); // Force 1024x768
|
||||
patchMemoryFromString(0x08053b24, "b803000000"); // Skips resolution set by the Dip Switches.
|
||||
}
|
||||
break;
|
||||
case OUTRUN_2_SP_SDX:
|
||||
{
|
||||
if (gWidth <= 800 && gHeight <= 480)
|
||||
@@ -2486,24 +2600,11 @@ int initResolutionPatches()
|
||||
break;
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
{
|
||||
if (gWidth <= 800 && gHeight <= 480)
|
||||
break;
|
||||
// If resolution is not the native of the game this patch kind of fix the Sun when the LensGlare effect is
|
||||
// shown.
|
||||
if ((gWidth > 800) && (gHeight > 480))
|
||||
{
|
||||
patchMemoryFromString(0x080e8e06, "9090909090"); // removes a call to a light function
|
||||
patchMemoryFromString(0x080e8e17, "9090909090"); // removes a call to a light function
|
||||
if (!getConfig()->outrunLensGlareEnabled)
|
||||
{
|
||||
detourFunction(0x080e8ac8, stubReturn); // Completely disables the lens glare effect
|
||||
}
|
||||
}
|
||||
setVariable(0x080b913a, 0x44200000);
|
||||
setVariable(0x081dada8, 0x44200000);
|
||||
setVariable(0x081dadb0, 0x44200000);
|
||||
|
||||
// Clean patched elf floating around
|
||||
// Fixes patched ELFs
|
||||
patchMemoryFromString(0x080b912e, "f043");
|
||||
patchMemoryFromString(0x080f39de, "c0ef43");
|
||||
patchMemoryFromString(0x080f3aa6, "2048");
|
||||
@@ -2519,6 +2620,20 @@ int initResolutionPatches()
|
||||
setVariable(0x081dadbc, 0x43f00000);
|
||||
patchMemoryFromString(0x081e7b3e, "7e");
|
||||
patchMemoryFromString(0x081e7b46, "89");
|
||||
|
||||
if (gWidth <= 800 && gHeight <= 480)
|
||||
break;
|
||||
// If resolution is not the native of the game this patch kind of fix the Sun when the LensGlare effect is
|
||||
// shown.
|
||||
if ((gWidth > 800) && (gHeight > 480))
|
||||
{
|
||||
patchMemoryFromString(0x080e8e06, "9090909090"); // removes a call to a light function
|
||||
patchMemoryFromString(0x080e8e17, "9090909090"); // removes a call to a light function
|
||||
if (!getConfig()->outrunLensGlareEnabled)
|
||||
{
|
||||
detourFunction(0x080e8ac8, stubReturn); // Completely disables the lens glare effect
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
@@ -2572,6 +2687,7 @@ int initResolutionPatches()
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (sdlDisplayId) SDL_free(sdlDisplayId);
|
||||
return 0;
|
||||
}
|
||||
case 1: // No touch screen
|
||||
@@ -2630,17 +2746,16 @@ int initResolutionPatches()
|
||||
}
|
||||
break;
|
||||
}
|
||||
setVariable(0x0805b104, phX); // X 1st screen
|
||||
setVariable(0x0805b0fd, phY); // Y 1st screen
|
||||
setVariable(0x0805b0f5, phW); // Width 1st screen
|
||||
setVariable(0x0805b0ed, phH); // Height 1st screen
|
||||
setVariable(0x0805afa2, phX2); // X 2nd screen
|
||||
setVariable(0x0805af9b, phY2); // Y 2st screen
|
||||
setVariable(0x0805af93, phW2); // Width 2nd screen
|
||||
setVariable(0x0805af8b, phH2); // Height 2nd screen
|
||||
setVariable(0x0805b104, phX); // X 1st screen
|
||||
setVariable(0x0805b0fd, phY); // Y 1st screen
|
||||
setVariable(0x0805b0f5, phW); // Width 1st screen
|
||||
setVariable(0x0805b0ed, phH); // Height 1st screen
|
||||
setVariable(0x0805afa2, phX2); // X 2nd screen
|
||||
setVariable(0x0805af9b, phY2); // Y 2st screen
|
||||
setVariable(0x0805af93, phW2); // Width 2nd screen
|
||||
setVariable(0x0805af8b, phH2); // Height 2nd screen
|
||||
// patchMemoryFromString(0x08052cc4, "0000"); // swap screens
|
||||
detourFunction(0x0804c628, glClearColorPH);
|
||||
|
||||
|
||||
// Test Menu
|
||||
if (isTestMode())
|
||||
@@ -2757,6 +2872,7 @@ int initResolutionPatches()
|
||||
{
|
||||
if (getConfig()->boostRenderRes)
|
||||
setVariable(0x084c9dbc, 1280);
|
||||
if (sdlDisplayId) SDL_free(sdlDisplayId);
|
||||
return 0;
|
||||
}
|
||||
else if (gWidth == 640 && gHeight == 480)
|
||||
@@ -2786,6 +2902,7 @@ int initResolutionPatches()
|
||||
{
|
||||
if (getConfig()->boostRenderRes)
|
||||
setVariable(0x084c3a9c, 1280);
|
||||
if (sdlDisplayId) SDL_free(sdlDisplayId);
|
||||
return 0;
|
||||
}
|
||||
else if (gWidth == 640 && gHeight == 480)
|
||||
@@ -2815,6 +2932,7 @@ int initResolutionPatches()
|
||||
{
|
||||
if (getConfig()->boostRenderRes)
|
||||
setVariable(0x084c3a9c, 1280);
|
||||
if (sdlDisplayId) SDL_free(sdlDisplayId);
|
||||
return 0;
|
||||
}
|
||||
else if (gWidth == 640 && gHeight == 480)
|
||||
@@ -2928,8 +3046,10 @@ int initResolutionPatches()
|
||||
break;
|
||||
case VIRTUA_FIGHTER_5:
|
||||
{
|
||||
if ((gWidth == 640 && gHeight == 480) || (gWidth == 1280 && gHeight == 768))
|
||||
if ((gWidth == 640 && gHeight == 480) || (gWidth == 1280 && gHeight == 768)) {
|
||||
if (sdlDisplayId) SDL_free(sdlDisplayId);
|
||||
return 0;
|
||||
}
|
||||
patchMemoryFromString(0x08054057, "b80a000000"); // Skips resolution set by the Dip Switches.
|
||||
setVariable(0x0847cf58, gWidth);
|
||||
setVariable(0x0847cf5c, gHeight);
|
||||
@@ -3153,5 +3273,7 @@ int initResolutionPatches()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sdlDisplayId)
|
||||
SDL_free(sdlDisplayId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+62
-15
@@ -15,12 +15,14 @@
|
||||
|
||||
#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;
|
||||
@@ -35,6 +37,7 @@ SDL_GLContext sdlContext = NULL;
|
||||
Display *x11Display = NULL;
|
||||
Window x11Window;
|
||||
char sdlGameTitle[256] = {0};
|
||||
bool sdlInputInitialized = false;
|
||||
|
||||
extern SDL_Cursor *customCursor;
|
||||
extern SDL_Cursor *touchCursor;
|
||||
@@ -99,6 +102,7 @@ void startSDL(int *argcp, char **argv)
|
||||
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());
|
||||
@@ -117,7 +121,7 @@ void startSDL(int *argcp, char **argv)
|
||||
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 ||
|
||||
gId == MJ4_REVG || gId == MJ4_EVO)
|
||||
gGrp == GROUP_MJ4 )
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
|
||||
}
|
||||
@@ -126,11 +130,18 @@ void startSDL(int *argcp, char **argv)
|
||||
|
||||
strcat(sdlGameTitle, getGameName());
|
||||
|
||||
int windowWidth = gWidth;
|
||||
|
||||
if(gGrp == GROUP_OUTRUN && getConfig()->width == 640)
|
||||
{
|
||||
gWidth = 800;
|
||||
}
|
||||
|
||||
// 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);
|
||||
sdlWindow = SDL_CreateWindow(sdlGameTitle, windowWidth, gHeight, windowFlags);
|
||||
|
||||
if (!sdlWindow)
|
||||
{
|
||||
@@ -139,7 +150,7 @@ void startSDL(int *argcp, char **argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (gId == GHOST_SQUAD_EVOLUTION || gId == MJ4_EVO || gId == MJ4_REVG)
|
||||
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);
|
||||
@@ -168,11 +179,11 @@ void startSDL(int *argcp, char **argv)
|
||||
|
||||
// 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);
|
||||
SDL_SetWindowMaximumSize(sdlWindow, windowWidth, gHeight);
|
||||
else if (gGrp != GROUP_LGJ)
|
||||
SDL_SetWindowMaximumSize(sdlWindow, displayMode->w, displayMode->h);
|
||||
|
||||
SDL_SetWindowMinimumSize(sdlWindow, gWidth, gHeight);
|
||||
SDL_SetWindowMinimumSize(sdlWindow, windowWidth, gHeight);
|
||||
|
||||
// Hacky way to make AxA games render the characters properly
|
||||
if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE)
|
||||
@@ -187,6 +198,9 @@ void startSDL(int *argcp, char **argv)
|
||||
|
||||
initBlitting();
|
||||
|
||||
if (gId != PRIMEVAL_HUNT && gGrp != GROUP_LGJ)
|
||||
SDL_SetWindowResizable(sdlWindow, true);
|
||||
|
||||
SDL_ShowWindow(sdlWindow);
|
||||
|
||||
Uint64 startTime = SDL_GetTicks();
|
||||
@@ -199,13 +213,9 @@ void startSDL(int *argcp, char **argv)
|
||||
running = 0;
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
SDL_GL_SwapWindow(sdlWindow);
|
||||
}
|
||||
|
||||
if (gId != PRIMEVAL_HUNT)
|
||||
SDL_SetWindowResizable(sdlWindow, true);
|
||||
|
||||
creatingWindow = false;
|
||||
|
||||
printf(" RESOLUTION: %dx%d\n", gWidth, gHeight);
|
||||
@@ -214,7 +224,12 @@ void startSDL(int *argcp, char **argv)
|
||||
if (customCursor)
|
||||
setCursor(customCursor);
|
||||
|
||||
if ((gId == MJ4_REVG || gId == MJ4_EVO || gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE) && strcmp(getConfig()->touchCursor, "") != 0 &&
|
||||
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)
|
||||
@@ -227,7 +242,33 @@ void sdlQuit()
|
||||
usleep(50000);
|
||||
setSwitch(SYSTEM, BUTTON_TEST, 0);
|
||||
usleep(50000);
|
||||
SDL_DestroyWindow(sdlWindow);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -237,6 +278,9 @@ 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:
|
||||
@@ -309,8 +353,11 @@ void pollEvents()
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gGrp == GROUP_HOD4 || gGrp == GROUP_HOD4_TEST)
|
||||
updateGunShake();
|
||||
updateCombinedAxes();
|
||||
processChangedActions();
|
||||
if (sdlInputInitialized)
|
||||
{
|
||||
if (gGrp == GROUP_HOD4 || gGrp == GROUP_HOD4_TEST)
|
||||
updateGunShake();
|
||||
updateCombinedAxes();
|
||||
processChangedActions();
|
||||
}
|
||||
}
|
||||
|
||||
+111
-6
@@ -6,6 +6,7 @@
|
||||
#include <SDL3/SDL_keyboard.h>
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <SDL3/SDL_scancode.h>
|
||||
#include <libgen.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -15,12 +16,15 @@
|
||||
|
||||
#include "blitStretching.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "forceFeedback.h"
|
||||
#include "gameControllerMappings.h"
|
||||
#include "iniParser.h"
|
||||
#include "sdlInput.h"
|
||||
#include "jvs.h"
|
||||
#include "log.h"
|
||||
#include "touchScreen.h"
|
||||
#include "wiimoteEvdev.h"
|
||||
|
||||
// --- GLOBAL STATE AND MAPPINGS ---
|
||||
ActionState gActionStates[MAX_ENTITIES][NUM_LOGICAL_ACTIONS] = {0};
|
||||
@@ -50,6 +54,7 @@ ControlBinding gJoyButtonBindings[MAX_JOYSTICKS][MAX_JOY_BUTTONS] = {0};
|
||||
BindingPair gControllerAxisBindings[MAX_JOYSTICKS][SDL_GAMEPAD_AXIS_COUNT] = {0};
|
||||
ControlBinding gControllerButtonBindings[MAX_JOYSTICKS][SDL_GAMEPAD_BUTTON_COUNT] = {0};
|
||||
HatBinding gJoyHatBindings[MAX_JOYSTICKS][MAX_JOY_HATS] = {0};
|
||||
bool gTriggerInsertKey = false;
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
@@ -58,14 +63,21 @@ extern int gHeight;
|
||||
int widthOri;
|
||||
int heightOri;
|
||||
extern SDL_Window *sdlWindow;
|
||||
extern bool sdlInputInitialized;
|
||||
|
||||
extern Dest dest;
|
||||
|
||||
extern int phX, phY, phW, phH;
|
||||
int phIsDragging = 0;
|
||||
|
||||
extern bool mj4MousePressed;
|
||||
int mj4MouseX = 0;
|
||||
int mj4MouseY = 0;
|
||||
extern int drawableW;
|
||||
extern int drawableH;
|
||||
extern bool mj4TouchedInsideScreen;
|
||||
|
||||
extern bool gTriggerInsertKey;
|
||||
|
||||
int jvsAnalogueMaxValue;
|
||||
int jvsAnalogueCenterValue;
|
||||
@@ -201,7 +213,7 @@ int initSdlInput(char *controlsPath)
|
||||
if (envDbPath)
|
||||
{
|
||||
SDL_AddGamepadMappingsFromFile(envDbPath);
|
||||
printf("%s, as a controller database.\n", envDbPath);
|
||||
printf("%s loaded as a controller database.\n", basename(envDbPath));
|
||||
}
|
||||
|
||||
gameType = getConfig()->gameType;
|
||||
@@ -366,8 +378,17 @@ int initSdlInput(char *controlsPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_free(joystics);
|
||||
}
|
||||
|
||||
if (sdlJoysticks.joysticksCount > 0)
|
||||
{
|
||||
sdlFfbInit();
|
||||
}
|
||||
|
||||
findAndOpenWiiMotes(&sdlJoysticks);
|
||||
startWiimoteThreads();
|
||||
|
||||
// Save any new GUID assignments back to the INI file.
|
||||
if (isProfileLoaded)
|
||||
saveGuidsToIni();
|
||||
@@ -378,6 +399,7 @@ int initSdlInput(char *controlsPath)
|
||||
// Apply any final game-specific mapping overrides.
|
||||
remapPerGame();
|
||||
|
||||
sdlInputInitialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1227,6 +1249,48 @@ void addActionToDirtyList(JVSPlayer player, LogicalAction action)
|
||||
*/
|
||||
void processSdlEvent(const SDL_Event *e)
|
||||
{
|
||||
// Make sure to use the variable, not the macro name
|
||||
if (e->type == SDL_WIIMOTION_EVENT)
|
||||
{
|
||||
int controllerIndex = e->user.code;
|
||||
int irX = (intptr_t)e->user.data1;
|
||||
int irY = (intptr_t)e->user.data2;
|
||||
|
||||
if (irX == 0 || irX == 1023 || irY == 0 || irY == 767)
|
||||
{
|
||||
gActionStates[controllerIndex + 1][LA_Reload].isActive = true;
|
||||
addActionToDirtyList(controllerIndex + 1, LA_Reload);
|
||||
}
|
||||
else
|
||||
{
|
||||
gActionStates[controllerIndex + 1][LA_Reload].isActive = false;
|
||||
addActionToDirtyList(controllerIndex + 1, LA_Reload);
|
||||
}
|
||||
|
||||
// The Wii Remote IR gives 0-1023 for X and 0-767 for Y.
|
||||
// We can normalize this to 0.0 - 1.0 to behave like our mouse input.
|
||||
float posX = 1.0f - (irX / 1023.0f);
|
||||
float posY = irY / 767.0f;
|
||||
|
||||
// printf("idx: %d, x: %f, y: %f\n", controller_index, posX, posY);
|
||||
|
||||
ActionState *stateX = &gActionStates[controllerIndex + 1][LA_GunX];
|
||||
if (fabs(stateX->analogValue - posX) > 0.001f)
|
||||
{
|
||||
stateX->analogValue = posX;
|
||||
addActionToDirtyList(controllerIndex + 1, LA_GunX);
|
||||
}
|
||||
|
||||
ActionState *stateY = &gActionStates[controllerIndex + 1][LA_GunY];
|
||||
if (fabs(stateY->analogValue - posY) > 0.001f)
|
||||
{
|
||||
stateY->analogValue = posY;
|
||||
addActionToDirtyList(controllerIndex + 1, LA_GunY);
|
||||
}
|
||||
|
||||
return; // Event handled
|
||||
}
|
||||
|
||||
switch (e->type)
|
||||
{
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
@@ -1517,14 +1581,16 @@ void processSdlEvent(const SDL_Event *e)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gameType == MAHJONG && getConfig()->emulateTouchscreen)
|
||||
handleMahjongTouch(e, drawableW, drawableH);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
double mX = e->motion.x;
|
||||
double mY = e->motion.y;
|
||||
float posX, posY;
|
||||
float mX = e->motion.x;
|
||||
float mY = e->motion.y;
|
||||
float posX = 0.0f, posY = 0.0f;
|
||||
|
||||
if (gId == PRIMEVAL_HUNT)
|
||||
{
|
||||
@@ -1534,8 +1600,8 @@ void processSdlEvent(const SDL_Event *e)
|
||||
|
||||
int motX = mX, motY = mY;
|
||||
phTouchScreenCursor(mX, mY, &motX, &motY);
|
||||
posX = ((double)(motX - phX) / (double)phW);
|
||||
posY = ((double)(motY - phY) / (double)phH);
|
||||
posX = ((float)(motX - phX) / (float)phW);
|
||||
posY = ((float)(motY - phY) / (float)phH);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1774,6 +1840,14 @@ void processChangedActions()
|
||||
switch (map->call_type)
|
||||
{
|
||||
case JVS_CALL_SWITCH:
|
||||
if ((gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP || gGrp == GROUP_ID5) && actionId == LA_CardInsert)
|
||||
{
|
||||
if (state->isActive)
|
||||
gTriggerInsertKey = true;
|
||||
else
|
||||
gTriggerInsertKey = false;
|
||||
break;
|
||||
}
|
||||
setSwitch(player, map->jvsInput, state->isActive);
|
||||
break;
|
||||
case JVS_CALL_ANALOGUE:
|
||||
@@ -1802,6 +1876,36 @@ void processChangedActions()
|
||||
(gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP || gGrp == GROUP_ID5))
|
||||
state->analogValue = (state->analogValue - 0.5f) * (getConfig()->idSteeringPercentageReduction / 100.0f) + 0.5f;
|
||||
|
||||
if (p1CrossHairInitialized && (map->jvsInput == ANALOGUE_1 || map->jvsInput == ANALOGUE_2))
|
||||
{
|
||||
static float lastAnalogue1 = 0.0f;
|
||||
static float lastAnalogue2 = 0.0f;
|
||||
if (map->jvsInput == ANALOGUE_1)
|
||||
lastAnalogue1 = state->analogValue;
|
||||
else
|
||||
lastAnalogue2 = state->analogValue;
|
||||
|
||||
float xPos = map->jvsInput == ANALOGUE_1 ? state->analogValue : lastAnalogue1;
|
||||
float yPos = map->jvsInput == ANALOGUE_2 ? state->analogValue : lastAnalogue2;
|
||||
|
||||
updateCrosshairPosition(player - 1, xPos, yPos);
|
||||
}
|
||||
|
||||
if (p2CrossHairInitialized && (map->jvsInput == ANALOGUE_3 || map->jvsInput == ANALOGUE_4))
|
||||
{
|
||||
static float lastAnalogue3 = 0.0f;
|
||||
static float lastAnalogue4 = 0.0f;
|
||||
if (map->jvsInput == ANALOGUE_3)
|
||||
lastAnalogue3 = state->analogValue;
|
||||
else
|
||||
lastAnalogue4 = state->analogValue;
|
||||
|
||||
float xPos = map->jvsInput == ANALOGUE_3 ? state->analogValue : lastAnalogue3;
|
||||
float yPos = map->jvsInput == ANALOGUE_4 ? state->analogValue : lastAnalogue4;
|
||||
|
||||
updateCrosshairPosition(player - 1, xPos, yPos);
|
||||
}
|
||||
|
||||
setAnalogue(map->jvsInput, (int)(state->analogValue * jvsAnalogueMaxValue));
|
||||
break;
|
||||
case JVS_CALL_COIN:
|
||||
@@ -2049,6 +2153,7 @@ int listSdlControllers(void)
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
SDL_free(joysticks);
|
||||
}
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
|
||||
@@ -799,6 +799,10 @@ void cacheModedShaderFiles()
|
||||
memcpy((void *)shaderFilesToPatch[x].shaderBuffer, buff, patchedFile.fsize);
|
||||
free(buff);
|
||||
}
|
||||
|
||||
xdl_free_mmfile(&oriFile);
|
||||
xdl_free_mmfile(&patch);
|
||||
xdl_free_mmfile(&patchedFile);
|
||||
}
|
||||
|
||||
if (getConfig()->showDebugMessages)
|
||||
|
||||
@@ -157,6 +157,10 @@ char *replaceInBlock(char *source, SearchReplace *searchReplace, int searchRepla
|
||||
char *new_result = replaceSubstring(result, start_index, end_index, target, replacement);
|
||||
if (new_result != NULL)
|
||||
{
|
||||
if (result != source)
|
||||
{
|
||||
free(result);
|
||||
}
|
||||
result = new_result;
|
||||
}
|
||||
}
|
||||
@@ -328,8 +332,15 @@ void gsEvoElfShaderPatcher()
|
||||
|
||||
newProgram = replaceInBlock(tmpProgram, gsevoElf, gsevoElfCount, "", "");
|
||||
|
||||
char *prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, gsevoElfMesa, gsevoElfCountMesa, "", "");
|
||||
if (prevProgram != tmpProgram && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
|
||||
prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, gsevoSet1, gsevoSetCount1, "", "");
|
||||
if (prevProgram != tmpProgram && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
|
||||
int newProgramLen = strlen(newProgram);
|
||||
|
||||
@@ -342,6 +353,9 @@ void gsEvoElfShaderPatcher()
|
||||
}
|
||||
|
||||
memcpy((void *)(size_t)gsevoElfShaderOffsets[i].startOffset, newProgram, newProgramLen);
|
||||
|
||||
if (newProgram != tmpProgram)
|
||||
free(newProgram);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,13 +433,18 @@ void srtvElfShaderPatcher()
|
||||
|
||||
cleanShaderString(newProgram);
|
||||
|
||||
char *prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, srtvElf2, srtvElfCount2, "", "");
|
||||
if (prevProgram != tmpProgram && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
|
||||
int newProgramLen = strlen(newProgram);
|
||||
|
||||
newProgram[newProgramLen] = '\0';
|
||||
|
||||
memcpy((void *)(size_t)srtvElfShaderOffsets[i].startOffset, newProgram, newProgramLen);
|
||||
if (newProgram != tmpProgram)
|
||||
free(newProgram);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,28 +483,23 @@ char *cgGetProgramString(char *program, int e)
|
||||
*/
|
||||
void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *program)
|
||||
{
|
||||
char *newProgram;
|
||||
char *newProgram = program;
|
||||
char *program_to_free = NULL;
|
||||
int newProgramLen;
|
||||
void *addr = __builtin_return_address(0);
|
||||
|
||||
if(gId == GHOST_SQUAD_EVOLUTION)
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
if (addr == (void *)0x8179fab)
|
||||
{
|
||||
newProgram = replaceInBlock(program, gsevoSet1, gsevoSetCount1, "", "");
|
||||
int newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
}
|
||||
else if(gGrp == GROUP_LGJ)
|
||||
else if (gGrp == GROUP_LGJ)
|
||||
{
|
||||
newProgram = replaceInBlock(program, lgjCompiledShadersPatch, lgjCompiledShadersPatchCount, "", "");
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if(gGrp == GROUP_ABC)
|
||||
else if (gGrp == GROUP_ABC)
|
||||
{
|
||||
if (target == GL_VERTEX_PROGRAM_ARB)
|
||||
{
|
||||
@@ -495,11 +509,8 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
{
|
||||
newProgram = replaceInBlock(program, abcFsMesa, abcFsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if(gGrp == GROUP_OUTRUN)
|
||||
else if (gGrp == GROUP_OUTRUN)
|
||||
{
|
||||
if (target == GL_VERTEX_PROGRAM_ARB)
|
||||
{
|
||||
@@ -517,9 +528,6 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
else
|
||||
newProgram = replaceInBlock(program, orFsMesa, orFsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if (gGrp == GROUP_VF5)
|
||||
{
|
||||
@@ -531,7 +539,10 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
if (gId == VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA || gId == VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVB ||
|
||||
gId == VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVB_6000)
|
||||
{
|
||||
char *prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, vf5VsAtifs, vf5VsAtifsCount, "", "");
|
||||
if (prevProgram != program && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -546,9 +557,6 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
else
|
||||
newProgram = replaceInBlock(program, vf5FsMesa, vf5FsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if (gId == R_TUNED)
|
||||
{
|
||||
@@ -560,11 +568,8 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
{
|
||||
newProgram = replaceInBlock(program, rtunedFsMesa, rtunedFsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if (gId == MJ4_REVG || gId == MJ4_EVO)
|
||||
else if (gGrp == GROUP_MJ4)
|
||||
{
|
||||
if (target == GL_VERTEX_PROGRAM_ARB)
|
||||
{
|
||||
@@ -574,11 +579,13 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
{
|
||||
newProgram = replaceInBlock(program, mj4FsMesa, mj4FsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
glProgramStringARB(target, program_fmt, program_len, program);
|
||||
|
||||
if (newProgram != program)
|
||||
program_to_free = newProgram;
|
||||
|
||||
newProgramLen = strlen(newProgram);
|
||||
glProgramStringARB(target, program_fmt, newProgramLen, newProgram);
|
||||
|
||||
if (gGrp == GROUP_VF5)
|
||||
{
|
||||
@@ -587,12 +594,11 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
int err = glGetError();
|
||||
if (err == 1282)
|
||||
{
|
||||
char *newProgram;
|
||||
newProgram = replaceInBlock(program, vf5VsAti2, vf5VsAtiCount2, "", "");
|
||||
int newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
glProgramStringARB(target, program_fmt, program_len, program);
|
||||
char *newProgram2 = replaceInBlock(newProgram, vf5VsAti2, vf5VsAtiCount2, "", "");
|
||||
int newProgramLen2 = strlen(newProgram2);
|
||||
glProgramStringARB(target, program_fmt, newProgramLen2, newProgram2);
|
||||
if (newProgram2 != newProgram)
|
||||
free(newProgram2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -616,9 +622,9 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
FILE *f;
|
||||
sprintf(filename, "%s/Error-%d.prg", dir_only, idxError);
|
||||
// sprintf(filename, "%s/%s", dir_only, fname);
|
||||
printf("Size: %d\n", program_len);
|
||||
printf("Size: %d\n", newProgramLen);
|
||||
f = fopen(filename, "w+");
|
||||
fwrite(program, sizeof(char), program_len, f);
|
||||
fwrite(newProgram, sizeof(char), newProgramLen, f);
|
||||
fclose(f);
|
||||
idxError++;
|
||||
}
|
||||
@@ -638,6 +644,8 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
}
|
||||
}
|
||||
}
|
||||
if (program_to_free)
|
||||
free(program_to_free);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -710,16 +718,20 @@ char *cgCreateProgram(uint32_t context, int program_type, const char *program, i
|
||||
|
||||
void gl_ShaderSourceARB(GLhandleARB shaderObj, GLsizei count, const GLcharARB **const string, const GLint *length)
|
||||
{
|
||||
if(gGrp != GROUP_HUMMER && gId != SEGA_RACE_TV)
|
||||
if (gGrp != GROUP_HUMMER && gId != SEGA_RACE_TV)
|
||||
{
|
||||
glShaderSourceARB(shaderObj, count, string, length);
|
||||
return;
|
||||
}
|
||||
|
||||
char *newProgram = malloc(250000);
|
||||
char *program_to_free = NULL;
|
||||
const GLcharARB *final_string = *string;
|
||||
GLint final_length = (length ? *length : 0);
|
||||
|
||||
if(gId == SEGA_RACE_TV)
|
||||
if (gId == SEGA_RACE_TV)
|
||||
{
|
||||
char *newProgram = NULL;
|
||||
char tmp[250000];
|
||||
void *addr = __builtin_return_address(0);
|
||||
if (addr <= (void *)0x82fd823)
|
||||
{
|
||||
@@ -729,35 +741,43 @@ void gl_ShaderSourceARB(GLhandleARB shaderObj, GLsizei count, const GLcharARB **
|
||||
{
|
||||
if (getConfig()->GPUVendor != NVIDIA_GPU)
|
||||
{
|
||||
sprintf(newProgram, "#version 120\r\n%s", *string);
|
||||
newProgram = replaceInBlock(newProgram, srtvVsMesa, srtvVsMesaCount, "", "");
|
||||
sprintf(tmp, "#version 120\r\n%s", *string);
|
||||
newProgram = replaceInBlock(tmp, srtvVsMesa, srtvVsMesaCount, "", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(newProgram, "%s", *string);
|
||||
newProgram = replaceInBlock(newProgram, srtvVsNvidia, srtvVsNvidiaCount, "", "");
|
||||
sprintf(tmp, "%s", *string);
|
||||
newProgram = replaceInBlock(tmp, srtvVsNvidia, srtvVsNvidiaCount, "", "");
|
||||
}
|
||||
}
|
||||
else if (addr == (void *)0x084bc06b)
|
||||
{
|
||||
if (getConfig()->GPUVendor != NVIDIA_GPU)
|
||||
{
|
||||
sprintf(newProgram, "#version 120\r\n%s", *string);
|
||||
newProgram = replaceInBlock(newProgram, srtvFsMesa, srtvFsMesaCount, "", "");
|
||||
sprintf(tmp, "#version 120\r\n%s", *string);
|
||||
newProgram = replaceInBlock(tmp, srtvFsMesa, srtvFsMesaCount, "", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
newProgram = strdup(*string);
|
||||
}
|
||||
}
|
||||
|
||||
if (newProgram)
|
||||
{
|
||||
final_string = newProgram;
|
||||
program_to_free = newProgram;
|
||||
}
|
||||
}
|
||||
else if (gGrp == GROUP_HUMMER)
|
||||
{
|
||||
void *addr = __builtin_return_address(0);
|
||||
if ((addr != (void *)0x830a501) && (addr != (void *)0x830a485) && (addr != (void *)0x08319841) && (addr != (void *)0x083197c5) &&
|
||||
(addr != (void *)0x83af939) && (addr != (void *)0x83af9b3) && (addr != (void *)0x83b0515) && (addr != (void *)0x83b058f))
|
||||
if ((addr != (void *)0x830a501) && (addr != (void *)0x830a485) && (addr != (void *)0x08319841) &&
|
||||
(addr != (void *)0x083197c5) && (addr != (void *)0x83af939) && (addr != (void *)0x83af9b3) &&
|
||||
(addr != (void *)0x83b0515) && (addr != (void *)0x83b058f))
|
||||
{
|
||||
sprintf(newProgram, "%s", hummerShaderFilesToMod[hummerExtremeShaderFileIndex].shaderBuffer);
|
||||
final_string = strdup(hummerShaderFilesToMod[hummerExtremeShaderFileIndex].shaderBuffer);
|
||||
program_to_free = (char *)final_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -765,26 +785,26 @@ void gl_ShaderSourceARB(GLhandleARB shaderObj, GLsizei count, const GLcharARB **
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
glShaderSourceARB(shaderObj, count, string, length);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *newString = malloc(250000);
|
||||
memset((void *)newString, '\0', 250000);
|
||||
newString = strdup(newProgram);
|
||||
if (length != NULL)
|
||||
{
|
||||
const int newLength = hummerShaderFilesToMod[hummerExtremeShaderFileIndex].shaderBufferSize;
|
||||
glShaderSourceARB(shaderObj, count, &newString, &newLength);
|
||||
final_length = strlen(final_string);
|
||||
glShaderSourceARB(shaderObj, count, &final_string, &final_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
glShaderSourceARB(shaderObj, count, &newString, length);
|
||||
glShaderSourceARB(shaderObj, count, &final_string, NULL);
|
||||
}
|
||||
|
||||
if (program_to_free)
|
||||
{
|
||||
free(program_to_free);
|
||||
}
|
||||
free((void *)newString);
|
||||
free(newProgram);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -956,9 +976,11 @@ void glBindTexture(GLenum target, GLuint texture)
|
||||
}
|
||||
}
|
||||
|
||||
int parseCgcArgs(const char *input, const char ***compilerArgs, const char **outputFileName)
|
||||
int parseCgcArgs(const char *input, const char ***compilerArgs, const char **outputFileName, char **bufferToFree)
|
||||
{
|
||||
char *token = strtok(strdup(input), " ");
|
||||
char *inputCopy = strdup(input);
|
||||
*bufferToFree = inputCopy;
|
||||
char *token = strtok(inputCopy, " ");
|
||||
char **args = malloc(256 * sizeof(char *));
|
||||
int count = 0;
|
||||
char *profile;
|
||||
@@ -1021,7 +1043,7 @@ char *findLibCg()
|
||||
if (currentDir != NULL)
|
||||
{
|
||||
size_t pathLen = strlen(currentDir) + strlen("/libCg.so") + 1;
|
||||
pathsToCheck[3] = malloc(pathLen);
|
||||
pathsToCheck[4] = malloc(pathLen);
|
||||
if (pathsToCheck[4] != NULL)
|
||||
{
|
||||
snprintf(pathsToCheck[4], pathLen, "%s/%s", currentDir, "libCg.so");
|
||||
@@ -1192,9 +1214,9 @@ int compileWithCGC(char *command)
|
||||
{
|
||||
const char **compilerArgs = NULL;
|
||||
const char *outputFileName = NULL;
|
||||
char *argsBuffer = NULL;
|
||||
|
||||
int profile = parseCgcArgs(command, &compilerArgs, &outputFileName);
|
||||
|
||||
int profile = parseCgcArgs(command, &compilerArgs, &outputFileName, &argsBuffer);
|
||||
size_t *ctx = _cgCreateContext();
|
||||
|
||||
char *program = NULL;
|
||||
@@ -1225,7 +1247,7 @@ int compileWithCGC(char *command)
|
||||
|
||||
fprintf(outputFile, "%s", compiledSource);
|
||||
fclose(outputFile);
|
||||
|
||||
free(argsBuffer);
|
||||
_cgDestroyProgram(program);
|
||||
_cgDestroyContext(ctx);
|
||||
return 0;
|
||||
|
||||
+188
-1
@@ -23,6 +23,14 @@ extern bool phShowCursorInGame;
|
||||
#define TOUCH_TOUCHING 0x82
|
||||
#define TOUCH_END 0x84
|
||||
|
||||
#define MJ4_PACKET_SIZE 10
|
||||
uint8_t mj4ResponseBuf[MJ4_PACKET_SIZE] = {0};
|
||||
bool mj4MousePressed = false;
|
||||
extern int mj4MouseX;
|
||||
extern int mj4MouseY;
|
||||
bool mj4ResponseReady = false;
|
||||
bool mj4TouchedInsideScreen = false;
|
||||
|
||||
void phCoordinates(int x, int y, int w, int h, int button)
|
||||
{
|
||||
coordX = (float)x / ((float)w * 0.0002441406);
|
||||
@@ -169,11 +177,190 @@ bool phIsInsideTouchScreen(int mX, int mY, int *x, int *y)
|
||||
}
|
||||
|
||||
void phTouchClick(int x, int y, int type)
|
||||
{
|
||||
{
|
||||
if (type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_EVENT_MOUSE_BUTTON_DOWN);
|
||||
else if (type == SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_EVENT_MOUSE_BUTTON_UP);
|
||||
else if (type == SDL_EVENT_MOUSE_MOTION)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_EVENT_MOUSE_MOTION);
|
||||
}
|
||||
|
||||
void handleMahjongTouch(const SDL_Event *e, int drawableW, int drawableH)
|
||||
{
|
||||
|
||||
mj4MousePressed = 0;
|
||||
mj4MouseX = e->button.x;
|
||||
mj4MouseY = drawableH - e->button.y;
|
||||
|
||||
if (e->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
mj4MousePressed = 1;
|
||||
|
||||
float winAspect = (float)drawableW / drawableH;
|
||||
float gameAspect = 1024.0f / 768.0f;
|
||||
|
||||
int viewportX, viewportY, viewportW, viewportH;
|
||||
|
||||
if (winAspect > gameAspect)
|
||||
{
|
||||
viewportH = drawableH;
|
||||
viewportW = (int)(drawableH * gameAspect);
|
||||
viewportX = (drawableW - viewportW) / 2;
|
||||
viewportY = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewportW = drawableW;
|
||||
viewportH = (int)(drawableW / gameAspect);
|
||||
viewportX = 0;
|
||||
viewportY = (drawableH - viewportH) / 2;
|
||||
}
|
||||
|
||||
if (mj4MouseX < viewportX || mj4MouseX >= viewportX + viewportW || mj4MouseY < viewportY || mj4MouseY >= viewportY + viewportH)
|
||||
{
|
||||
mj4TouchedInsideScreen = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mj4TouchedInsideScreen = true;
|
||||
float normalizedX = (mj4MouseX - viewportX) / (float)viewportW;
|
||||
float normalizedY = (mj4MouseY - viewportY) / (float)viewportH;
|
||||
|
||||
mj4MouseX = normalizedX * 1024.0f;
|
||||
mj4MouseY = normalizedY * 768.0f;
|
||||
}
|
||||
mj4BuildResponsePacket(0x54, mj4MouseX, mj4MouseY, mj4MousePressed);
|
||||
}
|
||||
|
||||
uint8_t mj4CalcChecksum(uint8_t *packet)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
for (int i = 0; i < MJ4_PACKET_SIZE - 1; ++i)
|
||||
sum += packet[i];
|
||||
return (sum + 0xAA) & 0xFF;
|
||||
}
|
||||
|
||||
void mj4BuildResponsePacket(uint8_t cmd, int pckA, int pckB, int pckC)
|
||||
{
|
||||
uint8_t *packet = mj4ResponseBuf;
|
||||
|
||||
packet[0] = 0x55;
|
||||
packet[1] = cmd;
|
||||
|
||||
if (cmd == 0x54)
|
||||
{
|
||||
packet[2] = pckC; // & 0xFF;
|
||||
packet[3] = pckA & 0xFF;
|
||||
packet[4] = (pckA >> 8) & 0xFF;
|
||||
packet[5] = pckB & 0xFF;
|
||||
packet[6] = (pckB >> 8) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[2] = pckA & 0xFF;
|
||||
packet[3] = pckB & 0xFF;
|
||||
packet[4] = pckC & 0xFF;
|
||||
packet[5] = 0x00;
|
||||
packet[6] = 0x00;
|
||||
}
|
||||
packet[7] = 0x00;
|
||||
packet[8] = 0x00;
|
||||
packet[9] = mj4CalcChecksum(packet);
|
||||
|
||||
mj4ResponseReady = true;
|
||||
}
|
||||
|
||||
int mj4WriteTouchPacket(const void *buf, size_t count)
|
||||
{
|
||||
static uint8_t packetBuf[10];
|
||||
static int packetPos = 0;
|
||||
bool degugMsg = getConfig()->showDebugMessages;
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
uint8_t b = ((uint8_t *)buf)[i];
|
||||
|
||||
if (packetPos == 0 && b != 0x55)
|
||||
continue;
|
||||
|
||||
packetBuf[packetPos++] = b;
|
||||
|
||||
if (packetPos == MJ4_PACKET_SIZE)
|
||||
{
|
||||
if (degugMsg)
|
||||
{
|
||||
printf("Serial1 FULL PACKET: ");
|
||||
for (int j = 0; j < MJ4_PACKET_SIZE; ++j)
|
||||
printf("%02X ", packetBuf[j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
uint8_t cmd = packetBuf[1];
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x52:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Init/handshake? (0x52)\n");
|
||||
// mj4_send_response(0x52, 0xff, 0xff);
|
||||
// start_streaming = 1;
|
||||
break;
|
||||
case 0x50:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Config/setup (baud) (0x50)\n");
|
||||
// mj4_send_response(0x50, 0x00, 0x00);
|
||||
break;
|
||||
case 0x70:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Status check/reply? (0x70)\n");
|
||||
mj4BuildResponsePacket(0x50, 0x30, 0x06, 0x04);
|
||||
break;
|
||||
case 0x42:
|
||||
if (degugMsg)
|
||||
printf(" (0x42)\n");
|
||||
mj4BuildResponsePacket(0x41, 0x03, 0x01, 0x00);
|
||||
break;
|
||||
case 0x62:
|
||||
if (degugMsg)
|
||||
printf(" (0x62)\n");
|
||||
mj4BuildResponsePacket(0x42, 0x03, 0x01, 0x00);
|
||||
break;
|
||||
case 0x6D:
|
||||
if (degugMsg)
|
||||
printf(" (0x6D)\n");
|
||||
mj4BuildResponsePacket(0x4D, 0x00, 0x00, 0x00);
|
||||
break;
|
||||
case 0x4D:
|
||||
if (degugMsg)
|
||||
printf(" (0x6D)\n");
|
||||
mj4BuildResponsePacket(0x41, 0x03, 0x01, 0x00);
|
||||
break;
|
||||
default:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Unknown command %02X\n", cmd);
|
||||
mj4ResponseReady = false;
|
||||
break;
|
||||
}
|
||||
packetPos = 0;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int mj4ReadTouchPacket(uint8_t *buf, size_t count)
|
||||
{
|
||||
if (mj4ResponseReady && count >= MJ4_PACKET_SIZE)
|
||||
{
|
||||
memcpy(buf, mj4ResponseBuf, MJ4_PACKET_SIZE);
|
||||
mj4ResponseReady = false;
|
||||
return MJ4_PACKET_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mj4TouchHolding()
|
||||
{
|
||||
if (mj4MousePressed && mj4TouchedInsideScreen)
|
||||
mj4BuildResponsePacket(0x54, mj4MouseX, mj4MouseY, 2);
|
||||
}
|
||||
@@ -9,3 +9,8 @@ ssize_t phRead(int fd, void *buf, size_t count);
|
||||
void phTouchScreenCursor(int mX, int mY, int *motX, int *motY);
|
||||
bool phIsInsideTouchScreen(int mX, int mY, int *x, int *y);
|
||||
void phTouchClick(int x, int y, int type);
|
||||
void handleMahjongTouch(const SDL_Event *e, int drawableW, int drawableH);
|
||||
void mj4BuildResponsePacket(uint8_t cmd, int pckA, int pckB, int pckC);
|
||||
int mj4ReadTouchPacket(uint8_t *buf, size_t max_len);
|
||||
int mj4WriteTouchPacket(const void *buf, size_t count);
|
||||
void mj4TouchHolding();
|
||||
@@ -0,0 +1,229 @@
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <SDL3/SDL_joystick.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <linux/input.h>
|
||||
#include <pthread.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "wiimoteEvdev.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sdlPlayerIndex;
|
||||
char mac[18];
|
||||
int fd;
|
||||
pthread_t threadId;
|
||||
int running;
|
||||
} WiiMoteDevice;
|
||||
|
||||
static WiiMoteDevice gWiiMotes[MAX_JOYSTICKS];
|
||||
static int gWiiMoteCount = 0;
|
||||
Uint32 SDL_WIIMOTION_EVENT = 0;
|
||||
|
||||
static void *wiimoteThreadFunc(void *arg)
|
||||
{
|
||||
WiiMoteDevice *wiimote = (WiiMoteDevice *)arg;
|
||||
struct input_event ev;
|
||||
int irX = 0;
|
||||
int irY = 0;
|
||||
|
||||
printf("Started IR thread for WiiMote P%d\n", wiimote->sdlPlayerIndex + 1);
|
||||
|
||||
while (wiimote->running)
|
||||
{
|
||||
if (read(wiimote->fd, &ev, sizeof(ev)) > 0)
|
||||
{
|
||||
if (ev.type == EV_ABS)
|
||||
{
|
||||
if (ev.code == ABS_HAT0X)
|
||||
{
|
||||
irX = ev.value;
|
||||
}
|
||||
else if (ev.code == ABS_HAT0Y)
|
||||
{
|
||||
irY = ev.value;
|
||||
}
|
||||
}
|
||||
else if (ev.type == EV_SYN && ev.code == SYN_REPORT)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = SDL_WIIMOTION_EVENT;
|
||||
event.user.code = wiimote->sdlPlayerIndex;
|
||||
event.user.data1 = (void *)(intptr_t)irX;
|
||||
event.user.data2 = (void *)(intptr_t)irY;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
printf("Exiting IR thread for WiiMote P%d\n", wiimote->sdlPlayerIndex + 1);
|
||||
close(wiimote->fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int getMacFromSyspath(struct udev *udev, const char *syspath, char *macBuffer, size_t len)
|
||||
{
|
||||
int ret = -1;
|
||||
struct udev_device *dev = udev_device_new_from_syspath(udev, syspath);
|
||||
if (!dev)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype(dev, "hid", NULL);
|
||||
if (parent)
|
||||
{
|
||||
const char *uniq = udev_device_get_property_value(parent, "HID_UNIQ");
|
||||
if (uniq)
|
||||
{
|
||||
strncpy(macBuffer, uniq, len - 1);
|
||||
macBuffer[len - 1] = '\0';
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
udev_device_unref(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int findAndOpenWiiMotes(SDLControllers *sdlControllers)
|
||||
{
|
||||
struct udev *udev = udev_new();
|
||||
if (!udev)
|
||||
{
|
||||
fprintf(stderr, "Failed to create udev context.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_JOYSTICKS; i++)
|
||||
{
|
||||
SDL_Joystick *joy = NULL;
|
||||
if (sdlControllers->controllers[i])
|
||||
joy = SDL_GetGamepadJoystick(sdlControllers->controllers[i]);
|
||||
else if (sdlControllers->joysticks[i])
|
||||
joy = sdlControllers->joysticks[i];
|
||||
|
||||
if (joy && strstr(SDL_GetJoystickName(joy), "Nintendo Wii Remote"))
|
||||
{
|
||||
const char *sdlDevPath = SDL_GetJoystickPathForID(i);
|
||||
if (!sdlDevPath)
|
||||
{
|
||||
fprintf(stderr, "Couldn't get device path for joystick %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *devPathCopy = strdup(sdlDevPath);
|
||||
if (!devPathCopy)
|
||||
continue;
|
||||
|
||||
struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "input", basename(devPathCopy));
|
||||
free(devPathCopy);
|
||||
|
||||
if (dev)
|
||||
{
|
||||
const char *syspath = udev_device_get_syspath(dev);
|
||||
if (syspath)
|
||||
{
|
||||
if (getMacFromSyspath(udev, syspath, gWiiMotes[gWiiMoteCount].mac, sizeof(gWiiMotes[gWiiMoteCount].mac)) == 0)
|
||||
{
|
||||
gWiiMotes[gWiiMoteCount].sdlPlayerIndex = i;
|
||||
printf("Found WiiMote P%d with MAC: %s\n", i + 1, gWiiMotes[gWiiMoteCount].mac);
|
||||
gWiiMoteCount++;
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gWiiMoteCount == 0)
|
||||
{
|
||||
printf("No Wii Remotes found by SDL.\n");
|
||||
udev_unref(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
|
||||
udev_enumerate_add_match_subsystem(enumerate, "input");
|
||||
udev_enumerate_scan_devices(enumerate);
|
||||
|
||||
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
|
||||
struct udev_list_entry *devListEntry;
|
||||
|
||||
udev_list_entry_foreach(devListEntry, devices)
|
||||
{
|
||||
const char *path = udev_list_entry_get_name(devListEntry);
|
||||
struct udev_device *dev = udev_device_new_from_syspath(udev, path);
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
const char *devnode = udev_device_get_devnode(dev);
|
||||
if (devnode && strstr(devnode, "/dev/input/event"))
|
||||
{
|
||||
struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype(dev, "input", NULL);
|
||||
if (parent)
|
||||
{
|
||||
const char *name = udev_device_get_sysattr_value(parent, "name");
|
||||
if (name && strstr(name, "Nintendo Wii Remote IR"))
|
||||
{
|
||||
char macFromHid[18] = {0};
|
||||
if (getMacFromSyspath(udev, path, macFromHid, sizeof(macFromHid)) == 0)
|
||||
{
|
||||
for (int j = 0; j < gWiiMoteCount; j++)
|
||||
{
|
||||
if (strcmp(macFromHid, gWiiMotes[j].mac) == 0)
|
||||
{
|
||||
gWiiMotes[j].fd = open(devnode, O_RDONLY | O_NONBLOCK);
|
||||
int flags = fcntl(gWiiMotes[j].fd, F_GETFL, 0);
|
||||
fcntl(gWiiMotes[j].fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (gWiiMotes[j].fd > 0)
|
||||
{
|
||||
printf("Successfully associated WiiMote P%d with IR device %s\n", gWiiMotes[j].sdlPlayerIndex + 1,
|
||||
devnode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
|
||||
udev_enumerate_unref(enumerate);
|
||||
udev_unref(udev);
|
||||
|
||||
return gWiiMoteCount;
|
||||
}
|
||||
|
||||
void startWiimoteThreads()
|
||||
{
|
||||
for (int i = 0; i < gWiiMoteCount; i++)
|
||||
{
|
||||
if (gWiiMotes[i].fd > 0)
|
||||
{
|
||||
gWiiMotes[i].running = 1;
|
||||
pthread_create(&gWiiMotes[i].threadId, NULL, wiimoteThreadFunc, &gWiiMotes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanupWiiMoteThreads()
|
||||
{
|
||||
for (int i = 0; i < gWiiMoteCount; i++)
|
||||
{
|
||||
if (gWiiMotes[i].running)
|
||||
{
|
||||
gWiiMotes[i].running = 0;
|
||||
pthread_join(gWiiMotes[i].threadId, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "sdlInput.h"
|
||||
|
||||
extern Uint32 SDL_WIIMOTION_EVENT;
|
||||
|
||||
int findAndOpenWiiMotes(SDLControllers *sdl_controllers);
|
||||
void startWiimoteThreads();
|
||||
void cleanupWiiMoteThreads();
|
||||
Reference in New Issue
Block a user