From c885b533dd54539f8e07656a60522f24a77f0d1e Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 22 Mar 2020 19:38:23 -0500 Subject: [PATCH] New script* API methods. Untested. --- singe/Menu.singe | 1 + singe/games.dat | 20 +- singe/main.c | 1111 ++++++++++++++++++++++++---------------------- singe/main.h | 36 ++ singe/singe.c | 277 +++++++++--- singe/singe.h | 8 +- singe/singe.pro | 4 +- 7 files changed, 848 insertions(+), 609 deletions(-) create mode 100644 singe/main.h diff --git a/singe/Menu.singe b/singe/Menu.singe index 72d6b6072..860618e57 100644 --- a/singe/Menu.singe +++ b/singe/Menu.singe @@ -49,6 +49,7 @@ for dir in lfs.dir(".") do end end table.sort(FOUND_GAMES, compareTitles) +scriptExecute(FOUND_GAMES[1]) font = fontLoad("ActionMax/font_LED_Real.ttf", 32); diff --git a/singe/games.dat b/singe/games.dat index d0375f212..7c03319fa 100644 --- a/singe/games.dat +++ b/singe/games.dat @@ -8,9 +8,7 @@ GAMES = { NO_MOUSE = false, RESOLUTION_X = 720, RESOLUTION_Y = 480, - SINDEN_GUN = { - -- Sinden Args Here, comma separated. - }, + SINDEN_GUN = "", CABINET = "ActionMax/cabinet_38AmbushAlley.png", MARQUEE = "ActionMax/marquee_38AmbushAlley.png", ATTRACT = "ActionMax/38AmbushAlley.mkv", @@ -32,9 +30,7 @@ GAMES = { NO_MOUSE = false, RESOLUTION_X = 720, RESOLUTION_Y = 480, - SINDEN_GUN = { - -- Sinden Args Here, comma separated. - }, + SINDEN_GUN = "", CABINET = "ActionMax/cabinet_BlueThunder.png", MARQUEE = "ActionMax/marquee_BlueThunder.png", ATTRACT = "ActionMax/BlueThunder.mkv", @@ -56,9 +52,7 @@ GAMES = { NO_MOUSE = false, RESOLUTION_X = 720, RESOLUTION_Y = 480, - SINDEN_GUN = { - -- Sinden Args Here, comma separated. - }, + SINDEN_GUN = "", CABINET = "ActionMax/cabinet_Hydrosub2021.png", MARQUEE = "ActionMax/marquee_Hydrosub2021.png", ATTRACT = "ActionMax/Hydrosub2021.mkv", @@ -80,9 +74,7 @@ GAMES = { NO_MOUSE = false, RESOLUTION_X = 720, RESOLUTION_Y = 480, - SINDEN_GUN = { - -- Sinden Args Here, comma separated. - }, + SINDEN_GUN = "", CABINET = "ActionMax/cabinet_PopsGhostly.png", MARQUEE = "ActionMax/marquee_PopsGhostly.png", ATTRACT = "ActionMax/PopsGhostly.mkv", @@ -104,9 +96,7 @@ GAMES = { NO_MOUSE = false, RESOLUTION_X = 720, RESOLUTION_Y = 480, - SINDEN_GUN = { - -- Sinden Args Here, comma separated. - }, + SINDEN_GUN = "", CABINET = "ActionMax/cabinet_SonicFury.png", MARQUEE = "ActionMax/marquee_SonicFury.png", ATTRACT = "ActionMax/SonicFury.mkv", diff --git a/singe/main.c b/singe/main.c index bab880908..fa5d85069 100644 --- a/singe/main.c +++ b/singe/main.c @@ -33,10 +33,11 @@ #include #include #include -#include +#include "thirdparty/utlist.h" + +#include "main.h" #include "stddclmr.h" -#include "common.h" #include "util.h" #include "frameFile.h" #include "videoPlayer.h" @@ -60,9 +61,379 @@ typedef struct ModeS { ResolutionT resolution; } ModeT; +typedef struct QueueS { + ConfigT conf; + struct QueueS *next; +} QueueT; -bool extractFile(char *filename, unsigned char *data, int32_t length); -void showUsage(char *name, char *message); + +static QueueT *_scriptQueue = NULL; + +static ModeT _modes[] = { + { { 4, 3 }, { 640, 480 } }, + { { 4, 3 }, { 800, 600 } }, + { { 4, 3 }, { 960, 720 } }, + { { 4, 3 }, { 1024, 768 } }, + { { 4, 3 }, { 1280, 960 } }, + { { 4, 3 }, { 1400, 1050 } }, + { { 4, 3 }, { 1440, 1080 } }, + { { 4, 3 }, { 1600, 1200 } }, + { { 4, 3 }, { 1856, 1392 } }, + { { 4, 3 }, { 1920, 1440 } }, + { { 4, 3 }, { 2048, 1536 } }, + { { 16, 9 }, { 1024, 576 } }, + { { 16, 9 }, { 1152, 648 } }, + { { 16, 9 }, { 1280, 720 } }, + { { 16, 9 }, { 1366, 768 } }, + { { 16, 9 }, { 1600, 900 } }, + { { 16, 9 }, { 1920, 1080 } }, + { { 16, 9 }, { 2560, 1440 } }, + { { 16, 9 }, { 3840, 2160 } }, + { { 16, 9 }, { 7680, 4320 } }, + { { 16, 10 }, { 1280, 800 } }, + { { 16, 10 }, { 1440, 900 } }, + { { 16, 10 }, { 1680, 1050 } }, + { { 16, 10 }, { 1920, 1200 } }, + { { 16, 10 }, { 2560, 1600 } }, + { { 0, 0 }, { 0, 0 } } +}; + + +ConfigT *createConf(char *exeName, int argc, char *argv[]); +void destroyConf(ConfigT **confPointer); +bool extractFile(char *filename, unsigned char *data, int32_t length); +void launcher(char *exeName, ConfigT *conf); +void showUsage(char *name, char *message); + + +ConfigT *createConf(char *exeName, int argc, char *argv[]) { + int32_t x = 0; + int32_t argCount = 0; + int32_t argIndex = 0; + int32_t code = 0; + int32_t aspectNum = -1; + int32_t aspectDom = -1; + char *aspectString = NULL; + char *sindenString = NULL; + char *temp = NULL; + const char *arg = NULL; + const char **cargv = (const char **)argv; + ConfigT *conf = NULL; + struct Arg_parser parser; + static struct ap_Option options[] = { + { 'a', "aspect", ap_yes }, + { 'c', "showcalculated", ap_no }, + { 'd', "datadir", ap_yes }, + { 'e', "volume_nonlvdp", ap_yes }, + { 'f', "fullscreen", ap_no }, + { 'g', "sindengun", ap_yes }, + { 'h', "help", ap_no }, + { 'k', "nologos", ap_no }, + { 'l', "volume_vldp", ap_yes }, + { 'm', "nomouse", ap_no }, + { 'o', "scalefactor", ap_yes }, + { 's', "nosound", ap_no }, + { 't', "trace", ap_no }, + { 'u', "stretch", ap_no }, + { 'v', "framefile", ap_yes }, + { 'w', "fullscreenwindow", ap_no }, + { 'x', "xresolution", ap_yes }, + { 'y', "yresolution", ap_yes }, + { 'z', "noconsole", ap_no }, + { 0, 0, ap_no } + }; + + if (!ap_init(&parser, argc, cargv, options, 0)) { + utilDie("Out of memory parsing arguments."); + } + if (ap_error(&parser)) { + utilDie("%s", ap_error(&parser)); + } + + // Default configuration values + conf = (ConfigT *)calloc(1, sizeof(ConfigT)); + if (!conf) utilDie("Out of memory creating config."); + conf->bestRatioIndex = -1; + conf->volumeVldp = 100; + conf->volumeNonVldp = 100; + conf->scaleFactor = 100; + + // Parse command line + argCount = ap_arguments(&parser); + for (argIndex=0; argIndex < ap_arguments(&parser); ++argIndex) { + + code = ap_code(&parser, argIndex); + if (!code) break; + arg = ap_argument(&parser, argIndex); + + // Handle options + switch (code) { + + // Aspect + case 'a': + if (aspectString) free(aspectString); + aspectString = strdup(arg); + argCount++; + break; + + // Show Calculated Frame File Values + case 'c': + conf->showCalculated = true; + break; + + // Data Dir + case 'd': + if (conf->dataDir) free(conf->dataDir); + conf->dataDir = strdup(arg); + argCount++; + break; + + // Effects Volume + case 'e': + conf->volumeNonVldp = atoi(arg); + argCount++; + break; + + // Full Screen + case 'f': + conf->fullScreen = true; + break; + + // Sinden Light Gun + case 'g': + if (sindenString) free(sindenString); + sindenString = strdup(arg); + argCount++; + break; + + // Help + case 'h': + showUsage(exeName, NULL); + break; + + // No Logos + case 'k': + conf->noLogos = true; + break; + + // Video Volume + case 'l': + conf->volumeVldp = atoi(arg); + argCount++; + break; + + // No Mouse + case 'm': + conf->noMouse = true; + break; + + // Overscan Zoom + case 'o': + conf->scaleFactor = atoi(arg); + argCount++; + break; + + // No Sound + case 's': + conf->noSound = true; + break; + + // Trace + case 't': + conf->tracing = true; + break; + + // Ugly Stretched Video + case 'u': + conf->stretchVideo = true; + break; + + // Video File + case 'v': + if (conf->videoFile) free(conf->videoFile); + conf->videoFile = strdup(arg); + argCount++; + break; + + // Full Screen Windowed + case 'w': + conf->fullScreenWindow = true; + break; + + // X Resolution + case 'x': + conf->xResolution = atoi(arg); + argCount++; + break; + + // Y Resolution + case 'y': + conf->yResolution = atoi(arg); + argCount++; + break; + + // No console output or splash screens + case 'z': + conf->noConsole = true; + break; + + default: + abort(); // Something bad happened + break; + } + } + + // For that dumb OS + if (!conf->noConsole) utilRedirectConsole(); + + // Did we get a filename or path to open? + if ((argc - argCount) != 1) showUsage(exeName, "No script file specified."); + conf->scriptFile = strdup(argv[argCount]); + utilFixPathSeparators(&conf->scriptFile, false); + // Exists? + if (!utilFileExists(conf->scriptFile)) { + // Missing. Is a path? + if (utilPathExists(conf->scriptFile)) { + // See if the script exists in the path. + temp = utilCreateString("%s%c%s.singe", conf->scriptFile, utilGetPathSeparator(), utilGetLastPathComponent(conf->scriptFile)); + if (utilFileExists(temp)) { + // Found script named for path inside path. + free(conf->scriptFile); + conf->scriptFile = temp; + temp = NULL; + } else { + // Not in the path either. + free(conf->scriptFile); + conf->scriptFile = NULL; + } + } else { + // Not a path either. + free(conf->scriptFile); + conf->scriptFile = NULL; + } + } + if (!conf->scriptFile) showUsage(exeName, "Unable to locate script."); + + // Do we need to generate a video name? + if (conf->videoFile) { + utilFixPathSeparators(&conf->videoFile, false); + if (!utilFileExists(conf->videoFile)) { + free(conf->videoFile); + conf->videoFile = NULL; + } + } else { + x = (int32_t)(strlen(conf->scriptFile) - strlen(utilGetFileExtension(conf->scriptFile))) - 1; + if (x < 0) { + x = 0; + } + temp = strdup(conf->scriptFile); + temp[x] = 0; + // Check all known extensions - lower case only, Windows users! + x = 0; + while (ffmpegExtensions[x]) { + conf->videoFile = utilCreateString("%s.%s", temp, ffmpegExtensions[x]); + if (utilFileExists(conf->videoFile)) { + break; + } + free(conf->videoFile); + conf->videoFile = NULL; + x++; + } + free(temp); + // If we still don't have one, try a framefile + if (!conf->videoFile) { + conf->videoFile = utilCreateString("%s.txt", temp); + if (!utilFileExists(conf->videoFile)) { + free(conf->videoFile); + conf->videoFile = NULL; + } + } + } + if (!conf->videoFile) showUsage(exeName, "Unable to locate video."); + // Is it a framefile? + if (strncmp(utilGetFileExtension(conf->videoFile), "txt", 3) == 0) { + conf->isFrameFile = true; + } + + // Do we need to generate a data directory name? + if (conf->dataDir) { + utilFixPathSeparators(&conf->dataDir, false); + // Try to create data directory to ensure it exists. + utilMkDirP(conf->dataDir, 0777); + // Does it exist? + if (!utilPathExists(conf->dataDir)) { + free(conf->dataDir); + conf->dataDir = NULL; + } + } else { + // Put it in the game folder. + x = (int32_t)(strlen(conf->scriptFile) - strlen(utilGetLastPathComponent(conf->scriptFile))) - 1; + if (x < 0) { + x = 0; + } + temp = strdup(conf->scriptFile); + temp[x] = 0; + conf->dataDir = strdup(temp); + free(temp); + temp = NULL; + } + if (!conf->dataDir) showUsage(exeName, "Unable to locate data directory."); + utilFixPathSeparators(&conf->dataDir, true); + + // Do the full screen options make sense? + if (conf->fullScreen && conf->fullScreenWindow) showUsage(exeName, "Full Screen or Full Screen Windowed. Pick one."); + + // Sane volume values? + if ((conf->volumeVldp < 0) || (conf->volumeVldp > 100)) showUsage(exeName, "Laserdisc volume must be between 0 and 100 percent."); + if ((conf->volumeNonVldp < 0) || (conf->volumeNonVldp > 100)) showUsage(exeName, "Effects volume must be between 0 and 100 percent."); + + // Sane scale factor? + if ((conf->scaleFactor < 50) || (conf->scaleFactor > 100)) showUsage(exeName, "Display scale must be between 50 and 100 percent."); + + // Sinden light gun? + if (sindenString) { + if (conf->scaleFactor != 100) showUsage(exeName, "Cannot use --sindengun and --scalefactor together."); + if (!parseSindenString(&sindenString, conf)) showUsage(exeName, "Bad argument count to --sindengun."); + } + + // Did they specify an aspect ratio? + if (aspectString) { + aspectNum = atoi(aspectString); + temp = strstr(aspectString, ":"); + if (temp) { + temp++; + aspectDom = atoi(temp); + temp = NULL; + } + if ((aspectNum > 0) && (aspectDom > 0)) { + // Do we understand what they asked for? + x = 0; + while (_modes[x].ratio.aspectNum != 0) { + if ((_modes[x].ratio.aspectNum == aspectNum) && (_modes[x].ratio.aspectDom == aspectDom)) { + conf->bestRatioIndex = x; + break; + } + x++; + } + } + free(aspectString); + } + + ap_free(&parser); + + return conf; +} + + +void destroyConf(ConfigT **confPointer) { + ConfigT *conf = *confPointer; + + if (conf->dataDir) free(conf->dataDir); + if (conf->videoFile) free(conf->videoFile); + if (conf->scriptFile) free(conf->scriptFile); + + free(conf); +} bool extractFile(char *filename, unsigned char *data, int32_t length) { @@ -81,6 +452,207 @@ bool extractFile(char *filename, unsigned char *data, int32_t length) { } +void launcher(char *exeName, ConfigT *conf) { + int32_t x = 0; + int32_t err = 0; + int32_t flags = 0; + int32_t bestResIndex = -1; + float thisRatio = 0; + float bestRatio = 9999; + char *temp = NULL; + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + SDL_Surface *icon = NULL; + SDL_DisplayMode mode; + + // Init SDL + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) utilDie("%s", SDL_GetError()); + if (SDL_GetCurrentDisplayMode(0, &mode) < 0) utilDie("%s", SDL_GetError()); + + // Determine resolution if not specified + if ((conf->xResolution <= 0) || (conf->yResolution <= 0)) { + if (conf->bestRatioIndex < 0) { + // Find our current aspect ratio + x = 0; + while (_modes[x].ratio.aspectNum != 0) { + thisRatio = fabsf(((float)_modes[x].ratio.aspectNum / (float)_modes[x].ratio.aspectDom) - ((float)mode.w / (float)mode.h)); + if (thisRatio < bestRatio) { + bestRatio = thisRatio; + conf->bestRatioIndex = x; + } + x++; + } + } + if (conf->bestRatioIndex < 0) showUsage(exeName, "Unknown aspect ratio."); + x = 0; + // Were both resolutions not specified? + if ((conf->xResolution <= 0) && (conf->yResolution <= 0)) { + // Are we full screen? + if (conf->fullScreen || conf->fullScreenWindow) { + // Use desktop resolution + conf->xResolution = mode.w; + conf->yResolution = mode.h; + } else { + // Find largest window that will fit on the screen but not fill it + while (_modes[x].ratio.aspectNum != 0) { + if ((_modes[x].ratio.aspectNum == _modes[conf->bestRatioIndex].ratio.aspectNum) && (_modes[x].ratio.aspectDom == _modes[conf->bestRatioIndex].ratio.aspectDom)) { + if ((_modes[x].resolution.width < mode.w) && (_modes[x].resolution.height < mode.h)) { + bestResIndex = x; + } + } + x++; + } + conf->xResolution = _modes[bestResIndex].resolution.width; + conf->yResolution = _modes[bestResIndex].resolution.height; + } + } else { + // Find unprovided width/height using provided value + while (_modes[x].ratio.aspectNum != 0) { + // Is this the aspect ratio we're using? + if ((_modes[conf->bestRatioIndex].ratio.aspectNum == _modes[x].ratio.aspectNum) && (_modes[conf->bestRatioIndex].ratio.aspectDom == _modes[x].ratio.aspectDom)) { + // Do we have the width or height? + if (conf->xResolution > 0) { + // We have the width. Is this the matching entry? + if (_modes[x].resolution.width == conf->xResolution) { + bestResIndex = x; + conf->yResolution = _modes[x].resolution.height; + break; + } + } else { + // We have the height. Is this the matching entry? + if (_modes[x].resolution.height == conf->yResolution) { + bestResIndex = x; + conf->xResolution = _modes[x].resolution.width; + break; + } + } + } + x++; + } + } + } + // Did we end up with a valid resolution? + if (conf->xResolution <= 0) showUsage(exeName, "Unable to determine X resolution. (Is the Y value sane?)"); + if (conf->yResolution <= 0) showUsage(exeName, "Unable to determine Y resolution. (Is the X value sane?)"); + if ((conf->xResolution > mode.w) || (conf->yResolution > mode.h)) showUsage(exeName, "Specified resolution is larger than the display."); + + // Do they want tracing? + if (conf->tracing) { + temp = utilCreateString("%strace.txt", conf->dataDir); + utilTraceStart(temp); + free(temp); + temp = NULL; + } + + // Init SDL_mixer ***FIX*** + flags = /* MIX_INIT_FLAC | */ MIX_INIT_MOD | /* MIX_INIT_MP3 | */ MIX_INIT_OGG | MIX_INIT_MID | MIX_INIT_OPUS; + err = Mix_Init(flags); + if (err != flags) utilDie("%s", Mix_GetError()); + + // Init SDL_image ***FIX*** + flags = IMG_INIT_JPG | IMG_INIT_PNG | /* IMG_INIT_TIF | */ IMG_INIT_WEBP; + err = IMG_Init(flags); + if (err != flags) utilDie("%s", IMG_GetError()); + + // Init SDL_ttf + if (TTF_Init() < 0) utilDie("%s", TTF_GetError()); + + // Create Resizable Window + window = SDL_CreateWindow("SINGE", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, conf->xResolution, conf->yResolution, SDL_WINDOW_RESIZABLE); + if (window == NULL) utilDie("%s", SDL_GetError()); + + // Window Icon + icon = IMG_LoadPNG_RW(SDL_RWFromMem(icon_png, icon_png_len)); + if (icon == NULL) utilDie("%s", SDL_GetError()); + SDL_SetWindowIcon(window, icon); + SDL_FreeSurface(icon); + icon = NULL; + + // Do we want full screen of some kind? + if (conf->fullScreen || conf->fullScreenWindow) { + flags = conf->fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP; + SDL_SetWindowFullscreen(window, (Uint32)flags); + } + + // Create an accelerated renderer. + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (renderer == NULL) utilDie("%s", SDL_GetError()); + + // Clear screen with black + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // Create audio mixer device + err = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 44100 /* freq */ * 16 /* bits */ * 2 /* channels */ * 2 /* seconds */); + if (err != 0) utilDie("%s", Mix_GetError()); + Mix_AllocateChannels(16); + + // Start our video playback system + if (frameFileInit()) utilDie("Unable to initialize framefile handler."); + if (videoInit()) utilDie("Unable to initialize video player."); + + // Finish our setup + SDL_DisableScreenSaver(); + + // Run Singe! + singe(window, renderer, conf); + + // Shutdown + videoQuit(); + frameFileQuit(); + Mix_CloseAudio(); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_EnableScreenSaver(); + TTF_Quit(); + IMG_Quit(); + Mix_Quit(); + SDL_Quit(); + utilTraceEnd(); + +#ifdef _WIN32 + if (!conf->noConsole) getchar(); +#endif +} + + +bool parseSindenString(char **sindenStringPointer, ConfigT *conf) { + char *sindenString = *sindenStringPointer; + char *temp = NULL; + + // Was it wrapped in quotes? + if ((sindenString[0] == '"') || (sindenString[0] == '\'')) { + sindenString[0] = ' '; + } + // Ok, this thing can have a mess of different arguments: + // WW - Just the width of the white border + // WW WB - Width of white border and then black border + // RW GW BW WW - Custom color "white" border and width + // RW GW BW WW WB - Custom color "white" border and width then width of black border + // RW GW BW WW RB GB BB WB - Custom color "white" border and width then custom color "black" border and width + temp = strtok(sindenString, " "); + while (temp != NULL) { + conf->sindenArgv[conf->sindenArgc++] = atoi(temp); + temp = strtok(NULL, " "); + if ((temp != NULL) && (conf->sindenArgc > SINDEN_OPTION_COUNT)) return false; + } + // Did we get a sane number of arguments? + if ((conf->sindenArgc != SINDEN_WHITE) && (conf->sindenArgc != SINDEN_WHITE_BLACK) && (conf->sindenArgc != SINDEN_CUSTOM_WHITE) && (conf->sindenArgc != SINDEN_CUSTOM_WHITE_BLACK) && (conf->sindenArgc != SINDEN_CUSTOM_WHITE_CUSTOM_BLACK)) return false; + free(sindenString); + + return true; +} + + +void queueScript(ConfigT *conf) { + QueueT *q; + + q = (QueueT *)calloc(1, sizeof(QueueT)); + memcpy(&q->conf, conf, sizeof(ConfigT)); + LL_APPEND(_scriptQueue, q); +} + + __attribute__((noreturn)) void showUsage(char *name, char *message) { char *temp = NULL; @@ -155,526 +727,23 @@ void showUsage(char *name, char *message) { int main(int argc, char *argv[]) { + char *exeName = (char *)argv[0]; + ConfigT *conf = NULL; + QueueT *q = NULL; - int32_t x = 0; - int32_t err = 0; - int32_t flags = 0; - int32_t bestResIndex = -1; - int32_t bestRatioIndex = -1; - int32_t aspectNum = -1; - int32_t aspectDom = -1; - int32_t argCount = 0; - int32_t argIndex = 0; - int32_t code = 0; - char *exeName = NULL; - char *temp = NULL; - char *aspectString = NULL; - char *sindenString = NULL; - float thisRatio = 0; - float bestRatio = 9999; - bool tracing = false; - SDL_Window *window = NULL; - SDL_Renderer *renderer = NULL; - SDL_Surface *icon = NULL; - SDL_DisplayMode mode; - const char *arg = NULL; - const char **cargv = (const char **)argv; - struct Arg_parser parser; - static struct ap_Option options[] = { - { 'a', "aspect", ap_yes }, - { 'c', "showcalculated", ap_no }, - { 'd', "datadir", ap_yes }, - { 'e', "volume_nonlvdp", ap_yes }, - { 'f', "fullscreen", ap_no }, - { 'g', "sindengun", ap_yes }, - { 'h', "help", ap_no }, - { 'k', "nologos", ap_no }, - { 'l', "volume_vldp", ap_yes }, - { 'm', "nomouse", ap_no }, - { 'o', "scalefactor", ap_yes }, - { 's', "nosound", ap_no }, - { 't', "trace", ap_no }, - { 'u', "stretch", ap_no }, - { 'v', "framefile", ap_yes }, - { 'w', "fullscreenwindow", ap_no }, - { 'x', "xresolution", ap_yes }, - { 'y', "yresolution", ap_yes }, - { 'z', "noconsole", ap_no }, - { 0, 0, ap_no } - }; - static ModeT modes[] = { - { { 4, 3 }, { 640, 480 } }, - { { 4, 3 }, { 800, 600 } }, - { { 4, 3 }, { 960, 720 } }, - { { 4, 3 }, { 1024, 768 } }, - { { 4, 3 }, { 1280, 960 } }, - { { 4, 3 }, { 1400, 1050 } }, - { { 4, 3 }, { 1440, 1080 } }, - { { 4, 3 }, { 1600, 1200 } }, - { { 4, 3 }, { 1856, 1392 } }, - { { 4, 3 }, { 1920, 1440 } }, - { { 4, 3 }, { 2048, 1536 } }, - { { 16, 9 }, { 1024, 576 } }, - { { 16, 9 }, { 1152, 648 } }, - { { 16, 9 }, { 1280, 720 } }, - { { 16, 9 }, { 1366, 768 } }, - { { 16, 9 }, { 1600, 900 } }, - { { 16, 9 }, { 1920, 1080 } }, - { { 16, 9 }, { 2560, 1440 } }, - { { 16, 9 }, { 3840, 2160 } }, - { { 16, 9 }, { 7680, 4320 } }, - { { 16, 10 }, { 1280, 800 } }, - { { 16, 10 }, { 1440, 900 } }, - { { 16, 10 }, { 1680, 1050 } }, - { { 16, 10 }, { 1920, 1200 } }, - { { 16, 10 }, { 2560, 1600 } }, - { { 0, 0 }, { 0, 0 } } - }; + // Queue initial script + conf = createConf(exeName, argc, argv); + queueScript(conf); - exeName = (char *)argv[0]; - - if (!ap_init(&parser, argc, cargv, options, 0)) { - utilDie("Out of memory parsing arguments."); + // Run script queue + while (_scriptQueue) { + q = _scriptQueue; + conf = (ConfigT *)&q->conf; + launcher(exeName, conf); + destroyConf(&conf); + LL_DELETE(_scriptQueue, q); + free(q); } - if (ap_error(&parser)) { - utilDie("%s", ap_error(&parser)); - } - - // Default configuration values - memset(&_conf, 0, sizeof(ConfigT)); - _conf.volumeVldp = 100; - _conf.volumeNonVldp = 100; - _conf.scaleFactor = 100; - - // Parse command line - argCount = ap_arguments(&parser); - for (argIndex=0; argIndex < ap_arguments(&parser); ++argIndex) { - - code = ap_code(&parser, argIndex); - if (!code) break; - arg = ap_argument(&parser, argIndex); - - // Handle options - switch (code) { - - // Aspect - case 'a': - if (aspectString) free(aspectString); - aspectString = strdup(arg); - argCount++; - break; - - // Show Calculated Frame File Values - case 'c': - _conf.showCalculated = true; - break; - - // Data Dir - case 'd': - if (_conf.dataDir) free(_conf.dataDir); - _conf.dataDir = strdup(arg); - argCount++; - break; - - // Effects Volume - case 'e': - _conf.volumeNonVldp = atoi(arg); - argCount++; - break; - - // Full Screen - case 'f': - _conf.fullScreen = true; - break; - - // Sinden Light Gun - case 'g': - if (sindenString) free(sindenString); - sindenString = strdup(arg); - argCount++; - break; - - // Help - case 'h': - showUsage(exeName, NULL); - break; - - // No Logos - case 'k': - _conf.noLogos = true; - break; - - // Video Volume - case 'l': - _conf.volumeVldp = atoi(arg); - argCount++; - break; - - // No Mouse - case 'm': - _conf.noMouse = true; - break; - - // Overscan Zoom - case 'o': - _conf.scaleFactor = atoi(arg); - argCount++; - break; - - // No Sound - case 's': - _conf.noSound = true; - break; - - // Trace - case 't': - tracing = true; - break; - - // Ugly Stretched Video - case 'u': - _conf.stretchVideo = true; - break; - - // Video File - case 'v': - if (_conf.videoFile) free(_conf.videoFile); - _conf.videoFile = strdup(arg); - argCount++; - break; - - // Full Screen Windowed - case 'w': - _conf.fullScreenWindow = true; - break; - - // X Resolution - case 'x': - _conf.xResolution = atoi(arg); - argCount++; - break; - - // Y Resolution - case 'y': - _conf.yResolution = atoi(arg); - argCount++; - break; - - // No console output or splash screens - case 'z': - _conf.noConsole = true; - break; - - default: - abort(); // Something bad happened - break; - } - } - - // For that dumb OS - if (!_conf.noConsole) utilRedirectConsole(); - - // Did we get a filename or path to open? - if ((argc - argCount) != 1) showUsage(exeName, "No script file specified."); - _conf.scriptFile = strdup(argv[argCount]); - utilFixPathSeparators(&_conf.scriptFile, false); - // Exists? - if (!utilFileExists(_conf.scriptFile)) { - // Missing. Is a path? - if (utilPathExists(_conf.scriptFile)) { - // See if the script exists in the path. - temp = utilCreateString("%s%c%s.singe", _conf.scriptFile, utilGetPathSeparator(), utilGetLastPathComponent(_conf.scriptFile)); - if (utilFileExists(temp)) { - // Found script named for path inside path. - free(_conf.scriptFile); - _conf.scriptFile = temp; - temp = NULL; - } else { - // Not in the path either. - free(_conf.scriptFile); - _conf.scriptFile = NULL; - } - } else { - // Not a path either. - free(_conf.scriptFile); - _conf.scriptFile = NULL; - } - } - if (!_conf.scriptFile) showUsage(exeName, "Unable to locate script."); - - // Do we need to generate a video name? - if (_conf.videoFile) { - utilFixPathSeparators(&_conf.videoFile, false); - if (!utilFileExists(_conf.videoFile)) { - free(_conf.videoFile); - _conf.videoFile = NULL; - } - } else { - x = (int32_t)(strlen(_conf.scriptFile) - strlen(utilGetFileExtension(_conf.scriptFile))) - 1; - if (x < 0) { - x = 0; - } - temp = strdup(_conf.scriptFile); - temp[x] = 0; - // Check all known extensions - lower case only, Windows users! - x = 0; - while (ffmpegExtensions[x]) { - _conf.videoFile = utilCreateString("%s.%s", temp, ffmpegExtensions[x]); - if (utilFileExists(_conf.videoFile)) { - break; - } - free(_conf.videoFile); - _conf.videoFile = NULL; - x++; - } - free(temp); - // If we still don't have one, try a framefile - if (!_conf.videoFile) { - _conf.videoFile = utilCreateString("%s.txt", temp); - if (!utilFileExists(_conf.videoFile)) { - free(_conf.videoFile); - _conf.videoFile = NULL; - } - } - } - if (!_conf.videoFile) showUsage(exeName, "Unable to locate video."); - // Is it a framefile? - if (strncmp(utilGetFileExtension(_conf.videoFile), "txt", 3) == 0) { - _conf.isFrameFile = true; - } - - // Do we need to generate a data directory name? - if (_conf.dataDir) { - utilFixPathSeparators(&_conf.dataDir, false); - // Try to create data directory to ensure it exists. - utilMkDirP(_conf.dataDir, 0777); - // Does it exist? - if (!utilPathExists(_conf.dataDir)) { - free(_conf.dataDir); - _conf.dataDir = NULL; - } - } else { - // Put it in the game folder. - x = (int32_t)(strlen(_conf.scriptFile) - strlen(utilGetLastPathComponent(_conf.scriptFile))) - 1; - if (x < 0) { - x = 0; - } - temp = strdup(_conf.scriptFile); - temp[x] = 0; - _conf.dataDir = strdup(temp); - free(temp); - temp = NULL; - } - if (!_conf.dataDir) showUsage(exeName, "Unable to locate data directory."); - utilFixPathSeparators(&_conf.dataDir, true); - - // Do they want tracing? - if (tracing) { - temp = utilCreateString("%strace.txt", _conf.dataDir); - utilTraceStart(temp); - free(temp); - temp = NULL; - } - - // Do the full screen options make sense? - if (_conf.fullScreen && _conf.fullScreenWindow) showUsage(exeName, "Full Screen or Full Screen Windowed. Pick one."); - - // Sane volume values? - if ((_conf.volumeVldp < 0) || (_conf.volumeVldp > 100)) showUsage(exeName, "Laserdisc volume must be between 0 and 100 percent."); - if ((_conf.volumeNonVldp < 0) || (_conf.volumeNonVldp > 100)) showUsage(exeName, "Effects volume must be between 0 and 100 percent."); - - // Sane scale factor? - if ((_conf.scaleFactor < 50) || (_conf.scaleFactor > 100)) showUsage(exeName, "Display scale must be between 50 and 100 percent."); - - // Sinden light gun? - if (sindenString) { - if (_conf.scaleFactor != 100) showUsage(exeName, "Cannot use --sindengun and --scalefactor together."); - // Was it wrapped in quotes? - if ((sindenString[0] == '"') || (sindenString[0] == '\'')) { - sindenString[0] = ' '; - } - // Ok, this thing can have a mess of different arguments: - // WW - Just the width of the white border - // WW WB - Width of white border and then black border - // RW GW BW WW - Custom color "white" border and width - // RW GW BW WW WB - Custom color "white" border and width then width of black border - // RW GW BW WW RB GB BB WB - Custom color "white" border and width then custom color "black" border and width - temp = strtok(sindenString, " "); - while (temp != NULL) { - _conf.sindenArgv[_conf.sindenArgc++] = atoi(temp); - temp = strtok(NULL, " "); - if ((temp != NULL) && (_conf.sindenArgc > SINDEN_OPTION_COUNT)) showUsage(exeName, "Too many arguments to --sindengun."); - } - // Did we get a sane number of arguments? - if ((_conf.sindenArgc != SINDEN_WHITE) && (_conf.sindenArgc != SINDEN_WHITE_BLACK) && (_conf.sindenArgc != SINDEN_CUSTOM_WHITE) && (_conf.sindenArgc != SINDEN_CUSTOM_WHITE_BLACK) && (_conf.sindenArgc != SINDEN_CUSTOM_WHITE_CUSTOM_BLACK)) showUsage(exeName, "Bad argument count to --sindengun."); - free(sindenString); - } - - // Init SDL - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) utilDie("%s", SDL_GetError()); - if (SDL_GetCurrentDisplayMode(0, &mode) < 0) utilDie("%s", SDL_GetError()); - - // Determine resolution if not specified - if ((_conf.xResolution <= 0) || (_conf.yResolution <= 0)) { - // Did they specify an aspect ratio? - if (aspectString) { - aspectNum = atoi(aspectString); - temp = strstr(aspectString, ":"); - if (temp) { - temp++; - aspectDom = atoi(temp); - temp = NULL; - } - if ((aspectNum > 0) && (aspectDom > 0)) { - // Do we understand what they asked for? - x = 0; - while (modes[x].ratio.aspectNum != 0) { - if ((modes[x].ratio.aspectNum == aspectNum) && (modes[x].ratio.aspectDom == aspectDom)) { - bestRatioIndex = x; - break; - } - x++; - } - } - free(aspectString); - } else { - // Find our current aspect ratio - x = 0; - while (modes[x].ratio.aspectNum != 0) { - thisRatio = fabsf(((float)modes[x].ratio.aspectNum / (float)modes[x].ratio.aspectDom) - ((float)mode.w / (float)mode.h)); - if (thisRatio < bestRatio) { - bestRatio = thisRatio; - bestRatioIndex = x; - } - x++; - } - } - if (bestRatioIndex < 0) showUsage(exeName, "Unknown aspect ratio."); - x = 0; - // Were both resolutions not specified? - if ((_conf.xResolution <= 0) && (_conf.yResolution <= 0)) { - // Are we full screen? - if (_conf.fullScreen || _conf.fullScreenWindow) { - // Use desktop resolution - _conf.xResolution = mode.w; - _conf.yResolution = mode.h; - } else { - // Find largest window that will fit on the screen but not fill it - while (modes[x].ratio.aspectNum != 0) { - if ((modes[x].ratio.aspectNum == modes[bestRatioIndex].ratio.aspectNum) && (modes[x].ratio.aspectDom == modes[bestRatioIndex].ratio.aspectDom)) { - if ((modes[x].resolution.width < mode.w) && (modes[x].resolution.height < mode.h)) { - bestResIndex = x; - } - } - x++; - } - _conf.xResolution = modes[bestResIndex].resolution.width; - _conf.yResolution = modes[bestResIndex].resolution.height; - } - } else { - // Find unprovided width/height using provided value - while (modes[x].ratio.aspectNum != 0) { - // Is this the aspect ratio we're using? - if ((modes[bestRatioIndex].ratio.aspectNum == modes[x].ratio.aspectNum) && (modes[bestRatioIndex].ratio.aspectDom == modes[x].ratio.aspectDom)) { - // Do we have the width or height? - if (_conf.xResolution > 0) { - // We have the width. Is this the matching entry? - if (modes[x].resolution.width == _conf.xResolution) { - bestResIndex = x; - _conf.yResolution = modes[x].resolution.height; - break; - } - } else { - // We have the height. Is this the matching entry? - if (modes[x].resolution.height == _conf.yResolution) { - bestResIndex = x; - _conf.xResolution = modes[x].resolution.width; - break; - } - } - } - x++; - } - } - } - // Did we end up with a valid resolution? - if (_conf.xResolution <= 0) showUsage(exeName, "Unable to determine X resolution. (Is the Y value sane?)"); - if (_conf.yResolution <= 0) showUsage(exeName, "Unable to determine Y resolution. (Is the X value sane?)"); - if ((_conf.xResolution > mode.w) || (_conf.yResolution > mode.h)) showUsage(exeName, "Specified resolution is larger than the display."); - - // Init SDL_mixer ***FIX*** - flags = /* MIX_INIT_FLAC | */ MIX_INIT_MOD | /* MIX_INIT_MP3 | */ MIX_INIT_OGG | MIX_INIT_MID | MIX_INIT_OPUS; - err = Mix_Init(flags); - if (err != flags) utilDie("%s", Mix_GetError()); - - // Init SDL_image ***FIX*** - flags = IMG_INIT_JPG | IMG_INIT_PNG | /* IMG_INIT_TIF | */ IMG_INIT_WEBP; - err = IMG_Init(flags); - if (err != flags) utilDie("%s", IMG_GetError()); - - // Init SDL_ttf - if (TTF_Init() < 0) utilDie("%s", TTF_GetError()); - - // Create Resizable Window - window = SDL_CreateWindow("SINGE", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _conf.xResolution, _conf.yResolution, SDL_WINDOW_RESIZABLE); - if (window == NULL) utilDie("%s", SDL_GetError()); - - // Window Icon - icon = IMG_LoadPNG_RW(SDL_RWFromMem(icon_png, icon_png_len)); - if (icon == NULL) utilDie("%s", SDL_GetError()); - SDL_SetWindowIcon(window, icon); - SDL_FreeSurface(icon); - icon = NULL; - - // Do we want full screen of some kind? - if (_conf.fullScreen || _conf.fullScreenWindow) { - flags = _conf.fullScreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP; - SDL_SetWindowFullscreen(window, (Uint32)flags); - } - - // Create an accelerated renderer. - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (renderer == NULL) utilDie("%s", SDL_GetError()); - - // Clear screen with black - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); - - // Create audio mixer device - err = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 44100 /* freq */ * 16 /* bits */ * 2 /* channels */ * 2 /* seconds */); - if (err != 0) utilDie("%s", Mix_GetError()); - Mix_AllocateChannels(16); - - // Start our video playback system - if (frameFileInit()) utilDie("Unable to initialize framefile handler."); - if (videoInit()) utilDie("Unable to initialize video player."); - - // Finish our setup - SDL_DisableScreenSaver(); - - // Run Singe! - singe(window, renderer); - - // Shutdown - videoQuit(); - frameFileQuit(); - Mix_CloseAudio(); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_EnableScreenSaver(); - TTF_Quit(); - IMG_Quit(); - Mix_Quit(); - SDL_Quit(); - if (_conf.dataDir) free(_conf.dataDir); - if (_conf.videoFile) free(_conf.videoFile); - if (_conf.scriptFile) free(_conf.scriptFile); - utilTraceEnd(); - ap_free(&parser); - -#ifdef _WIN32 - if (!_conf.noConsole) getchar(); -#endif return 0; } diff --git a/singe/main.h b/singe/main.h new file mode 100644 index 000000000..43dcd9624 --- /dev/null +++ b/singe/main.h @@ -0,0 +1,36 @@ +/* + * + * Singe 2 + * Copyright (C) 2006-2020 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef MAIN_H +#define MAIN_H + + +#include "common.h" + +#include "singe.h" + + +bool parseSindenString(char **sindenStringPointer, ConfigT *conf); +void queueScript(ConfigT *conf); + + +#endif // MAIN_H diff --git a/singe/singe.c b/singe/singe.c index b864b0857..beadd9a89 100644 --- a/singe/singe.c +++ b/singe/singe.c @@ -34,6 +34,7 @@ #include "thirdparty/manymouse/manymouse.h" #include "thirdparty/luafilesystem/src/lfs.h" +#include "main.h" #include "util.h" #include "frameFile.h" #include "videoPlayer.h" @@ -168,10 +169,6 @@ enum { }; -// Command line options -ConfigT _conf; - - typedef struct GlobalS { MouseT mice[MAX_MICE]; lua_State *luaContext; @@ -215,6 +212,7 @@ typedef struct GlobalS { FontT *fontList; FontT *fontCurrent; MappingT controlMappings[INPUT_COUNT]; + ConfigT conf; // Local copy of command line options } GlobalT; @@ -277,6 +275,9 @@ int32_t apiOverlayPlot(lua_State *L); int32_t apiOverlayPrint(lua_State *L); int32_t apiOverlaySetResolution(lua_State *L); +int32_t apiScriptExecute(lua_State *L); +int32_t apiScriptPush(lua_State *L); + int32_t apiSingeDisablePauseKey(lua_State *L); int32_t apiSingeEnablePauseKey(lua_State *L); int32_t apiSingeGetPauseFlag(lua_State *L); @@ -322,6 +323,7 @@ int32_t apiVldpGetPixel(lua_State *L); int32_t apiVldpGetWidth(lua_State *L); int32_t apiVldpVerbose(lua_State *L); +ConfigT *buildConfFromTable(lua_State *L); void doIndexDisplay(int32_t percent); void doLogos(void); void callLua(const char *func, const char *sig, ...); @@ -503,8 +505,8 @@ int32_t apiDiscAudio(lua_State *L) { d = lua_tonumber(L, 1); channel = (int32_t)d; d = lua_toboolean(L, 2); onOff = (bool)d; videoGetVolume(_global.videoHandle, &left, &right); - if (channel == 1) left = (onOff ? _conf.volumeVldp : 0); - if (channel == 2) right = (onOff ? _conf.volumeVldp : 0); + if (channel == 1) left = (onOff ? _global.conf.volumeVldp : 0); + if (channel == 2) right = (onOff ? _global.conf.volumeVldp : 0); videoSetVolume(_global.videoHandle, left, right); result = true; } @@ -534,7 +536,7 @@ int32_t apiDiscGetFrame(lua_State *L) { int64_t frame = 0; if (!_global.discStopped) { - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frame = frameFileGetFrame(_global.frameFileHandle, _global.videoHandle); } else { if (_global.videoHandle >= 0) frame = videoGetFrame(_global.videoHandle); @@ -587,7 +589,7 @@ int32_t apiDiscSearch(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int64_t)d; - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); @@ -638,7 +640,7 @@ int32_t apiDiscSkipBackward(lua_State *L) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); if (_global.videoHandle >= 0) frame = videoGetFrame(_global.videoHandle) - (int64_t)d; - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); @@ -682,7 +684,7 @@ int32_t apiDiscSkipForward(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); if (_global.videoHandle >= 0) frame = videoGetFrame(_global.videoHandle) + (int64_t)d; - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); @@ -717,7 +719,7 @@ int32_t apiDiscSkipToFrame(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int64_t)d; - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); @@ -747,7 +749,7 @@ int32_t apiDiscStepBackward(lua_State *L) { // No matter disc state, go back a frame. If playing, pause. if (_global.videoHandle >= 0) { - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frame = frameFileGetFrame(_global.frameFileHandle, _global.videoHandle) - 1; frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { @@ -771,7 +773,7 @@ int32_t apiDiscStepForward(lua_State *L) { // No matter disc state, go forward a frame. If playing, pause. if (_global.videoHandle >= 0) { - if (_conf.isFrameFile) { + if (_global.conf.isFrameFile) { frame = frameFileGetFrame(_global.frameFileHandle, _global.videoHandle) + 1; frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { @@ -2136,7 +2138,7 @@ int32_t apiVideoLoad(lua_State *L) { luaDie(L, "videoLoad", "Optional second parameter must be a string."); } } else { - data = _conf.dataDir; + data = _global.conf.dataDir; } video = (VideoT *)calloc(1, sizeof(VideoT)); if (!video) luaDie(L, "videoLoad", "Unable to allocate new video."); @@ -2432,8 +2434,8 @@ int32_t apiKeyboardSetMode(lua_State *L) { int32_t apiMouseEnable(lua_State *L) { // Enables mouse monitoring (void)L; - luaTrace(L, "mouseEnable", "%d", _conf.noMouse); - _global.mouseEnabled = (bool)!_conf.noMouse; + luaTrace(L, "mouseEnable", "%d", _global.conf.noMouse); + _global.mouseEnabled = (bool)!_global.conf.noMouse; return 0; } @@ -2500,7 +2502,7 @@ int32_t apiMouseGetPosition(lua_State *L) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); m = (int32_t)d; - if ((m < 0) || (m >= MAX_MICE)) luaDie(L, "apiMouseGetPosition", "Invalid mouse index: %d", m); + if ((m < 0) || (m >= MAX_MICE)) luaDie(L, "mouseGetPosition", "Invalid mouse index: %d", m); x = _global.axisCache[MAX_CONTROLLERS * CONTROLLER_AXIS_COUNT + m * MOUSE_AXIS_COUNT]; y = _global.axisCache[MAX_CONTROLLERS * CONTROLLER_AXIS_COUNT + m * MOUSE_AXIS_COUNT + 1]; result = true; @@ -2508,11 +2510,11 @@ int32_t apiMouseGetPosition(lua_State *L) { } if (result) { - luaTrace(L, "apiMouseGetPosition", "%d %d %d", m, x, y); + luaTrace(L, "mouseGetPosition", "%d %d %d", m, x, y); lua_pushinteger(L, x); lua_pushinteger(L, y); } else { - luaTrace(L, "apiMouseGetPosition", "Failed!"); + luaTrace(L, "mouseGetPosition", "Failed!"); lua_pushinteger(L, -1); lua_pushinteger(L, -1); } @@ -2552,6 +2554,62 @@ int32_t apiDiscGetState(lua_State *L) { } +int32_t apiScriptExecute(lua_State *L) { + int32_t n = lua_gettop(L); + ConfigT *conf = NULL; + bool result = false; + + if (n == 1) { + if (lua_istable(L, 1)) { + // Push next script. + conf = buildConfFromTable(L); + queueScript(conf); + free(conf); + // Stop this script running. + _global.running = false; + result = true; + } + } + + if (result) { + luaTrace(L, "scriptExecute", "Success."); + } else { + luaTrace(L, "scriptExecute", "Failed!"); + } + + return 0; +} + + +int32_t apiScriptPush(lua_State *L) { + int32_t n = lua_gettop(L); + ConfigT *conf = NULL; + bool result = false; + + if (n == 1) { + if (lua_istable(L, 1)) { + // Push next script. + conf = buildConfFromTable(L); + queueScript(conf); + free(conf); + // Push this script. + queueScript(&_global.conf); + // Stop this script running. + _global.running = false; + result = true; + } + } + + if (result) { + luaTrace(L, "scriptExecute", "Success."); + } else { + luaTrace(L, "scriptExecute", "Failed!"); + } + + return 0; +} + + int32_t apiSingeGetPauseFlag(lua_State *L) { /* * This function returns _global.pauseState's value to the lua script. @@ -2664,12 +2722,93 @@ int32_t apiSingeGetScriptPath(lua_State *L) { // sGameDirectory = singeGetScriptPath() // - luaTrace(L, "singeSetScriptPath", "%s", _conf.scriptFile); - lua_pushstring(L, _conf.scriptFile); + luaTrace(L, "singeSetScriptPath", "%s", _global.conf.scriptFile); + lua_pushstring(L, _global.conf.scriptFile); return 1; } +ConfigT *buildConfFromTable(lua_State *L) { + const char *confKey = NULL; + const char *valueString = NULL; + char *sindenString = NULL; + bool valueBoolean = false; + int64_t valueNumber = 0; + ConfigT *c = NULL; + + // Start with current config. + c = (ConfigT *)calloc(1, sizeof(ConfigT)); + memcpy(c, &_global.conf, sizeof(ConfigT)); + if (_global.conf.scriptFile) c->scriptFile = strdup(_global.conf.scriptFile); + if (_global.conf.videoFile) c->videoFile = strdup(_global.conf.videoFile); + if (_global.conf.dataDir) c->dataDir = strdup(_global.conf.dataDir); + + // Update with data in the table on the top of the Lua stack. + lua_pushnil(L); + while (lua_next(L, 1)) { + // Get key + confKey = lua_tostring(L, 2); + + // Get value + switch (lua_type(L, 3)) { + case LUA_TSTRING: + valueString = lua_tostring(L, 3); + valueBoolean = false; + valueNumber = 0; + break; + + case LUA_TBOOLEAN: + valueString = NULL; + valueBoolean = lua_toboolean(L, 3); + valueNumber = 0; + break; + + case LUA_TNUMBER: + valueString = NULL; + valueBoolean = false; + valueNumber = lua_tonumber(L, 3); + break; + + default: + valueString = NULL; + valueBoolean = false; + valueNumber = 0; + break; + } + + // Update config with new data + if (strcmp(confKey, "SCRIPT") == 0) { + if (c->scriptFile) free (c->scriptFile); + c->scriptFile = strdup(valueString); + } else if (strcmp(confKey, "VIDEO") == 0) { + if (c->videoFile) free(c->videoFile); + c->videoFile = strdup(valueString); + } else if (strcmp(confKey, "DATA") == 0) { + if (c->dataDir) free(c->dataDir); + c->dataDir = strdup(valueString); + } else if (strcmp(confKey, "STRETCH") == 0) { + c->stretchVideo = valueBoolean; + } else if (strcmp(confKey, "NO_MOUSE") == 0) { + c->noMouse = valueBoolean; + } else if (strcmp(confKey, "RESOLUTION_X") == 0) { + c->xResolution = valueNumber; + } else if (strcmp(confKey, "RESOLUTION_Y") == 0) { + c->yResolution = valueNumber; + } else if (strcmp(confKey, "SINDEN_GUN") == 0) { + if (sindenString) free(sindenString); + sindenString = strdup(valueString); + if (!parseSindenString(&sindenString, c)) c->sindenArgc = 0; + free(sindenString); + } + + // Clean up for next pair + lua_pop(L, 1); + } + + return c; +} + + void callLua(const char *func, const char *sig, ...) { va_list vl; bool done = false; @@ -3136,7 +3275,7 @@ void putPixel(int32_t x, int32_t y) { } -void singe(SDL_Window *window, SDL_Renderer *renderer) { +void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { int32_t x = 0; int32_t y = 0; int32_t xr = 0; @@ -3167,6 +3306,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { // Set up globals memset(&_global, 0, sizeof(GlobalT)); + memcpy(&_global.conf, conf, sizeof(ConfigT)); _global.colorForeground.r = 255; _global.colorForeground.g = 255; _global.colorForeground.b = 255; @@ -3227,15 +3367,15 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { if (luaL_loadbuffer(_global.luaContext, (char *)controls_cfg, controls_cfg_len, "Input Mappings") || lua_pcall(_global.luaContext, 0, 0, 0)) utilDie("%s", lua_tostring(_global.luaContext, -1)); if (utilFileExists("controls.cfg") && luaL_dofile(_global.luaContext, "controls.cfg")) utilDie("%s", lua_tostring(_global.luaContext, -1)); // Load mappings in main data dir - temp = utilCreateString("%s..%ccontrols.cfg", _conf.dataDir, utilGetPathSeparator()); + temp = utilCreateString("%s..%ccontrols.cfg", _global.conf.dataDir, utilGetPathSeparator()); if (utilFileExists(temp) && luaL_dofile(_global.luaContext, temp)) utilDie("%s", lua_tostring(_global.luaContext, -1)); free(temp); // Load mappings in game data dir - temp = utilCreateString("%scontrols.cfg", _conf.dataDir); + temp = utilCreateString("%scontrols.cfg", _global.conf.dataDir); if (utilFileExists(temp) && luaL_dofile(_global.luaContext, temp)) utilDie("%s", lua_tostring(_global.luaContext, -1)); free(temp); // Load mappings in game script dir - temp = strdup(_conf.scriptFile); + temp = strdup(_global.conf.scriptFile); temp2 = utilStrndup(temp, strlen(temp) - strlen(utilGetLastPathComponent(temp))); free(temp); temp = utilCreateString("%scontrols.cfg", temp2); @@ -3292,7 +3432,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { lua_close(_global.luaContext); // Show splash screens - if (!_conf.noLogos) { + if (!_global.conf.noLogos) { doLogos(); } @@ -3358,6 +3498,9 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { lua_register(_global.luaContext, "overlayPrint", apiOverlayPrint); lua_register(_global.luaContext, "overlaySetResolution", apiOverlaySetResolution); + lua_register(_global.luaContext, "scriptExecute", apiScriptExecute); + lua_register(_global.luaContext, "scriptPush", apiScriptPush); + lua_register(_global.luaContext, "singeGetPauseFlag", apiSingeGetPauseFlag); lua_register(_global.luaContext, "singeSetPauseFlag", apiSingeSetPauseFlag); lua_register(_global.luaContext, "singeEnablePauseKey", apiSingeEnablePauseKey); @@ -3406,15 +3549,15 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { // Open main video file doIndexDisplay(-1); videoSetIndexCallback(doIndexDisplay); - if (_conf.isFrameFile) { - _global.frameFileHandle = frameFileLoad(_conf.videoFile, _conf.dataDir, (bool)_conf.stretchVideo, _global.renderer, _conf.showCalculated); - if (_global.frameFileHandle < 0) utilDie("Unable to load framefile: %s", _conf.videoFile); + if (_global.conf.isFrameFile) { + _global.frameFileHandle = frameFileLoad(_global.conf.videoFile, _global.conf.dataDir, (bool)_global.conf.stretchVideo, _global.renderer, _global.conf.showCalculated); + if (_global.frameFileHandle < 0) utilDie("Unable to load framefile: %s", _global.conf.videoFile); frameFileSeek(_global.frameFileHandle, 0, &_global.videoHandle, &thisFrame); // Fills in _global.videoHandle } else { - _global.videoHandle = videoLoad(_conf.videoFile, _conf.dataDir, (bool)_conf.stretchVideo, _global.renderer); + _global.videoHandle = videoLoad(_global.conf.videoFile, _global.conf.dataDir, (bool)_global.conf.stretchVideo, _global.renderer); } - if (_global.videoHandle < 0) utilDie("Unable to load video file: %s", _conf.videoFile); - videoSetVolume(_global.videoHandle, _conf.volumeVldp, _conf.volumeVldp); + if (_global.videoHandle < 0) utilDie("Unable to load video file: %s", _global.conf.videoFile); + videoSetVolume(_global.videoHandle, _global.conf.volumeVldp, _global.conf.volumeVldp); videoSetIndexCallback(NULL); doIndexDisplay(-2); @@ -3430,15 +3573,15 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { sindenBlack.x = -1; // Overscan compensation - if (_conf.scaleFactor < 100) { - windowTarget.w = videoGetWidth(_global.videoHandle) * _conf.scaleFactor / 100; - windowTarget.h = videoGetHeight(_global.videoHandle) * _conf.scaleFactor / 100; + if (_global.conf.scaleFactor < 100) { + windowTarget.w = videoGetWidth(_global.videoHandle) * _global.conf.scaleFactor / 100; + windowTarget.h = videoGetHeight(_global.videoHandle) * _global.conf.scaleFactor / 100; windowTarget.x = (videoGetWidth(_global.videoHandle) - windowTarget.w) / 2; windowTarget.y = (videoGetHeight(_global.videoHandle) - windowTarget.h) / 2; } // Sinden Light Gun Border Setup - if (_conf.sindenArgc > 0) { + if (_global.conf.sindenArgc > 0) { //***TODO*** ADD MOUSE SCALING TO COMPENSATE FOR BORDER sindenWhiteColor.r = 255; sindenWhiteColor.g = 255; @@ -3449,41 +3592,41 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { sindenBlackColor.b = 0; sindenBlackColor.a = 255; // Ok, this thing can have a mess of different arguments: - switch(_conf.sindenArgc) { + switch(_global.conf.sindenArgc) { // WW - Just the width of the white border case SINDEN_WHITE: - sindenWhite.x = _conf.sindenArgv[0]; + sindenWhite.x = _global.conf.sindenArgv[0]; break; // WW WB - Width of white border and then black border case SINDEN_WHITE_BLACK: - sindenWhite.x = _conf.sindenArgv[0]; - sindenBlack.x = _conf.sindenArgv[1]; + sindenWhite.x = _global.conf.sindenArgv[0]; + sindenBlack.x = _global.conf.sindenArgv[1]; break; // RW GW BW WW - Custom color "white" border and width case SINDEN_CUSTOM_WHITE: - sindenWhiteColor.r = _conf.sindenArgv[0]; - sindenWhiteColor.g = _conf.sindenArgv[1]; - sindenWhiteColor.b = _conf.sindenArgv[2]; - sindenWhite.x = _conf.sindenArgv[3]; + sindenWhiteColor.r = _global.conf.sindenArgv[0]; + sindenWhiteColor.g = _global.conf.sindenArgv[1]; + sindenWhiteColor.b = _global.conf.sindenArgv[2]; + sindenWhite.x = _global.conf.sindenArgv[3]; break; // RW GW BW WW WB - Custom color "white" border and width then width of black border case SINDEN_CUSTOM_WHITE_BLACK: - sindenWhiteColor.r = _conf.sindenArgv[0]; - sindenWhiteColor.g = _conf.sindenArgv[1]; - sindenWhiteColor.b = _conf.sindenArgv[2]; - sindenWhite.x = _conf.sindenArgv[3]; - sindenBlack.x = _conf.sindenArgv[4]; + sindenWhiteColor.r = _global.conf.sindenArgv[0]; + sindenWhiteColor.g = _global.conf.sindenArgv[1]; + sindenWhiteColor.b = _global.conf.sindenArgv[2]; + sindenWhite.x = _global.conf.sindenArgv[3]; + sindenBlack.x = _global.conf.sindenArgv[4]; break; // RW GW BW WW RB GB BB WB - Custom color "white" border and width then custom color "black" border and width case SINDEN_CUSTOM_WHITE_CUSTOM_BLACK: - sindenWhiteColor.r = _conf.sindenArgv[0]; - sindenWhiteColor.g = _conf.sindenArgv[1]; - sindenWhiteColor.b = _conf.sindenArgv[2]; - sindenWhite.x = _conf.sindenArgv[3]; - sindenBlackColor.r = _conf.sindenArgv[4]; - sindenBlackColor.g = _conf.sindenArgv[5]; - sindenBlackColor.b = _conf.sindenArgv[6]; - sindenBlack.x = _conf.sindenArgv[7]; + sindenWhiteColor.r = _global.conf.sindenArgv[0]; + sindenWhiteColor.g = _global.conf.sindenArgv[1]; + sindenWhiteColor.b = _global.conf.sindenArgv[2]; + sindenWhite.x = _global.conf.sindenArgv[3]; + sindenBlackColor.r = _global.conf.sindenArgv[4]; + sindenBlackColor.g = _global.conf.sindenArgv[5]; + sindenBlackColor.b = _global.conf.sindenArgv[6]; + sindenBlack.x = _global.conf.sindenArgv[7]; break; } if (sindenWhite.x >= 0) { @@ -3521,7 +3664,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { _global.mouseCount = MAX_MICE; } memset(_global.mice, 0, sizeof(_global.mice)); - _global.mouseEnabled = (bool)!_conf.noMouse; + _global.mouseEnabled = (bool)!_global.conf.noMouse; for (x=0; x<_global.mouseCount; x++) { strncpy(_global.mice[x].name, ManyMouse_DeviceName((unsigned)x), sizeof(_global.mice[x].name)); _global.mice[x].name[sizeof(_global.mice[x].name) - 1] = 0; @@ -3531,9 +3674,9 @@ void singe(SDL_Window *window, SDL_Renderer *renderer) { } // Grab mouse - _global.mouseGrabbed = true; - SDL_SetWindowGrab(_global.window, SDL_TRUE); - SDL_ShowCursor(SDL_DISABLE); +// _global.mouseGrabbed = true; +// SDL_SetWindowGrab(_global.window, SDL_TRUE); +// SDL_ShowCursor(SDL_DISABLE); // Clear axis cache for (x=0; x