示例#1
0
static int ndless_load(const char *docpath, NUC_FILE *docfile, void **base, int (**entry_address_ptr)(int, char*[]))
{
	struct nuc_stat docstat;
	if (nuc_stat(docpath, &docstat)) {
		puts("ndless_load: can't open doc");
		return 1;
	}

	void *docptr;
	if(emu_debug_alloc_ptr)
	{
		if(emu_debug_alloc_size < docstat.st_size)
		{
			puts("ndless_load: emu_debug_alloc_size too small!");
			docptr = malloc(docstat.st_size);	
		}
		else
			docptr = emu_debug_alloc_ptr;
	}
	else
		docptr = malloc(docstat.st_size);

	if (!docptr) {
		puts("ndless_load: can't malloc");
		return 1;
	}
	if (!nuc_fread(docptr, docstat.st_size, 1, docfile)) {
		puts("ndless_load: can't read doc");
		if (!emu_debug_alloc_ptr)
			free(docptr);
		return 1;
	}

	uint32_t *ptr32 = docptr, *ptr32_end = ptr32 + ((docstat.st_size / 4) < 512 ? (docstat.st_size / 4) : 512);
	while(ptr32 < ptr32_end)
	{
		// Found an embedded Zehn file
		if(*ptr32 == 0x6e68655a && *(ptr32 + 1) == 1)
		{
			nuc_fseek(docfile, (uint8_t*)(ptr32) - (uint8_t*)(docptr), SEEK_SET);
			int ret = zehn_load(docfile, base, entry_address_ptr);
			switch(ret)
			{
			case 0: // Execute as Zehn
			case 2: // Valid Zehn, but don't execute
				if(!emu_debug_alloc_ptr)
					free(docptr);
				return ret;
			case 1: // Invalid Zehn
				break;
			}
		}
		ptr32++;
	}

	*base = docptr;
	*entry_address_ptr = (int (*)(int argc, char *argv[]))(docptr + sizeof(PRGMSIG));

	return 0;
}
示例#2
0
extern "C" int zehn_load(NUC_FILE *file, void **mem_ptr, int (**entry)(int,char*[]), bool *supports_hww)
{
	Zehn_header header;

	// The Zehn file may not begin at the file start
	size_t file_start = nuc_ftell(file);

	if(nuc_fread(&header, sizeof(header), 1, file) != 1)
		return 1;

	if(header.signature != ZEHN_SIGNATURE || header.version != ZEHN_VERSION || header.file_size > header.alloc_size)
	{
		puts("[Zehn] This Zehn file is not supported!");
		return 1;
	}

	Storage<Zehn_reloc> relocs(header.reloc_count);
	Storage<Zehn_flag> flags(header.flag_count);
	Storage<uint8_t> extra_data(header.extra_size);

	if(nuc_fread(reinterpret_cast<void*>(relocs.data), sizeof(Zehn_reloc), header.reloc_count, file) != header.reloc_count
		|| nuc_fread(reinterpret_cast<void*>(flags.data), sizeof(Zehn_flag), header.flag_count, file) != header.flag_count
		|| nuc_fread(reinterpret_cast<void*>(extra_data.data), 1, header.extra_size, file) != header.extra_size)
	{
		puts("[Zehn] File read failed!");
		return 1;
	}

	size_t remaining_mem = header.alloc_size - nuc_ftell(file) + file_start, remaining_file = header.file_size - nuc_ftell(file) + file_start;

	if(emu_debug_alloc_ptr)
	{
		if(emu_debug_alloc_size < remaining_mem)
		{
			puts("[Zehn] emu_debug_alloc_size too small!");
			*mem_ptr = malloc(remaining_mem);
		}
		else
			*mem_ptr = emu_debug_alloc_ptr;
	}
	else
		*mem_ptr = malloc(remaining_mem);

	uint8_t *base = reinterpret_cast<uint8_t*>(*mem_ptr);
	if(!base)
	{
		puts("[Zehn] Memory allocation failed!");
		return 1;
	}

	if(relocs.data[0].type == Zehn_reloc_type::FILE_COMPRESSED)
	{
		if(relocs.data[0].offset != static_cast<int>(Zehn_compress_type::ZLIB))
		{
			puts("[Zehn] Compression format not supported!");
			return 1;
		}

		Storage<uint8_t> compressed(remaining_file);
		if(nuc_fread(compressed.data, remaining_file, 1, file) != 1)
		{
			puts("[Zehn] File read failed!");
			return 1;
		}

		uLongf dest_len = remaining_mem;
		if(uncompress(base, &dest_len, compressed.data, remaining_file) != Z_OK)
		{
			puts("[Zehn] Decompression failed!");
			return 1;
		}

		std::fill(base + dest_len, base + remaining_mem, 0);
	}
	else
	{
		if(nuc_fread(base, remaining_file, 1, file) != 1)
		{
			puts("[Zehn] File read failed!");
			return 1;
		}
	
		// Fill rest with zeros (.bss and other NOBITS sections)
		std::fill(base + remaining_file, base + remaining_mem, 0);
	}

	const char *application_name = "(unknown)", *application_author = "(unknown)", *application_notice = "(no notice)";
	unsigned int application_version = 1, ndless_version_min = 0, ndless_version_max = UINT_MAX,
		ndless_revision_min = 0, ndless_revision_max = UINT_MAX;

	// Iterate through each flag
	for(Zehn_flag &f : flags)
	{
		const char *ptr;
		switch(f.type)
		{
		case Zehn_flag_type::EXECUTABLE_NAME:
			if(!zehn_check_string(extra_data.begin(), f, 255, &application_name))
			{
				puts("[Zehn] Invalid application name!");
				return 1;
			}

			break;
		case Zehn_flag_type::EXECUTABLE_NOTICE:
			if(zehn_check_string(extra_data.begin(), f, 1024, &ptr))
				application_notice = ptr;

			break;
		case Zehn_flag_type::EXECUTABLE_AUTHOR:
			if(zehn_check_string(extra_data.begin(), f, 128, &ptr))
				application_author = ptr;

			break;
		case Zehn_flag_type::EXECUTABLE_VERSION:
			application_version = f.data;
			break;
		case Zehn_flag_type::NDLESS_VERSION_MIN:
			ndless_version_min = f.data;
			break;
		case Zehn_flag_type::NDLESS_REVISION_MIN:
			ndless_revision_min = f.data;
			break;
		case Zehn_flag_type::NDLESS_VERSION_MAX:
			ndless_version_max = f.data;
			break;
		case Zehn_flag_type::NDLESS_REVISION_MAX:
			ndless_revision_max = f.data;
			break;
		case Zehn_flag_type::RUNS_ON_COLOR:
			if(f.data == false && has_colors)
			{
				msgbox("Error", "The application %s doesn't support CX and CM calculators!", application_name);
				return 2;
			}
			break;
		case Zehn_flag_type::RUNS_ON_CLICKPAD:
			if(f.data == false && !is_touchpad)
			{
				msgbox("Error", "The application %s doesn't support clickpads!", application_name);
				return 2;
			}
			break;
		case Zehn_flag_type::RUNS_ON_TOUCHPAD:
			if(f.data == false && is_touchpad)
			{
				msgbox("Error", "The application %s doesn't support touchpads!", application_name);
				return 2;
			}
			break;
		case Zehn_flag_type::RUNS_ON_32MB:
			if(f.data == false && (!has_colors || is_cm))
			{
				msgbox("Error", "The application %s requires more than 32MB of RAM!", application_name);
				return 2;
			}
			break;
                case Zehn_flag_type::RUNS_ON_HWW:
                        *supports_hww = f.data;
			break;
		default:
			break;
		}
	}

	// Show some information about the executable
	if(isKeyPressed(KEY_NSPIRE_CAT))
	{
		char info[1536];
		sprintf(info, "Name: %s Version: %u\nAuthor: %s\nNotice: %s", application_name, application_version, application_author, application_notice);
		show_msgbox("Information about the executable", info);

		return 2;
	}

	if(NDLESS_VERSION < ndless_version_min || (NDLESS_VERSION == ndless_version_min && NDLESS_REVISION < ndless_revision_min))
	{
		msgbox("Error", "The application %s requires at least ndless %d.%d.%d!", application_name, ndless_version_min / 10, ndless_version_min % 10, ndless_revision_min);
		return 2;
	}

	if(NDLESS_VERSION > ndless_version_max || (NDLESS_VERSION == ndless_version_max && NDLESS_REVISION > ndless_revision_max))
	{
		if(ndless_revision_max != UINT_MAX)
			msgbox("Error", "The application %s requires ndless %d.%d.%d or older!", application_name, ndless_version_max / 10, ndless_version_max % 10, ndless_revision_max);
		else
			msgbox("Error", "The application %s requires ndless %d.%d or older!", application_name, ndless_version_max / 10, ndless_version_max % 10);

		return 2;
	}

	// Iterate through the reloc table
	for(Zehn_reloc &r : relocs)
	{
		if(r.offset >= remaining_mem)
		{
			puts("[Zehn] Wrong reloc in Zehn file!");
			return 1;
		}

		// No alignment guaranteed!
		uint32_t *place = reinterpret_cast<uint32_t*>(base + r.offset);
		switch(r.type)
		{
		//Handled above
		case Zehn_reloc_type::FILE_COMPRESSED:
                        break;
                case Zehn_reloc_type::UNALIGNED_RELOC:
                        if(r.offset != 0)
                        {
                            printf("[Zehn] Unexpected UNALIGNED_RELOC value %lu!\n", r.offset);
                            return 1;
                        }

			break;
		case Zehn_reloc_type::ADD_BASE:
			wu32(place, ru32(place) + reinterpret_cast<uint32_t>(base));
			break;
		case Zehn_reloc_type::ADD_BASE_GOT:
		{
			uint32_t u32;
			while((u32 = ru32(place)) != 0xFFFFFFFF)
				wu32(place++, u32 + reinterpret_cast<uint32_t>(base));

			break;
		}
		case Zehn_reloc_type::SET_ZERO:
			wu32(place, 0);
			break;
		default:
			printf("[Zehn] Unsupported reloc %d!\n", static_cast<int>(r.type));
			return 1;
		}
	}

	*entry = reinterpret_cast<int (*)(int,char*[])>(base + header.entry_offset);

	return 0;
}
示例#3
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;
}