summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--CMakeLists.txt15
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/romfs.cpp102
-rw-r--r--src/core/hle/romfs.h22
-rw-r--r--src/core/hle/service/apt/apt.cpp154
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.cpp6
-rw-r--r--src/core/hle/service/frd/frd.cpp43
-rw-r--r--src/core/hle/service/frd/frd.h13
-rw-r--r--src/core/hle/service/frd/frd_u.cpp2
10 files changed, 337 insertions, 24 deletions
diff --git a/.gitmodules b/.gitmodules
index 45ff650ef..3f5bae2b0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,7 +25,7 @@
25[submodule "fmt"] 25[submodule "fmt"]
26 path = externals/fmt 26 path = externals/fmt
27 url = https://github.com/fmtlib/fmt.git 27 url = https://github.com/fmtlib/fmt.git
28[submodule "externals/enet"] 28[submodule "enet"]
29 path = externals/enet 29 path = externals/enet
30 url = https://github.com/lsalzman/enet 30 url = https://github.com/lsalzman/enet
31[submodule "cpr"] 31[submodule "cpr"]
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad73cf495..9151ff786 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,21 @@ if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
19 DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) 19 DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks)
20endif() 20endif()
21 21
22# Sanity check : Check that all submodules are present
23# =======================================================================
24
25function(check_submodules_present)
26 file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules)
27 string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
28 foreach(module ${gitmodules})
29 string(REGEX REPLACE "path *= *" "" module ${module})
30 if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
31 message(SEND_ERROR "Git submodule ${module} not found."
32 "Please run: git submodule update --init --recursive")
33 endif()
34 endforeach()
35endfunction()
36check_submodules_present()
22 37
23# Detect current compilation architecture and create standard definitions 38# Detect current compilation architecture and create standard definitions
24# ======================================================================= 39# =======================================================================
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b80efe192..360f407f3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -60,6 +60,7 @@ set(SRCS
60 hle/kernel/timer.cpp 60 hle/kernel/timer.cpp
61 hle/kernel/vm_manager.cpp 61 hle/kernel/vm_manager.cpp
62 hle/kernel/wait_object.cpp 62 hle/kernel/wait_object.cpp
63 hle/romfs.cpp
63 hle/service/ac/ac.cpp 64 hle/service/ac/ac.cpp
64 hle/service/ac/ac_i.cpp 65 hle/service/ac/ac_i.cpp
65 hle/service/ac/ac_u.cpp 66 hle/service/ac/ac_u.cpp
@@ -258,6 +259,7 @@ set(HEADERS
258 hle/kernel/vm_manager.h 259 hle/kernel/vm_manager.h
259 hle/kernel/wait_object.h 260 hle/kernel/wait_object.h
260 hle/result.h 261 hle/result.h
262 hle/romfs.h
261 hle/service/ac/ac.h 263 hle/service/ac/ac.h
262 hle/service/ac/ac_i.h 264 hle/service/ac/ac_i.h
263 hle/service/ac/ac_u.h 265 hle/service/ac/ac_u.h
diff --git a/src/core/hle/romfs.cpp b/src/core/hle/romfs.cpp
new file mode 100644
index 000000000..3157df71d
--- /dev/null
+++ b/src/core/hle/romfs.cpp
@@ -0,0 +1,102 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/swap.h"
7#include "core/hle/romfs.h"
8
9namespace RomFS {
10
11struct Header {
12 u32_le header_length;
13 u32_le dir_hash_table_offset;
14 u32_le dir_hash_table_length;
15 u32_le dir_table_offset;
16 u32_le dir_table_length;
17 u32_le file_hash_table_offset;
18 u32_le file_hash_table_length;
19 u32_le file_table_offset;
20 u32_le file_table_length;
21 u32_le data_offset;
22};
23
24static_assert(sizeof(Header) == 0x28, "Header has incorrect size");
25
26struct DirectoryMetadata {
27 u32_le parent_dir_offset;
28 u32_le next_dir_offset;
29 u32_le first_child_dir_offset;
30 u32_le first_file_offset;
31 u32_le same_hash_next_dir_offset;
32 u32_le name_length; // in bytes
33 // followed by directory name
34};
35
36static_assert(sizeof(DirectoryMetadata) == 0x18, "DirectoryMetadata has incorrect size");
37
38struct FileMetadata {
39 u32_le parent_dir_offset;
40 u32_le next_file_offset;
41 u64_le data_offset;
42 u64_le data_length;
43 u32_le same_hash_next_file_offset;
44 u32_le name_length; // in bytes
45 // followed by file name
46};
47
48static_assert(sizeof(FileMetadata) == 0x20, "FileMetadata has incorrect size");
49
50static bool MatchName(const u8* buffer, u32 name_length, const std::u16string& name) {
51 std::vector<char16_t> name_buffer(name_length / sizeof(char16_t));
52 std::memcpy(name_buffer.data(), buffer, name_length);
53 return name == std::u16string(name_buffer.begin(), name_buffer.end());
54}
55
56const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path) {
57 constexpr u32 INVALID_FIELD = 0xFFFFFFFF;
58
59 // Split path into directory names and file name
60 std::vector<std::u16string> dir_names = path;
61 dir_names.pop_back();
62 const std::u16string& file_name = path.back();
63
64 Header header;
65 std::memcpy(&header, romfs, sizeof(header));
66
67 // Find directories of each level
68 DirectoryMetadata dir;
69 const u8* current_dir = romfs + header.dir_table_offset;
70 std::memcpy(&dir, current_dir, sizeof(dir));
71 for (const std::u16string& dir_name : dir_names) {
72 u32 child_dir_offset;
73 child_dir_offset = dir.first_child_dir_offset;
74 while (true) {
75 if (child_dir_offset == INVALID_FIELD) {
76 return nullptr;
77 }
78 const u8* current_child_dir = romfs + header.dir_table_offset + child_dir_offset;
79 std::memcpy(&dir, current_child_dir, sizeof(dir));
80 if (MatchName(current_child_dir + sizeof(dir), dir.name_length, dir_name)) {
81 current_dir = current_child_dir;
82 break;
83 }
84 child_dir_offset = dir.next_dir_offset;
85 }
86 }
87
88 // Find the file
89 FileMetadata file;
90 u32 file_offset = dir.first_file_offset;
91 while (file_offset != INVALID_FIELD) {
92 const u8* current_file = romfs + header.file_table_offset + file_offset;
93 std::memcpy(&file, current_file, sizeof(file));
94 if (MatchName(current_file + sizeof(file), file.name_length, file_name)) {
95 return romfs + header.data_offset + file.data_offset;
96 }
97 file_offset = file.next_file_offset;
98 }
99 return nullptr;
100}
101
102} // namespace RomFS
diff --git a/src/core/hle/romfs.h b/src/core/hle/romfs.h
new file mode 100644
index 000000000..ee9f29760
--- /dev/null
+++ b/src/core/hle/romfs.h
@@ -0,0 +1,22 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <vector>
9#include "common/common_types.h"
10
11namespace RomFS {
12
13/**
14 * Gets the pointer to a file in a RomFS image.
15 * @param romfs The pointer to the RomFS image
16 * @param path A vector containing the directory names and file name of the path to the file
17 * @return the pointer to the file
18 * @todo reimplement this with a full RomFS manager
19 */
20const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path);
21
22} // namespace RomFS
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 25e7b777d..df4b5cc3f 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -6,11 +6,13 @@
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/file_sys/file_backend.h"
9#include "core/hle/applets/applet.h" 10#include "core/hle/applets/applet.h"
10#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/mutex.h" 12#include "core/hle/kernel/mutex.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/shared_memory.h" 14#include "core/hle/kernel/shared_memory.h"
15#include "core/hle/romfs.h"
14#include "core/hle/service/apt/apt.h" 16#include "core/hle/service/apt/apt.h"
15#include "core/hle/service/apt/apt_a.h" 17#include "core/hle/service/apt/apt_a.h"
16#include "core/hle/service/apt/apt_s.h" 18#include "core/hle/service/apt/apt_s.h"
@@ -27,6 +29,7 @@ namespace APT {
27 29
28/// Handle to shared memory region designated to for shared system font 30/// Handle to shared memory region designated to for shared system font
29static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; 31static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
32static bool shared_font_loaded = false;
30static bool shared_font_relocated = false; 33static bool shared_font_relocated = false;
31 34
32static Kernel::SharedPtr<Kernel::Mutex> lock; 35static Kernel::SharedPtr<Kernel::Mutex> lock;
@@ -71,7 +74,7 @@ void Initialize(Service::Interface* self) {
71void GetSharedFont(Service::Interface* self) { 74void GetSharedFont(Service::Interface* self) {
72 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 75 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
73 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); 76 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
74 if (!shared_font_mem) { 77 if (!shared_font_loaded) {
75 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); 78 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
76 rb.Push<u32>(-1); // TODO: Find the right error code 79 rb.Push<u32>(-1); // TODO: Find the right error code
77 rb.Skip(1 + 2, true); 80 rb.Skip(1 + 2, true);
@@ -644,36 +647,146 @@ void CheckNew3DS(Service::Interface* self) {
644 LOG_WARNING(Service_APT, "(STUBBED) called"); 647 LOG_WARNING(Service_APT, "(STUBBED) called");
645} 648}
646 649
647void Init() { 650static u32 DecompressLZ11(const u8* in, u8* out) {
648 AddService(new APT_A_Interface); 651 u32_le decompressed_size;
649 AddService(new APT_S_Interface); 652 memcpy(&decompressed_size, in, sizeof(u32));
650 AddService(new APT_U_Interface); 653 in += 4;
651 654
652 HLE::Applets::Init(); 655 u8 type = decompressed_size & 0xFF;
653 656 ASSERT(type == 0x11);
654 // Load the shared system font (if available). 657 decompressed_size >>= 8;
658
659 u32 current_out_size = 0;
660 u8 flags = 0, mask = 1;
661 while (current_out_size < decompressed_size) {
662 if (mask == 1) {
663 flags = *(in++);
664 mask = 0x80;
665 } else {
666 mask >>= 1;
667 }
668
669 if (flags & mask) {
670 u8 byte1 = *(in++);
671 u32 length = byte1 >> 4;
672 u32 offset;
673 if (length == 0) {
674 u8 byte2 = *(in++);
675 u8 byte3 = *(in++);
676 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
677 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
678 } else if (length == 1) {
679 u8 byte2 = *(in++);
680 u8 byte3 = *(in++);
681 u8 byte4 = *(in++);
682 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
683 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
684 } else {
685 u8 byte2 = *(in++);
686 length = (byte1 >> 4) + 0x1;
687 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
688 }
689
690 for (u32 i = 0; i < length; i++) {
691 *out = *(out - offset);
692 ++out;
693 }
694
695 current_out_size += length;
696 } else {
697 *(out++) = *(in++);
698 current_out_size++;
699 }
700 }
701 return decompressed_size;
702}
703
704static bool LoadSharedFont() {
705 // TODO (wwylele): load different font archive for region CHN/KOR/TWN
706 const u64_le shared_font_archive_id_low = 0x0004009b00014002;
707 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
708 std::vector<u8> shared_font_archive_id(16);
709 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
710 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
711 FileSys::Path archive_path(shared_font_archive_id);
712 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
713 if (archive_result.Failed())
714 return false;
715
716 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
717 FileSys::Path file_path(romfs_path);
718 FileSys::Mode open_mode = {};
719 open_mode.read_flag.Assign(1);
720 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
721 if (file_result.Failed())
722 return false;
723
724 auto romfs = std::move(file_result).Unwrap();
725 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
726 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
727 romfs->backend->Close();
728
729 const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"});
730 if (font_file == nullptr)
731 return false;
732
733 struct {
734 u32_le status;
735 u32_le region;
736 u32_le decompressed_size;
737 INSERT_PADDING_WORDS(0x1D);
738 } shared_font_header{};
739 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
740
741 shared_font_header.status = 2; // successfully loaded
742 shared_font_header.region = 1; // region JPN/EUR/USA
743 shared_font_header.decompressed_size =
744 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
745 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
746 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
747
748 return true;
749}
750
751static bool LoadLegacySharedFont() {
752 // This is the legacy method to load shared font.
655 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header 753 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
656 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided 754 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
657 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file 755 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
658 // "shared_font.bin" in the Citra "sysdata" directory. 756 // "shared_font.bin" in the Citra "sysdata" directory.
659
660 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; 757 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
661 758
662 FileUtil::CreateFullPath(filepath); // Create path if not already created 759 FileUtil::CreateFullPath(filepath); // Create path if not already created
663 FileUtil::IOFile file(filepath, "rb"); 760 FileUtil::IOFile file(filepath, "rb");
664
665 if (file.IsOpen()) { 761 if (file.IsOpen()) {
666 // Create shared font memory object
667 using Kernel::MemoryPermission;
668 shared_font_mem =
669 Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
670 MemoryPermission::ReadWrite, MemoryPermission::Read, 0,
671 Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
672 // Read shared font data
673 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); 762 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
763 return true;
764 }
765
766 return false;
767}
768
769void Init() {
770 AddService(new APT_A_Interface);
771 AddService(new APT_S_Interface);
772 AddService(new APT_U_Interface);
773
774 HLE::Applets::Init();
775
776 using Kernel::MemoryPermission;
777 shared_font_mem =
778 Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
779 MemoryPermission::ReadWrite, MemoryPermission::Read, 0,
780 Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
781
782 if (LoadSharedFont()) {
783 shared_font_loaded = true;
784 } else if (LoadLegacySharedFont()) {
785 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
786 shared_font_loaded = true;
674 } else { 787 } else {
675 LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); 788 LOG_WARNING(Service_APT, "Unable to load shared font");
676 shared_font_mem = nullptr; 789 shared_font_loaded = false;
677 } 790 }
678 791
679 lock = Kernel::Mutex::Create(false, "APT_U:Lock"); 792 lock = Kernel::Mutex::Create(false, "APT_U:Lock");
@@ -693,6 +806,7 @@ void Init() {
693 806
694void Shutdown() { 807void Shutdown() {
695 shared_font_mem = nullptr; 808 shared_font_mem = nullptr;
809 shared_font_loaded = false;
696 shared_font_relocated = false; 810 shared_font_relocated = false;
697 lock = nullptr; 811 lock = nullptr;
698 notification_event = nullptr; 812 notification_event = nullptr;
diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
index 57eb39d75..6d2474702 100644
--- a/src/core/hle/service/apt/bcfnt/bcfnt.cpp
+++ b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
@@ -78,7 +78,8 @@ void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAd
78 memcpy(&cmap, data, sizeof(cmap)); 78 memcpy(&cmap, data, sizeof(cmap));
79 79
80 // Relocate the offsets in the CMAP section 80 // Relocate the offsets in the CMAP section
81 cmap.next_cmap_offset += offset; 81 if (cmap.next_cmap_offset != 0)
82 cmap.next_cmap_offset += offset;
82 83
83 memcpy(data, &cmap, sizeof(cmap)); 84 memcpy(data, &cmap, sizeof(cmap));
84 } else if (memcmp(section_header.magic, "CWDH", 4) == 0) { 85 } else if (memcmp(section_header.magic, "CWDH", 4) == 0) {
@@ -86,7 +87,8 @@ void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAd
86 memcpy(&cwdh, data, sizeof(cwdh)); 87 memcpy(&cwdh, data, sizeof(cwdh));
87 88
88 // Relocate the offsets in the CWDH section 89 // Relocate the offsets in the CWDH section
89 cwdh.next_cwdh_offset += offset; 90 if (cwdh.next_cwdh_offset != 0)
91 cwdh.next_cwdh_offset += offset;
90 92
91 memcpy(data, &cwdh, sizeof(cwdh)); 93 memcpy(data, &cwdh, sizeof(cwdh));
92 } else if (memcmp(section_header.magic, "TGLP", 4) == 0) { 94 } else if (memcmp(section_header.magic, "TGLP", 4) == 0) {
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
index 76ecda8b7..7ad7798da 100644
--- a/src/core/hle/service/frd/frd.cpp
+++ b/src/core/hle/service/frd/frd.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/string_util.h" 7#include "common/string_util.h"
8#include "core/hle/ipc.h" 8#include "core/hle/ipc.h"
9#include "core/hle/ipc_helpers.h"
9#include "core/hle/result.h" 10#include "core/hle/result.h"
10#include "core/hle/service/frd/frd.h" 11#include "core/hle/service/frd/frd.h"
11#include "core/hle/service/frd/frd_a.h" 12#include "core/hle/service/frd/frd_a.h"
@@ -105,6 +106,48 @@ void GetMyScreenName(Service::Interface* self) {
105 LOG_WARNING(Service_FRD, "(STUBBED) called"); 106 LOG_WARNING(Service_FRD, "(STUBBED) called");
106} 107}
107 108
109void UnscrambleLocalFriendCode(Service::Interface* self) {
110 const size_t scrambled_friend_code_size = 12;
111 const size_t friend_code_size = 8;
112
113 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1C, 1, 2);
114 const u32 friend_code_count = rp.Pop<u32>();
115 size_t in_buffer_size;
116 const VAddr scrambled_friend_codes = rp.PopStaticBuffer(&in_buffer_size, false);
117 ASSERT_MSG(in_buffer_size == (friend_code_count * scrambled_friend_code_size),
118 "Wrong input buffer size");
119
120 size_t out_buffer_size;
121 VAddr unscrambled_friend_codes = rp.PeekStaticBuffer(0, &out_buffer_size);
122 ASSERT_MSG(out_buffer_size == (friend_code_count * friend_code_size),
123 "Wrong output buffer size");
124
125 for (u32 current = 0; current < friend_code_count; ++current) {
126 // TODO(B3N30): Unscramble the codes and compare them against the friend list
127 // Only write 0 if the code isn't in friend list, otherwise write the
128 // unscrambled one
129 //
130 // Code for unscrambling (should be compared to HW):
131 // std::array<u16, 6> scambled_friend_code;
132 // Memory::ReadBlock(scrambled_friend_codes+(current*scrambled_friend_code_size),
133 // scambled_friend_code.data(), scrambled_friend_code_size); std::array<u16, 4>
134 // unscrambled_friend_code; unscrambled_friend_code[0] = scambled_friend_code[0] ^
135 // scambled_friend_code[5]; unscrambled_friend_code[1] = scambled_friend_code[1] ^
136 // scambled_friend_code[5]; unscrambled_friend_code[2] = scambled_friend_code[2] ^
137 // scambled_friend_code[5]; unscrambled_friend_code[3] = scambled_friend_code[3] ^
138 // scambled_friend_code[5];
139
140 u64 result = 0ull;
141 Memory::WriteBlock(unscrambled_friend_codes + (current * sizeof(result)), &result,
142 sizeof(result));
143 }
144
145 LOG_WARNING(Service_FRD, "(STUBBED) called");
146 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
147 rb.Push(RESULT_SUCCESS);
148 rb.PushStaticBuffer(unscrambled_friend_codes, out_buffer_size, 0);
149}
150
108void SetClientSdkVersion(Service::Interface* self) { 151void SetClientSdkVersion(Service::Interface* self) {
109 u32* cmd_buff = Kernel::GetCommandBuffer(); 152 u32* cmd_buff = Kernel::GetCommandBuffer();
110 153
diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h
index e61940ea0..66a87c8cd 100644
--- a/src/core/hle/service/frd/frd.h
+++ b/src/core/hle/service/frd/frd.h
@@ -96,6 +96,19 @@ void GetMyFriendKey(Service::Interface* self);
96void GetMyScreenName(Service::Interface* self); 96void GetMyScreenName(Service::Interface* self);
97 97
98/** 98/**
99 * FRD::UnscrambleLocalFriendCode service function
100 * Inputs:
101 * 1 : Friend code count
102 * 2 : ((count * 12) << 14) | 0x402
103 * 3 : Pointer to encoded friend codes. Each is 12 bytes large
104 * 64 : ((count * 8) << 14) | 2
105 * 65 : Pointer to write decoded local friend codes to. Each is 8 bytes large.
106 * Outputs:
107 * 1 : Result of function, 0 on success, otherwise error code
108 */
109void UnscrambleLocalFriendCode(Service::Interface* self);
110
111/**
99 * FRD::SetClientSdkVersion service function 112 * FRD::SetClientSdkVersion service function
100 * Inputs: 113 * Inputs:
101 * 1 : Used SDK Version 114 * 1 : Used SDK Version
diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp
index 496f29ca9..6970ff768 100644
--- a/src/core/hle/service/frd/frd_u.cpp
+++ b/src/core/hle/service/frd/frd_u.cpp
@@ -36,7 +36,7 @@ const Interface::FunctionInfo FunctionTable[] = {
36 {0x00190042, nullptr, "GetFriendFavoriteGame"}, 36 {0x00190042, nullptr, "GetFriendFavoriteGame"},
37 {0x001A00C4, nullptr, "GetFriendInfo"}, 37 {0x001A00C4, nullptr, "GetFriendInfo"},
38 {0x001B0080, nullptr, "IsIncludedInFriendList"}, 38 {0x001B0080, nullptr, "IsIncludedInFriendList"},
39 {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, 39 {0x001C0042, UnscrambleLocalFriendCode, "UnscrambleLocalFriendCode"},
40 {0x001D0002, nullptr, "UpdateGameModeDescription"}, 40 {0x001D0002, nullptr, "UpdateGameModeDescription"},
41 {0x001E02C2, nullptr, "UpdateGameMode"}, 41 {0x001E02C2, nullptr, "UpdateGameMode"},
42 {0x001F0042, nullptr, "SendInvitation"}, 42 {0x001F0042, nullptr, "SendInvitation"},