* More work prepping for bytecode.
* Improve decidedly shitty putc buffer to just directly render. Old behavior kept around via -DBUFFER=1.
* I need to reimplement scrollback still
Corbenik
==============================
-## Oh god yet another rebrand of--
+This is (yet another) CFW for the 3DS. Unlike other CFWs, this was mostly written from scratch and for fun. I'm a control freak, and this carries quite a bit of my mindset being a LFS/Gentoo user.
+
+Some parts are inherited from other CFWs - e.g. the firmware loading code in src/firm is mostly based on Cakes, and the signature patch bytecode is roughly based on the implementation in Luma3DS.
-No. Definitely not.
+Out of the bunch, corbenik is most similar to cakes of the bunch, in that it uses external patches. Unlike cakes, patches consist of a headered bytecode file. See `doc/bytecode.md`, `host/bytecode_asm.py` and `patch/*` for more on this.
-This is (yet another) CFW for the 3DS. Unlike other CFWs, this was mostly written from scratch and for fun. I'm a control freak, and this carries quite a bit of my mindset, and some OSDev and self-done RE with it.
+## Oh god yet another rebrand of--
-Some parts are inherited from other CFWs - e.g. the firmware loading code in src/firm is mostly based on Cakes, and sigpatch/firmprot/svcbackdoor were loosely based on Luma3DS's patches.
+No. This is >75% original code. If you notice, there's a large amount of history because I didn't just dump the code on github; it's been in a repo all along while I worked on it and before it was made public.
-The *plan* at least is to be most similar to Cakes out of the bunch. That is, it will use external patches from the SD card's filesystem.
+I also am not claiming to have written everything. See `doc/credits.md`.
-Unlike cakes, patches will be some form of program, be it bytecode, a scripting language or dynamically loaded binaries. This is not yet implemented due to technical problems, but IS on the roadmap to get done as soon as I figure out *why* it isn't working. This code is located in a branch offline, due to the fact that it doesn't work.
+It shares close to zero of the actual architecture with other CFWs, only trivially implemented parts that look the same no matter who coded them.
## Rationale
-I was initially going to make dynamic cakes, but I quickly realized a fatal flaw in any "patch" format: what you can do from a patch is limited to what the parser handles. This exact problem is why sempatches are used sometimes instead of classic diffs on the LKML, and it isn't a problem that will be solved without code. In my opinion, the best way to fix it is to simply externalize patches.
+I was initially going to make dynamic cakes, but I quickly realized a fatal flaw in any "patch" format: what you can do from a patch is limited to what the parser handles. This exact problem is why sempatches are used sometimes instead of classic diffs on the LKML, and it isn't a problem that will be solved without code. In my opinion, the best way to fix it is to simply externalize patches as programs. I also had a number of mad science experiments which would be very hard to perform in the context of ReiNAND based firmwares, and Cakes wouldn't make it easy either.
## Comparison
-If you want to know how Corbenik sizes up to other CFWs as of NOW - see `doc/features.md`. I don't intend to sugarcoat - Corbenik is under development and is incomplete. There will be no stable release until a number of common features are implemented, and patches have been externalized.
+If you want to know how Corbenik sizes up to other CFWs as of NOW - see `doc/features.md`. I don't intend to sugarcoat - Corbenik is under development and is incomplete. There will be no stable release until a number of common features are implemented, such as emunand.
-That said, feedback is welcome, but don't report anything obvious. Chances are I know, since Corbenik is my day-to-day CFW now.
+That said, feedback is welcome, but don't report anything obvious. Chances are I know, since Corbenik is my day-to-day CFW now, and I'll run into the same bugs as you.
For compilation instructions, see `doc/compiling.md`.
-Unless otherwise noted, everything original in this repo can be used under the terms of the GNU GPLv2 or later. This includes situations where there's no copyright header within a source file. I get lazy with those; assume everything can be used under the GPL, or files were from software licensed GPLv2 or later (and thus are upgraded.) No source files within this repo bear questionable licenses.
+Unless otherwise noted, everything original in this repo can be used under the terms of the GNU GPLv3 or later. This includes situations where there's no copyright header within a source file. I get lazy with those; assume everything can be used under the GPL, or files were from software licensed GPLv2 or later (and thus are upgraded.) No source files within this repo bear questionable licenses.
## Quote of the Day
* Make config file for corbenik plaintext. Nobody likes binary configs. They suck. Massively. Especially when you fuck up a setting and need to change it on something that isn't a 3ds.
* In place firmware loading/patching.
* Weird hack central - Developer 11.4 leaked. Allow injecting debug module? Ehehehehe. No clue what the point of this would be, but hey, it'd be FUCKING cool, man.
+ * Optimize the code in svc. It's slow, because it checks the filesystem for 0xff files that don't exist. It should list the dir instead.
Shortterm
-------------
+++ /dev/null
-#include "common.h"
-#include "firm/fcram.h"
-#include "firm/firm.h"
-
-// Yes, this is EXACTLY what it looks like. We dynamically link and
-// load patches as binaries; they use functions from corbenik to do
-// the work, and therefore must have a function table in them.
-
-// See vco/template for how this magic works.
-
-// This ensures relatively small patches while also having incredible
-// flexibility unlike a 'patch format'.
-
-extern exefs_h* firm_p9_exefs;
-exefs_h*
-get_firm_proc9_exefs()
-{
- return firm_p9_exefs;
-}
-
-extern exefs_h* twl_firm_p9_exefs;
-exefs_h*
-get_twl_proc9_exefs()
-{
- return twl_firm_p9_exefs;
-}
-
-extern exefs_h* agb_firm_p9_exefs;
-exefs_h*
-get_agb_proc9_exefs()
-{
- return agb_firm_p9_exefs;
-}
-
-int
-execp(char* path)
-{
- int basename = 0;
- for (basename = strlen(path); basename > 0; basename--)
- if (path[basename] == '/')
- break;
- basename++;
-
- fprintf(stderr, "Exec: %s\n", &path[basename]);
-
- struct system_patch patch;
-
- // Load patch from path.
- FILE* f = fopen(path, "r");
- fread(&patch, 1, sizeof(patch), f);
-
- fprintf(stderr, " [h]");
-
- fread((uint8_t*)FCRAM_PATCHBIN_EXEC_LOC, 1, patch.patch_size, f);
-
- fprintf(stderr, "[x]");
-
- fclose(f);
-
- fprintf(stderr, "[b]\n");
-
- int (*patch_loc)() = (void*)FCRAM_PATCHBIN_EXEC_LOC;
-
- int ret = (*patch_loc)();
-
- fprintf(stderr, " Exit: %d\n", ret);
-
- return ret;
-}
set 1, 0xE0
*/
+extern exefs_h* firm_p9_exefs;
+
PATCH(aadowngrade)
{
- exefs_h* firm_p9_exefs = get_firm_proc9_exefs();
-
uint8_t* firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) +
firm_p9_exefs->fileHeaders[0].offset;
uint32_t size = firm_p9_exefs->fileHeaders[0].size;
set 4, 0x00, 0x20, 0xC0, 0x46
*/
+extern exefs_h* firm_p9_exefs;
+
PATCH(firmprot)
{
- exefs_h* firm_p9_exefs = get_firm_proc9_exefs();
-
uint8_t* firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) +
firm_p9_exefs->fileHeaders[0].offset;
uint32_t size = firm_p9_exefs->fileHeaders[0].size;
set 4, 0x00, 0x20, 0x70, 0x47
*/
+extern exefs_h* firm_p9_exefs;
+
PATCH(signatures)
{
- exefs_h* firm_p9_exefs = get_firm_proc9_exefs();
// Look for signature checks
char str[] = PATH_SERVICES "/00.bin";
char* at = str + (strlen(str) - 6);
- for (uint32_t i = 0; i <= 0xff; i++) {
- // Get string for svc.
- at[0] = ("0123456789abcdef")[((i >> 4) & 0xf)];
- // This is just hexdump. Nothing complicated.
- at[1] = ("0123456789abcdef")[(i & 0xf)];
+ // FIXME - This is really slow. Some way to optimize it?
+ for (uint32_t i = 0; i <= 0xf; i++) {
+ // Get string for svc.
+ at[0] = ("0123456789abcdef")[i];
- FILE* data = fopen(str, "r");
- if (!data) {
- continue; // No file for svc. Move on.
- }
+ for (uint32_t j = 0; j < 0xf; j++) {
+ // This is just hexdump. Nothing complicated.
+ at[1] = ("0123456789abcdef")[j];
- // Refuse to replace non-NULL services unless the user says to.
- if (svcTable[i] && !config.options[OPTION_REPLACE_ALLOCATED_SVC]) {
- fclose(data);
- fprintf(stderr, "Svc: %x non-null, moving on\n", i);
- continue;
- }
+ FILE* data = fopen(str, "r");
+ if (!data)
+ continue; // No file for svc. Move on.
- uint32_t size = fsize(data);
- uint8_t* read_to = (void*)FCRAM_JUNK_LOCATION;
+ uint32_t svc = (i << 4) & j;
- fprintf(stderr, "Svc: %s, %d bytes\n", at, size);
+ // Refuse to replace non-NULL services unless the user says to.
+ if (svcTable[svc] && !config.options[OPTION_REPLACE_ALLOCATED_SVC]) {
+ fclose(data);
+ fprintf(stderr, "Svc: %x non-null, moving on\n", i);
+ continue;
+ }
- fread(read_to, 1, size, data);
+ uint32_t size = fsize(data);
+ uint8_t* read_to = (void*)FCRAM_JUNK_LOCATION;
- fclose(data);
+ fprintf(stderr, "Svc: %s, %d bytes\n", at, size);
- if (!freeSpace) {
- for (freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF;
- freeSpace++)
- ;
- }
+ fread(read_to, 1, size, data);
- fprintf(stderr, "Svc: Copy code to %x\n", (uint32_t)freeSpace);
+ fclose(data);
- memcpy(freeSpace, read_to, size);
- svcTable[i] =
- 0xFFFF0000 + ((uint8_t*)freeSpace - (uint8_t*)exceptionsPage);
+ if (!freeSpace) {
+ for (freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF;
+ freeSpace++)
+ ;
+ }
- freeSpace +=
- size; // We keep track of this because there's more than 7B free.
+ fprintf(stderr, "Svc: Copy code to %x\n", (uint32_t)freeSpace);
- fprintf(stderr, "Svc: entry set as %x\n", svcTable[0x7B]);
+ memcpy(freeSpace, read_to, size);
+ svcTable[svc] =
+ 0xFFFF0000 + ((uint8_t*)freeSpace - (uint8_t*)exceptionsPage);
+
+ freeSpace +=
+ size; // We keep track of this because there's more than 7B free.
+
+ fprintf(stderr, "Svc: entry set as %x\n", svcTable[svc]);
+ }
}
return 0;
// TODO - Basically all this needs to move to patcher programs.
uint32_t wait_key();
-int execp(char* path);
extern int patch_signatures();
extern int patch_firmprot();
static unsigned int top_cursor_x = 0, top_cursor_y = 0;
static unsigned int bottom_cursor_x = 0, bottom_cursor_y = 0;
+#ifdef BUFFER
static char text_buffer_top[TEXT_TOP_HEIGHT * TEXT_TOP_WIDTH + 1];
static char text_buffer_bottom[TEXT_BOTTOM_HEIGHT * TEXT_BOTTOM_WIDTH + 1];
static char color_buffer_top[TEXT_TOP_HEIGHT * TEXT_TOP_WIDTH + 1];
static char color_buffer_bottom[TEXT_BOTTOM_HEIGHT * TEXT_BOTTOM_WIDTH + 1];
+#endif
static uint32_t colors[16] = {
0x000000, // Black
}
}
+#ifdef BUFFER
void
clear_text(uint8_t* screen)
{
}
}
}
+#endif
void
clear_screen(uint8_t* screen)
{
clear_disp(screen);
+#ifdef BUFFER
clear_text(screen);
+#endif
}
void
_UNUSED unsigned int height = 0;
unsigned int* cursor_x = NULL;
unsigned int* cursor_y = NULL;
+#ifdef BUFFER
char* colorbuf = NULL;
char* strbuf = NULL;
-
+#else
+ uint8_t* screen = NULL;
+#endif
unsigned char* color = NULL;
if (buf == TOP_SCREEN) {
width = TEXT_TOP_WIDTH;
height = TEXT_TOP_HEIGHT;
+#ifdef BUFFER
colorbuf = color_buffer_top;
strbuf = text_buffer_top;
+#else
+ screen = framebuffers->top_left;
+#endif
cursor_x = &top_cursor_x;
cursor_y = &top_cursor_y;
color = &color_top;
} else if (buf == BOTTOM_SCREEN) {
width = TEXT_BOTTOM_WIDTH;
height = TEXT_BOTTOM_HEIGHT;
+#ifdef BUFFER
colorbuf = color_buffer_bottom;
strbuf = text_buffer_bottom;
+#else
+ screen = framebuffers->bottom;
+#endif
cursor_x = &bottom_cursor_x;
cursor_y = &bottom_cursor_y;
color = &color_bottom;
}
while (cursor_y[0] >= height) {
+#ifdef BUFFER
// Scroll.
for (unsigned int y = 0; y < height - 1; y++) {
memset(&strbuf[y * width], 0, width);
memset(&strbuf[(height - 1) * width], 0, width);
memset(&colorbuf[(height - 1) * width], 0, width);
- cursor_y[0]--;
-
clear_disp(buf); // Clear screen.
+#else
+ clear_disp(buf);
+ cursor_x[0] = 0;
+ cursor_y[0] = 0;
+/* uint32_t col = SCREEN_TOP_HEIGHT * SCREEN_DEPTH;
+ uint32_t one_c = 8 * SCREEN_DEPTH;
+ for (unsigned int x = 0; x < width * 8; x++) {
+ memmove(&screen[x * col + one_c], &screen[x * col + one_c], col - one_c);
+ } */
+#endif
+ cursor_y[0]--;
}
switch (c) {
case '\n':
+#ifdef BUFFER
strbuf[cursor_y[0] * width + cursor_x[0]] = 0;
colorbuf[cursor_y[0] * width + cursor_x[0]] = 0;
+#endif
cursor_y[0]++;
// Fall through intentional.
case '\r':
cursor_x[0] = 0; // Reset to beginning of line.
break;
default:
+#ifdef BUFFER
strbuf[cursor_y[0] * width + cursor_x[0]] = c;
colorbuf[cursor_y[0] * width + cursor_x[0]] = *color;
colorbuf[cursor_y[0] * width + cursor_x[0] + 1] = 0;
}
+#else
+ draw_character(screen, c, cursor_x[0], cursor_y[0], colors[(*color >> 4) & 0xF], colors[*color & 0xF]);
+#endif
+
cursor_x[0]++;
break;
fflush(void* channel)
{
if (channel == TOP_SCREEN) {
+#ifdef BUFFER
if (kill_output)
return;
color_bg);
}
}
+#endif
} else if (channel == BOTTOM_SCREEN) {
+#ifdef BUFFER
if (kill_output)
return;
color_bg);
}
}
+#endif
} else {
f_sync(&(((FILE*)channel)->handle)); // Sync to disk.
}