| Solaris |
|
|
Many games hardcode the path to their data during compilation. If you are looking to relocated the whole game so users can install it anywhere then you are probably going to have to patch the source code. The question is how do you find out what directory the executable is run from? "arg[0] is going to be unreliable. Luckily there are 3 ways I know of doing this. The first is to use the pid of the process and retrieve the path using the proc filesystem. The second way is use dlinfo(3C). The third way is to use getexecname(3C).
First lets look at a example of the problem. You will notice that DATADIR is hardcoded in the source code. The hardcoding can also occur in a header file, and Makefile, or as an argument to a "configure". In the example loadData function call concatenates DATADIR with the name of the file and returns an open file.
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
// The file hero.png would normally be found at DATADIR/hero.png
#define DATADIR "/usr/share/mygame"
FILE \*heroImg;
void playGame(){
// Use your imagination
return;
}
// Open a file from the data directory
FILE \*loadData(char \*name){
FILE \*dataFile;
char \*pathName;
int pathSize;
pathSize=strlen(name)+strlen(DATADIR)+2;
pathName=malloc(pathSize);
(void)snprintf(pathName,pathSize,"%s/%s",DATADIR,name);
dataFile=fopen(pathName,"r");
if (dataFile==(FILE \*)NULL){
fprintf(stderr,"ERROR: Data File %s not found\n",pathName);
free(pathName);
exit(1);
}
free(pathName);
return(dataFile);
}
int main(int argc, char\*\* argv) {
heroImg = loadData("hero.png");
playGame();
(void)fclose(heroImg);
return (EXIT_SUCCESS);
}
Lets have a look at the code for each of the possible solutions. Just the code for the loadData function call will be shown.
You can find a symbolic link to a running executable from /proc/<PID>/path/a.out. A resolvepath(2) system call is then used to return the path which the symbolic link is pointing to.
#include <unistd.h>
#include <limits.h>
// Open a file from the data directory
FILE \*loadData(char \*name){
FILE \*dataFile;
char \*pathName;
char \*procPath;
char \*execPath;
char \*tmpPath;
char \*p;
int pathSize;
const pid~_t pid=getpid();
procPath = malloc(PATH~_MAX+1);
execPath = malloc(PATH~_MAX+1);
// Get the symbolic link to the executable of the current process
(void)snprintf(procPath,PATH~_MAX,"/proc/%d/path/a.out",pid);
// From the symbolic link get the path to the executable
if (resolvepath(procPath,execPath,PATH~_MAX)<1) {
fprintf(stderr,"ERROR: resolvepath failed\n");
free(procPath);
exit(1);
}
free(procPath);
// Split the path into a directory (execPath) and filename (p)
if ((p=strrchr(execPath,'/'))==(char \*)NULL){
fprintf(stderr,"ERROR: Something went really wrong!");
exit(1);
}
\*(p++)=(char)0;
// add everything together to make a path to the file
pathName=malloc(PATH~_MAX+1);
(void)snprintf(pathName,PATH~_MAX,"%s/../share/%s/%s",execPath,p,name);
free(execPath);
dataFile=fopen(pathName,"r");
if (dataFile==(FILE \*)NULL){
fprintf(stderr,"ERROR: Data File %s not found\n",pathName);
free(pathName);
exit(1);
}
free(pathName);
return(dataFile);
}
The dlinfo(3C) library call can give the directory of the currently running process if you pass the RTLD_DI_ORIGIN request. In a couple of programs which I have used this method, I have had a clash with some unrelated definitions in the header files needed for dlinfo and the application. To get around this, you need to copy the relevant definitions from the header files and include it in your code.
#include <unistd.h>
#include <dlfcn.h>
#include <link.h>
#include <limits.h>
FILE \*loadData(char \*name){
FILE \*dataFile;
char \*pathName;
char \*execDir;
char \*tmpPath;
char \*p;
int pathSize;
const pid~_t pid=getpid();
// get the origin of the currently process
execDir = malloc(PATH~_MAX+1);
if (dlinfo(RTLD~_SELF,RTLD~_DI~_ORIGIN,execDir)<0){
fprintf(stderr,"ERROR: dlinfo failed\n");
free(execDir);
exit(1);
}
// make p = the filename of the executable
if ((p=strrchr(getexecname(),'/'))==(char \*)NULL){
p=(char \*)getexecname();
} else {
p++;
}
pathName=malloc(PATH_MAX+1);
(void)snprintf(pathName,PATH_MAX,"%s/../share/%s/%s",execDir,p,name);
free(execDir);
dataFile=fopen(pathName,"r");
if (dataFile==(FILE \*)NULL){
fprintf(stderr,"ERROR: Data File %s not found\n",pathName);
free(pathName);
exit(1);
}
free(pathName);
return(dataFile);
}
The last example code is just using the getexecname(3C) function call.
#include <unistd.h>
#include <limits.h>
FILE \*loadData(char \*name){
FILE \*dataFile;
char \*pathName;
char \*execDir;
const char \*execName = getexecname();
char \*tmpPath;
char \*p;
int pathSize;
const pid~_t pid=getpid();
execDir = malloc(PATH~_MAX+1);
// Check if execname is a absolute path or not
if (execName[0]=='/'){
strncpy(execDir,execName,PATH~_MAX);
} else {
char cwdbuf[PATH~_MAX];
snprintf(execDir,PATH~_MAX,"%s/%s",getcwd(cwdbuf,PATH~_MAX),execName);
}
// Split the path into a directory (execPath) and filename (p)
if ((p=strrchr(execDir,'/'))==(char \*)NULL){
fprintf(stderr,"ERROR: Something went really wrong!");
free(execDir);
exit(1);
}
\*(p++)=(char)0;
pathName=malloc(PATH~_MAX+1);
(void)snprintf(pathName,PATH~_MAX,"%s/../share/%s/%s",execDir,p,name);
free(execDir);
dataFile=fopen(pathName,"r");
if (dataFile==(FILE \*)NULL){
fprintf(stderr,"ERROR: Data File %s not found\n",pathName);
free(pathName);
exit(1);
}
free(pathName);
return(dataFile);
}
As you can see there is atleast 3 ways to solve this problem. You might want to write your own path relocating function which you can easily patch into the game code.
Good Luck with your porting!
Terms of Use
|
Privacy
|
Trademarks
|
Copyright Policy
|
Site Guidelines
|
Site Map
|
Help
Your use of this web site or any of its content or software indicates your agreement to be bound by these Terms of Use.
© 2012, Oracle Corporation and/or its affiliates.