mirror of
https://github.com/lindbergh-loader/lindbergh-loader.git
synced 2026-10-14 01:49:12 +03:00
Compare commits
10
Commits
v170
..
neo-pr-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1c98e6a7f | ||
|
|
2e2a2b4052 | ||
|
|
23152f5788 | ||
|
|
11b657e961 | ||
|
|
5ea59a05fd | ||
|
|
8d1f2764f3 | ||
|
|
f677f1cd3f | ||
|
|
3cd8e7019f | ||
|
|
38643436f2 | ||
|
|
527bdc418f |
@@ -3,15 +3,19 @@ name: Automated Builds
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/ISSUE_TEMPLATE/*'
|
||||
- '.docs/changelog'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/ISSUE_TEMPLATE/*'
|
||||
- '.docs/changelog'
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
@@ -40,9 +44,6 @@ jobs:
|
||||
NEW_RUN_NUMBER=$(( ${{ github.run_number }} + 161 ))
|
||||
echo "NEW_RUN_NUMBER=$NEW_RUN_NUMBER" >> $GITHUB_ENV
|
||||
|
||||
# - name: Display Updated Number
|
||||
# run: echo "Updated Run Number: ${{ env.NEW_RUN_NUMBER }}"
|
||||
|
||||
- name: Create Preview Release
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [2.0.7]
|
||||
### Added
|
||||
- Option -g | --gamepath to lindbergh to point to games in a different folder.
|
||||
- lindbergh elf searches for lindbergh.so in common library folders ("/usr/lib/i386-linux-gnu", "/usr/lib32", etc).
|
||||
- Improved Distro detection function to inform that the Distro is not based on Debian.
|
||||
|
||||
## [2.0.6]
|
||||
### Fixed
|
||||
- Reverted (Fixed LGJ, VT3 and ID4/ID5 JVS passthrough).
|
||||
- Outrun 2 High Scores.
|
||||
|
||||
## [2.0.5]
|
||||
### Fixed
|
||||
- Fixed LGJ, VT3 and ID4/ID5 JVS passthrough.
|
||||
|
||||
## [2.0.4]
|
||||
### Added
|
||||
- Boost render res option for Rambo, LGJ, HOD4, HOD-EX, ID4/ID5, 2Spicy and Harley.
|
||||
|
||||
@@ -90,6 +90,10 @@ sudo ln -s /lib/i386-linux-gnu/libglut.so /lib/i386-linux-gnu/libglut.so.3
|
||||
|
||||
- You need to set `SKIP_OUTRUN_CABINET_CHECK` to 1 in `lindbergh.conf` to play in DX, SDX or Standard cabinet types.
|
||||
|
||||
### Hummer (all versions)
|
||||
|
||||
The games will ask for libGLcore.so.1 and libnvidia-tls.so.1 you can copy for example libkswapapi.so as libGLcore.so.1 and libnvidia-tls.so.1 and the game will work fine.
|
||||
|
||||
## Configuration File : lindbergh.conf
|
||||
|
||||
All configurable options are set and explained in the [`lindbergh.conf`](lindbergh.conf) file itself, and should be reasonably easy to understand. This chapter further explains some of those setup options.
|
||||
|
||||
+151
-30
@@ -862,7 +862,7 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
int controller = open(controllers->controller[i].path, O_RDONLY);
|
||||
if (controller < 0)
|
||||
{
|
||||
strcpy(controllers->controller[i].name, "Unknown");
|
||||
strncpy(controllers->controller[i].name, "Unknown", SIZE);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -912,16 +912,26 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
{
|
||||
if (test_bit(code, bit[EV_KEY]))
|
||||
{
|
||||
if(controllers->controller[i].inputCount >= MAX_INPUTS) { // the number of input is limited
|
||||
fprintf(stderr, "warning, maximum number of inputs reached !\n");
|
||||
break;
|
||||
}
|
||||
|
||||
controllers->controller[i].enabled = 1;
|
||||
ControllerInput *controllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
controllerInput->evType = EV_KEY;
|
||||
controllerInput->evCode = code;
|
||||
controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
|
||||
strcpy(controllerInput->inputName, controllers->controller[i].name);
|
||||
strncpy(controllerInput->inputName, controllers->controller[i].name, SIZE);
|
||||
strcat(controllerInput->inputName, "_");
|
||||
strcat(controllerInput->inputName, codename(EV_KEY, code));
|
||||
normaliseName(controllerInput->inputName);
|
||||
if (snprintf(controllerInput->inputTechName, SIZE, "%s:KEY:%i", controllers->controller[i].path, code) >= 1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
strncpy(controllerInput->inputTechNegName, "-", SIZE); // unassignable value (not "" while some conf are empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -939,31 +949,70 @@ static ControllerStatus listControllers(Controllers *controllers)
|
||||
controllerInput->evType = EV_ABS;
|
||||
controllerInput->evCode = code;
|
||||
controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
|
||||
strcpy(controllerInput->inputName, controllers->controller[i].name);
|
||||
strncpy(controllerInput->inputName, controllers->controller[i].name, SIZE);
|
||||
strcat(controllerInput->inputName, "_");
|
||||
strcat(controllerInput->inputName, codename(EV_ABS, code));
|
||||
normaliseName(controllerInput->inputName);
|
||||
if (snprintf(controllerInput->inputTechName, SIZE, "%s:ABS:%i", controllers->controller[i].path, code) >= 1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
if (snprintf(controllerInput->inputTechNegName, SIZE, "%s:ABS_NEG:%i", controllers->controller[i].path, code) >= 1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
|
||||
ControllerInput *minControllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
minControllerInput->evType = EV_ABS;
|
||||
minControllerInput->evCode = code;
|
||||
minControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MIN;
|
||||
strcpy(minControllerInput->inputName, controllerInput->inputName);
|
||||
strncpy(minControllerInput->inputName, controllerInput->inputName, SIZE);
|
||||
strcat(minControllerInput->inputName, "_MIN");
|
||||
if (snprintf(minControllerInput->inputTechName, SIZE, "%s:ABS:%i:MIN", controllers->controller[i].path, code) >= 1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
if (snprintf(minControllerInput->inputTechNegName, SIZE, "%s:ABS_NEG:%i:MIN", controllers->controller[i].path, code) >=
|
||||
1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
|
||||
ControllerInput *maxControllerInput =
|
||||
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
maxControllerInput->evType = EV_ABS;
|
||||
maxControllerInput->evCode = code;
|
||||
maxControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MAX;
|
||||
strcpy(maxControllerInput->inputName, controllerInput->inputName);
|
||||
strncpy(maxControllerInput->inputName, controllerInput->inputName, SIZE);
|
||||
strcat(maxControllerInput->inputName, "_MAX");
|
||||
if (snprintf(maxControllerInput->inputTechName, SIZE, "%s:ABS:%i:MAX", controllers->controller[i].path, code) >= 1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
if (snprintf(maxControllerInput->inputTechNegName, SIZE, "%s:ABS_NEG:%i:MAX", controllers->controller[i].path, code) >=
|
||||
1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
|
||||
ControllerInput *shakeControllerInput = &controllers->controller[i].inputs[controllers->controller[i].inputCount++];
|
||||
shakeControllerInput->evType = EV_ABS;
|
||||
shakeControllerInput->evCode = code;
|
||||
shakeControllerInput->specialFunction = ANALOGUE_SHAKE;
|
||||
strncpy(shakeControllerInput->inputName, controllerInput->inputName, SIZE);
|
||||
strcat(shakeControllerInput->inputName, "_SHAKE");
|
||||
if (snprintf(shakeControllerInput->inputTechName, SIZE, "%s:ABS:%i:SHAKE", controllers->controller[i].path, code) >=
|
||||
1024)
|
||||
{
|
||||
// hum ok, truncated value
|
||||
}
|
||||
|
||||
struct input_absinfo absoluteFeatures;
|
||||
ioctl(controller, EVIOCGABS(code), &absoluteFeatures);
|
||||
controllers->controller[i].absMin[code] = absoluteFeatures.minimum;
|
||||
controllers->controller[i].absMax[code] = absoluteFeatures.maximum;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1066,31 +1115,67 @@ void *controllerThread(void *_args)
|
||||
((double)event.value - (double)args->controller->absMin[event.code]) /
|
||||
((double)args->controller->absMax[event.code] - (double)args->controller->absMin[event.code]);
|
||||
|
||||
if(args->controller->absTriggers[event.code].isNeg == 1) {
|
||||
scaled = 1.0 - scaled;
|
||||
}
|
||||
|
||||
if (args->controller->absTriggers[event.code].enabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].channel;
|
||||
// Deadzone handling
|
||||
if (scaled < analogue_deadzones[channel].start_max)
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue) {
|
||||
// Deadzone handling
|
||||
if (scaled < analogue_deadzones[channel].start_max)
|
||||
scaled = 0.0;
|
||||
if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max)
|
||||
if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max)
|
||||
scaled = 0.5;
|
||||
if (scaled > analogue_deadzones[channel].end_min)
|
||||
if (scaled > analogue_deadzones[channel].end_min)
|
||||
scaled = 1.0;
|
||||
|
||||
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
|
||||
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
|
||||
} else {
|
||||
setSwitch(args->controller->absTriggers[event.code].player,
|
||||
args->controller->absTriggers[event.code].channel, scaled < 0.8 ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (args->controller->absTriggers[event.code].minEnabled)
|
||||
{
|
||||
setSwitch(args->controller->absTriggers[event.code].minPlayer,
|
||||
args->controller->absTriggers[event.code].minChannel, scaled < 0.2);
|
||||
if(event.value <= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2)) {
|
||||
if (args->controller->absTriggers[event.code].minEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].minChannel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue) {
|
||||
setAnalogue(channel, scaled < 0.2 ? 0.0 : pow(2, jvsBits) * 1.0);
|
||||
} else {
|
||||
setSwitch(args->controller->absTriggers[event.code].minPlayer,
|
||||
args->controller->absTriggers[event.code].minChannel, scaled < 0.2 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args->controller->absTriggers[event.code].maxEnabled)
|
||||
{
|
||||
setSwitch(args->controller->absTriggers[event.code].maxPlayer,
|
||||
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8);
|
||||
if(event.value >= ((args->controller->absMin[event.code] + args->controller->absMax[event.code]) / 2)) {
|
||||
if (args->controller->absTriggers[event.code].maxEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].maxChannel;
|
||||
|
||||
if (args->controller->absTriggers[event.code].isAnalogue) {
|
||||
setAnalogue(channel, scaled > 0.8 ? pow(2, jvsBits) : 0.0);
|
||||
} else {
|
||||
setSwitch(args->controller->absTriggers[event.code].maxPlayer,
|
||||
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args->controller->absTriggers[event.code].shakeEnabled)
|
||||
{
|
||||
int channel = args->controller->absTriggers[event.code].shakeChannel;
|
||||
if( (scaled > args->controller->absTriggers[event.code].shakePreviousScaled && scaled - args->controller->absTriggers[event.code].shakePreviousScaled > 0.1) ||
|
||||
(scaled < args->controller->absTriggers[event.code].shakePreviousScaled && -scaled + args->controller->absTriggers[event.code].shakePreviousScaled > 0.1)) {
|
||||
setAnalogue(channel, 0);
|
||||
} else {
|
||||
setAnalogue(channel, pow(2, jvsBits) / 2);
|
||||
}
|
||||
args->controller->absTriggers[event.code].shakePreviousScaled = scaled;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1215,7 +1300,7 @@ ControllerStatus getArcadeInputByName(char *name, ArcadeInput *input)
|
||||
{
|
||||
if (strcmp(tempName, name) == 0)
|
||||
{
|
||||
strcpy(input->name, arcadeInputs[index].name);
|
||||
strncpy(input->name, arcadeInputs[index].name, SIZE);
|
||||
input->channel = arcadeInputs[index].channel;
|
||||
input->player = arcadeInputs[index].player;
|
||||
return CONTROLLER_STATUS_SUCCESS;
|
||||
@@ -1243,6 +1328,9 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
controllers->controller[i].absTriggers[j].enabled = 0;
|
||||
controllers->controller[i].absTriggers[j].minEnabled = 0;
|
||||
controllers->controller[i].absTriggers[j].maxEnabled = 0;
|
||||
controllers->controller[i].absTriggers[j].shakeEnabled = 0;
|
||||
controllers->controller[i].absTriggers[j].isNeg = 0;
|
||||
controllers->controller[i].absTriggers[j].isAnalogue = 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < KEY_MAX; j++)
|
||||
@@ -1255,8 +1343,20 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
for (int j = 0; j < controllers->controller[i].inputCount; j++)
|
||||
{
|
||||
char *mapping = getMapping(controllers->controller[i].inputs[j].inputName);
|
||||
if (mapping == NULL)
|
||||
continue;
|
||||
int negabs = 0;
|
||||
|
||||
if (mapping == NULL) {
|
||||
// give a 2nd chance with a techninal mapping
|
||||
mapping = getMapping(controllers->controller[i].inputs[j].inputTechName);
|
||||
if (mapping == NULL) {
|
||||
// give a 3rd change with negativ technical mapping
|
||||
mapping = getMapping(controllers->controller[i].inputs[j].inputTechNegName);
|
||||
negabs = 1;
|
||||
if (mapping == NULL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArcadeInput input = {0};
|
||||
ControllerStatus status = getArcadeInputByName(mapping, &input);
|
||||
@@ -1277,10 +1377,13 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].channel =
|
||||
input.channel;
|
||||
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name,
|
||||
input.name);
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].player =
|
||||
input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1289,10 +1392,13 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxChannel =
|
||||
input.channel;
|
||||
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName,
|
||||
input.name);
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxPlayer =
|
||||
input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1301,10 +1407,25 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minChannel =
|
||||
input.channel;
|
||||
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName,
|
||||
input.name);
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minPlayer =
|
||||
input.player;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isNeg = negabs;
|
||||
if (strstr(input.name, "ANALOGUE") == NULL)
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ANALOGUE_SHAKE:
|
||||
{
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeEnabled = 1;
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeChannel =
|
||||
input.channel;
|
||||
strncpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakeName,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].shakePlayer =
|
||||
input.player;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1319,8 +1440,8 @@ ControllerStatus startControllerThreads(Controllers *controllers)
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].channel =
|
||||
input.channel;
|
||||
strcpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name,
|
||||
input.name);
|
||||
strncpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name,
|
||||
input.name, SIZE);
|
||||
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].player =
|
||||
input.player;
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#define SIZE 1024
|
||||
#define SIZE 300
|
||||
#define CONTROLLER_THREAD_MAX 256
|
||||
|
||||
#define MAX_INPUTS 512
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -31,6 +31,14 @@ typedef struct
|
||||
int maxEnabled;
|
||||
|
||||
int isAnalogue;
|
||||
int isNeg; // reversed axis
|
||||
|
||||
char shakeName[SIZE];
|
||||
int shakeChannel;
|
||||
int shakeEnabled;
|
||||
int shakePlayer;
|
||||
double shakePreviousScaled;
|
||||
|
||||
int isCoin;
|
||||
} ArcadeInput;
|
||||
|
||||
@@ -44,12 +52,15 @@ typedef enum {
|
||||
NO_SPECIAL_FUNCTION = 0,
|
||||
ANALOGUE_TO_DIGITAL_MAX,
|
||||
ANALOGUE_TO_DIGITAL_MIN,
|
||||
DIGITAL_TO_ANALOGUE
|
||||
DIGITAL_TO_ANALOGUE,
|
||||
ANALOGUE_SHAKE
|
||||
} SpecialFunction;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char inputName[SIZE];
|
||||
char inputTechName[SIZE];
|
||||
char inputTechNegName[SIZE];
|
||||
int evType;
|
||||
int evCode;
|
||||
SpecialFunction specialFunction;
|
||||
@@ -63,7 +74,7 @@ typedef struct
|
||||
int absMax[ABS_MAX];
|
||||
int absMin[ABS_MAX];
|
||||
|
||||
ControllerInput inputs[SIZE];
|
||||
ControllerInput inputs[MAX_INPUTS];
|
||||
int inputCount;
|
||||
|
||||
ArcadeInput keyTriggers[KEY_MAX];
|
||||
|
||||
+132
-19
@@ -177,32 +177,76 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
|
||||
}
|
||||
}
|
||||
|
||||
char *checkIDlike()
|
||||
char *trimOS_ID(char *str)
|
||||
{
|
||||
FILE *file = fopen("/etc/os-release", "r");
|
||||
if (!file)
|
||||
if (!str)
|
||||
return str;
|
||||
|
||||
char *end;
|
||||
while (isspace((unsigned char)*str) || *str == '"' || *str == '\'')
|
||||
str++;
|
||||
|
||||
if (*str == 0)
|
||||
return str;
|
||||
|
||||
end = str + strlen(str) - 1;
|
||||
while (end > str && (isspace((unsigned char)*end) || *end == '"' || *end == '\''))
|
||||
end--;
|
||||
|
||||
*(end + 1) = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool checkOS_ID()
|
||||
{
|
||||
FILE *fp = fopen("/etc/os-release", "r");
|
||||
if (!fp)
|
||||
{
|
||||
perror("Failed to open /etc/os-release");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
char *result = NULL;
|
||||
bool found = false;
|
||||
|
||||
while (fgets(line, sizeof(line), file))
|
||||
while (fgets(line, sizeof(line), fp))
|
||||
{
|
||||
if (strncmp(line, "ID_LIKE=", 8) == 0)
|
||||
if (strncmp(line, "ID=", 3) == 0)
|
||||
{
|
||||
char *id_like = strchr(line, '=');
|
||||
if (id_like)
|
||||
char *value = trimOS_ID(line + 3);
|
||||
if (strcmp(value, "debian") == 0 || strcmp(value, "ubuntu") == 0)
|
||||
{
|
||||
result = strdup(id_like);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (strncmp(line, "ID_LIKE=", 8) == 0)
|
||||
{
|
||||
char value_part[256];
|
||||
strncpy(value_part, line + 8, sizeof(value_part) - 1);
|
||||
value_part[sizeof(value_part) - 1] = '\0';
|
||||
|
||||
char *value_trimmed = trimOS_ID(value_part);
|
||||
char *token = strtok(value_trimmed, " \t\n");
|
||||
|
||||
while (token != NULL)
|
||||
{
|
||||
char *clean_token = trimOS_ID(token);
|
||||
if (strcmp(clean_token, "debian") == 0 || strcmp(clean_token, "ubuntu") == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
token = strtok(NULL, " \t\n");
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
return result;
|
||||
|
||||
fclose(fp);
|
||||
return found;
|
||||
}
|
||||
|
||||
void __attribute__((constructor)) hook_init()
|
||||
@@ -221,12 +265,7 @@ void __attribute__((constructor)) hook_init()
|
||||
|
||||
gId = getConfig()->crc32;
|
||||
|
||||
char *id_like = checkIDlike();
|
||||
if (id_like == NULL)
|
||||
{
|
||||
log_warn("Unable to get system info on current OS");
|
||||
}
|
||||
else if (strstr(id_like, "debian") == NULL)
|
||||
if (!checkOS_ID())
|
||||
{
|
||||
log_warn("Seems like you're not in debian-like system. There might be unexpected issues.");
|
||||
}
|
||||
@@ -337,9 +376,67 @@ DIR *opendir(const char *dirname)
|
||||
return _opendir(dirname + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for Outrun high scores
|
||||
switch (gId)
|
||||
{
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST2:
|
||||
{
|
||||
if (strcmp(dirname, "/home/disk1/rankingdata") == 0)
|
||||
{
|
||||
return _opendir("./rankingdata");
|
||||
}
|
||||
}
|
||||
}
|
||||
return _opendir(dirname);
|
||||
}
|
||||
|
||||
int remove(const char *path)
|
||||
{
|
||||
int (*_remove)(const char *path) = dlsym(RTLD_NEXT, "remove");
|
||||
|
||||
switch (gId)
|
||||
{
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST2:
|
||||
{
|
||||
if (strcmp(path, "/home/disk1/rankingdata/%s") == 0)
|
||||
{
|
||||
return _remove("./rankingdata/%s");
|
||||
}
|
||||
}
|
||||
}
|
||||
return _remove(path);
|
||||
}
|
||||
|
||||
int mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
int (*_mkdir)(const char *path, mode_t mode) = dlsym(RTLD_NEXT, "mkdir");
|
||||
|
||||
switch (gId)
|
||||
{
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST2:
|
||||
{
|
||||
if (strcmp(path, "/home/disk1/rankingdata") == 0)
|
||||
{
|
||||
return _mkdir("./rankingdata", mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _mkdir(path, mode);
|
||||
}
|
||||
|
||||
int __xstat64(int ver, const char *path, struct stat64 *stat_buf)
|
||||
{
|
||||
int (*___xstat64)(int ver, const char *path, struct stat64 *stat_buf) = dlsym(RTLD_NEXT, "__xstat64");
|
||||
@@ -572,6 +669,22 @@ FILE *fopen(const char *restrict pathname, const char *restrict mode)
|
||||
pathname = newPathname;
|
||||
}
|
||||
|
||||
// Fix for Outrun high scores
|
||||
switch (gId)
|
||||
{
|
||||
case OUTRUN_2_SP_SDX:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST2:
|
||||
{
|
||||
if ((newPathname = strstr(pathname, "/home/disk1")) != NULL)
|
||||
{
|
||||
pathname = newPathname + 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This forces LGJ games and ID games to not use the pre-compiled shaders.
|
||||
if ((strstr(pathname, "asm_lbg") != NULL) || (strstr(pathname, "asm_gl") != NULL))
|
||||
{
|
||||
|
||||
+179
-8
@@ -1,8 +1,12 @@
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "evdevinput.h"
|
||||
#include "version.h"
|
||||
@@ -14,8 +18,10 @@
|
||||
#define PRELOAD_FILE_FLATPAK "/app/lib32/lindbergh.so"
|
||||
#define TEAM "bobbydilley, retrofan, dkeruza-neo, doozer, francesco, rolel, caviar-x"
|
||||
#define LINDBERGH_CONFIG_PATH "LINDBERGH_CONFIG_PATH"
|
||||
#define MAX_PATH_LEN 1024
|
||||
|
||||
uint32_t elf_crc = 0;
|
||||
char *ldPreloadLibrary = "";
|
||||
|
||||
// List of all lindbergh executables known, not including the test executables
|
||||
char *games[] = {"main.exe",
|
||||
@@ -231,6 +237,95 @@ void testModePath(char *name)
|
||||
strcat(name, " -t");
|
||||
}
|
||||
|
||||
bool isValidDirectory(const char *path)
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(path, &statbuf) != 0)
|
||||
return false;
|
||||
|
||||
return S_ISDIR(statbuf.st_mode);
|
||||
}
|
||||
|
||||
char *find32bLibFolder(void)
|
||||
{
|
||||
const char *candidates[] = {"lib32", "lib/i386-linux-gnu", "lib/i686-linux-gnu", "lib"};
|
||||
int num_candidates = sizeof(candidates) / sizeof(candidates[0]);
|
||||
|
||||
char current_path[PATH_MAX];
|
||||
struct stat st;
|
||||
char *result_path = NULL;
|
||||
|
||||
for (int i = 0; i < num_candidates; ++i)
|
||||
{
|
||||
int written = snprintf(current_path, sizeof(current_path), "/usr/%s", candidates[i]);
|
||||
|
||||
if (written < 0 || (size_t)written >= sizeof(current_path))
|
||||
continue;
|
||||
|
||||
if (stat(current_path, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
{
|
||||
size_t path_len = strlen(current_path);
|
||||
result_path = (char *)malloc(path_len + 1);
|
||||
|
||||
if (result_path == NULL)
|
||||
{
|
||||
log_fatal("Failed to allocate memory for result path.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(result_path, current_path);
|
||||
return result_path;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *findLDPreloadLibrary()
|
||||
{
|
||||
const char *library_name = PRELOAD_FILE_NAME;
|
||||
static char *empty_string = "";
|
||||
char *found_path_ptr = NULL;
|
||||
|
||||
char *search_dir = find32bLibFolder();
|
||||
|
||||
if (search_dir == NULL || search_dir[0] == '\0')
|
||||
{
|
||||
log_error("Error: search_dir is NULL or empty.\n");
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
char full_library_path[PATH_MAX];
|
||||
|
||||
int len_written = snprintf(full_library_path, sizeof(full_library_path), "%s/%s", search_dir, library_name);
|
||||
|
||||
if (len_written < 0 || (size_t)len_written >= sizeof(full_library_path))
|
||||
{
|
||||
log_error("find_preload_library_in_specific_dir: Warning: Constructed path is too long or invalid: %s/%s\n", search_dir,
|
||||
library_name);
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
if (access(full_library_path, F_OK) == 0)
|
||||
{
|
||||
found_path_ptr = (char *)malloc(strlen(full_library_path) + 1);
|
||||
if (found_path_ptr == NULL)
|
||||
{
|
||||
log_error("find_preload_library_in_specific_dir: Failed to allocate memory for result path");
|
||||
return empty_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(found_path_ptr, full_library_path);
|
||||
return found_path_ptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return empty_string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the environment variables are set correctly
|
||||
* to run the game.
|
||||
@@ -252,10 +347,12 @@ void setEnvironmentVariables(int isFlatpak, int zink, int nvidia)
|
||||
setenv(LD_LIBRARY_PATH, libraryPath, 1);
|
||||
|
||||
// Ensure the preload path is set correctly
|
||||
if (!isFlatpak)
|
||||
setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1);
|
||||
else
|
||||
if (isFlatpak)
|
||||
setenv(LD_PRELOAD, PRELOAD_FILE_FLATPAK, 1);
|
||||
else if (strcmp(ldPreloadLibrary, "") != 0)
|
||||
setenv(LD_PRELOAD, ldPreloadLibrary, 1);
|
||||
else
|
||||
setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1);
|
||||
|
||||
if (zink)
|
||||
setenv("MESA_LOADER_DRIVER_OVERRIDE", "zink", 1);
|
||||
@@ -283,6 +380,7 @@ void printUsage(char *argv[])
|
||||
printf(" --version Displays the version of the loader and team's names\n");
|
||||
printf(" --help Displays this usage text\n");
|
||||
printf(" --config | -c Specifies configuration path\n");
|
||||
printf(" --gamepath | -g Specifies game path\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,9 +437,48 @@ int main(int argc, char *argv[])
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
char curDir[MAX_PATH_LEN];
|
||||
if (getcwd(curDir, sizeof(curDir)) == NULL)
|
||||
{
|
||||
log_error("Error: could not get the current directory.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Check if a folder was passed as a parameter
|
||||
char gamePath[PATH_MAX] = {0};
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gamepath") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
break;
|
||||
}
|
||||
strncpy(gamePath, argv[i + 1], PATH_MAX);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the passed folder is valid
|
||||
if (!isValidDirectory(gamePath))
|
||||
{
|
||||
log_error("The game folder is invalid.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Look for the games
|
||||
struct dirent *ent;
|
||||
DIR *dir = opendir(".");
|
||||
DIR *dir;
|
||||
|
||||
if (gamePath[0] != '\0')
|
||||
{
|
||||
dir = opendir(gamePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = opendir(curDir);
|
||||
}
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
@@ -398,6 +535,8 @@ int main(int argc, char *argv[])
|
||||
return listControllers();
|
||||
}
|
||||
|
||||
ldPreloadLibrary = findLDPreloadLibrary();
|
||||
|
||||
if (!lindberghSharedObjectFound)
|
||||
{
|
||||
FILE *file = fopen(PRELOAD_FILE_FLATPAK, "r");
|
||||
@@ -406,7 +545,7 @@ int main(int argc, char *argv[])
|
||||
fclose(file);
|
||||
isFlatpak = 1;
|
||||
}
|
||||
else
|
||||
else if (strcmp(ldPreloadLibrary, "") == 0)
|
||||
{
|
||||
log_error("The preload object lindbergh.so was not found in this directory.");
|
||||
return EXIT_FAILURE;
|
||||
@@ -466,6 +605,15 @@ int main(int argc, char *argv[])
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gamepath") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
break;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Treat the argument as the game name
|
||||
strcpy(forceGamePath, argv[i]);
|
||||
forceGame = 1;
|
||||
@@ -475,14 +623,18 @@ int main(int argc, char *argv[])
|
||||
setEnvironmentVariables(isFlatpak, zink, nvidia);
|
||||
|
||||
char command[128] = {0};
|
||||
strcpy(command, "./");
|
||||
if (gamePath[0] != '\0')
|
||||
sprintf(command, "%s/", gamePath);
|
||||
else
|
||||
strcpy(command, "./");
|
||||
|
||||
if (forceGame)
|
||||
{
|
||||
strcat(command, forceGamePath);
|
||||
}
|
||||
else if (segaboot)
|
||||
{
|
||||
strcat(command, "segaboot -t");
|
||||
strcat(command, "segaboot -t");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -514,5 +666,24 @@ int main(int argc, char *argv[])
|
||||
|
||||
log_info("Starting $ %s", command);
|
||||
|
||||
return system(command);
|
||||
if (gamePath[0] != '\0')
|
||||
{
|
||||
if (chdir(gamePath) != 0)
|
||||
{
|
||||
log_error("Could not change to game path.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
int sysCmd = system(command);
|
||||
|
||||
if (gamePath[0] != '\0')
|
||||
{
|
||||
if (chdir(curDir) != 0)
|
||||
{
|
||||
log_error("Could not return to the original directory.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return sysCmd;
|
||||
}
|
||||
|
||||
@@ -159,11 +159,8 @@ void *readJVSFrameThread(void * arg)
|
||||
ackSize = 0;
|
||||
waitForEnd = 0;
|
||||
|
||||
do {
|
||||
while(jvsFrameBuffer.ready == 1) {
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// printf("SERIAL thread debug: trying to read byte.\n");
|
||||
// Try to read a byte from serial, this call will be blocking if VMIN > 0 and VTIME = 0
|
||||
bytesRead = read(fd, &localBuffer[byteCount], 1);
|
||||
@@ -198,7 +195,6 @@ void *readJVSFrameThread(void * arg)
|
||||
}
|
||||
} while (waitForEnd);
|
||||
|
||||
|
||||
// Lock the buffer while we write to it
|
||||
pthread_mutex_lock(&jvsBuffer_lock);
|
||||
memcpy(jvsFrameBuffer.buffer, localBuffer, byteCount);
|
||||
@@ -247,7 +243,7 @@ JVSFrame readJVSFrameFromThread() {
|
||||
|
||||
// Check if we have a valid frame
|
||||
if (jvsFrameBuffer.ready == 1) {
|
||||
memcpy(&frame, &jvsFrameBuffer, sizeof(JVSFrame));
|
||||
frame = jvsFrameBuffer;
|
||||
// It has been red, we disable this frame
|
||||
jvsFrameBuffer.ready = 0;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user