Skip to content

Commit

Permalink
Revise path handling to address issue pete-gordon#188
Browse files Browse the repository at this point in the history
Purpose of this change:
* To make Oricutron runnable without cd'ing to its directory first
* To support relative paths with the -t option
* On macOS, to help command line users by not requiring a bundled app,
  allow "./oricutron" in the source directory much like under Linux.
* To make the macOS version runnable from the source directory like
  you might expect if coming from Linux, e.g. by "make && ./oricutron".

Code changes:
* Factor out FILEPREFIX from ROMPREFIX and IMAGEPREFIX constants
* Apply FILEPREFIX for all filenames in oricutron.cfg which was
  done previously only for the keymap path
* Let function get_fileprefix() replace the FILEPREFIX constant,
  and use run-time concatenation instead of compile-time
* For Linux let get_fileprefix() return program binary's directory
* For macOS, let get_fileprefix() return parent of .app bundle if
  the program is bundled, otherwise do as in Linux.
  • Loading branch information
erik-persson committed Nov 19, 2023
1 parent bf4f678 commit 05f515e
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 88 deletions.
51 changes: 32 additions & 19 deletions gui.c
Expand Up @@ -384,16 +384,28 @@ struct osdmenu menus[] = { { "Main Menu", LAST_ITEM(mainitems)-4, mainite
{ "Overclock", LAST_ITEM(ovopitems), ovopitems },
{ "Keyboard options", LAST_ITEM(keopitems), keopitems }};

#define MKPATH_MAX (1024)

// Prepend file prefix to filename
static void mkpath( char buf[MKPATH_MAX], const char *filename )
{
strncpy( buf, get_fileprefix(), MKPATH_MAX );
buf[MKPATH_MAX-1] = 0;
strncat( buf, filename, MKPATH_MAX-strlen(buf)-1 );
}

// Load a 24bit BMP for the GUI
SDL_bool gimg_load( struct guiimg *gi )
{
SDL_RWops *f;
Uint8 hdrbuf[640*3];
Sint32 x, y;
SDL_bool fileok;
char path[MKPATH_MAX];

// Get the file
f = SDL_RWFromFile( gi->filename, "rb" );
mkpath( path, gi->filename );
f = SDL_RWFromFile( path, "rb" );
if( !f )
{
error_printf( "Unable to open '%s'\n", gi->filename );
Expand Down Expand Up @@ -2196,44 +2208,45 @@ void swap_render_mode( struct machine *oric, struct osdmenuitem *mitem, int newr
setemumode( oric, NULL, EM_RUNNING );
}


// Set things to default that can't fail
void preinit_gui( struct machine *oric )
{
int i;
for( i=0; i<NUM_TZ; i++ ) tz[i] = NULL;
for( i=0; i<NUM_GIMG; i++ ) gimgs[i].buf = NULL;
strcpy( tapepath, FILEPREFIX"tapes" );
mkpath( tapepath, "tapes" );
strcpy( tapefile, "" );
strcpy( diskpath, FILEPREFIX"disks" );
mkpath( diskpath, "disks" );
strcpy( diskfile, "" );
strcpy( telediskpath, FILEPREFIX"teledisks" );
mkpath( telediskpath, "teledisks" );
strcpy( telediskfile, "" );
strcpy( pravdiskpath, FILEPREFIX"pravdisks" );
mkpath( pravdiskpath, "pravdisks" );
strcpy( pravdiskfile, "" );
strcpy( atmosromfile, ROMPREFIX"basic11b" );
strcpy( oric1romfile, ROMPREFIX"basic10" );
strcpy( mdiscromfile, ROMPREFIX"microdis" );
strcpy( bd500romfile, ROMPREFIX"bd500" );
strcpy( jasmnromfile, ROMPREFIX"jasmin" );
strcpy( pravetzromfile[0], ROMPREFIX"pravetzt" );
strcpy( pravetzromfile[1], ROMPREFIX"8dos2" );
mkpath( atmosromfile, ROMPREFIX"basic11b" );
mkpath( oric1romfile, ROMPREFIX"basic10" );
mkpath( mdiscromfile, ROMPREFIX"microdis" );
mkpath( bd500romfile, ROMPREFIX"bd500" );
mkpath( jasmnromfile, ROMPREFIX"jasmin" );
mkpath( pravetzromfile[0], ROMPREFIX"pravetzt" );
mkpath( pravetzromfile[1], ROMPREFIX"8dos2" );
telebankfiles[0][0] = 0;
telebankfiles[1][0] = 0;
telebankfiles[2][0] = 0;
telebankfiles[3][0] = 0;
telebankfiles[4][0] = 0;
strcpy( telebankfiles[5], ROMPREFIX"teleass" );
strcpy( telebankfiles[6], ROMPREFIX"hyperbas" );
strcpy( telebankfiles[7], ROMPREFIX"telmon24" );
mkpath( telebankfiles[5], ROMPREFIX"teleass" );
mkpath( telebankfiles[6], ROMPREFIX"hyperbas" );
mkpath( telebankfiles[7], ROMPREFIX"telmon24" );
set_render_mode( oric, RENDERMODE_NULL );
strcpy( snappath, FILEPREFIX"snapshots" );
mkpath( snappath, "snapshots" );
strcpy( snapfile, "" );
strcpy( mappingpath, FILEPREFIX"keymap" );
mkpath( mappingpath, "keymap" );
strcpy( mappingfile, "" );

#ifndef __APPLE__
SDL_COMPAT_WM_SetIcon( SDL_LoadBMP( IMAGEPREFIX"winicon.bmp" ), NULL );
char path[MKPATH_MAX];
mkpath( path, IMAGEPREFIX"winicon.bmp" );
SDL_COMPAT_WM_SetIcon( SDL_LoadBMP( path ), NULL );
#endif
}

Expand Down
162 changes: 124 additions & 38 deletions main.c
Expand Up @@ -86,7 +86,7 @@ extern char jasmnromfile[];
extern char pravetzromfile[2][1024];
extern char telebankfiles[8][1024];

static char keymap_path[4096+32];
static char keymap_path[4096];
static int load_keymap = SDL_FALSE;

struct context
Expand Down Expand Up @@ -178,6 +178,100 @@ static char *rendermodes[] = { "{{INVALID}}",

static char *swdepths[] = { "8", "16", "32", NULL };

static char *fileprefix = 0;

// Initialize path prefix for locating program resources
static void init_fileprefix( char *argv[] )
{
(void) argv;
#if defined(__amigaos4__)

fileprefix = "PROGDIR:";

#elif defined(__ANDROID__)

fileprefix = "/data/data/com.emul.oricutron/files/";

#elif defined(__linux__) || defined(__APPLE__)

// Find program directory
fileprefix = realpath( argv[0], 0 ); // convert to absolute path
int len = strlen( fileprefix );
while ( len && fileprefix[len-1] != PATHSEP ) // strip the program filename
len--;
fileprefix[len] = 0;

#if defined(__APPLE__)
// When bundled, program is in <something>/Oricutron.app/Contents/MacOS/
// but data files are further up in the parent of Oricutron.app.
const char *suffix = ".app/Contents/MacOS/";
int suffix_len = strlen(suffix);
if ( len > suffix_len && !strcasecmp( fileprefix + len - suffix_len, suffix ) )
{
// Located in bundle - go to parent of .app dir
len -= suffix_len; // strip the suffix we matched
while ( len && fileprefix[len-1] != PATHSEP ) // strip the .app dir
len--;
fileprefix[len] = 0;
}
// else not bundled, stay in program directory
#endif

#else
fileprefix = "";
#endif
}

// Get prefix string to locate program resources
const char *get_fileprefix()
{
return fileprefix;
}

// Check if filename looks to be absolute path
SDL_bool is_abs_path( const char *filename )
{
char c = *(filename++);

// Detect unix-style absolute path
if ( c == '/' || c == '\\' || c == '~' )
return SDL_TRUE;

// Detect Amiga style absolute path or DOS drive letter
while ( c )
{
if ( c == ':' )
return SDL_TRUE;
c = *(filename++);
}

return SDL_FALSE;
}

// Prepend file prefix to relative path, buffer of size maxlen
void add_fileprefix( char *filename, int maxlen )
{
const char *prefix = get_fileprefix();
Sint32 prefix_len = strlen( prefix );
if ( prefix_len == 0 )
return; // no file prefix on this platform

Sint32 filename_len = strlen( filename );
if ( prefix_len + filename_len + 1 > maxlen )
return; // won't fit in buffer

if ( is_abs_path ( filename ) )
return; // don't apply prefix to absolute path

// Move filename including terminating zero
for ( int i=filename_len+1; i>=0; i-- )
filename[prefix_len+i] = filename[i];

// Put the prefix before
for (int i=0; i<prefix_len; i++)
filename[i] = prefix[i];
}

static SDL_bool istokend( char c )
{
if( isws( c ) ) return SDL_TRUE;
Expand Down Expand Up @@ -232,6 +326,14 @@ SDL_bool read_config_string( char *buf, char *token, char *dest, Sint32 maxlen )
return SDL_TRUE;
}

SDL_bool read_config_path( char *buf, char *token, char *dest, Sint32 maxlen )
{
if ( !read_config_string( buf, token, dest, maxlen ) )
return SDL_FALSE;
add_fileprefix( dest, maxlen );
return SDL_TRUE;
}

SDL_bool read_config_bool( char *buf, char *token, SDL_bool *dest )
{
Sint32 i, toklen;
Expand Down Expand Up @@ -402,9 +504,11 @@ static void load_config( struct start_opts *sto, struct machine *oric )
FILE *f;
Sint32 i, j;
char tbtmp[32];
char keymap_file[4096];
char config_path[4096];

f = fopen( FILEPREFIX"oricutron.cfg", "r" );
strcpy(config_path, "oricutron.cfg");
add_fileprefix(config_path, 4096);
f = fopen( config_path, "r" );
if( !f ) return;

while( !feof( f ) )
Expand All @@ -424,20 +528,20 @@ static void load_config( struct start_opts *sto, struct machine *oric )
if( read_config_bool( &sto->lctmp[i], "palghosting", &oric->palghost ) ) continue;
if( read_config_bool( &sto->lctmp[i], "rom16", &oric->rom16 ) ) continue;
if( read_config_bool( &sto->lctmp[i], "dos70", &oric->dos70 ) ) continue;
if( read_config_string( &sto->lctmp[i], "diskimage", sto->start_disk, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "tapeimage", sto->start_tape, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "symbols", sto->start_syms, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "tapepath", tapepath, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "diskpath", diskpath, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "telediskpath", telediskpath, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "pravdiskpath", pravdiskpath, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "atmosrom", atmosromfile, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "oric1rom", oric1romfile, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "mdiscrom", mdiscromfile, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "bd500rom", bd500romfile, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "jasminrom", jasmnromfile, 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "pravetzrom", pravetzromfile[0], 1024 ) ) continue;
if( read_config_string( &sto->lctmp[i], "pravetz8drom", pravetzromfile[1], 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "diskimage", sto->start_disk, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "tapeimage", sto->start_tape, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "symbols", sto->start_syms, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "tapepath", tapepath, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "diskpath", diskpath, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "telediskpath", telediskpath, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "pravdiskpath", pravdiskpath, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "atmosrom", atmosromfile, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "oric1rom", oric1romfile, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "mdiscrom", mdiscromfile, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "bd500rom", bd500romfile, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "jasminrom", jasmnromfile, 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "pravetzrom", pravetzromfile[0], 1024 ) ) continue;
if( read_config_path( &sto->lctmp[i], "pravetz8drom", pravetzromfile[1], 1024 ) ) continue;
if( read_config_int( &sto->lctmp[i], "rampattern", &oric->rampattern, 0, 1 ) ) continue;
if( read_config_option( &sto->lctmp[i], "swdepth", &oric->sw_depth, swdepths ) )
{
Expand All @@ -461,7 +565,7 @@ static void load_config( struct start_opts *sto, struct machine *oric )
for( j=0; j<8; j++ )
{
sprintf( tbtmp, "telebank%c", j+'0' );
if( read_config_string( &sto->lctmp[i], tbtmp, telebankfiles[j], 1024 ) ) break;
if( read_config_path( &sto->lctmp[i], tbtmp, telebankfiles[j], 1024 ) ) break;
}
if( read_config_bool( &sto->lctmp[i], "lightpen", &oric->lightpen ) ) continue;
if( read_config_bool( &sto->lctmp[i], "printenable", &oric->printenable ) ) continue;
Expand Down Expand Up @@ -533,10 +637,8 @@ static void load_config( struct start_opts *sto, struct machine *oric )
if( read_config_bool( &sto->lctmp[i], "disable_menuscheme", &oric->disable_menuscheme ) ) continue;
if( read_config_bool( &sto->lctmp[i], "show_keyboard", &oric->show_keyboard ) ) continue;
if( read_config_bool( &sto->lctmp[i], "sticky_mod_keys", &oric->sticky_mod_keys ) )continue;
if( read_config_string( &sto->lctmp[i], "autoload_keyboard_mapping", keymap_file, 4096 ) )
if( read_config_path( &sto->lctmp[i], "autoload_keyboard_mapping", keymap_path, 4096 ) )
{
strcpy(keymap_path, FILEPREFIX"");
strcat(keymap_path, keymap_file);
load_keymap = SDL_TRUE;
continue;
}
Expand Down Expand Up @@ -1612,23 +1714,7 @@ int main( int argc, char *argv[] )
putenv("SDL_VIDEO_CENTERED=center");
#endif

#ifdef __APPLE__
// --------------------------------------------------------------------
// This makes relative paths work in C++ in Xcode by changing directory
// to the Resources folder inside the .app bundle
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
char path[PATH_MAX];
if (CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
{
// this directory is something/Oricutron.app/Contents/Resources
// go down 3 times to find the app containing directory
strcat(path, "/../../..");
chdir(path);
}
CFRelease(resourcesURL);
//printf("Current Path: %s\n", path);
#endif
init_fileprefix( argv );

memset(&ctx.oric, 0, sizeof(ctx.oric));
if (!init(&ctx.oric, argc, argv))
Expand Down
40 changes: 9 additions & 31 deletions system.h
Expand Up @@ -34,40 +34,18 @@
/* Output audio frequency */
#define AUDIO_FREQ 44100


#if defined(__amigaos4__)

#define PATHSEP '/'
#define PATHSEPSTR "/"
#define FILEPREFIX "PROGDIR:"
#define ROMPREFIX "PROGDIR:roms/"
#define IMAGEPREFIX "PROGDIR:images/"

#elif defined(WIN32)

#define PATHSEP '\\'
#define PATHSEPSTR "\\"
#define FILEPREFIX
#define ROMPREFIX "roms\\"
#define IMAGEPREFIX "images\\"

#elif defined(__ANDROID__)

#define PATHSEP '/'
#define PATHSEPSTR "/"
#define FILEPREFIX "/data/data/com.emul.oricutron/files/"
#define ROMPREFIX FILEPREFIX"roms/"
#define IMAGEPREFIX FILEPREFIX"images/"

#if defined(WIN32)
#define PATHSEP '\\'
#define PATHSEPSTR "\\"
#else
#define PATHSEP '/'
#define PATHSEPSTR "/"
#endif

#define PATHSEP '/'
#define PATHSEPSTR "/"
#define FILEPREFIX
#define ROMPREFIX "roms/"
#define IMAGEPREFIX "images/"
#define ROMPREFIX "roms"PATHSEPSTR
#define IMAGEPREFIX "images"PATHSEPSTR

#endif
const char *get_fileprefix();

/* SDL related stuff */
#include "system_sdl.h"
Expand Down

0 comments on commit 05f515e

Please sign in to comment.