TitleKeys.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #include <cpp3ds/System/I18n.hpp>
  2. #include <cpp3ds/System/FileSystem.hpp>
  3. #include <dirent.h>
  4. #include "Download.hpp"
  5. #include "TitleKeys.hpp"
  6. namespace {
  7. std::map<cpp3ds::Uint64, cpp3ds::Uint32[4]> titleKeys;
  8. std::vector<cpp3ds::Uint64> titleIds;
  9. }
  10. namespace FreeShop {
  11. /* encTitleKeys.bin format
  12. 4 bytes Number of entries
  13. 12 bytes Reserved
  14. entry(32 bytes in size):
  15. 4 bytes Common key index(0-5)
  16. 4 bytes Reserved
  17. 8 bytes Title Id
  18. 16 bytes Encrypted title key
  19. */
  20. void TitleKeys::ensureTitleKeys()
  21. {
  22. static bool keysChecked = false;
  23. if (!keysChecked)
  24. {
  25. keysChecked = true;
  26. cpp3ds::FileInputStream file;
  27. std::string keyDirectory = cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/keys/");
  28. struct dirent *ent;
  29. DIR *dir = opendir(keyDirectory.c_str());
  30. if (!dir)
  31. return;
  32. while (ent = readdir(dir))
  33. {
  34. if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
  35. continue;
  36. std::string filename = keyDirectory + ent->d_name;
  37. file.open(filename);
  38. if (isValidFile(file))
  39. {
  40. size_t count = file.getSize() / 32;
  41. cpp3ds::Uint64 titleId;
  42. cpp3ds::Uint32 titleKey[4];
  43. for (int i = 0; i < count; ++i)
  44. {
  45. file.seek(24 + i * 32);
  46. file.read(&titleId, 8);
  47. file.read(titleKey, 16);
  48. titleId = __builtin_bswap64(titleId);
  49. int type = titleId >> 32;
  50. switch (type) {
  51. case Game:
  52. case Update:
  53. case Demo:
  54. case DLC:
  55. case DSiWare:
  56. if (titleKeys.find(titleId) == titleKeys.end())
  57. {
  58. titleIds.push_back(titleId);
  59. for (int j = 0; j < 4; ++j)
  60. titleKeys[titleId][j] = titleKey[j];
  61. }
  62. default:
  63. break;
  64. }
  65. }
  66. }
  67. }
  68. closedir(dir);
  69. }
  70. }
  71. std::vector<cpp3ds::Uint64> TitleKeys::getRelated(cpp3ds::Uint64 titleId, TitleType type)
  72. {
  73. ensureTitleKeys();
  74. std::vector<cpp3ds::Uint64> related;
  75. cpp3ds::Uint32 titleLower = (titleId & 0xFFFFFFFF) >> 8;
  76. for (const auto &key : titleIds)
  77. if ((key >> 32 == type) && (titleLower == (key & 0xFFFFFFFF) >> 8))
  78. related.push_back(key);
  79. return related;
  80. }
  81. std::vector<cpp3ds::Uint64> &TitleKeys::getIds()
  82. {
  83. ensureTitleKeys();
  84. return titleIds;
  85. }
  86. cpp3ds::Uint32 *TitleKeys::get(cpp3ds::Uint64 titleId)
  87. {
  88. ensureTitleKeys();
  89. if (titleKeys.find(titleId) == titleKeys.end())
  90. return nullptr;
  91. return titleKeys[titleId];
  92. }
  93. bool TitleKeys::isValidFile(cpp3ds::FileInputStream &file)
  94. {
  95. std::vector<char> data;
  96. cpp3ds::Int64 size = file.getSize() - 16; // Size minus header
  97. int count;
  98. file.seek(0);
  99. file.read(&count, 4);
  100. if (size % 32 != 0 || size / 32 != count)
  101. return false;
  102. count = (count > 3) ? 3 : count; // Check a max of 3 items
  103. data.resize(count * 32);
  104. file.seek(16);
  105. file.read(&data[0], data.size());
  106. return isValidData(&data[0], data.size());
  107. }
  108. bool TitleKeys::isValidFile(const std::string &filename)
  109. {
  110. cpp3ds::FileInputStream file;
  111. if (!file.open(filename))
  112. return false;
  113. return isValidFile(file);
  114. }
  115. bool TitleKeys::isValidData(const void *data, size_t size)
  116. {
  117. auto ptr = reinterpret_cast<const cpp3ds::Uint8*>(data);
  118. size_t count = size / 32;
  119. for (int i = 0; i < count; ++i)
  120. {
  121. cpp3ds::Uint64 titleId = *reinterpret_cast<const cpp3ds::Uint64*>(ptr + 8 + i * 32);
  122. if (__builtin_bswap64(titleId) >> 48 != 4)
  123. return false;
  124. }
  125. return true;
  126. }
  127. bool TitleKeys::isValidUrl(const std::string &url, cpp3ds::String *errorOut)
  128. {
  129. std::vector<char> keyBuf;
  130. bool passed = false;
  131. errorOut->clear();
  132. FreeShop::Download download(url);
  133. download.setDataCallback([&](const void* data, size_t len, size_t processed, const cpp3ds::Http::Response& response)
  134. {
  135. auto httpStatus = response.getStatus();
  136. if (httpStatus == cpp3ds::Http::Response::MovedPermanently || httpStatus == cpp3ds::Http::Response::MovedTemporarily)
  137. return true;
  138. // If HTTP request is good, verify that contents are a titlekey dump
  139. if (passed = httpStatus == cpp3ds::Http::Response::Ok || httpStatus == cpp3ds::Http::Response::PartialContent)
  140. {
  141. const char *buf = reinterpret_cast<const char*>(data);
  142. keyBuf.insert(keyBuf.end(), buf + 16, buf + len);
  143. return false;
  144. }
  145. *errorOut = _("Failed.\n\nHTTP Status: %d", static_cast<int>(httpStatus));
  146. return false;
  147. });
  148. download.run();
  149. if (passed)
  150. {
  151. passed = isValidData(&keyBuf[0], keyBuf.size());
  152. if (!passed)
  153. *errorOut = _("Invalid title key format.");
  154. }
  155. if (download.getLastResponse().getStatus() == cpp3ds::Http::Response::TimedOut)
  156. *errorOut = _("Request timed out.\nTry again.");
  157. if (!passed && errorOut->isEmpty())
  158. *errorOut = _("Invalid URL.");
  159. return passed;
  160. }
  161. } // namespace FreeShop