text: #include <sys/iosupport.h>
  #include <malloc.h>
  
  #include <3ds.h>
  
  #include "../core/clipboard.h"
  #include "../core/error.h"
  #include "../core/fs.h"
  #include "../core/screen.h"
  #include "../core/task/task.h"
  #include "../core/ui/ui.h"
  #include "section.h"
  #include "task/uitask.h"
  
  #define CURRENT_KPROCESS (*(void**) 0xFFFF9004)
  
  #define KPROCESS_PID_OFFSET_OLD (0xB4)
  #define KPROCESS_PID_OFFSET_NEW (0xBC)
  
  static bool backdoor_ran = false;
  static bool n3ds = false;
  static u32 old_pid = 0;
  
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wreturn-type"
  static __attribute__((naked)) Result svcGlobalBackdoor(s32 (*callback)()) {
      asm volatile(
              "svc 0x30\n"
              "bx lr"
      );
  }
  #pragma GCC diagnostic pop
  
  static s32 patch_pid_kernel() {
      u32 *pidPtr = (u32*) (CURRENT_KPROCESS + (n3ds ? KPROCESS_PID_OFFSET_NEW : KPROCESS_PID_OFFSET_OLD));
  
      old_pid = *pidPtr;
      *pidPtr = 0;
  
      backdoor_ran = true;
      return 0;
  }
  
  static s32 restore_pid_kernel() {
      u32 *pidPtr = (u32*) (CURRENT_KPROCESS + (n3ds ? KPROCESS_PID_OFFSET_NEW : KPROCESS_PID_OFFSET_OLD));
  
      *pidPtr = old_pid;
  
      backdoor_ran = true;
      return 0;
  }
  
  static bool attempt_patch_pid() {
      backdoor_ran = false;
      APT_CheckNew3DS(&n3ds);
  
      svcGlobalBackdoor(patch_pid_kernel);
      srvExit();
      srvInit();
      svcGlobalBackdoor(restore_pid_kernel);
  
      return backdoor_ran;
  }
  
  static void (*exit_funcs[16])()= {NULL};
  static u32 exit_func_count = 0;
  
  static void* soc_buffer = NULL;
  
  void cleanup_services() {
      for(u32 i = 0; i < exit_func_count; i++) {
          if(exit_funcs[i] != NULL) {
              exit_funcs[i]();
              exit_funcs[i] = NULL;
          }
      }
  
      exit_func_count = 0;
  
      if(soc_buffer != NULL) {
          free(soc_buffer);
          soc_buffer = NULL;
      }
  }
  
  #define INIT_SERVICE(initStatement, exitFunc) (R_SUCCEEDED(res = (initStatement)) && (exit_funcs[exit_func_count++] = (exitFunc)))
  
  Result init_services() {
      Result res = 0;
  
      soc_buffer = memalign(0x1000, 0x100000);
      if(soc_buffer != NULL) {
          Handle tempAM = 0;
          if(R_SUCCEEDED(res = srvGetServiceHandle(&tempAM, "am:net"))) {
              svcCloseHandle(tempAM);
  
              if(INIT_SERVICE(amInit(), amExit)
                 && INIT_SERVICE(cfguInit(), cfguExit)
                 && INIT_SERVICE(acInit(), acExit)
                 && INIT_SERVICE(ptmuInit(), ptmuExit)
                 && INIT_SERVICE(pxiDevInit(), pxiDevExit)
                 && INIT_SERVICE(httpcInit(0), httpcExit)
                 && INIT_SERVICE(socInit(soc_buffer, 0x100000), (void (*)()) socExit));
          }
      } else {
          res = R_APP_OUT_OF_MEMORY;
      }
  
      if(R_FAILED(res)) {
          cleanup_services();
      }
  
      return res;
  }
  
  static u32 old_time_limit = UINT32_MAX;
  
  void init() {
      gfxInitDefault();
  
      Result romfsRes = romfsInit();
      if(R_FAILED(romfsRes)) {
          error_panic("Fallo al montar archivos de \nRom (RomFS): %08lX", romfsRes);
          return;
      }
  
      if(R_FAILED(init_services())) {
          if(!attempt_patch_pid()) {
              error_panic("Consola no hackeada.\nPor favor hackeela e intentelo de nuevo.");
              return;
          }
  
          Result initRes = init_services();
          if(R_FAILED(initRes)) {
              error_panic("Fallo al iniciar servicios: %08lX", initRes);
              return;
          }
      }
  
      osSetSpeedupEnable(true);
  
      APT_GetAppCpuTimeLimit(&old_time_limit);
      Result cpuRes = APT_SetAppCpuTimeLimit(30);
      if(R_FAILED(cpuRes)) {
          error_panic("Fallo al establecer el limite de \ntiempo de la CPU: %08lX", cpuRes);
          return;
      }
  
      AM_InitializeExternalTitleDatabase(false);
  
      screen_init();
      ui_init();
      task_init();
  }
  
  void cleanup() {
      clipboard_clear();
  
      task_exit();
      ui_exit();
      screen_exit();
  
      if(old_time_limit != UINT32_MAX) {
          APT_SetAppCpuTimeLimit(old_time_limit);
      }
  
      osSetSpeedupEnable(false);
  
      cleanup_services();
  
      romfsExit();
  
      gfxExit();
  }
  
  int main(int argc, const char* argv[]) {
      if(argc > 0 && envIsHomebrew()) {
          fs_set_3dsx_path(argv[0]);
      }
  
      init();
  
      mainmenu_open();
      while(aptMainLoop() && ui_update());
  
      cleanup();
  
      return 0;
  }