/* This is a parser for the loadercfg.txt file. It's not vulnerable, I swear. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "types.h"
#include "mem_map.h"
#include "arm9/fatfs/ff.h"
#include "arm9/console.h"
#include "arm9/main.h"
#include "arm9/interrupt.h"
#include "util.h"
#include "hid.h"
#include "arm9/config.h"
#define MAX_FILE_SIZE 0x4000 - 1
static const char *filepath = "sdmc:\\loadercfg.txt";
static FIL file;
typedef struct {
char *textData; // ptr to the first char of an attribute's data inside filebuf
u32 textLength; // length of the above data, without '\0' ofc
void *data; // used to store the final native data
} AttributeEntryType;
typedef struct {
// function to convert the textData to arbitrary native data
bool (*parse)(AttributeEntryType *attr);
// opposite of parse(), used to update/create an entry in the file
bool (*write)(AttributeEntryType *attr);
} FunctionsEntryType;
static bool parseConfigFile();
static bool parseBootOption(AttributeEntryType *attr);
static bool parseBootOptionPad(AttributeEntryType *attr);
static bool parseBootMode(AttributeEntryType *attr);
static const char *keyStrings[] = {
"BOOT_OPTION1",
"BOOT_OPTION2",
"BOOT_OPTION3",
"BOOT_OPTION1_NAND_IMAGE",
"BOOT_OPTION2_NAND_IMAGE",
"BOOT_OPTION3_NAND_IMAGE",
"BOOT_OPTION1_BUTTONS",
"BOOT_OPTION2_BUTTONS",
"BOOT_OPTION3_BUTTONS",
"BOOT_MODE"
};
static FunctionsEntryType keyFunctions[] = {
{ parseBootOption, NULL },
{ parseBootOption, NULL },
{ parseBootOption, NULL },
{ parseBootOption, NULL },
{ parseBootOption, NULL },
{ parseBootOption, NULL },
{ parseBootOptionPad, NULL },
{ parseBootOptionPad, NULL },
{ parseBootOptionPad, NULL },
{ parseBootMode, NULL }
};
static AttributeEntryType attributes[numKeys];
static char *filebuf = NULL;
/* This loads the config file from SD card and parses it */
bool loadConfigFile()
{
FILINFO fileStat;
u32 fileSize;
unsigned bytesRead;
if(f_stat(filepath, &fileStat) != FR_OK)
{
printf("Failed to get config-file status!\n");
goto fail;
}
fileSize = fileStat.fsize;
if(fileSize > MAX_FILE_SIZE || fileSize == 0)
{
printf("Invalid config-file size!\n");
goto fail;
}
filebuf = (char *) malloc(fileSize + 1);
if(!filebuf)
{
printf("Out of memory!\n");
goto fail;
}
if(f_open(&file, filepath, FA_READ) != FR_OK)
{
printf("Failed to open config-file for reading!\n");
goto fail;
}
if(f_read(&file, filebuf, fileSize, &bytesRead) != FR_OK || bytesRead != fileSize)
{
printf("Failed to read from config-file!\n");
f_close(&file);
goto fail;
}
// terminate string buf
filebuf[fileSize] = '\0';
return parseConfigFile();
fail:
return false;
}
/* returns the start of an attribute's data. */
static char *findDefinition(const char *attrName)
{
char *start;
char c;
start = strstr(filebuf, attrName);
if(start)
{
/* verify definition char */
start = start + strlen(attrName);
while(*start == ' ') start++;
c = *start;
if(c != '=')
start = NULL;
else
{
start ++;
while(*start == ' ') start++;
}
}
return start;
}
/* returns the length of an attribute's data. */
static u32 parseDefinition(char *attrData)
{
static const char LF = 0x0A, CR = 0x0D, NEL = 0x15;
u32 len = 0;
char *cur = attrData;
char c;
if(!attrData)
return 0;
// look for linebreaks that mark the end of a definition
for(; (c = *cur) != '\0'; cur++)
{
if(c == LF || c == CR || c == NEL)
break;
len ++;
c = *cur;
}
return len;
}
static bool parseConfigFile()
{
AttributeEntryType *curAttr;
u32 len;
char *text;
bool ret;
// pass #1: look for definitions and populate our lookup table.
for(u32 i=0; i<numKeys; i++)
{
curAttr = &attributes[i];
text = findDefinition(keyStrings[i]);
curAttr->textData = text;
if(!text)
{
curAttr->textLength = 0;
continue;
}
len = parseDefinition(text);
curAttr->textLength = len;
}
// pass #2: parse text-data and convert it into a native form
for(u32 i=0; i<numKeys; i++)
{
curAttr = &attributes[i];
if(!curAttr->textData)
{
curAttr->data = NULL;
continue;
}
else
{ // TODO: what to do if ret == false ?
//printf("Parsing attr text with size %i\n%s\n", curAttr->textLength, curAttr->textData);
ret = keyFunctions[i].parse(curAttr);
//if(ret) printf("success!\n");
}
}
return true;
}
static bool isValidPath(char *path)
{
char c;
bool gotMountpoint = false;
for(; (c = *path) != '\0'; path++)
{
// no multiple mountpoints
if(c == ':')
{
if(gotMountpoint)
return false;
else
gotMountpoint = true;
}
// no dir-return
if(c == '.' && path[1] == '.')
return false;
// no space after dir slash
if((c == '\\' || c == '/') && path[1] == ' ')
return false;
// no non ASCII chars
if(!isascii(c))
return false;
}
if(!gotMountpoint)
return false;
return true;
}
static bool parseBootOption(AttributeEntryType *attr)
{
attr->data = NULL;
char *buf = (char *) malloc(attr->textLength + 1);
if(!buf) return false;
memcpy(buf, attr->textData, attr->textLength);
buf[attr->textLength] = '\0';
if(!isValidPath(buf))
{
free(buf);
return false;
}
attr->data = buf;
return true;
}
static bool parseBootOptionPad(AttributeEntryType *attr)
{
char c;
char *textData = attr->textData;
u32 padValue = 0;
u32 i;
static const char * convTable[] = {
"A", "B", "SELECT", "START", "RIGHT", "LEFT",
"UP", "DOWN", "R", "L", "X", "Y"
};
for(; (c = *textData) != '\0' && textData < attr->textData + attr->textLength;)
{
for(i=0; i<arrayEntries(convTable); i++)
{
if(strnicmp(textData, convTable[i], strlen(convTable[i])) == 0)
{
padValue |= 1 << i;
break;
}
}
if(i == arrayEntries(convTable))
textData ++;
else textData += strlen(convTable[i]);
}
attr->data = (u32 *) malloc(sizeof(u32));
if(!attr->data)
return false;
*(u32 *)attr->data = padValue;
return true;
}
static bool parseBootMode(AttributeEntryType *attr)
{
// TODO
attr->data = NULL;
return true;
}
void *configCopyText(int key)
{
if(key < 0 || key >= KLast)
return NULL;
if(!attributes[key].textData)
return NULL;
char *buf = (char *) malloc(attributes[key].textLength + 1);
if(!buf)
return NULL;
memcpy(buf, attributes[key].textData, attributes[key].textLength);
buf[attributes[key].textLength] = '\0';
return buf;
}
const void *configGetData(int key)
{
if(key < 0 || key >= KLast)
return NULL;
return attributes[key].data;
}
const char *configGetKeyText(int key)
{
if(key < 0 || key >= KLast)
return NULL;
return keyStrings[key];
}