Newer
Older
uwuboot / source / arm9 / menu_firmloader.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "types.h"
#include "mem_map.h"
#include "arm9/console.h"
#include "arm9/dev.h"
#include "fatfs/ff.h"
#include "hid.h"
#include "util.h"
#include "arm9/main.h"
#include "arm9/timer.h"
#include "arm9/menu.h"
#include "arm9/firm.h"
#include "arm9/config.h"

static bool firmLoaded = 0;

static bool tryLoadFirmware(const char *filepath);
static u32 loadFirmSd(const char *filePath);

bool isFirmLoaded(void)
{
	return firmLoaded;
}

bool menuLaunchFirm(const char *filePath)
{
	u32 keys;
	
	if(!filePath)
		return false;
	
	firmLoaded = 0;
	
	clearConsoles();
	consoleSelect(&con_top);
	
	printf("FIRM Loader\n\n");

	menuUpdateGlobalState();
	
	if(!tryLoadFirmware(filePath))
	{
		printf("\n\x1B[31mBad firmware.\e[0m\n\n");
		printf("Press B to return.\n");
		
		do {
			hidScanInput();
			keys = hidKeysDown();
		}
		while(!(keys & KEY_B));
		
		goto fail;
	}
	
	firmLoaded = 1;
	
	printf("\n\x1B[32mFirmware verification SUCCESS!\e[0m\n\n");
	
	/*
	printf("Attempting to run firmware, press B to exit...\n");
	
	for(int i=0; i<0x800000; i++)
	{
		hidScanInput();
		keys = hidKeysDown();
		
		if(keys & KEY_B)
			goto fail;
	}
	*/
	
	menuSetReturnToState(MENU_STATE_EXIT);
	
	return true;

fail:
	
	printf("FIRM-Launch aborted.\n");
	
	TIMER_sleep(2000);
	
	firmLoaded = 0;
	
	menuSetReturnToState(STATE_PREVIOUS);
	
	return false;
}

// Does very basic checks whether the firmware actually exists.
bool statFirmware(const char *filePath)
{
	FILINFO fileStat;
	u32 fileSize;
	
	ensure_mounted(filePath);

	if(strncmp(filePath, "sdmc:", 5) == 0)
	{
		if(f_stat(filePath, &fileStat) != FR_OK)
			return false;
		
		fileSize = fileStat.fsize;
		
		if(fileSize == 0 || FIRM_MAX_SIZE > FIRM_MAX_SIZE)
			return false;
		
		return true;
	}
	
	// unknown mountpoint
	return false;
}

bool tryLoadFirmwareFromSettings(void)
{
	const char *path;
	int keyBootOption, keyPad;
	int i;
	u32 padValue, expectedPadValue;

	firmLoaded = 0;

	clearConsoles();
	consoleSelect(&con_top);

	printf("Loading FIRM from settings\n\n");

	keyBootOption = KBootOption1;
	keyPad = KBootOption1Buttons;

	for(i=0; i<3; i++, keyBootOption++, keyPad++)
	{
		path = (const char *)configGetData(keyBootOption);
		if(path)
		{
			printf("\nBoot Option #%i:\n%s\n", i + 1, path);
			
			// check pad value
			const u32 *temp;
			temp = (const u32 *)configGetData(keyPad);
			if(temp)
			{
				expectedPadValue = *temp;
				hidScanInput();
				padValue = HID_KEY_MASK_ALL & hidKeysHeld();
				if(padValue != expectedPadValue)
				{
					printf("Skipping, right buttons are not pressed.\n");
					printf("%" PRIX32 " %" PRIX32 "\n", padValue, expectedPadValue);
					continue;
				}
			}
			
			// check if fw still exists
			if(!statFirmware(path))
			{
				printf("Couldn't find firmware...\n");
				continue;
			}

			if(menuLaunchFirm(path))
				break;
			else
			{
				clearConsoles();
				consoleSelect(&con_top);
			}
		}

		// ... we failed, try next one
	}

	if(i >= 3)
		return false;

	return true;
}

bool menuTryLoadFirmwareFromSettings(void)
{
	const char *path;
	int keyBootOption, keyPad;
	int i;
	u32 padValue, expectedPadValue;

	clearConsoles();
	consoleSelect(&con_top);

	printf("Loading FIRM from settings\n\n");

	keyBootOption = KBootOption1;
	keyPad = KBootOption1Buttons;

	for(i=0; i<3; i++, keyBootOption++, keyPad++)
	{
		path = (const char *)configGetData(keyBootOption);
		if(path)
		{
			printf("\nBoot Option #%i:\n%s\n", i + 1, path);
			
			// check if fw still exists
			if(!statFirmware(path))
			{
				printf("Couldn't find firmware...\n");
				continue;
			}
			
			// check pad value
			const u32 *temp;
			temp = (const u32 *)configGetData(keyPad);
			if(temp)
			{
				expectedPadValue = *temp;
				hidScanInput();
				padValue = HID_KEY_MASK_ALL & hidKeysHeld();
				if(padValue != expectedPadValue)
				{
					printf("Skipping, right buttons are not pressed.\n");
					printf("%" PRIX32 " %" PRIX32 "\n", padValue, expectedPadValue);
					continue;
				}
			}

			if(menuLaunchFirm(path))
				break;
			else
			{
				clearConsoles();
				consoleSelect(&con_top);
			}
		}

		// ... we failed, try next one
	}

	if(i >= 3)
	{
		menuSetReturnToState(STATE_PREVIOUS);
		return false;
	}
	
	menuSetReturnToState(MENU_STATE_EXIT);

	return true;
}

static bool tryLoadFirmware(const char *filepath)
{
	u32 fw_size;

	if(!filepath)
		return false;

	printf("Loading firmware:\n%s\n\n", filepath);

	if(strncmp(filepath, "sdmc:", 5) == 0)
		fw_size = loadFirmSd(filepath);
	else
		return false;	// TODO: Support more devices

	if(fw_size == 0)
		return false;

	return firm_verify(fw_size);
}

static u32 loadFirmSd(const char *filePath)
{
	FIL file;
	u32 fileSize;
	UINT bytesRead = 0;
	FILINFO fileStat;

	if(f_stat(filePath, &fileStat) != FR_OK)
	{
		printf("Failed to get file status!\n");
		return 0;
	}

	fileSize = fileStat.fsize;

	if(f_open(&file, filePath, FA_READ) != FR_OK)
	{
		printf("Failed to open '%s'!\n", filePath);
		return 0;
	}

	if(fileSize == 0 || FIRM_MAX_SIZE > FIRM_MAX_SIZE)
	{
		f_close(&file);
		return 0;
	}

	if(f_read(&file, (u8*)FIRM_LOAD_ADDR, fileSize, &bytesRead) != FR_OK)
	{
		printf("Failed to read from file!\n");
		fileSize = 0;
	}

	if(bytesRead != fileSize)
		fileSize = 0;

	f_close(&file);

	return fileSize;
}