]> Chaos Git - corbenik/corbenik.git/commitdiff
More refactoring; load_firm now uses a heuristic for unknown FIRMs, and no longer...
authorchaoskagami <chaos.kagami@gmail.com>
Thu, 1 Sep 2016 22:05:11 +0000 (18:05 -0400)
committerchaoskagami <chaos.kagami@gmail.com>
Thu, 1 Sep 2016 22:05:11 +0000 (18:05 -0400)
include/firm/firm.h
include/firm/headers.h
source/firm/firm.c
source/firm/version.c
source/menu.c

index d657b407d0cf8c1502bbbf50d40fe22ceaa6ce03..2e0b83cd53896413f58fc5ef1d6f2952e1ee0487 100644 (file)
@@ -11,6 +11,13 @@ enum consoles
     console_n3ds  ///< N3DS
 };
 
+enum firm_type
+{
+    type_native, ///< NATIVE FIRM
+    type_twl,    ///< TWL FIRM
+    type_agb     ///< AGB FIRM
+};
+
 /* Storage struct for version information of FIRMs
  */
 struct firm_signature
@@ -19,6 +26,7 @@ struct firm_signature
     unsigned int version;    ///< CDN/contents version of FIRM
     char version_string[16]; ///< Human readable version number
     enum consoles console;   ///< Console type
+    enum firm_type type;     ///< Type of FIRM.
 };
 
 extern firm_h *firm_loc;
@@ -67,4 +75,8 @@ void boot_firm();
  */
 void boot_cfw();
 
+/* Loads a firmware off disk, returning it. The memory should be free()'d when done, unless you plan to boot.
+ */
+firm_h* load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size);
+
 #endif
index 11c05d0c41ce20c975d0a380b749bf92516a8bf9..09fba3c590d105463646e45d57446886f2ae722a 100644 (file)
@@ -14,7 +14,7 @@
 #define NCCH_MAGIC        (0x4843434E) ///< Lit. "NCCH"
 #define NCSD_MAGIC        (0x4453434E) ///< Lit. "NCSD"
 #define FIRM_MAGIC        (0x4D524946) ///< Lit. "FIRM"
-#define ARM9BIN_MAGIC     (0x47704770)
+#define ARM9BIN_MAGIC     (0x47704770) ///< Lit. "GpGp"
 #define LGY_ARM9BIN_MAGIC (0xB0862000)
 
 typedef struct firm_section_h
index 4e97f7babf3ea77a4b9eedf9bad90b398b5fbe9e..56d340291c29986b3cbe12e8962b52032ea1c13f 100644 (file)
@@ -29,7 +29,7 @@ static int update_96_keys = 0;
 static volatile uint32_t *const a11_entry = (volatile uint32_t *)0x1FFFFFF8;
 
 // Fwd decl
-int decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title, uint8_t version);
+int decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig);
 
 #define SECTOR_SIZE 0x200
 
@@ -72,10 +72,13 @@ void dump_firm(firm_h** buffer, uint8_t index) {
 
     uint8_t detver = 0;
 
+    struct firm_signature s;
+    s.version = 0;
+    s.type = type_native;
     if(index == 1)
-        detver = 0x10;
+        s.version = 0x10;
 
-    if(decrypt_arm9bin((arm9bin_h*)((uint8_t*)firm + firm->section[2].offset), NATIVE_FIRM_TITLEID, detver)) {
+    if(decrypt_arm9bin((arm9bin_h*)((uint8_t*)firm + firm->section[2].offset), &s)) {
         abort("  Failed to decrypt FIRM%u arm9loader.\n", index);
     }
 
@@ -261,11 +264,11 @@ decrypt_firm_title(firm_h *dest, ncch_h *ncch, uint32_t *size, void *key)
 }
 
 int
-decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title, uint8_t version)
+decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig)
 {
     uint8_t slot = 0x15;
 
-    if (firm_title == NATIVE_FIRM_TITLEID && version > 0x0F) {
+    if (sig->type == type_native && sig->version > 0x0F) {
         uint8_t decrypted_keyx[AES_BLOCK_SIZE];
 
         slot0x11key96_init();
@@ -285,10 +288,10 @@ decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title, uint8_t version)
     use_aeskey(slot);
     ctr_decrypt(arm9bin, arm9bin, (size_t)size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, header->ctr);
 
-    if (firm_title == NATIVE_FIRM_TITLEID)
+    if (sig->type == type_native)
         return *(uint32_t *)arm9bin != ARM9BIN_MAGIC;
 
-    else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID)
+    else if (sig->type == type_twl || sig->type == type_agb)
         return *(uint32_t *)arm9bin != LGY_ARM9BIN_MAGIC;
 
     return 1;
@@ -325,25 +328,34 @@ decrypt_firm(firm_h *dest, const char *path_firmkey, const char *path_cetk, uint
 
 extern int patch_services();
 
-int
-load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size, uint64_t firm_title)
+firm_h*
+load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size)
 {
+    firm_h *dest;
+
     int status = 0;
     int firmware_changed = 0;
 
-    if (read_file(dest, path, *size) == 0) {
+    FILE* f = fopen(path, "r");
+    if (!f) {
         fprintf(stderr, "  FIRM file is missing.\n");
-        return 1;
-    } else {
-        fprintf(stderr, "  Loaded FIRM off filesystem\n");
+        return NULL;
     }
+    *size = fsize(f);
+
+    dest = (firm_h*)malloc(*size);
+
+    fread(dest, 1, *size, f);
+
+    fclose(f);
+    fprintf(stderr, "  Loaded FIRM off filesystem\n");
 
     // Check and decrypt FIRM if it is encrypted.
     if (dest->magic != FIRM_MAGIC) {
         status = decrypt_firm(dest, path_firmkey, path_cetk, size);
         if (status != 0) {
             fprintf(stderr, "  Decryption seems to have failed\n");
-            return 1;
+            return NULL;
         }
         firmware_changed = 1; // Decryption performed.
     } else {
@@ -351,6 +363,43 @@ load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char *
     }
 
     struct firm_signature *fsig = get_firm_info(dest);
+    int unknown_firm_loaded = 0;
+
+    if (fsig->version == 0xFF) {
+        // Oh boy, here's a fun case. We have no clue what the user is loading, so
+        // a number of additional sanity checks and autodetection have to be done.
+
+        unknown_firm_loaded = 1;
+
+        fsig->type = type_native;
+
+        if (dest->section[3].size != 0) {
+            // Check for the presence of a TWL/AGB only sysmodule
+            if( !memcmp((char*)dest + dest->section[0].offset + 0x400, "TwlBg", 5))
+                fsig->type = type_twl;
+            else if( !memcmp((char*)dest + dest->section[0].offset + 0x400, "AgbBg", 5))
+                fsig->type = type_agb;
+        }
+
+        for (firm_section_h *section = dest->section; section < dest->section + 4; section++) {
+            if (section->type == FIRM_TYPE_ARM9) {
+                if (fsig->type == type_native) {
+                    char* k9l = (char*)memfind((uint8_t*)dest + section->offset, section->size, "K9L", 3);
+                    if (k9l == NULL) // O3DS.
+                        fsig->console = console_o3ds;
+                    else // N3DS.
+                        fsig->console = console_n3ds;
+                } else {
+                    if (section->address == 0x08006800) // O3DS entry
+                        fsig->console = console_o3ds;
+                    else if (section->address == 0x08006000) // N3DS entry
+                        fsig->console = console_n3ds;
+                }
+
+                break;
+            }
+        }
+    }
 
     // The N3DS firm has an additional encryption layer for ARM9
     if (fsig->console == console_n3ds) {
@@ -360,21 +409,21 @@ load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char *
                 // Check whether the arm9bin is encrypted.
                 int arm9bin_iscrypt = 0;
                 uint32_t magic = *(uint32_t *)((uintptr_t)dest + section->offset + 0x800);
-                if (firm_title == NATIVE_FIRM_TITLEID)
+                if (fsig->type == type_native)
                     arm9bin_iscrypt = (magic != ARM9BIN_MAGIC);
-                else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID)
+                else if (fsig->type == type_twl || fsig->type == type_twl)
                     arm9bin_iscrypt = (magic != LGY_ARM9BIN_MAGIC);
 
                 if (arm9bin_iscrypt) {
                     // Decrypt the arm9bin.
-                    if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), firm_title, fsig->version)) {
+                    if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), fsig)) {
                         fprintf(stderr, "  ARM9 segment is encrypted\n");
-                        return 1;
+                        return NULL;
                     }
                     firmware_changed = 1; // Decryption of arm9bin performed.
                 } else {
                     fprintf(stderr, "  ARM9 segment is decrypted\n");
-                    if (firm_title == NATIVE_FIRM_TITLEID && fsig->version > 0x0F) {
+                    if (fsig->type == type_native && fsig->version > 0x0F) {
                         slot0x11key96_init(); // This has to be loaded
                                               // regardless, otherwise boot will
                                               // fail.
@@ -397,16 +446,16 @@ load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char *
         fprintf(stderr, "  Patching arm9 entrypoint\n");
 
         // Patch the entrypoint to skip arm9loader
-        if (firm_title == NATIVE_FIRM_TITLEID) {
+        if (fsig->type == type_native) {
             dest->a9Entry = 0x0801B01C;
-        } else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID) {
+        } else if (fsig->type == type_twl || fsig->type == type_agb) {
             dest->a9Entry = 0x0801301C;
         }
         // The entrypoints seem to be the same across different FIRM versions,
         //  so we don't change them.
     }
 
-    return 0;
+    return dest;
 }
 
 __attribute__ ((noreturn))
@@ -516,19 +565,15 @@ load_firms()
 
     fprintf(stderr, "FIRM load triggered.\n");
 
-    firm_loc = malloc(firm_size);
-
     fprintf(stderr, "Loading NATIVE_FIRM\n");
-    if (load_firm(firm_loc, get_opt((void*)OPTION_NFIRM_PATH), PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size, NATIVE_FIRM_TITLEID) != 0) {
+    if ((firm_loc = load_firm(get_opt((void*)OPTION_NFIRM_PATH), PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size)) != NULL) {
         abort("\n  Failed to load NATIVE_FIRM.\n");
     }
     find_proc9(firm_loc, &firm_proc9, &firm_p9_exefs);
     fprintf(stderr, "  Ver: %x, %u\n", get_firm_info(firm_loc)->version, get_firm_info(firm_loc)->console );
 
-    twl_firm_loc = malloc(twl_firm_size);
-
     fprintf(stderr, "TWL_FIRM\n");
-    if (load_firm(twl_firm_loc, get_opt((void*)OPTION_TFIRM_PATH), PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size, TWL_FIRM_TITLEID) != 0) {
+    if ((twl_firm_loc = load_firm(get_opt((void*)OPTION_TFIRM_PATH), PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size)) != NULL) {
         fprintf(stderr, "\n  TWL_FIRM failed to load.\n");
         state = 1;
     } else {
@@ -536,10 +581,8 @@ load_firms()
         fprintf(stderr, "  Ver: %x, %u\n", get_firm_info(twl_firm_loc)->version, get_firm_info(twl_firm_loc)->console );
     }
 
-    agb_firm_loc = malloc(agb_firm_size);
-
     fprintf(stderr, "AGB_FIRM\n");
-    if (load_firm(agb_firm_loc, get_opt((void*)OPTION_AFIRM_PATH), PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size, AGB_FIRM_TITLEID) != 0) {
+    if ((agb_firm_loc = load_firm(get_opt((void*)OPTION_AFIRM_PATH), PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size)) != NULL) {
         fprintf(stderr, "\n  AGB_FIRM failed to load.\n");
         state = 1;
     } else {
index a6663398fa6b7b7fc2426b236b698073dc7412fd..7a63e32e1faa4c2d99e5b09e241fb3f42f4f94ec 100644 (file)
@@ -2,6 +2,8 @@
 
 // We use the firm's section 0's hash to identify the version
 struct firm_signature firm_signatures[] = {
+    // NFIRM (old)
+
     {.sig = { 0xEE, 0xE2, 0x81, 0x2E, 0xB9, 0x10, 0x0D, 0x03, 0xFE, 0xA2, 0x3F, 0x44, 0xB5, 0x1C, 0xB3, 0x5E },
      .version = 0x1F,
      .version_string = "4.1.0",
@@ -34,6 +36,9 @@ struct firm_signature firm_signatures[] = {
      .version = 0x52,
      .version_string = "11.0.0",
      .console = console_o3ds },
+
+    // NFIRM (new)
+
     {.sig = { 0x31, 0xCC, 0x46, 0xCD, 0x61, 0x7A, 0xE7, 0x13, 0x7F, 0xE5, 0xFC, 0x20, 0x46, 0x91, 0x6A, 0xBB },
      .version = 0x04,
      .version_string = "9.0.0",
@@ -54,6 +59,9 @@ struct firm_signature firm_signatures[] = {
      .version = 0x21,
      .version_string = "11.0.0",
      .console = console_n3ds },
+
+    // TWL
+
     {.sig = { 0xE8, 0xB8, 0x82, 0xF5, 0x8C, 0xC4, 0x1B, 0x24, 0x05, 0x60, 0x6D, 0xB8, 0x74, 0xF5, 0xE5, 0xDD },
      .version = 0x16,
      .version_string = "6.2.0",
@@ -62,6 +70,9 @@ struct firm_signature firm_signatures[] = {
      .version = 0x00,
      .version_string = "9.0.0",
      .console = console_n3ds },
+
+    // AGB
+
     {.sig = { 0x65, 0xB7, 0x55, 0x78, 0x97, 0xE6, 0x5C, 0xD6, 0x11, 0x74, 0x95, 0xDD, 0x61, 0xE8, 0x08, 0x40 },
      .version = 0x0B,
      .version_string = "6.0.0",
@@ -78,7 +89,7 @@ struct firm_signature firm_signatures[] = {
      .version_string = "Dev 11.4",
      .console = console_o3ds },
 
-    {.version = 0xFF, .version_string = "Not found" } // Terminate list
+    {.version = 0xFF, .version_string = "Unknown" } // Terminate list
 };
 
 struct firm_signature *
index 953cc6e8f9087268957323cd17bfeb81aaa5199c..d38b73cc4c11e2662f743316d137ee1f647a86c5 100644 (file)
@@ -6,6 +6,25 @@
 struct options_s *patches = NULL;
 uint8_t *enable_list;
 
+#define MAX_FIRMS 50
+struct options_s *firm = NULL;
+
+static int current_menu_index_patches = 0;
+static int desc_is_fname_sto = 0;
+
+#if defined(CHAINLOADER) && CHAINLOADER == 1
+void chainload_menu();
+#endif
+
+#ifndef REL
+#define REL "master"
+#endif
+
+#define ln(s) { s, "", unselectable, 0, NULL, NULL, 0, 0 }
+#define lnh(s) { s, "", unselectable, 0, NULL, NULL, 0, 1 }
+
+void config_main_menu();
+
 static struct options_s options[] = {
     // Patches.
     { "General Options", "", unselectable, 0, NULL, NULL, 0, 1 },
@@ -93,9 +112,75 @@ static struct options_s options[] = {
     { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
 };
 
-static int current_menu_index_patches = 0;
+static struct options_s config_opts[] = {
+    { "Options",
+      "Internal options for the CFW.\nThese are part of " FW_NAME " itself.",
+      option, options, (void(*)(void*))show_menu, NULL, 0, 0 },
+    { "Patches",
+      "External bytecode patches found in `" PATH_PATCHES "`.\nYou can choose which to enable.",
+      option, NULL, (void(*)(void*))show_menu, NULL, 0, 0 },
 
-static int desc_is_fname_sto = 0;
+    // Sentinel.
+    { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
+};
+
+static struct options_s help_d[] = {
+    lnh("About"),
+    ln("  This is another 3DS CFW for power users."),
+    ln("  It seeks to address some faults in other"),
+    ln("  CFWs and is generally just another choice"),
+    ln("  for users - but primarily is intended for"),
+    ln("  developers. It is not for the faint of heart."),
+    ln(""),
+    lnh("Usage"),
+    ln("  A         -> Select/Toggle/Increment"),
+    ln("  B         -> Back/Boot"),
+    ln("  X         -> Decrement"),
+    ln("  Select    -> Help/Information"),
+    ln("  Down      -> Down"),
+    ln("  Right     -> Down five"),
+    ln("  Up        -> Up"),
+    ln("  Left      -> Up five"),
+    ln("  L+R+Start -> Menu Screenshot"),
+    ln(""),
+    lnh("Credits"),
+    ln("  @mid-kid, @Wolfvak, @Reisyukaku, @AuroraWright"),
+    ln("  @d0k3, @TuxSH, @Steveice10, @delebile,"),
+    ln("  @Normmatt, @b1l1s, @dark-samus, @TiniVi,"),
+    ln("  @gemarcano, and anyone else I may have"),
+    ln("  forgotten (yell at me, please!)"),
+    ln(""),
+    lnh("  <https://github.com/chaoskagami/corbenik>"),
+    { NULL, NULL, unselectable, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
+};
+
+static struct options_s main_s[] = {
+    { "Configuration",
+      "Configuration options for the CFW.",
+      option, 0, config_main_menu, NULL, 0, 0 },
+    { "Readme",
+      "Mini-readme.\nWhy are you opening help on this, though?\nThat's kind of silly.",
+      option, help_d, (void(*)(void*))show_menu, NULL, 0, 0 },
+    { "Reboot",
+      "Reboots the console.",
+      option, 0, reset, NULL, 0, 0 },
+    { "Power off",
+      "Powers off the console.",
+      option, 0, poweroff, NULL, 0, 0 },
+#if defined(CHAINLOADER) && CHAINLOADER == 1
+    { "Chainload",
+      "Boot another ARM9 payload file.",
+       option, 0, chainload_menu, NULL, 0, 0 },
+#endif
+    { "Boot Firmware",
+      "Patches the firmware, and boots it.",
+      break_menu, 0, NULL, NULL, 0, 0 },
+
+    // Sentinel.
+    { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
+};
+
+//===================================================================================================
 
 char* patch_get_enable(void* val) {
     uint32_t opt = (uint32_t)val;
@@ -147,42 +232,71 @@ void patch_func(char* fpath) {
     }
 }
 
-#ifndef REL
-#define REL "master"
-#endif
+//===================================================================================================
+#if 0
+char* get_nfirm(void* val) {
+    return config->firms[0];
+}
 
-#define ln(s) { s, "", unselectable, 0, NULL, NULL, 0, 0 }
-#define lnh(s) { s, "", unselectable, 0, NULL, NULL, 0, 1 }
+char* get_tfirm(void* val) {
+    return config->firms[1];
+}
 
-static struct options_s help_d[] = {
-    lnh("About"),
-    ln("  This is another 3DS CFW for power users."),
-    ln("  It seeks to address some faults in other"),
-    ln("  CFWs and is generally just another choice"),
-    ln("  for users - but primarily is intended for"),
-    ln("  developers. It is not for the faint of heart."),
-    ln(""),
-    lnh("Usage"),
-    ln("  A         -> Select/Toggle/Increment"),
-    ln("  B         -> Back/Boot"),
-    ln("  X         -> Decrement"),
-    ln("  Select    -> Help/Information"),
-    ln("  Down      -> Down"),
-    ln("  Right     -> Down five"),
-    ln("  Up        -> Up"),
-    ln("  Left      -> Up five"),
-    ln("  L+R+Start -> Menu Screenshot"),
-    ln(""),
-    lnh("Credits"),
-    ln("  @mid-kid, @Wolfvak, @Reisyukaku, @AuroraWright"),
-    ln("  @d0k3, @TuxSH, @Steveice10, @delebile,"),
-    ln("  @Normmatt, @b1l1s, @dark-samus, @TiniVi,"),
-    ln("  @gemarcano, and anyone else I may have"),
-    ln("  forgotten (yell at me, please!)"),
-    ln(""),
-    lnh("  <https://github.com/chaoskagami/corbenik>"),
-    { NULL, NULL, unselectable, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
-};
+char* get_afirm(void* val) {
+    return config->firms[2];
+}
+
+char* patch_get_enable(void* val) {
+    uint32_t opt = (uint32_t)val;
+    uint32_t raw = enable_list[opt];
+    static char ret[2] = " ";
+    ret[0] = ' ';
+    if (raw == 1)
+        ret[0] = '*';
+    return ret;
+}
+
+void patch_set_enable(void* val) {
+    uint32_t opt = (uint32_t)val;
+    enable_list[opt] = !enable_list[opt];
+}
+
+void patch_func(char* fpath) {
+    FILINFO f2;
+    if (f_stat(fpath, &f2) != FR_OK)
+        return;
+
+    if (!(f2.fattrib & AM_DIR)) {
+        struct system_patch p;
+        read_file(&p, fpath, sizeof(struct system_patch));
+
+        if (memcmp(p.magic, "AIDA", 4))
+            return;
+
+        patches[current_menu_index_patches].name = strdup_self(p.name);
+        if (desc_is_fname_sto)
+            patches[current_menu_index_patches].param = strdup_self(fpath);
+        else
+            patches[current_menu_index_patches].desc = strdup_self(p.desc);
+
+        uint32_t val = p.uuid;
+
+        patches[current_menu_index_patches].param = (void*)val;
+
+        patches[current_menu_index_patches].handle = option;
+        patches[current_menu_index_patches].indent = 0;
+
+        patches[current_menu_index_patches].func  = patch_set_enable;
+        patches[current_menu_index_patches].value = patch_get_enable;
+
+        if (desc_is_fname_sto)
+            enable_list[p.uuid] = 0;
+
+        current_menu_index_patches++;
+    }
+}
+#endif
+//===================================================================================================
 
 void
 reset()
@@ -210,22 +324,6 @@ poweroff()
     ctr_system_poweroff();
 }
 
-#if defined(CHAINLOADER) && CHAINLOADER == 1
-void chainload_menu();
-#endif
-
-static struct options_s config_opts[] = {
-    { "Options",
-      "Internal options for the CFW.\nThese are part of " FW_NAME " itself.",
-      option, options, (void(*)(void*))show_menu, NULL, 0, 0 },
-    { "Patches",
-      "External bytecode patches found in `" PATH_PATCHES "`.\nYou can choose which to enable.",
-      option, NULL, (void(*)(void*))show_menu, NULL, 0, 0 },
-
-    // Sentinel.
-    { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
-};
-
 // This is dual purpose. When we actually list
 // patches to build the cache - desc_is_fname
 // will be set to 1.
@@ -265,32 +363,6 @@ void config_main_menu() {
     generate_patch_cache();
 }
 
-static struct options_s main_s[] = {
-    { "Configuration",
-      "Configuration options for the CFW.",
-      option, 0, config_main_menu, NULL, 0, 0 },
-    { "Readme",
-      "Mini-readme.\nWhy are you opening help on this, though?\nThat's kind of silly.",
-      option, help_d, (void(*)(void*))show_menu, NULL, 0, 0 },
-    { "Reboot",
-      "Reboots the console.",
-      option, 0, reset, NULL, 0, 0 },
-    { "Power off",
-      "Powers off the console.",
-      option, 0, poweroff, NULL, 0, 0 },
-#if defined(CHAINLOADER) && CHAINLOADER == 1
-    { "Chainload",
-      "Boot another ARM9 payload file.",
-       option, 0, chainload_menu, NULL, 0, 0 },
-#endif
-    { "Boot Firmware",
-      "Patches the firmware, and boots it.",
-      break_menu, 0, NULL, NULL, 0, 0 },
-
-    // Sentinel.
-    { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two.
-};
-
 void
 menu_handler()
 {