Add all the code from Dev version.

This commit is contained in:
dkeruza-neo
2026-01-27 03:03:21 -05:00
parent ab20dd9420
commit 3b7d10514f
41 changed files with 2608 additions and 223 deletions
+1 -1
View File
@@ -134,7 +134,7 @@ jobs:
# Use jq to build the JSON payload and pipe it to curl
jq -n \
--arg content "New lindbergh loader development build **v${{ needs.prepare-source.outputs.release_version }}** has been released!" \
--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" \
+3 -2
View File
@@ -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)
+13
View File
@@ -2,6 +2,19 @@
All notable changes to this project will be documented in this file.
## [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.
BIN
View File
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
echo "Install Packages Running..."
sudo dpkg --add-architecture i386
sudo apt update # && sudo apt 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
+1 -1
View File
@@ -8,4 +8,4 @@ sudo apt -y install --no-install-recommends \
libpcsclite1:i386 libncurses5:i386 unzip libsndio-dev libsndio-dev:i386 pulseaudio-utils:i386 zlib1g:i386 libgpg-error0:i386 \
libasound2 libasound2-dev libasound2:i386 libasound2-dev:i386 libfreetype6-dev:i386 libdbus-1-dev libpulse-dev libdbus-1-dev:i386 \
libudev-dev:i386 libxcursor-dev:i386 libxfixes-dev:i386 libxi-dev:i386 libxrandr-dev:i386 libxss-dev:i386 libxxf86vm-dev:i386 git libvulkan1:i386 \
mesa-vulkan-drivers:i386
mesa-vulkan-drivers:i386 libxtst-dev:i386
+38
View File
@@ -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;
}
+155
View File
@@ -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;
}
+3
View File
@@ -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
View File
@@ -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)
+8
View File
@@ -248,9 +248,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 +327,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);
+18 -2
View File
@@ -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);
@@ -127,6 +131,18 @@ void createDefaultIni(const char *filePath)
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");
+20 -4
View File
@@ -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)
{
+555
View File
@@ -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, &currentFBO);
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;
}
}
}
+29
View File
@@ -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
View File
@@ -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;
}
+3
View File
@@ -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
View File
@@ -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
+2
View File
@@ -82,6 +82,8 @@ typedef struct
int enabled;
int inUse;
int hasFFB;
int ffbEffectId;
double lastAnalogueValue[8];
} Controller;
+196
View File
@@ -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);
}
}
}
+12
View File
@@ -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
+15 -1
View File
@@ -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"
@@ -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 (gId == MJ4_REVG || gId == MJ4_EVO)
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;
+11 -3
View File
@@ -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"
@@ -30,6 +31,8 @@ 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];
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)
-4
View File
@@ -1,4 +0,0 @@
#include <GL/glx.h>
void glXToSDLSwapBuffers(Display *dpy, GLXDrawable drawable);
int glxSDLmyCreateWindow(int *param1);
+2
View File
@@ -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)
+99 -9
View File
@@ -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 ((gId == MJ4_EVO || gId == MJ4_REVG || 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] && (gId == MJ4_EVO || gId == MJ4_REVG || 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 || gId == MJ4_REVG || gId == MJ4_EVO) && 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) &&
@@ -1660,4 +1750,4 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result)
return res;
}
return _gmtime_r(timep, result);
}
}
+27 -4
View File
@@ -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 = &currentSection->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 = &section->pairs[section->numPairs++];
pair->key = strdup(key);
pair->value = strdup(value);
+1 -1
View File
@@ -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];
+8 -1
View File
@@ -345,7 +345,8 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
snprintf(newLdLibPath, sizeof(newLdLibPath), "%s", currentLibraryPath);
}
char *tmpLibPath = dirname(strdup(ldLibPath));
char *ldLibPathCopy = strdup(ldLibPath);
char *tmpLibPath = dirname(ldLibPathCopy);
if (strcmp(tmpLibPath, ".") != 0)
{
strcat(newLdLibPath, ":");
@@ -363,6 +364,8 @@ void setEnvironmentVariables(const char *ldLibPath, const char *originalDir, con
strcat(newLdLibPath, ":");
strcat(newLdLibPath, gameDir);
}
free(ldLibPathCopy);
strcat(newLdLibPath, ":.:lib:../lib");
@@ -869,6 +872,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;
}
@@ -903,6 +907,9 @@ int main(int argc, char *argv[])
setEnvironmentVariables(libPath, originalDir, targetedGameDir, zink, nvidia, extConfigPath, extControlsPath, extControlsDbPath,
libOpenal);
free(libPath);
if (libOpenal) free(libOpenal);
if (gdb)
{
char temp[128];
+34
View File
@@ -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);
// }
}
}
+2
View File
@@ -0,0 +1,2 @@
void send_command(int fd, char command);
void processGPOpacket(unsigned char data);
+176
View File
@@ -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:
+131 -29
View File
@@ -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 != 260) // target boss fight // Added 2 because of the crosshairs.
if (curTextureID != 260) // target boss fight
{
x += OffsetX;
y -= OffsetY;
@@ -1278,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
@@ -1291,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;
@@ -2485,24 +2579,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");
@@ -2518,6 +2599,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:
@@ -2571,6 +2666,7 @@ int initResolutionPatches()
{
case 0:
{
if (sdlDisplayId) SDL_free(sdlDisplayId);
return 0;
}
case 1: // No touch screen
@@ -2629,17 +2725,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())
@@ -2756,6 +2851,7 @@ int initResolutionPatches()
{
if (getConfig()->boostRenderRes)
setVariable(0x084c9dbc, 1280);
if (sdlDisplayId) SDL_free(sdlDisplayId);
return 0;
}
else if (gWidth == 640 && gHeight == 480)
@@ -2785,6 +2881,7 @@ int initResolutionPatches()
{
if (getConfig()->boostRenderRes)
setVariable(0x084c3a9c, 1280);
if (sdlDisplayId) SDL_free(sdlDisplayId);
return 0;
}
else if (gWidth == 640 && gHeight == 480)
@@ -2814,6 +2911,7 @@ int initResolutionPatches()
{
if (getConfig()->boostRenderRes)
setVariable(0x084c3a9c, 1280);
if (sdlDisplayId) SDL_free(sdlDisplayId);
return 0;
}
else if (gWidth == 640 && gHeight == 480)
@@ -2927,8 +3025,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);
@@ -3152,5 +3252,7 @@ int initResolutionPatches()
default:
break;
}
if (sdlDisplayId)
SDL_free(sdlDisplayId);
return 0;
}
+39 -3
View File
@@ -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;
@@ -100,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());
@@ -188,7 +191,7 @@ void startSDL(int *argcp, char **argv)
initBlitting();
if (gId != PRIMEVAL_HUNT)
if (gId != PRIMEVAL_HUNT && gGrp != GROUP_LGJ)
SDL_SetWindowResizable(sdlWindow, true);
SDL_ShowWindow(sdlWindow);
@@ -203,7 +206,6 @@ 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);
}
@@ -215,6 +217,11 @@ void startSDL(int *argcp, char **argv)
if (customCursor)
setCursor(customCursor);
if (getConfig()->enableCrosshairs &&
(gGrp == GROUP_HOD4 || gGrp == GROUP_HOD4_TEST || gGrp == GROUP_HOD4_SP || gGrp == GROUP_HOD4_SP_TEST || gGrp == GROUP_RAMBO ||
gId == GHOST_SQUAD_EVOLUTION || gId == PRIMEVAL_HUNT))
initCrossHairs();
if ((gId == MJ4_REVG || gId == MJ4_EVO || gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE) && strcmp(getConfig()->touchCursor, "") != 0 &&
getConfig()->emulateTouchscreen)
setCursor(touchCursor);
@@ -228,7 +235,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);
}
@@ -238,6 +271,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:
+101 -2
View File
@@ -16,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};
@@ -51,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;
@@ -66,8 +70,14 @@ 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;
@@ -368,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();
@@ -1230,8 +1249,47 @@ void addActionToDirtyList(JVSPlayer player, LogicalAction action)
*/
void processSdlEvent(const SDL_Event *e)
{
if (!sdlInputInitialized)
return;
// 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)
{
@@ -1523,6 +1581,8 @@ void processSdlEvent(const SDL_Event *e)
}
}
}
if (gameType == MAHJONG && getConfig()->emulateTouchscreen)
handleMahjongTouch(e, drawableW, drawableH);
}
}
break;
@@ -1780,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:
@@ -1808,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:
@@ -2055,6 +2153,7 @@ int listSdlControllers(void)
}
printf("\n");
}
SDL_free(joysticks);
}
SDL_Quit();
return 0;
+4
View File
@@ -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)
+82 -58
View File
@@ -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,9 +568,6 @@ 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)
{
@@ -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);
}
/**
@@ -958,7 +978,9 @@ void glBindTexture(GLenum target, GLuint texture)
int parseCgcArgs(const char *input, const char ***compilerArgs, const char **outputFileName)
{
char *token = strtok(strdup(input), " ");
char *inputCopy = strdup(input);
char *toFree = inputCopy;
char *token = strtok(inputCopy, " ");
char **args = malloc(256 * sizeof(char *));
int count = 0;
char *profile;
@@ -992,6 +1014,7 @@ int parseCgcArgs(const char *input, const char ***compilerArgs, const char **out
}
(*compilerArgs)[argIndex] = NULL;
free(args);
free(toFree);
if (strcmp(profile, "vp40") == 0)
{
return 6150;
@@ -1021,7 +1044,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");
@@ -1228,6 +1251,7 @@ int compileWithCGC(char *command)
_cgDestroyProgram(program);
_cgDestroyContext(ctx);
free((void *)*compilerArgs);
return 0;
}
+188 -1
View File
@@ -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);
}
+5
View File
@@ -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();
+229
View File
@@ -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);
}
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "sdlInput.h"
extern Uint32 SDL_WIIMOTION_EVENT;
int findAndOpenWiiMotes(SDLControllers *sdl_controllers);
void startWiimoteThreads();
void cleanupWiiMoteThreads();