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);