├── icon.png ├── audio.wav ├── banner.png ├── source ├── data │ └── builtin_rootca.bin ├── config.h ├── types.h ├── lib.h ├── config.cpp ├── menu.h ├── svchax │ ├── svchax.h │ └── svchax.c ├── utils.h ├── utf8proc │ ├── LICENSE.md │ ├── utf8proc.c │ └── utf8proc.h ├── fts_fuzzy_match.h ├── cia.h ├── menu.cpp ├── json │ └── json-forwards.h ├── font.h ├── data.h ├── cia.c ├── utils.cpp └── main.cpp ├── .gitmodules ├── Makefile └── README.md /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llakssz/CIAngel/HEAD/icon.png -------------------------------------------------------------------------------- /audio.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llakssz/CIAngel/HEAD/audio.wav -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llakssz/CIAngel/HEAD/banner.png -------------------------------------------------------------------------------- /source/data/builtin_rootca.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llakssz/CIAngel/HEAD/source/data/builtin_rootca.bin -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "buildtools"] 2 | path = buildtools 3 | url = https://github.com/Steveice10/buildtools.git 4 | -------------------------------------------------------------------------------- /source/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "json/json.h" 4 | 5 | class CConfig { 6 | public: 7 | CConfig(); 8 | void LoadConfig(std::string sConfigPath); 9 | bool SaveConfig(); 10 | 11 | enum Mode { 12 | DOWNLOAD_CIA, 13 | INSTALL_CIA, 14 | INSTALL_TICKET 15 | }; 16 | 17 | Mode GetMode(); 18 | void SetMode(Mode mode); 19 | std::string GetRegionFilter(); 20 | void SetRegionFilter(std::string region); 21 | 22 | private: 23 | std::string m_sConfigPath; 24 | 25 | // Config options 26 | Mode m_eMode; 27 | std::string m_sRegionFilter; 28 | 29 | Json::Value m_Json; 30 | }; 31 | -------------------------------------------------------------------------------- /source/types.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 3DSGuy 3 | 4 | This file is part of make_cdn_cia. 5 | 6 | make_cdn_cia is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | make_cdn_cia is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with make_cdn_cia. If not, see . 18 | **/ 19 | #include 20 | #include 21 | //Bools 22 | typedef enum 23 | { 24 | False, 25 | True 26 | } _boolean; 27 | 28 | typedef enum 29 | { 30 | Good, 31 | Fail 32 | } return_basic; 33 | 34 | typedef enum 35 | { 36 | ARGC_FAIL = 1, 37 | ARGV_FAIL, 38 | IO_FAIL, 39 | FILE_PROCESS_FAIL 40 | } errors; 41 | 42 | typedef enum 43 | { 44 | BE = 0, 45 | LE = 1 46 | } endianness_flag; 47 | -------------------------------------------------------------------------------- /source/lib.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 3DSGuy 3 | 4 | This file is part of make_cdn_cia. 5 | 6 | make_cdn_cia is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | make_cdn_cia is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with make_cdn_cia. If not, see . 18 | **/ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef _WIN32 29 | #include 30 | #include 31 | #include 32 | #include 33 | #else 34 | #include 35 | #include 36 | #endif 37 | 38 | #include "types.h" 39 | #include "utils.h" 40 | 41 | 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # TARGET # 2 | 3 | TARGET := 3DS 4 | LIBRARY := 0 5 | 6 | ifeq ($(TARGET),3DS) 7 | ifeq ($(strip $(DEVKITPRO)),) 8 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPro") 9 | endif 10 | 11 | ifeq ($(strip $(DEVKITARM)),) 12 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 13 | endif 14 | endif 15 | 16 | # COMMON CONFIGURATION # 17 | 18 | NAME := CIAngel 19 | 20 | BUILD_DIR := build 21 | OUTPUT_DIR := output 22 | INCLUDE_DIRS := include 23 | SOURCE_DIRS := source 24 | 25 | BUILD_FILTER := source/svchax/test/test.c 26 | 27 | EXTRA_OUTPUT_FILES := 28 | 29 | LIBRARY_DIRS := $(DEVKITPRO)/libctru 30 | LIBRARIES := ctru m 31 | 32 | BUILD_FLAGS := -DVERSION_STRING="\"`git describe --tags --abbrev=0`\"" -DREVISION_STRING="\"`git rev-parse --short HEAD`\"" 33 | RUN_FLAGS := 34 | 35 | # 3DS CONFIGURATION # 36 | 37 | TITLE := $(NAME) 38 | DESCRIPTION := CIA Downloader using Nintendo CDN 39 | AUTHOR := _____ 40 | PRODUCT_CODE := CTR-H-ANGEL 41 | UNIQUE_ID := 0xA617 42 | 43 | SYSTEM_MODE := 64MB 44 | SYSTEM_MODE_EXT := Legacy 45 | 46 | ICON_FLAGS := 47 | 48 | ROMFS_DIR := romfs 49 | BANNER_AUDIO := audio.wav 50 | BANNER_IMAGE := banner.png 51 | ICON := icon.png 52 | 53 | # INTERNAL # 54 | 55 | include buildtools/make_base 56 | -------------------------------------------------------------------------------- /source/config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | #include "json/json.h" 5 | 6 | using namespace std; 7 | 8 | CConfig::CConfig() 9 | { 10 | // Empty 11 | } 12 | 13 | void CConfig::LoadConfig(string sConfigPath) 14 | { 15 | // Store the config path so we can save it later 16 | m_sConfigPath = sConfigPath; 17 | 18 | std::ifstream ifs(m_sConfigPath); 19 | if (ifs.is_open()) { 20 | ifs >> m_Json; 21 | } 22 | 23 | // Mode defaults to Install CIA 24 | m_eMode = (Mode)m_Json.get("Mode", (int)Mode::INSTALL_CIA).asInt(); 25 | m_sRegionFilter = m_Json.get("RegionFilter", "off").asString(); 26 | } 27 | 28 | bool CConfig::SaveConfig() 29 | { 30 | // Update the config 31 | m_Json["Mode"] = (int)m_eMode; 32 | m_Json["RegionFilter"] = m_sRegionFilter; 33 | 34 | // Write to file 35 | std::ofstream ofs(m_sConfigPath, std::ofstream::trunc); 36 | if (ofs.is_open()) 37 | { 38 | ofs << m_Json; 39 | return true; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | CConfig::Mode CConfig::GetMode() 46 | { 47 | return m_eMode; 48 | } 49 | 50 | void CConfig::SetMode(CConfig::Mode mode) 51 | { 52 | m_eMode = mode; 53 | SaveConfig(); 54 | } 55 | 56 | std::string CConfig::GetRegionFilter() 57 | { 58 | return m_sRegionFilter; 59 | } 60 | 61 | void CConfig::SetRegionFilter(std::string region) 62 | { 63 | m_sRegionFilter = region; 64 | SaveConfig(); 65 | } -------------------------------------------------------------------------------- /source/menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef __MENU_H_INCLUDED__ 4 | #define __MENU_H_INCLUDED__ 5 | #include <3ds.h> 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | 14 | #define MAX_SELECTED_OPTIONS 0x10 15 | 16 | #define COLOR_TITLE 0x0000FF 17 | #define COLOR_NEUTRAL 0xFFFFFF 18 | #define COLOR_SELECTED 0xFF0000 19 | #define COLOR_BACKGROUND 0x000000 20 | 21 | #define CONSOLE_REVERSE CONSOLE_ESC(7m) 22 | 23 | typedef struct ConsoleMenu { 24 | PrintConsole menuConsole; 25 | } ConsoleMenu; 26 | 27 | typedef struct { 28 | int score; 29 | int index; 30 | std::string titleid; 31 | std::string titlekey; 32 | std::string name; 33 | std::string region; 34 | std::string code; 35 | } game_item; 36 | 37 | extern ConsoleMenu currentMenu; 38 | void init_menu(gfxScreen_t screen); 39 | void menu_draw_string(const char* str, int pos_x, int pos_y, const char* color); 40 | void menu_draw_string_full(const char* str, int pos_y, const char* color); 41 | void titles_multkey_draw(const char *title, const char* footer, int back, std::vector *options, void* data, 42 | bool (*callback)(int result, u32 key, void* data)); 43 | void menu_multkey_draw(const char *title, const char* footer, int back, int count, const char *options[], void* data, 44 | bool (*callback)(int result, u32 key, void* data)); 45 | 46 | #endif // __MENU_H_INCLUDED__ 47 | -------------------------------------------------------------------------------- /source/svchax/svchax.h: -------------------------------------------------------------------------------- 1 | #ifndef __SVCHAX_H__ 2 | #define _SVCHAX_H__ 3 | 4 | /* 5 | * for 3DSX builds, svchax_init expects that: 6 | * 7 | * - gfxInit was already called. 8 | * - new 3DS higher clockrate and L2 cache are disabled. 9 | * - there is at least 64 KBytes (16 pages) of unallocated linear memory. 10 | * ( the current 3dsx loaders and ctrulib's default allocator will keep 1MB 11 | * of unallocated linear memory, so this is only relevant when using 12 | * a custom allocator) 13 | * 14 | * 15 | * svchax_init will grant full svc access to the calling thread and process 16 | * up to system version 10.7 (kernel version 2.50-11), by using: 17 | * - memchunkhax1 for kernel version <= 2.46-0 18 | * - memchunkhax2 for 2.46-0 < kernel version <= 2.50-11 19 | * 20 | * access to privileged services can also be obtained by calling 21 | * svchax_init with patch_srv set to true. 22 | * 23 | * __ctr_svchax and __ctr_svchax_srv will reflect the current 24 | * status of the privileged access for svc calls and services respectively. 25 | * 26 | * svchax assumes that CIA builds already have access to svcBackdoor 27 | * and will skip running memchunkhax there. 28 | * 29 | */ 30 | 31 | #include <3ds/types.h> 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | Result svchax_init(bool patch_srv); 38 | 39 | extern u32 __ctr_svchax; 40 | extern u32 __ctr_svchax_srv; 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | 47 | #endif //_SVCHAX_H__ 48 | -------------------------------------------------------------------------------- /source/utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 3DSGuy 3 | 4 | This file is part of make_cdn_cia. 5 | 6 | make_cdn_cia is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | make_cdn_cia is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with make_cdn_cia. If not, see . 18 | **/ 19 | #include <3ds.h> 20 | 21 | #define NUS_URL "http://ccs.cdn.c.shop.nintendowifi.net/ccs/download/" 22 | #define SEED_URL "https://kagiya-ctr.cdn.nintendo.net/title/" 23 | #define JSON_URL_DEFAULT "http://3ds.titlekeys.gq/json_enc" 24 | #define JSON_URL_FILE "/CIAngel/source.txt" 25 | 26 | // Check for updates every 3 days automatically? 27 | #define JSON_UPDATE_INTERVAL_IN_SECONDS (60 * 60 * 24 * 3) 28 | 29 | #define JSON_TYPE_WINGS 1 30 | #define JSON_TYPE_ONLINE 2 31 | 32 | // C++ only code 33 | #ifdef __cplusplus 34 | #include 35 | #include 36 | std::string GetSerialType(std::string sSerial); 37 | std::string upperCase(std::string input); 38 | std::string getJsonUrl(); 39 | #endif 40 | 41 | //MISC 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | void char_to_int_array(unsigned char destination[], char source[], int size, u32 endianness, int base); 46 | void endian_memcpy(u8 *destination, u8 *source, u32 size, u32 endianness); 47 | void u8_hex_print_be(u8 *array, int len); 48 | void u8_hex_print_le(u8 *array, int len); 49 | u32 align_value(u32 value, u32 alignment); 50 | void resolve_flag(unsigned char flag, unsigned char *flag_bool); 51 | void resolve_flag_u16(u16 flag, unsigned char *flag_bool); 52 | bool download_JSON(); 53 | bool check_JSON(); 54 | //IO Related 55 | void PrintProgress(PrintConsole *console, u32 nSize, u32 nCurrent); 56 | void WriteBuffer(void *buffer, u64 size, u64 offset, FILE *output); 57 | void write_align_padding(FILE *output, size_t alignment); 58 | u64 GetFileSize_u64(char *filename); 59 | int TruncateFile_u64(char *filename, u64 filelen); 60 | int fseek_64(FILE *fp, u64 file_pos, int whence); 61 | int makedir(const char* dir); 62 | char *getcwdir(char *buffer,int maxlen); 63 | bool FileExists (const char *name); 64 | Result DownloadFile(const char *url, FILE *os, bool bProgress); 65 | Result DownloadFileInstall(const char *url, Handle *handle, u32* offset); 66 | Result InstallSeed(u64 titleId, const void* seed); 67 | //Data Size conversion 68 | u16 u8_to_u16(u8 *value, u32 endianness); 69 | u32 u8_to_u32(u8 *value, u32 endianness); 70 | u64 u8_to_u64(u8 *value, u32 endianness); 71 | int u16_to_u8(u8 *out_value, u16 in_value, u32 endianness); 72 | int u32_to_u8(u8 *out_value, u32 in_value, u32 endianness); 73 | int u64_to_u8(u8 *out_value, u64 in_value, u32 endianness); 74 | //from ctrtool 75 | void memdump(FILE* fout, const char* prefix, const u8* data, u32 size); 76 | // HID related 77 | u32 wait_key(); 78 | u32 wait_key_specific(const char* msg, u32 key); 79 | // graphics functions 80 | void clear_screen(gfxScreen_t screen); 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CIAngel 2 | 3 | # Archived, CDN access is not available anymore. RIP. 4 | 5 | 6 | Now we can get games directly on the 3DS! 7 | Using a Title ID and an encrypted title key, or searching for a title by name, GOOD CIAs will be produced or directly installed that can be redownloaded from eshop and updated from eshop if new content comes out. These CIAs will not interfere with content from eshop. 8 | 9 | You can choose to create a CIA, install the game directly, or install only the ticket. 10 | 11 | 12 | 13 | Many thanks for machinamentum and FirmwareDownloader! Thanks to make_cdn_cia! 14 | 15 | License is GPL to comply with the usage of make_cdn_cia code. 16 | 17 | 18 | This will be improved, updated, I look forward to people contributing to this project! 19 | 20 | # Latest Version 21 | You can scan the following QRCode in FBI to install the latest version of CIAngel, this will always point at the latest release's CIAngel.cia file: 22 | ![CIAngel.cia](https://thedgtl.net/3ds/CIAngel.php?cachebust=2) 23 | 24 | # Usage 25 | ## Search by name 26 | CIAngel utilizes HBKBlib to search for titles by name. Data is read from /CIAngel/wings.json (Which is downloaded automatically on first launch) to search for the name entered. 27 | 28 | ## Download queue 29 | When viewing the search result list, you can press X to add the title to the download queue. Selecting "Process download queue" will allow you to then download or install all of the queued titles one after another. This uses the currently selected download/install mode. 30 | 31 | ## Input.txt support 32 | CIAngel can read a text file (sd:/CIAngel/input.txt) that has 2 lines. 33 | 34 | The first line must be the title id. 35 | The second line must be the encrypted title key. 36 | 37 | # Thanks! 38 | CIAngel has been developed with many contributions from the community, both ideas and code contributions. Many thanks to the following (non-exhaustive) list of contributors! 39 | * Cearp 40 | * Drakia 41 | * superbudvar 42 | * mysamdog 43 | * cerea1killer 44 | * machinamentum for the original FirmwareDownload code 45 | * Steveice10 for many insights into CIA handling in FBI 46 | 47 | # Building 48 | CIAngel has a few dependencies required to build, as well as a git submodule that will need to be fetched. 49 | 50 | When initially fetching the project, the easiest way to get the code and submodules for building is the following: 51 | 52 | `git clone --recursive https://github.com/llakssz/CIAngel.git` 53 | 54 | If you have already checked out the code without the submodules, you can run the following command in the project directory to fetch the submodule data: 55 | 56 | `git submodule update --init --recursive` 57 | 58 | CIAngel depends on the following projects: 59 | - The latest version of [libctru](https://github.com/smealum/ctrulib) 60 | - The latest version of [hbkb](https://gbatemp.net/threads/hbkblib-a-3ds-keyboard-library.397568/) 61 | 62 | ## Building and installing HBKB 63 | HBKB doesn't currently have a 'make install' command, and has to be manually installed once built. 64 | 65 | 1. You will need to modify the Makefile to remove the DEVKITARM define at the top 66 | 2. Once you have run 'make', you will need to copy the resulting lib and header to your DEVKITARM directory: 67 | 68 | `cp hbkb/lib/libhbkb.a $DEVKITPRO/libctru/lib` 69 | `cp hbkb_include_header/hbkb.h $DEVKITPRO/libctru/include/` 70 | 3. Hopefully CIAngel now builds for you! 71 | -------------------------------------------------------------------------------- /source/utf8proc/LICENSE.md: -------------------------------------------------------------------------------- 1 | ## utf8proc license ## 2 | 3 | **utf8proc** is a software package originally developed 4 | by Jan Behrens and the rest of the Public Software Group, who 5 | deserve nearly all of the credit for this library, that is now maintained by the Julia-language developers. Like the original utf8proc, 6 | whose copyright and license statements are reproduced below, all new 7 | work on the utf8proc library is licensed under the [MIT "expat" 8 | license](http://opensource.org/licenses/MIT): 9 | 10 | *Copyright © 2014-2015 by Steven G. Johnson, Jiahao Chen, Tony Kelman, Jonas Fonseca, and other contributors listed in the git history.* 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a 13 | copy of this software and associated documentation files (the "Software"), 14 | to deal in the Software without restriction, including without limitation 15 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | and/or sell copies of the Software, and to permit persons to whom the 17 | Software is furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | DEALINGS IN THE SOFTWARE. 29 | 30 | ## Original utf8proc license ## 31 | 32 | *Copyright (c) 2009, 2013 Public Software Group e. V., Berlin, Germany* 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a 35 | copy of this software and associated documentation files (the "Software"), 36 | to deal in the Software without restriction, including without limitation 37 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 38 | and/or sell copies of the Software, and to permit persons to whom the 39 | Software is furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 49 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 50 | DEALINGS IN THE SOFTWARE. 51 | 52 | ## Unicode data license ## 53 | 54 | This software distribution contains derived data from a modified version of 55 | the Unicode data files. The following license applies to that data: 56 | 57 | **COPYRIGHT AND PERMISSION NOTICE** 58 | 59 | *Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed 60 | under the Terms of Use in http://www.unicode.org/copyright.html.* 61 | 62 | Permission is hereby granted, free of charge, to any person obtaining a 63 | copy of the Unicode data files and any associated documentation (the "Data 64 | Files") or Unicode software and any associated documentation (the 65 | "Software") to deal in the Data Files or Software without restriction, 66 | including without limitation the rights to use, copy, modify, merge, 67 | publish, distribute, and/or sell copies of the Data Files or Software, and 68 | to permit persons to whom the Data Files or Software are furnished to do 69 | so, provided that (a) the above copyright notice(s) and this permission 70 | notice appear with all copies of the Data Files or Software, (b) both the 71 | above copyright notice(s) and this permission notice appear in associated 72 | documentation, and (c) there is clear notice in each modified Data File or 73 | in the Software as well as in the documentation associated with the Data 74 | File(s) or Software that the data or software has been modified. 75 | 76 | THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 77 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 78 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 79 | THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS 80 | INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR 81 | CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 82 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 83 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 84 | PERFORMANCE OF THE DATA FILES OR SOFTWARE. 85 | 86 | Except as contained in this notice, the name of a copyright holder shall 87 | not be used in advertising or otherwise to promote the sale, use or other 88 | dealings in these Data Files or Software without prior written 89 | authorization of the copyright holder. 90 | 91 | Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be 92 | registered in some jurisdictions. All other trademarks and registered 93 | trademarks mentioned herein are the property of their respective owners. 94 | -------------------------------------------------------------------------------- /source/fts_fuzzy_match.h: -------------------------------------------------------------------------------- 1 | // LICENSE 2 | // 3 | // This software is dual-licensed to the public domain and under the following 4 | // license: you are granted a perpetual, irrevocable license to copy, modify, 5 | // publish, and distribute this file as you see fit. 6 | 7 | // VERSION 0.1.0 8 | 9 | #ifndef FTS_FUZZY_MATCH_H 10 | #define FTS_FUZZY_MATCH_H 11 | 12 | #include // ::tolower, ::toupper 13 | 14 | namespace fts { 15 | 16 | // Returns true if each character in pattern is found sequentially within str 17 | static bool fuzzy_match(char const * pattern, char const * str) 18 | { 19 | while (*pattern != '\0' && *str != '\0') { 20 | if (tolower(*pattern) == tolower(*str)) 21 | ++pattern; 22 | ++str; 23 | } 24 | 25 | return *pattern == '\0' ? true : false; 26 | } 27 | 28 | // Returns true if each character in pattern is found sequentially within str 29 | // iff found then outScore is also set. Score value has no intrinsic meaning. Range varies with pattern. 30 | // Can only compare scores with same search pattern. 31 | static bool fuzzy_match(char const * pattern, char const * str, int & outScore) 32 | { 33 | // Score consts 34 | const int adjacency_bonus = 5; // bonus for adjacent matches 35 | const int separator_bonus = 10; // bonus if match occurs after a separator 36 | const int camel_bonus = 10; // bonus if match is uppercase and prev is lower 37 | 38 | const int leading_letter_penalty = -3; // penalty applied for every letter in str before the first match 39 | const int max_leading_letter_penalty = -9; // maximum penalty for leading letters 40 | const int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter 41 | 42 | 43 | // Loop variables 44 | int score = 0; 45 | char const * patternIter = pattern; 46 | char const * strIter = str; 47 | bool prevMatched = false; 48 | bool prevLower = false; 49 | bool prevSeparator = true; // true so if first letter match gets separator bonus 50 | 51 | // Use "best" matched letter if multiple string letters match the pattern 52 | char const * bestLetter = NULL; 53 | int bestLetterScore = 0; 54 | 55 | // Loop over strings 56 | while (*strIter != '\0') 57 | { 58 | const char patternLetter = *patternIter; 59 | const char strLetter = *strIter; 60 | 61 | bool nextMatch = *patternIter != '\0' && tolower(patternLetter) == tolower(strLetter); 62 | bool rematch = bestLetter && tolower(*bestLetter) == tolower(strLetter); 63 | 64 | bool advanced = nextMatch && bestLetter; 65 | bool patternRepeat = bestLetter && patternIter != '\0' && tolower(*bestLetter) == tolower(patternLetter); 66 | 67 | if (advanced || patternRepeat) 68 | { 69 | score += bestLetterScore; 70 | bestLetter = NULL; 71 | bestLetterScore = 0; 72 | } 73 | 74 | if (nextMatch || rematch) 75 | { 76 | int newScore = 0; 77 | 78 | // Apply penalty for each letter before the first pattern match 79 | // Note: std::max because penalties are negative values. So max is smallest penalty. 80 | if (patternIter == pattern) 81 | { 82 | int count = int(strIter - str); 83 | int penalty = leading_letter_penalty * count; 84 | if (penalty < max_leading_letter_penalty) 85 | penalty = max_leading_letter_penalty; 86 | 87 | score += penalty; 88 | } 89 | 90 | // Apply bonus for consecutive bonuses 91 | if (prevMatched) 92 | newScore += adjacency_bonus; 93 | 94 | // Apply bonus for matches after a separator 95 | if (prevSeparator) 96 | newScore += separator_bonus; 97 | 98 | // Apply bonus across camel case boundaries 99 | if (prevLower && isupper(strLetter)) 100 | newScore += camel_bonus; 101 | 102 | // Update pattern iter IFF the next pattern letter was matched 103 | if (nextMatch) 104 | ++patternIter; 105 | 106 | // Update best letter in str which may be for a "next" letter or a rematch 107 | if (newScore >= bestLetterScore) 108 | { 109 | // Apply penalty for now skipped letter 110 | if (bestLetter != NULL) 111 | score += unmatched_letter_penalty; 112 | 113 | bestLetter = strIter; 114 | bestLetterScore = newScore; 115 | } 116 | 117 | prevMatched = true; 118 | } 119 | else 120 | { 121 | score += unmatched_letter_penalty; 122 | prevMatched = false; 123 | } 124 | 125 | // Separators should be more easily defined 126 | prevLower = islower(strLetter) != 0; 127 | prevSeparator = strLetter == '_' || strLetter == ' '; 128 | 129 | ++strIter; 130 | } 131 | 132 | // Apply score for last match 133 | if (bestLetter) 134 | score += bestLetterScore; 135 | 136 | // Did not match full pattern 137 | if (*patternIter != '\0') 138 | return false; 139 | 140 | outScore = score; 141 | return true; 142 | } 143 | 144 | } // namespace fts 145 | 146 | #endif // FTS_FUZZY_MATCH_H 147 | -------------------------------------------------------------------------------- /source/cia.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 3DSGuy 3 | 4 | This file is part of make_cdn_cia. 5 | 6 | make_cdn_cia is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | make_cdn_cia is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with make_cdn_cia. If not, see . 18 | **/ 19 | 20 | #include <3ds.h> 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | //Sig Types 26 | #define Elliptic_Curve_1 0x05000100 27 | #define RSA_2048_SHA256 0x04000100 28 | #define RSA_4096_SHA256 0x03000100 29 | #define Elliptic_Curve_0 0x02000100 30 | #define RSA_2048_SHA1 0x01000100 31 | #define RSA_4096_SHA1 0x00000100 32 | //Errors 33 | #define ERR_UNRECOGNISED_SIG 2 34 | 35 | typedef struct 36 | { 37 | u8 modulus[0x100]; 38 | u8 exponent[0x4]; 39 | } __attribute__((__packed__)) 40 | RSA_2048_PUB_KEY; 41 | 42 | typedef struct 43 | { 44 | u8 padding_0[0x3c]; 45 | u8 issuer[0x40]; 46 | u8 tag_0[4]; 47 | u8 name[0x40]; 48 | u8 tag_1[0x4]; 49 | RSA_2048_PUB_KEY pubk; 50 | u8 padding_1[0x34]; 51 | } __attribute__((__packed__)) 52 | CERT_2048KEY_DATA_STRUCT; 53 | 54 | typedef struct 55 | { 56 | u8 padding_0[0x3c]; 57 | u8 issuer[0x40]; 58 | u8 version; 59 | u8 ca_crl_version; 60 | u8 signer_crl_version; 61 | u8 padding_1; 62 | } __attribute__((__packed__)) 63 | TMD_SIG_STRUCT; 64 | 65 | typedef struct 66 | { 67 | u8 content_id[4]; 68 | u8 content_index[2]; 69 | u8 content_type[2]; 70 | u8 content_size[8]; 71 | u8 sha_256_hash[0x20]; 72 | } __attribute__((__packed__)) 73 | TMD_CONTENT_CHUNK_STRUCT; 74 | 75 | typedef struct 76 | { 77 | TMD_SIG_STRUCT tmd_sig; 78 | u8 system_version[8]; 79 | u8 title_id[8]; 80 | u8 title_type[4]; 81 | u8 reserved[0x40]; 82 | u8 access_rights[4]; 83 | u8 title_version[2]; 84 | u8 content_count[2]; 85 | u8 boot_content[2]; 86 | u8 padding[2]; 87 | u8 sha_256_hash[0x20]; 88 | u8 content_info_records[0x900]; 89 | } __attribute__((__packed__)) 90 | TMD_STRUCT; 91 | 92 | typedef struct 93 | { 94 | u8 padding_0[0x3c]; 95 | u8 issuer[0x40]; 96 | u8 ECDH[0x3c]; 97 | u8 unknown[3]; 98 | } __attribute__((__packed__)) 99 | TIK_SIG_STRUCT; 100 | 101 | typedef struct 102 | { 103 | TIK_SIG_STRUCT tik_sig; 104 | u8 encrypted_title_key[0x10]; 105 | u8 unknown_0; 106 | u8 ticket_id[8]; 107 | u8 ticket_consoleID[4]; 108 | u8 title_id[8]; 109 | u8 unknown_1[2]; 110 | u8 title_version[2]; 111 | u8 unused_0[8]; 112 | u8 unused_1; 113 | u8 common_key_index; 114 | u8 unknown_2[0x15e]; 115 | } __attribute__((__packed__)) 116 | TIK_STRUCT; 117 | 118 | typedef struct 119 | { 120 | u8 result; 121 | 122 | FILE *tmd; 123 | u8 title_id[8]; 124 | u16 title_version; 125 | u32 tmd_size; 126 | u32 cert_offset[2]; 127 | u32 cert_size[2]; 128 | u16 content_count; 129 | TMD_CONTENT_CHUNK_STRUCT *content_struct; 130 | 131 | u16 *title_index; 132 | } __attribute__((__packed__)) 133 | TMD_CONTEXT; 134 | 135 | typedef struct 136 | { 137 | u8 result; 138 | 139 | FILE *tik; 140 | u8 title_id[8]; 141 | u16 title_version; 142 | u32 tik_size; 143 | u32 cert_offset[2]; 144 | u32 cert_size[2]; 145 | } __attribute__((__packed__)) 146 | TIK_CONTEXT; 147 | 148 | typedef struct 149 | { 150 | u32 header_size; 151 | u16 type; 152 | u16 version; 153 | u32 cert_size; 154 | u32 tik_size; 155 | u32 tmd_size; 156 | u32 meta_size; 157 | u64 content_size; 158 | u8 content_index[0x2000]; 159 | } CIA_HEADER; 160 | 161 | // Main Functions 162 | int generate_cia(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output); 163 | int install_cia(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context); 164 | 165 | //Processing Functions 166 | TIK_CONTEXT process_tik(FILE *tik); 167 | TMD_CONTEXT process_tmd(FILE *tmd); 168 | CIA_HEADER set_cia_header(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context); 169 | 170 | //Reading/Calc Functions 171 | u32 get_tik_size(u32 sig_size); 172 | u32 get_tmd_size(u32 sig_size, u16 content_count); 173 | u32 get_sig_size(u32 offset, FILE *file); 174 | u32 get_cert_size(u32 offset, FILE *file); 175 | u64 get_content_size(TMD_CONTEXT tmd_context); 176 | u64 read_content_size(TMD_CONTENT_CHUNK_STRUCT content_struct); 177 | u32 get_total_cert_size(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context); 178 | u32 get_content_id(TMD_CONTENT_CHUNK_STRUCT content_struct); 179 | u64 get_title_id(TMD_CONTEXT content_struct); 180 | 181 | //Writing functions 182 | int write_cia_header(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output); 183 | int write_cert_chain(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output); 184 | int write_tik(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output); 185 | int write_tmd(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output); 186 | int write_content(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output); 187 | 188 | // Install functions 189 | int install_cia_header(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle); 190 | int install_cert_chain(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle); 191 | int install_tik(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle); 192 | int install_tmd(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle); 193 | Result install_content(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle); 194 | void install_write_align_padding(Handle handle, u32* offset, size_t alignment); 195 | 196 | //Get Struct Functions 197 | TIK_STRUCT get_tik_struct(u32 sig_size, FILE *tik); 198 | TMD_STRUCT get_tmd_struct(u32 sig_size, FILE *tmd); 199 | TMD_CONTENT_CHUNK_STRUCT get_tmd_content_struct(u32 sig_size, u8 index, FILE *tmd); 200 | 201 | //Printing Functions 202 | void print_content_chunk_info(TMD_CONTENT_CHUNK_STRUCT content_struct); 203 | 204 | //Checking Functions 205 | int check_tid(u8 *tid_0, u8 *tid_1); 206 | 207 | #ifdef __cplusplus 208 | } 209 | #endif 210 | -------------------------------------------------------------------------------- /source/menu.cpp: -------------------------------------------------------------------------------- 1 | /* Code borrowed from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/menu.c and tortured until it bent to my will */ 2 | #include "menu.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include "utils.h" 11 | 12 | int selected_options[MAX_SELECTED_OPTIONS]; 13 | 14 | void init_menu(gfxScreen_t screen) 15 | { 16 | // Create our new console, initialize it, and switch back to the previous console 17 | PrintConsole* currentConsole = consoleSelect(¤tMenu.menuConsole); 18 | consoleInit(screen, ¤tMenu.menuConsole); 19 | 20 | consoleSelect(currentConsole); 21 | } 22 | 23 | ConsoleMenu currentMenu; 24 | 25 | void menu_draw_string(const char* str, int pos_x, int pos_y, const char* color) 26 | { 27 | currentMenu.menuConsole.cursorX = pos_x; 28 | currentMenu.menuConsole.cursorY = pos_y; 29 | printf("%s%s%s", color, str, CONSOLE_RESET); 30 | 31 | gfxFlushBuffers(); 32 | } 33 | 34 | void menu_draw_string_full(const char* str, int pos_y, const char* color) 35 | { 36 | currentMenu.menuConsole.cursorX = 0; 37 | currentMenu.menuConsole.cursorY = pos_y; 38 | printf(color); 39 | 40 | if (currentMenu.menuConsole.consoleWidth == 50) 41 | { 42 | printf("%-50s", str); 43 | } 44 | else 45 | { 46 | printf("%-40s", str); 47 | } 48 | printf(CONSOLE_RESET); 49 | 50 | gfxFlushBuffers(); 51 | } 52 | 53 | void menu_draw_info(PrintConsole &console, const game_item &game) 54 | { 55 | PrintConsole* currentConsole = consoleSelect(&console); 56 | consoleClear(); 57 | 58 | printf("Name: %s\n", game.name.c_str()); 59 | printf("Serial: %s\n", game.code.c_str()); 60 | printf("Region: %s\n", game.region.c_str()); 61 | printf("TitleID: %s\n", game.titleid.c_str()); 62 | printf("Type: %s\n", GetSerialType(game.code).c_str()); 63 | 64 | consoleSelect(currentConsole); 65 | } 66 | 67 | void titles_multkey_draw(const char *title, const char* footer, int back, std::vector *options, void* data, 68 | bool (*callback)(int result, u32 key, void* data)) 69 | 70 | { 71 | // Set up a console on the bottom screen for info 72 | GSPGPU_FramebufferFormats infoOldFormat = gfxGetScreenFormat(GFX_BOTTOM); 73 | PrintConsole infoConsole; 74 | PrintConsole* currentConsole = consoleSelect(&infoConsole); 75 | consoleInit(GFX_BOTTOM, &infoConsole); 76 | 77 | // Select our menu console and clear the screen 78 | consoleSelect(¤tMenu.menuConsole); 79 | 80 | int count = options->size(); 81 | bool firstLoop = true; 82 | int current = 0; 83 | int previous_index = 0; 84 | int menu_offset = 0; 85 | int previous_menu_offset = 1; 86 | int menu_pos_y; 87 | int menu_end_y = currentMenu.menuConsole.consoleHeight -1; 88 | int current_pos_y = 0; 89 | 90 | while (aptMainLoop()) { 91 | if(firstLoop || previous_index != current) { 92 | firstLoop = false; 93 | int results_per_page = menu_end_y - menu_pos_y; 94 | int current_page = current / results_per_page; 95 | menu_offset = current_page * results_per_page; 96 | char prev_entry[50]; 97 | char entry[50]; 98 | sprintf(prev_entry, "%-*.*s (%s) %*.*s", 99 | 31, 31, (*options)[previous_index].name.c_str(), 100 | (*options)[previous_index].region.c_str(), 101 | 10, 10, (*options)[previous_index].code.c_str()); 102 | sprintf(entry, "%-*.*s (%s) %*.*s", 103 | 31, 31, (*options)[current].name.c_str(), 104 | (*options)[current].region.c_str(), 105 | 10, 10, (*options)[current].code.c_str()); 106 | if(menu_offset == previous_menu_offset) { 107 | menu_draw_string(prev_entry, 1, menu_pos_y + (previous_index-menu_offset), CONSOLE_WHITE); 108 | menu_draw_string(entry, 1, menu_pos_y + (current-menu_offset), CONSOLE_REVERSE); 109 | } else { 110 | consoleClear(); 111 | current_pos_y=0; 112 | // Draw the header 113 | menu_draw_string(title, 0, current_pos_y++, CONSOLE_RED); 114 | menu_pos_y = current_pos_y; 115 | for (int i = 0; menu_offset + i < count && i < results_per_page; i++) { 116 | sprintf(entry, "%-*.*s (%s) %*.*s", 117 | 31, 31, (*options)[menu_offset + i].name.c_str(), 118 | (*options)[menu_offset + i].region.c_str(), 119 | 10, 10, (*options)[menu_offset + i].code.c_str()); 120 | if(i+menu_offset == current) { 121 | menu_draw_string(entry, 1, current_pos_y, CONSOLE_REVERSE); 122 | } else { 123 | menu_draw_string(entry, 1, current_pos_y, CONSOLE_WHITE); 124 | } 125 | current_pos_y++; 126 | previous_menu_offset = menu_offset; 127 | } 128 | if (footer != NULL) 129 | { 130 | // Draw the footer if one is provided 131 | current_pos_y = currentMenu.menuConsole.consoleHeight - 1; 132 | menu_draw_string_full(footer, current_pos_y, CONSOLE_BLUE CONSOLE_REVERSE); 133 | } 134 | } 135 | previous_index = current; 136 | 137 | menu_draw_info(infoConsole, (*options)[current]); 138 | } 139 | u32 key = wait_key(); 140 | 141 | if (key & KEY_UP) { 142 | if (current <= 0) { 143 | current = count - 1; 144 | } else { 145 | current--; 146 | } 147 | } else if (key & KEY_DOWN) { 148 | if (current >= count - 1) { 149 | current = 0; 150 | } else { 151 | current++; 152 | } 153 | } else if (key & KEY_RIGHT) { 154 | current += 5; 155 | if (current >= count) current = count - 1; 156 | } else if (key & KEY_LEFT) { 157 | current -= 5; 158 | if (current < 0) current = 0; 159 | } else if (callback(current, key, data)) { 160 | break; 161 | } 162 | } 163 | 164 | // Reselect the original console 165 | consoleSelect(currentConsole); 166 | 167 | // Reset the gfx format on the bottom screen 168 | gfxSetScreenFormat(GFX_BOTTOM, infoOldFormat); 169 | } 170 | 171 | void menu_multkey_draw(const char *title, const char* footer, int back, int count, const char *options[], void* data, 172 | bool (*callback)(int result, u32 key, void* data)) 173 | 174 | { 175 | // Select our menu console and clear the screen 176 | PrintConsole* currentConsole = consoleSelect(¤tMenu.menuConsole); 177 | 178 | int current = 0; 179 | int previous_index = 1; 180 | int menu_offset = 0; 181 | int previous_menu_offset = 1; 182 | int menu_pos_y; 183 | int menu_end_y = currentMenu.menuConsole.consoleHeight -2; 184 | int current_pos_y = 0; 185 | 186 | while (aptMainLoop()) { 187 | if(previous_index != current) { 188 | int results_per_page = menu_end_y - menu_pos_y; 189 | int current_page = current / results_per_page; 190 | menu_offset = current_page * results_per_page; 191 | if(menu_offset == previous_menu_offset) { 192 | menu_draw_string(options[menu_offset + previous_index], 1, menu_pos_y + (previous_index-menu_offset), CONSOLE_WHITE); 193 | menu_draw_string(options[menu_offset + current], 1, menu_pos_y + (current-menu_offset), CONSOLE_REVERSE); 194 | } else { 195 | consoleClear(); 196 | current_pos_y=0; 197 | // Draw the header 198 | menu_draw_string(title, 0, current_pos_y++, CONSOLE_RED); 199 | menu_pos_y = current_pos_y; 200 | for (int i = 0; (menu_offset + i) < count && i < results_per_page; i++) { 201 | if(i+menu_offset == current) { 202 | menu_draw_string(options[menu_offset + i], 1, current_pos_y, CONSOLE_REVERSE); 203 | } else { 204 | menu_draw_string(options[menu_offset + i], 1, current_pos_y, CONSOLE_WHITE); 205 | } 206 | current_pos_y++; 207 | previous_menu_offset = menu_offset; 208 | } 209 | if (footer != NULL) 210 | { 211 | // Draw the footer if one is provided 212 | current_pos_y = currentMenu.menuConsole.consoleHeight - 1; 213 | menu_draw_string_full(footer, current_pos_y, CONSOLE_BLUE CONSOLE_REVERSE); 214 | } 215 | } 216 | previous_index = current; 217 | } 218 | u32 key = wait_key(); 219 | 220 | if (key & KEY_UP) { 221 | if (current <= 0) { 222 | current = count - 1; 223 | } else { 224 | current--; 225 | } 226 | } else if (key & KEY_DOWN) { 227 | if (current >= count - 1) { 228 | current = 0; 229 | } else { 230 | current++; 231 | } 232 | } else if (key & KEY_RIGHT) { 233 | current += 5; 234 | if (current >= count) current = count - 1; 235 | } else if (key & KEY_LEFT) { 236 | current -= 5; 237 | if (current < 0) current = 0; 238 | } else if (callback(current, key, data)) { 239 | break; 240 | } 241 | } 242 | 243 | // Reselect the original console 244 | consoleSelect(currentConsole); 245 | } 246 | -------------------------------------------------------------------------------- /source/json/json-forwards.h: -------------------------------------------------------------------------------- 1 | /// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). 2 | /// It is intended to be used with #include "json/json-forwards.h" 3 | /// This header provides forward declaration for all JsonCpp types. 4 | 5 | // ////////////////////////////////////////////////////////////////////// 6 | // Beginning of content of file: LICENSE 7 | // ////////////////////////////////////////////////////////////////////// 8 | 9 | /* 10 | The JsonCpp library's source code, including accompanying documentation, 11 | tests and demonstration applications, are licensed under the following 12 | conditions... 13 | 14 | The author (Baptiste Lepilleur) explicitly disclaims copyright in all 15 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 16 | this software is released into the Public Domain. 17 | 18 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 19 | 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is 20 | released under the terms of the MIT License (see below). 21 | 22 | In jurisdictions which recognize Public Domain property, the user of this 23 | software may choose to accept it either as 1) Public Domain, 2) under the 24 | conditions of the MIT License (see below), or 3) under the terms of dual 25 | Public Domain/MIT License conditions described here, as they choose. 26 | 27 | The MIT License is about as close to Public Domain as a license can get, and is 28 | described in clear, concise terms at: 29 | 30 | http://en.wikipedia.org/wiki/MIT_License 31 | 32 | The full text of the MIT License follows: 33 | 34 | ======================================================================== 35 | Copyright (c) 2007-2010 Baptiste Lepilleur 36 | 37 | Permission is hereby granted, free of charge, to any person 38 | obtaining a copy of this software and associated documentation 39 | files (the "Software"), to deal in the Software without 40 | restriction, including without limitation the rights to use, copy, 41 | modify, merge, publish, distribute, sublicense, and/or sell copies 42 | of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be 46 | included in all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 49 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 50 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 51 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 52 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 53 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 54 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | ======================================================================== 57 | (END LICENSE TEXT) 58 | 59 | The MIT license is compatible with both the GPL and commercial 60 | software, affording one all of the rights of Public Domain with the 61 | minor nuisance of being required to keep the above copyright notice 62 | and license text in the source code. Note also that by accepting the 63 | Public Domain "license" you can re-license your copy using whatever 64 | license you like. 65 | 66 | */ 67 | 68 | // ////////////////////////////////////////////////////////////////////// 69 | // End of content of file: LICENSE 70 | // ////////////////////////////////////////////////////////////////////// 71 | 72 | 73 | 74 | 75 | 76 | #ifndef JSON_FORWARD_AMALGATED_H_INCLUDED 77 | # define JSON_FORWARD_AMALGATED_H_INCLUDED 78 | /// If defined, indicates that the source file is amalgated 79 | /// to prevent private header inclusion. 80 | #define JSON_IS_AMALGAMATION 81 | 82 | // ////////////////////////////////////////////////////////////////////// 83 | // Beginning of content of file: include/json/config.h 84 | // ////////////////////////////////////////////////////////////////////// 85 | 86 | // Copyright 2007-2010 Baptiste Lepilleur 87 | // Distributed under MIT license, or public domain if desired and 88 | // recognized in your jurisdiction. 89 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 90 | 91 | #ifndef JSON_CONFIG_H_INCLUDED 92 | #define JSON_CONFIG_H_INCLUDED 93 | #include 94 | #include //typdef String 95 | 96 | /// If defined, indicates that json library is embedded in CppTL library. 97 | //# define JSON_IN_CPPTL 1 98 | 99 | /// If defined, indicates that json may leverage CppTL library 100 | //# define JSON_USE_CPPTL 1 101 | /// If defined, indicates that cpptl vector based map should be used instead of 102 | /// std::map 103 | /// as Value container. 104 | //# define JSON_USE_CPPTL_SMALLMAP 1 105 | 106 | // If non-zero, the library uses exceptions to report bad input instead of C 107 | // assertion macros. The default is to use exceptions. 108 | #ifndef JSON_USE_EXCEPTION 109 | #define JSON_USE_EXCEPTION 1 110 | #endif 111 | 112 | /// If defined, indicates that the source file is amalgated 113 | /// to prevent private header inclusion. 114 | /// Remarks: it is automatically defined in the generated amalgated header. 115 | // #define JSON_IS_AMALGAMATION 116 | 117 | #ifdef JSON_IN_CPPTL 118 | #include 119 | #ifndef JSON_USE_CPPTL 120 | #define JSON_USE_CPPTL 1 121 | #endif 122 | #endif 123 | 124 | #ifdef JSON_IN_CPPTL 125 | #define JSON_API CPPTL_API 126 | #elif defined(JSON_DLL_BUILD) 127 | #if defined(_MSC_VER) || defined(__MINGW32__) 128 | #define JSON_API __declspec(dllexport) 129 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 130 | #endif // if defined(_MSC_VER) 131 | #elif defined(JSON_DLL) 132 | #if defined(_MSC_VER) || defined(__MINGW32__) 133 | #define JSON_API __declspec(dllimport) 134 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 135 | #endif // if defined(_MSC_VER) 136 | #endif // ifdef JSON_IN_CPPTL 137 | #if !defined(JSON_API) 138 | #define JSON_API 139 | #endif 140 | 141 | // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for 142 | // integer 143 | // Storages, and 64 bits integer support is disabled. 144 | // #define JSON_NO_INT64 1 145 | 146 | #if defined(_MSC_VER) // MSVC 147 | # if _MSC_VER <= 1200 // MSVC 6 148 | // Microsoft Visual Studio 6 only support conversion from __int64 to double 149 | // (no conversion from unsigned __int64). 150 | # define JSON_USE_INT64_DOUBLE_CONVERSION 1 151 | // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' 152 | // characters in the debug information) 153 | // All projects I've ever seen with VS6 were using this globally (not bothering 154 | // with pragma push/pop). 155 | # pragma warning(disable : 4786) 156 | # endif // MSVC 6 157 | 158 | # if _MSC_VER >= 1500 // MSVC 2008 159 | /// Indicates that the following function is deprecated. 160 | # define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) 161 | # endif 162 | 163 | #endif // defined(_MSC_VER) 164 | 165 | #if defined(_MSC_VER) && _MSC_VER <= 1600 // MSVC <= 2010 166 | # define JSONCPP_OVERRIDE 167 | #else 168 | # define JSONCPP_OVERRIDE override 169 | #endif // MSVC <= 2010 170 | 171 | 172 | #ifndef JSON_HAS_RVALUE_REFERENCES 173 | 174 | #if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 175 | #define JSON_HAS_RVALUE_REFERENCES 1 176 | #endif // MSVC >= 2010 177 | 178 | #ifdef __clang__ 179 | #if __has_feature(cxx_rvalue_references) 180 | #define JSON_HAS_RVALUE_REFERENCES 1 181 | #endif // has_feature 182 | 183 | #elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) 184 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) 185 | #define JSON_HAS_RVALUE_REFERENCES 1 186 | #endif // GXX_EXPERIMENTAL 187 | 188 | #endif // __clang__ || __GNUC__ 189 | 190 | #endif // not defined JSON_HAS_RVALUE_REFERENCES 191 | 192 | #ifndef JSON_HAS_RVALUE_REFERENCES 193 | #define JSON_HAS_RVALUE_REFERENCES 0 194 | #endif 195 | 196 | #ifdef __clang__ 197 | #elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) 198 | # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) 199 | # define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) 200 | # elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) 201 | # define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) 202 | # endif // GNUC version 203 | #endif // __clang__ || __GNUC__ 204 | 205 | #if !defined(JSONCPP_DEPRECATED) 206 | #define JSONCPP_DEPRECATED(message) 207 | #endif // if !defined(JSONCPP_DEPRECATED) 208 | 209 | #if __GNUC__ >= 6 210 | # define JSON_USE_INT64_DOUBLE_CONVERSION 1 211 | #endif 212 | 213 | #if !defined(JSON_IS_AMALGAMATION) 214 | 215 | # include "version.h" 216 | 217 | # if JSONCPP_USING_SECURE_MEMORY 218 | # include "allocator.h" //typedef Allocator 219 | # endif 220 | 221 | #endif // if !defined(JSON_IS_AMALGAMATION) 222 | 223 | namespace Json { 224 | typedef int Int; 225 | typedef unsigned int UInt; 226 | #if defined(JSON_NO_INT64) 227 | typedef int LargestInt; 228 | typedef unsigned int LargestUInt; 229 | #undef JSON_HAS_INT64 230 | #else // if defined(JSON_NO_INT64) 231 | // For Microsoft Visual use specific types as long long is not supported 232 | #if defined(_MSC_VER) // Microsoft Visual Studio 233 | typedef __int64 Int64; 234 | typedef unsigned __int64 UInt64; 235 | #else // if defined(_MSC_VER) // Other platforms, use long long 236 | typedef long long int Int64; 237 | typedef unsigned long long int UInt64; 238 | #endif // if defined(_MSC_VER) 239 | typedef Int64 LargestInt; 240 | typedef UInt64 LargestUInt; 241 | #define JSON_HAS_INT64 242 | #endif // if defined(JSON_NO_INT64) 243 | #if JSONCPP_USING_SECURE_MEMORY 244 | #define JSONCPP_STRING std::basic_string, Json::SecureAllocator > 245 | #define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > 246 | #define JSONCPP_OSTREAM std::basic_ostream> 247 | #define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > 248 | #define JSONCPP_ISTREAM std::istream 249 | #else 250 | #define JSONCPP_STRING std::string 251 | #define JSONCPP_OSTRINGSTREAM std::ostringstream 252 | #define JSONCPP_OSTREAM std::ostream 253 | #define JSONCPP_ISTRINGSTREAM std::istringstream 254 | #define JSONCPP_ISTREAM std::istream 255 | #endif // if JSONCPP_USING_SECURE_MEMORY 256 | } // end namespace Json 257 | 258 | #endif // JSON_CONFIG_H_INCLUDED 259 | 260 | // ////////////////////////////////////////////////////////////////////// 261 | // End of content of file: include/json/config.h 262 | // ////////////////////////////////////////////////////////////////////// 263 | 264 | 265 | 266 | 267 | 268 | 269 | // ////////////////////////////////////////////////////////////////////// 270 | // Beginning of content of file: include/json/forwards.h 271 | // ////////////////////////////////////////////////////////////////////// 272 | 273 | // Copyright 2007-2010 Baptiste Lepilleur 274 | // Distributed under MIT license, or public domain if desired and 275 | // recognized in your jurisdiction. 276 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 277 | 278 | #ifndef JSON_FORWARDS_H_INCLUDED 279 | #define JSON_FORWARDS_H_INCLUDED 280 | 281 | #if !defined(JSON_IS_AMALGAMATION) 282 | #include "config.h" 283 | #endif // if !defined(JSON_IS_AMALGAMATION) 284 | 285 | namespace Json { 286 | 287 | // writer.h 288 | class FastWriter; 289 | class StyledWriter; 290 | 291 | // reader.h 292 | class Reader; 293 | 294 | // features.h 295 | class Features; 296 | 297 | // value.h 298 | typedef unsigned int ArrayIndex; 299 | class StaticString; 300 | class Path; 301 | class PathArgument; 302 | class Value; 303 | class ValueIteratorBase; 304 | class ValueIterator; 305 | class ValueConstIterator; 306 | 307 | } // namespace Json 308 | 309 | #endif // JSON_FORWARDS_H_INCLUDED 310 | 311 | // ////////////////////////////////////////////////////////////////////// 312 | // End of content of file: include/json/forwards.h 313 | // ////////////////////////////////////////////////////////////////////// 314 | 315 | 316 | 317 | 318 | 319 | #endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED 320 | -------------------------------------------------------------------------------- /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 | //--------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /source/svchax/svchax.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include 5 | #include "svchax.h" 6 | 7 | #define CURRENT_KTHREAD 0xFFFF9000 8 | #define CURRENT_KPROCESS 0xFFFF9004 9 | #define CURRENT_KPROCESS_HANDLE 0xFFFF8001 10 | #define RESOURCE_LIMIT_THREADS 0x2 11 | 12 | #define MCH2_THREAD_COUNT_MAX 0x20 13 | #define MCH2_THREAD_STACKS_SIZE 0x1000 14 | 15 | #define SVC_ACL_OFFSET(svc_id) (((svc_id) >> 5) << 2) 16 | #define SVC_ACL_MASK(svc_id) (0x1 << ((svc_id) & 0x1F)) 17 | #define THREAD_PAGE_ACL_OFFSET 0xF38 18 | 19 | u32 __ctr_svchax = 0; 20 | u32 __ctr_svchax_srv = 0; 21 | 22 | extern void* __service_ptr; 23 | 24 | typedef u32(*backdoor_fn)(u32 arg0, u32 arg1); 25 | 26 | __attribute((naked)) 27 | static u32 svc_7b(backdoor_fn entry_fn, ...) // can pass up to two arguments to entry_fn(...) 28 | { 29 | __asm__ volatile( 30 | "push {r0, r1, r2} \n\t" 31 | "mov r3, sp \n\t" 32 | "add r0, pc, #12 \n\t" 33 | "svc 0x7B \n\t" 34 | "add sp, sp, #8 \n\t" 35 | "ldr r0, [sp], #4 \n\t" 36 | "bx lr \n\t" 37 | "cpsid aif \n\t" 38 | "ldr r2, [r3], #4 \n\t" 39 | "ldmfd r3!, {r0, r1} \n\t" 40 | "push {r3, lr} \n\t" 41 | "blx r2 \n\t" 42 | "pop {r3, lr} \n\t" 43 | "str r0, [r3, #-4]! \n\t" 44 | "bx lr \n\t"); 45 | return 0; 46 | } 47 | 48 | static void k_enable_all_svcs(u32 isNew3DS) 49 | { 50 | u32* thread_ACL = *(*(u32***)CURRENT_KTHREAD + 0x22) - 0x6; 51 | u32* process_ACL = *(u32**)CURRENT_KPROCESS + (isNew3DS ? 0x24 : 0x22); 52 | 53 | memset(thread_ACL, 0xFF, 0x10); 54 | memset(process_ACL, 0xFF, 0x10); 55 | } 56 | 57 | static u32 k_read_kaddr(u32* kaddr) 58 | { 59 | return *kaddr; 60 | } 61 | 62 | static u32 read_kaddr(u32 kaddr) 63 | { 64 | return svc_7b((backdoor_fn)k_read_kaddr, kaddr); 65 | } 66 | 67 | static u32 k_write_kaddr(u32* kaddr, u32 val) 68 | { 69 | *kaddr = val; 70 | return 0; 71 | } 72 | 73 | static void write_kaddr(u32 kaddr, u32 val) 74 | { 75 | svc_7b((backdoor_fn)k_write_kaddr, kaddr, val); 76 | } 77 | 78 | __attribute__((naked)) 79 | static u32 get_thread_page(void) 80 | { 81 | __asm__ volatile( 82 | "sub r0, sp, #8 \n\t" 83 | "mov r1, #1 \n\t" 84 | "mov r2, #0 \n\t" 85 | "svc 0x2A \n\t" 86 | "mov r0, r1, LSR#12 \n\t" 87 | "mov r0, r0, LSL#12 \n\t" 88 | "bx lr \n\t"); 89 | return 0; 90 | } 91 | 92 | typedef struct 93 | { 94 | Handle started_event; 95 | Handle lock; 96 | volatile u32 target_kaddr; 97 | volatile u32 target_val; 98 | } mch2_thread_args_t; 99 | 100 | typedef struct 101 | { 102 | u32* stack_top; 103 | Handle handle; 104 | bool keep; 105 | mch2_thread_args_t args; 106 | } mch2_thread_t; 107 | 108 | typedef struct 109 | { 110 | u32 old_cpu_time_limit; 111 | bool isNew3DS; 112 | u32 kernel_fcram_mapping_offset; 113 | 114 | Handle arbiter; 115 | volatile u32 alloc_address; 116 | volatile u32 alloc_size; 117 | u8* flush_buffer; 118 | 119 | Handle dummy_threads_lock; 120 | Handle target_threads_lock; 121 | Handle main_thread_lock; 122 | u32* thread_page_va; 123 | u32 thread_page_kva; 124 | 125 | u32 threads_limit; 126 | Handle alloc_thread; 127 | Handle poll_thread; 128 | mch2_thread_t threads[MCH2_THREAD_COUNT_MAX]; 129 | } mch2_vars_t; 130 | 131 | static void alloc_thread_entry(mch2_vars_t* mch2) 132 | { 133 | u32 tmp; 134 | 135 | svcControlMemory(&tmp, mch2->alloc_address, 0x0, mch2->alloc_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE); 136 | svcExitThread(); 137 | } 138 | 139 | static void dummy_thread_entry(Handle lock) 140 | { 141 | svcWaitSynchronization(lock, U64_MAX); 142 | svcExitThread(); 143 | } 144 | 145 | static void check_tls_thread_entry(bool* keep) 146 | { 147 | *keep = !((u32)getThreadLocalStorage() & 0xFFF); 148 | svcExitThread(); 149 | } 150 | 151 | static void target_thread_entry(mch2_thread_args_t* args) 152 | { 153 | svcSignalEvent(args->started_event); 154 | svcWaitSynchronization(args->lock, U64_MAX); 155 | 156 | if (args->target_kaddr) 157 | write_kaddr(args->target_kaddr, args->target_val); 158 | 159 | svcExitThread(); 160 | } 161 | 162 | static u32 get_first_free_basemem_page(bool isNew3DS) 163 | { 164 | s64 v1; 165 | int memused_base; 166 | int memused_base_linear; // guessed 167 | 168 | memused_base = osGetMemRegionUsed(MEMREGION_BASE); 169 | 170 | svcGetSystemInfo(&v1, 2, 0); 171 | memused_base_linear = 0x6C000 + v1 + 172 | (osGetKernelVersion() > SYSTEM_VERSION(2, 49, 0) ? (isNew3DS ? 0x2000 : 0x1000) : 0x0); 173 | 174 | return (osGetKernelVersion() > SYSTEM_VERSION(2, 40, 0) ? 0xE0000000 : 0xF0000000) // kernel FCRAM mapping 175 | + (isNew3DS ? 0x10000000 : 0x08000000) // FCRAM size 176 | - (memused_base - memused_base_linear) // memory usage for pages allocated without the MEMOP_LINEAR flag 177 | - 0x1000; // skip to the start addr of the next free page 178 | 179 | } 180 | 181 | static u32 get_threads_limit(void) 182 | { 183 | Handle resource_limit_handle; 184 | s64 thread_limit_current; 185 | s64 thread_limit_max; 186 | u32 thread_limit_name = RESOURCE_LIMIT_THREADS; 187 | 188 | svcGetResourceLimit(&resource_limit_handle, CURRENT_KPROCESS_HANDLE); 189 | svcGetResourceLimitCurrentValues(&thread_limit_current, resource_limit_handle, &thread_limit_name, 1); 190 | svcGetResourceLimitLimitValues(&thread_limit_max, resource_limit_handle, &thread_limit_name, 1); 191 | svcCloseHandle(resource_limit_handle); 192 | 193 | if (thread_limit_max > MCH2_THREAD_COUNT_MAX) 194 | thread_limit_max = MCH2_THREAD_COUNT_MAX; 195 | 196 | return thread_limit_max - thread_limit_current; 197 | } 198 | 199 | static void do_memchunkhax2(void) 200 | { 201 | static u8 flush_buffer[0x8000]; 202 | static u8 thread_stacks[MCH2_THREAD_STACKS_SIZE]; 203 | 204 | int i; 205 | u32 tmp; 206 | mch2_vars_t mch2 = {0}; 207 | 208 | mch2.flush_buffer = flush_buffer; 209 | mch2.threads_limit = get_threads_limit(); 210 | mch2.kernel_fcram_mapping_offset = (osGetKernelVersion() > SYSTEM_VERSION(2, 40, 0)) ? 0xC0000000 : 0xD0000000; 211 | 212 | for (i = 0; i < MCH2_THREAD_COUNT_MAX; i++) 213 | mch2.threads[i].stack_top = (u32*)((u32)thread_stacks + (i + 1) * (MCH2_THREAD_STACKS_SIZE / MCH2_THREAD_COUNT_MAX)); 214 | 215 | APT_CheckNew3DS(&mch2.isNew3DS); 216 | APT_GetAppCpuTimeLimit(&mch2.old_cpu_time_limit); 217 | APT_SetAppCpuTimeLimit(5); 218 | 219 | for (i = 0; i < mch2.threads_limit; i++) 220 | { 221 | svcCreateThread(&mch2.threads[i].handle, (ThreadFunc)check_tls_thread_entry, (u32)&mch2.threads[i].keep, 222 | mch2.threads[i].stack_top, 0x18, 0); 223 | svcWaitSynchronization(mch2.threads[i].handle, U64_MAX); 224 | } 225 | 226 | for (i = 0; i < mch2.threads_limit; i++) 227 | if (!mch2.threads[i].keep) 228 | svcCloseHandle(mch2.threads[i].handle); 229 | 230 | svcCreateEvent(&mch2.dummy_threads_lock, 1); 231 | svcClearEvent(mch2.dummy_threads_lock); 232 | 233 | for (i = 0; i < mch2.threads_limit; i++) 234 | if (!mch2.threads[i].keep) 235 | svcCreateThread(&mch2.threads[i].handle, (ThreadFunc)dummy_thread_entry, mch2.dummy_threads_lock, 236 | mch2.threads[i].stack_top, 0x3F - i, 0); 237 | 238 | svcSignalEvent(mch2.dummy_threads_lock); 239 | 240 | for (i = mch2.threads_limit - 1; i >= 0; i--) 241 | if (!mch2.threads[i].keep) 242 | { 243 | svcWaitSynchronization(mch2.threads[i].handle, U64_MAX); 244 | svcCloseHandle(mch2.threads[i].handle); 245 | mch2.threads[i].handle = 0; 246 | } 247 | 248 | svcSleepThread(40000000LL); 249 | svcCloseHandle(mch2.dummy_threads_lock); 250 | 251 | u32 fragmented_address = 0; 252 | 253 | mch2.arbiter = __sync_get_arbiter(); 254 | 255 | u32 linear_buffer; 256 | svcControlMemory(&linear_buffer, 0, 0, 0x1000, MEMOP_ALLOC_LINEAR, MEMPERM_READ | MEMPERM_WRITE); 257 | 258 | u32 linear_size = 0xF000; 259 | u32 skip_pages = 2; 260 | mch2.alloc_size = ((((linear_size - (skip_pages << 12)) + 0x1000) >> 13) << 12); 261 | u32 mem_free = osGetMemRegionFree(MEMREGION_APPLICATION); 262 | 263 | u32 fragmented_size = mem_free - linear_size; 264 | extern u32 __ctru_heap; 265 | extern u32 __ctru_heap_size; 266 | fragmented_address = __ctru_heap + __ctru_heap_size; 267 | u32 linear_address; 268 | mch2.alloc_address = fragmented_address + fragmented_size; 269 | 270 | svcControlMemory(&linear_address, 0x0, 0x0, linear_size, MEMOP_ALLOC_LINEAR, 271 | MEMPERM_READ | MEMPERM_WRITE); 272 | 273 | if (fragmented_size) 274 | svcControlMemory(&tmp, (u32)fragmented_address, 0x0, fragmented_size, MEMOP_ALLOC, 275 | MEMPERM_READ | MEMPERM_WRITE); 276 | 277 | if (skip_pages) 278 | svcControlMemory(&tmp, (u32)linear_address, 0x0, (skip_pages << 12), MEMOP_FREE, MEMPERM_DONTCARE); 279 | 280 | for (i = skip_pages; i < (linear_size >> 12) ; i += 2) 281 | svcControlMemory(&tmp, (u32)linear_address + (i << 12), 0x0, 0x1000, MEMOP_FREE, MEMPERM_DONTCARE); 282 | 283 | u32 alloc_address_kaddr = osConvertVirtToPhys((void*)linear_address) + mch2.kernel_fcram_mapping_offset; 284 | 285 | mch2.thread_page_kva = get_first_free_basemem_page(mch2.isNew3DS) - 0x10000; // skip down 16 pages 286 | ((u32*)linear_buffer)[0] = 1; 287 | ((u32*)linear_buffer)[1] = mch2.thread_page_kva; 288 | ((u32*)linear_buffer)[2] = alloc_address_kaddr + (((mch2.alloc_size >> 12) - 3) << 13) + (skip_pages << 12); 289 | 290 | u32 dst_memchunk = linear_address + (((mch2.alloc_size >> 12) - 2) << 13) + (skip_pages << 12); 291 | 292 | memcpy(flush_buffer, flush_buffer + 0x4000, 0x4000); 293 | 294 | GSPGPU_InvalidateDataCache((void*)dst_memchunk, 16); 295 | GSPGPU_FlushDataCache((void*)linear_buffer, 16); 296 | memcpy(flush_buffer, flush_buffer + 0x4000, 0x4000); 297 | 298 | svcCreateThread(&mch2.alloc_thread, (ThreadFunc)alloc_thread_entry, (u32)&mch2, 299 | mch2.threads[MCH2_THREAD_COUNT_MAX - 1].stack_top, 0x3F, 1); 300 | 301 | while ((u32) svcArbitrateAddress(mch2.arbiter, mch2.alloc_address, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, 0, 302 | 0) == 0xD9001814); 303 | 304 | GX_TextureCopy((void*)linear_buffer, 0, (void*)dst_memchunk, 0, 16, 8); 305 | memcpy(flush_buffer, flush_buffer + 0x4000, 0x4000); 306 | gspWaitForPPF(); 307 | 308 | svcWaitSynchronization(mch2.alloc_thread, U64_MAX); 309 | svcCloseHandle(mch2.alloc_thread); 310 | 311 | u32* mapped_page = (u32*)(mch2.alloc_address + mch2.alloc_size - 0x1000); 312 | 313 | volatile u32* thread_ACL = &mapped_page[THREAD_PAGE_ACL_OFFSET >> 2]; 314 | 315 | svcCreateEvent(&mch2.main_thread_lock, 0); 316 | svcCreateEvent(&mch2.target_threads_lock, 1); 317 | svcClearEvent(mch2.target_threads_lock); 318 | 319 | for (i = 0; i < mch2.threads_limit; i++) 320 | { 321 | if (mch2.threads[i].keep) 322 | continue; 323 | 324 | mch2.threads[i].args.started_event = mch2.main_thread_lock; 325 | mch2.threads[i].args.lock = mch2.target_threads_lock; 326 | mch2.threads[i].args.target_kaddr = 0; 327 | 328 | thread_ACL[0] = 0; 329 | GSPGPU_FlushDataCache((void*)thread_ACL, 16); 330 | GSPGPU_InvalidateDataCache((void*)thread_ACL, 16); 331 | 332 | svcClearEvent(mch2.main_thread_lock); 333 | svcCreateThread(&mch2.threads[i].handle, (ThreadFunc)target_thread_entry, (u32)&mch2.threads[i].args, 334 | mch2.threads[i].stack_top, 0x18, 0); 335 | svcWaitSynchronization(mch2.main_thread_lock, U64_MAX); 336 | 337 | if (thread_ACL[0]) 338 | { 339 | thread_ACL[SVC_ACL_OFFSET(0x7B) >> 2] = SVC_ACL_MASK(0x7B); 340 | GSPGPU_FlushDataCache((void*)thread_ACL, 16); 341 | GSPGPU_InvalidateDataCache((void*)thread_ACL, 16); 342 | mch2.threads[i].args.target_kaddr = get_thread_page() + THREAD_PAGE_ACL_OFFSET + SVC_ACL_OFFSET(0x7B); 343 | mch2.threads[i].args.target_val = SVC_ACL_MASK(0x7B); 344 | break; 345 | } 346 | 347 | } 348 | 349 | svcSignalEvent(mch2.target_threads_lock); 350 | 351 | for (i = 0; i < mch2.threads_limit; i++) 352 | { 353 | if (!mch2.threads[i].handle) 354 | continue; 355 | 356 | if (!mch2.threads[i].keep) 357 | svcWaitSynchronization(mch2.threads[i].handle, U64_MAX); 358 | 359 | svcCloseHandle(mch2.threads[i].handle); 360 | } 361 | 362 | svcCloseHandle(mch2.target_threads_lock); 363 | svcCloseHandle(mch2.main_thread_lock); 364 | 365 | svcControlMemory(&tmp, mch2.alloc_address, 0, mch2.alloc_size, MEMOP_FREE, MEMPERM_DONTCARE); 366 | write_kaddr(alloc_address_kaddr + linear_size - 0x3000 + 0x4, alloc_address_kaddr + linear_size - 0x1000); 367 | svcControlMemory(&tmp, (u32)fragmented_address, 0x0, fragmented_size, MEMOP_FREE, MEMPERM_DONTCARE); 368 | 369 | for (i = 1 + skip_pages; i < (linear_size >> 12) ; i += 2) 370 | svcControlMemory(&tmp, (u32)linear_address + (i << 12), 0x0, 0x1000, MEMOP_FREE, MEMPERM_DONTCARE); 371 | 372 | svcControlMemory(&tmp, linear_buffer, 0, 0x1000, MEMOP_FREE, MEMPERM_DONTCARE); 373 | 374 | APT_SetAppCpuTimeLimit(mch2.old_cpu_time_limit); 375 | } 376 | 377 | 378 | static void gspwn(u32 dst, u32 src, u32 size, u8* flush_buffer) 379 | { 380 | memcpy(flush_buffer, flush_buffer + 0x4000, 0x4000); 381 | GSPGPU_InvalidateDataCache((void*)dst, size); 382 | GSPGPU_FlushDataCache((void*)src, size); 383 | memcpy(flush_buffer, flush_buffer + 0x4000, 0x4000); 384 | 385 | GX_TextureCopy((void*)src, 0, (void*)dst, 0, size, 8); 386 | gspWaitForPPF(); 387 | 388 | memcpy(flush_buffer, flush_buffer + 0x4000, 0x4000); 389 | } 390 | 391 | /* pseudo-code: 392 | * if(val2) 393 | * { 394 | * *(u32*)val1 = val2; 395 | * *(u32*)(val2 + 8) = (val1 - 4); 396 | * } 397 | * else 398 | * *(u32*)val1 = 0x0; 399 | */ 400 | 401 | // X-X--X-X 402 | // X-XXXX-X 403 | 404 | static void memchunkhax1_write_pair(u32 val1, u32 val2) 405 | { 406 | u32 linear_buffer; 407 | u8* flush_buffer; 408 | u32 tmp; 409 | 410 | u32* next_ptr3; 411 | u32* prev_ptr3; 412 | 413 | u32* next_ptr1; 414 | u32* prev_ptr6; 415 | 416 | svcControlMemory(&linear_buffer, 0, 0, 0x10000, MEMOP_ALLOC_LINEAR, MEMPERM_READ | MEMPERM_WRITE); 417 | 418 | flush_buffer = (u8*)(linear_buffer + 0x8000); 419 | 420 | svcControlMemory(&tmp, linear_buffer + 0x1000, 0, 0x1000, MEMOP_FREE, 0); 421 | svcControlMemory(&tmp, linear_buffer + 0x3000, 0, 0x2000, MEMOP_FREE, 0); 422 | svcControlMemory(&tmp, linear_buffer + 0x6000, 0, 0x1000, MEMOP_FREE, 0); 423 | 424 | next_ptr1 = (u32*)(linear_buffer + 0x0004); 425 | gspwn(linear_buffer + 0x0000, linear_buffer + 0x1000, 16, flush_buffer); 426 | 427 | next_ptr3 = (u32*)(linear_buffer + 0x2004); 428 | prev_ptr3 = (u32*)(linear_buffer + 0x2008); 429 | gspwn(linear_buffer + 0x2000, linear_buffer + 0x3000, 16, flush_buffer); 430 | 431 | prev_ptr6 = (u32*)(linear_buffer + 0x5008); 432 | gspwn(linear_buffer + 0x5000, linear_buffer + 0x6000, 16, flush_buffer); 433 | 434 | *next_ptr1 = *next_ptr3; 435 | *prev_ptr6 = *prev_ptr3; 436 | 437 | *prev_ptr3 = val1 - 4; 438 | *next_ptr3 = val2; 439 | gspwn(linear_buffer + 0x3000, linear_buffer + 0x2000, 16, flush_buffer); 440 | svcControlMemory(&tmp, 0, 0, 0x2000, MEMOP_ALLOC_LINEAR, MEMPERM_READ | MEMPERM_WRITE); 441 | 442 | gspwn(linear_buffer + 0x1000, linear_buffer + 0x0000, 16, flush_buffer); 443 | gspwn(linear_buffer + 0x6000, linear_buffer + 0x5000, 16, flush_buffer); 444 | 445 | svcControlMemory(&tmp, linear_buffer + 0x0000, 0, 0x1000, MEMOP_FREE, 0); 446 | svcControlMemory(&tmp, linear_buffer + 0x2000, 0, 0x4000, MEMOP_FREE, 0); 447 | svcControlMemory(&tmp, linear_buffer + 0x7000, 0, 0x9000, MEMOP_FREE, 0); 448 | 449 | } 450 | 451 | static void do_memchunkhax1(void) 452 | { 453 | u32 saved_vram_value = *(u32*)0x1F000008; 454 | 455 | // 0x1F000000 contains the enable bit for svc 0x7B 456 | memchunkhax1_write_pair(get_thread_page() + THREAD_PAGE_ACL_OFFSET + SVC_ACL_OFFSET(0x7B), 0x1F000000); 457 | 458 | write_kaddr(0x1F000008, saved_vram_value); 459 | } 460 | 461 | Result svchax_init(bool patch_srv) 462 | { 463 | bool isNew3DS; 464 | APT_CheckNew3DS(&isNew3DS); 465 | 466 | u32 kver = osGetKernelVersion(); 467 | 468 | if (!__ctr_svchax) 469 | { 470 | if (__service_ptr) 471 | { 472 | if (kver > SYSTEM_VERSION(2, 50, 11)) 473 | return -1; 474 | else if (kver > SYSTEM_VERSION(2, 46, 0)) 475 | do_memchunkhax2(); 476 | else 477 | do_memchunkhax1(); 478 | } 479 | 480 | svc_7b((backdoor_fn)k_enable_all_svcs, isNew3DS); 481 | 482 | __ctr_svchax = 1; 483 | } 484 | 485 | if (patch_srv && !__ctr_svchax_srv) 486 | { 487 | u32 PID_kaddr = read_kaddr(CURRENT_KPROCESS) + (isNew3DS ? 0xBC : (kver > SYSTEM_VERSION(2, 40, 0)) ? 0xB4 : 0xAC); 488 | u32 old_PID = read_kaddr(PID_kaddr); 489 | write_kaddr(PID_kaddr, 0); 490 | srvExit(); 491 | srvInit(); 492 | write_kaddr(PID_kaddr, old_PID); 493 | 494 | __ctr_svchax_srv = 1; 495 | } 496 | 497 | return 0; 498 | } 499 | -------------------------------------------------------------------------------- /source/data.h: -------------------------------------------------------------------------------- 1 | #define TICKET_SIZE 0xA50 2 | const char tikTemp[TICKET_SIZE] = { 3 | 0x00, 0x01, 0x00, 0x04, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 4 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 5 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 6 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 7 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 8 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 9 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 10 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 11 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 12 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 13 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 14 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 15 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 16 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 17 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 18 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 19 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 20 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 21 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 22 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 23 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 24 | 0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0, 0x00, 0x00, 0x00, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 30 | 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x2D, 31 | 0x58, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x63, 0x00, 0x00, 32 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 36 | 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 37 | 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 38 | 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 39 | 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 0xFE, 0xED, 0xFA, 0xCE, 40 | 0x01, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 41 | 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 43 | 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, 60 | 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, 62 | 0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 64 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 65 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 74 | 0x91, 0x9E, 0xBE, 0x46, 0x4A, 0xD0, 0xF5, 0x52, 0xCD, 0x1B, 0x72, 0xE7, 75 | 0x88, 0x49, 0x10, 0xCF, 0x55, 0xA9, 0xF0, 0x2E, 0x50, 0x78, 0x96, 0x41, 76 | 0xD8, 0x96, 0x68, 0x3D, 0xC0, 0x05, 0xBD, 0x0A, 0xEA, 0x87, 0x07, 0x9D, 77 | 0x8A, 0xC2, 0x84, 0xC6, 0x75, 0x06, 0x5F, 0x74, 0xC8, 0xBF, 0x37, 0xC8, 78 | 0x80, 0x44, 0x40, 0x95, 0x02, 0xA0, 0x22, 0x98, 0x0B, 0xB8, 0xAD, 0x48, 79 | 0x38, 0x3F, 0x6D, 0x28, 0xA7, 0x9D, 0xE3, 0x96, 0x26, 0xCC, 0xB2, 0xB2, 80 | 0x2A, 0x0F, 0x19, 0xE4, 0x10, 0x32, 0xF0, 0x94, 0xB3, 0x9F, 0xF0, 0x13, 81 | 0x31, 0x46, 0xDE, 0xC8, 0xF6, 0xC1, 0xA9, 0xD5, 0x5C, 0xD2, 0x8D, 0x9E, 82 | 0x1C, 0x47, 0xB3, 0xD1, 0x1F, 0x4F, 0x54, 0x26, 0xC2, 0xC7, 0x80, 0x13, 83 | 0x5A, 0x27, 0x75, 0xD3, 0xCA, 0x67, 0x9B, 0xC7, 0xE8, 0x34, 0xF0, 0xE0, 84 | 0xFB, 0x58, 0xE6, 0x88, 0x60, 0xA7, 0x13, 0x30, 0xFC, 0x95, 0x79, 0x17, 85 | 0x93, 0xC8, 0xFB, 0xA9, 0x35, 0xA7, 0xA6, 0x90, 0x8F, 0x22, 0x9D, 0xEE, 86 | 0x2A, 0x0C, 0xA6, 0xB9, 0xB2, 0x3B, 0x12, 0xD4, 0x95, 0xA6, 0xFE, 0x19, 87 | 0xD0, 0xD7, 0x26, 0x48, 0x21, 0x68, 0x78, 0x60, 0x5A, 0x66, 0x53, 0x8D, 88 | 0xBF, 0x37, 0x68, 0x99, 0x90, 0x5D, 0x34, 0x45, 0xFC, 0x5C, 0x72, 0x7A, 89 | 0x0E, 0x13, 0xE0, 0xE2, 0xC8, 0x97, 0x1C, 0x9C, 0xFA, 0x6C, 0x60, 0x67, 90 | 0x88, 0x75, 0x73, 0x2A, 0x4E, 0x75, 0x52, 0x3D, 0x2F, 0x56, 0x2F, 0x12, 91 | 0xAA, 0xBD, 0x15, 0x73, 0xBF, 0x06, 0xC9, 0x40, 0x54, 0xAE, 0xFA, 0x81, 92 | 0xA7, 0x14, 0x17, 0xAF, 0x9A, 0x4A, 0x06, 0x6D, 0x0F, 0xFC, 0x5A, 0xD6, 93 | 0x4B, 0xAB, 0x28, 0xB1, 0xFF, 0x60, 0x66, 0x1F, 0x44, 0x37, 0xD4, 0x9E, 94 | 0x1E, 0x0D, 0x94, 0x12, 0xEB, 0x4B, 0xCA, 0xCF, 0x4C, 0xFD, 0x6A, 0x34, 95 | 0x08, 0x84, 0x79, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 100 | 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 101 | 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 106 | 0x58, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x63, 0x00, 0x00, 107 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 111 | 0x00, 0x00, 0x00, 0x00, 0x13, 0x7A, 0x08, 0x94, 0xAD, 0x50, 0x5B, 0xB6, 112 | 0xC6, 0x7E, 0x2E, 0x5B, 0xDD, 0x6A, 0x3B, 0xEC, 0x43, 0xD9, 0x10, 0xC7, 113 | 0x72, 0xE9, 0xCC, 0x29, 0x0D, 0xA5, 0x85, 0x88, 0xB7, 0x7D, 0xCC, 0x11, 114 | 0x68, 0x0B, 0xB3, 0xE2, 0x9F, 0x4E, 0xAB, 0xBB, 0x26, 0xE9, 0x8C, 0x26, 115 | 0x01, 0x98, 0x5C, 0x04, 0x1B, 0xB1, 0x43, 0x78, 0xE6, 0x89, 0x18, 0x1A, 116 | 0xAD, 0x77, 0x05, 0x68, 0xE9, 0x28, 0xA2, 0xB9, 0x81, 0x67, 0xEE, 0x3E, 117 | 0x10, 0xD0, 0x72, 0xBE, 0xEF, 0x1F, 0xA2, 0x2F, 0xA2, 0xAA, 0x3E, 0x13, 118 | 0xF1, 0x1E, 0x18, 0x36, 0xA9, 0x2A, 0x42, 0x81, 0xEF, 0x70, 0xAA, 0xF4, 119 | 0xE4, 0x62, 0x99, 0x82, 0x21, 0xC6, 0xFB, 0xB9, 0xBD, 0xD0, 0x17, 0xE6, 120 | 0xAC, 0x59, 0x04, 0x94, 0xE9, 0xCE, 0xA9, 0x85, 0x9C, 0xEB, 0x2D, 0x2A, 121 | 0x4C, 0x17, 0x66, 0xF2, 0xC3, 0x39, 0x12, 0xC5, 0x8F, 0x14, 0xA8, 0x03, 122 | 0xE3, 0x6F, 0xCC, 0xDC, 0xCC, 0xDC, 0x13, 0xFD, 0x7A, 0xE7, 0x7C, 0x7A, 123 | 0x78, 0xD9, 0x97, 0xE6, 0xAC, 0xC3, 0x55, 0x57, 0xE0, 0xD3, 0xE9, 0xEB, 124 | 0x64, 0xB4, 0x3C, 0x92, 0xF4, 0xC5, 0x0D, 0x67, 0xA6, 0x02, 0xDE, 0xB3, 125 | 0x91, 0xB0, 0x66, 0x61, 0xCD, 0x32, 0x88, 0x0B, 0xD6, 0x49, 0x12, 0xAF, 126 | 0x1C, 0xBC, 0xB7, 0x16, 0x2A, 0x06, 0xF0, 0x25, 0x65, 0xD3, 0xB0, 0xEC, 127 | 0xE4, 0xFC, 0xEC, 0xDD, 0xAE, 0x8A, 0x49, 0x34, 0xDB, 0x8E, 0xE6, 0x7F, 128 | 0x30, 0x17, 0x98, 0x62, 0x21, 0x15, 0x5D, 0x13, 0x1C, 0x6C, 0x3F, 0x09, 129 | 0xAB, 0x19, 0x45, 0xC2, 0x06, 0xAC, 0x70, 0xC9, 0x42, 0xB3, 0x6F, 0x49, 130 | 0xA1, 0x18, 0x3B, 0xCD, 0x78, 0xB6, 0xE4, 0xB4, 0x7C, 0x6C, 0x5C, 0xAC, 131 | 0x0F, 0x8D, 0x62, 0xF8, 0x97, 0xC6, 0x95, 0x3D, 0xD1, 0x2F, 0x28, 0xB7, 132 | 0x0C, 0x5B, 0x7D, 0xF7, 0x51, 0x81, 0x9A, 0x98, 0x34, 0x65, 0x26, 0x25, 133 | 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 137 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 138 | 0x70, 0x41, 0x38, 0xEF, 0xBB, 0xBD, 0xA1, 0x6A, 0x98, 0x7D, 0xD9, 0x01, 139 | 0x32, 0x6D, 0x1C, 0x94, 0x59, 0x48, 0x4C, 0x88, 0xA2, 0x86, 0x1B, 0x91, 140 | 0xA3, 0x12, 0x58, 0x7A, 0xE7, 0x0E, 0xF6, 0x23, 0x7E, 0xC5, 0x0E, 0x10, 141 | 0x32, 0xDC, 0x39, 0xDD, 0xE8, 0x9A, 0x96, 0xA8, 0xE8, 0x59, 0xD7, 0x6A, 142 | 0x98, 0xA6, 0xE7, 0xE3, 0x6A, 0x0C, 0xFE, 0x35, 0x2C, 0xA8, 0x93, 0x05, 143 | 0x82, 0x34, 0xFF, 0x83, 0x3F, 0xCB, 0x3B, 0x03, 0x81, 0x1E, 0x9F, 0x0D, 144 | 0xC0, 0xD9, 0xA5, 0x2F, 0x80, 0x45, 0xB4, 0xB2, 0xF9, 0x41, 0x1B, 0x67, 145 | 0xA5, 0x1C, 0x44, 0xB5, 0xEF, 0x8C, 0xE7, 0x7B, 0xD6, 0xD5, 0x6B, 0xA7, 146 | 0x57, 0x34, 0xA1, 0x85, 0x6D, 0xE6, 0xD4, 0xBE, 0xD6, 0xD3, 0xA2, 0x42, 147 | 0xC7, 0xC8, 0x79, 0x1B, 0x34, 0x22, 0x37, 0x5E, 0x5C, 0x77, 0x9A, 0xBF, 148 | 0x07, 0x2F, 0x76, 0x95, 0xEF, 0xA0, 0xF7, 0x5B, 0xCB, 0x83, 0x78, 0x9F, 149 | 0xC3, 0x0E, 0x3F, 0xE4, 0xCC, 0x83, 0x92, 0x20, 0x78, 0x40, 0x63, 0x89, 150 | 0x49, 0xC7, 0xF6, 0x88, 0x56, 0x5F, 0x64, 0x9B, 0x74, 0xD6, 0x3D, 0x8D, 151 | 0x58, 0xFF, 0xAD, 0xDA, 0x57, 0x1E, 0x95, 0x54, 0x42, 0x6B, 0x13, 0x18, 152 | 0xFC, 0x46, 0x89, 0x83, 0xD4, 0xC8, 0xA5, 0x62, 0x8B, 0x06, 0xB6, 0xFC, 153 | 0x5D, 0x50, 0x7C, 0x13, 0xE7, 0xA1, 0x8A, 0xC1, 0x51, 0x1E, 0xB6, 0xD6, 154 | 0x2E, 0xA5, 0x44, 0x8F, 0x83, 0x50, 0x14, 0x47, 0xA9, 0xAF, 0xB3, 0xEC, 155 | 0xC2, 0x90, 0x3C, 0x9D, 0xD5, 0x2F, 0x92, 0x2A, 0xC9, 0xAC, 0xDB, 0xEF, 156 | 0x58, 0xC6, 0x02, 0x18, 0x48, 0xD9, 0x6E, 0x20, 0x87, 0x32, 0xD3, 0xD1, 157 | 0xD9, 0xD9, 0xEA, 0x44, 0x0D, 0x91, 0x62, 0x1C, 0x7A, 0x99, 0xDB, 0x88, 158 | 0x43, 0xC5, 0x9C, 0x1F, 0x2E, 0x2C, 0x7D, 0x9B, 0x57, 0x7D, 0x51, 0x2C, 159 | 0x16, 0x6D, 0x6F, 0x7E, 0x1A, 0xAD, 0x4A, 0x77, 0x4A, 0x37, 0x44, 0x7E, 160 | 0x78, 0xFE, 0x20, 0x21, 0xE1, 0x4A, 0x95, 0xD1, 0x12, 0xA0, 0x68, 0xAD, 161 | 0xA0, 0x19, 0xF4, 0x63, 0xC7, 0xA5, 0x56, 0x85, 0xAA, 0xBB, 0x68, 0x88, 162 | 0xB9, 0x24, 0x64, 0x83, 0xD1, 0x8B, 0x9C, 0x80, 0x6F, 0x47, 0x49, 0x18, 163 | 0x33, 0x17, 0x82, 0x34, 0x4A, 0x4B, 0x85, 0x31, 0x33, 0x4B, 0x26, 0x30, 164 | 0x32, 0x63, 0xD9, 0xD2, 0xEB, 0x4F, 0x4B, 0xB9, 0x96, 0x02, 0xB3, 0x52, 165 | 0xF6, 0xAE, 0x40, 0x46, 0xC6, 0x9A, 0x5E, 0x7E, 0x8E, 0x4A, 0x18, 0xEF, 166 | 0x9B, 0xC0, 0xA2, 0xDE, 0xD6, 0x13, 0x10, 0x41, 0x70, 0x12, 0xFD, 0x82, 167 | 0x4C, 0xC1, 0x16, 0xCF, 0xB7, 0xC4, 0xC1, 0xF7, 0xEC, 0x71, 0x77, 0xA1, 168 | 0x74, 0x46, 0xCB, 0xDE, 0x96, 0xF3, 0xED, 0xD8, 0x8F, 0xCD, 0x05, 0x2F, 169 | 0x0B, 0x88, 0x8A, 0x45, 0xFD, 0xAF, 0x2B, 0x63, 0x13, 0x54, 0xF4, 0x0D, 170 | 0x16, 0xE5, 0xFA, 0x9C, 0x2C, 0x4E, 0xDA, 0x98, 0xE7, 0x98, 0xD1, 0x5E, 171 | 0x60, 0x46, 0xDC, 0x53, 0x63, 0xF3, 0x09, 0x6B, 0x2C, 0x60, 0x7A, 0x9D, 172 | 0x8D, 0xD5, 0x5B, 0x15, 0x02, 0xA6, 0xAC, 0x7D, 0x3C, 0xC8, 0xD8, 0xC5, 173 | 0x75, 0x99, 0x8E, 0x7D, 0x79, 0x69, 0x10, 0xC8, 0x04, 0xC4, 0x95, 0x23, 174 | 0x50, 0x57, 0xE9, 0x1E, 0xCD, 0x26, 0x37, 0xC9, 0xC1, 0x84, 0x51, 0x51, 175 | 0xAC, 0x6B, 0x9A, 0x04, 0x90, 0xAE, 0x3E, 0xC6, 0xF4, 0x77, 0x40, 0xA0, 176 | 0xDB, 0x0B, 0xA3, 0x6D, 0x07, 0x59, 0x56, 0xCE, 0xE7, 0x35, 0x4E, 0xA3, 177 | 0xE9, 0xA4, 0xF2, 0x72, 0x0B, 0x26, 0x55, 0x0C, 0x7D, 0x39, 0x43, 0x24, 178 | 0xBC, 0x0C, 0xB7, 0xE9, 0x31, 0x7D, 0x8A, 0x86, 0x61, 0xF4, 0x21, 0x91, 179 | 0xFF, 0x10, 0xB0, 0x82, 0x56, 0xCE, 0x3F, 0xD2, 0x5B, 0x74, 0x5E, 0x51, 180 | 0x94, 0x90, 0x6B, 0x4D, 0x61, 0xCB, 0x4C, 0x2E, 0x00, 0x00, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 183 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 186 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 187 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 188 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 189 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 190 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 191 | 0x00, 0x00, 0x00, 0x01, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 192 | 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 193 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 194 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 195 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 196 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xE8, 0xEF, 0x6C, 197 | 0xB2, 0x79, 0xC9, 0xE2, 0xEE, 0xE1, 0x21, 0xC6, 0xEA, 0xF4, 0x4F, 0xF6, 198 | 0x39, 0xF8, 0x8F, 0x07, 0x8B, 0x4B, 0x77, 0xED, 0x9F, 0x95, 0x60, 0xB0, 199 | 0x35, 0x82, 0x81, 0xB5, 0x0E, 0x55, 0xAB, 0x72, 0x11, 0x15, 0xA1, 0x77, 200 | 0x70, 0x3C, 0x7A, 0x30, 0xFE, 0x3A, 0xE9, 0xEF, 0x1C, 0x60, 0xBC, 0x1D, 201 | 0x97, 0x46, 0x76, 0xB2, 0x3A, 0x68, 0xCC, 0x04, 0xB1, 0x98, 0x52, 0x5B, 202 | 0xC9, 0x68, 0xF1, 0x1D, 0xE2, 0xDB, 0x50, 0xE4, 0xD9, 0xE7, 0xF0, 0x71, 203 | 0xE5, 0x62, 0xDA, 0xE2, 0x09, 0x22, 0x33, 0xE9, 0xD3, 0x63, 0xF6, 0x1D, 204 | 0xD7, 0xC1, 0x9F, 0xF3, 0xA4, 0xA9, 0x1E, 0x8F, 0x65, 0x53, 0xD4, 0x71, 205 | 0xDD, 0x7B, 0x84, 0xB9, 0xF1, 0xB8, 0xCE, 0x73, 0x35, 0xF0, 0xF5, 0x54, 206 | 0x05, 0x63, 0xA1, 0xEA, 0xB8, 0x39, 0x63, 0xE0, 0x9B, 0xE9, 0x01, 0x01, 207 | 0x1F, 0x99, 0x54, 0x63, 0x61, 0x28, 0x70, 0x20, 0xE9, 0xCC, 0x0D, 0xAB, 208 | 0x48, 0x7F, 0x14, 0x0D, 0x66, 0x26, 0xA1, 0x83, 0x6D, 0x27, 0x11, 0x1F, 209 | 0x20, 0x68, 0xDE, 0x47, 0x72, 0x14, 0x91, 0x51, 0xCF, 0x69, 0xC6, 0x1B, 210 | 0xA6, 0x0E, 0xF9, 0xD9, 0x49, 0xA0, 0xF7, 0x1F, 0x54, 0x99, 0xF2, 0xD3, 211 | 0x9A, 0xD2, 0x8C, 0x70, 0x05, 0x34, 0x82, 0x93, 0xC4, 0x31, 0xFF, 0xBD, 212 | 0x33, 0xF6, 0xBC, 0xA6, 0x0D, 0xC7, 0x19, 0x5E, 0xA2, 0xBC, 0xC5, 0x6D, 213 | 0x20, 0x0B, 0xAF, 0x6D, 0x06, 0xD0, 0x9C, 0x41, 0xDB, 0x8D, 0xE9, 0xC7, 214 | 0x20, 0x15, 0x4C, 0xA4, 0x83, 0x2B, 0x69, 0xC0, 0x8C, 0x69, 0xCD, 0x3B, 215 | 0x07, 0x3A, 0x00, 0x63, 0x60, 0x2F, 0x46, 0x2D, 0x33, 0x80, 0x61, 0xA5, 216 | 0xEA, 0x6C, 0x91, 0x5C, 0xD5, 0x62, 0x35, 0x79, 0xC3, 0xEB, 0x64, 0xCE, 217 | 0x44, 0xEF, 0x58, 0x6D, 0x14, 0xBA, 0xAA, 0x88, 0x34, 0x01, 0x9B, 0x3E, 218 | 0xEB, 0xEE, 0xD3, 0x79, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 219 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 220 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 221 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 222 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 223 | }; 224 | -------------------------------------------------------------------------------- /source/cia.c: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 3DSGuy 3 | 4 | This file is part of make_cdn_cia. 5 | 6 | make_cdn_cia is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | make_cdn_cia is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with make_cdn_cia. If not, see . 18 | **/ 19 | #include "lib.h" 20 | #include "cia.h" 21 | #include <3ds.h> 22 | #define TRUE 1 23 | #define FALSE 0 24 | 25 | int generate_cia(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output) 26 | { 27 | write_cia_header(tmd_context,tik_context,output); 28 | write_cert_chain(tmd_context,tik_context,output); 29 | write_tik(tmd_context,tik_context,output); 30 | write_tmd(tmd_context,tik_context,output); 31 | Result res = write_content(tmd_context,tik_context,output); 32 | fclose(output); 33 | fclose(tik_context.tik); 34 | fclose(tmd_context.tmd); 35 | free(tmd_context.content_struct); 36 | 37 | return res; 38 | } 39 | 40 | int install_cia(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context) 41 | { 42 | Handle handle; 43 | Result res; 44 | u64 titleId = get_title_id(tmd_context); 45 | FS_MediaType dest = ((titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD; 46 | 47 | // Make sure this isn't a N3DS only title being installed on an O3DS 48 | bool n3ds = false; 49 | if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds) 50 | { 51 | // 28 bits shift = 2 is a system title for N3ds 52 | // 24 bits shift = F is a N3DS exclusive game (Seems to always have 0xF7 in the titleid) 53 | if (((titleId >> 28) & 0xF) == 2 || ((titleId >> 24) & 0xF) == 0xF) 54 | { 55 | printf("Title requires a N3DS.\n"); 56 | return -1; 57 | } 58 | } 59 | 60 | // Remove the ticket and title content, incase a bad one already exists on the system 61 | AM_DeleteTitle(dest, titleId); 62 | AM_DeleteTicket(titleId); 63 | 64 | if(dest == MEDIATYPE_SD) { 65 | AM_QueryAvailableExternalTitleDatabase(NULL); 66 | } 67 | 68 | res = AM_StartCiaInstall(dest, &handle); 69 | if (R_FAILED(res)) 70 | { 71 | printf("Error starting CIA install: %ld.\n", res); 72 | return res; 73 | } 74 | 75 | u32 offset = 0; 76 | 77 | install_cia_header(tmd_context, tik_context, &offset, handle); 78 | install_cert_chain(tmd_context, tik_context, &offset, handle); 79 | install_tik(tmd_context, tik_context, &offset, handle); 80 | install_tmd(tmd_context, tik_context, &offset, handle); 81 | res = install_content(tmd_context, tik_context, &offset, handle); 82 | fclose(tik_context.tik); 83 | fclose(tmd_context.tmd); 84 | free(tmd_context.content_struct); 85 | 86 | if (R_FAILED(res)) 87 | { 88 | printf("Error installing CIA.\n"); 89 | AM_CancelCIAInstall(handle); 90 | return res; 91 | } 92 | 93 | res = AM_FinishCiaInstall(handle); 94 | if (R_FAILED(res)) 95 | { 96 | printf("Error finishing CIA install.\n"); 97 | } 98 | 99 | return res; 100 | } 101 | 102 | TIK_CONTEXT process_tik(FILE *tik) 103 | { 104 | TIK_CONTEXT tik_context; 105 | memset(&tik_context,0x0,sizeof(tik_context)); 106 | 107 | tik_context.tik = tik; 108 | 109 | u32 sig_size = get_sig_size(0x0,tik); 110 | if(sig_size == ERR_UNRECOGNISED_SIG){ 111 | printf("[!] The CETK signature could not be recognised\n"); 112 | tik_context.result = ERR_UNRECOGNISED_SIG; 113 | return tik_context; 114 | } 115 | 116 | TIK_STRUCT tik_struct = get_tik_struct(sig_size,tik); 117 | tik_context.tik_size = get_tik_size(sig_size); 118 | tik_context.title_version = u8_to_u16(tik_struct.title_version,BIG_ENDIAN); 119 | 120 | if(tik_context.tik_size == ERR_UNRECOGNISED_SIG){ 121 | tik_context.result = ERR_UNRECOGNISED_SIG; 122 | return tik_context; 123 | } 124 | 125 | tik_context.cert_offset[0] = tik_context.tik_size; 126 | tik_context.cert_size[0] = get_cert_size(tik_context.tik_size,tik); 127 | tik_context.cert_offset[1] = tik_context.tik_size + tik_context.cert_size[0]; 128 | tik_context.cert_size[1] = get_cert_size(tik_context.cert_offset[1],tik); 129 | 130 | if(tik_context.cert_size[0] == ERR_UNRECOGNISED_SIG || tik_context.cert_size[1] == ERR_UNRECOGNISED_SIG){ 131 | printf("[!] One or both of the signatures in the CETK 'Cert Chain' are unrecognised\n"); 132 | tik_context.result = ERR_UNRECOGNISED_SIG; 133 | return tik_context; 134 | } 135 | memcpy(tik_context.title_id,tik_struct.title_id,8); 136 | 137 | //printf("[+] CETK Title ID: "); u8_hex_print_be(tik_context.title_id,0x8); printf("\n"); 138 | //printf("[+] CETK Size: 0x%x\n",tik_context.tik_size); 139 | //printf("[+] CERT Size: 0x%x\n",tik_context.cert_size); 140 | 141 | return tik_context; 142 | } 143 | 144 | TMD_CONTEXT process_tmd(FILE *tmd) 145 | { 146 | TMD_CONTEXT tmd_context; 147 | memset(&tmd_context,0x0,sizeof(tmd_context)); 148 | 149 | tmd_context.tmd = tmd; 150 | 151 | u32 sig_size = get_sig_size(0x0,tmd); 152 | if(sig_size == ERR_UNRECOGNISED_SIG){ 153 | printf("[!] The TMD signature could not be recognised\n"); 154 | tmd_context.result = ERR_UNRECOGNISED_SIG; 155 | return tmd_context; 156 | } 157 | 158 | 159 | TMD_STRUCT tmd_struct = get_tmd_struct(sig_size,tmd); 160 | tmd_context.content_count = u8_to_u16(tmd_struct.content_count,BIG_ENDIAN); 161 | tmd_context.tmd_size = get_tmd_size(sig_size,tmd_context.content_count); 162 | tmd_context.title_version = u8_to_u16(tmd_struct.title_version,BIG_ENDIAN); 163 | 164 | tmd_context.cert_offset[0] = tmd_context.tmd_size; 165 | tmd_context.cert_size[0] = get_cert_size(tmd_context.tmd_size,tmd); 166 | tmd_context.cert_offset[1] = tmd_context.tmd_size + tmd_context.cert_size[0]; 167 | tmd_context.cert_size[1] = get_cert_size(tmd_context.cert_offset[1],tmd); 168 | 169 | if(tmd_context.cert_size[0] == ERR_UNRECOGNISED_SIG || tmd_context.cert_size[1] == ERR_UNRECOGNISED_SIG){ 170 | printf("[!] One or both of the signatures in the TMD 'Cert Chain' are unrecognised\n"); 171 | tmd_context.result = ERR_UNRECOGNISED_SIG; 172 | return tmd_context; 173 | } 174 | memcpy(tmd_context.title_id,tmd_struct.title_id,8); 175 | 176 | tmd_context.content_struct = malloc(sizeof(TMD_CONTENT_CHUNK_STRUCT)*tmd_context.content_count); 177 | for(u8 i = 0; i < tmd_context.content_count; i++){ 178 | tmd_context.content_struct[i] = get_tmd_content_struct(sig_size,i,tmd); 179 | } 180 | return tmd_context; 181 | } 182 | 183 | CIA_HEADER set_cia_header(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context) 184 | { 185 | CIA_HEADER cia_header; 186 | memset(&cia_header,0,sizeof(cia_header)); 187 | cia_header.header_size = sizeof(CIA_HEADER); 188 | cia_header.type = 0; 189 | cia_header.version = 0; 190 | cia_header.cert_size = get_total_cert_size(tmd_context,tik_context); 191 | cia_header.tik_size = tik_context.tik_size; 192 | cia_header.tmd_size = tmd_context.tmd_size; 193 | cia_header.meta_size = 0; 194 | cia_header.content_size = get_content_size(tmd_context); 195 | for(int i = 0; i < tmd_context.content_count; i++) { 196 | u16 index = u8_to_u16(tmd_context.content_struct[i].content_index, BIG_ENDIAN); 197 | cia_header.content_index[index / 8] |= 0x80 >> (index & 7); 198 | } 199 | return cia_header; 200 | } 201 | 202 | u32 get_tik_size(u32 sig_size) 203 | { 204 | return (0x4 + sig_size + sizeof(TIK_STRUCT)); 205 | } 206 | 207 | u32 get_tmd_size(u32 sig_size, u16 content_count) 208 | { 209 | return (0x4 + sig_size + sizeof(TMD_STRUCT) + sizeof(TMD_CONTENT_CHUNK_STRUCT)*content_count); 210 | } 211 | 212 | u32 get_sig_size(u32 offset, FILE *file) 213 | { 214 | fseek(file,offset,SEEK_SET); 215 | u32 sig_type; 216 | fread(&sig_type,0x4,1,file); 217 | switch(sig_type){ 218 | /** 219 | case(RSA_4096_SHA1): return 0x200; 220 | case(RSA_2048_SHA1): return 0x100; 221 | case(Elliptic_Curve_0): return 0x3C; 222 | **/ 223 | case(RSA_4096_SHA256): return 0x200; 224 | case(RSA_2048_SHA256): return 0x100; 225 | //case(Elliptic_Curve_1): return 0x3C; 226 | } 227 | return ERR_UNRECOGNISED_SIG; 228 | } 229 | 230 | u32 get_cert_size(u32 offset, FILE *file) 231 | { 232 | u32 sig_size = get_sig_size(offset,file); 233 | if(sig_size == ERR_UNRECOGNISED_SIG) 234 | return ERR_UNRECOGNISED_SIG; 235 | return (0x4+sig_size+sizeof(CERT_2048KEY_DATA_STRUCT)); 236 | } 237 | 238 | u32 get_total_cert_size(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context) 239 | { 240 | return (tik_context.cert_size[1] + tik_context.cert_size[0] + tmd_context.cert_size[0]); 241 | } 242 | 243 | u64 get_content_size(TMD_CONTEXT tmd_context) 244 | { 245 | u64 content_size = 0x0; 246 | for(int i = 0; i < tmd_context.content_count; i++) 247 | content_size += read_content_size(tmd_context.content_struct[i]); 248 | return content_size; 249 | } 250 | 251 | u64 read_content_size(TMD_CONTENT_CHUNK_STRUCT content_struct) 252 | { 253 | return u8_to_u64(content_struct.content_size,BIG_ENDIAN); 254 | } 255 | 256 | u32 get_content_id(TMD_CONTENT_CHUNK_STRUCT content_struct) 257 | { 258 | return u8_to_u32(content_struct.content_id,BIG_ENDIAN); 259 | } 260 | 261 | u64 get_title_id(TMD_CONTEXT content_struct) 262 | { 263 | return u8_to_u64(content_struct.title_id,BIG_ENDIAN); 264 | } 265 | 266 | int write_cia_header(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output) 267 | { 268 | CIA_HEADER cia_header = set_cia_header(tmd_context,tik_context); 269 | fseek(output,0x0,SEEK_SET); 270 | fwrite(&cia_header,sizeof(cia_header),1,output); 271 | 272 | // Make sure we end on a 64-byte boundry 273 | write_align_padding(output, 64); 274 | 275 | return 0; 276 | } 277 | 278 | int write_cert_chain(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output) 279 | { 280 | u8 cert[0x1000]; 281 | //The order of Certs in CIA goes, Root Cert, Cetk Cert, TMD Cert. In CDN format each file has it's own cert followed by a Root cert 282 | 283 | //Taking Root Cert from Cetk Cert chain(can be taken from TMD Cert Chain too) 284 | memset(cert,0x0,tik_context.cert_size[1]); 285 | fseek(tik_context.tik,tik_context.cert_offset[1],SEEK_SET); 286 | fread(&cert,tik_context.cert_size[1],1,tik_context.tik); 287 | fwrite(&cert,tik_context.cert_size[1],1,output); 288 | 289 | //Writing Cetk Cert 290 | memset(cert,0x0,tik_context.cert_size[0]); 291 | fseek(tik_context.tik,tik_context.cert_offset[0],SEEK_SET); 292 | fread(&cert,tik_context.cert_size[0],1,tik_context.tik); 293 | fwrite(&cert,tik_context.cert_size[0],1,output); 294 | 295 | //Writing TMD Cert 296 | memset(cert,0x0,tmd_context.cert_size[0]); 297 | fseek(tmd_context.tmd,tmd_context.cert_offset[0],SEEK_SET); 298 | fread(&cert,tmd_context.cert_size[0],1,tmd_context.tmd); 299 | fwrite(&cert,tmd_context.cert_size[0],1,output); 300 | 301 | // Make sure we end on a 64-byte boundry 302 | write_align_padding(output, 64); 303 | 304 | return 0; 305 | } 306 | 307 | int write_tik(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output) 308 | { 309 | u8 tik[tik_context.tik_size]; 310 | 311 | memset(tik,0x0,tik_context.tik_size); 312 | fseek(tik_context.tik,0x0,SEEK_SET); 313 | fread(&tik,tik_context.tik_size,1,tik_context.tik); 314 | fwrite(&tik,tik_context.tik_size,1,output); 315 | 316 | // Make sure we end on a 64-byte boundry 317 | write_align_padding(output, 64); 318 | 319 | return 0; 320 | } 321 | 322 | int write_tmd(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output) 323 | { 324 | u8 tmd[tmd_context.tmd_size]; 325 | memset(tmd,0x0,tmd_context.tmd_size); 326 | fseek(tmd_context.tmd,0x0,SEEK_SET); 327 | fread(&tmd,tmd_context.tmd_size,1,tmd_context.tmd); 328 | fwrite(&tmd,tmd_context.tmd_size,1,output); 329 | 330 | // Make sure we end on a 64-byte boundry 331 | write_align_padding(output, 64); 332 | 333 | return 0; 334 | } 335 | 336 | int write_content(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, FILE *output) 337 | { 338 | Result res = 0; 339 | for(int i = 0; i < tmd_context.content_count; i++) { 340 | printf("Downloading content %d of %d\n", i + 1, tmd_context.content_count); 341 | char content_id[16]; 342 | char title_id[32]; 343 | sprintf(content_id,"%08lx",get_content_id(tmd_context.content_struct[i])); 344 | sprintf(title_id,"%016llx",get_title_id(tmd_context)); 345 | 346 | char *url = malloc(48 + strlen(NUS_URL) + 1); 347 | sprintf(url, "%s%s/%s", NUS_URL, title_id, content_id); 348 | res = DownloadFile(url, output, true); 349 | free(url); 350 | 351 | if (R_FAILED(res)) 352 | { 353 | break; 354 | } 355 | 356 | } 357 | return res; 358 | } 359 | 360 | 361 | int install_cia_header(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle) 362 | { 363 | u32 bytesWritten; 364 | CIA_HEADER cia_header = set_cia_header(tmd_context,tik_context); 365 | 366 | FSFILE_Write(handle, &bytesWritten, *offset, &cia_header, sizeof(cia_header), 0); 367 | *offset += bytesWritten; 368 | 369 | // Make sure we end on a 64-byte boundry 370 | install_write_align_padding(handle, offset, 64); 371 | 372 | return 0; 373 | } 374 | 375 | int install_cert_chain(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle) 376 | { 377 | u32 bytesWritten; 378 | u8 cert[0x1000]; 379 | //The order of Certs in CIA goes, Root Cert, Cetk Cert, TMD Cert. In CDN format each file has it's own cert followed by a Root cert 380 | 381 | //Taking Root Cert from Cetk Cert chain(can be taken from TMD Cert Chain too) 382 | memset(cert,0x0,tik_context.cert_size[1]); 383 | fseek(tik_context.tik,tik_context.cert_offset[1],SEEK_SET); 384 | fread(&cert,tik_context.cert_size[1],1,tik_context.tik); 385 | FSFILE_Write(handle, &bytesWritten, *offset, &cert, tik_context.cert_size[1], 0); 386 | *offset += bytesWritten; 387 | 388 | //Writing Cetk Cert 389 | memset(cert,0x0,tik_context.cert_size[0]); 390 | fseek(tik_context.tik,tik_context.cert_offset[0],SEEK_SET); 391 | fread(&cert,tik_context.cert_size[0],1,tik_context.tik); 392 | FSFILE_Write(handle, &bytesWritten, *offset, &cert, tik_context.cert_size[0], 0); 393 | *offset += bytesWritten; 394 | 395 | //Writing TMD Cert 396 | memset(cert,0x0,tmd_context.cert_size[0]); 397 | fseek(tmd_context.tmd,tmd_context.cert_offset[0],SEEK_SET); 398 | fread(&cert,tmd_context.cert_size[0],1,tmd_context.tmd); 399 | FSFILE_Write(handle, &bytesWritten, *offset, &cert, tmd_context.cert_size[0], 0); 400 | *offset += bytesWritten; 401 | 402 | // Make sure we end on a 64-byte boundry 403 | install_write_align_padding(handle, offset, 64); 404 | 405 | return 0; 406 | } 407 | 408 | int install_tik(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle) 409 | { 410 | u32 bytesWritten; 411 | u8 tik[tik_context.tik_size]; 412 | 413 | memset(tik,0x0,tik_context.tik_size); 414 | fseek(tik_context.tik,0x0,SEEK_SET); 415 | fread(&tik,tik_context.tik_size,1,tik_context.tik); 416 | FSFILE_Write(handle, &bytesWritten, *offset, &tik, tik_context.tik_size, 0); 417 | *offset += bytesWritten; 418 | 419 | // Make sure we end on a 64-byte boundry 420 | install_write_align_padding(handle, offset, 64); 421 | 422 | return 0; 423 | } 424 | 425 | int install_tmd(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle) 426 | { 427 | u32 bytesWritten; 428 | u8 tmd[tmd_context.tmd_size]; 429 | memset(tmd,0x0,tmd_context.tmd_size); 430 | fseek(tmd_context.tmd,0x0,SEEK_SET); 431 | fread(&tmd,tmd_context.tmd_size,1,tmd_context.tmd); 432 | FSFILE_Write(handle, &bytesWritten, *offset, &tmd, tmd_context.tmd_size, 0); 433 | *offset += bytesWritten; 434 | 435 | // Make sure we end on a 64-byte boundry 436 | install_write_align_padding(handle, offset, 64); 437 | 438 | return 0; 439 | } 440 | 441 | Result install_content(TMD_CONTEXT tmd_context, TIK_CONTEXT tik_context, u32* offset, Handle handle) 442 | { 443 | Result res = 0; 444 | for(int i = 0; i < tmd_context.content_count; i++) { 445 | printf("Installing content %d of %d\n", i + 1, tmd_context.content_count); 446 | char content_id[16]; 447 | char title_id[32]; 448 | sprintf(content_id,"%08lx",get_content_id(tmd_context.content_struct[i])); 449 | sprintf(title_id,"%016llx",get_title_id(tmd_context)); 450 | 451 | char *url = malloc(48 + strlen(NUS_URL) + 1); 452 | sprintf(url, "%s%s/%s", NUS_URL, title_id, content_id); 453 | 454 | res = DownloadFileInstall(url, &handle, offset); 455 | free(url); 456 | 457 | if (R_FAILED(res)) 458 | { 459 | break; 460 | } 461 | } 462 | 463 | return res; 464 | } 465 | 466 | void install_write_align_padding(Handle handle, u32* offset, size_t alignment) 467 | { 468 | long int usedbytes = *offset & (alignment - 1); 469 | if (usedbytes) 470 | { 471 | u32 bytesWritten; 472 | // Create the padding strings 473 | long int padbytes = (alignment - usedbytes); 474 | char* pad = (char*)malloc(padbytes); 475 | memset(pad, 0, padbytes); 476 | 477 | // Write it, and increase the offset 478 | FSFILE_Write(handle, &bytesWritten, *offset, pad, padbytes, 0); 479 | *offset += bytesWritten; 480 | free(pad); 481 | } 482 | } 483 | 484 | 485 | TIK_STRUCT get_tik_struct(u32 sig_size, FILE *tik) 486 | { 487 | TIK_STRUCT tik_struct; 488 | fseek(tik,(0x4+sig_size),SEEK_SET); 489 | fread(&tik_struct,sizeof(tik_struct),1,tik); 490 | return tik_struct; 491 | } 492 | 493 | TMD_STRUCT get_tmd_struct(u32 sig_size, FILE *tmd) 494 | { 495 | TMD_STRUCT tmd_struct; 496 | fseek(tmd,(0x4+sig_size),SEEK_SET); 497 | fread(&tmd_struct,sizeof(tmd_struct),1,tmd); 498 | return tmd_struct; 499 | } 500 | 501 | TMD_CONTENT_CHUNK_STRUCT get_tmd_content_struct(u32 sig_size, u8 index, FILE *tmd) 502 | { 503 | fseek(tmd,(0x4+sig_size+sizeof(TMD_STRUCT)+sizeof(TMD_CONTENT_CHUNK_STRUCT)*index),SEEK_SET); 504 | TMD_CONTENT_CHUNK_STRUCT content_struct; 505 | fread(&content_struct,sizeof(content_struct),1,tmd); 506 | return content_struct; 507 | } 508 | 509 | void print_content_chunk_info(TMD_CONTENT_CHUNK_STRUCT content_struct) 510 | { 511 | printf("\n[+] Content ID: %08lx\n",u8_to_u32(content_struct.content_id,BIG_ENDIAN)); 512 | printf("[+] Content Index: %d\n",u8_to_u16(content_struct.content_index,BIG_ENDIAN)); 513 | printf("[+] Content Type: %d\n",u8_to_u16(content_struct.content_type,BIG_ENDIAN)); 514 | printf("[+] Content Size: 0x%llx\n",u8_to_u64(content_struct.content_size,BIG_ENDIAN)); 515 | printf("[+] SHA-256 Hash: "); u8_hex_print_be(content_struct.sha_256_hash,0x20); printf("\n"); 516 | } 517 | 518 | int check_tid(u8 *tid_0, u8 *tid_1) 519 | { 520 | for(int i = 0; i < 8; i++){ 521 | if(tid_0[i] != tid_1[i]) 522 | return FALSE; 523 | } 524 | return TRUE; 525 | } 526 | -------------------------------------------------------------------------------- /source/utils.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 3DSGuy 3 | 4 | This file is part of make_cdn_cia. 5 | 6 | make_cdn_cia is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | make_cdn_cia is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with make_cdn_cia. If not, see . 18 | **/ 19 | #include "lib.h" 20 | #include "data/builtin_rootca_bin.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | //MISC 30 | void char_to_int_array(unsigned char destination[], char source[], int size, u32 endianness, int base) 31 | { 32 | char tmp[size][2]; 33 | unsigned char *byte_array = (unsigned char*)malloc(size*sizeof(unsigned char)); 34 | memset(byte_array, 0, size); 35 | memset(destination, 0, size); 36 | memset(tmp, 0, size*2); 37 | 38 | for (int i = 0; i < size; i ++){ 39 | tmp[i][0] = source[(i*2)]; 40 | tmp[i][1] = source[((i*2)+1)]; 41 | tmp[i][2] = '\0'; 42 | byte_array[i] = (unsigned char)strtol(tmp[i], NULL, base); 43 | } 44 | endian_memcpy(destination,byte_array,size,endianness); 45 | /** 46 | for (int i = 0; i < size; i++){ 47 | switch (endianness){ 48 | case(BIG_ENDIAN): 49 | destination[i] = byte_array[i]; 50 | break; 51 | case(LITTLE_ENDIAN): 52 | destination[i] = byte_array[((size-1)-i)]; 53 | break; 54 | } 55 | } 56 | **/ 57 | free(byte_array); 58 | } 59 | 60 | void endian_memcpy(u8 *destination, u8 *source, u32 size, u32 endianness) 61 | { 62 | for (u32 i = 0; i < size; i++){ 63 | switch (endianness){ 64 | case(BIG_ENDIAN): 65 | destination[i] = source[i]; 66 | break; 67 | case(LITTLE_ENDIAN): 68 | destination[i] = source[((size-1)-i)]; 69 | break; 70 | } 71 | } 72 | } 73 | 74 | void u8_hex_print_be(u8 *array, int len) 75 | { 76 | for(int i = 0; i < len; i++) 77 | printf("%02x",array[i]); 78 | } 79 | 80 | void u8_hex_print_le(u8 *array, int len) 81 | { 82 | for(int i = 0; i < len; i++) 83 | printf("%02x",array[len - i - 1]); 84 | } 85 | 86 | u32 align_value(u32 value, u32 alignment) 87 | { 88 | u32 tmp = value; 89 | while(tmp > alignment) 90 | tmp -= alignment; 91 | return (value + (alignment - tmp)); 92 | } 93 | 94 | void resolve_flag(unsigned char flag, unsigned char *flag_bool) 95 | { 96 | unsigned char bit_mask[8] = {0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1}; 97 | for(int i = 0; i < 8; i++){ 98 | if (flag >= bit_mask[i]){ 99 | flag_bool[7-i] = True; 100 | flag -= bit_mask[i]; 101 | } 102 | else 103 | flag_bool[7-i] = False; 104 | } 105 | } 106 | 107 | void resolve_flag_u16(u16 flag, unsigned char *flag_bool) 108 | { 109 | u16 bit_mask[16] = {0x8000,0x4000,0x2000,0x1000,0x800,0x400,0x200,0x100,0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1}; 110 | for(int i = 0; i < 16; i++){ 111 | if (flag >= bit_mask[i]){ 112 | flag_bool[15-i] = True; 113 | flag -= bit_mask[i]; 114 | } 115 | else 116 | flag_bool[15-i] = False; 117 | } 118 | } 119 | 120 | //IO Related 121 | int diff_ms(timeval t1, timeval t2) 122 | { 123 | return (((t1.tv_sec - t2.tv_sec) * 1000000) + 124 | (t1.tv_usec - t2.tv_usec))/1000; 125 | } 126 | 127 | void PrintProgress(PrintConsole *console, u32 nSize, u32 nCurrent) 128 | { 129 | // Don't attempt to calculate anything if we don't have a final size 130 | if (nSize == 0) return; 131 | 132 | // Switch to the progress console 133 | PrintConsole* currentConsole = consoleSelect(console); 134 | consoleClear(); 135 | 136 | // Set the start time if nLastSize is 0 137 | static u64 nStartTime; 138 | if (nCurrent == 0) 139 | { 140 | nStartTime = osGetTime(); 141 | } 142 | 143 | // Offset everything by 10 lines to kinda center it 144 | printf("\n\n\n\n\n\n\n\n\n\n"); 145 | 146 | // Calculate percent and bar width 147 | double fPercent = ((double)nCurrent / nSize) * 100.0; 148 | u16 barDrawWidth = (fPercent / 100) * 40; 149 | 150 | int i = 0; 151 | for (i = 0; i < barDrawWidth; i++) 152 | { 153 | printf("|"); 154 | } 155 | printf("\n"); 156 | 157 | // Output current progress 158 | printf(" %0.2f / %0.2fMB % 3.2f%%\n", ((double)nCurrent) / 1024 / 1024, ((double)nSize) / 1024 / 1024, fPercent); 159 | 160 | // Calculate download speed 161 | if (nCurrent > 0) 162 | { 163 | u64 nTime = osGetTime(); 164 | double seconds = ((double)(nTime - nStartTime)) / 1000; 165 | 166 | if (seconds > 0) 167 | { 168 | double speed = ((nCurrent / seconds) / 1024); 169 | printf(" Avg Speed: %.02f KB/s\n", speed); 170 | } 171 | } 172 | 173 | // Make sure the screen updates 174 | gfxFlushBuffers(); 175 | gspWaitForVBlank(); 176 | 177 | // Switch back to the original console 178 | consoleSelect(currentConsole); 179 | } 180 | 181 | void WriteBuffer(void *buffer, u64 size, u64 offset, FILE *output) 182 | { 183 | fseek_64(output,offset,SEEK_SET); 184 | fwrite(buffer,size,1,output); 185 | } 186 | 187 | void write_align_padding(FILE *output, size_t alignment) 188 | { 189 | long int pos = ftell(output); 190 | long int usedbytes = pos & (alignment - 1); 191 | if (usedbytes) 192 | { 193 | long int padbytes = (alignment - usedbytes); 194 | char* pad = (char*)malloc(padbytes); 195 | memset(pad, 0, padbytes); 196 | fwrite(pad, padbytes, 1, output); 197 | free(pad); 198 | } 199 | } 200 | 201 | u64 GetFileSize_u64(char *filename) 202 | { 203 | u64 size; 204 | #ifdef _WIN32 205 | int fh; 206 | u64 n; 207 | fh = _open( filename, 0 ); 208 | n = _lseeki64(fh, 0, SEEK_END); 209 | _close(fh); 210 | size = (n / sizeof(short))*2; 211 | #else 212 | FILE *file = fopen(filename,"rb"); 213 | fseeko(file, 0L, SEEK_END); 214 | size = ftello(file); 215 | fclose(file); 216 | #endif 217 | return size; 218 | } 219 | 220 | int TruncateFile_u64(char *filename, u64 filelen) 221 | { 222 | #ifdef _WIN32 223 | HANDLE fh; 224 | 225 | LARGE_INTEGER fp; 226 | fp.QuadPart = filelen; 227 | 228 | fh = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 229 | if (fh == INVALID_HANDLE_VALUE) { 230 | printf("[!] Invalid File handle\n"); 231 | return 1; 232 | } 233 | 234 | if (SetFilePointerEx(fh, fp, NULL, FILE_BEGIN) == 0 || 235 | SetEndOfFile(fh) == 0) { 236 | printf("[!] truncate failed\n"); 237 | CloseHandle(fh); 238 | return 1; 239 | } 240 | 241 | CloseHandle(fh); 242 | return 0; 243 | #else 244 | return truncate(filename,filelen); 245 | #endif 246 | } 247 | 248 | int fseek_64(FILE *fp, u64 file_pos, int whence) 249 | { 250 | #ifdef _WIN32 251 | fpos_t pos = file_pos; 252 | return fsetpos(fp,&pos); //I can't believe the 2gb problem with Windows & MINGW, maybe I have a bad installation :/ 253 | #else 254 | return fseeko(fp,file_pos,whence); 255 | #endif 256 | } 257 | 258 | int makedir(const char* dir) 259 | { 260 | #ifdef _WIN32 261 | return _mkdir(dir); 262 | #else 263 | return mkdir(dir, 0777); 264 | #endif 265 | } 266 | 267 | char *getcwdir(char *buffer,int maxlen) 268 | { 269 | #ifdef _WIN32 270 | return _getcwd(buffer,maxlen); 271 | #else 272 | return getcwd(buffer,maxlen); 273 | #endif 274 | } 275 | 276 | bool FileExists (const char *name){ 277 | struct stat buffer; 278 | return (stat (name, &buffer) == 0); 279 | } 280 | 281 | void DownloadFile_InternalSave(void* out, unsigned char* buffer, u32 readSize) 282 | { 283 | FILE* os = (FILE*)out; 284 | fwrite(buffer, readSize, 1, os); 285 | } 286 | 287 | static u32 install_offset = 0; 288 | void DownloadFile_InternalInstall(void* out, unsigned char* buffer, u32 readSize) 289 | { 290 | u32 bytesWritten; 291 | Handle* handle = (Handle*)out; 292 | 293 | FSFILE_Write(*handle, &bytesWritten, install_offset, buffer, readSize, 0); 294 | 295 | install_offset += bytesWritten; 296 | } 297 | 298 | Result DownloadFile_Internal(const char *url, void *out, bool bProgress, 299 | void (*write)(void* out, unsigned char* buffer, u32 readSize)) 300 | { 301 | httpcContext context; 302 | u32 fileSize = 0; 303 | u32 procSize = 0; 304 | Result ret = 0; 305 | Result dlret = HTTPC_RESULTCODE_DOWNLOADPENDING; 306 | u32 status; 307 | u32 bufSize = 1024 * 256; 308 | u32 readSize = 0; 309 | httpcOpenContext(&context, HTTPC_METHOD_GET, (char*)url, 1); 310 | httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); 311 | 312 | // If we're showing progress, set up a console on the bottom screen 313 | GSPGPU_FramebufferFormats infoOldFormat = gfxGetScreenFormat(GFX_BOTTOM); 314 | PrintConsole infoConsole; 315 | if (bProgress) 316 | { 317 | PrintConsole* currentConsole = consoleSelect(&infoConsole); 318 | consoleInit(GFX_BOTTOM, &infoConsole); 319 | consoleSelect(currentConsole); 320 | } 321 | 322 | ret = httpcBeginRequest(&context); 323 | if (ret != 0) goto _out; 324 | 325 | ret = httpcGetResponseStatusCode(&context, &status); 326 | if (ret != 0) goto _out; 327 | 328 | if (status != 200) 329 | { 330 | ret = status; 331 | goto _out; 332 | } 333 | 334 | ret = httpcGetDownloadSizeState(&context, NULL, &fileSize); 335 | if (ret != 0) goto _out; 336 | 337 | { 338 | unsigned char *buffer = (unsigned char *)linearAlloc(bufSize); 339 | if (buffer == NULL) 340 | { 341 | printf("Error allocating download buffer\n"); 342 | ret = -1; 343 | goto _out; 344 | } 345 | 346 | // Initialize the Progress bar if we're showing one 347 | if (bProgress) 348 | { 349 | PrintProgress(&infoConsole, fileSize, procSize); 350 | } 351 | 352 | while (dlret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING) 353 | { 354 | // Check if the app is closing 355 | if (!aptMainLoop()) { 356 | ret = -1; 357 | break; 358 | } 359 | 360 | // Check if the user has pressed B, and cancel if so 361 | hidScanInput(); 362 | u32 keys = hidKeysDown(); 363 | if (keys & KEY_B) 364 | { 365 | ret = -1; 366 | break; 367 | } 368 | 369 | memset(buffer, 0, bufSize); 370 | 371 | dlret = httpcDownloadData(&context, buffer, bufSize, &readSize); 372 | write(out, buffer, readSize); 373 | 374 | procSize += readSize; 375 | if (bProgress) 376 | { 377 | PrintProgress(&infoConsole, fileSize, procSize); 378 | } 379 | } 380 | 381 | printf("\n"); 382 | linearFree(buffer); 383 | } 384 | _out: 385 | httpcCloseContext(&context); 386 | 387 | // If showing progress, restore the bottom screen 388 | if (bProgress) 389 | { 390 | gfxSetScreenFormat(GFX_BOTTOM, infoOldFormat); 391 | } 392 | 393 | return ret; 394 | } 395 | 396 | Result DownloadFile(const char *url, FILE *os, bool bProgress) 397 | { 398 | return DownloadFile_Internal(url, os, bProgress, DownloadFile_InternalSave); 399 | } 400 | 401 | // This function DOES NOT support HTTPS 402 | Result DownloadFileInstall(const char *url, Handle *handle, u32* offset) 403 | { 404 | install_offset = *offset; 405 | Result res = DownloadFile_Internal(url, handle, true, DownloadFile_InternalInstall); 406 | *offset = install_offset; 407 | return res; 408 | } 409 | 410 | // Install the passed in seed for the titleid. 411 | // Code from FBI: https://github.com/Steveice10/FBI/blob/45f12418520e99795c8e4d4dee3a1f000acc91a5/source/core/util.c#L239 412 | // Copyright (C) 2015 Steveice10 413 | Result InstallSeed(u64 titleId, const void* seed) { 414 | u32 *cmdbuf = getThreadCommandBuffer(); 415 | 416 | cmdbuf[0] = IPC_MakeHeader(0x87a, 6, 0); // 0x087a0180 417 | cmdbuf[1] = (u32) (titleId & 0xFFFFFFFF); 418 | cmdbuf[2] = (u32) (titleId >> 32); 419 | memcpy(&cmdbuf[3], seed, 16); 420 | 421 | Result ret = 0; 422 | if(R_FAILED(ret = svcSendSyncRequest(*fsGetSessionHandle()))) { 423 | return ret; 424 | } 425 | 426 | ret = cmdbuf[1]; 427 | return ret; 428 | } 429 | 430 | //Data Size conversion 431 | u16 u8_to_u16(u8 *value, u32 endianness) 432 | { 433 | u16 new_value = 0; 434 | switch(endianness){ 435 | case(BIG_ENDIAN): new_value = (value[1]<<0) | (value[0]<<8); break; 436 | case(LITTLE_ENDIAN): new_value = (value[0]<<0) | (value[1]<<8); break; 437 | } 438 | return new_value; 439 | } 440 | 441 | u32 u8_to_u32(u8 *value, u32 endianness) 442 | { 443 | u32 new_value = 0; 444 | switch(endianness){ 445 | case(BIG_ENDIAN): new_value = (value[3]<<0) | (value[2]<<8) | (value[1]<<16) | (value[0]<<24); break; 446 | case(LITTLE_ENDIAN): new_value = (value[0]<<0) | (value[1]<<8) | (value[2]<<16) | (value[3]<<24); break; 447 | } 448 | return new_value; 449 | } 450 | 451 | 452 | u64 u8_to_u64(u8 *value, u32 endianness) 453 | { 454 | u64 u64_return = 0; 455 | switch(endianness){ 456 | case(BIG_ENDIAN): 457 | u64_return |= (u64)value[7]<<0; 458 | u64_return |= (u64)value[6]<<8; 459 | u64_return |= (u64)value[5]<<16; 460 | u64_return |= (u64)value[4]<<24; 461 | u64_return |= (u64)value[3]<<32; 462 | u64_return |= (u64)value[2]<<40; 463 | u64_return |= (u64)value[1]<<48; 464 | u64_return |= (u64)value[0]<<56; 465 | break; 466 | //return (value[7]<<0) | (value[6]<<8) | (value[5]<<16) | (value[4]<<24) | (value[3]<<32) | (value[2]<<40) | (value[1]<<48) | (value[0]<<56); 467 | case(LITTLE_ENDIAN): 468 | u64_return |= (u64)value[0]<<0; 469 | u64_return |= (u64)value[1]<<8; 470 | u64_return |= (u64)value[2]<<16; 471 | u64_return |= (u64)value[3]<<24; 472 | u64_return |= (u64)value[4]<<32; 473 | u64_return |= (u64)value[5]<<40; 474 | u64_return |= (u64)value[6]<<48; 475 | u64_return |= (u64)value[7]<<56; 476 | break; 477 | //return (value[0]<<0) | (value[1]<<8) | (value[2]<<16) | (value[3]<<24) | (value[4]<<32) | (value[5]<<40) | (value[6]<<48) | (value[7]<<56); 478 | } 479 | return u64_return; 480 | } 481 | 482 | int u16_to_u8(u8 *out_value, u16 in_value, u32 endianness) 483 | { 484 | switch(endianness){ 485 | case(BIG_ENDIAN): 486 | out_value[0]=(in_value >> 8); 487 | out_value[1]=(in_value >> 0); 488 | break; 489 | case(LITTLE_ENDIAN): 490 | out_value[0]=(in_value >> 0); 491 | out_value[1]=(in_value >> 8); 492 | break; 493 | } 494 | return 0; 495 | } 496 | 497 | int u32_to_u8(u8 *out_value, u32 in_value, u32 endianness) 498 | { 499 | switch(endianness){ 500 | case(BIG_ENDIAN): 501 | out_value[0]=(in_value >> 24); 502 | out_value[1]=(in_value >> 16); 503 | out_value[2]=(in_value >> 8); 504 | out_value[3]=(in_value >> 0); 505 | break; 506 | case(LITTLE_ENDIAN): 507 | out_value[0]=(in_value >> 0); 508 | out_value[1]=(in_value >> 8); 509 | out_value[2]=(in_value >> 16); 510 | out_value[3]=(in_value >> 24); 511 | break; 512 | } 513 | return 0; 514 | } 515 | 516 | int u64_to_u8(u8 *out_value, u64 in_value, u32 endianness) 517 | { 518 | switch(endianness){ 519 | case(BIG_ENDIAN): 520 | out_value[0]=(in_value >> 56); 521 | out_value[1]=(in_value >> 48); 522 | out_value[2]=(in_value >> 40); 523 | out_value[3]=(in_value >> 32); 524 | out_value[4]=(in_value >> 24); 525 | out_value[5]=(in_value >> 16); 526 | out_value[6]=(in_value >> 8); 527 | out_value[7]=(in_value >> 0); 528 | break; 529 | case(LITTLE_ENDIAN): 530 | out_value[0]=(in_value >> 0); 531 | out_value[1]=(in_value >> 8); 532 | out_value[2]=(in_value >> 16); 533 | out_value[3]=(in_value >> 24); 534 | out_value[4]=(in_value >> 32); 535 | out_value[5]=(in_value >> 40); 536 | out_value[6]=(in_value >> 48); 537 | out_value[7]=(in_value >> 56); 538 | break; 539 | } 540 | return 0; 541 | } 542 | 543 | //Copied from ctrtool 544 | void memdump(FILE* fout, const char* prefix, const u8* data, u32 size) 545 | { 546 | u32 i; 547 | int prefixlen = strlen(prefix); 548 | u32 offs = 0; 549 | u32 line = 0; 550 | while(size) 551 | { 552 | u32 max = 32; 553 | 554 | if (max > size) 555 | max = size; 556 | 557 | if (line==0) 558 | fprintf(fout, "%s", prefix); 559 | else 560 | fprintf(fout, "%*s", prefixlen, ""); 561 | 562 | 563 | for(i=0; i 0) 581 | { 582 | return keys; 583 | } 584 | gfxFlushBuffers(); 585 | gspWaitForVBlank(); 586 | } 587 | 588 | return 0; 589 | } 590 | 591 | u32 wait_key_specific(const char* message, u32 key) 592 | { 593 | printf(message); 594 | while (true) 595 | { 596 | u32 keys = wait_key(); 597 | if (keys & key) 598 | { 599 | return keys; 600 | } 601 | } 602 | } 603 | 604 | // Graphics Functions 605 | void clear_screen(gfxScreen_t screen) 606 | { 607 | u8* buffer1; 608 | u8* buffer2 = NULL; 609 | u16 width, height; 610 | u32 bpp; 611 | GSPGPU_FramebufferFormats format = gfxGetScreenFormat(screen); 612 | 613 | if (screen == GFX_TOP) 614 | { 615 | buffer1 = gfxGetFramebuffer(screen, GFX_LEFT, &width, &height); 616 | buffer2 = gfxGetFramebuffer(screen, GFX_RIGHT, &width, &height); 617 | } else { 618 | buffer1 = gfxGetFramebuffer(screen, GFX_LEFT, &width, &height); 619 | } 620 | 621 | switch (format) 622 | { 623 | case GSP_RGBA8_OES: 624 | bpp = 4; 625 | case GSP_BGR8_OES: 626 | bpp = 3; 627 | case GSP_RGB565_OES: 628 | case GSP_RGB5_A1_OES: 629 | case GSP_RGBA4_OES: 630 | bpp = 2; 631 | default: 632 | bpp = 3; 633 | } 634 | 635 | memset(buffer1, 0, (width * height * bpp)); 636 | if (buffer2) 637 | memset(buffer2, 0, (width * height * bpp)); 638 | 639 | gfxFlushBuffers(); 640 | gfxSwapBuffers(); 641 | gspWaitForVBlank(); 642 | } 643 | 644 | bool download_JSON() { 645 | printf("\nAttempting to download JSON...\n"); 646 | 647 | remove("/CIAngel/wings.json.tmp"); 648 | FILE *oh = fopen("/CIAngel/wings.json.tmp", "wb"); 649 | 650 | if (oh) { 651 | Result res = DownloadFile(getJsonUrl().c_str(), oh, false); 652 | int size = ftell(oh); 653 | fclose(oh); 654 | if (res == 0 && size >= 0) { 655 | remove("/CIAngel/wings.json"); 656 | rename("/CIAngel/wings.json.tmp", "/CIAngel/wings.json"); 657 | return true; 658 | } 659 | } 660 | 661 | remove("/CIAngel/wings.json.tmp"); 662 | printf("Failed to download JSON\n"); 663 | return false; 664 | } 665 | 666 | bool check_JSON() { 667 | struct stat filestats; 668 | int ret = stat("/CIAngel/wings.json", &filestats); 669 | time_t curtime = time(NULL); 670 | 671 | if (ret == 0) { 672 | u64 mtime; 673 | sdmc_getmtime("/CIAngel/wings.json", &mtime); 674 | double age_seconds = difftime(curtime, mtime); 675 | double age_days = age_seconds / (60 * 60 * 24); 676 | 677 | if (age_seconds > JSON_UPDATE_INTERVAL_IN_SECONDS) { 678 | printf("Your wings.json is %d days old\n\n", (int)age_days); 679 | printf("Press A to update, or any other key to skip.\n"); 680 | 681 | u32 keys = wait_key(); 682 | 683 | if (keys & KEY_A) { 684 | return download_JSON(); 685 | } 686 | return true; 687 | } 688 | } else { 689 | printf("No wings.json\n"); 690 | 691 | printf("\nPress A to Download, or any other key to return.\n"); 692 | u32 keys = wait_key(); 693 | 694 | if (keys & KEY_A) { 695 | return download_JSON(); 696 | } 697 | printf("\nDon't expect search to work\n"); 698 | return false; 699 | } 700 | 701 | return true; 702 | } 703 | 704 | std::string GetSerialType(std::string sSerial) 705 | { 706 | std::string sType = "Unknown"; 707 | if (sSerial.substr(0, 3) == "TWL") 708 | { 709 | sType = "DSiWare"; 710 | } 711 | else 712 | { 713 | switch (sSerial.c_str()[4]) 714 | { 715 | case 'N': 716 | case 'P': 717 | sType = "Game"; 718 | break; 719 | case 'T': 720 | sType = "Demo"; 721 | break; 722 | case 'U': 723 | sType = "Update"; 724 | break; 725 | case 'M': 726 | sType = "DLC"; 727 | break; 728 | } 729 | } 730 | 731 | return sType; 732 | } 733 | 734 | std::string upperCase(std::string input) { 735 | for (std::string::iterator it = input.begin(); it != input.end(); ++ it) 736 | *it = toupper(*it); 737 | return input; 738 | } 739 | 740 | std::string getJsonUrl() 741 | { 742 | std::string sJsonUrl = JSON_URL_DEFAULT; 743 | 744 | std::ifstream jsonFile; 745 | jsonFile.open(JSON_URL_FILE, std::ofstream::in); 746 | if (jsonFile.is_open()) 747 | { 748 | getline(jsonFile, sJsonUrl); 749 | } 750 | 751 | return sJsonUrl; 752 | } 753 | -------------------------------------------------------------------------------- /source/utf8proc/utf8proc.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright (c) 2015 Steven G. Johnson, Jiahao Chen, Peter Colberg, Tony Kelman, Scott P. Jones, and other contributors. 4 | * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * This library contains derived data from a modified version of the 27 | * Unicode data files. 28 | * 29 | * The original data files are available at 30 | * http://www.unicode.org/Public/UNIDATA/ 31 | * 32 | * Please notice the copyright statement in the file "utf8proc_data.c". 33 | */ 34 | 35 | 36 | /* 37 | * File name: utf8proc.c 38 | * 39 | * Description: 40 | * Implementation of libutf8proc. 41 | */ 42 | 43 | 44 | #include "utf8proc.h" 45 | #include "utf8proc_data.h" 46 | 47 | 48 | UTF8PROC_DLLEXPORT const utf8proc_int8_t utf8proc_utf8class[256] = { 49 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 50 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 51 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 52 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 53 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 54 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 55 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 56 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 57 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 62 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 63 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 64 | 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 }; 65 | 66 | #define UTF8PROC_HANGUL_SBASE 0xAC00 67 | #define UTF8PROC_HANGUL_LBASE 0x1100 68 | #define UTF8PROC_HANGUL_VBASE 0x1161 69 | #define UTF8PROC_HANGUL_TBASE 0x11A7 70 | #define UTF8PROC_HANGUL_LCOUNT 19 71 | #define UTF8PROC_HANGUL_VCOUNT 21 72 | #define UTF8PROC_HANGUL_TCOUNT 28 73 | #define UTF8PROC_HANGUL_NCOUNT 588 74 | #define UTF8PROC_HANGUL_SCOUNT 11172 75 | /* END is exclusive */ 76 | #define UTF8PROC_HANGUL_L_START 0x1100 77 | #define UTF8PROC_HANGUL_L_END 0x115A 78 | #define UTF8PROC_HANGUL_L_FILLER 0x115F 79 | #define UTF8PROC_HANGUL_V_START 0x1160 80 | #define UTF8PROC_HANGUL_V_END 0x11A3 81 | #define UTF8PROC_HANGUL_T_START 0x11A8 82 | #define UTF8PROC_HANGUL_T_END 0x11FA 83 | #define UTF8PROC_HANGUL_S_START 0xAC00 84 | #define UTF8PROC_HANGUL_S_END 0xD7A4 85 | 86 | /* Should follow semantic-versioning rules (semver.org) based on API 87 | compatibility. (Note that the shared-library version number will 88 | be different, being based on ABI compatibility.): */ 89 | #define STRINGIZEx(x) #x 90 | #define STRINGIZE(x) STRINGIZEx(x) 91 | UTF8PROC_DLLEXPORT const char *utf8proc_version(void) { 92 | return STRINGIZE(UTF8PROC_VERSION_MAJOR) "." STRINGIZE(UTF8PROC_VERSION_MINOR) "." STRINGIZE(UTF8PROC_VERSION_PATCH) ""; 93 | } 94 | 95 | UTF8PROC_DLLEXPORT const char *utf8proc_errmsg(utf8proc_ssize_t errcode) { 96 | switch (errcode) { 97 | case UTF8PROC_ERROR_NOMEM: 98 | return "Memory for processing UTF-8 data could not be allocated."; 99 | case UTF8PROC_ERROR_OVERFLOW: 100 | return "UTF-8 string is too long to be processed."; 101 | case UTF8PROC_ERROR_INVALIDUTF8: 102 | return "Invalid UTF-8 string"; 103 | case UTF8PROC_ERROR_NOTASSIGNED: 104 | return "Unassigned Unicode code point found in UTF-8 string."; 105 | case UTF8PROC_ERROR_INVALIDOPTS: 106 | return "Invalid options for UTF-8 processing chosen."; 107 | default: 108 | return "An unknown error occurred while processing UTF-8 data."; 109 | } 110 | } 111 | 112 | #define utf_cont(ch) (((ch) & 0xc0) == 0x80) 113 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate( 114 | const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_int32_t *dst 115 | ) { 116 | utf8proc_uint32_t uc; 117 | const utf8proc_uint8_t *end; 118 | 119 | *dst = -1; 120 | if (!strlen) return 0; 121 | end = str + ((strlen < 0) ? 4 : strlen); 122 | uc = *str++; 123 | if (uc < 0x80) { 124 | *dst = uc; 125 | return 1; 126 | } 127 | // Must be between 0xc2 and 0xf4 inclusive to be valid 128 | if ((uc - 0xc2) > (0xf4-0xc2)) return UTF8PROC_ERROR_INVALIDUTF8; 129 | if (uc < 0xe0) { // 2-byte sequence 130 | // Must have valid continuation character 131 | if (str >= end || !utf_cont(*str)) return UTF8PROC_ERROR_INVALIDUTF8; 132 | *dst = ((uc & 0x1f)<<6) | (*str & 0x3f); 133 | return 2; 134 | } 135 | if (uc < 0xf0) { // 3-byte sequence 136 | if ((str + 1 >= end) || !utf_cont(*str) || !utf_cont(str[1])) 137 | return UTF8PROC_ERROR_INVALIDUTF8; 138 | // Check for surrogate chars 139 | if (uc == 0xed && *str > 0x9f) 140 | return UTF8PROC_ERROR_INVALIDUTF8; 141 | uc = ((uc & 0xf)<<12) | ((*str & 0x3f)<<6) | (str[1] & 0x3f); 142 | if (uc < 0x800) 143 | return UTF8PROC_ERROR_INVALIDUTF8; 144 | *dst = uc; 145 | return 3; 146 | } 147 | // 4-byte sequence 148 | // Must have 3 valid continuation characters 149 | if ((str + 2 >= end) || !utf_cont(*str) || !utf_cont(str[1]) || !utf_cont(str[2])) 150 | return UTF8PROC_ERROR_INVALIDUTF8; 151 | // Make sure in correct range (0x10000 - 0x10ffff) 152 | if (uc == 0xf0) { 153 | if (*str < 0x90) return UTF8PROC_ERROR_INVALIDUTF8; 154 | } else if (uc == 0xf4) { 155 | if (*str > 0x8f) return UTF8PROC_ERROR_INVALIDUTF8; 156 | } 157 | *dst = ((uc & 7)<<18) | ((*str & 0x3f)<<12) | ((str[1] & 0x3f)<<6) | (str[2] & 0x3f); 158 | return 4; 159 | } 160 | 161 | UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_codepoint_valid(utf8proc_int32_t uc) { 162 | return (((utf8proc_uint32_t)uc)-0xd800 > 0x07ff) && ((utf8proc_uint32_t)uc < 0x110000); 163 | } 164 | 165 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) { 166 | if (uc < 0x00) { 167 | return 0; 168 | } else if (uc < 0x80) { 169 | dst[0] = uc; 170 | return 1; 171 | } else if (uc < 0x800) { 172 | dst[0] = 0xC0 + (uc >> 6); 173 | dst[1] = 0x80 + (uc & 0x3F); 174 | return 2; 175 | // Note: we allow encoding 0xd800-0xdfff here, so as not to change 176 | // the API, however, these are actually invalid in UTF-8 177 | } else if (uc < 0x10000) { 178 | dst[0] = 0xE0 + (uc >> 12); 179 | dst[1] = 0x80 + ((uc >> 6) & 0x3F); 180 | dst[2] = 0x80 + (uc & 0x3F); 181 | return 3; 182 | } else if (uc < 0x110000) { 183 | dst[0] = 0xF0 + (uc >> 18); 184 | dst[1] = 0x80 + ((uc >> 12) & 0x3F); 185 | dst[2] = 0x80 + ((uc >> 6) & 0x3F); 186 | dst[3] = 0x80 + (uc & 0x3F); 187 | return 4; 188 | } else return 0; 189 | } 190 | 191 | /* internal "unsafe" version that does not check whether uc is in range */ 192 | static utf8proc_ssize_t unsafe_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) { 193 | if (uc < 0x00) { 194 | return 0; 195 | } else if (uc < 0x80) { 196 | dst[0] = uc; 197 | return 1; 198 | } else if (uc < 0x800) { 199 | dst[0] = 0xC0 + (uc >> 6); 200 | dst[1] = 0x80 + (uc & 0x3F); 201 | return 2; 202 | } else if (uc == 0xFFFF) { 203 | dst[0] = 0xFF; 204 | return 1; 205 | } else if (uc == 0xFFFE) { 206 | dst[0] = 0xFE; 207 | return 1; 208 | } else if (uc < 0x10000) { 209 | dst[0] = 0xE0 + (uc >> 12); 210 | dst[1] = 0x80 + ((uc >> 6) & 0x3F); 211 | dst[2] = 0x80 + (uc & 0x3F); 212 | return 3; 213 | } else if (uc < 0x110000) { 214 | dst[0] = 0xF0 + (uc >> 18); 215 | dst[1] = 0x80 + ((uc >> 12) & 0x3F); 216 | dst[2] = 0x80 + ((uc >> 6) & 0x3F); 217 | dst[3] = 0x80 + (uc & 0x3F); 218 | return 4; 219 | } else return 0; 220 | } 221 | 222 | /* internal "unsafe" version that does not check whether uc is in range */ 223 | static const utf8proc_property_t *unsafe_get_property(utf8proc_int32_t uc) { 224 | /* ASSERT: uc >= 0 && uc < 0x110000 */ 225 | return utf8proc_properties + ( 226 | utf8proc_stage2table[ 227 | utf8proc_stage1table[uc >> 8] + (uc & 0xFF) 228 | ] 229 | ); 230 | } 231 | 232 | UTF8PROC_DLLEXPORT const utf8proc_property_t *utf8proc_get_property(utf8proc_int32_t uc) { 233 | return uc < 0 || uc >= 0x110000 ? utf8proc_properties : unsafe_get_property(uc); 234 | } 235 | 236 | /* return whether there is a grapheme break between boundclasses lbc and tbc */ 237 | static utf8proc_bool grapheme_break(int lbc, int tbc) { 238 | return 239 | (lbc == UTF8PROC_BOUNDCLASS_START) ? true : 240 | (lbc == UTF8PROC_BOUNDCLASS_CR && 241 | tbc == UTF8PROC_BOUNDCLASS_LF) ? false : 242 | (lbc >= UTF8PROC_BOUNDCLASS_CR && lbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : 243 | (tbc >= UTF8PROC_BOUNDCLASS_CR && tbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : 244 | (tbc == UTF8PROC_BOUNDCLASS_EXTEND) ? false : 245 | (lbc == UTF8PROC_BOUNDCLASS_L && 246 | (tbc == UTF8PROC_BOUNDCLASS_L || 247 | tbc == UTF8PROC_BOUNDCLASS_V || 248 | tbc == UTF8PROC_BOUNDCLASS_LV || 249 | tbc == UTF8PROC_BOUNDCLASS_LVT)) ? false : 250 | ((lbc == UTF8PROC_BOUNDCLASS_LV || 251 | lbc == UTF8PROC_BOUNDCLASS_V) && 252 | (tbc == UTF8PROC_BOUNDCLASS_V || 253 | tbc == UTF8PROC_BOUNDCLASS_T)) ? false : 254 | ((lbc == UTF8PROC_BOUNDCLASS_LVT || 255 | lbc == UTF8PROC_BOUNDCLASS_T) && 256 | tbc == UTF8PROC_BOUNDCLASS_T) ? false : 257 | (lbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR && 258 | tbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR) ? false : 259 | (tbc != UTF8PROC_BOUNDCLASS_SPACINGMARK); 260 | } 261 | 262 | /* return whether there is a grapheme break between codepoints c1 and c2 */ 263 | UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_grapheme_break(utf8proc_int32_t c1, utf8proc_int32_t c2) { 264 | return grapheme_break(utf8proc_get_property(c1)->boundclass, 265 | utf8proc_get_property(c2)->boundclass); 266 | } 267 | 268 | UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_tolower(utf8proc_int32_t c) 269 | { 270 | utf8proc_int32_t cl = utf8proc_get_property(c)->lowercase_mapping; 271 | return cl >= 0 ? cl : c; 272 | } 273 | 274 | UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_toupper(utf8proc_int32_t c) 275 | { 276 | utf8proc_int32_t cu = utf8proc_get_property(c)->uppercase_mapping; 277 | return cu >= 0 ? cu : c; 278 | } 279 | 280 | /* return a character width analogous to wcwidth (except portable and 281 | hopefully less buggy than most system wcwidth functions). */ 282 | UTF8PROC_DLLEXPORT int utf8proc_charwidth(utf8proc_int32_t c) { 283 | return utf8proc_get_property(c)->charwidth; 284 | } 285 | 286 | UTF8PROC_DLLEXPORT utf8proc_category_t utf8proc_category(utf8proc_int32_t c) { 287 | return utf8proc_get_property(c)->category; 288 | } 289 | 290 | UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t c) { 291 | static const char s[][3] = {"Cn","Lu","Ll","Lt","Lm","Lo","Mn","Mc","Me","Nd","Nl","No","Pc","Pd","Ps","Pe","Pi","Pf","Po","Sm","Sc","Sk","So","Zs","Zl","Zp","Cc","Cf","Cs","Co"}; 292 | return s[utf8proc_category(c)]; 293 | } 294 | 295 | #define utf8proc_decompose_lump(replacement_uc) \ 296 | return utf8proc_decompose_char((replacement_uc), dst, bufsize, \ 297 | options & ~UTF8PROC_LUMP, last_boundclass) 298 | 299 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, utf8proc_option_t options, int *last_boundclass) { 300 | const utf8proc_property_t *property; 301 | utf8proc_propval_t category; 302 | utf8proc_int32_t hangul_sindex; 303 | if (uc < 0 || uc >= 0x110000) return UTF8PROC_ERROR_NOTASSIGNED; 304 | property = unsafe_get_property(uc); 305 | category = property->category; 306 | hangul_sindex = uc - UTF8PROC_HANGUL_SBASE; 307 | if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) { 308 | if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT) { 309 | utf8proc_int32_t hangul_tindex; 310 | if (bufsize >= 1) { 311 | dst[0] = UTF8PROC_HANGUL_LBASE + 312 | hangul_sindex / UTF8PROC_HANGUL_NCOUNT; 313 | if (bufsize >= 2) dst[1] = UTF8PROC_HANGUL_VBASE + 314 | (hangul_sindex % UTF8PROC_HANGUL_NCOUNT) / UTF8PROC_HANGUL_TCOUNT; 315 | } 316 | hangul_tindex = hangul_sindex % UTF8PROC_HANGUL_TCOUNT; 317 | if (!hangul_tindex) return 2; 318 | if (bufsize >= 3) dst[2] = UTF8PROC_HANGUL_TBASE + hangul_tindex; 319 | return 3; 320 | } 321 | } 322 | if (options & UTF8PROC_REJECTNA) { 323 | if (!category) return UTF8PROC_ERROR_NOTASSIGNED; 324 | } 325 | if (options & UTF8PROC_IGNORE) { 326 | if (property->ignorable) return 0; 327 | } 328 | if (options & UTF8PROC_LUMP) { 329 | if (category == UTF8PROC_CATEGORY_ZS) utf8proc_decompose_lump(0x0020); 330 | if (uc == 0x2018 || uc == 0x2019 || uc == 0x02BC || uc == 0x02C8) 331 | utf8proc_decompose_lump(0x0027); 332 | if (category == UTF8PROC_CATEGORY_PD || uc == 0x2212) 333 | utf8proc_decompose_lump(0x002D); 334 | if (uc == 0x2044 || uc == 0x2215) utf8proc_decompose_lump(0x002F); 335 | if (uc == 0x2236) utf8proc_decompose_lump(0x003A); 336 | if (uc == 0x2039 || uc == 0x2329 || uc == 0x3008) 337 | utf8proc_decompose_lump(0x003C); 338 | if (uc == 0x203A || uc == 0x232A || uc == 0x3009) 339 | utf8proc_decompose_lump(0x003E); 340 | if (uc == 0x2216) utf8proc_decompose_lump(0x005C); 341 | if (uc == 0x02C4 || uc == 0x02C6 || uc == 0x2038 || uc == 0x2303) 342 | utf8proc_decompose_lump(0x005E); 343 | if (category == UTF8PROC_CATEGORY_PC || uc == 0x02CD) 344 | utf8proc_decompose_lump(0x005F); 345 | if (uc == 0x02CB) utf8proc_decompose_lump(0x0060); 346 | if (uc == 0x2223) utf8proc_decompose_lump(0x007C); 347 | if (uc == 0x223C) utf8proc_decompose_lump(0x007E); 348 | if ((options & UTF8PROC_NLF2LS) && (options & UTF8PROC_NLF2PS)) { 349 | if (category == UTF8PROC_CATEGORY_ZL || 350 | category == UTF8PROC_CATEGORY_ZP) 351 | utf8proc_decompose_lump(0x000A); 352 | } 353 | } 354 | if (options & UTF8PROC_STRIPMARK) { 355 | if (category == UTF8PROC_CATEGORY_MN || 356 | category == UTF8PROC_CATEGORY_MC || 357 | category == UTF8PROC_CATEGORY_ME) return 0; 358 | } 359 | if (options & UTF8PROC_CASEFOLD) { 360 | if (property->casefold_mapping != UINT16_MAX) { 361 | const utf8proc_int32_t *casefold_entry; 362 | utf8proc_ssize_t written = 0; 363 | for (casefold_entry = &utf8proc_sequences[property->casefold_mapping]; 364 | *casefold_entry >= 0; casefold_entry++) { 365 | written += utf8proc_decompose_char(*casefold_entry, dst+written, 366 | (bufsize > written) ? (bufsize - written) : 0, options, 367 | last_boundclass); 368 | if (written < 0) return UTF8PROC_ERROR_OVERFLOW; 369 | } 370 | return written; 371 | } 372 | } 373 | if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) { 374 | if (property->decomp_mapping != UINT16_MAX && 375 | (!property->decomp_type || (options & UTF8PROC_COMPAT))) { 376 | const utf8proc_int32_t *decomp_entry; 377 | utf8proc_ssize_t written = 0; 378 | for (decomp_entry = &utf8proc_sequences[property->decomp_mapping]; 379 | *decomp_entry >= 0; decomp_entry++) { 380 | written += utf8proc_decompose_char(*decomp_entry, dst+written, 381 | (bufsize > written) ? (bufsize - written) : 0, options, 382 | last_boundclass); 383 | if (written < 0) return UTF8PROC_ERROR_OVERFLOW; 384 | } 385 | return written; 386 | } 387 | } 388 | if (options & UTF8PROC_CHARBOUND) { 389 | utf8proc_bool boundary; 390 | int tbc = property->boundclass; 391 | boundary = grapheme_break(*last_boundclass, tbc); 392 | *last_boundclass = tbc; 393 | if (boundary) { 394 | if (bufsize >= 1) dst[0] = 0xFFFF; 395 | if (bufsize >= 2) dst[1] = uc; 396 | return 2; 397 | } 398 | } 399 | if (bufsize >= 1) *dst = uc; 400 | return 1; 401 | } 402 | 403 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose( 404 | const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, 405 | utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options 406 | ) { 407 | /* strlen will be ignored, if UTF8PROC_NULLTERM is set in options */ 408 | utf8proc_ssize_t wpos = 0; 409 | if ((options & UTF8PROC_COMPOSE) && (options & UTF8PROC_DECOMPOSE)) 410 | return UTF8PROC_ERROR_INVALIDOPTS; 411 | if ((options & UTF8PROC_STRIPMARK) && 412 | !(options & UTF8PROC_COMPOSE) && !(options & UTF8PROC_DECOMPOSE)) 413 | return UTF8PROC_ERROR_INVALIDOPTS; 414 | { 415 | utf8proc_int32_t uc; 416 | utf8proc_ssize_t rpos = 0; 417 | utf8proc_ssize_t decomp_result; 418 | int boundclass = UTF8PROC_BOUNDCLASS_START; 419 | while (1) { 420 | if (options & UTF8PROC_NULLTERM) { 421 | rpos += utf8proc_iterate(str + rpos, -1, &uc); 422 | /* checking of return value is not necessary, 423 | as 'uc' is < 0 in case of error */ 424 | if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8; 425 | if (rpos < 0) return UTF8PROC_ERROR_OVERFLOW; 426 | if (uc == 0) break; 427 | } else { 428 | if (rpos >= strlen) break; 429 | rpos += utf8proc_iterate(str + rpos, strlen - rpos, &uc); 430 | if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8; 431 | } 432 | decomp_result = utf8proc_decompose_char( 433 | uc, buffer + wpos, (bufsize > wpos) ? (bufsize - wpos) : 0, options, 434 | &boundclass 435 | ); 436 | if (decomp_result < 0) return decomp_result; 437 | wpos += decomp_result; 438 | /* prohibiting integer overflows due to too long strings: */ 439 | if (wpos < 0 || 440 | wpos > (utf8proc_ssize_t)(SSIZE_MAX/sizeof(utf8proc_int32_t)/2)) 441 | return UTF8PROC_ERROR_OVERFLOW; 442 | } 443 | } 444 | if ((options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) && bufsize >= wpos) { 445 | utf8proc_ssize_t pos = 0; 446 | while (pos < wpos-1) { 447 | utf8proc_int32_t uc1, uc2; 448 | const utf8proc_property_t *property1, *property2; 449 | uc1 = buffer[pos]; 450 | uc2 = buffer[pos+1]; 451 | property1 = unsafe_get_property(uc1); 452 | property2 = unsafe_get_property(uc2); 453 | if (property1->combining_class > property2->combining_class && 454 | property2->combining_class > 0) { 455 | buffer[pos] = uc2; 456 | buffer[pos+1] = uc1; 457 | if (pos > 0) pos--; else pos++; 458 | } else { 459 | pos++; 460 | } 461 | } 462 | } 463 | return wpos; 464 | } 465 | 466 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options) { 467 | /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored 468 | ASSERT: 'buffer' has one spare byte of free space at the end! */ 469 | if (options & (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS | UTF8PROC_STRIPCC)) { 470 | utf8proc_ssize_t rpos; 471 | utf8proc_ssize_t wpos = 0; 472 | utf8proc_int32_t uc; 473 | for (rpos = 0; rpos < length; rpos++) { 474 | uc = buffer[rpos]; 475 | if (uc == 0x000D && rpos < length-1 && buffer[rpos+1] == 0x000A) rpos++; 476 | if (uc == 0x000A || uc == 0x000D || uc == 0x0085 || 477 | ((options & UTF8PROC_STRIPCC) && (uc == 0x000B || uc == 0x000C))) { 478 | if (options & UTF8PROC_NLF2LS) { 479 | if (options & UTF8PROC_NLF2PS) { 480 | buffer[wpos++] = 0x000A; 481 | } else { 482 | buffer[wpos++] = 0x2028; 483 | } 484 | } else { 485 | if (options & UTF8PROC_NLF2PS) { 486 | buffer[wpos++] = 0x2029; 487 | } else { 488 | buffer[wpos++] = 0x0020; 489 | } 490 | } 491 | } else if ((options & UTF8PROC_STRIPCC) && 492 | (uc < 0x0020 || (uc >= 0x007F && uc < 0x00A0))) { 493 | if (uc == 0x0009) buffer[wpos++] = 0x0020; 494 | } else { 495 | buffer[wpos++] = uc; 496 | } 497 | } 498 | length = wpos; 499 | } 500 | if (options & UTF8PROC_COMPOSE) { 501 | utf8proc_int32_t *starter = NULL; 502 | utf8proc_int32_t current_char; 503 | const utf8proc_property_t *starter_property = NULL, *current_property; 504 | utf8proc_propval_t max_combining_class = -1; 505 | utf8proc_ssize_t rpos; 506 | utf8proc_ssize_t wpos = 0; 507 | utf8proc_int32_t composition; 508 | for (rpos = 0; rpos < length; rpos++) { 509 | current_char = buffer[rpos]; 510 | current_property = unsafe_get_property(current_char); 511 | if (starter && current_property->combining_class > max_combining_class) { 512 | /* combination perhaps possible */ 513 | utf8proc_int32_t hangul_lindex; 514 | utf8proc_int32_t hangul_sindex; 515 | hangul_lindex = *starter - UTF8PROC_HANGUL_LBASE; 516 | if (hangul_lindex >= 0 && hangul_lindex < UTF8PROC_HANGUL_LCOUNT) { 517 | utf8proc_int32_t hangul_vindex; 518 | hangul_vindex = current_char - UTF8PROC_HANGUL_VBASE; 519 | if (hangul_vindex >= 0 && hangul_vindex < UTF8PROC_HANGUL_VCOUNT) { 520 | *starter = UTF8PROC_HANGUL_SBASE + 521 | (hangul_lindex * UTF8PROC_HANGUL_VCOUNT + hangul_vindex) * 522 | UTF8PROC_HANGUL_TCOUNT; 523 | starter_property = NULL; 524 | continue; 525 | } 526 | } 527 | hangul_sindex = *starter - UTF8PROC_HANGUL_SBASE; 528 | if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT && 529 | (hangul_sindex % UTF8PROC_HANGUL_TCOUNT) == 0) { 530 | utf8proc_int32_t hangul_tindex; 531 | hangul_tindex = current_char - UTF8PROC_HANGUL_TBASE; 532 | if (hangul_tindex >= 0 && hangul_tindex < UTF8PROC_HANGUL_TCOUNT) { 533 | *starter += hangul_tindex; 534 | starter_property = NULL; 535 | continue; 536 | } 537 | } 538 | if (!starter_property) { 539 | starter_property = unsafe_get_property(*starter); 540 | } 541 | if (starter_property->comb1st_index >= 0 && 542 | current_property->comb2nd_index >= 0) { 543 | composition = utf8proc_combinations[ 544 | starter_property->comb1st_index + 545 | current_property->comb2nd_index 546 | ]; 547 | if (composition >= 0 && (!(options & UTF8PROC_STABLE) || 548 | !(unsafe_get_property(composition)->comp_exclusion))) { 549 | *starter = composition; 550 | starter_property = NULL; 551 | continue; 552 | } 553 | } 554 | } 555 | buffer[wpos] = current_char; 556 | if (current_property->combining_class) { 557 | if (current_property->combining_class > max_combining_class) { 558 | max_combining_class = current_property->combining_class; 559 | } 560 | } else { 561 | starter = buffer + wpos; 562 | starter_property = NULL; 563 | max_combining_class = -1; 564 | } 565 | wpos++; 566 | } 567 | length = wpos; 568 | } 569 | { 570 | utf8proc_ssize_t rpos, wpos = 0; 571 | utf8proc_int32_t uc; 572 | if (options & UTF8PROC_CHARBOUND) { 573 | for (rpos = 0; rpos < length; rpos++) { 574 | uc = buffer[rpos]; 575 | wpos += unsafe_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos); 576 | } 577 | } else { 578 | for (rpos = 0; rpos < length; rpos++) { 579 | uc = buffer[rpos]; 580 | wpos += utf8proc_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos); 581 | } 582 | } 583 | ((utf8proc_uint8_t *)buffer)[wpos] = 0; 584 | return wpos; 585 | } 586 | } 587 | 588 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map( 589 | const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options 590 | ) { 591 | utf8proc_int32_t *buffer; 592 | utf8proc_ssize_t result; 593 | *dstptr = NULL; 594 | result = utf8proc_decompose(str, strlen, NULL, 0, options); 595 | if (result < 0) return result; 596 | buffer = (utf8proc_int32_t *) malloc(result * sizeof(utf8proc_int32_t) + 1); 597 | if (!buffer) return UTF8PROC_ERROR_NOMEM; 598 | result = utf8proc_decompose(str, strlen, buffer, result, options); 599 | if (result < 0) { 600 | free(buffer); 601 | return result; 602 | } 603 | result = utf8proc_reencode(buffer, result, options); 604 | if (result < 0) { 605 | free(buffer); 606 | return result; 607 | } 608 | { 609 | utf8proc_int32_t *newptr; 610 | newptr = (utf8proc_int32_t *) realloc(buffer, (size_t)result+1); 611 | if (newptr) buffer = newptr; 612 | } 613 | *dstptr = (utf8proc_uint8_t *)buffer; 614 | return result; 615 | } 616 | 617 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str) { 618 | utf8proc_uint8_t *retval; 619 | utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | 620 | UTF8PROC_DECOMPOSE); 621 | return retval; 622 | } 623 | 624 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str) { 625 | utf8proc_uint8_t *retval; 626 | utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | 627 | UTF8PROC_COMPOSE); 628 | return retval; 629 | } 630 | 631 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str) { 632 | utf8proc_uint8_t *retval; 633 | utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | 634 | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT); 635 | return retval; 636 | } 637 | 638 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str) { 639 | utf8proc_uint8_t *retval; 640 | utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | 641 | UTF8PROC_COMPOSE | UTF8PROC_COMPAT); 642 | return retval; 643 | } 644 | 645 | -------------------------------------------------------------------------------- /source/utf8proc/utf8proc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Steven G. Johnson, Jiahao Chen, Peter Colberg, Tony Kelman, Scott P. Jones, and other contributors. 3 | * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all 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 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | * DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | 25 | /** 26 | * @mainpage 27 | * 28 | * utf8proc is a free/open-source (MIT/expat licensed) C library 29 | * providing Unicode normalization, case-folding, and other operations 30 | * for strings in the UTF-8 encoding, supporting Unicode version 31 | * 8.0.0. See the utf8proc home page (http://julialang.org/utf8proc/) 32 | * for downloads and other information, or the source code on github 33 | * (https://github.com/JuliaLang/utf8proc). 34 | * 35 | * For the utf8proc API documentation, see: @ref utf8proc.h 36 | * 37 | * The features of utf8proc include: 38 | * 39 | * - Transformation of strings (@ref utf8proc_map) to: 40 | * - decompose (@ref UTF8PROC_DECOMPOSE) or compose (@ref UTF8PROC_COMPOSE) Unicode combining characters (http://en.wikipedia.org/wiki/Combining_character) 41 | * - canonicalize Unicode compatibility characters (@ref UTF8PROC_COMPAT) 42 | * - strip "ignorable" (@ref UTF8PROC_IGNORE) characters, control characters (@ref UTF8PROC_STRIPCC), or combining characters such as accents (@ref UTF8PROC_STRIPMARK) 43 | * - case-folding (@ref UTF8PROC_CASEFOLD) 44 | * - Unicode normalization: @ref utf8proc_NFD, @ref utf8proc_NFC, @ref utf8proc_NFKD, @ref utf8proc_NFKC 45 | * - Detecting grapheme boundaries (@ref utf8proc_grapheme_break and @ref UTF8PROC_CHARBOUND) 46 | * - Character-width computation: @ref utf8proc_charwidth 47 | * - Classification of characters by Unicode category: @ref utf8proc_category and @ref utf8proc_category_string 48 | * - Encode (@ref utf8proc_encode_char) and decode (@ref utf8proc_iterate) Unicode codepoints to/from UTF-8. 49 | */ 50 | 51 | /** @file */ 52 | 53 | #ifndef UTF8PROC_H 54 | #define UTF8PROC_H 55 | 56 | /** @name API version 57 | * 58 | * The utf8proc API version MAJOR.MINOR.PATCH, following 59 | * semantic-versioning rules (http://semver.org) based on API 60 | * compatibility. 61 | * 62 | * This is also returned at runtime by @ref utf8proc_version; however, the 63 | * runtime version may append a string like "-dev" to the version number 64 | * for prerelease versions. 65 | * 66 | * @note The shared-library version number in the Makefile may be different, 67 | * being based on ABI compatibility rather than API compatibility. 68 | */ 69 | /** @{ */ 70 | /** The MAJOR version number (increased when backwards API compatibility is broken). */ 71 | #define UTF8PROC_VERSION_MAJOR 1 72 | /** The MINOR version number (increased when new functionality is added in a backwards-compatible manner). */ 73 | #define UTF8PROC_VERSION_MINOR 3 74 | /** The PATCH version (increased for fixes that do not change the API). */ 75 | #define UTF8PROC_VERSION_PATCH 0 76 | /** @} */ 77 | 78 | #include 79 | #include 80 | #ifdef _MSC_VER 81 | typedef signed char utf8proc_int8_t; 82 | typedef unsigned char utf8proc_uint8_t; 83 | typedef short utf8proc_int16_t; 84 | typedef unsigned short utf8proc_uint16_t; 85 | typedef int utf8proc_int32_t; 86 | typedef unsigned int utf8proc_uint32_t; 87 | # ifdef _WIN64 88 | typedef __int64 utf8proc_ssize_t; 89 | typedef unsigned __int64 utf8proc_size_t; 90 | # else 91 | typedef int utf8proc_ssize_t; 92 | typedef unsigned int utf8proc_size_t; 93 | # endif 94 | # ifndef __cplusplus 95 | typedef unsigned char utf8proc_bool; 96 | enum {false, true}; 97 | # else 98 | typedef bool utf8proc_bool; 99 | # endif 100 | #else 101 | # include 102 | # include 103 | typedef int8_t utf8proc_int8_t; 104 | typedef uint8_t utf8proc_uint8_t; 105 | typedef int16_t utf8proc_int16_t; 106 | typedef uint16_t utf8proc_uint16_t; 107 | typedef int32_t utf8proc_int32_t; 108 | typedef uint32_t utf8proc_uint32_t; 109 | typedef size_t utf8proc_size_t; 110 | typedef ssize_t utf8proc_ssize_t; 111 | typedef bool utf8proc_bool; 112 | #endif 113 | #include 114 | 115 | #ifdef _WIN32 116 | # ifdef UTF8PROC_EXPORTS 117 | # define UTF8PROC_DLLEXPORT __declspec(dllexport) 118 | # else 119 | # define UTF8PROC_DLLEXPORT __declspec(dllimport) 120 | # endif 121 | #elif __GNUC__ >= 4 122 | # define UTF8PROC_DLLEXPORT __attribute__ ((visibility("default"))) 123 | #else 124 | # define UTF8PROC_DLLEXPORT 125 | #endif 126 | 127 | #ifdef __cplusplus 128 | extern "C" { 129 | #endif 130 | 131 | #ifndef SSIZE_MAX 132 | #define SSIZE_MAX ((size_t)SIZE_MAX/2) 133 | #endif 134 | 135 | #ifndef UINT16_MAX 136 | # define UINT16_MAX ~(utf8proc_uint16_t)0 137 | #endif 138 | 139 | /** 140 | * Option flags used by several functions in the library. 141 | */ 142 | typedef enum { 143 | /** The given UTF-8 input is NULL terminated. */ 144 | UTF8PROC_NULLTERM = (1<<0), 145 | /** Unicode Versioning Stability has to be respected. */ 146 | UTF8PROC_STABLE = (1<<1), 147 | /** Compatibility decomposition (i.e. formatting information is lost). */ 148 | UTF8PROC_COMPAT = (1<<2), 149 | /** Return a result with decomposed characters. */ 150 | UTF8PROC_COMPOSE = (1<<3), 151 | /** Return a result with decomposed characters. */ 152 | UTF8PROC_DECOMPOSE = (1<<4), 153 | /** Strip "default ignorable characters" such as SOFT-HYPHEN or ZERO-WIDTH-SPACE. */ 154 | UTF8PROC_IGNORE = (1<<5), 155 | /** Return an error, if the input contains unassigned codepoints. */ 156 | UTF8PROC_REJECTNA = (1<<6), 157 | /** 158 | * Indicating that NLF-sequences (LF, CRLF, CR, NEL) are representing a 159 | * line break, and should be converted to the codepoint for line 160 | * separation (LS). 161 | */ 162 | UTF8PROC_NLF2LS = (1<<7), 163 | /** 164 | * Indicating that NLF-sequences are representing a paragraph break, and 165 | * should be converted to the codepoint for paragraph separation 166 | * (PS). 167 | */ 168 | UTF8PROC_NLF2PS = (1<<8), 169 | /** Indicating that the meaning of NLF-sequences is unknown. */ 170 | UTF8PROC_NLF2LF = (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS), 171 | /** Strips and/or convers control characters. 172 | * 173 | * NLF-sequences are transformed into space, except if one of the 174 | * NLF2LS/PS/LF options is given. HorizontalTab (HT) and FormFeed (FF) 175 | * are treated as a NLF-sequence in this case. All other control 176 | * characters are simply removed. 177 | */ 178 | UTF8PROC_STRIPCC = (1<<9), 179 | /** 180 | * Performs unicode case folding, to be able to do a case-insensitive 181 | * string comparison. 182 | */ 183 | UTF8PROC_CASEFOLD = (1<<10), 184 | /** 185 | * Inserts 0xFF bytes at the beginning of each sequence which is 186 | * representing a single grapheme cluster (see UAX#29). 187 | */ 188 | UTF8PROC_CHARBOUND = (1<<11), 189 | /** Lumps certain characters together. 190 | * 191 | * E.g. HYPHEN U+2010 and MINUS U+2212 to ASCII "-". See lump.md for details. 192 | * 193 | * If NLF2LF is set, this includes a transformation of paragraph and 194 | * line separators to ASCII line-feed (LF). 195 | */ 196 | UTF8PROC_LUMP = (1<<12), 197 | /** Strips all character markings. 198 | * 199 | * This includes non-spacing, spacing and enclosing (i.e. accents). 200 | * @note This option works only with @ref UTF8PROC_COMPOSE or 201 | * @ref UTF8PROC_DECOMPOSE 202 | */ 203 | UTF8PROC_STRIPMARK = (1<<13), 204 | } utf8proc_option_t; 205 | 206 | /** @name Error codes 207 | * Error codes being returned by almost all functions. 208 | */ 209 | /** @{ */ 210 | /** Memory could not be allocated. */ 211 | #define UTF8PROC_ERROR_NOMEM -1 212 | /** The given string is too long to be processed. */ 213 | #define UTF8PROC_ERROR_OVERFLOW -2 214 | /** The given string is not a legal UTF-8 string. */ 215 | #define UTF8PROC_ERROR_INVALIDUTF8 -3 216 | /** The @ref UTF8PROC_REJECTNA flag was set and an unassigned codepoint was found. */ 217 | #define UTF8PROC_ERROR_NOTASSIGNED -4 218 | /** Invalid options have been used. */ 219 | #define UTF8PROC_ERROR_INVALIDOPTS -5 220 | /** @} */ 221 | 222 | /* @name Types */ 223 | 224 | /** Holds the value of a property. */ 225 | typedef utf8proc_int16_t utf8proc_propval_t; 226 | 227 | /** Struct containing information about a codepoint. */ 228 | typedef struct utf8proc_property_struct { 229 | /** 230 | * Unicode category. 231 | * @see utf8proc_category_t. 232 | */ 233 | utf8proc_propval_t category; 234 | utf8proc_propval_t combining_class; 235 | /** 236 | * Bidirectional class. 237 | * @see utf8proc_bidi_class_t. 238 | */ 239 | utf8proc_propval_t bidi_class; 240 | /** 241 | * @anchor Decomposition type. 242 | * @see utf8proc_decomp_type_t. 243 | */ 244 | utf8proc_propval_t decomp_type; 245 | utf8proc_uint16_t decomp_mapping; 246 | utf8proc_uint16_t casefold_mapping; 247 | utf8proc_int32_t uppercase_mapping; 248 | utf8proc_int32_t lowercase_mapping; 249 | utf8proc_int32_t titlecase_mapping; 250 | utf8proc_int32_t comb1st_index; 251 | utf8proc_int32_t comb2nd_index; 252 | unsigned bidi_mirrored:1; 253 | unsigned comp_exclusion:1; 254 | /** 255 | * Can this codepoint be ignored? 256 | * 257 | * Used by @ref utf8proc_decompose_char when @ref UTF8PROC_IGNORE is 258 | * passed as an option. 259 | */ 260 | unsigned ignorable:1; 261 | unsigned control_boundary:1; 262 | /** 263 | * Boundclass. 264 | * @see utf8proc_boundclass_t. 265 | */ 266 | unsigned boundclass:4; 267 | /** The width of the codepoint. */ 268 | unsigned charwidth:2; 269 | } utf8proc_property_t; 270 | 271 | /** Unicode categories. */ 272 | typedef enum { 273 | UTF8PROC_CATEGORY_CN = 0, /**< Other, not assigned */ 274 | UTF8PROC_CATEGORY_LU = 1, /**< Letter, uppercase */ 275 | UTF8PROC_CATEGORY_LL = 2, /**< Letter, lowercase */ 276 | UTF8PROC_CATEGORY_LT = 3, /**< Letter, titlecase */ 277 | UTF8PROC_CATEGORY_LM = 4, /**< Letter, modifier */ 278 | UTF8PROC_CATEGORY_LO = 5, /**< Letter, other */ 279 | UTF8PROC_CATEGORY_MN = 6, /**< Mark, nonspacing */ 280 | UTF8PROC_CATEGORY_MC = 7, /**< Mark, spacing combining */ 281 | UTF8PROC_CATEGORY_ME = 8, /**< Mark, enclosing */ 282 | UTF8PROC_CATEGORY_ND = 9, /**< Number, decimal digit */ 283 | UTF8PROC_CATEGORY_NL = 10, /**< Number, letter */ 284 | UTF8PROC_CATEGORY_NO = 11, /**< Number, other */ 285 | UTF8PROC_CATEGORY_PC = 12, /**< Punctuation, connector */ 286 | UTF8PROC_CATEGORY_PD = 13, /**< Punctuation, dash */ 287 | UTF8PROC_CATEGORY_PS = 14, /**< Punctuation, open */ 288 | UTF8PROC_CATEGORY_PE = 15, /**< Punctuation, close */ 289 | UTF8PROC_CATEGORY_PI = 16, /**< Punctuation, initial quote */ 290 | UTF8PROC_CATEGORY_PF = 17, /**< Punctuation, final quote */ 291 | UTF8PROC_CATEGORY_PO = 18, /**< Punctuation, other */ 292 | UTF8PROC_CATEGORY_SM = 19, /**< Symbol, math */ 293 | UTF8PROC_CATEGORY_SC = 20, /**< Symbol, currency */ 294 | UTF8PROC_CATEGORY_SK = 21, /**< Symbol, modifier */ 295 | UTF8PROC_CATEGORY_SO = 22, /**< Symbol, other */ 296 | UTF8PROC_CATEGORY_ZS = 23, /**< Separator, space */ 297 | UTF8PROC_CATEGORY_ZL = 24, /**< Separator, line */ 298 | UTF8PROC_CATEGORY_ZP = 25, /**< Separator, paragraph */ 299 | UTF8PROC_CATEGORY_CC = 26, /**< Other, control */ 300 | UTF8PROC_CATEGORY_CF = 27, /**< Other, format */ 301 | UTF8PROC_CATEGORY_CS = 28, /**< Other, surrogate */ 302 | UTF8PROC_CATEGORY_CO = 29, /**< Other, private use */ 303 | } utf8proc_category_t; 304 | 305 | /** Bidirectional character classes. */ 306 | typedef enum { 307 | UTF8PROC_BIDI_CLASS_L = 1, /**< Left-to-Right */ 308 | UTF8PROC_BIDI_CLASS_LRE = 2, /**< Left-to-Right Embedding */ 309 | UTF8PROC_BIDI_CLASS_LRO = 3, /**< Left-to-Right Override */ 310 | UTF8PROC_BIDI_CLASS_R = 4, /**< Right-to-Left */ 311 | UTF8PROC_BIDI_CLASS_AL = 5, /**< Right-to-Left Arabic */ 312 | UTF8PROC_BIDI_CLASS_RLE = 6, /**< Right-to-Left Embedding */ 313 | UTF8PROC_BIDI_CLASS_RLO = 7, /**< Right-to-Left Override */ 314 | UTF8PROC_BIDI_CLASS_PDF = 8, /**< Pop Directional Format */ 315 | UTF8PROC_BIDI_CLASS_EN = 9, /**< European Number */ 316 | UTF8PROC_BIDI_CLASS_ES = 10, /**< European Separator */ 317 | UTF8PROC_BIDI_CLASS_ET = 11, /**< European Number Terminator */ 318 | UTF8PROC_BIDI_CLASS_AN = 12, /**< Arabic Number */ 319 | UTF8PROC_BIDI_CLASS_CS = 13, /**< Common Number Separator */ 320 | UTF8PROC_BIDI_CLASS_NSM = 14, /**< Nonspacing Mark */ 321 | UTF8PROC_BIDI_CLASS_BN = 15, /**< Boundary Neutral */ 322 | UTF8PROC_BIDI_CLASS_B = 16, /**< Paragraph Separator */ 323 | UTF8PROC_BIDI_CLASS_S = 17, /**< Segment Separator */ 324 | UTF8PROC_BIDI_CLASS_WS = 18, /**< Whitespace */ 325 | UTF8PROC_BIDI_CLASS_ON = 19, /**< Other Neutrals */ 326 | UTF8PROC_BIDI_CLASS_LRI = 20, /**< Left-to-Right Isolate */ 327 | UTF8PROC_BIDI_CLASS_RLI = 21, /**< Right-to-Left Isolate */ 328 | UTF8PROC_BIDI_CLASS_FSI = 22, /**< First Strong Isolate */ 329 | UTF8PROC_BIDI_CLASS_PDI = 23, /**< Pop Directional Isolate */ 330 | } utf8proc_bidi_class_t; 331 | 332 | /** Decomposition type. */ 333 | typedef enum { 334 | UTF8PROC_DECOMP_TYPE_FONT = 1, /**< Font */ 335 | UTF8PROC_DECOMP_TYPE_NOBREAK = 2, /**< Nobreak */ 336 | UTF8PROC_DECOMP_TYPE_INITIAL = 3, /**< Initial */ 337 | UTF8PROC_DECOMP_TYPE_MEDIAL = 4, /**< Medial */ 338 | UTF8PROC_DECOMP_TYPE_FINAL = 5, /**< Final */ 339 | UTF8PROC_DECOMP_TYPE_ISOLATED = 6, /**< Isolated */ 340 | UTF8PROC_DECOMP_TYPE_CIRCLE = 7, /**< Circle */ 341 | UTF8PROC_DECOMP_TYPE_SUPER = 8, /**< Super */ 342 | UTF8PROC_DECOMP_TYPE_SUB = 9, /**< Sub */ 343 | UTF8PROC_DECOMP_TYPE_VERTICAL = 10, /**< Vertical */ 344 | UTF8PROC_DECOMP_TYPE_WIDE = 11, /**< Wide */ 345 | UTF8PROC_DECOMP_TYPE_NARROW = 12, /**< Narrow */ 346 | UTF8PROC_DECOMP_TYPE_SMALL = 13, /**< Small */ 347 | UTF8PROC_DECOMP_TYPE_SQUARE = 14, /**< Square */ 348 | UTF8PROC_DECOMP_TYPE_FRACTION = 15, /**< Fraction */ 349 | UTF8PROC_DECOMP_TYPE_COMPAT = 16, /**< Compat */ 350 | } utf8proc_decomp_type_t; 351 | 352 | /** Boundclass property. */ 353 | typedef enum { 354 | UTF8PROC_BOUNDCLASS_START = 0, /**< Start */ 355 | UTF8PROC_BOUNDCLASS_OTHER = 1, /**< Other */ 356 | UTF8PROC_BOUNDCLASS_CR = 2, /**< Cr */ 357 | UTF8PROC_BOUNDCLASS_LF = 3, /**< Lf */ 358 | UTF8PROC_BOUNDCLASS_CONTROL = 4, /**< Control */ 359 | UTF8PROC_BOUNDCLASS_EXTEND = 5, /**< Extend */ 360 | UTF8PROC_BOUNDCLASS_L = 6, /**< L */ 361 | UTF8PROC_BOUNDCLASS_V = 7, /**< V */ 362 | UTF8PROC_BOUNDCLASS_T = 8, /**< T */ 363 | UTF8PROC_BOUNDCLASS_LV = 9, /**< Lv */ 364 | UTF8PROC_BOUNDCLASS_LVT = 10, /**< Lvt */ 365 | UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR = 11, /**< Regional indicator */ 366 | UTF8PROC_BOUNDCLASS_SPACINGMARK = 12, /**< Spacingmark */ 367 | } utf8proc_boundclass_t; 368 | 369 | /** 370 | * Array containing the byte lengths of a UTF-8 encoded codepoint based 371 | * on the first byte. 372 | */ 373 | UTF8PROC_DLLEXPORT extern const utf8proc_int8_t utf8proc_utf8class[256]; 374 | 375 | /** 376 | * Returns the utf8proc API version as a string MAJOR.MINOR.PATCH 377 | * (http://semver.org format), possibly with a "-dev" suffix for 378 | * development versions. 379 | */ 380 | UTF8PROC_DLLEXPORT const char *utf8proc_version(void); 381 | 382 | /** 383 | * Returns an informative error string for the given utf8proc error code 384 | * (e.g. the error codes returned by @ref utf8proc_map). 385 | */ 386 | UTF8PROC_DLLEXPORT const char *utf8proc_errmsg(utf8proc_ssize_t errcode); 387 | 388 | /** 389 | * Reads a single codepoint from the UTF-8 sequence being pointed to by `str`. 390 | * The maximum number of bytes read is `strlen`, unless `strlen` is 391 | * negative (in which case up to 4 bytes are read). 392 | * 393 | * If a valid codepoint could be read, it is stored in the variable 394 | * pointed to by `codepoint_ref`, otherwise that variable will be set to -1. 395 | * In case of success, the number of bytes read is returned; otherwise, a 396 | * negative error code is returned. 397 | */ 398 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate(const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_int32_t *codepoint_ref); 399 | 400 | /** 401 | * Check if a codepoint is valid (regardless of whether it has been 402 | * assigned a value by the current Unicode standard). 403 | * 404 | * @return 1 if the given `codepoint` is valid and otherwise return 0. 405 | */ 406 | UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_codepoint_valid(utf8proc_int32_t codepoint); 407 | 408 | /** 409 | * Encodes the codepoint as an UTF-8 string in the byte array pointed 410 | * to by `dst`. This array must be at least 4 bytes long. 411 | * 412 | * In case of success the number of bytes written is returned, and 413 | * otherwise 0 is returned. 414 | * 415 | * This function does not check whether `codepoint` is valid Unicode. 416 | */ 417 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t codepoint, utf8proc_uint8_t *dst); 418 | 419 | /** 420 | * Look up the properties for a given codepoint. 421 | * 422 | * @param codepoint The Unicode codepoint. 423 | * 424 | * @returns 425 | * A pointer to a (constant) struct containing information about 426 | * the codepoint. 427 | * @par 428 | * If the codepoint is unassigned or invalid, a pointer to a special struct is 429 | * returned in which `category` is 0 (@ref UTF8PROC_CATEGORY_CN). 430 | */ 431 | UTF8PROC_DLLEXPORT const utf8proc_property_t *utf8proc_get_property(utf8proc_int32_t codepoint); 432 | 433 | /** Decompose a codepoint into an array of codepoints. 434 | * 435 | * @param codepoint the codepoint. 436 | * @param dst the destination buffer. 437 | * @param bufsize the size of the destination buffer. 438 | * @param options one or more of the following flags: 439 | * - @ref UTF8PROC_REJECTNA - return an error `codepoint` is unassigned 440 | * - @ref UTF8PROC_IGNORE - strip "default ignorable" codepoints 441 | * - @ref UTF8PROC_CASEFOLD - apply Unicode casefolding 442 | * - @ref UTF8PROC_COMPAT - replace certain codepoints with their 443 | * compatibility decomposition 444 | * - @ref UTF8PROC_CHARBOUND - insert 0xFF bytes before each grapheme cluster 445 | * - @ref UTF8PROC_LUMP - lump certain different codepoints together 446 | * - @ref UTF8PROC_STRIPMARK - remove all character marks 447 | * @param last_boundclass 448 | * Pointer to an integer variable containing 449 | * the previous codepoint's boundary class if the @ref UTF8PROC_CHARBOUND 450 | * option is used. Otherwise, this parameter is ignored. 451 | * 452 | * @return 453 | * In case of success, the number of codepoints written is returned; in case 454 | * of an error, a negative error code is returned (@ref utf8proc_errmsg). 455 | * @par 456 | * If the number of written codepoints would be bigger than `bufsize`, the 457 | * required buffer size is returned, while the buffer will be overwritten with 458 | * undefined data. 459 | */ 460 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char( 461 | utf8proc_int32_t codepoint, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, 462 | utf8proc_option_t options, int *last_boundclass 463 | ); 464 | 465 | /** 466 | * The same as @ref utf8proc_decompose_char, but acts on a whole UTF-8 467 | * string and orders the decomposed sequences correctly. 468 | * 469 | * If the @ref UTF8PROC_NULLTERM flag in `options` is set, processing 470 | * will be stopped, when a NULL byte is encounted, otherwise `strlen` 471 | * bytes are processed. The result (in the form of 32-bit unicode 472 | * codepoints) is written into the buffer being pointed to by 473 | * `buffer` (which must contain at least `bufsize` entries). In case of 474 | * success, the number of codepoints written is returned; in case of an 475 | * error, a negative error code is returned (@ref utf8proc_errmsg). 476 | * 477 | * If the number of written codepoints would be bigger than `bufsize`, the 478 | * required buffer size is returned, while the buffer will be overwritten with 479 | * undefined data. 480 | */ 481 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose( 482 | const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, 483 | utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options 484 | ); 485 | 486 | /** 487 | * Reencodes the sequence of `length` codepoints pointed to by `buffer` 488 | * UTF-8 data in-place (i.e., the result is also stored in `buffer`). 489 | * 490 | * @param buffer the (native-endian UTF-32) unicode codepoints to re-encode. 491 | * @param length the length (in codepoints) of the buffer. 492 | * @param options a bitwise or (`|`) of one or more of the following flags: 493 | * - @ref UTF8PROC_NLF2LS - convert LF, CRLF, CR and NEL into LS 494 | * - @ref UTF8PROC_NLF2PS - convert LF, CRLF, CR and NEL into PS 495 | * - @ref UTF8PROC_NLF2LF - convert LF, CRLF, CR and NEL into LF 496 | * - @ref UTF8PROC_STRIPCC - strip or convert all non-affected control characters 497 | * - @ref UTF8PROC_COMPOSE - try to combine decomposed codepoints into composite 498 | * codepoints 499 | * - @ref UTF8PROC_STABLE - prohibit combining characters that would violate 500 | * the unicode versioning stability 501 | * 502 | * @return 503 | * In case of success, the length (in bytes) of the resulting UTF-8 string is 504 | * returned; otherwise, a negative error code is returned (@ref utf8proc_errmsg). 505 | * 506 | * @warning The amount of free space pointed to by `buffer` must 507 | * exceed the amount of the input data by one byte, and the 508 | * entries of the array pointed to by `str` have to be in the 509 | * range `0x0000` to `0x10FFFF`. Otherwise, the program might crash! 510 | */ 511 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options); 512 | 513 | /** 514 | * Given a pair of consecutive codepoints, return whether a grapheme break is 515 | * permitted between them (as defined by the extended grapheme clusters in UAX#29). 516 | */ 517 | UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_grapheme_break(utf8proc_int32_t codepoint1, utf8proc_int32_t codepoint2); 518 | 519 | 520 | /** 521 | * Given a codepoint `c`, return the codepoint of the corresponding 522 | * lower-case character, if any; otherwise (if there is no lower-case 523 | * variant, or if `c` is not a valid codepoint) return `c`. 524 | */ 525 | UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_tolower(utf8proc_int32_t c); 526 | 527 | /** 528 | * Given a codepoint `c`, return the codepoint of the corresponding 529 | * upper-case character, if any; otherwise (if there is no upper-case 530 | * variant, or if `c` is not a valid codepoint) return `c`. 531 | */ 532 | UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_toupper(utf8proc_int32_t c); 533 | 534 | /** 535 | * Given a codepoint, return a character width analogous to `wcwidth(codepoint)`, 536 | * except that a width of 0 is returned for non-printable codepoints 537 | * instead of -1 as in `wcwidth`. 538 | * 539 | * @note 540 | * If you want to check for particular types of non-printable characters, 541 | * (analogous to `isprint` or `iscntrl`), use @ref utf8proc_category. */ 542 | UTF8PROC_DLLEXPORT int utf8proc_charwidth(utf8proc_int32_t codepoint); 543 | 544 | /** 545 | * Return the Unicode category for the codepoint (one of the 546 | * @ref utf8proc_category_t constants.) 547 | */ 548 | UTF8PROC_DLLEXPORT utf8proc_category_t utf8proc_category(utf8proc_int32_t codepoint); 549 | 550 | /** 551 | * Return the two-letter (nul-terminated) Unicode category string for 552 | * the codepoint (e.g. `"Lu"` or `"Co"`). 553 | */ 554 | UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t codepoint); 555 | 556 | /** 557 | * Maps the given UTF-8 string pointed to by `str` to a new UTF-8 558 | * string, allocated dynamically by `malloc` and returned via `dstptr`. 559 | * 560 | * If the @ref UTF8PROC_NULLTERM flag in the `options` field is set, 561 | * the length is determined by a NULL terminator, otherwise the 562 | * parameter `strlen` is evaluated to determine the string length, but 563 | * in any case the result will be NULL terminated (though it might 564 | * contain NULL characters with the string if `str` contained NULL 565 | * characters). Other flags in the `options` field are passed to the 566 | * functions defined above, and regarded as described. 567 | * 568 | * In case of success the length of the new string is returned, 569 | * otherwise a negative error code is returned. 570 | * 571 | * @note The memory of the new UTF-8 string will have been allocated 572 | * with `malloc`, and should therefore be deallocated with `free`. 573 | */ 574 | UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map( 575 | const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options 576 | ); 577 | 578 | /** @name Unicode normalization 579 | * 580 | * Returns a pointer to newly allocated memory of a NFD, NFC, NFKD or NFKC 581 | * normalized version of the null-terminated string `str`. These 582 | * are shortcuts to calling @ref utf8proc_map with @ref UTF8PROC_NULLTERM 583 | * combined with @ref UTF8PROC_STABLE and flags indicating the normalization. 584 | */ 585 | /** @{ */ 586 | /** NFD normalization (@ref UTF8PROC_DECOMPOSE). */ 587 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str); 588 | /** NFC normalization (@ref UTF8PROC_COMPOSE). */ 589 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str); 590 | /** NFD normalization (@ref UTF8PROC_DECOMPOSE and @ref UTF8PROC_COMPAT). */ 591 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str); 592 | /** NFD normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT). */ 593 | UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str); 594 | /** @} */ 595 | 596 | #ifdef __cplusplus 597 | } 598 | #endif 599 | 600 | #endif 601 | 602 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include <3ds.h> 26 | 27 | #include "config.h" 28 | #include "menu.h" 29 | #include "utils.h" 30 | #include "cia.h" 31 | #include "data.h" 32 | #include "types.h" 33 | 34 | #include "svchax/svchax.h" 35 | #include "json/json.h" 36 | #include "fts_fuzzy_match.h" 37 | #include "utf8proc/utf8proc.h" 38 | 39 | static const u16 top = 0x140; 40 | static bool bSvcHaxAvailable = true; 41 | static bool bExit = false; 42 | static bool bRedoSearch = false; 43 | int sourceDataType; 44 | Json::Value sourceData; 45 | 46 | CConfig config; 47 | 48 | struct find_game_item { 49 | std::string titleid; 50 | find_game_item(std::string titleid) : titleid(titleid) {} 51 | bool operator () ( const game_item& gi ) const { 52 | return gi.titleid == titleid; 53 | } 54 | }; 55 | 56 | // Vector used for download queue 57 | std::vector game_queue; 58 | 59 | bool compareByScore(const game_item &a, const game_item &b) 60 | { 61 | return a.score > b.score; 62 | } 63 | 64 | Result ProcessCIA(std::string dir, std::string titleName) 65 | { 66 | FILE *tik = fopen((dir + "/ticket").c_str(), "rb"); 67 | if (!tik) 68 | { 69 | return -1; 70 | } 71 | TIK_CONTEXT tik_context = process_tik(tik); 72 | 73 | FILE *tmd = fopen((dir + "/tmd").c_str(),"rb"); 74 | if (!tmd) 75 | { 76 | fclose(tik); 77 | return -1; 78 | } 79 | TMD_CONTEXT tmd_context = process_tmd(tmd); 80 | 81 | if(tik_context.result != 0 || tmd_context.result != 0){ 82 | printf("[!] Input files could not be processed successfully\n"); 83 | free(tmd_context.content_struct); 84 | fclose(tik); 85 | fclose(tmd); 86 | return -1; 87 | } 88 | 89 | int result; 90 | if (config.GetMode() == CConfig::Mode::INSTALL_CIA) 91 | { 92 | result = install_cia(tmd_context, tik_context); 93 | } 94 | else 95 | { 96 | FILE *output = fopen((dir + "/" + titleName + ".cia").c_str(),"wb"); 97 | if (!output) 98 | { 99 | result = -2; 100 | } 101 | else 102 | { 103 | result = generate_cia(tmd_context, tik_context, output); 104 | if(result != 0) 105 | { 106 | remove((dir + "/" + titleName + ".cia").c_str()); 107 | } 108 | } 109 | } 110 | 111 | // free allocated memory/handles 112 | free(tmd_context.content_struct); 113 | fclose(tik); 114 | fclose(tmd); 115 | 116 | // Clean up temp files 117 | remove((dir + "/ticket").c_str()); 118 | remove((dir + "/tmd").c_str()); 119 | return result; 120 | } 121 | 122 | std::string u32_to_hex_string(u32 i) 123 | { 124 | std::stringstream stream; 125 | stream << std::setfill ('0') << std::setw(sizeof(u32)*2) << std::hex << i; 126 | return stream.str(); 127 | } 128 | 129 | int mkpath(std::string s,mode_t mode) 130 | { 131 | size_t pre=0,pos; 132 | std::string dir; 133 | int mdret = 0; 134 | 135 | if(s[s.size()-1]!='/'){ 136 | // force trailing / so we can handle everything in loop 137 | s+='/'; 138 | } 139 | 140 | while((pos=s.find_first_of('/',pre))!=std::string::npos){ 141 | dir=s.substr(0,pos++); 142 | pre=pos; 143 | if(dir.size()==0) continue; // if leading / first time is 0 length 144 | if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){ 145 | return mdret; 146 | } 147 | } 148 | return mdret; 149 | } 150 | 151 | char parse_hex(char c) 152 | { 153 | if ('0' <= c && c <= '9') return c - '0'; 154 | if ('A' <= c && c <= 'F') return c - 'A' + 10; 155 | if ('a' <= c && c <= 'f') return c - 'a' + 10; 156 | std::abort(); 157 | } 158 | 159 | char* parse_string(const std::string & s) 160 | { 161 | char* buffer = new char[s.size() / 2]; 162 | for (std::size_t i = 0; i != s.size() / 2; ++i) 163 | buffer[i] = 16 * parse_hex(s[2 * i]) + parse_hex(s[2 * i + 1]); 164 | return buffer; 165 | } 166 | 167 | std::string get_file_contents(const char *filename) 168 | { 169 | std::ifstream in(filename, std::ios::in | std::ios::binary); 170 | if (in) 171 | { 172 | return(std::string((std::istreambuf_iterator(in)), std::istreambuf_iterator())); 173 | } 174 | throw(errno); 175 | } 176 | 177 | void CreateTicket(std::string titleId, std::string encTitleKey, char* titleVersion, std::string outputFullPath) 178 | { 179 | std::ofstream ofs; 180 | 181 | ofs.open(outputFullPath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); 182 | ofs.write(tikTemp, TICKET_SIZE); 183 | ofs.close(); 184 | 185 | ofs.open(outputFullPath, std::ofstream::out | std::ofstream::in | std::ofstream::binary); 186 | 187 | //write version 188 | ofs.seekp(top+0xA6, std::ios::beg); 189 | ofs.write(titleVersion, 0x2); 190 | 191 | //write title id 192 | char* nTitleID = parse_string(titleId); 193 | ofs.seekp(top+0x9C, std::ios::beg); 194 | ofs.write(nTitleID, 0x8); 195 | free(nTitleID); 196 | 197 | //write key 198 | char* nTitleKey = parse_string(encTitleKey); 199 | ofs.seekp(top+0x7F, std::ios::beg); 200 | ofs.write(nTitleKey, 0x10); 201 | free(nTitleKey); 202 | 203 | ofs.close(); 204 | } 205 | 206 | void InstallTicket(std::string FullPath, std::string TitleId) 207 | { 208 | Handle hTik; 209 | u32 writtenbyte; 210 | std::string curr = get_file_contents(FullPath.c_str()); 211 | 212 | // Remove the ticket incase there was a bad one previously installed 213 | char* nTitleId = parse_string(TitleId); 214 | u64 titleId = u8_to_u64((u8*)nTitleId, BIG_ENDIAN); 215 | free (nTitleId); 216 | AM_DeleteTicket(titleId); 217 | 218 | // Install new ticket 219 | AM_InstallTicketBegin(&hTik); 220 | FSFILE_Write(hTik, &writtenbyte, 0, curr.c_str(), 0x100000, 0); 221 | AM_InstallTicketFinish(hTik); 222 | printf("Ticket Installed."); 223 | //delete temp ticket, ticket folder still exists... ugly. later stream directly to the handle 224 | remove(FullPath.c_str()); 225 | } 226 | 227 | Result DownloadTitle(std::string titleId, std::string encTitleKey, std::string titleName, std::string region) 228 | { 229 | // Convert the titleid to a u64 for later use 230 | char* nTitleId = parse_string(titleId); 231 | u64 uTitleId = u8_to_u64((u8*)nTitleId, BIG_ENDIAN); 232 | free (nTitleId); 233 | 234 | // Wait for wifi to be available 235 | u32 wifi = 0; 236 | Result ret = 0; 237 | Result res = 0; 238 | while(R_SUCCEEDED(ret = ACU_GetWifiStatus(&wifi)) && wifi == 0) 239 | { 240 | hidScanInput(); 241 | if (hidKeysDown() & KEY_B) 242 | { 243 | ret = -1; 244 | break; 245 | } 246 | } 247 | 248 | if (R_FAILED(ret)) 249 | { 250 | printf("Unable to access internet.\n"); 251 | return ret; 252 | } 253 | 254 | std::string outputDir = "/CIAngel"; 255 | 256 | if (titleName.length() == 0) 257 | { 258 | titleName = titleId; 259 | } 260 | 261 | // Include region in filename 262 | if (region.length() > 0) 263 | { 264 | titleName = titleName + " (" + region + ")"; 265 | } 266 | 267 | std::string mode_text; 268 | if(config.GetMode() == CConfig::Mode::DOWNLOAD_CIA) 269 | { 270 | mode_text = "create"; 271 | } 272 | else if(config.GetMode() == CConfig::Mode::INSTALL_CIA) 273 | { 274 | mode_text = "install"; 275 | } 276 | 277 | 278 | printf("Starting - %s\n", titleName.c_str()); 279 | 280 | // If in install mode, download/install the SEED entry 281 | if (config.GetMode() == CConfig::Mode::INSTALL_CIA) 282 | { 283 | // Download and install the SEEDDB entry if install mode 284 | // Code based on code from FBI: https://github.com/Steveice10/FBI/blob/master/source/core/util.c#L254 285 | // Copyright (C) 2015 Steveice10 286 | u8 seed[16]; 287 | static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"}; 288 | u8 region = CFG_REGION_USA; 289 | CFGU_GetSystemLanguage(®ion); 290 | 291 | if(region <= CFG_REGION_TWN) { 292 | char url[128]; 293 | snprintf(url, 128, SEED_URL "0x%016llX/ext_key?country=%s", uTitleId, regionStrings[region]); 294 | 295 | httpcContext context; 296 | if(R_SUCCEEDED(res = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1))) { 297 | httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); 298 | 299 | u32 responseCode = 0; 300 | if(R_SUCCEEDED(res = httpcBeginRequest(&context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(&context, &responseCode))) { 301 | if(responseCode == 200) { 302 | u32 pos = 0; 303 | u32 bytesRead = 0; 304 | while(pos < sizeof(seed) && (R_SUCCEEDED(res = httpcDownloadData(&context, &seed[pos], sizeof(seed) - pos, &bytesRead)) || (u32)res == HTTPC_RESULTCODE_DOWNLOADPENDING)) { 305 | pos += bytesRead; 306 | } 307 | } else { 308 | res = -1; 309 | } 310 | } 311 | 312 | httpcCloseContext(&context); 313 | } 314 | 315 | if (R_SUCCEEDED(res)) 316 | { 317 | res = InstallSeed(uTitleId, seed); 318 | if (R_FAILED(res)) 319 | { 320 | printf("Error installing SEEDDB entry: 0x%lx\n", res); 321 | } 322 | } 323 | } 324 | } 325 | 326 | // Make sure the CIA doesn't already exist 327 | std::string cp = outputDir + "/" + titleName + ".cia"; 328 | if (config.GetMode() == CConfig::Mode::DOWNLOAD_CIA && FileExists(cp.c_str())) 329 | { 330 | printf("%s already exists.\n", cp.c_str()); 331 | return 0; 332 | } 333 | 334 | std::ofstream ofs; 335 | 336 | FILE *oh = fopen((outputDir + "/tmp/tmd").c_str(), "wb"); 337 | if (!oh) 338 | { 339 | printf("Error opening %s/tmp/tmd\n", outputDir.c_str()); 340 | return -1; 341 | } 342 | res = DownloadFile((NUS_URL + titleId + "/tmd").c_str(), oh, false); 343 | fclose(oh); 344 | if (res != 0) 345 | { 346 | printf("Could not download TMD. Internet/Title ID is OK?\n"); 347 | return res; 348 | } 349 | 350 | // Read version 351 | std::ifstream tmdfs; 352 | tmdfs.open(outputDir + "/tmp/tmd", std::ofstream::out | std::ofstream::in | std::ofstream::binary); 353 | char titleVersion[2]; 354 | tmdfs.seekg(top+0x9C, std::ios::beg); 355 | tmdfs.read(titleVersion, 0x2); 356 | tmdfs.close(); 357 | 358 | CreateTicket(titleId, encTitleKey, titleVersion, outputDir + "/tmp/ticket"); 359 | 360 | printf("Now %s the CIA...\n", mode_text.c_str()); 361 | 362 | res = ProcessCIA(outputDir + "/tmp", titleName); 363 | if (res != 0) 364 | { 365 | printf("Could not %s the CIA.\n", mode_text.c_str()); 366 | return res; 367 | } 368 | 369 | if (config.GetMode() == CConfig::Mode::DOWNLOAD_CIA) 370 | { 371 | rename((outputDir + "/tmp/" + titleName + ".cia").c_str(), (outputDir + "/" + titleName + ".cia").c_str()); 372 | } 373 | 374 | printf(" DONE!\n"); 375 | 376 | return res; 377 | } 378 | 379 | void ProcessGameQueue() 380 | { 381 | // Create the tickets folder if we're in ticket mode 382 | char empty_titleVersion[2] = {0x00, 0x00}; 383 | 384 | std::vector::iterator game = game_queue.begin(); 385 | while(aptMainLoop() && game != game_queue.end()) 386 | { 387 | std::string selected_titleid = (*game).titleid; 388 | std::string selected_enckey = (*game).titlekey; 389 | std::string selected_name = (*game).name; 390 | std::string selected_region = (*game).region; 391 | 392 | if (config.GetMode() == CConfig::Mode::INSTALL_TICKET) 393 | { 394 | CreateTicket(selected_titleid, selected_enckey, empty_titleVersion, "/CIAngel/tmp/ticket"); 395 | InstallTicket("/CIAngel/tmp/ticket", selected_titleid); 396 | } 397 | else 398 | { 399 | Result res = DownloadTitle(selected_titleid, selected_enckey, selected_name, selected_region); 400 | if (R_FAILED(res)) { 401 | printf("Error processing queue. Returning to menu\n"); 402 | break; 403 | } 404 | } 405 | 406 | game = game_queue.erase(game); 407 | } 408 | 409 | wait_key_specific("Press A to continue.\n", KEY_A); 410 | } 411 | 412 | void removeForbiddenChar(std::string* s) 413 | { 414 | std::string::iterator it; 415 | std::string illegalChars = "\\/:?\"<>|"; 416 | for (it = s->begin() ; it < s->end() ; ++it){ 417 | bool found = illegalChars.find(*it) != std::string::npos; 418 | if(found) 419 | { 420 | *it = ' '; 421 | } 422 | } 423 | } 424 | 425 | std::istream& GetLine(std::istream& is, std::string& t) 426 | { 427 | t.clear(); 428 | std::istream::sentry se(is, true); 429 | std::streambuf* sb = is.rdbuf(); 430 | 431 | for (;;) { 432 | int c = sb->sbumpc(); 433 | switch (c) { 434 | case '\n': 435 | return is; 436 | case '\r': 437 | if (sb->sgetc() == '\n') 438 | sb->sbumpc(); 439 | return is; 440 | case EOF: 441 | if (t.empty()) 442 | is.setstate(std::ios::eofbit); 443 | return is; 444 | default: 445 | t += (char)c; 446 | } 447 | } 448 | } 449 | 450 | std::string ToHex(const std::string& s) 451 | { 452 | std::ostringstream ret; 453 | for (std::string::size_type i = 0; i < s.length(); ++i) 454 | { 455 | int z = s[i]&0xff; 456 | ret << std::hex << std::setfill('0') << std::setw(2) << z; 457 | } 458 | return ret.str(); 459 | } 460 | 461 | void load_JSON_data() 462 | { 463 | printf("loading wings.json...\n"); 464 | std::ifstream ifs("/CIAngel/wings.json"); 465 | Json::Reader reader; 466 | Json::Value obj; 467 | reader.parse(ifs, obj); 468 | sourceData = obj; // array of characters 469 | 470 | if(sourceData[0]["titleID"].isString()) { 471 | sourceDataType = JSON_TYPE_ONLINE; 472 | } else if (sourceData[0]["titleid"].isString()) { 473 | sourceDataType = JSON_TYPE_WINGS; 474 | } 475 | } 476 | 477 | void loadConfig() 478 | { 479 | // Load config, and force mode to DOWNLOAD_CIA if svcHax not available, then resave 480 | config.LoadConfig("/CIAngel/config.json"); 481 | if (!bSvcHaxAvailable) 482 | { 483 | config.SetMode(CConfig::Mode::DOWNLOAD_CIA); 484 | } 485 | config.SaveConfig(); 486 | } 487 | 488 | // Search menu keypress callback 489 | bool menu_search_keypress(int selected, u32 key, void* data) 490 | { 491 | std::vector* cb_data = (std::vector*)data; 492 | 493 | // If key is 0, it means aptMainLoop() returned false, so we're exiting 494 | // Go back to the previous menu which will handle quitting 495 | if (!key) { 496 | return true; 497 | } 498 | 499 | // B goes back a screen 500 | if (key & KEY_B) 501 | { 502 | return true; 503 | } 504 | 505 | // A triggers the default action on the selected title 506 | if (key & KEY_A) 507 | { 508 | // Clean up the console since we'll be using it 509 | consoleClear(); 510 | 511 | // Fetch the title data and start downloading 512 | std::string selected_titleid = (*cb_data)[selected].titleid; 513 | std::string selected_enckey = (*cb_data)[selected].titlekey; 514 | std::string selected_name = (*cb_data)[selected].name; 515 | std::string selected_region = (*cb_data)[selected].region; 516 | 517 | printf("OK - %s\n", selected_name.c_str()); 518 | //removes any problem chars, not sure if whitespace is a problem too...? 519 | removeForbiddenChar(&selected_name); 520 | 521 | if(config.GetMode() == CConfig::Mode::INSTALL_TICKET) 522 | { 523 | char empty_titleVersion[2] = {0x00, 0x00}; 524 | CreateTicket(selected_titleid, selected_enckey, empty_titleVersion, "/CIAngel/tmp/ticket"); 525 | InstallTicket("/CIAngel/tmp/ticket", selected_titleid); 526 | } 527 | else 528 | { 529 | DownloadTitle(selected_titleid, selected_enckey, selected_name, selected_region); 530 | } 531 | 532 | wait_key_specific("\nPress A to continue.\n", KEY_A); 533 | return true; 534 | } 535 | 536 | // X triggers adding items to the download queue 537 | if (key & KEY_X) 538 | { 539 | consoleClear(); 540 | std::string titleid = (*cb_data)[selected].titleid; 541 | if (std::find_if(game_queue.begin(), game_queue.end(), find_game_item(titleid)) == game_queue.end()) 542 | { 543 | game_queue.push_back((*cb_data)[selected]); 544 | 545 | printf("Game added to queue.\n"); 546 | } 547 | else 548 | { 549 | printf("Game already in queue.\n"); 550 | } 551 | 552 | printf("Queue size: %d\n", game_queue.size()); 553 | 554 | if(wait_key_specific("\nPress A to continue.\nPress B to go back to the list.\n", KEY_A | KEY_B) & KEY_B) 555 | { 556 | bRedoSearch = true; 557 | } 558 | return true; 559 | } 560 | 561 | return false; 562 | } 563 | 564 | /* Search filter functions */ 565 | // Fuzzy match based on the game name 566 | bool search_by_name(std::string &searchString, Json::Value &gameData, int &outScore) 567 | { 568 | return fts::fuzzy_match(searchString.c_str(), gameData["ascii_name"].asCString(), outScore); 569 | } 570 | 571 | // Wildcard match based on game serial 572 | bool search_by_serial(std::string &searchString, Json::Value &gameData, int &outScore) 573 | { 574 | if (sourceDataType == JSON_TYPE_WINGS) 575 | { 576 | return (upperCase(gameData["code"].asString()).find(upperCase(searchString)) != std::string::npos); 577 | } 578 | else 579 | { 580 | return (upperCase(gameData["serial"].asString()).find(upperCase(searchString)) != std::string::npos); 581 | } 582 | } 583 | 584 | /* Menu Action Functions */ 585 | void action_search(bool (*match)(std::string &searchString, Json::Value &gameData, int &outScore)) 586 | { 587 | SwkbdState swkbd; 588 | 589 | consoleClear(); 590 | printf("Please enter text to search for:\n"); 591 | 592 | swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, -1); 593 | swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, 0); 594 | swkbdSetHintText(&swkbd, "Please enter text to search for"); 595 | char textBuf[255]; 596 | if (swkbdInputText(&swkbd, textBuf, sizeof(textBuf)) != SWKBD_BUTTON_CONFIRM) 597 | { 598 | return; 599 | } 600 | std::string searchString(textBuf); 601 | 602 | do 603 | { 604 | 605 | bRedoSearch = false; 606 | 607 | // User has entered their input, so let's scrap the keyboard 608 | clear_screen(GFX_BOTTOM); 609 | 610 | std::vector display_output; 611 | int outScore; 612 | 613 | for (unsigned int i = 0; i < sourceData.size(); i++) { 614 | // Check the region filter 615 | std::string regionFilter = config.GetRegionFilter(); 616 | if(regionFilter != "off" && sourceData[i]["region"].asString() != regionFilter) { 617 | continue; 618 | } 619 | 620 | // Check that the encTitleKey isn't null 621 | if (sourceData[i]["encTitleKey"].isNull()) 622 | { 623 | continue; 624 | } 625 | 626 | // Create an ASCII version of the name if one doesn't exist yet 627 | if (sourceData[i]["ascii_name"].isNull()) 628 | { 629 | // Normalize the name down to ASCII 630 | utf8proc_option_t options = (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPMARK | UTF8PROC_STRIPCC); 631 | utf8proc_uint8_t* szName; 632 | utf8proc_uint8_t *str = (utf8proc_uint8_t*)sourceData[i]["name"].asCString(); 633 | utf8proc_map(str, 0, &szName, options); 634 | 635 | sourceData[i]["ascii_name"] = (const char*)szName; 636 | 637 | free(szName); 638 | } 639 | 640 | if (match(searchString, sourceData[i], outScore)) 641 | { 642 | 643 | game_item item; 644 | item.score = outScore; 645 | item.index = i; 646 | 647 | switch(sourceDataType) { 648 | case JSON_TYPE_WINGS: 649 | item.titleid = sourceData[i]["titleid"].asString(); 650 | item.titlekey = sourceData[i]["enckey"].asString(); 651 | item.name = sourceData[i]["ascii_name"].asString(); 652 | item.region = sourceData[i]["region"].asString(); 653 | item.code = sourceData[i]["code"].asString(); 654 | break; 655 | case JSON_TYPE_ONLINE: 656 | item.titleid = sourceData[i]["titleID"].asString(); 657 | item.titlekey = sourceData[i]["encTitleKey"].asString(); 658 | item.name = sourceData[i]["ascii_name"].asString(); 659 | item.region = sourceData[i]["region"].asString(); 660 | item.code = sourceData[i]["serial"].asString(); 661 | break; 662 | } 663 | 664 | std::string typeCheck = item.titleid.substr(4,4); 665 | //if title id belongs to gameapp/dlc/update/dsiware, use it. if not, ignore. case sensitve of course 666 | if(typeCheck == "0000" || typeCheck == "008c" || typeCheck == "000e" || typeCheck == "8004"){ 667 | display_output.push_back(item); 668 | } 669 | } 670 | } 671 | 672 | unsigned int display_amount = display_output.size(); 673 | 674 | // We technically have 30 rows to work with, minus 2 for header/footer. But stick with 20 entries for now 675 | 676 | if (display_amount == 0) 677 | { 678 | printf("No matching titles found.\n"); 679 | wait_key_specific("\nPress A to return.\n", KEY_A); 680 | return; 681 | } 682 | 683 | // sort similar names by fuzzy score 684 | if(display_amount>1) { 685 | std::sort(display_output.begin(), display_output.end(), compareByScore); 686 | } 687 | 688 | std::string mode_text; 689 | switch (config.GetMode()) 690 | { 691 | case CConfig::Mode::DOWNLOAD_CIA: 692 | mode_text = "Create CIA"; 693 | break; 694 | case CConfig::Mode::INSTALL_CIA: 695 | mode_text = "Install CIA"; 696 | break; 697 | case CConfig::Mode::INSTALL_TICKET: 698 | mode_text = "Create Ticket"; 699 | break; 700 | } 701 | 702 | char footer[51]; 703 | char header[51]; 704 | sprintf(header, "Select a Title (found %i results)", display_amount); 705 | sprintf(footer, "Press A to %s. Press X to queue.", mode_text.c_str()); 706 | titles_multkey_draw(header, footer, 1, &display_output, &display_output, menu_search_keypress); 707 | 708 | } while(bRedoSearch == true); 709 | } 710 | 711 | void action_prompt_queue() 712 | { 713 | consoleClear(); 714 | 715 | std::string mode_text; 716 | switch (config.GetMode()) 717 | { 718 | case CConfig::Mode::DOWNLOAD_CIA: 719 | mode_text = "download"; 720 | break; 721 | case CConfig::Mode::INSTALL_CIA: 722 | mode_text = "install"; 723 | break; 724 | case CConfig::Mode::INSTALL_TICKET: 725 | mode_text = "create tickets for"; 726 | break; 727 | } 728 | 729 | printf("Queue contains %d items.\n", game_queue.size()); 730 | printf("Press A to %s queue.\n", mode_text.c_str()); 731 | printf("Press B to return to menu.\n"); 732 | printf("Press X to clear queue.\n"); 733 | 734 | while (aptMainLoop()) 735 | { 736 | u32 key = wait_key(); 737 | if (key & KEY_B) 738 | { 739 | break; 740 | } 741 | 742 | if (key & KEY_X) 743 | { 744 | game_queue.clear(); 745 | break; 746 | } 747 | 748 | if (key & KEY_A) 749 | { 750 | ProcessGameQueue(); 751 | break; 752 | } 753 | } 754 | 755 | } 756 | 757 | void action_manual_entry() 758 | { 759 | SwkbdState swkbd; 760 | swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, -1); 761 | swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, 0); 762 | char textBuf[255]; 763 | if (swkbdInputText(&swkbd, textBuf, sizeof(textBuf)) != SWKBD_BUTTON_CONFIRM) 764 | { 765 | return; 766 | } 767 | 768 | consoleClear(); 769 | 770 | // Keep looping so the user can retry if they enter a bad id/key 771 | while(true) 772 | { 773 | printf("Please enter a titleID:\n"); 774 | swkbdSetHintText(&swkbd, "Please enter a titleID"); 775 | if (swkbdInputText(&swkbd, textBuf, sizeof(textBuf)) != SWKBD_BUTTON_CONFIRM) 776 | { 777 | break; 778 | } 779 | std::string titleId(textBuf); 780 | std::string key; 781 | 782 | for (unsigned int i = 0; i < sourceData.size(); i++){ 783 | std::string tempId = sourceData[i]["titleid"].asString(); 784 | std::string tempKey = sourceData[i]["enckey"].asString(); 785 | 786 | if(tempId.compare(titleId) == 0 && tempKey.length() == 32) { 787 | printf("Found encTitleKey, proceeding automatically\n"); 788 | key = tempKey; 789 | break; 790 | } 791 | } 792 | if(key.length() != 32) { 793 | printf("Please enter the corresponding encTitleKey:\n"); 794 | swkbdSetHintText(&swkbd, "Please enter the corresponding encTitleKey"); 795 | if (swkbdInputText(&swkbd, textBuf, sizeof(textBuf)) != SWKBD_BUTTON_CONFIRM) 796 | { 797 | break; 798 | } 799 | key.assign(textBuf, sizeof(textBuf)); 800 | } 801 | if (titleId.length() == 16 && key.length() == 32) 802 | { 803 | DownloadTitle(titleId, key, "", ""); 804 | wait_key_specific("\nPress A to continue.\n", KEY_A); 805 | break; 806 | } 807 | else 808 | { 809 | printf("There was an error in you input:\n"); 810 | if(titleId.length() != 16) { 811 | printf("titleIDs are 16 chars long, not %i\n", titleId.length()); 812 | } 813 | if(key.length() != 32) { 814 | printf("encTitleKeys are 32 chars long, not %i\n", key.length()); 815 | } 816 | } 817 | } 818 | } 819 | 820 | void action_input_txt() 821 | { 822 | consoleClear(); 823 | 824 | std::ifstream input; 825 | std::string titleId; 826 | std::string key; 827 | 828 | input.open("/CIAngel/input.txt", std::ofstream::in); 829 | GetLine(input, titleId); 830 | GetLine(input, key); 831 | DownloadTitle(titleId, key, "", ""); 832 | 833 | wait_key_specific("\nPress A to continue.\n", KEY_A); 834 | } 835 | 836 | void action_toggle_install() 837 | { 838 | consoleClear(); 839 | CConfig::Mode nextMode = CConfig::Mode::INSTALL_CIA; 840 | 841 | switch (config.GetMode()) 842 | { 843 | case CConfig::Mode::DOWNLOAD_CIA: 844 | nextMode = CConfig::Mode::INSTALL_CIA; 845 | break; 846 | case CConfig::Mode::INSTALL_CIA: 847 | nextMode = CConfig::Mode::INSTALL_TICKET; 848 | break; 849 | case CConfig::Mode::INSTALL_TICKET: 850 | nextMode = CConfig::Mode::DOWNLOAD_CIA; 851 | break; 852 | } 853 | 854 | if (nextMode == CConfig::Mode::INSTALL_TICKET || nextMode == CConfig::Mode::INSTALL_CIA) 855 | { 856 | if (!bSvcHaxAvailable) 857 | { 858 | nextMode = CConfig::Mode::DOWNLOAD_CIA; 859 | printf(CONSOLE_RED "Kernel access not available.\nCan't enable Install modes.\nYou can only make a CIA.\n" CONSOLE_RESET); 860 | wait_key_specific("\nPress A to continue.", KEY_A); 861 | } 862 | } 863 | 864 | config.SetMode(nextMode); 865 | } 866 | 867 | void action_toggle_region() 868 | { 869 | consoleClear(); 870 | std::string regionFilter = config.GetRegionFilter(); 871 | if(regionFilter == "off") { 872 | regionFilter = "ALL"; 873 | } else if (regionFilter == "ALL") { 874 | regionFilter = "EUR"; 875 | } else if (regionFilter == "EUR") { 876 | regionFilter = "USA"; 877 | } else if (regionFilter == "USA") { 878 | regionFilter = "JPN"; 879 | } else if (regionFilter == "JPN") { 880 | regionFilter = "off"; 881 | } 882 | config.SetRegionFilter(regionFilter); 883 | } 884 | 885 | void action_about() 886 | { 887 | consoleClear(); 888 | 889 | printf(CONSOLE_RED "CIAngel\n\n" CONSOLE_RESET); 890 | printf("Download, create, and install CIAs directly\n"); 891 | printf("from Nintendo's CDN servers. Grabbing the\n"); 892 | printf("latest games has never been so easy.\n\n"); 893 | 894 | printf("Contributors: Cearp, Drakia, superbudvar,\n"); 895 | printf(" mysamdog, cerea1killer,\n"); 896 | printf(" DanTheMan827\n"); 897 | 898 | printf("\n\nCommit: " REVISION_STRING "\n\n"); 899 | 900 | printf("\nPress any button to continue."); 901 | wait_key(); 902 | } 903 | 904 | void action_exit() 905 | { 906 | bExit = true; 907 | } 908 | 909 | void action_download_json() 910 | { 911 | consoleClear(); 912 | 913 | download_JSON(); 914 | load_JSON_data(); 915 | } 916 | 917 | // Main menu keypress callback 918 | bool menu_main_keypress(int selected, u32 key, void*) 919 | { 920 | // If key is 0, it means aptMainLoop() returned false, so we're quitting 921 | if (!key) { 922 | return true; 923 | } 924 | 925 | // A button triggers standard actions 926 | if (key & KEY_A) 927 | { 928 | switch (selected) 929 | { 930 | case 0: 931 | action_search(search_by_name); 932 | break; 933 | case 1: 934 | action_search(search_by_serial); 935 | break; 936 | case 2: 937 | action_prompt_queue(); 938 | break; 939 | case 3: 940 | action_manual_entry(); 941 | break; 942 | case 4: 943 | action_input_txt(); 944 | break; 945 | case 5: 946 | action_download_json(); 947 | break; 948 | case 6: 949 | action_about(); 950 | break; 951 | case 7: 952 | action_exit(); 953 | break; 954 | } 955 | return true; 956 | } 957 | // L button triggers mode toggle 958 | else if (key & KEY_L) 959 | { 960 | action_toggle_install(); 961 | return true; 962 | } 963 | // R button triggers region toggle 964 | else if (key & KEY_R) 965 | { 966 | action_toggle_region(); 967 | return true; 968 | } 969 | 970 | return false; 971 | } 972 | 973 | // Draw the main menu 974 | void menu_main() 975 | { 976 | const char *options[] = { 977 | "Search for a title by name", 978 | "Search for a title by serial", 979 | "Process download queue", 980 | "Enter a title key/ID pair", 981 | "Fetch title key/ID from input.txt", 982 | "Download wings.json", 983 | "About CIAngel", 984 | "Exit", 985 | }; 986 | char footer[50]; 987 | 988 | while (!bExit && aptMainLoop()) 989 | { 990 | std::string mode_text; 991 | switch (config.GetMode()) 992 | { 993 | case CConfig::Mode::DOWNLOAD_CIA: 994 | mode_text = "Create CIA"; 995 | break; 996 | case CConfig::Mode::INSTALL_CIA: 997 | mode_text = "Install CIA"; 998 | break; 999 | case CConfig::Mode::INSTALL_TICKET: 1000 | mode_text = "Create Ticket"; 1001 | break; 1002 | } 1003 | 1004 | // We have to update the footer every draw, incase the user switches install mode or region 1005 | sprintf(footer, "Mode (L):%s Region (R):%s Queue: %d", mode_text.c_str(), config.GetRegionFilter().c_str(), game_queue.size()); 1006 | 1007 | menu_multkey_draw("CIAngel by cearp and Drakia", footer, 0, sizeof(options) / sizeof(char*), options, NULL, menu_main_keypress); 1008 | 1009 | clear_screen(GFX_BOTTOM); 1010 | } 1011 | } 1012 | 1013 | int main(int argc, const char* argv[]) 1014 | { 1015 | /* Sadly svchax crashes too much, so only allow install mode when running as a CIA 1016 | // Trigger svchax so we can install CIAs 1017 | if(argc > 0) { 1018 | svchax_init(true); 1019 | if(!__ctr_svchax || !__ctr_svchax_srv) { 1020 | bSvcHaxAvailable = false; 1021 | //printf("Failed to acquire kernel access. Install mode disabled.\n"); 1022 | } 1023 | } 1024 | */ 1025 | 1026 | // argc is 0 when running as a CIA, and 1 when running as a 3dsx 1027 | if (argc > 0) 1028 | { 1029 | bSvcHaxAvailable = false; 1030 | } 1031 | 1032 | u32 *soc_sharedmem, soc_sharedmem_size = 0x100000; 1033 | gfxInitDefault(); 1034 | consoleInit(GFX_TOP, NULL); 1035 | 1036 | httpcInit(0); 1037 | soc_sharedmem = (u32 *)memalign(0x1000, soc_sharedmem_size); 1038 | socInit(soc_sharedmem, soc_sharedmem_size); 1039 | sslcInit(0); 1040 | hidInit(); 1041 | acInit(); 1042 | cfguInit(); 1043 | 1044 | if (bSvcHaxAvailable) 1045 | { 1046 | amInit(); 1047 | AM_InitializeExternalTitleDatabase(false); 1048 | } 1049 | 1050 | init_menu(GFX_TOP); 1051 | 1052 | // Make sure all CIAngel directories exists on the SD card 1053 | mkpath("/CIAngel", 0777); 1054 | mkpath("/CIAngel/tmp/", 0777); 1055 | loadConfig(); 1056 | 1057 | // Set up the reading of json 1058 | check_JSON(); 1059 | load_JSON_data(); 1060 | 1061 | menu_main(); 1062 | 1063 | if (bSvcHaxAvailable) 1064 | { 1065 | amExit(); 1066 | } 1067 | 1068 | cfguExit(); 1069 | acExit(); 1070 | gfxExit(); 1071 | hidExit(); 1072 | httpcExit(); 1073 | socExit(); 1074 | sslcExit(); 1075 | } 1076 | --------------------------------------------------------------------------------