unsigned _show_msgbox(const char *title, const char *msg, unsigned button_num, ...) { va_list ap; char title16[(strlen(title) + 1) * 2]; char msg16[(strlen(msg) + 1) * 2]; char undef_buf[8]; unsigned button_pressed = 0; memset(undef_buf, 0, sizeof(undef_buf)); va_start(ap, button_num); ascii2utf16(title16, title, sizeof(title16)); ascii2utf16(msg16, msg, sizeof(msg16)); *(char**)undef_buf = "DLG"; BOOL incolor = lcd_isincolor(); void *saved_screen = NULL; if (has_colors && !incolor) { if ((saved_screen = malloc(SCREEN_WIDTH * SCREEN_HEIGHT * 2))) { // The screen buffer size of the color mode is used, but before switching to it memcpy(saved_screen, SCREEN_BASE_ADDRESS, SCREEN_WIDTH * SCREEN_HEIGHT * 2); memset(SCREEN_BASE_ADDRESS, 0xFF, SCREEN_WIDTH * SCREEN_HEIGHT * 2); // clrscr. avoid displaying a grayscaled buffer in colors } lcd_incolor(); } /* required since OS 2.1 for OS key scan */ int orig_mask = TCT_Local_Control_Interrupts(0); if (button_num == 2 || button_num == 3) { char *button1 = va_arg(ap, char*); char *button2 = va_arg(ap, char*); char button1_16[(strlen(button1) + 1) * 2]; char button2_16[(strlen(button2) + 1) * 2]; ascii2utf16(button1_16, button1, sizeof(button1_16)); ascii2utf16(button2_16, button2, sizeof(button2_16)); if (button_num == 2) { button_pressed = _show_msgbox_2b(0, title16, msg16, button1_16, 1, button2_16, 2, undef_buf); } else { char *button3 = va_arg(ap, char*); char button3_16[(strlen(button3) + 1) * 2]; ascii2utf16(button3_16, button3, sizeof(button3_16)); button_pressed = _show_msgbox_3b(0, title16, msg16, button1_16, 1, button2_16, 2, button3_16, 3, undef_buf); } } else {
// 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; }