// Calls callback() for each file and folder found in folder and its subfolders. context is passed to callback() and can be NULL. // callback() must return: // * 0 to continue the scan // * 1 to abort the scan // * 2 to continue the scan but not recurse through the current directory // Returns non-zero if callback() asked to abort. int file_each(const char *folder, int (*callback)(const char *path, void *context), void *context) { char subfolder_or_file[FILENAME_MAX]; DIR *dp; struct dirent *ep; struct stat statbuf; #define MAX_FILES_IN_DIR 100 #define MEAN_FILE_NAME_SIZE 50 #define FILENAMES_BUF_SIZE (MEAN_FILE_NAME_SIZE * MAX_FILES_IN_DIR) char *filenames; char *filenames_ptrs[MAX_FILES_IN_DIR]; if (!(filenames = malloc(FILENAMES_BUF_SIZE))) return 0; if (!(dp = opendir(folder))) { free(filenames); return 0; } // list the directory to sort its content unsigned i; unsigned filenames_used_bytes = 0; char *ptr; for (i = 0, ptr = filenames; (ep = readdir(dp)) && i < MAX_FILES_IN_DIR && ptr < filenames + FILENAMES_BUF_SIZE; i++) { if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, "..")) { i--; continue; } size_t dname_len = strlen(ep->d_name); if (FILENAMES_BUF_SIZE - filenames_used_bytes < dname_len + 1) break; strcpy(ptr, ep->d_name); filenames_ptrs[i] = ptr; ptr += dname_len + 1; } unsigned filenum = i; qsort(filenames_ptrs, filenum, sizeof(char*), scmp); for (i = 0; i < filenum; i++) { strcpy(subfolder_or_file, folder); if (subfolder_or_file[strlen(subfolder_or_file) - 1] != '/') strcat(subfolder_or_file, "/"); strcat(subfolder_or_file, filenames_ptrs[i]); if (stat(subfolder_or_file, &statbuf) == -1) continue; int next_action = callback(subfolder_or_file, context); if (next_action == 1) { closedir(dp); free(filenames); return 1; } if (S_ISDIR(statbuf.st_mode) && next_action != 2) { if (file_each(subfolder_or_file, callback, context)) { closedir(dp); free(filenames); return 1; } } } closedir(dp); free(filenames); return 0; }
// Run a program. Returns 0xDEAD if can't run it or 0xBEEF if the error dialog should be skipped. Else returns the program return code. // If resident_ptr isn't NULL, the program's memory block isn't freed and is stored in resident_ptr. It may be freed later with ld_free(). // Resident program shouldn't use argv after returning. // argsn/args don't include the program path. args doesn't need to be NULL terminated. Can be 0/NULL. int ld_exec_with_args(const char *path, int argsn, char *args[], void **resident_ptr) { char prgm_path[FILENAME_MAX]; char doc_path[FILENAME_MAX]; // non const unsigned i; char **argv = NULL; char **argvptr; int argc; int ret; BOOL isassoc = FALSE; strcpy(doc_path, path); strcpy(prgm_path, path); // may deffer if using file association // File association char extbuf[FILENAME_MAX]; strcpy(extbuf, prgm_path); char *ext = strrchr(extbuf, '.'); if (!ext || ext == extbuf) { puts("ld_exec: can't find file extension"); return 0xDEAD; // shouldn't happen, all files have a .tns extension } *ext = '\0'; // keep the extension before .tns ext = strrchr(extbuf, '.'); unsigned pathlen = strlen(extbuf); // without '.' #define MAX_EXT_LEN 8 if (ext && extbuf + pathlen - ext <= (MAX_EXT_LEN+1) && extbuf + pathlen - ext > 1) { // looks like an extension cfg_open(); char ext_key[4 + MAX_EXT_LEN + 1]; // ext.extension strcpy(ext_key, "ext"); strcat(ext_key, ext); char *prgm_name_noext = cfg_get(ext_key); if (prgm_name_noext) { char prgm_name[FILENAME_MAX + 4]; strcpy(prgm_name, prgm_name_noext); strcat(prgm_name, ".tns"); struct assoc_file_each_cb_ctx context = {prgm_name, prgm_path, &isassoc}; file_each("/", assoc_file_each_cb, &context); } cfg_close(); } ld_bin_format = LD_ERROR_BIN; uint32_t signature; NUC_FILE *prgm = nuc_fopen(prgm_path, "rb"); if(nuc_fread(&signature, sizeof(signature), 1, prgm) != 1) { // empty file? nuc_fclose(prgm); return 0xDEAD; } nuc_fseek(prgm, 0, SEEK_SET); void *base = 0; int (*entry)(int argc, char *argv[]); switch(signature) { case 0x00475250: //"PRG\0" if((ret = ndless_load(prgm_path, prgm, &base, &entry)) == 0) { nuc_fclose(prgm); ld_bin_format = LD_PRG_BIN; break; } nuc_fclose(prgm); return ret == 1 ? 0xDEAD : 0xBEEF; case 0x544c4662: //"bFLT" if(bflt_load(prgm, &base, &entry) == 0) { nuc_fclose(prgm); ld_bin_format = LD_BFLT_BIN; break; } nuc_fclose(prgm); return 0xDEAD; case 0x6e68655a: //"Zehn" if((ret = zehn_load(prgm, &base, &entry)) == 0) { nuc_fclose(prgm); ld_bin_format = LD_ZEHN_BIN; break; } if(base && base != emu_debug_alloc_ptr) free(base); nuc_fclose(prgm); return ret == 1 ? 0xDEAD : 0xBEEF; default: nuc_fclose(prgm); return 0xDEAD; } int intmask = TCT_Local_Control_Interrupts(-1); /* TODO workaround: disable the interrupts to avoid the clock on the screen */ wait_no_key_pressed(); // let the user release the Enter key, to avoid being read by the program void *savedscr = malloc(SCREEN_BYTES_SIZE); if (!savedscr) { puts("ld_exec: can't malloc savedscr"); ret = 0xDEAD; goto ld_exec_with_args_quit; } memcpy(savedscr, (void*) SCREEN_BASE_ADDRESS, SCREEN_BYTES_SIZE); argc = 1 + argsn; if (isassoc) argc++; argv = malloc((argc + 1) * sizeof(char*)); if (!argv) { puts("ld_exec: can't malloc argv"); ret = 0xDEAD; goto ld_exec_with_args_quit; } argv[0] = prgm_path; argvptr = &argv[1]; if (isassoc) { argv[1] = doc_path; argvptr++; } if (args) memcpy(argvptr, args, argsn * sizeof(char*)); argv[argc] = NULL; if (has_colors) { volatile unsigned *palette = (volatile unsigned*)0xC0000200; for (i = 0; i < 16/2; i++) *palette++ = ((i * 2 + 1) << (1 + 16)) | ((i * 2 + 1) << (6 + 16)) | ((i * 2 + 1) << (11 + 16)) | ((i * 2) << 1) | ((i * 2) << 6) | ((i * 2) << 11); // set the grayscale palette ut_disable_watchdog(); // seems to be sometimes renabled by the OS } is_current_prgm_resident = FALSE; clear_cache(); ret = entry(argc, argv); /* run the program */ if (has_colors) lcd_incolor(); // in case not restored by the program if (!plh_noscrredraw) memcpy((void*) SCREEN_BASE_ADDRESS, savedscr, SCREEN_BYTES_SIZE); ld_exec_with_args_quit: free(savedscr); wait_no_key_pressed(); // let the user release the key used to exit the program, to avoid being read by the OS TCT_Local_Control_Interrupts(intmask); if (ret != 0xDEAD && resident_ptr) { *resident_ptr = base; return ret; } if (is_current_prgm_resident) // required by the program itself return ret; if (!emu_debug_alloc_ptr) free(base); free(argv); return ret; }