diff --git a/.vs/cdecrypt.vcxproj b/.vs/cdecrypt.vcxproj index 416cf80..f37a03f 100644 --- a/.vs/cdecrypt.vcxproj +++ b/.vs/cdecrypt.vcxproj @@ -114,7 +114,7 @@ Level4 Disabled true - _CRT_SECURE_NO_WARNINGS;AES_ROM_TABLES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;AES_ROM_TABLES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug 4200;4201 @@ -132,7 +132,7 @@ Level4 Disabled true - _CRT_SECURE_NO_WARNINGS;AES_ROM_TABLES;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;AES_ROM_TABLES;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug 4200;4201 @@ -151,7 +151,7 @@ MaxSpeed true true - _CRT_SECURE_NO_WARNINGS;AES_ROM_TABLES;WIN32;_CONSOLE;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;AES_ROM_TABLES;WIN32;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded 4200;4201 @@ -172,7 +172,7 @@ MaxSpeed true true - _CRT_SECURE_NO_WARNINGS;AES_ROM_TABLES;WIN32;_CONSOLE;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;AES_ROM_TABLES;WIN32;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded 4200;4201 diff --git a/cdecrypt.c b/cdecrypt.c index 242aac7..2a1321a 100644 --- a/cdecrypt.c +++ b/cdecrypt.c @@ -32,7 +32,11 @@ #define MAX_ENTRIES 90000 #define MAX_LEVELS 16 -#define FST_MAGIC ((uint32_t)'FST\0') +#define FST_MAGIC 0x46535400 // 'FST\0' +// We use part of the root cert name used by TMD/TIK to identify them +#define TMD_MAGIC 0x4350303030303030ULL // 'CP000000' +#define TIK_MAGIC 0x5853303030303030ULL // 'XS000000' +#define T_MAGIC_OFFSET 0x0150 #define HASH_BLOCK_SIZE 0xFC00 #define HASHES_SIZE 0x0400 @@ -51,7 +55,7 @@ uint64_t h0_fail = 0; enum ContentType { - CONTENT_REQUIRED = (1 << 0), // not sure + CONTENT_REQUIRED = (1 << 0), // Not sure CONTENT_SHARED = (1 << 15), CONTENT_OPTIONAL = (1 << 14), }; @@ -348,24 +352,78 @@ static bool extract_file(FILE* src, uint64_t part_data_offset, uint64_t file_off int main_utf8(int argc, char** argv) { int r = EXIT_FAILURE; - char str[1024]; + char str[MAX_PATH], *tmd_path = NULL, *tik_path = NULL; FILE* src = NULL; TitleMetaData* tmd = NULL; uint8_t *tik = NULL, *cnt = NULL; - if (argc != 3) { - printf("%s %s (c) 2013-2015 crediar, (c) 2020 VitaSmith\n\n" - "Usage: %s \n\n" - "Decrypt Wii U NUS content files.\n\n", + if (argc < 2) { + printf("%s %s - Wii U NUS content file decrypter\n" + "Copyright (c) 2013-2015 crediar, Copyright (c) 2020 VitaSmith\n" + "Visit https://github.com/VitaSmith/cdecrypt for official source and downloads.\n\n" + "Usage: %s \n\n" + "This program is free software; you can redistribute it and/or modify it under\n" + "the terms of the GNU General Public License as published by the Free Software\n" + "Foundation; either version 3 of the License or any later version.\n", appname(argv[0]), APP_VERSION_STR, appname(argv[0])); return EXIT_SUCCESS; } - uint32_t tmd_len = read_file(argv[1], (uint8_t**)&tmd); + if (!is_directory(argv[1])) { + uint8_t* buf = NULL; + uint32_t size = read_file_max(argv[1], &buf, T_MAGIC_OFFSET + sizeof(uint64_t)); + if (size == 0) + goto out; + if (size >= T_MAGIC_OFFSET + sizeof(uint64_t)) { + uint64_t magic = getbe64(&buf[T_MAGIC_OFFSET]); + free(buf); + if (magic == TMD_MAGIC) { + tmd_path = strdup(argv[1]); + if (argc < 3) { + tik_path = strdup(argv[1]); + tik_path[strlen(tik_path) - 2] = 'i'; + tik_path[strlen(tik_path) - 1] = 'k'; + } + else { + tik_path = strdup(argv[2]); + } + } else if (magic == TIK_MAGIC) { + tik_path = strdup(argv[1]); + if (argc < 3) { + tmd_path = strdup(argv[1]); + tmd_path[strlen(tik_path) - 2] = 'm'; + tmd_path[strlen(tik_path) - 1] = 'd'; + } + else { + tmd_path = strdup(argv[2]); + } + } + } + + // File too small or without expected magic + if ((tmd_path == NULL) || (tik_path == NULL)) { + argv[1][get_trailing_slash(argv[1])] = 0; + if (argv[1][0] == 0) { + argv[1][0] = '.'; + argv[1][1] = 0; + } + } + } + + // If the condition below is true, argv[1] is a directory + if ((tmd_path == NULL) || (tik_path == NULL)) { + size_t size = strlen(argv[1]); + tmd_path = calloc(size + 10, 1); + tik_path = calloc(size + 10, 1); + sprintf(tmd_path, "%s%ctitle.tmd", argv[1], PATH_SEP); + sprintf(tik_path, "%s%ctitle.tik", argv[1], PATH_SEP); + } + + uint32_t tmd_len = read_file(tmd_path, (uint8_t**)&tmd); if (tmd_len == 0) goto out; - uint32_t tik_len = read_file(argv[2], &tik); + uint32_t tik_len = read_file(tik_path, &tik); if (tik_len == 0) goto out; @@ -512,6 +570,8 @@ int main_utf8(int argc, char** argv) free(tmd); free(tik); free(cnt); + free(tmd_path); + free(tik_path); if (src != NULL) fclose(src); return r; diff --git a/util.c b/util.c index 1c3c46f..1e74cd4 100644 --- a/util.c +++ b/util.c @@ -94,7 +94,7 @@ size_t get_trailing_slash(const char* path) return (i == 0) ? 0: i + 1; } -uint32_t read_file(const char* path, uint8_t** buf) +uint32_t read_file_max(const char* path, uint8_t** buf, uint32_t max_size) { FILE* file = fopen_utf8(path, "rb"); if (file == NULL) { @@ -105,6 +105,8 @@ uint32_t read_file(const char* path, uint8_t** buf) fseek(file, 0L, SEEK_END); uint32_t size = (uint32_t)ftell(file); fseek(file, 0L, SEEK_SET); + if (max_size != 0) + size = min(size, max_size); *buf = calloc(size, 1); if (*buf == NULL) { @@ -124,6 +126,20 @@ uint32_t read_file(const char* path, uint8_t** buf) return size; } +uint64_t get_file_size(const char* path) +{ + FILE* file = fopen_utf8(path, "rb"); + if (file == NULL) { + fprintf(stderr, "ERROR: Can't open '%s'\n", path); + return 0; + } + + fseek(file, 0L, SEEK_END); + uint64_t size = ftell64(file); + fclose(file); + return size; +} + void create_backup(const char* path) { struct stat64 st; diff --git a/util.h b/util.h index 23b5a74..acb53b6 100644 --- a/util.h +++ b/util.h @@ -56,6 +56,10 @@ #define PATH_SEP '/' #endif +#ifndef MAX_PATH +#define MAX_PATH 256 +#endif + #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif @@ -206,6 +210,8 @@ size_t get_trailing_slash(const char* path); bool is_file(const char* path); bool is_directory(const char* path); -uint32_t read_file(const char* path, uint8_t** buf); +uint32_t read_file_max(const char* path, uint8_t** buf, uint32_t max_size); +#define read_file(path, buf) read_file_max(path, buf, 0) +uint64_t get_file_size(const char* path); void create_backup(const char* path); bool write_file(const uint8_t* buf, const uint32_t size, const char* path, const bool backup);