mirror of
https://github.com/lindbergh-loader/lindbergh-loader.git
synced 2026-09-29 02:07:59 +03:00
Add all the code from Dev version.
This commit is contained in:
@@ -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" \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
Binary file not shown.
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Install Packages Running..."
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update # && sudo apt upgrade -y
|
||||
sudo apt -y install --no-install-recommends \
|
||||
build-essential gcc-multilib g++-multilib cmake fuse freeglut3-dev:i386 libvdpau1:i386 libstdc++5:i386 libxmu6:i386 \
|
||||
libpcsclite1:i386 unzip libsndio-dev libsndio-dev:i386 pulseaudio-utils:i386 zlib1g:i386 libgpg-error0:i386 \
|
||||
libfreetype6-dev:i386 libdbus-1-dev libpulse-dev libdbus-1-dev:i386 \
|
||||
libudev-dev:i386 libxcursor-dev:i386 libxfixes-dev:i386 libxi-dev:i386 libxrandr-dev:i386 libxss-dev:i386 libxxf86vm-dev:i386 git libvulkan1:i386 \
|
||||
mesa-vulkan-drivers:i386 libxtst-dev:i386
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
bool cardReaderInitialized = false;
|
||||
|
||||
void (*idWriteFileHeader)(void *) = NULL;
|
||||
|
||||
uint8_t cardHeader[] = {0x08, 0x00, 0x00, 0x00, 0x81, 0x00, 0x59, 0xda, 0x00, 0x00, 0x54, 0x4d, 0x50, 0x06, 0x03, 0x23, 0x10, 0x41, 0x62,
|
||||
0xad, 0x00, 0x2b, 0x0b, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x71, 0x52, 0x70, 0x00, 0x00,
|
||||
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xd2};
|
||||
@@ -243,3 +245,156 @@ void cardReaderSetFd(int serial, int fd, char *fName)
|
||||
log_info("Cardfile %s created.\n", fName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void idPatchEject()
|
||||
{
|
||||
switch (gId)
|
||||
{
|
||||
case INITIALD_4_EXP_REVB:
|
||||
{
|
||||
patchMemoryFromString(0x082939f9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x08293a2b, "85");
|
||||
patchMemoryFromString(0x0819251e, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVC:
|
||||
{
|
||||
patchMemoryFromString(0x08293aa9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x08293adb, "85");
|
||||
patchMemoryFromString(0x081925ce, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_EXP_REVD:
|
||||
{
|
||||
patchMemoryFromString(0x08295479, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x082954ab, "85");
|
||||
patchMemoryFromString(0x0819349e, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVA:
|
||||
{
|
||||
patchMemoryFromString(0x0828149d, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x08281603, "85");
|
||||
patchMemoryFromString(0x0817943e, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVB:
|
||||
{
|
||||
patchMemoryFromString(0x082815c5, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x0828172b, "85");
|
||||
patchMemoryFromString(0x081793ce, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVC:
|
||||
{
|
||||
patchMemoryFromString(0x08281ae9, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x08281c51, "85");
|
||||
patchMemoryFromString(0x0817990c, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVD:
|
||||
{
|
||||
patchMemoryFromString(0x082833dd, "85"); // tickInsertWait
|
||||
patchMemoryFromString(0x08283543, "85");
|
||||
patchMemoryFromString(0x08179c1c, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVG:
|
||||
{
|
||||
patchMemoryFromString(0x082a4ae5, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x082a4b17, "85");
|
||||
patchMemoryFromString(0x08190cbe, "eb"); // reqRead
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP:
|
||||
{
|
||||
patchMemoryFromString(0x083e1eb9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083e1eeb, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP_20:
|
||||
{
|
||||
patchMemoryFromString(0x083ebd1b, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083ebd51, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_EXP_20A:
|
||||
{
|
||||
patchMemoryFromString(0x083ec07b, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083ec0b1, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_JAP_REVA:
|
||||
{
|
||||
patchMemoryFromString(0x083d92bd, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083d92f1, "85");
|
||||
}
|
||||
break;
|
||||
case INITIALD_5_JAP_REVF:
|
||||
{
|
||||
patchMemoryFromString(0x083ec6b9, "84"); // tickInsertWait
|
||||
patchMemoryFromString(0x083ec6eb, "85");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idWriteFileHeaderTram(void *param1)
|
||||
{
|
||||
idPatchEject();
|
||||
idWriteFileHeader(param1);
|
||||
}
|
||||
|
||||
bool idCardFileExists(const char *folderPath, long expectedSize, bool twoDigits)
|
||||
{
|
||||
if (folderPath != NULL && strlen(folderPath) > 0)
|
||||
{
|
||||
struct stat buffer;
|
||||
stat(folderPath, &buffer);
|
||||
if (!S_ISDIR(buffer.st_mode))
|
||||
{
|
||||
log_error("ID Card Folder does not exist! Check the config file.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
char fileName[20];
|
||||
char currentFullPath[1024];
|
||||
char *fmt;
|
||||
int maxCards = 999;
|
||||
char *cardFilename = "InidCrd%03d.crd";
|
||||
|
||||
if (twoDigits)
|
||||
{
|
||||
maxCards = 99;
|
||||
cardFilename = "InidCard%02d.crd";
|
||||
}
|
||||
|
||||
for (int i = 0; i <= maxCards; ++i)
|
||||
{
|
||||
fmt = "%s%s";
|
||||
sprintf(fileName, cardFilename, i);
|
||||
if (folderPath != NULL && strlen(folderPath) > 0)
|
||||
{
|
||||
char lastChar = folderPath[strlen(folderPath) - 1];
|
||||
if (lastChar != '/')
|
||||
fmt = "%s/%s";
|
||||
}
|
||||
|
||||
if (snprintf(currentFullPath, sizeof(currentFullPath), fmt, folderPath, fileName) >= sizeof(currentFullPath))
|
||||
{
|
||||
fprintf(stderr, "Error: Constructed path for '%s' in folder '%s' is too long.\n", fileName, folderPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct stat fileStat;
|
||||
if (stat(currentFullPath, &fileStat) == 0)
|
||||
{
|
||||
if (fileStat.st_size == expectedSize)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -33,3 +33,6 @@ int initCardReader();
|
||||
size_t cardReaderWrite(int fd, const void *buf, size_t count);
|
||||
size_t cardReaderRead(int fd, void *buf, size_t count);
|
||||
void cardReaderSetFd(int serial, int fd, char *fName);
|
||||
void idPatchEject();
|
||||
void idWriteFileHeaderTram(void *param1);
|
||||
bool idCardFileExists(const char *folderPath, long expectedSize, bool twoDigits);
|
||||
+19
-2
@@ -14,8 +14,6 @@ EmulatorConfig config = {0};
|
||||
|
||||
extern uint32_t elf_crc;
|
||||
|
||||
FILE *configFile = NULL;
|
||||
|
||||
#define CONFIG_PATH "lindbergh.ini"
|
||||
#define MAX_LINE_LENGTH 1024
|
||||
|
||||
@@ -47,6 +45,7 @@ static int detectGame(uint32_t elf_crc)
|
||||
config.emulateDriveboard = gameData->emulateDriveboard;
|
||||
config.emulateMotionboard = gameData->emulateMotionboard;
|
||||
config.emulateHW210CardReader = gameData->emulateHW210CardReader;
|
||||
config.emulateIDCardReader = gameData->emulateIDCardReader;
|
||||
config.emulateTouchscreen = gameData->emulateTouchscreen;
|
||||
config.gameLindberghColour = gameData->gameLindberghColour;
|
||||
return 0;
|
||||
@@ -116,9 +115,12 @@ void setDefaultValues(EmulatorConfig *cfg)
|
||||
cfg->emulateDriveboard = 0;
|
||||
cfg->emulateMotionboard = 0;
|
||||
cfg->emulateHW210CardReader = 0;
|
||||
cfg->emulateIDCardReader = 0;
|
||||
cfg->idCardFileAutoload = 1;
|
||||
cfg->emulateTouchscreen = 0;
|
||||
strcpy(cfg->cardFile1, "Card_01.crd");
|
||||
strcpy(cfg->cardFile2, "Card_02.crd");
|
||||
strcpy(cfg->idCardFolder, "");
|
||||
cfg->emulateJVS = 1;
|
||||
cfg->fullscreen = 0;
|
||||
cfg->lindberghColour = YELLOW;
|
||||
@@ -187,6 +189,11 @@ void setDefaultValues(EmulatorConfig *cfg)
|
||||
cfg->whiteBorderPercentage = 0.02f;
|
||||
cfg->blackBorderPercentage = 0.0f;
|
||||
cfg->inputMode = 1;
|
||||
cfg->enableCrosshairs = 0;
|
||||
strcpy(cfg->p1CrossHairPath, "");
|
||||
strcpy(cfg->p2CrossHairPath, "");
|
||||
cfg->customCrossHairWidth = 64;
|
||||
cfg->customCrossHairHeight = 64;
|
||||
}
|
||||
|
||||
static const char *getValue(const IniConfig *ini, const char *sectionName, const char *key)
|
||||
@@ -326,11 +333,14 @@ void applyIniConfig(EmulatorConfig *config, const IniConfig *ini)
|
||||
config->emulateDriveboard = getInt(ini, "Emulation", "EMULATE_DRIVEBOARD", config->emulateDriveboard);
|
||||
config->emulateMotionboard = getInt(ini, "Emulation", "EMULATE_MOTIONBOARD", config->emulateMotionboard);
|
||||
config->emulateHW210CardReader = getInt(ini, "Emulation", "EMULATE_HW210_CARDREADER", config->emulateHW210CardReader);
|
||||
config->emulateIDCardReader = getInt(ini, "Emulation", "EMULATE_ID_CARDREADER", config->emulateIDCardReader);
|
||||
config->emulateTouchscreen = getInt(ini, "Emulation", "EMULATE_TOUCHSCREEN", config->emulateTouchscreen);
|
||||
|
||||
// [Cards]
|
||||
config->idCardFileAutoload = getInt(ini, "Cards", "ID_CARDFILE_AUTOLOAD", config->idCardFileAutoload);
|
||||
getString(ini, "Cards", "CARDFILE_01", config->cardFile1, MAX_PATH_LENGTH);
|
||||
getString(ini, "Cards", "CARDFILE_02", config->cardFile2, MAX_PATH_LENGTH);
|
||||
getString(ini, "Cards", "ID_CARDFOLDER", config->idCardFolder, MAX_PATH_LENGTH);
|
||||
|
||||
// [Paths]
|
||||
getString(ini, "Paths", "JVS_PATH", config->jvsPath, MAX_PATH_LENGTH);
|
||||
@@ -369,6 +379,13 @@ void applyIniConfig(EmulatorConfig *config, const IniConfig *ini)
|
||||
config->id5ChineseLanguage = getInt(ini, "GameSpecific", "ID5_CHINESE_LANGUAGE", config->id5ChineseLanguage);
|
||||
config->idSteeringPercentageReduction = getFloat(ini, "GameSpecific", "ID_STEERING_REDUCTION_PERCENTAGE", config->idSteeringPercentageReduction);
|
||||
|
||||
// [CrossHairs]
|
||||
config->enableCrosshairs = getInt(ini, "CrossHairs", "ENABLE_CROSSHAIRS", config->enableCrosshairs);
|
||||
getString(ini, "CrossHairs", "P1_CROSSHAIR_PATH", config->p1CrossHairPath, MAX_PATH_LENGTH);
|
||||
getString(ini, "CrossHairs", "P2_CROSSHAIR_PATH", config->p2CrossHairPath, MAX_PATH_LENGTH);
|
||||
config->customCrossHairWidth = getInt(ini, "CrossHairs", "CUSTOM_CROSSHAIRS_WIDTH", config->customCrossHairWidth);
|
||||
config->customCrossHairHeight = getInt(ini, "CrossHairs", "CUSTOM_CROSSHAIRS_HEIGHT", config->customCrossHairHeight);
|
||||
|
||||
config->showDebugMessages = getInt(ini, "System", "DEBUG_MSGS", config->showDebugMessages);
|
||||
const char *colourRaw = getValue(ini, "System", "LINDBERGH_COLOUR");
|
||||
if (colourRaw)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "controlIniGen.h"
|
||||
#include "iniParser.h"
|
||||
@@ -211,6 +212,9 @@ const size_t gDefaultMahjongBindingsSize = sizeof(gDefaultMahjongBindings) / siz
|
||||
|
||||
char *toUpperCase(const char *str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
char *result = (char *)malloc(strlen(str) + 1);
|
||||
|
||||
if (result == NULL)
|
||||
@@ -269,9 +273,15 @@ static void getBindingString(const ControlBinding *binding, char *buffer, size_t
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "JOY%d_HAT%d_RIGHT", binding->deviceIndex, binding->sdlId);
|
||||
break;
|
||||
case INPUT_TYPE_GAMEPAD_BUTTON:
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "GC%d_BUTTON_%s", binding->deviceIndex,
|
||||
toUpperCase(SDL_GetGamepadStringForButton(binding->sdlId)));
|
||||
{
|
||||
char *buttonName = toUpperCase(SDL_GetGamepadStringForButton(binding->sdlId));
|
||||
if (buttonName)
|
||||
{
|
||||
snprintf(temp_buffer, sizeof(temp_buffer), "GC%d_BUTTON_%s", binding->deviceIndex, buttonName);
|
||||
free(buttonName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INPUT_TYPE_JOY_AXIS:
|
||||
case INPUT_TYPE_GAMEPAD_AXIS:
|
||||
{
|
||||
@@ -279,8 +289,14 @@ static void getBindingString(const ControlBinding *binding, char *buffer, size_t
|
||||
if (binding->type == INPUT_TYPE_JOY_AXIS)
|
||||
snprintf(prefix, sizeof(prefix), "JOY%d_AXIS_%d", binding->deviceIndex, binding->sdlId);
|
||||
else
|
||||
snprintf(prefix, sizeof(prefix), "GC%d_AXIS_%s", binding->deviceIndex,
|
||||
toUpperCase(SDL_GetGamepadStringForAxis(binding->sdlId)));
|
||||
{
|
||||
char *axisName = toUpperCase(SDL_GetGamepadStringForAxis(binding->sdlId));
|
||||
if (axisName)
|
||||
{
|
||||
snprintf(prefix, sizeof(prefix), "GC%d_AXIS_%s", binding->deviceIndex, axisName);
|
||||
free(axisName);
|
||||
}
|
||||
}
|
||||
|
||||
switch (binding->axisMode)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
// #include <SDL3/SDL_timer.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <dlfcn.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <pthread.h>
|
||||
#include <SDL3/SDL_pixels.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "resolution.h"
|
||||
|
||||
#define MAX_PLAYERS 2
|
||||
#define INACTIVITY_TIMEOUT 3
|
||||
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern int gWidth;
|
||||
extern int gHeight;
|
||||
extern int drawableW;
|
||||
extern int drawableH;
|
||||
|
||||
extern int phX, phY, phW, phH;
|
||||
extern int phX2, phY2, phW2, phH2;
|
||||
|
||||
static Crosshair crossHair[MAX_PLAYERS];
|
||||
static GLuint gShaderProgram = 0;
|
||||
static GLint gUProjectionLoc = -1;
|
||||
static GLint gUTextureLoc = -1;
|
||||
static pthread_t pollingThreadId = 0;
|
||||
|
||||
void (*real_glDrawArrays)(GLenum, GLint, GLsizei) = NULL;
|
||||
|
||||
bool p1CrossHairInitialized = false;
|
||||
bool p2CrossHairInitialized = false;
|
||||
bool testMode = false;
|
||||
|
||||
int textureIdIdxAdjust = 0;
|
||||
|
||||
|
||||
static const char *vertex_shader_source = "#version 120\n"
|
||||
"attribute vec2 a_pos;\n"
|
||||
"attribute vec2 a_tex_coord;\n"
|
||||
"varying vec2 v_tex_coord;\n"
|
||||
"uniform mat4 u_projection;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = u_projection * vec4(a_pos.x, a_pos.y, 0.0, 1.0);\n"
|
||||
" v_tex_coord = a_tex_coord;\n"
|
||||
"}\n";
|
||||
static const char *fragment_shader_source = "#version 120\n"
|
||||
"varying vec2 v_tex_coord;\n"
|
||||
"uniform sampler2D u_texture;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = texture2D(u_texture, v_tex_coord);\n"
|
||||
"}\n";
|
||||
|
||||
GLuint compileShader(GLenum type, const char *source)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, NULL);
|
||||
glCompileShader(shader);
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, NULL, infoLog);
|
||||
fprintf(stderr, "ERROR: Shader compilation failed\n%s\n", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint createShaderProgram()
|
||||
{
|
||||
GLuint vert = compileShader(GL_VERTEX_SHADER, vertex_shader_source);
|
||||
if (vert == 0)
|
||||
return 0;
|
||||
GLuint frag = compileShader(GL_FRAGMENT_SHADER, fragment_shader_source);
|
||||
if (frag == 0)
|
||||
{
|
||||
glDeleteShader(vert);
|
||||
return 0;
|
||||
}
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vert);
|
||||
glAttachShader(program, frag);
|
||||
glBindAttribLocation(program, 0, "a_pos");
|
||||
glBindAttribLocation(program, 1, "a_tex_coord");
|
||||
glLinkProgram(program);
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(program, 512, NULL, infoLog);
|
||||
fprintf(stderr, "ERROR: Shader linking failed\n%s\n", infoLog);
|
||||
}
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
return program;
|
||||
}
|
||||
|
||||
void createCrosshairGeometry(Crosshair *ch)
|
||||
{
|
||||
float w = (float)ch->width;
|
||||
float h = (float)ch->height;
|
||||
float vertices[] = {0.0f, h, 0.0f, 1.0f, w, h, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
w, h, 1.0f, 1.0f, w, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
glGenVertexArrays(1, &ch->vao);
|
||||
glGenBuffers(1, &ch->vbo);
|
||||
glBindVertexArray(ch->vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, ch->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void initCrossHairs()
|
||||
{
|
||||
if (gShaderProgram == 0)
|
||||
{
|
||||
gShaderProgram = createShaderProgram();
|
||||
gUProjectionLoc = glGetUniformLocation(gShaderProgram, "u_projection");
|
||||
gUTextureLoc = glGetUniformLocation(gShaderProgram, "u_texture");
|
||||
}
|
||||
|
||||
if (gShaderProgram == 0)
|
||||
return;
|
||||
|
||||
if (loadCrosshairImage(0, getConfig()->p1CrossHairPath))
|
||||
p1CrossHairInitialized = true;
|
||||
|
||||
if (loadCrosshairImage(1, getConfig()->p2CrossHairPath))
|
||||
p2CrossHairInitialized = true;
|
||||
|
||||
if (isTestMode() || gGrp == GROUP_HOD4_TEST || gGrp == GROUP_HOD4_SP_TEST)
|
||||
testMode = true;
|
||||
|
||||
if (!testMode && gId != PRIMEVAL_HUNT)
|
||||
startPollingThread();
|
||||
|
||||
if (!real_glDrawArrays)
|
||||
real_glDrawArrays = dlsym(RTLD_NEXT, "glDrawArrays");
|
||||
|
||||
textureIdIdxAdjust = p1CrossHairInitialized + p2CrossHairInitialized;
|
||||
}
|
||||
|
||||
int loadCrosshairImage(int player, const char *filepath)
|
||||
{
|
||||
if (player < 0 || player >= MAX_PLAYERS)
|
||||
return 0;
|
||||
|
||||
if (crossHair[player].surface)
|
||||
{
|
||||
SDL_DestroySurface(crossHair[player].surface);
|
||||
crossHair[player].surface = NULL;
|
||||
}
|
||||
if (crossHair[player].texture)
|
||||
{
|
||||
glDeleteTextures(1, &crossHair[player].texture);
|
||||
crossHair[player].texture = 0;
|
||||
}
|
||||
if (crossHair[player].vao)
|
||||
{
|
||||
glDeleteVertexArrays(1, &crossHair[player].vao);
|
||||
crossHair[player].vao = 0;
|
||||
}
|
||||
if (crossHair[player].vbo)
|
||||
{
|
||||
glDeleteBuffers(1, &crossHair[player].vbo);
|
||||
crossHair[player].vbo = 0;
|
||||
}
|
||||
|
||||
SDL_Surface *surface = IMG_Load(filepath);
|
||||
if (!surface)
|
||||
{
|
||||
fprintf(stderr, "Failed to load PNG for player %d: %s\n", player + 1, SDL_GetError());
|
||||
return 0;
|
||||
}
|
||||
crossHair[player].surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
|
||||
SDL_DestroySurface(surface);
|
||||
|
||||
crossHair[player].x = (int)(drawableW / 2.0f);
|
||||
crossHair[player].y = (int)(drawableH / 2.0f);
|
||||
crossHair[player].visible = false;
|
||||
// crossHair[player].lastMovementTime = time(NULL);
|
||||
crossHair[player].texture = 0;
|
||||
crossHair[player].vao = 0;
|
||||
crossHair[player].vbo = 0;
|
||||
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
GLuint tex;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, crossHair[player].surface->w, crossHair[player].surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
crossHair[player].surface->pixels);
|
||||
|
||||
SDL_DestroySurface(crossHair[player].surface);
|
||||
|
||||
crossHair[player].width = getConfig()->customCrossHairWidth;
|
||||
crossHair[player].height = getConfig()->customCrossHairHeight;
|
||||
crossHair[player].texture = tex;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void updateCrosshairPosition(int player, float normX, float normY)
|
||||
{
|
||||
if (player < 0 || player >= MAX_PLAYERS)
|
||||
return;
|
||||
crossHair[player].x = normX * drawableW;
|
||||
crossHair[player].y = normY * drawableH;
|
||||
// crossHair[player].lastMovementTime = time(NULL);
|
||||
// crossHair[player].visible = true;
|
||||
if (testMode || gId == PRIMEVAL_HUNT)
|
||||
crossHair[player].visible = true;
|
||||
}
|
||||
|
||||
void renderCrosshairs(void)
|
||||
{
|
||||
if (gId == PRIMEVAL_HUNT)
|
||||
glViewport(phX, phY, phW, phH);
|
||||
else if (gGrp == GROUP_HOD4_SP)
|
||||
glViewport(0, 0, gWidth, gHeight);
|
||||
|
||||
GLint texFormat = 0;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &texFormat);
|
||||
|
||||
if (texFormat == 0x1908 && gId == GHOST_SQUAD_EVOLUTION)
|
||||
return;
|
||||
|
||||
GLint last_program, last_vao, last_vbo, last_active_texture, last_depth_func;
|
||||
GLboolean last_blend_enabled, last_depth_enabled; //, last_srgb_enabled;
|
||||
GLint last_blend_src, last_blend_dst;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vao);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vbo);
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &last_active_texture);
|
||||
last_blend_enabled = glIsEnabled(GL_BLEND);
|
||||
last_depth_enabled = glIsEnabled(GL_DEPTH_TEST);
|
||||
// last_srgb_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &last_blend_src);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &last_blend_dst);
|
||||
glGetIntegerv(GL_DEPTH_FUNC, &last_depth_func);
|
||||
|
||||
// if (last_srgb_enabled)
|
||||
// glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glUseProgram(gShaderProgram);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(gUTextureLoc, 0);
|
||||
|
||||
time_t currentTime = time(NULL);
|
||||
for (int i = 0; i < MAX_PLAYERS; ++i)
|
||||
{
|
||||
// if (difftime(currentTime, crossHair[i].lastMovementTime) > INACTIVITY_TIMEOUT)
|
||||
// {
|
||||
// crossHair[i].visible = false;
|
||||
// }
|
||||
if (!crossHair[i].visible)
|
||||
continue;
|
||||
|
||||
if (crossHair[i].texture == 0 && crossHair[i].surface != NULL)
|
||||
{
|
||||
glGenTextures(1, &crossHair[i].texture);
|
||||
glBindTexture(GL_TEXTURE_2D, crossHair[i].texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, crossHair[i].surface->w, crossHair[i].surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
crossHair[i].surface->pixels);
|
||||
|
||||
crossHair[i].width = getConfig()->customCrossHairWidth;
|
||||
crossHair[i].height = getConfig()->customCrossHairHeight;
|
||||
createCrosshairGeometry(&crossHair[i]);
|
||||
|
||||
SDL_DestroySurface(crossHair[i].surface);
|
||||
crossHair[i].surface = NULL;
|
||||
}
|
||||
|
||||
if (crossHair[i].texture != 0 && crossHair[i].vao != 0)
|
||||
{
|
||||
float x = crossHair[i].x - (crossHair[i].width / 2.0f);
|
||||
float y = crossHair[i].y - (crossHair[i].height / 2.0f);
|
||||
float projection[16] = {0.0f};
|
||||
projection[0] = 2.0f / drawableW;
|
||||
projection[5] = -2.0f / drawableH;
|
||||
projection[10] = -1.0f;
|
||||
projection[12] = -1.0f + (x * projection[0]);
|
||||
projection[13] = 1.0f + (y * projection[5]);
|
||||
projection[15] = 1.0f;
|
||||
glUniformMatrix4fv(gUProjectionLoc, 1, GL_FALSE, projection);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, crossHair[i].texture);
|
||||
glBindVertexArray(crossHair[i].vao);
|
||||
real_glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(last_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_vbo);
|
||||
glUseProgram(last_program);
|
||||
glActiveTexture(last_active_texture);
|
||||
glBlendFunc(last_blend_src, last_blend_dst);
|
||||
if (last_blend_enabled)
|
||||
glEnable(GL_BLEND);
|
||||
else
|
||||
glDisable(GL_BLEND);
|
||||
if (last_depth_enabled)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
else
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(last_depth_func);
|
||||
// if (last_srgb_enabled)
|
||||
// glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
|
||||
void bindAndPosition(Crosshair *c)
|
||||
{
|
||||
if (!c->texture)
|
||||
return;
|
||||
;
|
||||
|
||||
float x = c->x - c->width / 2.0f;
|
||||
float y = c->y - c->height / 2.0f;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, c->texture);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(x, y);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(x + c->width, y);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(x + c->width, y + c->height);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(x, y + c->height);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void renderGsEvoCrosshairs(void)
|
||||
{
|
||||
GLint texFormat = 0;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &texFormat);
|
||||
|
||||
if (texFormat == 0x1908 && gId == GHOST_SQUAD_EVOLUTION)
|
||||
return;
|
||||
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
glDisable(GL_DEPTH_TEST); // for HOD4
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, drawableW, drawableH, 0, -1, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
if (p1CrossHairInitialized && crossHair[0].visible)
|
||||
bindAndPosition(&crossHair[0]);
|
||||
|
||||
if (p2CrossHairInitialized && crossHair[1].visible)
|
||||
bindAndPosition(&crossHair[1]);
|
||||
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
void destroyCrosshairs(void)
|
||||
{
|
||||
stopPollingThread();
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if (crossHair[i].texture)
|
||||
glDeleteTextures(1, &crossHair[i].texture);
|
||||
if (crossHair[i].vao)
|
||||
glDeleteVertexArrays(1, &crossHair[i].vao);
|
||||
if (crossHair[i].vbo)
|
||||
glDeleteBuffers(1, &crossHair[i].vbo);
|
||||
if (crossHair[i].surface)
|
||||
SDL_DestroySurface(crossHair[i].surface);
|
||||
}
|
||||
if (gShaderProgram)
|
||||
glDeleteProgram(gShaderProgram);
|
||||
}
|
||||
|
||||
void glDrawArrays(GLenum mode, GLint first, GLsizei count)
|
||||
{
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
GLint currentFBO = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, ¤tFBO);
|
||||
renderGsEvoCrosshairs();
|
||||
}
|
||||
if (!real_glDrawArrays)
|
||||
real_glDrawArrays = dlsym(RTLD_NEXT, "glDrawArrays");
|
||||
real_glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool keepRunning;
|
||||
} PollingArgs;
|
||||
|
||||
PollingArgs gPollingArgs;
|
||||
|
||||
static void *gsevoPollingThreadFunc(void *arg)
|
||||
{
|
||||
PollingArgs *args = (PollingArgs *)arg;
|
||||
|
||||
while (args->keepRunning)
|
||||
{
|
||||
uint8_t p1Mode = *(uint8_t *)0x086617E8;
|
||||
uint8_t p2Mode = *(uint8_t *)0x08661994;
|
||||
if (p1Mode == 0x2)
|
||||
crossHair[0].visible = true;
|
||||
else
|
||||
crossHair[0].visible = false;
|
||||
|
||||
if (p2Mode == 0x2)
|
||||
crossHair[1].visible = true;
|
||||
else
|
||||
crossHair[1].visible = false;
|
||||
|
||||
SDL_Delay(10);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *pollingThreadFunc(void *arg)
|
||||
{
|
||||
PollingArgs *args = (PollingArgs *)arg;
|
||||
uint32_t *pGunMgr;
|
||||
uint32_t *pPlayerMgr;
|
||||
|
||||
if (gId == THE_HOUSE_OF_THE_DEAD_4_REVA)
|
||||
{
|
||||
pGunMgr = (uint32_t *)0x0a711758;
|
||||
pPlayerMgr = (uint32_t *)0x0a7117a8;
|
||||
}
|
||||
else if (gId == THE_HOUSE_OF_THE_DEAD_4_REVB || gId == THE_HOUSE_OF_THE_DEAD_4_REVC)
|
||||
{
|
||||
pGunMgr = (uint32_t *)0x0a6f27a8;
|
||||
pPlayerMgr = (uint32_t *)0x0a6f27f8;
|
||||
}
|
||||
else if (gId == RAMBO)
|
||||
pPlayerMgr = (uint32_t *)0x0842fe9c;
|
||||
else if (gId == RAMBO_CHINA)
|
||||
pPlayerMgr = (uint32_t *)0x084304fc;
|
||||
else if (gId == THE_HOUSE_OF_THE_DEAD_4_SPECIAL_REVB)
|
||||
pPlayerMgr = (uint32_t *)0x0A69F92C;
|
||||
else
|
||||
args->keepRunning = false;
|
||||
|
||||
while (args->keepRunning)
|
||||
{
|
||||
if (gGrp != GROUP_HOD4 || *pGunMgr != 0x0)
|
||||
{
|
||||
uint8_t *gameMode;
|
||||
if (gGrp == GROUP_HOD4)
|
||||
{
|
||||
uint32_t *gameModeAddress = *(void **)pGunMgr + 0x2c;
|
||||
gameMode = *(uint8_t **)gameModeAddress + 0x38;
|
||||
}
|
||||
if (gGrp != GROUP_HOD4 || *gameMode == 8)
|
||||
{
|
||||
if (*pPlayerMgr != 0x0)
|
||||
{
|
||||
uint32_t *p1ModeAddress = *(void **)pPlayerMgr + 0x34;
|
||||
uint32_t *p2ModeAddress = *(void **)pPlayerMgr + 0x38;
|
||||
|
||||
if (*p1ModeAddress != 0x0)
|
||||
{
|
||||
uint8_t *p1Mode = *(uint8_t **)p1ModeAddress + 0x38;
|
||||
if (*p1Mode == 3 || *p1Mode == 5)
|
||||
crossHair[0].visible = true;
|
||||
else
|
||||
crossHair[0].visible = false;
|
||||
}
|
||||
|
||||
if (*p2ModeAddress != 0x0)
|
||||
{
|
||||
uint8_t *p2Mode = *(uint8_t **)p2ModeAddress + 0x38;
|
||||
if (*p2Mode == 3 || *p2Mode == 5)
|
||||
crossHair[1].visible = true;
|
||||
else
|
||||
crossHair[1].visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void startPollingThread()
|
||||
{
|
||||
if (gPollingArgs.keepRunning)
|
||||
return;
|
||||
|
||||
gPollingArgs.keepRunning = true;
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
pthread_create(&pollingThreadId, NULL, gsevoPollingThreadFunc, &gPollingArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_create(&pollingThreadId, NULL, pollingThreadFunc, &gPollingArgs);
|
||||
}
|
||||
}
|
||||
|
||||
void stopPollingThread()
|
||||
{
|
||||
if (gPollingArgs.keepRunning)
|
||||
{
|
||||
gPollingArgs.keepRunning = false;
|
||||
if (pollingThreadId)
|
||||
{
|
||||
pthread_join(pollingThreadId, NULL);
|
||||
pollingThreadId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include <SDL3/SDL_surface.h>
|
||||
typedef struct
|
||||
{
|
||||
float x, y;
|
||||
int width, height;
|
||||
|
||||
unsigned int texture;
|
||||
unsigned int vao;
|
||||
unsigned int vbo;
|
||||
SDL_Surface *surface;
|
||||
time_t lastMovementTime;
|
||||
bool visible;
|
||||
} Crosshair;
|
||||
|
||||
extern bool p1CrossHairInitialized;
|
||||
extern bool p2CrossHairInitialized;
|
||||
|
||||
void initCrossHairs();
|
||||
int loadCrosshairImage(int player, const char *filepath);
|
||||
void updateCrosshairPosition(int player, float normX, float normY);
|
||||
void renderCrosshairs(void);
|
||||
void destroyCrosshairs(void);
|
||||
|
||||
void startPollingThread();
|
||||
void stopPollingThread();
|
||||
+187
-92
@@ -1,131 +1,226 @@
|
||||
#include "string.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "driveBoard.h"
|
||||
#include "forceFeedback.h"
|
||||
#include "hook.h"
|
||||
#include "jvs.h"
|
||||
|
||||
#define DRIVEBOARD_READY 0x00
|
||||
#define DRIVEBOARD_NOT_INIT 0x11
|
||||
#define DRIVEBOARD_BUSY 0x44
|
||||
|
||||
int wheelTest = 0;
|
||||
unsigned char response = DRIVEBOARD_READY;
|
||||
extern uint32_t gId;
|
||||
extern int gGrp;
|
||||
extern DeviceType hooks[5];
|
||||
|
||||
double steerValue = 0.5;
|
||||
double force = 0.00;
|
||||
unsigned char response;
|
||||
int wheelInitialized;
|
||||
|
||||
uint8_t buffer[64];
|
||||
uint8_t bufferIdx = 0;
|
||||
|
||||
int initDriveboard()
|
||||
{
|
||||
wheelTest = 0;
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
|
||||
setAnalogue(ANALOGUE_1, steerValue);
|
||||
|
||||
wheelInitialized = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enableRead = 0;
|
||||
|
||||
ssize_t driveboardRead(int fd, void *buf, size_t count)
|
||||
{
|
||||
memcpy(buf, &response, 1);
|
||||
memset(buffer, '\0', 64);
|
||||
if (enableRead)
|
||||
{
|
||||
char *mybuf = (char *)buf;
|
||||
mybuf[0] = response;
|
||||
enableRead = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t driveboardWrite(int fd, const void *buf, size_t count)
|
||||
{
|
||||
|
||||
if (count != 4)
|
||||
const uint8_t *bytes = (const uint8_t *)buf;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
//printf("Error: Drive board count not what expected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char *buffer = (unsigned char *)buf;
|
||||
|
||||
switch (buffer[0])
|
||||
{
|
||||
case 0xFF:
|
||||
{
|
||||
//printf("Driveboard: Drive board reset\n");
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
{
|
||||
//printf("Driveboard: Drive board reset 2\n");
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFC:
|
||||
{
|
||||
//printf("Driveboard: Start wheel bounds testing\n");
|
||||
wheelTest = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
if (buffer[1] == 0 && buffer[2] == 0)
|
||||
buffer[bufferIdx++] = bytes[i];
|
||||
if (bufferIdx == 4)
|
||||
{
|
||||
force = 0;
|
||||
// if (buffer[0] != 0xfd)// && buffer[0] != 0x00 && buffer[0] != 0xff && buffer[0] != 0x01 && buffer[0] != 0xfa)
|
||||
// {
|
||||
// printf("Write: fd: %d,count: %zu, bufferIdx: %d\n", fd, count, bufferIdx);
|
||||
// for (int x = 0; x < 4; x++)
|
||||
// {
|
||||
// printf("0x%02x ", buffer[x]);
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
processDrivePacket(buffer, 1);
|
||||
sdlFfbDriveboard(buffer, 4);
|
||||
if (count != 7)
|
||||
bufferIdx = 0;
|
||||
}
|
||||
|
||||
if (buffer[1] == 1 && buffer[2] == 1)
|
||||
else if (bufferIdx == 7)
|
||||
{
|
||||
|
||||
if (steerValue >= 0.9 && force > 0)
|
||||
break;
|
||||
|
||||
if (steerValue <= 0.1 && force < 0)
|
||||
break;
|
||||
|
||||
steerValue += force;
|
||||
setAnalogue(ANALOGUE_1, (int)(steerValue * 1024));
|
||||
uint8_t player2Buffer[4];
|
||||
player2Buffer[0] = buffer[0];
|
||||
player2Buffer[1] = buffer[4];
|
||||
player2Buffer[2] = buffer[5];
|
||||
player2Buffer[3] = buffer[6];
|
||||
processDrivePacket(player2Buffer, 2);
|
||||
// if (player2Buffer[0] != 0xfd && player2Buffer[0] != 0x00 && player2Buffer[0] != 0xff && player2Buffer[0] != 0x01)
|
||||
// {
|
||||
// printf("Write P2: fd: %d,count: %zu, bufferIdx: %d\n", fd, count, bufferIdx);
|
||||
// for (int x = 0; x < 4; x++)
|
||||
// {
|
||||
// printf("0x%02x ", player2Buffer[x]);
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
bufferIdx = 0;
|
||||
}
|
||||
|
||||
//printf("Driveboard move %f %f\n", steerValue, force);
|
||||
}
|
||||
break;
|
||||
return count;
|
||||
}
|
||||
|
||||
case 0x9e:
|
||||
case 0x84:
|
||||
void processDrivePacket(uint8_t *buf, int player)
|
||||
{
|
||||
static int fcP1 = 0;
|
||||
static int fcP2 = 0;
|
||||
|
||||
switch (buf[0])
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
|
||||
if (buffer[1] == 1)
|
||||
force = ((-1 * ((double)buffer[2] / 128.0)) * 2) / 100;
|
||||
|
||||
if (buffer[1] == 0)
|
||||
force = ((1 - ((double)buffer[2] / 128.0)) * 2) / 100;
|
||||
|
||||
//printf("Driveboard set force%f %f\n", steerValue, force);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFA:
|
||||
case 0xFD:
|
||||
{
|
||||
//printf("Driveboard: auto turn wheel mode\n");
|
||||
|
||||
if (wheelTest)
|
||||
case 0xFF:
|
||||
{
|
||||
// printf("Increment wheel until 0.9 -> %d\n", (int)(steerValue * 255));
|
||||
steerValue += 0.09;
|
||||
setAnalogue(ANALOGUE_1, (int)(steerValue * 1024));
|
||||
response = DRIVEBOARD_BUSY;
|
||||
if (steerValue >= 0.9)
|
||||
printf("Driveboard: Drive board reset\n");
|
||||
// response = 0x88;
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
{
|
||||
printf("Driveboard: Drive board reset 2\n");
|
||||
response = DRIVEBOARD_NOT_INIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFC:
|
||||
{
|
||||
printf("Driveboard: Start wheel bounds testing\n");
|
||||
if (player == 1)
|
||||
{
|
||||
wheelTest = 0;
|
||||
response = DRIVEBOARD_READY;
|
||||
if (fcP1 == 0)
|
||||
{
|
||||
response = DRIVEBOARD_BUSY;
|
||||
fcP1++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (player == 2)
|
||||
{
|
||||
if (fcP2 == 0)
|
||||
{
|
||||
response = DRIVEBOARD_BUSY;
|
||||
fcP2++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
{
|
||||
if ((buf[1] == 0x00 && buf[2] == 0x00 && buf[3] == 0x00) || (buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x01) ||
|
||||
(buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x00))
|
||||
wheelInitialized = 1;
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x83:
|
||||
case 0x86:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9e:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
case 0x84:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
|
||||
if ((buf[1] == 0x01 && buf[2] == 0x00 && buf[3] == 0x05) && !wheelInitialized)
|
||||
{
|
||||
if (player == 1)
|
||||
setAnalogue(ANALOGUE_1, (int)(0.5f * 1024.0f));
|
||||
else
|
||||
setAnalogue(ANALOGUE_5, (int)(0.5f * 1024.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t cmd1 = buf[1];
|
||||
if (gGrp == GROUP_OUTRUN_TEST)
|
||||
{
|
||||
if (buf[4] != 0)
|
||||
cmd1 = buf[4];
|
||||
}
|
||||
|
||||
float scaled = 0.0f;
|
||||
if (cmd1 == 1)
|
||||
scaled = (128.0f - (buf[2] - 1)) / 256.0f;
|
||||
|
||||
if (cmd1 == 0)
|
||||
scaled = (256.0f - (buf[2] + 1)) / 256.0f;
|
||||
|
||||
if (!wheelInitialized)
|
||||
{
|
||||
if (player == 1)
|
||||
setAnalogue(ANALOGUE_1, (int)(scaled * 1024.0f));
|
||||
else
|
||||
setAnalogue(ANALOGUE_5, (int)(scaled * 1024.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//printf("Driveboard: Unknown command received %X\n", buffer[0]);
|
||||
break;
|
||||
|
||||
case 0xF1:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFA:
|
||||
case 0xFD:
|
||||
{
|
||||
response = DRIVEBOARD_READY;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// printf("Driveboard: Unknown command received %X\n", buffer[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
enableRead = 1;
|
||||
}
|
||||
|
||||
int driveBoardioctl(int fd, unsigned int request, void *data)
|
||||
{
|
||||
if (enableRead)
|
||||
{
|
||||
uint8_t d = 1;
|
||||
memcpy(data, &d, sizeof(uint8_t));
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int initDriveboard();
|
||||
void processDrivePacket(uint8_t *buf, int player);
|
||||
ssize_t driveboardRead(int fd, void *buf, size_t count);
|
||||
ssize_t driveboardWrite(int fd, const void *buf, size_t count);
|
||||
int driveBoardioctl(int fd, unsigned int request, void *data);
|
||||
+171
-3
@@ -9,12 +9,14 @@
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "evdevInput.h"
|
||||
#include "config.h"
|
||||
#include "crossHair.h"
|
||||
#include "jvs.h"
|
||||
#include "log.h"
|
||||
|
||||
@@ -843,13 +845,138 @@ void normaliseName(char *string)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
struct ff_effect effect;
|
||||
} FFBThreadData;
|
||||
|
||||
void *ffbEffectThread(void *arg)
|
||||
{
|
||||
FFBThreadData *data = (FFBThreadData *)arg;
|
||||
int fd = data->fd;
|
||||
struct ff_effect effect = data->effect;
|
||||
|
||||
// Trigger the effect
|
||||
struct input_event ffb_event;
|
||||
ffb_event.type = EV_FF;
|
||||
ffb_event.code = effect.id;
|
||||
ffb_event.value = 1;
|
||||
|
||||
if (write(fd, &ffb_event, sizeof(ffb_event)) == -1)
|
||||
{
|
||||
perror("Failed to trigger FFB effect");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FFB effect triggered with ID %d\n", effect.id);
|
||||
|
||||
usleep(effect.replay.length * 1000);
|
||||
}
|
||||
|
||||
// Erase FFB effect
|
||||
if (ioctl(fd, EVIOCRMFF, effect.id) == -1)
|
||||
{
|
||||
perror("Failed to erase FFB effect");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FFB effect with ID %d erased\n", effect.id);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void initFFB(Controller *controller, uint16_t weak_magnitude, uint16_t strong_magnitude, uint16_t length)
|
||||
{
|
||||
if (!controller->hasFFB)
|
||||
{
|
||||
printf("Controller does not support FFB.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = open(controller->path, O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror("Failed to open FFB device");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ff_effect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
|
||||
// // Configure the FFB effect
|
||||
// effect.type = FF_RUMBLE;
|
||||
// effect.id = -1; // Let the system assign an ID
|
||||
// effect.u.rumble.weak_magnitude = weak_magnitude;
|
||||
// effect.u.rumble.strong_magnitude = strong_magnitude;
|
||||
// effect.replay.length = length; // Duration
|
||||
// effect.replay.delay = 0;
|
||||
|
||||
// Configure as periodic
|
||||
effect.type = FF_PERIODIC;
|
||||
effect.id = -1; // Let the system assign an ID
|
||||
effect.u.periodic.waveform = FF_SINE; // FF_SINE/FF_SQUARE/FF_TRIANGLE
|
||||
effect.u.periodic.magnitude = 0x100; // magnitude
|
||||
effect.u.periodic.offset = 0;
|
||||
effect.u.periodic.phase = 0;
|
||||
effect.u.periodic.period = 100; // period between bumps
|
||||
effect.u.periodic.envelope.attack_length = 100; // attack
|
||||
effect.u.periodic.envelope.attack_level = 0;
|
||||
effect.u.periodic.envelope.fade_length = 100; // fade
|
||||
effect.u.periodic.envelope.fade_level = 0;
|
||||
effect.replay.length = 500; // duration
|
||||
effect.replay.delay = 0; // No delay
|
||||
|
||||
// Upload the effect
|
||||
if (ioctl(fd, EVIOCSFF, &effect) == -1)
|
||||
{
|
||||
perror("Failed to upload FFB effect");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("FFB effect initialized with ID %d\n", effect.id);
|
||||
|
||||
// Prepare thread data
|
||||
FFBThreadData *threadData = malloc(sizeof(FFBThreadData));
|
||||
if (!threadData)
|
||||
{
|
||||
perror("Failed to allocate thread data");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
threadData->fd = fd;
|
||||
threadData->effect = effect;
|
||||
|
||||
// Start the thread
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, ffbEffectThread, threadData) != 0)
|
||||
{
|
||||
perror("Failed to create FFB thread");
|
||||
free(threadData);
|
||||
close(fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
|
||||
ControllerStatus loadEvdevControllers(Controllers *controllers)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
controllers->count = scandir("/dev/input", &namelist, isEventDevice, alphasort);
|
||||
int count = scandir("/dev/input", &namelist, isEventDevice, alphasort);
|
||||
if (count < 0)
|
||||
{
|
||||
return CONTROLLER_STATUS_ERROR;
|
||||
}
|
||||
controllers->count = count;
|
||||
|
||||
controllers->controller = malloc(sizeof(Controller) * controllers->count);
|
||||
if (controllers == NULL)
|
||||
if (controllers->controller == NULL)
|
||||
{
|
||||
for (int i = 0; i < controllers->count; i++)
|
||||
{
|
||||
@@ -1020,6 +1147,28 @@ ControllerStatus loadEvdevControllers(Controllers *controllers)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Test if Controller support FFB
|
||||
if (test_bit(EV_FF, bit[0]))
|
||||
{
|
||||
controllers->controller[i].hasFFB = 1;
|
||||
printf("FFB is available on controller %s\n", controllers->controller[i].name);
|
||||
|
||||
// FFB effect
|
||||
initFFB(&controllers->controller[i], 0xFFFF, 0xFFFF, 2000);
|
||||
|
||||
if (controllers->controller[i].ffbEffectId >= 0)
|
||||
{
|
||||
printf("FFB effect initialized with ID: %d\n", controllers->controller[i].ffbEffectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Failed to initialize FFB effect on controller %s\n", controllers->controller[i].name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
controllers->controller[i].hasFFB = 0;
|
||||
}
|
||||
|
||||
close(controller);
|
||||
}
|
||||
@@ -1111,7 +1260,6 @@ void *controllerThread(void *_args)
|
||||
else
|
||||
setSwitch(args->controller->keyTriggers[event.code].player, args->controller->keyTriggers[event.code].channel,
|
||||
event.value == 0 ? 0 : 1);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1168,6 +1316,26 @@ void *controllerThread(void *_args)
|
||||
if(channel == ANALOGUE_1 && (gGrp == GROUP_ID4_EXP || gGrp == GROUP_ID4_JAP || gGrp == GROUP_ID5))
|
||||
scaled = (scaled - 0.5f) * 0.35f + 0.5f;
|
||||
|
||||
if (channel == ANALOGUE_1 || channel == ANALOGUE_2)
|
||||
{
|
||||
args->controller->lastAnalogueValue[channel] = scaled;
|
||||
|
||||
double x_pos = (channel == ANALOGUE_1) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_1];
|
||||
double y_pos = (channel == ANALOGUE_2) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_2];
|
||||
|
||||
updateCrosshairPosition(0, x_pos, y_pos);
|
||||
}
|
||||
|
||||
if (channel == ANALOGUE_3 || channel == ANALOGUE_4)
|
||||
{
|
||||
args->controller->lastAnalogueValue[channel] = scaled;
|
||||
|
||||
double x_pos = (channel == ANALOGUE_3) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_3];
|
||||
double y_pos = (channel == ANALOGUE_4) ? scaled : args->controller->lastAnalogueValue[ANALOGUE_4];
|
||||
|
||||
updateCrosshairPosition(1, x_pos, y_pos);
|
||||
}
|
||||
|
||||
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -82,6 +82,8 @@ typedef struct
|
||||
|
||||
int enabled;
|
||||
int inUse;
|
||||
int hasFFB;
|
||||
int ffbEffectId;
|
||||
double lastAnalogueValue[8];
|
||||
|
||||
} Controller;
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_haptic.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "forceFeedback.h"
|
||||
#include "sdlInput.h"
|
||||
|
||||
static int effect_id = -1;
|
||||
extern SDLControllers sdlJoysticks;
|
||||
|
||||
void sdlFfbInit(void)
|
||||
{
|
||||
int i, num_haptics;
|
||||
SDL_HapticID *haptics = SDL_GetHaptics(&num_haptics);
|
||||
if (haptics)
|
||||
{
|
||||
for (i = 0; i < num_haptics; ++i)
|
||||
{
|
||||
SDL_HapticID instance_id = haptics[i];
|
||||
SDL_Log("Haptic %" SDL_PRIu32 ": %s", instance_id, SDL_GetHapticNameForID(instance_id));
|
||||
sdlJoysticks.haptics[i] = SDL_OpenHaptic(instance_id);
|
||||
sdlFfbRumble(1.0, 1.0, 200);
|
||||
}
|
||||
|
||||
SDL_free(haptics);
|
||||
}
|
||||
else {
|
||||
SDL_Log("No haptic support!");
|
||||
}
|
||||
|
||||
// int num_joysticks;
|
||||
// SDL_JoystickID *joystics = SDL_GetJoysticks(&num_joysticks);
|
||||
// if (joystics)
|
||||
// {
|
||||
// SDL_JoystickID instance_id;
|
||||
// for (int i = 0; i < num_joysticks; ++i)
|
||||
// {
|
||||
// instance_id = joystics[i];
|
||||
// // sdlJoysticks.haptics[i] = SDL_HapticOpenFromJoystick(sdlJoysticks.joysticks[i]);
|
||||
// // sdlJoysticks.haptics[i] = SDL_OpenHapticFromJoystick(SDL_GetGamepadJoystick(sdlJoysticks.controllers[i]));
|
||||
// sdlJoysticks.haptics[i] = SDL_OpenHaptic(instance_id);
|
||||
// printf("JoyName: %s\n", SDL_GetJoystickNameForID(instance_id));
|
||||
// if (sdlJoysticks.haptics[i] && SDL_GetHapticFeatures(sdlJoysticks.haptics[i]) & SDL_HAPTIC_LEFTRIGHT)
|
||||
// {
|
||||
// printf("Haptic initialized!\n");
|
||||
// sdlFfbRumble(1.0, 1.0, 200);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// printf("No haptic support!\n");
|
||||
// sdlJoysticks.haptics[i] = NULL;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void sdlFfbRumble(float left, float right, int duration_ms)
|
||||
{
|
||||
if (!sdlJoysticks.haptics[0])
|
||||
return;
|
||||
SDL_HapticEffect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||
effect.leftright.length = duration_ms;
|
||||
effect.leftright.large_magnitude = (Uint16)(left * 0xFFFF);
|
||||
effect.leftright.small_magnitude = (Uint16)(right * 0xFFFF);
|
||||
if (effect_id >= 0)
|
||||
SDL_DestroyHapticEffect(sdlJoysticks.haptics[0], effect_id);
|
||||
effect_id = SDL_CreateHapticEffect(sdlJoysticks.haptics[0], &effect);
|
||||
SDL_RunHapticEffect(sdlJoysticks.haptics[0], effect_id, 1);
|
||||
}
|
||||
|
||||
void sdlFfbShutdown(void)
|
||||
{
|
||||
if (sdlJoysticks.haptics[0])
|
||||
{
|
||||
SDL_CloseHaptic(sdlJoysticks.haptics[0]);
|
||||
sdlJoysticks.haptics[0] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void sdlFfbDriveboard(const unsigned char *buffer, size_t count)
|
||||
{
|
||||
|
||||
// printf("FFB driveboard: count=%zu, data:", count);
|
||||
// for (size_t i = 0; i < count; ++i) {
|
||||
// printf(" 0x%02x", buffer[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
|
||||
switch (buffer[0])
|
||||
{
|
||||
case 0x80:
|
||||
{ // power
|
||||
if (buffer[2] == 0x01)
|
||||
{
|
||||
printf("0x80 command: Triggering full-power rumble\n");
|
||||
// ffb_rumble(1.0f, 1.0f, 500);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x85:
|
||||
{ // Rumble/vibrate
|
||||
uint8_t speed = buffer[1];
|
||||
uint8_t power = buffer[2];
|
||||
|
||||
float force = (float)power / 63.0f; // Map power 1-63 to 0.0-1.0
|
||||
|
||||
int duration = 100;
|
||||
printf("Triggering rumble: force=%.2f, duration=%dms\n", force, duration);
|
||||
sdlFfbRumble(force, force, duration);
|
||||
break;
|
||||
}
|
||||
case 0x84:
|
||||
{ // Movement command
|
||||
uint8_t direction = buffer[1];
|
||||
uint8_t value = buffer[2];
|
||||
|
||||
float left = 0.0f, right = 0.0f;
|
||||
int duration = 100; // ms
|
||||
|
||||
if (direction == 0x00)
|
||||
{ // right - value from 0x7F (min) to 0x01 (max)
|
||||
|
||||
right = (127.0f - (float)value) / 127.0f;
|
||||
}
|
||||
else if (direction == 0x01)
|
||||
{ // left - value goes from 0x00 (min) to 0x7F (max)
|
||||
|
||||
left = (float)value / 127.0f;
|
||||
}
|
||||
|
||||
printf("Triggering movement rumble: left=%.2f, right=%.2f, duration=%dms\n", left, right, duration);
|
||||
// sdlFfbRumble(left, right, duration);
|
||||
break;
|
||||
}
|
||||
case 0x86: // Friction
|
||||
printf("Friction:");
|
||||
printf(" Power: %d", buffer[1]);
|
||||
printf(" / Percentage: %d\n", buffer[2]);
|
||||
break;
|
||||
case 0x8:
|
||||
break;
|
||||
case 0x87: // move and set target point
|
||||
printf("Move and set target point\n");
|
||||
break;
|
||||
case 0x88: // move to current target point
|
||||
printf("Move to current target point\n");
|
||||
break;
|
||||
case 0x8B: // centering strength
|
||||
printf("Centering strength:");
|
||||
printf(" param1: %d", buffer[1]);
|
||||
printf(" / param2: %d\n", buffer[2]);
|
||||
break;
|
||||
case 0xFB:
|
||||
{ // Playback sud package
|
||||
|
||||
uint8_t effect = buffer[2];
|
||||
printf("Outrun FFB 0xFB effect: 0x%02x\n", effect);
|
||||
|
||||
if (effect == 0x02)
|
||||
{
|
||||
sdlFfbRumble(1.0f, 1.0f, 120);
|
||||
}
|
||||
else if (effect == 0x10 || effect == 0x0B || effect == 0x04)
|
||||
{
|
||||
sdlFfbRumble(0.0f, 1.0f, 120);
|
||||
}
|
||||
else if (effect == 0x00 || effect == 0x1B || effect == 0x14)
|
||||
{
|
||||
sdlFfbRumble(1.0f, 0.0f, 120);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ffb_output(const unsigned char *buffer, size_t count)
|
||||
{
|
||||
/* printf("FFB output: count=%zu, data:", count);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
printf(" 0x%02x", buffer[i]);
|
||||
printf("\n"); */
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
if (buffer[0] & 0x40)
|
||||
{
|
||||
// printf("GPO: ABC Vibration triggered (bit 6 set)\n");
|
||||
// sdlFfbRumble(1.0f, 1.0f, 120);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef FFB_H
|
||||
#define FFB_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void sdlFfbInit(void);
|
||||
void sdlFfbDhutdown(void);
|
||||
void sdlFfbRumble(float left, float right, int duration_ms);
|
||||
void sdlFfbDriveboard(const unsigned char *buffer, size_t count);
|
||||
void sdlFfbOutput(const unsigned char *buffer, size_t count);
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#include <GL/glx.h>
|
||||
|
||||
void glXToSDLSwapBuffers(Display *dpy, GLXDrawable drawable);
|
||||
int glxSDLmyCreateWindow(int *param1);
|
||||
@@ -44,10 +44,12 @@ int getGPUVendorID()
|
||||
if (!config->GPUVendorString)
|
||||
{
|
||||
fprintf(stderr, "Error: Could not get renderer string\n");
|
||||
SDL_GL_DestroyContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
return ERROR_GPU;
|
||||
}
|
||||
gettingGPUVendor = false;
|
||||
SDL_GL_DestroyContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
if (strstr(config->GPUVendorString, "NVIDIA") != NULL)
|
||||
|
||||
+99
-9
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ IniConfig *iniLoad(const char *filename)
|
||||
}
|
||||
IniConfig *config = calloc(1, sizeof(IniConfig));
|
||||
if (!config)
|
||||
{
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
char line[1024];
|
||||
IniSection *currentSection = NULL;
|
||||
while (fgets(line, sizeof(line), file))
|
||||
@@ -54,7 +57,14 @@ IniConfig *iniLoad(const char *filename)
|
||||
continue;
|
||||
if (trimmedLine[0] == '[' && trimmedLine[strlen(trimmedLine) - 1] == ']')
|
||||
{
|
||||
config->sections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
IniSection *newSections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
if (!newSections)
|
||||
{
|
||||
iniFree(config);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
config->sections = newSections;
|
||||
currentSection = &config->sections[config->numSections++];
|
||||
currentSection->numPairs = 0;
|
||||
currentSection->pairs = NULL;
|
||||
@@ -70,7 +80,14 @@ IniConfig *iniLoad(const char *filename)
|
||||
value++;
|
||||
key = trimString(key);
|
||||
value = trimString(value);
|
||||
currentSection->pairs = realloc(currentSection->pairs, (currentSection->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
IniKeyValuePair *newPairs = realloc(currentSection->pairs, (currentSection->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
if (!newPairs)
|
||||
{
|
||||
iniFree(config);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
currentSection->pairs = newPairs;
|
||||
IniKeyValuePair *pair = ¤tSection->pairs[currentSection->numPairs++];
|
||||
pair->key = strdup(key);
|
||||
pair->value = strdup(value);
|
||||
@@ -142,7 +159,10 @@ bool iniSetValue(IniConfig *config, const char *sectionName, const char *key, co
|
||||
IniSection *section = iniGetSection(config, sectionName);
|
||||
if (!section)
|
||||
{
|
||||
config->sections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
IniSection *newSections = realloc(config->sections, (config->numSections + 1) * sizeof(IniSection));
|
||||
if (!newSections)
|
||||
return false;
|
||||
config->sections = newSections;
|
||||
section = &config->sections[config->numSections++];
|
||||
section->name = strdup(sectionName);
|
||||
section->pairs = NULL;
|
||||
@@ -162,7 +182,10 @@ bool iniSetValue(IniConfig *config, const char *sectionName, const char *key, co
|
||||
}
|
||||
|
||||
// Key not found, add a new key-value pair.
|
||||
section->pairs = realloc(section->pairs, (section->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
IniKeyValuePair *newPairs = realloc(section->pairs, (section->numPairs + 1) * sizeof(IniKeyValuePair));
|
||||
if (!newPairs)
|
||||
return false;
|
||||
section->pairs = newPairs;
|
||||
IniKeyValuePair *pair = §ion->pairs[section->numPairs++];
|
||||
pair->key = strdup(key);
|
||||
pair->value = strdup(value);
|
||||
|
||||
+1
-1
@@ -488,7 +488,7 @@ JVSStatus processPacket(int *packetSize)
|
||||
////printf("CMD_CONVEY_ID\n");
|
||||
size = 1;
|
||||
outputPacket.data[outputPacket.length++] = REPORT_SUCCESS;
|
||||
char idData[100];
|
||||
char idData[100] = {0};
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
idData[i] = (char)inputPacket.data[index + i];
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
|
||||
// Function to send a command
|
||||
void send_command(int fd, char command)
|
||||
{
|
||||
// write(fd, &command, 1);
|
||||
}
|
||||
|
||||
void processGPOpacket(unsigned char data)
|
||||
{
|
||||
for (int i = 7; i >= 4; i--)
|
||||
{
|
||||
int bit = (data >> i) & 1;
|
||||
if (bit != 0)
|
||||
{
|
||||
// printf("bit = %d\n", i);
|
||||
}
|
||||
// printf("Bit %d: %d (0x%x)\n", i, bit, bit << i);
|
||||
// if (i == 4)
|
||||
// {
|
||||
// printf("%c - bit: %d\n", bit ? 'a' : 'A', bit);
|
||||
// send_command(fdHW210, bit ? 'a' : 'A');
|
||||
// usleep(500);
|
||||
// }
|
||||
// else if (i == 6)
|
||||
// {
|
||||
// printf("%c - bit: %d\n", bit ? 'b' : 'B', bit);
|
||||
// send_command(fdHW210, bit ? 'b' : 'B');
|
||||
// usleep(500);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
void send_command(int fd, char command);
|
||||
void processGPOpacket(unsigned char data);
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -799,6 +799,10 @@ void cacheModedShaderFiles()
|
||||
memcpy((void *)shaderFilesToPatch[x].shaderBuffer, buff, patchedFile.fsize);
|
||||
free(buff);
|
||||
}
|
||||
|
||||
xdl_free_mmfile(&oriFile);
|
||||
xdl_free_mmfile(&patch);
|
||||
xdl_free_mmfile(&patchedFile);
|
||||
}
|
||||
|
||||
if (getConfig()->showDebugMessages)
|
||||
|
||||
@@ -157,6 +157,10 @@ char *replaceInBlock(char *source, SearchReplace *searchReplace, int searchRepla
|
||||
char *new_result = replaceSubstring(result, start_index, end_index, target, replacement);
|
||||
if (new_result != NULL)
|
||||
{
|
||||
if (result != source)
|
||||
{
|
||||
free(result);
|
||||
}
|
||||
result = new_result;
|
||||
}
|
||||
}
|
||||
@@ -328,8 +332,15 @@ void gsEvoElfShaderPatcher()
|
||||
|
||||
newProgram = replaceInBlock(tmpProgram, gsevoElf, gsevoElfCount, "", "");
|
||||
|
||||
char *prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, gsevoElfMesa, gsevoElfCountMesa, "", "");
|
||||
if (prevProgram != tmpProgram && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
|
||||
prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, gsevoSet1, gsevoSetCount1, "", "");
|
||||
if (prevProgram != tmpProgram && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
|
||||
int newProgramLen = strlen(newProgram);
|
||||
|
||||
@@ -342,6 +353,9 @@ void gsEvoElfShaderPatcher()
|
||||
}
|
||||
|
||||
memcpy((void *)(size_t)gsevoElfShaderOffsets[i].startOffset, newProgram, newProgramLen);
|
||||
|
||||
if (newProgram != tmpProgram)
|
||||
free(newProgram);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,13 +433,18 @@ void srtvElfShaderPatcher()
|
||||
|
||||
cleanShaderString(newProgram);
|
||||
|
||||
char *prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, srtvElf2, srtvElfCount2, "", "");
|
||||
if (prevProgram != tmpProgram && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
|
||||
int newProgramLen = strlen(newProgram);
|
||||
|
||||
newProgram[newProgramLen] = '\0';
|
||||
|
||||
memcpy((void *)(size_t)srtvElfShaderOffsets[i].startOffset, newProgram, newProgramLen);
|
||||
if (newProgram != tmpProgram)
|
||||
free(newProgram);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,28 +483,23 @@ char *cgGetProgramString(char *program, int e)
|
||||
*/
|
||||
void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *program)
|
||||
{
|
||||
char *newProgram;
|
||||
char *newProgram = program;
|
||||
char *program_to_free = NULL;
|
||||
int newProgramLen;
|
||||
void *addr = __builtin_return_address(0);
|
||||
|
||||
if(gId == GHOST_SQUAD_EVOLUTION)
|
||||
if (gId == GHOST_SQUAD_EVOLUTION)
|
||||
{
|
||||
if (addr == (void *)0x8179fab)
|
||||
{
|
||||
newProgram = replaceInBlock(program, gsevoSet1, gsevoSetCount1, "", "");
|
||||
int newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
}
|
||||
else if(gGrp == GROUP_LGJ)
|
||||
else if (gGrp == GROUP_LGJ)
|
||||
{
|
||||
newProgram = replaceInBlock(program, lgjCompiledShadersPatch, lgjCompiledShadersPatchCount, "", "");
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if(gGrp == GROUP_ABC)
|
||||
else if (gGrp == GROUP_ABC)
|
||||
{
|
||||
if (target == GL_VERTEX_PROGRAM_ARB)
|
||||
{
|
||||
@@ -495,11 +509,8 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
{
|
||||
newProgram = replaceInBlock(program, abcFsMesa, abcFsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if(gGrp == GROUP_OUTRUN)
|
||||
else if (gGrp == GROUP_OUTRUN)
|
||||
{
|
||||
if (target == GL_VERTEX_PROGRAM_ARB)
|
||||
{
|
||||
@@ -517,9 +528,6 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
else
|
||||
newProgram = replaceInBlock(program, orFsMesa, orFsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if (gGrp == GROUP_VF5)
|
||||
{
|
||||
@@ -531,7 +539,10 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
if (gId == VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA || gId == VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVB ||
|
||||
gId == VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVB_6000)
|
||||
{
|
||||
char *prevProgram = newProgram;
|
||||
newProgram = replaceInBlock(newProgram, vf5VsAtifs, vf5VsAtifsCount, "", "");
|
||||
if (prevProgram != program && prevProgram != newProgram)
|
||||
free(prevProgram);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -546,9 +557,6 @@ void gl_ProgramStringARB(int target, int program_fmt, int program_len, char *pro
|
||||
else
|
||||
newProgram = replaceInBlock(program, vf5FsMesa, vf5FsMesaCount, "", "");
|
||||
}
|
||||
newProgramLen = strlen(newProgram);
|
||||
program = newProgram;
|
||||
program_len = newProgramLen;
|
||||
}
|
||||
else if (gId == R_TUNED)
|
||||
{
|
||||
@@ -560,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
@@ -23,6 +23,14 @@ extern bool phShowCursorInGame;
|
||||
#define TOUCH_TOUCHING 0x82
|
||||
#define TOUCH_END 0x84
|
||||
|
||||
#define MJ4_PACKET_SIZE 10
|
||||
uint8_t mj4ResponseBuf[MJ4_PACKET_SIZE] = {0};
|
||||
bool mj4MousePressed = false;
|
||||
extern int mj4MouseX;
|
||||
extern int mj4MouseY;
|
||||
bool mj4ResponseReady = false;
|
||||
bool mj4TouchedInsideScreen = false;
|
||||
|
||||
void phCoordinates(int x, int y, int w, int h, int button)
|
||||
{
|
||||
coordX = (float)x / ((float)w * 0.0002441406);
|
||||
@@ -169,11 +177,190 @@ bool phIsInsideTouchScreen(int mX, int mY, int *x, int *y)
|
||||
}
|
||||
|
||||
void phTouchClick(int x, int y, int type)
|
||||
{
|
||||
{
|
||||
if (type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_EVENT_MOUSE_BUTTON_DOWN);
|
||||
else if (type == SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_EVENT_MOUSE_BUTTON_UP);
|
||||
else if (type == SDL_EVENT_MOUSE_MOTION)
|
||||
phCoordinates(x, y, phW2, phH2, SDL_EVENT_MOUSE_MOTION);
|
||||
}
|
||||
|
||||
void handleMahjongTouch(const SDL_Event *e, int drawableW, int drawableH)
|
||||
{
|
||||
|
||||
mj4MousePressed = 0;
|
||||
mj4MouseX = e->button.x;
|
||||
mj4MouseY = drawableH - e->button.y;
|
||||
|
||||
if (e->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
mj4MousePressed = 1;
|
||||
|
||||
float winAspect = (float)drawableW / drawableH;
|
||||
float gameAspect = 1024.0f / 768.0f;
|
||||
|
||||
int viewportX, viewportY, viewportW, viewportH;
|
||||
|
||||
if (winAspect > gameAspect)
|
||||
{
|
||||
viewportH = drawableH;
|
||||
viewportW = (int)(drawableH * gameAspect);
|
||||
viewportX = (drawableW - viewportW) / 2;
|
||||
viewportY = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewportW = drawableW;
|
||||
viewportH = (int)(drawableW / gameAspect);
|
||||
viewportX = 0;
|
||||
viewportY = (drawableH - viewportH) / 2;
|
||||
}
|
||||
|
||||
if (mj4MouseX < viewportX || mj4MouseX >= viewportX + viewportW || mj4MouseY < viewportY || mj4MouseY >= viewportY + viewportH)
|
||||
{
|
||||
mj4TouchedInsideScreen = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mj4TouchedInsideScreen = true;
|
||||
float normalizedX = (mj4MouseX - viewportX) / (float)viewportW;
|
||||
float normalizedY = (mj4MouseY - viewportY) / (float)viewportH;
|
||||
|
||||
mj4MouseX = normalizedX * 1024.0f;
|
||||
mj4MouseY = normalizedY * 768.0f;
|
||||
}
|
||||
mj4BuildResponsePacket(0x54, mj4MouseX, mj4MouseY, mj4MousePressed);
|
||||
}
|
||||
|
||||
uint8_t mj4CalcChecksum(uint8_t *packet)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
for (int i = 0; i < MJ4_PACKET_SIZE - 1; ++i)
|
||||
sum += packet[i];
|
||||
return (sum + 0xAA) & 0xFF;
|
||||
}
|
||||
|
||||
void mj4BuildResponsePacket(uint8_t cmd, int pckA, int pckB, int pckC)
|
||||
{
|
||||
uint8_t *packet = mj4ResponseBuf;
|
||||
|
||||
packet[0] = 0x55;
|
||||
packet[1] = cmd;
|
||||
|
||||
if (cmd == 0x54)
|
||||
{
|
||||
packet[2] = pckC; // & 0xFF;
|
||||
packet[3] = pckA & 0xFF;
|
||||
packet[4] = (pckA >> 8) & 0xFF;
|
||||
packet[5] = pckB & 0xFF;
|
||||
packet[6] = (pckB >> 8) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[2] = pckA & 0xFF;
|
||||
packet[3] = pckB & 0xFF;
|
||||
packet[4] = pckC & 0xFF;
|
||||
packet[5] = 0x00;
|
||||
packet[6] = 0x00;
|
||||
}
|
||||
packet[7] = 0x00;
|
||||
packet[8] = 0x00;
|
||||
packet[9] = mj4CalcChecksum(packet);
|
||||
|
||||
mj4ResponseReady = true;
|
||||
}
|
||||
|
||||
int mj4WriteTouchPacket(const void *buf, size_t count)
|
||||
{
|
||||
static uint8_t packetBuf[10];
|
||||
static int packetPos = 0;
|
||||
bool degugMsg = getConfig()->showDebugMessages;
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
uint8_t b = ((uint8_t *)buf)[i];
|
||||
|
||||
if (packetPos == 0 && b != 0x55)
|
||||
continue;
|
||||
|
||||
packetBuf[packetPos++] = b;
|
||||
|
||||
if (packetPos == MJ4_PACKET_SIZE)
|
||||
{
|
||||
if (degugMsg)
|
||||
{
|
||||
printf("Serial1 FULL PACKET: ");
|
||||
for (int j = 0; j < MJ4_PACKET_SIZE; ++j)
|
||||
printf("%02X ", packetBuf[j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
uint8_t cmd = packetBuf[1];
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x52:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Init/handshake? (0x52)\n");
|
||||
// mj4_send_response(0x52, 0xff, 0xff);
|
||||
// start_streaming = 1;
|
||||
break;
|
||||
case 0x50:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Config/setup (baud) (0x50)\n");
|
||||
// mj4_send_response(0x50, 0x00, 0x00);
|
||||
break;
|
||||
case 0x70:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Status check/reply? (0x70)\n");
|
||||
mj4BuildResponsePacket(0x50, 0x30, 0x06, 0x04);
|
||||
break;
|
||||
case 0x42:
|
||||
if (degugMsg)
|
||||
printf(" (0x42)\n");
|
||||
mj4BuildResponsePacket(0x41, 0x03, 0x01, 0x00);
|
||||
break;
|
||||
case 0x62:
|
||||
if (degugMsg)
|
||||
printf(" (0x62)\n");
|
||||
mj4BuildResponsePacket(0x42, 0x03, 0x01, 0x00);
|
||||
break;
|
||||
case 0x6D:
|
||||
if (degugMsg)
|
||||
printf(" (0x6D)\n");
|
||||
mj4BuildResponsePacket(0x4D, 0x00, 0x00, 0x00);
|
||||
break;
|
||||
case 0x4D:
|
||||
if (degugMsg)
|
||||
printf(" (0x6D)\n");
|
||||
mj4BuildResponsePacket(0x41, 0x03, 0x01, 0x00);
|
||||
break;
|
||||
default:
|
||||
if (degugMsg)
|
||||
printf("MJ4: Unknown command %02X\n", cmd);
|
||||
mj4ResponseReady = false;
|
||||
break;
|
||||
}
|
||||
packetPos = 0;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int mj4ReadTouchPacket(uint8_t *buf, size_t count)
|
||||
{
|
||||
if (mj4ResponseReady && count >= MJ4_PACKET_SIZE)
|
||||
{
|
||||
memcpy(buf, mj4ResponseBuf, MJ4_PACKET_SIZE);
|
||||
mj4ResponseReady = false;
|
||||
return MJ4_PACKET_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mj4TouchHolding()
|
||||
{
|
||||
if (mj4MousePressed && mj4TouchedInsideScreen)
|
||||
mj4BuildResponsePacket(0x54, mj4MouseX, mj4MouseY, 2);
|
||||
}
|
||||
@@ -9,3 +9,8 @@ ssize_t phRead(int fd, void *buf, size_t count);
|
||||
void phTouchScreenCursor(int mX, int mY, int *motX, int *motY);
|
||||
bool phIsInsideTouchScreen(int mX, int mY, int *x, int *y);
|
||||
void phTouchClick(int x, int y, int type);
|
||||
void handleMahjongTouch(const SDL_Event *e, int drawableW, int drawableH);
|
||||
void mj4BuildResponsePacket(uint8_t cmd, int pckA, int pckB, int pckC);
|
||||
int mj4ReadTouchPacket(uint8_t *buf, size_t max_len);
|
||||
int mj4WriteTouchPacket(const void *buf, size_t count);
|
||||
void mj4TouchHolding();
|
||||
@@ -0,0 +1,229 @@
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <SDL3/SDL_joystick.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <linux/input.h>
|
||||
#include <pthread.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "wiimoteEvdev.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sdlPlayerIndex;
|
||||
char mac[18];
|
||||
int fd;
|
||||
pthread_t threadId;
|
||||
int running;
|
||||
} WiiMoteDevice;
|
||||
|
||||
static WiiMoteDevice gWiiMotes[MAX_JOYSTICKS];
|
||||
static int gWiiMoteCount = 0;
|
||||
Uint32 SDL_WIIMOTION_EVENT = 0;
|
||||
|
||||
static void *wiimoteThreadFunc(void *arg)
|
||||
{
|
||||
WiiMoteDevice *wiimote = (WiiMoteDevice *)arg;
|
||||
struct input_event ev;
|
||||
int irX = 0;
|
||||
int irY = 0;
|
||||
|
||||
printf("Started IR thread for WiiMote P%d\n", wiimote->sdlPlayerIndex + 1);
|
||||
|
||||
while (wiimote->running)
|
||||
{
|
||||
if (read(wiimote->fd, &ev, sizeof(ev)) > 0)
|
||||
{
|
||||
if (ev.type == EV_ABS)
|
||||
{
|
||||
if (ev.code == ABS_HAT0X)
|
||||
{
|
||||
irX = ev.value;
|
||||
}
|
||||
else if (ev.code == ABS_HAT0Y)
|
||||
{
|
||||
irY = ev.value;
|
||||
}
|
||||
}
|
||||
else if (ev.type == EV_SYN && ev.code == SYN_REPORT)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = SDL_WIIMOTION_EVENT;
|
||||
event.user.code = wiimote->sdlPlayerIndex;
|
||||
event.user.data1 = (void *)(intptr_t)irX;
|
||||
event.user.data2 = (void *)(intptr_t)irY;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
printf("Exiting IR thread for WiiMote P%d\n", wiimote->sdlPlayerIndex + 1);
|
||||
close(wiimote->fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int getMacFromSyspath(struct udev *udev, const char *syspath, char *macBuffer, size_t len)
|
||||
{
|
||||
int ret = -1;
|
||||
struct udev_device *dev = udev_device_new_from_syspath(udev, syspath);
|
||||
if (!dev)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype(dev, "hid", NULL);
|
||||
if (parent)
|
||||
{
|
||||
const char *uniq = udev_device_get_property_value(parent, "HID_UNIQ");
|
||||
if (uniq)
|
||||
{
|
||||
strncpy(macBuffer, uniq, len - 1);
|
||||
macBuffer[len - 1] = '\0';
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
udev_device_unref(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int findAndOpenWiiMotes(SDLControllers *sdlControllers)
|
||||
{
|
||||
struct udev *udev = udev_new();
|
||||
if (!udev)
|
||||
{
|
||||
fprintf(stderr, "Failed to create udev context.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_JOYSTICKS; i++)
|
||||
{
|
||||
SDL_Joystick *joy = NULL;
|
||||
if (sdlControllers->controllers[i])
|
||||
joy = SDL_GetGamepadJoystick(sdlControllers->controllers[i]);
|
||||
else if (sdlControllers->joysticks[i])
|
||||
joy = sdlControllers->joysticks[i];
|
||||
|
||||
if (joy && strstr(SDL_GetJoystickName(joy), "Nintendo Wii Remote"))
|
||||
{
|
||||
const char *sdlDevPath = SDL_GetJoystickPathForID(i);
|
||||
if (!sdlDevPath)
|
||||
{
|
||||
fprintf(stderr, "Couldn't get device path for joystick %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *devPathCopy = strdup(sdlDevPath);
|
||||
if (!devPathCopy)
|
||||
continue;
|
||||
|
||||
struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "input", basename(devPathCopy));
|
||||
free(devPathCopy);
|
||||
|
||||
if (dev)
|
||||
{
|
||||
const char *syspath = udev_device_get_syspath(dev);
|
||||
if (syspath)
|
||||
{
|
||||
if (getMacFromSyspath(udev, syspath, gWiiMotes[gWiiMoteCount].mac, sizeof(gWiiMotes[gWiiMoteCount].mac)) == 0)
|
||||
{
|
||||
gWiiMotes[gWiiMoteCount].sdlPlayerIndex = i;
|
||||
printf("Found WiiMote P%d with MAC: %s\n", i + 1, gWiiMotes[gWiiMoteCount].mac);
|
||||
gWiiMoteCount++;
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gWiiMoteCount == 0)
|
||||
{
|
||||
printf("No Wii Remotes found by SDL.\n");
|
||||
udev_unref(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
|
||||
udev_enumerate_add_match_subsystem(enumerate, "input");
|
||||
udev_enumerate_scan_devices(enumerate);
|
||||
|
||||
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
|
||||
struct udev_list_entry *devListEntry;
|
||||
|
||||
udev_list_entry_foreach(devListEntry, devices)
|
||||
{
|
||||
const char *path = udev_list_entry_get_name(devListEntry);
|
||||
struct udev_device *dev = udev_device_new_from_syspath(udev, path);
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
const char *devnode = udev_device_get_devnode(dev);
|
||||
if (devnode && strstr(devnode, "/dev/input/event"))
|
||||
{
|
||||
struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype(dev, "input", NULL);
|
||||
if (parent)
|
||||
{
|
||||
const char *name = udev_device_get_sysattr_value(parent, "name");
|
||||
if (name && strstr(name, "Nintendo Wii Remote IR"))
|
||||
{
|
||||
char macFromHid[18] = {0};
|
||||
if (getMacFromSyspath(udev, path, macFromHid, sizeof(macFromHid)) == 0)
|
||||
{
|
||||
for (int j = 0; j < gWiiMoteCount; j++)
|
||||
{
|
||||
if (strcmp(macFromHid, gWiiMotes[j].mac) == 0)
|
||||
{
|
||||
gWiiMotes[j].fd = open(devnode, O_RDONLY | O_NONBLOCK);
|
||||
int flags = fcntl(gWiiMotes[j].fd, F_GETFL, 0);
|
||||
fcntl(gWiiMotes[j].fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (gWiiMotes[j].fd > 0)
|
||||
{
|
||||
printf("Successfully associated WiiMote P%d with IR device %s\n", gWiiMotes[j].sdlPlayerIndex + 1,
|
||||
devnode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
|
||||
udev_enumerate_unref(enumerate);
|
||||
udev_unref(udev);
|
||||
|
||||
return gWiiMoteCount;
|
||||
}
|
||||
|
||||
void startWiimoteThreads()
|
||||
{
|
||||
for (int i = 0; i < gWiiMoteCount; i++)
|
||||
{
|
||||
if (gWiiMotes[i].fd > 0)
|
||||
{
|
||||
gWiiMotes[i].running = 1;
|
||||
pthread_create(&gWiiMotes[i].threadId, NULL, wiimoteThreadFunc, &gWiiMotes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanupWiiMoteThreads()
|
||||
{
|
||||
for (int i = 0; i < gWiiMoteCount; i++)
|
||||
{
|
||||
if (gWiiMotes[i].running)
|
||||
{
|
||||
gWiiMotes[i].running = 0;
|
||||
pthread_join(gWiiMotes[i].threadId, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "sdlInput.h"
|
||||
|
||||
extern Uint32 SDL_WIIMOTION_EVENT;
|
||||
|
||||
int findAndOpenWiiMotes(SDLControllers *sdl_controllers);
|
||||
void startWiimoteThreads();
|
||||
void cleanupWiiMoteThreads();
|
||||
Reference in New Issue
Block a user