[
  {
    "path": ".gitignore",
    "content": "*.bin\n*.o\nfirst\nsecond\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 molecule\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "CC=arm-vita-eabi-gcc\nCFLAGS=-Os -fno-builtin-printf -fPIC -fno-builtin-memset -Wall -Wextra -Wno-unused-variable -DFW_365\nOBJCOPY=arm-vita-eabi-objcopy\nLDFLAGS=-nodefaultlibs -nostdlib\n\nfat.bin: first.bin second.bin\n\t./gen.py fat.tpl first.bin second.bin fat.bin\n\nfirst.bin: first\n\t$(OBJCOPY) -O binary $^ $@\n\nsecond.bin: second\n\t$(OBJCOPY) -O binary $^ $@\n\nfirst: first.o\n\t$(CC) -o $@ $^ $(LDFLAGS) -T first.x\n\nsecond: second.o\n\t$(CC) -o $@ $^ $(LDFLAGS) -T second.x\n\nclean:\n\t-rm -f fat.bin second.bin first.bin *.o\n"
  },
  {
    "path": "README.md",
    "content": "You need [vitasdk](https://vitasdk.org/).\n\n1. `make` the payload\n2. Copy `fat.bin` to `installer/res`\n3. CMake the installer `mkdir build && cd build && cmake .. && make`\n\nFirmware specific offsets are in first.c and nsbl.h. Logo is raw framebuffer data gzipped. If you make this too big (bigger than original logo size), you WILL perma-brick your Vita.\n\nThe source is for advanced users only. Users should download the [prebuilt package](https://enso.henkaku.xyz/). If something goes wrong, you WILL perma-brick your Vita. There is no recovery, even if you have a hardware mod. The only possible recovery is if you have a hardware mod and you dump the eMMC _before_ getting bricked, then you can restore the dump. Dumps are device-specific and encrypted with a device-specific key.\n\nAgain, even if you just change the logo, there's a good chance you will perma-brick your Vita. You have been warned.\n"
  },
  {
    "path": "first.c",
    "content": "/* first.c -- fixup environment and exec second payload\n *\n * Copyright (C) 2017 molecule\n *\n * This software may be modified and distributed under the terms\n * of the MIT license.  See the LICENSE file for details.\n */\n#include <inttypes.h>\n#include \"nsbl.h\"\n\n// ALL OFFSETS ARE SPECIFIC TO 3.60\n\n#define SECOND_PAYLOAD_OFFSET (5)\n#define SECOND_PAYLOAD_SIZE (0x4000)\n\nvoid go(void) {\n    printf(\"first\\n\");\n    // first payload reinits os0 device and reads second payload from emmc\n    // this is because we only have 0x180 bytes for first payload\n\n    memset((char*)0x511673A0, 0, 0x600); // clean after us\n\n    // we need to patch call to read block 1 instead of block 0 as the master block\n    // now that we store a copy of real partition table in block 1\n    *(uint16_t*)0x510202CE = 0x2101; // movs r1, #1\n\n    clean_dcache((void *)0x510202C0, 0x20);\n    flush_icache();\n\n    // reinit os0_dev\n    int (*fat_init_dev)() = (void*)0x5101FF21;\n    char *os0_dev = (void*)0x51167784;\n    int ret = fat_init_dev(os0_dev, 0x110000, 0x510010FD, 0x51028010); // os0_dev, flags, read_block, some_storage\n\n    // TODO: what do these do? but we need them for some reason\n    *(uint32_t*)(os0_dev + 0x2C) = 0;\n    *(uint32_t*)(os0_dev + 0x78) = 0x1A000100;\n    *(uint32_t*)(os0_dev + 0x84) = 0x1A001000;\n    *(uint32_t*)(os0_dev + 0x90) = 0x0001002B;\n\n    // restore corrupted boot args with our copy\n    memcpy(boot_args, (*sysroot_ctx_ptr)->boot_args, sizeof(*boot_args));\n\n    // allocate buffer for code\n    SceKernelAllocMemBlockKernelOpt opt;\n    int blk;\n    void *base;\n    void (*f)();\n    memset(&opt, 0, sizeof(opt));\n    opt.size = sizeof(opt);\n    opt.attr = 0xA0400000;\n    blk = sceKernelAllocMemBlock(\"\", 0x1020D006, SECOND_PAYLOAD_SIZE, NULL);\n    f = NULL;\n    if (blk >= 0) {\n        if (sceKernelGetMemBlockBase(blk, &base) >= 0) {\n            // read code buffer\n            if (read_block_os0(0x51028010, SECOND_PAYLOAD_OFFSET, SECOND_PAYLOAD_SIZE/512, base) >= 0) {\n                // TODO: perhaps add some simple integrity check here?\n                if (sceKernelRemapBlock(blk, 0x1020D005) >= 0) {\n                    clean_dcache(base, SECOND_PAYLOAD_SIZE);\n                    flush_icache();\n                    f = (void*)(base + 1);\n                }\n            }\n        }\n    }\n\n    // fallback: if for some reason we weren't able to allocate/read the payload, then don't run it\n    if (f != NULL) {\n        f();\n    }\n\n    // restore context and resume boot\n\n    uint32_t *sp = *(uint32_t**)(0x51030100 + 0x220); // sp top for core 0\n    uint32_t *old_sp = sp - 0x11d;\n\n    // r0: 0x51167784 os0_dev\n    // r1: 0xfffffffe\n    // r2: sp - 0x110\n    // r3: 0\n\n    __asm__ volatile (\n        \"movw r0, #0x7784\\n\"\n        \"movt r0, #0x5116\\n\"\n        \"movw r1, #0xfffe\\n\"\n        \"movt r1, #0xffff\\n\"\n        \"mov r2, %0\\n\"\n        \"mov r3, #0\\n\"\n        \"mov sp, %1\\n\"\n        \"mov r4, %2\\n\"\n        \"bx r4\\n\" :: \"r\" (sp - 0x110), \"r\" (old_sp), \"r\" (0x5101F779) : \"r0\", \"r1\", \"r2\", \"r3\", \"r4\"\n    );\n}\n\n__attribute__ ((section (\".text.start\"), naked)) void start(void)  {\n    __asm__ volatile (\n        \"mov r0, #0\\n\"\n        \"movt r0, #0x51f0\\n\"\n        \"mov sp, r0\\n\"\n        \"b go\\n\"\n    );\n}\n"
  },
  {
    "path": "first.x",
    "content": "OUTPUT_FORMAT(\"elf32-littlearm\", \"elf32-bigarm\", \"elf32-littlearm\")\nOUTPUT_ARCH(arm)\n\nENTRY(start)\n\nSECTIONS\n{\n  . = 0x51167220;\n  .text   : { *(.text.start) *(.text   .text.*   .gnu.linkonce.t.*) *(.sceStub.text.*) }\n  .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }\n  .data   : { *(.data   .data.*   .gnu.linkonce.d.*) }\n  .bss    : { *(.bss    .bss.*    .gnu.linkonce.b.*) *(COMMON) }\n}\n"
  },
  {
    "path": "gen.py",
    "content": "#!/usr/bin/env python2\n\nfrom sys import argv, exit\nimport struct\n\ndef p32(x):\n\treturn struct.pack(\"<I\", x)\n\ndef ins(dst, off, src):\n\tif type(src) is list:\n\t\tout = b\"\"\n\t\tfor x in src:\n\t\t\tout += p32(x)\n\telse:\n\t\tout = src\n\tdst[off:off+len(out)] = out\n\ndef main():\n\t# ./gen.py fat.tpl payload.bin fat.out\n\n\twith open(argv[1], \"rb\") as fin:\n\t\tfat_tpl = bytearray(fin.read())\n\twith open(argv[2], \"rb\") as fin:\n\t\tfirst_bin = fin.read()\n\twith open(argv[3], \"rb\") as fin:\n\t\tsecond_bin = fin.read()\n\n\tif len(first_bin) >= 0x180:\n\t\tprint \"your first payload is too big!\"\n\t\texit(-1)\n\tif len(second_bin) >= 0x2000:\n\t\tprint \"your second payload is too big!\"\n\t\texit(-1)\n\n\ttemp_store = 0x511671A0\n\tpivot = 0x5101504C # e890b672 ldm r0, {r1, r4, r5, r6, r9, sl, ip, sp, pc}\n\tpop_pc = 0x5100155F\n\tpop_r0_pc = 0x5100E4D1\n\tpop_r1_r2_r4_r6_pc = 0x51024C53\n\tblx_r3_pop_r3_pc = 0x510058AF\n\tpop_r3_pc = 0x510058B1\n\tflush_icache = 0x51014691 # ICIALLUIS\n\tclean_dcache = 0x510146DD\n\tdebug_printf = 0x51012D45\n\n\tpivot_args = [0, 0, 0, 0, 0, 0, 0, temp_store + 0x40, pop_pc]\n\trop = [\n\t\tpop_r0_pc,\n\t\ttemp_store,                # r0\n\n\t\tpop_r1_r2_r4_r6_pc,\n\t\t0x800,                     # r1\n\t\t0,                         # r2\n\t\t0,                         # r4\n\t\t0,                         # r6\n\n\t\tpop_r3_pc,\n\t\tclean_dcache,              # r3\n\n\t\tblx_r3_pop_r3_pc,\n\t\tflush_icache,              # r3\n\n\t\tblx_r3_pop_r3_pc,\n\t\t0,                         # r3\n\t\ttemp_store + 0x80|1,\n\t]\n\n\tBASE = 0x5400\n\n\t# write pivot_args to temp_store\n\tins(fat_tpl, BASE, pivot_args)\n\t# write rop to temp_store + 0x40\n\tins(fat_tpl, BASE + 0x40, rop)\n\t# write payload to temp_store + 0x80\n\tins(fat_tpl, BASE + 0x80, first_bin)\n\t# write second payload starting from block 5\n\tins(fat_tpl, 5 * 0x200, second_bin)\n\t# write func ptr\n\tins(fat_tpl, BASE + 0x638, [pivot])\n\t# write R0 arg to func ptr\n\tins(fat_tpl, BASE + 0x63C, [temp_store])\n\n\twith open(argv[4], \"wb\") as fout:\n\t\tfout.write(fat_tpl)\n\n\nif __name__ == \"__main__\":\n\tmain()\n"
  },
  {
    "path": "installer/.gitignore",
    "content": "build\n"
  },
  {
    "path": "installer/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8)\n\nif(NOT DEFINED CMAKE_TOOLCHAIN_FILE)\n  if(DEFINED ENV{VITASDK})\n    set(CMAKE_TOOLCHAIN_FILE \"$ENV{VITASDK}/share/vita.toolchain.cmake\" CACHE PATH \"toolchain file\")\n  else()\n    message(FATAL_ERROR \"Please define VITASDK to point to your SDK path!\")\n  endif()\nendif()\n\nset(SHORT_NAME enso_installer)\nproject(${SHORT_NAME})\ninclude(\"${VITASDK}/share/vita.cmake\" REQUIRED)\n\nset(VITA_APP_NAME \"ensō\")\nset(VITA_TITLEID  \"MLCL00003\")\n\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -std=gnu99 -fno-builtin-printf\")\nif (DEFINED SAFETY)\n  set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wall -Wextra -Werror\")\nendif()\nset(IGNORE_WARNING \"${SAFETY}\")\n\nstring(TIMESTAMP BUILD_DATE UTC)\n\nset(CID \"00000000000000000000000000000000\" CACHE STRING \"Customized CID\")\nset(PERSONALIZED 0 CACHE BOOL \"Personalize build\")\n\nstring(LENGTH ${CID} CID_LEN)\n\nif(NOT ${CID_LEN} EQUAL 32)\n  message(FATAL_ERROR \"Invalid CID\")\nendif()\n\nset(i 0)\nwhile(i LESS ${CID_LEN})\n  string(SUBSTRING ${CID} ${i} 2 ch)\n  if(${i} EQUAL 0)\n    set(BUILD_CID \"0x${ch} ^ 0xAA\")\n  else()\n    set(BUILD_CID \"${BUILD_CID}, 0x${ch} ^ 0xAA\")\n  endif()\n  math(EXPR i \"${i} + 2\")\nendwhile()\n\nconfigure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)\ninclude_directories(${PROJECT_BINARY_DIR})\n\ninclude_directories(\n  src\n)\n\nlink_directories(\n  ${CMAKE_BINARY_DIR}/emmc_helper_stubs\n  ${CMAKE_BINARY_DIR}/emmc_helper_user_stubs\n)\n\nadd_executable(${SHORT_NAME}\n  src/main.c\n  src/debug_screen.c\n  src/debug_screen_font.c\n  src/sha256.c\n)\n\nadd_dependencies(${SHORT_NAME} emmc_helper.skprx)\n\ntarget_link_libraries(${SHORT_NAME}\n  SceDisplay_stub\n  SceCtrl_stub\n  SceShellSvc_stub\n  SceProcessmgr_stub\n  SceVshBridge_stub\n  SceHttp_stub\n  SceNet_stub\n  SceNetCtl_stub\n  SceSysmodule_stub\n  SceSblUpdateMgr_stub\n  taihen_stub\n  emmc_helper_user_stub_weak\n)\n\nvita_create_self(${SHORT_NAME}.self ${SHORT_NAME} UNSAFE)\n\n# emmc_helper: kernel version\nadd_executable(emmc_helper\n  src/kernel.c\n  src/crc32.c\n)\nset_target_properties(emmc_helper\n  PROPERTIES LINK_FLAGS\n  -nostdlib\n)\ntarget_link_libraries(emmc_helper\n  k\n  gcc\n  SceThreadmgrForDriver_stub\n  SceModulemgrForDriver_stub\n  SceModulemgrForKernel_367_stub\n  SceSblAIMgrForDriver_stub\n  SceIofilemgrForDriver_stub\n  SceSysmemForDriver_stub\n  taihenForKernel_stub\n)\nvita_create_self(emmc_helper.skprx emmc_helper\n  CONFIG ${CMAKE_SOURCE_DIR}/src/kernel_exports.yml\n  UNSAFE\n)\nvita_create_stubs(emmc_helper_stubs emmc_helper ${CMAKE_SOURCE_DIR}/src/kernel_exports.yml KERNEL)\n\n# second kernel helper to work around unloading a kernel module with exported funcs\nadd_executable(kernel2\n  src/kernel2.c\n)\nset_target_properties(kernel2\n  PROPERTIES LINK_FLAGS\n  -nostdlib\n)\ntarget_link_libraries(kernel2\n  gcc\n  taihenForKernel_stub\n)\nvita_create_self(kernel2.skprx kernel2\n  CONFIG ${CMAKE_SOURCE_DIR}/src/kernel2.yml\n  UNSAFE\n)\n\n# emmc_helper: user version\nadd_executable(emmc_helper_user\n  src/user.c\n)\nset_target_properties(emmc_helper_user\n  PROPERTIES LINK_FLAGS\n  -nostdlib\n)\ntarget_link_libraries(emmc_helper_user\n  emmc_helper_stub\n)\nvita_create_self(emmc_helper_user.suprx emmc_helper_user\n  CONFIG ${CMAKE_SOURCE_DIR}/src/user_exports.yml\n  UNSAFE\n)\nvita_create_stubs(emmc_helper_user_stubs emmc_helper_user ${CMAKE_SOURCE_DIR}/src/user_exports.yml)\n\n\nadd_dependencies(emmc_helper_user emmc_helper_stubs)\nadd_dependencies(${SHORT_NAME} emmc_helper_user_stubs)\nadd_dependencies(${SHORT_NAME} kernel2.skprx)\nadd_dependencies(${SHORT_NAME} emmc_helper.skprx)\nadd_dependencies(${SHORT_NAME} emmc_helper_user.suprx)\n\nvita_create_vpk(${SHORT_NAME}.vpk ${VITA_TITLEID} ${SHORT_NAME}.self\n  VERSION ${VITA_VERSION}\n  NAME ${VITA_APP_NAME}\n  FILE ${CMAKE_SOURCE_DIR}/res/fat.bin fat.bin\n  FILE ${CMAKE_BINARY_DIR}/emmc_helper.skprx emmc_helper.skprx\n  FILE ${CMAKE_BINARY_DIR}/emmc_helper_user.suprx emmc_helper.suprx\n  FILE ${CMAKE_BINARY_DIR}/kernel2.skprx kernel2.skprx\n  FILE ${CMAKE_SOURCE_DIR}/res/icon.png sce_sys/icon0.png\n  FILE ${CMAKE_SOURCE_DIR}/res/template.xml sce_sys/livearea/contents/template.xml\n  FILE ${CMAKE_SOURCE_DIR}/res/bg.png sce_sys/livearea/contents/bg.png\n  FILE ${CMAKE_SOURCE_DIR}/res/startup.png sce_sys/livearea/contents/startup.png\n)\n"
  },
  {
    "path": "installer/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 molecule\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "installer/personalize.sh",
    "content": "#!/bin/bash\n\nmake_flags=\"-j 4\"\n\nif (( $# < 2 )); then\n  echo \"$0 git_dir [cid1 cid2 ...]\"\n  exit 0\nelse\n  git_dir=\"$1\"; shift\n  src_dir=\"`pwd`\"\nfi\n\nwhile test $# -gt 0\ndo\n  cid=\"`echo $1 | tr '[:upper:]' '[:lower:]'`\"\n  echo \"Building for ${cid}\"\n  mkdir \"/tmp/$cid\"\n  pushd \"/tmp/$cid\"\n  cmake -DPERSONALIZED=1 -DCID=\"${cid}\" \"${src_dir}\"\n  make ${make_flags}\n  popd\n  mv \"/tmp/$cid/fat_installer.vpk\" \"${git_dir}/dl/${cid}.vpk\"\n  rm -rf \"/tmp/$cid\"\n  pushd \"${git_dir}\"\n  git add \"dl/${cid}.vpk\"\n  popd\n  list=\"${list}\"$'\\n'\"${cid}\"\n  shift\ndone\n\necho \"Committing to git...\"\npushd \"${git_dir}\"\ngit commit -m \"Added new personalized builds for: ${list}\"\ngit push\necho \"Done.\"\n"
  },
  {
    "path": "installer/res/template.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<livearea style=\"a1\" format-ver=\"01.00\" content-rev=\"1\">\n\t<livearea-background>\n\t\t<image>bg.png</image>\n\t</livearea-background>\n\t\n\t<gate>\n\t\t<startup-image>startup.png</startup-image>\n\t</gate>\n</livearea>\n"
  },
  {
    "path": "installer/src/crc32.c",
    "content": "/*-\n *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or\n *  code or tables extracted from it, as desired without restriction.\n *\n *  First, the polynomial itself and its table of feedback terms.  The\n *  polynomial is\n *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0\n *\n *  Note that we take it \"backwards\" and put the highest-order term in\n *  the lowest-order bit.  The X^32 term is \"implied\"; the LSB is the\n *  X^31 term, etc.  The X^0 term (usually shown as \"+1\") results in\n *  the MSB being 1\n *\n *  Note that the usual hardware shift register implementation, which\n *  is what we're using (we're merely optimizing it by doing eight-bit\n *  chunks at a time) shifts bits into the lowest-order term.  In our\n *  implementation, that means shifting towards the right.  Why do we\n *  do it this way?  Because the calculated CRC must be transmitted in\n *  order from highest-order term to lowest-order term.  UARTs transmit\n *  characters in order from LSB to MSB.  By storing the CRC this way\n *  we hand it to the UART in the order low-byte to high-byte; the UART\n *  sends each low-bit to hight-bit; and the result is transmission bit\n *  by bit from highest- to lowest-order term without requiring any bit\n *  shuffling on our part.  Reception works similarly\n *\n *  The feedback terms table consists of 256, 32-bit entries.  Notes\n *\n *      The table can be generated at runtime if desired; code to do so\n *      is shown later.  It might not be obvious, but the feedback\n *      terms simply represent the results of eight shift/xor opera\n *      tions for all combinations of data and CRC register values\n *\n *      The values must be right-shifted by eight bits by the \"updcrc\n *      logic; the shift must be unsigned (bring in zeroes).  On some\n *      hardware you could probably optimize the shift in assembler by\n *      using byte-swap instructions\n *      polynomial $edb88320\n *\n *\n * CRC32 code derived from work by Gary S. Brown.\n */\n\n#include <stdint.h>\n#include <stddef.h>\n\nstatic uint32_t crc32_tab[] = {\n\t0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,\n\t0xe963a535, 0x9e6495a3,\t0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,\n\t0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,\n\t0xf3b97148, 0x84be41de,\t0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n\t0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,\t0x14015c4f, 0x63066cd9,\n\t0xfa0f3d63, 0x8d080df5,\t0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,\n\t0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\t0x35b5a8fa, 0x42b2986c,\n\t0xdbbbc9d6, 0xacbcf940,\t0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n\t0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,\n\t0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,\n\t0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\t0x76dc4190, 0x01db7106,\n\t0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n\t0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,\n\t0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,\n\t0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,\n\t0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n\t0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,\n\t0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,\n\t0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,\n\t0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n\t0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,\n\t0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,\n\t0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,\n\t0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n\t0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,\n\t0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,\n\t0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,\n\t0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n\t0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,\n\t0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,\n\t0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,\n\t0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n\t0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,\n\t0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,\n\t0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,\n\t0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n\t0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,\n\t0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,\n\t0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,\n\t0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n\t0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,\n\t0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,\n\t0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d\n};\n\nuint32_t\ncrc32(uint32_t crc, const void *buf, size_t size)\n{\n\tconst uint8_t *p;\n\n\tp = buf;\n\tcrc = crc ^ ~0U;\n\n\twhile (size--)\n\t\tcrc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);\n\n\treturn crc ^ ~0U;\n}\n"
  },
  {
    "path": "installer/src/debug_screen.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <stdarg.h>\n#include <inttypes.h>\n\n#include <psp2/display.h>\n#include <psp2/kernel/sysmem.h>\n#include <psp2/kernel/threadmgr.h>\n\nextern unsigned char psvDebugScreenFont[];\n\n#define SCREEN_WIDTH    (960)\n#define SCREEN_HEIGHT   (544)\n#define SCREEN_FB_WIDTH (960)\n#define SCREEN_FB_SIZE  (2 * 1024 * 1024)\n#define SCREEN_FB_ALIGN (256 * 1024)\n#define SCREEN_GLYPH_W  (8)\n#define SCREEN_GLYPH_H  (8)\n\n#define COLOR_BLACK      0xFF000000\n#define COLOR_RED        0xFF0000FF\n#define COLOR_BLUE       0xFF00FF00\n#define COLOR_YELLOW     0xFF00FFFF\n#define COLOR_GREEN      0xFFFF0000\n#define COLOR_MAGENTA    0xFFFF00FF\n#define COLOR_CYAN       0xFFFFFF00\n#define COLOR_WHITE      0xFFFFFFFF\n#define COLOR_GREY       0xFF808080\n#define COLOR_DEFAULT_FG COLOR_WHITE\n#define COLOR_DEFAULT_BG COLOR_BLACK\n\nstatic int psvDebugScreenMutex; /*< avoid race condition when outputing strings */\nstatic uint32_t psvDebugScreenCoordX = 0;\nstatic uint32_t psvDebugScreenCoordY = 0;\nstatic uint32_t psvDebugScreenColorFg = COLOR_DEFAULT_FG;\nstatic uint32_t psvDebugScreenColorBg = COLOR_DEFAULT_BG;\nstatic SceDisplayFrameBuf psvDebugScreenFrameBuf = {\n\t\tsizeof(SceDisplayFrameBuf), NULL, SCREEN_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT};\n\nuint32_t psvDebugScreenSetFgColor(uint32_t color) {\n\tuint32_t prev_color = psvDebugScreenColorFg;\n\tpsvDebugScreenColorFg = color;\n\treturn prev_color;\n}\n\nuint32_t psvDebugScreenSetBgColor(uint32_t color) {\n\tuint32_t prev_color = psvDebugScreenColorBg;\n\tpsvDebugScreenColorBg = color;\n\treturn prev_color;\n}\n\nstatic size_t psvDebugScreenEscape(const char *str){\n\tint i,j, p=0, params[8]={};\n\tfor(i=0; i<8 && str[i]!='\\0'; i++){\n\t\tif(str[i] >= '0' && str[i] <= '9'){\n\t\t\tparams[p]=(params[p]*10) + (str[i] - '0');\n\t\t}else if(str[i] == ';'){\n\t\t\tp++;\n\t\t}else if(str[i] == 'f' || str[i] == 'H'){\n\t\t\tpsvDebugScreenCoordX = params[0] * SCREEN_GLYPH_W;\n\t\t\tpsvDebugScreenCoordY = params[1] * SCREEN_GLYPH_H;\n\t\t\tbreak;\n\t\t}else if (str[i] == 'm'){\n\t\t\tfor(j=0; j<=p; j++){\n\t\t\t\tswitch(params[j]/10){/*bold,dim,underline,blink,invert,hidden => unsupported yet */\n\t\t\t\t#define BIT2BYTE(bit)    ( ((!!(bit&4))<<23) | ((!!(bit&2))<<15) | ((!!(bit&1))<<7) )\n\t\t\t\tcase  0:psvDebugScreenSetFgColor(COLOR_DEFAULT_FG);psvDebugScreenSetBgColor(COLOR_DEFAULT_BG);break;\n\t\t\t\tcase  3:psvDebugScreenSetFgColor(BIT2BYTE(params[j]%10));break;\n\t\t\t\tcase  9:psvDebugScreenSetFgColor(BIT2BYTE(params[j]%10) | 0x7F7F7F7F);break;\n\t\t\t\tcase  4:psvDebugScreenSetBgColor(BIT2BYTE(params[j]%10));break;\n\t\t\t\tcase 10:psvDebugScreenSetBgColor(BIT2BYTE(params[j]%10) | 0x7F7F7F7F);break;\n\t\t\t\t#undef BIT2BYTE\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn i;\n}\n\nint psvDebugScreenInit() {\n\tpsvDebugScreenMutex = sceKernelCreateMutex(\"log_mutex\", 0, 0, NULL);\n\tSceUID displayblock = sceKernelAllocMemBlock(\"display\", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, SCREEN_FB_SIZE, NULL);\n\tsceKernelGetMemBlockBase(displayblock, (void**)&psvDebugScreenFrameBuf.base);\n\n\tSceDisplayFrameBuf framebuf = {\n\t\t.size = sizeof(framebuf),\n\t\t.base = psvDebugScreenFrameBuf.base,\n\t\t.pitch = SCREEN_WIDTH,\n\t\t.pixelformat = SCE_DISPLAY_PIXELFORMAT_A8B8G8R8,\n\t\t.width = SCREEN_WIDTH,\n\t\t.height = SCREEN_HEIGHT,\n\t};\n\n\treturn sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);\n}\n\nvoid psvDebugScreenClear(int bg_color){\n\tpsvDebugScreenCoordX = psvDebugScreenCoordY = 0;\n\tint i;\n\tfor(i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) {\n\t\t((uint32_t*)psvDebugScreenFrameBuf.base)[i] = bg_color;\n\t}\n}\n\nvoid* psvDebugScreenBase(void) {\n\treturn psvDebugScreenFrameBuf.base;\n}\n\nint psvDebugScreenPuts(const char * text){\n\tint c, i, j, l;\n\tuint8_t *font;\n\tuint32_t *vram_ptr;\n\tuint32_t *vram;\n\n\tsceKernelLockMutex(psvDebugScreenMutex, 1, NULL);\n\n\tfor (c = 0; text[c] != '\\0' ; c++) {\n\t\tif (psvDebugScreenCoordX + 8 > SCREEN_WIDTH) {\n\t\t\tpsvDebugScreenCoordY += SCREEN_GLYPH_H;\n\t\t\tpsvDebugScreenCoordX = 0;\n\t\t}\n\t\tif (psvDebugScreenCoordY + 8 > SCREEN_HEIGHT) {\n\t\t\tpsvDebugScreenClear(psvDebugScreenColorBg);\n\t\t}\n\t\tif (text[c] == '\\n') {\n\t\t\tpsvDebugScreenCoordX = 0;\n\t\t\tpsvDebugScreenCoordY += SCREEN_GLYPH_H;\n\t\t\tcontinue;\n\t\t} else if (text[c] == '\\r') {\n\t\t\tpsvDebugScreenCoordX = 0;\n\t\t\tcontinue;\n\t\t} else if ((text[c] == '\\e') && (text[c+1] == '[')) { /* escape code (change color, position ...) */\n\t\t\tc+=psvDebugScreenEscape(text+2)+2;\n\t\t\tcontinue;\n\t\t}\n\n\t\tvram = ((uint32_t*)psvDebugScreenFrameBuf.base) + psvDebugScreenCoordX + psvDebugScreenCoordY * SCREEN_FB_WIDTH;\n\n\t\tfont = &psvDebugScreenFont[ (int)text[c] * 8];\n\t\tfor (i = l = 0; i < SCREEN_GLYPH_W; i++, l += SCREEN_GLYPH_W, font++) {\n\t\t\tvram_ptr  = vram;\n\t\t\tfor (j = 0; j < SCREEN_GLYPH_W; j++) {\n\t\t\t\tif ((*font & (128 >> j))) *vram_ptr = psvDebugScreenColorFg;\n\t\t\t\telse *vram_ptr = psvDebugScreenColorBg;\n\t\t\t\tvram_ptr++;\n\t\t\t}\n\t\t\tvram += SCREEN_FB_WIDTH;\n\t\t}\n\t\tpsvDebugScreenCoordX += SCREEN_GLYPH_W;\n\t}\n\n\tsceKernelUnlockMutex(psvDebugScreenMutex, 1);\n\treturn c;\n}\n\nint psvDebugScreenPrintf(const char *format, ...) {\n\tchar buf[512];\n\n\tva_list opt;\n\tva_start(opt, format);\n\tint ret = vsnprintf(buf, sizeof(buf), format, opt);\n\tpsvDebugScreenPuts(buf);\n\tva_end(opt);\n\n\treturn ret;\n}\n"
  },
  {
    "path": "installer/src/debug_screen.h",
    "content": "#pragma once\n\nint psvDebugScreenPrintf(const char *format, ...);\nint psvDebugScreenInit();\nvoid* psvDebugScreenBase(void);\n"
  },
  {
    "path": "installer/src/debug_screen_font.c",
    "content": "/*\n * PSP Software Development Kit - http://www.pspdev.org\n * -----------------------------------------------------------------------\n * Licensed under the BSD license, see LICENSE in PSPSDK root for details.\n *\n * font.c - Debug Font.\n *\n * Copyright (c) 2005 Marcus R. Brown <mrbrown@ocgnet.org>\n * Copyright (c) 2005 James Forshaw <tyranid@gmail.com>\n * Copyright (c) 2005 John Kelley <ps2dev@kelley.ca>\n *\n * $Id: font.c 540 2005-07-08 19:35:10Z warren $\n */\n\nunsigned char psvDebugScreenFont[]=\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x3c\\x42\\xa5\\x81\\xa5\\x99\\x42\\x3c\"\n\"\\x3c\\x7e\\xdb\\xff\\xff\\xdb\\x66\\x3c\\x6c\\xfe\\xfe\\xfe\\x7c\\x38\\x10\\x00\"\n\"\\x10\\x38\\x7c\\xfe\\x7c\\x38\\x10\\x00\\x10\\x38\\x54\\xfe\\x54\\x10\\x38\\x00\"\n\"\\x10\\x38\\x7c\\xfe\\xfe\\x10\\x38\\x00\\x00\\x00\\x00\\x30\\x30\\x00\\x00\\x00\"\n\"\\xff\\xff\\xff\\xe7\\xe7\\xff\\xff\\xff\\x38\\x44\\x82\\x82\\x82\\x44\\x38\\x00\"\n\"\\xc7\\xbb\\x7d\\x7d\\x7d\\xbb\\xc7\\xff\\x0f\\x03\\x05\\x79\\x88\\x88\\x88\\x70\"\n\"\\x38\\x44\\x44\\x44\\x38\\x10\\x7c\\x10\\x30\\x28\\x24\\x24\\x28\\x20\\xe0\\xc0\"\n\"\\x3c\\x24\\x3c\\x24\\x24\\xe4\\xdc\\x18\\x10\\x54\\x38\\xee\\x38\\x54\\x10\\x00\"\n\"\\x10\\x10\\x10\\x7c\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\xff\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\xff\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\xf0\\x10\\x10\\x10\\x10\"\n\"\\x10\\x10\\x10\\x1f\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\xff\\x10\\x10\\x10\\x10\"\n\"\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x1f\\x10\\x10\\x10\\x10\\x00\\x00\\x00\\xf0\\x10\\x10\\x10\\x10\"\n\"\\x10\\x10\\x10\\x1f\\x00\\x00\\x00\\x00\\x10\\x10\\x10\\xf0\\x00\\x00\\x00\\x00\"\n\"\\x81\\x42\\x24\\x18\\x18\\x24\\x42\\x81\\x01\\x02\\x04\\x08\\x10\\x20\\x40\\x80\"\n\"\\x80\\x40\\x20\\x10\\x08\\x04\\x02\\x01\\x00\\x10\\x10\\xff\\x10\\x10\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x20\\x20\\x20\\x00\\x00\\x20\\x00\"\n\"\\x50\\x50\\x50\\x00\\x00\\x00\\x00\\x00\\x50\\x50\\xf8\\x50\\xf8\\x50\\x50\\x00\"\n\"\\x20\\x78\\xa0\\x70\\x28\\xf0\\x20\\x00\\xc0\\xc8\\x10\\x20\\x40\\x98\\x18\\x00\"\n\"\\x40\\xa0\\x40\\xa8\\x90\\x98\\x60\\x00\\x10\\x20\\x40\\x00\\x00\\x00\\x00\\x00\"\n\"\\x10\\x20\\x40\\x40\\x40\\x20\\x10\\x00\\x40\\x20\\x10\\x10\\x10\\x20\\x40\\x00\"\n\"\\x20\\xa8\\x70\\x20\\x70\\xa8\\x20\\x00\\x00\\x20\\x20\\xf8\\x20\\x20\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x20\\x20\\x40\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x60\\x60\\x00\\x00\\x00\\x08\\x10\\x20\\x40\\x80\\x00\"\n\"\\x70\\x88\\x98\\xa8\\xc8\\x88\\x70\\x00\\x20\\x60\\xa0\\x20\\x20\\x20\\xf8\\x00\"\n\"\\x70\\x88\\x08\\x10\\x60\\x80\\xf8\\x00\\x70\\x88\\x08\\x30\\x08\\x88\\x70\\x00\"\n\"\\x10\\x30\\x50\\x90\\xf8\\x10\\x10\\x00\\xf8\\x80\\xe0\\x10\\x08\\x10\\xe0\\x00\"\n\"\\x30\\x40\\x80\\xf0\\x88\\x88\\x70\\x00\\xf8\\x88\\x10\\x20\\x20\\x20\\x20\\x00\"\n\"\\x70\\x88\\x88\\x70\\x88\\x88\\x70\\x00\\x70\\x88\\x88\\x78\\x08\\x10\\x60\\x00\"\n\"\\x00\\x00\\x20\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x20\\x20\\x40\"\n\"\\x18\\x30\\x60\\xc0\\x60\\x30\\x18\\x00\\x00\\x00\\xf8\\x00\\xf8\\x00\\x00\\x00\"\n\"\\xc0\\x60\\x30\\x18\\x30\\x60\\xc0\\x00\\x70\\x88\\x08\\x10\\x20\\x00\\x20\\x00\"\n\"\\x70\\x88\\x08\\x68\\xa8\\xa8\\x70\\x00\\x20\\x50\\x88\\x88\\xf8\\x88\\x88\\x00\"\n\"\\xf0\\x48\\x48\\x70\\x48\\x48\\xf0\\x00\\x30\\x48\\x80\\x80\\x80\\x48\\x30\\x00\"\n\"\\xe0\\x50\\x48\\x48\\x48\\x50\\xe0\\x00\\xf8\\x80\\x80\\xf0\\x80\\x80\\xf8\\x00\"\n\"\\xf8\\x80\\x80\\xf0\\x80\\x80\\x80\\x00\\x70\\x88\\x80\\xb8\\x88\\x88\\x70\\x00\"\n\"\\x88\\x88\\x88\\xf8\\x88\\x88\\x88\\x00\\x70\\x20\\x20\\x20\\x20\\x20\\x70\\x00\"\n\"\\x38\\x10\\x10\\x10\\x90\\x90\\x60\\x00\\x88\\x90\\xa0\\xc0\\xa0\\x90\\x88\\x00\"\n\"\\x80\\x80\\x80\\x80\\x80\\x80\\xf8\\x00\\x88\\xd8\\xa8\\xa8\\x88\\x88\\x88\\x00\"\n\"\\x88\\xc8\\xc8\\xa8\\x98\\x98\\x88\\x00\\x70\\x88\\x88\\x88\\x88\\x88\\x70\\x00\"\n\"\\xf0\\x88\\x88\\xf0\\x80\\x80\\x80\\x00\\x70\\x88\\x88\\x88\\xa8\\x90\\x68\\x00\"\n\"\\xf0\\x88\\x88\\xf0\\xa0\\x90\\x88\\x00\\x70\\x88\\x80\\x70\\x08\\x88\\x70\\x00\"\n\"\\xf8\\x20\\x20\\x20\\x20\\x20\\x20\\x00\\x88\\x88\\x88\\x88\\x88\\x88\\x70\\x00\"\n\"\\x88\\x88\\x88\\x88\\x50\\x50\\x20\\x00\\x88\\x88\\x88\\xa8\\xa8\\xd8\\x88\\x00\"\n\"\\x88\\x88\\x50\\x20\\x50\\x88\\x88\\x00\\x88\\x88\\x88\\x70\\x20\\x20\\x20\\x00\"\n\"\\xf8\\x08\\x10\\x20\\x40\\x80\\xf8\\x00\\x70\\x40\\x40\\x40\\x40\\x40\\x70\\x00\"\n\"\\x00\\x00\\x80\\x40\\x20\\x10\\x08\\x00\\x70\\x10\\x10\\x10\\x10\\x10\\x70\\x00\"\n\"\\x20\\x50\\x88\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xf8\\x00\"\n\"\\x40\\x20\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x70\\x08\\x78\\x88\\x78\\x00\"\n\"\\x80\\x80\\xb0\\xc8\\x88\\xc8\\xb0\\x00\\x00\\x00\\x70\\x88\\x80\\x88\\x70\\x00\"\n\"\\x08\\x08\\x68\\x98\\x88\\x98\\x68\\x00\\x00\\x00\\x70\\x88\\xf8\\x80\\x70\\x00\"\n\"\\x10\\x28\\x20\\xf8\\x20\\x20\\x20\\x00\\x00\\x00\\x68\\x98\\x98\\x68\\x08\\x70\"\n\"\\x80\\x80\\xf0\\x88\\x88\\x88\\x88\\x00\\x20\\x00\\x60\\x20\\x20\\x20\\x70\\x00\"\n\"\\x10\\x00\\x30\\x10\\x10\\x10\\x90\\x60\\x40\\x40\\x48\\x50\\x60\\x50\\x48\\x00\"\n\"\\x60\\x20\\x20\\x20\\x20\\x20\\x70\\x00\\x00\\x00\\xd0\\xa8\\xa8\\xa8\\xa8\\x00\"\n\"\\x00\\x00\\xb0\\xc8\\x88\\x88\\x88\\x00\\x00\\x00\\x70\\x88\\x88\\x88\\x70\\x00\"\n\"\\x00\\x00\\xb0\\xc8\\xc8\\xb0\\x80\\x80\\x00\\x00\\x68\\x98\\x98\\x68\\x08\\x08\"\n\"\\x00\\x00\\xb0\\xc8\\x80\\x80\\x80\\x00\\x00\\x00\\x78\\x80\\xf0\\x08\\xf0\\x00\"\n\"\\x40\\x40\\xf0\\x40\\x40\\x48\\x30\\x00\\x00\\x00\\x90\\x90\\x90\\x90\\x68\\x00\"\n\"\\x00\\x00\\x88\\x88\\x88\\x50\\x20\\x00\\x00\\x00\\x88\\xa8\\xa8\\xa8\\x50\\x00\"\n\"\\x00\\x00\\x88\\x50\\x20\\x50\\x88\\x00\\x00\\x00\\x88\\x88\\x98\\x68\\x08\\x70\"\n\"\\x00\\x00\\xf8\\x10\\x20\\x40\\xf8\\x00\\x18\\x20\\x20\\x40\\x20\\x20\\x18\\x00\"\n\"\\x20\\x20\\x20\\x00\\x20\\x20\\x20\\x00\\xc0\\x20\\x20\\x10\\x20\\x20\\xc0\\x00\"\n\"\\x40\\xa8\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x50\\xf8\\x00\\x00\\x00\"\n\"\\x70\\x88\\x80\\x80\\x88\\x70\\x20\\x60\\x90\\x00\\x00\\x90\\x90\\x90\\x68\\x00\"\n\"\\x10\\x20\\x70\\x88\\xf8\\x80\\x70\\x00\\x20\\x50\\x70\\x08\\x78\\x88\\x78\\x00\"\n\"\\x48\\x00\\x70\\x08\\x78\\x88\\x78\\x00\\x20\\x10\\x70\\x08\\x78\\x88\\x78\\x00\"\n\"\\x20\\x00\\x70\\x08\\x78\\x88\\x78\\x00\\x00\\x70\\x80\\x80\\x80\\x70\\x10\\x60\"\n\"\\x20\\x50\\x70\\x88\\xf8\\x80\\x70\\x00\\x50\\x00\\x70\\x88\\xf8\\x80\\x70\\x00\"\n\"\\x20\\x10\\x70\\x88\\xf8\\x80\\x70\\x00\\x50\\x00\\x00\\x60\\x20\\x20\\x70\\x00\"\n\"\\x20\\x50\\x00\\x60\\x20\\x20\\x70\\x00\\x40\\x20\\x00\\x60\\x20\\x20\\x70\\x00\"\n\"\\x50\\x00\\x20\\x50\\x88\\xf8\\x88\\x00\\x20\\x00\\x20\\x50\\x88\\xf8\\x88\\x00\"\n\"\\x10\\x20\\xf8\\x80\\xf0\\x80\\xf8\\x00\\x00\\x00\\x6c\\x12\\x7e\\x90\\x6e\\x00\"\n\"\\x3e\\x50\\x90\\x9c\\xf0\\x90\\x9e\\x00\\x60\\x90\\x00\\x60\\x90\\x90\\x60\\x00\"\n\"\\x90\\x00\\x00\\x60\\x90\\x90\\x60\\x00\\x40\\x20\\x00\\x60\\x90\\x90\\x60\\x00\"\n\"\\x40\\xa0\\x00\\xa0\\xa0\\xa0\\x50\\x00\\x40\\x20\\x00\\xa0\\xa0\\xa0\\x50\\x00\"\n\"\\x90\\x00\\x90\\x90\\xb0\\x50\\x10\\xe0\\x50\\x00\\x70\\x88\\x88\\x88\\x70\\x00\"\n\"\\x50\\x00\\x88\\x88\\x88\\x88\\x70\\x00\\x20\\x20\\x78\\x80\\x80\\x78\\x20\\x20\"\n\"\\x18\\x24\\x20\\xf8\\x20\\xe2\\x5c\\x00\\x88\\x50\\x20\\xf8\\x20\\xf8\\x20\\x00\"\n\"\\xc0\\xa0\\xa0\\xc8\\x9c\\x88\\x88\\x8c\\x18\\x20\\x20\\xf8\\x20\\x20\\x20\\x40\"\n\"\\x10\\x20\\x70\\x08\\x78\\x88\\x78\\x00\\x10\\x20\\x00\\x60\\x20\\x20\\x70\\x00\"\n\"\\x20\\x40\\x00\\x60\\x90\\x90\\x60\\x00\\x20\\x40\\x00\\x90\\x90\\x90\\x68\\x00\"\n\"\\x50\\xa0\\x00\\xa0\\xd0\\x90\\x90\\x00\\x28\\x50\\x00\\xc8\\xa8\\x98\\x88\\x00\"\n\"\\x00\\x70\\x08\\x78\\x88\\x78\\x00\\xf8\\x00\\x60\\x90\\x90\\x90\\x60\\x00\\xf0\"\n\"\\x20\\x00\\x20\\x40\\x80\\x88\\x70\\x00\\x00\\x00\\x00\\xf8\\x80\\x80\\x00\\x00\"\n\"\\x00\\x00\\x00\\xf8\\x08\\x08\\x00\\x00\\x84\\x88\\x90\\xa8\\x54\\x84\\x08\\x1c\"\n\"\\x84\\x88\\x90\\xa8\\x58\\xa8\\x3c\\x08\\x20\\x00\\x00\\x20\\x20\\x20\\x20\\x00\"\n\"\\x00\\x00\\x24\\x48\\x90\\x48\\x24\\x00\\x00\\x00\\x90\\x48\\x24\\x48\\x90\\x00\"\n\"\\x28\\x50\\x20\\x50\\x88\\xf8\\x88\\x00\\x28\\x50\\x70\\x08\\x78\\x88\\x78\\x00\"\n\"\\x28\\x50\\x00\\x70\\x20\\x20\\x70\\x00\\x28\\x50\\x00\\x20\\x20\\x20\\x70\\x00\"\n\"\\x28\\x50\\x00\\x70\\x88\\x88\\x70\\x00\\x50\\xa0\\x00\\x60\\x90\\x90\\x60\\x00\"\n\"\\x28\\x50\\x00\\x88\\x88\\x88\\x70\\x00\\x50\\xa0\\x00\\xa0\\xa0\\xa0\\x50\\x00\"\n\"\\xfc\\x48\\x48\\x48\\xe8\\x08\\x50\\x20\\x00\\x50\\x00\\x50\\x50\\x50\\x10\\x20\"\n\"\\xc0\\x44\\xc8\\x54\\xec\\x54\\x9e\\x04\\x10\\xa8\\x40\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x20\\x50\\x88\\x50\\x20\\x00\\x00\\x88\\x10\\x20\\x40\\x80\\x28\\x00\\x00\"\n\"\\x7c\\xa8\\xa8\\x68\\x28\\x28\\x28\\x00\\x38\\x40\\x30\\x48\\x48\\x30\\x08\\x70\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xff\\xf0\\xf0\\xf0\\xf0\\x0f\\x0f\\x0f\\x0f\"\n\"\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x3c\\x3c\\x00\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x00\"\n\"\\xc0\\xc0\\xc0\\xc0\\xc0\\xc0\\xc0\\xc0\\x0f\\x0f\\x0f\\x0f\\xf0\\xf0\\xf0\\xf0\"\n\"\\xfc\\xfc\\xfc\\xfc\\xfc\\xfc\\xfc\\xfc\\x03\\x03\\x03\\x03\\x03\\x03\\x03\\x03\"\n\"\\x3f\\x3f\\x3f\\x3f\\x3f\\x3f\\x3f\\x3f\\x11\\x22\\x44\\x88\\x11\\x22\\x44\\x88\"\n\"\\x88\\x44\\x22\\x11\\x88\\x44\\x22\\x11\\xfe\\x7c\\x38\\x10\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x10\\x38\\x7c\\xfe\\x80\\xc0\\xe0\\xf0\\xe0\\xc0\\x80\\x00\"\n\"\\x01\\x03\\x07\\x0f\\x07\\x03\\x01\\x00\\xff\\x7e\\x3c\\x18\\x18\\x3c\\x7e\\xff\"\n\"\\x81\\xc3\\xe7\\xff\\xff\\xe7\\xc3\\x81\\xf0\\xf0\\xf0\\xf0\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x0f\\x0f\\x0f\\x0f\\x0f\\x0f\\x0f\\x0f\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xf0\\xf0\\xf0\\xf0\\x33\\x33\\xcc\\xcc\\x33\\x33\\xcc\\xcc\"\n\"\\x00\\x20\\x20\\x50\\x50\\x88\\xf8\\x00\\x20\\x20\\x70\\x20\\x70\\x20\\x20\\x00\"\n\"\\x00\\x00\\x00\\x50\\x88\\xa8\\x50\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"\n\"\\x00\\x00\\x00\\x00\\xff\\xff\\xff\\xff\\xf0\\xf0\\xf0\\xf0\\xf0\\xf0\\xf0\\xf0\"\n\"\\x0f\\x0f\\x0f\\x0f\\x0f\\x0f\\x0f\\x0f\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x68\\x90\\x90\\x90\\x68\\x00\\x30\\x48\\x48\\x70\\x48\\x48\\x70\\xc0\"\n\"\\xf8\\x88\\x80\\x80\\x80\\x80\\x80\\x00\\xf8\\x50\\x50\\x50\\x50\\x50\\x98\\x00\"\n\"\\xf8\\x88\\x40\\x20\\x40\\x88\\xf8\\x00\\x00\\x00\\x78\\x90\\x90\\x90\\x60\\x00\"\n\"\\x00\\x50\\x50\\x50\\x50\\x68\\x80\\x80\\x00\\x50\\xa0\\x20\\x20\\x20\\x20\\x00\"\n\"\\xf8\\x20\\x70\\xa8\\xa8\\x70\\x20\\xf8\\x20\\x50\\x88\\xf8\\x88\\x50\\x20\\x00\"\n\"\\x70\\x88\\x88\\x88\\x50\\x50\\xd8\\x00\\x30\\x40\\x40\\x20\\x50\\x50\\x50\\x20\"\n\"\\x00\\x00\\x00\\x50\\xa8\\xa8\\x50\\x00\\x08\\x70\\xa8\\xa8\\xa8\\x70\\x80\\x00\"\n\"\\x38\\x40\\x80\\xf8\\x80\\x40\\x38\\x00\\x70\\x88\\x88\\x88\\x88\\x88\\x88\\x00\"\n\"\\x00\\xf8\\x00\\xf8\\x00\\xf8\\x00\\x00\\x20\\x20\\xf8\\x20\\x20\\x00\\xf8\\x00\"\n\"\\xc0\\x30\\x08\\x30\\xc0\\x00\\xf8\\x00\\x18\\x60\\x80\\x60\\x18\\x00\\xf8\\x00\"\n\"\\x10\\x28\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\xa0\\x40\"\n\"\\x00\\x20\\x00\\xf8\\x00\\x20\\x00\\x00\\x00\\x50\\xa0\\x00\\x50\\xa0\\x00\\x00\"\n\"\\x00\\x18\\x24\\x24\\x18\\x00\\x00\\x00\\x00\\x30\\x78\\x78\\x30\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x3e\\x20\\x20\\x20\\xa0\\x60\\x20\\x00\"\n\"\\xa0\\x50\\x50\\x50\\x00\\x00\\x00\\x00\\x40\\xa0\\x20\\x40\\xe0\\x00\\x00\\x00\"\n\"\\x00\\x38\\x38\\x38\\x38\\x38\\x38\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\";\n\n\n"
  },
  {
    "path": "installer/src/enso.h",
    "content": "#pragma once\n\n// user prototypes\nint ensoCheckOs0(void);\nint ensoCheckMBR(void);\nint ensoCheckBlocks(void);\nint ensoWriteConfig(void);\nint ensoWriteBlocks(void);\nint ensoWriteMBR(void);\nint ensoCheckRealMBR(void);\nint ensoUninstallMBR(void);\nint ensoCleanUpBlocks(void);\n\n// kernel prototypes\nint k_ensoCheckOs0(void);\nint k_ensoCheckMBR(void);\nint k_ensoCheckBlocks(void);\nint k_ensoWriteConfig(void);\nint k_ensoWriteBlocks(void);\nint k_ensoWriteMBR(void);\nint k_ensoCheckRealMBR(void);\nint k_ensoUninstallMBR(void);\nint k_ensoCleanUpBlocks(void);\n\nenum {\n\tE_PREVIOUS_INSTALL = 1,\n\tE_MBR_BUT_UNKNOWN = 2,\n\tE_UNKNOWN_DATA = 3,\n};\n\n#define BLOCKS_OUTPUT \"ux0:data/blocks.bin\"\n"
  },
  {
    "path": "installer/src/kernel.c",
    "content": "#include <psp2kern/kernel/modulemgr.h>\n#include <psp2kern/io/fcntl.h>\n#include <psp2kern/io/stat.h>\n#include <psp2kern/kernel/threadmgr.h>\n#include <psp2kern/kernel/sysmem.h>\n#include <psp2kern/kernel/cpu.h>\n\n#include <taihen.h>\n\n#include <libk/stdarg.h>\n#include <libk/string.h>\n#include <libk/stdio.h>\n\n#include \"enso.h\"\n\n#define printf(str, x...) do { printf_file(\"%s:%d: \" str, __PRETTY_FUNCTION__, __LINE__, ## x); } while (0)\n#define ARRAYSIZE(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))\n\nint ksceSblAimgrIsDolce(void);\nuint32_t crc32(uint32_t crc, const void *buf, size_t size);\n\nenum {\n\tBLOCK_SIZE = 0x200,\n\tOFF_PARTITION_TABLE = 0,\n\tOFF_REAL_PARTITION_TABLE = 1 * BLOCK_SIZE,\n\tOFF_FAKE_OS0 = 2 * BLOCK_SIZE,\n\tFAT_BIN_SIZE = 0x6000, // NOTE: first 0x400 bytes are not written\n\tFAT_BIN_USEFUL_SIZE = 0x6000 - 0x400,\n\n\tOS0_SIZE = 0x3820 * BLOCK_SIZE,\n\tOS0_CRC32 = 0x69b0c99d,\n};\n\ntypedef struct {\n\tuint32_t off;\n\tuint32_t sz;\n\tuint8_t code;\n\tuint8_t type;\n\tuint8_t active;\n\tuint32_t flags;\n\tuint16_t unk;\n} __attribute__((packed)) partition_t;\n\ntypedef struct {\n\tchar magic[0x20];\n\tuint32_t version;\n\tuint32_t device_size;\n\tchar unk1[0x28];\n\tpartition_t partitions[0x10];\n\tchar unk2[0x5e];\n\tchar unk3[0x10 * 4];\n\tuint16_t sig;\n} __attribute__((packed)) master_block_t;\n\nint printf_file(const char *format, ...) {\n\tchar line[512] = {0};\n\tva_list arg;\n\n\tva_start(arg, format);\n\tvsprintf(line, format, arg);\n\tva_end(arg);\n\n\tint fd = ksceIoOpen(\"ux0:data/enso.log\", SCE_O_WRONLY | SCE_O_APPEND | SCE_O_CREAT, 0777);\n\tif (fd < 0)\n\t\treturn 0;\n\tksceIoWrite(fd, line, strlen(line));\n\tksceIoClose(fd);\n\n\treturn 0;\n}\n\nconst char *part_code(int code) {\n\tstatic char *codes[] = {\n\t\t\"empty\",\n\t\t\"first_partition\",\n\t\t\"slb2\",\n\t\t\"os0\",\n\t\t\"vs0\",\n\t\t\"vd0\",\n\t\t\"tm0\",\n\t\t\"ur0\",\n\t\t\"ux0\",\n\t\t\"gro0\",\n\t\t\"grw0\",\n\t\t\"ud0\",\n\t\t\"sa0\",\n\t\t\"some_data\",\n\t\t\"pd0\",\n\t\t\"invalid\"\n\t};\n\treturn codes[code];\n}\n\nconst char *part_type(int type) {\n\tif (type == 6)\n\t\treturn \"FAT16\";\n\telse if (type == 7)\n\t\treturn \"exFAT\";\n\telse if (type == 0xDA)\n\t\treturn \"raw\";\n\treturn \"unknown\";\n}\n\nconst char *device = \"sdstor0:int-lp-act-entire\";\n\nint run_on_thread(void *func) {\n\tint ret = 0;\n\tint res = 0;\n\tint uid = 0;\n\n\tret = uid = ksceKernelCreateThread(\"run_on_thread\", func, 64, 0x1000, 0, 0, 0);\n\n\tif (ret < 0) {\n\t\tprintf(\"failed to create a thread: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tif ((ret = ksceKernelStartThread(uid, 0, NULL)) < 0) {\n\t\tprintf(\"failed to start a thread: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tif ((ret = ksceKernelWaitThreadEnd(uid, &res, NULL)) < 0) {\n\t\tprintf(\"failed to wait a thread: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = res;\n\ncleanup:\n\tif (uid > 0)\n\t\tksceKernelDeleteThread(uid);\n\n\treturn ret;\n}\n\nint find_active_os0(master_block_t *master) {\n\tint active_os0 = -1;\n\n\tfor (size_t i = 0; i < ARRAYSIZE(master->partitions); ++i) {\n\t\tpartition_t *p = &master->partitions[i];\n\t\tprintf(\"Partition %d, code=%s, type=%s, active=%d, off=0x%08x, sz=0x%08x, flags=0x%08x, unk=0x%08x\\n\",\n\t\t\ti, part_code(p->code), part_type(p->type), p->active, p->off, p->sz, p->flags, p->unk);\n\t\tif (p->active == 1 && p->code == 3)\n\t\t\tactive_os0 = i;\n\t}\n\n\treturn active_os0;\n}\n\nint check_os0(void) {\n\tint ret = 0;\n\tint fd = 0;\n\n\tprintf(\"checking os0\\n\");\n\n\tret = fd = ksceIoOpen(device, SCE_O_RDONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// read MBR and find active os0\n\tstatic master_block_t master;\n\tif ((ret = ksceIoRead(fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to read master block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tint active_os0 = find_active_os0(&master);\n\tif (active_os0 == -1) {\n\t\tprintf(\"failed to find active os0 partition\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tuint32_t off = master.partitions[active_os0].off * BLOCK_SIZE;\n\tif ((ret = ksceIoLseek(fd, off, SCE_SEEK_SET)) != (int)off) {\n\t\tprintf(\"failed to seek to os0: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tuint32_t crc = 0;\n\tfor (int i = 0; i < OS0_SIZE / BLOCK_SIZE; ++i) {\n\t\tstatic char buffer[BLOCK_SIZE];\n\t\tif ((ret = ksceIoRead(fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\tprintf(\"failed to read a block: 0x%08x\\n\", ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tcrc = crc32(crc, buffer, sizeof(buffer));\n\t}\n\n\tprintf(\"got os0 crc32: 0x%08x\\n\", crc);\n\tif (crc != OS0_CRC32) {\n\t\tprintf(\"error: crc does not match!\\n\");\n\t\tret = -1;\n\t} else {\n\t\tret = 0;\n\t}\n\ncleanup:\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\treturn ret;\n}\n\nint k_ensoCheckOs0(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(check_os0);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint is_mbr(void *data) {\n\tmaster_block_t *master = data;\n\tif (memcmp(master->magic, \"Sony Computer Entertainment Inc.\", 0x20) != 0)\n\t\treturn 0;\n\tif (master->sig != 0xAA55)\n\t\treturn 0;\n\treturn 1;\n}\n\nint is_empty(void *data) {\n\tuint8_t *buf = data;\n\tfor (int i = 0; i < BLOCK_SIZE; ++i)\n\t\tif (buf[i] != 0xAA)\n\t\t\treturn 0;\n\treturn 1;\n}\n\nint check_mbr() {\n\tint ret = 0;\n\tint fd = 0;\n\n\tprintf(\"check_mbr\\n\");\n\n\tstatic master_block_t master;\n\tret = fd = ksceIoOpen(device, SCE_O_RDONLY, 0777);\n\tif (fd < 0) {\n\t\tprintf(\"failed to open the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tif ((ret = ksceIoRead(fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to read master block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\tif (!is_mbr(&master)) {\n\t\tprintf(\"error: master block is not MBR\\n\");\n\t\tret = -1;\n\t}\n\ncleanup:\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\treturn ret;\n}\n\nint k_ensoCheckMBR(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(check_mbr);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint dump_blocks(void) {\n\tint wfd = 0;\n\tint fd = 0;\n\tint ret = 0;\n\n\tprintf(\"dumping blocks to %s\\n\", BLOCKS_OUTPUT);\n\n\tret = wfd = ksceIoOpen(BLOCKS_OUTPUT, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open %s for write: 0x%08x\\n\", BLOCKS_OUTPUT, ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = fd = ksceIoOpen(device, SCE_O_RDONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tfor (int i = 0; i < FAT_BIN_SIZE / BLOCK_SIZE; ++i) {\n\t\tstatic char buffer[BLOCK_SIZE];\n\t\tif ((ret = ksceIoRead(fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\tprintf(\"failed to read block %d: 0x%08x\\n\", i, ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tif ((ret = ksceIoWrite(wfd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\tprintf(\"failed to write block %d: 0x%08x\\n\", i, ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\tret = 0;\n\tprintf(\"copied successfully\\n\");\n\ncleanup:\n\tif (wfd > 0)\n\t\tksceIoClose(wfd);\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\treturn ret;\n}\n\nint check_blocks(void) {\n\tint ret = 0;\n\tint fd = 0;\n\n\tstatic master_block_t master;\n\n\tprintf(\"checking blocks\\n\");\n\n\tret = fd = ksceIoOpen(device, SCE_O_RDONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// check that block 1 is MBR\n\tif ((ret = ksceIoLseek(fd, OFF_REAL_PARTITION_TABLE, SCE_SEEK_SET)) != OFF_REAL_PARTITION_TABLE) {\n\t\tprintf(\"failed to seek the device to real mbr: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tif ((ret = ksceIoRead(fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to read the real mbr block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tif (is_mbr(&master)) {\n\t\t// if it is, check that the rest of the blocks match a known crc32 value\n\t\tif ((ret = ksceIoLseek(fd, OFF_FAKE_OS0, SCE_SEEK_SET)) != OFF_FAKE_OS0) {\n\t\t\tprintf(\"failed to seek the device to fake os0: 0x%08x\\n\", ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tuint32_t crc = 0;\n\t\tfor (int i = 0; i < FAT_BIN_USEFUL_SIZE / BLOCK_SIZE; ++i) {\n\t\t\tstatic char buffer[BLOCK_SIZE];\n\t\t\tif ((ret = ksceIoRead(fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\t\tprintf(\"failed to read a block: 0x%08x\\n\", ret);\n\t\t\t\tret = -1;\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tcrc = crc32(crc, buffer, sizeof(buffer));\n\t\t}\n\t\tprintf(\"crc32[2; 48] = 0x%08x\\n\", crc);\n\t\tuint32_t known_crc[] = { 0xa6b37650, 0x723111d1 };\n\t\tint found = 0;\n\t\tfor (size_t i = 0; i < ARRAYSIZE(known_crc); ++i) {\n\t\t\tif (crc == known_crc[i]) {\n\t\t\t\tfound = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!found) {\n\t\t\tprintf(\"warning: got unknown checksum\\n\");\n\t\t\tdump_blocks();\n\t\t\tret = E_MBR_BUT_UNKNOWN;\n\t\t} else {\n\t\t\tret = E_PREVIOUS_INSTALL;\n\t\t}\n\t} else {\n\t\t// otherwise just check that the data's empty, including real mbr\n\t\tif ((ret = ksceIoLseek(fd, OFF_REAL_PARTITION_TABLE, SCE_SEEK_SET)) != OFF_REAL_PARTITION_TABLE) {\n\t\t\tprintf(\"failed to seek the device to real mbr block (2): 0x%08x\\n\", ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\t// -1 because block 0 in fat.bin is fake MBR\n\t\tfor (int i = 0; i < FAT_BIN_SIZE / 0x200 - 1; ++i) {\n\t\t\tstatic char buffer[BLOCK_SIZE];\n\t\t\tif ((ret = ksceIoRead(fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\t\tprintf(\"failed to read a block (2): 0x%08x\\n\", ret);\n\t\t\t\tret = -1;\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t\tif (!is_empty(&buffer)) {\n\t\t\t\tprintf(\"unknown data was found in block %d\\n\", i + 1);\n\t\t\t\tdump_blocks();\n\t\t\t\tret = E_UNKNOWN_DATA;\n\t\t\t\tgoto cleanup;\n\t\t\t}\n\t\t}\n\n\t\t// all blocks checked, all good\n\t\tret = 0;\n\t}\n\ncleanup:\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\treturn ret;\n}\n\nint k_ensoCheckBlocks() {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(check_blocks);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint write_config() {\n\tint pstv = 0;\n\tint uid = 0;\n\tint ret = 0;\n\tint fd = 0;\n\tSceKernelModuleInfo info = {0};\n\tchar *pos = NULL;\n\tint len = 0;\n\n\tprintf(\"write_config\\n\");\n\n\tksceIoMkdir(\"ur0:tai\", 0777); // make directory if it does not exist\n\n\tpstv = ksceSblAimgrIsDolce();\n\tprintf(\"writing config for %s\\n\", pstv ? \"PSTV\" : \"PS Vita\");\n\n\tret = uid = ksceKernelLoadModule(pstv ? \"os0:psp2config_dolce.skprx\" : \"os0:psp2config_vita.skprx\", 0, NULL);\n\tif (ret < 0) {\n\t\tprintf(\"failed to load psp2config module: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tinfo.size = sizeof(info);\n\tret = ksceKernelGetModuleInfo(KERNEL_PID, uid, &info);\n\tif (ret < 0) {\n\t\tprintf(\"failed to get module info: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tstatic char config[0x1000];\n\n\tif (info.segments[0].memsz >= sizeof(config)) {\n\t\tprintf(\"config does not fit, size=0x%08x\\n\", info.segments[0].memsz);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tmemcpy(config, (char*)info.segments[0].vaddr + 0xD4, info.segments[0].memsz - 0xD4);\n\n\tif (memcmp(config, \"#\\n# PSP2\", 8) != 0) {\n\t\tprintf(\"config is corrupt\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = fd = ksceIoOpen(\"ur0:tai/boot_config.txt\", SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open ur0:tai/boot_config.txt for write: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tpos = strstr(config, \"\\n- appspawn vs0:vsh/shell/shell.self\");\n\tif (!pos) {\n\t\tprintf(\"failed to patch config: cannot locate appspawn line\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// write first part: warning message\n\tconst char *patch1 = \n\t\t\"# WARNING: DO NOT EDIT THIS FILE. IF YOU JUST WANT TO RUN A PLUGIN ON BOOT,\\n\"\n\t\t\"# EDIT ux0:tai/config.txt INSTEAD. IF YOU BREAK THIS FILE, YOUR VITA WILL NO\\n\"\n\t\t\"# LONGER BOOT. IF THAT HAPPENS, YOU CAN ENTER SAFE MODE AND RESET ALL SETTINGS\\n\"\n\t\t\"# TO RESET THIS FILE. THIS FILE IS UNIQUE TO EACH VITA MODEL. DO NOT BLINDLY\\n\"\n\t\t\"# USE SOMEONE ELSE'S CONFIG.\\n\";\n\tlen = strlen(patch1);\n\tif ((ret = ksceIoWrite(fd, patch1, len)) != len) {\n\t\tprintf(\"failed to write config 1st part: wrote 0x%08x expected 0x%08x\\n\", ret, len);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// write second part: everything before appspawn\n\tlen = pos - config;\n\tif ((ret = ksceIoWrite(fd, config, len)) != len) {\n\t\tprintf(\"failed to write config 2nd part: wrote 0x%08x expected 0x%08x\\n\", ret, len);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// write 3rd part: patch: load taihen and henkaku\n\tconst char *patch2 = \"\\n- load\\tur0:tai/taihen.skprx\\n- load\\tur0:tai/henkaku.skprx\\n\";\n\tlen = strlen(patch2);\n\tif ((ret = ksceIoWrite(fd, patch2, len)) != len) {\n\t\tprintf(\"failed to write config 3rd part: wrote 0x%08x expected 0x%08x\\n\", ret, len);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// write 4th part: rest of config\n\tlen = strlen(pos);\n\tif ((ret = ksceIoWrite(fd, pos, len)) != len) {\n\t\tprintf(\"failed to write config 4th part: wrote 0x%08x expected 0x%08x\\n\", ret, len);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\ncleanup:\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\tif (uid > 0)\n\t\tksceKernelUnloadModule(uid, 0, NULL);\n\n\treturn ret;\n}\n\nint k_ensoWriteConfig() {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(write_config);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint write_blocks(void) {\n\tint ret = 0;\n\tint fd = 0;\n\tint read_fd = 0;\n\tint fat_fd = 0;\n\n\tprintf(\"writing blocks 2-..\\n\");\n\n\tret = fat_fd = ksceIoOpen(\"ux0:app/MLCL00003/fat.bin\", SCE_O_RDONLY, 0);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open fat.bin for read: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = fd = ksceIoOpen(device, SCE_O_WRONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open device for write: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = read_fd = ksceIoOpen(device, SCE_O_RDONLY, 0);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open device for read: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoLseek(fd, OFF_FAKE_OS0, SCE_SEEK_SET)) != OFF_FAKE_OS0) {\n\t\tprintf(\"failed to seek the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoLseek(fat_fd, OFF_FAKE_OS0, SCE_SEEK_SET)) != OFF_FAKE_OS0) {\n\t\tprintf(\"failed to seek fat.bin: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tfor (int i = 0; i < FAT_BIN_USEFUL_SIZE / BLOCK_SIZE; ++i) {\n\t\tstatic char buffer[BLOCK_SIZE];\n\t\tif ((ret = ksceIoRead(fat_fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\tprintf(\"failed to read fat.bin at block %d: 0x%08x\\n\", i + 2, ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tif ((ret = ksceIoWrite(fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\t\tprintf(\"failed to write fat.bin to device at block %d: 0x%08x\\n\", i + 2, ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t\t// now read it back and confirm we wrote correctly\n\t\tstatic char read_buffer[BLOCK_SIZE];\n\t\tint off = BLOCK_SIZE * (i + 2);\n\t\tif ((ret = ksceIoLseek(read_fd, off, SCE_SEEK_SET)) != off) {\n\t\t\tprintf(\"failed to seek read_fd: 0x%08x\\n\", ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tif ((ret = ksceIoRead(read_fd, read_buffer, sizeof(read_buffer))) != sizeof(read_buffer)) {\n\t\t\tprintf(\"failed to read into read_buffer: 0x%08x\\n\", ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t\tif (memcmp(read_buffer, buffer, BLOCK_SIZE) != 0) {\n\t\t\tprintf(\"error: write failed\\n\");\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\tprintf(\"success!\\n\");\n\tret = 0;\n\ncleanup:\n\tif (fat_fd > 0)\n\t\tksceIoClose(fat_fd);\n\tif (read_fd > 0)\n\t\tksceIoClose(read_fd);\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\tksceIoSync(device, 0); // sync write\n\n\treturn ret;\n}\n\nint k_ensoWriteBlocks(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(write_blocks);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint write_mbr(void) {\n\tint ret = 0;\n\tint fd = 0;\n\tint read_fd = 0;\n\n\tret = read_fd = ksceIoOpen(device, SCE_O_RDONLY, 0);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open device for read: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = fd = ksceIoOpen(device, SCE_O_WRONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open device for write: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tstatic master_block_t master;\n\tif ((ret = ksceIoRead(read_fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to read master block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// write a copy to block 1\n\tif ((ret = ksceIoLseek(fd, OFF_REAL_PARTITION_TABLE, SCE_SEEK_SET)) != OFF_REAL_PARTITION_TABLE) {\n\t\tprintf(\"failed to seek the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoWrite(fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to write a copy of MBR: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\t// check it's actually written by reading back and comparing\n\tstatic uint8_t buffer[BLOCK_SIZE];\n\tif ((ret = ksceIoLseek(read_fd, OFF_REAL_PARTITION_TABLE, SCE_SEEK_SET)) != OFF_REAL_PARTITION_TABLE) {\n\t\tprintf(\"failed to seek the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoRead(read_fd, buffer, sizeof(buffer))) != sizeof(buffer)) {\n\t\tprintf(\"failed to read real mbr: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif (memcmp(buffer, &master, BLOCK_SIZE) != 0) {\n\t\tprintf(\"error: blocks do not match where they should\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tint active_os0 = find_active_os0(&master);\n\tif (active_os0 == -1) {\n\t\tprintf(\"failed to find active os0\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tmaster.partitions[active_os0].off = 2;\n\n\tif ((ret = ksceIoLseek(fd, OFF_PARTITION_TABLE, SCE_SEEK_SET)) != OFF_PARTITION_TABLE) {\n\t\tprintf(\"failed to seek the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoWrite(fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"error: failed to write modified MBR\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\tprintf(\"success!\\n\");\n\ncleanup:\n\tif (read_fd > 0)\n\t\tksceIoClose(read_fd);\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\tksceIoSync(device, 0); // sync write\n\n\treturn ret;\n}\n\nint k_ensoWriteMBR(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(write_mbr);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint check_real_mbr() {\n\tint ret = 0;\n\tint fd = 0;\n\n\tprintf(\"check_real_mbr\\n\");\n\n\tstatic master_block_t master;\n\tret = fd = ksceIoOpen(device, SCE_O_RDONLY, 0777);\n\tif (fd < 0) {\n\t\tprintf(\"failed to open the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoRead(fd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to read real master block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\tif (!is_mbr(&master)) {\n\t\tprintf(\"error: real master block is not MBR\\n\");\n\t\tprintf(\"this really shouldn't happen...\\n\");\n\t\tret = -1;\n\t}\n\ncleanup:\n\tif (fd > 0)\n\t\tksceIoClose(fd);\n\n\treturn ret;\n}\n\nint k_ensoCheckRealMBR(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(check_real_mbr);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint uninstall_mbr() {\n\tint ret = 0;\n\tint rfd = 0;\n\tint wfd = 0;\n\n\tprintf(\"uninstall_mbr\\n\");\n\n\tret = rfd = ksceIoOpen(device, SCE_O_RDONLY, 0);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open the device for read: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tstatic master_block_t master;\n\tif ((ret = ksceIoRead(rfd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to read real master block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tksceIoClose(rfd);\n\trfd = 0;\n\n\tif (!is_mbr(&master)) {\n\t\tprintf(\"error: real master block is not MBR\\n\");\n\t\tprintf(\"this really shouldn't happen...\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = wfd = ksceIoOpen(device, SCE_O_WRONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open the device for write: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoWrite(wfd, &master, sizeof(master))) != sizeof(master)) {\n\t\tprintf(\"failed to write real master block: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\ncleanup:\n\tif (rfd > 0)\n\t\tksceIoClose(rfd);\n\tif (wfd > 0)\n\t\tksceIoClose(wfd);\n\n\tksceIoSync(device, 0); // sync write\n\n\treturn ret;\n}\n\nint k_ensoUninstallMBR(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(uninstall_mbr);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint clean_up_blocks() {\n\tint ret = 0;\n\tint wfd = 0;\n\n\tret = wfd = ksceIoOpen(device, SCE_O_WRONLY, 0777);\n\tif (ret < 0) {\n\t\tprintf(\"failed to open the device for write: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tif ((ret = ksceIoLseek(wfd, OFF_REAL_PARTITION_TABLE, SCE_SEEK_SET)) != OFF_REAL_PARTITION_TABLE) {\n\t\tprintf(\"failed to seek the device: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tstatic uint8_t clean_block[BLOCK_SIZE];\n\tmemset(clean_block, 0xAA, sizeof(clean_block));\n\n\t// wipe it out starting from block 1\n\tfor (int i = 1; i < FAT_BIN_SIZE / BLOCK_SIZE; ++i) {\n\t\tif ((ret = ksceIoWrite(wfd, clean_block, sizeof(clean_block))) != sizeof(clean_block)) {\n\t\t\tprintf(\"failed to clean block %d: 0x%08x\\n\", i, ret);\n\t\t\tret = -1;\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\tret = 0;\n\ncleanup:\n\tif (wfd > 0)\n\t\tksceIoClose(wfd);\n\n\tksceIoSync(device, 0); // sync write\n\n\treturn ret;\n}\n\nint k_ensoCleanUpBlocks(void) {\n\tint ret = 0;\n\tint state = 0;\n\n\tENTER_SYSCALL(state);\n\tret = run_on_thread(clean_up_blocks);\n\tEXIT_SYSCALL(state);\n\n\treturn ret;\n}\n\nint module_start(int args, void *argv) {\n\t(void)args;\n\t(void)argv;\n\tprintf(\"enso kernel module started\\n\");\n\n\treturn SCE_KERNEL_START_SUCCESS;\n}\nvoid _start() __attribute__ ((weak, alias (\"module_start\")));\n\nint module_stop() {\n\tprintf(\"enso kernel module stopped\\n\");\n\n\treturn SCE_KERNEL_STOP_SUCCESS;\n}\n"
  },
  {
    "path": "installer/src/kernel2.c",
    "content": "#include <psp2kern/kernel/modulemgr.h>\n\n#include <taihen.h>\n\nstatic tai_hook_ref_t unload_allowed_hook;\nstatic SceUID unload_allowed_uid;\n\nint unload_allowed_patched(void) {\n\tTAI_CONTINUE(int, unload_allowed_hook);\n\treturn 1; // always allowed\n}\n\nint module_start(int args, void *argv) {\n\t(void)args;\n\t(void)argv;\n\tunload_allowed_uid = taiHookFunctionImportForKernel(KERNEL_PID, \n\t\t&unload_allowed_hook,     // Output a reference\n\t\t\"SceKernelModulemgr\",     // Name of module being hooked\n\t\t0x11F9B314,               // NID specifying SceSblACMgrForKernel\n\t\t0xBBA13D9C,               // Function NID\n\t\tunload_allowed_patched);  // Name of the hook function\n\n\treturn SCE_KERNEL_START_SUCCESS;\n}\nvoid _start() __attribute__ ((weak, alias (\"module_start\")));\n\nint module_stop() {\n\ttaiHookReleaseForKernel(unload_allowed_uid, unload_allowed_hook);\n\n\treturn SCE_KERNEL_STOP_SUCCESS;\n}\n"
  },
  {
    "path": "installer/src/kernel2.yml",
    "content": "kernel2:\n  attributes: 0\n  version:\n    major: 1\n    minor: 1\n  main:\n    start: module_start\n    stop: module_stop\n"
  },
  {
    "path": "installer/src/kernel_exports.yml",
    "content": "emmc_helper:\n  attributes: 0\n  version:\n    major: 1\n    minor: 1\n  main:\n    start: module_start\n    stop: module_stop\n  modules:\n    emmc_helper:\n      syscall: true\n      functions:\n        - k_ensoCheckOs0\n        - k_ensoCheckMBR\n        - k_ensoCheckBlocks\n        - k_ensoWriteConfig\n        - k_ensoWriteBlocks\n        - k_ensoWriteMBR\n        - k_ensoCheckRealMBR\n        - k_ensoUninstallMBR\n        - k_ensoCleanUpBlocks\n"
  },
  {
    "path": "installer/src/main.c",
    "content": "#include <psp2/kernel/processmgr.h>\n#include <psp2/kernel/modulemgr.h>\n#include <psp2/io/fcntl.h>\n#include <psp2/io/devctl.h>\n#include <psp2/ctrl.h>\n#include <psp2/shellutil.h>\n#include <psp2/net/http.h>\n#include <psp2/net/net.h>\n#include <psp2/sysmodule.h>\n#include <psp2/kernel/sysmem.h>\n#include <psp2/net/netctl.h>\n#include <psp2/io/stat.h>\n#include <taihen.h>\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"debug_screen.h\"\n#include \"enso.h\"\n#include \"version.h\"\n#include \"sha256.h\"\n\n#define printf psvDebugScreenPrintf\n#define ARRAYSIZE(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))\n\nint _vshSblAimgrGetConsoleId(char cid[32]);\nint sceSblSsUpdateMgrSetBootMode(int x);\nint vshPowerRequestColdReset(void);\n\nenum {\n\tSCREEN_WIDTH = 960,\n\tSCREEN_HEIGHT = 544,\n\tPROGRESS_BAR_WIDTH = SCREEN_WIDTH,\n\tPROGRESS_BAR_HEIGHT = 10,\n\tLINE_SIZE = SCREEN_WIDTH,\n};\n\nstatic unsigned buttons[] = {\n\tSCE_CTRL_SELECT,\n\tSCE_CTRL_START,\n\tSCE_CTRL_UP,\n\tSCE_CTRL_RIGHT,\n\tSCE_CTRL_DOWN,\n\tSCE_CTRL_LEFT,\n\tSCE_CTRL_LTRIGGER,\n\tSCE_CTRL_RTRIGGER,\n\tSCE_CTRL_TRIANGLE,\n\tSCE_CTRL_CIRCLE,\n\tSCE_CTRL_CROSS,\n\tSCE_CTRL_SQUARE,\n};\n\nconst char check_cid[16] = BUILD_CID;\n\nuint32_t get_key(void) {\n\tstatic unsigned prev = 0;\n\tSceCtrlData pad;\n\twhile (1) {\n\t\tmemset(&pad, 0, sizeof(pad));\n\t\tsceCtrlPeekBufferPositive(0, &pad, 1);\n\t\tunsigned new = prev ^ (pad.buttons & prev);\n\t\tprev = pad.buttons;\n\t\tfor (size_t i = 0; i < sizeof(buttons)/sizeof(*buttons); ++i)\n\t\t\tif (new & buttons[i])\n\t\t\t\treturn buttons[i];\n\n\t\tsceKernelDelayThread(1000); // 1ms\n\t}\n}\n\nvoid press_exit(void) {\n\tprintf(\"Press any key to exit this application.\\n\");\n\tget_key();\n\tsceKernelExitProcess(0);\n}\n\nvoid press_reboot(void) {\n\tprintf(\"Press any key to reboot.\\n\");\n\tget_key();\n\tvshPowerRequestColdReset();\n}\n\nint g_kernel_module, g_user_module, g_kernel2_module;\n\n#define APP_PATH \"ux0:app/MLCL00003/\"\n\nint load_helper(void) {\n\tint ret = 0;\n\n\ttai_module_args_t args = {0};\n\targs.size = sizeof(args);\n\targs.args = 0;\n\targs.argp = \"\";\n\n\tif ((ret = g_kernel2_module = taiLoadStartKernelModuleForUser(APP_PATH \"kernel2.skprx\", &args)) < 0) {\n\t\tprintf(\"Failed to load kernel workaround: 0x%08x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\tif ((ret = g_kernel_module = taiLoadStartKernelModuleForUser(APP_PATH \"emmc_helper.skprx\", &args)) < 0) {\n\t\tprintf(\"Failed to load kernel module: 0x%08x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\tif ((ret = g_user_module = sceKernelLoadStartModule(APP_PATH \"emmc_helper.suprx\", 0, NULL, 0, NULL, NULL)) < 0) {\n\t\tprintf(\"Failed to load user module: 0x%08x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nint stop_helper(void) {\n\ttai_module_args_t args = {0};\n\targs.size = sizeof(args);\n\targs.args = 0;\n\targs.argp = \"\";\n\n\tint ret = 0;\n\tint res = 0;\n\n\tif (g_user_module > 0) {\n\t\tret = sceKernelStopUnloadModule(g_user_module, 0, NULL, 0, NULL, NULL);\n\t\tif (ret < 0) {\n\t\t\tprintf(\"Failed to unload user module: 0x%08x\\n\", ret);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tif (g_kernel_module > 0) {\n\t\tret = taiStopUnloadKernelModuleForUser(g_kernel_module, &args, NULL, &res);\n\t\tif (ret < 0) {\n\t\t\tprintf(\"Failed to unload kernel module: 0x%08x\\n\", ret);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tif (g_kernel2_module > 0) {\n\t\tret = taiStopUnloadKernelModuleForUser(g_kernel2_module, &args, NULL, &res);\n\t\tif (ret < 0) {\n\t\t\tprintf(\"Failed to unload kernel workaround module: 0x%08x\\n\", ret);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nint lock_system(void) {\n\tint ret = 0;\n\n\tprintf(\"Locking system...\\n\");\n\tret = sceShellUtilInitEvents(0);\n\tif (ret < 0) {\n\t\tprintf(\"failed: 0x%08X\\n\", ret);\n\t\treturn -1;\n\t}\n\tret = sceShellUtilLock(7);\n\tif (ret < 0) {\n\t\tprintf(\"failed: 0x%08X\\n\", ret);\n\t\treturn -1;\n\t}\n\tret = sceKernelPowerLock(0);\n\tif (ret < 0) {\n\t\tprintf(\"failed: 0x%08X\\n\", ret);\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nint unlock_system(void) {\n\tsceKernelPowerUnlock(0);\n\tsceShellUtilUnlock(7);\n\n\treturn 0;\n}\n\nvoid draw_rect(int x, int y, int width, int height, uint32_t color) {\n\tvoid *base = psvDebugScreenBase();\n\n\tfor (int j = y; j < y + height; ++j)\n\t\tfor (int i = x; i < x + width; ++i)\n\t\t\t((uint32_t*)base)[j * LINE_SIZE + i] = color;\n}\n\nint g_tpl;\n\nint download_file(const char *src, const char *dst, uint8_t *expect_hash) {\n\tint ret;\n\n\tint conn = sceHttpCreateConnectionWithURL(g_tpl, src, 0);\n\tif (conn < 0) {\n\t\tprintf(\"sceHttpCreateConnectionWithURL: 0x%x\\n\", conn);\n\t\treturn conn;\n\t}\n\tint req = sceHttpCreateRequestWithURL(conn, 0, src, 0);\n\tif (req < 0) {\n\t\tprintf(\"sceHttpCreateRequestWithURL: 0x%x\\n\", req);\n\t\tsceHttpDeleteConnection(conn);\n\t\treturn req;\n\t}\n\tret = sceHttpSendRequest(req, NULL, 0);\n\tif (ret < 0) {\n\t\tprintf(\"sceHttpSendRequest: 0x%x\\n\", ret);\n\t\tgoto end;\n\t}\n\tstatic unsigned char buf[4096];\n\n\tuint64_t length = 0;\n\tret = sceHttpGetResponseContentLength(req, &length);\n\n\tint fd = sceIoOpen(dst, SCE_O_TRUNC | SCE_O_CREAT | SCE_O_WRONLY, 6);\n\tint total_read = 0;\n\tif (fd < 0) {\n\t\tprintf(\"sceIoOpen: 0x%x\\n\", fd);\n\t\tret = fd;\n\t\tgoto end;\n\t}\n\n\tSHA256_CTX ctx = {0};\n\tsha256_init(&ctx);\n\n\t// draw progress bar background\n\tdraw_rect(0, SCREEN_HEIGHT - PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT, 0xFF666666);\n\twhile (1) {\n\t\tint read = sceHttpReadData(req, buf, sizeof(buf));\n\t\tif (read < 0) {\n\t\t\tprintf(\"sceHttpReadData error! 0x%x\\n\", read);\n\t\t\tret = read;\n\t\t\tgoto end2;\n\t\t}\n\t\tif (read == 0)\n\t\t\tbreak;\n\t\tret = sceIoWrite(fd, buf, read);\n\t\tif (ret < 0 || ret != read) {\n\t\t\tprintf(\"sceIoWrite error! 0x%x\\n\", ret);\n\t\t\tgoto end2;\n\t\t}\n\t\tsha256_update(&ctx, buf, read);\n\t\ttotal_read += read;\n\t\tdraw_rect(1, SCREEN_HEIGHT - PROGRESS_BAR_HEIGHT + 1, ((uint64_t)(PROGRESS_BAR_WIDTH - 2)) * total_read / length, PROGRESS_BAR_HEIGHT - 2, 0xFFFFFFFF);\n\t}\n\n\tuint8_t hash[32] = {0};\n\tsha256_final(&ctx, hash);\n\tif (memcmp(hash, expect_hash, sizeof(hash)) != 0) {\n\t\tprintf(\"the file got corrupted in transit\\n\");\n\t\tret = -1;\n\t} else {\n\t\tret = 0;\n\t}\n\nend2:\n\tsceIoClose(fd);\nend:\n\tsceHttpDeleteRequest(req);\n\tsceHttpDeleteConnection(conn);\n\n\treturn ret;\n}\n\nint init_net(void) {\n\tSceNetInitParam netInitParam;\n\tint ret;\n\tvoid *base;\n\n\tret = sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_NET);\n\tif (ret < 0) {\n\t\tprintf(\"SCE_SYSMODULE_PROMOTER_UTIL(SCE_SYSMODULE_NET): %x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\tret = sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_HTTP);\n\tif (ret < 0) {\n\t\tprintf(\"SCE_SYSMODULE_PROMOTER_UTIL(SCE_SYSMODULE_HTTP): %x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\tret = sceHttpInit(1*1024*1024);\n\tif (ret < 0) {\n\t\tprintf(\"sceHttpInit(): %x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\tint block = sceKernelAllocMemBlock(\"net\", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, 1*1024*1024, NULL);\n\tif (block < 0) {\n\t\tprintf(\"failed to allocate net block: 0x%08x\\n\", block);\n\t\treturn -1;\n\t}\n\tret = sceKernelGetMemBlockBase(block, &base);\n\n\tnetInitParam.memory = base;\n\tnetInitParam.size = 1*1024*1024;\n\tnetInitParam.flags = 0;\n\tret = sceNetInit(&netInitParam);\n\tif (ret < 0) {\n\t\tprintf(\"sceNetInit(): %x\\n\", ret);\n\t\treturn -1;\t\t\n\t}\n\n\tret = sceNetCtlInit();\n\tif (ret < 0) {\n\t\tprintf(\"sceNetCtlInit(): %x\\n\", ret);\n\t\treturn -1;\n\t}\n\n\tg_tpl = sceHttpCreateTemplate(\"enso installer\", 2, 1);\n\tif (g_tpl < 0) {\n\t\tprintf(\"sceHttpCreateTemplate: 0x%x\\n\", g_tpl);\n\t\treturn -1;\t\t\n\t}\n\tsceHttpSetAutoRedirect(g_tpl, 1);\n\n\treturn 0;\n}\n\nint extract(const char *pup, const char *psp2swu) {\n\tint inf, outf;\n\n\tif ((inf = sceIoOpen(pup, SCE_O_RDONLY, 0)) < 0) {\n\t\treturn -1;\n\t}\n\n\tif ((outf = sceIoOpen(psp2swu, SCE_O_CREAT | SCE_O_WRONLY | SCE_O_TRUNC, 6)) < 0) {\n\t\treturn -1;\n\t}\n\n\tint ret = -1;\n\tint count;\n\n\tif (sceIoLseek(inf, 0x18, SCE_SEEK_SET) < 0) {\n\t\tgoto end;\n\t}\n\n\tif (sceIoRead(inf, &count, 4) < 4) {\n\t\tgoto end;\n\t}\n\n\tif (sceIoLseek(inf, 0x80, SCE_SEEK_SET) < 0) {\n\t\tgoto end;\n\t}\n\n\tstruct {\n\t\tuint64_t id;\n\t\tuint64_t off;\n\t\tuint64_t len;\n\t\tuint64_t field_18;\n\t} __attribute__((packed)) file_entry;\n\n\tfor (int i = 0; i < count; i++) {\n\n\t\tif (sceIoRead(inf, &file_entry, sizeof(file_entry)) != sizeof(file_entry)) {\n\t\t\tgoto end;\n\t\t}\n\n\t\tif (file_entry.id == 0x200) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (file_entry.id == 0x200) {\n\t\tchar buffer[1024];\n\t\tsize_t rd;\n\n\t\tif (sceIoLseek(inf, file_entry.off, SCE_SEEK_SET) < 0) {\n\t\t\tgoto end;\n\t\t}\n\n\t\twhile (file_entry.len && (rd = sceIoRead(inf, buffer, sizeof(buffer))) > 0) {\n\t\t\tif (rd > file_entry.len) {\n\t\t\t\trd = file_entry.len;\n\t\t\t}\n\t\t\tsceIoWrite(outf, buffer, rd);\n\t\t\tfile_entry.len -= rd;\n\t\t}\n\n\t\tif (file_entry.len == 0) {\n\t\t\tret = 0;\n\t\t}\n\t}\n\nend:\n\tsceIoClose(inf);\n\tsceIoClose(outf);\n\treturn ret;\n}\n\nint reinstall_firmware(void) {\n\tint ret = 0;\n\n\tstop_helper();\n\tunlock_system();\n\tsceKernelPowerLock(0); // don't want the screen to turn off during download\n\n\tret = init_net();\n\tif (ret < 0) {\n\t\tprintf(\"failed to init network functions\\n\");\n\t\tgoto cleanup;\n\t}\n\n\t// delete old update files\n\tconst char *files[] = {\n\t\t\"ud0:PSP2UPDATE/PSP2UPDAT.PUP\",\n\t\t\"ud0:PSP2UPDATE/PSP2UPDAT.PUP_\",\n\t\t\"ud0:PSP2UPDATE/psp2swu.self\",\n\t\t\"ud0:PSP2UPDATE/psp2swu.self_\"\n\t};\n\tfor (size_t i = 0; i < ARRAYSIZE(files); ++i)\n\t\tsceIoRemove(files[i]);\n\tfor (size_t i = 0; i < ARRAYSIZE(files); ++i) {\n\t\tint fd = sceIoOpen(files[i], SCE_O_RDONLY, 0);\n\t\tif (fd != (int)0x80010002) {\n\t\t\tprintf(\"failed to clean up old files: 0x%08x\\n\", fd);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t// make sure directory is present\n\tsceIoMkdir(\"ud0:PSP2UPDATE\", 0777);\n\n\tuint8_t psp2updat_hash[] = { 0x8c, 0xc2, 0xe2, 0x66, 0x66, 0x26, 0xc4, 0xff, 0x8f, 0x58, 0x2b, 0xf2, 0x09, 0x47, 0x35, 0x26,\n\t\t0xe8, 0x25, 0xe2, 0xa5, 0xe3, 0x8e, 0x39, 0xb2, 0x59, 0xa8, 0xa4, 0x6e, 0x25, 0xef, 0x37, 0x1c };\n\n\tprintf(\"Downloading PSP2UPDAT.PUP...\\n\");\n\tif (download_file(\"http://update.henkaku.xyz/update/PSP2UPDAT.FULL.360.PUP\",\n\t\t\t\"ud0:PSP2UPDATE/PSP2UPDAT.PUP_\", psp2updat_hash) < 0) {\n\t\tprintf(\"Failed to download update file.\\n\");\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tprintf(\"Extracting updater...\\n\");\n\textract(\"ud0:PSP2UPDATE/PSP2UPDAT.PUP_\", \"ud0:PSP2UPDATE/psp2swu.self_\");\n\n\tif ((ret = sceIoRename(\"ud0:PSP2UPDATE/PSP2UPDAT.PUP_\", \"ud0:PSP2UPDATE/PSP2UPDAT.PUP\")) < 0) {\n\t\tprintf(\"failed to rename PSP2UPDAT: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\tif ((ret = sceIoRename(\"ud0:PSP2UPDATE/psp2swu.self_\", \"ud0:PSP2UPDATE/psp2swu.self\")) < 0) {\n\t\tprintf(\"failed to rename psp2swu: 0x%08x\\n\", ret);\n\t\tret = -1;\n\t\tgoto cleanup;\n\t}\n\n\tsceIoSync(\"ud0:\", 0);\n\tsceIoSync(\"ud0:PSP2UPDATE/PSP2UPDAT.PUP\", 0);\n\tsceIoSync(\"ud0:PSP2UPDATE/psp2swu.self\", 0);\n\n\tprintf(\"Rebooting to update in 5 seconds...\\n\");\n\tprintf(\"Close this app now if you changed your mind.\\n\");\n\tsceKernelPowerUnlock(0);\n\n\tsceKernelDelayThread(5 * 1000 * 1000);\n\n\tsceSblSsUpdateMgrSetBootMode(48);\n\tvshPowerRequestColdReset();\n\n\tret = 0;\n\ncleanup:\n\treturn ret;\n}\n\nint do_install(void) {\n\tint ret = 0;\n\n\tif (lock_system() < 0)\n\t\treturn -1;\n\n\tprintf(\"Checking MBR... \");\n\tret = ensoCheckMBR();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\tgoto err;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"Checking os0... \");\n\tret = ensoCheckOs0();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\tprintf(\"\\nos0 modifications detected.\\nYou should reinstall 3.60 and try again.\\n\");\n\n\t\tprintf(\"Press X to download and install 3.60 PUP, any other key to exit.\\n\");\n\t\tif (get_key() == SCE_CTRL_CROSS) {\n\t\t\tif (reinstall_firmware() < 0) {\n\t\t\t\tprintf(\"failed to trigger a reinstall\\n\");\n\t\t\t\tret = -1;\n\t\t\t}\n\t\t}\n\n\t\tgoto err;\n\t}\n\tprintf(\"ok!\\n\");\n\tprintf(\"\\n\");\n\n\tprintf(\"Checking for previous installation... \");\n\tret = ensoCheckBlocks();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\tgoto err;\n\t}\n\tprintf(\"ok!\\n\", ret);\n\tif (ret == 0) {\n\t\t// all good, blocks are empty\n\t} else if (ret == E_PREVIOUS_INSTALL) {\n\t\tprintf(\"Previous installation was detected and will be overwritten.\\nPress X to continue, any other key to exit.\\n\");\n\t\tif (get_key() != SCE_CTRL_CROSS)\n\t\t\tgoto err;\n\t} else if (ret == E_MBR_BUT_UNKNOWN) {\n\t\tprintf(\"MBR was detected but installation checksum does not match.\\nA dump was created at %s.\\nPress X to continue, any other key to exit.\\n\", BLOCKS_OUTPUT);\n\t\tif (get_key() != SCE_CTRL_CROSS)\n\t\t\tgoto err;\n\t} else if (ret == E_UNKNOWN_DATA) {\n\t\tprintf(\"Unknown data was detected.\\nA dump was created at %s.\\nThe installation will be aborted.\\n\", BLOCKS_OUTPUT);\n\t\tgoto err;\n\t} else {\n\t\tprintf(\"Unknown error code.\\n\");\n\t\tgoto err;\n\t}\n\n\tprintf(\"Writing config... \");\n\tret = ensoWriteConfig();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\tgoto err;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"Writing blocks... \");\n\tret = ensoWriteBlocks();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\tgoto err;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"Writing MBR... \");\n\tret = ensoWriteMBR();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\tgoto err;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"\\nThe installation was completed successfully.\\n\");\n\n\tunlock_system();\n\treturn 0;\nerr:\n\tunlock_system();\n\treturn -1;\n}\n\nint do_uninstall(void) {\n\tint ret = 0;\n\n\tprintf(\"Checking MBR in block 1... \");\n\tret = ensoCheckRealMBR();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\treturn -1;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"Uninstalling MBR patch... \");\n\tret = ensoUninstallMBR();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\treturn -1;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"Cleaning up payload blocks... \");\n\tret = ensoCleanUpBlocks();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\treturn -1;\n\t}\n\tprintf(\"ok!\\n\");\n\n\tprintf(\"Deleting boot config... \");\n\tsceIoRemove(\"ur0:tai/boot_config.txt\");\n\tprintf(\"ok!\\n\");\n\n\treturn 0;\n}\n\nint do_reinstall_config(void) {\n\tint ret = 0;\n\n\tprintf(\"Writing config... \");\n\tret = ensoWriteConfig();\n\tif (ret < 0) {\n\t\tprintf(\"failed\\n\");\n\t\treturn -1;\n\t}\n\tprintf(\"ok!\\n\");\n\n\treturn 0;\n}\n\nint check_build(void) {\n\tif (BUILD_PERSONALIZED) {\n\t\tchar right_cid[16];\n\t\tchar cur_cid[16];\n\t\tfor (int i = 0; i < 16; i++) {\n\t\t\tright_cid[i] = check_cid[i] ^ 0xAA; // super leet encryption\n\t\t}\n\t\t_vshSblAimgrGetConsoleId(cur_cid);\n\t\tif (memcmp(cur_cid, right_cid, 16) == 0) {\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nint check_safe_mode(void) {\n\tif (sceIoDevctl(\"ux0:\", 0x3001, NULL, 0, NULL, 0) == 0x80010030) {\n\t\treturn 1;\n\t} else {\n\t\treturn 0;\n\t}\n}\n\nint check_henkaku(void) {\n\tint fd;\n\n\tif ((fd = sceIoOpen(\"ur0:tai/taihen.skprx\", SCE_O_RDONLY, 0)) < 0) {\n\t\treturn 0;\n\t}\n\tsceIoClose(fd);\n\tif ((fd = sceIoOpen(\"ur0:tai/henkaku.skprx\", SCE_O_RDONLY, 0)) < 0) {\n\t\treturn 0;\n\t}\n\tsceIoClose(fd);\n\treturn 1;\n}\n\nint main(int argc, char *argv[]) {\n\t(void)argc;\n\t(void)argv;\n\n\tint should_reboot = 0;\n\tint ret = 0;\n\n\tpsvDebugScreenInit();\n\n\tif (!check_build()) {\n\t\treturn 0;\n\t}\n\n\tprintf(\"Built On: %s\\n\\n\", BUILD_DATE);\n\n\tif (check_safe_mode()) {\n\t\tprintf(\"Please disable HENkaku Safe Mode from Settings before running this installer.\\n\\n\");\n\t\tpress_exit();\n\t}\n\n\tif (!check_henkaku()) {\n\t\tprintf(\"Your HENkaku version is too old! Please install R10 from https://henkaku.xyz/go/ (not the offline installer!)\\n\\n\");\n\t\tpress_exit();\n\t}\n\n#if BUILD_PERSONALIZED\n\tprintf(\"Please visit https://enso.henkaku.xyz/beta/ for installation instructions.\\n\\n\");\n\n\tuint32_t sequence[] = { SCE_CTRL_CROSS, SCE_CTRL_TRIANGLE, SCE_CTRL_SQUARE, SCE_CTRL_CIRCLE };\n\tfor (size_t i = 0; i < sizeof(sequence)/sizeof(*sequence); ++i) {\n\t\tif (get_key() != sequence[i])\n\t\t\tpress_exit();\n\t}\n#endif\n\n\tprintf(\"This software will make PERMANENT modifications to your Vita. If anything goes wrong, \\n\"\n\t\t   \"there is NO RECOVERY (not even with a hardware flasher). The creators provide this \\n\"\n\t\t   \"tool \\\"as is\\\", without warranty of any kind, express or implied and cannot be held \\n\"\n\t\t   \"liable for any damage done.\\n\\n\");\n\tprintf(\"Press CIRCLE to accept these terms or any other key to not accept.\\n\\n\");\n\n\tif (get_key() != SCE_CTRL_CIRCLE) {\n\t\tpress_exit();\n\t}\n\n\tret = load_helper();\n\tif (ret < 0)\n\t\tgoto cleanup;\n\n\tprintf(\"Options:\\n\\n\");\n\tprintf(\"  CROSS      Install/reinstall the hack.\\n\");\n\tprintf(\"  TRIANGLE   Uninstall the hack.\\n\");\n\tprintf(\"  SQUARE     Fix boot configuration (choose this if taiHEN isn't loading on boot).\\n\");\n\tprintf(\"  CIRCLE     Exit without doing anything.\\n\\n\");\n\nagain:\n\tswitch (get_key()) {\n\tcase SCE_CTRL_CROSS:\n\t\tret = do_install();\n\t\tshould_reboot = 1;\n\t\tbreak;\n\tcase SCE_CTRL_TRIANGLE:\n\t\tret = do_uninstall();\n\t\tshould_reboot = 1;\n\t\tbreak;\n\tcase SCE_CTRL_SQUARE:\n\t\tret = do_reinstall_config();\n\t\tbreak;\n\tcase SCE_CTRL_CIRCLE:\n\t\tbreak;\n\tdefault:\n\t\tgoto again;\n\t}\n\n\tif (ret < 0) {\n\t\tprintf(\"\\nAn error has occurred.\\n\");\n\t\tprintf(\"The log file can be found at ux0:data/enso.log\\n\\n\");\n\t\tshould_reboot = 0;\n\t} else {\n\t\tprintf(\"Success.\\n\\n\");\n\t}\n\ncleanup:\n\tstop_helper();\n\n\tif (should_reboot) {\n\t\tpress_reboot();\n\t} else {\n\t\tpress_exit();\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "installer/src/sha256.c",
    "content": "/*********************************************************************\n* Filename:   sha256.c\n* Author:     Brad Conte (brad AT bradconte.com)\n* Copyright:\n* Disclaimer: This code is presented \"as is\" without any guarantees.\n* Details:    Implementation of the SHA-256 hashing algorithm.\n              SHA-256 is one of the three algorithms in the SHA2\n              specification. The others, SHA-384 and SHA-512, are not\n              offered in this implementation.\n              Algorithm specification can be found here:\n               * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf\n              This implementation uses little endian byte order.\n*********************************************************************/\n\n/*************************** HEADER FILES ***************************/\n#include <stdlib.h>\n#include <string.h>\n#include \"sha256.h\"\n\n/****************************** MACROS ******************************/\n#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))\n#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))\n\n#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n\n/**************************** VARIABLES *****************************/\nstatic const WORD k[64] = {\n\t0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n\t0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n\t0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n\t0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n\t0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n\t0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n\t0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n\t0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n};\n\n/*********************** FUNCTION DEFINITIONS ***********************/\nvoid sha256_transform(SHA256_CTX *ctx, const BYTE data[])\n{\n\tWORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n\n\tfor (i = 0, j = 0; i < 16; ++i, j += 4)\n\t\tm[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n\tfor ( ; i < 64; ++i)\n\t\tm[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n\n\ta = ctx->state[0];\n\tb = ctx->state[1];\n\tc = ctx->state[2];\n\td = ctx->state[3];\n\te = ctx->state[4];\n\tf = ctx->state[5];\n\tg = ctx->state[6];\n\th = ctx->state[7];\n\n\tfor (i = 0; i < 64; ++i) {\n\t\tt1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];\n\t\tt2 = EP0(a) + MAJ(a,b,c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + t1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = t1 + t2;\n\t}\n\n\tctx->state[0] += a;\n\tctx->state[1] += b;\n\tctx->state[2] += c;\n\tctx->state[3] += d;\n\tctx->state[4] += e;\n\tctx->state[5] += f;\n\tctx->state[6] += g;\n\tctx->state[7] += h;\n}\n\nvoid sha256_init(SHA256_CTX *ctx)\n{\n\tctx->datalen = 0;\n\tctx->bitlen = 0;\n\tctx->state[0] = 0x6a09e667;\n\tctx->state[1] = 0xbb67ae85;\n\tctx->state[2] = 0x3c6ef372;\n\tctx->state[3] = 0xa54ff53a;\n\tctx->state[4] = 0x510e527f;\n\tctx->state[5] = 0x9b05688c;\n\tctx->state[6] = 0x1f83d9ab;\n\tctx->state[7] = 0x5be0cd19;\n}\n\nvoid sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)\n{\n\tWORD i;\n\n\tfor (i = 0; i < len; ++i) {\n\t\tctx->data[ctx->datalen] = data[i];\n\t\tctx->datalen++;\n\t\tif (ctx->datalen == 64) {\n\t\t\tsha256_transform(ctx, ctx->data);\n\t\t\tctx->bitlen += 512;\n\t\t\tctx->datalen = 0;\n\t\t}\n\t}\n}\n\nvoid sha256_final(SHA256_CTX *ctx, BYTE hash[])\n{\n\tWORD i;\n\n\ti = ctx->datalen;\n\n\t// Pad whatever data is left in the buffer.\n\tif (ctx->datalen < 56) {\n\t\tctx->data[i++] = 0x80;\n\t\twhile (i < 56)\n\t\t\tctx->data[i++] = 0x00;\n\t}\n\telse {\n\t\tctx->data[i++] = 0x80;\n\t\twhile (i < 64)\n\t\t\tctx->data[i++] = 0x00;\n\t\tsha256_transform(ctx, ctx->data);\n\t\tmemset(ctx->data, 0, 56);\n\t}\n\n\t// Append to the padding the total message's length in bits and transform.\n\tctx->bitlen += ctx->datalen * 8;\n\tctx->data[63] = ctx->bitlen;\n\tctx->data[62] = ctx->bitlen >> 8;\n\tctx->data[61] = ctx->bitlen >> 16;\n\tctx->data[60] = ctx->bitlen >> 24;\n\tctx->data[59] = ctx->bitlen >> 32;\n\tctx->data[58] = ctx->bitlen >> 40;\n\tctx->data[57] = ctx->bitlen >> 48;\n\tctx->data[56] = ctx->bitlen >> 56;\n\tsha256_transform(ctx, ctx->data);\n\n\t// Since this implementation uses little endian byte ordering and SHA uses big endian,\n\t// reverse all the bytes when copying the final state to the output hash.\n\tfor (i = 0; i < 4; ++i) {\n\t\thash[i]      = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 4]  = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 8]  = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n\t\thash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n\t}\n}\n"
  },
  {
    "path": "installer/src/sha256.h",
    "content": "/*********************************************************************\n* Filename:   sha256.h\n* Author:     Brad Conte (brad AT bradconte.com)\n* Copyright:\n* Disclaimer: This code is presented \"as is\" without any guarantees.\n* Details:    Defines the API for the corresponding SHA1 implementation.\n*********************************************************************/\n\n#ifndef SHA256_H\n#define SHA256_H\n\n/*************************** HEADER FILES ***************************/\n#include <stddef.h>\n\n/****************************** MACROS ******************************/\n#define SHA256_BLOCK_SIZE 32            // SHA256 outputs a 32 byte digest\n\n/**************************** DATA TYPES ****************************/\ntypedef unsigned char BYTE;             // 8-bit byte\ntypedef unsigned int  WORD;             // 32-bit word, change to \"long\" for 16-bit machines\n\ntypedef struct {\n\tBYTE data[64];\n\tWORD datalen;\n\tunsigned long long bitlen;\n\tWORD state[8];\n} SHA256_CTX;\n\n/*********************** FUNCTION DECLARATIONS **********************/\nvoid sha256_init(SHA256_CTX *ctx);\nvoid sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);\nvoid sha256_final(SHA256_CTX *ctx, BYTE hash[]);\n\n#endif   // SHA256_H\n"
  },
  {
    "path": "installer/src/user.c",
    "content": "#include <psp2/kernel/modulemgr.h>\n\n#include \"enso.h\"\n\n// user library as a workaround to load kernel module at runtime\n\nint ensoCheckOs0(void) {\n\treturn k_ensoCheckOs0();\n}\n\nint ensoCheckMBR(void) {\n\treturn k_ensoCheckMBR();\n}\n\nint ensoCheckBlocks(void) {\n\treturn k_ensoCheckBlocks();\n}\n\nint ensoWriteConfig(void) {\n\treturn k_ensoWriteConfig();\n}\n\nint ensoWriteBlocks(void) {\n\treturn k_ensoWriteBlocks();\n}\n\nint ensoWriteMBR(void) {\n\treturn k_ensoWriteMBR();\n}\n\nint ensoCheckRealMBR(void) {\n\treturn k_ensoCheckRealMBR();\n}\n\nint ensoUninstallMBR(void) {\n\treturn k_ensoUninstallMBR();\n}\n\nint ensoCleanUpBlocks(void) {\n\treturn k_ensoCleanUpBlocks();\n}\n\nint module_start(int args, void *argv) {\n\t(void)args;\n\t(void)argv;\n\treturn SCE_KERNEL_START_SUCCESS;\n}\nvoid _start() __attribute__ ((weak, alias (\"module_start\")));\n\nint module_stop() {\n\treturn SCE_KERNEL_STOP_SUCCESS;\n}\n"
  },
  {
    "path": "installer/src/user_exports.yml",
    "content": "emmc_helper_user:\n  attributes: 0\n  version:\n    major: 1\n    minor: 1\n  main:\n    start: module_start\n    stop: module_stop\n  modules:\n    emmc_helper_user:\n      functions:\n        - ensoCheckOs0\n        - ensoCheckMBR\n        - ensoCheckBlocks\n        - ensoWriteConfig\n        - ensoWriteBlocks\n        - ensoWriteMBR\n        - ensoCheckRealMBR\n        - ensoUninstallMBR\n        - ensoCleanUpBlocks\n"
  },
  {
    "path": "installer/src/version.h.in",
    "content": "#pragma once\n\n#define BUILD_DATE \"${BUILD_DATE}\"\n#define BUILD_CID {${BUILD_CID}}\n#define BUILD_PERSONALIZED (${PERSONALIZED})\n"
  },
  {
    "path": "logo.h",
    "content": "static const unsigned char logo_data[] = {\n  0x1f, 0x8b, 0x08, 0x00, 0x6f, 0xed, 0x0f, 0x59, 0x02, 0x03, 0xed, 0xdc,\n  0x41, 0x6e, 0x03, 0x21, 0x10, 0x44, 0x51, 0xdf, 0xff, 0xd2, 0xce, 0x09,\n  0x22, 0xdb, 0x63, 0x18, 0x37, 0x55, 0xef, 0x49, 0xb3, 0x0c, 0xd0, 0xec,\n  0xbe, 0x48, 0xf2, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x3d,\n  0x9f, 0xcf, 0x97, 0x1f, 0x00, 0x00, 0x00, 0x24, 0xf6, 0xae, 0x16, 0x06,\n  0x00, 0x00, 0xa0, 0xb9, 0x7d, 0x75, 0x30, 0x00, 0x00, 0x00, 0x2d, 0xdd,\n  0xab, 0x81, 0x01, 0x00, 0x00, 0x68, 0x6a, 0x5f, 0x0d, 0x0c, 0x00, 0x00,\n  0x40, 0x4b, 0xfb, 0x6a, 0x60, 0x00, 0x00, 0x00, 0x5a, 0xda, 0x57, 0x03,\n  0x03, 0x00, 0x00, 0xd0, 0xd2, 0xbe, 0x1a, 0x18, 0x00, 0x00, 0x00, 0xfd,\n  0x0b, 0x00, 0x00, 0x00, 0x39, 0xed, 0xab, 0x81, 0x01, 0x00, 0x00, 0x68,\n  0xea, 0x5f, 0x0d, 0x0c, 0x00, 0x00, 0x40, 0x43, 0xfb, 0xea, 0x5f, 0x00,\n  0x00, 0x00, 0xf4, 0x2f, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00, 0x00,\n  0x38, 0xa5, 0x7d, 0x35, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x17, 0x00, 0x00,\n  0x00, 0xf4, 0x2f, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xd0,\n  0xbf, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0xfd, 0x0b, 0x00, 0x00, 0x80, 0xfe,\n  0xd5, 0xbf, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0xfd, 0x0b, 0x00, 0x00, 0x80,\n  0x06, 0xd6, 0xbe, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xd0,\n  0xbf, 0x00, 0x00, 0x00, 0x70, 0x76, 0x03, 0x03, 0x00, 0x00, 0x40, 0x7a,\n  0xff, 0x02, 0x00, 0x00, 0x40, 0x7a, 0x03, 0x03, 0x00, 0x00, 0x80, 0xfe,\n  0x05, 0x00, 0x00, 0x80, 0xf3, 0x1b, 0x18, 0x00, 0x00, 0x00, 0xd2, 0x1b,\n  0x18, 0x00, 0x00, 0x00, 0xd2, 0x1b, 0x18, 0x00, 0x00, 0x00, 0xd2, 0x1b,\n  0x18, 0x00, 0x00, 0x00, 0x92, 0x3b, 0x18, 0x00, 0x00, 0x00, 0x92, 0x3b,\n  0x18, 0x00, 0x00, 0x00, 0xd2, 0x1b, 0x18, 0x00, 0x00, 0x00, 0xf4, 0x2f,\n  0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xd0, 0xbf, 0x00, 0x00,\n  0x00, 0xa0, 0x7f, 0x01, 0x00, 0x00, 0x40, 0xff, 0x02, 0x00, 0x00, 0x80,\n  0xfe, 0x05, 0x00, 0x00, 0x00, 0xfd, 0x0b, 0x00, 0x00, 0x80, 0xfe, 0xd5,\n  0xbf, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xd0, 0xbf, 0x00,\n  0x00, 0x00, 0xa0, 0x7f, 0x01, 0x00, 0x00, 0x40, 0xff, 0x02, 0x00, 0x00,\n  0x80, 0xfe, 0x05, 0x00, 0x00, 0x00, 0xfd, 0x0b, 0x00, 0x00, 0x00, 0xfa,\n  0x17, 0x00, 0x00, 0x00, 0xfd, 0xab, 0x7f, 0x01, 0x00, 0x00, 0xd0, 0xbf,\n  0xfa, 0x17, 0x00, 0x00, 0x00, 0xfd, 0x0b, 0x00, 0x00, 0x00, 0xfa, 0x17,\n  0x00, 0x00, 0x00, 0xf4, 0x2f, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00,\n  0x00, 0xd0, 0xbf, 0x00, 0x00, 0x00, 0xa0, 0x7f, 0x01, 0x00, 0x00, 0x40,\n  0xff, 0x02, 0x00, 0x00, 0xd0, 0xdd, 0xb8, 0x57, 0x3f, 0x00, 0x00, 0x00,\n  0x48, 0x6d, 0x5e, 0x3d, 0x0c, 0x00, 0x00, 0x40, 0x6b, 0xf7, 0x6a, 0x60,\n  0x00, 0x00, 0x00, 0x5a, 0xba, 0x57, 0x07, 0x03, 0x00, 0x00, 0xd0, 0xd4,\n  0xbd, 0x3a, 0x18, 0x00, 0x00, 0x80, 0xb6, 0xf6, 0xd5, 0xc0, 0x00, 0x00,\n  0x00, 0xb4, 0xb4, 0xaf, 0x06, 0x06, 0x00, 0x00, 0xa0, 0xa1, 0x7b, 0x75,\n  0x30, 0x00, 0x00, 0x00, 0x6d, 0xed, 0xab, 0x81, 0x01, 0x00, 0x00, 0x68,\n  0x69, 0x5f, 0x0d, 0x0c, 0x00, 0x00, 0x40, 0x4b, 0xfb, 0x6a, 0x60, 0x00,\n  0x00, 0x00, 0xa6, 0xb6, 0xa6, 0x06, 0x06, 0x00, 0x00, 0x60, 0x62, 0xfb,\n  0x3a, 0x2b, 0x00, 0x00, 0x00, 0xa9, 0xfd, 0xeb, 0xdc, 0x00, 0x00, 0x00,\n  0x68, 0x5f, 0xe7, 0x07, 0x00, 0x00, 0x40, 0x3b, 0x9a, 0x03, 0x00, 0x00,\n  0x80, 0xc9, 0xdd, 0xa8, 0xe5, 0x01, 0x00, 0x00, 0xd0, 0xbe, 0x66, 0x02,\n  0x00, 0x00, 0xe0, 0xec, 0x56, 0xd4, 0xf5, 0x00, 0x00, 0x00, 0x68, 0x5f,\n  0xf3, 0x01, 0x00, 0x00, 0xa0, 0x0d, 0xcd, 0x09, 0x00, 0x00, 0xc0, 0xe4,\n  0x2e, 0x34, 0x2b, 0x00, 0x00, 0x00, 0xe9, 0x4d, 0x68, 0x5e, 0x00, 0x00,\n  0x00, 0xd2, 0x5b, 0xb0, 0xb1, 0x7f, 0x35, 0x30, 0x00, 0x00, 0x40, 0x5f,\n  0xff, 0x9a, 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x68, 0x6e, 0x00, 0x00, 0x00,\n  0xce, 0xef, 0x40, 0xb3, 0x03, 0x00, 0x00, 0xa0, 0x01, 0xcd, 0x0e, 0x00,\n  0x00, 0x80, 0x06, 0x3c, 0x7d, 0x76, 0x0d, 0x0c, 0x00, 0x00, 0xa0, 0xff,\n  0xcc, 0x0f, 0x00, 0x00, 0x80, 0xfe, 0x33, 0x3f, 0x00, 0x00, 0x00, 0xfa,\n  0xcf, 0xfc, 0x00, 0x00, 0x00, 0xe8, 0x3f, 0xf3, 0x03, 0x00, 0x00, 0xa0,\n  0xff, 0xcc, 0x0f, 0x00, 0x00, 0xc0, 0xea, 0xfe, 0x73, 0x07, 0xfa, 0x17,\n  0x00, 0x00, 0x40, 0xfb, 0xb9, 0x03, 0x00, 0x00, 0x00, 0xb4, 0x9f, 0x3b,\n  0x00, 0x00, 0x00, 0x40, 0xfb, 0xb9, 0x03, 0x00, 0x00, 0x00, 0xb4, 0x9f,\n  0x3b, 0x00, 0x00, 0x00, 0x40, 0xfb, 0xb9, 0x03, 0x00, 0x00, 0x00, 0xd6,\n  0xb6, 0x5f, 0x6a, 0xff, 0xe9, 0x5f, 0x00, 0x00, 0x00, 0xfd, 0xab, 0x7f,\n  0xf5, 0x2f, 0x00, 0x00, 0x80, 0xfe, 0x35, 0x3f, 0x00, 0x00, 0x00, 0xfa,\n  0xcf, 0xfc, 0x00, 0x00, 0x00, 0xe8, 0x3f, 0xf3, 0x03, 0x00, 0x00, 0xa0,\n  0xff, 0xcc, 0x0f, 0x00, 0x00, 0xc0, 0x2f, 0x1b, 0x50, 0xff, 0x02, 0x00,\n  0x00, 0xa0, 0x7f, 0xcd, 0x0e, 0x00, 0x00, 0x80, 0x06, 0x34, 0x3b, 0x00,\n  0x00, 0x00, 0xa7, 0x34, 0x60, 0x52, 0x07, 0xfa, 0xdd, 0x67, 0x00, 0x00,\n  0x00, 0xfd, 0xab, 0x7f, 0xf5, 0x2f, 0x00, 0x00, 0x40, 0x7b, 0xff, 0x26,\n  0xb4, 0x60, 0xe3, 0xcc, 0x00, 0x00, 0x00, 0x3c, 0xf4, 0xaf, 0xf6, 0x05,\n  0x00, 0x00, 0xd0, 0xbf, 0x61, 0x4d, 0xe8, 0xed, 0x17, 0x00, 0x00, 0x40,\n  0xff, 0xa6, 0x77, 0x61, 0xcb, 0x9c, 0x00, 0x00, 0x00, 0xf4, 0xb6, 0xa1,\n  0xf6, 0x05, 0x00, 0x00, 0x40, 0xff, 0xea, 0x5f, 0x00, 0x00, 0x00, 0x0d,\n  0x7c, 0x7e, 0x23, 0x6a, 0x5f, 0x00, 0x00, 0x00, 0x56, 0x74, 0xe2, 0xe4,\n  0x5e, 0x4c, 0x9b, 0x07, 0x00, 0x00, 0x00, 0x0d, 0x9c, 0xde, 0xf2, 0x00,\n  0x00, 0x00, 0x68, 0x47, 0xed, 0x0b, 0x00, 0x00, 0x40, 0x53, 0x43, 0x6a,\n  0x5f, 0x00, 0x00, 0x00, 0xee, 0x6e, 0xc9, 0xbb, 0x9a, 0x72, 0xd5, 0x39,\n  0xf5, 0x2f, 0x00, 0x00, 0x80, 0xfe, 0x9d, 0xd8, 0x96, 0xd3, 0xcf, 0x07,\n  0x00, 0x00, 0x40, 0x6f, 0x03, 0x4f, 0xfd, 0x00, 0x00, 0x00, 0x40, 0xfb,\n  0x02, 0x00, 0x00, 0xa0, 0x81, 0xb5, 0x2f, 0x00, 0x00, 0x00, 0x1a, 0x58,\n  0xfb, 0x02, 0x00, 0x00, 0xa0, 0x83, 0x35, 0x30, 0x00, 0x00, 0x00, 0xfa,\n  0x57, 0x03, 0x03, 0x00, 0x00, 0xa0, 0x83, 0xf5, 0x2f, 0x00, 0x00, 0x00,\n  0x3a, 0x58, 0x03, 0x03, 0x00, 0x00, 0xa0, 0x81, 0xf5, 0x2f, 0x00, 0x00,\n  0x00, 0x19, 0x1d, 0x7c, 0xe5, 0x2c, 0x00, 0x00, 0x00, 0x30, 0xbd, 0x87,\n  0x57, 0xed, 0x0d, 0x00, 0x00, 0x00, 0x13, 0xba, 0x78, 0xf7, 0x3e, 0x00,\n  0x00, 0x00, 0xd0, 0xd2, 0xda, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0xbf, 0x1a,\n  0x18, 0x00, 0x00, 0x80, 0x96, 0x06, 0x06, 0x00, 0x00, 0x00, 0x0d, 0x0c,\n  0x00, 0x00, 0x00, 0x79, 0xfd, 0xab, 0x8f, 0x01, 0x00, 0x00, 0x48, 0x6f,\n  0x60, 0xef, 0xc5, 0x00, 0x00, 0x00, 0x68, 0x60, 0x1d, 0x0c, 0x00, 0x00,\n  0x40, 0x6f, 0xff, 0x6a, 0x60, 0x00, 0x00, 0x00, 0x34, 0x30, 0x00, 0x00,\n  0x00, 0x64, 0xf5, 0xaf, 0x06, 0x06, 0x00, 0x00, 0xa0, 0xa5, 0x7f, 0x35,\n  0x30, 0x00, 0x00, 0x00, 0x0d, 0xed, 0xab, 0x81, 0x01, 0x00, 0x00, 0x38,\n  0xa1, 0x7f, 0xef, 0x5c, 0x0b, 0x00, 0x00, 0x00, 0xee, 0x6e, 0xdf, 0x29,\n  0xeb, 0x02, 0x00, 0x00, 0xc0, 0xae, 0xfe, 0x9d, 0xb8, 0x36, 0x00, 0x00,\n  0x00, 0x4c, 0xea, 0x53, 0x0d, 0x0c, 0x00, 0x00, 0xc0, 0xf4, 0xfe, 0x3d,\n  0x6d, 0x1f, 0x00, 0x00, 0x00, 0xf8, 0xb4, 0x49, 0xf5, 0x2f, 0x00, 0x00,\n  0x00, 0x0d, 0xfd, 0x7b, 0xfa, 0x7e, 0x00, 0x00, 0x00, 0xa0, 0x7f, 0x01,\n  0x00, 0x00, 0xd0, 0xbf, 0xfa, 0x17, 0x00, 0x00, 0x80, 0x8e, 0xfe, 0x4d,\n  0xda, 0x13, 0x00, 0x00, 0x00, 0xfd, 0xab, 0x7f, 0x01, 0x00, 0x00, 0x68,\n  0xee, 0xdf, 0x5d, 0x3d, 0xaa, 0x7f, 0x01, 0x00, 0x00, 0x48, 0xef, 0x5f,\n  0x7f, 0xff, 0x0b, 0x00, 0x00, 0x80, 0xfe, 0xd5, 0xbf, 0x00, 0x00, 0x00,\n  0xe8, 0x5f, 0x00, 0x00, 0x00, 0x48, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0x48,\n  0x68, 0x60, 0x6f, 0xbf, 0x00, 0x00, 0x00, 0x4c, 0xee, 0xdf, 0x15, 0x6d,\n  0x7a, 0xc7, 0x1e, 0x00, 0x00, 0x00, 0xf0, 0x4d, 0x97, 0x7e, 0xdb, 0xa7,\n  0xda, 0x17, 0x00, 0x00, 0x80, 0x53, 0xda, 0xf7, 0x6a, 0xa7, 0xee, 0x6e,\n  0x6b, 0x00, 0x00, 0x00, 0x58, 0xd9, 0xbd, 0x9f, 0xf6, 0xea, 0x1d, 0xef,\n  0xca, 0x00, 0x00, 0x00, 0xb0, 0xb3, 0x7d, 0x57, 0x7e, 0x00, 0x00, 0x00,\n  0x90, 0xde, 0xbe, 0xfa, 0x17, 0x00, 0x00, 0x80, 0x77, 0x3b, 0x56, 0xfb,\n  0x02, 0x00, 0x00, 0x90, 0xda, 0xbc, 0x9f, 0x36, 0xa5, 0xf6, 0x05, 0x00,\n  0x00, 0x20, 0xa5, 0x7b, 0x77, 0xb5, 0xa9, 0xee, 0x05, 0x00, 0x00, 0x20,\n  0xad, 0x7b, 0x77, 0xbf, 0x1f, 0x03, 0x00, 0x00, 0xc0, 0xd4, 0xf6, 0x5d,\n  0x71, 0x2e, 0x00, 0x00, 0x00, 0x98, 0xd0, 0xbe, 0xab, 0xcf, 0x0a, 0x00,\n  0x00, 0x00, 0x29, 0xed, 0x0b, 0x00, 0x00, 0x00, 0xa7, 0xf4, 0x2f, 0x00,\n  0x00, 0x00, 0x4c, 0x6d, 0xdf, 0x15, 0xed, 0x0c, 0x00, 0x00, 0x00, 0x93,\n  0xfb, 0xf7, 0x8e, 0x35, 0x00, 0x00, 0x00, 0x60, 0x72, 0xfb, 0x6a, 0x60,\n  0x00, 0x00, 0x00, 0x4e, 0xef, 0xdf, 0x5f, 0xaf, 0x07, 0x00, 0x00, 0x00,\n  0x13, 0xfb, 0xf7, 0xd5, 0x9a, 0x00, 0x00, 0x00, 0x30, 0xad, 0x7f, 0x27,\n  0xad, 0x09, 0x00, 0x00, 0x00, 0xfa, 0x17, 0x00, 0x00, 0x00, 0xf4, 0x2f,\n  0x00, 0x00, 0x00, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xda, 0xfb, 0xd7, 0xff,\n  0x7f, 0x06, 0x00, 0x00, 0x40, 0xff, 0xea, 0x5f, 0x00, 0x00, 0x00, 0x3a,\n  0x1b, 0xf8, 0xd5, 0x3a, 0xfa, 0x17, 0x00, 0x00, 0x80, 0xc9, 0xfd, 0xfb,\n  0x4e, 0xb7, 0x6a, 0x5f, 0x00, 0x00, 0x00, 0x12, 0xfa, 0xf7, 0xbf, 0x8e,\n  0xbd, 0xfa, 0x73, 0x00, 0x00, 0x00, 0x70, 0x42, 0x03, 0x7f, 0xd3, 0xcc,\n  0x00, 0x00, 0x00, 0x90, 0xd8, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x7f, 0x01,\n  0x00, 0x00, 0xe0, 0xec, 0x0e, 0x06, 0x00, 0x00, 0x80, 0xf4, 0x06, 0x06,\n  0x00, 0x00, 0x80, 0xf4, 0x06, 0x06, 0x00, 0x00, 0x80, 0xd4, 0x16, 0x06,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xe7, 0x0f, 0xea, 0x59, 0x23, 0xf7,\n  0x00, 0xe0, 0x1f, 0x00\n};\nstatic const unsigned int logo_len = 2740;\n"
  },
  {
    "path": "nsbl.h",
    "content": "/* nsbl.h -- imported data from non-secure bootloader\n *\n * Copyright (C) 2017 molecule\n *\n * This software may be modified and distributed under the terms\n * of the MIT license.  See the LICENSE file for details.\n */\n#ifndef NSBL_HEADER\n#define NSBL_HEADER\n\n#include <inttypes.h>\n\n#define NULL ((void *)0)\n\ntypedef struct SceModuleExports {\n  uint16_t size;           // size of this structure; 0x20 for Vita 1.x\n  uint8_t  lib_version[2]; //\n  uint16_t attribute;      // ?\n  uint16_t num_functions;  // number of exported functions\n  uint16_t num_vars;       // number of exported variables\n  uint16_t unk;\n  uint32_t num_tls_vars;   // number of exported TLS variables?  <-- pretty sure wrong // yifanlu\n  uint32_t lib_nid;        // NID of this specific export list; one PRX can export several names\n  char     *lib_name;      // name of the export module\n  uint32_t *nid_table;     // array of 32-bit NIDs for the exports, first functions then vars\n  void     **entry_table;  // array of pointers to exported functions and then variables\n} __attribute__((packed)) SceModuleExports;\n\n#define EI_NIDENT 16\ntypedef struct Elf32_Ehdr {\n  unsigned char e_ident[EI_NIDENT]; /* ident bytes */\n  uint16_t  e_type;     /* file type */\n  uint16_t  e_machine;    /* target machine */\n  uint32_t  e_version;    /* file version */\n  uint32_t  e_entry;    /* start address */\n  uint32_t e_phoff;    /* phdr file offset */\n  uint32_t e_shoff;    /* shdr file offset */\n  uint32_t  e_flags;    /* file flags */\n  uint16_t  e_ehsize;   /* sizeof ehdr */\n  uint16_t  e_phentsize;    /* sizeof phdr */\n  uint16_t  e_phnum;    /* number phdrs */\n  uint16_t  e_shentsize;    /* sizeof shdr */\n  uint16_t  e_shnum;    /* number shdrs */\n  uint16_t  e_shstrndx;   /* shdr string index */\n} __attribute__((packed)) Elf32_Ehdr;\n\ntypedef struct {\n  uint32_t  p_type;   /* entry type */\n  uint32_t p_offset; /* file offset */\n  uint32_t  p_vaddr;  /* virtual address */\n  uint32_t  p_paddr;  /* physical address */\n  uint32_t  p_filesz; /* file size */\n  uint32_t  p_memsz;  /* memory size */\n  uint32_t  p_flags;  /* entry flags */\n  uint32_t  p_align;  /* memory/file alignment */\n} __attribute__((packed)) Elf32_Phdr;\n\ntypedef struct SceModuleSelfSectionInfo {\n  uint64_t offset;\n  uint64_t size;\n  uint32_t compressed; // 2=compressed\n  uint32_t unknown1;\n  uint32_t encrypted; // 1=encrypted\n  uint32_t unknown2;\n} __attribute__((packed)) SceModuleSelfSectionInfo;\n\n#ifdef FW_365\n\n// firmware specific internal structures\n\ntypedef struct SceBootArgs {\n  uint16_t version;\n  uint16_t size;\n  uint32_t fw_version;\n  uint32_t ship_version;\n  uint32_t field_C;\n  uint32_t field_10;\n  uint32_t field_14;\n  uint32_t field_18;\n  uint32_t field_1C;\n  uint32_t field_20;\n  uint32_t field_24;\n  uint32_t field_28;\n  uint8_t debug_flags[8];\n  uint32_t field_34;\n  uint32_t field_38;\n  uint32_t field_3C;\n  uint32_t field_40;\n  uint32_t field_44;\n  uint32_t field_48;\n  uint32_t aslr_seed;\n  uint32_t field_50;\n  uint32_t field_54;\n  uint32_t field_58;\n  uint32_t field_5C;\n  uint32_t dram_base;\n  uint32_t dram_size;\n  uint32_t field_68;\n  uint32_t boot_type_indicator_1;\n  uint8_t serial[0x10];\n  uint32_t secure_kernel_enp_addr;\n  uint32_t secure_kernel_enp_size;\n  uint32_t field_88;\n  uint32_t field_8C;\n  uint32_t kprx_auth_sm_self_addr;\n  uint32_t kprx_auth_sm_self_size;\n  uint32_t prog_rvk_srvk_addr;\n  uint32_t prog_rvk_srvk_size;\n  uint16_t model;\n  uint16_t device_type;\n  uint16_t device_config;\n  uint16_t retail_type;\n  uint32_t field_A8;\n  uint32_t field_AC;\n  uint8_t session_id[0x10];\n  uint32_t field_C0;\n  uint32_t boot_type_indicator_2;\n  uint32_t field_C8;\n  uint32_t field_CC;\n  uint32_t resume_context_addr;\n  uint32_t field_D4;\n  uint32_t field_D8;\n  uint32_t field_DC;\n  uint32_t field_E0;\n  uint32_t field_E4;\n  uint32_t field_E8;\n  uint32_t field_EC;\n  uint32_t field_F0;\n  uint32_t field_F4;\n  uint32_t bootldr_revision;\n  uint32_t magic;\n  uint8_t session_key[0x20];\n  uint8_t unused[0xE0];\n} __attribute__((packed)) SceBootArgs;\n\ntypedef struct SceSysrootContext {\n  uint32_t reserved[27];\n  SceBootArgs *boot_args;\n} __attribute__((packed)) SceSysrootContext;\n\ntypedef struct SceModuleLoadList {\n  const char *filename;\n} __attribute__((packed)) SceModuleLoadList;\n\ntypedef struct SceObject {\n  uint32_t field_0;\n  void *obj_data;\n  char data[];\n} __attribute__((packed)) SceObject;\n\ntypedef struct SceModuleSegment {\n  uint32_t p_filesz;\n  uint32_t p_memsz;\n  uint16_t p_flags;\n  uint16_t p_align_bits;\n  void *buf;\n  int32_t buf_blkid;\n} __attribute__((packed)) SceModuleSegment;\n\ntypedef struct SceModuleObject {\n  struct SceModuleObject *next;\n  uint16_t exeflags;\n  uint8_t status;\n  uint8_t field_7;\n  uint32_t min_sysver;\n  int32_t modid;\n  int32_t user_modid;\n  int32_t pid;\n  uint16_t modattribute;\n  uint16_t modversion;\n  uint32_t modid_name;\n  SceModuleExports *ent_top_user;\n  SceModuleExports *ent_end_user;\n  uint32_t stub_start_user;\n  uint32_t stub_end_user;\n  uint32_t module_nid;\n  uint32_t modinfo_field_38;\n  uint32_t modinfo_field_3C;\n  uint32_t modinfo_field_40;\n  uint32_t exidx_start_user;\n  uint32_t exidx_end_user;\n  uint32_t extab_start_user;\n  uint32_t extab_end_user;\n  uint16_t num_export_libs;\n  uint16_t num_import_libs;\n  uint32_t field_54;\n  uint32_t field_58;\n  uint32_t field_5C;\n  uint32_t field_60;\n  void *imports;\n  const char *path;\n  uint32_t total_loadable;\n  struct SceModuleSegment segments[3];\n  void *type_6FFFFF00_buf;\n  uint32_t type_6FFFFF00_bufsz;\n  void *module_start;\n  void *module_init;\n  void *module_stop;\n  uint32_t field_C0;\n  uint32_t field_C4;\n  uint32_t field_C8;\n  uint32_t field_CC;\n  uint32_t field_D0;\n  struct SceObject *prev_loaded;\n} __attribute__((packed)) SceModuleObject;\n\ntypedef struct SceKernelAllocMemBlockKernelOpt {\n  uint32_t size;\n  uint32_t field_4;\n  uint32_t attr;\n  uint32_t field_C;\n  uint32_t paddr;\n  uint32_t alignment;\n  uint32_t field_18;\n  uint32_t field_1C;\n  uint32_t mirror_blkid;\n  int32_t pid;\n  uint32_t field_28;\n  uint32_t field_2C;\n  uint32_t field_30;\n  uint32_t field_34;\n  uint32_t field_38;\n  uint32_t field_3C;\n  uint32_t field_40;\n  uint32_t field_44;\n  uint32_t field_48;\n  uint32_t field_4C;\n  uint32_t field_50;\n  uint32_t field_54;\n} __attribute__((packed)) SceKernelAllocMemBlockKernelOpt;\n\ntypedef struct SceModuleDecryptContext {\n  void *header;\n  uint32_t header_len;\n  Elf32_Ehdr *elf_ehdr;\n  Elf32_Phdr *elf_phdr;\n  uint8_t type;\n  uint8_t init_completed;\n  uint8_t field_12;\n  uint8_t field_13;\n  SceModuleSelfSectionInfo *section_info;\n  void *header_buffer;\n  uint32_t sbl_ctx;\n  uint32_t field_20;\n  uint32_t fd;\n  int32_t pid;\n  uint32_t max_size;\n} __attribute__((packed)) SceModuleDecryptContext;\n\n// firmware specific function offsets\n#ifdef DEBUG\nstatic int (*printf)(const char *fmt, ...) = (void*)0x51013919;\n#else\n#define printf(...)\n#endif\nstatic void *(*memset)(void *dst, int ch, int sz) = (void*)0x51013C41;\nstatic void *(*memcpy)(void *dst, const void *src, int sz) = (void *)0x51013BC1;\nstatic void *(*memmove)(void *dst, const void *src, int sz) = (void *)0x5102152D;\nstatic void (*clean_dcache)(void *dst, int len) = (void*)0x510146DD;\nstatic int (*read_block_os0)() = (void*)0x510010FD;\nstatic void (*flush_icache)() = (void*)0x51014691;\nstatic int (*strncmp)(const char *s1, const char *s2, int len) = (void *)0x51013CA0;\nstatic SceObject *(*get_obj_for_uid)(int uid) = (void *)0x51017785;\nstatic int (*module_load)(const SceModuleLoadList *list, int *uids, int count, int) = (void *)0x51001551;\nstatic int (*sceKernelAllocMemBlock)(const char *name, int type, int size, SceKernelAllocMemBlockKernelOpt *opt) = (void *)0x51007161;\nstatic int (*sceKernelGetMemBlockBase)(int32_t uid, void **basep) = (void *)0x510057E1;\nstatic int (*sceKernelRemapBlock)(int32_t uid, int type) = (void *)0x51007171;\n\n// firmware specific patch offsets\n\nstatic SceBootArgs *boot_args = (void *)0x51167528;\nstatic SceSysrootContext **sysroot_ctx_ptr = (void *)0x51138A3C;\nstatic void **module_load_func_ptr = (void *)0x510277A8;\n\n// sysstate patches\n#define SCEDISPLAY_LOGO_OFFSET (0x8990)\n#define SBLAUTHMGR_OFFSET_PATCH_ARG (168)\n#define SYSSTATE_IS_MANUFACTURING_MODE_OFFSET (0x1500)\n#define SYSSTATE_IS_DEV_MODE_OFFSET (0xE28)\n#define SYSSTATE_RET_CHECK_BUG (0xD92)\nstatic const uint8_t sysstate_ret_patch[] = {0x13, 0x22, 0xc8, 0xf2, 0x01, 0x02};\n#define SYSSTATE_SD0_STRING (0x2448)\nstatic const char ur0_path[] = \"ur0:\";\n#define SYSSTATE_SD0_PSP2CONFIG_STRING (0x2396)\nstatic const char ur0_psp2config_path[] = \"ur0:tai/boot_config.txt\";\n#define SYSSTATE_FINAL_CALL (0x130)\n#define SYSSTATE_FINAL (0x18C9)\n\n#else\n#error \"No firmware defined or firmware not supported.\"\n#endif\n\n#endif\n"
  },
  {
    "path": "second.c",
    "content": "/* second.c -- bootloader patches\n *\n * Copyright (C) 2017 molecule\n *\n * This software may be modified and distributed under the terms\n * of the MIT license.  See the LICENSE file for details.\n */\n#include <inttypes.h>\n#include \"nsbl.h\"\n#include \"logo.h\"\n\n#define unlikely(expr) __builtin_expect(!!(expr), 0)\n\n#define DACR_OFF(stmt)                 \\\ndo {                                   \\\n    unsigned prev_dacr;                \\\n    __asm__ volatile(                  \\\n        \"mrc p15, 0, %0, c3, c0, 0 \\n\" \\\n        : \"=r\" (prev_dacr)             \\\n    );                                 \\\n    __asm__ volatile(                  \\\n        \"mcr p15, 0, %0, c3, c0, 0 \\n\" \\\n        : : \"r\" (0xFFFF0000)           \\\n    );                                 \\\n    stmt;                              \\\n    __asm__ volatile(                  \\\n        \"mcr p15, 0, %0, c3, c0, 0 \\n\" \\\n        : : \"r\" (prev_dacr)            \\\n    );                                 \\\n} while (0)\n\n#define INSTALL_HOOK_THUMB(func, addr) \\\ndo {                                                \\\n    unsigned *target;                                 \\\n    target = (unsigned*)(addr);                       \\\n    *target++ = 0xC004F8DF; /* ldr.w    ip, [pc, #4] */ \\\n    *target++ = 0xBF004760; /* bx ip; nop */          \\\n    *target = (unsigned)func;                         \\\n} while (0)\n\n#define INSTALL_RET_THUMB(addr, ret)   \\\ndo {                                   \\\n    unsigned *target;                  \\\n    target = (unsigned*)(addr);        \\\n    *target = 0x47702000 | (ret); /* movs r0, #ret; bx lr */ \\\n} while (0)\n\n// sdstor restore globals\nstatic int (*sdstor_read_sector_async)(void* ctx, int sector, char* buffer, int nSectors) = NULL;\nstatic int (*sdstor_read_sector)(void* ctx, int sector, char* buffer, int nSectors) = NULL;\nstatic void *(*get_sd_context_part_validate_mmc)(int sd_ctx_index) = NULL;\n\n// debug globals\n#ifdef DEBUG\nstatic int (*set_crash_flag)(int) = NULL;\n#endif\n\n// sigpatch globals\nstatic int g_sigpatch_disabled = 0;\nstatic int g_homebrew_decrypt = 0;\nstatic int (*sbl_parse_header)(uint32_t ctx, const void *header, int len, void *args) = NULL;\nstatic int (*sbl_set_up_buffer)(uint32_t ctx, int segidx) = NULL;\nstatic int (*sbl_decrypt)(uint32_t ctx, void *buf, int sz) = NULL;\n\n// sysstate final function\nstatic void __attribute__((noreturn)) (*sysstate_final)(void) = NULL;\n\n// utility functions\n\n#if 0\nstatic int hex_dump(const char *addr, unsigned int size)\n{\n    unsigned int i;\n    for (i = 0; i < (size >> 4); i++)\n    {\n        printf(\"0x%08X: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\\n\", addr, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);\n        addr += 0x10;\n    }\n    return 0;\n}\n#endif\n\nstatic void **get_export_func(SceModuleObject *mod, uint32_t lib_nid, uint32_t func_nid) {\n    for (SceModuleExports *ent = mod->ent_top_user; ent != mod->ent_end_user; ent++) {\n        if (ent->lib_nid == lib_nid) {\n            for (int i = 0; i < ent->num_functions; i++) {\n                if (ent->nid_table[i] == func_nid) {\n                    return &ent->entry_table[i];\n                }\n            }\n        }\n    }\n    return NULL;\n}\n\nstatic int is_safe_mode(void) {\n    SceBootArgs *boot_args = (*sysroot_ctx_ptr)->boot_args;\n    uint32_t v;\n\n    if (boot_args->debug_flags[7] != 0xFF) {\n        return 1;\n    }\n\n    v = boot_args->boot_type_indicator_2 & 0x7F;\n    if (v == 0xB || (v == 4 && boot_args->resume_context_addr)) {\n        v = ~boot_args->field_CC;\n        if (((v >> 8) & 0x54) == 0x54 && (v & 0xC0) == 0) {\n            return 1;\n        } else {\n            return 0;\n        }\n    } else if (v == 4) {\n        return 0;\n    }\n\n    if (v == 0x1F || (uint32_t)(v - 0x18) <= 1) {\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nstatic int is_update_mode(void) {\n    SceBootArgs *boot_args = (*sysroot_ctx_ptr)->boot_args;\n\n    if (boot_args->debug_flags[4] != 0xFF) {\n        return 1;\n    } else {\n        return 0;\n    }\n}\n\nstatic inline int skip_patches(void) {\n    return is_safe_mode() || is_update_mode();\n}\n\n// sdif patches for MBR redirection\n\nstatic int sdstor_read_sector_patched(void* ctx, int sector, char* buffer, int nSectors) {\n    int ret;\n#ifndef NO_MBR_REDIRECT\n    if (unlikely(sector == 0 && nSectors > 0)) {\n        printf(\"read sector 0 for %d at context 0x%08X\\n\", nSectors, ctx);\n        if (get_sd_context_part_validate_mmc(0) == ctx) {\n            printf(\"patching sector 0 read to sector 1\\n\");\n            ret = sdstor_read_sector(ctx, 1, buffer, 1);\n            if (ret >= 0 && nSectors > 1) {\n                ret = sdstor_read_sector(ctx, 1, buffer + 0x200, nSectors-1);\n            }\n            return ret;\n        }\n    }\n#endif\n\n    return sdstor_read_sector(ctx, sector, buffer, nSectors);\n}\n\nstatic int sdstor_read_sector_async_patched(void* ctx, int sector, char* buffer, int nSectors) {\n    int ret;\n#ifndef NO_MBR_REDIRECT\n    if (unlikely(sector == 0 && nSectors > 0)) {\n        printf(\"read sector async 0 for %d at context 0x%08X\\n\", nSectors, ctx);\n        if (get_sd_context_part_validate_mmc(0) == ctx) {\n            printf(\"patching sector 0 read to sector 1\\n\");\n            ret = sdstor_read_sector_async(ctx, 1, buffer, 1);\n            if (ret >= 0 && nSectors > 1) {\n                ret = sdstor_read_sector_async(ctx, 1, buffer + 0x200, nSectors-1);\n            }\n            return ret;\n        }\n    }\n#endif\n\n    return sdstor_read_sector_async(ctx, sector, buffer, nSectors);\n}\n\n// sigpatches for bootup\n\nstatic int sbl_parse_header_patched(uint32_t ctx, const void *header, int len, void *args) {\n    int ret = sbl_parse_header(ctx, header, len, args);\n    if (unlikely(!g_sigpatch_disabled)) {\n        DACR_OFF(\n            g_homebrew_decrypt = (ret < 0);\n        );\n        if (g_homebrew_decrypt) {\n            *(uint32_t *)(args + SBLAUTHMGR_OFFSET_PATCH_ARG) = 0x40;\n            ret = 0;\n        }\n    }\n    return ret;\n}\n\nstatic int sbl_set_up_buffer_patched(uint32_t ctx, int segidx) {\n    if (unlikely(!g_sigpatch_disabled)) {\n        if (g_homebrew_decrypt) {\n            return 2; // always compressed!\n        }\n    }\n    return sbl_set_up_buffer(ctx, segidx);\n}\n\nstatic int sbl_decrypt_patched(uint32_t ctx, void *buf, int sz) {\n    if (unlikely(!g_sigpatch_disabled)) {\n        if (g_homebrew_decrypt) {\n            return 0;\n        }\n    }\n    return sbl_decrypt(ctx, buf, sz);\n}\n\nstatic void __attribute__((noreturn)) sysstate_final_hook(void) {\n    printf(\"after kernel load! disabling temporary sigpatches\\n\");\n\n    DACR_OFF(\n        g_sigpatch_disabled = 1;\n    );\n\n    sysstate_final();\n}\n\n// main function to hook stuff\n\n#define HOOK_EXPORT(name, lib_nid, func_nid) do {           \\\n    void **func = get_export_func(mod, lib_nid, func_nid);  \\\n    printf(#name \": 0x%08X\\n\", *func);                      \\\n    DACR_OFF(                                               \\\n        name = *func;                                       \\\n        *func = name ## _patched;                           \\\n    );                                                      \\\n} while (0)\n#define FIND_EXPORT(name, lib_nid, func_nid) do {           \\\n    void **func = get_export_func(mod, lib_nid, func_nid);  \\\n    printf(#name \": 0x%08X\\n\", *func);                      \\\n    DACR_OFF(                                               \\\n        name = *func;                                       \\\n    );                                                      \\\n} while (0)\nstatic int module_load_patched(const SceModuleLoadList *list, int *uids, int count, int unk) {\n    int ret;\n    SceObject *obj;\n    SceModuleObject *mod;\n    int skip;\n    int sysmem_idx = -1, display_idx = -1, sdif_idx = -1, authmgr_idx = -1, sysstate_idx = -1;\n\n    skip = skip_patches();\n    for (int i = 0; i < count; i++) {\n        if (!list[i].filename) {\n            continue; // wtf sony why don't you sanitize input\n        }\n        printf(\"before start %s\\n\", list[i].filename);\n        if (!skip && strncmp(list[i].filename, \"display.skprx\", 13) == 0) {\n            display_idx = i;\n        } else if (strncmp(list[i].filename, \"sdif.skprx\", 10) == 0) {\n            sdif_idx = i; // never skip MBR redirection patches\n        } else if (!skip && strncmp(list[i].filename, \"authmgr.skprx\", 13) == 0) {\n            authmgr_idx = i;\n        } else if (!skip && strncmp(list[i].filename, \"sysstatemgr.skprx\", 17) == 0) {\n            sysstate_idx = i;\n        }\n#ifdef DEBUG\n        if (strncmp(list[i].filename, \"sysmem.skprx\", 12) == 0) {\n            sysmem_idx = i;\n        }\n#endif\n    }\n    ret = module_load(list, uids, count, unk);\n#ifdef DEBUG\n    // get sysmem functions\n    if (sysmem_idx >= 0) {\n        obj = get_obj_for_uid(uids[sysmem_idx]);\n        if (obj != NULL) {\n            mod = (SceModuleObject *)&obj->data;\n            FIND_EXPORT(set_crash_flag, 0x13D793B7, 0xA465A31A);\n            FIND_EXPORT(printf, 0x88758561, 0x391B74B7);\n        } else {\n            printf(\"module data invalid for sysmem.skprx!\\n\");\n        }\n    }\n#endif\n    // patch logo\n    if (display_idx >= 0) {\n        obj = get_obj_for_uid(uids[display_idx]);\n        if (obj != NULL) {\n            mod = (SceModuleObject *)&obj->data;\n            printf(\"logo at offset: %x\\n\", mod->segments[0].buf + SCEDISPLAY_LOGO_OFFSET);\n            DACR_OFF(\n                memcpy(mod->segments[0].buf + SCEDISPLAY_LOGO_OFFSET, logo_data, logo_len);\n            );\n            // no cache flush needed because this is just data\n        } else {\n            printf(\"module data invalid for display.skprx!\\n\");\n        }\n    }\n    // patch sdif\n    if (sdif_idx >= 0) {\n        obj = get_obj_for_uid(uids[sdif_idx]);\n        if (obj != NULL) {\n            mod = (SceModuleObject *)&obj->data;\n            HOOK_EXPORT(sdstor_read_sector_async, 0x96D306FA, 0x6F8D529B);\n            HOOK_EXPORT(sdstor_read_sector, 0x96D306FA, 0xB9593652);\n            FIND_EXPORT(get_sd_context_part_validate_mmc, 0x96D306FA, 0x6A71987F);\n        } else {\n            printf(\"module data invalid for sdif.skprx!\\n\");\n        }\n    }\n    // patch authmgr\n    if (authmgr_idx >= 0) {\n        obj = get_obj_for_uid(uids[authmgr_idx]);\n        if (obj != NULL) {\n            mod = (SceModuleObject *)&obj->data;\n            HOOK_EXPORT(sbl_parse_header, 0x7ABF5135, 0xF3411881);\n            HOOK_EXPORT(sbl_set_up_buffer, 0x7ABF5135, 0x89CCDA2C);\n            HOOK_EXPORT(sbl_decrypt, 0x7ABF5135, 0xBC422443);\n        } else {\n            printf(\"module data invalid for authmgr.skprx!\\n\");\n        }\n    }\n    // patch sysstate to load unsigned boot configs\n    if (sysstate_idx >= 0) {\n        obj = get_obj_for_uid(uids[sysstate_idx]);\n        if (obj != NULL) {\n            mod = (SceModuleObject *)&obj->data;\n            DACR_OFF(\n                INSTALL_RET_THUMB(mod->segments[0].buf + SYSSTATE_IS_MANUFACTURING_MODE_OFFSET, 1);\n                *(uint32_t *)(mod->segments[0].buf + SYSSTATE_IS_DEV_MODE_OFFSET) = 0x20012001;\n                memcpy(mod->segments[0].buf + SYSSTATE_RET_CHECK_BUG, sysstate_ret_patch, sizeof(sysstate_ret_patch));\n                memcpy(mod->segments[0].buf + SYSSTATE_SD0_STRING, ur0_path, sizeof(ur0_path));\n                memcpy(mod->segments[0].buf + SYSSTATE_SD0_PSP2CONFIG_STRING, ur0_psp2config_path, sizeof(ur0_psp2config_path));\n                // this patch actually corrupts two words of data, but they are only used in debug printing and seem to be fine\n                INSTALL_HOOK_THUMB(sysstate_final_hook, mod->segments[0].buf + SYSSTATE_FINAL_CALL);\n                sysstate_final = mod->segments[0].buf + SYSSTATE_FINAL;\n            );\n        } else {\n            printf(\"module data invalid for sysstatemgr.skprx!\\n\");\n        }\n    }\n    return ret;\n}\n#undef HOOK_EXPORT\n#undef FIND_EXPORT\n\nvoid go(void) {\n    printf(\"second\\n\");\n\n    // patch module_load/module_start\n    *module_load_func_ptr = module_load_patched;\n    printf(\"module_load_patched: 0x%08X\\n\", module_load_patched);\n}\n\n__attribute__ ((section (\".text.start\"))) void start(void) {\n    go();\n}\n"
  },
  {
    "path": "second.x",
    "content": "OUTPUT_FORMAT(\"elf32-littlearm\", \"elf32-bigarm\", \"elf32-littlearm\")\nOUTPUT_ARCH(arm)\n\nENTRY(start)\n\nSECTIONS\n{\n  . = 0x51e00000;\n  .text   : { *(.text.start) *(.text   .text.*   .gnu.linkonce.t.*) *(.sceStub.text.*) }\n  .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }\n  .data   : { *(.data   .data.*   .gnu.linkonce.d.*) }\n  .bss    : { *(.bss    .bss.*    .gnu.linkonce.b.*) *(COMMON) }\n}\n"
  }
]