REVISION := $(shell git rev-list --count HEAD)
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
-CFLAGS := -Wall -Wextra -MMD -MP -O2 -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -DVERSION=\"r$(REVISION)\"
-FLAGS := dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) --no-print-directory
-LDFLAGS := -nostdlib -lgcc
+CFLAGS := -Wall -Wextra -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"r$(REVISION)\"
+FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
+LDFLAGS := -nostdlib -Wl,-z,defs -lgcc
objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
--- /dev/null
+While most everything in this repo is original, some parts are derived from existing software, and I make the best effort to document what is derivative in the interest of giving proper credits:
+
+src/firm - Firmware decryptor originally from CakesFW (http://github.com/mid-kid/CakesForeveryWan)
+src/fatfs - FatFS - http://elm-chan.org/fsw/ff/00index_e.html . This version originates from Decrypt9's repo.
+src/std - Work was originally based on memfuncs.c and memory.c from Cakes and ReiNand, but contains near none of the original code now.
--- /dev/null
+The stdlib/ directory is a self-contained, mostly conformant implementation of the C Standard Library for 3DS in ARM9 code. It supports a good chunk of standard functions and interfaces which follow:
+
+FILE* fopen(const char* name, const char* mode)
+
+ Opens a file @name@ with access mode @mode@ and returns an opaque FILE pointer.
+
+size_t fread(void* buf, size_t size, size_t elem, FILE* handle)
+
+ Reads @elem@ elements of size @size@ from the file @handle@, storing read data to @buf@.
+
+size_t fread(void* buf, size_t size, size_t elem, FILE* handle)
+
+ Writes @elem@ elements of size @size@ to the file @handle@, reading data to write from @buf@.
+
+void fseek(FILE* file, );
+
+/* This memory map is mainly to assist in doing stuff in code.
+MEMORY
+{
+ INSTRUCTION_TCM (rw) : ORIGIN = 0x00000000, LENGTH = 0x08000000
+ ARM_INTERNAL (rw) : ORIGIN = 0x08000000, LENGTH = 0x00100000
+ NEW_INTERNAL (rw) : ORIGIN = 0x08100000, LENGTH = 0x00080000
+ IO_MEMORY (rw) : ORIGIN = 0x10000000, LENGTH = 0x08000000
+ VRAM (rw) : ORIGIN = 0x18000000, LENGTH = 0x00600000
+ DSP_MEMORY (rw) : ORIGIN = 0x1FF00000, LENGTH = 0x00080000
+ AXI_WRAM (rw) : ORIGIN = 0x1FF80000, LENGTH = 0x00080000
+ FCRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000000
+ NEW_FCRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x08000000
+ DATA_TCM (rw) : ORIGIN = 0xFFF00000, LENGTH = 0x00004000
+ BOOTROM (rw) : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000
+} */
+
ENTRY(_start)
SECTIONS
{
. = 0x23F00000;
- .text.start : { *(.text.start) }
- .text : { *(.text) }
- .data : { *(.data) }
- .bss : { *(.bss COMMON) }
- .rodata : { *(.rodata) }
+
+ START_SECTION = .;
+ .text.start : {
+ *(.text.start)
+ }
+ START_SECTION_END = .;
+
+ TEXT_SECTION = .;
+ .text : {
+ *(.text)
+ }
+ TEXT_SECTION_END = .;
+
+ DATA_SECTION = .;
+ .data : {
+ *(.data)
+ }
+ DATA_SECTION_END = .;
+
+ BSS_SECTION = .;
+ .bss : {
+ *(.bss COMMON)
+ }
+ BSS_SECTION_END = .;
+
+ RODATA_SECTION = .;
+ .rodata : {
+ *(.rodata)
+ }
+ RODATA_SECTION_END = .;
+
. = ALIGN(4);
}
#include "std/font.h"
#include "std/fs.h"
#include "std/memory.h"
+#include "std/abort.h"
#include "firm/fcram.h"
#include "firm/crypto.h"
#include "common.h"
-FILE conf_handle;
+FILE* conf_handle;
void regenerate_config() {
f_mkdir(PATH_CFW);
f_mkdir(PATH_KEYS);
f_mkdir(PATH_EXEFS);
- cprintf(BOTTOM_SCREEN, "Created directory structure.\n");
+ fprintf(BOTTOM_SCREEN, "Created directory structure.\n");
memset(&config, 0, sizeof(config));
memcpy(&(config.magic), CONFIG_MAGIC, 4);
config.config_ver = config_version;
- fopen(&conf_handle, PATH_CONFIG, "w");
- fwrite(&config, 1, sizeof(config), &conf_handle);
- fclose(&conf_handle);
+ if(!(conf_handle = fopen(PATH_CONFIG, "w")))
+ abort("Failed to open config for write?\n");
- cprintf(BOTTOM_SCREEN, "Config file written.\n");
+ fwrite(&config, 1, sizeof(config), conf_handle);
+ fclose(conf_handle);
+
+ fprintf(BOTTOM_SCREEN, "Config file written.\n");
}
void load_config() {
// Zero on success.
- if (fopen(&conf_handle, PATH_CONFIG, "r")) {
- cprintf(BOTTOM_SCREEN, "Config file is missing:\n"
+ if (!(conf_handle = fopen(PATH_CONFIG, "r"))) {
+ fprintf(BOTTOM_SCREEN, "Config file is missing:\n"
" %s\n"
"Will create it with defaults.\n",
PATH_CONFIG);
regenerate_config();
} else {
- fread(&config, 1, sizeof(config), &conf_handle);
- fclose(&conf_handle);
+ fread(&config, 1, sizeof(config), conf_handle);
+ fclose(conf_handle);
if (memcmp(&(config.magic), CONFIG_MAGIC, 4)) {
- cprintf(BOTTOM_SCREEN, "Config file at:\n"
+ fprintf(BOTTOM_SCREEN, "Config file at:\n"
" %s\n"
"has incorrect magic:\n"
" '%c%c%c%c'\n"
}
if (config.config_ver < config_version) {
- cprintf(BOTTOM_SCREEN, "Config file has outdated version:\n"
+ fprintf(BOTTOM_SCREEN, "Config file has outdated version:\n"
" %s\n"
"Regenerating with defaults.\n",
PATH_CONFIG);
}
}
- cprintf(BOTTOM_SCREEN, "Config file loaded.\n");
+ fprintf(BOTTOM_SCREEN, "Config file loaded.\n");
}
uint8_t key[AES_BLOCK_SIZE];
if (read_file(key, PATH_SLOT0X11KEY96, AES_BLOCK_SIZE) ||
read_file(key, PATH_ALT_SLOT0X11KEY96, AES_BLOCK_SIZE)) {
- // If we can't read the key, we assume it's not needed, and the firmware is the right version.
- // Otherwise, we make sure the error message for decrypting arm9bin mentions this.
+ // Read key successfully.
aes_setkey(0x11, key, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
// Tell boot_firm it needs to regenerate the keys.
update_96_keys = 1;
}
+ // If we can't read the key, we assume it's not needed, and the firmware is the right version.
+ // Otherwise, we make sure the error message for decrypting arm9bin mentions this.
}
int decrypt_cetk_key(void *key, const void *cetk)
// At i, there's 7 keys with 4 bytes padding between them.
// We only need the 2nd.
memcpy(common_key_y, i + AES_BLOCK_SIZE + 4, sizeof(common_key_y));
- cprintf(BOTTOM_SCREEN, "Found the common key Y\n");
+ fprintf(BOTTOM_SCREEN, "Found the common key Y\n");
break;
}
memcpy(iv, ticket->titleID, sizeof(ticket->titleID));
- cprintf(BOTTOM_SCREEN, "Decrypting key\n");
+ fprintf(BOTTOM_SCREEN, "Decrypting key\n");
memcpy(key, ticket->titleKey, sizeof(ticket->titleKey));
aes(key, key, 1, iv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
uint8_t exefs_key[16] = {0};
uint8_t exefs_iv[16] = {0};
- cprintf(BOTTOM_SCREEN, "Decrypting the NCCH\n");
+ fprintf(BOTTOM_SCREEN, "Decrypting the NCCH\n");
aes_setkey(0x16, key, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x16);
aes(ncch, ncch, *size / AES_BLOCK_SIZE, firm_iv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
exefs_h *exefs = (exefs_h *)((void *)ncch + ncch->exeFSOffset * MEDIA_UNITS);
uint32_t exefs_size = ncch->exeFSSize * MEDIA_UNITS;
- cprintf(BOTTOM_SCREEN, "Decrypting the exefs\n");
+ fprintf(BOTTOM_SCREEN, "Decrypting the exefs\n");
aes_setkey(0x2C, exefs_key, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x2C);
aes(exefs, exefs, exefs_size / AES_BLOCK_SIZE, exefs_iv, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
int decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title) {
uint8_t slot = 0x15;
- cprintf(BOTTOM_SCREEN, "Decrypting ARM9 FIRM binary\n");
+ fprintf(BOTTOM_SCREEN, "Decrypting ARM9 FIRM binary\n");
// if (firm_title == NATIVE_FIRM_TITLEID && version > 0x0F) {
if (firm_title == NATIVE_FIRM_TITLEID) {
// Firmware is likely encrypted. Decrypt.
if (!read_file(firm_key, path_firmkey, AES_BLOCK_SIZE)) {
- cprintf(BOTTOM_SCREEN, "Failed to load FIRM key,\n"
+ fprintf(BOTTOM_SCREEN, "Failed to load FIRM key,\n"
" Attempting to generate with CETK.\n");
if (!read_file(fcram_temp, path_cetk, FCRAM_SPACING)) {
- cprintf(BOTTOM_SCREEN, "Failed to load CETK\n");
+ fprintf(BOTTOM_SCREEN, "Failed to load CETK\n");
return 1;
}
- cprintf(BOTTOM_SCREEN, "Loaded CETK\n");
+ fprintf(BOTTOM_SCREEN, "Loaded CETK\n");
if (decrypt_cetk_key(firm_key, fcram_temp) != 0) {
- cprintf(BOTTOM_SCREEN, "Failed to decrypt the CETK\n");
+ fprintf(BOTTOM_SCREEN, "Failed to decrypt the CETK\n");
return 1;
}
- cprintf(BOTTOM_SCREEN, "Saving FIRM key for future use\n");
+ fprintf(BOTTOM_SCREEN, "Saving FIRM key for future use\n");
write_file(firm_key, path_firmkey, AES_BLOCK_SIZE);
} else {
- cprintf(BOTTOM_SCREEN, "Loaded FIRM key\n");
+ fprintf(BOTTOM_SCREEN, "Loaded FIRM key\n");
}
- cprintf(BOTTOM_SCREEN, "Decrypting FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Decrypting FIRM\n");
if (decrypt_firm_title(dest, (void *)dest, size, firm_key) != 0) {
- cprintf(BOTTOM_SCREEN, "Failed to decrypt the firmware\n");
+ fprintf(BOTTOM_SCREEN, "Failed to decrypt the firmware\n");
return 1;
}
return 0;
int firmware_changed = 0;
if (read_file(dest, path, *size)) {
- cprintf(BOTTOM_SCREEN, "Failed to read FIRM from SD\n");
+ fprintf(BOTTOM_SCREEN, "Failed to read FIRM from SD\n");
// Only whine about this if it's NATIVE_FIRM, which is important.
if (firm_title == NATIVE_FIRM_TITLEID) {
- cprintf(BOTTOM_SCREEN, "Failed to load NATIVE_FIRM from:\n"
+ fprintf(BOTTOM_SCREEN, "Failed to load NATIVE_FIRM from:\n"
" " PATH_NATIVE_F "\n"
"This is fatal. Aborting.\n");
}
status = decrypt_firm(dest, path_firmkey, path_cetk, size, firm_title);
if (status != 0) {
if (firm_title == NATIVE_FIRM_TITLEID) {
- cprintf(BOTTOM_SCREEN, "Failed to decrypt firmware.\n"
+ fprintf(BOTTOM_SCREEN, "Failed to decrypt firmware.\n"
"This is fatal. Aborting.\n");
}
goto exit_error;
}
firmware_changed = 1; // Decryption performed.
} else {
- cprintf(BOTTOM_SCREEN, "FIRM not encrypted\n");
+ fprintf(BOTTOM_SCREEN, "FIRM not encrypted\n");
}
// The N3DS firm has an additional encryption layer for ARM9
if (arm9bin_iscrypt) {
// Decrypt the arm9bin.
if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), firm_title)) {
- cprintf(BOTTOM_SCREEN, "Couldn't decrypt ARM9 FIRM binary.\n"
+ fprintf(BOTTOM_SCREEN, "Couldn't decrypt ARM9 FIRM binary.\n"
"Check if you have the needed key at:\n"
" " PATH_SLOT0X11KEY96 "\n");
status = 1;
}
firmware_changed = 1; // Decryption of arm9bin performed.
} else {
- cprintf(BOTTOM_SCREEN, "ARM9 FIRM binary not encrypted\n");
+ fprintf(BOTTOM_SCREEN, "ARM9 FIRM binary not encrypted\n");
// if (firm_type == NATIVE_FIRM && firm_current->version > 0x0F) {
slot0x11key96_init(); // This has to be loaded regardless, otherwise boot will fail.
// }
// Save firmware.bin if decryption was done.
if (firmware_changed) {
- cprintf(BOTTOM_SCREEN, "Saving decrypted FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Saving decrypted FIRM\n");
write_file(dest, path, *size);
}
//if (firm_current->console == console_n3ds)
{
- cprintf(BOTTOM_SCREEN, "Fixing arm9 entrypoint\n");
+ fprintf(BOTTOM_SCREEN, "Fixing arm9 entrypoint\n");
// Patch the entrypoint to skip arm9loader
if (firm_title == NATIVE_FIRM_TITLEID) {
*(uint8_t *)(keydata + 0xF) += 1;
}
- cprintf(BOTTOM_SCREEN, "Updated keyX keyslots\n");
+ fprintf(BOTTOM_SCREEN, "Updated keyX keyslots\n");
}
struct memory_header *memory = (void *)(memory_loc + 1);
memcpy((void *)memory->location, memory + 1, memory->size);
memory = (void *)((uintptr_t)(memory + 1) + memory->size);
}
- cprintf(BOTTOM_SCREEN, "Copied memory\n");
+ fprintf(BOTTOM_SCREEN, "Copied memory\n");
for (firm_section_h *section = firm_loc->section;
section < firm_loc->section + 4 && section->address != 0; section++) {
memcpy((void *)section->address, (void *)firm_loc + section->offset, section->size);
}
- cprintf(BOTTOM_SCREEN, "Copied FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Copied FIRM\n");
*a11_entry = (uint32_t)disable_lcds;
while (*a11_entry); // Make sure it jumped there correctly before changing it.
*a11_entry = (uint32_t)firm_loc->a11Entry;
- cprintf(BOTTOM_SCREEN, "Prepared arm11 entry, jumping to FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Prepared arm11 entry, jumping to FIRM\n");
((void (*)())firm_loc->a9Entry)();
}
int load_firms() {
- cprintf(TOP_SCREEN, "[Loading FIRM]");
+ fprintf(TOP_SCREEN, "[Loading FIRM]");
- cprintf(BOTTOM_SCREEN, "Loading NATIVE_FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Loading NATIVE_FIRM\n");
if (load_firm(firm_loc, PATH_NATIVE_F, PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size, NATIVE_FIRM_TITLEID) != 0)
return 1;
- cprintf(BOTTOM_SCREEN, "Loading TWL_FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Loading TWL_FIRM\n");
if(load_firm(twl_firm_loc, PATH_TWL_F, PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size, TWL_FIRM_TITLEID))
- cprintf(BOTTOM_SCREEN, "TWL_FIRM failed to load.\n");
+ fprintf(BOTTOM_SCREEN, "TWL_FIRM failed to load.\n");
- cprintf(BOTTOM_SCREEN, "Loading AGB_FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Loading AGB_FIRM\n");
if(load_firm(agb_firm_loc, PATH_AGB_F, PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size, AGB_FIRM_TITLEID))
- cprintf(BOTTOM_SCREEN, "AGB_FIRM failed to load.\n");
+ fprintf(BOTTOM_SCREEN, "AGB_FIRM failed to load.\n");
return 0;
}
void boot_cfw() {
- cprintf(TOP_SCREEN, "[Patching]");
+ fprintf(TOP_SCREEN, "[Patching]");
// if (patch_firm_all() != 0)
// return;
// and either the patches have been modified, or the file doesn't exist.
if ((save_firm || config.options[OPTION_AUTOBOOT]) &&
f_stat(PATH_NATIVE_P, NULL) != 0) {
- cprintf(BOTTOM_SCREEN, "Saving patched NATIVE_FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Saving patched NATIVE_FIRM\n");
if (write_file(firm_loc, PATH_NATIVE_P, firm_size) != firm_size) {
- cprintf(BOTTOM_SCREEN, "%pFailed to save patched FIRM.\nWriting SD failed.\nThis is fatal.\n", COLOR(RED, BLACK));
+ fprintf(BOTTOM_SCREEN, "%pFailed to save patched FIRM.\nWriting SD failed.\nThis is fatal.\n", COLOR(RED, BLACK));
return;
}
}
if ((save_firm || config.options[OPTION_AUTOBOOT]) &&
f_stat(PATH_MEMBIN, NULL) != 0) {
- cprintf(BOTTOM_SCREEN, "Saving patched memory\n");
+ fprintf(BOTTOM_SCREEN, "Saving patched memory\n");
if (write_file(memory_loc, PATH_MEMBIN, *memory_loc) != *memory_loc) {
- cprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK));
+ fprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK));
return;
}
}
if (f_stat(PATH_TWL_P, NULL) != 0) {
- cprintf(BOTTOM_SCREEN, "Saving patched TWL_FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Saving patched TWL_FIRM\n");
if (write_file(twl_firm_loc, PATH_TWL_P, twl_firm_size) != twl_firm_size) {
- cprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK));
+ fprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK));
return;
}
}
if (f_stat(PATH_AGB_P, NULL) != 0) {
- cprintf(BOTTOM_SCREEN, "Saving patched AGB_FIRM\n");
+ fprintf(BOTTOM_SCREEN, "Saving patched AGB_FIRM\n");
if (write_file(agb_firm_loc, PATH_AGB_P, agb_firm_size) != agb_firm_size) {
- cprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK));
+ fprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK));
return;
}
}
//-----------------------------------------------------------------------------
-static const struct { uint8_t bus_id, reg_addr } dev_data[] = {
+static const struct { uint8_t bus_id, reg_addr; } dev_data[] = {
{0, 0x4A}, {0, 0x7A}, {0, 0x78},
{1, 0x4A}, {1, 0x78}, {1, 0x2C},
{1, 0x2E}, {1, 0x40}, {1, 0x44},
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
};
-const uint8_t i2cGetDeviceBusId(uint8_t device_id) {
+uint8_t i2cGetDeviceBusId(uint8_t device_id) {
return dev_data[device_id].bus_id;
}
-const uint8_t i2cGetDeviceRegAddr(uint8_t device_id) {
+uint8_t i2cGetDeviceRegAddr(uint8_t device_id) {
return dev_data[device_id].reg_addr;
}
(volatile uint8_t*)(I2C3_REG_OFF + I2C_REG_DATA),
};
-volatile uint8_t* const i2cGetDataReg(uint8_t bus_id) {
+volatile uint8_t* i2cGetDataReg(uint8_t bus_id) {
return reg_data_addrs[bus_id];
}
(volatile uint8_t*)(I2C3_REG_OFF + I2C_REG_CNT),
};
-volatile uint8_t* const i2cGetCntReg(uint8_t bus_id) {
+volatile uint8_t* i2cGetCntReg(uint8_t bus_id) {
return reg_cnt_addrs[bus_id];
}
//-----------------------------------------------------------------------------
-int i2cWaitBusy(uint8_t bus_id) {
+void i2cWaitBusy(uint8_t bus_id) {
while (*i2cGetCntReg(bus_id) & 0x80);
}
#define I2C_DEV_GYRO 10
#define I2C_DEV_IR 13
-const uint8_t i2cGetDeviceBusId(uint8_t device_id);
-const uint8_t i2cGetDeviceRegAddr(uint8_t device_id);
+uint8_t i2cGetDeviceBusId(uint8_t device_id);
+uint8_t i2cGetDeviceRegAddr(uint8_t device_id);
-volatile uint8_t* const i2cGetDataReg(uint8_t bus_id);
-volatile uint8_t* const i2cGetCntReg(uint8_t bus_id);
+volatile uint8_t* i2cGetDataReg(uint8_t bus_id);
+volatile uint8_t* i2cGetCntReg(uint8_t bus_id);
-int i2cWaitBusy(uint8_t bus_id);
+void i2cWaitBusy(uint8_t bus_id);
int i2cGetResult(uint8_t bus_id);
uint8_t i2cGetData(uint8_t bus_id);
void i2cStop(uint8_t bus_id, uint8_t arg0);
}
void header() {
- cprintf(TOP_SCREEN, "%p[Corbenik - The Rebirth - %s]\n", COLOR(CYAN, BLACK), VERSION);
+ fprintf(stdout, "\x1b[33;40m[.corbenik//%s]\n", VERSION);
}
int menu_options() { return MENU_MAIN; }
header();
- cprintf(TOP_SCREEN, "Corbenik is a 3DS firmware patcher\n"
+ fprintf(stdout, "Corbenik is a 3DS firmware patcher\n"
" commonly known as a CFW. It seeks to address\n"
" some faults in other CFWs and is generally\n"
" just another choice for users - but primarily\n"
}
int menu_reset() {
+ fumount(); // Unmount SD.
+
// Reboot.
- cprintf(BOTTOM_SCREEN, "Resetting system.\n");
+ fprintf(BOTTOM_SCREEN, "Resetting system.\n");
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2);
while(1);
}
int menu_poweroff() {
+ fumount(); // Unmount SD.
+
// Reboot.
- cprintf(BOTTOM_SCREEN, "Powering off system.\n");
+ fprintf(BOTTOM_SCREEN, "Powering off system.\n");
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0);
while(1);
}
set_cursor(TOP_SCREEN, 0, 0);
const char *list[] = {
- "Options",
- "Patches",
- "Info",
- "Help/Readme",
- "Reset",
- "Power off",
- "Boot firmware"
+ "Options ",
+ "Patches ",
+ "Info ",
+ "Help/Readme ",
+ "Reset ",
+ "Power off ",
+ "Boot firmware "
};
header();
for(int i=0; i < menu_max; i++) {
if (cursor_y == i)
- cprintf(TOP_SCREEN, "%p>> ", COLOR(GREEN, BLACK));
+ fprintf(TOP_SCREEN, "\x1b[42m>> ");
else
- cprintf(TOP_SCREEN, " ");
- cprintf(TOP_SCREEN, "%s\n", list[i]);
+ fprintf(TOP_SCREEN, " ");
+ fprintf(TOP_SCREEN, "%s\n", list[i]);
}
uint32_t key = wait_key();
void main() {
if (fmount()) {
// Failed to mount SD. Bomb out.
- cprintf(BOTTOM_SCREEN, "%pFailed to mount SD card.\n", COLOR(RED, BLACK));
+ fprintf(BOTTOM_SCREEN, "%pFailed to mount SD card.\n", COLOR(RED, BLACK));
} else {
- cprintf(BOTTOM_SCREEN, "Mounted SD card.\n");
+ fprintf(BOTTOM_SCREEN, "Mounted SD card.\n");
}
load_config(); // Load configuration.
--- /dev/null
+#ifndef __ABORT_H
+#define __ABORT_H
+
+#define abort(x...) { \
+ fprintf(2, x); \
+ fumount(); \
+ while(1); \
+}
+
+#endif
#include <stdarg.h>
#include "memory.h"
#include "font.h"
+#include "../fatfs/ff.h"
+#include "fs.h"
static unsigned int top_cursor_x = 0, top_cursor_y = 0;
static unsigned int bottom_cursor_x = 0, bottom_cursor_y = 0;
uint32_t size = 0;
char* buffer = 0;
uint32_t buffer_size = 0;
- if (screen == TOP_SCREEN)
+ if ((int)screen == TOP_SCREEN)
screen = framebuffers->top_left;
- else if (screen == BOTTOM_SCREEN)
+ else if ((int)screen == BOTTOM_SCREEN)
screen = framebuffers->bottom;
if(screen == framebuffers->top_left ||
}
}
-void putc(int buf, unsigned char color, const int c) {
- unsigned int width = 0;
- unsigned int height = 0;
- unsigned int size = 0;
- unsigned int cursor_x;
- unsigned int cursor_y;
- char* colorbuf;
- char* strbuf;
-
- if (buf == TOP_SCREEN) {
- width = TEXT_TOP_WIDTH;
- height = TEXT_TOP_HEIGHT;
- size = TEXT_TOP_SIZE;
- colorbuf = color_buffer_top;
- strbuf = text_buffer_top;
- cursor_x = top_cursor_x;
- cursor_y = top_cursor_y;
- } else if (buf == BOTTOM_SCREEN) {
- width = TEXT_BOTTOM_WIDTH;
- height = TEXT_BOTTOM_HEIGHT;
- size = TEXT_BOTTOM_SIZE;
- colorbuf = color_buffer_bottom;
- strbuf = text_buffer_bottom;
- cursor_x = bottom_cursor_x;
- cursor_y = bottom_cursor_y;
+void putc(void* buf, unsigned char color, const int c) {
+ if ((int)buf == stdout || (int)buf == stderr) {
+ unsigned int width = 0;
+ unsigned int height = 0;
+ unsigned int size = 0;
+ unsigned int cursor_x;
+ unsigned int cursor_y;
+ char* colorbuf;
+ char* strbuf;
+
+ if ((int)buf == TOP_SCREEN) {
+ width = TEXT_TOP_WIDTH;
+ height = TEXT_TOP_HEIGHT;
+ size = TEXT_TOP_SIZE;
+ colorbuf = color_buffer_top;
+ strbuf = text_buffer_top;
+ cursor_x = top_cursor_x;
+ cursor_y = top_cursor_y;
+ } else if ((int)buf == BOTTOM_SCREEN) {
+ width = TEXT_BOTTOM_WIDTH;
+ height = TEXT_BOTTOM_HEIGHT;
+ size = TEXT_BOTTOM_SIZE;
+ colorbuf = color_buffer_bottom;
+ strbuf = text_buffer_bottom;
+ cursor_x = bottom_cursor_x;
+ cursor_y = bottom_cursor_y;
+ }
+
+ unsigned int offset = width * cursor_y + cursor_x;
+
+ if (offset >= size) {
+ // Scroll a line back. This involves memcpy.
+ // Yes, memcpy overwrites part of the buffer it is reading.
+ memcpy(strbuf, strbuf+width, size-width);
+ memcpy(colorbuf, colorbuf+width, size-width);
+ cursor_y -= 1;
+ offset = width * cursor_y + cursor_x;
+ }
+
+ if (offset >= size) {
+ // So if we're being real, this won't ever happen.
+ return;
+ }
+
+ switch(c) {
+ case '\n':
+ cursor_y++; // Increment line.
+ cursor_x = 0;
+ break;
+ case '\r':
+ cursor_x = 0; // Reset to beginning of line.
+ break;
+ default:
+ strbuf[offset] = c;
+ colorbuf[offset] = color; // White on black.
+ cursor_x++;
+ if (cursor_x >= width) {
+ cursor_y++;
+ cursor_x = 0;
+ }
+ break;
+ }
+
+ if ((int)buf == TOP_SCREEN) {
+ top_cursor_x = cursor_x;
+ top_cursor_y = cursor_y;
+ } else if ((int)buf == BOTTOM_SCREEN) {
+ bottom_cursor_x = cursor_x;
+ bottom_cursor_y = cursor_y;
+ }
} else {
- return; // Invalid buffer.
+ // FILE*, not stdin or stdout.
+ fwrite(&c, 1, 1, (FILE*)buf);
}
-
- unsigned int offset = width * cursor_y + cursor_x;
-
- if (offset >= size) {
- // Scroll a line back. This involves memcpy.
- // Yes, memcpy overwrites part of the buffer it is reading.
- memcpy(strbuf, strbuf+width, size-width);
- memcpy(colorbuf, colorbuf+width, size-width);
- cursor_y -= 1;
- offset = width * cursor_y + cursor_x;
- }
-
- if (offset >= size) {
- // So if we're being real, this won't ever happen.
- return;
- }
-
- switch(c) {
- case '\n':
- cursor_y++; // Increment line.
- cursor_x = 0;
- break;
- case '\r':
- cursor_x = 0; // Reset to beginning of line.
- break;
- default:
- strbuf[offset] = c;
- colorbuf[offset] = color; // White on black.
- cursor_x++;
- if (cursor_x >= width) {
- cursor_y++;
- cursor_x = 0;
- }
- break;
- }
-
- if (buf == TOP_SCREEN) {
- top_cursor_x = cursor_x;
- top_cursor_y = cursor_y;
- } else if (buf == BOTTOM_SCREEN) {
- bottom_cursor_x = cursor_x;
- bottom_cursor_y = cursor_y;
- }
}
-void puts(int buf, unsigned char color, const char *string) {
+void puts(void* buf, unsigned char color, const char *string) {
char *ref = (char*)string;
while(*ref != '\0') {
}
}
-void put_int(int channel, unsigned char color, int n) {
- char conv[16], out[16];
- memset(conv, 0, 16);
- memset(out, 0, 16);
+void put_int64(void* channel, unsigned char color, int64_t n, int length) {
+ char conv[32], out[32];
+ memset(conv, 0, 32);
+ memset(out, 0, 32);
int i = 0, sign = 0;
if (n < 0) {
if (sign)
conv[i++] = '-';
+ if (length > 0)
+ out[length] = '\0';
+
int len = strlen(conv);
for(int i=0; i < len; i++)
out[i] = conv[(len-1) - i];
puts(channel, color, out);
}
-void put_uint(int channel, unsigned char color, unsigned int n) {
- char conv[16], out[16];
- memset(conv, 0, 16);
- memset(out, 0, 16);
+void put_uint64(void* channel, unsigned char color, uint64_t n, int length) {
+ char conv[32], out[32];
+ memset(conv, 0, 32);
+ memset(out, 0, 32);
int i = 0;
do {
conv[i++] = (n % 10) + '0';
} while((n /= 10) != 0);
+ if (length > 0)
+ out[length] = '\0';
+
int len = strlen(conv);
for(int i=0; i < len; i++)
out[i] = conv[(len-1) - i];
puts(channel, color, out);
}
-void cflush(int channel) {
- if (channel == TOP_SCREEN) {
+void put_uint(void* channel, unsigned char color, unsigned int n, int length) {
+ put_uint64(channel, color, n, length);
+}
+
+void put_int(void* channel, unsigned char color, int n, int length) {
+ put_int64(channel, color, n, length);
+}
+
+void fflush(void* channel) {
+ if ((int)channel == TOP_SCREEN) {
for(int x=0; x < TEXT_TOP_WIDTH; x++) {
for(int y=0; y < TEXT_TOP_HEIGHT; y++) {
char c = text_buffer_top[y*TEXT_TOP_WIDTH+x];
draw_character(framebuffers->top_left, c, x, y, color_fg, color_bg);
}
}
- } else if (channel == BOTTOM_SCREEN) {
+ } else if ((int)channel == BOTTOM_SCREEN) {
for(int x=0; x < TEXT_BOTTOM_WIDTH; x++) {
for(int y=0; y < TEXT_BOTTOM_HEIGHT; y++) {
char c = text_buffer_bottom[y*TEXT_BOTTOM_WIDTH+x];
draw_character(framebuffers->bottom, c, x, y, color_fg, color_bg);
}
}
+ } else {
+ f_sync(&(((FILE*)channel)->handle)); // Sync to disk.
}
}
-void cprintf(int channel, const char* format, ...) {
+void fprintf(void* channel, const char* format, ...) {
va_list ap;
va_start( ap, format );
unsigned char color = 0xf0;
while(*ref != '\0') {
- if(*ref == '%') {
+ if (*ref == 0x1B && *(++ref) == '[' && ( (int)channel == stdout || (int)channel == stderr) ) {
+ansi_codes:
+ // Ansi escape code.
+ ++ref;
+ // [30-37] Set text color
+ if (*ref == '3') {
+ ++ref;
+ if(*ref >= '0' && *ref <= '7') {
+ // Valid FG color.
+ color &= 0x0f; // Remove fg color.
+ color |= (*ref - '0') << 4;
+ }
+ }
+ // [40-47] Set bg color
+ else if (*ref == '4') {
+ ++ref;
+ if(*ref >= '0' && *ref <= '7') {
+ // Valid BG color.
+ color &= 0xf0; // Remove bg color.
+ color |= *ref - '0';
+ }
+ }
+
+ ++ref;
+
+ if (*ref == ';') {
+ goto ansi_codes; // Another code.
+ }
+
+ // Loop until the character is somewhere 0x40 - 0x7E, which terminates an ANSI sequence
+ while(!(*ref >= 0x40 && *ref <= 0x7E)) ref++;
+ } else if (*ref == '%') {
+ int type_size = 0;
+ int length = -1;
+check_format:
// Format string.
- ref++;
+ ++ref;
switch(*ref) {
case 'd':
- put_int(channel, color, va_arg( ap, int ));
+ switch(type_size) {
+ case 2:
+ put_int64(channel, color, va_arg( ap, int64_t ), length);
+ break;
+ default:
+ put_int(channel, color, va_arg( ap, int ), length);
+ break;
+ }
break;
case 'u':
- put_uint(channel, color, va_arg( ap, unsigned int ));
+ switch(type_size) {
+ case 2:
+ put_uint64(channel, color, va_arg( ap, uint64_t ), length);
+ break;
+ default:
+ put_uint(channel, color, va_arg( ap, unsigned int ), length);
+ break;
+ }
break;
case 's':
puts(channel, color, va_arg( ap, char* ));
case '%':
putc(channel, color, '%');
break;
+ case 'h':
+ goto check_format; // Integers get promoted. No point here.
+ case 'l':
+ ++type_size;
+ goto check_format;
default:
+ if (*ref >= '0' && *ref <= '9') {
+ length = *ref - '0';
+ goto check_format;
+ }
break;
}
} else {
putc(channel, color, *ref);
}
- ref++;
+ ++ref;
}
va_end( ap );
- cflush(channel);
+ fflush(channel);
}
void draw_character(uint8_t* screen, const char character, const unsigned int pos_x, const unsigned int pos_y, const uint32_t color_fg, const uint32_t color_bg);
#define TOP_SCREEN 0
-#define BOTTOM_SCREEN 1
+#define BOTTOM_SCREEN 2
-void putc(int buf, unsigned char color, const int c);
-void puts(int buf, unsigned char color, const char *string);
-void cflush(int channel);
+#define stdout TOP_SCREEN
+#define stderr BOTTOM_SCREEN
-void put_int(int channel, unsigned char color, int n);
-void put_uint(int channel, unsigned char color, unsigned int n);
+void putc(void* buf, unsigned char color, const int c);
+void puts(void* buf, unsigned char color, const char *string);
+void fflush(void* channel);
void set_cursor(int channel, unsigned int x, unsigned int y);
// Like printf. Supports the following format specifiers:
-// %s %c %d %u
-// The following non-standard formats are also supported (but are subject to replacement)
-// %p - unsigned char, changes color of text (will be replaced with ANSI codes eventually)
-void cprintf(int channel, const char* format, ...);
+// %s - char*
+// %c - char
+// %d - int
+// %u - unsigned int
+// The following non-standard
+// The following prefixes are also supported:
+// %h - word (stub)
+// %hh - byte (stub)
+// %[0-9][0-9]
+// Formats are also supported (but are subject to replacement)
+// %p - unsigned char, changes color of text (will be replaced with ANSI codes eventually)
+void fprintf(void* channel, const char* format, ...);
#define BLACK 0
#define BLUE 1
#include "fs.h"
#include "memory.h"
#include "../fatfs/ff.h"
+#include "draw.h"
static FATFS fs;
+static FILE files[MAX_FILES_OPEN];
+
int fmount(void) {
- if (f_mount(&fs, "0:", 1)) return 1;
+ if (f_mount(&fs, "0:", 1))
+ return 1;
+
+ for(int i=0; i < MAX_FILES_OPEN; i++)
+ memset(&files[i], 0, sizeof(FILE));
+
return 0;
}
int fumount(void) {
- if (f_mount(NULL, "0:", 1)) return 1;
+ for(int i=0; i < MAX_FILES_OPEN; i++)
+ if (files[i].is_open)
+ fclose(&files[i]);
+
+ if (f_mount(NULL, "0:", 1))
+ return 1;
+
return 0;
}
-int fopen(FILE* fp, const char *filename, const char *mode) {
+FILE* fopen(const char *filename, const char *mode) {
if (*mode != 'r' && *mode != 'w' && *mode != 'a')
- return 1; // Mode not valid.
+ return NULL; // Mode not valid.
+
+ FILE* fp;
+ int i;
+ for(i=0; i < MAX_FILES_OPEN; i++) {
+ if(!files[i].is_open) {
+ fp = &files[i];
+ break;
+ }
+ }
+
+ if (i == MAX_FILES_OPEN)
+ return NULL; // Out of handles.
fp->mode = (*mode == 'r' ? FA_READ : (FA_WRITE | FA_OPEN_ALWAYS));
- return f_open(&(fp->handle), filename, fp->mode);
+ if (f_open(&(fp->handle), filename, fp->mode))
+ return NULL;
+
+ fp->is_open = 1;
+
+ return fp;
}
void fclose(FILE* fp) {
f_close(&(fp->handle));
+
+ memset(fp, 0, sizeof(FILE));
}
void fseek(FILE* fp, int64_t offset, int whence) {
}
size_t write_file(void* data, char* path, size_t size) {
- FILE temp;
- fopen(&temp, path, "w");
- size_t written = fwrite(data, 1, size, &temp);
- fclose(&temp);
- return written;
+ FILE* temp = fopen(path, "w");
+
+ if (!temp)
+ return 0;
+
+ size_t wrote = fwrite(data, 1, size, temp);
+
+ fclose(temp);
+
+ return wrote;
}
size_t read_file(void* data, char* path, size_t size) {
- FILE temp;
- fopen(&temp, path, "r");
+ FILE* temp = fopen(path, "r");
+
+ if (!temp)
+ return 0;
+
+ size_t read = fread(data, 1, size, temp);
- size_t read = fread(data, 1, size, &temp);
- fclose(&temp);
+ fclose(temp);
return read;
}
#include "memory.h"
#include "../fatfs/ff.h"
+#define MAX_FILES_OPEN 64
+
typedef struct {
FIL handle;
uint32_t mode;
size_t size;
size_t at;
-} FILE;
+ uint8_t is_open;
+} __attribute__((packed)) FILE;
#define SEEK_SET 0
#define SEEK_CUR 1
int fmount (void);
int fumount (void);
-int fopen (FILE* fp, const char *filename, const char *mode);
+FILE* fopen (const char *filename, const char *mode);
void fclose (FILE* fp);