cfg.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <cryptopp/osrng.h>
  7. #include <cryptopp/sha.h>
  8. #include "common/file_util.h"
  9. #include "common/logging/log.h"
  10. #include "common/string_util.h"
  11. #include "common/swap.h"
  12. #include "core/file_sys/archive_systemsavedata.h"
  13. #include "core/file_sys/errors.h"
  14. #include "core/file_sys/file_backend.h"
  15. #include "core/hle/ipc.h"
  16. #include "core/hle/ipc_helpers.h"
  17. #include "core/hle/result.h"
  18. #include "core/hle/service/cfg/cfg.h"
  19. #include "core/hle/service/cfg/cfg_i.h"
  20. #include "core/hle/service/cfg/cfg_nor.h"
  21. #include "core/hle/service/cfg/cfg_s.h"
  22. #include "core/hle/service/cfg/cfg_u.h"
  23. #include "core/hle/service/fs/archive.h"
  24. #include "core/hle/service/service.h"
  25. #include "core/memory.h"
  26. #include "core/settings.h"
  27. namespace Service {
  28. namespace CFG {
  29. /// The maximum number of block entries that can exist in the config file
  30. static const u32 CONFIG_FILE_MAX_BLOCK_ENTRIES = 1479;
  31. namespace {
  32. /**
  33. * The header of the config savedata file,
  34. * contains information about the blocks in the file
  35. */
  36. struct SaveFileConfig {
  37. u16 total_entries; ///< The total number of set entries in the config file
  38. u16 data_entries_offset; ///< The offset where the data for the blocks start, this is hardcoded
  39. /// to 0x455C as per hardware
  40. SaveConfigBlockEntry block_entries[CONFIG_FILE_MAX_BLOCK_ENTRIES]; ///< The block headers, the
  41. /// maximum possible value is
  42. /// 1479 as per hardware
  43. u32 unknown; ///< This field is unknown, possibly padding, 0 has been observed in hardware
  44. };
  45. static_assert(sizeof(SaveFileConfig) == 0x455C,
  46. "SaveFileConfig header must be exactly 0x455C bytes");
  47. enum ConfigBlockID {
  48. StereoCameraSettingsBlockID = 0x00050005,
  49. SoundOutputModeBlockID = 0x00070001,
  50. ConsoleUniqueID1BlockID = 0x00090000,
  51. ConsoleUniqueID2BlockID = 0x00090001,
  52. ConsoleUniqueID3BlockID = 0x00090002,
  53. UsernameBlockID = 0x000A0000,
  54. BirthdayBlockID = 0x000A0001,
  55. LanguageBlockID = 0x000A0002,
  56. CountryInfoBlockID = 0x000B0000,
  57. CountryNameBlockID = 0x000B0001,
  58. StateNameBlockID = 0x000B0002,
  59. EULAVersionBlockID = 0x000D0000,
  60. ConsoleModelBlockID = 0x000F0004,
  61. };
  62. struct UsernameBlock {
  63. char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
  64. u32 zero;
  65. u32 ng_word;
  66. };
  67. static_assert(sizeof(UsernameBlock) == 0x1C, "UsernameBlock must be exactly 0x1C bytes");
  68. struct BirthdayBlock {
  69. u8 month; ///< The month of the birthday
  70. u8 day; ///< The day of the birthday
  71. };
  72. static_assert(sizeof(BirthdayBlock) == 2, "BirthdayBlock must be exactly 2 bytes");
  73. struct ConsoleModelInfo {
  74. u8 model; ///< The console model (3DS, 2DS, etc)
  75. u8 unknown[3]; ///< Unknown data
  76. };
  77. static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes");
  78. struct ConsoleCountryInfo {
  79. u8 unknown[3]; ///< Unknown data
  80. u8 country_code; ///< The country code of the console
  81. };
  82. static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
  83. }
  84. static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}};
  85. static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
  86. static const UsernameBlock CONSOLE_USERNAME_BLOCK = {u"CITRA", 0, 0};
  87. static const BirthdayBlock PROFILE_BIRTHDAY = {3, 25}; // March 25th, 2014
  88. static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
  89. static const u8 UNITED_STATES_COUNTRY_ID = 49;
  90. /// TODO(Subv): Find what the other bytes are
  91. static const ConsoleCountryInfo COUNTRY_INFO = {{0, 0, 0}, UNITED_STATES_COUNTRY_ID};
  92. /**
  93. * TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games,
  94. * for example Nintendo Zone
  95. * Thanks Normmatt for providing this information
  96. */
  97. static const std::array<float, 8> STEREO_CAMERA_SETTINGS = {
  98. 62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f,
  99. 10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f,
  100. };
  101. static_assert(sizeof(STEREO_CAMERA_SETTINGS) == 0x20,
  102. "STEREO_CAMERA_SETTINGS must be exactly 0x20 bytes");
  103. static const u32 CONFIG_SAVEFILE_SIZE = 0x8000;
  104. static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
  105. static Service::FS::ArchiveHandle cfg_system_save_data_archive;
  106. static const std::vector<u8> cfg_system_savedata_id = {
  107. 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
  108. };
  109. static u32 preferred_region_code = 0;
  110. void GetCountryCodeString(Service::Interface* self) {
  111. u32* cmd_buff = Kernel::GetCommandBuffer();
  112. u32 country_code_id = cmd_buff[1];
  113. if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
  114. LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
  115. cmd_buff[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config,
  116. ErrorSummary::WrongArgument, ErrorLevel::Permanent)
  117. .raw;
  118. return;
  119. }
  120. cmd_buff[1] = 0;
  121. cmd_buff[2] = country_codes[country_code_id];
  122. }
  123. void GetCountryCodeID(Service::Interface* self) {
  124. u32* cmd_buff = Kernel::GetCommandBuffer();
  125. u16 country_code = cmd_buff[1];
  126. u16 country_code_id = 0;
  127. // The following algorithm will fail if the first country code isn't 0.
  128. DEBUG_ASSERT(country_codes[0] == 0);
  129. for (u16 id = 0; id < country_codes.size(); ++id) {
  130. if (country_codes[id] == country_code) {
  131. country_code_id = id;
  132. break;
  133. }
  134. }
  135. if (0 == country_code_id) {
  136. LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff,
  137. country_code >> 8);
  138. cmd_buff[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config,
  139. ErrorSummary::WrongArgument, ErrorLevel::Permanent)
  140. .raw;
  141. cmd_buff[2] = 0xFFFF;
  142. return;
  143. }
  144. cmd_buff[1] = 0;
  145. cmd_buff[2] = country_code_id;
  146. }
  147. u32 GetRegionValue() {
  148. if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
  149. return preferred_region_code;
  150. return Settings::values.region_value;
  151. }
  152. void SecureInfoGetRegion(Service::Interface* self) {
  153. u32* cmd_buff = Kernel::GetCommandBuffer();
  154. cmd_buff[1] = RESULT_SUCCESS.raw;
  155. cmd_buff[2] = GetRegionValue();
  156. }
  157. void GenHashConsoleUnique(Service::Interface* self) {
  158. IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0);
  159. const u32 app_id_salt = rp.Pop<u32>() & 0x000FFFFF;
  160. IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
  161. std::array<u8, 12> buffer;
  162. const ResultCode result = GetConfigInfoBlock(ConsoleUniqueID2BlockID, 8, 2, buffer.data());
  163. rb.Push(result);
  164. if (result.IsSuccess()) {
  165. std::memcpy(&buffer[8], &app_id_salt, sizeof(u32));
  166. std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash;
  167. CryptoPP::SHA256().CalculateDigest(hash.data(), buffer.data(), sizeof(buffer));
  168. u32 low, high;
  169. memcpy(&low, &hash[hash.size() - 8], sizeof(u32));
  170. memcpy(&high, &hash[hash.size() - 4], sizeof(u32));
  171. rb.Push(low);
  172. rb.Push(high);
  173. } else {
  174. rb.Push<u32>(0);
  175. rb.Push<u32>(0);
  176. }
  177. LOG_DEBUG(Service_CFG, "called app_id_salt=0x%X", app_id_salt);
  178. }
  179. void GetRegionCanadaUSA(Service::Interface* self) {
  180. u32* cmd_buff = Kernel::GetCommandBuffer();
  181. cmd_buff[1] = RESULT_SUCCESS.raw;
  182. u8 canada_or_usa = 1;
  183. if (canada_or_usa == GetRegionValue()) {
  184. cmd_buff[2] = 1;
  185. } else {
  186. cmd_buff[2] = 0;
  187. }
  188. }
  189. void GetSystemModel(Service::Interface* self) {
  190. u32* cmd_buff = Kernel::GetCommandBuffer();
  191. u32 data;
  192. // TODO(Subv): Find out the correct error codes
  193. cmd_buff[1] =
  194. Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8, reinterpret_cast<u8*>(&data)).raw;
  195. cmd_buff[2] = data & 0xFF;
  196. }
  197. void GetModelNintendo2DS(Service::Interface* self) {
  198. u32* cmd_buff = Kernel::GetCommandBuffer();
  199. u32 data;
  200. // TODO(Subv): Find out the correct error codes
  201. cmd_buff[1] =
  202. Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8, reinterpret_cast<u8*>(&data)).raw;
  203. u8 model = data & 0xFF;
  204. if (model == Service::CFG::NINTENDO_2DS)
  205. cmd_buff[2] = 0;
  206. else
  207. cmd_buff[2] = 1;
  208. }
  209. void GetConfigInfoBlk2(Service::Interface* self) {
  210. u32* cmd_buff = Kernel::GetCommandBuffer();
  211. u32 size = cmd_buff[1];
  212. u32 block_id = cmd_buff[2];
  213. VAddr data_pointer = cmd_buff[4];
  214. if (!Memory::IsValidVirtualAddress(data_pointer)) {
  215. cmd_buff[1] = -1; // TODO(Subv): Find the right error code
  216. return;
  217. }
  218. std::vector<u8> data(size);
  219. cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data.data()).raw;
  220. Memory::WriteBlock(data_pointer, data.data(), data.size());
  221. }
  222. void GetConfigInfoBlk8(Service::Interface* self) {
  223. u32* cmd_buff = Kernel::GetCommandBuffer();
  224. u32 size = cmd_buff[1];
  225. u32 block_id = cmd_buff[2];
  226. VAddr data_pointer = cmd_buff[4];
  227. if (!Memory::IsValidVirtualAddress(data_pointer)) {
  228. cmd_buff[1] = -1; // TODO(Subv): Find the right error code
  229. return;
  230. }
  231. std::vector<u8> data(size);
  232. cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data.data()).raw;
  233. Memory::WriteBlock(data_pointer, data.data(), data.size());
  234. }
  235. void SetConfigInfoBlk4(Service::Interface* self) {
  236. u32* cmd_buff = Kernel::GetCommandBuffer();
  237. u32 block_id = cmd_buff[1];
  238. u32 size = cmd_buff[2];
  239. VAddr data_pointer = cmd_buff[4];
  240. if (!Memory::IsValidVirtualAddress(data_pointer)) {
  241. cmd_buff[1] = -1; // TODO(Subv): Find the right error code
  242. return;
  243. }
  244. std::vector<u8> data(size);
  245. Memory::ReadBlock(data_pointer, data.data(), data.size());
  246. cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw;
  247. }
  248. void UpdateConfigNANDSavegame(Service::Interface* self) {
  249. u32* cmd_buff = Kernel::GetCommandBuffer();
  250. cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
  251. }
  252. void FormatConfig(Service::Interface* self) {
  253. u32* cmd_buff = Kernel::GetCommandBuffer();
  254. cmd_buff[1] = Service::CFG::FormatConfig().raw;
  255. }
  256. static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) {
  257. // Read the header
  258. SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
  259. auto itr =
  260. std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
  261. [&](const SaveConfigBlockEntry& entry) { return entry.block_id == block_id; });
  262. if (itr == std::end(config->block_entries)) {
  263. LOG_ERROR(Service_CFG, "Config block 0x%X with flags %u and size %u was not found",
  264. block_id, flag, size);
  265. return ResultCode(ErrorDescription::NotFound, ErrorModule::Config,
  266. ErrorSummary::WrongArgument, ErrorLevel::Permanent);
  267. }
  268. if ((itr->flags & flag) == 0) {
  269. LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id,
  270. size);
  271. return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config,
  272. ErrorSummary::WrongArgument, ErrorLevel::Permanent);
  273. }
  274. if (itr->size != size) {
  275. LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size,
  276. block_id, flag);
  277. return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config,
  278. ErrorSummary::WrongArgument, ErrorLevel::Permanent);
  279. }
  280. void* pointer;
  281. // The data is located in the block header itself if the size is less than 4 bytes
  282. if (itr->size <= 4)
  283. pointer = &itr->offset_or_data;
  284. else
  285. pointer = &cfg_config_file_buffer[itr->offset_or_data];
  286. return MakeResult<void*>(pointer);
  287. }
  288. ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
  289. void* pointer;
  290. CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
  291. memcpy(output, pointer, size);
  292. return RESULT_SUCCESS;
  293. }
  294. ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) {
  295. void* pointer;
  296. CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
  297. memcpy(pointer, input, size);
  298. return RESULT_SUCCESS;
  299. }
  300. ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* data) {
  301. SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
  302. if (config->total_entries >= CONFIG_FILE_MAX_BLOCK_ENTRIES)
  303. return ResultCode(-1); // TODO(Subv): Find the right error code
  304. // Insert the block header with offset 0 for now
  305. config->block_entries[config->total_entries] = {block_id, 0, size, flags};
  306. if (size > 4) {
  307. u32 offset = config->data_entries_offset;
  308. // Perform a search to locate the next offset for the new data
  309. // use the offset and size of the previous block to determine the new position
  310. for (int i = config->total_entries - 1; i >= 0; --i) {
  311. // Ignore the blocks that don't have a separate data offset
  312. if (config->block_entries[i].size > 4) {
  313. offset = config->block_entries[i].offset_or_data + config->block_entries[i].size;
  314. break;
  315. }
  316. }
  317. config->block_entries[config->total_entries].offset_or_data = offset;
  318. // Write the data at the new offset
  319. memcpy(&cfg_config_file_buffer[offset], data, size);
  320. } else {
  321. // The offset_or_data field in the header contains the data itself if it's 4 bytes or less
  322. memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size);
  323. }
  324. ++config->total_entries;
  325. return RESULT_SUCCESS;
  326. }
  327. ResultCode DeleteConfigNANDSaveFile() {
  328. FileSys::Path path("/config");
  329. return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path);
  330. }
  331. ResultCode UpdateConfigNANDSavegame() {
  332. FileSys::Mode mode = {};
  333. mode.write_flag.Assign(1);
  334. mode.create_flag.Assign(1);
  335. FileSys::Path path("/config");
  336. auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);
  337. ASSERT_MSG(config_result.Succeeded(), "could not open file");
  338. auto config = std::move(config_result).Unwrap();
  339. config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());
  340. return RESULT_SUCCESS;
  341. }
  342. ResultCode FormatConfig() {
  343. ResultCode res = DeleteConfigNANDSaveFile();
  344. // The delete command fails if the file doesn't exist, so we have to check that too
  345. if (!res.IsSuccess() && res != FileSys::ERROR_FILE_NOT_FOUND) {
  346. return res;
  347. }
  348. // Delete the old data
  349. cfg_config_file_buffer.fill(0);
  350. // Create the header
  351. SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
  352. // This value is hardcoded, taken from 3dbrew, verified by hardware, it's always the same value
  353. config->data_entries_offset = 0x455C;
  354. // Insert the default blocks
  355. u8 zero_buffer[0xC0] = {};
  356. // 0x00030001 - Unknown
  357. res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
  358. if (!res.IsSuccess())
  359. return res;
  360. res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE,
  361. STEREO_CAMERA_SETTINGS.data());
  362. if (!res.IsSuccess())
  363. return res;
  364. res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE,
  365. &SOUND_OUTPUT_MODE);
  366. if (!res.IsSuccess())
  367. return res;
  368. u32 random_number;
  369. u64 console_id;
  370. GenerateConsoleUniqueId(random_number, console_id);
  371. u64_le console_id_le = console_id;
  372. res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(console_id_le), 0xE, &console_id_le);
  373. if (!res.IsSuccess())
  374. return res;
  375. res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(console_id_le), 0xE, &console_id_le);
  376. if (!res.IsSuccess())
  377. return res;
  378. u32_le random_number_le = random_number;
  379. res = CreateConfigInfoBlk(ConsoleUniqueID3BlockID, sizeof(random_number_le), 0xE,
  380. &random_number_le);
  381. if (!res.IsSuccess())
  382. return res;
  383. res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE,
  384. &CONSOLE_USERNAME_BLOCK);
  385. if (!res.IsSuccess())
  386. return res;
  387. res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
  388. if (!res.IsSuccess())
  389. return res;
  390. res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
  391. if (!res.IsSuccess())
  392. return res;
  393. res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
  394. if (!res.IsSuccess())
  395. return res;
  396. u16_le country_name_buffer[16][0x40] = {};
  397. std::u16string region_name = Common::UTF8ToUTF16("Gensokyo");
  398. for (size_t i = 0; i < 16; ++i) {
  399. std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
  400. }
  401. // 0x000B0001 - Localized names for the profile Country
  402. res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE,
  403. country_name_buffer);
  404. if (!res.IsSuccess())
  405. return res;
  406. // 0x000B0002 - Localized names for the profile State/Province
  407. res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE,
  408. country_name_buffer);
  409. if (!res.IsSuccess())
  410. return res;
  411. // 0x000B0003 - Unknown, related to country/address (zip code?)
  412. res = CreateConfigInfoBlk(0x000B0003, 0x4, 0xE, zero_buffer);
  413. if (!res.IsSuccess())
  414. return res;
  415. // 0x000C0000 - Unknown
  416. res = CreateConfigInfoBlk(0x000C0000, 0xC0, 0xE, zero_buffer);
  417. if (!res.IsSuccess())
  418. return res;
  419. // 0x000C0001 - Unknown
  420. res = CreateConfigInfoBlk(0x000C0001, 0x14, 0xE, zero_buffer);
  421. if (!res.IsSuccess())
  422. return res;
  423. // 0x000D0000 - Accepted EULA version
  424. res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer);
  425. if (!res.IsSuccess())
  426. return res;
  427. res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
  428. if (!res.IsSuccess())
  429. return res;
  430. // 0x00170000 - Unknown
  431. res = CreateConfigInfoBlk(0x00170000, 0x4, 0xE, zero_buffer);
  432. if (!res.IsSuccess())
  433. return res;
  434. // Save the buffer to the file
  435. res = UpdateConfigNANDSavegame();
  436. if (!res.IsSuccess())
  437. return res;
  438. return RESULT_SUCCESS;
  439. }
  440. ResultCode LoadConfigNANDSaveFile() {
  441. // Open the SystemSaveData archive 0x00010017
  442. FileSys::Path archive_path(cfg_system_savedata_id);
  443. auto archive_result =
  444. Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
  445. // If the archive didn't exist, create the files inside
  446. if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) {
  447. // Format the archive to create the directories
  448. Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData,
  449. FileSys::ArchiveFormatInfo(), archive_path);
  450. // Open it again to get a valid archive now that the folder exists
  451. archive_result =
  452. Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
  453. }
  454. ASSERT_MSG(archive_result.Succeeded(), "Could not open the CFG SystemSaveData archive!");
  455. cfg_system_save_data_archive = *archive_result;
  456. FileSys::Path config_path("/config");
  457. FileSys::Mode open_mode = {};
  458. open_mode.read_flag.Assign(1);
  459. auto config_result = Service::FS::OpenFileFromArchive(*archive_result, config_path, open_mode);
  460. // Read the file if it already exists
  461. if (config_result.Succeeded()) {
  462. auto config = std::move(config_result).Unwrap();
  463. config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
  464. return RESULT_SUCCESS;
  465. }
  466. return FormatConfig();
  467. }
  468. void Init() {
  469. AddService(new CFG_I);
  470. AddService(new CFG_NOR);
  471. AddService(new CFG_S);
  472. AddService(new CFG_U);
  473. LoadConfigNANDSaveFile();
  474. preferred_region_code = 0;
  475. }
  476. void Shutdown() {}
  477. /// Checks if the language is available in the chosen region, and returns a proper one
  478. static SystemLanguage AdjustLanguageInfoBlock(u32 region, SystemLanguage language) {
  479. static const std::array<std::vector<SystemLanguage>, 7> region_languages{{
  480. // JPN
  481. {LANGUAGE_JP},
  482. // USA
  483. {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_ES, LANGUAGE_PT},
  484. // EUR
  485. {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
  486. LANGUAGE_RU},
  487. // AUS
  488. {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
  489. LANGUAGE_RU},
  490. // CHN
  491. {LANGUAGE_ZH},
  492. // KOR
  493. {LANGUAGE_KO},
  494. // TWN
  495. {LANGUAGE_TW},
  496. }};
  497. const auto& available = region_languages[region];
  498. if (std::find(available.begin(), available.end(), language) == available.end()) {
  499. return available[0];
  500. }
  501. return language;
  502. }
  503. void SetPreferredRegionCode(u32 region_code) {
  504. preferred_region_code = region_code;
  505. LOG_INFO(Service_CFG, "Preferred region code set to %u", preferred_region_code);
  506. if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) {
  507. const SystemLanguage current_language = GetSystemLanguage();
  508. const SystemLanguage adjusted_language =
  509. AdjustLanguageInfoBlock(region_code, current_language);
  510. if (current_language != adjusted_language) {
  511. LOG_WARNING(Service_CFG, "System language %d does not fit the region. Adjusted to %d",
  512. static_cast<int>(current_language), static_cast<int>(adjusted_language));
  513. SetSystemLanguage(adjusted_language);
  514. }
  515. }
  516. }
  517. void SetUsername(const std::u16string& name) {
  518. ASSERT(name.size() <= 10);
  519. UsernameBlock block{};
  520. name.copy(block.username, name.size());
  521. SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block);
  522. }
  523. std::u16string GetUsername() {
  524. UsernameBlock block;
  525. GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block);
  526. // the username string in the block isn't null-terminated,
  527. // so we need to find the end manually.
  528. std::u16string username(block.username, ARRAY_SIZE(block.username));
  529. const size_t pos = username.find(u'\0');
  530. if (pos != std::u16string::npos)
  531. username.erase(pos);
  532. return username;
  533. }
  534. void SetBirthday(u8 month, u8 day) {
  535. BirthdayBlock block = {month, day};
  536. SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block);
  537. }
  538. std::tuple<u8, u8> GetBirthday() {
  539. BirthdayBlock block;
  540. GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block);
  541. return std::make_tuple(block.month, block.day);
  542. }
  543. void SetSystemLanguage(SystemLanguage language) {
  544. u8 block = language;
  545. SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block);
  546. }
  547. SystemLanguage GetSystemLanguage() {
  548. u8 block;
  549. GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block);
  550. return static_cast<SystemLanguage>(block);
  551. }
  552. void SetSoundOutputMode(SoundOutputMode mode) {
  553. u8 block = mode;
  554. SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block);
  555. }
  556. SoundOutputMode GetSoundOutputMode() {
  557. u8 block;
  558. GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block);
  559. return static_cast<SoundOutputMode>(block);
  560. }
  561. void GenerateConsoleUniqueId(u32& random_number, u64& console_id) {
  562. CryptoPP::AutoSeededRandomPool rng;
  563. random_number = rng.GenerateWord32(0, 0xFFFF);
  564. u64_le local_friend_code_seed;
  565. rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
  566. sizeof(local_friend_code_seed));
  567. console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
  568. }
  569. ResultCode SetConsoleUniqueId(u32 random_number, u64 console_id) {
  570. u64_le console_id_le = console_id;
  571. ResultCode res =
  572. SetConfigInfoBlock(ConsoleUniqueID1BlockID, sizeof(console_id_le), 0xE, &console_id_le);
  573. if (!res.IsSuccess())
  574. return res;
  575. res = SetConfigInfoBlock(ConsoleUniqueID2BlockID, sizeof(console_id_le), 0xE, &console_id_le);
  576. if (!res.IsSuccess())
  577. return res;
  578. u32_le random_number_le = random_number;
  579. res = SetConfigInfoBlock(ConsoleUniqueID3BlockID, sizeof(random_number_le), 0xE,
  580. &random_number_le);
  581. if (!res.IsSuccess())
  582. return res;
  583. return RESULT_SUCCESS;
  584. }
  585. u64 GetConsoleUniqueId() {
  586. u64_le console_id_le;
  587. GetConfigInfoBlock(ConsoleUniqueID2BlockID, sizeof(console_id_le), 0xE, &console_id_le);
  588. return console_id_le;
  589. }
  590. } // namespace CFG
  591. } // namespace Service