├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── build.sh ├── externals ├── Toolchain3dsArm9 │ ├── LICENSE │ └── Toolchain3dsArm9.cmake └── brahma-loader-build.sh ├── license.txt ├── resources ├── BrahmaAppInfo ├── BrahmaIcon.png └── CakesROP │ ├── drunkenlogo.grit │ └── drunkenlogo.png ├── scripts ├── .gitignore ├── cdn_download.py ├── dump_ticket_keys.py ├── ncchinfo_gen.py ├── ncchinfo_tgen.py ├── print_ticket_keys.py ├── sdinfo_gen.py └── seeddb_gen.py └── source ├── CMakeLists.txt ├── common.h ├── decryptor ├── aes.c ├── aes.h ├── decryptor.c ├── decryptor.h ├── game.c ├── game.h ├── nand.c ├── nand.h ├── nandfat.c ├── nandfat.h ├── sha.c ├── sha.h ├── titlekey.c └── titlekey.h ├── draw.c ├── draw.h ├── fatfs ├── delay.h ├── delay.s ├── diskio.c ├── diskio.h ├── ff.c ├── ff.h ├── ffconf.h ├── integer.h ├── option │ ├── cc932.c │ ├── ccsbcs.c │ ├── syscall.c │ └── unicode.c ├── sdmmc.c └── sdmmc.h ├── font.h ├── fs.c ├── fs.h ├── hid.c ├── hid.h ├── i2c.c ├── i2c.h ├── loader-bs ├── bootstrap.ld └── bs-start.s ├── loader-gw ├── gateway.ld └── gw-start.s ├── main.c ├── menu.c ├── menu.h ├── platform.c └── platform.h /.gitignore: -------------------------------------------------------------------------------- 1 | Launcher.dat 2 | Decrypt9.bin 3 | Decrypt9.elf 4 | Decrypt9.3dsx 5 | Decrypt9.smdh 6 | /build 7 | /output 8 | /release 9 | 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "externals/BrahmaLoader"] 2 | path = externals/BrahmaLoader 3 | url = https://github.com/d0k3/BrahmaLoader 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | language: c 5 | 6 | before_install: 7 | - wget http://sourceforge.net/projects/devkitpro/files/Automated%20Installer/devkitARMupdate.pl 8 | - export DEVKITPRO=/home/travis/devkitPro 9 | - export DEVKITARM=${DEVKITPRO}/devkitARM 10 | - wget http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-x86_64.tar.gz 11 | 12 | install: 13 | - sudo perl devkitARMupdate.pl 14 | - tar -xvf cmake-3.1.0-Linux-x86_64.tar.gz 15 | - export PATH="$PWD/cmake-3.1.0-Linux-x86_64/bin/:$PATH" 16 | 17 | script: 18 | - git submodule update --init --recursive 19 | - ./build.sh 20 | 21 | env: 22 | global: 23 | secure: "QeQ3FDs8vOcB7+TJBPIszTPo6EuYOTcqTlC15fAXTHLLnGsY36ySSMKXKq9aV5mqJr9t92WGY169fAW35vg9wwQAx09A2EIEkaqLysozkNxaR9LralgwEEAXXJP2lD5NvJ0n13PMPloGyDCnjBRUjJ+WdDz5H6F329PbItKo5uE=" 24 | 25 | after_success: 26 | - > 27 | if [ "$TRAVIS_BRANCH" == "master" ]; then 28 | GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" 29 | GITREV="`git show -s --format='%h'`" 30 | REV_NAME_BS="decrypt9-${GITDATE}-${GITREV}-bootstrap" 31 | REV_NAME_B2="decrypt9-${GITDATE}-${GITREV}-brahma2" 32 | 33 | sudo apt-get -qq install lftp p7zip-full 34 | mkdir "$REV_NAME_BS" "$REV_NAME_B2" 35 | 36 | cp -R README.md build/source/decrypt9-bs.bin scripts "$REV_NAME_BS" 37 | 7z a "$REV_NAME_BS" "$REV_NAME_BS/*" 38 | cp -R README.md build/source/Decrypt9.3dsx build/source/Decrypt9.smdh scripts "$REV_NAME_B2" 39 | 7z a "$REV_NAME_B2" "$REV_NAME_B2/*" 40 | 41 | lftp -c "open -u builds,$BUILD_PASSWORD sftp://archshift.com; put -O '/decrypt9/nightly/bootstrap' '${REV_NAME_BS}.7z'" 42 | lftp -c "open -u builds,$BUILD_PASSWORD sftp://archshift.com; put -O '/decrypt9/nightly/brahma2' '${REV_NAME_B2}.7z'" 43 | fi 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | include(ExternalProject) 3 | project(Decrypt9) 4 | 5 | # set(DECRYPT9_LOADER_CAKEHAX ON CACHE BOOL "Build CakeHax payload") 6 | # set(DECRYPT9_LOADER_CAKESROP ON CACHE BOOL "Build CakesROP payload") 7 | set(DECRYPT9_LOADER_BRAHMA ON CACHE BOOL "Build Brahma payload") 8 | set(DECRYPT9_LOADER_BRAHMA2_STANDALONE ON CACHE BOOL "Build standalone Brahma2 app") 9 | 10 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Os") 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Os") 12 | set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g -Wall -Os") 13 | 14 | add_subdirectory(source) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Decrypt9 2 | [![Build Status](https://travis-ci.org/archshift/Decrypt9.svg?branch=master)](https://travis-ci.org/archshift/Decrypt9) 3 | 4 | ### Download 5 | 6 | [Nightly builds](http://builds.archshift.com/decrypt9/nightly) 7 | 8 | ## Generating xorpads for encrypted files 9 | 10 | You can build Decrypt9 by using `./build.sh` in the root folder. You need DevkitARM to build. 11 | 12 | Binaries can then be found in the `build/source` folder. 13 | 14 | ### Decrypting gamecart dumps 15 | 16 | You can use http://dukesrg.no-ip.org/3ds/go to load the Launcher.dat on your 3DS. This should work on almost any firmware version below 9.3. 17 | 18 | Then use `ncchinfo_gen` on your encrypted game (dump the game with the Gateway launcher). 19 | 20 | Then, **if you're on firmware that is less than 7.x**, create/edit `slot0x25KeyX.bin` in a **hex editor** and put in the 7.x KeyX (no, I won't give it to you). 21 | 22 | Place `ncchinfo.bin` (and `slot0x25KeyX.bin`, if on less then 7.x) on your SD card, and run the decryptor. It should take a while, especially if you're decrypting a larger game. 23 | 24 | ## Credits 25 | 26 | Roxas75 for the method of ARM9 code injection 27 | 28 | Cha(N), Kane49, and all other FatFS contributors for FatFS 29 | 30 | Normmatt for `sdmc.s` as well as project infrastructure (Makefile, linker setup, etc) 31 | 32 | Relys, sbJFn5r for the decryptor 33 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)" 4 | 5 | mkdir -p "${SCRIPT_PATH}/build" 6 | cd "${SCRIPT_PATH}/build" 7 | cmake .. -DCMAKE_TOOLCHAIN_FILE=../externals/Toolchain3dsArm9/Toolchain3dsArm9.cmake 8 | 9 | make -j5 10 | -------------------------------------------------------------------------------- /externals/Toolchain3dsArm9/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Lectem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /externals/Toolchain3dsArm9/Toolchain3dsArm9.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Generic) 2 | set(CMAKE_SYSTEM_PROCESSOR armv5te) 3 | set(3DS TRUE) # To be used for multiplatform projects 4 | 5 | # DevkitPro Paths are broken on windows, so we have to fix those 6 | macro(msys_to_cmake_path MsysPath ResultingPath) 7 | if(WIN32) 8 | string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}") 9 | else() 10 | set(${ResultingPath} "${MsysPath}") 11 | endif() 12 | endmacro() 13 | 14 | msys_to_cmake_path("$ENV{DEVKITPRO}" DEVKITPRO) 15 | if(NOT IS_DIRECTORY ${DEVKITPRO}) 16 | message(FATAL_ERROR "Please set DEVKITPRO in your environment") 17 | endif() 18 | 19 | msys_to_cmake_path("$ENV{DEVKITARM}" DEVKITARM) 20 | if(NOT IS_DIRECTORY ${DEVKITARM}) 21 | message(FATAL_ERROR "Please set DEVKITARM in your environment") 22 | endif() 23 | 24 | # Prefix detection only works with compiler id "GNU" 25 | # CMake will look for prefixed g++, cpp, ld, etc. automatically 26 | if(WIN32) 27 | set(CMAKE_ASM_COMPILER "${DEVKITARM}/bin/arm-none-eabi-gcc.exe") 28 | set(CMAKE_C_COMPILER "${DEVKITARM}/bin/arm-none-eabi-gcc.exe") 29 | set(CMAKE_CXX_COMPILER "${DEVKITARM}/bin/arm-none-eabi-g++.exe") 30 | set(CMAKE_AR "${DEVKITARM}/bin/arm-none-eabi-gcc-ar.exe" CACHE STRING "") 31 | set(CMAKE_RANLIB "${DEVKITARM}/bin/arm-none-eabi-gcc-ranlib.exe" CACHE STRING "") 32 | set(DKA_OBJCOPY "${DEVKITARM}/bin/arm-none-eabi-objcopy.exe") 33 | else() 34 | set(CMAKE_ASM_COMPILER "${DEVKITARM}/bin/arm-none-eabi-gcc") 35 | set(CMAKE_C_COMPILER "${DEVKITARM}/bin/arm-none-eabi-gcc") 36 | set(CMAKE_CXX_COMPILER "${DEVKITARM}/bin/arm-none-eabi-g++") 37 | set(CMAKE_AR "${DEVKITARM}/bin/arm-none-eabi-gcc-ar" CACHE STRING "") 38 | set(CMAKE_RANLIB "${DEVKITARM}/bin/arm-none-eabi-gcc-ranlib" CACHE STRING "") 39 | set(DKA_OBJCOPY "${DEVKITARM}/bin/arm-none-eabi-objcopy") 40 | endif() 41 | 42 | set(CMAKE_FIND_ROOT_PATH ${DEVKITARM} ${DEVKITPRO}) 43 | 44 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 45 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 46 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 47 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 48 | 49 | SET(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available") 50 | 51 | add_definitions(-DARM9) 52 | 53 | set(ARCH "-mthumb -mthumb-interwork -march=armv5te -mtune=arm946e-s") 54 | set(CMAKE_C_FLAGS "${ARCH} -fomit-frame-pointer -ffast-math" CACHE STRING "C flags") 55 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions" CACHE STRING "C++ flags") 56 | set(CMAKE_ASM_FLAGS "${ARCH} -x assembler-with-cpp" CACHE STRING "ASM flags") 57 | 58 | set(DKA_LDFLAGS "-nostartfiles ${ARCH}") 59 | set(CMAKE_EXE_LINKER_FLAGS "${DKA_LDFLAGS}" CACHE STRING "Linker flags (EXE)") 60 | set(CMAKE_MODULE_LINKER_FLAGS "${DKA_LDFLAGS}" CACHE STRING "Linker flags (module)") 61 | set(CMAKE_STATIC_LINKER_FLAGS "${DKA_LDFLAGS}" CACHE STRING "Linker flags (static)") 62 | -------------------------------------------------------------------------------- /externals/brahma-loader-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)" 5 | 6 | TARGET_DIR="$1" 7 | TARGET_NAME="$2" 8 | APPINFO_FILE="$3" 9 | 10 | TARGET_BIN="${TARGET_DIR}/${TARGET_NAME}.bin" 11 | 12 | LDR_DIR="${SCRIPT_PATH}/BrahmaLoader" 13 | LDR_APPINFO_FILE="${LDR_DIR}/resources/AppInfo" 14 | LDR_PAYLOAD_FILE="${LDR_DIR}/data/payload.bin" 15 | LDR_OUTPUT_DIR="${LDR_DIR}/output" 16 | 17 | cp "$TARGET_BIN" "$LDR_PAYLOAD_FILE" 18 | cp "$APPINFO_FILE" "$LDR_APPINFO_FILE" 19 | ( cd "$LDR_DIR" && make ) 20 | find "${LDR_OUTPUT_DIR}" -type f -exec cp {} "$TARGET_DIR" \; 21 | rm -r "${LDR_OUTPUT_DIR}" 22 | -------------------------------------------------------------------------------- /resources/BrahmaAppInfo: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # APP_TITLE: Name of the homebrew app, also name of folder and .3DSX/.SMDH 3 | # APP_DESCRIPTION: Description of the homebrew app in HB launcher 4 | # APP_AUTHOR: Author of the homebrew app, also display in HB launcher 5 | # 6 | # PAYLOAD_PATH: Path to the ARM9 payload on the 3DS SD card (don't forget "//") 7 | # PAYLOAD_OFFSET: Offset of the ARM9 payload in the file given above 8 | # PAYLOAD_MAXSIZE: Maximum size of the ARM9 payload, cannot exceed 0x100000 9 | # 10 | # PAYLOAD_MEM: If set to anything, force loading the payload from memory 11 | # 12 | # VOODOO: This may be used to boost the boot rate of a given payload, it can take 13 | # values from 0x00...0x3F, experimentation is recommended, also see below 14 | # 15 | # If PAYLOAD_PATH is not given, payload is loaded from memory (data dir in source) 16 | # PAYLOAD_OFFSET is optional if offset is 0x00000000 17 | # PAYLOAD_MAXSIZE is ignored if it is zero, and shouldn't be required at all 18 | #--------------------------------------------------------------------------------- 19 | 20 | #--------------------------------------------------------------------------------- 21 | # The VOODOO parameter: 22 | # This paramter is processed in binary (0b111111 = 0x3F = 63). Starting from the 23 | # right, this is what those bits do (and of course you can combine any of these 24 | # fixes, this is what is intended). 25 | # Bit 0: Load (unnecessary) HB services. 26 | # 0b000001/0x01 => Load them 27 | # Bit 1: "Magic Fix", no one (might not be absolutely correct) knows what it does, 28 | # but CTR Boot Manager, BootCTR and HBL itself all use it. 29 | # 0b000010/0x02 => Use magic fix 30 | # Bit 2 and Bit 3: Set the length of the bootfix delay. 31 | # 0b000000/0x00 => 0 frames 32 | # 0b000100/0x04 => 50 frames 33 | # 0b001000/0x08 => 100 frames 34 | # 0b001100/0x0C => 150 frames 35 | # Bit 4 and Bit 5: Initialize console and print some nonsense text. 36 | # 0b010000/0x10 => Use this fix for the bottom screen 37 | # 0b100000/0x20 => Use this fix for the top screen 38 | #--------------------------------------------------------------------------------- 39 | 40 | APP_TITLE = Decrypt9 41 | APP_DESCRIPTION = Xorpad generator & NAND data decryptor. 42 | APP_AUTHOR = Archshift & others 43 | 44 | PAYLOAD_MEM = 1 45 | VOODOO = 0x3 46 | -------------------------------------------------------------------------------- /resources/BrahmaIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archshift/Decrypt9/a77d7decd15232ca65f87e63cf9c0e025b0e55ae/resources/BrahmaIcon.png -------------------------------------------------------------------------------- /resources/CakesROP/drunkenlogo.grit: -------------------------------------------------------------------------------- 1 | -W3 2 | # disable alpha and set opaque bit for all pixels 3 | -gT! 4 | 5 | # use lz77 compression 6 | -gzl 7 | 8 | # 16 bit bitmap 9 | -gB16 10 | 11 | -gb 12 | -------------------------------------------------------------------------------- /resources/CakesROP/drunkenlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archshift/Decrypt9/a77d7decd15232ca65f87e63cf9c0e025b0e55ae/resources/CakesROP/drunkenlogo.png -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore 3DS CDN downloads 2 | /0004* 3 | # Ignore BIN files and CIA / 3DS 4 | *.bin 5 | *.3ds 6 | *.cia 7 | -------------------------------------------------------------------------------- /scripts/cdn_download.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # This script is old and shitty 3 | 4 | import os 5 | import errno 6 | import sys 7 | import urllib2 8 | from struct import unpack 9 | from binascii import hexlify 10 | from hashlib import sha256 11 | from Crypto.Cipher import AES 12 | 13 | 14 | def mkdir_p(path): 15 | try: 16 | os.makedirs(path) 17 | except OSError as exc: # Python >2.5 18 | if exc.errno == errno.EEXIST and os.path.isdir(path): 19 | pass 20 | else: 21 | raise 22 | 23 | 24 | # ######## # From https://stackoverflow.com/questions/5783517/downloading-progress-bar-urllib2-python 25 | def chunk_report(bytes_so_far, chunk_size, total_size): 26 | percent = float(bytes_so_far) / total_size 27 | percent = round(percent*100, 2) 28 | sys.stdout.write("Downloaded %d of %d bytes (%0.2f%%)\r" % (bytes_so_far, total_size, percent)) 29 | 30 | if bytes_so_far >= total_size: 31 | sys.stdout.write('\n') 32 | 33 | 34 | def chunk_read(response, outfname, chunk_size=2*1024*1024, report_hook=None): 35 | fh = open(outfname, 'wb') 36 | total_size = response.info().getheader('Content-Length').strip() 37 | total_size = int(total_size) 38 | bytes_so_far = 0 39 | 40 | while 1: 41 | if report_hook: 42 | report_hook(bytes_so_far, chunk_size, total_size) 43 | 44 | chunk = response.read(chunk_size) 45 | bytes_so_far += len(chunk) 46 | if not chunk: 47 | break 48 | 49 | fh.write(chunk) 50 | fh.close() 51 | # ######## # 52 | 53 | 54 | def display_usage_info(): 55 | print 'Usage: cdn_download.py TitleID TitleKey [-redown -redec]' 56 | print '-redown : redownload content' 57 | print '-redec : re-attempt content decryption' 58 | raise SystemExit(0) 59 | 60 | if len(sys.argv) < 3: 61 | display_usage_info() 62 | 63 | titleid = sys.argv[1] 64 | titlekey = sys.argv[2] 65 | forceDownload = 0 66 | forceDecrypt = 0 67 | 68 | for i in xrange(len(sys.argv)): 69 | if sys.argv[i] == '-redown': 70 | forceDownload = 1 71 | elif sys.argv[i] == '-redec': 72 | forceDecrypt = 1 73 | 74 | if len(titleid) != 16 or len(titlekey) != 32: 75 | print 'Invalid arguments' 76 | raise SystemExit(0) 77 | 78 | baseurl = 'http://nus.cdn.c.shop.nintendowifi.net/ccs/download/' + titleid 79 | 80 | print 'Downloading TMD...' 81 | 82 | try: 83 | tmd = urllib2.urlopen(baseurl + '/tmd') 84 | except urllib2.URLError, e: 85 | print 'ERROR: Bad title ID?' 86 | raise SystemExit(0) 87 | 88 | tmd = tmd.read() 89 | 90 | mkdir_p(titleid) 91 | open(titleid + '/tmd', 'wb').write(tmd) 92 | print 'Done\n' 93 | 94 | if tmd[:4] != '\x00\x01\x00\x04': 95 | print 'Unexpected signature type.' 96 | raise SystemExit(0) 97 | 98 | # Set Proper Version 99 | version = unpack('>H', tmd[0x1dc:0x1de])[0] 100 | # Set Save Size 101 | saveSize = (unpack('H', tmd[0x206:0x208])[0] 104 | 105 | print 'Content count: ' + str(contentCount) + '\n' 106 | 107 | 108 | # Download Contents 109 | fSize = 16*1024 110 | for i in xrange(contentCount): 111 | cOffs = 0xB04+(0x30*i) 112 | cID = format(unpack('>I', tmd[cOffs:cOffs+4])[0], '08x') 113 | cIDX = format(unpack('>H', tmd[cOffs+4:cOffs+6])[0], '04x') 114 | cSIZE = format(unpack('>Q', tmd[cOffs+8:cOffs+16])[0], 'd') 115 | cHASH = format(unpack('>32s', tmd[cOffs+16:cOffs+48])[0]) 116 | 117 | print 'Content ID: ' + cID 118 | print 'Content Index: ' + cIDX 119 | print 'Content Size: ' + cSIZE 120 | print 'Content Hash: ' + hexlify(cHASH) 121 | 122 | aes_obj = AES.new(titlekey.decode("hex"), AES.MODE_CBC, (cIDX + '0000000000000000000000000000').decode("hex")) 123 | 124 | outfname = titleid + '/' + cID 125 | if os.path.exists(outfname) == 0 or forceDownload == 1\ 126 | or os.path.getsize(outfname) != unpack('>Q', tmd[cOffs+8:cOffs+16])[0]: 127 | response = urllib2.urlopen(baseurl + '/' + cID) 128 | chunk_read(response, outfname, report_hook=chunk_report) 129 | 130 | # If we redownloaded the content, then decrypting it is implied. 131 | with open(outfname, 'rb') as fo: 132 | ciphertext = fo.read() 133 | dec = aes_obj.decrypt(ciphertext) 134 | with open(outfname + '.dec', 'wb') as fo: 135 | fo.write(dec) 136 | 137 | elif os.path.exists(outfname + '.dec') == 0 or forceDecrypt == 1 \ 138 | or os.path.getsize(outfname + '.dec') != unpack('>Q', tmd[cOffs+8:cOffs+16])[0]: 139 | with open(outfname, 'rb') as fo: 140 | ciphertext = fo.read() 141 | dec = aes_obj.decrypt(ciphertext) 142 | with open(outfname + '.dec', 'wb') as fo: 143 | fo.write(dec) 144 | 145 | with open(outfname + '.dec', 'rb') as fh: 146 | fh.seek(0, os.SEEK_END) 147 | fhSize = fh.tell() 148 | if fh.tell() != unpack('>Q', tmd[cOffs+8:cOffs+16])[0]: 149 | print 'Title size mismatch. Download likely incomplete' 150 | print 'Downloaded: ' + format(fh.tell(), 'd') 151 | raise SystemExit(0) 152 | fh.seek(0) 153 | hash = sha256() 154 | 155 | while fh.tell() != fhSize: 156 | hash.update(fh.read(0x1000000)) 157 | print 'checking hash: ' + format(float(fh.tell()*100)/fhSize, '.1f') + '% done\r', 158 | 159 | sha256file = hash.hexdigest() 160 | if sha256file != hexlify(cHASH): 161 | print 'hash mismatched, Decryption likely failed, wrong key?' 162 | print 'got hash: ' + sha256file 163 | raise SystemExit(0) 164 | fh.seek(0x100) 165 | if fh.read(4) != 'NCCH': 166 | fh.seek(0x60) 167 | if fh.read(4) != 'WfA\0': 168 | print 'Not NCCH, nor DSiWare, file likely corrupted' 169 | raise SystemExit(0) 170 | else: 171 | print 'Not an NCCH container, likely DSiWare' 172 | fh.seek(0, os.SEEK_END) 173 | fSize += fh.tell() 174 | 175 | print '\n' 176 | 177 | print 'Done!' 178 | -------------------------------------------------------------------------------- /scripts/dump_ticket_keys.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # This script is awful, but it 'works for me'(TM) 3 | 4 | import sys 5 | import os 6 | import re 7 | import binascii 8 | import struct 9 | 10 | # encTitleKeys.bin format 11 | # 12 | # 4 bytes Number of entries 13 | # 12 bytes Reserved 14 | # 15 | # entry(32 bytes in size): 16 | # 4 bytes Common key index(0-5) 17 | # 4 bytes Reserved 18 | # 8 bytes Title Id 19 | # 16 bytes Encrypted title key 20 | 21 | if len(sys.argv) < 2: 22 | print 'Usage: dump_ticket_keys.py ticket.db' 23 | print 'Instead of ticket.db, you can also pass in an individual ticket' 24 | print 'or many tickets merged together' 25 | sys.exit(0) 26 | 27 | if not os.path.isfile(sys.argv[1]): 28 | print "Input file '%s' doesn't exist." % sys.argv[1] 29 | raise SystemExit(0) 30 | 31 | with open(sys.argv[1], 'rb') as fh: 32 | ticks = fh.read() 33 | 34 | ticketOffsets = [m.start() for m in re.finditer(b'Root-CA00000003-XS0000000c', ticks)] 35 | 36 | ticks = bytearray(ticks) 37 | 38 | # This check can fail on other DB files, etc (since 'Root-CA' 39 | # can appear in them, but not necessarily mean there are tickets). 40 | # Just make sure to only pass in a ticket.db file or individual 41 | # tickets (or a file with a bunch of individual ones merged). 42 | if len(ticketOffsets) == 0: 43 | print 'No tickets found. Did you input the correct file?' 44 | sys.exit(0) 45 | 46 | encKeys = [] 47 | 48 | for offs in ticketOffsets: 49 | encKey = ticks[offs+0x7F:offs+0x8F] 50 | tId = ticks[offs+0x9C:offs+0xA4] 51 | commonKeyIndex = ticks[offs+0xB1] 52 | 53 | if [encKey, tId, commonKeyIndex] in encKeys: 54 | continue 55 | if ticks[offs+0x7C] != 0x1: # This value is always 0x1 in a ticket. 56 | continue 57 | if commonKeyIndex > 5: 58 | continue 59 | 60 | # if commonKeyIndex != 0: # Uncomment these two lines to grab just eShop ticket info. 61 | # continue 62 | 63 | print 'Encrypted title key: ' + binascii.hexlify(encKey) 64 | print 'Title ID: ' + binascii.hexlify(tId) 65 | print 'Common key index: %u' % commonKeyIndex 66 | print '' 67 | 68 | encKeys.append([encKey, tId, commonKeyIndex]) 69 | 70 | outData = struct.pack('>((3-i)*8)) & 0xFF) 123 | 124 | return bytes(counter) 125 | 126 | 127 | def parseNCSD(fh): 128 | print 'Parsing NCSD in file "%s":' % os.path.basename(fh.name) 129 | entries = 0 130 | data = '' 131 | 132 | fh.seek(0) 133 | header = ncsdHdr() 134 | fh.readinto(header) #Reads header into structure 135 | 136 | for i in xrange(len(header.offset_sizeTable)): 137 | if header.offset_sizeTable[i].offset: 138 | result = parseNCCH(fh, header.offset_sizeTable[i].offset * mediaUnitSize, i, reverseCtypeArray(header.titleId), 0) 139 | entries += result[0] 140 | data = data + result[1] 141 | return [entries, data] 142 | 143 | def parseNCCH(fh, offs=0, idx=0, titleId='', standAlone=1): 144 | tab = ' ' if not standAlone else ' ' 145 | if not standAlone: 146 | print ' Parsing %s NCCH' % ncsdPartitions[idx] 147 | else: 148 | print 'Parsing NCCH in file "%s":' % os.path.basename(fh.name) 149 | entries = 0 150 | data = '' 151 | 152 | fh.seek(offs) 153 | header = ncchHdr() 154 | fh.readinto(header) #Reads header into structure 155 | 156 | if titleId == '': 157 | titleId = reverseCtypeArray(header.titleId) 158 | 159 | keyY = bytearray(header.signature[:16]) 160 | 161 | if not standAlone: 162 | print tab + 'NCCH Offset: %08X' % offs 163 | print tab + 'Product code: ' + str(bytearray(header.productCode)).rstrip('\x00') 164 | if not standAlone: 165 | print tab + 'Partition number: %d' % idx 166 | print tab + 'KeyY: %s' % hexlify(keyY).upper() 167 | print tab + 'Title ID: %s' % reverseCtypeArray(header.titleId) 168 | print tab + 'Format version: %d' % header.formatVersion 169 | 170 | uses7xCrypto = bytearray(header.flags)[3] 171 | if uses7xCrypto: 172 | print tab + 'Uses 7.x NCCH crypto' 173 | 174 | usesSeedCrypto = bytearray(header.flags)[7] 175 | if usesSeedCrypto == 0x20: 176 | usesSeedCrypto = 1 177 | print tab + 'Uses 9.x SEED crypto' 178 | else: 179 | usesSeedCrypto = 0 180 | 181 | 182 | print '' 183 | 184 | if header.exhdrSize: 185 | data = data + parseNCCHSection(header, ncchSection.exheader, 0, 0, 1, tab) 186 | data = data + genOutName(titleId, ncsdPartitions[idx], b'exheader') 187 | entries += 1 188 | print '' 189 | if header.exefsSize: #We need generate two xorpads for exefs if it uses 7.x crypto, since only a part of it uses the new crypto. 190 | data = data + parseNCCHSection(header, ncchSection.exefs, 0, 0, 1, tab) 191 | data = data + genOutName(titleId, ncsdPartitions[idx], b'exefs_norm') 192 | entries += 1 193 | if uses7xCrypto or usesSeedCrypto: 194 | data = data + parseNCCHSection(header, ncchSection.exefs, uses7xCrypto, usesSeedCrypto, 0, tab) 195 | data = data + genOutName(titleId, ncsdPartitions[idx], b'exefs_7x') 196 | entries += 1 197 | print '' 198 | if header.romfsSize: 199 | data = data + parseNCCHSection(header, ncchSection.romfs, uses7xCrypto, usesSeedCrypto, 1, tab) 200 | data = data + genOutName(titleId, ncsdPartitions[idx], b'romfs') 201 | entries += 1 202 | print '' 203 | 204 | print '' 205 | 206 | return [entries, data] 207 | 208 | def parseNCCHSection(header, type, uses7xCrypto, usesSeedCrypto, doPrint, tab): 209 | if type == ncchSection.exheader: 210 | sectionName = 'ExHeader' 211 | offset = 0x200 #Always 0x200 212 | sectionSize = header.exhdrSize * mediaUnitSize 213 | elif type == ncchSection.exefs: 214 | sectionName = 'ExeFS' 215 | offset = header.exefsOffset * mediaUnitSize 216 | sectionSize = header.exefsSize * mediaUnitSize 217 | elif type == ncchSection.romfs: 218 | sectionName = 'RomFS' 219 | offset = header.romfsOffset * mediaUnitSize 220 | sectionSize = header.romfsSize * mediaUnitSize 221 | else: 222 | print 'Invalid NCCH section type was somehow passed in. :/' 223 | sys.exit() 224 | 225 | counter = getNcchAesCounter(header, type) 226 | keyY = bytearray(header.signature[:16]) 227 | titleId = struct.unpack(' 112: 243 | print "Output file name too large. This shouldn't happen." 244 | sys.exit() 245 | 246 | return outName + (b'\x00'*(112-len(outName))) #Pad out so whole entry is 160 bytes (48 bytes are set before filename) 247 | 248 | 249 | 250 | if __name__ == "__main__": 251 | if len(sys.argv) < 2: 252 | print 'usage: ncchinfo_gen.py files..' 253 | print ' Supports parsing both CCI(.3ds) and NCCH files.' 254 | print ' Wildcards are supported' 255 | print ' Example: ncchinfo_gen.py *.ncch SM3DL.3ds' 256 | sys.exit() 257 | 258 | inpFiles = [] 259 | existFiles = [] 260 | 261 | for i in xrange(len(sys.argv)-1): 262 | inpFiles = inpFiles + glob.glob(sys.argv[i+1].replace('[','[[]')) #Needed for wildcard support on Windows 263 | 264 | for i in xrange(len(inpFiles)): 265 | if os.path.isfile(inpFiles[i]): 266 | existFiles.append(inpFiles[i]) 267 | 268 | if existFiles == []: 269 | print "Input files don't exist" 270 | sys.exit() 271 | 272 | print '' 273 | 274 | entries = 0 275 | data = '' 276 | 277 | for file in existFiles: 278 | result = [] 279 | 280 | with open(file,'rb') as fh: 281 | fh.seek(0x100) 282 | magic = fh.read(4) 283 | if magic == b'NCSD': 284 | result = parseNCSD(fh) 285 | print '' 286 | elif magic == b'NCCH': 287 | result = parseNCCH(fh) 288 | print '' 289 | 290 | if result: 291 | entries += result[0] 292 | data = data + result[1] 293 | 294 | dndFix = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'ncchinfo.bin') #Fix drag'n'drop 295 | with open(dndFix, 'wb') as fh: 296 | fh.write(struct.pack('>((3-i)*8)) & 0xFF) 123 | 124 | return bytes(counter) 125 | 126 | 127 | def parseNCSD(fh): 128 | print 'Parsing NCSD in file "%s":' % os.path.basename(fh.name) 129 | entries = 0 130 | data = '' 131 | 132 | fh.seek(0) 133 | header = ncsdHdr() 134 | fh.readinto(header) #Reads header into structure 135 | 136 | for i in xrange(len(header.offset_sizeTable)): 137 | if header.offset_sizeTable[i].offset: 138 | result = parseNCCH(fh, header.offset_sizeTable[i].offset * mediaUnitSize, i, reverseCtypeArray(header.titleId), 0) 139 | entries += result[0] 140 | data = data + result[1] 141 | return [entries, data] 142 | 143 | def parseNCCH(fh, offs=0, idx=0, titleId='', standAlone=1): 144 | tab = ' ' if not standAlone else ' ' 145 | if not standAlone: 146 | print ' Parsing %s NCCH' % ncsdPartitions[idx] 147 | else: 148 | print 'Parsing NCCH in file "%s":' % os.path.basename(fh.name) 149 | entries = 0 150 | data = '' 151 | 152 | fh.seek(offs) 153 | header = ncchHdr() 154 | fh.readinto(header) #Reads header into structure 155 | 156 | if titleId == '': 157 | titleId = reverseCtypeArray(header.titleId) 158 | 159 | keyY = bytearray(header.signature[:16]) 160 | 161 | if not standAlone: 162 | print tab + 'NCCH Offset: %08X' % offs 163 | print tab + 'Product code: ' + str(bytearray(header.productCode)).rstrip('\x00') 164 | if not standAlone: 165 | print tab + 'Partition number: %d' % idx 166 | print tab + 'KeyY: %s' % hexlify(keyY).upper() 167 | print tab + 'Title ID: %s' % reverseCtypeArray(header.titleId) 168 | print tab + 'Format version: %d' % header.formatVersion 169 | 170 | uses7xCrypto = bytearray(header.flags)[3] 171 | if uses7xCrypto: 172 | print tab + 'Uses 7.x NCCH crypto' 173 | 174 | usesSeedCrypto = bytearray(header.flags)[7] 175 | if usesSeedCrypto == 0x20: 176 | usesSeedCrypto = 1 177 | print tab + 'Uses 9.x SEED crypto' 178 | else: 179 | usesSeedCrypto = 0 180 | 181 | 182 | print '' 183 | 184 | if header.exhdrSize: 185 | data = data + parseNCCHSection(header, ncchSection.exheader, 0, 0, 1, tab) 186 | data = data + genOutName(titleId, ncsdPartitions[idx], b'exheader') 187 | entries += 1 188 | print '' 189 | if header.exefsSize: #We need generate two xorpads for exefs if it uses 7.x crypto, since only a part of it uses the new crypto. 190 | data = data + parseNCCHSection(header, ncchSection.exefs, 0, 0, 1, tab) 191 | data = data + genOutName(titleId, ncsdPartitions[idx], b'exefs_norm') 192 | entries += 1 193 | if uses7xCrypto: 194 | data = data + parseNCCHSection(header, ncchSection.exefs, uses7xCrypto, usesSeedCrypto, 0, tab) 195 | data = data + genOutName(titleId, ncsdPartitions[idx], b'exefs_7x') 196 | entries += 1 197 | print '' 198 | if header.romfsSize: 199 | data = data + parseNCCHSection(header, ncchSection.romfs, uses7xCrypto, usesSeedCrypto, 1, tab) 200 | data = data + genOutName(titleId, ncsdPartitions[idx], b'romfs') 201 | entries += 1 202 | print '' 203 | 204 | print '' 205 | 206 | return [entries, data] 207 | 208 | def parseNCCHSection(header, type, uses7xCrypto, usesSeedCrypto, doPrint, tab): 209 | if type == ncchSection.exheader: 210 | sectionName = 'ExHeader' 211 | offset = 0x200 #Always 0x200 212 | sectionSize = header.exhdrSize * mediaUnitSize 213 | elif type == ncchSection.exefs: 214 | sectionName = 'ExeFS' 215 | offset = header.exefsOffset * mediaUnitSize 216 | sectionSize = header.exefsSize * mediaUnitSize 217 | elif type == ncchSection.romfs: 218 | sectionName = 'RomFS' 219 | offset = header.romfsOffset * mediaUnitSize 220 | sectionSize = header.romfsSize * mediaUnitSize 221 | else: 222 | print 'Invalid NCCH section type was somehow passed in. :/' 223 | sys.exit() 224 | 225 | counter = getNcchAesCounter(header, type) 226 | keyY = bytearray(header.signature[:16]) 227 | titleId = struct.unpack(' 112: 243 | print "Output file name too large. This shouldn't happen." 244 | sys.exit() 245 | 246 | return outName + (b'\x00'*(112-len(outName))) #Pad out so whole entry is 160 bytes (48 bytes are set before filename) 247 | 248 | 249 | 250 | if __name__ == "__main__": 251 | if len(sys.argv) < 2: 252 | print 'usage: ncchinfo_tgen.py files..' 253 | print ' Supports parsing both CCI(.3ds) and NCCH files.' 254 | print ' Wildcards are supported' 255 | print ' Example: ncchinfo_tgen.py *.ncch SM3DL.3ds' 256 | sys.exit() 257 | 258 | inpFiles = [] 259 | existFiles = [] 260 | 261 | for i in xrange(len(sys.argv)-1): 262 | inpFiles = inpFiles + glob.glob(sys.argv[i+1].replace('[','[[]')) #Needed for wildcard support on Windows 263 | 264 | for i in xrange(len(inpFiles)): 265 | if os.path.isfile(inpFiles[i]): 266 | existFiles.append(inpFiles[i]) 267 | 268 | if existFiles == []: 269 | print "Input files don't exist" 270 | sys.exit() 271 | 272 | print '' 273 | 274 | entries = 0 275 | data = '' 276 | 277 | for file in existFiles: 278 | result = [] 279 | 280 | with open(file,'rb') as fh: 281 | fh.seek(0x100) 282 | magic = fh.read(4) 283 | if magic == b'NCSD': 284 | result = parseNCSD(fh) 285 | print '' 286 | elif magic == b'NCCH': 287 | result = parseNCCH(fh) 288 | print '' 289 | 290 | if result: 291 | entries += result[0] 292 | data = data + result[1] 293 | 294 | dndFix = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'ncchinfo.bin') #Fix drag'n'drop 295 | with open(dndFix, 'wb') as fh: 296 | fh.write(struct.pack(' 180: 42 | print "Filename too long. This shouldn't happen." 43 | print '%s' % fname 44 | raise SystemExit(0) 45 | ctrlist.append([ctr, fname, fsizeMB, fname]) 46 | os.chdir(savedpath) 47 | 48 | 49 | if len(sys.argv) < 2: 50 | print 'usage: sdinfo_gen.py folderpath' 51 | print 'folderpath: folder on your SD that contains "dbs", "title", etc.' 52 | print 'For example, sdinfo_gen.py "X:/Nintendo 3DS/xxxxxxxxx/xxxxxxxx/"' 53 | raise SystemExit(0) 54 | 55 | savedpath = os.getcwd() 56 | try: 57 | os.chdir(sys.argv[1]) 58 | except Exception as ex: 59 | print str(ex) 60 | raise SystemExit(0) 61 | 62 | ctrlist = [] 63 | 64 | parse_dir('dbs', ctrlist) 65 | parse_dir('extdata', ctrlist) 66 | parse_dir('title', ctrlist) 67 | # parsedir('backups', ctrlist) # Untested 68 | 69 | os.chdir(savedpath) 70 | 71 | if not ctrlist == []: 72 | fh = open('SDinfo.bin', 'wb') 73 | listlen = len(ctrlist) 74 | fh.write(struct.pack('=0: 10 | SBoffset = savedata.find('SEEDDB') 11 | tidoffset = (struct.unpack('\\sysdata\\0001000f\\'" 46 | print 'Decyrpt NAND FAT16 partition and dump these files for this script.' 47 | sys.exit() 48 | 49 | for filename in sys.argv[1:]: 50 | titleid,seed = GetIDandSEED(filename) 51 | tids += titleid 52 | seeds += seed 53 | 54 | if len(tids)<1: 55 | print 'No SEED found.' 56 | sys.exit() 57 | 58 | outpath = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'seeddb.bin') 59 | with open(outpath,'wb') as seeddb: 60 | print '\nGenerating seeddb.bin ...' 61 | seeddb.write(struct.pack(' ${TARGET_NAME}.bin 65 | DEPENDS $ VERBATIM) 66 | endfunction() 67 | 68 | # if(DECRYPT9_LOADER_CAKEHAX) 69 | # set(EXEC_GW ON) 70 | # endif() 71 | 72 | # if(DECRYPT9_LOADER_CAKESROP) 73 | # set(EXEC_GW ON) 74 | # endif() 75 | 76 | if(DECRYPT9_LOADER_BRAHMA) 77 | set(EXEC_BS ON) 78 | endif() 79 | 80 | if(DECRYPT9_LOADER_BRAHMA2_STANDALONE) 81 | set(EXEC_BS ON) 82 | set(BRAHMA2_APP_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Decrypt9.3dsx") 83 | set(BRAHMA2_SMDH_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Decrypt9.smdh") 84 | add_custom_command(OUTPUT ${BRAHMA2_APP_OUTPUT} ${BRAHMA2_SMDH_OUTPUT} 85 | COMMAND ${CMAKE_SOURCE_DIR}/externals/brahma-loader-build.sh 86 | "${CMAKE_CURRENT_BINARY_DIR}" ${TARGET_NAME_BS} "${CMAKE_SOURCE_DIR}/resources/BrahmaAppInfo" 87 | DEPENDS ${TARGET_NAME_BS}.bin VERBATIM) 88 | add_custom_target(decrypt9-brahma2 ALL SOURCES ${BRAHMA2_APP_OUTPUT}) 89 | endif() 90 | 91 | if(EXEC_BS) 92 | set(LD_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/loader-bs/bootstrap.ld") 93 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-T'${LD_FILE_PATH}'") 94 | add_output_bin(${TARGET_NAME_BS} EXEC_BOOTSTRAP ${SOURCE_FILES} ${HEADER_FILES} ${BS_SOURCE_FILES}) 95 | endif() 96 | 97 | if(EXEC_GW) 98 | set(LD_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/loader-gw/gateway.ld") 99 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-T'${LD_FILE_PATH}'") 100 | add_output_bin(${TARGET_NAME_GW} EXEC_GATEWAY ${SOURCE_FILES} ${HEADER_FILES} ${GW_SOURCE_FILES}) 101 | endif() 102 | -------------------------------------------------------------------------------- /source/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define u8 uint8_t 12 | #define u16 uint16_t 13 | #define u32 uint32_t 14 | #define u64 uint64_t 15 | 16 | #define vu8 volatile u8 17 | #define vu16 volatile u16 18 | #define vu32 volatile u32 19 | #define vu64 volatile u64 20 | 21 | #define max(a,b) \ 22 | ({ __typeof__ (a) _a = (a); \ 23 | __typeof__ (b) _b = (b); \ 24 | _a > _b ? _a : _b; }) 25 | #define min(a,b) \ 26 | ({ __typeof__ (a) _a = (a); \ 27 | __typeof__ (b) _b = (b); \ 28 | _a < _b ? _a : _b; }) 29 | #define getbe16(d) \ 30 | (((d)[0]<<8) | (d)[1]) 31 | #define getbe32(d) \ 32 | ((((u32) getbe16(d))<<16) | ((u32) getbe16(d+2))) 33 | #define getbe64(d) \ 34 | ((((u64) getbe32(d))<<32) | ((u64) getbe32(d+4))) 35 | #define getle16(d) (*((u16*) (d))) 36 | #define getle32(d) (*((u32*) (d))) 37 | #define getle64(d) (*((u64*) (d))) 38 | #define align(v,a) \ 39 | (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) 40 | 41 | // work files / directories 42 | #define GAME_DIR "/D9Game" 43 | #define WORK_DIR "/Decrypt9" 44 | 45 | inline char* strupper(const char* str) { 46 | char* buffer = (char*)malloc(strlen(str) + 1); 47 | for (int i = 0; i < strlen(str); ++i) 48 | buffer[i] = toupper((unsigned)str[i]); 49 | return buffer; 50 | } 51 | 52 | inline char* strlower(const char* str) { 53 | char* buffer = (char*)malloc(strlen(str) + 1); 54 | for (int i = 0; i < strlen(str); ++i) 55 | buffer[i] = tolower((unsigned)str[i]); 56 | return buffer; 57 | } 58 | -------------------------------------------------------------------------------- /source/decryptor/aes.c: -------------------------------------------------------------------------------- 1 | /* original version by megazig */ 2 | #include "aes.h" 3 | 4 | void setup_aeskeyX(u8 keyslot, void* keyx) 5 | { 6 | u32 * _keyx = (u32*)keyx; 7 | *REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | 0x80; 8 | *REG_AESKEYXFIFO = _keyx[0]; 9 | *REG_AESKEYXFIFO = _keyx[1]; 10 | *REG_AESKEYXFIFO = _keyx[2]; 11 | *REG_AESKEYXFIFO = _keyx[3]; 12 | } 13 | 14 | void setup_aeskeyY(u8 keyslot, void* keyy) 15 | { 16 | u32 * _keyy = (u32*)keyy; 17 | *REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | 0x80; 18 | *REG_AESKEYYFIFO = _keyy[0]; 19 | *REG_AESKEYYFIFO = _keyy[1]; 20 | *REG_AESKEYYFIFO = _keyy[2]; 21 | *REG_AESKEYYFIFO = _keyy[3]; 22 | } 23 | 24 | void setup_aeskey(u8 keyslot, void* key) 25 | { 26 | u32 * _key = (u32*)key; 27 | *REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | 0x80; 28 | *REG_AESKEYFIFO = _key[0]; 29 | *REG_AESKEYFIFO = _key[1]; 30 | *REG_AESKEYFIFO = _key[2]; 31 | *REG_AESKEYFIFO = _key[3]; 32 | } 33 | 34 | void use_aeskey(u32 keyno) 35 | { 36 | if (keyno > 0x3F) 37 | return; 38 | *REG_AESKEYSEL = keyno; 39 | *REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */ 40 | } 41 | 42 | void set_ctr(void* iv) 43 | { 44 | u32 * _iv = (u32*)iv; 45 | *REG_AESCNT = (*REG_AESCNT) | AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER; 46 | *(REG_AESCTR + 0) = _iv[3]; 47 | *(REG_AESCTR + 1) = _iv[2]; 48 | *(REG_AESCTR + 2) = _iv[1]; 49 | *(REG_AESCTR + 3) = _iv[0]; 50 | } 51 | 52 | void add_ctr(void* ctr, u32 carry) 53 | { 54 | u32 counter[4]; 55 | u8 *outctr = (u8 *) ctr; 56 | u32 sum; 57 | int32_t i; 58 | 59 | for(i=0; i<4; i++) { 60 | counter[i] = (outctr[i*4+0]<<24) | (outctr[i*4+1]<<16) | (outctr[i*4+2]<<8) | (outctr[i*4+3]<<0); 61 | } 62 | 63 | for(i=3; i>=0; i--) 64 | { 65 | sum = counter[i] + carry; 66 | if (sum < counter[i]) { 67 | carry = 1; 68 | } 69 | else { 70 | carry = 0; 71 | } 72 | counter[i] = sum; 73 | } 74 | 75 | for(i=0; i<4; i++) 76 | { 77 | outctr[i*4+0] = counter[i]>>24; 78 | outctr[i*4+1] = counter[i]>>16; 79 | outctr[i*4+2] = counter[i]>>8; 80 | outctr[i*4+3] = counter[i]>>0; 81 | } 82 | } 83 | 84 | void aes_decrypt(void* inbuf, void* outbuf, size_t size, u32 mode) 85 | { 86 | u32 in = (u32)inbuf; 87 | u32 out = (u32)outbuf; 88 | size_t block_count = size; 89 | size_t blocks; 90 | while (block_count != 0) 91 | { 92 | blocks = (block_count >= 0xFFFF) ? 0xFFFF : block_count; 93 | *REG_AESCNT = 0; 94 | *REG_AESBLKCNT = blocks << 16; 95 | *REG_AESCNT = mode | 96 | AES_CNT_START | 97 | AES_CNT_FLUSH_READ | 98 | AES_CNT_FLUSH_WRITE; 99 | aes_fifos((void*)in, (void*)out, blocks); 100 | in += blocks * AES_BLOCK_SIZE; 101 | out += blocks * AES_BLOCK_SIZE; 102 | block_count -= blocks; 103 | } 104 | } 105 | 106 | void aes_fifos(void* inbuf, void* outbuf, size_t blocks) 107 | { 108 | u32 in = (u32)inbuf; 109 | if (!in) return; 110 | 111 | u32 out = (u32)outbuf; 112 | size_t curblock = 0; 113 | while (curblock != blocks) 114 | { 115 | while (aescnt_checkwrite()); 116 | 117 | int ii = 0; 118 | for (ii = in; ii != in + AES_BLOCK_SIZE; ii += 4) 119 | { 120 | set_aeswrfifo( *(u32*)(ii) ); 121 | } 122 | if (out) 123 | { 124 | while (aescnt_checkread()) ; 125 | for (ii = out; ii != out + AES_BLOCK_SIZE; ii += 4) 126 | { 127 | *(u32*)ii = read_aesrdfifo(); 128 | } 129 | } 130 | curblock++; 131 | } 132 | } 133 | 134 | void set_aeswrfifo(u32 value) 135 | { 136 | *REG_AESWRFIFO = value; 137 | } 138 | 139 | u32 read_aesrdfifo(void) 140 | { 141 | return *REG_AESRDFIFO; 142 | } 143 | 144 | u32 aes_getwritecount() 145 | { 146 | return *REG_AESCNT & 0x1F; 147 | } 148 | 149 | u32 aes_getreadcount() 150 | { 151 | return (*REG_AESCNT >> 5) & 0x1F; 152 | } 153 | 154 | u32 aescnt_checkwrite() 155 | { 156 | size_t ret = aes_getwritecount(); 157 | return (ret > 0xF); 158 | } 159 | 160 | u32 aescnt_checkread() 161 | { 162 | size_t ret = aes_getreadcount(); 163 | return (ret <= 3); 164 | } 165 | -------------------------------------------------------------------------------- /source/decryptor/aes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define AES_BLOCK_SIZE 0x10 6 | 7 | #define AES_CCM_DECRYPT_MODE (0 << 27) 8 | #define AES_CCM_ENCRYPT_MODE (1 << 27) 9 | #define AES_CTR_MODE (2 << 27) 10 | #define AES_CBC_DECRYPT_MODE (4 << 27) 11 | #define AES_CBC_ENCRYPT_MODE (5 << 27) 12 | #define AES_ECB_DECRYPT_MODE (6 << 27) 13 | #define AES_ECB_ENCRYPT_MODE (7 << 27) 14 | 15 | #define REG_AESCNT ((volatile u32*)0x10009000) 16 | #define REG_AESBLKCNT ((volatile u32*)0x10009004) 17 | #define REG_AESWRFIFO ((volatile u32*)0x10009008) 18 | #define REG_AESRDFIFO ((volatile u32*)0x1000900C) 19 | #define REG_AESKEYSEL ((volatile u8 *)0x10009010) 20 | #define REG_AESKEYCNT ((volatile u8 *)0x10009011) 21 | #define REG_AESCTR ((volatile u32*)0x10009020) 22 | #define REG_AESKEYFIFO ((volatile u32*)0x10009100) 23 | #define REG_AESKEYXFIFO ((volatile u32*)0x10009104) 24 | #define REG_AESKEYYFIFO ((volatile u32*)0x10009108) 25 | 26 | #define AES_CNT_START 0x80000000 27 | #define AES_CNT_INPUT_ORDER 0x02000000 28 | #define AES_CNT_OUTPUT_ORDER 0x01000000 29 | #define AES_CNT_INPUT_ENDIAN 0x00800000 30 | #define AES_CNT_OUTPUT_ENDIAN 0x00400000 31 | #define AES_CNT_FLUSH_READ 0x00000800 32 | #define AES_CNT_FLUSH_WRITE 0x00000400 33 | 34 | #define AES_CNT_CTRNAND_MODE (AES_CTR_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN) 35 | #define AES_CNT_TWLNAND_MODE AES_CTR_MODE 36 | #define AES_CNT_TITLEKEY_DECRYPT_MODE (AES_CBC_DECRYPT_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN) 37 | #define AES_CNT_TITLEKEY_ENCRYPT_MODE (AES_CBC_ENCRYPT_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN) 38 | 39 | 40 | void setup_aeskeyX(u8 keyslot, void* keyx); 41 | void setup_aeskeyY(u8 keyslot, void* keyy); 42 | void setup_aeskey(u8 keyslot, void* keyy); 43 | void use_aeskey(u32 keyno); 44 | void set_ctr(void* iv); 45 | void add_ctr(void* ctr, u32 carry); 46 | void aes_decrypt(void* inbuf, void* outbuf, size_t size, u32 mode); 47 | void aes_fifos(void* inbuf, void* outbuf, size_t blocks); 48 | void set_aeswrfifo(u32 value); 49 | u32 read_aesrdfifo(void); 50 | u32 aes_getwritecount(); 51 | u32 aes_getreadcount(); 52 | u32 aescnt_checkwrite(); 53 | u32 aescnt_checkread(); 54 | -------------------------------------------------------------------------------- /source/decryptor/decryptor.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include "draw.h" 3 | #include "decryptor/decryptor.h" 4 | #include "decryptor/aes.h" 5 | 6 | 7 | u32 CryptBuffer(CryptBufferInfo *info) 8 | { 9 | u8 ctr[16] __attribute__((aligned(32))); 10 | memcpy(ctr, info->ctr, 16); 11 | 12 | u8* buffer = info->buffer; 13 | u32 size = info->size; 14 | u32 mode = info->mode; 15 | 16 | if (info->setKeyY) { 17 | u8 keyY[16] __attribute__((aligned(32))); 18 | memcpy(keyY, info->keyY, 16); 19 | setup_aeskeyY(info->keyslot, keyY); 20 | info->setKeyY = 0; 21 | } 22 | use_aeskey(info->keyslot); 23 | 24 | for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) { 25 | set_ctr(ctr); 26 | if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE) 27 | memcpy(ctr, buffer, 0x10); 28 | aes_decrypt((void*) buffer, (void*) buffer, 1, mode); 29 | if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE) 30 | memcpy(ctr, buffer, 0x10); 31 | else if ((mode & (0x7 << 27)) == AES_CTR_MODE) 32 | add_ctr(ctr, 0x1); 33 | } 34 | 35 | memcpy(info->ctr, ctr, 16); 36 | 37 | return 0; 38 | } 39 | 40 | u32 CreatePad(PadInfo *info) 41 | { 42 | u8* buffer = BUFFER_ADDRESS; 43 | u32 result = 0; 44 | 45 | if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given 46 | return 1; 47 | 48 | CryptBufferInfo decryptInfo = {.keyslot = info->keyslot, .setKeyY = info->setKeyY, .mode = info->mode, .buffer = buffer}; 49 | memcpy(decryptInfo.ctr, info->ctr, 16); 50 | memcpy(decryptInfo.keyY, info->keyY, 16); 51 | u32 size_bytes = info->size_mb * 1024*1024; 52 | for (u32 i = 0; i < size_bytes; i += BUFFER_MAX_SIZE) { 53 | u32 curr_block_size = min(BUFFER_MAX_SIZE, size_bytes - i); 54 | decryptInfo.size = curr_block_size; 55 | memset(buffer, 0x00, curr_block_size); 56 | ShowProgress(i, size_bytes); 57 | CryptBuffer(&decryptInfo); 58 | if (!DebugFileWrite((void*)buffer, curr_block_size, i)) { 59 | result = 1; 60 | break; 61 | } 62 | } 63 | 64 | ShowProgress(0, 0); 65 | FileClose(); 66 | 67 | return result; 68 | } 69 | -------------------------------------------------------------------------------- /source/decryptor/decryptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define BUFFER_ADDRESS ((u8*) 0x21000000) 6 | #define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64) 7 | 8 | typedef struct { 9 | u32 keyslot; 10 | u32 setKeyY; 11 | u8 ctr[16]; 12 | u8 keyY[16]; 13 | u32 size; 14 | u32 mode; 15 | u8* buffer; 16 | } __attribute__((packed)) CryptBufferInfo; 17 | 18 | typedef struct { 19 | u32 keyslot; 20 | u32 setKeyY; 21 | u8 ctr[16]; 22 | u8 keyY[16]; 23 | u32 size_mb; 24 | u32 mode; 25 | char filename[180]; 26 | } __attribute__((packed, aligned(16))) PadInfo; 27 | 28 | 29 | u32 CryptBuffer(CryptBufferInfo *info); 30 | u32 CreatePad(PadInfo *info); 31 | -------------------------------------------------------------------------------- /source/decryptor/game.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "decryptor/decryptor.h" 5 | 6 | #define GC_NCCH_PROCESS (1<<0) 7 | #define GC_CIA_PROCESS (1<<1) 8 | #define GC_CIA_DEEP (1<<2) 9 | #define GC_NCCH_ENCRYPT (1<<3) 10 | #define GC_CIA_ENCRYPT (1<<4) 11 | #define GC_CXI_ONLY (1<<5) 12 | 13 | #define MAX_ENTRIES 1024 14 | 15 | typedef struct { 16 | u64 titleId; 17 | u8 external_seed[16]; 18 | u8 reserved[8]; 19 | } __attribute__((packed)) SeedInfoEntry; 20 | 21 | typedef struct { 22 | u32 n_entries; 23 | u8 padding[12]; 24 | SeedInfoEntry entries[MAX_ENTRIES]; 25 | } __attribute__((packed)) SeedInfo; 26 | 27 | typedef struct { 28 | u8 ctr[16]; 29 | u32 size_mb; 30 | char filename[180]; 31 | } __attribute__((packed)) SdInfoEntry; 32 | 33 | typedef struct { 34 | u32 n_entries; 35 | SdInfoEntry entries[MAX_ENTRIES]; 36 | } __attribute__((packed, aligned(16))) SdInfo; 37 | 38 | typedef struct { 39 | u8 ctr[16]; 40 | u8 keyY[16]; 41 | u32 size_mb; 42 | u8 reserved[4]; 43 | u32 usesSeedCrypto; 44 | u32 uses7xCrypto; 45 | u64 titleId; 46 | char filename[112]; 47 | } __attribute__((packed)) NcchInfoEntry; 48 | 49 | typedef struct { 50 | u32 padding; 51 | u32 ncch_info_version; 52 | u32 n_entries; 53 | u8 reserved[4]; 54 | NcchInfoEntry entries[MAX_ENTRIES]; 55 | } __attribute__((packed, aligned(16))) NcchInfo; 56 | 57 | typedef struct { 58 | u8 signature[0x100]; 59 | u8 magic[0x4]; 60 | u32 size; 61 | u64 partitionId; 62 | u16 makercode; 63 | u16 version; 64 | u8 reserved0[0x4]; 65 | u64 programId; 66 | u8 reserved1[0x10]; 67 | u8 hash_logo[0x20]; 68 | char productCode[0x10]; 69 | u8 hash_exthdr[0x20]; 70 | u32 size_exthdr; 71 | u8 reserved2[0x4]; 72 | u8 flags[0x8]; 73 | u32 offset_plain; 74 | u32 size_plain; 75 | u32 offset_logo; 76 | u32 size_logo; 77 | u32 offset_exefs; 78 | u32 size_exefs; 79 | u32 size_exefs_hash; 80 | u8 reserved3[0x4]; 81 | u32 offset_romfs; 82 | u32 size_romfs; 83 | u32 size_romfs_hash; 84 | u8 reserved4[0x4]; 85 | u8 hash_exefs[0x20]; 86 | u8 hash_romfs[0x20]; 87 | } __attribute__((packed, aligned(16))) NcchHeader; 88 | 89 | 90 | u32 GetSdCtr(u8* ctr, const char* path); 91 | u32 SdInfoGen(SdInfo* info); 92 | u32 CryptSdToSd(const char* filename, u32 offset, u32 size, CryptBufferInfo* info); 93 | u32 GetHashFromFile(const char* filename, u32 offset, u32 size, u8* hash); 94 | u32 CheckHashFromFile(const char* filename, u32 offset, u32 size, u8* hash); 95 | u32 CryptNcch(const char* filename, u32 offset, u32 size, u64 seedId, u8* encrypt_flags); 96 | u32 CryptCia(const char* filename, u8* ncch_crypt, bool cia_encrypt, bool cxi_only); 97 | 98 | // --> FEATURE FUNCTIONS <-- 99 | u32 NcchPadgen(u32 param); 100 | u32 SdPadgen(u32 param); 101 | u32 UpdateSeedDb(u32 param); 102 | u32 CryptGameFiles(u32 param); 103 | u32 CryptSdFiles(u32 param); 104 | -------------------------------------------------------------------------------- /source/decryptor/nand.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include "draw.h" 3 | #include "platform.h" 4 | #include "decryptor/aes.h" 5 | #include "decryptor/decryptor.h" 6 | #include "decryptor/nand.h" 7 | #include "fatfs/sdmmc.h" 8 | 9 | // see: http://3dbrew.org/wiki/Flash_Filesystem 10 | static PartitionInfo partitions[] = { 11 | { "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE }, 12 | { "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE }, 13 | { "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE }, 14 | { "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE }, 15 | { "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE }, 16 | { "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS 17 | { "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS 18 | }; 19 | 20 | static u32 emunand_header = 0; 21 | static u32 emunand_offset = 0; 22 | 23 | 24 | u32 SetNand(bool use_emunand) 25 | { 26 | u8* buffer = BUFFER_ADDRESS; 27 | 28 | if (use_emunand) { 29 | u32 nand_size_sectors = getMMCDevice(0)->total_size; 30 | // check for Gateway type EmuNAND 31 | sdmmc_sdcard_readsectors(nand_size_sectors, 1, buffer); 32 | if (memcmp(buffer + 0x100, "NCSD", 4) == 0) { 33 | emunand_header = nand_size_sectors; 34 | emunand_offset = 0; 35 | Debug("Using EmuNAND @ %06X/%06X", emunand_header, emunand_offset); 36 | return 0; 37 | } 38 | // check for RedNAND type EmuNAND 39 | sdmmc_sdcard_readsectors(1, 1, buffer); 40 | if (memcmp(buffer + 0x100, "NCSD", 4) == 0) { 41 | emunand_header = 1; 42 | emunand_offset = 1; 43 | Debug("Using RedNAND @ %06X/%06X", emunand_header, emunand_offset); 44 | return 0; 45 | } 46 | // no EmuNAND found 47 | Debug("EmuNAND is not available"); 48 | return 1; 49 | } else { 50 | emunand_header = 0; 51 | emunand_offset = 0; 52 | return 0; 53 | } 54 | } 55 | 56 | bool IsEmuNand() 57 | { 58 | return emunand_header; 59 | } 60 | 61 | static inline int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out) 62 | { 63 | if (emunand_header) { 64 | if (sector_no == 0) { 65 | int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out); 66 | if (errorcode) return errorcode; 67 | sector_no = 1; 68 | numsectors--; 69 | out += 0x200; 70 | } 71 | return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out); 72 | } else return sdmmc_nand_readsectors(sector_no, numsectors, out); 73 | } 74 | 75 | static inline int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in) 76 | { 77 | if (emunand_header) { 78 | if (sector_no == 0) { 79 | int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in); 80 | if (errorcode) return errorcode; 81 | sector_no = 1; 82 | numsectors--; 83 | in += 0x200; 84 | } 85 | return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in); 86 | } else return sdmmc_nand_writesectors(sector_no, numsectors, in); 87 | } 88 | 89 | PartitionInfo* GetPartitionInfo(u32 partition_id) 90 | { 91 | u32 partition_num = 0; 92 | 93 | if (partition_id == P_CTRNAND) { 94 | partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6; 95 | } else { 96 | for(; !(partition_id & (1<= 32) ? NULL : &(partitions[partition_num]); 100 | } 101 | 102 | u32 CtrNandPadgen(u32 param) 103 | { 104 | u32 keyslot; 105 | u32 nand_size; 106 | 107 | if(GetUnitPlatform() == PLATFORM_3DS) { 108 | keyslot = 0x4; 109 | nand_size = 758; 110 | } else { 111 | keyslot = 0x5; 112 | nand_size = 1055; 113 | } 114 | 115 | Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); 116 | Debug("Filename: nand.fat16.xorpad"); 117 | 118 | PadInfo padInfo = {.keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size, .filename = "nand.fat16.xorpad", .mode = AES_CNT_CTRNAND_MODE}; 119 | if(GetNandCtr(padInfo.ctr, 0xB930000) != 0) 120 | return 1; 121 | 122 | return CreatePad(&padInfo); 123 | } 124 | 125 | u32 TwlNandPadgen(u32 param) 126 | { 127 | u32 size_mb = (partitions[0].size + (1024 * 1024) - 1) / (1024 * 1024); 128 | Debug("Creating TWLNAND FAT16 xorpad. Size (MB): %u", size_mb); 129 | Debug("Filename: twlnand.fat16.xorpad"); 130 | 131 | PadInfo padInfo = { 132 | .keyslot = partitions[0].keyslot, 133 | .setKeyY = 0, 134 | .size_mb = size_mb, 135 | .filename = "twlnand.fat16.xorpad", 136 | .mode = AES_CNT_TWLNAND_MODE}; 137 | if(GetNandCtr(padInfo.ctr, partitions[0].offset) != 0) 138 | return 1; 139 | 140 | return CreatePad(&padInfo); 141 | } 142 | 143 | u32 GetNandCtr(u8* ctr, u32 offset) 144 | { 145 | static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"}; 146 | static const u8* version_ctrs[] = { 147 | (u8*)0x080D7CAC, 148 | (u8*)0x080D858C, 149 | (u8*)0x080D748C, 150 | (u8*)0x080D740C, 151 | (u8*)0x080D74CC, 152 | (u8*)0x080D794C 153 | }; 154 | static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32); 155 | static u8* ctr_start = NULL; 156 | 157 | if (ctr_start == NULL) { 158 | for (u32 i = 0; i < version_ctrs_len; i++) { 159 | if (*(u32*)version_ctrs[i] == 0x5C980) { 160 | Debug("System version %s", versions[i]); 161 | ctr_start = (u8*) version_ctrs[i] + 0x30; 162 | } 163 | } 164 | 165 | // If value not in previous list start memory scanning (test range) 166 | if (ctr_start == NULL) { 167 | for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) { 168 | if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) { 169 | ctr_start = c + 0x30; 170 | Debug("CTR start 0x%08X", ctr_start); 171 | break; 172 | } 173 | } 174 | } 175 | 176 | if (ctr_start == NULL) { 177 | Debug("CTR start not found!"); 178 | return 1; 179 | } 180 | } 181 | 182 | // the ctr is stored backwards in memory 183 | if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region 184 | for (u32 i = 0; i < 16; i++) 185 | ctr[i] = *(ctr_start + (0xF - i)); 186 | } else { // TWL region 187 | for (u32 i = 0; i < 16; i++) 188 | ctr[i] = *(ctr_start + 0x88 + (0xF - i)); 189 | } 190 | 191 | // increment counter 192 | add_ctr(ctr, offset / 0x10); 193 | 194 | return 0; 195 | } 196 | 197 | u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition) 198 | { 199 | CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode}; 200 | if(GetNandCtr(info.ctr, offset) != 0) 201 | return 1; 202 | 203 | u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE; 204 | u32 start_sector = offset / NAND_SECTOR_SIZE; 205 | ReadNandSectors(start_sector, n_sectors, buffer); 206 | CryptBuffer(&info); 207 | 208 | return 0; 209 | } 210 | 211 | u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition) 212 | { 213 | u8* buffer = BUFFER_ADDRESS; 214 | u32 result = 0; 215 | 216 | if (!DebugFileCreate(filename, true)) 217 | return 1; 218 | 219 | for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) { 220 | u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i)); 221 | ShowProgress(i, size); 222 | DecryptNandToMem(buffer, offset + i, read_bytes, partition); 223 | if(!DebugFileWrite(buffer, read_bytes, i)) { 224 | result = 1; 225 | break; 226 | } 227 | } 228 | 229 | ShowProgress(0, 0); 230 | FileClose(); 231 | 232 | return result; 233 | } 234 | 235 | u32 DumpNand(u32 param) 236 | { 237 | u8* buffer = BUFFER_ADDRESS; 238 | u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE; 239 | u32 result = 0; 240 | 241 | Debug("Dumping System NAND. Size (MB): %u", nand_size / (1024 * 1024)); 242 | 243 | if (!DebugFileCreate((IsEmuNand()) ? "EmuNAND.bin" : "NAND.bin", true)) 244 | return 1; 245 | 246 | u32 n_sectors = nand_size / NAND_SECTOR_SIZE; 247 | for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { 248 | u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i)); 249 | ShowProgress(i, n_sectors); 250 | ReadNandSectors(i, read_sectors, buffer); 251 | if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) { 252 | result = 1; 253 | break; 254 | } 255 | } 256 | 257 | ShowProgress(0, 0); 258 | FileClose(); 259 | 260 | return result; 261 | } 262 | 263 | u32 DecryptNandPartition(PartitionInfo* p_info) 264 | { 265 | char filename[32]; 266 | u8 magic[NAND_SECTOR_SIZE]; 267 | 268 | Debug("Dumping & Decrypting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024)); 269 | if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0) 270 | return 1; 271 | if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) { 272 | Debug("Decryption error, please contact us"); 273 | return 1; 274 | } 275 | snprintf(filename, 32, "%s.bin", p_info->name); 276 | 277 | return DecryptNandToFile(filename, p_info->offset, p_info->size, p_info); 278 | } 279 | 280 | u32 DecryptNandPartitions(u32 param) 281 | { 282 | u32 result = 0; 283 | 284 | for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) 285 | result |= (param & partition_id) ? DecryptNandPartition(GetPartitionInfo(partition_id)) : 0; 286 | 287 | return result; 288 | } 289 | 290 | u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition) 291 | { 292 | CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode}; 293 | if(GetNandCtr(info.ctr, offset) != 0) 294 | return 1; 295 | 296 | u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE; 297 | u32 start_sector = offset / NAND_SECTOR_SIZE; 298 | CryptBuffer(&info); 299 | WriteNandSectors(start_sector, n_sectors, buffer); 300 | 301 | return 0; 302 | } 303 | 304 | u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition) 305 | { 306 | u8* buffer = BUFFER_ADDRESS; 307 | u32 result = 0; 308 | 309 | if (!DebugFileOpen(filename)) 310 | return 1; 311 | 312 | if (FileGetSize() != size) { 313 | Debug("%s has wrong size", filename); 314 | FileClose(); 315 | return 1; 316 | } 317 | 318 | for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) { 319 | u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i)); 320 | ShowProgress(i, size); 321 | if(!DebugFileRead(buffer, read_bytes, i)) { 322 | result = 1; 323 | break; 324 | } 325 | EncryptMemToNand(buffer, offset + i, read_bytes, partition); 326 | } 327 | 328 | ShowProgress(0, 0); 329 | FileClose(); 330 | 331 | return result; 332 | } 333 | 334 | u32 RestoreNand(u32 param) 335 | { 336 | u8* buffer = BUFFER_ADDRESS; 337 | u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE; 338 | u32 result = 0; 339 | u8 magic[4]; 340 | 341 | if (IsEmuNand()) { 342 | if (!DebugFileOpen("EmuNAND.bin") && !DebugFileOpen("NAND.bin")) 343 | return 1; 344 | } else if (!DebugFileOpen("NAND.bin")) 345 | return 1; 346 | if (nand_size != FileGetSize()) { 347 | FileClose(); 348 | Debug("NAND backup has the wrong size!"); 349 | return 1; 350 | }; 351 | if(!DebugFileRead(magic, 4, 0x100)) 352 | return 1; 353 | if (memcmp(magic, "NCSD", 4) != 0) { 354 | Debug("Not a proper NAND backup!"); 355 | return 1; 356 | } 357 | 358 | Debug("Restoring System NAND. Size (MB): %u", nand_size / (1024 * 1024)); 359 | 360 | u32 n_sectors = nand_size / NAND_SECTOR_SIZE; 361 | for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { 362 | u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i)); 363 | ShowProgress(i, n_sectors); 364 | if(!DebugFileRead(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) { 365 | result = 1; 366 | break; 367 | } 368 | WriteNandSectors(i, read_sectors, buffer); 369 | } 370 | 371 | ShowProgress(0, 0); 372 | FileClose(); 373 | 374 | return result; 375 | } 376 | 377 | u32 InjectNandPartition(PartitionInfo* p_info) 378 | { 379 | char filename[32]; 380 | u8 magic[NAND_SECTOR_SIZE]; 381 | 382 | // File check 383 | snprintf(filename, 32, "%s.bin", p_info->name); 384 | if (FileOpen(filename)) { 385 | FileClose(); 386 | } else { 387 | return 1; 388 | } 389 | 390 | Debug("Encrypting & Injecting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024)); 391 | 392 | // Encryption check 393 | if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0) 394 | return 1; 395 | if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) { 396 | Debug("Decryption error, please contact us"); 397 | return 1; 398 | } 399 | 400 | // File check 401 | if (FileOpen(filename)) { 402 | if(!DebugFileRead(magic, 8, 0)) { 403 | FileClose(); 404 | return 1; 405 | } 406 | if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) { 407 | Debug("Bad file content, won't inject"); 408 | FileClose(); 409 | return 1; 410 | } 411 | FileClose(); 412 | } 413 | 414 | return EncryptFileToNand(filename, p_info->offset, p_info->size, p_info); 415 | } 416 | 417 | u32 InjectNandPartitions(u32 param) 418 | { 419 | u32 result = 1; 420 | 421 | for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) 422 | result &= (param & partition_id) ? InjectNandPartition(GetPartitionInfo(partition_id)) : 1; 423 | 424 | return result; 425 | } 426 | -------------------------------------------------------------------------------- /source/decryptor/nand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define NAND_SECTOR_SIZE 0x200 6 | #define SECTORS_PER_READ (BUFFER_MAX_SIZE / NAND_SECTOR_SIZE) 7 | 8 | #define P_TWLN (1<<0) 9 | #define P_TWLP (1<<1) 10 | #define P_AGBSAVE (1<<2) 11 | #define P_FIRM0 (1<<3) 12 | #define P_FIRM1 (1<<4) 13 | #define P_CTRNAND (1<<5) 14 | #define P_ALL (P_TWLN | P_TWLP | P_AGBSAVE | P_FIRM0 | P_FIRM1 | P_CTRNAND) 15 | 16 | typedef struct { 17 | char name[16]; 18 | u8 magic[8]; 19 | u32 offset; 20 | u32 size; 21 | u32 keyslot; 22 | u32 mode; 23 | } __attribute__((packed)) PartitionInfo; 24 | 25 | bool IsEmuNand(); 26 | PartitionInfo* GetPartitionInfo(u32 partition_id); 27 | u32 GetNandCtr(u8* ctr, u32 offset); 28 | 29 | u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition); 30 | u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition); 31 | u32 DecryptNandPartition(PartitionInfo* p); 32 | 33 | u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition); 34 | u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition); 35 | u32 InjectNandPartition(PartitionInfo* p); 36 | 37 | // --> FEATURE FUNCTIONS <-- 38 | u32 SetNand(bool use_emunand); 39 | 40 | u32 CtrNandPadgen(u32 param); 41 | u32 TwlNandPadgen(u32 param); 42 | 43 | u32 DumpNand(u32 param); 44 | u32 RestoreNand(u32 param); 45 | u32 DecryptNandPartitions(u32 param); 46 | u32 InjectNandPartitions(u32 param); 47 | -------------------------------------------------------------------------------- /source/decryptor/nandfat.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include "draw.h" 3 | #include "decryptor/decryptor.h" 4 | #include "decryptor/nand.h" 5 | #include "decryptor/nandfat.h" 6 | 7 | NandFileInfo fileList[] = { 8 | { "ticket.db", "ticket_emu.db", "DBS TICKET DB ", P_CTRNAND }, 9 | { "title.db", "title_emu.db", "DBS TITLE DB ", P_CTRNAND }, 10 | { "import.db", "import_emu.db", "DBS IMPORT DB ", P_CTRNAND }, 11 | { "certs.db", "certs_emu.db", "DBS CERTS DB ", P_CTRNAND }, 12 | { "SecureInfo_A", "SecureInfo_A", "RW SYS SECURE~? ", P_CTRNAND }, 13 | { "LocalFriendCodeSeed_B", "LocalFriendCodeSeed_B", "RW SYS LOCALF~? ", P_CTRNAND }, 14 | { "rand_seed", "rand_seed", "RW SYS RAND_S~? ", P_CTRNAND }, 15 | { "movable.sed", "movable.sed", "PRIVATE MOVABLE SED", P_CTRNAND }, 16 | { "seedsave.bin", "seedsave.bin", "DATA ???????????SYSDATA 0001000F 00000000 ", P_CTRNAND } 17 | }; 18 | 19 | 20 | NandFileInfo* GetNandFileInfo(u32 file_id) 21 | { 22 | u32 file_num = 0; 23 | for(; !(file_id & (1<= 32) ? NULL : &(fileList[file_num]); 25 | } 26 | 27 | u32 SeekFileInNand(u32* offset, u32* size, const char* path, PartitionInfo* partition) 28 | { 29 | // poor mans NAND FAT file seeker: 30 | // - path must be in FAT 8+3 format, without dots or slashes 31 | // example: DIR1_______DIR2_______FILENAMEEXT 32 | // - can't handle long filenames 33 | // - dirs must not exceed 1024 entries 34 | // - fragmentation not supported 35 | 36 | u8* buffer = BUFFER_ADDRESS; 37 | u32 p_size = partition->size; 38 | u32 p_offset = partition->offset; 39 | u32 fat_pos = 0; 40 | bool found = false; 41 | 42 | if (strnlen(path, 256) % (8+3) != 0) 43 | return 1; 44 | 45 | DecryptNandToMem(buffer, p_offset, NAND_SECTOR_SIZE, partition); 46 | 47 | // good FAT header description found here: http://www.compuphase.com/mbr_fat.htm 48 | u32 fat_start = NAND_SECTOR_SIZE * getle16(buffer + 0x0E); 49 | u32 fat_count = buffer[0x10]; 50 | u32 fat_size = NAND_SECTOR_SIZE * getle16(buffer + 0x16) * fat_count; 51 | u32 root_size = getle16(buffer + 0x11) * 0x20; 52 | u32 cluster_start = fat_start + fat_size + root_size; 53 | u32 cluster_size = buffer[0x0D] * NAND_SECTOR_SIZE; 54 | 55 | for (*offset = p_offset + fat_start + fat_size; strnlen(path, 256) >= 8+3; path += 8+3) { 56 | if (*offset - p_offset > p_size) 57 | return 1; 58 | found = false; 59 | DecryptNandToMem(buffer, *offset, cluster_size, partition); 60 | for (u32 i = 0x00; i < cluster_size; i += 0x20) { 61 | const static char zeroes[8+3] = { 0x00 }; 62 | // skip invisible, deleted and lfn entries 63 | if ((buffer[i] == '.') || (buffer[i] == 0xE5) || (buffer[i+0x0B] == 0x0F)) 64 | continue; 65 | else if (memcmp(buffer + i, zeroes, 8+3) == 0) 66 | return 1; 67 | u32 p; // search for path in fat folder structure, accept '?' wildcards 68 | for (p = 0; (p < 8+3) && (path[p] == '?' || buffer[i+p] == path[p]); p++); 69 | if (p != 8+3) continue; 70 | // entry found, store offset and move on 71 | fat_pos = getle16(buffer + i + 0x1A); 72 | *offset = p_offset + cluster_start + (fat_pos - 2) * cluster_size; 73 | *size = getle32(buffer + i + 0x1C); 74 | found = true; 75 | break; 76 | } 77 | if (!found) break; 78 | } 79 | 80 | // check for fragmentation 81 | if (found && (*size > cluster_size)) { 82 | if (fat_size / fat_count > 0x100000) // prevent buffer overflow 83 | return 1; // fishy FAT table size - should never happen 84 | DecryptNandToMem(buffer, p_offset + fat_start, fat_size / fat_count, partition); 85 | for (u32 i = 0; i < (*size - 1) / cluster_size; i++) { 86 | if (*(((u16*) buffer) + fat_pos + i) != fat_pos + i + 1) 87 | return 1; 88 | } // no need to check the files last FAT table entry 89 | } 90 | 91 | return (found) ? 0 : 1; 92 | } 93 | 94 | u32 DebugSeekFileInNand(u32* offset, u32* size, const char* filename, const char* path, PartitionInfo* partition) 95 | { 96 | Debug("Searching for %s...", filename); 97 | if (SeekFileInNand(offset, size, path, partition) != 0) { 98 | Debug("Failed!"); 99 | return 1; 100 | } 101 | if (*size < 1024) 102 | Debug("Found at %08X, size %ub", *offset, *size); 103 | else if (*size < 1024 * 1024) 104 | Debug("Found at %08X, size %ukB", *offset, *size / 1024); 105 | else 106 | Debug("Found at %08X, size %uMB", *offset, *size / (1024*1024)); 107 | 108 | return 0; 109 | } 110 | 111 | u32 DumpFile(u32 param) 112 | { 113 | NandFileInfo* f_info = GetNandFileInfo(param); 114 | PartitionInfo* p_info = GetPartitionInfo(f_info->partition_id); 115 | u32 offset; 116 | u32 size; 117 | 118 | if (DebugSeekFileInNand(&offset, &size, (IsEmuNand()) ? f_info->name_emu : f_info->name_sys, f_info->path, p_info) != 0) 119 | return 1; 120 | if (DecryptNandToFile((IsEmuNand()) ? f_info->name_emu : f_info->name_sys, offset, size, p_info) != 0) 121 | return 1; 122 | 123 | return 0; 124 | } 125 | 126 | u32 InjectFile(u32 param) 127 | { 128 | NandFileInfo* f_info = GetNandFileInfo(param); 129 | PartitionInfo* p_info = GetPartitionInfo(f_info->partition_id); 130 | u32 offset; 131 | u32 size; 132 | 133 | if (DebugSeekFileInNand(&offset, &size, f_info->name_sys, f_info->path, p_info) != 0) 134 | return 1; 135 | if (EncryptFileToNand(f_info->name_sys, offset, size, p_info) != 0) 136 | return 1; 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /source/decryptor/nandfat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "decryptor/nand.h" 4 | #include "common.h" 5 | 6 | #define MAX_ENTRIES 1024 7 | 8 | #define F_TICKET (1<<0) 9 | #define F_TITLE (1<<1) 10 | #define F_IMPORT (1<<2) 11 | #define F_CERTS (1<<3) 12 | #define F_SECUREINFO (1<<4) 13 | #define F_LOCALFRIEND (1<<5) 14 | #define F_RANDSEED (1<<6) 15 | #define F_MOVABLE (1<<7) 16 | #define F_SEEDSAVE (1<<8) 17 | 18 | typedef struct { 19 | char name_sys[32]; 20 | char name_emu[32]; 21 | char path[64]; 22 | u32 partition_id; 23 | } NandFileInfo; 24 | 25 | u32 SeekFileInNand(u32* offset, u32* size, const char* path, PartitionInfo* partition); 26 | u32 DebugSeekFileInNand(u32* offset, u32* size, const char* filename, const char* path, PartitionInfo* partition); 27 | // --> FEATURE FUNCTIONS <-- 28 | u32 DumpFile(u32 param); 29 | u32 InjectFile(u32 param); 30 | -------------------------------------------------------------------------------- /source/decryptor/sha.c: -------------------------------------------------------------------------------- 1 | #include "sha.h" 2 | 3 | void sha_init(u32 mode) 4 | { 5 | while(*REG_SHACNT & 1); 6 | *REG_SHACNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND; 7 | } 8 | 9 | void sha_update(const void* src, u32 size) 10 | { 11 | const u32* src32 = (const u32*)src; 12 | 13 | while(size >= 0x40) { 14 | while(*REG_SHACNT & 1); 15 | for(u32 i = 0; i < 4; i++) { 16 | *REG_SHAINFIFO = *src32++; 17 | *REG_SHAINFIFO = *src32++; 18 | *REG_SHAINFIFO = *src32++; 19 | *REG_SHAINFIFO = *src32++; 20 | } 21 | size -= 0x40; 22 | } 23 | while(*REG_SHACNT & 1); 24 | memcpy((void*)REG_SHAINFIFO, src32, size); 25 | } 26 | 27 | void sha_get(void* res) { 28 | *REG_SHACNT = (*REG_SHACNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND; 29 | while(*REG_SHACNT & SHA_FINAL_ROUND); 30 | while(*REG_SHACNT & 1); 31 | memcpy(res, (void*)REG_SHAHASH, (256 / 8)); 32 | } 33 | -------------------------------------------------------------------------------- /source/decryptor/sha.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define REG_SHACNT ((volatile uint32_t*)0x1000A000) 6 | #define REG_SHABLKCNT ((volatile uint32_t*)0x1000A004) 7 | #define REG_SHAHASH ((volatile uint32_t*)0x1000A040) 8 | #define REG_SHAINFIFO ((volatile uint32_t*)0x1000A080) 9 | 10 | #define SHA_CNT_STATE 0x00000003 11 | #define SHA_CNT_OUTPUT_ENDIAN 0x00000008 12 | #define SHA_CNT_MODE 0x00000030 13 | #define SHA_CNT_ENABLE 0x00010000 14 | #define SHA_CNT_ACTIVE 0x00020000 15 | 16 | #define SHA_HASH_READY 0x00000000 17 | #define SHA_NORMAL_ROUND 0x00000001 18 | #define SHA_FINAL_ROUND 0x00000002 19 | 20 | #define SHA256_MODE 0 21 | #define SHA224_MODE 0x00000010 22 | #define SHA1_MODE 0x00000020 23 | 24 | 25 | void sha_init(u32 mode); 26 | void sha_update(const void* src, u32 size); 27 | void sha_get(void* res); 28 | -------------------------------------------------------------------------------- /source/decryptor/titlekey.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include "draw.h" 3 | #include "platform.h" 4 | #include "decryptor/aes.h" 5 | #include "decryptor/decryptor.h" 6 | #include "decryptor/nand.h" 7 | #include "decryptor/nandfat.h" 8 | #include "decryptor/titlekey.h" 9 | 10 | // From https://github.com/profi200/Project_CTR/blob/master/makerom/pki/prod.h#L19 11 | static const u8 common_keyy[6][16] = { 12 | {0xD0, 0x7B, 0x33, 0x7F, 0x9C, 0xA4, 0x38, 0x59, 0x32, 0xA2, 0xE2, 0x57, 0x23, 0x23, 0x2E, 0xB9} , // 0 - eShop Titles 13 | {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C} , // 1 - System Titles 14 | {0xC4, 0x75, 0xCB, 0x3A, 0xB8, 0xC7, 0x88, 0xBB, 0x57, 0x5E, 0x12, 0xA1, 0x09, 0x07, 0xB8, 0xA4} , // 2 15 | {0xE4, 0x86, 0xEE, 0xE3, 0xD0, 0xC0, 0x9C, 0x90, 0x2F, 0x66, 0x86, 0xD4, 0xC0, 0x6F, 0x64, 0x9F} , // 3 16 | {0xED, 0x31, 0xBA, 0x9C, 0x04, 0xB0, 0x67, 0x50, 0x6C, 0x44, 0x97, 0xA3, 0x5B, 0x78, 0x04, 0xFC} , // 4 17 | {0x5E, 0x66, 0x99, 0x8A, 0xB4, 0xE8, 0x93, 0x16, 0x06, 0x85, 0x0F, 0xD7, 0xA1, 0x6D, 0xD7, 0x55} , // 5 18 | }; 19 | 20 | 21 | u32 DecryptTitlekey(TitleKeyEntry* entry) 22 | { 23 | CryptBufferInfo info = {.keyslot = 0x3D, .setKeyY = 1, .size = 16, .buffer = entry->encryptedTitleKey, .mode = AES_CNT_TITLEKEY_DECRYPT_MODE}; 24 | memset(info.ctr, 0, 16); 25 | memcpy(info.ctr, entry->titleId, 8); 26 | memcpy(info.keyY, (void *)common_keyy[entry->commonKeyIndex], 16); 27 | 28 | CryptBuffer(&info); 29 | 30 | return 0; 31 | } 32 | 33 | u32 DecryptTitlekeysFile(u32 param) 34 | { 35 | EncKeysInfo *info = (EncKeysInfo*)0x20316000; 36 | 37 | if (!DebugFileOpen("encTitleKeys.bin")) 38 | return 1; 39 | 40 | if (!DebugFileRead(info, 16, 0)) { 41 | FileClose(); 42 | return 1; 43 | } 44 | 45 | if (!info->n_entries || info->n_entries > MAX_ENTRIES) { 46 | Debug("Too many/few entries specified: %i", info->n_entries); 47 | FileClose(); 48 | return 1; 49 | } 50 | 51 | Debug("Number of entries: %i", info->n_entries); 52 | if (!DebugFileRead(info->entries, info->n_entries * sizeof(TitleKeyEntry), 16)) { 53 | FileClose(); 54 | return 1; 55 | } 56 | 57 | FileClose(); 58 | 59 | Debug("Decrypting Title Keys..."); 60 | for (u32 i = 0; i < info->n_entries; i++) 61 | DecryptTitlekey(&(info->entries[i])); 62 | 63 | if (!DebugFileCreate("decTitleKeys.bin", true)) 64 | return 1; 65 | if (!DebugFileWrite(info, info->n_entries * sizeof(TitleKeyEntry) + 16, 0)) { 66 | FileClose(); 67 | return 1; 68 | } 69 | FileClose(); 70 | 71 | return 0; 72 | } 73 | 74 | u32 DecryptTitlekeysNand(u32 param) 75 | { 76 | PartitionInfo* ctrnand_info = GetPartitionInfo(P_CTRNAND);; 77 | u8* buffer = BUFFER_ADDRESS; 78 | EncKeysInfo *info = (EncKeysInfo*) 0x20316000; 79 | 80 | u32 nKeys = 0; 81 | u32 offset = 0; 82 | u32 size = 0; 83 | 84 | Debug("Searching for ticket.db..."); 85 | if (SeekFileInNand(&offset, &size, "DBS TICKET DB ", ctrnand_info) != 0) { 86 | Debug("Failed!"); 87 | return 1; 88 | } 89 | Debug("Found at %08X, size %uMB", offset, size / (1024 * 1024)); 90 | 91 | Debug("Decrypting Title Keys..."); 92 | memset(info, 0, 0x10); 93 | for (u32 t_offset = 0; t_offset < size; t_offset += NAND_SECTOR_SIZE * (SECTORS_PER_READ-1)) { 94 | u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - t_offset)); 95 | ShowProgress(t_offset, size); 96 | DecryptNandToMem(buffer, offset + t_offset, read_bytes, ctrnand_info); 97 | for (u32 i = 0; i < read_bytes - NAND_SECTOR_SIZE; i++) { 98 | if(memcmp(buffer + i, (u8*) "Root-CA00000003-XS0000000c", 26) == 0) { 99 | u32 exid; 100 | u8* titleId = buffer + i + 0x9C; 101 | u32 commonKeyIndex = *(buffer + i + 0xB1); 102 | u8* titlekey = buffer + i + 0x7F; 103 | for (exid = 0; exid < nKeys; exid++) 104 | if (memcmp(titleId, info->entries[exid].titleId, 8) == 0) 105 | break; 106 | if (exid < nKeys) 107 | continue; // continue if already dumped 108 | memset(&(info->entries[nKeys]), 0, sizeof(TitleKeyEntry)); 109 | memcpy(info->entries[nKeys].titleId, titleId, 8); 110 | memcpy(info->entries[nKeys].encryptedTitleKey, titlekey, 16); 111 | info->entries[nKeys].commonKeyIndex = commonKeyIndex; 112 | DecryptTitlekey(&(info->entries[nKeys])); 113 | nKeys++; 114 | } 115 | } 116 | if (nKeys == MAX_ENTRIES) { 117 | Debug("Maximum number of titlekeys found"); 118 | break; 119 | } 120 | } 121 | info->n_entries = nKeys; 122 | ShowProgress(0, 0); 123 | 124 | Debug("Decrypted %u unique Title Keys", nKeys); 125 | 126 | if(nKeys > 0) { 127 | if (!DebugFileCreate((IsEmuNand()) ? "decTitleKeys_emu.bin" : "decTitleKeys.bin", true)) 128 | return 1; 129 | if (!DebugFileWrite(info, 0x10 + nKeys * 0x20, 0)) { 130 | FileClose(); 131 | return 1; 132 | } 133 | FileClose(); 134 | } else { 135 | return 1; 136 | } 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /source/decryptor/titlekey.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define MAX_ENTRIES 1024 6 | 7 | typedef struct { 8 | u32 commonKeyIndex; 9 | u8 reserved[4]; 10 | u8 titleId[8]; 11 | u8 encryptedTitleKey[16]; 12 | } __attribute__((packed)) TitleKeyEntry; 13 | 14 | typedef struct { 15 | u32 n_entries; 16 | u8 reserved[12]; 17 | TitleKeyEntry entries[MAX_ENTRIES]; 18 | } __attribute__((packed, aligned(16))) EncKeysInfo; 19 | 20 | 21 | u32 DecryptTitlekey(TitleKeyEntry* entry); 22 | 23 | // --> FEATURE FUNCTIONS <-- 24 | u32 DecryptTitlekeysFile(u32 param); 25 | u32 DecryptTitlekeysNand(u32 param); 26 | -------------------------------------------------------------------------------- /source/draw.c: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Normmatt 2 | // Licensed under GPLv2 or any later version 3 | // Refer to the license.txt file included. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "font.h" 11 | #include "draw.h" 12 | #include "fs.h" 13 | #define STD_COLOR_BG COLOR_BLACK 14 | #define STD_COLOR_FONT COLOR_WHITE 15 | 16 | #define DBG_COLOR_BG COLOR_BLACK 17 | #define DBG_COLOR_FONT COLOR_WHITE 18 | 19 | #define DBG_START_Y 10 20 | #define DBG_END_Y (SCREEN_HEIGHT - 10) 21 | #define DBG_START_X 10 22 | #define DBG_END_X (SCREEN_WIDTH_TOP - 10) 23 | #define DBG_STEP_Y 10 24 | 25 | #define DBG_N_CHARS_Y ((DBG_END_Y - DBG_START_Y) / DBG_STEP_Y) 26 | #define DBG_N_CHARS_X (((DBG_END_X - DBG_START_X) / 8) + 1) 27 | 28 | static char debugstr[DBG_N_CHARS_X * DBG_N_CHARS_Y] = { 0 }; 29 | 30 | void ClearScreen(u8* screen, int width, int color) 31 | { 32 | if (color == COLOR_TRANSPARENT) color = COLOR_BLACK; 33 | for (int i = 0; i < (width * SCREEN_HEIGHT); i++) { 34 | *(screen++) = color >> 16; // B 35 | *(screen++) = color >> 8; // G 36 | *(screen++) = color & 0xFF; // R 37 | } 38 | } 39 | 40 | void ClearScreenFull(bool clear_top, bool clear_bottom) 41 | { 42 | if (clear_top) { 43 | ClearScreen(TOP_SCREEN0, SCREEN_WIDTH_TOP, STD_COLOR_BG); 44 | ClearScreen(TOP_SCREEN1, SCREEN_WIDTH_TOP, STD_COLOR_BG); 45 | } 46 | if (clear_bottom) { 47 | ClearScreen(BOT_SCREEN0, SCREEN_WIDTH_BOT, STD_COLOR_BG); 48 | ClearScreen(BOT_SCREEN1, SCREEN_WIDTH_BOT, STD_COLOR_BG); 49 | } 50 | } 51 | 52 | void DrawCharacter(u8* screen, int character, int x, int y, int color, int bgcolor) 53 | { 54 | for (int yy = 0; yy < 8; yy++) { 55 | int xDisplacement = (x * BYTES_PER_PIXEL * SCREEN_HEIGHT); 56 | int yDisplacement = ((SCREEN_HEIGHT - (y + yy) - 1) * BYTES_PER_PIXEL); 57 | u8* screenPos = screen + xDisplacement + yDisplacement; 58 | 59 | u8 charPos = font[character * 8 + yy]; 60 | for (int xx = 7; xx >= 0; xx--) { 61 | if ((charPos >> xx) & 1) { 62 | *(screenPos + 0) = color >> 16; // B 63 | *(screenPos + 1) = color >> 8; // G 64 | *(screenPos + 2) = color & 0xFF; // R 65 | } else if (bgcolor != COLOR_TRANSPARENT) { 66 | *(screenPos + 0) = bgcolor >> 16; // B 67 | *(screenPos + 1) = bgcolor >> 8; // G 68 | *(screenPos + 2) = bgcolor & 0xFF; // R 69 | } 70 | screenPos += BYTES_PER_PIXEL * SCREEN_HEIGHT; 71 | } 72 | } 73 | } 74 | 75 | void DrawString(u8* screen, const char *str, int x, int y, int color, int bgcolor) 76 | { 77 | for (int i = 0; i < strlen(str); i++) 78 | DrawCharacter(screen, str[i], x + i * 8, y, color, bgcolor); 79 | } 80 | 81 | void DrawStringF(int x, int y, bool use_top, const char *format, ...) 82 | { 83 | char str[256] = {}; 84 | va_list va; 85 | 86 | va_start(va, format); 87 | vsnprintf(str, 256, format, va); 88 | va_end(va); 89 | 90 | if (use_top) { 91 | DrawString(TOP_SCREEN0, str, x, y, STD_COLOR_FONT, STD_COLOR_BG); 92 | DrawString(TOP_SCREEN1, str, x, y, STD_COLOR_FONT, STD_COLOR_BG); 93 | } else { 94 | DrawString(BOT_SCREEN0, str, x, y, STD_COLOR_FONT, STD_COLOR_BG); 95 | DrawString(BOT_SCREEN1, str, x, y, STD_COLOR_FONT, STD_COLOR_BG); 96 | } 97 | } 98 | 99 | void Screenshot(const char* path) 100 | { 101 | u8* buffer = (u8*) 0x21000000; // careful, this area is used by other functions in Decrypt9 102 | u8* buffer_t = buffer + (400 * 240 * 3); 103 | u8 bmp_header[54] = { 104 | 0x42, 0x4D, 0x36, 0xCA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 105 | 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 106 | 0x00, 0x00, 0x00, 0xCA, 0x08, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 107 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 108 | }; 109 | static u32 n = 0; 110 | 111 | if (path == NULL) { 112 | for (; n < 1000; n++) { 113 | char filename[16]; 114 | snprintf(filename, 16, "snap%03i.bmp", (int) n); 115 | if (!FileOpen(filename)) { 116 | FileCreate(filename, true); 117 | break; 118 | } 119 | FileClose(); 120 | } 121 | if (n >= 1000) 122 | return; 123 | } else { 124 | FileCreate(path, true); 125 | } 126 | 127 | memset(buffer, 0x1F, 400 * 240 * 3 * 2); 128 | for (u32 x = 0; x < 400; x++) 129 | for (u32 y = 0; y < 240; y++) 130 | memcpy(buffer_t + (y*400 + x) * 3, TOP_SCREEN0 + (x*240 + y) * 3, 3); 131 | for (u32 x = 0; x < 320; x++) 132 | for (u32 y = 0; y < 240; y++) 133 | memcpy(buffer + (y*400 + x + 40) * 3, BOT_SCREEN0 + (x*240 + y) * 3, 3); 134 | FileWrite(bmp_header, 54, 0); 135 | FileWrite(buffer, 400 * 240 * 3 * 2, 54); 136 | FileClose(); 137 | } 138 | 139 | void DebugClear() 140 | { 141 | memset(debugstr, 0x00, DBG_N_CHARS_X * DBG_N_CHARS_Y); 142 | ClearScreen(TOP_SCREEN0, SCREEN_WIDTH_TOP, DBG_COLOR_BG); 143 | ClearScreen(TOP_SCREEN1, SCREEN_WIDTH_TOP, DBG_COLOR_BG); 144 | } 145 | 146 | void Debug(const char *format, ...) 147 | { 148 | char tempstr[DBG_N_CHARS_X] = { 0 }; 149 | va_list va; 150 | 151 | va_start(va, format); 152 | vsnprintf(tempstr, DBG_N_CHARS_X, format, va); 153 | va_end(va); 154 | 155 | memmove(debugstr + DBG_N_CHARS_X, debugstr, DBG_N_CHARS_X * (DBG_N_CHARS_Y - 1)); 156 | snprintf(debugstr, DBG_N_CHARS_X, "%-*.*s", DBG_N_CHARS_X - 1, DBG_N_CHARS_X - 1, tempstr); 157 | 158 | int pos_y = DBG_START_Y; 159 | for (char* str = debugstr + (DBG_N_CHARS_X * (DBG_N_CHARS_Y - 1)); str >= debugstr; str -= DBG_N_CHARS_X) { 160 | if (str[0] != '\0') { 161 | DrawString(TOP_SCREEN0, str, DBG_START_X, pos_y, DBG_COLOR_FONT, DBG_COLOR_BG); 162 | DrawString(TOP_SCREEN1, str, DBG_START_X, pos_y, DBG_COLOR_FONT, DBG_COLOR_BG); 163 | pos_y += DBG_STEP_Y; 164 | } 165 | } 166 | } 167 | 168 | void ShowProgress(u64 current, u64 total) 169 | { 170 | const u32 progX = SCREEN_WIDTH_TOP - 40; 171 | const u32 progY = SCREEN_HEIGHT - 20; 172 | 173 | if (total > 0) { 174 | char progStr[8]; 175 | snprintf(progStr, 8, "%3llu%%", (current * 100) / total); 176 | DrawString(TOP_SCREEN0, progStr, progX, progY, DBG_COLOR_FONT, DBG_COLOR_BG); 177 | DrawString(TOP_SCREEN1, progStr, progX, progY, DBG_COLOR_FONT, DBG_COLOR_BG); 178 | } else { 179 | DrawString(TOP_SCREEN0, " ", progX, progY, DBG_COLOR_FONT, DBG_COLOR_BG); 180 | DrawString(TOP_SCREEN1, " ", progX, progY, DBG_COLOR_FONT, DBG_COLOR_BG); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /source/draw.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Normmatt 2 | // Licensed under GPLv2 or any later version 3 | // Refer to the license.txt file included. 4 | 5 | #pragma once 6 | 7 | #include "common.h" 8 | 9 | #define BYTES_PER_PIXEL 3 10 | #define SCREEN_HEIGHT 240 11 | #define SCREEN_WIDTH_TOP 400 12 | #define SCREEN_WIDTH_BOT 320 13 | 14 | #define RGB(r,g,b) (r<<24|b<<16|g<<8|r) 15 | 16 | #define COLOR_BLACK RGB(0x00, 0x00, 0x00) 17 | #define COLOR_WHITE RGB(0xFF, 0xFF, 0xFF) 18 | #define COLOR_RED RGB(0xFF, 0x00, 0x00) 19 | #define COLOR_GREEN RGB(0x00, 0xFF, 0x00) 20 | #define COLOR_BLUE RGB(0xFF, 0x00, 0xFF) 21 | #define COLOR_GREY RGB(0x77, 0x77, 0x77) 22 | #define COLOR_PURPLE RGB(0x66, 0x00, 0xFF) 23 | #define COLOR_TRANSPARENT RGB(0xFF, 0x00, 0xEF) // otherwise known as 'super fuchsia' 24 | 25 | #ifdef EXEC_GATEWAY 26 | #define TOP_SCREEN0 (u8*)(*(u32*)((uint32_t)0x080FFFC0 + 4 * (*(u32*)0x080FFFD8 & 1))) 27 | #define BOT_SCREEN0 (u8*)(*(u32*)0x080FFFD4) 28 | #define TOP_SCREEN1 TOP_SCREEN0 29 | #define BOT_SCREEN1 BOT_SCREEN0 30 | #elif defined(EXEC_BOOTSTRAP) 31 | #define TOP_SCREEN0 (u8*)(0x20000000) 32 | #define TOP_SCREEN1 (u8*)(0x20046500) 33 | #define BOT_SCREEN0 (u8*)(0x2008CA00) 34 | #define BOT_SCREEN1 (u8*)(0x200C4E00) 35 | #else 36 | #error "Unknown execution method" 37 | #endif 38 | 39 | void ClearScreen(unsigned char *screen, int width, int color); 40 | void ClearScreenFull(bool clear_top, bool clear_bottom); 41 | 42 | void DrawCharacter(unsigned char *screen, int character, int x, int y, int color, int bgcolor); 43 | void DrawString(unsigned char *screen, const char *str, int x, int y, int color, int bgcolor); 44 | void DrawStringF(int x, int y, bool use_top, const char *format, ...); 45 | 46 | void Screenshot(const char* path); 47 | void DebugClear(); 48 | void Debug(const char *format, ...); 49 | 50 | void ShowProgress(u64 current, u64 total); 51 | -------------------------------------------------------------------------------- /source/fatfs/delay.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Normmatt 2 | // Licensed under GPLv2 or any later version 3 | // Refer to the license.txt file included. 4 | 5 | #pragma once 6 | 7 | #include "common.h" 8 | 9 | void ioDelay(u32 us); 10 | -------------------------------------------------------------------------------- /source/fatfs/delay.s: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Normmatt 2 | // Licensed under GPLv2 or any later version 3 | // Refer to the license.txt file included. 4 | 5 | .arm 6 | .global ioDelay 7 | .type ioDelay STT_FUNC 8 | 9 | @ioDelay ( u32 us ) 10 | ioDelay: 11 | ldr r1, =0x18000000 @ VRAM 12 | 1: 13 | @ Loop doing uncached reads from VRAM to make loop timing more reliable 14 | ldr r2, [r1] 15 | subs r0, #1 16 | bgt 1b 17 | bx lr 18 | -------------------------------------------------------------------------------- /source/fatfs/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2013 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control module to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include "diskio.h" /* FatFs lower layer API */ 11 | #include "sdmmc.h" 12 | 13 | /* Definitions of physical drive number for each media */ 14 | #define ATA 0 15 | #define MMC 1 16 | #define USB 2 17 | 18 | 19 | /*-----------------------------------------------------------------------*/ 20 | /* Inidialize a Drive */ 21 | /*-----------------------------------------------------------------------*/ 22 | 23 | DSTATUS disk_initialize ( 24 | BYTE pdrv /* Physical drive nmuber (0..) */ 25 | ) 26 | { 27 | sdmmc_sdcard_init(); 28 | return RES_OK; 29 | } 30 | 31 | 32 | 33 | /*-----------------------------------------------------------------------*/ 34 | /* Get Disk Status */ 35 | /*-----------------------------------------------------------------------*/ 36 | 37 | DSTATUS disk_status ( 38 | BYTE pdrv /* Physical drive nmuber (0..) */ 39 | ) 40 | { 41 | return RES_OK; // Stubbed 42 | } 43 | 44 | 45 | 46 | /*-----------------------------------------------------------------------*/ 47 | /* Read Sector(s) */ 48 | /*-----------------------------------------------------------------------*/ 49 | 50 | DRESULT disk_read ( 51 | BYTE pdrv, /* Physical drive nmuber (0..) */ 52 | BYTE *buff, /* Data buffer to store read data */ 53 | DWORD sector, /* Sector address (LBA) */ 54 | UINT count /* Number of sectors to read (1..128) */ 55 | ) 56 | { 57 | if (sdmmc_sdcard_readsectors(sector,count,buff)) 58 | return RES_PARERR; 59 | 60 | return RES_OK; 61 | } 62 | 63 | 64 | 65 | /*-----------------------------------------------------------------------*/ 66 | /* Write Sector(s) */ 67 | /*-----------------------------------------------------------------------*/ 68 | 69 | #if _USE_WRITE 70 | DRESULT disk_write ( 71 | BYTE pdrv, /* Physical drive nmuber (0..) */ 72 | const BYTE *buff, /* Data to be written */ 73 | DWORD sector, /* Sector address (LBA) */ 74 | UINT count /* Number of sectors to write (1..128) */ 75 | ) 76 | { 77 | if (sdmmc_sdcard_writesectors(sector,count,buff)) 78 | return RES_PARERR; 79 | 80 | return RES_OK; 81 | } 82 | #endif 83 | 84 | 85 | /*-----------------------------------------------------------------------*/ 86 | /* Miscellaneous Functions */ 87 | /*-----------------------------------------------------------------------*/ 88 | 89 | #if _USE_IOCTL 90 | DRESULT disk_ioctl ( 91 | BYTE pdrv, /* Physical drive nmuber (0..) */ 92 | BYTE cmd, /* Control code */ 93 | void *buff /* Buffer to send/receive control data */ 94 | ) 95 | { 96 | return RES_PARERR; // Stubbed 97 | } 98 | #endif 99 | -------------------------------------------------------------------------------- /source/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2013 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #define _USE_WRITE 1 /* 1: Enable disk_write function */ 13 | #define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */ 14 | 15 | #include "integer.h" 16 | 17 | 18 | /* Status of Disk Functions */ 19 | typedef BYTE DSTATUS; 20 | 21 | /* Results of Disk Functions */ 22 | typedef enum { 23 | RES_OK = 0, /* 0: Successful */ 24 | RES_ERROR, /* 1: R/W Error */ 25 | RES_WRPRT, /* 2: Write Protected */ 26 | RES_NOTRDY, /* 3: Not Ready */ 27 | RES_PARERR /* 4: Invalid Parameter */ 28 | } DRESULT; 29 | 30 | 31 | /*---------------------------------------*/ 32 | /* Prototypes for disk control functions */ 33 | 34 | 35 | DSTATUS disk_initialize (BYTE pdrv); 36 | DSTATUS disk_status (BYTE pdrv); 37 | DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); 38 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); 39 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 40 | 41 | 42 | /* Disk Status Bits (DSTATUS) */ 43 | 44 | #define STA_NOINIT 0x01 /* Drive not initialized */ 45 | #define STA_NODISK 0x02 /* No medium in the drive */ 46 | #define STA_PROTECT 0x04 /* Write protected */ 47 | 48 | 49 | /* Command code for disk_ioctrl fucntion */ 50 | 51 | /* Generic command (used by FatFs) */ 52 | #define CTRL_SYNC 0 /* Flush disk cache (for write functions) */ 53 | #define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */ 54 | #define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */ 55 | #define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */ 56 | #define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */ 57 | 58 | /* Generic command (not used by FatFs) */ 59 | #define CTRL_POWER 5 /* Get/Set power status */ 60 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 61 | #define CTRL_EJECT 7 /* Eject media */ 62 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 63 | 64 | /* MMC/SDC specific ioctl command */ 65 | #define MMC_GET_TYPE 10 /* Get card type */ 66 | #define MMC_GET_CSD 11 /* Get CSD */ 67 | #define MMC_GET_CID 12 /* Get CID */ 68 | #define MMC_GET_OCR 13 /* Get OCR */ 69 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 70 | 71 | /* ATA/CF specific ioctl command */ 72 | #define ATA_GET_REV 20 /* Get F/W revision */ 73 | #define ATA_GET_MODEL 21 /* Get model name */ 74 | #define ATA_GET_SN 22 /* Get serial number */ 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /source/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs - FAT file system module configuration file R0.10a (C)ChaN, 2014 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #ifndef _FFCONF 6 | #define _FFCONF 29000 /* Revision ID */ 7 | 8 | 9 | /*---------------------------------------------------------------------------/ 10 | / Functions and Buffer Configurations 11 | /---------------------------------------------------------------------------*/ 12 | 13 | #define _FS_TINY 1 /* 0:Normal or 1:Tiny */ 14 | /* When _FS_TINY is set to 1, it reduces memory consumption _MAX_SS bytes each 15 | / file object. For file data transfer, FatFs uses the common sector buffer in 16 | / the file system object (FATFS) instead of private sector buffer eliminated 17 | / from the file object (FIL). */ 18 | 19 | 20 | #define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ 21 | /* Setting _FS_READONLY to 1 defines read only configuration. This removes 22 | / writing functions, f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(), 23 | / f_rename(), f_truncate() and useless f_getfree(). */ 24 | 25 | 26 | #define _FS_MINIMIZE 0 /* 0 to 3 */ 27 | /* The _FS_MINIMIZE option defines minimization level to remove API functions. 28 | / 29 | / 0: All basic functions are enabled. 30 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), 31 | / f_truncate() and f_rename() function are removed. 32 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 33 | / 3: f_lseek() function is removed in addition to 2. */ 34 | 35 | 36 | #define _USE_STRFUNC 0 /* 0:Disable or 1-2:Enable */ 37 | /* To enable string functions, set _USE_STRFUNC to 1 or 2. */ 38 | 39 | 40 | #define _USE_MKFS 0 /* 0:Disable or 1:Enable */ 41 | /* To enable f_mkfs() function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ 42 | 43 | 44 | #define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */ 45 | /* To enable fast seek feature, set _USE_FASTSEEK to 1. */ 46 | 47 | 48 | #define _USE_LABEL 0 /* 0:Disable or 1:Enable */ 49 | /* To enable volume label functions, set _USE_LAVEL to 1 */ 50 | 51 | 52 | #define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ 53 | /* To enable f_forward() function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ 54 | 55 | 56 | /*---------------------------------------------------------------------------/ 57 | / Locale and Namespace Configurations 58 | /---------------------------------------------------------------------------*/ 59 | 60 | #define _CODE_PAGE 932 61 | /* The _CODE_PAGE specifies the OEM code page to be used on the target system. 62 | / Incorrect setting of the code page can cause a file open failure. 63 | / 64 | / 932 - Japanese Shift-JIS (DBCS, OEM, Windows) 65 | / 936 - Simplified Chinese GBK (DBCS, OEM, Windows) 66 | / 949 - Korean (DBCS, OEM, Windows) 67 | / 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) 68 | / 1250 - Central Europe (Windows) 69 | / 1251 - Cyrillic (Windows) 70 | / 1252 - Latin 1 (Windows) 71 | / 1253 - Greek (Windows) 72 | / 1254 - Turkish (Windows) 73 | / 1255 - Hebrew (Windows) 74 | / 1256 - Arabic (Windows) 75 | / 1257 - Baltic (Windows) 76 | / 1258 - Vietnam (OEM, Windows) 77 | / 437 - U.S. (OEM) 78 | / 720 - Arabic (OEM) 79 | / 737 - Greek (OEM) 80 | / 775 - Baltic (OEM) 81 | / 850 - Multilingual Latin 1 (OEM) 82 | / 858 - Multilingual Latin 1 + Euro (OEM) 83 | / 852 - Latin 2 (OEM) 84 | / 855 - Cyrillic (OEM) 85 | / 866 - Russian (OEM) 86 | / 857 - Turkish (OEM) 87 | / 862 - Hebrew (OEM) 88 | / 874 - Thai (OEM, Windows) 89 | / 1 - ASCII (Valid for only non-LFN cfg.) */ 90 | 91 | 92 | #define _USE_LFN 1 /* 0 to 3 */ 93 | #define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ 94 | /* The _USE_LFN option switches the LFN feature. 95 | / 96 | / 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect. 97 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 98 | / 2: Enable LFN with dynamic working buffer on the STACK. 99 | / 3: Enable LFN with dynamic working buffer on the HEAP. 100 | / 101 | / When enable LFN feature, Unicode handling functions ff_convert() and ff_wtoupper() 102 | / function must be added to the project. 103 | / The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When use stack for the 104 | / working buffer, take care on stack overflow. When use heap memory for the working 105 | / buffer, memory management functions, ff_memalloc() and ff_memfree(), must be added 106 | / to the project. */ 107 | 108 | 109 | #define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ 110 | /* To switch the character encoding on the FatFs API (TCHAR) to Unicode, enable LFN 111 | / feature and set _LFN_UNICODE to 1. This option affects behavior of string I/O 112 | / functions. */ 113 | 114 | 115 | #define _STRF_ENCODE 0 /* 0:ANSI/OEM, 1:UTF-16LE, 2:UTF-16BE, 3:UTF-8 */ 116 | /* When Unicode API is enabled by _LFN_UNICODE option, this option selects the character 117 | / encoding on the file to be read/written via string I/O functions, f_gets(), f_putc(), 118 | / f_puts and f_printf(). This option has no effect when Unicode API is not enabled. */ 119 | 120 | 121 | #define _FS_RPATH 1 /* 0 to 2 */ 122 | /* The _FS_RPATH option configures relative path feature. 123 | / 124 | / 0: Disable relative path feature and remove related functions. 125 | / 1: Enable relative path. f_chdrive() and f_chdir() function are available. 126 | / 2: f_getcwd() function is available in addition to 1. 127 | / 128 | / Note that output of the f_readdir() fnction is affected by this option. */ 129 | 130 | 131 | /*---------------------------------------------------------------------------/ 132 | / Drive/Volume Configurations 133 | /---------------------------------------------------------------------------*/ 134 | 135 | #define _VOLUMES 8 136 | /* Number of volumes (logical drives) to be used. */ 137 | 138 | 139 | #define _STR_VOLUME_ID 0 /* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */ 140 | #define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" 141 | /* When _STR_VOLUME_ID is set to 1, also pre-defined string can be used as drive number 142 | / in the path name. _VOLUME_STRS defines the drive ID strings for each logical drives. 143 | / Number of items must be equal to _VOLUMES. Valid characters for the drive ID strings 144 | / are: 0-9 and A-Z. */ 145 | 146 | 147 | #define _MULTI_PARTITION 0 /* 0:Single partition, 1:Enable multiple partition */ 148 | /* By default(0), each logical drive number is bound to the same physical drive number 149 | / and only a FAT volume found on the physical drive is mounted. When it is set to 1, 150 | / each logical drive number is bound to arbitrary drive/partition listed in VolToPart[]. 151 | */ 152 | 153 | 154 | #define _MIN_SS 512 155 | #define _MAX_SS 512 156 | /* These options configure the sector size to be supported. (512, 1024, 2048 or 4096) 157 | / Always set both 512 for most systems, all memory card and hard disk. But a larger 158 | / value may be required for on-board flash memory and some type of optical media. 159 | / When _MIN_SS != _MAX_SS, FatFs is configured to multiple sector size and 160 | / GET_SECTOR_SIZE command must be implemented to the disk_ioctl() function. */ 161 | 162 | 163 | #define _USE_ERASE 0 /* 0:Disable or 1:Enable */ 164 | /* To enable sector erase feature, set _USE_ERASE to 1. Also CTRL_ERASE_SECTOR command 165 | / should be added to the disk_ioctl() function. */ 166 | 167 | 168 | #define _FS_NOFSINFO 0 /* 0 to 3 */ 169 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 170 | / option and f_getfree() function at first time after volume mount will force 171 | / a full FAT scan. Bit 1 controls the last allocated cluster number as bit 0. 172 | / 173 | / bit0=0: Use free cluster count in the FSINFO if available. 174 | / bit0=1: Do not trust free cluster count in the FSINFO. 175 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 176 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 177 | */ 178 | 179 | 180 | 181 | /*---------------------------------------------------------------------------/ 182 | / System Configurations 183 | /---------------------------------------------------------------------------*/ 184 | 185 | #define _WORD_ACCESS 0 /* 0 or 1 */ 186 | /* The _WORD_ACCESS option is an only platform dependent option. It defines 187 | / which access method is used to the word data on the FAT volume. 188 | / 189 | / 0: Byte-by-byte access. Always compatible with all platforms. 190 | / 1: Word access. Do not choose this unless under both the following conditions. 191 | / 192 | / * Address misaligned memory access is always allowed for all instructions. 193 | / * Byte order on the memory is little-endian. 194 | / 195 | / If it is the case, _WORD_ACCESS can also be set to 1 to improve performance 196 | / and reduce code size. 197 | */ 198 | 199 | 200 | #define _FS_LOCK 0 /* 0:Disable or >=1:Enable */ 201 | /* To enable file lock control feature, set _FS_LOCK to 1 or greater. 202 | / The value defines how many files/sub-directories can be opened simultaneously. 203 | / This feature consumes _FS_LOCK * 12 bytes of bss area. */ 204 | 205 | 206 | #define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ 207 | #define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ 208 | #define _SYNC_t HANDLE /* O/S dependent sync object type. e.g. HANDLE, OS_EVENT*, ID and etc.. */ 209 | /*#include */ 210 | 211 | /* A header file that defines sync object types on the O/S, such as windows.h, 212 | / ucos_ii.h and semphr.h, should be included here when enable this option. 213 | / The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs module. 214 | / 215 | / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. 216 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 217 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 218 | / function must be added to the project. 219 | */ 220 | 221 | 222 | #endif /* _FFCONFIG */ 223 | -------------------------------------------------------------------------------- /source/fatfs/integer.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------*/ 2 | /* Integer type definitions for FatFs module */ 3 | /*-------------------------------------------*/ 4 | 5 | #ifndef _FF_INTEGER 6 | #define _FF_INTEGER 7 | 8 | #ifdef _WIN32 /* FatFs development platform */ 9 | 10 | #include 11 | #include 12 | 13 | #else /* Embedded platform */ 14 | 15 | /* This type MUST be 8 bit */ 16 | typedef unsigned char BYTE; 17 | 18 | /* These types MUST be 16 bit */ 19 | typedef short SHORT; 20 | typedef unsigned short WORD; 21 | typedef unsigned short WCHAR; 22 | 23 | /* These types MUST be 16 bit or 32 bit */ 24 | typedef int INT; 25 | typedef unsigned int UINT; 26 | 27 | /* These types MUST be 32 bit */ 28 | typedef long LONG; 29 | typedef unsigned long DWORD; 30 | 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /source/fatfs/option/syscall.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample code of OS dependent controls for FatFs */ 3 | /* (C)ChaN, 2014 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "../ff.h" 8 | 9 | 10 | #if _FS_REENTRANT 11 | /*------------------------------------------------------------------------*/ 12 | /* Create a Synchronization Object 13 | /*------------------------------------------------------------------------*/ 14 | /* This function is called in f_mount() function to create a new 15 | / synchronization object, such as semaphore and mutex. When a 0 is returned, 16 | / the f_mount() function fails with FR_INT_ERR. 17 | */ 18 | 19 | int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any error */ 20 | BYTE vol, /* Corresponding logical drive being processed */ 21 | _SYNC_t *sobj /* Pointer to return the created sync object */ 22 | ) 23 | { 24 | int ret; 25 | 26 | 27 | *sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */ 28 | ret = (int)(*sobj != INVALID_HANDLE_VALUE); 29 | 30 | // *sobj = SyncObjects[vol]; /* uITRON (give a static created sync object) */ 31 | // ret = 1; /* The initial value of the semaphore must be 1. */ 32 | 33 | // *sobj = OSMutexCreate(0, &err); /* uC/OS-II */ 34 | // ret = (int)(err == OS_NO_ERR); 35 | 36 | // *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */ 37 | // ret = (int)(*sobj != NULL); 38 | 39 | return ret; 40 | } 41 | 42 | 43 | 44 | /*------------------------------------------------------------------------*/ 45 | /* Delete a Synchronization Object */ 46 | /*------------------------------------------------------------------------*/ 47 | /* This function is called in f_mount() function to delete a synchronization 48 | / object that created with ff_cre_syncobj function. When a 0 is returned, 49 | / the f_mount() function fails with FR_INT_ERR. 50 | */ 51 | 52 | int ff_del_syncobj ( /* !=0:Function succeeded, ==0:Could not delete due to any error */ 53 | _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 54 | ) 55 | { 56 | int ret; 57 | 58 | 59 | ret = CloseHandle(sobj); /* Win32 */ 60 | 61 | // ret = 1; /* uITRON (nothing to do) */ 62 | 63 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */ 64 | // ret = (int)(err == OS_NO_ERR); 65 | 66 | // vSemaphoreDelete(sobj); /* FreeRTOS */ 67 | // ret = 1; 68 | 69 | return ret; 70 | } 71 | 72 | 73 | 74 | /*------------------------------------------------------------------------*/ 75 | /* Request Grant to Access the Volume */ 76 | /*------------------------------------------------------------------------*/ 77 | /* This function is called on entering file functions to lock the volume. 78 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 79 | */ 80 | 81 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 82 | _SYNC_t sobj /* Sync object to wait */ 83 | ) 84 | { 85 | int ret; 86 | 87 | ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */ 88 | 89 | // ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */ 90 | 91 | // OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */ 92 | // ret = (int)(err == OS_NO_ERR); 93 | 94 | // ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */ 95 | 96 | return ret; 97 | } 98 | 99 | 100 | 101 | /*------------------------------------------------------------------------*/ 102 | /* Release Grant to Access the Volume */ 103 | /*------------------------------------------------------------------------*/ 104 | /* This function is called on leaving file functions to unlock the volume. 105 | */ 106 | 107 | void ff_rel_grant ( 108 | _SYNC_t sobj /* Sync object to be signaled */ 109 | ) 110 | { 111 | ReleaseMutex(sobj); /* Win32 */ 112 | 113 | // sig_sem(sobj); /* uITRON */ 114 | 115 | // OSMutexPost(sobj); /* uC/OS-II */ 116 | 117 | // xSemaphoreGive(sobj); /* FreeRTOS */ 118 | } 119 | 120 | #endif 121 | 122 | 123 | 124 | 125 | #if _USE_LFN == 3 /* LFN with a working buffer on the heap */ 126 | /*------------------------------------------------------------------------*/ 127 | /* Allocate a memory block */ 128 | /*------------------------------------------------------------------------*/ 129 | /* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. 130 | */ 131 | 132 | void* ff_memalloc ( /* Returns pointer to the allocated memory block */ 133 | UINT msize /* Number of bytes to allocate */ 134 | ) 135 | { 136 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 137 | } 138 | 139 | 140 | /*------------------------------------------------------------------------*/ 141 | /* Free a memory block */ 142 | /*------------------------------------------------------------------------*/ 143 | 144 | void ff_memfree ( 145 | void* mblock /* Pointer to the memory block to free */ 146 | ) 147 | { 148 | free(mblock); /* Discard the memory block with POSIX API */ 149 | } 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /source/fatfs/option/unicode.c: -------------------------------------------------------------------------------- 1 | #include "../ff.h" 2 | 3 | #if _USE_LFN != 0 4 | 5 | #if _CODE_PAGE == 932 /* Japanese Shift_JIS */ 6 | #include "cc932.c" 7 | #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ 8 | #include "cc936.c" 9 | #elif _CODE_PAGE == 949 /* Korean */ 10 | #include "cc949.c" 11 | #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ 12 | #include "cc950.c" 13 | #else /* Small character-set */ 14 | #include "ccsbcs.c" 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /source/fatfs/sdmmc.c: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Normmatt 2 | // Licensed under GPLv2 or any later version 3 | // Refer to the license.txt file included. 4 | 5 | #include "common.h" 6 | 7 | #include "sdmmc.h" 8 | #include "delay.h" 9 | 10 | //Uncomment to enable 32bit fifo support? 11 | //not currently working 12 | //#define DATA32_SUPPORT 13 | 14 | static struct mmcdevice handleNAND; 15 | static struct mmcdevice handleSD; 16 | 17 | mmcdevice *getMMCDevice(int drive) 18 | { 19 | if(drive==0) return &handleNAND; 20 | return &handleSD; 21 | } 22 | 23 | int __attribute__((noinline)) geterror(struct mmcdevice *ctx) 24 | { 25 | //if(ctx->error == 0x4) return -1; 26 | //else return 0; 27 | return (ctx->error << 29) >> 31; 28 | } 29 | 30 | 31 | void __attribute__((noinline)) inittarget(struct mmcdevice *ctx) 32 | { 33 | sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); 34 | setckl(ctx->clk); 35 | if (ctx->SDOPT == 0) { 36 | sdmmc_mask16(REG_SDOPT, 0, 0x8000); 37 | } else { 38 | sdmmc_mask16(REG_SDOPT, 0x8000, 0); 39 | } 40 | 41 | } 42 | 43 | 44 | void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args) 45 | { 46 | bool getSDRESP = (cmd << 15) >> 31; 47 | u16 flags = (cmd << 15) >> 31; 48 | const bool readdata = cmd & 0x20000; 49 | const bool writedata = cmd & 0x40000; 50 | 51 | if (readdata || writedata) 52 | flags |= TMIO_STAT0_DATAEND; 53 | 54 | ctx->error = 0; 55 | while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working? 56 | sdmmc_write16(REG_SDIRMASK0,0); 57 | sdmmc_write16(REG_SDIRMASK1,0); 58 | sdmmc_write16(REG_SDSTATUS0,0); 59 | sdmmc_write16(REG_SDSTATUS1,0); 60 | 61 | #ifdef DATA32_SUPPORT 62 | if (readdata) 63 | sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0x800); 64 | if (writedata) 65 | sdmmc_mask16(REG_SDDATACTL32, 0x800, 0x1000); 66 | #else 67 | sdmmc_mask16(REG_SDDATACTL32,0x1800,0); 68 | #endif 69 | 70 | sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); 71 | sdmmc_write16(REG_SDCMDARG1,args >> 16); 72 | sdmmc_write16(REG_SDCMD,cmd &0xFFFF); 73 | 74 | u32 size = ctx->size; 75 | u16 *dataPtr = (u16*)ctx->data; 76 | #ifdef DATA32_SUPPORT 77 | u32 *dataPtr32 = (u32*)ctx->data; 78 | #endif 79 | 80 | bool useBuf = ( NULL != dataPtr ); 81 | #ifdef DATA32_SUPPORT 82 | bool useBuf32 = (useBuf && (0 == (3 & ((u32)dataPtr)))); 83 | #endif 84 | 85 | u16 status0 = 0; 86 | while(true) { 87 | u16 status1 = sdmmc_read16(REG_SDSTATUS1); 88 | if (status1 & TMIO_STAT1_RXRDY) { 89 | if (readdata && useBuf) { 90 | sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); 91 | //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); 92 | if (size > 0x1FF) { 93 | #ifdef DATA32_SUPPORT 94 | if (useBuf32) { 95 | for(int i = 0; i<0x200; i+=4) 96 | *dataPtr32++ = sdmmc_read32(REG_SDFIFO32); 97 | } else { 98 | #endif 99 | for(int i = 0; i<0x200; i+=2) 100 | *dataPtr++ = sdmmc_read16(REG_SDFIFO); 101 | #ifdef DATA32_SUPPORT 102 | } 103 | #endif 104 | size -= 0x200; 105 | } 106 | } 107 | } 108 | 109 | if (status1 & TMIO_STAT1_TXRQ) { 110 | if (writedata && useBuf) { 111 | sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); 112 | //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ); 113 | if (size > 0x1FF) { 114 | #ifdef DATA32_SUPPORT 115 | for (int i = 0; i<0x200; i+=4) 116 | sdmmc_write32(REG_SDFIFO32,*dataPtr32++); 117 | #else 118 | for (int i = 0; i<0x200; i+=2) 119 | sdmmc_write16(REG_SDFIFO,*dataPtr++); 120 | #endif 121 | size -= 0x200; 122 | } 123 | } 124 | } 125 | if (status1 & TMIO_MASK_GW) { 126 | ctx->error |= 4; 127 | break; 128 | } 129 | 130 | if (!(status1 & TMIO_STAT1_CMD_BUSY)) { 131 | status0 = sdmmc_read16(REG_SDSTATUS0); 132 | if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) 133 | ctx->error |= 0x1; 134 | if (status0 & TMIO_STAT0_DATAEND) 135 | ctx->error |= 0x2; 136 | 137 | if ((status0 & flags) == flags) 138 | break; 139 | } 140 | } 141 | ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); 142 | ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); 143 | sdmmc_write16(REG_SDSTATUS0,0); 144 | sdmmc_write16(REG_SDSTATUS1,0); 145 | 146 | if (getSDRESP != 0) { 147 | ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16); 148 | ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16); 149 | ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16); 150 | ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16); 151 | } 152 | } 153 | 154 | int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in) 155 | { 156 | if (handleSD.isSDHC == 0) 157 | sector_no <<= 9; 158 | inittarget(&handleSD); 159 | sdmmc_write16(REG_SDSTOP,0x100); 160 | 161 | #ifdef DATA32_SUPPORT 162 | sdmmc_write16(REG_SDBLKCOUNT32,numsectors); 163 | #endif 164 | 165 | sdmmc_write16(REG_SDBLKCOUNT,numsectors); 166 | handleSD.data = in; 167 | handleSD.size = numsectors << 9; 168 | sdmmc_send_command(&handleSD,0x52C19,sector_no); 169 | return geterror(&handleSD); 170 | } 171 | 172 | int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out) 173 | { 174 | if (handleSD.isSDHC == 0) 175 | sector_no <<= 9; 176 | inittarget(&handleSD); 177 | sdmmc_write16(REG_SDSTOP,0x100); 178 | 179 | #ifdef DATA32_SUPPORT 180 | sdmmc_write16(REG_SDBLKCOUNT32,numsectors); 181 | sdmmc_write16(REG_SDBLKLEN32,0x200); 182 | #endif 183 | 184 | sdmmc_write16(REG_SDBLKCOUNT,numsectors); 185 | handleSD.data = out; 186 | handleSD.size = numsectors << 9; 187 | sdmmc_send_command(&handleSD,0x33C12,sector_no); 188 | return geterror(&handleSD); 189 | } 190 | 191 | 192 | 193 | int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out) 194 | { 195 | if (handleNAND.isSDHC == 0) 196 | sector_no <<= 9; 197 | inittarget(&handleNAND); 198 | sdmmc_write16(REG_SDSTOP,0x100); 199 | 200 | #ifdef DATA32_SUPPORT 201 | sdmmc_write32(REG_SDBLKCOUNT32,numsectors); 202 | #else 203 | sdmmc_write16(REG_SDBLKCOUNT,numsectors); 204 | #endif 205 | 206 | handleNAND.data = out; 207 | handleNAND.size = numsectors << 9; 208 | sdmmc_send_command(&handleNAND,0x33C12,sector_no); 209 | inittarget(&handleSD); 210 | return geterror(&handleNAND); 211 | } 212 | 213 | int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in) //experimental 214 | { 215 | if (handleNAND.isSDHC == 0) 216 | sector_no <<= 9; 217 | inittarget(&handleNAND); 218 | sdmmc_write16(REG_SDSTOP,0x100); 219 | 220 | #ifdef DATA32_SUPPORT 221 | sdmmc_write32(REG_SDBLKCOUNT32,numsectors); 222 | #else 223 | sdmmc_write16(REG_SDBLKCOUNT,numsectors); 224 | #endif 225 | 226 | handleNAND.data = in; 227 | handleNAND.size = numsectors << 9; 228 | sdmmc_send_command(&handleNAND,0x52C19,sector_no); 229 | inittarget(&handleSD); 230 | return geterror(&handleNAND); 231 | } 232 | 233 | static u32 calcSDSize(u8* csd, int type) 234 | { 235 | u32 result = 0; 236 | if (type == -1) type = csd[14] >> 6; 237 | switch (type) { 238 | case 0: 239 | { 240 | u32 block_len = csd[9] & 0xf; 241 | block_len = 1 << block_len; 242 | u32 mult = (csd[4] >> 7) | ((csd[5] & 3) << 1); 243 | mult = 1 << (mult + 2); 244 | result = csd[8] & 3; 245 | result = (result << 8) | csd[7]; 246 | result = (result << 2) | (csd[6] >> 6); 247 | result = (result + 1) * mult * block_len / 512; 248 | } 249 | break; 250 | case 1: 251 | result = csd[7] & 0x3f; 252 | result = (result << 8) | csd[6]; 253 | result = (result << 8) | csd[5]; 254 | result = (result + 1) * 1024; 255 | break; 256 | } 257 | return result; 258 | } 259 | 260 | void InitSD() 261 | { 262 | //NAND 263 | handleNAND.isSDHC = 0; 264 | handleNAND.SDOPT = 0; 265 | handleNAND.res = 0; 266 | handleNAND.initarg = 1; 267 | handleNAND.clk = 0x80; 268 | handleNAND.devicenumber = 1; 269 | 270 | //SD 271 | handleSD.isSDHC = 0; 272 | handleSD.SDOPT = 0; 273 | handleSD.res = 0; 274 | handleSD.initarg = 0; 275 | handleSD.clk = 0x80; 276 | handleSD.devicenumber = 0; 277 | 278 | //sdmmc_mask16(0x100,0x800,0); 279 | //sdmmc_mask16(0x100,0x1000,0); 280 | //sdmmc_mask16(0x100,0x0,0x402); 281 | //sdmmc_mask16(0xD8,0x22,0x2); 282 | //sdmmc_mask16(0x100,0x2,0); 283 | //sdmmc_mask16(0xD8,0x22,0); 284 | //sdmmc_write16(0x104,0); 285 | //sdmmc_write16(0x108,1); 286 | //sdmmc_mask16(REG_SDRESET,1,0); //not in new Version -- nintendo's code does this 287 | //sdmmc_mask16(REG_SDRESET,0,1); //not in new Version -- nintendo's code does this 288 | //sdmmc_mask16(0x20,0,0x31D); 289 | //sdmmc_mask16(0x22,0,0x837F); 290 | //sdmmc_mask16(0xFC,0,0xDB); 291 | //sdmmc_mask16(0xFE,0,0xDB); 292 | ////sdmmc_write16(REG_SDCLKCTL,0x20); 293 | ////sdmmc_write16(REG_SDOPT,0x40EE); 294 | ////sdmmc_mask16(0x02,0x3,0); 295 | //sdmmc_write16(REG_SDCLKCTL,0x40); 296 | //sdmmc_write16(REG_SDOPT,0x40EB); 297 | //sdmmc_mask16(0x02,0x3,0); 298 | //sdmmc_write16(REG_SDBLKLEN,0x200); 299 | //sdmmc_write16(REG_SDSTOP,0); 300 | 301 | *(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32 302 | *(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32 303 | #ifdef DATA32_SUPPORT 304 | *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 305 | #else 306 | *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 307 | #endif 308 | *(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2; 309 | #ifdef DATA32_SUPPORT 310 | *(vu16*)0x10006100 &= 0xFFFFu; //SDDATACTL32 311 | *(vu16*)0x100060D8 &= 0xFFDFu; //SDDATACTL 312 | *(vu16*)0x10006104 = 512; //SDBLKLEN32 313 | #else 314 | *(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32 315 | *(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL 316 | *(vu16*)0x10006104 = 0; //SDBLKLEN32 317 | #endif 318 | *(vu16*)0x10006108 = 1; //SDBLKCOUNT32 319 | *(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET 320 | *(vu16*)0x100060E0 |= 1u; //SDRESET 321 | *(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 322 | *(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 323 | *(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 324 | *(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 325 | *(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL 326 | #ifdef DATA32_SUPPORT 327 | *(vu16*)0x10006024 = 0x20; 328 | *(vu16*)0x10006028 = 0x40EE; 329 | #else 330 | *(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20 331 | *(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE 332 | #endif 333 | *(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL 334 | *(vu16*)0x10006026 = 512; //SDBLKLEN 335 | *(vu16*)0x10006008 = 0; //SDSTOP 336 | 337 | inittarget(&handleSD); 338 | } 339 | 340 | int Nand_Init() 341 | { 342 | inittarget(&handleNAND); 343 | ioDelay(0xF000); 344 | 345 | sdmmc_send_command(&handleNAND,0,0); 346 | 347 | do { 348 | do { 349 | sdmmc_send_command(&handleNAND,0x10701,0x100000); 350 | } while ( !(handleNAND.error & 1) ); 351 | } while((handleNAND.ret[0] & 0x80000000) == 0); 352 | 353 | sdmmc_send_command(&handleNAND,0x10602,0x0); 354 | if (handleNAND.error & 0x4) return -1; 355 | 356 | sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10); 357 | if (handleNAND.error & 0x4) return -1; 358 | 359 | sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10); 360 | if (handleNAND.error & 0x4) return -1; 361 | 362 | handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0],0); 363 | handleNAND.clk = 1; 364 | setckl(1); 365 | 366 | sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10); 367 | if (handleNAND.error & 0x4) return -1; 368 | 369 | handleNAND.SDOPT = 1; 370 | 371 | sdmmc_send_command(&handleNAND,0x10506,0x3B70100); 372 | if (handleNAND.error & 0x4) return -1; 373 | 374 | sdmmc_send_command(&handleNAND,0x10506,0x3B90100); 375 | if (handleNAND.error & 0x4) return -1; 376 | 377 | sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10); 378 | if (handleNAND.error & 0x4) return -1; 379 | 380 | sdmmc_send_command(&handleNAND,0x10410,0x200); 381 | if (handleNAND.error & 0x4) return -1; 382 | 383 | handleNAND.clk |= 0x200; 384 | 385 | inittarget(&handleSD); 386 | 387 | return 0; 388 | } 389 | 390 | int SD_Init() 391 | { 392 | inittarget(&handleSD); 393 | //ioDelay(0x3E8); 394 | ioDelay(0xF000); 395 | sdmmc_send_command(&handleSD,0,0); 396 | sdmmc_send_command(&handleSD,0x10408,0x1AA); 397 | //u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E; 398 | u32 temp = (handleSD.error & 0x1) << 0x1E; 399 | 400 | //int count = 0; 401 | u32 temp2 = 0; 402 | do { 403 | do { 404 | sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); 405 | sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); 406 | temp2 = 1; 407 | } while ( !(handleSD.error & 1) ); 408 | 409 | } while((handleSD.ret[0] & 0x80000000) == 0); 410 | //do 411 | //{ 412 | // sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); 413 | // sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); 414 | // 415 | //} 416 | //while(!(handleSD.ret[0] & 0x80000000)); 417 | 418 | if(!((handleSD.ret[0] >> 30) & 1) || !temp) 419 | temp2 = 0; 420 | 421 | handleSD.isSDHC = temp2; 422 | //handleSD.isSDHC = (handleSD.ret[0] & 0x40000000); 423 | 424 | sdmmc_send_command(&handleSD,0x10602,0); 425 | if (handleSD.error & 0x4) return -1; 426 | 427 | sdmmc_send_command(&handleSD,0x10403,0); 428 | if (handleSD.error & 0x4) return -1; 429 | handleSD.initarg = handleSD.ret[0] >> 0x10; 430 | 431 | sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10); 432 | if (handleSD.error & 0x4) return -1; 433 | 434 | handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1); 435 | handleSD.clk = 1; 436 | setckl(1); 437 | 438 | sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10); 439 | if (handleSD.error & 0x4) return -1; 440 | 441 | sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); 442 | if (handleSD.error & 0x4) return -1; 443 | 444 | handleSD.SDOPT = 1; 445 | sdmmc_send_command(&handleSD,0x10446,0x2); 446 | if (handleSD.error & 0x4) return -1; 447 | 448 | sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10); 449 | if (handleSD.error & 0x4) return -1; 450 | 451 | sdmmc_send_command(&handleSD,0x10410,0x200); 452 | if (handleSD.error & 0x4) return -1; 453 | handleSD.clk |= 0x200; 454 | 455 | return 0; 456 | } 457 | 458 | void sdmmc_sdcard_init() 459 | { 460 | InitSD(); 461 | Nand_Init(); 462 | SD_Init(); 463 | } 464 | -------------------------------------------------------------------------------- /source/fatfs/sdmmc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Normmatt 2 | // Licensed under GPLv2 or any later version 3 | // Refer to the license.txt file included. 4 | 5 | #pragma once 6 | 7 | #include "common.h" 8 | 9 | #define SDMMC_BASE 0x10006000u 10 | 11 | #define REG_SDCMD 0x00 12 | #define REG_SDPORTSEL 0x02 13 | #define REG_SDCMDARG 0x04 14 | #define REG_SDCMDARG0 0x04 15 | #define REG_SDCMDARG1 0x06 16 | #define REG_SDSTOP 0x08 17 | #define REG_SDBLKCOUNT 0x0a 18 | 19 | #define REG_SDRESP0 0x0c 20 | #define REG_SDRESP1 0x0e 21 | #define REG_SDRESP2 0x10 22 | #define REG_SDRESP3 0x12 23 | #define REG_SDRESP4 0x14 24 | #define REG_SDRESP5 0x16 25 | #define REG_SDRESP6 0x18 26 | #define REG_SDRESP7 0x1a 27 | 28 | #define REG_SDSTATUS0 0x1c 29 | #define REG_SDSTATUS1 0x1e 30 | 31 | #define REG_SDIRMASK0 0x20 32 | #define REG_SDIRMASK1 0x22 33 | #define REG_SDCLKCTL 0x24 34 | 35 | #define REG_SDBLKLEN 0x26 36 | #define REG_SDOPT 0x28 37 | #define REG_SDFIFO 0x30 38 | 39 | #define REG_SDDATACTL 0xd8 40 | #define REG_SDRESET 0xe0 41 | #define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? 42 | 43 | #define REG_SDDATACTL32 0x100 44 | #define REG_SDBLKLEN32 0x104 45 | #define REG_SDBLKCOUNT32 0x108 46 | #define REG_SDFIFO32 0x10C 47 | 48 | #define REG_CLK_AND_WAIT_CTL 0x138 49 | #define REG_RESET_SDIO 0x1e0 50 | 51 | #define TMIO_STAT0_CMDRESPEND 0x0001 52 | #define TMIO_STAT0_DATAEND 0x0004 53 | #define TMIO_STAT0_CARD_REMOVE 0x0008 54 | #define TMIO_STAT0_CARD_INSERT 0x0010 55 | #define TMIO_STAT0_SIGSTATE 0x0020 56 | #define TMIO_STAT0_WRPROTECT 0x0080 57 | #define TMIO_STAT0_CARD_REMOVE_A 0x0100 58 | #define TMIO_STAT0_CARD_INSERT_A 0x0200 59 | #define TMIO_STAT0_SIGSTATE_A 0x0400 60 | #define TMIO_STAT1_CMD_IDX_ERR 0x0001 61 | #define TMIO_STAT1_CRCFAIL 0x0002 62 | #define TMIO_STAT1_STOPBIT_ERR 0x0004 63 | #define TMIO_STAT1_DATATIMEOUT 0x0008 64 | #define TMIO_STAT1_RXOVERFLOW 0x0010 65 | #define TMIO_STAT1_TXUNDERRUN 0x0020 66 | #define TMIO_STAT1_CMDTIMEOUT 0x0040 67 | #define TMIO_STAT1_RXRDY 0x0100 68 | #define TMIO_STAT1_TXRQ 0x0200 69 | #define TMIO_STAT1_ILL_FUNC 0x2000 70 | #define TMIO_STAT1_CMD_BUSY 0x4000 71 | #define TMIO_STAT1_ILL_ACCESS 0x8000 72 | 73 | //Comes from TWLSDK mongoose.tef DWARF info 74 | #define SDMC_NORMAL 0x00000000 75 | #define SDMC_ERR_COMMAND 0x00000001 76 | #define SDMC_ERR_CRC 0x00000002 77 | #define SDMC_ERR_END 0x00000004 78 | #define SDMC_ERR_TIMEOUT 0x00000008 79 | #define SDMC_ERR_FIFO_OVF 0x00000010 80 | #define SDMC_ERR_FIFO_UDF 0x00000020 81 | #define SDMC_ERR_WP 0x00000040 82 | #define SDMC_ERR_ABORT 0x00000080 83 | #define SDMC_ERR_FPGA_TIMEOUT 0x00000100 84 | #define SDMC_ERR_PARAM 0x00000200 85 | #define SDMC_ERR_R1_STATUS 0x00000800 86 | #define SDMC_ERR_NUM_WR_SECTORS 0x00001000 87 | #define SDMC_ERR_RESET 0x00002000 88 | #define SDMC_ERR_ILA 0x00004000 89 | #define SDMC_ERR_INFO_DETECT 0x00008000 90 | 91 | #define SDMC_STAT_ERR_UNKNOWN 0x00080000 92 | #define SDMC_STAT_ERR_CC 0x00100000 93 | #define SDMC_STAT_ERR_ECC_FAILED 0x00200000 94 | #define SDMC_STAT_ERR_CRC 0x00800000 95 | #define SDMC_STAT_ERR_OTHER 0xf9c70008 96 | 97 | #define TMIO_MASK_ALL 0x837f031d 98 | 99 | #define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ 100 | TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) 101 | 102 | #define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND) 103 | #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) 104 | 105 | typedef struct mmcdevice { 106 | u8* data; 107 | u32 size; 108 | u32 error; 109 | u16 stat0; 110 | u16 stat1; 111 | u32 ret[4]; 112 | u32 initarg; 113 | u32 isSDHC; 114 | u32 clk; 115 | u32 SDOPT; 116 | u32 devicenumber; 117 | u32 total_size; //size in sectors of the device 118 | u32 res; 119 | } mmcdevice; 120 | 121 | /*int sdmmc_sdcard_init(); 122 | void sdmmc_sdcard_readsector(uint32_t sector_no, void *out); 123 | void sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, void *out); 124 | void sdmmc_sdcard_writesector(uint32_t sector_no, void *in); 125 | void sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, void *in); 126 | void sdmmc_blktransferinit();*/ 127 | 128 | void sdmmc_sdcard_init(); 129 | int sdmmc_sdcard_readsector(u32 sector_no, u8 *out); 130 | int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); 131 | int sdmmc_sdcard_writesector(u32 sector_no, u8 *in); 132 | int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in); 133 | 134 | int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); 135 | int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in); 136 | 137 | mmcdevice *getMMCDevice(int drive); 138 | 139 | void InitSDMMC(); 140 | int Nand_Init(); 141 | int SD_Init(); 142 | 143 | static inline u16 sdmmc_read16(u16 reg) { 144 | return *(vu16*)(SDMMC_BASE + reg); 145 | } 146 | 147 | static inline void sdmmc_write16(u16 reg, u16 val) { 148 | *(vu16*)(SDMMC_BASE + reg) = val; 149 | } 150 | 151 | static inline u32 sdmmc_read32(u16 reg) { 152 | return *(vu32*)(SDMMC_BASE + reg); 153 | } 154 | 155 | static inline void sdmmc_write32(u16 reg, u32 val) { 156 | *(vu32*)(SDMMC_BASE + reg) = val; 157 | } 158 | 159 | static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) { 160 | u16 val = sdmmc_read16(reg); 161 | val &= ~clear; 162 | val |= set; 163 | sdmmc_write16(reg, val); 164 | } 165 | 166 | static inline void setckl(u32 data) 167 | { 168 | sdmmc_mask16(REG_SDCLKCTL, 0x100, 0); 169 | sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF); 170 | sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); 171 | } 172 | -------------------------------------------------------------------------------- /source/font.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file was autogenerated by raw2c. 3 | Visit http://www.devkitpro.org 4 | */ 5 | 6 | //--------------------------------------------------------------------------------- 7 | #ifndef _font_h_ 8 | #define _font_h_ 9 | //--------------------------------------------------------------------------------- 10 | static const unsigned char font[] = { 11 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, 12 | 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 13 | 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x3c, 0x3c, 0x18, 0xff, 0xe7, 0x18, 0x3c, 0x00, 14 | 0x10, 0x38, 0x7c, 0xfe, 0xee, 0x10, 0x38, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 15 | 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 16 | 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, 17 | 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x08, 0x0c, 0x0a, 0x0a, 0x08, 0x78, 0xf0, 0x00, 18 | 0x18, 0x14, 0x1a, 0x16, 0x72, 0xe2, 0x0e, 0x1c, 0x10, 0x54, 0x38, 0xee, 0x38, 0x54, 0x10, 0x00, 19 | 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, 20 | 0x18, 0x3c, 0x5a, 0x18, 0x5a, 0x3c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, 21 | 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x00, 0x1c, 0x22, 0x38, 0x44, 0x44, 0x38, 0x88, 0x70, 22 | 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, 0x18, 0x3c, 0x5a, 0x18, 0x5a, 0x3c, 0x18, 0x7e, 23 | 0x18, 0x3c, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x5a, 0x3c, 0x18, 0x00, 24 | 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 25 | 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x24, 0x42, 0xff, 0x42, 0x24, 0x00, 0x00, 26 | 0x00, 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00, 28 | 0x6c, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 29 | 0x10, 0x7c, 0xd0, 0x7c, 0x16, 0xfc, 0x10, 0x00, 0x00, 0x66, 0xac, 0xd8, 0x36, 0x6a, 0xcc, 0x00, 30 | 0x38, 0x4c, 0x38, 0x78, 0xce, 0xcc, 0x7a, 0x00, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 31 | 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 32 | 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 35 | 0x7c, 0xce, 0xde, 0xf6, 0xe6, 0xe6, 0x7c, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7e, 0x00, 36 | 0x7c, 0xc6, 0x06, 0x1c, 0x70, 0xc6, 0xfe, 0x00, 0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00, 37 | 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, 0xfe, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 38 | 0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, 0xfe, 0xc6, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, 39 | 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00, 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00, 40 | 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20, 41 | 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 42 | 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, 43 | 0x7c, 0x82, 0x9e, 0xa6, 0x9e, 0x80, 0x7c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 44 | 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 45 | 0xfc, 0x66, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, 46 | 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xce, 0xc6, 0x7e, 0x00, 47 | 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 48 | 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 49 | 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x82, 0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, 50 | 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 51 | 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x06, 52 | 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xe6, 0x00, 0x7c, 0xc6, 0xc0, 0x7c, 0x06, 0xc6, 0x7c, 0x00, 53 | 0x7e, 0x5a, 0x5a, 0x18, 0x18, 0x18, 0x3c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 54 | 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x82, 0x00, 55 | 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, 56 | 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 57 | 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 58 | 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 59 | 0x30, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 60 | 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x00, 61 | 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 62 | 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x78, 63 | 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 64 | 0x00, 0x0c, 0x00, 0x1c, 0x0c, 0x0c, 0xcc, 0x78, 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, 65 | 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xd6, 0xd6, 0x00, 66 | 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 67 | 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x7c, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, 68 | 0x00, 0x00, 0xde, 0x76, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0x7c, 0x00, 69 | 0x10, 0x30, 0xfc, 0x30, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 70 | 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 71 | 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 72 | 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, 0x0e, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0e, 0x00, 73 | 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0xe0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xe0, 0x00, 74 | 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 75 | 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x70, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 76 | 0x0e, 0x10, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 0x7c, 0x82, 0x38, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 77 | 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0xe0, 0x10, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 78 | 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0xc0, 0x7c, 0x18, 0x70, 79 | 0x7c, 0x82, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 80 | 0xe0, 0x10, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 81 | 0x7c, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 0xe0, 0x10, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 82 | 0xc6, 0x00, 0x7c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x38, 0x38, 0x7c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 83 | 0x0e, 0x10, 0xfe, 0x60, 0x78, 0x60, 0xfe, 0x00, 0x00, 0x00, 0x7c, 0x12, 0x7e, 0xd0, 0x7e, 0x00, 84 | 0x7e, 0xc8, 0xc8, 0xfe, 0xc8, 0xc8, 0xce, 0x00, 0x7c, 0x82, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 85 | 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0xe0, 0x10, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 86 | 0x7c, 0x82, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0xe0, 0x10, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 87 | 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 88 | 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x18, 0x7c, 0xd6, 0xd0, 0xd6, 0x7c, 0x18, 0x00, 89 | 0x38, 0x6c, 0x60, 0xf0, 0x60, 0xf2, 0xdc, 0x00, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x00, 90 | 0xf8, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0x06, 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, 91 | 0x0e, 0x10, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0x0e, 0x10, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 92 | 0x0e, 0x10, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x0e, 0x10, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 93 | 0x66, 0x98, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x98, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0x00, 94 | 0x38, 0x0c, 0x3c, 0x34, 0x00, 0x7e, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 95 | 0x30, 0x00, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, 0xc0, 0xc8, 0xd0, 0xfe, 0x46, 0x8c, 0x1e, 0x00, 97 | 0xc0, 0xc8, 0xd0, 0xec, 0x5c, 0xbe, 0x0c, 0x00, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x18, 0x00, 98 | 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 99 | 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 100 | 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 101 | 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 102 | 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 103 | 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 104 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 105 | 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 106 | 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 107 | 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 109 | 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 110 | 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 111 | 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 112 | 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 113 | 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 114 | 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 115 | 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 116 | 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 117 | 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 118 | 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 119 | 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 121 | 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 122 | 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00, 0x74, 0xcc, 0xc8, 0xdc, 0x76, 0x00, 0x78, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xdc, 0x40, 124 | 0xfe, 0x62, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x02, 0x7e, 0xec, 0x6c, 0x6c, 0x48, 0x00, 125 | 0xfe, 0x62, 0x30, 0x18, 0x30, 0x62, 0xfe, 0x00, 0x00, 0x00, 0x7e, 0xd0, 0xc8, 0xc8, 0x70, 0x00, 126 | 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xf8, 0x80, 0x00, 0x00, 0x7e, 0xd8, 0x18, 0x18, 0x10, 0x00, 127 | 0x38, 0x10, 0x7c, 0xd6, 0xd6, 0x7c, 0x10, 0x38, 0x7c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x7c, 0x00, 128 | 0x7c, 0xc6, 0xc6, 0xc6, 0x6c, 0x28, 0xee, 0x00, 0x3c, 0x22, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, 129 | 0x00, 0x00, 0x66, 0x99, 0x99, 0x66, 0x00, 0x00, 0x00, 0x06, 0x7c, 0x9e, 0xf2, 0x7c, 0xc0, 0x00, 130 | 0x00, 0x00, 0x7c, 0xc0, 0xf8, 0xc0, 0x7c, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 131 | 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, 132 | 0x30, 0x18, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x00, 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x00, 133 | 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 134 | 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 135 | 0x38, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 136 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x00, 137 | 0xd8, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0xc0, 0xf0, 0x00, 0x00, 0x00, 0x00, 138 | 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 139 | 140 | }; 141 | const int font_size = sizeof(font); 142 | //--------------------------------------------------------------------------------- 143 | #endif //_font_h_ 144 | //--------------------------------------------------------------------------------- 145 | -------------------------------------------------------------------------------- /source/fs.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include "draw.h" 3 | 4 | #include "fatfs/ff.h" 5 | 6 | static FATFS fs; 7 | static FIL file; 8 | static DIR dir; 9 | 10 | bool InitFS() 11 | { 12 | #ifndef EXEC_GATEWAY 13 | // TODO: Magic? 14 | *(u32*)0x10000020 = 0; 15 | *(u32*)0x10000020 = 0x340; 16 | #endif 17 | bool ret = (f_mount(&fs, "0:", 0) == FR_OK); 18 | #ifdef WORK_DIR 19 | f_chdir(WORK_DIR); 20 | #endif 21 | return ret; 22 | } 23 | 24 | void DeinitFS() 25 | { 26 | f_mount(NULL, "0:", 0); 27 | } 28 | 29 | bool FileOpen(const char* path) 30 | { 31 | unsigned flags = FA_READ | FA_WRITE | FA_OPEN_EXISTING; 32 | if (*path == '/') 33 | path++; 34 | bool ret = (f_open(&file, path, flags) == FR_OK); 35 | #ifdef WORK_DIR 36 | f_chdir("/"); // temporarily change the current directory 37 | if (!ret) ret = (f_open(&file, path, flags) == FR_OK); 38 | f_chdir(WORK_DIR); 39 | #endif 40 | f_lseek(&file, 0); 41 | f_sync(&file); 42 | return ret; 43 | } 44 | 45 | bool DebugFileOpen(const char* path) 46 | { 47 | Debug("Opening %s ...", path); 48 | if (!FileOpen(path)) { 49 | Debug("Could not open %s!", path); 50 | return false; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | bool FileCreate(const char* path, bool truncate) 57 | { 58 | unsigned flags = FA_READ | FA_WRITE; 59 | flags |= truncate ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS; 60 | if (*path == '/') 61 | path++; 62 | bool ret = (f_open(&file, path, flags) == FR_OK); 63 | f_lseek(&file, 0); 64 | f_sync(&file); 65 | return ret; 66 | } 67 | 68 | bool DebugFileCreate(const char* path, bool truncate) { 69 | Debug("Creating %s ...", path); 70 | if (!FileCreate(path, truncate)) { 71 | Debug("Could not create %s!", path); 72 | return false; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | size_t FileRead(void* buf, size_t size, size_t foffset) 79 | { 80 | UINT bytes_read = 0; 81 | f_lseek(&file, foffset); 82 | f_read(&file, buf, size, &bytes_read); 83 | return bytes_read; 84 | } 85 | 86 | bool DebugFileRead(void* buf, size_t size, size_t foffset) { 87 | size_t bytesRead = FileRead(buf, size, foffset); 88 | if(bytesRead != size) { 89 | Debug("ERROR, file is too small!"); 90 | return false; 91 | } 92 | 93 | return true; 94 | } 95 | 96 | size_t FileWrite(void* buf, size_t size, size_t foffset) 97 | { 98 | UINT bytes_written = 0; 99 | f_lseek(&file, foffset); 100 | f_write(&file, buf, size, &bytes_written); 101 | f_sync(&file); 102 | return bytes_written; 103 | } 104 | 105 | bool DebugFileWrite(void* buf, size_t size, size_t foffset) 106 | { 107 | size_t bytesWritten = FileWrite(buf, size, foffset); 108 | if(bytesWritten != size) { 109 | Debug("ERROR, SD card may be full!"); 110 | return false; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | size_t FileGetSize() 117 | { 118 | return f_size(&file); 119 | } 120 | 121 | void FileClose() 122 | { 123 | f_close(&file); 124 | } 125 | 126 | bool DirMake(const char* path) 127 | { 128 | FRESULT res = f_mkdir(path); 129 | bool ret = (res == FR_OK) || (res == FR_EXIST); 130 | return ret; 131 | } 132 | 133 | bool DebugDirMake(const char* path) 134 | { 135 | Debug("Creating dir %s ...", path); 136 | if (!DirMake(path)) { 137 | Debug("Could not create %s!", path); 138 | return false; 139 | } 140 | 141 | return true; 142 | } 143 | 144 | bool DirOpen(const char* path) 145 | { 146 | return (f_opendir(&dir, path) == FR_OK); 147 | } 148 | 149 | bool DebugDirOpen(const char* path) 150 | { 151 | Debug("Opening %s ...", path); 152 | if (!DirOpen(path)) { 153 | Debug("Could not open %s!", path); 154 | return false; 155 | } 156 | 157 | return true; 158 | } 159 | 160 | bool DirRead(char* fname, int fsize) 161 | { 162 | FILINFO fno; 163 | fno.lfname = fname; 164 | fno.lfsize = fsize; 165 | bool ret = false; 166 | while (f_readdir(&dir, &fno) == FR_OK) { 167 | if (fno.fname[0] == 0) break; 168 | if ((fno.fname[0] != '.') && !(fno.fattrib & AM_DIR)) { 169 | if (fname[0] == 0) 170 | strcpy(fname, fno.fname); 171 | ret = true; 172 | break; 173 | } 174 | } 175 | return ret; 176 | } 177 | 178 | void DirClose() 179 | { 180 | f_closedir(&dir); 181 | } 182 | 183 | bool GetFileListWorker(char** list, int* lsize, char* fpath, int fsize, bool recursive) 184 | { 185 | DIR pdir; 186 | FILINFO fno; 187 | char* fname = fpath + strnlen(fpath, fsize - 1); 188 | bool ret = false; 189 | 190 | if (f_opendir(&pdir, fpath) != FR_OK) 191 | return false; 192 | (fname++)[0] = '/'; 193 | fno.lfname = fname; 194 | fno.lfsize = fsize - (fname - fpath); 195 | 196 | while (f_readdir(&pdir, &fno) == FR_OK) { 197 | if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) 198 | continue; // filter out virtual entries 199 | if (fname[0] == 0) 200 | strncpy(fname, fno.fname, (fsize - 1) - (fname - fpath)); 201 | if (fno.fname[0] == 0) { 202 | ret = true; 203 | break; 204 | } else if (fno.fattrib & AM_DIR) { 205 | if (recursive && !GetFileListWorker(list, lsize, fpath, fsize, recursive)) 206 | break; 207 | } else { 208 | snprintf(*list, *lsize, "%s\n", fpath); 209 | for(;(*list)[0] != '\0' && (*lsize) > 1; (*list)++, (*lsize)--); 210 | if ((*lsize) <= 1) break; 211 | } 212 | } 213 | f_closedir(&pdir); 214 | 215 | return ret; 216 | } 217 | 218 | bool GetFileList(const char* path, char* list, int lsize, bool recursive) 219 | { 220 | char fpath[256]; // 256 is the maximum length of a full path 221 | strncpy(fpath, path, 256); 222 | return GetFileListWorker(&list, &lsize, fpath, 256, recursive); 223 | } 224 | 225 | static uint64_t ClustersToBytes(FATFS* fs, DWORD clusters) 226 | { 227 | uint64_t sectors = clusters * fs->csize; 228 | #if _MAX_SS != _MIN_SS 229 | return sectors * fs->ssize; 230 | #else 231 | return sectors * _MAX_SS; 232 | #endif 233 | } 234 | 235 | uint64_t RemainingStorageSpace() 236 | { 237 | DWORD free_clusters; 238 | FATFS *fs2; 239 | FRESULT res = f_getfree("0:", &free_clusters, &fs2); 240 | if (res) 241 | return -1; 242 | 243 | return ClustersToBytes(&fs, free_clusters); 244 | } 245 | -------------------------------------------------------------------------------- /source/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | bool InitFS(); 6 | void DeinitFS(); 7 | 8 | /** Opens existing files */ 9 | bool FileOpen(const char* path); 10 | bool DebugFileOpen(const char* path); 11 | 12 | /** Opens new files (and creates them if they don't already exist) */ 13 | bool FileCreate(const char* path, bool truncate); 14 | bool DebugFileCreate(const char* path, bool truncate); 15 | 16 | /** Reads contents of the opened file */ 17 | size_t FileRead(void* buf, size_t size, size_t foffset); 18 | bool DebugFileRead(void* buf, size_t size, size_t foffset); 19 | 20 | /** Writes to the opened file */ 21 | size_t FileWrite(void* buf, size_t size, size_t foffset); 22 | bool DebugFileWrite(void* buf, size_t size, size_t foffset); 23 | 24 | /** Gets the size of the opened file */ 25 | size_t FileGetSize(); 26 | 27 | /** Creates a directory */ 28 | bool DirMake(const char* path); 29 | bool DebugDirMake(const char* path); 30 | 31 | /** Opens an existing directory */ 32 | bool DirOpen(const char* path); 33 | bool DebugDirOpen(const char* path); 34 | 35 | /** Reads next file name to fname from opened directory, 36 | returns false if all files in directory are processed. 37 | fname needs to be allocated to fsize bytes minimum. */ 38 | bool DirRead(char* fname, int fsize); 39 | 40 | /** Get list of files under a given path **/ 41 | bool GetFileList(const char* path, char* list, int lsize, bool recursive); 42 | 43 | /** Gets remaining space on SD card in bytes */ 44 | uint64_t RemainingStorageSpace(); 45 | 46 | void FileClose(); 47 | 48 | void DirClose(); 49 | -------------------------------------------------------------------------------- /source/hid.c: -------------------------------------------------------------------------------- 1 | #include "hid.h" 2 | 3 | u32 InputWait() { 4 | u32 pad_state_old = HID_STATE; 5 | while (true) { 6 | u32 pad_state = HID_STATE; 7 | if (pad_state ^ pad_state_old) 8 | return ~pad_state; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/hid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define HID_STATE (*(volatile u32*)0x10146000) 6 | 7 | #define BUTTON_A (1 << 0) 8 | #define BUTTON_B (1 << 1) 9 | #define BUTTON_SELECT (1 << 2) 10 | #define BUTTON_START (1 << 3) 11 | #define BUTTON_RIGHT (1 << 4) 12 | #define BUTTON_LEFT (1 << 5) 13 | #define BUTTON_UP (1 << 6) 14 | #define BUTTON_DOWN (1 << 7) 15 | #define BUTTON_R1 (1 << 8) 16 | #define BUTTON_L1 (1 << 9) 17 | #define BUTTON_X (1 << 10) 18 | #define BUTTON_Y (1 << 11) 19 | #define BUTTON_ANY 0x00000FFF 20 | 21 | u32 InputWait(); 22 | -------------------------------------------------------------------------------- /source/i2c.c: -------------------------------------------------------------------------------- 1 | #include "i2c.h" 2 | #include "draw.h" 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | static const struct { u8 bus_id, reg_addr } dev_data[] = { 7 | {0, 0x4A}, {0, 0x7A}, {0, 0x78}, 8 | {1, 0x4A}, {1, 0x78}, {1, 0x2C}, 9 | {1, 0x2E}, {1, 0x40}, {1, 0x44}, 10 | {2, 0xD6}, {2, 0xD0}, {2, 0xD2}, 11 | {2, 0xA4}, {2, 0x9A}, {2, 0xA0}, 12 | }; 13 | 14 | const inline u8 i2cGetDeviceBusId(u8 device_id) { 15 | return dev_data[device_id].bus_id; 16 | } 17 | 18 | const inline u8 i2cGetDeviceRegAddr(u8 device_id) { 19 | return dev_data[device_id].reg_addr; 20 | } 21 | 22 | //----------------------------------------------------------------------------- 23 | 24 | static vu8* const reg_data_addrs[] = { 25 | (vu8*)(I2C1_REG_OFF + I2C_REG_DATA), 26 | (vu8*)(I2C2_REG_OFF + I2C_REG_DATA), 27 | (vu8*)(I2C3_REG_OFF + I2C_REG_DATA), 28 | }; 29 | 30 | inline vu8* const i2cGetDataReg(u8 bus_id) { 31 | return reg_data_addrs[bus_id]; 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | 36 | static vu8* const reg_cnt_addrs[] = { 37 | (vu8*)(I2C1_REG_OFF + I2C_REG_CNT), 38 | (vu8*)(I2C2_REG_OFF + I2C_REG_CNT), 39 | (vu8*)(I2C3_REG_OFF + I2C_REG_CNT), 40 | }; 41 | 42 | inline vu8* const i2cGetCntReg(u8 bus_id) { 43 | return reg_cnt_addrs[bus_id]; 44 | } 45 | 46 | //----------------------------------------------------------------------------- 47 | 48 | inline void i2cWaitBusy(u8 bus_id) { 49 | while (*i2cGetCntReg(bus_id) & 0x80); 50 | } 51 | 52 | inline bool i2cGetResult(u8 bus_id) { 53 | i2cWaitBusy(bus_id); 54 | return (*i2cGetCntReg(bus_id) >> 4) & 1; 55 | } 56 | 57 | void i2cStop(u8 bus_id, u8 arg0) { 58 | *i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0; 59 | i2cWaitBusy(bus_id); 60 | *i2cGetCntReg(bus_id) = 0xC5; 61 | } 62 | 63 | //----------------------------------------------------------------------------- 64 | 65 | bool i2cSelectDevice(u8 bus_id, u8 dev_reg) { 66 | i2cWaitBusy(bus_id); 67 | *i2cGetDataReg(bus_id) = dev_reg; 68 | *i2cGetCntReg(bus_id) = 0xC2; 69 | return i2cGetResult(bus_id); 70 | } 71 | 72 | bool i2cSelectRegister(u8 bus_id, u8 reg) { 73 | i2cWaitBusy(bus_id); 74 | *i2cGetDataReg(bus_id) = reg; 75 | *i2cGetCntReg(bus_id) = 0xC0; 76 | return i2cGetResult(bus_id); 77 | } 78 | 79 | //----------------------------------------------------------------------------- 80 | 81 | u8 i2cReadRegister(u8 dev_id, u8 reg) { 82 | u8 bus_id = i2cGetDeviceBusId(dev_id); 83 | u8 dev_addr = i2cGetDeviceRegAddr(dev_id); 84 | 85 | for (size_t i = 0; i < 8; i++) { 86 | if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) { 87 | if (i2cSelectDevice(bus_id, dev_addr | 1)) { 88 | i2cWaitBusy(bus_id); 89 | i2cStop(bus_id, 1); 90 | i2cWaitBusy(bus_id); 91 | return *i2cGetDataReg(bus_id); 92 | } 93 | } 94 | *i2cGetCntReg(bus_id) = 0xC5; 95 | i2cWaitBusy(bus_id); 96 | } 97 | return 0xff; 98 | } 99 | 100 | bool i2cReadRegisterBuffer(unsigned int dev_id, int reg, u8* buffer, size_t buf_size) { 101 | u8 bus_id = i2cGetDeviceBusId(dev_id); 102 | u8 dev_addr = i2cGetDeviceRegAddr(dev_id); 103 | 104 | size_t j = 0; 105 | while (!i2cSelectDevice(bus_id, dev_addr) 106 | || !i2cSelectRegister(bus_id, reg) 107 | || !i2cSelectDevice(bus_id, dev_addr | 1)) 108 | { 109 | i2cWaitBusy(bus_id); 110 | *i2cGetCntReg(bus_id) = 0xC5; 111 | i2cWaitBusy(bus_id); 112 | if (++j >= 8) 113 | return false; 114 | } 115 | 116 | if (buf_size != 1) { 117 | for (int i = 0; i < buf_size - 1; i++) { 118 | i2cWaitBusy(bus_id); 119 | *i2cGetCntReg(bus_id) = 0xF0; 120 | i2cWaitBusy(bus_id); 121 | buffer[i] = *i2cGetDataReg(bus_id); 122 | } 123 | } 124 | 125 | i2cWaitBusy(bus_id); 126 | *i2cGetCntReg(bus_id) = 0xE1; 127 | i2cWaitBusy(bus_id); 128 | *buffer = *i2cGetDataReg(bus_id); 129 | return true; 130 | } 131 | 132 | bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data) { 133 | u8 bus_id = i2cGetDeviceBusId(dev_id); 134 | u8 dev_addr = i2cGetDeviceRegAddr(dev_id); 135 | 136 | for (int i = 0; i < 8; i++) { 137 | if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) { 138 | i2cWaitBusy(bus_id); 139 | *i2cGetDataReg(bus_id) = data; 140 | *i2cGetCntReg(bus_id) = 0xC1; 141 | i2cStop(bus_id, 0); 142 | if (i2cGetResult(bus_id)) 143 | return true; 144 | } 145 | *i2cGetCntReg(bus_id) = 0xC5; 146 | i2cWaitBusy(bus_id); 147 | } 148 | 149 | return false; 150 | } -------------------------------------------------------------------------------- /source/i2c.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define I2C1_REG_OFF 0x10161000 6 | #define I2C2_REG_OFF 0x10144000 7 | #define I2C3_REG_OFF 0x10148000 8 | 9 | #define I2C_REG_DATA 0 10 | #define I2C_REG_CNT 1 11 | #define I2C_REG_CNTEX 2 12 | #define I2C_REG_SCL 4 13 | 14 | #define I2C_DEV_MCU 3 15 | #define I2C_DEV_GYRO 10 16 | #define I2C_DEV_IR 13 17 | 18 | const u8 i2cGetDeviceBusId(u8 device_id); 19 | const u8 i2cGetDeviceRegAddr(u8 device_id); 20 | 21 | vu8* const i2cGetDataReg(u8 bus_id); 22 | vu8* const i2cGetCntReg(u8 bus_id); 23 | 24 | void i2cWaitBusy(u8 bus_id); 25 | bool i2cGetResult(u8 bus_id); 26 | u8 i2cGetData(u8 bus_id); 27 | void i2cStop(u8 bus_id, u8 arg0); 28 | 29 | bool i2cSelectDevice(u8 bus_id, u8 dev_reg); 30 | bool i2cSelectRegister(u8 bus_id, u8 reg); 31 | 32 | u8 i2cReadRegister(u8 dev_id, u8 reg); 33 | bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data); 34 | 35 | bool i2cReadRegisterBuffer(unsigned int dev_id, int reg, u8* buffer, size_t buf_size); -------------------------------------------------------------------------------- /source/loader-bs/bootstrap.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_start) 4 | 5 | MEMORY 6 | { 7 | ram : ORIGIN = 0x23F00000, LENGTH = 128K 8 | } 9 | 10 | SECTIONS 11 | { 12 | .init : 13 | { 14 | __text_start = . ; 15 | KEEP (*(.init)) 16 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 17 | } >ram = 0xff 18 | .plt : { *(.plt) } >ram = 0xff 19 | 20 | .text : /* ALIGN (4): */ 21 | { 22 | *(.text .stub .text.* .gnu.linkonce.t.*) 23 | KEEP (*(.text.*personality*)) 24 | /* .gnu.warning sections are handled specially by elf32.em. */ 25 | *(.gnu.warning) 26 | *(.glue_7t) *(.glue_7) *(.vfp11_veneer) 27 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 28 | } >ram = 0xff 29 | 30 | .fini : 31 | { 32 | KEEP (*(.fini)) 33 | } >ram =0xff 34 | 35 | __text_end = . ; 36 | 37 | .rodata : 38 | { 39 | *(.rodata) 40 | *all.rodata*(*) 41 | *(.roda) 42 | *(.rodata.*) 43 | *(.gnu.linkonce.r*) 44 | SORT(CONSTRUCTORS) 45 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 46 | } >ram = 0xff 47 | 48 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ram 49 | __exidx_start = .; 50 | .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ram 51 | __exidx_end = .; 52 | 53 | /* Ensure the __preinit_array_start label is properly aligned. We 54 | could instead move the label definition inside the section, but 55 | the linker would then create the section even if it turns out to 56 | be empty, which isn't pretty. */ 57 | . = ALIGN(32 / 8); 58 | PROVIDE (__preinit_array_start = .); 59 | .preinit_array : { KEEP (*(.preinit_array)) } >ram = 0xff 60 | PROVIDE (__preinit_array_end = .); 61 | PROVIDE (__init_array_start = .); 62 | .init_array : { KEEP (*(.init_array)) } >ram = 0xff 63 | PROVIDE (__init_array_end = .); 64 | PROVIDE (__fini_array_start = .); 65 | .fini_array : { KEEP (*(.fini_array)) } >ram = 0xff 66 | PROVIDE (__fini_array_end = .); 67 | 68 | .ctors : 69 | { 70 | /* gcc uses crtbegin.o to find the start of the constructors, so 71 | we make sure it is first. Because this is a wildcard, it 72 | doesn't matter if the user does not actually link against 73 | crtbegin.o; the linker won't look for a file to match a 74 | wildcard. The wildcard also means that it doesn't matter which 75 | directory crtbegin.o is in. */ 76 | KEEP (*crtbegin.o(.ctors)) 77 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 78 | KEEP (*(SORT(.ctors.*))) 79 | KEEP (*(.ctors)) 80 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 81 | } >ram = 0xff 82 | 83 | .dtors : 84 | { 85 | KEEP (*crtbegin.o(.dtors)) 86 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 87 | KEEP (*(SORT(.dtors.*))) 88 | KEEP (*(.dtors)) 89 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 90 | } >ram = 0xff 91 | 92 | .eh_frame : 93 | { 94 | KEEP (*(.eh_frame)) 95 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 96 | } >ram = 0xff 97 | 98 | .gcc_except_table : 99 | { 100 | *(.gcc_except_table) 101 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 102 | } >ram = 0xff 103 | .jcr : { KEEP (*(.jcr)) } >ram = 0 104 | .got : { *(.got.plt) *(.got) } >ram = 0 105 | 106 | .data ALIGN(4) : { 107 | __data_start = ABSOLUTE(.); 108 | *(.data) 109 | *(.data.*) 110 | *(.gnu.linkonce.d*) 111 | CONSTRUCTORS 112 | . = ALIGN(4); 113 | __data_end = ABSOLUTE(.) ; 114 | } >ram = 0xff 115 | 116 | .bss ALIGN(4) : 117 | { 118 | __bss_start = ABSOLUTE(.); 119 | __bss_start__ = ABSOLUTE(.); 120 | *(.dynbss) 121 | *(.gnu.linkonce.b*) 122 | *(.bss*) 123 | *(COMMON) 124 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 125 | __bss_end__ = ABSOLUTE(.); 126 | __end__ = ABSOLUTE(.); 127 | } >ram 128 | 129 | .stack 0x80000 : { _stack = .; *(.stack) } 130 | } 131 | -------------------------------------------------------------------------------- /source/loader-bs/bs-start.s: -------------------------------------------------------------------------------- 1 | #ifdef EXEC_BOOTSTRAP 2 | 3 | .section ".init" 4 | .global _start 5 | .extern main 6 | .align 4 7 | .arm 8 | 9 | #define SIZE_32KB 0b01110 10 | #define SIZE_128KB 0b10000 11 | #define SIZE_512KB 0b10010 12 | #define SIZE_2MB 0b10100 13 | #define SIZE_128MB 0b11010 14 | #define SIZE_256MB 0b11011 15 | #define SIZE_4GB 0b11111 16 | 17 | @ Makes a MPU partition value 18 | #define MAKE_PARTITION(offset, size_enum) \ 19 | (((offset) >> 12 << 12) | ((size_enum) << 1) | 1) 20 | 21 | 22 | _start: 23 | b _init 24 | 25 | @ required, don't move :) 26 | @ will be set to FIRM ARM9 entry point by BRAHMA 27 | arm9ep_backup: .long 0xFFFF0000 28 | 29 | _mpu_partition_table: 30 | .word MAKE_PARTITION(0x00000000, SIZE_4GB) @ 0: Background region 31 | .word MAKE_PARTITION(0x00000000, SIZE_128MB) @ 1: Instruction TCM (mirrored every 32KB) 32 | .word MAKE_PARTITION(0x08000000, SIZE_2MB) @ 2: ARM9 internal memory 33 | .word MAKE_PARTITION(0x10000000, SIZE_128MB) @ 3: IO region 34 | .word MAKE_PARTITION(0x18000000, SIZE_128MB) @ 4: external device memory 35 | .word MAKE_PARTITION(0x1FF80000, SIZE_512KB) @ 5: AXI WRAM 36 | .word MAKE_PARTITION(0x20000000, SIZE_256MB) @ 6: FCRAM 37 | .word 0 @ 7: Unused 38 | 39 | _populate_mpu: 40 | push {r4-r5, lr} 41 | ldr r4, =_mpu_partition_table 42 | 43 | ldr r5, [r4, #0x0] @ mmu_partition_table[0] load 44 | mcr p15, 0, r5, c6, c0, 0 @ mmu_partition_table[0] write 45 | ldr r5, [r4, #0x4] 46 | mcr p15, 0, r5, c6, c1, 0 47 | ldr r5, [r4, #0x8] 48 | mcr p15, 0, r5, c6, c2, 0 49 | ldr r5, [r4, #0xC] 50 | mcr p15, 0, r5, c6, c3, 0 51 | ldr r5, [r4, #0x10] 52 | mcr p15, 0, r5, c6, c4, 0 53 | ldr r5, [r4, #0x14] 54 | mcr p15, 0, r5, c6, c5, 0 55 | ldr r5, [r4, #0x18] 56 | mcr p15, 0, r5, c6, c6, 0 57 | ldr r5, [r4, #0x1C] 58 | mcr p15, 0, r5, c6, c7, 0 59 | 60 | @ Give read/write access to all the memory regions 61 | ldr r5, =0x03333333 62 | mcr p15, 0, r5, c5, c0, 2 @ data access 63 | ldr r5, =0x03300330 64 | mcr p15, 0, r5, c5, c0, 3 @ instruction access 65 | 66 | mov r5, #0x66 67 | mcr p15, 0, r5, c2, c0, 0 @ data cachable 68 | mcr p15, 0, r5, c2, c0, 1 @ instruction cachable 69 | 70 | mov r5, #0x10 71 | mcr p15, 0, r5, c3, c0, 0 @ data bufferable 72 | 73 | pop {r4-r5, pc} 74 | 75 | _enable_caches: 76 | push {r4-r5, lr} 77 | 78 | bl _populate_mpu 79 | mov r5, #0 80 | mcr p15, 0, r5, c7, c5, 0 @ flush I-cache 81 | mcr p15, 0, r5, c7, c6, 0 @ flush D-cache 82 | 83 | mrc p15, 0, r4, c1, c0, 0 84 | orr r4, r4, #(1<<12) @ instruction cache enable 85 | orr r4, r4, #(1<<2) @ data cache enable 86 | orr r4, r4, #(1<<0) @ mpu enable 87 | mcr p15, 0, r4, c1, c0, 0 88 | 89 | pop {r4-r5, pc} 90 | 91 | _init: 92 | push {r0-r12, lr} 93 | 94 | bl _enable_caches 95 | bl main 96 | 97 | mrc p15, 0, r4, c1, c0, 0 98 | bic r4, r4, #(1<<0) @ mpu disable 99 | mcr p15, 0, r4, c1, c0, 0 100 | 101 | pop {r0-r12, lr} 102 | 103 | @ return control to FIRM 104 | ldr pc, arm9ep_backup 105 | 106 | #endif // EXEC_BOOTSTRAP 107 | -------------------------------------------------------------------------------- /source/loader-gw/gateway.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_start) 4 | 5 | MEMORY 6 | { 7 | ram : ORIGIN = 0x08000000, LENGTH = 128K 8 | } 9 | 10 | SECTIONS 11 | { 12 | .init : 13 | { 14 | __text_start = . ; 15 | KEEP (*(.init)) 16 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 17 | } >ram = 0xff 18 | .plt : { *(.plt) } >ram = 0xff 19 | 20 | .text : /* ALIGN (4): */ 21 | { 22 | *(.text .stub .text.* .gnu.linkonce.t.*) 23 | KEEP (*(.text.*personality*)) 24 | /* .gnu.warning sections are handled specially by elf32.em. */ 25 | *(.gnu.warning) 26 | *(.glue_7t) *(.glue_7) *(.vfp11_veneer) 27 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 28 | } >ram = 0xff 29 | 30 | .fini : 31 | { 32 | KEEP (*(.fini)) 33 | } >ram =0xff 34 | 35 | __text_end = . ; 36 | 37 | .rodata : 38 | { 39 | *(.rodata) 40 | *all.rodata*(*) 41 | *(.roda) 42 | *(.rodata.*) 43 | *(.gnu.linkonce.r*) 44 | SORT(CONSTRUCTORS) 45 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 46 | } >ram = 0xff 47 | 48 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ram 49 | __exidx_start = .; 50 | .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ram 51 | __exidx_end = .; 52 | 53 | /* Ensure the __preinit_array_start label is properly aligned. We 54 | could instead move the label definition inside the section, but 55 | the linker would then create the section even if it turns out to 56 | be empty, which isn't pretty. */ 57 | . = ALIGN(32 / 8); 58 | PROVIDE (__preinit_array_start = .); 59 | .preinit_array : { KEEP (*(.preinit_array)) } >ram = 0xff 60 | PROVIDE (__preinit_array_end = .); 61 | PROVIDE (__init_array_start = .); 62 | .init_array : { KEEP (*(.init_array)) } >ram = 0xff 63 | PROVIDE (__init_array_end = .); 64 | PROVIDE (__fini_array_start = .); 65 | .fini_array : { KEEP (*(.fini_array)) } >ram = 0xff 66 | PROVIDE (__fini_array_end = .); 67 | 68 | .ctors : 69 | { 70 | /* gcc uses crtbegin.o to find the start of the constructors, so 71 | we make sure it is first. Because this is a wildcard, it 72 | doesn't matter if the user does not actually link against 73 | crtbegin.o; the linker won't look for a file to match a 74 | wildcard. The wildcard also means that it doesn't matter which 75 | directory crtbegin.o is in. */ 76 | KEEP (*crtbegin.o(.ctors)) 77 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 78 | KEEP (*(SORT(.ctors.*))) 79 | KEEP (*(.ctors)) 80 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 81 | } >ram = 0xff 82 | 83 | .dtors : 84 | { 85 | KEEP (*crtbegin.o(.dtors)) 86 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 87 | KEEP (*(SORT(.dtors.*))) 88 | KEEP (*(.dtors)) 89 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 90 | } >ram = 0xff 91 | 92 | .eh_frame : 93 | { 94 | KEEP (*(.eh_frame)) 95 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 96 | } >ram = 0xff 97 | 98 | .gcc_except_table : 99 | { 100 | *(.gcc_except_table) 101 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 102 | } >ram = 0xff 103 | .jcr : { KEEP (*(.jcr)) } >ram = 0 104 | .got : { *(.got.plt) *(.got) } >ram = 0 105 | 106 | .data ALIGN(4) : { 107 | __data_start = ABSOLUTE(.); 108 | *(.data) 109 | *(.data.*) 110 | *(.gnu.linkonce.d*) 111 | CONSTRUCTORS 112 | . = ALIGN(4); 113 | __data_end = ABSOLUTE(.) ; 114 | } >ram = 0xff 115 | 116 | .bss ALIGN(4) : 117 | { 118 | __bss_start = ABSOLUTE(.); 119 | __bss_start__ = ABSOLUTE(.); 120 | *(.dynbss) 121 | *(.gnu.linkonce.b*) 122 | *(.bss*) 123 | *(COMMON) 124 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 125 | __bss_end__ = ABSOLUTE(.); 126 | __end__ = ABSOLUTE(.); 127 | } >ram 128 | 129 | .stack 0x80000 : { _stack = .; *(.stack) } 130 | } 131 | -------------------------------------------------------------------------------- /source/loader-gw/gw-start.s: -------------------------------------------------------------------------------- 1 | #ifdef EXEC_GATEWAY 2 | 3 | .section ".init" 4 | 5 | .global _start 6 | .extern main 7 | 8 | .align 4 9 | .arm 10 | 11 | _vectors: 12 | ldr pc, =InfiniteLoop 13 | .pool 14 | ldr pc, =InfiniteLoop 15 | .pool 16 | ldr pc, =InfiniteLoop 17 | .pool 18 | ldr pc, =InfiniteLoop 19 | .pool 20 | ldr pc, =InfiniteLoop 21 | .pool 22 | ldr pc, =InfiniteLoop 23 | .pool 24 | 25 | _start: 26 | ldr sp,=0x22140000 27 | 28 | @@wait for the arm11 kernel threads to be ready 29 | ldr r1, =0x10000 30 | waitLoop9: 31 | sub r1, #1 32 | 33 | cmp r1, #0 34 | bgt waitLoop9 35 | 36 | ldr r1, =0x10000 37 | waitLoop92: 38 | sub r1, #1 39 | 40 | cmp r1, #0 41 | bgt waitLoop92 42 | 43 | ldr sp,=0x22160000 44 | ldr r3, =main 45 | blx r3 46 | .pool 47 | 48 | InfiniteLoop: 49 | b InfiniteLoop 50 | 51 | #endif // EXEC_GATEWAY 52 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "draw.h" 3 | #include "fs.h" 4 | #include "hid.h" 5 | #include "menu.h" 6 | #include "i2c.h" 7 | #include "decryptor/game.h" 8 | #include "decryptor/nand.h" 9 | #include "decryptor/nandfat.h" 10 | #include "decryptor/titlekey.h" 11 | 12 | #define SUBMENU_START 5 13 | 14 | 15 | MenuInfo menu[] = 16 | { 17 | { 18 | "XORpad Generator Options", 4, 19 | { 20 | { "NCCH Padgen", &NcchPadgen, false, false, 0 }, 21 | { "SD Padgen", &SdPadgen, false, false, 0 }, 22 | { "CTRNAND Padgen", &CtrNandPadgen, false, false, 0 }, 23 | { "TWLNAND Padgen", &TwlNandPadgen, false, false, 0 } 24 | } 25 | }, 26 | { 27 | "Titlekey Decrypt Options", 3, 28 | { 29 | { "Titlekey Decrypt (file)", &DecryptTitlekeysFile, false, false, 0 }, 30 | { "Titlekey Decrypt (SysNAND)", &DecryptTitlekeysNand, false, false, 0 }, 31 | { "Titlekey Decrypt (EmuNAND)", &DecryptTitlekeysNand, false, true, 0 } 32 | } 33 | }, 34 | { 35 | "SysNAND Options", 6, 36 | { 37 | { "NAND Backup", &DumpNand, false, false, 0 }, 38 | { "NAND Restore", &RestoreNand, true, false, 0 }, 39 | { "Partition Dump...", NULL, false, false, SUBMENU_START + 0 }, 40 | { "Partition Inject...", NULL, true, false, SUBMENU_START + 2 }, 41 | { "File Dump...", NULL, false, false, SUBMENU_START + 4 }, 42 | { "File Inject...", NULL, true, false, SUBMENU_START + 6 } 43 | } 44 | }, 45 | { 46 | "EmuNAND Options", 7, 47 | { 48 | { "EmuNAND Backup", &DumpNand, false, true, 0 }, 49 | { "EmuNAND Restore", &RestoreNand, true, true, 0 }, 50 | { "Partition Dump...", NULL, false, true, SUBMENU_START + 1 }, 51 | { "Partition Inject...", NULL, true, true, SUBMENU_START + 3 }, 52 | { "File Dump...", NULL, false, true, SUBMENU_START + 5 }, 53 | { "File Inject...", NULL, true, true, SUBMENU_START + 7 }, 54 | { "Update SeedDB", &UpdateSeedDb, false, true, 0 } 55 | } 56 | }, 57 | { 58 | "Game Decryptor Options", 6, 59 | { 60 | { "NCCH/NCSD Decryptor", &CryptGameFiles, false, false, GC_NCCH_PROCESS }, 61 | { "NCCH/NCSD Encryptor", &CryptGameFiles, false, false, GC_NCCH_PROCESS | GC_NCCH_ENCRYPT }, 62 | { "CIA Decryptor (shallow)", &CryptGameFiles, false, false, GC_CIA_PROCESS }, 63 | { "CIA Decryptor (deep)", &CryptGameFiles, false, false, GC_CIA_PROCESS | GC_CIA_DEEP }, 64 | { "CIA Decryptor (CXI only)", &CryptGameFiles, false, false, GC_CIA_PROCESS | GC_CIA_DEEP | GC_CXI_ONLY }, 65 | { "SD Decryptor/Encryptor", &CryptSdFiles, false, false, 0 } 66 | } 67 | }, 68 | // everything below is not contained in the main menu 69 | { 70 | "Partition Dump... (SysNAND)", 6, // ID 0 71 | { 72 | { "Dump TWLN Partition", &DecryptNandPartitions, false, false, P_TWLN }, 73 | { "Dump TWLP Partition", &DecryptNandPartitions, false, false, P_TWLP }, 74 | { "Dump AGBSAVE Partition", &DecryptNandPartitions, false, false, P_AGBSAVE }, 75 | { "Dump FIRM0 Partition", &DecryptNandPartitions, false, false, P_FIRM0 }, 76 | { "Dump FIRM1 Partition", &DecryptNandPartitions, false, false, P_FIRM1 }, 77 | { "Dump CTRNAND Partition", &DecryptNandPartitions, false, false, P_CTRNAND } 78 | } 79 | }, 80 | { 81 | "Partition Dump...(EmuNAND)", 6, // ID 1 82 | { 83 | { "Dump TWLN Partition", &DecryptNandPartitions, false, true, P_TWLN }, 84 | { "Dump TWLP Partition", &DecryptNandPartitions, false, true, P_TWLP }, 85 | { "Dump AGBSAVE Partition", &DecryptNandPartitions, false, true, P_AGBSAVE }, 86 | { "Dump FIRM0 Partition", &DecryptNandPartitions, false, true, P_FIRM0 }, 87 | { "Dump FIRM1 Partition", &DecryptNandPartitions, false, true, P_FIRM1 }, 88 | { "Dump CTRNAND Partition", &DecryptNandPartitions, false, true, P_CTRNAND } 89 | } 90 | }, 91 | { 92 | "Partition Inject... (SysNAND)", 6, // ID 2 93 | { 94 | { "Inject TWLN Partition", &InjectNandPartitions, true, false, P_TWLN }, 95 | { "Inject TWLP Partition", &InjectNandPartitions, true, false, P_TWLP }, 96 | { "Inject AGBSAVE Partition", &InjectNandPartitions, true, false, P_AGBSAVE }, 97 | { "Inject FIRM0 Partition", &InjectNandPartitions, true, false, P_FIRM0 }, 98 | { "Inject FIRM1 Partition", &InjectNandPartitions, true, false, P_FIRM1 }, 99 | { "Inject CTRNAND Partition", &InjectNandPartitions, true, false, P_CTRNAND } 100 | } 101 | }, 102 | { 103 | "Partition Inject... (EmuNAND)", 6, // ID 3 104 | { 105 | { "Inject TWLN Partition", &InjectNandPartitions, true, true, P_TWLN }, 106 | { "Inject TWLP Partition", &InjectNandPartitions, true, true, P_TWLP }, 107 | { "Inject AGBSAVE Partition", &InjectNandPartitions, true, true, P_AGBSAVE }, 108 | { "Inject FIRM0 Partition", &InjectNandPartitions, true, true, P_FIRM0 }, 109 | { "Inject FIRM1 Partition", &InjectNandPartitions, true, true, P_FIRM1 }, 110 | { "Inject CTRNAND Partition", &InjectNandPartitions, true, true, P_CTRNAND } 111 | } 112 | }, 113 | { 114 | "File Dump... (SysNAND)", 8, // ID 4 115 | { 116 | { "Dump ticket.db", &DumpFile, false, false, F_TICKET }, 117 | { "Dump title.db", &DumpFile, false, false, F_TITLE }, 118 | { "Dump import.db", &DumpFile, false, false, F_IMPORT }, 119 | { "Dump certs.db", &DumpFile, false, false, F_CERTS }, 120 | { "Dump SecureInfo_A", &DumpFile, false, false, F_SECUREINFO }, 121 | { "Dump LocalFriendCodeSeed_B", &DumpFile, false, false, F_LOCALFRIEND }, 122 | { "Dump rand_seed", &DumpFile, false, false, F_RANDSEED }, 123 | { "Dump movable.sed", &DumpFile, false, false, F_MOVABLE } 124 | } 125 | }, 126 | { 127 | "File Dump... (EmuNAND)", 9, // ID 5 128 | { 129 | { "Dump ticket.db", &DumpFile, false, true, F_TICKET }, 130 | { "Dump title.db", &DumpFile, false, true, F_TITLE }, 131 | { "Dump import.db", &DumpFile, false, true, F_IMPORT }, 132 | { "Dump certs.db", &DumpFile, false, true, F_CERTS }, 133 | { "Dump SecureInfo_A", &DumpFile, false, true, F_SECUREINFO }, 134 | { "Dump LocalFriendCodeSeed_B", &DumpFile, false, true, F_LOCALFRIEND }, 135 | { "Dump rand_seed", &DumpFile, false, true, F_RANDSEED }, 136 | { "Dump movable.sed", &DumpFile, false, true, F_MOVABLE }, 137 | { "Dump seedsave.bin", &DumpFile, false, true, F_SEEDSAVE } 138 | } 139 | }, 140 | { 141 | "File Inject... (SysNAND)", 8, // ID 6 142 | { 143 | { "Inject ticket.db", &InjectFile, true, false, F_TICKET }, 144 | { "Inject title.db", &InjectFile, true, false, F_TITLE }, 145 | { "Inject import.db", &InjectFile, true, false, F_IMPORT }, 146 | { "Inject certs.db", &InjectFile, true, false, F_CERTS }, 147 | { "Inject SecureInfo_A", &InjectFile, true, false, F_SECUREINFO }, 148 | { "Inject LocalFriendCodeSeed_B", &InjectFile, true, false, F_LOCALFRIEND }, 149 | { "Inject rand_seed", &InjectFile, true, false, F_RANDSEED }, 150 | { "Inject movable.sed", &InjectFile, true, false, F_MOVABLE } 151 | } 152 | }, 153 | { 154 | "File Inject... (EmuNAND)", 9, // ID 7 155 | { 156 | { "Inject ticket.db", &InjectFile, true, true, F_TICKET }, 157 | { "Inject title.db", &InjectFile, true, true, F_TITLE }, 158 | { "Inject import.db", &InjectFile, true, true, F_IMPORT }, 159 | { "Inject certs.db", &InjectFile, true, true, F_CERTS }, 160 | { "Inject SecureInfo_A", &InjectFile, true, true, F_SECUREINFO }, 161 | { "Inject LocalFriendCodeSeed_B", &InjectFile, true, true, F_LOCALFRIEND }, 162 | { "Inject rand_seed", &InjectFile, true, true, F_RANDSEED }, 163 | { "Inject movable.sed", &InjectFile, true, true, F_MOVABLE }, 164 | { "Inject seedsave.bin", &InjectFile, true, true, F_SEEDSAVE } 165 | } 166 | }, 167 | { 168 | NULL, 0, {}, // empty menu to signal end 169 | } 170 | }; 171 | 172 | 173 | void Reboot() 174 | { 175 | i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); 176 | while(true); 177 | } 178 | 179 | int main() 180 | { 181 | ClearScreenFull(true, true); 182 | InitFS(); 183 | 184 | ProcessMenu(menu, SUBMENU_START); 185 | 186 | DeinitFS(); 187 | Reboot(); 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /source/menu.c: -------------------------------------------------------------------------------- 1 | #include "menu.h" 2 | #include "draw.h" 3 | #include "hid.h" 4 | #include "fs.h" 5 | #include "decryptor/nand.h" 6 | 7 | 8 | u32 UnmountSd() 9 | { 10 | u32 pad_state; 11 | 12 | DebugClear(); 13 | Debug("Unmounting SD card..."); 14 | DeinitFS(); 15 | Debug("SD is unmounted, you may remove it now."); 16 | Debug("Put the SD card back in before pressing B!"); 17 | Debug(""); 18 | Debug("(B to return, START to reboot)"); 19 | while (true) { 20 | pad_state = InputWait(); 21 | if (((pad_state & BUTTON_B) && InitFS()) || (pad_state & BUTTON_START)) 22 | break; 23 | } 24 | 25 | return pad_state; 26 | } 27 | 28 | void DrawMenu(MenuInfo* currMenu, u32 index, bool fullDraw, bool subMenu) 29 | { 30 | bool top_screen = true; 31 | u32 menublock_x0 = (top_screen) ? 76 : 36; 32 | u32 menublock_x1 = (top_screen) ? 50 : 10; 33 | u32 menublock_y0 = 40; 34 | u32 menublock_y1 = menublock_y0 + currMenu->n_entries * 10; 35 | 36 | if (fullDraw) { // draw full menu 37 | ClearScreenFull(true, !top_screen); 38 | DrawStringF(menublock_x0, menublock_y0 - 20, top_screen, "%s", currMenu->name); 39 | DrawStringF(menublock_x0, menublock_y0 - 10, top_screen, "=============================="); 40 | DrawStringF(menublock_x0, menublock_y1 + 0, top_screen, "=============================="); 41 | DrawStringF(menublock_x0, menublock_y1 + 10, top_screen, (subMenu) ? "A: Choose B: Return" : "A: Choose"); 42 | DrawStringF(menublock_x0, menublock_y1 + 20, top_screen, "SELECT: Unmount SD"); 43 | DrawStringF(menublock_x0, menublock_y1 + 30, top_screen, "START: Reboot"); 44 | DrawStringF(menublock_x1, SCREEN_HEIGHT - 20, top_screen, "Remaining SD storage space: %llu MiB", RemainingStorageSpace() / 1024 / 1024); 45 | DrawStringF(menublock_x1, SCREEN_HEIGHT - 30, top_screen, "Game directory: %s", GAME_DIR); 46 | if (DirOpen(WORK_DIR)) { 47 | DrawStringF(menublock_x1, SCREEN_HEIGHT - 40, top_screen, "Work directory: %s", WORK_DIR); 48 | DirClose(); 49 | } 50 | } 51 | 52 | if (!top_screen) 53 | DrawStringF(10, 10, true, "Selected: %-*.*s", 32, 32, currMenu->entries[index].name); 54 | 55 | for (u32 i = 0; i < currMenu->n_entries; i++) { // draw menu entries / selection [] 56 | char* name = currMenu->entries[i].name; 57 | DrawStringF(menublock_x0, menublock_y0 + (i*10), top_screen, (i == index) ? "[%s]" : " %s ", name); 58 | } 59 | } 60 | 61 | u32 ProcessEntry(MenuEntry* entry) 62 | { 63 | u32 pad_state; 64 | u32 res = 0; 65 | 66 | // unlock sequence for dangerous features 67 | if (entry->dangerous) { 68 | u32 unlockSequenceEmu[] = { BUTTON_LEFT, BUTTON_RIGHT, BUTTON_DOWN, BUTTON_UP, BUTTON_A }; 69 | u32 unlockSequenceSys[] = { BUTTON_LEFT, BUTTON_UP, BUTTON_RIGHT, BUTTON_UP, BUTTON_A }; 70 | u32 unlockLvlMax = 5; 71 | u32* unlockSequence = (entry->emunand) ? unlockSequenceEmu : unlockSequenceSys; 72 | u32 unlockLvl = 0; 73 | DebugClear(); 74 | Debug("You selected \"%s\".", entry->name); 75 | Debug("This feature writes to the %s.", (entry->emunand) ? "EmuNAND" : "SysNAND"); 76 | Debug("Doing this is potentially dangerous!"); 77 | Debug(""); 78 | Debug("If you wish to proceed, enter:"); 79 | Debug((entry->emunand) ? ", , , , " : ", , , , "); 80 | Debug(""); 81 | Debug("(B to return, START to reboot)"); 82 | while (true) { 83 | ShowProgress(unlockLvl, unlockLvlMax); 84 | if (unlockLvl == unlockLvlMax) 85 | break; 86 | pad_state = InputWait(); 87 | if (!(pad_state & BUTTON_ANY)) 88 | continue; 89 | else if (pad_state & unlockSequence[unlockLvl]) 90 | unlockLvl++; 91 | else if (pad_state & (BUTTON_B | BUTTON_START)) 92 | break; 93 | else if (unlockLvl == 0 || !(pad_state & unlockSequence[unlockLvl-1])) 94 | unlockLvl = 0; 95 | } 96 | ShowProgress(0, 0); 97 | if (unlockLvl < unlockLvlMax) 98 | return pad_state; 99 | } 100 | 101 | // execute this entries function 102 | DebugClear(); 103 | res = (SetNand(entry->emunand) == 0) ? (*(entry->function))(entry->param) : 1; 104 | Debug("%s: %s!", entry->name, (res == 0) ? "succeeded" : "failed"); 105 | Debug(""); 106 | Debug("Press B to return, START to reboot."); 107 | while(!(pad_state = InputWait() & (BUTTON_B | BUTTON_START))); 108 | 109 | // returns the last known pad_state 110 | return pad_state; 111 | } 112 | 113 | u32 ProcessMenu(MenuInfo* info, u32 n_entries_main) 114 | { 115 | MenuInfo mainMenu; 116 | MenuInfo* currMenu = &mainMenu; 117 | MenuInfo* prevMenu[MENU_MAX_DEPTH]; 118 | u32 prevIndex[MENU_MAX_DEPTH]; 119 | u32 index = 0; 120 | u32 menuLvl = 0; 121 | 122 | // build main menu structure from submenus 123 | memset(&mainMenu, 0x00, sizeof(MenuInfo)); 124 | for (u32 i = 0; i < n_entries_main && i < MENU_MAX_ENTRIES; i++) { 125 | mainMenu.entries[i].name = info[i].name; 126 | mainMenu.entries[i].function = NULL; 127 | mainMenu.entries[i].param = i; 128 | mainMenu.entries[i].dangerous = 0; 129 | mainMenu.entries[i].emunand = 0; 130 | } 131 | #ifndef BUILD_NAME 132 | mainMenu.name = "Decrypt9 Main Menu"; 133 | #else 134 | mainMenu.name = BUILD_NAME; 135 | #endif 136 | mainMenu.n_entries = (n_entries_main > MENU_MAX_ENTRIES) ? MENU_MAX_ENTRIES : n_entries_main; 137 | DrawMenu(&mainMenu, 0, true, false); 138 | 139 | // main processing loop 140 | while (true) { 141 | bool full_draw = true; 142 | u32 pad_state = InputWait(); 143 | if ((pad_state & BUTTON_A) && (currMenu->entries[index].function == NULL)) { 144 | if (menuLvl < MENU_MAX_DEPTH) { 145 | prevMenu[menuLvl] = currMenu; 146 | prevIndex[menuLvl] = index; 147 | menuLvl++; 148 | } 149 | currMenu = info + currMenu->entries[index].param; 150 | index = 0; 151 | } else if (pad_state & BUTTON_A) { 152 | pad_state = ProcessEntry(currMenu->entries + index); 153 | } else if ((pad_state & BUTTON_B) && (menuLvl > 0)) { 154 | menuLvl--; 155 | currMenu = prevMenu[menuLvl]; 156 | index = prevIndex[menuLvl]; 157 | } else if (pad_state & BUTTON_DOWN) { 158 | index = (index == currMenu->n_entries - 1) ? 0 : index + 1; 159 | full_draw = false; 160 | } else if (pad_state & BUTTON_UP) { 161 | index = (index == 0) ? currMenu->n_entries - 1 : index - 1; 162 | full_draw = false; 163 | } else if ((pad_state & BUTTON_R1) && (menuLvl == 1)) { 164 | if (++currMenu - info >= n_entries_main) currMenu = info; 165 | index = 0; 166 | } else if ((pad_state & BUTTON_L1) && (menuLvl == 1)) { 167 | if (--currMenu < info) currMenu = info + n_entries_main - 1; 168 | index = 0; 169 | } else if (pad_state & BUTTON_SELECT) { 170 | pad_state = UnmountSd(); 171 | } else if (pad_state & BUTTON_X) { 172 | Screenshot(NULL); 173 | } else { 174 | full_draw = false; 175 | } 176 | if (pad_state & BUTTON_START) { 177 | break; 178 | } 179 | DrawMenu(currMenu, index, full_draw, menuLvl > 0); 180 | } 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /source/menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define MENU_MAX_ENTRIES 12 6 | #define MENU_MAX_DEPTH 4 7 | typedef struct { 8 | char* name; 9 | u32 (*function)(u32 param); 10 | bool dangerous; 11 | bool emunand; 12 | u32 param; 13 | } MenuEntry; 14 | 15 | typedef struct { 16 | char* name; 17 | u32 n_entries; 18 | MenuEntry entries[MENU_MAX_ENTRIES]; 19 | } MenuInfo; 20 | 21 | u32 ProcessMenu(MenuInfo* info, u32 n_entries_main); 22 | -------------------------------------------------------------------------------- /source/platform.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "platform.h" 3 | 4 | #define CONFIG_PLATFORM_REG ((volatile u32*)0x10140FFC) 5 | 6 | Platform GetUnitPlatform() 7 | { 8 | switch (*CONFIG_PLATFORM_REG) { 9 | case 7: 10 | return PLATFORM_N3DS; 11 | case 1: 12 | default: 13 | return PLATFORM_3DS; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /source/platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum { 4 | PLATFORM_3DS, 5 | PLATFORM_N3DS, 6 | } Platform; 7 | 8 | Platform GetUnitPlatform(); 9 | --------------------------------------------------------------------------------