Compare commits

..
4 Commits
Author SHA1 Message Date
dkeruza c5fa5871d1 Merge pull request #85 from dkeruza-neo/master
changelog and guide.
2025-04-05 18:17:04 -04:00
dkeruza-neo 5be9aef4e0 changelog and guide 2025-04-05 18:16:04 -04:00
dkeruza f89f8e41ab Merge pull request #84 from dkeruza-neo/master
Stand alone lindbergh loader
2025-04-05 17:45:30 -04:00
dkeruza-neo ad085a1610 Stand alone lindbergh ELF. 2025-04-05 17:43:45 -04:00
3 changed files with 438 additions and 261 deletions
+4 -1
View File
@@ -2,7 +2,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## ## [2.0.8] ## [2.0.9]
- lindbergh elf mostly redone to make the loader work as a stand alone mode (read guide.md).
## [2.0.8]
### Added ### Added
- Changed preprocessor for avoiding multiple inclusions to #pragma once and clean some includes. - Changed preprocessor for avoiding multiple inclusions to #pragma once and clean some includes.
### Fixed ### Fixed
+44
View File
@@ -224,3 +224,47 @@ flatpak install com.github.tchx84.Flatseal
``` ```
Then, run Flatseal and grant Lindbergh Loader access to additional directories. Then, run Flatseal and grant Lindbergh Loader access to additional directories.
### Stand alone mode
Now, you have the option to run the games without copying any files to the game's folder.
Your system's 32 bit library folder, your current folder and the game's folder will be added to LD_LIBRARY_PATH env variable,
so you can han the loader libs in any of those locations.
Passing -c option you can specify the location of the .conf file like this:
```
./lindbergh -c /my/custom/folder/myconfig.conf
```
## -g or --gamepath
With this new option, you can specify the location of the game, where the game's ELF resides.
```
./lindbergh -g /my/custom/folder/hod4/elf
```
The loader will search for known elf files in that folder but you can also run it like this:
```
./lindbergh myCustom.elf -g /my/custom/folder/hod4/elf
```
The loader will load `myCustom.elf` inside the passed folder.
## Passing a folder containing an elf
With this option, you pass a full path, including an ELF name like this:
```
./lindbergh /my/custom/folder/hod4/elf/myCustom.elf
```
The loader will load the ELF in that folder.
You can combine all options.
You can have the needed libraries in the folder where `lindbergh` resides, in the system's 32 bit lib folder or in the game's folder.
The loader will try to find the files.
If by mistake, you pass both, a full path including an ELF and a path with -g option, the path passed with -g option will be omitted.
Remember that if the path containg spaces, which is not recommended, pass the path inside double quotes or `\` before the space.
+390 -260
View File
@@ -1,4 +1,6 @@
#include <dirent.h> #include <dirent.h>
#include <libgen.h>
#include <linux/limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -15,13 +17,37 @@
#define LD_LIBRARY_PATH "LD_LIBRARY_PATH" #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
#define LD_PRELOAD "LD_PRELOAD" #define LD_PRELOAD "LD_PRELOAD"
#define PRELOAD_FILE_NAME "lindbergh.so" #define PRELOAD_FILE_NAME "lindbergh.so"
#define PRELOAD_FILE_FLATPAK "/app/lib32/lindbergh.so"
#define TEAM "bobbydilley, retrofan, dkeruza-neo, doozer, francesco, rolel, caviar-x" #define TEAM "bobbydilley, retrofan, dkeruza-neo, doozer, francesco, rolel, caviar-x"
#define LINDBERGH_CONFIG_PATH "LINDBERGH_CONFIG_PATH" #define LINDBERGH_CONFIG_PATH "LINDBERGH_CONFIG_PATH"
#define MAX_PATH_LEN 1024 #define MAX_PATH_LEN 1024
/**
* @brief Calculates the CRC32 of a single byte.
*
* This function calculates the CRC32 value for a given byte of data.
*
* @param crc The current CRC32 value.
* @param data The byte of data to calculate the CRC32 for.
* @return The updated CRC32 value.
*/
uint32_t calcCrc32(uint32_t crc, uint8_t data)
{
crc ^= data; // No shift needed; working in LSB-first order
for (int i = 0; i < 8; i++)
{
if (crc & 1)
{
crc = (crc >> 1) ^ 0xEDB88320;
}
else
{
crc = (crc >> 1);
}
}
return crc;
}
uint32_t elf_crc = 0; uint32_t elf_crc = 0;
char *ldPreloadLibrary = "";
// List of all lindbergh executables known, not including the test executables // List of all lindbergh executables known, not including the test executables
char *games[] = {"main.exe", char *games[] = {"main.exe",
@@ -126,23 +152,14 @@ uint32_t cleanElfCRC32[] = {
int cleanElfCRC32Count = sizeof(cleanElfCRC32) / sizeof(uint32_t); int cleanElfCRC32Count = sizeof(cleanElfCRC32) / sizeof(uint32_t);
uint32_t calcCrc32(uint32_t crc, uint8_t data) /**
{ * @brief Looks up a CRC value in the cleanElfCRC32 table.
crc ^= data; // No shift needed; working in LSB-first order *
for (int i = 0; i < 8; i++) * This function searches for a given CRC value within the cleanElfCRC32 array.
{ *
if (crc & 1) * @param crc The CRC value to look up.
{ * @return 1 if the CRC is found in the table, 0 otherwise.
crc = (crc >> 1) ^ 0xEDB88320; */
}
else
{
crc = (crc >> 1);
}
}
return crc;
}
int lookupCrcTable(uint32_t crc) int lookupCrcTable(uint32_t crc)
{ {
for (int x = 0; x < cleanElfCRC32Count; x++) for (int x = 0; x < cleanElfCRC32Count; x++)
@@ -153,6 +170,73 @@ int lookupCrcTable(uint32_t crc)
return 0; return 0;
} }
/**
* @brief Removes double quotes from a string.
*
* This function iterates through the input string and removes any double quote characters.
* It modifies the string in place.
*
* @param str The string to remove double quotes from.
*/
void removeDoubleQuotes(char *str)
{
if (str == NULL)
{
return;
}
int j = 0;
for (int i = 0; str[i] != '\0'; i++)
{
if (str[i] != '"')
{
str[j] = str[i];
j++;
}
}
str[j] = '\0';
}
/**
* @brief Calculates the CRC32 of a file.
*
* This function calculates the CRC32 of the specified file.
*
* @param filename The path to the file.
* @param crc The pointer to the variable that will contain the CRC32.
* @return 0 if the CRC32 was calculated successfully, -1 otherwise.
*/
int calculateFileCRC32(const char *filename, uint32_t *crc)
{
FILE *file = fopen(filename, "rb");
if (!file)
{
log_error("Could not open file to calculate the CRC32.");
return -1;
}
*crc = 0xFFFFFFFF;
uint8_t buffer[4096];
size_t bytesRead;
while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) > 0)
{
for (size_t i = 0; i < bytesRead; i++)
{
*crc = calcCrc32(*crc, buffer[i]);
}
}
fclose(file);
return 0;
}
/**
* @brief Checks if the ELF file is clean based on its CRC32.
*
* This function calculates the CRC32 of the specified ELF file and compares it
* against a list of known clean ELF CRC32 values.
* @param command The command line string containing the path to the ELF file.
*/
void isCleanElf(char *command) void isCleanElf(char *command)
{ {
char *last_space = strrchr(command, ' '); char *last_space = strrchr(command, ' ');
@@ -166,38 +250,12 @@ void isCleanElf(char *command)
strncpy(elfName, command, length); strncpy(elfName, command, length);
elfName[length] = '\0'; elfName[length] = '\0';
// Remove double quotes from path if it was added. removeDoubleQuotes(elfName);
int j = 0;
for (int i = 0; elfName[i] != '\0'; i++)
{
if (elfName[i] != '"')
{
elfName[j] = elfName[i];
j++;
}
}
elfName[j] = '\0';
FILE *file = fopen(elfName, "rb"); uint32_t crc;
if (!file) if (calculateFileCRC32(elfName, &crc) != 0)
{
log_error("Could not calculate the Elf CRC32.");
return; return;
}
uint32_t crc = 0xFFFFFFFF;
uint8_t buffer[4096];
size_t bytesRead;
while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) > 0)
{
for (size_t i = 0; i < bytesRead; i++)
{
crc = calcCrc32(crc, buffer[i]);
}
}
fclose(file);
crc = crc ^ 0xFFFFFFFF; crc = crc ^ 0xFFFFFFFF;
if (!lookupCrcTable(crc)) if (!lookupCrcTable(crc))
@@ -218,26 +276,27 @@ void isCleanElf(char *command)
*/ */
void testModePath(char *name) void testModePath(char *name)
{ {
printf("name: %s\n", name);
char *pos; char *pos;
// Check if a different testmode elf is used // Check if a different testmode elf is used
pos = strstr(name, "/hod4M.elf"); pos = strstr(name, "hod4M.elf");
if (pos != NULL) if (pos != NULL)
{ {
strcpy(pos, "/hod4testM.elf"); strcpy(pos, "hod4testM.elf");
return; return;
} }
pos = strstr(name, "/hodexRI.elf"); pos = strstr(name, "hodexRI.elf");
if (pos != NULL) if (pos != NULL)
{ {
strcpy(pos, "/hodextestR.elf"); strcpy(pos, "hodextestR.elf");
return; return;
} }
pos = strstr(name, "/Jennifer\"/Jennifer"); pos = strstr(name, "/Jennifer/\"Jennifer");
if (pos != NULL) if (pos != NULL)
{ {
strcpy(pos, "/Jennifer\"/../JenTest/JenTest"); strcpy(pos, "/Jennifer/\"../JenTest/JenTest");
return; return;
} }
@@ -248,17 +307,17 @@ void testModePath(char *name)
return; return;
} }
pos = strstr(name, "/apacheM.elf"); pos = strstr(name, "apacheM.elf");
if (pos != NULL) if (pos != NULL)
{ {
strcpy(pos, "/apachetestM.elf"); strcpy(pos, "apachetestM.elf");
return; return;
} }
pos = strstr(name, "/vt3_Lindbergh\"/vt3_Lindbergh"); pos = strstr(name, "/vt3_Lindbergh/\"vt3_Lindbergh");
if (pos != NULL) if (pos != NULL)
{ {
strcpy(pos, "/vt3_Lindbergh\"/vt3_testmode"); strcpy(pos, "/vt3_Lindbergh/\"vt3_testmode");
return; return;
} }
@@ -273,6 +332,13 @@ void testModePath(char *name)
strcat(name, " -t"); strcat(name, " -t");
} }
/**
* @brief Checks if a given path is a valid directory.
*
* This function uses the stat system call to determine if the provided path
* corresponds to a directory.
* @param path The path to check.
*/
bool isValidDirectory(const char *path) bool isValidDirectory(const char *path)
{ {
struct stat statbuf; struct stat statbuf;
@@ -283,93 +349,122 @@ bool isValidDirectory(const char *path)
return S_ISDIR(statbuf.st_mode); return S_ISDIR(statbuf.st_mode);
} }
/**
* @brief Finds a suitable 32-bit library folder.
*
* This function searches for a directory that is likely to contain 32-bit
* libraries, based on a predefined list of candidate paths.
* @return A dynamically allocated string containing the path to the found
* directory, or NULL if no suitable directory is found.
*/
char *find32bLibFolder(void) char *find32bLibFolder(void)
{ {
const char *candidates[] = {"lib32", "lib/i386-linux-gnu", "lib/i686-linux-gnu", "lib"}; const char *folderCandidates[] = {"/app/lib32", "/usr/lib/i386-linux-gnu", "/usr/lib/i686-linux-gnu", "/usr/lib32", "/usr/lib"};
int num_candidates = sizeof(candidates) / sizeof(candidates[0]); int numCandidates = sizeof(folderCandidates) / sizeof(folderCandidates[0]);
char current_path[PATH_MAX]; char currentPath[PATH_MAX];
struct stat st; struct stat st;
char *result_path = NULL; char *resultPath = NULL;
for (int i = 0; i < num_candidates; ++i) for (int i = 0; i < numCandidates; ++i)
{ {
int written = snprintf(current_path, sizeof(current_path), "/usr/%s", candidates[i]); int written = snprintf(currentPath, sizeof(currentPath), "%s", folderCandidates[i]);
if (written < 0 || (size_t)written >= sizeof(current_path)) if (written < 0 || (size_t)written >= sizeof(currentPath))
continue; continue;
if (stat(current_path, &st) == 0 && S_ISDIR(st.st_mode)) if (stat(currentPath, &st) == 0 && S_ISDIR(st.st_mode))
{ {
size_t path_len = strlen(current_path); size_t pathLen = strlen(currentPath);
result_path = (char *)malloc(path_len + 1); resultPath = (char *)malloc(pathLen + 1);
if (result_path == NULL) if (resultPath == NULL)
{ {
log_fatal("Failed to allocate memory for result path."); log_fatal("Failed to allocate memory for result path.");
return NULL; return NULL;
} }
strcpy(result_path, current_path); strcpy(resultPath, currentPath);
return result_path; return resultPath;
} }
} }
return NULL; return NULL;
} }
/**
* @brief Finds the path to the LD_PRELOAD library.
*
* This function searches for the lindbergh.so library in various locations,
* including the current directory and system library folders.
* @return A dynamically allocated string containing the path to the library, or an empty string if not found.
*/
char *findLDPreloadLibrary() char *findLDPreloadLibrary()
{ {
const char *library_name = PRELOAD_FILE_NAME; char *foundPathPtr = NULL;
static char *empty_string = "";
char *found_path_ptr = NULL;
char *search_dir = find32bLibFolder(); char *systemLibFolder = find32bLibFolder();
if (search_dir == NULL || search_dir[0] == '\0') if (systemLibFolder == NULL || systemLibFolder[0] == '\0')
{ {
log_error("Error: search_dir is NULL or empty.\n"); log_error("Error: library folder not found.\n");
return empty_string; return "";
} }
char full_library_path[PATH_MAX]; char fullLibPath[PATH_MAX];
int len_written = snprintf(full_library_path, sizeof(full_library_path), "%s/%s", search_dir, library_name); int lenWritten = snprintf(fullLibPath, sizeof(fullLibPath), "%s/%s", systemLibFolder, PRELOAD_FILE_NAME);
if (len_written < 0 || (size_t)len_written >= sizeof(full_library_path)) if (lenWritten < 0 || (size_t)lenWritten >= sizeof(fullLibPath))
{ {
log_error("find_preload_library_in_specific_dir: Warning: Constructed path is too long or invalid: %s/%s\n", search_dir, log_error("Warning: Constructed path is too long or invalid: %s/%s\n", systemLibFolder, PRELOAD_FILE_NAME);
library_name); return "";
return empty_string;
} }
if (access(full_library_path, F_OK) == 0) if (access(PRELOAD_FILE_NAME, F_OK) == 0) // lindbergh.so in current folder
{ {
found_path_ptr = (char *)malloc(strlen(full_library_path) + 1); char curDir[MAX_PATH_LEN];
if (found_path_ptr == NULL) if (getcwd(curDir, sizeof(curDir)) == NULL)
{ return "";
log_error("find_preload_library_in_specific_dir: Failed to allocate memory for result path"); size_t len = strlen(curDir) + strlen(PRELOAD_FILE_NAME) + 1;
return empty_string; foundPathPtr = (char *)malloc(len + 1);
} if (foundPathPtr == NULL)
return "";
else else
{ snprintf(foundPathPtr, len + 1, "%s/%s", curDir, PRELOAD_FILE_NAME);
strcpy(found_path_ptr, full_library_path);
return found_path_ptr;
}
} }
else else if (access(fullLibPath, F_OK) == 0) // lindbergh.so in system lib folder
{ {
return empty_string; foundPathPtr = (char *)malloc(strlen(fullLibPath) + 1);
if (foundPathPtr == NULL)
return "";
else
strcpy(foundPathPtr, fullLibPath);
} }
else // lindbergh.so in the ELF folder.
{
return PRELOAD_FILE_NAME;
}
return foundPathPtr;
} }
/** /**
* Makes sure the environment variables are set correctly * Makes sure the environment variables are set correctly
*
* This function sets the LD_LIBRARY_PATH and LD_PRELOAD environment variables
* to ensure that the game can find the necessary libraries and preload the
* lindbergh.so library.
*
* @param ldLibPath The path to the LD_PRELOAD library.
* @param curDir The current working directory.
* @param gameDir The directory where the game is located.
* @param zink Flag indicating whether to use Zink.
* @param nvidia Flag indicating whether to use NVIDIA GPU.
* to run the game. * to run the game.
*/ */
void setEnvironmentVariables(int isFlatpak, int zink, int nvidia) void setEnvironmentVariables(char *ldLibPath, char *curDir, char *gameDir, int zink, int nvidia)
{ {
// Ensure the library path is set correctly // Ensure the library path is set correctly
char libraryPath[128] = {0}; char libraryPath[MAX_PATH_LEN] = {0};
const char *currentLibraryPath = getenv(LD_LIBRARY_PATH); const char *currentLibraryPath = getenv(LD_LIBRARY_PATH);
if (currentLibraryPath != NULL) if (currentLibraryPath != NULL)
@@ -380,15 +475,33 @@ void setEnvironmentVariables(int isFlatpak, int zink, int nvidia)
strcat(libraryPath, ".:lib:../lib"); strcat(libraryPath, ".:lib:../lib");
char *tmpLibPath = dirname(strdup(ldLibPath));
if (strcmp(tmpLibPath, ".") != 0)
{
strcat(libraryPath, ":");
strcat(libraryPath, tmpLibPath);
}
if (curDir != NULL && curDir[0] != '\0' && strcmp(curDir, tmpLibPath) != 0)
{
strcat(libraryPath, ":");
strcat(libraryPath, curDir);
}
if (gameDir != NULL && gameDir[0] != '\0')
{
strcat(libraryPath, ":");
strcat(libraryPath, gameDir);
}
setenv(LD_LIBRARY_PATH, libraryPath, 1); setenv(LD_LIBRARY_PATH, libraryPath, 1);
// Ensure the preload path is set correctly // Ensure the preload path is set correctly
if (isFlatpak) if (strcmp(ldLibPath, "") != 0)
setenv(LD_PRELOAD, PRELOAD_FILE_FLATPAK, 1); setenv(LD_PRELOAD, ldLibPath, 1);
else if (strcmp(ldPreloadLibrary, "") != 0)
setenv(LD_PRELOAD, ldPreloadLibrary, 1);
else else
setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1); setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1); // Should not reach here
if (zink) if (zink)
setenv("MESA_LOADER_DRIVER_OVERRIDE", "zink", 1); setenv("MESA_LOADER_DRIVER_OVERRIDE", "zink", 1);
@@ -401,6 +514,11 @@ void setEnvironmentVariables(int isFlatpak, int zink, int nvidia)
} }
/** /**
* @brief Prints the usage information for the Lindbergh loader.
*
* This function displays the command-line options and usage instructions for
* the Lindbergh loader.
* @param argv The command-line arguments array.
* Prints the usage for the loader * Prints the usage for the loader
*/ */
void printUsage(char *argv[]) void printUsage(char *argv[])
@@ -416,10 +534,16 @@ void printUsage(char *argv[])
printf(" --version Displays the version of the loader and team's names\n"); printf(" --version Displays the version of the loader and team's names\n");
printf(" --help Displays this usage text\n"); printf(" --help Displays this usage text\n");
printf(" --config | -c Specifies configuration path\n"); printf(" --config | -c Specifies configuration path\n");
printf(" --gamepath | -g Specifies game path\n"); printf(" --gamepath | -g Specifies game path without ELF name\n");
} }
/** /**
* @brief Lists available evdev controllers and their inputs.
*
* This function initializes the controller system and then iterates through
* the available controllers, printing their names and the names of their
* inputs.
* @return EXIT_SUCCESS on success, EXIT_FAILURE on failure.
* Lists available evdev controllers and their inputs * Lists available evdev controllers and their inputs
*/ */
int listControllers() int listControllers()
@@ -453,8 +577,10 @@ int listControllers()
} }
/** /**
* Small utility to automatically detect the game and run it without * @brief Prints the version information of the Lindbergh loader.
* having to type a long string in. *
* This function displays the version number of the Lindbergh loader and the
* names of the team members.
*/ */
void printVersion() { void printVersion() {
printf("Lindbergh Loader v%d.%d\n", MAJOR_VERSION, MINOR_VERSION); printf("Lindbergh Loader v%d.%d\n", MAJOR_VERSION, MINOR_VERSION);
@@ -467,7 +593,9 @@ void printVersion() {
*/ */
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Check for --version before directory operations char gameELF[128] = {0};
char gameDir[MAX_PATH_LEN] = {0};
if (argc > 1 && strcmp(argv[1], "--version") == 0) { if (argc > 1 && strcmp(argv[1], "--version") == 0) {
printVersion(); printVersion();
return EXIT_SUCCESS; return EXIT_SUCCESS;
@@ -483,151 +611,46 @@ int main(int argc, char *argv[])
{ {
return listControllers(); return listControllers();
} }
// We save the current dir to return to it later
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 game 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;
}
}
// Look for the games
struct dirent *ent;
DIR *dir;
if (gamePath[0] != '\0')
{
// Check if the passed folder is valid
if (!isValidDirectory(gamePath))
{
log_error("The game folder is invalid.");
return EXIT_FAILURE;
}
dir = opendir(gamePath);
}
else
{
dir = opendir(curDir);
}
if (dir == NULL)
{
log_error("Could not list files in current directory.");
return EXIT_FAILURE;
}
char *game = NULL;
int lindberghSharedObjectFound = 0;
int isFlatpak = 0;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type != DT_REG)
continue;
if (strcmp(ent->d_name, PRELOAD_FILE_NAME) == 0)
{
lindberghSharedObjectFound = 1;
continue;
}
int index = 0;
while (game == NULL)
{
if (strcmp(games[index], "END") == 0)
break;
if (strcmp(ent->d_name, games[index]) == 0)
{
game = games[index];
break;
}
index++;
}
if (game != NULL && lindberghSharedObjectFound)
break;
}
closedir(dir);
ldPreloadLibrary = findLDPreloadLibrary();
if (!lindberghSharedObjectFound)
{
FILE *file = fopen(PRELOAD_FILE_FLATPAK, "r");
if (file)
{
fclose(file);
isFlatpak = 1;
}
else if (strcmp(ldPreloadLibrary, "") == 0)
{
log_error("The preload object lindbergh.so was not found in this directory.");
return EXIT_FAILURE;
}
}
// Build up the command to start the game // Build up the command to start the game
int testMode = false;
int testMode = 0; int gdb = false;
int gdb = 0; int zink = false;
int zink = 0; int nvidia = false;
int nvidia = 0; int forceGame = false;
int forceGame = 0; int segaboot = false;
int segaboot = 0; char *extConfigPath = NULL;
char extConfigPath[PATH_MAX] = {0}; char *passedGamePath = NULL;
char forceGamePath[128] = {0}; char *forcedGamePath = NULL;
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++)
{ {
if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--test") == 0) if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--test") == 0)
{ {
testMode = 1; testMode = true;
continue; continue;
} }
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--segaboot") == 0) if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--segaboot") == 0)
{ {
segaboot = 1; segaboot = true;
continue; continue;
} }
if (strcmp(argv[i], "--gdb") == 0) if (strcmp(argv[i], "--gdb") == 0)
{ {
gdb = 1; gdb = true;
continue; continue;
} }
if (strcmp(argv[i], "-z") == 0 || strcmp(argv[i], "--zink") == 0) if (strcmp(argv[i], "-z") == 0 || strcmp(argv[i], "--zink") == 0)
{ {
zink = 1; zink = true;
continue; continue;
} }
if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--nvidia") == 0) if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--nvidia") == 0)
{ {
nvidia = 1; nvidia = true;
continue; continue;
} }
@@ -637,7 +660,7 @@ int main(int argc, char *argv[])
{ {
break; break;
} }
strncpy(extConfigPath, argv[i + 1], PATH_MAX); extConfigPath = strdup(argv[i + 1]);
i += 1; i += 1;
continue; continue;
} }
@@ -645,46 +668,153 @@ int main(int argc, char *argv[])
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gamepath") == 0) if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gamepath") == 0)
{ {
if (i + 1 >= argc) if (i + 1 >= argc)
{
break; break;
}
passedGamePath = strdup(argv[i + 1]);
i += 1; i += 1;
continue; continue;
} }
// Treat the argument as the game name // Treat the argument as the game name
strcpy(forceGamePath, argv[i]); forcedGamePath = strdup(argv[i]);
forceGame = 1; forceGame = true;
}
// We save the current dir to return to it later
char curDir[MAX_PATH_LEN];
if (getcwd(curDir, sizeof(curDir)) == NULL)
{
log_error("Error: could not get the current directory.");
return EXIT_FAILURE;
}
char command[PATH_MAX] = {0};
char *forcedGameDir;
char *forcedGameElf;
if (forceGame)
{
if (forcedGamePath != NULL)
{
forcedGameDir = dirname(strdup(forcedGamePath));
forcedGameElf = basename(strdup(forcedGamePath));
if (strcmp(forcedGameDir, ".") == 0) // Only the ELF file name was passed.
{
if (passedGamePath[0] != '\0')
snprintf(gameDir, MAX_PATH_LEN, "%s", passedGamePath);
else
snprintf(gameDir, MAX_PATH_LEN, "./");
}
else
{
if (passedGamePath != NULL) // ELF with path and also -g option pointing to a folder
log_warn("Warning: elf passed with a path, and -g option enabled. Ignoring -g option.");
snprintf(gameDir, MAX_PATH_LEN, "%s", forcedGameDir);
}
if (strcmp(forcedGameElf, ".") != 0)
strcpy(gameELF, forcedGameElf);
// Check if the passed folder is valid
if (!isValidDirectory(gameDir))
{
log_error("%s is not a valid directory here.", gameDir);
return EXIT_FAILURE;
}
}
}
else
{
// Look for the game ELF's
struct dirent *ent;
DIR *dir;
if (passedGamePath != NULL && passedGamePath[0] != '\0')
{
snprintf(gameDir, MAX_PATH_LEN, "%s", passedGamePath);
// Check if the passed folder is valid
if (!isValidDirectory(gameDir))
{
log_error("%s is not a valid directory.", gameDir);
return EXIT_FAILURE;
}
dir = opendir(gameDir);
}
else
{
snprintf(gameDir, MAX_PATH_LEN, "%s", curDir);
dir = opendir(curDir);
}
if (dir == NULL)
{
log_error("Could not list files in current directory.");
return EXIT_FAILURE;
}
bool found = false;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type != DT_REG)
continue;
int index = 0;
while (!found)
{
if (strcmp(games[index], "END") == 0)
break;
if (strcmp(ent->d_name, games[index]) == 0)
{
strcpy(gameELF, games[index]);
found = true;
break;
}
index++;
}
}
closedir(dir);
}
if (segaboot)
{
strcpy(gameELF, "segaboot");
testMode = true;
}
if (gameELF[0] == '\0')
{
log_error("No lindbergh game found in this directory.");
printUsage(argv);
return EXIT_FAILURE;
}
sprintf(command, "\"%s/\"%s", gameDir, gameELF);
char *ldPreloadLibrary = findLDPreloadLibrary();
if (strcmp(ldPreloadLibrary, PRELOAD_FILE_NAME) == 0)
{
char lindberghSoPath[MAX_PATH_LEN];
if (gameDir[0] != '\0')
snprintf(lindberghSoPath, strlen(gameDir) + sizeof(PRELOAD_FILE_NAME) + 1, "%s/%s", gameDir, PRELOAD_FILE_NAME);
if (access(lindberghSoPath, F_OK) != 0)
{
log_error("The preload object lindbergh.so was not found.");
return EXIT_FAILURE;
}
ldPreloadLibrary = strdup(lindberghSoPath);
} }
// Ensure environment variables are set correctly // Ensure environment variables are set correctly
setEnvironmentVariables(isFlatpak, zink, nvidia); setEnvironmentVariables(ldPreloadLibrary, curDir, gameDir, zink, nvidia);
char command[128] = {0};
if (gamePath[0] != '\0')
sprintf(command, "\"%s\"/", gamePath);
else
strcpy(command, "./");
if (forceGame)
{
strcat(command, forceGamePath);
}
else if (segaboot)
{
strcat(command, "segaboot -t");
}
else
{
if (game == NULL)
{
log_error("No lindbergh game found in this directory.");
return EXIT_FAILURE;
}
strcat(command, game);
}
if (testMode) if (testMode)
testModePath(command); testModePath(command);
isCleanElf(command); isCleanElf(command);
if (gdb) if (gdb)
@@ -695,16 +825,16 @@ int main(int argc, char *argv[])
strcpy(command, temp); strcpy(command, temp);
} }
if (extConfigPath[0] != '\0') if (extConfigPath != NULL && extConfigPath[0] != '\0')
{ {
setenv(LINDBERGH_CONFIG_PATH, extConfigPath, 1); setenv(LINDBERGH_CONFIG_PATH, extConfigPath, 1);
} }
log_info("Starting $ %s", command); log_info("Starting $ %s", command);
if (gamePath[0] != '\0') if (gameDir[0] != '\0')
{ {
if (chdir(gamePath) != 0) if (chdir(gameDir) != 0)
{ {
log_error("Could not change to game path."); log_error("Could not change to game path.");
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -712,7 +842,7 @@ int main(int argc, char *argv[])
} }
int sysCmd = system(command); int sysCmd = system(command);
if (gamePath[0] != '\0') if (gameDir[0] != '\0')
{ {
if (chdir(curDir) != 0) if (chdir(curDir) != 0)
{ {