/* SSNES - A frontend for libretro. * SSNES Salamander - A frontend for managing some pre-launch tasks. * Copyright (C) 2010-2012 - Hans-Kristian Arntzen * Copyright (C) 2011-2012 - Daniel De Matteis * * SSNES 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 Found- * ation, either version 3 of the License, or (at your option) any later version. * * SSNES 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 SSNES. * If not, see . */ #include #include #if defined(__CELLOS_LV2__) #include #include #include #include #include #include #include #include #elif defined(_XBOX) #include #endif #include "../../compat/strl.h" #include "../../conf/config_file.h" #if defined(_XBOX) #include "../../msvc/msvc_compat.h" #elif defined(__CELLOS_LV2__) #define NP_POOL_SIZE (128*1024) #endif #ifndef PATH_MAX #define PATH_MAX 512 #endif #define MAX_PATH_LENGTH 1024 #ifdef HAVE_LOGGER #include "logger.h" #define SSNES_LOG(...) logger_send("SSNES Salamander: " __VA_ARGS__); #define SSNES_ERR(...) logger_send("SSNES Salamander [ERROR] :: " __VA_ARGS__); #define SSNES_WARN(...) logger_send("SSNES Salamander [WARN] :: " __VA_ARGS__); #else #define SSNES_LOG(...) do { \ fprintf(stderr, "SSNES Salamander: " __VA_ARGS__); \ fflush(stderr); \ } while (0) #define SSNES_ERR(...) do { \ fprintf(stderr, "SSNES Salamander [ERROR] :: " __VA_ARGS__); \ fflush(stderr); \ } while (0) #define SSNES_WARN(...) do { \ fprintf(stderr, "SSNES Salamander [WARN] :: " __VA_ARGS__); \ fflush(stderr); \ } while (0) #endif #if defined(__CELLOS_LV2__) static uint8_t np_pool[NP_POOL_SIZE]; char contentInfoPath[MAX_PATH_LENGTH]; char usrDirPath[MAX_PATH_LENGTH]; SYS_PROCESS_PARAM(1001, 0x100000) #elif defined(_XBOX) DWORD volume_device_type; #endif char LIBSNES_DIR_PATH[MAX_PATH_LENGTH]; char SYS_CONFIG_FILE[MAX_PATH_LENGTH]; char libretro_path[MAX_PATH_LENGTH]; static bool path_file_exists(const char *path) { FILE *dummy = fopen(path, "rb"); if (dummy) { fclose(dummy); return true; } return false; } static void dir_list_free(char **dir_list) { if (!dir_list) return; char **orig = dir_list; while (*dir_list) free(*dir_list++); free(orig); } #ifdef _XBOX static void fill_pathname_base(char *out_dir, const char *in_path, size_t size) { const char *ptr = strrchr(in_path, '/'); if (!ptr) ptr = strrchr(in_path, '\\'); if (ptr) ptr++; else ptr = in_path; strlcpy(out_dir, ptr, size); } #endif static char **dir_list_new(const char *dir, const char *ext) { size_t cur_ptr = 0; size_t cur_size = 32; char **dir_list = NULL; #if defined(_XBOX) WIN32_FIND_DATA ffd; HANDLE hFind = INVALID_HANDLE_VALUE; char path_buf[PATH_MAX]; if (strlcpy(path_buf, dir, sizeof(path_buf)) >= sizeof(path_buf)) goto error; if (strlcat(path_buf, "*", sizeof(path_buf)) >= sizeof(path_buf)) goto error; if (ext) { if (strlcat(path_buf, ext, sizeof(path_buf)) >= sizeof(path_buf)) goto error; } hFind = FindFirstFile(path_buf, &ffd); if (hFind == INVALID_HANDLE_VALUE) goto error; dir_list = (char**)calloc(cur_size, sizeof(char*)); if (!dir_list) goto error; do { // Not a perfect search of course, but hopefully good enough in practice. if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; if (ext && !strstr(ffd.cFileName, ext)) continue; dir_list[cur_ptr] = (char*)malloc(PATH_MAX); if (!dir_list[cur_ptr]) goto error; strlcpy(dir_list[cur_ptr], dir, PATH_MAX); strlcat(dir_list[cur_ptr], ffd.cFileName, PATH_MAX); cur_ptr++; if (cur_ptr + 1 == cur_size) // Need to reserve for NULL. { cur_size *= 2; dir_list = (char**)realloc(dir_list, cur_size * sizeof(char*)); if (!dir_list) goto error; // Make sure it's all NULL'd out since we cannot rely on realloc to do this. memset(dir_list + cur_ptr, 0, (cur_size - cur_ptr) * sizeof(char*)); } }while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); return dir_list; error: SSNES_ERR("Failed to open directory: \"%s\"\n", dir); if (hFind != INVALID_HANDLE_VALUE) FindClose(hFind); #elif defined(__CELLOS_LV2__) DIR *directory = NULL; const struct dirent *entry = NULL; directory = opendir(dir); if (!directory) goto error; dir_list = (char**)calloc(cur_size, sizeof(char*)); if (!dir_list) goto error; while ((entry = readdir(directory))) { // Not a perfect search of course, but hopefully good enough in practice. if (ext && !strstr(entry->d_name, ext)) continue; dir_list[cur_ptr] = (char*)malloc(PATH_MAX); if (!dir_list[cur_ptr]) goto error; strlcpy(dir_list[cur_ptr], dir, PATH_MAX); strlcat(dir_list[cur_ptr], "/", PATH_MAX); strlcat(dir_list[cur_ptr], entry->d_name, PATH_MAX); cur_ptr++; if (cur_ptr + 1 == cur_size) // Need to reserve for NULL. { cur_size *= 2; dir_list = (char**)realloc(dir_list, cur_size * sizeof(char*)); if (!dir_list) goto error; // Make sure it's all NULL'd out since we cannot rely on realloc to do this. memset(dir_list + cur_ptr, 0, (cur_size - cur_ptr) * sizeof(char*)); } } closedir(directory); return dir_list; error: SSNES_ERR("Failed to open directory: \"%s\"\n", dir); if (directory) closedir(directory); #endif dir_list_free(dir_list); return NULL; } static void find_and_set_first_file(void) { //Last fallback - we'll need to start the first executable file // we can find in the SSNES cores directory #if defined(_XBOX) char ** dir_list = dir_list_new("game:\\", ".xex"); #elif defined(__CELLOS_LV2__) char ** dir_list = dir_list_new(LIBSNES_DIR_PATH, ".SELF"); #endif if (!dir_list) { SSNES_ERR("Failed last fallback - SSNES Salamander will exit.\n"); return; } char * first_executable = dir_list[0]; if(first_executable) { #ifdef _XBOX //Check if it's SSNES Salamander itself - if so, first_executable needs to //be overridden char fname_tmp[MAX_PATH_LENGTH]; fill_pathname_base(fname_tmp, first_executable, sizeof(fname_tmp)); if(strcmp(fname_tmp, "SSNES-Salamander.xex") == 0) { SSNES_WARN("First entry is SSNES Salamander itself, increment entry by one and check if it exists.\n"); first_executable = dir_list[1]; fill_pathname_base(fname_tmp, first_executable, sizeof(fname_tmp)); if(!first_executable) { SSNES_WARN("There is no second entry - no choice but to boot SSNES Salamander\n"); first_executable = dir_list[0]; fill_pathname_base(fname_tmp, first_executable, sizeof(fname_tmp)); } } snprintf(first_executable, sizeof(first_executable), "game:\\%s", fname_tmp); #endif SSNES_LOG("Start first entry in libretro cores dir: [%s].\n", first_executable); strlcpy(libretro_path, first_executable, sizeof(libretro_path)); } else { SSNES_ERR("Failed last fallback - SSNES Salamander will exit.\n"); } dir_list_free(dir_list); } static void init_settings(void) { char tmp_str[MAX_PATH_LENGTH]; bool config_file_exists; if(!path_file_exists(SYS_CONFIG_FILE)) { FILE * f; config_file_exists = false; SSNES_ERR("Config file \"%s\" doesn't exist. Creating...\n", SYS_CONFIG_FILE); f = fopen(SYS_CONFIG_FILE, "w"); fclose(f); } else config_file_exists = true; //try to find CORE executable char core_executable[1024]; #if defined(_XBOX) snprintf(core_executable, sizeof(core_executable), "game:\\CORE.xex"); #elif defined(__CELLOS_LV2__) snprintf(core_executable, sizeof(core_executable), "%s/CORE.SELF", LIBSNES_DIR_PATH); #endif if(path_file_exists(core_executable)) { //Start CORE executable snprintf(libretro_path, sizeof(libretro_path), core_executable); SSNES_LOG("Start [%s].\n", libretro_path); } else { if(config_file_exists) { config_file_t * conf = config_file_new(SYS_CONFIG_FILE); config_get_array(conf, "libretro_path", tmp_str, sizeof(tmp_str)); snprintf(libretro_path, sizeof(libretro_path), tmp_str); } if(!config_file_exists || !strcmp(libretro_path, "")) find_and_set_first_file(); else { SSNES_LOG("Start [%s] found in ssnes.cfg.\n", libretro_path); } } } static void get_environment_settings (void) { #if defined(_XBOX) //for devkits only, we will need to mount all partitions for retail //in a different way //DmMapDevkitDrive(); int result_filecache = XSetFileCacheSize(0x100000); if(result_filecache != TRUE) { SSNES_ERR("Couldn't change number of bytes reserved for file system cache.\n"); } unsigned long result = XMountUtilityDriveEx(XMOUNTUTILITYDRIVE_FORMAT0,8192, 0); if(result != ERROR_SUCCESS) { SSNES_ERR("Couldn't mount/format utility drive.\n"); } // detect install environment unsigned long license_mask; if (XContentGetLicenseMask(&license_mask, NULL) != ERROR_SUCCESS) { SSNES_LOG("SSNES was launched as a standalone DVD, or using DVD emulation, or from the development area of the HDD.\n"); } else { XContentQueryVolumeDeviceType("GAME",&volume_device_type, NULL); switch(volume_device_type) { case XCONTENTDEVICETYPE_HDD: SSNES_LOG("SSNES was launched from a content package on HDD.\n"); break; case XCONTENTDEVICETYPE_MU: SSNES_LOG("SSNES was launched from a content package on USB or Memory Unit.\n"); break; case XCONTENTDEVICETYPE_ODD: SSNES_LOG("SSNES was launched from a content package on Optical Disc Drive.\n"); break; default: SSNES_LOG("SSNES was launched from a content package on an unknown device type.\n"); break; } } strlcpy(SYS_CONFIG_FILE, "game:\\ssnes.cfg", sizeof(SYS_CONFIG_FILE)); #elif defined(__CELLOS_LV2__) unsigned int get_type; unsigned int get_attributes; CellGameContentSize size; char dirName[CELL_GAME_DIRNAME_SIZE]; memset(&size, 0x00, sizeof(CellGameContentSize)); int ret = cellGameBootCheck(&get_type, &get_attributes, &size, dirName); if(ret < 0) { SSNES_ERR("cellGameBootCheck() Error: 0x%x.\n", ret); } else { SSNES_LOG("cellGameBootCheck() OK.\n"); SSNES_LOG("Directory name: [%s].\n", dirName); SSNES_LOG(" HDD Free Size (in KB) = [%d] Size (in KB) = [%d] System Size (in KB) = [%d].\n", size.hddFreeSizeKB, size.sizeKB, size.sysSizeKB); switch(get_type) { case CELL_GAME_GAMETYPE_DISC: SSNES_LOG("SSNES was launched on Optical Disc Drive.\n"); break; case CELL_GAME_GAMETYPE_HDD: SSNES_LOG("SSNES was launched on HDD.\n"); break; } if((get_attributes & CELL_GAME_ATTRIBUTE_APP_HOME) == CELL_GAME_ATTRIBUTE_APP_HOME) SSNES_LOG("SSNES was launched from host machine (APP_HOME).\n"); ret = cellGameContentPermit(contentInfoPath, usrDirPath); if(ret < 0) { SSNES_ERR("cellGameContentPermit() Error: 0x%x\n", ret); } else { SSNES_LOG("cellGameContentPermit() OK.\n"); SSNES_LOG("contentInfoPath : [%s].\n", contentInfoPath); SSNES_LOG("usrDirPath : [%s].\n", usrDirPath); } /* now we fill in all the variables */ snprintf(SYS_CONFIG_FILE, sizeof(SYS_CONFIG_FILE), "%s/ssnes.cfg", usrDirPath); snprintf(LIBSNES_DIR_PATH, sizeof(LIBSNES_DIR_PATH), "%s/cores", usrDirPath); } #endif } int main(int argc, char *argv[]) { int ret; #if defined(_XBOX) XINPUT_STATE state; get_environment_settings(); XInputGetState(0, &state); if(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) { //override path, boot first XEX in cores directory SSNES_LOG("Fallback - Will boot first XEX in SSNES directory.\n"); find_and_set_first_file(); } else { //normal XEX loading path init_settings(); } XLaunchNewImage(libretro_path, NULL); SSNES_LOG("Launch libretro core: [%s] (return code: %x]).\n", libretro_path, ret); #elif defined(__CELLOS_LV2__) CellPadData pad_data; char spawn_data[256], spawn_data_size[16]; SceNpDrmKey * k_licensee = NULL; cellSysmoduleLoadModule(CELL_SYSMODULE_IO); cellSysmoduleLoadModule(CELL_SYSMODULE_FS); cellSysmoduleLoadModule(CELL_SYSMODULE_SYSUTIL_GAME); cellSysmoduleLoadModule(CELL_SYSMODULE_NET); cellSysmoduleLoadModule(CELL_SYSMODULE_SYSUTIL_NP); sys_net_initialize_network(); #ifdef HAVE_LOGGER logger_init(); #endif sceNpInit(NP_POOL_SIZE, np_pool); get_environment_settings(); cellPadInit(7); cellPadGetData(0, &pad_data); if(pad_data.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_TRIANGLE) { //override path, boot first SELF in cores directory SSNES_LOG("Fallback - Will boot first SELF in SSNES cores/ directory.\n"); find_and_set_first_file(); } else { //normal SELF loading path init_settings(); } cellPadEnd(); #ifdef HAVE_LOGGER logger_shutdown(); #endif for(unsigned int i = 0; i < sizeof(spawn_data); ++i) spawn_data[i] = i & 0xff; sprintf(spawn_data_size, "%d", 256); const char * const spawn_argv[] = { spawn_data_size, "test argv for", "sceNpDrmProcessExitSpawn2()", NULL }; ret = sceNpDrmProcessExitSpawn2(k_licensee, libretro_path, (const char** const)spawn_argv, NULL, (sys_addr_t)spawn_data, 256, 1000, SYS_PROCESS_PRIMARY_STACK_SIZE_1M); SSNES_LOG("Launch libretro core: [%s] (return code: %x]).\n", libretro_path, ret); if(ret < 0) { SSNES_LOG("SELF file is not of NPDRM type, trying another approach to boot it...\n"); sys_game_process_exitspawn2(libretro_path, NULL, NULL, NULL, 0, 1000, SYS_PROCESS_PRIMARY_STACK_SIZE_1M); } sceNpTerm(); sys_net_finalize_network(); cellSysmoduleUnloadModule(CELL_SYSMODULE_SYSUTIL_NP); cellSysmoduleUnloadModule(CELL_SYSMODULE_NET); cellSysmoduleUnloadModule(CELL_SYSMODULE_SYSUTIL_GAME); cellSysmoduleLoadModule(CELL_SYSMODULE_FS); cellSysmoduleLoadModule(CELL_SYSMODULE_IO); #endif return 1; }